work on new packet
This commit is contained in:
parent
5af79a5303
commit
7fe1d0c5bc
@ -13,7 +13,7 @@ cdef enum:
|
||||
ENOBUFS = 105 # No buffer space available
|
||||
|
||||
cdef extern from "netinet/ip.h":
|
||||
struct iphdr:
|
||||
struct ip_header:
|
||||
u_int8_t tos
|
||||
u_int16_t tot_len
|
||||
u_int16_t id
|
||||
@ -174,7 +174,7 @@ cdef class Packet:
|
||||
cdef nfqnl_msg_packet_hdr *_hdr
|
||||
cdef nfqnl_msg_packet_hw *_hw
|
||||
cdef bint _verdict_is_set # True if verdict has been issued, otherwise false
|
||||
cdef u_int32_t _modified_mark # Mark given to packet
|
||||
cdef u_int32_t _mark # Mark given to packet
|
||||
cdef bytes _given_payload # New payload of packet, or null
|
||||
|
||||
# From NFQ packet header:
|
||||
@ -198,14 +198,13 @@ cdef class Packet:
|
||||
#cdef readonly u_int32_t physoutdev
|
||||
|
||||
cdef set_nfq_data(self, nfq_q_handle *qh, nfq_data *nfa)
|
||||
cdef void verdict(self, u_int8_t verdict)
|
||||
cdef void verdict(self, u_int32_t verdict)
|
||||
cpdef get_inint(self, bint name=*)
|
||||
cpdef get_outint(self, bint name=*)
|
||||
cpdef update_mark(self, u_int32_t mark)
|
||||
cpdef Py_ssize_t get_payload_len(self)
|
||||
cpdef double get_timestamp(self)
|
||||
cpdef set_payload(self, bytes payload)
|
||||
#cpdef get_mark(self)
|
||||
cpdef accept(self)
|
||||
cpdef drop(self)
|
||||
cpdef forward(self, u_int16_t queue_num)
|
||||
|
74
new_packet.pxd
Normal file
74
new_packet.pxd
Normal file
@ -0,0 +1,74 @@
|
||||
cdef extern from "sys/types.h":
|
||||
ctypedef unsigned char u_int8_t
|
||||
ctypedef unsigned short int u_int16_t
|
||||
ctypedef unsigned int u_int32_t
|
||||
|
||||
cdef extern from "<errno.h>":
|
||||
int errno
|
||||
|
||||
# cython define
|
||||
cdef extern from "netinet/ip.h":
|
||||
struct iphdr:
|
||||
u_int8_t tos
|
||||
u_int16_t tot_len
|
||||
u_int16_t id
|
||||
u_int16_t frag_off
|
||||
u_int8_t ttl
|
||||
u_int8_t protocol
|
||||
u_int16_t check
|
||||
u_int32_t saddr
|
||||
u_int32_t daddr
|
||||
|
||||
# cython define
|
||||
cdef extern from "netinet/tcp.h":
|
||||
struct tcphdr:
|
||||
u_int16_t th_sport
|
||||
u_int16_t th_dport
|
||||
u_int32_t th_seq
|
||||
u_int32_t th_ack
|
||||
|
||||
u_int8_t th_x2:4
|
||||
u_int8_t th_off:4
|
||||
|
||||
u_int8_t th_flags
|
||||
|
||||
u_int16_t th_win
|
||||
u_int16_t th_sum
|
||||
u_int16_t th_urp
|
||||
|
||||
# cython define
|
||||
cdef extern from "netinet/udp.h":
|
||||
struct udphdr:
|
||||
u_int16_t uh_sport
|
||||
u_int16_t uh_dport
|
||||
u_int16_t uh_ulen
|
||||
u_int16_t uh_sum
|
||||
|
||||
cdef struct icmphdr:
|
||||
u_int8_t type
|
||||
|
||||
# from netinet/in.h:
|
||||
cdef enum:
|
||||
IPPROTO_IP = 0 # Dummy protocol for TCP.
|
||||
IPPROTO_ICMP = 1 # Internet Control Message Protocol.
|
||||
IPPROTO_TCP = 6 # Transmission Control Protocol.
|
||||
IPPROTO_UDP = 17 # User Datagram Protocol.
|
||||
|
||||
|
||||
cdef class CPacket:
|
||||
cdef nfq_q_handle *_qh
|
||||
cdef nfq_data *_nfa
|
||||
cdef nfqnl_msg_packet_hdr *_hdr
|
||||
cdef nfqnl_msg_packet_hw *_hw
|
||||
|
||||
cdef u_int16_t __queue_num
|
||||
cdef bint threaded
|
||||
|
||||
cdef bint _verdict_is_set
|
||||
cdef u_int32_t _mark
|
||||
|
||||
# Packet details:
|
||||
cdef Py_ssize_t payload_len
|
||||
cdef readonly unsigned char *payload
|
||||
cdef timeval timestamp
|
||||
cdef u_int8_t hw_addr[8]
|
214
new_packet.pyx
Normal file
214
new_packet.pyx
Normal file
@ -0,0 +1,214 @@
|
||||
class CPacket:
|
||||
'''parent class designed to index/parse full tcp/ip packets (including ethernet). two alternate
|
||||
constructors are supplied to support nfqueue or raw sockets.
|
||||
raw socket:
|
||||
packet = RawPacket.interface(data, address, socket)
|
||||
nfqueue:
|
||||
packet = RawPacket.netfilter(nfqueue)
|
||||
the before_exit method can be overridden to extend the parsing functionality, for example to group
|
||||
objects in namedtuples or to index application data.
|
||||
'''
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if (cls is RawPacket):
|
||||
raise TypeError('RawPacket can only be used via inheritance.')
|
||||
|
||||
return object.__new__(cls)
|
||||
|
||||
def __cinit__(self):
|
||||
self._verdict_is_set = False
|
||||
self._mark = 0
|
||||
|
||||
self.protocol = PROTO.NOT_SET
|
||||
|
||||
def __str__(self):
|
||||
cdef iphdr * hdr = < iphdr * > self.payload
|
||||
protocol = PROTOCOLS.get(hdr.protocol, "Unknown protocol")
|
||||
|
||||
return "%s packet, %s bytes" % (protocol, self.payload_len)
|
||||
|
||||
# NOTE: this will be callback target for nfqueue
|
||||
cdef netfilter(nfq_q_handle * qh, nfgenmsg * nfmsg, nfq_data * nfa, void * data):
|
||||
'''alternate constructor. used to start listener/proxy instances using nfqueue bindings.'''
|
||||
|
||||
'''Assign a packet from NFQ to this object. Parse the header and load local values.'''
|
||||
|
||||
self = cls()
|
||||
self.parse()
|
||||
|
||||
self._qh = qh
|
||||
self._nfa = nfa
|
||||
self._hdr = nfq_get_msg_packet_hdr(nfa)
|
||||
|
||||
self.id = ntohl(self._hdr.packet_id)
|
||||
self.hw_protocol = ntohs(self._hdr.hw_protocol)
|
||||
self.hook = self._hdr.hook
|
||||
|
||||
self.payload_len = nfq_get_payload(self._nfa, & self.data)
|
||||
if self.payload_len < 0:
|
||||
raise OSError("Failed to get payload of packet.")
|
||||
|
||||
# timestamp gets assigned via pointer/struct -> time_val: (t_sec, t_usec).
|
||||
nfq_get_timestamp(self._nfa, & self.timestamp)
|
||||
|
||||
cdef u_int_32_t self._mark = nfq_get_nfmark(nfa)
|
||||
|
||||
# if (self.continue_condition):
|
||||
# self._before_exit()
|
||||
|
||||
# TODO: send to module callback here
|
||||
# with gil:
|
||||
# callback(self)
|
||||
|
||||
return 0
|
||||
|
||||
cdef void verdict(self, u_int32_t verdict):
|
||||
'''Call appropriate set_verdict... function on packet.'''
|
||||
|
||||
if self._verdict_is_set:
|
||||
raise RuntimeWarning("Verdict already given for this packet.")
|
||||
|
||||
cdef u_int32_t modified_payload_len = 0
|
||||
cdef unsigned char * modified_payload = NULL
|
||||
|
||||
# rewriting payload data/len
|
||||
if self._given_payload:
|
||||
modified_payload_len = len(self._given_payload)
|
||||
modified_payload = self._given_payload
|
||||
|
||||
if self._modified_mark:
|
||||
nfq_set_verdict2(
|
||||
self._qh, self.id, verdict, self._modified_mark, modified_payload_len, modified_payload
|
||||
)
|
||||
|
||||
else:
|
||||
nfq_set_verdict(
|
||||
self._qh, self.id, verdict, modified_payload_len, modified_payload
|
||||
)
|
||||
|
||||
self._verdict_is_set = True
|
||||
|
||||
cdef def parse(self) with nogil:
|
||||
'''index tcp/ip packet layers 3 & 4 for use as instance objects.
|
||||
the before_exit method will be called before returning, which can be used to create
|
||||
subclass specific objects like namedtuples or application layer data.'''
|
||||
|
||||
cdef iphdr * ip_header = < iphdr * > self.payload
|
||||
|
||||
cdef u_int8_t iphdr_len = iphdr.tos & 15) * 4
|
||||
|
||||
if (iphdr.protocol == IPPROTO_TCP):
|
||||
|
||||
cdef tcphdr * tcp_header = < tcphdr * > self.payload[iphdr_len:]
|
||||
|
||||
return 0
|
||||
|
||||
if (iphdr.protocol == IPPROTO_UDP):
|
||||
cdef tcphdr * icmp_header = < tcphdr * > self.payload[iphdr_len:]
|
||||
|
||||
return 0
|
||||
|
||||
if (iphdr.protocol == IPPROTO_ICMP):
|
||||
cdef icmphdr * icmp_header = < icmphdr * > self.payload[iphdr_len:]
|
||||
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
def _before_exit(self):
|
||||
'''executes before returning from parse call.
|
||||
May be overridden.
|
||||
'''
|
||||
pass
|
||||
|
||||
@property
|
||||
def continue_condition(self):
|
||||
'''controls whether the _before_exit method gets called. must return a boolean.
|
||||
May be overridden.
|
||||
'''
|
||||
return True
|
||||
|
||||
|
||||
cdef class NetfilterQueue:
|
||||
'''Handle a single numbered queue.'''
|
||||
|
||||
def __cinit__(self, *args, **kwargs):
|
||||
self.af = kwargs.get('af', PF_INET)
|
||||
|
||||
self.h = nfq_open()
|
||||
if self.h == NULL:
|
||||
raise OSError('Failed to open NFQueue.')
|
||||
|
||||
# This does NOT kick out previous running queues
|
||||
nfq_unbind_pf(self.h, self.af)
|
||||
|
||||
if nfq_bind_pf(self.h, self.af) < 0:
|
||||
raise OSError('Failed to bind family %s. Are you root?' % self.af)
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.qh != NULL:
|
||||
nfq_destroy_queue(self.qh)
|
||||
|
||||
# Don't call nfq_unbind_pf unless you want to disconnect any other
|
||||
# processes using this libnetfilter_queue on this protocol family!
|
||||
nfq_close(self.h)
|
||||
|
||||
def bind(self, int queue_num, object user_callback, u_int16_t max_len=DEFAULT_MAX_QUEUELEN,
|
||||
u_int8_t mode=NFQNL_COPY_PACKET, u_int16_t range=MaxPacketSize, u_int32_t sock_len=SockRcvSize):
|
||||
'''Create and bind to a new queue.'''
|
||||
|
||||
cdef unsigned int newsiz
|
||||
|
||||
self.user_callback = user_callback
|
||||
self.qh = nfq_create_queue(self.h, queue_num, < nfq_callback * > global_callback, < void * > self)
|
||||
if self.qh == NULL:
|
||||
raise OSError(f'Failed to create queue {queue_num}')
|
||||
|
||||
if range > MaxCopySize:
|
||||
range = MaxCopySize
|
||||
|
||||
if nfq_set_mode(self.qh, mode, range) < 0:
|
||||
raise OSError("Failed to set packet copy mode.")
|
||||
|
||||
nfq_set_queue_maxlen(self.qh, max_len)
|
||||
|
||||
newsiz = nfnl_rcvbufsiz(nfq_nfnlh(self.h), sock_len)
|
||||
if newsiz != sock_len * 2:
|
||||
raise RuntimeWarning("Socket rcvbuf limit is now %d, requested %d." % (newsiz, sock_len))
|
||||
|
||||
def unbind(self):
|
||||
'''Destroy the queue.'''
|
||||
|
||||
if self.qh != NULL:
|
||||
nfq_destroy_queue(self.qh)
|
||||
|
||||
self.qh = NULL
|
||||
# See warning about nfq _unbind_pf in __dealloc__ above.
|
||||
|
||||
def get_fd(self):
|
||||
'''Get the file descriptor of the queue handler.'''
|
||||
|
||||
return nfq_fd(self.h)
|
||||
|
||||
def run(self, bint block=True):
|
||||
'''Accept packets using recv.'''
|
||||
|
||||
cdef int fd = self.get_fd()
|
||||
cdef char buf[4096]
|
||||
cdef int rv
|
||||
cdef int recv_flags
|
||||
|
||||
recv_flags = 0
|
||||
|
||||
while True:
|
||||
with nogil:
|
||||
rv = recv(fd, buf, sizeof(buf), recv_flags)
|
||||
|
||||
print(rv)
|
||||
|
||||
if (rv >= 0):
|
||||
nfq_handle_packet(self.h, buf, rv)
|
||||
|
||||
else:
|
||||
if errno != ENOBUFS:
|
||||
break
|
13
setup2.py
Normal file
13
setup2.py
Normal file
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from setuptools import setup
|
||||
from Cython.Build import cythonize
|
||||
from Cython.Distutils import build_ext
|
||||
|
||||
cmd = {'build_ext': build_ext}
|
||||
ext = Extension(
|
||||
'dnx_nfqueue', sources=['new_packet.pyx'], libraries=['netfilter_queue'])
|
||||
|
||||
setup(
|
||||
name='DNX-NFQUEUE', cmdclass=cmd, ext_modules=cythonize(ext, language_level='3')
|
||||
)
|
Loading…
Reference in New Issue
Block a user