问题
I'd like to make something like this:
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
So that A
can send packets to C
through B
.
I attempted to build this thing by running a scapy program on B
that would sniff ports e
and f
, and in each case modify the destination IP and MAC address in the packet and then send it along through the other interface. Something like:
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)
However, when I run this thing (full source here) all sorts of crazy things start to happen... Some of the packets get through, and I even get some responses (testing with ping
) but there's some type of feedback loop that's causing a bunch of duplicate packets to get sent...
Any ideas what's going on here? Is it crazy to try to do this?
I'm kind of suspicious that the feedback loops are being caused by the fact that B
is doing some processing of its own on the packets... Is there any way to prevent the OS from processing a packet after I've sniffed it?
回答1:
It is kinda crazy to do this, but it's not a bad way to spend your time. You'll learn a bunch of interesting stuff. However you might want to think about hooking the packets a little lower - I don't think scapy is capable of actually intercepting packets - all libpcap does is set you promisc and let you see everything, so you and the kernel are both getting the same stuff. If you're turning around and resending it, thats likely the cause of your packet storm.
However, you could set up some creative firewall rules that partition each interface off from each-other and hand the packets around that way, or use something like divert sockets to actually thieve the packets away from the kernel so you can have your way with them.
回答2:
IP packets bridging using scapy:
- first make sure you have ip forwarding disabled otherwise duplicate packets will be noticed:
echo "0" > /proc/sys/net/ipv4/ip_forward <br>
- second run the following python/scapy script:
!/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()
On my pc:
# ./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]
来源:https://stackoverflow.com/questions/9337545/writing-an-ethernet-bridge-in-python-with-scapy