From b250f70e33b2d65a441732a27d7b22aed76b73e3 Mon Sep 17 00:00:00 2001
From: dowright <dowright@nationofnobodies.com>
Date: Fri, 27 Aug 2021 12:33:39 -0700
Subject: [PATCH] work on new packet

---
 new_packet.pxd |   2 +-
 new_packet.pyx |  83 ++++++++++++--------------------
 nfq_test.py    | 127 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 158 insertions(+), 54 deletions(-)
 create mode 100644 nfq_test.py

diff --git a/new_packet.pxd b/new_packet.pxd
index 7204354..f00fcbd 100644
--- a/new_packet.pxd
+++ b/new_packet.pxd
@@ -199,7 +199,7 @@ cdef class CPacket:
     cdef Py_ssize_t data_len
     cdef readonly unsigned char *data
     cdef timeval timestamp
-    cdef u_int8_t hw_addr[8]
+    cdef u_int8_t hw_addr[6]
 
     cdef u_int32_t parse(self, nfq_q_handle *qh, nfq_data *nfa) nogil
     cdef void _parse(self) nogil
diff --git a/new_packet.pyx b/new_packet.pyx
index 00ca8e7..0482b3a 100644
--- a/new_packet.pyx
+++ b/new_packet.pyx
@@ -26,7 +26,7 @@ def set_user_callback(ref):
 
     user_callback = ref
 
-cdef int nf_callback(nfq_q_handle *qh, nfgenmsg *nfmsg, nfq_data *nfa, void *data) with gil:
+cdef int nf_callback(nfq_q_handle *qh, nfgenmsg *nfmsg, nfq_data *nfa, void *data):
 
     cdef u_int32_t mark
 
@@ -145,52 +145,6 @@ cdef class CPacket:
 
         return self.timestamp.tv_sec + (self.timestamp.tv_usec / 1000000.0)
 
-    cdef u_int8_t get_inint(self, bint name=False):
-        '''Returns index of inbound interface of packet. If the packet sourced from localhost or the input
-        interface is not known, 0 will be returned.
-        '''
-        # if name=True, socket.if_indextoname() will be returned.
-        # '''
-
-        # cdef object in_interface_name
-
-        cdef u_int8_t in_interface
-
-        in_interface = nfq_get_indev(self._nfa)
-
-        return in_interface
-
-        # try:
-        #     in_interface_name = socket.if_indextoname(in_interface)
-        # except OSError:
-        #     in_interface_name = 'unknown'
-
-        # return in_interface_name
-
-    # NOTE: keeping these funtions separate instead of making an argument option to adjust which interface to return.
-    # this will keep it explicit for which interface is returning to minimize chance of confusion/bugs.
-    cdef u_int8_t get_outint(self, bint name=False):
-        '''Returns index of outbound interface of packet. If the packet is destined for localhost or the output
-        interface is not yet known, 0 will be returned.
-        '''
-        # if name=True, socket.if_indextoname() will be returned.
-        # '''
-
-        # cdef object out_interface_name
-
-        cdef u_int8_t out_interface
-
-        out_interface = nfq_get_outdev(self._nfa)
-
-        return out_interface
-
-        # try:
-        #     out_interface_name = socket.if_indextoname(out_interface)
-        # except OSError:
-        #     out_interface_name = 'unknown'
-
-        # return out_interface_name
-
     cpdef update_mark(self, u_int32_t mark):
         '''Modifies the running mark of the packet.'''
 
@@ -220,16 +174,37 @@ cdef class CPacket:
 
         self.verdict(NF_REPEAT)
 
+    def get_inint_name(self):
+
+        # cdef object *int_name
+        #
+        # nfq_get_indev_name(self)
+
+        pass
+
+    def get_outint_name(self):
+
+        # cdef object *int_name
+        #
+        # nfq_get_outdev_name(self)
+
+        pass
+
     def get_hw(self):
         '''Return hardware information of the packet.
 
             hw_info = (
-                self.get_inint(), self.get_outint(), mac_addr, self.get_timestamp()
+                in_interface, out_interface, mac_addr, self.get_timestamp()
             )
         '''
 
         cdef object mac_addr
         cdef tuple hw_info
+        cdef int in_interface
+        cdef int out_interface
+
+        in_interface = nfq_get_indev(self._nfa)
+        out_interface = nfq_get_outdev(self._nfa)
 
         self._hw = nfq_get_packet_hw(self._nfa)
         if self._hw == NULL:
@@ -238,14 +213,16 @@ cdef class CPacket:
             # parsing process and forcing error handling will ensure it is dealt with [properly].
             raise OSError('MAC address not available in OUTPUT and PREROUTING chains')
 
