CSDN博客

img collide

网络包的接收过程

发表于2004/9/26 19:23:00  3644人阅读

分类: Linux 内核研究 dsr阅读源码相关

1) 网卡设备驱动程序将硬件中断中接收到数据帧存入sk_buff结构, 然后检查硬件帧头,
识别帧类型,
放入接收队列, 激活接收软中断作进一步处理. 接收软中断(net_rx_action)提取接收包,
根据它所
在的设备和协议类型传递给各自的包处理器. 包处理器用dev_add_pack()注册,
如果注册的设备号是
零则表明它接收所有设备的包, 如果注册的包类型是(ETH_P_ALL), 则表示它接收所有类型的包.
如果
系统注册有(ETH_P_ALL)类型的包处理器,
系统会通过dev_queue_xmit_nit()将每一发送包复制一份
副本传递给它们.

struct packet_type
{
unsigned short 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 *);
void *data; /* Private to the packet type */
struct packet_type *next;
};

; net/core/dev.c:

static struct packet_type *ptype_base[16];
16路散列索引链表,用于普通注册类型的包处理器
static struct packet_type *ptype_all =
NULL; 包窃听器链表,用于注册类型为ETH_P_ALL的包处理器

static void net_rx_action(struct softirq_action *h)
{
int this_cpu = smp_processor_id();
struct softnet_data *queue = &softnet_data[this_cpu];
unsigned long start_time = jiffies;
int bugdet = netdev_max_backlog; 最大接收队列长度

br_read_lock(BR_NETPROTO_LOCK);

for (;;) {
struct sk_buff *skb;
struct net_device *rx_dev;

local_irq_disable();
skb = __skb_dequeue(&queue->input_pkt_queue);
local_irq_enable();

if (skb == NULL)
break;

skb_bond(skb); 用(skb->master)重新绑定包所在设备

rx_dev = skb->dev;

#ifdef CONFIG_NET_FASTROUTE
if (skb->pkt_type == PACKET_FASTROUTE) {
netdev_rx_stat[this_cpu].fastroute_deferred_out++;
dev_queue_xmit(skb);
dev_put(rx_dev);
continue;
}
#endif
skb->h.raw = skb->nh.raw = skb->data; 这时(skb->data)指向帧头尾部,为包头的开始
{
struct packet_type *ptype, *pt_prev;
unsigned short type = skb->protocol;

pt_prev = NULL;
for (ptype = ptype_all; ptype; ptype = ptype->next) {
if (!ptype->dev || ptype->dev == skb->dev) {
if (pt_prev) {
if (!pt_prev->data) {
deliver_to_old_ones(pt_prev, skb, 0); 传递给旧式包处理器
} else {
atomic_inc(&skb->users);
pt_prev->func(skb,
      skb->dev,
      pt_prev);
}
}
pt_prev = ptype;
}
}

#ifdef CONFIG_NET_DIVERT
if (skb->dev->divert && skb->dev->divert->divert)
handle_diverter(skb);
#endif /* CONFIG_NET_DIVERT */


#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (skb->dev->br_port != NULL &&
    br_handle_frame_hook != NULL) {
handle_bridge(skb, pt_prev);
dev_put(rx_dev);
continue;
}
#endif

for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
if (ptype->type == type &&
    (!ptype->dev || ptype->dev == skb->dev)) {
if (pt_prev) {
if (!pt_prev->data)
deliver_to_old_ones(pt_prev, skb, 0);
else {
atomic_inc(&skb->users);
pt_prev->func(skb,
      skb->dev,
      pt_prev);
}
}
pt_prev = ptype;
}
}

if (pt_prev) {
if (!pt_prev->data)
deliver_to_old_ones(pt_prev, skb, 1);
else
pt_prev->func(skb, skb->dev, pt_prev);
} else
kfree_skb(skb);
}

dev_put(rx_dev);

if (bugdet-- < 0 || jiffies - start_time > 1)
goto softnet_break;

#ifdef CONFIG_NET_HW_FLOWCONTROL
if (queue->throttle && queue->input_pkt_queue.qlen < no_cong_thresh ) {
if (atomic_dec_and_test(&netdev_dropping)) {
queue->throttle = 0;
netdev_wakeup();
goto softnet_break;
}
}
#endif

} 接收包已全部处理完
br_read_unlock(BR_NETPROTO_LOCK);

local_irq_disable();
if (queue->throttle) {
queue->throttle = 0;
#ifdef CONFIG_NET_HW_FLOWCONTROL
if (atomic_dec_and_test(&netdev_dropping))
netdev_wakeup();
#endif
}
local_irq_enable();

