从头开始编写以太网桥
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从头开始编写以太网桥相关的知识,希望对你有一定的参考价值。
题:
如何在代码中创建一个双线程桥(不使用brctl addbr br0
或类似的网络方法,但是编写代码),其中一个从ETH1获取以太网帧并将它们放在ETH2上,而另一个从ETH2获取并放置它们在ETH1上,两个进程不会连续读取彼此的以太网帧?
细节:
我正在尝试创建一个设备来保护系统之间的连接(对于我们无法更新的很多遗留系统),为了做到这一点,我们需要将这个设备放在旧系统的以太网端口上。面向网络的以太网端口。基本上是一个硬件中间人。
我可以使用标准以太网桥和一些花哨的IPTables前后路由规则来完成我需要的几乎所有东西,但我想尝试实现一些执行此操作的代码。我已经在C和Python中尝试过这个,我最终创建了一个无限循环的以太网帧。例如在C中,我可以打开一个套接字到ETH1以便在混杂模式下阅读:
int open_eth_read_socket(char *interface_name) {
int socket_fd;
struct ifreq if_options;
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
// Get socket
socket_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(socket_fd < 0) exit(EXIT_ERROR);
// Zero ifreq
memset(&if_options, 0, sizeof(struct ifreq));
// Put interface_name in
strcpy(if_options.ifr_name, interface_name);
// Set timout
int ret = setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO,
(char *)&timeout,sizeof(timeout));
if(ret < 0) exit(EXIT_ERROR);
// Bind this socket to the specified interface.
ret = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE,
interface_name, strlen(interface_name));
if(ret < 0) exit(EXIT_ERROR);
// Get flags
if(ioctl(socket_fd, SIOCGIFFLAGS, &ifopts) < 0) exit(EXIT_ERROR);
// Add promisc flag
ifopts.ifr_flags |= IFF_PROMISC;
// Set flags
if(ioctl(socket_fd, SIOCSIFFLAGS, &ifopts) < 0) exit(EXIT_ERROR);
return socket_fd;
}
然后我打开一个套接字到ETH2进行写入,我可以在它们之间有程序逻辑。 ETH1-> ETH2的工作方式与ETH2-> ETH1完全相同,只要两个线程(C中的pthread,Python中的线程.Thread)不同时运行即可。如果它们是,那么这个过程就像一座桥梁,所有的地狱都会失败。
ETH1-> ETH2线程从ETH1读取以太网帧并将它们写入ETH2。当ETH2-> ETH1线程从ETH2以太网套接字读取时,它读取另一个线程写入的帧,并开始无限循环。
Python版本看起来很相似,但我尝试过使用pcapy和dpkt,但都不行。我遇到了一个似乎经历过类似问题的Scapy bridge question,但那里的答案似乎并没有解释为什么会发生这种情况或者真的如何修复它,以及Bogdan Paun的代码很好,但是如何解决这个如果一个人想学习
对于那些感兴趣的人,您需要检查以太网帧是否为socket.PACKET_OUTGOING
类型。在Python中:
frame, inf = sock.recvfrom(constants.MAX_SOCKET_READ_BYTES)
if inf[2] == socket.PACKET_OUTGOING:
continue # Or whatever to skip
在c(来自Raw Socket promiscuous mode not sniffing what I write):
unsigned char buf[1500];
struct sockaddr_ll addr;
socklen_t addr_len = sizeof(addr);
n = recvfrom(fd, buf, 2000, 0, (struct sockaddr*)&addr, &addr_len);
if (addr.sll_pkttype == PACKET_OUTGOING) {
// Skip it...
}
读取传出的以太网帧(或者就此而言的IP数据包)不会将它们从堆栈中删除,因此丢弃/忽略它们不会影响传出流量。
以上是关于从头开始编写以太网桥的主要内容,如果未能解决你的问题,请参考以下文章