2012-02-18 73 views
3

我想做出这样的事情:用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将嗅探端口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正在对数据包做一些自己的处理的事实引起的......有没有什么办法可以防止操作系统在处理完数据包之后处理数据包?我嗅过它吗?

回答

2

做这件事有点疯狂,但这不是一个花时间的坏方法。你会学到一堆有趣的东西。但是你可能想要考虑把数据包连接到一个较低的位置 - 我不认为scapy能够实际拦截数据包 - 所有的libpcap都会设置你的promisc并让你看到所有的东西,所以你和内核都变得相同了东东。如果您正在转向并重新发送它,那可能是您的数据包风暴的原因。但是,您可以设置一些创造性的防火墙规则,将每个接口彼此分开并以这种方式传递数据包,或者使用类似divert sockets的东西来实际将数据包从内核中分离出来,这样您就可以拥有自己的与他们的方式。

+0

哦,我,疏导插座让我想了喜悦哭泣。 – mgalgs 2012-02-18 06:21:36

+2

这个力量很强。 – synthesizerpatel 2012-02-18 06:30:17

+1

任何现代linux内核都不存在divert套接字;然而,你可能会考虑像['nfqueue'](http://stackoverflow.com/questions/6757914/unable-to-make-nfqueue-bindings) – 2012-04-16 21:45:12

3

IP包桥接使用Scapy的:

  1. 首先要确保你有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]