-        # NOTE: can this not just be directly referenced below?
-        # self.hw_addr = self._hw.hw_addr
+        cdef char* hw_addr = self._hw.hw_addr
 
-        mac_addr = PyBytes_FromStringAndSize(<char*>self._hw.hw_addr, 8)
+        # NOTE: this is 8 bytes in source and lib_netfilter_queue, but unsure why since mac addresses are only 6
+        # bytes. the last two bytes may be padding, but either way removing here so it will not need to be done
+        # on the python side.
+        mac_addr = PyBytes_FromStringAndSize(hw_addr, 6)
 
         hw_info = (
-            self.get_inint(),
-            self.get_outint(),
+            in_interface,
+            out_interface,
             mac_addr,
             self.get_timestamp(),
         )
diff --git a/nfq_test.py b/nfq_test.py
new file mode 100644
index 0000000..3bde4ea
--- /dev/null
+++ b/nfq_test.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+
+import threading
+
+from time import perf_counter_ns
+from ipaddress import IPv4Address
+
+from new_packet import set_user_callback, NetfilterQueue
+
+TEST_FORWARD = 0
+
+def print_and_accept(pkt, pkt_mark):
+    print('+'*30)
+
+    start = perf_counter_ns()
+
+#    print(pkt_mark)
+
+    hw_info = pkt.get_hw()
+
+#    print(hw_info)
+
+#    print(hw_info[2].hex())
+
+#    print('-'*30)
+
+#    data = pkt.get_raw_packet()
+
+#    print(data[0], (data[0] & 15) * 4)
+
+#    print('-'*30)
+
+    ip_header = pkt.get_ip_header()
+
+#    print(ip_header)
+#    print(ip_header[6], IPv4Address(ip_header[8]), IPv4Address(ip_header[9]))
+
+#    print('-'*30)
+
+    proto_header = pkt.get_proto_header()
+
+#    print(pkt.get_proto_header())
+
+#    print('-'*30)
+
+    payload = pkt.get_payload()
+#    print(pkt.get_payload())
+
+#    t, s, d = pkt.get_ip_header()
+
+#    print(array('i', pkt.get_ip_header()))
+#    print(pkt, pkt.get_timestamp())
+
+#    print(f'[IN] {pkt.get_inint()} {pkt.get_inint(name=True)}')
+#    print(f'[OUT] {pkt.get_outint()} {pkt.get_outint(name=True)}')
+
+#    pkt.update_mark(69)
+
+#    print(f'[MARK] {pkt.get_initial_mark()} {pkt.get_modified_mark()} ')
+
+#    print(pkt.payload_test)
+
+    total = perf_counter_ns() - start
+
+    pkt.accept()
+
+    print(f'GRABBED IN: {total} ns')
+
+    print('='*30)
+
+def q_one(pkt):
+    print('+'*30)
+
+    print('[Q1/rcvd]')
+#    print(f'[IN] {pkt.get_inint()} {pkt.get_inint(name=True)}')
+#    print(f'[OUT] {pkt.get_outint()} {pkt.get_outint(name=True)}')
+
+#    pkt.update_mark(69)
+
+#    print(f'[MARK] {pkt.get_initial_mark()} {pkt.get_modified_mark()}')
+
+#    pkt.forward(2)
+#    print('[Q1/forward] > 2')
+
+    print('-'*30)
+
+def q_two(pkt):
+    print('+'*30)
+
+    print('[Q2/rcvd]')
+    print(f'[IN] {pkt.get_inint()} {pkt.get_inint(name=True)}')
+    print(f'[OUT] {pkt.get_outint()} {pkt.get_outint(name=True)}')
+
+    pkt.update_mark(70)
+
+    print(f'[MARK] {pkt.get_initial_mark()} {pkt.get_modified_mark()}')
+
+    pkt.accept()
+
+    print('[Q2/accept]')
+
+    print('-'*30)
+
+def queue(callback, queue_num):
+    set_user_callback(print_and_accept)
+
+    nfqueue = NetfilterQueue()
+    nfqueue.bind(queue_num)
+
+    print(f'[START] QUEUE-{queue_num}')
+
+    try:
+        nfqueue.run()
+    except KeyboardInterrupt:
+        print('')
+
+    finally:
+        nfqueue.unbind()
+
+if __name__ == '__main__':
+
+    if (TEST_FORWARD):
+        threading.Thread(target=queue, args=(q_one, 1)).start()
+        threading.Thread(target=queue, args=(q_two, 2)).start()
+
+    else:
+        queue(print_and_accept, 1)