拓扑

vlan_dev --> br-lan --> IP层 (br本身也会有一个fdb条目)

网桥创建/添加节点

模块初始化

static int __init br_init(void)
{int err;/*注册STP协议*/err = stp_proto_register(&br_stp_proto);/*分配fdb的slab: "bridge_fdb_cache", 变量br_fdb_cache*/err = br_fdb_init();/*命名空间退出时删除掉bridge*/err = register_pernet_subsys(&br_net_ops);/*初始化br_netfilter*/err = br_netfilter_init();/*注册netdev通知链,监听网络接口变化时间*/err = register_netdevice_notifier(&br_device_notifier);err = br_netlink_init();/*注册IOCTL函数*/brioctl_set(br_ioctl_deviceless_stub);#if IS_ENABLED(CONFIG_ATM_LANE)br_fdb_test_addr_hook = br_fdb_test_addr;
#endifreturn 0;
....../*异常处理*/
}

数据结构

net_bridge

struct net_bridge
{spinlock_t         lock;/*port链表*/struct list_head     port_list;/*cpu port的net_dev*/struct net_device     *dev;struct br_cpu_netstats __percpu *stats;spinlock_t          hash_lock;/*fdb hash表*/struct hlist_head        hash[BR_HASH_SIZE];
#ifdef CONFIG_BRIDGE_NETFILTERstruct rtable             fake_rtable;bool                nf_call_iptables;bool               nf_call_ip6tables;bool              nf_call_arptables;
#endifu16               group_fwd_mask;/* STP */bridge_id           designated_root;bridge_id           bridge_id;u32               root_path_cost;unsigned long            max_age;unsigned long           hello_time;unsigned long            forward_delay;unsigned long         bridge_max_age;unsigned long            ageing_time;unsigned long           bridge_hello_time;unsigned long         bridge_forward_delay;u8             group_addr[ETH_ALEN];u16                root_port;enum {BR_NO_STP,      /* no spanning tree */BR_KERNEL_STP,        /* old STP in kernel */BR_USER_STP,     /* new RSTP in userspace */} stp_enabled;/*拓扑变化事件*/unsigned char            topology_change;unsigned char           topology_change_detected;#ifdef CONFIG_BRIDGE_IGMP_SNOOPINGunsigned char            multicast_router;u8             multicast_disabled:1;u8             multicast_querier:1;u32             hash_elasticity;u32             hash_max;u32                multicast_last_member_count;u32             multicast_startup_queries_sent;u32              multicast_startup_query_count;unsigned long         multicast_last_member_interval;unsigned long            multicast_membership_interval;unsigned long         multicast_querier_interval;unsigned long            multicast_query_interval;unsigned long          multicast_query_response_interval;unsigned long         multicast_startup_query_interval;spinlock_t         multicast_lock;struct net_bridge_mdb_htable __rcu *mdb;struct hlist_head        router_list;struct timer_list       multicast_router_timer;struct timer_list        multicast_querier_timer;struct timer_list       multicast_query_timer;
#endif/*STP相关定时器*/struct timer_list     hello_timer;struct timer_list       tcn_timer;struct timer_list     topology_change_timer;/*fdb条目回收定时器*/struct timer_list       gc_timer;struct kobject         *ifobj;
#ifdef CONFIG_BRIDGE_VLAN_FILTERINGu8               vlan_enabled;struct net_port_vlans __rcu    *vlan_info;
#endif
};

net_bridge_port

struct net_bridge_port
{/*关联的网桥*/struct net_bridge     *br;struct net_device       *dev;struct list_head       list;/* STP */u8                priority;/*状态: 转发or学习*/u8               state;u16               port_no;unsigned char           topology_change_ack;unsigned char           config_pending;/*端口号*/port_id               port_id;port_id             designated_port;bridge_id           designated_root;bridge_id           designated_bridge;u32               path_cost;u32               designated_cost;unsigned long           designated_age;struct timer_list        forward_delay_timer;struct timer_list       hold_timer;struct timer_list        message_age_timer;struct kobject            kobj;struct rcu_head            rcu;unsigned long           flags;
#define BR_HAIRPIN_MODE     0x00000001
#define BR_BPDU_GUARD           0x00000002
#define BR_ROOT_BLOCK       0x00000004
#define BR_MULTICAST_FAST_LEAVE 0x00000008
#define BR_ADMIN_COST       0x00000010#ifdef CONFIG_BRIDGE_IGMP_SNOOPINGu32             multicast_startup_queries_sent;unsigned char            multicast_router;struct timer_list      multicast_router_timer;struct timer_list        multicast_query_timer;struct hlist_head     mglist;struct hlist_node        rlist;
#endif#ifdef CONFIG_SYSFSchar               sysfs_name[IFNAMSIZ];
#endif#ifdef CONFIG_NET_POLL_CONTROLLERstruct netpoll           *np;
#endif
#ifdef CONFIG_BRIDGE_VLAN_FILTERING/*vlan信息*/struct net_port_vlans __rcu    *vlan_info;
#endif
};

