网络设备的两个结构体net_device和in_device

网络设备初始化

函数alloc_netdev_mqs分配一个网络设备结构体net_device,in_device结构体由inetdev_init分配和初始化。

struct net_device *alloc_netdev_mqs(...)
{
	struct net_device *p;
    p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
}
static struct in_device *inetdev_init(struct net_device *dev)
{
    struct in_device *in_dev;
    in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
}

两者的关联

net_device结构主要用于内核自身(设备驱动、上层协议等)对网络设备的操作;in_device主要是保存用户态对此设备的配置信息,比如IP地址的配置,其保存在in_device的成员ifa_list中。两个结构体通过指针互指联系在一起。


新创建的网络设备net_device在注册的时候,会调用netdevice通知链发送NETDEV_REGISTER事件。在此链上,有devinet_init注册的处理函数inetdev_event。其判断net_device是否已有相应的in_device结构,没有的话调用inetdev_init分配。inetdev_init函数最后,将初始化后的in_device结构赋值给net_device的ip_ptr指针。

int register_netdevice(struct net_device *dev)
{
    ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
}

static struct in_device *inetdev_init(struct net_device *dev)
{
	in_dev->dev = dev;             //net_device赋值到in_device结构体的成员dev
    rcu_assign_pointer(dev->ip_ptr, in_dev);           //in_device赋值到net_device的成员ip_ptr。
}

static int inetdev_event(struct notifier_block *this, unsigned long event,
             void *ptr)
{
    struct net_device *dev = netdev_notifier_info_to_dev(ptr);
    struct in_device *in_dev = __in_dev_get_rtnl(dev);

    if (!in_dev) {
        if (event == NETDEV_REGISTER) {
            in_dev = inetdev_init(dev);       //net_device注册事件,分配in_device。
        }
    }
}

static struct notifier_block ip_netdev_notifier = {
    .notifier_call = inetdev_event,
};

void __init devinet_init(void)
{
    register_netdevice_notifier(&ip_netdev_notifier);  //注册netdevice的通知链
}

net_device结构体

net_device结构体,定义的相关成员包括:网卡硬件类信息、统计类信息、上层协议处理接口、流控接口等。
struct net_device {
    int         irq;
    struct list_head    ptype_all;   //监听此设备上所有报文的处理函数链表

    int         ifindex;             //接口索引值

    struct net_device_stats stats;   //收发报文的统计信息

    const struct net_device_ops *netdev_ops;
    const struct ethtool_ops *ethtool_ops;

    struct in_device __rcu  *ip_ptr;
    struct inet6_dev __rcu  *ip6_ptr;

    rx_handler_func_t __rcu *rx_handler;
    void __rcu      *rx_handler_data;

    struct netdev_queue __rcu *ingress_queue;
	
	struct Qdisc        *qdisc;  //流控
};

in_device结构体

in_device结构体,其中两个重要成员:保存邻居表配置的arp_parms和保存IPv4配置的cnf,二者保存的参数都可通过相应的proc文件修改。
struct neigh_parms {
	void    *sysctl_table;           //neigh_sysctl_table实现proc文件操作data中配置
    int data[NEIGH_VAR_DATA_MAX];
    DECLARE_BITMAP(data_state, NEIGH_VAR_DATA_MAX);
};

struct ipv4_devconf {
    void    *sysctl;                 //devinet_sysctl_table实现proc文件操作data中配置
    int data[IPV4_DEVCONF_MAX];
    DECLARE_BITMAP(state, IPV4_DEVCONF_MAX);
};

struct in_device {
    struct net_device   *dev;
    struct in_ifaddr    *ifa_list;    //IP地址链
    ...   //多播相关结构
    struct neigh_parms  *arp_parms;   //邻居表(arp)参数配置
    struct ipv4_devconf cnf;          //IPv4相关device配置
};
arp_parms和cnf的初始化
static struct in_device *inetdev_init(struct net_device *dev)
{
    struct in_device *in_dev;

    memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,  //cnf取默认值dev_net(dev)->ipv4.devconf_dflt
            sizeof(in_dev->cnf));
    in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);  //arp_parms取默认值arp_tbl->parms

	err = devinet_sysctl_register(in_dev);          //注册sysctl处理函数
}

