Linux内核(一):网桥转发与Netfilter初始化

本文介绍了Linux内核中网桥的工作原理,包括RCU同步机制和RTNL互斥锁,以及模块初始化过程。网桥通过学习源MAC与端口关系建立转发表,并详细阐述了网桥的初始化过程。此外,还探讨了Netfilter的初始化,其钩子函数以优先级为序组成二维链表,涉及不同协议。最后,概述了二层网桥转发流程。
摘要由CSDN通过智能技术生成

概念

  • RCU机制:Linux的同步机制,读-拷贝修改。原理是,RCU保护的共享数据结构,读者不需要锁,写者要写的时候先拷贝一份副本,修改副本,等到没有读者读原数据之后,将指向原数据的指针改为指向副本。允许多个读者同时访问被保护数据,是否允许多个写者并行访问取决于写者间的同步机制。
  • RTNL互斥锁:内核的接口很多都显式的要求在rtnl lock的保护下,它是一个mutex锁,用来保护netdevice等相关的数据,在内核、用户配置间的一致性。

PS:内核代码版本为4.18.0

模块初始化

网桥

网桥的作用:
1、起初,当网桥没有记录mac和端口对应关系时,会将报文在其所有端口广播发出,类似HUB。
2、每转发一个报文,都学习源MAC与端口的对应关系,建立地址-端口的对照表(CAM表)。
3、每转发一个报文,都根据目的MAC地址,从CAM表中查找对应发送数据的端口。

(net/bridge/br_private.h文件定义了桥相关的结构体)
struct net_device: 网络设备结构
struct sk_buff: 应用层的数据包结构
struct net_bridge: 桥的结构体
struct net_bridge_port: 桥的端口成员结构体
struct net_bridge_fdb_entry: 桥的CAM转发表项结构体,记录端口和mac地址信息
struct net_bridge_mdb_htable: 组播组转发表结构体
struct net_bridge_mdb_entry: 组播组转发表项结构体
struct net_port_vlans: 端口vlan结构体
struct mac_addr: MAC地址结构体

网桥初始化

//net/bridge/br.c
static int __init br_init(void)
{
   
    err = stp_proto_register(&br_stp_proto);   //生成树协议
    err = br_fdb_init();   //kmem_cache_create创建struct net_bridge_fdb_entry高速缓存,用来记录网桥端口中mac地址信息
    err = register_pernet_subsys(&br_net_ops);   //在一个网络命名空间里注册子系统
    err = br_nf_core_init();   //路由相关,初始化struct dst_ops fake_dst_ops的percpu_counter计数器,用于统计与加锁(net/bridge/br_nf_core.c/include/net/dst_ops.h)
    err = register_netdevice_notifier(&br_device_notifier);   //struct notifier_block br_device_notifier定义通知调用函数br_device_event,处理网桥相关的事件
    err = register_switchdev_notifier(&br_switchdev_notifier);   //struct notifier_block br_switchdev_notifier定义通知调用函数br_switchdev_event,学习MAC地址
    err = br_netlink_init();   //初始化netlink,用来与用户程序通信,用到rtnl(net/bridge/br_netlink.c)
    brioctl_set(br_ioctl_deviceless_stub);   //注册一个函数,在添加新的br时使用
}

//net/bridge/br_netfilter_hooks.c
//注册bridge的hook函数
static int __init br_netfilter_init(void)
{
   
    ret = register_pernet_subsys(&brnf_net_ops);   //注册子系统
    ret = register_netdevice_notifier(&brnf_notifier);   //struct brnf_device_event定义了通知调用函数brnf_device_event,调用nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops))注册hook函数
#ifdef CONFIG_SYSCTL
    brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table);   //使应用程序空间用户能够方便修改内核参数
#endif
    RCU_INIT_POINTER(nf_br_ops, &br_ops);   //创建nf_br_ops的RCU指针
    printk(KERN_NOTICE "Bridge firewalling registered\n");
    return 0;
}
  • br_add_bridge(net/bridge/br_if.c)添加网桥时,调用br_dev_setup(net/bridge/br_device.c)初始化一些网桥的信息,eth_hw_addr_random硬件MAC地址,ether_setup以太网信息,SET_NETDEV_DEVTYPE,br_netfilter_rtable_init(net/bridge/br_nf_core.c)路由表等。
  • br_add_if(net/bridge/br_if.c)添加网桥端口,调用netdev_rx_handler_register(dev, br_handle_frame, p)注册handler,dev为设备,br_handle_frame为接收处理函数,p为端口数据(net/core/dev.c)。
//net/core/dev.c
int netdev_rx_handler_register(struct net_device *dev,
                   rx_handler_func_t *rx_handler,
                   void *rx_handler_data)
{
   
    /* Note: rx_handler_data must be set before rx_handler */
    rcu_assign_pointer(dev->rx_handler_data, rx_handler_data);   //struct net_bridge_port *p
    rcu_assign_pointer(dev->rx_handler, rx_handler);   //br_handle_frame
    return 0;
}

Netfilter初始化

从初始化可以看出,netfilter的钩子是一个二维链表,不同协议下分别是各hook点的链表,以优先级为顺序。

static int __init sock_init(void)
{
   
    int err;
    /*
     *      Initialize the network sysctl infrastructure.
     */
    err = net_sysctl_init();
    if (err)
        goto out;
    /*
     *      Initialize skbuff SLAB cache
     */
    skb_init();
    /*
     *      Initialize the protocols module.
     */
    init_inodecache();
    err = register_filesystem(&sock_fs_type);
    if (err)
        goto out_fs;
    sock_mnt = kern_mount(&sock_fs_type);
    if (IS_ERR(sock_mnt)) {
   
        err = PTR_ERR(sock_mnt);
        goto out_mount;
    }
    /* The real protocol initialization is performed in later initcalls.
     */
#ifdef CONFIG_NETFILTER
    err = netfilter_init();   //初始化netfilter(net/netfilter/core.c),netfilter_net_ops定义初始化函数netfilter_net_init
    if (err)
        goto out;
#endif
    ptp_classifier_init();
out:
    return err;
out_mount:
    unregister_filesystem(&sock_fs_type);
out_fs:
    goto out;
}
core_initcall(sock_init);   /* early initcall */

//net/netfilter/core.c
static int __net_init netfilter_net_init(struct net *net)
{
   
    //区分协议
    __netfilter_net_init(net->nf.hooks_ipv4, ARRAY_SIZE(net->nf.hooks_ipv4));
    __netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6));
#ifdef CONFIG_NETFILTER_FAMILY_ARP
    __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp));
#endif
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
    __netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge));
#endif
#if IS_ENABLED(CONFIG_DECNET)
    __netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet));
#endif

#ifdef CONFIG_PROC_FS
    net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
                        net->proc_net);
    if (!net->nf.proc_netfilter) {
   
        if (!net_eq(net, &init_net))
            pr_err("cannot create netfilter proc entry");
        return -ENOMEM;
    }
#endif
    return 0;
}

static void __net_init
__netfilter_net_init(struct nf_hook_entries __rcu **e, int max)
{
   
    int h;
    for (h = 0; h < max; h++)
        RCU_INIT_POINTER(e[h], NULL);
}

HOOK

钩子函数声明

//include/linux/netfilter.h
static inline int
NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,
    struct net_device *in, struct net_device *out,
    int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
   
    int ret = nf_hook(pf, hook,
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值