From 1a67eb8a9d7e0116d957f52f39f06a838a6b87d3 Mon Sep 17 00:00:00 2001 From: Phil <phil@secdev.org> Date: Mon, 28 Jul 2008 16:13:00 +0200 Subject: [PATCH] Created a NetCache and Neighbour object to abstract link layer address resolution --- scapy/arch.py | 52 ---------------------------- scapy/config.py | 64 ++++++++++++++++++++++++++++++++-- scapy/layers/inet.py | 5 ++- scapy/layers/l2.py | 82 +++++++++++++++++++++++++++++++++++--------- scapy/route.py | 3 +- scapy/sendrecv.py | 6 ++-- 6 files changed, 136 insertions(+), 76 deletions(-) diff --git a/scapy/arch.py b/scapy/arch.py index f37385c6..8730534e 100644 --- a/scapy/arch.py +++ b/scapy/arch.py @@ -371,55 +371,3 @@ def get_if_hwaddr(iff): -##################### -## ARP cache stuff ## -##################### - -ARPTIMEOUT=120 - - -if 0 and DNET: ## XXX Can't use this because it does not resolve IPs not in cache - dnet_arp_object = dnet.arp() - def getmacbyip(ip, chainCC=0): - tmp = map(ord, inet_aton(ip)) - if (tmp[0] & 0xf0) == 0xe0: # mcast @ - return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) - iff,a,gw = config.conf.route.route(ip) - if iff == "lo": - return "ff:ff:ff:ff:ff:ff" - if gw != "0.0.0.0": - ip = gw - res = dnet_arp_object.get(dnet.addr(ip)) - if res is None: - return None - else: - return res.ntoa() -else: - def getmacbyip(ip, chainCC=0): - tmp = map(ord, inet_aton(ip)) - if (tmp[0] & 0xf0) == 0xe0: # mcast @ - return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) - iff,a,gw = config.conf.route.route(ip) - if ( (iff == "lo") or (ip == config.conf.route.get_if_bcast(iff)) ): - return "ff:ff:ff:ff:ff:ff" - if gw != "0.0.0.0": - ip = gw - - if config.conf.arp_cache.has_key(ip): - mac, timeout = config.conf.arp_cache[ip] - if not timeout or (time.time()-timeout < ARPTIMEOUT): - return mac - - res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), - type=ETH_P_ARP, - iface = iff, - timeout=2, - verbose=0, - chainCC=chainCC, - nofilter=1) - if res is not None: - mac = res.payload.hwsrc - config.conf.arp_cache[ip] = (mac,time.time()) - return mac - return None - diff --git a/scapy/config.py b/scapy/config.py index 73982fa1..e3e17fc5 100644 --- a/scapy/config.py +++ b/scapy/config.py @@ -1,4 +1,4 @@ -import os +import os,time from data import * import base_classes import arch,themes @@ -104,6 +104,65 @@ class LayersList(list): return "\n".join(s) def register(self, layer): self.append(layer) + + +class CacheInstance(dict): + def __init__(self, name="noname", timeout=None): + self.timeout = timeout + self.name = name + self._timetable = {} + def __getitem__(self, item): + val = dict.__getitem__(self,item) + if self.timeout is not None: + t = self._timetable[item] + if time.time()-t > self.timeout: + raise KeyError(item) + return val + def get(self, item, default=None): + try: + return self[item] + except KeyError: + return default + def __setitem__(self, item, v): + self._timetable[item] = time.time() + dict.__setitem__(self, item,v) + def __repr__(self): + if self.timeout is None: + n = len(self) + else: + n = 0 + t0 = time.time() + for t,v in self.itervalues(): + if t0-t <= self.timeout: + n += 1 + return "%s: %i valid items. Timeout=%rs" % (self.name, n, self.timeout) + + + +class NetCache: + def __init__(self): + self._caches_list = [] + + + def add_cache(self, cache): + self._caches_list.append(cache) + setattr(self,cache.name,cache) + def new_cache(self, name, timeout=None): + c = CacheInstance(name=name, timeout=timeout) + self.add_cache(c) + def __delattr__(self, attr): + raise AttributeError("Cannot delete attributes") + def update(self, other): + for co in other._caches_list: + if hasattr(self, co.name): + getattr(self,co.name).update(co) + else: + self.add_cache(co.copy()) + def flush(self): + for c in self._caches_list: + c.flush() + def __repr__(self): + return "\n".join(repr(c) for c in self._caches_list) @@ -173,7 +232,8 @@ extensions_paths: path or list of paths where extensions are to be looked for manufdb = load_manuf("/usr/share/wireshark/wireshark/manuf") stats_classic_protocols = [] stats_dot11_protocols = [] - arp_cache = {} + netcache = NetCache() + conf=Conf() diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py index 0acf9096..2a1ddaa7 100644 --- a/scapy/layers/inet.py +++ b/scapy/layers/inet.py @@ -6,7 +6,7 @@ from scapy.fields import * from scapy.packet import * from scapy.volatile import * from scapy.config import conf -from scapy.sendrecv import sr,sr1 +from scapy.sendrecv import sr,sr1,srp1 from scapy.plist import PacketList,SndRcvList import scapy.as_resolvers @@ -504,6 +504,9 @@ conf.l3types.register(ETH_P_IP, IP) conf.l3types.register_num2layer(ETH_P_ALL, IP) +conf.neighbor.register_l3(Ether, IP, lambda l2,l3: getmacbyip(l3.dst)) + + ################### ## Fragmentation ## ################### diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py index 6fd8ab21..2f7bb309 100644 --- a/scapy/layers/l2.py +++ b/scapy/layers/l2.py @@ -6,6 +6,61 @@ from scapy.plist import SndRcvList from scapy.fields import * from scapy.sendrecv import srp,srp1 + + + +################# +## Tools ## +################# + + +class Neighbor: + def __init__(self): + self.resolvers = {} + + def register_l3(self, l2, l3, resolve_method): + self.resolvers[l2,l3]=resolve_method + + def resolve(self, l2inst, l3inst): + return self.resolvers[l2inst.__class__,l3inst.__class__](l2inst,l3inst) + + def __repr__(self): + return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2,l3 in self.resolvers) + +conf.neighbor = Neighbor() + +conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s + + +def getmacbyip(ip, chainCC=0): + tmp = map(ord, inet_aton(ip)) + if (tmp[0] & 0xf0) == 0xe0: # mcast @ + return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) + iff,a,gw = config.conf.route.route(ip) + if ( (iff == "lo") or (ip == config.conf.route.get_if_bcast(iff)) ): + return "ff:ff:ff:ff:ff:ff" + if gw != "0.0.0.0": + ip = gw + + mac = config.conf.netcache.arp_cache.get(ip) + if mac: + return mac + + res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), + type=ETH_P_ARP, + iface = iff, + timeout=2, + verbose=0, + chainCC=chainCC, + nofilter=1) + if res is not None: + mac = res.payload.hwsrc + config.conf.netcache.arp_cache[ip] = mac + return mac + return None + + + ### Fields class DestMACField(MACField): @@ -13,23 +68,10 @@ class DestMACField(MACField): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: - dstip = None - if isinstance(pkt.payload, IPv6): - dstip = pkt.payload.dst - elif isinstance(pkt.payload, IP): - dstip = pkt.payload.dst - elif isinstance(pkt.payload, ARP): - dstip = pkt.payload.pdst - if isinstance(dstip, Gen): - dstip = dstip.__iter__().next() - if dstip is not None: - if isinstance(pkt.payload, IPv6): - x = getmacbyip6(dstip, chainCC=1) - else: - x = getmacbyip(dstip, chainCC=1) + x = conf.neighbor.resolve(pkt,pkt.payload) if x is None: x = "ff:ff:ff:ff:ff:ff" - warning("Mac address to reach %s not found\n"%dstip) + warning("Mac address to reach destination not found") return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) @@ -48,6 +90,7 @@ class SourceMACField(MACField): dstip = pkt.payload.pdst if isinstance(dstip, Gen): dstip = dstip.__iter__().next() + if dstip is not None: if isinstance(pkt.payload, IPv6): iff,a,nh = conf.route6.route(dstip) @@ -296,7 +339,7 @@ class ARP(Packet): else: return "ARP %ARP.op% %ARP.psrc% > %ARP.pdst%" - +conf.neighbor.register_l3(Ether, ARP, lambda l2,l3: getmacbyip(l3.pdst)) class GRE(Packet): name = "GRE" @@ -352,6 +395,13 @@ conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedL conf.l3types.register(ETH_P_ARP, ARP) + + + +### Technics + + + def arpcachepoison(target, victim, interval=60): """Poison target's cache with (your MAC,victim's IP) couple arpcachepoison(target, victim, [interval=60]) -> None diff --git a/scapy/route.py b/scapy/route.py index 8ee2a966..1bf0b10a 100644 --- a/scapy/route.py +++ b/scapy/route.py @@ -85,8 +85,7 @@ class Route: self.routes[i] = (the_net,the_msk,gw,iface,the_addr) else: self.routes[i] = (net,msk,gw,iface,the_addr) - for i in conf.arp_cache.keys(): - del(conf.arp_cache[i]) + conf.netcache.flush() diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index 85b2a152..02bf3858 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -92,7 +92,7 @@ def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, m try: os.setpgrp() # Chance process group to avoid ctrl-C sent_times = [p.sent_time for p in all_stimuli if p.sent_time] - cPickle.dump( (conf.arp_cache,sent_times), wrpipe ) + cPickle.dump( (conf.netcache,sent_times), wrpipe ) wrpipe.close() except: pass @@ -158,11 +158,11 @@ def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, m raise finally: try: - ac,sent_times = cPickle.load(rdpipe) + nc,sent_times = cPickle.load(rdpipe) except EOFError: warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid()) else: - conf.arp_cache.update(ac) + conf.netcache.update(nc) for p,t in zip(all_stimuli, sent_times): p.sent_time = t os.waitpid(pid,0) -- GitLab