之后在ipip_fb_tunnel_init()中对IPIP隧道进行了参数的设置,包括名字,协议号什么的。最后就注册这个新创建的设备吧
if ((err = register_netdev(ipn->fb_tunnel_dev))) goto err_reg_dev;这样整个的初始化过程就做完了,下面简单分析一下发送和接收的过程。
二. IPIP的接收我们之前说到过,对应从网卡收上来的报文,过完链路层后就会到ip_rcv()中,大概是这样的路线:
ip_rcv()->ip_rcv_finish()->ip_local_deliver()->ip_local_deliver_finish(),最终会在其中看到
ret = ipprot->handler(skb); if (ret < 0) { protocol = -ret; goto resubmit; }调用注册的协议的处理函数,也就是最终会调到tunnel4_rcv()->ipip_rcv()。
if ((tunnel = ipip_tunnel_lookup(dev_net(skb->dev), iph->saddr, iph->daddr)) != NULL) { /* 查找对应的tunnel */ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { read_unlock(&ipip_lock); kfree_skb(skb); return 0; } secpath_reset(skb); skb->mac_header = skb->network_header; /* 修改报文的mac头指向网络层开始,为了下面使用netif_rx 能传给上层? */ skb_reset_network_header(skb); skb->protocol = htons(ETH_P_IP); skb->pkt_type = PACKET_HOST; /* 填充报文信息 */ tunnel->dev->stats.rx_packets++; tunnel->dev->stats.rx_bytes += skb->len; skb->dev = tunnel->dev; skb_dst_drop(skb); nf_reset(skb); ipip_ecn_decapsulate(iph, skb); netif_rx(skb); /* 传递给上层协议栈 */ read_unlock(&ipip_lock); return 0; } 三. IPIP的发送在初始化的时候,我们看到IPIP报文的发送时通过ipip_tunnel_xmit()函数进行的。在发送时,要给原有的IP报文头前添加新的IP头,我们略过这个函数的前面的路由处理的部分,直接看关键的添加报文头的地方:
max_headroom = (LL_RESERVED_SPACE(tdev)+sizeof(struct iphdr)); if (skb_headroom(skb) < max_headroom || skb_shared(skb) || (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);/* 为新的报文头分配空间 */ if (!new_skb) { ip_rt_put(rt); stats->tx_dropped++; dev_kfree_skb(skb); return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); dev_kfree_skb(skb); skb = new_skb; old_iph = ip_hdr(skb); } skb->transport_header = skb->network_header; /* 重新设置传输层的头位置 */ skb_push(skb, sizeof(struct iphdr)); skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); skb_dst_drop(skb); skb_dst_set(skb, &rt->u.dst); /* * Push down and install the IPIP header. */ /* 设置新的IP头字段 */ iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr)>>2; iph->frag_off = df; iph->protocol = IPPROTO_IPIP; iph->tos = INET_ECN_encapsulate(tos, old_iph->tos); iph->daddr = rt->rt_dst; iph->saddr = rt->rt_src; if ((iph->ttl = tiph->ttl) == 0) iph->ttl = old_iph->ttl;最后调用IPTUNNEL_XMIT()宏发送出去。
Linux公社的RSS地址:https://www.linuxidc.com/rssFeed.aspx