IP隧道基础研究
static char banner[] __initdata = KERN_INFO "IPv4 over IPv4 tunneling driver\n"; static struct xfrm_tunnel ipip_handler = { .handler = ipip_rcv, //看下面接收处理函数实现 .err_handler = ipip_err, .priority = 1, }; static int __init ipip_init(void) // net/ipv4/ipip.c { int err; printk(banner); //打印信息 //注册ipip接收处理函数 if (xfrm4_tunnel_register(&ipip_handler)) { printk(KERN_INFO "ipip init: can't register tunnel\n"); return -EAGAIN; } //分配一个网络设备,设备名tunl0 ipip_fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "tunl0", ipip_tunnel_setup); if (!ipip_fb_tunnel_dev) { err = -ENOMEM; goto err1; } ipip_fb_tunnel_dev->init = ipip_fb_tunnel_init; //初始化函数 //注册这个网络设备,会调用上面的初始化函数 if ((err = register_netdev(ipip_fb_tunnel_dev))) goto err2; out: return err; err2: free_netdev(ipip_fb_tunnel_dev); err1: xfrm4_tunnel_deregister(&ipip_handler); goto out; } //注册处理函数 static struct xfrm_tunnel *tunnel4_handlers; int xfrm4_tunnel_register(struct xfrm_tunnel *handler) { struct xfrm_tunnel **pprev; int ret = -EEXIST; int priority = handler->priority; //权限 mutex_lock(&tunnel4_mutex); for (pprev = &tunnel4_handlers; *pprev; pprev = &(*pprev)->next) { if ((*pprev)->priority > priority) //找到位置,从小到大排列 break; if ((*pprev)->priority == priority) //重复,出错 goto err; } handler->next = *pprev; *pprev = handler; ret = 0; err: mutex_unlock(&tunnel4_mutex); return ret; } ipip协议 static struct net_protocol tunnel4_protocol = { //IPIP协议处理 .handler = tunnel4_rcv, .err_handler = tunnel4_err, .no_policy = 1, }; ipip协议注册 static int __init tunnel4_init(void) { //向核心注册ipip协议 if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) { printk(KERN_ERR "tunnel4 init: can't add protocol\n"); return -EAGAIN; } return 0; } static int tunnel4_rcv(struct sk_buff *skb) { struct xfrm_tunnel *handler; if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto drop; //调用连表中的所有处理函数,已经安大小排过序了 for (handler = tunnel4_handlers; handler; handler = handler->next) if (!handler->handler(skb)) //处理函数返回0,正确结束 return 0; //有一个不对就发送icmp目的和端口不可达包 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); drop: kfree_skb(skb); return 0; } 现在我们看出只要是调用了xfrm4_tunnel_register函数向ipip协议注册了处理函数那么就会被ipip协议接收函数调用,权限字段值越小越先被调用. 在ip_local_deliver_finish函数中(可以参考linux协议占函数流程一文)会根据ip协议中的协议字段调用相应的协议处理函数。 ...... int protocol = skb->nh.iph->protocol; ...... if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { ...... ret = ipprot->handler(skb); //调用ipip协议的处理函数tunnel4_rcv ...... } [接收处理函数实现] 每一个IP数据包均交由ip_rcv函数处理,在进行一些必要的判断后,ip_rcv对于发送给本机的数据包将交给上层处理程序。 对于IPIP包来说,其处理函数是ipip_rcv(就如TCP包的处理函数是tcp_rcv一样,IP层不加区分)。 也就是说,当一个目的地址为本机的封包到达后,ip_rcv函数进行一些基本检查并除去IP头,然后交由ipip_rcv解封。 ipip_rcv所做的工作就是去掉封包头,还原数据包,然后把还原后的数据包放入相应的接收队列netif_rx(). static int ipip_rcv(struct sk_buff *skb) { struct iphdr *iph; struct ip_tunnel *tunnel; iph = skb->nh.iph; read_lock(&ipip_lock); //在hash表中查询 if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { //检测IPSEC包策略 read_unlock(&ipip_lock); kfree_skb(skb); return 0; } secpath_reset(skb); //释放sec_path skb->mac.raw = skb->nh.raw; skb->nh.raw = skb->data; //指向这个封装的ip头 skb->protocol = htons(ETH_P_IP);//协议变为IP skb->pkt_type = PACKET_HOST; tunnel->stat.rx_packets++; tunnel->stat.rx_bytes += skb->len; skb->dev = tunnel->dev;//指向这个虚拟设备 dst_release(skb->dst); //释放路由缓存,需要从新查找 skb->dst = NULL; nf_reset(skb); //释放ip_conntrack结构 ipip_ecn_decapsulate(iph, skb); //ECN解封,IPSEC相关 netif_rx(skb);//重新递交 read_unlock(&ipip_lock); return 0; } read_unlock(&ipip_lock); return -1; //查询不到出错 } static struct ip_tunnel *tunnels_r_l[HASH_SIZE]; //(remote,local) static struct ip_tunnel *tunnels_r[HASH_SIZE]; //(remote,*) static struct ip_tunnel *tunnels_l[HASH_SIZE]; //(*,local) static struct ip_tunnel *tunnels_wc[1]; //(*,*) static struct ip_tunnel **tunnels[4] = { tunnels_wc, tunnels_l, tunnels_r, tunnels_r_l }; static struct ip_tunnel * ipip_tunnel_lookup(u32 remote, u32 local) { //计算出hash值 unsigned h0 = HASH(remote); unsigned h1 = HASH(local); struct ip_tunnel *t; //在源和目的地址都在的hash表中寻找 for (t = tunnels_r_l[h0^h1]; t; t = t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; } //在只有目的地址hash表中寻找 for (t = tunnels_r[h0]; t; t = t->next) { if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; } //在只有源地址hash表中寻找 for (t = tunnels_l[h1]; t; t = t->next) { if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) return t; } if ((t = tunnels_wc[0]) != NULL && (t->dev->flags&IFF_UP)) return t; return NULL; } [\接收处理函数实现]
posted on 2013-08-28 11:07 SuperKing 阅读(1000) 评论(0) 编辑 收藏 举报