【问题标题】:writing an ethernet bridge in python with scapy用 scapy 在 python 中编写以太网桥
【发布时间】:2012-02-18 01:06:12
【问题描述】:

我想做这样的事情:

            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可以通过BC发送数据包。

我试图通过在B 上运行scapy 程序来构建这个东西,该程序将嗅探端口ef,并在每种情况下修改数据包中的目标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 正在对数据包进行自己的一些处理这一事实引起的......有没有办法阻止操作系统在我处理数据包之后闻过吗?

【问题讨论】:

    标签: python network-programming packet-sniffers scapy


    【解决方案1】:

    使用 scapy 桥接 IP 数据包:

    1. 首先确保您已禁用 ip 转发,否则会发现重复的数据包:

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

    1. 第二次运行以下 python/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]
    

    【讨论】:

      【解决方案2】:

      这样做有点疯狂,但这并不是一种消磨时间的坏方法。你会学到很多有趣的东西。但是,您可能想考虑将数据包挂低一点 - 我不认为 scapy 能够真正拦截数据包 - libpcap 所做的一切都是让您混杂并让您看到所有内容,因此您和内核都得到相同的东西。如果您转身重新发送它,那可能是您的数据包风暴的原因。

      但是,您可以设置一些创造性的防火墙规则,将每个接口彼此隔开并以这种方式传递数据包,或者使用divert sockets 之类的东西将数据包从内核中偷走,这样您就可以拥有以你的方式与他们相处。

      【讨论】:

      • 这个力道很强。
      • 转移套接字在任何现代 linux 内核中都不存在;但是,您可能会考虑类似nfqueue
      猜你喜欢
      • 2018-06-11
      • 1970-01-01
      • 2020-12-07
      • 2017-10-09
      • 1970-01-01
      • 2013-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多