前几章稍分析了网桥,接着我们继续1中的收包流程,说到
对该数据包转达到其他L3协议的处理函数:
type = skb->protocol;
list_for_each_entry_rcu(ptype,
&
ptype_base[ntohs(
type) & PTYPE_HASH_MASK], list) {
if (ptype->type == type &&
(ptype->dev == null_or_orig || ptype->dev == skb->dev ||
ptype->dev == orig_dev)) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
->static inline int deliver_skb(struct sk_buff *skb,
struct packet_type *pt_prev,
struct net_device *orig_dev)
{
atomic_inc(&skb->users);
return pt_prev->
func
(skb, skb->dev, pt_prev, orig_dev);
}
ptype_base
: 协议模块通过dev_add_pack()加入 arp pppoe ip等
我们看ptype_base的建立
void dev_add_pack(struct packet_type *pt)
{
int hash;
spin_lock_bh(&ptype_lock);
if (pt->type == htons(
ETH_P_ALL)) 如果是ETH_P_ALL类型,则加入ptype_all 用于sniffer等
list_add_rcu(&pt->list, &
ptype_all);
else {
hash = ntohs(pt->type) & PTYPE_HASH_MASK; 否则使用hash方式插入ptype_base中
list_add_rcu(&pt->list, &
ptype_base[hash]);
}
spin_unlock_bh(&ptype_lock);
}
众多协议通过此接口注册协议处理功能
dev_add_pack(&arp_packet_type); arp
dev_add_pack(&
ip_packet_type); ip
dev_add_pack(&ipv6_packet_type); ipv6
dev_add_pack(&vlan_packet_type); vlan-802.1q
dev_add_pack(&pppoes_ptype); pppoes
dev_add_pack(&pppoed_ptype); pppoed
当然我们主要看dev_add_pack(&
ip_packet_type); ip
->inet_init() /net/ipv4/af_inet.c
->dev_add_pack(&
ip_packet_type
);
static struct packet_type ip_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_IP),
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
.func = ip_rcv,
.gso_send_check = inet_gso_send_check,
.gso_segment = inet_gso_segment,
.gro_receive = inet_gro_receive,
.gro_complete = inet_gro_complete,
}; ip协议入口
->ip_rcv()
->if (skb->pkt_type == PACKET_OTHERHOST)
goto drop; pkt_type表示报文类型 。PACKET_OTHERHOST表示非去往本机但是在特定模式下被接受的报文
->pskb_may_pull(skb, sizeof(struct iphdr)) pskb_may_pull在调用 skb_pull() 去掉外层协议头之前,通常先调用此函数判断一下是否有足
够的数据用于“pull”。
->iph = ip_hdr(skb); 取得ip头
->if (iph->ihl < 5 || iph->version != 4) 普通IP数据包首部长度(不包含任何选项)字段的值是5
goto inhdr_error; iph->ihl<5说明iph->ihl指的是IP包的首部长度,首部一行是32bit也就是4byte(字节)
->if (!pskb_may_pull(skb, iph->ihl*4)) iph->ihl*4是20,是首部最长的长度,此语句是说如果头部长度不能pull,则error
goto inhdr_error;
->if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) 用汇编来校验和
goto inhdr_error;
->NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish); 交给netfilter过滤,过滤通过则交给ip_rcv_finish处理
->ip_rcv_finish()
->ip_route_input() 查找路由信息
->if (iph->ihl > 5 && ip_rcv_options(skb)) 如果IP头部大于20字节,则表示IP头部包含IP选项,需要进行选项处理.
goto drop;
->dst_input(skb); dst_input实际上会调用skb->dst->input(skb).input函数会根据路由信息设置为合适的函数指针,如果是递交到本地的
则为ip_local_deliver,若是转发则为ip_forward
两条路径:
1)
ip_local_deliver
2) ip_forward
先看
ip_local_deliver:
->ip_local_deliver()
->if
(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { 判断是否为分片报文
if (
ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) 进行重组
return 0;
}
-> ip_local_deliver_finish();
->ip_local_deliver_finish()
->__skb_pull(skb, ip_hdrlen(skb)); 剥离IP头
->skb_reset_transport_header(skb); 置传输层的头
->protocol = ip_hdr(skb)->protocol 得到协议号
->raw_local_deliver() ->raw_v4_input() 进入raw socket处理流程
->hash = protocol & (MAX_INET_PROTOS - 1);
ipprot = rcu_dereference(
inet_protos[hash]
struct net_protocol *
inet_protos[MAX_INET_PROTOS];
int
inet_add_protocol(const struct
net_protocol
*prot, unsigned char protocol)
{
int hash, ret;
hash = protocol & (MAX_INET_PROTOS - 1);
spin_lock_bh(&inet_proto_lock);
if (inet_protos[hash]) {
ret = -1;
} else {
inet_protos[hash] = prot;
ret = 0;
}
spin_unlock_bh(&inet_proto_lock);
return ret;
}
inet_protos[]是INET SOCKET层全部协议的接收处理函数集的一个数组,每个协议以自己的协议号(比如icmp协议就以IPPROTO_ICMP,也就是数字1作为下标,把自己添加到该数组,数组中存储了net_protocol结构,例如针对TCP协议,相关的结构定义如下:
inet_add_protocol(&icmp_protocol, IPPROTO_ICMP)
inet_add_protocol(&udp_protocol, IPPROTO_UDP)
inet_add_protocol(&
tcp_protocol, IPPROTO_TCP)
inet_add_protocol(&igmp_protocol, IPPROTO_IGMP)
static const struct net_protocol
tcp_protocol
= {
.handler =
tcp_v4_rcv,
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
.gro_receive = tcp4_gro_receive,
.gro_complete = tcp4_gro_complete,
.no_policy = 1,
.netns_ok = 1,
};
->(!ipprot->no_policy) ipprot->no_policy如果为1,表明当前处理的协议设置了IP_SEC处理规则
->ipprot->
handler(skb); .handler =
tcp_v4_rcv, 若是TCP则调用该函数 数据包进入TCP协议栈继续处理