Initial commit
This commit is contained in:
commit
09a000ac50
|
@ -0,0 +1,2 @@
|
|||
*.so
|
||||
build/
|
|
@ -0,0 +1 @@
|
|||
v0.1, 12 May 2011 -- Initial release.
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2011, Kerkhoff Technologies, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Kerkhoff Technologies, Inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,5 @@
|
|||
CHANGES.txt
|
||||
LICENSE.txt
|
||||
README.txt
|
||||
netfilterqueue.pyx
|
||||
setup.py
|
|
@ -0,0 +1,69 @@
|
|||
==============
|
||||
NetfilterQueue
|
||||
==============
|
||||
|
||||
NetfilterQueue provides access to packets matched by an iptables rule in
|
||||
Linux. Packets so matched can be accepted, dropped, altered, or given a mark.
|
||||
|
||||
Libnetfilter_queue (the netfilter library, not this module) is part of the `Netfilter project <http://netfilter.org/projects/libnetfilter_queue/>`_.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Coming soon...
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
NetfilterQueue is a C extention module that links against libnetfilter_queue. Before installing, ensure you have:
|
||||
|
||||
1. A C compiler
|
||||
|
||||
2. Python development files
|
||||
|
||||
3. Libnetfilter_queue development files and associated dependencies
|
||||
|
||||
On Debian or Ubuntu, these files are install with::
|
||||
|
||||
sudo apt-get install build-essential python-dev libnetfilter-queue-dev
|
||||
|
||||
From PyPI
|
||||
---------
|
||||
|
||||
To install from PyPI by pip::
|
||||
|
||||
pip install netfilterqueue
|
||||
|
||||
From source
|
||||
-----------
|
||||
|
||||
To install from source::
|
||||
|
||||
wget http://pypi.python.org/packages/source/n/netfilterqueue/netfilterqueue-0.1.tar.gz
|
||||
tar -xvzf netfilterqueue-0.1.tar.gz
|
||||
cd netfilterqueue-0.1
|
||||
python setup.py install
|
||||
|
||||
Setup will use Cython if it is installed, regenerating the .c source from the .pyx before compiling the .so.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Coming soon...
|
||||
|
||||
Now route packets to the queue::
|
||||
|
||||
# iptables -I INPUT -p tcp --dport 80 -j NFQUEUE --queue-num 1
|
||||
|
||||
The only special part of the rule is the target. Rules can have any match and
|
||||
can be added to any table or chain.
|
||||
|
||||
Valid queue numbers are integers from 0 to 65,536 inclusive.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
TODO: fix this up
|
||||
|
||||
* compiled to max 2048-byte packets, so won't work on LO?
|
||||
* full API not implemented: omits set_payload(), interface methods, and what else?
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,191 @@
|
|||
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 "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
|
||||
|
||||
# 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 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
|
||||
|
||||
cdef extern from "netinet/in.h":
|
||||
u_int32_t ntohl (u_int32_t __netlong) nogil
|
||||
u_int16_t ntohs (u_int16_t __netshort) nogil
|
||||
u_int32_t htonl (u_int32_t __hostlong) nogil
|
||||
u_int16_t htons (u_int16_t __hostshort) nogil
|
||||
|
||||
cdef extern from "libnfnetlink/linux_nfnetlink.h":
|
||||
struct nfgenmsg:
|
||||
u_int8_t nfgen_family
|
||||
u_int8_t version
|
||||
u_int16_t res_id
|
||||
|
||||
cdef extern from "libnetfilter_queue/linux_nfnetlink_queue.h":
|
||||
enum nfqnl_config_mode:
|
||||
NFQNL_COPY_NONE
|
||||
NFQNL_COPY_META
|
||||
NFQNL_COPY_PACKET
|
||||
struct nfqnl_msg_packet_hdr:
|
||||
u_int32_t packet_id
|
||||
u_int16_t hw_protocol
|
||||
u_int8_t hook
|
||||
|
||||
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)
|
||||
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_verdict_mark(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, char **data)
|
||||
int nfq_get_timestamp(nfq_data *nfad, timeval *tv)
|
||||
nfqnl_msg_packet_hw *nfq_get_packet_hw(nfq_data *nfad)
|
||||
|
||||
# Dummy defines from linux/socket.h:
|
||||
cdef enum: # Protocol families, same as address families.
|
||||
PF_INET = 2
|
||||
PF_INET6 = 10
|
||||
|
||||
cdef extern from "sys/socket.h":
|
||||
ssize_t recv(int __fd, void *__buf, size_t __n, int __flags) nogil
|
||||
|
||||
# Dummy defines from linux/netfilter.h
|
||||
cdef enum:
|
||||
NF_DROP
|
||||
NF_ACCEPT
|
||||
NF_STOLEN
|
||||
NF_QUEUE
|
||||
NF_REPEAT
|
||||
NF_STOP
|
||||
NF_MAX_VERDICT = NF_STOP
|
||||
|
||||
cdef class NFPacket:
|
||||
cdef nfq_q_handle *_qh
|
||||
cdef nfq_data *_nfa
|
||||
cdef nfqnl_msg_packet_hdr *_hdr
|
||||
cdef bint _verdict_is_set # True if verdict has been issued,
|
||||
# false otherwise
|
||||
cdef bint _mark_is_set # True if a mark has been given, false otherwise
|
||||
cdef u_int32_t _given_mark # Mark given to packet
|
||||
cdef unsigned char *_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
|
||||
|
||||
# Packet details:
|
||||
cdef Py_ssize_t payload_len
|
||||
cdef readonly char *payload
|
||||
cdef timeval timestamp
|
||||
# TODO: implement these
|
||||
#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_int8_t verdict)
|
||||
cpdef Py_ssize_t get_payload_len(self)
|
||||
cpdef double get_timestamp(self)
|
||||
cpdef set_mark(self, u_int32_t mark)
|
||||
cpdef accept(self)
|
||||
cpdef drop(self)
|
||||
|
||||
cdef class NFQueue:
|
||||
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
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
## cython: profile=True
|
||||
import socket
|
||||
|
||||
# Constants for module users
|
||||
COPY_NONE = 1
|
||||
COPY_META = 2
|
||||
COPY_PACKET = 3
|
||||
|
||||
# Packet copying defaults
|
||||
DEF MaxPacketSize = 0xFFFF
|
||||
DEF BufferSize = 4096
|
||||
DEF MetadataSize = 80
|
||||
DEF MaxCopySize = BufferSize - MetadataSize
|
||||
|
||||
cdef int global_callback(nfq_q_handle *qh, nfgenmsg *nfmsg,
|
||||
nfq_data *nfa, void *data) with gil:
|
||||
"""
|
||||
Create an NFPacket and pass it to appropriate Python/Cython callback.
|
||||
|
||||
Working in a Python or Cython callback, rather than directly in this
|
||||
callback, results in a ~7% performance hit.
|
||||
"""
|
||||
# cdef nfqnl_msg_packet_hdr *_hdr = nfq_get_msg_packet_hdr(nfa)
|
||||
# cdef u_int32_t id = ntohl(_hdr.packet_id)
|
||||
# nfq_set_verdict(
|
||||
# qh,
|
||||
# id,
|
||||
# NF_ACCEPT,
|
||||
# 0, # TODO: adapt to use self._given_payload
|
||||
# NULL # TODO: adapt to use self._given_payload
|
||||
# )
|
||||
packet = NFPacket()
|
||||
packet.set_nfq_data(qh, nfa)
|
||||
(<NFQueue>data).handle(packet)
|
||||
return 1
|
||||
|
||||
cdef class NFPacket:
|
||||
"""A packet received from NFQueue."""
|
||||
def __cinit__(self):
|
||||
self._verdict_is_set = False
|
||||
self._mark_is_set = False
|
||||
self._given_payload = NULL
|
||||
|
||||
def __str__(self):
|
||||
cdef iphdr *hdr = <iphdr*>self.payload
|
||||
protocol = "Unknown protocol"
|
||||
for name in filter(lambda x: x.startswith("IPPROTO"), dir(socket)):
|
||||
if getattr(socket, name) == hdr.protocol:
|
||||
protocol = name[8:]
|
||||
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.")
|
||||
|
||||
nfq_get_timestamp(self._nfa, &self.timestamp)
|
||||
|
||||
cdef void verdict(self, u_int8_t verdict):
|
||||
"""Call appropriate set_verdict... function on packet."""
|
||||
#if self._verdict_is_set:
|
||||
# raise RuntimeWarning("Verdict already given for this packet.")
|
||||
|
||||
if self._mark_is_set:
|
||||
nfq_set_verdict_mark( # TODO: make this use nfq_set_verdict2 if available on system
|
||||
self._qh,
|
||||
self.id,
|
||||
verdict,
|
||||
htonl(self._given_mark),
|
||||
0, # TODO: adapt to use self._given_payload
|
||||
NULL # TODO: adapt to use self._given_payload
|
||||
)
|
||||
else:
|
||||
nfq_set_verdict(
|
||||
self._qh,
|
||||
self.id,
|
||||
verdict,
|
||||
0, # TODO: adapt to use self._given_payload
|
||||
NULL # TODO: adapt to use self._given_payload
|
||||
)
|
||||
|
||||
#self._verdict_is_set = True
|
||||
|
||||
def get_payload(self):
|
||||
cdef object py_string = PyString_FromStringAndSize(self.payload, self.payload_len)
|
||||
return py_string
|
||||
|
||||
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)
|
||||
|
||||
# TODO: implement this
|
||||
#cpdef set_payload(self, unsigned char *payload):
|
||||
# """Set the new payload of this packet."""
|
||||
# self._given_payload = payload
|
||||
|
||||
cpdef set_mark(self, u_int32_t mark):
|
||||
self._given_mark = mark
|
||||
self._mark_is_set = True
|
||||
|
||||
cpdef accept(self):
|
||||
"""Accept the packet."""
|
||||
self.verdict(NF_ACCEPT)
|
||||
|
||||
cpdef drop(self):
|
||||
"""Drop the packet."""
|
||||
self.verdict(NF_DROP)
|
||||
|
||||
cdef class NFQueue:
|
||||
"""Handle a single numbered queue."""
|
||||
def __cinit__(self, *args, **kwargs):
|
||||
if "af" in kwargs:
|
||||
self.af = kwargs["af"]
|
||||
else:
|
||||
self.af = socket.AF_INET
|
||||
|
||||
self.h = nfq_open()
|
||||
if self.h == NULL:
|
||||
raise OSError("Failed to open NFQueue.")
|
||||
nfq_unbind_pf(self.h, self.af) # This does NOT kick out previous
|
||||
# running queues
|
||||
if nfq_bind_pf(self.h, self.af) < 0:
|
||||
raise OSError("Failed to bind family %s." % 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, object handler, int queue_num, u_int32_t maxlen, u_int8_t mode=NFQNL_COPY_PACKET, u_int32_t range=MaxPacketSize):
|
||||
"""Create a new queue with the given callback function."""
|
||||
self.qh = nfq_create_queue(self.h, queue_num, <nfq_callback*>global_callback, <void*>handler)
|
||||
if self.qh == NULL:
|
||||
raise OSError("Failed to create queue %s." % 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, maxlen)
|
||||
|
||||
def unbind(self):
|
||||
"""Destroy the queue."""
|
||||
if self.qh != NULL:
|
||||
nfq_destroy_queue(self.qh)
|
||||
# See warning about nfq_unbind_pf in __dealloc__ above.
|
||||
|
||||
def run(self):
|
||||
"""Begin accepting packets."""
|
||||
cdef int fd = nfq_fd(self.h)
|
||||
cdef char buf[BufferSize]
|
||||
cdef int rv
|
||||
with nogil:
|
||||
rv = recv(fd, buf, sizeof(buf), 0)
|
||||
while rv >= 0:
|
||||
nfq_handle_packet(self.h, buf, rv)
|
||||
with nogil:
|
||||
rv = recv(fd, buf, sizeof(buf), 0)
|
||||
|
||||
def handle(self, NFPacket packet):
|
||||
"""Handle a single packet. User-defined classes should override this."""
|
||||
packet.accept()
|
|
@ -0,0 +1,45 @@
|
|||
from distutils.core import setup, Extension
|
||||
|
||||
VERSION = "0.1"
|
||||
|
||||
try:
|
||||
# Use Cython
|
||||
from Cython.Distutils import build_ext
|
||||
ext = Extension(
|
||||
"netfilterqueue",
|
||||
sources=["netfilterqueue.pyx",],
|
||||
libraries=["netfilter_queue"],
|
||||
)
|
||||
except ImportError:
|
||||
# No Cython
|
||||
build_ext = None
|
||||
ext = Extension(
|
||||
"netfilterqueue",
|
||||
sources = ["netfilterqueue.c"],
|
||||
libraries=["netfilter_queue"],
|
||||
)
|
||||
|
||||
setup(
|
||||
cmdclass = {"build_ext": build_ext},
|
||||
ext_modules = [ext],
|
||||
name="NetfilterQueue",
|
||||
version=VERSION,
|
||||
license="BSD",
|
||||
author="Matthew Fox",
|
||||
author_email="matthew@kerkhofftech.ca",
|
||||
url="https://github.com/kti/python-netfilter_queue",
|
||||
description="Python bindings for libnetfilter_queue",
|
||||
long_description=open("README.txt").read(),
|
||||
download_url="http://pypi.python.org/packages/source/N/NetfilterQueue/NetfilterQueue-%s.tar.gz" % VERSION,
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Topic :: System :: Networking",
|
||||
"Topic :: Security",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Telecommunications Industry",
|
||||
"Programming Language :: Cython",
|
||||
"Programming Language :: Python :: 2",
|
||||
]
|
||||
)
|
Loading…
Reference in New Issue