透明桥接和NAT共存的实现

来源:互联网 发布:网络电视韩国高清25 编辑:程序博客网 时间:2024/06/05 04:20

1、什么是透明桥

透明桥接主要用于以太网环境中。透明网桥不更改数据帧的内容,也不作为数据帧的源或目的地,它使得由它连接起来的各个网段看起来像是在一条电缆上。若是收到一个帧,而其中的MAC地址不在自己的桥接表里,就会将这个帧扩散到所有的接口,桥接还会扩散所有的广播包,占用网络的有效带宽,造成网络的堵塞。使用生成树协议”(Spanning Tree Protocol)可以很好地避免桥接引起的广播风暴。

但本项目的网关设备只有一个LAN,不存在以上对LAN口的选择,不存在广播风暴。

 

       2、系统结构

透明桥和NAT共存的网络拓扑结构图如下:


功能描述:

透明桥:将路由器的两个接口LANWAN作为一个桥, LAN端进来数据直接转发到WAN端, 不作任何修改。 同理, WAN端进来的数据直接转发到LAN端。

NAT网关: 所有从WAN出去的数据,将作NAT转换。

 

       3、程序实现架构

       该上网方式的实现分为应用层和内核部分的实现, 应用层实现桥的建立和通告内核某段IP地址需要作透明桥,内核根据应用层通过的IP地址范围判断哪些IP需要走透明桥,那些需要做NAT转换。

      

       4、内核实现方式

(1) 配置编译内核时,需要将802.1d Ethernet Bridging 编译到内核中,

a)         Make menuconfig

b)        选择 Networking options

c)        选择 802.1d Ethernet Bridging

(2) /proc文件系统,建立一个内核和应用层通信的接口,以方便应用层将透明桥的IP地址范围通告给内核

在系统中需要定义几个全局变量,记录透明桥的IP地址范围

unsigned char trans_bridge_enable = 0; //透明桥使能变量

unsigned int trans_bridge_gateway = 0x00; //NAT时的网关地址

unsigned int trans_bridge_start = 0x00; //透明桥的起始IP地址

unsigned int trans_bridge_end = 0x00; //透明桥的结束IP地址

unsigned int trans_bridge_wanip = 0x00; //NAT网关WANIP地址

 

(3) 修改net_rx_action( )函数,添加代码,判断那些IP地址需要走透明桥模式,如果不作透明桥的,则走正常的NAT转换。

判断方式:

a)从LAN端来的数据(LAN端的网络接口是eth0),如果源IP地址在透明桥的IP地址段范围内,并且目的IP不是WAN端的IP地址,那么该数据包将走透明桥。

b)从WAN端来的数据(WAN端的网络接口是eth1),如果目的IP地址在透明桥的IP地址范围内,那么该数据包将走透明桥

实现函数原型如下,蓝色部分为添加的程序(所有有关透明桥的程序,都在TRANS_BRIDGE_SUPPORT 宏的定义范围内)

#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)

#ifdef TRANS_BRIDGE_SUPPORT

if (trans_bridge_enable == 1) //透明桥已经启动(trans_bridge_enable是内核中的全局变量,用户通过对/proc下的透明桥字符设备的write和read操作分别写和读该变量

