分类: LINUX
2009-02-11 10:26:10
LINUX2.6.18-i386内核UDP包进站流程
l UDP包处理系列函数的注册
在af_inet.c文件中声明的UDP协议处理函数结构体
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
通过inet_add_protocol()函数放入网络协议hash表inet_protos[MAX_INET_PROTOS]中,其中MAX_INET_PROTOS宏的定义为256。
代码为:if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
其中IPPROTO_UDP宏是在in.h中定义的,值为17,即此结构体被映射到inet_protos数组的第17个元素的位置。
udp_protocol.handler被赋值为udp_rcv()函数,此函数是UDP协议的进站处理函数接口,其函数原型在/usr/src/linux-2.6.18.i386/net/ipv4/udp.c文件中被定义;inet_add_protocol的函数原型在protocol.c文件中定义,struct net_protocol结构体在protocol.h中定义。
struct net_protocol {
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info);
int (*gso_send_check)(struct sk_buff *skb);
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
int no_policy;
};
l UDP包处理函数的调用流程
从网卡过来的数据包在通过第二个钩子NF_IP_LOCAL_IN时,调用钩子处理函数ip_local_deliver_finish()函数对数据包的协议和本机初始化是注册的协议处理函数进行匹配,根据数据包头中的协议值在inet_protos数组中找到相对应的包处理结构体,根据结构体中指定的函数进行下一步的处理。(函数代码在ip_input.c中)
l UDP进站包中获得端口号
在从网卡获得的sk_buff包结构中的共用体h中有一项struct udphdr *uh为UDP头结构体,具体定义在usr/src/linux-2.6.18.i386/include/linux/udp.h中,此结构体有四个成员,分别为:发送端主机端口号、接收端主机端口号、UDP包长度和校验和。如想获取端口号可根据实际需要通过ntohs()函数将相应的端口号从网络字节序转换为主机字节序即可。
例:
如A主机往B主机发送UDP包,获取A主机设定的UDP端口号可使用ntohs(skb->h.uh->dest)获得。
struct sk_buff结构体在skbuff.h中
struct sk_buff {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
struct sock *sk;
struct skb_timeval tstamp;
struct net_device *dev;
struct net_device *input_dev;
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct ipv6hdr *ipv6h;
unsigned char *raw;
} h;
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
unsigned char *raw;
} nh;
union {
unsigned char *raw;
} mac;
struct dst_entry *dst;
struct sec_path *sp;
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[48];
unsigned int len,
data_len,
mac_len,
csum;
__u32 priority;
__u8 local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
__u8 pkt_type:3,
fclone:2,
#ifndef CONFIG_XEN
ipvs_property:1;
#else
ipvs_property:1,
proto_data_valid:1,
proto_csum_blank:1;
#endif
__be16 protocol;
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_NETFILTER
struct nf_conntrack *nfct;
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
__u32 nfmark;
#endif /* CONFIG_NETFILTER */
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
/* These elements must be at the end, see alloc_skb() for details. */
unsigned int truesize;
atomic_t users;
unsigned char *head,
*data,
*tail,
*end;
};
路径说明:
af_inet.c /usr/src/linux-2.6.18.i386/net/ipv4/
in.h /usr/src/linux-2.6.18.i386/include/linux/
ip_input.c /usr/src/linux-2.6.18.i386/net/ipv4/
protocol.c /usr/src/linux-2.6.18.i386/net/ipv4/
protocol.h /usr/src/linux-2.6.18.i386/include/net/
skbuff.h /usr/src/linux-2.6.18.i386/include/linux/