创建网桥

创建网桥是在 sock_ioctl中

static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{case SIOCGIFBR:case SIOCSIFBR:case SIOCBRADDBR:case SIOCBRDELBR:err = -ENOPKG;if (!br_ioctl_hook)request_module("bridge");mutex_lock(&br_ioctl_mutex);if (br_ioctl_hook)err = br_ioctl_hook(net, cmd, argp);mutex_unlock(&br_ioctl_mutex);
}

br_ioctl_hook的钩子在br_init复制,指向br_ioctl_deviceless_stub函数。

int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
{switch (cmd) {case SIOCGIFBR:case SIOCSIFBR:return old_deviceless(net, uarg);case SIOCBRADDBR:case SIOCBRDELBR:{char buf[IFNAMSIZ];if (!ns_capable(net->user_ns, CAP_NET_ADMIN))return -EPERM;if (copy_from_user(buf, uarg, IFNAMSIZ))return -EFAULT;buf[IFNAMSIZ-1] = 0;if (cmd == SIOCBRADDBR)return br_add_bridge(net, buf);return br_del_bridge(net, buf);}}return -EOPNOTSUPP;
}

创建网桥设备:br_add_bridge函数<br_if.c>

删除网桥设备:br_del_bridge函数

创建网桥:

int br_add_bridge(struct net *net, const char *name)
{struct net_device *dev;int res;/*分配net_device, 同时使用br_dev_setup初始化*/dev = alloc_netdev(sizeof(struct net_bridge), name,br_dev_setup);/*linkops有何用?*/dev_net_set(dev, net);dev->rtnl_link_ops = &br_link_ops;/*注册*/res = register_netdev(dev);}

很简单: 注册一个net_device而已,唯一的区别在net_device的赋值上,赋值函数br_dev_setup<br_device.c>。

void br_dev_setup(struct net_device *dev)
{/*net_device的私有数据存储net_bridge* 继承关系*/struct net_bridge *br = netdev_priv(dev);/*随机生成一个mac*/eth_hw_addr_random(dev);/*设置以太网dev的参数*/ether_setup(dev);/*重点: 操作集发包函数和ioctl*/dev->netdev_ops = &br_netdev_ops;dev->destructor = br_dev_free;/*ethtools工具的ops*/SET_ETHTOOL_OPS(dev, &br_ethtool_ops);/*设置device_type为bridge*/SET_NETDEV_DEVTYPE(dev, &br_type);/*rx队列的长度设置为0?*/dev->tx_queue_len = 0;/*接口标识: IFF_EBRIDGE 网桥设备*/dev->priv_flags = IFF_EBRIDGE;dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | NETIF_F_LLTX |NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_CTAG_TX;dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |NETIF_F_HW_VLAN_CTAG_TX;br->dev = dev;spin_lock_init(&br->lock);INIT_LIST_HEAD(&br->port_list);spin_lock_init(&br->hash_lock);br->bridge_id.prio[0] = 0x80;br->bridge_id.prio[1] = 0x00;memcpy(br->group_addr, eth_reserved_addr_base, ETH_ALEN);/*默认是不开STP的*/br->stp_enabled = BR_NO_STP;br->group_fwd_mask = BR_GROUPFWD_DEFAULT;/*STP相关参数*/br->designated_root = br->bridge_id;br->bridge_max_age = br->max_age = 20 * HZ;br->bridge_hello_time = br->hello_time = 2 * HZ;br->bridge_forward_delay = br->forward_delay = 15 * HZ;br->ageing_time = 300 * HZ;/*设置虚拟的路由fake_rtable*/br_netfilter_rtable_init(br);/*STP定时器*/br_stp_timer_init(br);/*空函数*/br_multicast_init(br);
}
  1. bridge结构保存在net_device的私有数据中,继承关系;
  2. 随机生成一个mac;
  3. 设置以太网dev的参数
  4. 设置net_dev操作集netdev_ops为br_netdev_ops,主要关注发包函数和ioctl
  5. 接口标识: IFF_EBRIDGE
  6. 设置STP定时器以及参数,默认是不开STP的;
  7. 设置虚拟路由;?