{

       struct iphdr *iph;

       int found = 0;

       unsigned int saddr;

       unsigned int daddr;

       struct ethhdr *eth; 

       eth = (struct ethhdr *)(skb->data - 14);  //以太网帧的头部长度是14个字节

                                  

                    

       if (ntohs(eth->h_proto) == ETH_P_IP) //处理IP数据包

       {

              iph = (struct iphdr *)(skb->data); //skb->nh.iph;

              saddr = iph->saddr;

              daddr = iph->daddr;

       //判断来自LAN端的数据包是否应走透明桥模式

              if (!memcmp(skb->dev->name, LAN_IFNAME, sizeof(LAN_IFNAME)))

              {

                     if (saddr >= trans_bridge_start && saddr <= trans_bridge_end

                            && daddr != trans_bridge_wanip)

                            found = 1;

              }

              //判断来自WAN端的数据包是否应走透明桥模式

              else if (!memcmp(skb->dev->name, WAN_IFNAME, sizeof(WAN_IFNAME)))

              {

                     if (daddr >= trans_bridge_start && daddr <= trans_bridge_end)

                            found = 2;

              }

              else

                     found = 0;

       }

       else if (ntohs(eth->h_proto) == ETH_P_ARP)//处理ARP数据包

       {

              unsigned char *sha, *tha;

              u32 sip, tip;

              struct arphdr *arp;

              unsigned char *arp_ptr;

              unsigned int subnet;

              unsigned int bridge_subnet;

              bridge_subnet = trans_bridge_start & trans_bridge_netmask;

              subnet = tip & trans_bridge_netmask;

              //取得ARP

              arp = skb->nh.arph;

              arp_ptr= (unsigned char *)(arp+1);

              sha=arp_ptr;

              arp_ptr += skb->dev->addr_len;

              //取得发送 ARP包的源IP地址

              memcpy(&sip, arp_ptr, 4);

              arp_ptr += 4;

              tha=arp_ptr;

              arp_ptr += skb->dev->addr_len;

              //取得该ARP包的目的IP地址

              memcpy(&tip, arp_ptr, 4);

              //判断来自LAN端的数据包是否应走透明桥模式

              if (!memcmp(skb->dev->name, LAN_IFNAME, sizeof(LAN_IFNAME)))

              {

                     if (sip >= trans_bridge_start && sip <= trans_bridge_end

                            && daddr != trans_bridge_wanip)

                                   found = 1;

              }

              //判断来自WAN端的数据包是否应走透明桥模式

              else if (!memcmp(skb->dev->name, WAN_IFNAME, sizeof(WAN_IFNAME)))

              {

                     if (tip >= trans_bridge_start && tip <= trans_bridge_end)

                            found = 2;

              }

              else

                     found = 0;

 

       }

       else

              found = 0;

        //如果found大于0,则走透明桥模式

        if (found > 0)

        {

                     if (skb->dev->br_port != NULL &&

                         br_handle_frame_hook != NULL) {

                            handle_bridge(skb, pt_prev);  //调用实现桥接的内核函数handle_bridge

                            dev_put(rx_dev); //设备引用计数减一

                            continue;

                     }

        }

}

#endif

#endif

(4)编写/proc文件读/写的驱动函数,proc_write_transbridge()和proc_read_transbridge(),是应用层可以分别向/proc文件系统写入和读取与透明桥相关的一些参数。

定义内核中的全局变量:

#ifdef TRANS_BRIDGE_SUPPORT

#define TRANSBRIDGE_FILE  "sfitc/transbridge"

unsigned char trans_bridge_enable = 0;
unsigned int trans_bridge_gateway = 0x00;
unsigned int trans_bridge_start = 0x00;     
unsigned int trans_bridge_end = 0x00;      
unsigned int trans_bridge_netmask = 0x00;
unsigned int trans_bridge_subnet = 0x00;
unsigned int trans_bridge_wanip = 0x00;    //wan ip address

/* ======================================== */
//name:         proc_write_transbridge
//description: write function of file_operations, user can
//                  write this file to transmit data
//                  from user mode to kernel
//parameters: buffer
//return:         if success , return count , or return -1
/* ======================================== */

