linux下的ip tunnel workflow

预置条件:

 

PC1发送包到PC2,PC1和PC2在不同的局域网中,要使得PC1的包能够到达PC2,有两种途径:

  1. 使用NAT
  2. 使用ip tunnel

这里,我们只介绍ip tunnel这种方式。

PC1发往PC2的包的src为192.168.1.10,dst为192.168.2.10,PC1首先将包发往网关GW1。在网关GW1和GW2之间建立ip tunnel,使得PC1的包能到达GW2。

先介绍网关GW1和GW2的ip tunnel的配置。

GW1:

#ip tunnel add tun0 mode ipip remote 10.70.128.20 local 10.70.128.10 ttl 64

#ip link set tun0 up

#ip route add 192.168.2.0/24 dev tun0

 

GW1的配置与GW1类似。

3条命令后面两条很好理解,一个是使得tun0设备up起来,另一条是配置路由,即所有发往192.168.2.0/24的包都路由到tun0设备,从tun0这个设备发出去。

我们着重来介绍下ip tunnel这条命令。

Ip tunnel命令的实现是向kernel的ipip module发送IOC命令SIOCADDTUNNEL(参考iproute2-4.20/ip/iptunnel.c)

我们来看看ipip module的处理。

(linux-4.7.1/net/ipv4/ipip.c)

ipip_tunnel_ioctl()->ip_tunnel_ioctl()->ip_tunnel_create()->__ip_tunnel_create()

__ip_tunnel_create()分配struct net_device, 即创建tun0设备。

alloc_netdev()中会调用这个ops->setup()

ipip module挂载的时候,ipip_init()->register_pernet_device(&ipip_net_ops)

register_pernet_device(&ipip_net_ops)会调用ipip_net_ops.init(),即调用ipip_init_net();

ipip_init_net()

->ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0")

                                     ->itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms)

所以这里的ops->setup()就是ipip_link_ops.setup(), 即ipip_tunnel_setup()。

这里设置了dev->netdev_ops为ipip_netdev_ops.

最后,将ip tunnel的信息记录到该dev。

即tunnel->parms记录了配置命令中的remote,local ttl等参数信息。

 

配置完ip tunnel后up了该tunnel device,并配置路由到该device的路由entry.

接下来看GW1收到PC1发往PC2的包时的处理。

以Kenel ipv4为例来说明函数调用过程。

ip_rcv() ->

NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL, skb, dev, NULL, ip_rcv_finish) ->

ip_rcv_finish() -> ip_route_input_noref() -> ip_route_input_slow() -> fib_lookup() //查找路由表

                                                                                                                        -> ip_mkroute_input()

PC1发往PC2的包的路由结果不是local,所以要求GW1的eth0的forward特性要打开,否则就是HOST UNREACHABLE.

Ip_mkroute_input() -> __mkroute_input() 设置dst.input为ip_forward(), 设置dst.output为ip_output()

 

ip_rcv_finish() -> dst_input(skb),即进入了ip_forward()处理。

ip_forward() ->

NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, net, NULL, skb, skb->dev, rt->dst.dev, ip_forward_finish) ->

ip_forward_finish() -> dst_output(net, sk, skb) //即ip_output()

ip_output() ->  //这里会设置skb->dev 为出去的接口dev;

NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb, NULL, dev, ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)) ->

Ip_finish_output() -> ip_finish_output2()

Ip_finish_output2()首先查找neighbour, __ipv4_neigh_lookup_noref(), 然后调用dst_neigh_output()

dst_neigh_output() -> n->output(n, skb) //这里这个n->output()就是neigh_direct_output()

neigh_direct_output() -> dev_queue_xmit() -> __dev_queue_xmit()

__dev_queue_xmit()判断设备是否up,在up状态下调用dev_hard_start_xmit()

dev_hard_start_xmit() -> xmit_one() -> netdev_start_xmit() -> __netdev_start_xmit()

__netdev_start_xmit()这里调用dev->netdev_ops->ndo_start_xmit()

在ip tunnel这个例子中,这个dev->netdev_ops->ndo_start_xmit()就是ipip_tunnel_xmit()

ipip_tunnel_xmit() -> ip_tunnel_xmit() -> iptunnel_xmit()

ip_tunnel_xmit()会以ip tunnel命令中的local, remote为key进行路由查找,并设置包的dst skb_dst()为这个找到的路由entry. 这个路由的结果是从GW1 eth1出去。

iptunnel_xmit()会build一个外层的iphdr, 改iphdr的src为ip tunnel命令中local, dst为ip tunnel命令中的remote。最后ip_local_out()发出去。

所以PC1发往PC2的包,在GW1的eth1的出去后的形式为:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值