struct net_device网络设备结构体详解

本文介绍了Linux内核中网络设备的基本管理方法,包括网络设备的创建、注册、注销等操作,并详细解析了structnet_device结构体及其相关API函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在linux中使用struct net_device结构体来描述每一个网络设备。同时这个用来刻画网络设备的struct net_device结构体包含的字段非常的多,以至于内核的开发者都觉得在现在的linux内核中,这个struct net_device是一个大的错误。
在本篇文章中,只介绍struct net_device中的一些字段,其他的字段在以后使用的时候再说。

#define IFNAMSIZ 32
   struct net_device
   {
       //用于存放网络设备的设备名称;
       char name[IFNAMSIZ];

       //网络设备的别名;
       char *ifalias;

       //网络设备的接口索引值,独一无二的网络设备标识符;
       int ifindex;


        //这个字段用于构建网络设备名的哈希散列表,而struct net中的
      //name_hlist就指向每个哈希散列表的链表头;
      struct hlist_node name_hlist;

        //用于构建网络设备的接口索引值哈希散列表,在struct net中的
      //index_hlist用于指向接口索引值哈希散列表的链表头;
      struct hlist_node index_hlist;

      //用于将每一个网络设备加入到一个网络命名空间中的网络设备双链表中
      struct list_head dev_list;

      //网络设备接口的标识符,其状态类型被定义在<linux/if.h>之中;
      unsigned int flags;

      //网络设备接口的标识符,但对用户空间不可见;
      unsigned short  priv_flags;

      //接口硬件类型,在<if_arp.h>中定义了每一个接口硬件类型;
      unsigned short type;

      //网络设备接口的最大传输单元;
      unsigned mtu;

      //硬件接口头长度;
      unsigned short hard_header_len;

      //网络设备接口的MAC地址;
      unsigned char *dev_addr;

      //网络设备接口的单播模式
      int uc_promisc;

      //网络设备接口的混杂模式;
      unsigned int promiscuity;

      //网络设备接口的全组播模式;
      unsigend int allmulti;

      //secondary unicast mac address
      struct netdev_hw_addr_list uc;

      //list of device hw address;
      struct netdev_hw_addr_list dev_addrs;

      //hw broadcast address;
      unsigned char broadcast[MAX_ADDR_LEN];

      //multicast mac address;
      struct dev_addr_list *mac_list;


        //网络设备接口的数据包接收队列;
      struct netdev_queue rx_queue;

      //网络设备接口的数据包发送队列;
      struct netdev_queue *tx;

      //Number of TX queues allocated at alloc_netdev_mq() time
      unsigned int num_tx_queues;

      //Number of TX queues currently active in device;
      unsigned int real_num_tx_queues;

      //Max frame per queue allowned;
      unsigned long tx_queue_len;

        //网络设备接口的状态;
      unsigned long state;

      //网络设备接口的统计情况;
      struct net_device_state states;

      //用于执行网络设备所在的命名空间;
      struct net *nd_net;     
   }

下面的这幅图用于展示linux内核如何利用struct net和struct net_device来构成一个网络命名空间:
这里写图片描述

1.分配一个网络设备函数,即分配一个struct net_device结构体:alloc_netdev(sizeof_priv, name, setup);

这个alloc_netdev()函数本质上是一个宏定义:

    #define alloc_netdev(sizeof_priv, name, setup) \
 alloc_netdev_mq(sizeof_priv, name, setup, 1)

struct net_device *alloc_netdev_mq(int sizeof_priv,
    const char *name, void(*setup)(struct net_device*),
    unsigned int queue_count)
 EXPORT_SYMBOL(alloc_netdev_mq)  

参数介绍:
- sizeof_priv:为分配给网络设备私有空间的大小;
- name :网络设备的名称;
- setup:对分配的网络设备进行初始化的回调函数;
- queue_count:分配给网络设备的子队列数;

