написание моста Ethernet в Python с Scapy - PullRequest
3 голосов
/ 18 февраля 2012

Я хотел бы сделать что-то вроде этого:

            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 мог отправлять пакеты на C через B.

Я попытался построить этозапустив программу scapy на B, которая прослушивает порты 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)

Однако, когда я запускаю эту вещь (полный исходный код здесь ), начинают происходить всякие безумные вещи ... Некоторые пакеты проходят, и ядаже получить некоторые ответы (тестирование с ping), но есть какой-то тип обратной связи, который вызывает отправку множества дублирующих пакетов ...

Есть идеи, что здесь происходит?Это безумие, пытаясь сделать это?

Я подозреваю, что петли обратной связи вызваны тем фактом, что B выполняет некоторую собственную обработку пакетов ...Любой способ предотвратить обработку пакета ОС после того, как я его прослушал?

Ответы [ 2 ]

3 голосов
/ 29 февраля 2016

IP-пакетов с использованием scapy:

  1. сначала убедитесь, что IP-переадресация отключена, в противном случае будут замечены повторяющиеся пакеты:

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

  1. второй запуск следующего скрипта python / scapy:

! / USR / бен / 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]
2 голосов
/ 18 февраля 2012

Это довольно безумно, но это неплохой способ провести время.Вы узнаете кучу интересных вещей.Однако вы можете подумать о подключении пакетов чуть ниже - я не думаю, что scapy способен на самом деле перехватывать пакеты - все, что делает libpcap - это настраивает вас на обещание и позволяет вам видеть все, так что вы и ядро ​​получаете одинаковоевещи.Если вы переворачиваете и пересылаете его, это, вероятно, является причиной вашего пакета шторма.

Однако вы можете настроить некоторые творческие правила брандмауэра, которые отделяют каждый интерфейс друг от друга и передавать пакеты таким образом, или использовать что-то вроде переадресация сокетов для фактического кражи пакетовот ядра, так что вы можете иметь с ними свой путь.

...