You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fw-daemon/nfqueue/nfqueue.go

181 lines
3.5 KiB

package nfqueue
/*
#cgo LDFLAGS: -lnetfilter_queue
#cgo CFLAGS: -Wall
#include "nfqueue.h"
*/
import "C"
import (
"net"
"os"
"runtime"
"sync"
"syscall"
"time"
"unsafe"
)
type nfQueue struct {
DefaultVerdict Verdict
Timeout time.Duration
qid uint16
h *C.struct_nfq_handle
//qh *C.struct_q_handle
qh *C.struct_nfq_q_handle
fd int
lk sync.Mutex
pktch chan *Packet
}
func NewNFQueue(qid uint16) (nfq *nfQueue) {
if os.Geteuid() != 0 {
}
if os.Geteuid() != 0 {
panic("Must be ran by root.")
}
nfq = &nfQueue{DefaultVerdict: ACCEPT, Timeout: time.Microsecond * 5, qid: qid}
return nfq
}
/*
This returns a channel that will receive packets,
the user then must call pkt.Accept() or pkt.Drop()
*/
func (this *nfQueue) Process() <-chan *Packet {
if this.h != nil {
return this.pktch
}
this.init()
go func() {
runtime.LockOSThread()
C.loop_for_packets(this.h)
}()
return this.pktch
}
func (this *nfQueue) init() {
var err error
if this.h, err = C.nfq_open(); err != nil || this.h == nil {
panic(err)
}
//if this.qh, err = C.nfq_create_queue(this.h, qid, C.get_cb(), unsafe.Pointer(nfq)); err != nil || this.qh == nil {
this.pktch = make(chan *Packet, 1)
if C.nfq_unbind_pf(this.h, C.AF_INET) < 0 {
this.Destroy()
panic("nfq_unbind_pf(AF_INET) failed, are you running root?.")
}
if C.nfq_unbind_pf(this.h, C.AF_INET6) < 0 {
this.Destroy()
panic("nfq_unbind_pf(AF_INET6) failed.")
}
if C.nfq_bind_pf(this.h, C.AF_INET) < 0 {
this.Destroy()
panic("nfq_bind_pf(AF_INET) failed.")
}
if C.nfq_bind_pf(this.h, C.AF_INET6) < 0 {
this.Destroy()
panic("nfq_bind_pf(AF_INET6) failed.")
}
if this.qh, err = C.create_queue(this.h, C.uint16_t(this.qid), unsafe.Pointer(this)); err != nil || this.qh == nil {
C.nfq_close(this.h)
panic(err)
}
this.fd = int(C.nfq_fd(this.h))
if C.nfq_set_mode(this.qh, C.NFQNL_COPY_PACKET, 0xffff) < 0 {
this.Destroy()
panic("nfq_set_mode(NFQNL_COPY_PACKET) failed.")
}
if C.nfq_set_queue_maxlen(this.qh, 1024*8) < 0 {
this.Destroy()
panic("nfq_set_queue_maxlen(1024 * 8) failed.")
}
}
func (this *nfQueue) Destroy() {
this.lk.Lock()
defer this.lk.Unlock()
if this.fd != 0 && this.Valid() {
syscall.Close(this.fd)
}
if this.qh != nil {
C.nfq_destroy_queue(this.qh)
this.qh = nil
}
if this.h != nil {
C.nfq_close(this.h)
this.h = nil
}
if this.pktch != nil {
close(this.pktch)
}
}
func (this *nfQueue) Valid() bool {
return this.h != nil && this.qh != nil
}
//export go_nfq_callback
func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32,
version, protocol, tos, ttl uint8, saddr, daddr unsafe.Pointer,
sport, dport, checksum uint16, payload_len uint32, payload, nfqptr unsafe.Pointer) (v uint32) {
var (
nfq = (*nfQueue)(nfqptr)
ipver = IPVersion(version)
ipsz = C.int(ipver.Size())
)
bs := C.GoBytes(payload, (C.int)(payload_len))
verdict := make(chan uint32, 1)
pkt := Packet{
QueueId: nfq.qid,
Id: id,
HWProtocol: hwproto,
Hook: hook,
Mark: *mark,
Payload: bs,
IPHeader: &IPHeader{
Version: ipver,
Protocol: IPProtocol(protocol),
Tos: tos,
TTL: ttl,
Src: net.IP(C.GoBytes(saddr, ipsz)),
Dst: net.IP(C.GoBytes(daddr, ipsz)),
},
TCPUDPHeader: &TCPUDPHeader{
SrcPort: sport,
DstPort: dport,
Checksum: checksum,
},
verdict: verdict,
}
nfq.pktch <- &pkt
select {
case v = <-pkt.verdict:
*mark = pkt.Mark
case <-time.After(nfq.Timeout):
v = uint32(nfq.DefaultVerdict)
}
return v
}