struct net_device *alloc_netdev_mq( int sizeof_priv, const char *name, 
            void (*setup)( struct net_device *), unsigned int queue_count )
{
       struct net_device *dev ;

       struct netdev_queue *tx ;

       int alloc_size ;

       alloc_size  = sizeof(struct net_device);

       dev = kzalloc(alloc_size, GFP_KERNEL);

       tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL);

       dev_addr_init(dev);//对struct net_device中的dev_addrs成员进行初始化;

       dev_unicast_init(dev); //对struct net_device中的uc成员进行初始化; 

       dev_net_set(dev, &init_net);//对网络设备的命名空间进行初始化,默认为init_net;

       dev->_tx = tx ;//设置网络设备的发送队列;
       dev->num_tx_queues = queue_count ;
       dev->real_num_tx_queues = queue_count ;

       netdev_init_queues(dev);//对struct net_device中的rx_queue成员进行初始化;

       setup(dev); //对struct net_device结构体进行初始化;

       strcpy(dev->name, name);//设置网路设备的设备名称;   

}

EXPORT_SYMBOL(alloc_netdev_mq);

2.释放一个网络设备:

void free_netdev(struct net_device *dev);
EXPORT_SYMBOL(free_netdev);

3.注册一个网络设备,只有对一个网络设备进行注册以后,这个网络设备才会在内核中起作用:

 int register_netdev(struct net_device *dev)
EXPORT_SYMBOL(register_netdev);   

返回值:
- 0:表示注册成功;
- a negative errno code :表示注册失败;

int register_netdev(struct net_device *dev)
{
    struct hlist_head *head;
    struct hlist_node *p;
    int ret;
    struct net *net = dev_net(dev); // 获取网络设备所在的命名空间;
    rtnl_lock();//获取rtnl信号量;

    if(!dev_valid_name(dev->name)) //判断网络设备的设备名是否有效;
    {}  

    dev->ifindex = dev_new_index(net); //从网络设备所在的命名空间中找到一个全局唯一的网络
                                       //接口索引值;
    dev->iflink = dev->ifindex ;

    //用于判断网络命名空间中是否有相同名字的网络设备存在;
    head = dev_name_hash(net, dev->name);
    hlist_for_each(p,head)
    {
       struct net_device *d = hlist_entry(p, struct net_device, name_hlist);
       if(!strncmp(d->name, dev->name, 32))
          {
             ret = -EEXIST ;
          }
    }

    set_bit(__LINK_STATE_PRESENT, &dev->state);//设置网络设备的状态;
    list_netdevice(dev); //将网络设备叫入到相应的命名空间之中;
}
EXPORT_SYMBOL(register_netdev);

3.注销一个网络设备结构体:

void unregister_netdev(struct net_device *dev);
EXPORT_SYMBOL(unregister_netdev);

4.设置一个网络设备的MAC地址:

int dev_set_mac_address(struct net_device *dev, 
                    struct sockaddr *sa);
EXPORT_SYMBOL(dev_set_mac_address);

5.设置一个网络设备的最大传输单元:

int dev_set_mtu(struct net_device *dev, int new_mtu);
EXPORT_SYMBOL(dev_set_mtu);

6.改变一个网络设备的flag标识符:

int dev_change_flags(struct net_device *dev, unsigend flags);
EXPORT_SYMBOL(dev_change_flags);

7.获取一个网络设备的flag标识符:

unsigned dev_get_flags(struct net_device *dev);
EXPORT_SYMBOL(dev_get_flags);

8.给网络设备添加一个单播MAC地址,当网络设备在发送单播时使用单播MAC地址

int dev_unicast_add(struct net_device *dev, void *addr);
EXPORT_SYMBOL(dev_unicast_add);

9.删除网络设备中的单播MAC地址:

int dev_unicast_delete(struct net_device *dev, void *addr);
EXPORT_SYMBOL(dev_unicast_delete);

10.给网络设备添加一个设备地址:

