2、协议相关
2.1、第3层协议的管理
在Linux内核中,有两种不同目的的3层协议:
(1) ptype_all管理的协议主要用于分析目的,它接收所有到达第3层协议的数据包。
(2) ptype_base管理正常的3层协议,仅接收具有正确协议标志符的数据包,例如,Internet的0x0800。
![Linux网络协议栈(四)——链路层(2)_第1张图片](http://img.e-com-net.com/image/product/b21e299010d344ae816a11d1752e6634.jpg)
注意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
#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
#define ETH_P_PUP 0x0200 /* Xerox PUP packet */
#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */
#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */
#define ETH_P_DEC 0x6000 /* DEC Assigned proto */
#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */
#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */
#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */
#define ETH_P_LAT 0x6004 /* DEC LAT */
#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */
#define ETH_P_CUST 0x6006 /* DEC Customer use */
#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */
#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
#define ETH_P_ATALK 0x809B /* Appletalk DDP */
#define ETH_P_AARP 0x80F3 /* Appletalk AARP */
#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
#define ETH_P_IPX 0x8137 /* IPX over DIX */
#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol
* defined in draft-wilson-wrec-wccp-v2-00.txt */
#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */
#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */
#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */
#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */
#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */
#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport
* over Ethernet
*/
#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
/*
* Non DIX types. Won't clash for 1500 types.
*/
#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
#define ETH_P_802_2 0x0004 /* 802.2 frames */
#define ETH_P_SNAP 0x0005 /* Internal only */
#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */
#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/
#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */
#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */
#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/
#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */
#define ETH_P_MOBITEX 0x0015 /* Mobitex ([email protected]) */
#define ETH_P_CONTROL 0x0016 /* Card specific control frames */
#define ETH_P_IRDA 0x0017 /* Linux-IrDA */
#define ETH_P_ECONET 0x0018 /* Acorn Econet */
#define ETH_P_HDLC 0x0019 /* HDLC frames */
#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) *
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);
}
该函数主要设置数据帧的类型,返回协议的类型。