Add run_socket, which uses socket.recv so that gevent can monkeypatch it. Fixes #5

This commit is contained in:
Matt Fox 2016-06-27 23:48:53 -07:00
parent 2960dc3401
commit ccb353011a
4 changed files with 970 additions and 245 deletions

View File

@ -29,6 +29,26 @@ it. ::
nfqueue.unbind()
You can also make your own socket so that it can be used with gevent, for example. ::
from netfilterqueue import NetfilterQueue
import socket
def print_and_accept(pkt):
print(pkt)
pkt.accept()
nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
s = socket.fromfd(nfqueue.get_fd(), socket.AF_UNIX, socket.SOCK_STREAM)
try:
nfqueue.run_socket(s)
except KeyboardInterrupt:
print('')
s.close()
nfqueue.unbind()
To send packets destined for your LAN to the script, type something like::
iptables -I INPUT -d 192.168.0.0/24 -j NFQUEUE --queue-num 1
@ -107,6 +127,12 @@ a call to ``bind``, then start receiving packets with a call to ``run``.
block=False to let your thread continue. You can get the file descriptor
of the socket with the ``get_fd`` method.
``QueueHandler.run_socket(socket)``
Send packets to your callback, but use the supplied socket instead of
recv, so that, for example, gevent can monkeypatch it. You can make a
socket with ``socket.fromfd(nfqueue.get_fd(), socket.AF_UNIX, socket.SOCK_STREAM)``
and optionally make it non-blocking with ``socket.setblocking(False)``.
Packet objects
--------------
@ -191,8 +217,6 @@ The fields are:
Limitations
===========
More details coming soon...
* Compiled with a 4096-byte buffer for packets, so it probably won't work on
loopback or Ethernet with jumbo packets. If this is a problem, either lower
MTU on your loopback, disable jumbo packets, or get Cython,

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,8 @@ cdef extern from "<errno.h>":
# dummy defines from asm-generic/errno.h:
cdef enum:
EAGAIN = 11 # Try again
EWOULDBLOCK = EAGAIN
ENOBUFS = 105 # No buffer space available
cdef extern from "netinet/ip.h":

View File

@ -24,6 +24,7 @@ DEF SockCopySize = MaxCopySize + SockOverhead
# Socket queue should hold max number of packets of copysize bytes
DEF SockRcvSize = DEFAULT_MAX_QUEUELEN * SockCopySize / 2
import socket
cimport cpython.version
cdef int global_callback(nfq_q_handle *qh, nfgenmsg *nfmsg,
@ -31,7 +32,6 @@ cdef int global_callback(nfq_q_handle *qh, nfgenmsg *nfmsg,
"""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)
@ -195,8 +195,8 @@ cdef class NetfilterQueue:
return nfq_fd(self.h)
def run(self, block=True):
"""Begin accepting packets."""
cdef int fd = nfq_fd(self.h)
"""Accept packets using recv."""
cdef int fd = self.get_fd()
cdef char buf[BufferSize]
cdef int rv
cdef int recv_flags
@ -211,6 +211,26 @@ cdef class NetfilterQueue:
if errno != ENOBUFS:
break
def run_socket(self, s):
"""Accept packets using socket.recv so that, for example, gevent can monkeypatch it."""
try:
while True:
buf = s.recv(BufferSize)
rv = len(buf)
if rv >= 0:
nfq_handle_packet(self.h, buf, rv)
else:
break
except socket.error as e:
err = e.args[0]
if 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.
pass
else:
# This is bad. Let the caller handle it.
raise e
PROTOCOLS = {
0: "HOPOPT",
1: "ICMP",