diff --git a/new_packet.pxd b/new_packet.pxd index f00fcbd..4d47456 100644 --- a/new_packet.pxd +++ b/new_packet.pxd @@ -58,7 +58,6 @@ cdef enum: cdef extern from "Python.h": object PyBytes_FromStringAndSize(char *s, Py_ssize_t len) - object PyString_FromStringAndSize(char *s, Py_ssize_t len) cdef extern from "sys/time.h": ctypedef long time_t @@ -113,39 +112,19 @@ cdef extern from "libnetfilter_queue/libnetfilter_queue.h": nfq_handle *nfq_open() int nfq_close(nfq_handle *h) - int nfq_bind_pf(nfq_handle *h, u_int16_t pf) int nfq_unbind_pf(nfq_handle *h, u_int16_t pf) - ctypedef int *nfq_callback(nfq_q_handle *gh, nfgenmsg *nfmsg, - nfq_data *nfad, void *data) - nfq_q_handle *nfq_create_queue(nfq_handle *h, - u_int16_t num, - nfq_callback *cb, - void *data) + ctypedef int *nfq_callback(nfq_q_handle *gh, nfgenmsg *nfmsg, nfq_data *nfad, void *data) + nfq_q_handle *nfq_create_queue(nfq_handle *h, u_int16_t num, nfq_callback *cb, void *data) int nfq_destroy_queue(nfq_q_handle *qh) - int nfq_handle_packet(nfq_handle *h, char *buf, int len) + int nfq_set_mode(nfq_q_handle *qh, u_int8_t mode, unsigned int len) + q_set_queue_maxlen(nfq_q_handle *qh, u_int32_t queuelen) + int nfq_set_verdict(nfq_q_handle *qh, u_int32_t id, u_int32_t verdict, u_int32_t data_len, unsigned char *buf) nogil + int nfq_set_verdict2(nfq_q_handle *qh, u_int32_t id, u_int32_t verdict, u_int32_t mark, + u_int32_t datalen, unsigned char *buf) nogil - int nfq_set_mode(nfq_q_handle *qh, - u_int8_t mode, unsigned int len) - - q_set_queue_maxlen(nfq_q_handle *qh, - u_int32_t queuelen) - - int nfq_set_verdict(nfq_q_handle *qh, - u_int32_t id, - u_int32_t verdict, - u_int32_t data_len, - unsigned char *buf) nogil - - int nfq_set_verdict2(nfq_q_handle *qh, - u_int32_t id, - u_int32_t verdict, - u_int32_t mark, - u_int32_t datalen, - unsigned char *buf) nogil int nfq_set_queue_maxlen(nfq_q_handle *qh, u_int32_t queuelen) - int nfq_fd(nfq_handle *h) nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(nfq_data *nfad) nogil int nfq_get_payload(nfq_data *nfad, unsigned char **data) nogil @@ -198,15 +177,13 @@ cdef class CPacket: # Packet details: cdef Py_ssize_t data_len cdef readonly unsigned char *data + cdef readonly unsigned char *payload cdef timeval timestamp - 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 cdef void verdict(self, u_int32_t verdict) cdef double get_timestamp(self) - cdef u_int8_t get_inint(self, bint name=?) - cdef u_int8_t get_outint(self, bint name=?) cpdef update_mark(self, u_int32_t mark) cpdef accept(self) cpdef drop(self) diff --git a/new_packet.pyx b/new_packet.pyx index 0482b3a..f0b3aab 100644 --- a/new_packet.pyx +++ b/new_packet.pyx @@ -26,13 +26,13 @@ def set_user_callback(ref): user_callback = ref -cdef int nf_callback(nfq_q_handle *qh, nfgenmsg *nfmsg, nfq_data *nfa, void *data): +cdef int nf_callback(nfq_q_handle *qh, nfgenmsg *nfmsg, nfq_data *nfa, void *data) with gil: cdef u_int32_t mark packet = CPacket() - with nogil: - mark = packet.parse(qh, nfa) + # with nogil: + mark = packet.parse(qh, nfa) user_callback(packet, mark) @@ -79,48 +79,58 @@ cdef class CPacket: self.ip_header = self.data - cdef u_int8_t hdr_shift = 4 - cdef u_int8_t hdr_multiplier = 4 - cdef u_int8_t hdr_xand = 15 cdef u_int8_t iphdr_len - iphdr_len = self.ip_header.ver_ihl & hdr_xand - iphdr_len = iphdr_len * hdr_multiplier + iphdr_len = (self.ip_header.ver_ihl & 15) * 4 # NOTE: tshoot print printf('ip header length=%f\n', iphdr_len) cdef u_int8_t tcphdr_len - cdef u_int8_t udphdr_len = 8 - cdef u_int8_t icmphdr_len = 4 + cdef u_int8_t protohdr_len cdef void *data = &self.data[iphdr_len] - cdef ptrdiff_t hdrptr = data - self.data + # printf('data=%p, self.data=%p\n', &data, &self.data) + # cdef ptrdiff_t hdrptr = data - self.data if (self.ip_header.protocol == IPPROTO_TCP): + printf('TCP=%s\n', self.data_len) - self.tcp_header = &hdrptr + self.tcp_header = &data - tcphdr_len = self.tcp_header.th_off >> hdr_shift - tcphdr_len = tcphdr_len & hdr_xand - tcphdr_len = tcphdr_len * hdr_multiplier + tcphdr_len = (self.tcp_header.th_off >> 4) & 15 + protohdr_len = tcphdr_len * 4 # NOTE: tshoot print - printf('TCP HEADER LEN=%f\n', tcphdr_len) + printf('TCP SPORT=%f\n', self.tcp_header.th_sport) - self.cmbhdr_len = iphdr_len + tcphdr_len + # self.cmbhdr_len = iphdr_len + tcphdr_len elif (self.ip_header.protocol == IPPROTO_UDP): - self.udp_header = &hdrptr + self.udp_header = &data - self.cmbhdr_len = iphdr_len + udphdr_len + protohdr_len = iphdr_len + 8 + + # self.cmbhdr_len = iphdr_len + 8 elif (self.ip_header.protocol == IPPROTO_ICMP): - self.icmp_header = &hdrptr + self.icmp_header = &data - self.cmbhdr_len = iphdr_len + icmphdr_len + protohdr_len = iphdr_len + 4 + + # self.cmbhdr_len = iphdr_len + 4 + + else: + printf('UNKNOWN PROTOCOL=%f\n', self.ip_header.protocol) + + self.cmbhdr_len = protohdr_len + 20 + + self.payload = &self.data[iphdr_len+protohdr_len] + + printf('CMBHDR LEN=%f\n', self.cmbhdr_len) + printf('DATA LEN=%f\n', self.data_len) cdef void verdict(self, u_int32_t verdict): '''Call appropriate set_verdict function on packet.''' @@ -208,17 +218,18 @@ cdef class CPacket: self._hw = nfq_get_packet_hw(self._nfa) if self._hw == NULL: + print(f'HW ERROR: {self._hw.hw_addr}') # nfq_get_packet_hw doesn't work on OUTPUT and PREROUTING chains # NOTE: making this a quick fail scenario since this would likely cause problems later in the packet # parsing process and forcing error handling will ensure it is dealt with [properly]. raise OSError('MAC address not available in OUTPUT and PREROUTING chains') - cdef char* hw_addr = self._hw.hw_addr + cdef u_int8_t[8] hw_addr = self._hw.hw_addr # 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) + mac_addr = PyBytes_FromStringAndSize(hw_addr, 6) hw_info = ( in_interface, @@ -234,6 +245,12 @@ cdef class CPacket: return self.data[:self.data_len] + def get_ip_header_raw(self): + return self.data[:20] + + def get_proto_header_raw(self): + return self.data[20:self.cmbhdr_len] + def get_ip_header(self): '''Return layer3 of packet data as a tuple converted directly from C struct.''' @@ -298,11 +315,20 @@ cdef class CPacket: def get_payload(self): '''Return payload (>layer4) as Python bytes.''' - cdef object payload + # cdef Py_ssize_t hdr_len = self.cmbhdr_len - payload = self.data[self.cmbhdr_len:self.data_len] + cdef Py_ssize_t payload_len = self.data_len - self.cmbhdr_len - return payload + + return self.payload[:payload_len] + + # if self.data_len - hdr_len > 0: + # # print(f'DATA_LEN-1 = {self.data_len-1}') + # # return self.data[hdr_len:self.data_len-1] + # return self.payload + + # else: + # return b'' cdef class NetfilterQueue: @@ -371,9 +397,7 @@ cdef class NetfilterQueue: cdef int fd = self.get_fd() cdef char buf[4096] cdef int rv - cdef int recv_flags - - recv_flags = 0 + cdef int recv_flags = 0 while True: with nogil: diff --git a/nfq_test.py b/nfq_test.py index 3bde4ea..4efb9f4 100644 --- a/nfq_test.py +++ b/nfq_test.py @@ -4,8 +4,11 @@ import threading from time import perf_counter_ns from ipaddress import IPv4Address +from struct import Struct -from new_packet import set_user_callback, NetfilterQueue +from new_packet import set_user_callback, NetfilterQueue # pylint: disable=import-error + +short_unpack = Struct('!H').unpack TEST_FORWARD = 0 @@ -17,34 +20,44 @@ def print_and_accept(pkt, pkt_mark): # print(pkt_mark) hw_info = pkt.get_hw() + print('[rcvd] hw info') -# print(hw_info) + # print(hw_info) # print(hw_info[2].hex()) -# print('-'*30) + # print('-'*30) -# data = pkt.get_raw_packet() + data = pkt.get_raw_packet() + + print('[rcvd] RAW PACKET') # print(data[0], (data[0] & 15) * 4) -# print('-'*30) + # print('-'*30) ip_header = pkt.get_ip_header() + if ip_header[6] == 6: + print('[rcvd] ip header') # print(ip_header) -# print(ip_header[6], IPv4Address(ip_header[8]), IPv4Address(ip_header[9])) + 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()) + if ip_header[6] == 6: + print('[rcvd] proto header') + print(f'th_off={proto_header[4]}', proto_header) -# print('-'*30) + print('-'*30) - payload = pkt.get_payload() -# print(pkt.get_payload()) + if ip_header[6] == 6: + + + payload = pkt.get_payload() + print(payload) # t, s, d = pkt.get_ip_header() @@ -102,7 +115,7 @@ def q_two(pkt): print('-'*30) def queue(callback, queue_num): - set_user_callback(print_and_accept) + set_user_callback(callback) nfqueue = NetfilterQueue() nfqueue.bind(queue_num) @@ -117,11 +130,27 @@ def queue(callback, queue_num): finally: nfqueue.unbind() +def tcp_test(packet, mark): + print('+'*30) + + start = perf_counter_ns() + ip_header = packet.get_ip_header() + if ip_header[6] == 6: + proto_header = packet.get_proto_header() + print(f'{IPv4Address(ip_header[8])}:{proto_header[0]} > {IPv4Address(ip_header[9])}:{proto_header[1]}') + print(packet.get_ip_header_raw()) + print(packet.get_proto_header_raw()) + print(packet.get_raw_packet()) + + print('='*30) + if __name__ == '__main__': + CALLBACK = tcp_test + 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) + queue(CALLBACK, 1)