透明桥接和NAT共存的实现
来源:互联网 发布:网络电视韩国高清25 编辑:程序博客网 时间:2024/06/05 04:20
1、什么是透明桥
透明桥接主要用于以太网环境中。透明网桥不更改数据帧的内容,也不作为数据帧的源或目的地,它使得由它连接起来的各个网段看起来像是在一条电缆上。若是收到一个帧,而其中的MAC地址不在自己的桥接表里,就会将这个帧扩散到所有的接口,桥接还会扩散所有的广播包,占用网络的有效带宽,造成网络的堵塞。使用“生成树协议”(Spanning Tree Protocol)可以很好地避免桥接引起的广播风暴。
但本项目的网关设备只有一个LAN,不存在以上对LAN口的选择,不存在广播风暴。
2、系统结构
透明桥和NAT共存的网络拓扑结构图如下:
功能描述:
透明桥:将路由器的两个接口LAN和WAN作为一个桥, 从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网关WAN端IP地址
(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
/* ========================================== */
if (*offp != 0) //define variable of file_operations for create file in /proc read: proc_read_transbridge, 在sfitc_init()中创建该文件: static int __init sfitc_init(void)
static ssize_t proc_read_transbridge(struct file *filp,
char *buf,size_t count , loff_t *offp)
{
char line[256];
int len;
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;
}
static struct file_operations transbridge_fops = {
write: proc_write_transbridge
};
#endif
{
......
#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);
}
- 透明桥接和NAT共存的实现
- virtualbox 虚拟机静态IP设置(用于Host Only和NAT共存的方式)
- virtualbox 虚拟机静态IP设置(用于Host Only和NAT共存的方式) .
- virtualbox 虚拟机静态IP设置(用于Host Only和NAT共存的方式)
- iptables NAT+squid实现透明代理
- 桥接和NAT的区别
- 虚拟机的桥接和nat
- UDP穿越NAT的原理和实现
- 系统服务和普通FORMS程序共存一体的实现
- UIButton不能实现image和title共存的问题
- 如何实现servlet和struts的友好共存
- 虚拟机的桥接模式和NAT模式的区别
- 虚拟机的桥接模式和NAT模式的区别
- iptables+NAT+squid 透明代理上网实现方案
- iptables+NAT+squid 透明代理上网实现方案
- 1、win10下连接本地系统上的Linux操作系统(分别以Nat方式和桥接模式实现)
- vmware关于桥接和NAT的区别
- 虚拟机里面桥接和NAT连接的区别
- 指针学习笔记
- 没有对立的技术,只有对立的心态
- C6TBT2
- QTableview 用法
- API 测试(3)
- 透明桥接和NAT共存的实现
- 从浙江卫视美女主播猝死看白领十大危险生活方式
- 新东方李老师的734条高频词组笔记(怕以后丢了,就剽窃了,实在是太有用了!~)
- JDBC的fetchsize和maxrows
- gdb多进程调试示例
- JAVA开发者最常去的20个英文网站
- Linux文件权限[转载自:http://dengyin2000.javaeye.com/blog/444175]
- linux中的截图命令
- C# Programming tips (2) Generics