static int proc_write_transbridge(struct file *file, const char *buffer, unsigned long count, void *data)
{
 char *value;
 char *tmp;

 
 //alloc memory for storing buffer
 value = kmalloc(count + 32, GFP_KERNEL);
 if (value == NULL)
  return -1;
 //copy data of buffer to value
 if (copy_from_user(value, buffer, count))     //将用户层的buffer读取到value中
 {
  printk("Copy ddos config from user failed/n");
  kfree(value);
  return -1;
 }

 trans_bridge_wanip = 0x0;
 trans_bridge_enable = 0;
 trans_bridge_gateway = 0x0;
 trans_bridge_start = 0;
 trans_bridge_end = 0;
 trans_bridge_netmask = 0;
 trans_bridge_subnet = 0;

 //call get_value_by_name function to get value of the variable
 tmp = get_value_by_name("trans_bridge_enable", value, count);
 if (tmp == NULL)
  goto Error;
 //convert string to decimal
 trans_bridge_enable = simple_strtoul(tmp, NULL, 10);
 if (trans_bridge_enable == 1)
 {
  tmp = get_value_by_name("trans_bridge_gateway", value, count);
  if (tmp != NULL)
  {
   trans_bridge_gateway = simple_strtoul(tmp, NULL, 16);
   tmp = NULL;
  }
  tmp = get_value_by_name("trans_bridge_start", value, count);
  if (tmp != NULL)
  {
   trans_bridge_start = simple_strtoul(tmp, NULL, 16);
   tmp = NULL;
  }
  tmp = get_value_by_name("trans_bridge_end", value, count);
  if (tmp != NULL)
  {
   trans_bridge_end = simple_strtoul(tmp, NULL, 16);
   tmp = NULL;
  }
  tmp = get_value_by_name("trans_bridge_netmask", value, count);
  if (tmp != NULL)
  {
   trans_bridge_netmask = simple_strtoul(tmp, NULL, 16);
   tmp = NULL;
  }
  tmp = get_value_by_name("trans_bridge_wanip", value, count);
  if (tmp != NULL)
  {
   trans_bridge_wanip = simple_strtoul(tmp, NULL, 16);
   tmp = NULL;
  }
  trans_bridge_subnet = trans_bridge_netmask & trans_bridge_start;
 }
 
 kfree(value);       //释放空间

 return count;
Error:
 kfree(value);
 return -EFAULT;
 
}

/* ========================================== */
//name:         proc_read_transbridge
//description:  read function of file_operations, user can read them
//parameters: buf
//return:         length of buf
/* ========================================== */


static ssize_t proc_read_transbridge(struct file *filp,
                                 char *buf,size_t count , loff_t *offp)
{
 char line[256];
 int len;

 if (*offp != 0)
  return 0;
 
 len = 0;
 len = sprintf(line, "enable = %d/n gateway = %x/n netmask = %x /n start = %x/n end = %x/n subnet = %x/n",
  trans_bridge_enable, trans_bridge_gateway,
  trans_bridge_netmask, trans_bridge_start,
  trans_bridge_end, trans_bridge_subnet);
 len += sprintf(&line[len], "wanip = %x/n", trans_bridge_wanip);
 line[len] = '/0';
 //copy message to buf for showing them to user
 copy_to_user(buf,line,len );    //将line指向的内容拷贝到用户层的buf中
 *offp = len;
 return len;
}

 

//define variable of file_operations for create file in /proc
static struct file_operations transbridge_fops = {

 read: proc_read_transbridge,
 write: proc_write_transbridge
};
#endif

 

在sfitc_init()中创建该文件:

static int __init  sfitc_init(void)
{

......
#ifdef TRANS_BRIDGE_SUPPORT
 dir = NULL;
 //调用create_proc_entry来创建文件 /proc/sfitc/transbridge
 dir = create_proc_entry(TRANSBRIDGE_FILE, 0777, NULL);
 if (dir != NULL)
 {
  dir->owner = THIS_MODULE;
  dir->proc_fops = &transbridge_fops;
 }
#endif

......

}

 

向内核注册sitic_init模块

module_init(sfitc_init);
module_exit(sfitc_fini);

 

并且,在本.c文件所在的当前目录下的Makefile中添加是将该模块静态编译进内核的选项文件 objs-y = sfitc.c。

 

 

      5、应用层的实现

应用层主要做三个方面的工作, 一是建立桥网络设备,另一个是通过内核建立的proc通信接口,通告透明桥的IP地址范围,以及其他一些参数,第三个是设置WAN网络设备的参数,并在该网络接口配置NAT功能。

(1) 桥网络设备的建立

Brctl addbr br0  //建立桥设备

Brctl addif br0 eth0 //LAN网络接口加入桥

