惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Latest news
Latest news
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
博客园 - 司徒正美
B
Blog RSS Feed
AWS News Blog
AWS News Blog
MyScale Blog
MyScale Blog
Scott Helme
Scott Helme
Cisco Talos Blog
Cisco Talos Blog
Last Week in AI
Last Week in AI
NISL@THU
NISL@THU
博客园 - Franky
P
Proofpoint News Feed
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
S
Schneier on Security
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
G
GRAHAM CLULEY
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
WordPress大学
WordPress大学
The Hacker News
The Hacker News
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Microsoft Azure Blog
Microsoft Azure Blog
T
The Exploit Database - CXSecurity.com
Engineering at Meta
Engineering at Meta
罗磊的独立博客
T
The Blog of Author Tim Ferriss
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
SecWiki News
SecWiki News
云风的 BLOG
云风的 BLOG
美团技术团队
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Security Latest
Security Latest
C
Cyber Attacks, Cyber Crime and Cyber Security
B
Blog
S
Security Affairs

博客园 - Edison.Feng

Objective-读Xml [转]iPhone Mapkit 之在地图加入坐标点 使用MKAnnotation和MKAnnotationView [转]Android:使用URL和URLConnection(多线程下载) 【转】httpModules 与 httpHandlers 【转】挣脱浏览器的束缚(2) - 别让脚本引入坏了事 [转]asp.net中利用ashx实现图片防盗链 求任何一个正数的组合,组合的规则是这个数等于1或2的整数幂之和,请列出组合的情况。 C#利用反射获取对象属性的修改情况 C#利用反射获取对象属性值 Clone:Xml序列化反序列克隆对象 [转]Linux slab 分配器剖析 [转]Linux对I/O端口资源的管理 [转]linux内核分析-初始化分析 start_kernel paging_init [转]Linux TCP/IP 协议栈的关键数据结构Socket Buffer(sk_buff ) 母牛生小牛(递归) MS SQL:Funcation拆分行 我使用WebService:Function.asmx的惊叹!!!
[转]Linux系统内核接收以太帧的处理程序
Edison.Feng · 2009-04-02 · via 博客园 - Edison.Feng

   我最近在研究TCP/IP协议,在网络上找到很多关于Linux系统内核接收以太帧的处理程序的资料,但是其排版布局已经颜色都是为经处理的,让人阅读起来,非常的痛苦,现在我将我重新排版并标识了代码颜色文件共享,希望给相关的朋友能带了方便。

Linux系统内核接收以太帧的处理程序

1. 前言

以太头中除了6字节目的MAC地址、6字节源MAC地址外,还有两字节的以太帧类型 值,如IPv40x0800ARP0x0806等,网卡驱动收到以太帧后通过接口函数netif_receive_skb()(netif_rx实 际最后也是调用netif_receive_skb)交到上层,而这个接口函数就完成对以太帧类型的区分,交到不同的协议处理程序。如果想自己编写某一以 太类型帧的处理程序,需要自己添加相应的代码。以下为Linux内核2.6代码。

2. 数据结构

每种协议都要定义一个packet_type结构,引导进入相关的协议数据处理函数,所有节点组成一个链表(HASH链表)

/* include/linux/netdevice.h */

struct packet_type

{

     __be16 type; /* This is really htons(ether_type). */

     struct net_device *dev; /* NULL is wildcarded here */

     int (*func) (      struct sk_buff *,

                       struct net_device *,

                       struct packet_type *,

                       struct net_device *    );

     void *af_packet_priv;

     struct list_head list;

};

参数说明:

type:以太帧类型,16位。

dev:所附着的网卡设备,如果为NULL则匹配全部网卡。

func:协议入口接收处理函数。

af_packet_priv:协议私有数据。

list:链表扣。

一般各协议的packet_type结构都是静态存在,初始化时只提供typefunc两个参数就可以了,每个协议在初始化时都要将此结构加入到系统类型链表中。

3. 处理函数

3.1 添加节点

/* net/core/dev.c */

/**

* dev_add_pack - add packet handler

* @pt: packet type declaration

*

* Add a protocol handler to the networking stack. The passed &packet_type

* is linked into kernel lists and may not be freed until it has been

* removed from the kernel lists.

*

* This call does not sleep therefore it can not

* guarantee all CPU's that are in middle of receiving packets

* will see the new packet type (until the next received packet).

*/

void dev_add_pack(struct packet_type *pt)

