proto header work
This commit is contained in:
parent
b250f70e33
commit
38f494df2c
@ -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)
|
||||
|
@ -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 = <iphdr*>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', <double>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 = <u_int32_t*>data - <u_int32_t*>self.data
|
||||
# printf('data=%p, self.data=%p\n', <void*>&data, <void*>&self.data)
|
||||
# cdef ptrdiff_t hdrptr = <u_int32_t*>data - <u_int32_t*>self.data
|
||||
|
||||
if (self.ip_header.protocol == IPPROTO_TCP):
|
||||
printf('TCP=%s\n', <char*>self.data_len)
|
||||
|
||||
self.tcp_header = <tcphdr*>&hdrptr
|
||||
self.tcp_header = <tcphdr*>&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', <double>tcphdr_len)
|
||||
printf('TCP SPORT=%f\n', <double>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 = <udphdr*>&hdrptr
|
||||
self.udp_header = <udphdr*>&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 = <icmphdr*>&hdrptr
|
||||
self.icmp_header = <icmphdr*>&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', <double>self.ip_header.protocol)
|
||||
|
||||
self.cmbhdr_len = protohdr_len + 20
|
||||
|
||||
self.payload = &self.data[iphdr_len+protohdr_len]
|
||||
|
||||
printf('CMBHDR LEN=%f\n', <double>self.cmbhdr_len)
|
||||
printf('DATA LEN=%f\n', <double>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(<char*>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 = <Py_ssize_t>self.cmbhdr_len
|
||||
|
||||
payload = self.data[<Py_ssize_t>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:
|
||||
|
53
nfq_test.py
53
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)
|
||||
|
Loading…
Reference in New Issue
Block a user