Skip to content
This repository was archived by the owner on Nov 10, 2023. It is now read-only.

Commit 668104b

Browse files
committed
Initial commit
1 parent 16c7a58 commit 668104b

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed

lib.go

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
package nfq
2+
3+
/*
4+
#cgo pkg-config: libnetfilter_queue
5+
#include <netinet/in.h>
6+
#include <linux/netfilter.h>
7+
#include <libnetfilter_queue/libnetfilter_queue.h>
8+
9+
extern void queueCallback(struct nfq_q_handle *, struct nfgenmsg *, struct nfq_data *, void *);
10+
11+
static inline u_int32_t get_id(struct nfqnl_msg_packet_hdr *ph) {
12+
return ntohl(ph->packet_id);
13+
}
14+
*/
15+
import "C"
16+
import (
17+
"errors"
18+
"sync"
19+
"syscall"
20+
"unsafe"
21+
)
22+
23+
const (
24+
maxPacketSize = 0xffff
25+
)
26+
27+
var (
28+
registry sync.Map
29+
)
30+
31+
func register(qh *C.struct_nfq_q_handle, nfq *NFQ) {
32+
registry.Store(qh, nfq)
33+
}
34+
35+
func unregister(qh *C.struct_nfq_q_handle) {
36+
registry.Delete(qh)
37+
}
38+
39+
func get(qh *C.struct_nfq_q_handle) *NFQ {
40+
if nfq, ok := registry.Load(qh); ok {
41+
return nfq.(*NFQ)
42+
}
43+
return nil
44+
}
45+
46+
// Packet ...
47+
type Packet struct {
48+
shared *packetShared
49+
data []byte
50+
mark uint32
51+
}
52+
53+
type packetShared struct {
54+
nfq *NFQ
55+
id uint32
56+
mx sync.Mutex
57+
err error
58+
}
59+
60+
func newPacket(nfq *NFQ, id uint32, mark uint32, data []byte) Packet {
61+
shared := &packetShared{nfq: nfq, id: id}
62+
return Packet{shared, data, mark}
63+
}
64+
65+
// Mark ...
66+
func (p Packet) Mark() uint32 {
67+
return p.mark
68+
}
69+
70+
// Data ...
71+
func (p Packet) Data() []byte {
72+
return p.data
73+
}
74+
75+
// WithMark ...
76+
func (p Packet) WithMark(mark uint32) Packet {
77+
p.mark = mark
78+
return p
79+
}
80+
81+
// WithData ...
82+
func (p Packet) WithData(data []byte) Packet {
83+
p.data = data
84+
return p
85+
}
86+
87+
// Accept ...
88+
func (p Packet) Accept() error {
89+
return p.setVerdict(C.NF_ACCEPT)
90+
}
91+
92+
// Drop ...
93+
func (p Packet) Drop() error {
94+
return p.setVerdict(C.NF_DROP)
95+
}
96+
97+
// Repeat ...
98+
func (p Packet) Repeat() error {
99+
return p.setVerdict(C.NF_REPEAT)
100+
}
101+
102+
// Queue ...
103+
func (p Packet) Queue(num uint16) error {
104+
verdict := (uint32(num) << 16) | C.NF_QUEUE
105+
return p.setVerdict(verdict)
106+
}
107+
108+
func (p Packet) setVerdict(verdict uint32) error {
109+
shared := p.shared
110+
shared.mx.Lock()
111+
defer shared.mx.Unlock()
112+
if shared.err != nil {
113+
return shared.err
114+
}
115+
116+
nfq := shared.nfq
117+
nfq.mx.RLock()
118+
defer nfq.mx.RUnlock()
119+
if nfq.closed {
120+
shared.err = errors.New("queue already closed")
121+
return shared.err
122+
}
123+
124+
var ptr *C.uchar
125+
if p.data != nil {
126+
ptr = (*C.uchar)(&p.data[0])
127+
}
128+
if C.nfq_set_verdict2(nfq.qh, C.u_int32_t(shared.id), C.u_int32_t(verdict), C.u_int32_t(p.mark), C.u_int32_t(len(p.data)), ptr) < 0 {
129+
shared.err = errors.New("nfq_set_verdict2() failed")
130+
return shared.err
131+
}
132+
shared.err = errors.New("verdict already set")
133+
return nil
134+
}
135+
136+
// NFQ ...
137+
type NFQ struct {
138+
h *C.struct_nfq_handle
139+
qh *C.struct_nfq_q_handle
140+
wfd int
141+
callback func(Packet)
142+
143+
closed bool
144+
mx sync.RWMutex
145+
wg sync.WaitGroup
146+
}
147+
148+
// New ...
149+
func New(num uint16, callback func(Packet)) (*NFQ, error) {
150+
h, err := open()
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
qh, err := createQueue(h, num)
156+
if err != nil {
157+
C.nfq_close(h)
158+
return nil, err
159+
}
160+
161+
rfd, wfd, err := pipe()
162+
if err != nil {
163+
C.nfq_destroy_queue(qh)
164+
C.nfq_close(h)
165+
return nil, err
166+
}
167+
168+
nfq := &NFQ{h: h, qh: qh, wfd: wfd, callback: callback}
169+
register(qh, nfq)
170+
nfq.wg.Add(1)
171+
go func() {
172+
defer nfq.wg.Done()
173+
defer syscall.Close(rfd)
174+
poll(h, rfd)
175+
}()
176+
177+
return nfq, nil
178+
}
179+
180+
// Close ...
181+
func (nfq *NFQ) Close() {
182+
nfq.mx.Lock()
183+
closed := nfq.closed
184+
nfq.closed = true
185+
nfq.mx.Unlock()
186+
187+
if !closed {
188+
unregister(nfq.qh)
189+
syscall.Close(nfq.wfd)
190+
191+
nfq.wg.Wait()
192+
193+
C.nfq_destroy_queue(nfq.qh)
194+
C.nfq_close(nfq.h)
195+
}
196+
}
197+
198+
func open() (*C.struct_nfq_handle, error) {
199+
h := C.nfq_open()
200+
if h == nil {
201+
return nil, errors.New("nfq_open() failed")
202+
}
203+
return h, nil
204+
}
205+
206+
func createQueue(h *C.struct_nfq_handle, num uint16) (*C.struct_nfq_q_handle, error) {
207+
qh := C.nfq_create_queue(h, C.u_int16_t(num), (*C.nfq_callback)(C.queueCallback), nil)
208+
if qh == nil {
209+
return nil, errors.New("nfq_create_queue() failed")
210+
}
211+
212+
if C.nfq_set_mode(qh, C.NFQNL_COPY_PACKET, maxPacketSize) < 0 {
213+
C.nfq_destroy_queue(qh)
214+
return nil, errors.New("nfq_set_mode() failed")
215+
}
216+
217+
return qh, nil
218+
}
219+
220+
//export queueCallback
221+
func queueCallback(qh *C.struct_nfq_q_handle, _ *C.struct_nfgenmsg, nfad *C.struct_nfq_data, _ unsafe.Pointer) {
222+
nfq := get(qh)
223+
if nfq == nil {
224+
return
225+
}
226+
227+
var payload *C.uchar
228+
229+
size := C.nfq_get_payload(nfad, &payload)
230+
if size < 0 {
231+
panic("nfq_get_payload() failed")
232+
}
233+
234+
ph := C.nfq_get_msg_packet_hdr(nfad)
235+
if ph == nil {
236+
panic("nfq_get_msg_packet_hdr() failed")
237+
}
238+
239+
id := uint32(C.get_id(ph))
240+
mark := uint32(C.nfq_get_nfmark(nfad))
241+
data := C.GoBytes(unsafe.Pointer(payload), size)
242+
packet := newPacket(nfq, id, mark, data)
243+
nfq.callback(packet)
244+
}
245+
246+
func pipe() (int, int, error) {
247+
pipe := make([]int, 2)
248+
if err := syscall.Pipe(pipe); err != nil {
249+
return 0, 0, err
250+
}
251+
return pipe[0], pipe[1], nil
252+
}
253+
254+
func poll(h *C.struct_nfq_handle, rfd int) error {
255+
buf := make([]byte, maxPacketSize)
256+
fd := int(C.nfq_fd(h))
257+
258+
epfd, err := syscall.EpollCreate1(0)
259+
if err != nil {
260+
return err
261+
}
262+
defer syscall.Close(epfd)
263+
264+
err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, rfd, &syscall.EpollEvent{Events: syscall.EPOLLIN, Fd: int32(rfd)})
265+
if err != nil {
266+
return err
267+
}
268+
err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, fd, &syscall.EpollEvent{Events: syscall.EPOLLIN, Fd: int32(fd)})
269+
if err != nil {
270+
return err
271+
}
272+
273+
events := make([]syscall.EpollEvent, 2)
274+
for {
275+
n, err := syscall.EpollWait(epfd, events, -1)
276+
if err != nil {
277+
return err
278+
}
279+
280+
for _, ev := range events[:n] {
281+
if ev.Fd == int32(rfd) {
282+
return nil
283+
}
284+
285+
if ev.Fd == int32(fd) {
286+
rv, _, err := syscall.Recvfrom(fd, buf, 0)
287+
if err != nil {
288+
return err
289+
}
290+
291+
ptr := (*C.char)(unsafe.Pointer(&buf[0]))
292+
if C.nfq_handle_packet(h, ptr, C.int(rv)) != 0 {
293+
return errors.New("nfq_handle_packet() failed")
294+
}
295+
}
296+
}
297+
}
298+
}

0 commit comments

Comments
 (0)