当前位置: 首页 > news >正文

内核协议栈源码阅读(三) --- 网桥处理

文章目录

    • 一、网桥处理概述
    • 二、本地多播
    • 三、ebtable 的实现原理以及 ebt_broute
      • 3.1 ebtable 的实现
      • 3.2 ebt_broute
      • 3.3 ebt_do_table
    • 四、br_handle_frame_finish
      • 4.1 br_fdb_update
      • 4.2 br_pass_frame_up
      • 4.3 多播 br_flood_forward
      • 4.4 非多播 __br_fdb_get + br_forward
      • 4.5 最终转发函数 __br_forward
    • 总结
    • 附录

从前面我们知道,软中断收包后会送入 netif_receive_skb 函数处理,该函数主要处理 netpoll网桥,前两步没有将报文处理掉最后才是寻找匹配的协议进行报文处理。

前面简单介绍了 netpoll 的逻辑,现在我们介绍网桥的处理。内核代码版本 linux 2.6

一、网桥处理概述

如果开启了网桥模块,则 handle_bridge 不是个空函数。

该函数:

static __inline__ int handle_bridge(struct sk_buff **pskb,struct packet_type **pt_prev, int *ret,struct net_device *orig_dev)
{struct net_bridge_port *port;if ((*pskb)->pkt_type == PACKET_LOOPBACK ||(port = rcu_dereference((*pskb)->dev->br_port)) == NULL)return 0;if (*pt_prev) {*ret = deliver_skb(*pskb, *pt_prev, orig_dev);*pt_prev = NULL;} return br_handle_frame_hook(port, pskb);
}
  • 包如果是回环包或者接收包的设备没有挂载到网桥上,则返回 0 进行后续的协议栈处理
  • 如果包非回环包且接收设备挂在网桥上,那么进行网桥处理,执行 br_handle_frame_hook
  • br_handle_frame_hookbr_init 初始化网桥模块时设置为 br_handle_frame 函数

接下来看 br_handle_frame 函数:

int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
{struct sk_buff *skb = *pskb;const unsigned char *dest = eth_hdr(skb)->h_dest;if (!is_valid_ether_addr(eth_hdr(skb)->h_source))goto err;if (unlikely(is_link_local(dest))) {skb->pkt_type = PACKET_HOST;return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL, br_handle_local_finish) != 0;}if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {if (br_should_route_hook) {if (br_should_route_hook(pskb)) return 0;skb = *pskb;dest = eth_hdr(skb)->h_dest;}if (!compare_ether_addr(p->br->dev->dev_addr, dest))skb->pkt_type = PACKET_HOST;NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,br_handle_frame_finish);return 1;}err:kfree_skb(skb);return 1;
}
  • 判断源mac地址是否非法(is_valid_ether_addr 需要mac地址非多播地址且非0)
  • 判断目的地址是否是局域网多播,如果是的话扔到 BRIDGE 协议的 LOCAL_IN 过滤链,通过过滤链后执行 br_handle_local_finish
    • NF 的组成为一个二维数组数组元素为一个链表;按找[pf][hook]组织,其中 PF_BRIDGEpfNF_BR_LOCAL_INhook 名字。除此之外还有以下几种 hook
    #define NF_BR_PRE_ROUTING	0
    /* If the packet is destined for this box. */
    #define NF_BR_LOCAL_IN		1
    /* If the packet is destined for another interface. */
    #define NF_BR_FORWARD		2
    /* Packets coming from a local process. */
    #define NF_BR_LOCAL_OUT		3
    /* Packets about to hit the wire. */
    #define NF_BR_POST_ROUTING	4
    /* Not really a hook, but used for the ebtables broute table */
    #define NF_BR_BROUTING		5
    #define NF_BR_NUMHOOKS		6
    /*
    NF_BR_PRE_ROUTING:数据包刚进入桥接设备时。
    NF_BR_LOCAL_IN:数据包被桥接设备本地处理时。
    NF_BR_FORWARD:数据包被桥接设备转发时。
    NF_BR_LOCAL_OUT:本地生成的数据包准备发送时。
    NF_BR_POST_ROUTING:数据包即将离开桥接设备时。
    */
    
    • 非本地多播,只有网桥port处于转发或者学习模式才可正常处理包,执行 br_should_route_hook
  • br_should_route_hookebtable 模块初始化时设置为 ebt_broute该函数返回1 表示路由转发该包(三层转发),返回 0 表示桥接转发该包(二层转发)
  • br_should_route_hook 返回 0 表示继续桥接处理,比较目的地址是否网桥 port 关联的设备 mac,是的话表示这个包就是发到虚拟网桥的这个网口的,修改包的 typePACKET_HOST
  • 执行 PF_BRIDGENF_BR_PRE_ROUTING 过滤链,执行完毕后执行 br_handle_frame_finish

小结:总共三个分支,下面我们分三个小节分别介绍这三部分

  • 本地多播:经过 PF_BRIDGENF_BR_LOCAL_IN 过滤链,最后执行 br_handle_local_finish
  • ebt_broute 判断路由还是桥接
  • 桥接的情况下,经过 PF_BRIDGENF_BR_PRE_ROUTING 过滤链,然后执行 br_handle_frame_finish

二、本地多播

本地多播的情况下:br_handle_local_finish 逻辑比较简单,更新网桥的 fdb 表