int dev_addr_add(struct net_device *dev, unsigned char *addr,
                  unsigned char addr_type)
EXPORT_SYMBOL(dev_addr_add);
addr_type : address type;

11.删除网络设备中的一个设备地址:

int dev_addr_del(struct net_device *dev, unsigend char *addr,
                  unsigned char addr_type);
EXPORT_SYMBOL(dev_addr_del);

12.设置网络设备的接口为混杂模式:

 int dev_set_promiscuity(struct net_device *dev, int inc);
 EXPORT_SYMBOL(dev_set_promiscuity);

当 inc > 0 将网络设备设置为混杂模式;
当 inc = 0 将网络设备设置为正常模式;
当 inc < 0 将去掉网络设备的混杂模式;

13.设置网络设备的接口为allmulticast模式:

 int dev_set_allmulti(struct net_device *dev, int inc);
 EXPORT_SYMBOL(dev_set_allmulti);
 inc 含义同上;

14.在给一个网络设备的设备名称进行赋值时,先要检测这个名字是否有效:

 int dev_valid_name(const char *name);
 EXPORT_SYMBOL(dev_valid_name);

返回:
1 :表示有效;
0 :表示无效;

15.通过设备的MAC地址以及设备类型来获取网络设备的设备结构体:

  struct net_device *dev_getbyhwaddr(struct net *net,
              unsigned short type, char *hwaddr);
 EXPORT_SYMBOL(dev_getbyhwaddr);

net : 网络命名空间;
type: media type of device;
hwaddr:hardware address;
通过memcmp(dev->dev_addr, hwaddr, dev->addr_len)来实现;

16.通过网络设备的接口索引值来获取网络设备结构体:

 struct net_device *dev_get_by_index(struct net *net, int ifindex);
 EXPORT_SYMBOL(dev_get_by_index);

17.通过网络设备的设备名来获取网络设备结构体:

struct net_device *dev_get_by_name(struct net *net, const char *name);
  EXPORT_SYMBOL(dev_get_by_name);

以上所介绍的所有函数位于/net/core/dev.c之中;

本文转载自 http://weiguozhihui.blog.51cto.com/3060615/1584894