NET_PROFILE_LEAVE(softnet_process);
return;

softnet_break: 接收包未能处理完
br_read_unlock(BR_NETPROTO_LOCK);

local_irq_disable();
netdev_rx_stat[this_cpu].time_squeeze++;
__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ); 再次激活接收软中断,以便在下次继续处理
local_irq_enable();

NET_PROFILE_LEAVE(softnet_process);
return;
}
static int deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int last)
{
static spinlock_t net_bh_lock = SPIN_LOCK_UNLOCKED;
int ret = NET_RX_DROP;


if (!last) {
skb = skb_clone(skb, GFP_ATOMIC); 复制包描述符, 共享其数据缓冲区
if (skb == NULL)
return ret;
}

/* The assumption (correct one) is that old protocols
   did not depened on BHs different of NET_BH and TIMER_BH.
*/

/* Emulate NET_BH with special spinlock */
spin_lock(&net_bh_lock);

/* Disable timers and wait for all timers completion */
tasklet_disable(bh_task_vec+TIMER_BH);

ret = pt->func(skb, skb->dev, pt);

tasklet_enable(bh_task_vec+TIMER_BH);
spin_unlock(&net_bh_lock);
return ret;
}
void dev_add_pack(struct packet_type *pt) 注册包处理器
{
int hash;

br_write_lock_bh(BR_NETPROTO_LOCK);

#ifdef CONFIG_NET_FASTROUTE
/* Hack to detect packet socket */
if (pt->data) {
netdev_fastroute_obstacles++;
dev_clear_fastroute(pt->dev);
}
#endif
if (pt->type == htons(ETH_P_ALL)) {
netdev_nit++;
pt->next=ptype_all;
ptype_all=pt;
} else {
hash=ntohs(pt->type)&15;
pt->next = ptype_base[hash];
ptype_base[hash] = pt;
}
br_write_unlock_bh(BR_NETPROTO_LOCK);
}
void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
{
struct packet_type *ptype;
get_fast_time(&skb->stamp);

br_read_lock(BR_NETPROTO_LOCK);
for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
{
/* Never send packets back to the socket
* they originated from - MvS (miquels@drinkel.ow.org)
*/
if ((ptype->dev == dev || !ptype->dev) &&
((struct sock *)ptype->data != skb->sk))
{
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
break;

/* skb->nh should be correctly
   set by sender, so that the second statement is
   just protection against buggy protocols.
*/
skb2->mac.raw = skb2->data;

if (skb2->nh.raw < skb2->data || skb2->nh.raw >= skb2->tail) {
if (net_ratelimit())
printk(KERN_DEBUG "protocol %04x is buggy, dev %s/n", skb2->protocol, dev->name);
skb2->nh.raw = skb2->data;
if (dev->hard_header)
skb2->nh.raw += dev->hard_header_len;
}

skb2->h.raw = skb2->nh.raw;
skb2->pkt_type = PACKET_OUTGOING; 将要向外传送的包
ptype->func(skb2, skb->dev, ptype);
}
}
br_read_unlock(BR_NETPROTO_LOCK);
}

; net/ethernet/eth.c:

unsigned short eth_type_trans(struct sk_buff *skb, struct net_device *dev)
识别以太网硬件帧头
{
struct ethhdr *eth;
unsigned char *rawp;

skb->mac.raw=skb->data; 指向所接收数据帧
skb_pull(skb,dev->hard_header_len); 退掉硬件头地址区域
eth= skb->mac.ethernet;

if(*eth->h_dest&1) 测试目标地址的最高位(接网络比特顺序,最高有效位先传送)
{
if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
skb->pkt_type=PACKET_BROADCAST; 接收到广播包
else
skb->pkt_type=PACKET_MULTICAST; 接收到同播包
}

/*
* This ALLMULTI check should be redundant by 1.4
* so don't forget to remove it.
*
* Seems, you forgot to remove it. All silly devices
* seems to set IFF_PROMISC.
*/

else if(1 /*dev->flags&IFF_PROMISC*/)
{
if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
skb->pkt_type=PACKET_OTHERHOST; 接收到来自其它主机的包
}

if (ntohs(eth->h_proto) >= 1536)
return eth->h_proto;

rawp = skb->data;

/*
* This is a magic hack to spot IPX packets. Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
* won't work for fault tolerant netware but does for the rest.
*/
if (*(unsigned short *)rawp == 0xFFFF)
return htons(ETH_P_802_3);

/*
* Real 802.2 LLC
*/
return htons(ETH_P_802_2);
}
阅读全文
0 0

相关文章推荐

img
取 消
img