在开发H323设备时,发现有一个公网ip地址实在不错,因为H245协议中的TransportAddress包含了ip地址的信息,有时候可能portforward后也搞不定(至少理论上是这样)。可惜用adsl上网,一般都是使用私有地址,没办法,试试看改内核。
使LAN中的一台PC(或其它设备以下称A)和某些internet网IP的PC(或其它设备以下称B)通信时,和拨号服务器(以下称C)一样,拥有外网IP地址。 基本思路: 从PPPOE(严格地说是PPP)来的数据数据包,避开NAT,直接转发到内网;反之亦然。我把截获数据包的点选在ip_rcv中。 当然,原来的拨号网关还有一个代理arp的功能,在arp_rcv中截下A发出的arp包,非凡应答一下(conv.c)。 具体过程如下: 1.在arp.c和ip_input.c中各埋下一个钩子(函数指针),实际的处理把它放到外面的模块中实现,这样便于调试和修改,内核只需要改一次。 2.在模块程序中实现实际的ip数据包的转发:假如ip包来自某个地址,则给这个包加上MAC帧头,然后从把这个包通过dev_queue_xmit()转发到内部局域网。跳过路由,NAT,防火墙等功能模块;在反方向,从A收到的MAC包(通过IP辨认),直接去头转发给ppp设备。 3.收到A的arp请求(通过 A的MAC地址确认)直接应答。然后丢弃这个arp请求包。 4.应用程序中读取ppp0的ip地址(就是拨号得到的动态ip),交给模块。 说明: 1.new_ip_rcv中返回0,表示这个数据包经过了短路处理,内核对这个包不做进一步处理。 返回1;此包有问题要系统丢弃它;其它,一般数据包,内核应该继续正常处理。 2.new_arp_rcv中返回0,表示是一般arp包,系统继续正常处理,返回1,系统对此包不作进一步处理。 3.程序中有许多hardcode的地方,如ppp0,eth0。还有拨号服务器判定数据包是否是A发出的时,用的是IP,可能用MAC地址更合理。 [code] 内核(2.2.20)的修改: 1. arp.c 加入: int (*arp_rcv_hook)(strUCt sk_buff *skb, struct device *dev, struct packet_type *pt) = 0; 改: ……. int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { ….. /* * Check for bad requests for 127.x.x.x and requests for multicast * and in the case of requests for us we add the requester to the arp * cache. */ /* Special case: IPv4 duplicate address detection packet (RFC2131) */ if(arp_rcv_hook && arp_rcv_hook(skb,dev,pt)) /* 大约689行*/ goto out; if (sip == 0) { ….. } 2. ip_input.c 加入: int (*ip_rcv_hook)(struct skb_buff *) = 0; 改: int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { …… if(ip_rcv_hook) /* 大约第464行*/ { int retval; retval = (*ip_rcv_hook)(skb); if(retval == 0) return 0; else if(retval == 1) goto drop; } #ifdef CONFIG_FIREWALL …… } 3. netsyms.c 加入: extern int (*arp_rcv_hook)(struct sk_buff*,struct device *,struct packet_type*); EXPORT_SYMBOL_NOVERS(arp_rcv_hook); extern int (*ip_rcv_hook)(struct sk_buff*); EXPORT_SYMBOL_NOVERS(ip_rcv_hook); 应用程序: /*conv.c shortcut between adsl and lan device * email: jundai20@hotmail.com */ #include #include #include #include #include #include #include "conv.h" int ppp_ipaddr; MODULE_PARM(ppp_ipaddr,"i"); /* This our_memcpy it is runs in kernel, * so the des, src is equ to phy address */ void our_memcpy(unsigned char* des,unsigned char* src,unsigned int len) { unsigned int i; for(i=0;i *(des+i) = *(src+i); } struct sk_buff* ppp2eth(struct sk_buff* skb) { unsigned char ethhdr[14] ={0x00,0x06,0x4e,0x00,0x04,0x94,0x00,0xe0,0x4c,0xe0,0xf8,0x35,0x08,0x00};/* change the mac addr pls*/ struct sk_buff* skb2; unsigned int size; size = skb->len + 14; skb2 = alloc_skb(size,GFP_ATOMIC); our_memcpy(skb2->data,ethhdr,14); our_memcpy(skb2->data+14,skb->data,skb->len); skb2->tail += size; skb2->len = size; __kfree_skb(skb); return skb2; } struct sk_buff* eth2ppp(struct sk_buff* skb) { return skb; } int new_ip_rcv(struct sk_buff *skb) { struct device* lan_eth,*ppp_h; struct sk_buff* skb_new; lan_eth = dev_get("eth1"); ppp_h = dev_get("ppp0"); if(lan_eth == NULL ppp_h == NULL) return 0; if(skb->dev->name[0] == 'p') { if(skb->nh.iph->saddr !
= 0x12345678)/* change to the ip addr as you wish*/ return 3; skb_new = ppp2eth(skb); skb_new->dev = lan_eth; dev_queue_xmit(skb_new); return 0; } else if (skb->dev->name[0] == 'e' && skb->dev->name[3] == '1') { if(skb->nh.iph->saddr != ppp_ipaddr) return 3; skb_new = eth2ppp(skb); skb_new->dev = ppp_h; dev_queue_xmit(skb_new); } // if we return 1 , we will free skb return 0; } int new_arp_rcv(struct sk_buff* skb,struct device* dev,struct packet_type* pt) { struct arphdr *arp = skb->nh.arph; unsigned char *arp_ptr= (unsigned char *)(arp+1); unsigned long sip,tip; unsigned char* sha; sha=arp_ptr; arp_ptr += dev->addr_len; our_memcpy((unsigned char*)(&sip),arp_ptr,4); arp_ptr += 4; arp_ptr += dev->addr_len; our_memcpy((unsigned char*)(&tip),arp_ptr,4); if(*sha== 0x00 && *(sha+1) == 0x06 && *(sha+2) == 0x4e && *(sha+3) == 0x00 && *(sha+4) == 0x04 && *(sha+5) == 0x94 && arp->ar_op == __constant_htons(ARPOP_REQUEST)) { arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); return 1; } else return 0; } int init_module() { EXPORT_NO_SYMBOLS; arp_rcv_hook = new_arp_rcv; ip_rcv_hook = new_ip_rcv; printk("now enter ipnat : %x...\n",ppp_ipaddr); return 0; } void cleanup_module() { arp_rcv_hook = 0; ip_rcv_hook = 0; printk("now net_hook will quit.\n"); } /* conv.h: head file for conv.c * email:jundai20@hotmail.com */ extern int (*ip_rcv_hook)(struct sk_buff *); extern int (*arp_rcv_hook)(struct sk_buff *,struct device* dev,struct packet_type* pt); extern void arp_send(int type, int ptype, u32 dest_ip, struct device *dev, u32 src_ip, unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th); extern struct device* dev_get(const char* name); extern struct sk_buff * skb_realloc_headroom(struct sk_buff *skb, int newheadroom); extern void __kfree_skb(struct sk_buff*); #ifndef ARPOP_REPLY #define ARPOP_REPLY 2 #endif #ifndef ETH_P_ARP #define ETH_P_ARP 0x0806 #endif #ifndef GFP_ATOMIC #define GFP_ATOMIC 0x08 #endif /* passnat.c : user interface for use conv * email: jundai20@hotmail.com */ #include #include #include #include #include #include int main() { struct ifreq ifr; struct sockaddr_in *skt; int eth_s; unsigned long ip_addr_rtrn; unsigned char command[256]={0}; if((eth_s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket");
exit(0); } strcpy(ifr.ifr_name, "ppp0"); if(ioctl(eth_s, SIOCGIFADDR, &ifr) < 0); skt = (struct sockaddr_in*)&ifr.i
|