| 73 | | class neighborCache: |
|---|
| 74 | | |
|---|
| 75 | | # TODO : add some method to modify default value for timeout |
|---|
| 76 | | # TODO : See what we can do for updating the neighbor cache |
|---|
| 77 | | # when receiving a packet. |
|---|
| 78 | | |
|---|
| 79 | | # Note: internally, our neighbor cache is scapy's arp_cache. This allows us |
|---|
| 80 | | # to have it updated when returning from sr() (a fork is done where a |
|---|
| 81 | | # fork is done and the updated cache returned at the end. |
|---|
| 82 | | |
|---|
| 83 | | def __init__(self): |
|---|
| 84 | | self.neighcache = {} |
|---|
| 85 | | |
|---|
| 86 | | def flush(self, statictoo=True): |
|---|
| 87 | | self.neighcache = {} |
|---|
| 88 | | |
|---|
| 89 | | def __repr__(self): |
|---|
| 90 | | res = [("Peer", "Link layer address", "State")] |
|---|
| 91 | | for addr in self.neighcache.keys(): |
|---|
| 92 | | try: |
|---|
| 93 | | inet_pton(socket.AF_INET6, addr) |
|---|
| 94 | | except: |
|---|
| 95 | | continue |
|---|
| 96 | | cur_entry = self.neighcache[addr] |
|---|
| 97 | | status = "REACHABLE" |
|---|
| 98 | | last_contact = cur_entry[1] |
|---|
| 99 | | if last_contact == 0: |
|---|
| 100 | | status = "STATIC" |
|---|
| 101 | | elif ((time.time() - last_contact) < NEIGHTIMEOUT): |
|---|
| 102 | | status = "REACHABLE" |
|---|
| 103 | | else: |
|---|
| 104 | | status = "STALE" |
|---|
| 105 | | res.append((addr, cur_entry[0], status)) |
|---|
| 106 | | |
|---|
| 107 | | colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, res)) |
|---|
| 108 | | fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth)) |
|---|
| 109 | | res = "\n".join(map(lambda x: fmt % x, res)) |
|---|
| 110 | | return res |
|---|
| 111 | | |
|---|
| 112 | | def addNeighbor(self, ip6, mac, static=False): |
|---|
| 113 | | """ |
|---|
| 114 | | Add a neighbor to the cache. If optional parameter 'static' is not |
|---|
| 115 | | set to True (the default), the entry will expire in 2 minutes. If |
|---|
| 116 | | 'static' is set to True, the entry in the neighbor cache is made |
|---|
| 117 | | static. This is practical in those cases : |
|---|
| 118 | | |
|---|
| 119 | | - peer's address is not advertised to be on-link |
|---|
| 120 | | - peer doed not answer to NS |
|---|
| 121 | | - you don't want to make queries to keep time or be stealthy, ... |
|---|
| 122 | | """ |
|---|
| 123 | | t = 0 |
|---|
| 124 | | if not static: |
|---|
| 125 | | t = time.time() |
|---|
| 126 | | self.neighcache[ip6] = (mac, t) |
|---|
| 127 | | |
|---|
| 128 | | def makeStatic(self, ip6): |
|---|
| 129 | | """ |
|---|
| 130 | | make the entry static in Scapy6 internal neighbor cache for |
|---|
| 131 | | 'ip6' neighbor. |
|---|
| 132 | | """ |
|---|
| 133 | | if self.neighcache.has_key(ip6): |
|---|
| 134 | | mac = self.neighcache[ip6][0] |
|---|
| 135 | | self.neighcache[ip6] = (mac, 0) |
|---|
| 136 | | else: |
|---|
| 137 | | warning("Unable to make neighbor cache entry for %s static. It does not exist." % ip6) |
|---|
| 138 | | |
|---|
| 139 | | def removeStatic(self, ip6): |
|---|
| 140 | | """ |
|---|
| 141 | | remove the static status for 'ip6' entry in Scapy6 internal |
|---|
| 142 | | neighbor cache. |
|---|
| 143 | | """ |
|---|
| 144 | | if self.neighcache.has_key(ip6): |
|---|
| 145 | | mac = self.neighcache[ip6][0] |
|---|
| 146 | | self.neighcache[ip6] = (mac, time.time()) |
|---|
| 147 | | else: |
|---|
| 148 | | warning("Unable to make neighbor cache entry for %s static. It does not exist." % ip6) |
|---|
| 149 | | |
|---|
| 150 | | def get(self, ip6, chainCC=0): |
|---|
| 151 | | """ |
|---|
| 152 | | Returns the link layer address to use for IPv6 traffic to 'ip6' address. |
|---|
| 153 | | If searched IPv6 address is multicast, then, ethernet address is computed. |
|---|
| 154 | | If that's not the case, Scapy6 routing table is used to find next hop for |
|---|
| 155 | | provided address. If one is found, cache is searched. If a valid (REACHABLE |
|---|
| 156 | | or STATIC) entry exist, content is returned. Else, resolution is performed |
|---|
| 157 | | by sending a Neighbor Solicitation. |
|---|
| 158 | | |
|---|
| 159 | | In all cases, if lookup fails, None is returned. |
|---|
| 160 | | """ |
|---|
| 161 | | |
|---|
| 162 | | if in6_ismaddr(ip6): # Multicast |
|---|
| 163 | | mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6)) |
|---|
| 164 | | return mac |
|---|
| 165 | | |
|---|
| 166 | | iff,a,nh = conf.route6.route(ip6, dev=conf.iface6) |
|---|
| 167 | | |
|---|
| 168 | | if iff == LOOPBACK_NAME: |
|---|
| 169 | | return "ff:ff:ff:ff:ff:ff" |
|---|
| 170 | | |
|---|
| 171 | | if nh != '::': |
|---|
| 172 | | ip6 = nh # Found next hop |
|---|
| 173 | | |
|---|
| 174 | | if self.neighcache.has_key(ip6): # search the cache |
|---|
| 175 | | mac, timeout = self.neighcache[ip6] |
|---|
| 176 | | if timeout and (time.time()-timeout < NEIGHTIMEOUT): |
|---|
| 177 | | return mac |
|---|
| 178 | | |
|---|
| 179 | | res = neighsol(ip6, a, iff, chainCC=chainCC) |
|---|
| 180 | | |
|---|
| 181 | | if res is not None: |
|---|
| 182 | | mac = res.src |
|---|
| 183 | | self.neighcache[ip6] = (mac,time.time()) |
|---|
| 184 | | return mac |
|---|
| 185 | | |
|---|
| 186 | | return None |
|---|
| 187 | | |
|---|
| 188 | | ip6_neigh_cache = neighborCache() |
|---|
| 189 | | |
|---|