static int br_handle_local_finish(struct sk_buff *skb)
{struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);if (p && p->state != BR_STATE_DISABLED)br_fdb_update(p->br, p, eth_hdr(skb)->h_source);return 0;	 /* process further */
}
  • 网桥端口不能是禁用状态,然后调用 br_fdb_update
  • fdb 表是二层转发表,记录了 mac 和网桥的端口的映射。即网桥收到一个包后根据目的 mac 查找该表,然后找到对应的端口转发出去
void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,const unsigned char *addr)
{struct hlist_head *head = &br->hash[br_mac_hash(addr)];struct net_bridge_fdb_entry *fdb;/* some users want to always flood. */if (hold_time(br) == 0)return;fdb = fdb_find(head, addr);if (likely(fdb)) {/* attempt to update an entry for a local interface */if (unlikely(fdb->is_local)) {if (net_ratelimit()) printk(KERN_WARNING "%s: received packet with "" own address as source address\n",source->dev->name);} else {/* fastpath: update of existing entry */fdb->dst = source;fdb->ageing_timer = jiffies;}} else {spin_lock(&br->hash_lock);if (!fdb_find(head, addr))fdb_create(head, source, addr, 0);/* else  we lose race and someone else inserts* it first, don't bother updating*/spin_unlock(&br->hash_lock);}
}
  • 如果找到表项则更新,找不到则新建
  • 查找过程则是遍历整个 hashmap,匹配 mac 地址(mac 地址是 hash key)

三、ebtable 的实现原理以及 ebt_broute

3.1 ebtable 的实现

ebtable 该模块只有内核编译时设置了 CONFIG_BRIDGE_EBT_BROUTE 才会参与编译,此时 br_should_route_hook 才不会是空指针,才会执行。

该模块功能类似于 iptables在二层做包过滤和操作,专门针对以太网帧进行过滤和修改。

用法类似于 iptables:ebtables -A FORWARD -s 00:11:22:33:44:55 -j DROP

功能:

  • 二层包过滤:根据 MAC 地址、以太网类型(EtherType)、VLAN 标签等信息过滤数据包。
  • 桥接防火墙:实现类似防火墙的功能,但作用于二层,控制桥接设备上的流量。
  • 包修改:可以修改以太网帧的头部信息,如修改源 MAC 地址、目的 MAC 地址等。
  • 桥接 NAT:支持对以太网帧进行地址转换(虽然不常用)。
  • 桥接流量控制:限制或允许特定类型的流量通过桥接设备。

ebtable 主要基于 netfilter 实现,前面我们已经简单介绍过 netfilter 的组成是一个二维数组按 pf 和 hook 组织ebtable 基于前面概述出现过的 PF_BRIDGE 的五种 hook 实现(忘记了可以往上翻 br_handle_frame 部分)。

下面我们看 ebtable 的具体实现:ebtable 在 PF_BRIDGENF_BR_LOCAL_INNF_BR_FORWARDNF_BR_LOCAL_OUTNF_BR_POST_ROUTINGNF_BR_PRE_ROUTING 等五种 hook 上注册了几个钩子函数,这些钩子函数调用不同的 ebtable 函数,这些 ebtable 函数是进入 ebtable 模块的入口。

// net/bridge/netfilter/ebtable_fileter.c
// 在 NF_BR_LOCAL_IN、NF_BR_FORWARD、NF_BR_LOCAL_OUT 阶段执行
static struct nf_hook_ops ebt_ops_filter[] = {{.hook		= ebt_hook,.owner		= THIS_MODULE,.pf		= PF_BRIDGE, // netfilter pf.hooknum	= NF_BR_LOCAL_IN, // netfilter hook.priority	= NF_BR_PRI_FILTER_BRIDGED,},{.hook		= ebt_hook,.owner		= THIS_MODULE,.pf		= PF_BRIDGE,.hooknum	= NF_BR_FORWARD,.priority	= NF_BR_PRI_FILTER_BRIDGED,},{
http://www.dtcms.com/a/311657.html

相关文章:

  • 每日五个pyecharts可视化图表-bars(1)
  • AG32mcu通过寄存器方式操作cpld
  • linux ssh公钥移除办法
  • K8S部署ELK(三):部署Elasticsearch搜索引擎
  • accept函数及示例
  • CMake指令:mark_as_advanced
  • Django 日志配置详解
  • gbase8s 常见表约束介绍
  • 数字化转型驱动中小制造企业的质量管理升级
  • 技术面试知识点详解 - 从电路到编程的全栈面经
  • 【密码学】5. 公钥密码
  • 【Python修仙编程】(二) Python3灵源初探(11)
  • Noob靶机
  • 集成电路学习:什么是CMSIS微控制器软件接口标准
  • 用键盘快速移动Word和WPS文字中的选中段落
  • K8S部署ELK(二):部署Kafka消息队列
  • 计算机分类大全
  • 【学习笔记】MySQL技术内幕InnoDB存储引擎——第9章 性能调优
  • Android 13/14/15 默认授权应用权限的实现方法
  • 广告牌+序列帧的Shader效果
  • rocky\centos安装docker镜像的命令
  • 深入理解C++中的list容器:介绍、使用与实现
  • Flutter dart运算符
  • mini-swe-agent源码解读(进行中)
  • Redis 7 哈希(Hash)使用指南
  • 细分推广场景,让推客推广更贴合用户当下需求
  • 存储过程的介绍、基本语法、delimiter的使用
  • 未来交通:元宇宙技术重塑出行体验
  • 用Unity结合VCC更改人物模型出现的BUG
  • WebSocket断线重连机制:保障实时通信的高可用性