net_device的操作集:br_netdev_ops

static const struct net_device_ops br_netdev_ops = {.ndo_open        = br_dev_open,.ndo_stop        = br_dev_stop,.ndo_init        = br_dev_init,.ndo_start_xmit      = br_dev_xmit,.ndo_get_stats64     = br_get_stats64,.ndo_set_mac_address  = br_set_mac_address,.ndo_set_rx_mode  = br_dev_set_multicast_list,.ndo_change_mtu        = br_change_mtu,.ndo_do_ioctl      = br_dev_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER.ndo_netpoll_setup  = br_netpoll_setup,.ndo_netpoll_cleanup    = br_netpoll_cleanup,.ndo_poll_controller  = br_poll_controller,
#endif.ndo_add_slave         = br_add_slave,.ndo_del_slave      = br_del_slave,.ndo_fix_features        = br_fix_features,.ndo_fdb_add        = br_fdb_add,.ndo_fdb_del      = br_fdb_delete,.ndo_fdb_dump      = br_fdb_dump,.ndo_bridge_getlink  = br_getlink,.ndo_bridge_setlink   = br_setlink,.ndo_bridge_dellink   = br_dellink,
};

发包函数: ndo_start_xmit = br_dev_xmit;

ioctl: ndo_do_ioctl = br_dev_ioctl;

还有FDB的添加删除操作:ndo_fdb_add等。

添加接口

添加接口是在net_dev_ops中的ioctl函数br_dev_ioctl中。

int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{struct net_bridge *br = netdev_priv(dev);switch(cmd) {case SIOCDEVPRIVATE:/*旧的IOCTL接口:add/devif* 获取bridge/port信息* 设置STP参数* 设置FDB老化时间*/return old_dev_ioctl(dev, rq, cmd);case SIOCBRADDIF:case SIOCBRDELIF:return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);}
}

最终的给网桥添加接口的函数br_add_if

int br_add_if(struct net_bridge *br, struct net_device *dev)
{struct net_bridge_port *p;int err = 0;bool changed_addr;/*非以太网设备不支持添加*//* Don't allow bridging non-ethernet like devices */if ((dev->flags & IFF_LOOPBACK) ||dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||!is_valid_ether_addr(dev->dev_addr))return -EINVAL;/* No bridging of bridges *//*不支持bridge下挂bridge*/if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit)return -ELOOP;/* Device is already being bridged */if (br_port_exists(dev))return -EBUSY;/*接口不支持网桥*//* No bridging devices that dislike that (e.g. wireless) */if (dev->priv_flags & IFF_DONT_BRIDGE)return -EOPNOTSUPP;p = new_nbp(br, dev);if (IS_ERR(p))return PTR_ERR(p);/*调用NETDEC_JOIN通知链*/call_netdevice_notifiers(NETDEV_JOIN, dev);/*设置混杂模式,不然会丢弃目的mac不是自己的包*/err = dev_set_promiscuity(dev, 1);if (err)goto put_back;err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),SYSFS_BRIDGE_PORT_ATTR);if (err)goto err1;err = br_sysfs_addif(p);if (err)goto err2;if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL))))goto err3;err = netdev_master_upper_dev_link(dev, br->dev);if (err)goto err4;/*设置设备的接收函数为网桥的接收函数br_handle_frame*/err = netdev_rx_handler_register(dev, br_handle_frame, p);if (err)goto err5;/*设置dev标记为bridge的一个port*/dev->priv_flags |= IFF_BRIDGE_PORT;/*禁止设备的lro*/dev_disable_lro(dev);/*加入网桥的port_list*/list_add_rcu(&p->list, &br->port_list);netdev_update_features(br->dev);spin_lock_bh(&br->lock);changed_addr = br_stp_recalculate_bridge_id(br);if (netif_running(dev) && netif_oper_up(dev) &&(br->dev->flags & IFF_UP))br_stp_enable_port(p);spin_unlock_bh(&br->lock);/*发送netlink通知newlink*/br_ifinfo_notify(RTM_NEWLINK, p);if (changed_addr)call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);/*设置接口的mtu为网桥中最小的mtu, 重点*/dev_set_mtu(br->dev, br_min_mtu(br));/*创建一条该接口的fdb,命中直接走协议栈,不转发*/if (br_fdb_insert(br, p, dev->dev_addr, 0))netdev_err(dev, "failed insert local address bridge forwarding table\n");kobject_uevent(&p->kobj, KOBJ_ADD);return 0;}

