Merge pull request #93 from oremanj/inoutdev

Add Packet accessors for interface indices
This commit is contained in:
Joshua Oreman 2023-03-01 00:50:48 -07:00 committed by GitHub
commit 2b6849b5d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 12 deletions

View File

@ -15,13 +15,14 @@ jobs:
fail-fast: false
matrix:
python:
- '3.6'
- '3.7'
- '3.8'
- '3.9'
- '3.10'
- '3.11'
- 'pypy-3.7'
- 'pypy-3.8'
- 'pypy-3.9'
check_lint: ['0']
extra_name: ['']
include:

View File

@ -1,3 +1,6 @@
v1.1.0, unreleased
Add Packet accessors for {indev, outdev, physindev, physoutdev} interface indices
v1.0.0, 14 Jan 2022
Propagate exceptions raised by the user's packet callback
Avoid calls to the packet callback during queue unbinding

View File

@ -217,6 +217,18 @@ Objects of this type are passed to your callback.
into our queue. Values 0 through 4 correspond to PREROUTING, INPUT,
FORWARD, OUTPUT, and POSTROUTING respectively.
``Packet.indev``, ``Packet.outdev``, ``Packet.physindev``, ``Packet.physoutdev``
The interface indices on which the packet arrived (``indev``) or is slated
to depart (``outdev``). These are integers, which can be converted to
names like "eth0" by using ``socket.if_indextoname()``. Zero means
no interface is applicable, either because the packet was locally generated
or locally received, or because the interface information wasn't available
when the packet was queued (for example, ``PREROUTING`` rules don't yet
know the ``outdev``). If the ``indev`` or ``outdev`` refers to a bridge
device, then the corresponding ``physindev`` or ``physoutdev`` will name
the bridge member on which the actual traffic occurred; otherwise
``physindev`` and ``physoutdev`` will be zero.
``Packet.retain()``
Allocate a copy of the packet payload for use after the callback
has returned. ``get_payload()`` will raise an exception at that

View File

@ -153,7 +153,11 @@ cdef extern from "libnetfilter_queue/libnetfilter_queue.h":
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)
int nfq_get_nfmark(nfq_data *nfad)
u_int32_t nfq_get_indev(nfq_data *nfad)
u_int32_t nfq_get_outdev(nfq_data *nfad)
u_int32_t nfq_get_physindev(nfq_data *nfad)
u_int32_t nfq_get_physoutdev(nfq_data *nfad)
nfnl_handle *nfq_nfnlh(nfq_handle *h)
# Dummy defines from linux/socket.h:
@ -184,8 +188,7 @@ cdef class NetfilterQueue:
cdef class Packet:
cdef NetfilterQueue _queue
cdef bint _verdict_is_set # True if verdict has been issued,
# false otherwise
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 bint _hwaddr_is_set
cdef bint _timestamp_is_set
@ -204,13 +207,10 @@ cdef class Packet:
cdef unsigned char *payload
cdef timeval timestamp
cdef u_int8_t hw_addr[8]
# TODO: implement these
#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 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, NetfilterQueue queue, nfq_data *nfa)
cdef drop_refs(self)

View File

@ -11,6 +11,11 @@ class Packet:
hw_protocol: int
id: int
mark: int
# These are ifindexes, pass to socket.if_indextoname() to get names:
indev: int
outdev: int
physindev: int
physoutdev: int
def get_hw(self) -> Optional[bytes]: ...
def get_payload(self) -> bytes: ...
def get_payload_len(self) -> int: ...

View File

@ -99,6 +99,10 @@ cdef class Packet:
nfq_get_timestamp(nfa, &self.timestamp)
self.mark = nfq_get_nfmark(nfa)
self.indev = nfq_get_indev(nfa)
self.outdev = nfq_get_outdev(nfa)
self.physindev = nfq_get_physindev(nfa)
self.physoutdev = nfq_get_physoutdev(nfa)
cdef drop_refs(self):
"""

View File

@ -119,11 +119,13 @@ async def test_mark_repeat(harness):
assert t0 < timestamps[0] < t1
async def test_hwaddr(harness):
async def test_hwaddr_and_inoutdev(harness):
hwaddrs = []
inoutdevs = []
def cb(pkt):
hwaddrs.append((pkt.get_hw(), pkt.hook, pkt.get_payload()[28:]))
inoutdevs.append((pkt.indev, pkt.outdev))
pkt.accept()
queue_num, nfq = harness.bind_queue(cb)
@ -162,6 +164,20 @@ async def test_hwaddr(harness):
(None, OUTPUT, b"four"),
]
if sys.implementation.name != "pypy":
# pypy doesn't appear to provide if_nametoindex()
iface1 = socket.if_nametoindex("veth1")
iface2 = socket.if_nametoindex("veth2")
else:
iface1, iface2 = inoutdevs[0]
assert 0 != iface1 != iface2 != 0
assert inoutdevs == [
(iface1, iface2),
(iface1, iface2),
(0, iface2),
(0, iface2),
]
async def test_errors(harness):
with pytest.warns(RuntimeWarning, match="rcvbuf limit is") as record: