diff --git a/firewall/interception/nfqexp/nfqexp.go b/firewall/interception/nfq/nfqexp.go similarity index 98% rename from firewall/interception/nfqexp/nfqexp.go rename to firewall/interception/nfq/nfqexp.go index 4c0d7e45..2c32c24c 100644 --- a/firewall/interception/nfqexp/nfqexp.go +++ b/firewall/interception/nfq/nfqexp.go @@ -1,7 +1,7 @@ // +build linux -// Package nfqexp contains a nfqueue library experiment. -package nfqexp +// Package nfq contains a nfqueue library experiment. +package nfq import ( "context" diff --git a/firewall/interception/nfqexp/packet.go b/firewall/interception/nfq/packet.go similarity index 99% rename from firewall/interception/nfqexp/packet.go rename to firewall/interception/nfq/packet.go index 620eb52d..bfaa0981 100644 --- a/firewall/interception/nfqexp/packet.go +++ b/firewall/interception/nfq/packet.go @@ -1,6 +1,6 @@ // +build linux -package nfqexp +package nfq import ( "errors" diff --git a/firewall/interception/nfqueue/README.md b/firewall/interception/nfqueue/README.md deleted file mode 100644 index 6aef746e..00000000 --- a/firewall/interception/nfqueue/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Parts of this package (this directory) are forked from the go-nfqueue repo: https://github.com/OneOfOne/go-nfqueue -These portions are copyrighted by Ahmed W. -The fork commit is (with high certainty): https://github.com/OneOfOne/go-nfqueue/commit/3bdd8bdfd98a1ed51119f9cf7494162484dfbe7c diff --git a/firewall/interception/nfqueue/doc.go b/firewall/interception/nfqueue/doc.go deleted file mode 100644 index 9fc4ab21..00000000 --- a/firewall/interception/nfqueue/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// +build linux - -// Package nfqueue provides network interception capabilities on linux via iptables nfqueue. -package nfqueue diff --git a/firewall/interception/nfqueue/multiqueue.go b/firewall/interception/nfqueue/multiqueue.go deleted file mode 100644 index ce60b8f1..00000000 --- a/firewall/interception/nfqueue/multiqueue.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build linux - -package nfqueue - -// suspended for now - -// import ( -// "sync" -// -// "github.com/safing/portmaster/network/packet" -// ) -// -// type multiQueue struct { -// qs []*NFQueue -// } -// -// func NewMultiQueue(min, max uint16) (mq *multiQueue) { -// mq = &multiQueue{make([]*NFQueue, 0, max-min)} -// for i := min; i < max; i++ { -// mq.qs = append(mq.qs, NewNFQueue(i)) -// } -// return mq -// } -// -// func (mq *multiQueue) Process() <-chan packet.Packet { -// var ( -// wg sync.WaitGroup -// out = make(chan packet.Packet, len(mq.qs)) -// ) -// for _, q := range mq.qs { -// wg.Add(1) -// go func(ch <-chan packet.Packet) { -// for pkt := range ch { -// out <- pkt -// } -// wg.Done() -// }(q.Process()) -// } -// go func() { -// wg.Wait() -// close(out) -// }() -// return out -// } -// func (mq *multiQueue) Destroy() { -// for _, q := range mq.qs { -// q.Destroy() -// } -// } diff --git a/firewall/interception/nfqueue/nfqueue.c b/firewall/interception/nfqueue/nfqueue.c deleted file mode 100644 index 93a271c4..00000000 --- a/firewall/interception/nfqueue/nfqueue.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "nfqueue.h" -#include "_cgo_export.h" - - -int nfqueue_cb_new(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { - - struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfa); - - if(ph == NULL) { - return 1; - } - - int id = ntohl(ph->packet_id); - - unsigned char * payload; - unsigned char * saddr, * daddr; - uint16_t sport = 0, dport = 0, checksum = 0; - uint32_t mark = nfq_get_nfmark(nfa); - - int len = nfq_get_payload(nfa, &payload); - - unsigned char * origpayload = payload; - int origlen = len; - - if(len < sizeof(struct iphdr)) { - return 0; - } - - struct iphdr * ip = (struct iphdr *) payload; - - if(ip->version == 4) { - uint32_t ipsz = (ip->ihl << 2); - if(len < ipsz) { - return 0; - } - len -= ipsz; - payload += ipsz; - - saddr = (unsigned char *)&ip->saddr; - daddr = (unsigned char *)&ip->daddr; - - if(ip->protocol == IPPROTO_TCP) { - if(len < sizeof(struct tcphdr)) { - return 0; - } - struct tcphdr *tcp = (struct tcphdr *) payload; - uint32_t tcpsz = (tcp->doff << 2); - if(len < tcpsz) { - return 0; - } - len -= tcpsz; - payload += tcpsz; - - sport = ntohs(tcp->source); - dport = ntohs(tcp->dest); - checksum = ntohs(tcp->check); - } else if(ip->protocol == IPPROTO_UDP) { - if(len < sizeof(struct udphdr)) { - return 0; - } - struct udphdr *u = (struct udphdr *) payload; - len -= sizeof(struct udphdr); - payload += sizeof(struct udphdr); - - sport = ntohs(u->source); - dport = ntohs(u->dest); - checksum = ntohs(u->check); - } - } else { - struct ipv6hdr *ip6 = (struct ipv6hdr*) payload; - saddr = (unsigned char *)&ip6->saddr; - daddr = (unsigned char *)&ip6->daddr; - //ipv6 - } - //pass everything we can and let Go handle it, I'm not a big fan of C - uint32_t verdict = go_nfq_callback(id, ntohs(ph->hw_protocol), ph->hook, &mark, ip->version, ip->protocol, - ip->tos, ip->ttl, saddr, daddr, sport, dport, checksum, origlen, origpayload, data); - return nfq_set_verdict2(qh, id, verdict, mark, 0, NULL); -} - -void loop_for_packets(struct nfq_handle *h) { - int fd = nfq_fd(h); - char buf[65535] __attribute__ ((aligned)); - int rv; - while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) { - nfq_handle_packet(h, buf, rv); - } -} diff --git a/firewall/interception/nfqueue/nfqueue.go b/firewall/interception/nfqueue/nfqueue.go deleted file mode 100644 index 545bcf2f..00000000 --- a/firewall/interception/nfqueue/nfqueue.go +++ /dev/null @@ -1,195 +0,0 @@ -// +build linux - -package nfqueue - -/* -#cgo LDFLAGS: -lnetfilter_queue -#cgo CFLAGS: -Wall -#include "nfqueue.h" -*/ -import "C" - -import ( - "errors" - "fmt" - "os" - "runtime" - "sync" - "syscall" - "time" - "unsafe" - - "github.com/safing/portbase/log" - "github.com/safing/portmaster/network/packet" -) - -var queues map[uint16]*NFQueue - -func init() { - queues = make(map[uint16]*NFQueue) -} - -// NFQueue holds a Linux NFQ Handle and associated information. -//nolint:maligned // FIXME -type NFQueue struct { - DefaultVerdict uint32 - Timeout time.Duration - qid uint16 - qidptr *uint16 - h *C.struct_nfq_handle - //qh *C.struct_q_handle - qh *C.struct_nfq_q_handle - fd int - lk sync.Mutex - - Packets chan packet.Packet -} - -// NewNFQueue initializes a new netfilter queue. -func NewNFQueue(qid uint16) (nfq *NFQueue, err error) { - if os.Geteuid() != 0 { - return nil, errors.New("must be root to intercept packets") - } - nfq = &NFQueue{DefaultVerdict: NFQ_DROP, Timeout: 3000 * time.Millisecond, qid: qid, qidptr: &qid} - queues[nfq.qid] = nfq - - err = nfq.init() - if err != nil { - return nil, err - } - - go func() { - runtime.LockOSThread() - C.loop_for_packets(nfq.h) - }() - - return nfq, nil -} - -// PacketChannel returns a packet channel -func (nfq *NFQueue) PacketChannel() <-chan packet.Packet { - return nfq.Packets -} - -func (nfq *NFQueue) init() error { - var err error - if nfq.h, err = C.nfq_open(); err != nil || nfq.h == nil { - return fmt.Errorf("could not open nfqueue: %s", err) - } - - //if nfq.qh, err = C.nfq_create_queue(nfq.h, qid, C.get_cb(), unsafe.Pointer(nfq)); err != nil || nfq.qh == nil { - - nfq.Packets = make(chan packet.Packet, 1) - - if C.nfq_unbind_pf(nfq.h, C.AF_INET) < 0 { - nfq.Destroy() - return errors.New("nfq_unbind_pf(AF_INET) failed, are you root?") - } - if C.nfq_unbind_pf(nfq.h, C.AF_INET6) < 0 { - nfq.Destroy() - return errors.New("nfq_unbind_pf(AF_INET6) failed") - } - - if C.nfq_bind_pf(nfq.h, C.AF_INET) < 0 { - nfq.Destroy() - return errors.New("nfq_bind_pf(AF_INET) failed") - } - if C.nfq_bind_pf(nfq.h, C.AF_INET6) < 0 { - nfq.Destroy() - return errors.New("nfq_bind_pf(AF_INET6) failed") - } - - if nfq.qh, err = C.create_queue(nfq.h, C.uint16_t(nfq.qid)); err != nil || nfq.qh == nil { - C.nfq_close(nfq.h) - return fmt.Errorf("could not create queue: %s", err) - } - - nfq.fd = int(C.nfq_fd(nfq.h)) - - if C.nfq_set_mode(nfq.qh, C.NFQNL_COPY_PACKET, 0xffff) < 0 { - nfq.Destroy() - return errors.New("nfq_set_mode(NFQNL_COPY_PACKET) failed") - } - if C.nfq_set_queue_maxlen(nfq.qh, 1024*8) < 0 { - nfq.Destroy() - return errors.New("nfq_set_queue_maxlen(1024 * 8) failed") - } - - return nil -} - -// Destroy closes all the nfqueues. -func (nfq *NFQueue) Destroy() { - nfq.lk.Lock() - defer nfq.lk.Unlock() - - if nfq.fd != 0 && nfq.Valid() { - syscall.Close(nfq.fd) - } - if nfq.qh != nil { - C.nfq_destroy_queue(nfq.qh) - nfq.qh = nil - } - if nfq.h != nil { - C.nfq_close(nfq.h) - nfq.h = nil - } - - // TODO: don't close, we're exiting anyway - // if nfq.Packets != nil { - // close(nfq.Packets) - // } -} - -// Valid returns whether the NFQueue is still valid. -func (nfq *NFQueue) Valid() bool { - return nfq.h != nil && nfq.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, payloadLen uint32, payload, data unsafe.Pointer) (v uint32) { - - qidptr := (*uint16)(data) - qid := *qidptr - - // nfq := (*NFQueue)(nfqptr) - bs := C.GoBytes(payload, (C.int)(payloadLen)) - - verdict := make(chan uint32, 1) - pkt := Packet{ - QueueID: qid, - ID: id, - HWProtocol: hwproto, - Hook: hook, - Mark: *mark, - verdict: verdict, - // StartedHandling: time.Now(), - } - - // Payload - pkt.Payload = bs - - if err := packet.Parse(bs, pkt.Info()); err != nil { - log.Warningf("nfqueue: failed to parse packet: %s; dropping", err) - *mark = 1702 - return queues[qid].DefaultVerdict - } - - // fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000")) - // BUG: "panic: send on closed channel" when shutting down - queues[qid].Packets <- &pkt - - select { - case v = <-pkt.verdict: - *mark = pkt.Mark - // *mark = 1710 - case <-time.After(queues[qid].Timeout): - v = queues[qid].DefaultVerdict - } - - // log.Tracef("nfqueue: took %s to handle packet", time.Now().Sub(pkt.StartedHandling).String()) - - return v -} diff --git a/firewall/interception/nfqueue/nfqueue.h b/firewall/interception/nfqueue/nfqueue.h deleted file mode 100644 index 1cd97188..00000000 --- a/firewall/interception/nfqueue/nfqueue.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -// #define _BSD_SOURCE -// #define __BSD_SOURCE - -// #define __FAVOR_BSD // Just Using _BSD_SOURCE didn't work on my system for some reason -// #define __USE_BSD -#include -// #include -// #include -#include -#include -#include -#include -#include -// #include -#include - -// extern int nfq_callback(uint8_t version, uint8_t protocol, unsigned char *saddr, unsigned char *daddr, -// uint16_t sport, uint16_t dport, unsigned char * extra, void* data); - -int nfqueue_cb_new(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data); -void loop_for_packets(struct nfq_handle *h); - -static inline struct nfq_q_handle * create_queue(struct nfq_handle *h, uint16_t qid) { - //we use this because it's more convient to pass the callback in C - // FIXME: check malloc success - uint16_t *data = malloc(sizeof(uint16_t)); - *data = qid; - return nfq_create_queue(h, qid, &nfqueue_cb_new, (void*)data); -} diff --git a/firewall/interception/nfqueue/packet.go b/firewall/interception/nfqueue/packet.go deleted file mode 100644 index ed0b7555..00000000 --- a/firewall/interception/nfqueue/packet.go +++ /dev/null @@ -1,129 +0,0 @@ -// +build linux - -package nfqueue - -import ( - "errors" - - "github.com/safing/portmaster/network/packet" -) - -// NFQ Errors -var ( - ErrVerdictSentOrTimedOut = errors.New("the verdict was already sent or timed out") -) - -// NFQ Packet Constants -//nolint:golint,stylecheck // FIXME -const ( - NFQ_DROP uint32 = 0 // discarded the packet - NFQ_ACCEPT uint32 = 1 // the packet passes, continue iterations - NFQ_STOLEN uint32 = 2 // gone away - NFQ_QUEUE uint32 = 3 // inject the packet into a different queue (the target queue number is in the high 16 bits of the verdict) - NFQ_REPEAT uint32 = 4 // iterate the same cycle once more - NFQ_STOP uint32 = 5 // accept, but don't continue iterations -) - -// Packet represents a packet with a NFQ reference. -type Packet struct { - packet.Base - - QueueID uint16 - ID uint32 - HWProtocol uint16 - Hook uint8 - Mark uint32 - - // StartedHandling time.Time - - verdict chan uint32 -} - -// func (pkt *Packet) String() string { -// return fmt.Sprintf("", -// pkt.QueueID, pkt.Id, pkt.Protocol, pkt.Src, pkt.SrcPort, pkt.Dst, pkt.DstPort, pkt.Mark, pkt.Checksum, pkt.Tos, pkt.TTL) -// } - -// nolint:unparam -func (pkt *Packet) setVerdict(v uint32) (err error) { - defer func() { - if x := recover(); x != nil { - err = ErrVerdictSentOrTimedOut - } - }() - pkt.verdict <- v - close(pkt.verdict) - // log.Tracef("filter: packet %s verdict %d", pkt, v) - return err -} - -// Marks: -// 17: Identifier -// 0/1: Just this packet/this Link -// 0/1/2: Accept, Block, Drop - -// func (pkt *Packet) Accept() error { -// return pkt.setVerdict(NFQ_STOP) -// } -// -// func (pkt *Packet) Block() error { -// pkt.Mark = 1701 -// return pkt.setVerdict(NFQ_ACCEPT) -// } -// -// func (pkt *Packet) Drop() error { -// return pkt.setVerdict(NFQ_DROP) -// } - -// Accept implements the packet interface. -func (pkt *Packet) Accept() error { - pkt.Mark = 1700 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// Block implements the packet interface. -func (pkt *Packet) Block() error { - pkt.Mark = 1701 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// Drop implements the packet interface. -func (pkt *Packet) Drop() error { - pkt.Mark = 1702 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// PermanentAccept implements the packet interface. -func (pkt *Packet) PermanentAccept() error { - pkt.Mark = 1710 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// PermanentBlock implements the packet interface. -func (pkt *Packet) PermanentBlock() error { - pkt.Mark = 1711 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// PermanentDrop implements the packet interface. -func (pkt *Packet) PermanentDrop() error { - pkt.Mark = 1712 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// RerouteToNameserver implements the packet interface. -func (pkt *Packet) RerouteToNameserver() error { - pkt.Mark = 1799 - return pkt.setVerdict(NFQ_ACCEPT) -} - -// RerouteToTunnel implements the packet interface. -func (pkt *Packet) RerouteToTunnel() error { - pkt.Mark = 1717 - return pkt.setVerdict(NFQ_ACCEPT) -} - -//HUGE warning, if the iptables rules aren't set correctly this can cause some problems. -// func (pkt *Packet) Repeat() error { -// return this.SetVerdict(REPEAT) -// } diff --git a/firewall/interception/nfqueue_linux.go b/firewall/interception/nfqueue_linux.go index 7868d640..3c3fed42 100644 --- a/firewall/interception/nfqueue_linux.go +++ b/firewall/interception/nfqueue_linux.go @@ -10,8 +10,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/safing/portbase/log" - "github.com/safing/portmaster/firewall/interception/nfqexp" - "github.com/safing/portmaster/firewall/interception/nfqueue" + "github.com/safing/portmaster/firewall/interception/nfq" "github.com/safing/portmaster/network/packet" ) @@ -37,12 +36,9 @@ var ( ) func init() { - flag.BoolVar(&experimentalNfqueueBackend, "experimental-nfqueue", false, "use experimental nfqueue packet") + flag.BoolVar(&experimentalNfqueueBackend, "experimental-nfqueue", true, "(deprecated flag; always used)") } -// nfQueueFactoryFunc creates a new nfQueue with qid as the queue number. -type nfQueueFactoryFunc func(qid uint16, v6 bool) (nfQueue, error) - // nfQueue encapsulates nfQueue providers type nfQueue interface { PacketChannel() <-chan packet.Packet @@ -228,15 +224,10 @@ func deactivateIPTables(protocol iptables.Protocol, rules, chains []string) erro // StartNfqueueInterception starts the nfqueue interception. func StartNfqueueInterception() (err error) { - var nfQueueFactory nfQueueFactoryFunc = func(qid uint16, v6 bool) (nfQueue, error) { - return nfqueue.NewNFQueue(qid) - } - + // @deprecated, remove in v1 if experimentalNfqueueBackend { - log.Infof("nfqueue: using experimental nfqueue backend") - nfQueueFactory = func(qid uint16, v6 bool) (nfQueue, error) { - return nfqexp.New(qid, v6) - } + log.Warningf("[DEPRECATED] --experimental-nfqueue has been deprecated as the backend is now used by default") + log.Warningf("[DEPRECATED] please remove the flag from your configuration!") } err = activateNfqueueFirewall() @@ -245,22 +236,22 @@ func StartNfqueueInterception() (err error) { return fmt.Errorf("could not initialize nfqueue: %s", err) } - out4Queue, err = nfQueueFactory(17040, false) + out4Queue, err = nfq.New(17040, false) if err != nil { _ = Stop() return fmt.Errorf("nfqueue(IPv4, out): %w", err) } - in4Queue, err = nfQueueFactory(17140, false) + in4Queue, err = nfq.New(17140, false) if err != nil { _ = Stop() return fmt.Errorf("nfqueue(IPv4, in): %w", err) } - out6Queue, err = nfQueueFactory(17060, true) + out6Queue, err = nfq.New(17060, true) if err != nil { _ = Stop() return fmt.Errorf("nfqueue(IPv6, out): %w", err) } - in6Queue, err = nfQueueFactory(17160, true) + in6Queue, err = nfq.New(17160, true) if err != nil { _ = Stop() return fmt.Errorf("nfqueue(IPv6, in): %w", err)