{

     int hash;

     spin_lock_bh(&ptype_lock);

     // 如果类型是全部以太类型,则节点链接到ptype_all

     if (pt->type == htons(ETH_P_ALL))

     {

         netdev_nit++;

         list_add_rcu(&pt->list, &ptype_all);

     }

     else

     {

         // 根据协议类型取个HASH,共个HASH链表

         hash = ntohs(pt->type) & 15;

         // 将节点链接到HASH链表中,list_add_rcu是加了smp_wmb()保护的list_add链表操作

         list_add_rcu(&pt->list, &ptype_base[hash]);

    

     spin_unlock_bh(&ptype_lock);

}

3.2 删除节点

/**

* __dev_remove_pack - remove packet handler

* @pt: packet type declaration

*

* Remove a protocol handler that was previously added to the kernel

* protocol handlers by dev_add_pack(). The passed &packet_type is removed

* from the kernel lists and can be freed or reused once this function

* returns.

*

* The packet type might still be in use by receivers

* and must not be freed until after all the CPU's have gone

* through a quiescent state.

*/

void __dev_remove_pack(struct packet_type *pt)

{

     struct list_head *head;

     struct packet_type *pt1;

     spin_lock_bh(&ptype_lock);

     // 根据协议类型找是在ptype_all表还是某一HASH链表中

     if (pt->type == htons(ETH_P_ALL))

     {

         netdev_nit--;

         head = &ptype_all;

     }

     else

         head = &ptype_base[ntohs(pt->type) & 15];

     // 直接用地址比对进行查找,而不是类型,因为同一个类型也可能有多个节点

     list_for_each_entry(pt1, head, list)

     {

         if (pt == pt1)

         {

              list_del_rcu(&pt->list);

              goto out;

         }

     }

         printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);

     out:

         spin_unlock_bh(&ptype_lock);

}

/**

* dev_remove_pack - remove packet handler

* @pt: packet type declaration

*

* Remove a protocol handler that was previously added to the kernel

* protocol handlers by dev_add_pack(). The passed &packet_type is removed

* from the kernel lists and can be freed or reused once this function

* returns.

*

* This call sleeps to guarantee that no CPU is looking at the packet

* type after return.

*/

// 只是__dev_remove_pack()的包裹函数

void dev_remove_pack(struct packet_type *pt)

{

     __dev_remove_pack(pt);

     synchronize_net();

}

4. 实例

4.1 IP

/* net/ipv4/af_inet.c */

static struct packet_type ip_packet_type =

{

     .type = __constant_htons(ETH_P_IP),

     .func = ip_rcv, // IP接收数据的入口点

};

static int __init inet_init(void)

{

     ......

     dev_add_pack(&ip_packet_type);

     ......

}

由于IP协议部分不能作为内核模块,所以是没有卸载函数的,没必要调用dev_remove_pack()函数。

4.2 8021q vlan

/* net/8021q/vlan.c */

static struct packet_type vlan_packet_type =

{

     .type = __constant_htons(ETH_P_8021Q),

     .func = vlan_skb_recv, /* VLAN receive method */

};

static int __init vlan_proto_init(void)

{

     ......

     dev_add_pack(&vlan_packet_type);

     ......

}

static void __exit vlan_cleanup_module(void)

{

     ......

     dev_remove_pack(&vlan_packet_type);

     ......

}

由于VLAN可为模块方式存在,所以在模块清除函数中要调用dev_remove_pack()

5. 网络接收

网卡驱动收到数据包构造出skb后,通过接口函数netif_receive_skb()传递到上层进行协议处理分配。

/* net/core/dev.c */

int netif_receive_skb(struct sk_buff *skb)

{

     ......

     // 先查处理所有以太类型的链表各节点

     list_for_each_entry_rcu(ptype, &ptype_all, list)

     {

         if (!ptype->dev || ptype->dev == skb->dev)

         {

              if (pt_prev)

              ret = deliver_skb(skb, pt_prev, orig_dev);

              pt_prev = ptype;

         }

     }

     ......

}

// 再查指定协议的HASH链表

list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list)

{

     if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev))

     {

         if (pt_prev)

              ret = deliver_skb(skb, pt_prev, orig_dev);

         pt_prev = ptype;

     }

     ......

}

// 该函数就是调用个协议的接收函数处理该skb包,进入第三层网络层处理

static __inline__ int deliver_skb(   struct sk_buff *skb,

                                          struct packet_type *pt_prev,

                                          struct net_device *orig_dev)

{

     atomic_inc(&skb->users);

     return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);

}

6. 结论

通过链表挂接方式,Linux内核可以很容易的添加各种协议的接收处理函数。

数据流程:

网卡驱动--->netif_rx()--->netif_receive_skb()->deliver_skb()->packet_type.func