Linux网络协议栈(四)——链路层(2)

2、协议相关
2.1、第3层协议的管理
在Linux内核中,有两种不同目的的3层协议:
(1)    ptype_all管理的协议主要用于分析目的,它接收所有到达第3层协议的数据包。
(2)    ptype_base管理正常的3层协议,仅接收具有正确协议标志符的数据包,例如,Internet的0x0800。

注意sb_buff与net_device中几个字段的区别:
sb_buff:
unsigned short        protocol
高层协议从二层设备的角度所看到的协议,典型的协议包括 IP,IPV6和 ARP,完整的列表在 include/linux/if_ether.h。
unsigned char pkt_type
帧的类型,可能的取值都在include/linux/if_packet.h 中定义.
      
net_device:
unsigned short type
    设备类型(以太网,帧中继等)。在include/linux/if_arp.h 中有完整的类型列表。

2.2、协议处理函数注册
当协议注册时,内核会调用dev_add_pack添加一个与之对应的packet_type数据结构:
//include/linux/netdevice.h
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            *af_packet_priv;
    
struct list_head    list;
};
type:协议类型,它可以取以下一些值。来看看if_ether.h中定义的协议的类型:
Code
dev:网络设备。PF_PACKET套接字通常使用它在特定的设备监听,例如,tcpdump -i eth0  通过PF_PACKET套接字创建一个packet_type实例,然后将dev指向eth0对应的net_device数据结构。
func:协议处理函数。
af_packet_priv:被PF_PACKET套接字使用。
当同一个类型的协议有多个packet_type实例时,输入的帧会被所有的协议处理函数处理。

IP协议的packet_type:
//net/ipv4/ip_output.c
static struct packet_type ip_packet_type = {
    .type 
= __constant_htons(ETH_P_IP),
    .func 
= ip_rcv,
};

//在inet_init中被调用
void __init ip_init(void)
{
    dev_add_pack(
&ip_packet_type);

    ip_rt_init();
    inet_initpeers();

#if defined(CONFIG_IP_MULTICAST) && defined(CONFIG_PROC_FS)
    igmp_mc_proc_init();
#endif
}
//net/core/dev.c
void dev_add_pack(struct packet_type *pt)
{
    
int hash;

    spin_lock_bh(
&ptype_lock);
    
if (pt->type == htons(ETH_P_ALL)) {
        netdev_nit
++;
        list_add_rcu(
&pt->list, &ptype_all);
    } 
else {
        hash 
= ntohs(pt->type) & 15;
        list_add_rcu(
&pt->list, &ptype_base[hash]);
    }
    spin_unlock_bh(
&ptype_lock);
}

2.3、以太网帧(Ethernet)与802.3帧
老的以太网的帧的格式与标准和802.3标准的格式分别如下:


以太网的帧头(Ethernet frame header)的定义:
//include/linux/if_ether.h
struct ethhdr {
    unsigned 
char    h_dest[ETH_ALEN];    /* destination eth addr    */
    unsigned 
char    h_source[ETH_ALEN];    /* source ether addr    */
    unsigned 
short    h_proto;        /* packet type ID field    */
} __attribute__((packed));

h_proto>1536的以太网类型:


2.4、eth_type_trans函数
//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,ETH_HLEN);
    
    
//取出以太网头
    eth = eth_hdr(skb);
    skb
->input_dev = dev;
    
    
//设置帧的类型
    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))
            
//如果帧的目标地址与网络设备的mac地址不同
            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.
     
*/
     
//IPX数据包没有802.2标准的LLC层,0xFFFF为其标志位
    if (*(unsigned short *)rawp == 0xFFFF)
        
return htons(ETH_P_802_3);
        
    
/*
     *    Real 802.2 LLC
     
*/
     
//802.2标准的LLC协议
    return htons(ETH_P_802_2);
}
该函数主要设置数据帧的类型,返回协议的类型。
posted @ 2009-09-26 11:39  YY哥  阅读(8384)  评论(0编辑  收藏  举报