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中定义的协议的类型:
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;
};
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);
}
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));
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);
}
该函数主要设置数据帧的类型,返回协议的类型。
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);
}