首先过滤条件:

  1. 非以太网设备不能加入bridge

  2. 网桥设备不能加入bridge, 准确来说是发送函数是br_dev_xmit的设备不能再加入网桥。

  3. 待加入的设备设置了不支持网桥的priv_flag:IFF_DONT_BRIDGE

然后步骤:

  1. 分配一个网桥端口结构:net_bridge_port
  2. 发送netdev通知链事件NETDEV_JOIN
  3. 标记带加入设备为网桥的一个port: IFF_BRIDGE_PORT
  4. 将net_bridge_port加入br的port_list链表
  5. 设置待加入设备的接收函数为网桥接收函数br_handle_frame
  6. 设置待加入设备mtu为网桥中最小的mtu
  7. 禁止待加入设备的lro功能
  8. linux4.x的内核,还会根据加入设备的headroom扩展br的headroom
  9. STP相关后面章节讨论

br_netfilter

datapath

发送

br的tx函数为net_device_ops的ndo_start_xmit成员:br_dev_xmit

netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{struct net_bridge *br = netdev_priv(dev);const unsigned char *dest = skb->data;struct net_bridge_fdb_entry *dst;struct net_bridge_mdb_entry *mdst;struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);u16 vid = 0;rcu_read_lock();
#ifdef CONFIG_BRIDGE_NETFILTERif (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) {br_nf_pre_routing_finish_bridge_slow(skb);rcu_read_unlock();return NETDEV_TX_OK;}
#endifu64_stats_update_begin(&brstats->syncp);brstats->tx_packets++;brstats->tx_bytes += skb->len;u64_stats_update_end(&brstats->syncp);/*查看skb是否有vid, 且和br的vid是否一致*/if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))goto out;BR_INPUT_SKB_CB(skb)->brdev = dev;skb_reset_mac_header(skb);skb_pull(skb, ETH_HLEN);if (is_broadcast_ether_addr(dest))br_flood_deliver(br, skb);else if (is_multicast_ether_addr(dest)) {if (unlikely(netpoll_tx_running(dev))) {br_flood_deliver(br, skb);goto out;}if (br_multicast_rcv(br, NULL, skb)) {kfree_skb(skb);goto out;}mdst = br_mdb_get(br, skb, vid);if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb))br_multicast_deliver(mdst, skb);elsebr_flood_deliver(br, skb);} else if ((dst = __br_fdb_get(br, dest, vid)) != NULL)/*直接发往对应端口*/br_deliver(dst->dst, skb);else/*没找到fdb,则洪范*/br_flood_deliver(br, skb);out:rcu_read_unlock();return NETDEV_TX_OK;
}
  1. 先检查skb的vid和br的cpuport的vid是否一致,不一致则不能发送;br本身也是可以有vid的。

  2. 单播: 找到fdb调用 br_deliver发往对应端口

    没找到fdb则调用br_flood_deliver洪范

    __br_deliver函数:

    static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
    {skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);if (!skb)return;skb->dev = to->dev;if (unlikely(netpoll_tx_running(to->br->dev))) {if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))kfree_skb(skb);else {skb_push(skb, ETH_HLEN);br_netpoll_send_skb(to, skb);}return;}NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,br_forward_finish);
    }
    
    1. 检查是否支持netpool
    2. 最终调用br_forward_finish将报文发出

接收

数据报文的接收函数: 每个port dev对应的的rx_handler: br_handle_frame<br_imput.c>

rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{struct net_bridge_port *p;struct sk_buff *skb = *pskb;const unsigned char *dest = eth_hdr(skb)->h_dest;br_should_route_hook_t *rhook;if (unlikely(skb->pkt_type == PACKET_LOOPBACK))return RX_HANDLER_PASS;if (!is_valid_ether_addr(eth_hdr(skb)->h_source))goto drop;/*如果有shareinfo则clone skb*/skb = skb_share_check(skb, GFP_ATOMIC);if (!skb)return RX_HANDLER_CONSUMED;p = br_port_get_rcu(skb->dev);/*预留地址01-80-C2开头的*/if (unlikely(is_link_local_ether_addr(dest))) {/** See IEEE 802.1D Table 7-10 Reserved addresses** Assignment                Value* Bridge Group Address     01-80-C2-00-00-00* (MAC Control) 802.3      01-80-C2-00-00-01* 端口聚合的目的地址也是特殊地址?* (Link Aggregation) 802.3    01-80-C2-00-00-02* 802.1X PAE address       01-80-C2-00-00-03** 802.1AB LLDP        01-80-C2-00-00-0E** Others reserved for future standardization*//*最后一个字节*/switch (dest[5]) {case 0x00:  /* Bridge Group Address *//* If STP is turned off,then must forward to keep loop detection */if (p->br->stp_enabled == BR_NO_STP)goto forward;break;/*MAC的流量控制pause帧,pouse帧的目的MAC为预留地址* 01-80-C2-00-00-01*/case 0x01:    /* IEEE MAC (Pause) */goto drop;default:/* Allow selective forwarding for most other protocols */if (p->br->group_fwd_mask & (1u << dest[5]))goto forward;}/* Deliver packet to local host only */if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL, br_handle_local_finish)) {return RX_HANDLER_CONSUMED; /* consumed by filter */} else {*pskb = skb;return RX_HANDLER_PASS; /* continue processing */}}forward:switch (p->state) {case BR_STATE_FORWARDING:rhook = rcu_dereference(br_should_route_hook);if (rhook) {if ((*rhook)(skb)) {*pskb = skb;return RX_HANDLER_PASS;}dest = eth_hdr(skb)->h_dest;}/* fall through */case BR_STATE_LEARNING:/*目的地是br的MAC,是发往本机的*/if (ether_addr_equal(p->br->dev->dev_addr, dest))skb->pkt_type = PACKET_HOST;/*转发*/NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,br_handle_frame_finish);break;default:
drop:kfree_skb(skb);}return RX_HANDLER_CONSUMED;
}
  1. 如果skb有shareinfo则clone skb, shareinfo一般用作gso和paged data;
  2. 根据dev查找网桥的port
  3. 如果目的MAC是预留地址 如pause帧则丢弃,pause帧的目的地址是01-80-C2-00-00-01
  4. 如果目的MAC是br的MAC则是发往本地的,置skb的pkt_type为PACKET_HOST;
  5. 转发并调用br_netfilter;

tips: 该接收函数只用于接收mac帧,即数据帧, 不接受stp协议报文,stp协议报文是llc协议,并不是mac协议。

br_handle_frame_finish函数

int br_handle_frame_finish(struct sk_buff *skb)
{const unsigned char *dest = eth_hdr(skb)->h_dest;struct net_bridge_port *p = br_port_get_rcu(skb->dev);struct net_bridge *br;struct net_bridge_fdb_entry *dst;struct net_bridge_mdb_entry *mdst;struct sk_buff *skb2;u16 vid = 0;if (!p || p->state == BR_STATE_DISABLED)goto drop;/*检查报文vid和端口的pvid是否一致*/if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid))goto drop;/* insert into forwarding database after filtering to avoid spoofing */br = p->br;/*根据源mac和vid更新fdb,添加fdb条目*/br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&br_multicast_rcv(br, p, skb))goto drop;if (p->state == BR_STATE_LEARNING)goto drop;BR_INPUT_SKB_CB(skb)->brdev = br->dev;/* The packet skb2 goes to the local host (NULL to skip). */skb2 = NULL;if (br->dev->flags & IFF_PROMISC)skb2 = skb;dst = NULL;if (is_broadcast_ether_addr(dest))skb2 = skb;else if (is_multicast_ether_addr(dest)) {mdst = br_mdb_get(br, skb, vid);if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {if ((mdst && mdst->mglist) ||br_multicast_is_router(br))skb2 = skb;br_multicast_forward(mdst, skb, skb2);skb = NULL;if (!skb2)goto out;} elseskb2 = skb;br->dev->stats.multicast++;/*根据vlan和目的mac查找fdb* is_local表示是否是发往本地的*/} else if ((dst = __br_fdb_get(br, dest, vid)) &&dst->is_local) {skb2 = skb;/* Do not forward the packet since it's local. */skb = NULL;}if (skb) {if (dst) {dst->used = jiffies;br_forward(dst->dst, skb, skb2);} else/*没找到端口,则洪范*/br_flood_forward(br, skb, skb2);}/*发往本地*/if (skb2)return br_pass_frame_up(skb2);out:return 0;
drop:kfree_skb(skb);goto out;
}
  1. 检查报文vid和端口的pvid是否一致,不一致则丢弃报文;
  2. 根据源mac和vid更新/添加fdb条目;
  3. 多播和单播处理,单播查找fdb
  4. 如果是发往本地的调用br_pass_frame_up;转发调用br_forward;
  5. 如果没有找到fdb,则洪范,即转发也发到本机,洪范转发br_flood_forward。