struct sk_buff 是 Linux 内核网络子系统中的一个非常重要的数据结构,它代表了内核中网络协议栈中的一个网络数据包。在 Linux 内核中,网络数据包都是封装在 sk_buff 中进行传输和处理的,因此可以说 sk_buff 是 Linux 网络子系统中最核心的数据结构之一。 下面是 struct sk_buff 结构体的详细说明: ```c struct sk_buff { struct sk_buff *next; /* 下一个 sk_buff */ struct sk_buff *prev; /* 上一个 sk_buff */ ktime_t tstamp; /* 时间戳 */ struct sock *sk; /* socket */ struct net_device *dev; /* 网络设备 */ unsigned long _skb_dst; /* 目标地址 */ unsigned long _skb_src; /* 源地址 */ struct skb_shared_info *shinfo; /* 共享数据 */ atomic_t users; /* 引用计数 */ unsigned int len, data_len; /* 总长度和数据长度 */ __u16 protocol; /* 协议类型 */ __u16 vlan_proto; /* VLAN 协议 */ __u16 vlan_tci; /* VLAN 标记 */ union { __be16 ip4_frag_id; /* IPv4 报文分片标识 */ __u8 hdr_len; /* 首部长度 */ __u16 mac_len; /* MAC 头长度 */ }; __u16 queue_mapping; /* 网络队列映射 */ __u16 tc_index; /* 网络流量控制 */ __u16 pkt_type; /* 数据包类型 */ __u32 priority; /* 优先级 */ __u32 skb_mstamp; /* 时间戳 */ u32 secmark; /* 安全标记 */ unsigned int mark; /* skb 标记 */ unsigned int nf_trace; /* 网络跟踪 */ __u32 hash; /* 哈希值 */ __u16 nfctinfo; /* nf_conntrack 信息 */ __u8 queue_bypass; /* 是否绕队列 */ __u8 protocol_was_802_3; /* 协议是否是 802.3 */ __u8 encapsulation; /* 封装类型 */ __u8 transport_header_was; /* 传输层首部是否有效 */ union { __wsum csum; /* 校验和 */ struct { __u16 csum_start; /* 校验和起始位置 */ __u16 csum_offset; /* 校验和偏移量 */ }; }; union { void *dst; /* 目标地址 */ struct { __be32 saddr; /* 源 IP 地址 */ __be32 daddr; /* 目标 IP 地址 */ } ip4; struct { const void *hdr; /* MAC 头指针 */ const void *payload; /* 数据负载指针 */ } mac; struct { unsigned char *tail; /* 尾部指针 */ unsigned char *end; /* 结束指针 */ }; }; }; ``` 下面是各个字段的详细说明: - next 和 prev 字段:这两个字段分别指向下一个和上一个 sk_buff,用于将 sk_buff 组织成链表。这样可以方便地进行遍历和管理多个 sk_buff。 - tstamp 字段:这个字段表示 sk_buff 的时间戳,记录了 sk_buff 的创建时间。 - sk 字段:这个字段指向一个 socket,表示这个 sk_buff 相关联的 socket。 - dev 字段:这个字段指向一个网络设备,表示这个 sk_buff 是从哪个网络设备接收到的,或者将要发送到哪个网络设备。 - \_skb_dst 和 \_skb_src 字段:这两个字段是目标地址和源地址的指针,分别指向目标地址和源地址的内存空间。 - shinfo 字段:这个字段指向一个 skb_shared_info 结构体,用于共享数据。 - users 字段:这个字段是一个引用计数器,用于记录当前有多少个指针指向这个 sk_buff。 - len 和 data_len 字段:这两个字段分别表示 sk_buff 的总长度和数据长度。 - protocol 字段:这个字段表示 sk_buff 中数据的协议类型,例如 ETH_P_IP 表示 IPv4 协议,ETH_P_ARP 表示 ARP 协议等。 - vlan_proto 和 vlan_tci 字段:这两个字段用于处理 VLAN 标记。 - ip4_frag_id 字段:这个字段用于处理 IPv4 报文分片标识。 - queue_mapping 字段:这个字段表示网络队列映射。 - tc_index 字段:这个字段表示网络流量控制。 - pkt_type 字段:这个字段表示数据包的类型,例如数据包是从网络设备接收而来的、或者是要发送到网络设备的等。 - priority 字段:这个字段表示 sk_buff 的优先级。 - skb_mstamp 字段:这个字段表示 sk_buff 的时间戳,记录了 sk_buff 的最后修改时间。 - secmark 字段:这个字段用于安全标记。 - mark 字段:这个字段用于 skb 标记。 - nf_trace 字段:这个字段用于网络跟踪。 - hash 字段:这个字段用于哈希值。 - nfctinfo 字段:这个字段用于 nf_conntrack 信息。 - queue_bypass 字段:这个字段表示是否绕过队列。 - protocol_was_802_3 字段:这个字段表示协议是否是 802.3。 - encapsulation 字段:这个字段表示封装类型。 - transport_header_was 字段:这个字段表示传输层首部是否有效。 - csum 字段:这个字段用于校验和。 - csum_start 和 csum_offset 字段:这两个字段分别表示校验和的起始位置和偏移量。 - dst 字段:这个字段指向目标地址。 - ip4.saddr 和 ip4.daddr 字段:这两个字段分别表示 IPv4 报文的源 IP 地址和目标 IP 地址。 - mac.hdr 和 mac.payload 字段:这两个字段分别指向 MAC 头和数据负载的指针。 - tail 和 end 字段:这两个字段分别指向 sk_buff 数据的尾部和结束位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值