arp_parms结构体
初始化时默认值取自于arp_tbl的parms定义,位于文件net/ipv4/arp.c。
struct neigh_table arp_tbl = {
    .parms      = {
        .tbl            = &arp_tbl,
        .reachable_time     = 30 * HZ,
        .data   = {
            [NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
            [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
		    ...
		}
	}
}
可通过proc文件修改in_device设备(eth0)的arp参数:

echo 200 > /proc/sys/net/ipv4/neigh/eth0/retrans_time
echo 3010 > /proc/sys/net/ipv4/neigh/eth0/base_reachable_time

邻居表可配置值的定义在文件include/net/neighbour.h中。

enum {                 
    NEIGH_VAR_RETRANS_TIME,
    NEIGH_VAR_BASE_REACHABLE_TIME,
    ...
}
内核定了了两个配置项操作函数,NEIGH_VAR与NEIGH_VAR_SET:

static inline void neigh_var_set(struct neigh_parms *p, int index, int val)
{
    set_bit(index, p->data_state);
    p->data[index] = val;
}
#define NEIGH_VAR(p, attr) ((p)->data[NEIGH_VAR_ ## attr])           //获取attr配置的值,如NEIGH_VAR(p, BASE_REACHABLE_TIME)
#define NEIGH_VAR_SET(p, attr, val) neigh_var_set(p, NEIGH_VAR_ ## attr, val)  //设置attr的值
cnf结构体
in_dev的cnf默认取值于全局变量ipv4_devconf_dflt:

static struct ipv4_devconf ipv4_devconf_dflt = {
    .data = {
        [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
        [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
		...
	},
};
可配置值定义在include/uapi/linux/ip.h文件:

enum
{                
    IPV4_DEVCONF_FORWARDING=1,
    IPV4_DEVCONF_MC_FORWARDING,
	...
}
可通过proc文件修改设备(eth0)的配置参数:

echo 1 > /proc/sys/net/ipv4/conf/eth0/forwarding
echo 1 > /proc/sys/net/ipv4/conf/eth0/mc_forwarding

内核定义的配置项操作函数,IN_DEV_CONF_GET与IN_DEV_CONF_SET,位于文件include/linux/inetdevice.h。

static inline int ipv4_devconf_get(struct in_device *in_dev, int index)
{
    index--;
    return in_dev->cnf.data[index];
}
static inline void ipv4_devconf_set(struct in_device *in_dev, int index, int val)
{
    index--;
    set_bit(index, in_dev->cnf.state);
    in_dev->cnf.data[index] = val;
}

#define IN_DEV_CONF_GET(in_dev, attr) \
    ipv4_devconf_get((in_dev), IPV4_DEVCONF_ ## attr)
#define IN_DEV_CONF_SET(in_dev, attr, val) \
    ipv4_devconf_set((in_dev), IPV4_DEVCONF_ ## attr, (val))
网络命名空间ipv4_devconf

另外要注意,系统还有一组基于net namespace的ipv4_devconf配置参数,其初值定义如下:

static struct ipv4_devconf ipv4_devconf = {
    .data = {
        [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
        [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
		...
    },
};

也可通过proc文件修改其配置:

echo 1 > /proc/sys/net/ipv4/forwarding

echo 1 > /proc/sys/net/ipv4/mc_forwarding

在内核代码中,可通过宏定义IPV4_DEVCONF_ALL获取其中attr的值:

#define IPV4_DEVCONF(cnf, attr) ((cnf).data[IPV4_DEVCONF_ ## attr - 1])
#define IPV4_DEVCONF_ALL(net, attr) \
    IPV4_DEVCONF((*(net)->ipv4.devconf_all), attr)

由于存在两套ipv4_devconf参数值(基于in_device的值和基于net namespace的值),内核定义了如下的判断宏,两者and/or/max:

#define IN_DEV_ANDCONF(in_dev, attr) \
    (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr) && \
     IN_DEV_CONF_GET((in_dev), attr))

#define IN_DEV_NET_ORCONF(in_dev, net, attr) \
    (IPV4_DEVCONF_ALL(net, attr) || \
     IN_DEV_CONF_GET((in_dev), attr))

#define IN_DEV_ORCONF(in_dev, attr) \
    IN_DEV_NET_ORCONF(in_dev, dev_net(in_dev->dev), attr)

#define IN_DEV_MAXCONF(in_dev, attr) \
    (max(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr), \
         IN_DEV_CONF_GET((in_dev), attr)))

内核版本

linux-4.14.4



`struct sk_buff` 是 Linux 内核中用来表示网络数据包的数据结构。它是一个很重要的数据结构,因为 Linux 内核中网络协议栈的所有数据包处理都是基于 sk_buff 进行的。 下面是 `struct sk_buff` 结构体的定义: ```c struct sk_buff { struct sk_buff *next; struct sk_buff *prev; ktime_t tstamp; struct sock *sk; struct net_device *dev; char cb[48]; unsigned int len; unsigned int data_len; __u16 mac_len; __u16 hdr_len; union { __u16 all; struct { __u16 nfmark : 16; } nfctmark; struct { __u16 pkt_type : 3; __u16 ignore_df : 1; __u16 nf_trace : 1; __u16 ip_summed : 2; __u16 ooo_okay : 1; __u16 l4_rxhash : 1; __u16 sw_hash : 1; __u16 sw_hash_valid : 1; __u16 l5_hash_valid : 1; __u16 l4_hash_valid : 1; __u16 fclone : 2; __u16 frag_list : 2; __u16 rxhash : 1; __u16 loopback : 1; __u16 vlan_present : 1; __u16 vlan_tci : 16; __u16 inner_protocol : 16; __u16 inner_transport_header : 16; } parsed; } encapsulation; unsigned char protocol; unsigned char pkt_type: 3; unsigned char fclone: 2; unsigned char ip_summed: 2; unsigned char ooo_okay: 1; __u16 vlan_proto; __u16 vlan_tci; union { struct { __be16 h_vlan_TCI; __be16 h_vlan_encapsulated_proto; }; __be32 ipv4; struct ipv6hdr *ipv6h; struct arphdr *arph; struct tcphdr *hth; struct udphdr *huh; struct icmphdr *icmph; } protocol_headers; union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; struct pppox_hdr *pppoe; struct snap_header *llc; struct cfm_pdu_header *cfm_pdu; struct batadv_unicast_packet *batman_adv; struct batadv_bcast_packet *batman_adv_bcast; struct batadv_icmp_packet *batman_adv_icmp; struct batadv_tvlv_packet *batman_adv_tvlv; struct batadv_frag_packet *batman_adv_frag; struct batadv_purge_packet *batman_adv_purge; struct batadv_gw_packet *batman_adv_gw; struct batadv_tt_change_packet *batman_adv_tt_change; struct batadv_mcast_packet *batman_adv_mcast; struct batadv_frag_list *batman_adv_frag_list; struct batadv_unicast_4addr_packet *batman_adv_unicast_4addr; struct batadv_bla_claim *batman_adv_bla_claim; struct batadv_bla_backbone_gw *batman_adv_bla_backbone_gw; struct batadv_bla_claim_reply *batman_adv_bla_claim_reply; struct batadv_bla_claim_confirm *batman_adv_bla_claim_confirm; struct batadv_bla_claim_ack *batman_adv_bla_claim_ack; struct batadv_bla_update *batman_adv_bla_update; struct batadv_bla_claim_broadcast *batman_adv_bla_claim_broadcast; struct batadv_bla_claim_broadcast_reply *batman_adv_bla_claim_broadcast_reply; } encapsulated; char *head; char *data; char *tail; char *end; unsigned int truesize; atomic_t users; }; ``` `struct sk_buff` 结构体中的字段含义如下: - `next` 和 `prev`:`struct sk_buff` 是一个双向链表,这两个字段用于链表操作; - `tstamp`:时间戳,用于记录数据包的接收或发送时间; - `sk`:指向网络套接字的指针; - `dev`:指向网络设备的指针; - `cb`:可选的控制块,用于保存一些协议栈内部使用的数据; - `len`:数据包的总长度; - `data_len`:数据包实际负载的长度; - `mac_len`:物理层帧头的长度; - `hdr_len`:网络层协议头的长度; - `encapsulation`:封装信息,用于保存协议头的解析结果; - `protocol`:网络协议号; - `pkt_type`:数据包类型; - `fclone`:用于指示是否进行数据包复制; - `ip_summed`:用于指示是否需要计算 IP 校验和; - `ooo_okay`:用于指示是否允许乱序到达的数据包; - `vlan_proto` 和 `vlan_tci`:VLAN 标签的协议号和标识符; - `protocol_headers`:协议头指针的联合体; - `encapsulated`:封装协议的联合体; - `head`:指向数据包缓冲区首地址的指针; - `data`:指向数据包负载首地址的指针; - `tail`:指向数据包负载末地址的指针; - `end`:指向数据包缓冲区末地址的指针; - `truesize`:数据包缓冲区的实际大小; - `users`:用于记录当前正在使用该数据包的线程数的原子计数器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值