转发函数 ==> 最终调用 br_dev_queue_push_xmit发包。

int br_dev_queue_push_xmit(struct sk_buff *skb)
{/* ip_fragment doesn't copy the MAC header */if (nf_bridge_maybe_copy_header(skb) ||(packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))) {kfree_skb(skb);} else {skb_push(skb, ETH_HLEN);/*删除虚拟的路由,直接调用设备的发包函数*/br_drop_fake_rtable(skb);dev_queue_xmit(skb);}return 0;
}

fdb

数据结构

net_bridge_fdb_entry

struct net_bridge_fdb_entry
{struct hlist_node      hlist;struct net_bridge_port        *dst;       /*网桥端口*/struct rcu_head         rcu;unsigned long           updated; /*上次更新的jiffes*/unsigned long           used;   /*上次使用jiffes*/mac_addr          addr;       /*mac地址用于匹配*/unsigned char          is_local;       /*是否是发往本地的*/unsigned char           is_static;      /*静态,不老化,永久*/__u16                vlan_id;    /*vlan_id用于*/
};

组织方式

内存 slab:br+fdb+cache

组织: 哈希表br->hash; net_bridge的hash成员。哈希桶的槽位256

条目上限:无上限;

学习

透明网桥,接收报文时自动学习.

br_handle_frame_finish中调用br_fdb_update学习fdb条目。

老化

gc_timer:资源回收定时器。

br_dev_setup

->br_stp_timer_init设置,处理函数为br_fdb_cleanup

setup_timer(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);

br_fdb_cleanup函数中:

比较条目的上次的更新时间是否足够久去老化,条目老化的时间,通过hold_time(br)函数获取。

void br_fdb_cleanup(unsigned long _data)
{struct net_bridge *br = (struct net_bridge *)_data;unsigned long delay = hold_time(br);unsigned long next_timer = jiffies + br->ageing_time;int i;spin_lock(&br->hash_lock);for (i = 0; i < BR_HASH_SIZE; i++) {struct net_bridge_fdb_entry *f;struct hlist_node *n;hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) {unsigned long this_timer;if (f->is_static)continue;this_timer = f->updated + delay;if (time_before_eq(this_timer, jiffies))fdb_delete(br, f);else if (time_before(this_timer, next_timer))next_timer = this_timer;}}spin_unlock(&br->hash_lock);mod_timer(&br->gc_timer, round_jiffies_up(next_timer));
}

老化时间:如果拓扑变化了,则为forward_delay:即15s;

否则老化时间:300s; 此时间在br_dev_setup函数中设置。

static inline unsigned long hold_time(const struct net_bridge *br)
{return br->topology_change ? br->forward_delay : br->ageing_time;
}
#define BR_DEFAULT_AGEING_TIME (300*HZ)

多播的datapath

mdb

STP

使用LLC协议而不是MAC的链路协议

BPDU包

LLC协议

netlink接口

