Source code for snare.forward

# coding: utf-8
from .sniffer import Module
from . import net
import scapy.all as scapy
import enum
import logging
import base64

logger = logging.getLogger(__name__)

[docs]class ForwarderModule(Module): """ ForwarderModule forwards packets received by the sniffer and in the ARP cache, after applying a filter. This serves to forward on packets intercepted, such as by ARP poisoning, onto the intended hosts. The filter function should return one packet, a list of packets, or None. Returned packets will be sent after having their eithernet addresses set. """ def __init__(self, arpcache, filter=None, iface=None, hwaddr=None, ipaddr=None, routes=None): self.arpcache = arpcache self.filter = filter self.iface = iface self.hwaddr = hwaddr self.ipaddr = ipaddr self.routes = routes self.sniffer = None
[docs] def start(self, sniffer): self.sniffer = sniffer if self.iface is None: self.iface = sniffer.iface if self.hwaddr is None: self.hwaddr = str(net.ifhwaddr(self.iface)) if self.ipaddr is None: self.ipaddr = str(net.ifaddr(self.iface)) if self.routes is None: self.routes = net.routes()
[docs] def nexthop(self, ip): """Returns the MAC address for the next hop towards the given IP""" if ip in self.arpcache: return self.arpcache[ip] default = None via = None for route in self.routes: if route.default(): default = route elif ip in route.dst: via = route.via break if via is None and default is not None: via = default.via if via is not None: return self.arpcache.get(str(via), None) return None
[docs] def process(self, pkt): # Drop packets that don't include Ethernet and IP. if any(layer not in pkt for layer in (scapy.IP, scapy.Ether)): return # Drop packets for whom we are the intended L3 recipient. if pkt[scapy.IP].dst == self.ipaddr: return # Drop packets for which we are the L2 source or we are not the destination. if pkt[scapy.Ether].dst != self.hwaddr or pkt[scapy.Ether].src == self.hwaddr: return # Determine the MAC address for the local destination or next hop. hwdst = self.nexthop(pkt[scapy.IP].dst) if hwdst is None: logger.debug("Dropping packet %s > %s: next hop unknown", pkt[scapy.IP].src, pkt[scapy.IP].dst) return pkt = pkt.copy() pkt[scapy.Ether].dst = hwdst src, dst = pkt[scapy.IP].src, pkt[scapy.IP].dst # After having patched the dst MAC, but before patching the src, apply the filter if self.filter is not None: pkt = self.filter(pkt) if pkt is None: logger.debug("Dropping packet packet %s > %s: filter returned None", src, dst) return if pkt is not None: pkt[scapy.Ether].src = self.hwaddr clear_chksums(pkt) # TODO: investigate why this is needed, because it should not be (scapy bug?). scapy.sendp(pkt, iface=self.iface) if logger.isEnabledFor(logging.DEBUG): logger.debug("Forwarded %s > %s to %s: %s", pkt[scapy.IP].src, pkt[scapy.IP].dst, pkt[scapy.Ether].dst, base64.b64encode(scapy.raw(pkt)).decode())
[docs]def clear_chksums(pkt): """ Deletes IP, UDP, and TCP checksums from pkt, such that they are recalculated by Scapy :param pkt: Packet for which to clear checksums. :type pkt: scapy.packet.Packet """ for layer in (scapy.IP, scapy.UDP, scapy.TCP): if layer in pkt: del pkt[layer].chksum