用Python写的以太网桥Scapy的
问题描述:
我想做出这样的事情:用Python写的以太网桥Scapy的
10.1.1.0/24 10.1.2.0/24
+------------+ +------------+ +------------+
| | | | | |
| | | | | |
| A d +-------+ e B f +-------+ g C |
| | | | | |
| | | | | |
+------------+ +------------+ +------------+
d e f g
10.1.1.1 10.1.1.2 10.1.2.1 10.1.2.2
这样A
可以通过B
数据包发送给C
。
我试图通过运行一个B
程序scapy将嗅探端口e
和f
,并在每种情况下修改数据包中的目的IP和MAC地址,然后沿着通过其他接口发送它来建立这个东西。喜欢的东西:
my_macs = [get_if_hwaddr(i) for i in get_if_list()]
pktcnt = 0
dest_mac_address = discover_mac_for_ip(dest_ip) #
output_mac = get_if_hwaddr(output_interface)
def process_packet(pkt):
# ignore packets that were sent from one of our own interfaces
if pkt[Ether].src in my_macs:
return
pktcnt += 1
p = pkt.copy()
# if this packet has an IP layer, change the dst field
# to our final destination
if IP in p:
p[IP].dst = dest_ip
# if this packet has an ethernet layer, change the dst field
# to our final destination. We have to worry about this since
# we're using sendp (rather than send) to send the packet. We
# also don't fiddle with it if it's a broadcast address.
if Ether in p \
and p[Ether].dst != 'ff:ff:ff:ff:ff:ff':
p[Ether].dst = dest_mac_address
p[Ether].src = output_mac
# use sendp to avoid ARP'ing and stuff
sendp(p, iface=output_interface)
sniff(iface=input_interface, prn=process_packet)
然而,当我跑这件事(完整的源here)疯狂的事情各种各样开始发生......一些包的打通了,我甚至得到一些反应(与ping
测试)但是有一些类型的反馈循环导致一堆重复的数据包被发送...
任何想法这里发生了什么?试图这样做是疯了吗?
我有点怀疑反馈循环是由于B
正在对数据包做一些自己的处理的事实引起的......有没有什么办法可以防止操作系统在处理完数据包之后处理数据包?我嗅过它吗?
答
做这件事有点疯狂,但这不是一个花时间的坏方法。你会学到一堆有趣的东西。但是你可能想要考虑把数据包连接到一个较低的位置 - 我不认为scapy能够实际拦截数据包 - 所有的libpcap都会设置你的promisc并让你看到所有的东西,所以你和内核都变得相同了东东。如果您正在转向并重新发送它,那可能是您的数据包风暴的原因。但是,您可以设置一些创造性的防火墙规则,将每个接口彼此分开并以这种方式传递数据包,或者使用类似divert sockets的东西来实际将数据包从内核中分离出来,这样您就可以拥有自己的与他们的方式。
答
IP包桥接使用Scapy的:
- 首先要确保你有IP转发禁用,否则重复的数据包将被注意到:
echo "0" > /proc/sys/net/ipv4/ip_forward <br>
- 第二次运行以下蟒蛇/ Scapy的脚本:
在/ usr/bin中/ python2
from optparse import OptionParser
from scapy.all import *
from threading import Thread
from struct import pack, unpack
from time import sleep
def sp_byte(val):
return pack("<B", val)
def su_nint(str):
return unpack(">I", str)[0]
def ipn2num(ipn):
"""ipn(etwork) is BE dotted string ip address
"""
if ipn.count(".") != 3:
print("ipn2num warning: string < %s > is not proper dotted IP address" % ipn)
return su_nint("".join([sp_byte(int(p)) for p in ipn.strip().split(".")]))
def get_route_if(iface):
try:
return [route for route in conf.route.routes if route[3] == iface and route[2] == "0.0.0.0"][0]
except IndexError:
print("Interface '%s' has no ip address configured or link is down?" % (iface));
return None;
class PacketCapture(Thread):
def __init__(self, net, nm, recv_iface, send_iface):
Thread.__init__(self)
self.net = net
self.netmask = nm
self.recv_iface = recv_iface
self.send_iface = send_iface
self.recv_mac = get_if_hwaddr(recv_iface)
self.send_mac = get_if_hwaddr(send_iface)
self.filter = "ether dst %s and ip" % self.recv_mac
self.arp_cache = []
self.name = "PacketCapture(%s on %s)" % (self.name, self.recv_iface)
self.fw_count = 0
def run(self):
print("%s: waiting packets (%s) on interface %s" % (self.name, self.filter, self.recv_iface))
sniff(count = 0, prn = self.process, store = 0, filter = self.filter, iface = self.recv_iface)
def process(self, pkt):
# only bridge IP packets
if pkt.haslayer(Ether) and pkt.haslayer(IP):
dst_n = ipn2num(pkt[IP].dst)
if dst_n & self.netmask != self.net:
# don't forward if the destination ip address
# doesn't match the destination network address
return
# update layer 2 addresses
rmac = self.get_remote_mac(pkt[IP].dst)
if rmac == None:
print("%s: packet not forwarded %s %s -) %s %s" % (self.name, pkt[Ether].src, pkt[IP].src, pkt[Ether].dst, pkt[IP].dst))
return
pkt[Ether].src = self.send_mac
pkt[Ether].dst = rmac
#print("%s: forwarding %s %s -> %s %s" % (self.name, pkt[Ether].src, pkt[IP].src, pkt[Ether].dst, pkt[IP].dst))
sendp(pkt, iface = self.send_iface)
self.fw_count += 1
def get_remote_mac(self, ip):
mac = ""
for m in self.arp_cache:
if m["ip"] == ip and m["mac"]:
return m["mac"]
mac = getmacbyip(ip)
if mac == None:
print("%s: Could not resolve mac address for destination ip address %s" % (self.name, ip))
else:
self.arp_cache.append({"ip": ip, "mac": mac})
return mac
def stop(self):
Thread._Thread__stop(self)
print("%s stopped" % self.name)
if __name__ == "__main__":
parser = OptionParser(description = "Bridge packets", prog = "brscapy", usage = "Usage: brscapy -l <intf> (--left= <intf>) -r <inft> (--right=<intf>)")
parser.add_option("-l", "--left", action = "store", dest = "left", default = None, choices = get_if_list(), help = "Left side network interface of the bridge")
parser.add_option("-r", "--right", action = "store", dest = "right", default = None, choices = get_if_list(), help = "Right side network interface of the bridge")
args, opts = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
lif = args.left
rif = args.right
lroute = get_route_if(lif)
rroute = get_route_if(rif)
if (lroute == None or rroute == None):
print("Invalid ip addressing on given interfaces");
exit(1)
if (len(lroute) != 5 or len(rroute) != 5):
print("Invalid scapy routes")
exit(1)
conf.verb = 0
lthread = PacketCapture(rroute[0], rroute[1], lif, rif)
rthread = PacketCapture(lroute[0], lroute[1], rif, lif)
lthread.start()
rthread.start()
try:
while True:
sys.stdout.write("FORWARD count: [%s -> %s %d] [%s <- %s %d]\r" % (lif, rif, lthread.fw_count, lif, rif, rthread.fw_count))
sys.stdout.flush()
sleep(0.1)
except KeyboardInterrupt:
pass
lthread.stop()
rthread.stop()
lthread.join()
rthread.join()
在我的电脑:
# ./brscapy.py --help
Usage: brscapy -l <intf> (--left= <intf>) -r <inft> (--right=<intf>)
Bridge packets
Options:
-h, --help show this help message and exit
-l LEFT, --left=LEFT Left side network interface of the bridge
-r RIGHT, --right=RIGHT
Right side network interface of the bridge
# ./brscapy.py -l e0 -r e2
PacketCapture(Thread-1 on e0): waiting packets (ether dst 00:16:41:ea:ff:dc and ip) on interface e0
PacketCapture(Thread-2 on e2): waiting packets (ether dst 00:0d:88:cc:ed:15 and ip) on interface e2
FORWARD count: [e0 -> e2 5] [e0 <- e2 5]
哦,我,疏导插座让我想了喜悦哭泣。 – mgalgs 2012-02-18 06:21:36
这个力量很强。 – synthesizerpatel 2012-02-18 06:30:17
任何现代linux内核都不存在divert套接字;然而,你可能会考虑像['nfqueue'](http://*.com/questions/6757914/unable-to-make-nfqueue-bindings) – 2012-04-16 21:45:12