linux网桥实现代码走读相关推荐

  1. linux vim ctags,Linux环境上代码阅读与编写的利器-vim+ctags+cscope

    Linux环境下代码阅读与编写的利器----vim+ctags+cscope 所谓工欲善其事,必先利其器. 从事Linux程序开发,特别是Linux驱动程序的开发,不管是通过windows下虚拟一个L ...

  2. linux网桥的简单理解和配置

    linux网桥的简单理解和配置 Linux网桥是linux虚拟网络设备之一.网上很多分析linux网桥的文章,例如代码层面的分析.这里不牵扯复杂的分析和配置,主要是面向虚拟机链接一个用途,小白我的备忘 ...

  3. [转载] Linux网桥浅析

    linux网桥浅析 原文链接:http://hi.baidu.com/_kouu/item/25787d38efec56637c034bd0 什么是桥接? 简单来说,桥接就是把一台机器上的若干个网络接 ...

  4. 【博客455】Linux网桥如何接管attach上来的设备的流量

    Linux网桥如何接管attach上来的设备的流量 先讲讲linux网桥 网桥是在内核中虚拟出来的,可以将主机上真实的物理网卡(如eth0,eth1),或虚拟的网卡(tap0,tap1,vnet0,v ...

  5. Linux网桥实现分析

    1.    前言 本文结合网络上关于Linux网桥的说明. Linux平台的代码阅读记录,整理的一篇总结性文档.由于时间仓促,分析可能存在不足之外,望大家见谅和指正. 对于接触过Linux 网络的童鞋 ...

  6. 在linux上一行代码不用写实现自动采集+hadoop分词

    在linux上一行代码不用写实现自动采集+hadoop分词 将下面的shell脚本保存成到xxx.sh,然后执行即可 cd /opt/hadoop mkdir spider wget -O spide ...

  7. linux 定时器 代码,linux C++ 定时器代码

    linux C++ 定时器代码:#include #include #include using namespace std; /* union sigval { int sival_int; //i ...

  8. 苹果和linux_苹果发布ResearchKit,Linux采用冲突代码,等等

    苹果和linux 在本周的开放源代码新闻摘要中,我们来看看Apple的开放源代码ResearchKit,Linux内核社区采用了冲突代码等等! 2015年3月7日至13日的开源新闻摘要 苹果推出Res ...

  9. linux端口转发_详解Linux网桥功能--概念、工作机制、相关命令及实例说明

    概述 在说Linux网桥之前,先介绍几个概念,有助于对网桥的功能及实现有更深的理解. 一个交换网络的逻辑图: 1. 冲突域 一个冲突域由所有能够看到同一个冲突或者被该冲突涉及到的设备组成.以太网使用C ...

最新文章

  1. win7网络的计算机名称,Win7指定的网络名不再可用快速解决教程
  2. BOM之navigator对象和用户代理检测
  3. 【论文阅读】Deep Adversarial Subspace Clustering
  4. 【转】03.Dicom 学习笔记-DICOM C-Get 消息服务
  5. 互联网晚报 | 2月17日 星期四 | 小鹏汽车回应总裁年薪超4亿;B站将上线开播前人脸认证功能;星巴克再次涨价...
  6. 神奇的python(二)之python打包成应用程序
  7. linux-tomcat-install
  8. C++时间类的运算符重载
  9. ps2019布尔运算快捷键_超实用:换个角度教你快速理解PS CS6布尔运算
  10. oracle 10g安装企业版,企业版Oracle10g的安装-过程
  11. Felix: Flexible Text Editing Through Tagging and Insertion (2020-03)
  12. C#基础List与ArrayList
  13. java 生成pdf 分页_java itext导出PDF 分页 github
  14. 200个句子搞定3500个高考词汇,究竟有多少词?
  15. python file是什么意思_Python一直提示runfile是什么意思?
  16. 【今日分享】官方实例用python调用OpenAI tahcTPG的APIkey生成智能问答
  17. Linux 中的逻辑卷 LVM 管理完整初学者指南
  18. 微信公众号验证Token
  19. 服务器raid卡安装在什么位置,安装raid卡
  20. Python解决抓取内容乱码问题(decode和encode解码)

热门文章

  1. JS结合PHP瀑布流,JavaScript_原生JS实现响应式瀑布流布局,原生JS实现的瀑布流布局,代 - phpStudy...
  2. java 判断是否信用卡_用java实现验证输入信用卡号码的正误
  3. 新东方有计算机课么,计算机课程的翻译
  4. CAD中如何快速等分一条直线或曲线?教程详解
  5. 黑马教程python入门之基础笔记day1/2
  6. Studing Day4 - python基础4
  7. 模电电路笔记———同向比例放大电路的使用
  8. 设置用户ID位是什么意思
  9. TCP网络调试助手上提示错误:“1035 未知错误”的有效解决方法,本人实测确实可行
  10. Bomb(数位DP板题)