Brctl addif br0 eth1 //WAN网络接口加入桥

Brctl setfd br0 0 //设置桥的转发延迟时间

Brctl stp br0 off  //关闭stp

Ifconfig br0 0.0.0.0 up //启动桥设备

(2) 桥设备的删除

       Ifconfig br0 down

       Brctl delif br0 eth0

       Brctl delif br0 eth1

       Brctl delbr br0

      

(3) 应用层启动透明桥函数:

       start_transbridge()建立桥设备,通告内核启动透明桥和透明桥的IP地址范围,启动和配置WAN网络接口,调用start_wan_done函数建立NAT和防火墙,以及重启动相关的服务

void start_transbridge()
{
 char wan_ipaddr[20];
 char wan_netmask[20];
 char wan_gateway[20];
 char wan_ifname[20];
 char lan_ifname[20];
 char bridge_ifname[20];
 int ret;
 FILE *file;
 char tmpbuff[31];
 struct sysinfo sinfo;

 //get parameters of the wan interface from nvram
 strcpy(wan_ipaddr, nvram_get("wan_bridge_ipaddr"));
 strcpy(wan_netmask, nvram_get("wan_bridge_netmask"));
 strcpy(wan_gateway, nvram_get("wan_bridge_gateway"));
 strcpy(wan_ifname, nvram_get("wan_ifname"));
 strncpy(lan_ifname, nvram_safe_get("lan_ifname"), sizeof(lan_ifname));

 /*set them for status display*/
 nvram_set("wan_ipaddr", wan_ipaddr);
 nvram_set("wan_netmask", wan_netmask);
 nvram_set("wan_gateway", wan_gateway);

 //setup the bridge device and LAN/WAN interface join the bridge device
 strncpy(bridge_ifname, nvram_safe_get("wan_bridge_name"), sizeof(bridge_ifname));
 eval("brctl", "addbr", bridge_ifname);
 eval("brctl", "addif", bridge_ifname,  lan_ifname);
 eval("brctl", "addif", bridge_ifname, wan_ifname);
 eval("brctl", "setfd", bridge_ifname, "0");
 eval("brctl", "stp", bridge_ifname, "off");
 eval("ifconfig", bridge_ifname , "0.0.0.0", "up");

 trans_bridge_up = 1;
 //set the DNS address for domain resolve
 file = fopen(WANDNS_FILE, "w");
 if (file != NULL)
 {
  char dns1[20];
  char dns2[20];

  strncpy(dns1, nvram_safe_get("wan_dns1"), sizeof(dns1));
  strncpy(dns2, nvram_safe_get("wan_dns2"), sizeof(dns2));
  if (inet_addr(dns1) > 0)
   fprintf(file, "nameserver %s/n", dns1);
  if (inet_addr(dns2) > 0)
   fprintf(file, "nameserver %s/n", dns2);
  fclose(file);
 }
 //config the WAN interface
 ret = ifconfig(wan_ifname, IFUP, wan_ipaddr, wan_netmask);
 //Set the default gateway
 route_add(wan_ifname, 0, "0.0.0.0", wan_gateway, "0.0.0.0");
 
 //get the system information
 sysinfo(&sinfo);
 memset((void *)tmpbuff, 0, sizeof(tmpbuff));
 sprintf(tmpbuff, "%d", sinfo.uptime);
 printf("Keep TIME = %d/n", tmpbuff);
 //Set the keep time of the  WAN interface for displaying
 nvram_set("wan_keep_start", tmpbuff);
 //WAN interface had run and connected successfully
 nvram_set("wan_conn_status", "1");
 //call this function to tell the kernel brige had been started
 transbridge_writeproc(1); //在该函数中打开由内核创建/proc/sfitc/transbridge文件,并向其写入数据

 //start relative services
 start_wan_done();
 

transbridge_writeproc()函数实现如下:

/*=============================================*/
// description: transparent bridge enable or disable,transmit the parameters
//                   to kernel like ipaddress
//parameter:  1 for enable , 0 for disable
//return:        none
//author:       wuzhi wang
/*=============================================*/
void transbridge_writeproc(int enable)
{
 int fd;
 char buffer[256];
 int len = 0;
 int count = 0;
 int n;
 unsigned int ip;
 char ipaddr[20];
 
  fd = open("/proc/sfitc/transbridge", O_RDWR);
 if (fd < 0)
 {
  return ;
 }
 len = sprintf(buffer, "trans_bridge_enable=%d", enable);
 buffer[len++] = '/0';
 
 memset((void *)ipaddr, 0, sizeof(ipaddr));
 strncpy(ipaddr, nvram_safe_get("wan_bridge_gateway"), sizeof(ipaddr));
 ip = inet_addr(ipaddr);
 len += sprintf(&buffer[len], "trans_bridge_gateway=%x", ip);
 buffer[len++] = '/0';

 memset((void *)ipaddr, 0, sizeof(ipaddr));
 strncpy(ipaddr, nvram_safe_get("wan_bridge_netmask"), sizeof(ipaddr));
 ip = inet_addr(ipaddr);
 len += sprintf(&buffer[len], "trans_bridge_netmask=%x", ip);
 buffer[len++] = '/0';

 memset((void *)ipaddr, 0, sizeof(ipaddr));
 strncpy(ipaddr, nvram_safe_get("wan_bridge_start"), sizeof(ipaddr));
 ip = inet_addr(ipaddr);
 len += sprintf(&buffer[len], "trans_bridge_start=%x", ip);
 buffer[len++] = '/0';

 memset((void *)ipaddr, 0, sizeof(ipaddr));
 strncpy(ipaddr, nvram_safe_get("wan_bridge_end"), sizeof(ipaddr));
 ip = inet_addr(ipaddr);
 len += sprintf(&buffer[len], "trans_bridge_end=%x", ip);
 buffer[len++] = '/0';

 
 memset((void *)ipaddr, 0, sizeof(ipaddr));
 strncpy(ipaddr, nvram_safe_get("wan_ipaddr"), sizeof(ipaddr));
 ip = inet_addr(ipaddr);
 len += sprintf(&buffer[len], "trans_bridge_wanip=%x", ip);
 buffer[len++] = '/0';

 write(fd, buffer, len);
 close(fd);

(4) 停止透明桥函数:

       stop_transbridge() 删除桥设备,通告内核停止透明桥功能

/*=============================================*/
// description: stop transparent bridge
//parameter:  none
//return:        none
//author:       wuzhi wang
/*=============================================*/
void stop_transbridge(void)
{
 char wan_ifname[20];
 int ret;
 char wan_gateway[20];
 char lan_ifname[20];
 strncpy(lan_ifname, nvram_safe_get("lan_ifname"), sizeof(lan_ifname));
 strcpy(wan_ifname, nvram_get("wan_ifname"));
 strcpy(wan_gateway, nvram_get("wan_gateway"));
 /*set them for status display*/
 nvram_set("wan_ipaddr", "0.0.0.0");
 nvram_set("wan_netmask", "0.0.0.0");
 nvram_set("wan_gateway", "0.0.0.0");

 nvram_set("wan_keep_start", "0");

 //stop  and delete bridge device 
 if (trans_bridge_up == 1)
 {
  eval("ifconfig", nvram_safe_get("wan_bridge_name"), "down");
  eval("brctl", "delif", nvram_safe_get("wan_bridge_name"),  wan_ifname);
  eval("brctl", "delif", nvram_safe_get("wan_bridge_name"),  lan_ifname);
  eval("brctl", "delbr", nvram_safe_get("wan_bridge_name"));
  trans_bridge_up = 0;
 }
 //Set the WAN interface to zero
 ret = ifconfig(wan_ifname, IFUP, "0.0.0.0", "0.0.0.0");
 if (ret != 0)
  printf("Set WAN static IP Error/n");
 //delete the default gateway
 route_del(wan_ifname, 0, "0.0.0.0", wan_gateway, "0.0.0.0");
 //tell the kernel transparent bridge had been stopped
 transbridge_writeproc(0);
}

原创粉丝点击