From 8fe02202de2db8edcf7e7ba0e7ed28116cb092dd Mon Sep 17 00:00:00 2001 From: shw Date: Tue, 11 Apr 2017 18:19:34 +0000 Subject: [PATCH] Very dirty/experimental replacement of nfqueue with native github.com/subgraph/go-nfnetlink package. --- nfqueue/LICENSE | 201 ------------------------------------------ nfqueue/multiqueue.go | 41 --------- nfqueue/nfqueue.c | 87 ------------------ nfqueue/nfqueue.go | 180 ------------------------------------- nfqueue/nfqueue.h | 27 ------ nfqueue/packet.go | 145 ------------------------------ sgfw/dns.go | 7 +- sgfw/policy.go | 173 +++++++++++++++++++++++++++--------- sgfw/rules.go | 15 ++-- sgfw/sgfw.go | 35 +++++--- 10 files changed, 167 insertions(+), 744 deletions(-) delete mode 100644 nfqueue/LICENSE delete mode 100644 nfqueue/multiqueue.go delete mode 100644 nfqueue/nfqueue.c delete mode 100644 nfqueue/nfqueue.go delete mode 100644 nfqueue/nfqueue.h delete mode 100644 nfqueue/packet.go diff --git a/nfqueue/LICENSE b/nfqueue/LICENSE deleted file mode 100644 index ad410e1..0000000 --- a/nfqueue/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/nfqueue/multiqueue.go b/nfqueue/multiqueue.go deleted file mode 100644 index 08532f8..0000000 --- a/nfqueue/multiqueue.go +++ /dev/null @@ -1,41 +0,0 @@ -package nfqueue - -import "sync" - -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 { - var ( - wg sync.WaitGroup - out = make(chan *Packet, len(mq.qs)) - ) - for _, q := range mq.qs { - wg.Add(1) - go func(ch <-chan *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/nfqueue/nfqueue.c b/nfqueue/nfqueue.c deleted file mode 100644 index bc09c13..0000000 --- a/nfqueue/nfqueue.c +++ /dev/null @@ -1,87 +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); -// struct nfqnl_msg_packet_hw *hwph = nfq_get_packet_hw(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); - - 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, len, payload, 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[4096] __attribute__ ((aligned)); - int rv; - while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) { - nfq_handle_packet(h, buf, rv); - } -} diff --git a/nfqueue/nfqueue.go b/nfqueue/nfqueue.go deleted file mode 100644 index 230c098..0000000 --- a/nfqueue/nfqueue.go +++ /dev/null @@ -1,180 +0,0 @@ -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 -} diff --git a/nfqueue/nfqueue.h b/nfqueue/nfqueue.h deleted file mode 100644 index e897bd7..0000000 --- a/nfqueue/nfqueue.h +++ /dev/null @@ -1,27 +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 num, void *data) { - //we use this because it's more convient to pass the callback in C - return nfq_create_queue(h, num, &nfqueue_cb_new, data); -} diff --git a/nfqueue/packet.go b/nfqueue/packet.go deleted file mode 100644 index aec8f36..0000000 --- a/nfqueue/packet.go +++ /dev/null @@ -1,145 +0,0 @@ -package nfqueue - -import ( - "fmt" - "net" - "syscall" -) - -type ( - IPVersion uint8 - IPProtocol uint8 - Verdict uint8 -) - -const ( - IPv4 = IPVersion(4) - IPv6 = IPVersion(6) - - //convience really - IGMP = IPProtocol(syscall.IPPROTO_IGMP) - RAW = IPProtocol(syscall.IPPROTO_RAW) - TCP = IPProtocol(syscall.IPPROTO_TCP) - UDP = IPProtocol(syscall.IPPROTO_UDP) - ICMP = IPProtocol(syscall.IPPROTO_ICMP) - ICMPv6 = IPProtocol(syscall.IPPROTO_ICMPV6) -) - -const ( - DROP Verdict = iota - ACCEPT - STOLEN - QUEUE - REPEAT - STOP -) - -var ( - ErrVerdictSentOrTimedOut error = fmt.Errorf("The verdict was already sent or timed out.") -) - -func (v IPVersion) String() string { - switch v { - case IPv4: - return "IPv4" - case IPv6: - return "IPv6" - } - return fmt.Sprintf("", uint8(v)) -} - -// Returns the byte size of the ip, IPv4 = 4 bytes, IPv6 = 16 -func (v IPVersion) Size() int { - switch v { - case IPv4: - return 4 - case IPv6: - return 16 - } - return 0 -} - -func (p IPProtocol) String() string { - switch p { - case RAW: - return "RAW" - case TCP: - return "TCP" - case UDP: - return "UDP" - case ICMP: - return "ICMP" - case ICMPv6: - return "ICMPv6" - case IGMP: - return "IGMP" - } - return fmt.Sprintf("", uint8(p)) -} - -func (v Verdict) String() string { - switch v { - case DROP: - return "DROP" - case ACCEPT: - return "ACCEPT" - } - return fmt.Sprintf("", uint8(v)) -} - -type IPHeader struct { - Version IPVersion - - Tos, TTL uint8 - Protocol IPProtocol - Src, Dst net.IP -} - -type TCPUDPHeader struct { - SrcPort, DstPort uint16 - Checksum uint16 //not implemented -} - -// TODO handle other protocols - -type Packet struct { - QueueId uint16 - Id uint32 - HWProtocol uint16 - Hook uint8 - Mark uint32 - Payload []byte - *IPHeader - *TCPUDPHeader - - 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) -} - -func (pkt *Packet) setVerdict(v Verdict) (err error) { - defer func() { - if x := recover(); x != nil { - err = ErrVerdictSentOrTimedOut - } - }() - pkt.verdict <- uint32(v) - close(pkt.verdict) - return err -} - -func (pkt *Packet) Accept() error { - return pkt.setVerdict(ACCEPT) -} - -func (pkt *Packet) Drop() error { - return pkt.setVerdict(DROP) -} - -//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/sgfw/dns.go b/sgfw/dns.go index dd7fc32..4ba3ba3 100644 --- a/sgfw/dns.go +++ b/sgfw/dns.go @@ -5,7 +5,8 @@ import ( "strings" "sync" - "github.com/subgraph/fw-daemon/nfqueue" +// "github.com/subgraph/go-nfnetlink" + "github.com/google/gopacket" ) type dnsCache struct { @@ -21,9 +22,9 @@ func newDNSCache() *dnsCache { } } -func (dc *dnsCache) processDNS(pkt *nfqueue.Packet) { +func (dc *dnsCache) processDNS(pkt gopacket.Packet) { dns := &dnsMsg{} - if !dns.Unpack(pkt.Payload) { + if !dns.Unpack(pkt.ApplicationLayer().Payload()) { log.Warning("Failed to Unpack DNS message") return } diff --git a/sgfw/policy.go b/sgfw/policy.go index deaf821..0e38139 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -5,7 +5,12 @@ import ( "strings" "sync" - "github.com/subgraph/fw-daemon/nfqueue" +// "encoding/binary" + +// nfnetlink "github.com/subgraph/go-nfnetlink" + nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" +// "github.com/google/gopacket" + "github.com/google/gopacket/layers" "github.com/subgraph/go-procsnitch" "net" ) @@ -30,7 +35,7 @@ type pendingConnection interface { type pendingPkt struct { pol *Policy name string - pkt *nfqueue.Packet + pkt *nfqueue.NFQPacket pinfo *procsnitch.Info } @@ -46,11 +51,27 @@ func (pp *pendingPkt) hostname() string { return pp.name } func (pp *pendingPkt) dst() net.IP { - return pp.pkt.Dst + dst := pp.pkt.Packet.NetworkLayer().NetworkFlow().Dst() + + if dst.EndpointType() != layers.EndpointIPv4 { + return nil + } + + return dst.Raw() +// pp.pkt.NetworkLayer().Layer } func (pp *pendingPkt) dstPort() uint16 { - return pp.pkt.DstPort +/* dst := pp.pkt.Packet.TransportLayer().TransportFlow().Dst() + + if dst.EndpointType() != layers.EndpointTCPPort { + return 0 + } + + return binary.BigEndian.Uint16(dst.Raw()) */ + _, dstp := getPacketTCPPorts(pp.pkt) + return dstp +// return pp.pkt.DstPort } func (pp *pendingPkt) accept() { @@ -58,7 +79,8 @@ func (pp *pendingPkt) accept() { } func (pp *pendingPkt) drop() { - pp.pkt.Mark = 1 +// XXX: This needs to be fixed +// pp.pkt.Mark = 1 pp.pkt.Accept() } @@ -101,17 +123,21 @@ func (fw *Firewall) policyForPath(path string) *Policy { return fw.policyMap[path] } -func (p *Policy) processPacket(pkt *nfqueue.Packet, pinfo *procsnitch.Info) { +func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info) { p.lock.Lock() defer p.lock.Unlock() - name := p.fw.dns.Lookup(pkt.Dst) + dstb := pkt.Packet.NetworkLayer().NetworkFlow().Dst().Raw() + dstip := net.IP(dstb) + name := p.fw.dns.Lookup(dstip) +// name := p.fw.dns.Lookup(pkt.Dst) if !FirewallConfig.LogRedact { - log.Infof("Lookup(%s): %s", pkt.Dst.String(), name) + log.Infof("Lookup(%s): %s", dstip.String(), name) } result := p.rules.filterPacket(pkt, pinfo, name) switch result { case FILTER_DENY: - pkt.Mark = 1 +// XXX: this needs to be fixed +// pkt.Mark = 1 pkt.Accept() case FILTER_ALLOW: pkt.Accept() @@ -228,41 +254,54 @@ func (p *Policy) hasPersistentRules() bool { return false } -func printPacket(pkt *nfqueue.Packet, hostname string, pinfo *procsnitch.Info) string { - proto := func() string { - switch pkt.Protocol { - case nfqueue.TCP: - return "TCP" - case nfqueue.UDP: - return "UDP" +func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info) string { + proto := "???" + SrcPort, DstPort := uint16(0), uint16(0) + SrcIp, DstIp := getPacketIP4Addrs(pkt) + + switch pkt.Packet.TransportLayer().TransportFlow().EndpointType() { + case 4: + proto = "TCP" + case 5: + proto = "UDP" default: - return "???" - } - }() + } + + if proto == "TCP" { + SrcPort, DstPort = getPacketTCPPorts(pkt) + } else if proto == "UDP" { + SrcPort, DstPort = getPacketUDPPorts(pkt) + } if FirewallConfig.LogRedact { hostname = STR_REDACTED } name := hostname if name == "" { - name = pkt.Dst.String() + name = DstIp.String() } if pinfo == nil { - return fmt.Sprintf("(%s %s:%d -> %s:%d)", proto, pkt.Src, pkt.SrcPort, name, pkt.DstPort) + return fmt.Sprintf("(%s %s:%d -> %s:%d)", proto, SrcIp, SrcPort, name, DstPort) } - - return fmt.Sprintf("%s %s %s:%d -> %s:%d", pinfo.ExePath, proto, pkt.Src, pkt.SrcPort, name, pkt.DstPort) + + return fmt.Sprintf("%s %s %s:%d -> %s:%d", pinfo.ExePath, proto, SrcIp, SrcPort, name, DstPort) } -func (fw *Firewall) filterPacket(pkt *nfqueue.Packet) { - if pkt.Protocol == nfqueue.UDP && pkt.SrcPort == 53 { - pkt.Accept() - fw.dns.processDNS(pkt) - return +func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { + if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { + srcport, _ := getPacketUDPPorts(pkt) + + if srcport == 53 { + pkt.Accept() + fw.dns.processDNS(pkt.Packet) + return + } + } + _, dstip := getPacketIP4Addrs(pkt) pinfo := findProcessForPacket(pkt) if pinfo == nil { - log.Warningf("No proc found for %s", printPacket(pkt, fw.dns.Lookup(pkt.Dst), nil)) + log.Warningf("No proc found for %s", printPacket(pkt, fw.dns.Lookup(dstip), nil)) pkt.Accept() return } @@ -276,7 +315,7 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.Packet) { } } } - log.Debugf("filterPacket [%s] %s", ppath, printPacket(pkt, fw.dns.Lookup(pkt.Dst), nil)) + log.Debugf("filterPacket [%s] %s", ppath, printPacket(pkt, fw.dns.Lookup(dstip), nil)) if basicAllowPacket(pkt) { pkt.Accept() return @@ -285,20 +324,68 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.Packet) { policy.processPacket(pkt, pinfo) } -func findProcessForPacket(pkt *nfqueue.Packet) *procsnitch.Info { - switch pkt.Protocol { - case nfqueue.TCP: - return procsnitch.LookupTCPSocketProcess(pkt.SrcPort, pkt.Dst, pkt.DstPort) - case nfqueue.UDP: - return procsnitch.LookupUDPSocketProcess(pkt.SrcPort) - default: - log.Warningf("Packet has unknown protocol: %d", pkt.Protocol) - return nil +func findProcessForPacket(pkt *nfqueue.NFQPacket) *procsnitch.Info { + _, dstip := getPacketIP4Addrs(pkt) + srcp, dstp := getPacketPorts(pkt) + + if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { + return procsnitch.LookupTCPSocketProcess(srcp, dstip, dstp) + } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { + return procsnitch.LookupUDPSocketProcess(srcp) } + + log.Warningf("Packet has unknown protocol: %d", pkt.Packet.NetworkLayer().LayerType()) + //log.Warningf("Packet has unknown protocol: %d", pkt.Protocol) + return nil +} + +func basicAllowPacket(pkt *nfqueue.NFQPacket) bool { + _, dstip := getPacketIP4Addrs(pkt) + return dstip.IsLoopback() || + dstip.IsLinkLocalMulticast() || + pkt.Packet.Layer(layers.LayerTypeTCP) == nil +// pkt.Protocol != nfqueue.TCP } -func basicAllowPacket(pkt *nfqueue.Packet) bool { - return pkt.Dst.IsLoopback() || - pkt.Dst.IsLinkLocalMulticast() || - pkt.Protocol != nfqueue.TCP +func getPacketIP4Addrs(pkt *nfqueue.NFQPacket) (net.IP, net.IP) { + ipLayer := pkt.Packet.Layer(layers.LayerTypeIPv4) + + if ipLayer == nil { + return net.IP{0,0,0,0}, net.IP{0,0,0,0} + } + + ip, _ := ipLayer.(*layers.IPv4) + return ip.SrcIP, ip.DstIP +} + +func getPacketTCPPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) { + tcpLayer := pkt.Packet.Layer(layers.LayerTypeTCP) + + if tcpLayer == nil { + return 0, 0 + } + + tcp, _ := tcpLayer.(*layers.TCP) + return uint16(tcp.SrcPort), uint16(tcp.DstPort) +} + +func getPacketUDPPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) { + udpLayer := pkt.Packet.Layer(layers.LayerTypeUDP) + + if udpLayer == nil { + return 0, 0 + } + + udp, _ := udpLayer.(*layers.UDP) + return uint16(udp.SrcPort), uint16(udp.DstPort) +} + +func getPacketPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) { + s, d := getPacketTCPPorts(pkt) + + if s == 0 && d == 0 { + s, d = getPacketUDPPorts(pkt) + } + + return s, d } diff --git a/sgfw/rules.go b/sgfw/rules.go index 66b7e50..5af6e66 100644 --- a/sgfw/rules.go +++ b/sgfw/rules.go @@ -11,7 +11,8 @@ import ( "strings" "unicode" - "github.com/subgraph/fw-daemon/nfqueue" + nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" +// "github.com/subgraph/go-nfnetlink" "github.com/subgraph/go-procsnitch" ) @@ -82,11 +83,13 @@ func (r *Rule) match(dst net.IP, dstPort uint16, hostname string) bool { return r.addr == binary.BigEndian.Uint32(dst.To4()) } -func (rl *RuleList) filterPacket(p *nfqueue.Packet, pinfo *procsnitch.Info, hostname string) FilterResult { - return rl.filter(p, p.Dst, p.DstPort, hostname, pinfo) +func (rl *RuleList) filterPacket(p *nfqueue.NFQPacket, pinfo *procsnitch.Info, hostname string) FilterResult { + _, dstip := getPacketIP4Addrs(p) + _, dstp := getPacketPorts(p) + return rl.filter(p, dstip, dstp, hostname, pinfo) } -func (rl *RuleList) filter(pkt *nfqueue.Packet, dst net.IP, dstPort uint16, hostname string, pinfo *procsnitch.Info) FilterResult { +func (rl *RuleList) filter(pkt *nfqueue.NFQPacket, dst net.IP, dstPort uint16, hostname string, pinfo *procsnitch.Info) FilterResult { if rl == nil { return FILTER_PROMPT } @@ -99,7 +102,9 @@ func (rl *RuleList) filter(pkt *nfqueue.Packet, dst net.IP, dstPort uint16, host } srcStr := STR_UNKNOWN if pkt != nil { - srcStr = fmt.Sprintf("%s:%d", pkt.Src, pkt.SrcPort) + srcip, _ := getPacketIP4Addrs(pkt) + srcp, _ := getPacketPorts(pkt) + srcStr = fmt.Sprintf("%s:%d", srcip, srcp) } log.Noticef("%s > %s %s %s -> %s:%d", r.getString(FirewallConfig.LogRedact), diff --git a/sgfw/sgfw.go b/sgfw/sgfw.go index 21679d5..bc7a90d 100644 --- a/sgfw/sgfw.go +++ b/sgfw/sgfw.go @@ -6,14 +6,14 @@ import ( "regexp" "sync" "syscall" - "time" +// "time" "bufio" "encoding/json" "strings" "github.com/op/go-logging" - - "github.com/subgraph/fw-daemon/nfqueue" + nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" +// "github.com/subgraph/go-nfnetlink" "github.com/subgraph/go-procsnitch" ) @@ -88,20 +88,31 @@ func (fw *Firewall) reloadRules() { func (fw *Firewall) runFilter() { q := nfqueue.NewNFQueue(0) - defer q.Destroy() - q.DefaultVerdict = nfqueue.DROP - q.Timeout = 5 * time.Minute - packets := q.Process() + // XXX: need to implement this +// q.DefaultVerdict = nfqueue.DROP + // XXX: need this as well +// q.Timeout = 5 * time.Minute - for { - select { - case pkt := <-packets: + ps, err := q.Open() + + if err != nil { + log.Fatal("Error opening NFQueue:", err) + } + defer q.Close() + + go func() { + for p := range ps { if fw.isEnabled() { - fw.filterPacket(pkt) + fw.filterPacket(p) } else { - pkt.Accept() + p.Accept() } + } + }() + + for { + select { case <-fw.reloadRulesChan: fw.loadRules() case <-fw.stopChan: