restructure and cleanup for potential release build.
This commit is contained in:
parent
38f494df2c
commit
ac7ce15097
@ -12,59 +12,58 @@ cdef enum:
|
||||
EWOULDBLOCK = EAGAIN
|
||||
ENOBUFS = 105 # No buffer space available
|
||||
|
||||
# cython define
|
||||
cdef struct iphdr:
|
||||
u_int8_t ver_ihl
|
||||
u_int8_t tos
|
||||
cdef extern from "netinet/ip.h":
|
||||
struct ip_header:
|
||||
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_int8_t ttl
|
||||
u_int8_t protocol
|
||||
u_int16_t check
|
||||
u_int32_t saddr
|
||||
u_int32_t daddr
|
||||
|
||||
# cython define
|
||||
cdef 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_off
|
||||
|
||||
u_int8_t th_flags
|
||||
u_int16_t th_win
|
||||
u_int16_t th_sum
|
||||
u_int16_t th_urp
|
||||
|
||||
# cython define
|
||||
cdef 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:
|
||||
# Dummy defines from netinet/in.h:
|
||||
cdef enum:
|
||||
IPPROTO_IP = 0 # Dummy protocol for TCP.
|
||||
IPPROTO_HOPOPTS = 0 # IPv6 Hop-by-Hop options.
|
||||
IPPROTO_ICMP = 1 # Internet Control Message Protocol.
|
||||
IPPROTO_IGMP = 2 # Internet Group Management Protocol. */
|
||||
IPPROTO_IPIP = 4 # IPIP tunnels (older KA9Q tunnels use 94).
|
||||
IPPROTO_TCP = 6 # Transmission Control Protocol.
|
||||
IPPROTO_EGP = 8 # Exterior Gateway Protocol.
|
||||
IPPROTO_PUP = 12 # PUP protocol.
|
||||
IPPROTO_UDP = 17 # User Datagram Protocol.
|
||||
IPPROTO_IDP = 22 # XNS IDP protocol.
|
||||
IPPROTO_TP = 29 # SO Transport Protocol Class 4.
|
||||
IPPROTO_IPV6 = 41 # IPv6 header.
|
||||
IPPROTO_ROUTING = 43 # IPv6 routing header.
|
||||
IPPROTO_FRAGMENT = 44 # IPv6 fragmentation header.
|
||||
IPPROTO_RSVP = 46 # Reservation Protocol.
|
||||
IPPROTO_GRE = 47 # General Routing Encapsulation.
|
||||
IPPROTO_ESP = 50 # encapsulating security payload.
|
||||
IPPROTO_AH = 51 # authentication header.
|
||||
IPPROTO_ICMPV6 = 58 # ICMPv6.
|
||||
IPPROTO_NONE = 59 # IPv6 no next header.
|
||||
IPPROTO_DSTOPTS = 60 # IPv6 destination options.
|
||||
IPPROTO_MTP = 92 # Multicast Transport Protocol.
|
||||
IPPROTO_ENCAP = 98 # Encapsulation Header.
|
||||
IPPROTO_PIM = 103 # Protocol Independent Multicast.
|
||||
IPPROTO_COMP = 108 # Compression Header Protocol.
|
||||
IPPROTO_SCTP = 132 # Stream Control Transmission Protocol.
|
||||
IPPROTO_RAW = 255 # Raw IP packets.
|
||||
IPPROTO_MAX
|
||||
|
||||
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
|
||||
struct timeval:
|
||||
time_t tv_sec
|
||||
time_t tv_usec
|
||||
|
||||
struct timezone:
|
||||
pass
|
||||
|
||||
@ -83,7 +82,6 @@ cdef extern from "libnfnetlink/linux_nfnetlink.h":
|
||||
cdef extern from "libnfnetlink/libnfnetlink.h":
|
||||
struct nfnl_handle:
|
||||
pass
|
||||
|
||||
unsigned int nfnl_rcvbufsiz(nfnl_handle *h, unsigned int size)
|
||||
|
||||
cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
@ -91,7 +89,6 @@ cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
NFQNL_COPY_NONE
|
||||
NFQNL_COPY_META
|
||||
NFQNL_COPY_PACKET
|
||||
|
||||
struct nfqnl_msg_packet_hdr:
|
||||
u_int32_t packet_id
|
||||
u_int16_t hw_protocol
|
||||
@ -100,41 +97,58 @@ cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
cdef extern from "libnetfilter_queue/libnetfilter_queue.h":
|
||||
struct nfq_handle:
|
||||
pass
|
||||
|
||||
struct nfq_q_handle:
|
||||
pass
|
||||
|
||||
struct nfq_data:
|
||||
pass
|
||||
|
||||
struct nfqnl_msg_packet_hw:
|
||||
u_int8_t hw_addr[8]
|
||||
|
||||
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_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_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
|
||||
int nfq_get_timestamp(nfq_data *nfad, timeval *tv) nogil
|
||||
nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(nfq_data *nfad)
|
||||
int nfq_get_payload(nfq_data *nfad, unsigned char **data)
|
||||
int nfq_get_timestamp(nfq_data *nfad, timeval *tv)
|
||||
nfqnl_msg_packet_hw *nfq_get_packet_hw(nfq_data *nfad)
|
||||
int nfq_get_nfmark (nfq_data *nfad) nogil
|
||||
int nfq_get_nfmark (nfq_data *nfad)
|
||||
u_int8_t nfq_get_indev(nfq_data *nfad)
|
||||
u_int8_t nfq_get_outdev(nfq_data *nfad)
|
||||
nfnl_handle *nfq_nfnlh(nfq_handle *h)
|
||||
|
||||
|
||||
# Dummy defines from linux/socket.h:
|
||||
cdef enum: # Protocol families, same as address families.
|
||||
PF_INET = 2
|
||||
@ -154,44 +168,52 @@ cdef enum:
|
||||
NF_STOP
|
||||
NF_MAX_VERDICT = NF_STOP
|
||||
|
||||
|
||||
cdef class CPacket:
|
||||
cdef class Packet:
|
||||
cdef nfq_q_handle *_qh
|
||||
cdef nfq_data *_nfa
|
||||
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 _mark # Mark given to packet
|
||||
cdef bytes _given_payload # New payload of packet, or null
|
||||
|
||||
cdef u_int32_t id
|
||||
|
||||
# protocol headers
|
||||
cdef iphdr *ip_header
|
||||
cdef tcphdr *tcp_header
|
||||
cdef udphdr *udp_header
|
||||
cdef icmphdr *icmp_header
|
||||
|
||||
cdef u_int8_t cmbhdr_len
|
||||
|
||||
cdef bint _verdict_is_set
|
||||
cdef u_int32_t _mark
|
||||
# From NFQ packet header:
|
||||
cdef readonly u_int32_t id
|
||||
cdef readonly u_int16_t hw_protocol
|
||||
cdef readonly u_int8_t hook
|
||||
cdef readonly u_int32_t mark
|
||||
|
||||
# Packet details:
|
||||
cdef Py_ssize_t data_len
|
||||
cdef readonly unsigned char *data
|
||||
cdef Py_ssize_t payload_len
|
||||
cdef readonly unsigned char *payload
|
||||
cdef timeval timestamp
|
||||
cdef u_int8_t hw_addr[8]
|
||||
|
||||
cdef u_int32_t parse(self, nfq_q_handle *qh, nfq_data *nfa) nogil
|
||||
cdef void _parse(self) nogil
|
||||
# TODO: implement these | likely not using in this manner.
|
||||
#cdef u_int8_t hw_addr[8] # A eui64-formatted address?
|
||||
#cdef readonly u_int32_t nfmark
|
||||
#cdef readonly u_int32_t indev
|
||||
#cdef readonly u_int32_t physindev
|
||||
#cdef readonly u_int32_t outdev
|
||||
#cdef readonly u_int32_t physoutdev
|
||||
|
||||
cdef set_nfq_data(self, nfq_q_handle *qh, nfq_data *nfa)
|
||||
cdef void verdict(self, u_int32_t verdict)
|
||||
cdef double get_timestamp(self)
|
||||
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 accept(self)
|
||||
cpdef drop(self)
|
||||
cpdef forward(self, u_int16_t queue_num)
|
||||
cpdef repeat(self)
|
||||
|
||||
cdef class NetfilterQueue:
|
||||
cdef object user_callback # User callback
|
||||
cdef nfq_handle *h # Handle to NFQueue library
|
||||
cdef nfq_q_handle *qh # A handle to the queue
|
||||
cdef u_int16_t af # Address family
|
||||
cdef packet_copy_size # Amount of packet metadata + data copied to buffer
|
||||
cdef packet_copy_size # Amount of packet metadata + data copied to buffer
|
||||
|
481
dnx-netfilterqueue.pyx
Normal file
481
dnx-netfilterqueue.pyx
Normal file
@ -0,0 +1,481 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
'''
|
||||
Bind to a Linux netfilter queue. Send packets to a user-specified callback
|
||||
function.
|
||||
|
||||
Copyright: (c) 2011, Kerkhoff Technologies Inc.
|
||||
License: MIT; see SOURCE-LICENSE.txt
|
||||
'''
|
||||
|
||||
'''
|
||||
Expanded features + general refactor to be used with DNXFIREWALL.
|
||||
- DOWRIGHT @ Wright Network Solutions, LLC.
|
||||
'''
|
||||
|
||||
import socket
|
||||
|
||||
# Constants for module users
|
||||
cdef int COPY_NONE = 0
|
||||
cdef int COPY_META = 1
|
||||
cdef int COPY_PACKET = 2
|
||||
|
||||
cdef u_int16_t DEFAULT_MAX_QUEUELEN = 1024
|
||||
cdef u_int16_t MaxPacketSize = 0xFFFF
|
||||
|
||||
# buffer size - metadata size
|
||||
cdef u_int16_t MaxCopySize = 4096 - 80
|
||||
|
||||
# Socket queue should hold max number of packets of copy size bytes
|
||||
# formula: DEF_MAX_QUEUELEN * (MaxCopySize+SockOverhead) / 2
|
||||
cdef u_int32_t SockRcvSize = 1024 * 4796 // 2
|
||||
|
||||
cdef int global_callback(nfq_q_handle * qh, nfgenmsg * nfmsg, nfq_data * nfa, void * data) with gil:
|
||||
'''Create a Packet and pass it to appropriate callback.'''
|
||||
|
||||
cdef NetfilterQueue nfqueue = < NetfilterQueue > data
|
||||
cdef object user_callback = < object > nfqueue.user_callback
|
||||
|
||||
packet = Packet()
|
||||
packet.set_nfq_data(qh, nfa)
|
||||
|
||||
user_callback(packet)
|
||||
|
||||
return 1
|
||||
|
||||
cdef class Packet:
|
||||
'''A packet received from NetfilterQueue.'''
|
||||
|
||||
def __cinit__(self):
|
||||
self._verdict_is_set = False
|
||||
self._mark = 0
|
||||
self._given_payload = None
|
||||
|
||||
def __str__(self):
|
||||
cdef ip_header * hdr = < ip_header * > self.payload
|
||||
protocol = PROTOCOLS.get(hdr.protocol, "Unknown protocol")
|
||||
|
||||
return "%s packet, %s bytes" % (protocol, self.payload_len)
|
||||
|
||||
cdef set_nfq_data(self, nfq_q_handle * qh, nfq_data * nfa):
|
||||
'''Assign a packet from NFQ to this object. Parse the header and load local values.'''
|
||||
|
||||
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.payload)
|
||||
if self.payload_len < 0:
|
||||
raise OSError("Failed to get payload of packet.")
|
||||
|
||||
self._payload
|
||||
|
||||
# timestamp gets assigned via pointer/struct -> time_val: (t_sec, t_usec).
|
||||
nfq_get_timestamp(self._nfa, &self.timestamp)
|
||||
|
||||
cdef u_int32_t mark = nfq_get_nfmark(nfa)
|
||||
|
||||
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
|
||||
|
||||
cpdef 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
|
||||
|
||||
in_interface = nfq_get_indev(self._nfa)
|
||||
if not name:
|
||||
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.
|
||||
cpdef 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
|
||||
|
||||
out_interface = nfq_get_outdev(self._nfa)
|
||||
if not name:
|
||||
return out_interface
|
||||
|
||||
try:
|
||||
out_interface_name = socket.if_indextoname(out_interface)
|
||||
except OSError:
|
||||
out_interface_name = 'unknown'
|
||||
|
||||
return out_interface_name
|
||||
|
||||
def get_mark(self):
|
||||
'''Return mark set at first receipt of packet.'''
|
||||
|
||||
return self._mark
|
||||
|
||||
cpdef update_mark(self, u_int32_t mark):
|
||||
'''Modifies the running mark of the packet. This will not override the initial mark received, but any
|
||||
subsequent update will override the previous.'''
|
||||
|
||||
self._modified_mark = mark
|
||||
|
||||
def get_hw(self):
|
||||
'''Return the source mac address of the packet.'''
|
||||
|
||||
cdef object mac_addr
|
||||
|
||||
self._hw = nfq_get_packet_hw(self._nfa)
|
||||
if self._hw == NULL:
|
||||
# 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')
|
||||
|
||||
# NOTE: can this not just be directly referenced below?
|
||||
self.hw_addr = self._hw.hw_addr
|
||||
|
||||
mac_addr = PyBytes_FromStringAndSize( < char * > self.hw_addr, 8)
|
||||
|
||||
return mac_addr
|
||||
|
||||
def get_payload(self):
|
||||
'''Return payload as Python string.'''
|
||||
|
||||
cdef object payload
|
||||
|
||||
payload = self.payload[:self.payload_len]
|
||||
|
||||
return payload
|
||||
|
||||
cpdef set_payload(self, bytes payload):
|
||||
'''Set the new payload of this packet.'''
|
||||
|
||||
self._given_payload = payload
|
||||
|
||||
cpdef Py_ssize_t get_payload_len(self):
|
||||
|
||||
return self.payload_len
|
||||
|
||||
cpdef double get_timestamp(self):
|
||||
|
||||
return self.timestamp.tv_sec + (self.timestamp.tv_usec / 1000000.0)
|
||||
|
||||
cpdef accept(self):
|
||||
'''Accept the packet.'''
|
||||
|
||||
self.verdict(NF_ACCEPT)
|
||||
|
||||
cpdef drop(self):
|
||||
'''Drop the packet.'''
|
||||
|
||||
self.verdict(NF_DROP)
|
||||
|
||||
cpdef forward(self, u_int16_t queue_num):
|
||||
'''Send the packet to a different queue.'''
|
||||
|
||||
cdef u_int32_t forward_to_queue
|
||||
|
||||
forward_to_queue = queue_num << 16 | NF_QUEUE
|
||||
|
||||
self.verdict(forward_to_queue)
|
||||
|
||||
cpdef repeat(self):
|
||||
'''Repeat the packet.'''
|
||||
|
||||
self.verdict(NF_REPEAT)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def run_socket(self, s):
|
||||
'''Accept packets using socket.recv so that, for example, gevent can monkeypatch it.'''
|
||||
|
||||
while True:
|
||||
try:
|
||||
buf = s.recv(4096)
|
||||
except socket.error as e:
|
||||
err = e.args[0]
|
||||
if err == ENOBUFS:
|
||||
continue
|
||||
|
||||
elif err == EAGAIN or err == EWOULDBLOCK:
|
||||
# This should only happen with a non-blocking socket, and the
|
||||
# app should call run_socket again when more data is available.
|
||||
break
|
||||
|
||||
else:
|
||||
# This is bad. Let the caller handle it.
|
||||
raise e
|
||||
|
||||
else:
|
||||
rv = len(buf)
|
||||
if rv >= 0:
|
||||
nfq_handle_packet(self.h, buf, rv)
|
||||
else:
|
||||
break
|
||||
|
||||
PROTOCOLS = {
|
||||
0: "HOPOPT",
|
||||
1: "ICMP",
|
||||
2: "IGMP",
|
||||
3: "GGP",
|
||||
4: "IP",
|
||||
5: "ST",
|
||||
6: "TCP",
|
||||
7: "CBT",
|
||||
8: "EGP",
|
||||
9: "IGP",
|
||||
10: "BBN-RCC-MON",
|
||||
11: "NVP-II",
|
||||
12: "PUP",
|
||||
13: "ARGUS",
|
||||
14: "EMCON",
|
||||
15: "XNET",
|
||||
16: "CHAOS",
|
||||
17: "UDP",
|
||||
18: "MUX",
|
||||
19: "DCN-MEAS",
|
||||
20: "HMP",
|
||||
21: "PRM",
|
||||
22: "XNS-IDP",
|
||||
23: "TRUNK-1",
|
||||
24: "TRUNK-2",
|
||||
25: "LEAF-1",
|
||||
26: "LEAF-2",
|
||||
27: "RDP",
|
||||
28: "IRTP",
|
||||
29: "ISO-TP4",
|
||||
30: "NETBLT",
|
||||
31: "MFE-NSP",
|
||||
32: "MERIT-INP",
|
||||
33: "DCCP",
|
||||
34: "3PC",
|
||||
35: "IDPR",
|
||||
36: "XTP",
|
||||
37: "DDP",
|
||||
38: "IDPR-CMTP",
|
||||
39: "TP++",
|
||||
40: "IL",
|
||||
41: "IPv6",
|
||||
42: "SDRP",
|
||||
43: "IPv6-Route",
|
||||
44: "IPv6-Frag",
|
||||
45: "IDRP",
|
||||
46: "RSVP",
|
||||
47: "GRE",
|
||||
48: "DSR",
|
||||
49: "BNA",
|
||||
50: "ESP",
|
||||
51: "AH",
|
||||
52: "I-NLSP",
|
||||
53: "SWIPE",
|
||||
54: "NARP",
|
||||
55: "MOBILE",
|
||||
56: "TLSP",
|
||||
57: "SKIP",
|
||||
58: "IPv6-ICMP",
|
||||
59: "IPv6-NoNxt",
|
||||
60: "IPv6-Opts",
|
||||
61: "any host internal protocol",
|
||||
62: "CFTP",
|
||||
63: "any local network",
|
||||
64: "SAT-EXPAK",
|
||||
65: "KRYPTOLAN",
|
||||
66: "RVD",
|
||||
67: "IPPC",
|
||||
68: "any distributed file system",
|
||||
69: "SAT-MON",
|
||||
70: "VISA",
|
||||
71: "IPCV",
|
||||
72: "CPNX",
|
||||
73: "CPHB",
|
||||
74: "WSN",
|
||||
75: "PVP",
|
||||
76: "BR-SAT-MON",
|
||||
77: "SUN-ND",
|
||||
78: "WB-MON",
|
||||
79: "WB-EXPAK",
|
||||
80: "ISO-IP",
|
||||
81: "VMTP",
|
||||
82: "SECURE-VMTP",
|
||||
83: "VINES",
|
||||
84: "TTP",
|
||||
85: "NSFNET-IGP",
|
||||
86: "DGP",
|
||||
87: "TCF",
|
||||
88: "EIGRP",
|
||||
89: "OSPFIGP",
|
||||
90: "Sprite-RPC",
|
||||
91: "LARP",
|
||||
92: "MTP",
|
||||
93: "AX.25",
|
||||
94: "IPIP",
|
||||
95: "MICP",
|
||||
96: "SCC-SP",
|
||||
97: "ETHERIP",
|
||||
98: "ENCAP",
|
||||
99: "any private encryption scheme",
|
||||
100: "GMTP",
|
||||
101: "IFMP",
|
||||
102: "PNNI",
|
||||
103: "PIM",
|
||||
104: "ARIS",
|
||||
105: "SCPS",
|
||||
106: "QNX",
|
||||
107: "A/N",
|
||||
108: "IPComp",
|
||||
109: "SNP",
|
||||
110: "Compaq-Peer",
|
||||
111: "IPX-in-IP",
|
||||
112: "VRRP",
|
||||
113: "PGM",
|
||||
114: "any 0-hop protocol",
|
||||
115: "L2TP",
|
||||
116: "DDX",
|
||||
117: "IATP",
|
||||
118: "STP",
|
||||
119: "SRP",
|
||||
120: "UTI",
|
||||
121: "SMP",
|
||||
122: "SM",
|
||||
123: "PTP",
|
||||
124: "ISIS",
|
||||
125: "FIRE",
|
||||
126: "CRTP",
|
||||
127: "CRUDP",
|
||||
128: "SSCOPMCE",
|
||||
129: "IPLT",
|
||||
130: "SPS",
|
||||
131: "PIPE",
|
||||
132: "SCTP",
|
||||
133: "FC",
|
||||
134: "RSVP-E2E-IGNORE",
|
||||
135: "Mobility",
|
||||
136: "UDPLite",
|
||||
137: "MPLS-in-IP",
|
||||
138: "manet",
|
||||
139: "HIP",
|
||||
140: "Shim6",
|
||||
255: "Reserved",
|
||||
}
|
@ -12,58 +12,59 @@ cdef enum:
|
||||
EWOULDBLOCK = EAGAIN
|
||||
ENOBUFS = 105 # No buffer space available
|
||||
|
||||
cdef extern from "netinet/ip.h":
|
||||
struct ip_header:
|
||||
u_int8_t tos
|
||||
# cython define
|
||||
cdef struct iphdr:
|
||||
u_int8_t ver_ihl
|
||||
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_int8_t ttl
|
||||
u_int8_t protocol
|
||||
u_int16_t check
|
||||
u_int32_t saddr
|
||||
u_int32_t daddr
|
||||
|
||||
# Dummy defines from netinet/in.h:
|
||||
# cython define
|
||||
cdef 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_off
|
||||
|
||||
u_int8_t th_flags
|
||||
u_int16_t th_win
|
||||
u_int16_t th_sum
|
||||
u_int16_t th_urp
|
||||
|
||||
# cython define
|
||||
cdef 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_HOPOPTS = 0 # IPv6 Hop-by-Hop options.
|
||||
IPPROTO_ICMP = 1 # Internet Control Message Protocol.
|
||||
IPPROTO_IGMP = 2 # Internet Group Management Protocol. */
|
||||
IPPROTO_IPIP = 4 # IPIP tunnels (older KA9Q tunnels use 94).
|
||||
IPPROTO_TCP = 6 # Transmission Control Protocol.
|
||||
IPPROTO_EGP = 8 # Exterior Gateway Protocol.
|
||||
IPPROTO_PUP = 12 # PUP protocol.
|
||||
IPPROTO_UDP = 17 # User Datagram Protocol.
|
||||
IPPROTO_IDP = 22 # XNS IDP protocol.
|
||||
IPPROTO_TP = 29 # SO Transport Protocol Class 4.
|
||||
IPPROTO_IPV6 = 41 # IPv6 header.
|
||||
IPPROTO_ROUTING = 43 # IPv6 routing header.
|
||||
IPPROTO_FRAGMENT = 44 # IPv6 fragmentation header.
|
||||
IPPROTO_RSVP = 46 # Reservation Protocol.
|
||||
IPPROTO_GRE = 47 # General Routing Encapsulation.
|
||||
IPPROTO_ESP = 50 # encapsulating security payload.
|
||||
IPPROTO_AH = 51 # authentication header.
|
||||
IPPROTO_ICMPV6 = 58 # ICMPv6.
|
||||
IPPROTO_NONE = 59 # IPv6 no next header.
|
||||
IPPROTO_DSTOPTS = 60 # IPv6 destination options.
|
||||
IPPROTO_MTP = 92 # Multicast Transport Protocol.
|
||||
IPPROTO_ENCAP = 98 # Encapsulation Header.
|
||||
IPPROTO_PIM = 103 # Protocol Independent Multicast.
|
||||
IPPROTO_COMP = 108 # Compression Header Protocol.
|
||||
IPPROTO_SCTP = 132 # Stream Control Transmission Protocol.
|
||||
IPPROTO_RAW = 255 # Raw IP packets.
|
||||
IPPROTO_MAX
|
||||
|
||||
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
|
||||
struct timeval:
|
||||
time_t tv_sec
|
||||
time_t tv_usec
|
||||
|
||||
struct timezone:
|
||||
pass
|
||||
|
||||
@ -82,6 +83,7 @@ cdef extern from "libnfnetlink/linux_nfnetlink.h":
|
||||
cdef extern from "libnfnetlink/libnfnetlink.h":
|
||||
struct nfnl_handle:
|
||||
pass
|
||||
|
||||
unsigned int nfnl_rcvbufsiz(nfnl_handle *h, unsigned int size)
|
||||
|
||||
cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
@ -89,6 +91,7 @@ cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
NFQNL_COPY_NONE
|
||||
NFQNL_COPY_META
|
||||
NFQNL_COPY_PACKET
|
||||
|
||||
struct nfqnl_msg_packet_hdr:
|
||||
u_int32_t packet_id
|
||||
u_int16_t hw_protocol
|
||||
@ -97,58 +100,41 @@ cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
cdef extern from "libnetfilter_queue/libnetfilter_queue.h":
|
||||
struct nfq_handle:
|
||||
pass
|
||||
|
||||
struct nfq_q_handle:
|
||||
pass
|
||||
|
||||
struct nfq_data:
|
||||
pass
|
||||
|
||||
struct nfqnl_msg_packet_hw:
|
||||
u_int8_t hw_addr[8]
|
||||
|
||||
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)
|
||||
int nfq_get_payload(nfq_data *nfad, unsigned char **data)
|
||||
int nfq_get_timestamp(nfq_data *nfad, timeval *tv)
|
||||
nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(nfq_data *nfad) nogil
|
||||
int nfq_get_payload(nfq_data *nfad, unsigned char **data) nogil
|
||||
int nfq_get_timestamp(nfq_data *nfad, timeval *tv) nogil
|
||||
nfqnl_msg_packet_hw *nfq_get_packet_hw(nfq_data *nfad)
|
||||
int nfq_get_nfmark (nfq_data *nfad)
|
||||
int nfq_get_nfmark (nfq_data *nfad) nogil
|
||||
u_int8_t nfq_get_indev(nfq_data *nfad)
|
||||
u_int8_t nfq_get_outdev(nfq_data *nfad)
|
||||
nfnl_handle *nfq_nfnlh(nfq_handle *h)
|
||||
|
||||
|
||||
# Dummy defines from linux/socket.h:
|
||||
cdef enum: # Protocol families, same as address families.
|
||||
PF_INET = 2
|
||||
@ -168,52 +154,44 @@ cdef enum:
|
||||
NF_STOP
|
||||
NF_MAX_VERDICT = NF_STOP
|
||||
|
||||
cdef class Packet:
|
||||
|
||||
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 bint _verdict_is_set # True if verdict has been issued, otherwise false
|
||||
cdef u_int32_t _mark # Mark given to packet
|
||||
cdef bytes _given_payload # New payload of packet, or null
|
||||
|
||||
# From NFQ packet header:
|
||||
cdef readonly u_int32_t id
|
||||
cdef readonly u_int16_t hw_protocol
|
||||
cdef readonly u_int8_t hook
|
||||
cdef readonly u_int32_t mark
|
||||
cdef u_int32_t id
|
||||
|
||||
# protocol headers
|
||||
cdef iphdr *ip_header
|
||||
cdef tcphdr *tcp_header
|
||||
cdef udphdr *udp_header
|
||||
cdef icmphdr *icmp_header
|
||||
|
||||
cdef u_int8_t cmbhdr_len
|
||||
|
||||
cdef bint _verdict_is_set
|
||||
cdef u_int32_t _mark
|
||||
|
||||
# Packet details:
|
||||
cdef Py_ssize_t payload_len
|
||||
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[8]
|
||||
|
||||
# TODO: implement these | likely not using in this manner.
|
||||
#cdef u_int8_t hw_addr[8] # A eui64-formatted address?
|
||||
#cdef readonly u_int32_t nfmark
|
||||
#cdef readonly u_int32_t indev
|
||||
#cdef readonly u_int32_t physindev
|
||||
#cdef readonly u_int32_t outdev
|
||||
#cdef readonly u_int32_t physoutdev
|
||||
|
||||
cdef set_nfq_data(self, nfq_q_handle *qh, nfq_data *nfa)
|
||||
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)
|
||||
cpdef get_inint(self, bint name=*)
|
||||
cpdef get_outint(self, bint name=*)
|
||||
cdef double get_timestamp(self)
|
||||
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 accept(self)
|
||||
cpdef drop(self)
|
||||
cpdef forward(self, u_int16_t queue_num)
|
||||
cpdef repeat(self)
|
||||
|
||||
cdef class NetfilterQueue:
|
||||
cdef object user_callback # User callback
|
||||
cdef nfq_handle *h # Handle to NFQueue library
|
||||
cdef nfq_q_handle *qh # A handle to the queue
|
||||
cdef u_int16_t af # Address family
|
||||
cdef packet_copy_size # Amount of packet metadata + data copied to buffer
|
||||
|
||||
cdef packet_copy_size # Amount of packet metadata + data copied to buffer
|
411
new_packet.pyx
411
new_packet.pyx
@ -1,411 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import socket
|
||||
|
||||
from libc.stdio cimport printf
|
||||
|
||||
# Constants for module users
|
||||
cdef int COPY_NONE = 0
|
||||
cdef int COPY_META = 1
|
||||
cdef int COPY_PACKET = 2
|
||||
|
||||
cdef u_int16_t DEFAULT_MAX_QUEUELEN = 1024
|
||||
cdef u_int16_t MaxPacketSize = 0xFFFF
|
||||
|
||||
# buffer size - metadata size
|
||||
cdef u_int16_t MaxCopySize = 4096 - 80
|
||||
|
||||
# Socket queue should hold max number of packets of copy size bytes
|
||||
# formula: DEF_MAX_QUEUELEN * (MaxCopySize+SockOverhead) / 2
|
||||
cdef u_int32_t SockRcvSize = 1024 * 4796 // 2
|
||||
|
||||
cdef object user_callback
|
||||
def set_user_callback(ref):
|
||||
'''Set required reference which will be called after packet data is parsed into C structs.'''
|
||||
global user_callback
|
||||
|
||||
user_callback = ref
|
||||
|
||||
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)
|
||||
|
||||
user_callback(packet, mark)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
cdef class CPacket:
|
||||
|
||||
def __cinit__(self):
|
||||
self._verdict_is_set = False
|
||||
self._mark = 0
|
||||
|
||||
# NOTE: this will be callback target for nfqueue
|
||||
cdef u_int32_t parse(self, nfq_q_handle *qh, nfq_data *nfa) nogil:
|
||||
|
||||
self._qh = qh
|
||||
self._nfa = nfa
|
||||
|
||||
self._hdr = nfq_get_msg_packet_hdr(nfa)
|
||||
self.id = ntohl(self._hdr.packet_id)
|
||||
# NOTE: these are not needed at this moment.
|
||||
# self.hw_protocol = ntohs(hdr.hw_protocol)
|
||||
# self.hook = hdr.hook
|
||||
|
||||
self.data_len = nfq_get_payload(self._nfa, &self.data)
|
||||
# TODO: figure this out. cant use no gil if its here.
|
||||
# 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)
|
||||
|
||||
self._mark = nfq_get_nfmark(nfa)
|
||||
|
||||
# splitting packet by tcp/ip layers
|
||||
self._parse()
|
||||
|
||||
return self._mark
|
||||
|
||||
# if (self.continue_condition):
|
||||
# self._before_exit()
|
||||
|
||||
cdef void _parse(self) nogil:
|
||||
|
||||
self.ip_header = <iphdr*>self.data
|
||||
|
||||
cdef u_int8_t iphdr_len
|
||||
|
||||
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 protohdr_len
|
||||
|
||||
cdef void *data = &self.data[iphdr_len]
|
||||
# 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*>&data
|
||||
|
||||
tcphdr_len = (self.tcp_header.th_off >> 4) & 15
|
||||
protohdr_len = tcphdr_len * 4
|
||||
|
||||
# NOTE: tshoot print
|
||||
printf('TCP SPORT=%f\n', <double>self.tcp_header.th_sport)
|
||||
|
||||
# self.cmbhdr_len = iphdr_len + tcphdr_len
|
||||
|
||||
elif (self.ip_header.protocol == IPPROTO_UDP):
|
||||
|
||||
self.udp_header = <udphdr*>&data
|
||||
|
||||
protohdr_len = iphdr_len + 8
|
||||
|
||||
# self.cmbhdr_len = iphdr_len + 8
|
||||
|
||||
elif (self.ip_header.protocol == IPPROTO_ICMP):
|
||||
|
||||
self.icmp_header = <icmphdr*>&data
|
||||
|
||||
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.'''
|
||||
|
||||
# TODO: figure out what to do about this. maybe just printf instead?
|
||||
if self._verdict_is_set:
|
||||
raise RuntimeWarning('Verdict already given for this packet.')
|
||||
|
||||
if self._mark:
|
||||
nfq_set_verdict2(
|
||||
self._qh, self.id, verdict, self._mark, self.data_len, self.data
|
||||
)
|
||||
|
||||
else:
|
||||
nfq_set_verdict(
|
||||
self._qh, self.id, verdict, self.data_len, self.data
|
||||
)
|
||||
|
||||
self._verdict_is_set = True
|
||||
|
||||
cdef double get_timestamp(self):
|
||||
|
||||
return self.timestamp.tv_sec + (self.timestamp.tv_usec / 1000000.0)
|
||||
|
||||
cpdef update_mark(self, u_int32_t mark):
|
||||
'''Modifies the running mark of the packet.'''
|
||||
|
||||
self._mark = mark
|
||||
|
||||
cpdef accept(self):
|
||||
'''Accept the packet.'''
|
||||
|
||||
self.verdict(NF_ACCEPT)
|
||||
|
||||
cpdef drop(self):
|
||||
'''Drop the packet.'''
|
||||
|
||||
self.verdict(NF_DROP)
|
||||
|
||||
cpdef forward(self, u_int16_t queue_num):
|
||||
'''Send the packet to a different queue.'''
|
||||
|
||||
cdef u_int32_t forward_to_queue
|
||||
|
||||
forward_to_queue = queue_num << 16 | NF_QUEUE
|
||||
|
||||
self.verdict(forward_to_queue)
|
||||
|
||||
cpdef repeat(self):
|
||||
'''Repeat the packet.'''
|
||||
|
||||
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 = (
|
||||
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:
|
||||
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 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(<char*>hw_addr, 6)
|
||||
|
||||
hw_info = (
|
||||
in_interface,
|
||||
out_interface,
|
||||
mac_addr,
|
||||
self.get_timestamp(),
|
||||
)
|
||||
|
||||
return hw_info
|
||||
|
||||
def get_raw_packet(self):
|
||||
'''Return layer 3-7 of packet data.'''
|
||||
|
||||
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.'''
|
||||
|
||||
cdef tuple ip_header
|
||||
|
||||
ip_header = (
|
||||
self.ip_header.ver_ihl,
|
||||
self.ip_header.tos,
|
||||
ntohs(self.ip_header.tot_len),
|
||||
ntohs(self.ip_header.id),
|
||||
ntohs(self.ip_header.frag_off),
|
||||
self.ip_header.ttl,
|
||||
self.ip_header.protocol,
|
||||
ntohs(self.ip_header.check),
|
||||
ntohl(self.ip_header.saddr),
|
||||
ntohl(self.ip_header.daddr),
|
||||
)
|
||||
|
||||
return ip_header
|
||||
|
||||
def get_proto_header(self):
|
||||
'''Return layer4 of packet data as a tuple converted directly from C struct.'''
|
||||
|
||||
cdef tuple proto_header
|
||||
|
||||
if (self.ip_header.protocol == IPPROTO_TCP):
|
||||
|
||||
proto_header = (
|
||||
ntohs(self.tcp_header.th_sport),
|
||||
ntohs(self.tcp_header.th_dport),
|
||||
ntohl(self.tcp_header.th_seq),
|
||||
ntohl(self.tcp_header.th_ack),
|
||||
|
||||
self.tcp_header.th_off,
|
||||
|
||||
self.tcp_header.th_flags,
|
||||
ntohs(self.tcp_header.th_win),
|
||||
ntohs(self.tcp_header.th_sum),
|
||||
ntohs(self.tcp_header.th_urp),
|
||||
)
|
||||
|
||||
elif (self.ip_header.protocol == IPPROTO_UDP):
|
||||
|
||||
proto_header = (
|
||||
ntohs(self.udp_header.uh_sport),
|
||||
ntohs(self.udp_header.uh_dport),
|
||||
ntohs(self.udp_header.uh_ulen),
|
||||
ntohs(self.udp_header.uh_sum),
|
||||
)
|
||||
|
||||
elif (self.ip_header.protocol == IPPROTO_ICMP):
|
||||
|
||||
proto_header = (
|
||||
self.icmp_header.type,
|
||||
)
|
||||
|
||||
else:
|
||||
proto_header = ()
|
||||
|
||||
return proto_header
|
||||
|
||||
def get_payload(self):
|
||||
'''Return payload (>layer4) as Python bytes.'''
|
||||
|
||||
# cdef Py_ssize_t hdr_len = <Py_ssize_t>self.cmbhdr_len
|
||||
|
||||
cdef Py_ssize_t payload_len = self.data_len - self.cmbhdr_len
|
||||
|
||||
|
||||
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:
|
||||
'''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, 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.qh = nfq_create_queue(self.h, queue_num, <nfq_callback*>nf_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 = 0
|
||||
|
||||
while True:
|
||||
with nogil:
|
||||
rv = recv(fd, buf, sizeof(buf), recv_flags)
|
||||
|
||||
if (rv >= 0):
|
||||
nfq_handle_packet(self.h, buf, rv)
|
||||
|
||||
else:
|
||||
if errno != ENOBUFS:
|
||||
break
|
13
setup2.py
13
setup2.py
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from Cython.Build import cythonize
|
||||
from Cython.Distutils import build_ext
|
||||
|
||||
cmd = {'build_ext': build_ext}
|
||||
ext = Extension(
|
||||
'new_packet', 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