在開發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