2
votes

I'm trying to write a small "firewall" using python, scapy and netfilter queue that also handles fragmented packets with no known order of arrival. So i thought of keeping the packets in a dictionary sorted by the IP header ID value and each entry is a list of tuples - The packet offset and the netfilter queue packet object itself (So when the verdict is decided i can either drop or accept). The problem im encountering is after appending a new packet to the list in the dictionary, it looks like the payload of the packet is appended to all of the other packets as well. I've looked it up and i think it got something to do with immutability but couldn't fine any good solution\explanation . I'm new to python and would really hope some guidance. Code:

def update_fragmented_lists(scapy_packet, pkt):
    current_dict = pkt_dict[scapy_packet[IP].id]
    if len(current_dict) < 4:
        current_dict.append((scapy_packet[IP].frag, pkt))
    else:
        for frag, waiting_pkt in current_dict:
            waiting_pkt.drop()
        del(pkt_dict[scapy_packet[IP].id])


def reconstruct_packet(packet_id):
   curr_dict = pkt_dict[packet_id]
   curr_dict = sorted(curr_dict, key=get_key)
   print(curr_dict)
   if IP(curr_dict[-1][1].get_payload()).flags == 1:
       return None
   last_off = 0
   http_req = ""
   for (offset, pkt) in curr_dict:
       scapy_packet = IP(pkt.get_payload())
       if offset*8 == last_off:
           http_req += scapy_packet[Raw].load
           last_off += len(scapy_packet[Raw].load)
       else:
           http_req = None
           break
   return http_req

def handle_packet(pkt):
    scapy_packet = IP(pkt.get_payload())
    packet_id = scapy_packet[IP].id
    if (scapy_packet[IP].flags == 1) or (scapy_packet[IP].flags == 0 and           scapy_packet[IP].frag != 0):
         update_fragmented_lists(scapy_packet, pkt)
         http_req = reconstruct_packet(packet_id)
        if http_req is not None:
            if check_forbidden_suffix(http_req):
                for offset, fragmented_pkt in pkt_dict[packet_id]:
                    fragmented_pkt.accept()
            else:
                for offset, fragmented_pkt in pkt_dict[packet_id]:
                    fragmented_pkt.drop()

 pkt_dict = defaultdict(list)
 nfqueue = NetfilterQueue()
 nfqueue.bind(1, handle_packet)
 try:   
      nfqueue.run()
 except KeyboardInterrupt:
      os.system('iptables -F')
      os.system('iptables -X')

Any help would be really appreciated!

1
what about using scapys built-in defragment()? - tintin
Its not a part of the assignemnt - vito

1 Answers

0
votes

It turns out that the netfilter queue object payload field is a pointer to the buffer in the network driver. So every packet received the buffer keeps appending the payload according to the id. What i did is keep reference to the scapy object i casted of the original payload and got the needed result.