2012-02-18 65 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]