msn: [email protected]
来源:http://yfydz.cublog.cn
1. 前言 在/proc/sys/net/ipv4/conf下的各个子目录下都有一个参数proxy_arp,这个参数可控制系统或各网卡是否支持ARP代理功能,在网关上打开ARP代理功能使得内部子网的机器即使在没配置网关地址的时候也能通过网关访问外网。 以下内核代码版本为2.6.17.11。 2. 功能实现 proxy_arp这个/proc项本身是在net/ipv4/decinet.c中定义,是ipv4_conf结构中的一部分,真正起到控制作用是在net/ipv4/arp.c中。 2.1 判断是否可以进行ARP代理 /* net/ipv4/arp.c */ /* * Check if we can use proxy ARP for this path */ // 该函数判定是否要进行ARP代理 // in_dev是ARP请求进入的网卡,rt是其目的IP对应的路由表项 static inline int arp_fwd_proxy(struct in_device *in_dev, struct rtable *rt) { struct in_device *out_dev; int imi, omi = -1; // // 判断该网卡是否进行ARP代理 // #define IN_DEV_PROXY_ARP(in_dev) \ // (ipv4_devconf.proxy_arp || (in_dev)->cnf.proxy_arp) if (!IN_DEV_PROXY_ARP(in_dev)) return 0; // in_dev的medium_id为0,不限制介质类型,可以进行ARP代理 if ((imi = IN_DEV_MEDIUM_ID(in_dev)) == 0) return 1; if (imi == -1) return 0; /* place to check for proxy_arp for routes */ // // 根据物理网卡net_device获取inet网卡in_device // 也就是查找是否有合适的数据发出网卡 // 这里说一下物理网卡和inet网卡的区别,物理网卡是数据链路层看到的网卡设备, // 是和每个物理网卡对应的,也可能对应虚拟的网卡设备,如ppp,ipsec,vlan等; // inet网卡是IP层看到的物理网卡的IP层信息,在inet网卡上可以捆绑多个 // IP别名地址,这些别名地址形成一个链表 // if ((out_dev = in_dev_get(rt->u.dst.dev)) != NULL) { // 获取发出网卡的medium_id omi = IN_DEV_MEDIUM_ID(out_dev); in_dev_put(out_dev); } // 判断进入网卡和发出网卡是否相同,不同的话就可以进行ARP代理 return (omi != imi && omi != -1); } 2.2 进行ARP代理 /* net/ipv4/arp.c */ static int arp_process(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct in_device *in_dev = in_dev_get(dev); struct arphdr *arp; unsigned char *arp_ptr; struct rtable *rt; unsigned char *sha, *tha; u32 sip, tip; u16 dev_type = dev->type; int addr_type; struct neighbour *n; ...... // 处理ARP请求包 if (arp->ar_op == htons(ARPOP_REQUEST) && ip_route_input(skb, tip, sip, 0, dev) == 0) { ...... // 首先ARP包进入的网卡要求允许转发,ip_forwarding != 0 } else if (IN_DEV_FORWARD(in_dev)) { // 充分条件1: 路由进行DNAT if ((rt->rt_flags&RTCF_DNAT) || // 充分条件2: 目的地址是单播,而且出网卡不同于入网卡,而且允许网卡ARP代理或者 // 在ARP表中有相关项 (addr_type == RTN_UNICAST && rt->u.dst.dev != dev && (arp_fwd_proxy(in_dev, rt) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) { // 如果neightbour表中有该源IP,释放 n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) neigh_release(n); // 3个充分条件发送ARP回应 if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED || // 1: 该包的neighbour控制要求本地排队,该标志在下面的pneigh_enqueue()函数中设置 skb->pkt_type == PACKET_HOST || // 2: 数据包类型为到本机的 in_dev->arp_parms->proxy_delay == 0) { // 3: inet网卡的ARP参数中proxy_delay为0,不过缺省情况下该值为(8 * HZ)/10 arp_send(RPOP_REPLY, ETH_P_ARP, sip, dev, tip,sha,dev->dev_addr,sha); } else { pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); in_dev_put(in_dev); return 0; } goto out; } } ...... 3. 结论 在网关设置了某个网卡上设置proxy_arp非0且网卡打开转发后,该网卡上收到的非本网段的ARP请求,该网卡就会满足回应本网卡信息的条件而发出ARP回应,这样即使内部机器没有设置网关,访问外部地址时会发出查找外部IP地址对应MAC地址的ARP请求,网关内部网卡就会回应自己的 MAC地址,从而能通过网关访问外部。当然前提是从内部机器看来目标机和内部机器在同一网段内,因此可以发出ARP请求,如果掩码很小不足以掩住目标机的话,会认为不在同一网络,从而不会发ARP请求了,直接返回对方不可达。