在开发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;
}