Question

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 Acan 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?

Was it helpful?

Solution

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.

OTHER TIPS

IP packets bridging using scapy:

  1. first make sure you have ip forwarding disabled otherwise duplicate packets will be noticed:

echo "0" > /proc/sys/net/ipv4/ip_forward <br>

  1. 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]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top