From 99851166a0ef072fc990ca0787ea737da0970a1a Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 30 Oct 2018 19:14:13 +0100 Subject: [PATCH] Start firewall/network/portmaster adaption --- firewall/config.go | 24 ++++ firewall/firewall.go | 116 ++++++++--------- firewall/inspection/inspection.go | 5 +- firewall/interception/interception_linux.go | 8 +- firewall/interception/nfqueue.go | 134 +++++++++++--------- firewall/interception/nfqueue/multiqueue.go | 85 +++++++------ firewall/interception/nfqueue/nfqueue.go | 70 +++++----- firewall/interception/nfqueue/packet.go | 2 +- network/connection.go | 8 +- network/link.go | 6 +- portmaster/tunnel.go | 4 +- 11 files changed, 250 insertions(+), 212 deletions(-) create mode 100644 firewall/config.go diff --git a/firewall/config.go b/firewall/config.go new file mode 100644 index 00000000..7311b1f8 --- /dev/null +++ b/firewall/config.go @@ -0,0 +1,24 @@ +package firewall + +import ( + "github.com/Safing/portbase/config" +) + +var ( + permanentVerdicts config.BoolOption +) + +func prep() error { + err := config.Register(&config.Option{ + Name: "Permanent Verdicts", + Key: "firewall/permanentVerdicts", + Description: "With permanent verdicts, control of a connection is fully handed back to the OS after the initial decision. This brings a great performance increase, but makes it impossible to change the decision of a link later on.", + ExpertiseLevel: config.ExpertiseLevelExpert, + OptType: config.OptTypeBool, + DefaultValue: true, + }) + if err != nil { + return err + } + configuredNameServers = config.Concurrent.GetAsBool("firewall/permanentVerdicts", true) +} diff --git a/firewall/firewall.go b/firewall/firewall.go index e8e52bca..2f317200 100644 --- a/firewall/firewall.go +++ b/firewall/firewall.go @@ -8,17 +8,15 @@ import ( "sync/atomic" "time" - "github.com/Safing/safing-core/configuration" - "github.com/Safing/safing-core/firewall/inspection" - "github.com/Safing/safing-core/firewall/interception" - "github.com/Safing/safing-core/log" - "github.com/Safing/safing-core/modules" - "github.com/Safing/safing-core/network" - "github.com/Safing/safing-core/network/packet" - "github.com/Safing/safing-core/port17/entry" - "github.com/Safing/safing-core/port17/mode" - "github.com/Safing/safing-core/portmaster" - "github.com/Safing/safing-core/process" + "github.com/Safing/portbase/config" + "github.com/Safing/portbase/log" + "github.com/Safing/portbase/modules" + "github.com/Safing/portmaster/firewall/inspection" + "github.com/Safing/portmaster/firewall/interception" + "github.com/Safing/portmaster/network" + "github.com/Safing/portmaster/network/packet" + "github.com/Safing/portmaster/portmaster" + "github.com/Safing/portmaster/process" ) var ( @@ -46,23 +44,25 @@ var ( ) func init() { + modules.Register("firewall", prep, start, stop, "database", "nameserver") +} - var err error +func prep() (err error) { _, localNet4, err = net.ParseCIDR("127.0.0.0/24") // Yes, this would normally be 127.0.0.0/8 // TODO: figure out any side effects if err != nil { - log.Criticalf("firewall: failed to parse cidr 127.0.0.0/24: %s", err) + return fmt.Errorf("firewall: failed to parse cidr 127.0.0.0/24: %s", err) } _, tunnelNet4, err = net.ParseCIDR("127.17.0.0/16") if err != nil { - log.Criticalf("firewall: failed to parse cidr 127.17.0.0/16: %s", err) + return fmt.Errorf("firewall: failed to parse cidr 127.17.0.0/16: %s", err) } _, tunnelNet6, err = net.ParseCIDR("fd17::/64") if err != nil { - log.Criticalf("firewall: failed to parse cidr fd17::/64: %s", err) + return fmt.Errorf("firewall: failed to parse cidr fd17::/64: %s", err) } var pA uint64 @@ -71,20 +71,19 @@ func init() { packetsBlocked = &pB var pD uint64 packetsDropped = &pD + + return nil } -func Start() { - firewallModule = modules.Register("Firewall", 128) - defer firewallModule.StopComplete() - +func start() { // start interceptor - go interception.Start() - go statLogger() + interception.Start() + go statLogger() + go run() // go run() // go run() // go run() - run() } func handlePacket(pkt packet.Packet) { @@ -189,19 +188,19 @@ func initialHandler(pkt packet.Packet, link *network.Link) { logInitialVerdict(link) // TODO: link this to real status - port17Active := mode.Client() + // port17Active := mode.Client() switch { - case port17Active && link.Inspect: - // tunnel link, but also inspect (after reroute) - link.Tunneled = true - link.SetFirewallHandler(inspectThenVerdict) - verdict(pkt, link.Verdict) - case port17Active: - // tunnel link, don't inspect - link.Tunneled = true - link.StopFirewallHandler() - permanentVerdict(pkt, network.ACCEPT) + // case port17Active && link.Inspect: + // // tunnel link, but also inspect (after reroute) + // link.Tunneled = true + // link.SetFirewallHandler(inspectThenVerdict) + // verdict(pkt, link.Verdict) + // case port17Active: + // // tunnel link, don't inspect + // link.Tunneled = true + // link.StopFirewallHandler() + // permanentVerdict(pkt, network.ACCEPT) case link.Inspect: link.SetFirewallHandler(inspectThenVerdict) inspectThenVerdict(pkt, link) @@ -227,11 +226,7 @@ func inspectThenVerdict(pkt packet.Packet, link *network.Link) { // we are done with inspecting link.StopFirewallHandler() - config.Changed() - config.RLock() - link.VerdictPermanent = config.PermanentVerdicts - config.RUnlock() - + link.VerdictPermanent = permanentVerdicts() if link.VerdictPermanent { link.Save() permanentVerdict(pkt, link.Verdict) @@ -276,18 +271,18 @@ func verdict(pkt packet.Packet, action network.Verdict) { pkt.Drop() } -func tunnelHandler(pkt packet.Packet) { - tunnelInfo := portmaster.GetTunnelInfo(pkt.GetIPHeader().Dst) - if tunnelInfo == nil { - pkt.Block() - return - } - - entry.CreateTunnel(pkt, tunnelInfo.Domain, tunnelInfo.RRCache.ExportAllARecords()) - log.Tracef("firewall: rerouting %s to tunnel entry point", pkt) - pkt.RerouteToTunnel() - return -} +// func tunnelHandler(pkt packet.Packet) { +// tunnelInfo := portmaster.GetTunnelInfo(pkt.GetIPHeader().Dst) +// if tunnelInfo == nil { +// pkt.Block() +// return +// } +// +// entry.CreateTunnel(pkt, tunnelInfo.Domain, tunnelInfo.RRCache.ExportAllARecords()) +// log.Tracef("firewall: rerouting %s to tunnel entry point", pkt) +// pkt.RerouteToTunnel() +// return +// } func logInitialVerdict(link *network.Link) { // switch link.Verdict { @@ -312,25 +307,26 @@ func logChangedVerdict(link *network.Link) { } func run() { - -packetProcessingLoop: for { select { - case <-firewallModule.Stop: - break packetProcessingLoop + case <-modules.ShuttingDown(): + return case pkt := <-interception.Packets: handlePacket(pkt) } } - } func statLogger() { for { - time.Sleep(10 * time.Second) - log.Tracef("firewall: packets accepted %d, blocked %d, dropped %d", atomic.LoadUint64(packetsAccepted), atomic.LoadUint64(packetsBlocked), atomic.LoadUint64(packetsDropped)) - atomic.StoreUint64(packetsAccepted, 0) - atomic.StoreUint64(packetsBlocked, 0) - atomic.StoreUint64(packetsDropped, 0) + select { + case <-modules.ShuttingDown(): + return + case <-time.After(10 * time.Second): + log.Tracef("firewall: packets accepted %d, blocked %d, dropped %d", atomic.LoadUint64(packetsAccepted), atomic.LoadUint64(packetsBlocked), atomic.LoadUint64(packetsDropped)) + atomic.StoreUint64(packetsAccepted, 0) + atomic.StoreUint64(packetsBlocked, 0) + atomic.StoreUint64(packetsDropped, 0) + } } } diff --git a/firewall/inspection/inspection.go b/firewall/inspection/inspection.go index 58f02d7d..0c2888b1 100644 --- a/firewall/inspection/inspection.go +++ b/firewall/inspection/inspection.go @@ -3,9 +3,10 @@ package inspection import ( - "github.com/Safing/safing-core/network" - "github.com/Safing/safing-core/network/packet" "sync" + + "github.com/Safing/portmaster/network" + "github.com/Safing/portmaster/network/packet" ) const ( diff --git a/firewall/interception/interception_linux.go b/firewall/interception/interception_linux.go index 88bdc0e5..1cfb159e 100644 --- a/firewall/interception/interception_linux.go +++ b/firewall/interception/interception_linux.go @@ -2,10 +2,8 @@ package interception -import "github.com/Safing/safing-core/network/packet" +import "github.com/Safing/portmaster/network/packet" -var Packets chan packet.Packet - -func init() { +var ( Packets = make(chan packet.Packet, 1000) -} +) diff --git a/firewall/interception/nfqueue.go b/firewall/interception/nfqueue.go index b9ef25e2..ada0a689 100644 --- a/firewall/interception/nfqueue.go +++ b/firewall/interception/nfqueue.go @@ -5,27 +5,33 @@ package interception import ( + "fmt" "sort" "strings" "github.com/coreos/go-iptables/iptables" - "github.com/Safing/safing-core/firewall/interception/nfqueue" - "github.com/Safing/safing-core/log" - "github.com/Safing/safing-core/modules" + "github.com/Safing/portmaster/firewall/interception/nfqueue" ) // iptables -A OUTPUT -p icmp -j", "NFQUEUE", "--queue-num", "1", "--queue-bypass -var nfqueueModule *modules.Module +var ( + v4chains []string + v4rules []string + v4once []string -var v4chains []string -var v4rules []string -var v4once []string + v6chains []string + v6rules []string + v6once []string -var v6chains []string -var v6rules []string -var v6once []string + out4Queue *nfqueue.NFQueue + in4Queue *nfqueue.NFQueue + out6Queue *nfqueue.NFQueue + in6Queue *nfqueue.NFQueue + + shutdownSignal = make(chan struct{}) +) func init() { @@ -238,70 +244,84 @@ func deactivateNfqueueFirewall() error { return nil } -func Start() { +// Start starts the nfqueue interception. +func Start() (err error) { - nfqueueModule = modules.Register("Firewall:Interception:Nfqueue", 192) - - if err := activateNfqueueFirewall(); err != nil { - log.Criticalf("could not activate firewall for nfqueue: %q", err) + err = activateNfqueueFirewall() + if err != nil { + Stop() + return fmt.Errorf("could not initialize nfqueue: %s", err) } - out4Queue := nfqueue.NewNFQueue(17040) - in4Queue := nfqueue.NewNFQueue(17140) - out6Queue := nfqueue.NewNFQueue(17060) - in6Queue := nfqueue.NewNFQueue(17160) + out4Queue, err = nfqueue.NewNFQueue(17040) + if err != nil { + Stop() + return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err) + } + in4Queue, err = nfqueue.NewNFQueue(17140) + if err != nil { + Stop() + return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err) + } + out6Queue, err = nfqueue.NewNFQueue(17060) + if err != nil { + Stop() + return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err) + } + in6Queue, err = nfqueue.NewNFQueue(17160) + if err != nil { + Stop() + return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err) + } - out4Channel := out4Queue.Process() - // if err != nil { - // log.Criticalf("could not open nfqueue out4") - // } else { - defer out4Queue.Destroy() - // } - in4Channel := in4Queue.Process() - // if err != nil { - // log.Criticalf("could not open nfqueue in4") - // } else { - defer in4Queue.Destroy() - // } - out6Channel := out6Queue.Process() - // if err != nil { - // log.Criticalf("could not open nfqueue out6") - // } else { - defer out6Queue.Destroy() - // } - in6Channel := in6Queue.Process() - // if err != nil { - // log.Criticalf("could not open nfqueue in6") - // } else { - defer in6Queue.Destroy() - // } + go handleInterception() + return nil +} -packetInterceptionLoop: +// Stop stops the nfqueue interception. +func Stop() error { + defer close(shutdownSignal) + + if out4Queue != nil { + out4Queue.Destroy() + } + if in4Queue != nil { + in4Queue.Destroy() + } + if out6Queue != nil { + out6Queue.Destroy() + } + if in6Queue != nil { + in6Queue.Destroy() + } + + err := deactivateNfqueueFirewall() + if err != nil { + return fmt.Errorf("interception: error while deactivating nfqueue: %s", err) + } + + return nil +} + +func handleInterception() { for { select { - case <-nfqueueModule.Stop: - break packetInterceptionLoop - case pkt := <-out4Channel: + case <-shutdownSignal: + return + case pkt := <-out4Queue.Packets: pkt.SetOutbound() Packets <- pkt - case pkt := <-in4Channel: + case pkt := <-in4Queue.Packets: pkt.SetInbound() Packets <- pkt - case pkt := <-out6Channel: + case pkt := <-out6Queue.Packets: pkt.SetOutbound() Packets <- pkt - case pkt := <-in6Channel: + case pkt := <-in6Queue.Packets: pkt.SetInbound() Packets <- pkt } } - - if err := deactivateNfqueueFirewall(); err != nil { - log.Criticalf("could not deactivate firewall for nfqueue: %q", err) - } - - nfqueueModule.StopComplete() - } func stringInSlice(slice []string, value string) bool { diff --git a/firewall/interception/nfqueue/multiqueue.go b/firewall/interception/nfqueue/multiqueue.go index 4ee43195..6fd4b71c 100644 --- a/firewall/interception/nfqueue/multiqueue.go +++ b/firewall/interception/nfqueue/multiqueue.go @@ -2,45 +2,48 @@ package nfqueue -import ( - "github.com/Safing/safing-core/network/packet" - "sync" -) +// suspended for now -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() - } -} +// 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.go b/firewall/interception/nfqueue/nfqueue.go index 1f991bea..aedd1850 100644 --- a/firewall/interception/nfqueue/nfqueue.go +++ b/firewall/interception/nfqueue/nfqueue.go @@ -17,17 +17,19 @@ import ( "syscall" "time" "unsafe" + "errors" + "fmt" - "github.com/Safing/safing-core/network/packet" + "github.com/Safing/portmaster/network/packet" ) -var queues map[uint16]*nfQueue +var queues map[uint16]*NFQueue func init() { - queues = make(map[uint16]*nfQueue) + queues = make(map[uint16]*NFQueue) } -type nfQueue struct { +type NFQueue struct { DefaultVerdict uint32 Timeout time.Duration qid uint16 @@ -38,83 +40,77 @@ type nfQueue struct { fd int lk sync.Mutex - pktch chan packet.Packet + Packets chan packet.Packet } -func NewNFQueue(qid uint16) (nfq *nfQueue) { +func NewNFQueue(qid uint16) (nfq *NFQueue, err error) { if os.Geteuid() != 0 { - panic("Must be ran by root.") + return nil, errors.New("must be root to intercept packets") } - nfq = &nfQueue{DefaultVerdict: NFQ_ACCEPT, Timeout: 100 * time.Millisecond, qid: qid, qidptr: &qid} + nfq = &NFQueue{DefaultVerdict: NFQ_ACCEPT, Timeout: 100 * time.Millisecond, qid: qid, qidptr: &qid} queues[nfq.qid] = nfq - return nfq -} -/* -This returns a channel that will recieve packets, -the user then must call pkt.Accept() or pkt.Drop() -*/ -func (this *nfQueue) Process() <-chan packet.Packet { - if this.h != nil { - return this.pktch + err = nfq.init() + if err != nil { + return nil, err } - this.init() go func() { runtime.LockOSThread() - C.loop_for_packets(this.h) + C.loop_for_packets(nfq.h) }() - return this.pktch + return nfq, nil } -func (this *nfQueue) init() { +func (this *NFQueue) init() error { var err error if this.h, err = C.nfq_open(); err != nil || this.h == nil { - panic(err) + fmt.Errorf("could not open nfqueue: %s", 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.Packet, 1) + this.Packets = make(chan packet.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?.") + return errors.New("nfq_unbind_pf(AF_INET) failed, are you root?") } if C.nfq_unbind_pf(this.h, C.AF_INET6) < 0 { this.Destroy() - panic("nfq_unbind_pf(AF_INET6) failed.") + return errors.New("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.") + return errors.New("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.") + return errors.New("nfq_bind_pf(AF_INET6) failed") } if this.qh, err = C.create_queue(this.h, C.uint16_t(this.qid)); err != nil || this.qh == nil { C.nfq_close(this.h) - panic(err) + return fmt.Errorf("could not create queue: %s", 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.") + return errors.New("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.") + return errors.New("nfq_set_queue_maxlen(1024 * 8) failed") } + + return nil } -func (this *nfQueue) Destroy() { +func (this *NFQueue) Destroy() { this.lk.Lock() defer this.lk.Unlock() @@ -131,12 +127,12 @@ func (this *nfQueue) Destroy() { } // TODO: don't close, we're exiting anyway - // if this.pktch != nil { - // close(this.pktch) + // if this.Packets != nil { + // close(this.Packets) // } } -func (this *nfQueue) Valid() bool { +func (this *NFQueue) Valid() bool { return this.h != nil && this.qh != nil } @@ -148,7 +144,7 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32, qidptr := (*uint16)(data) qid := uint16(*qidptr) - // nfq := (*nfQueue)(nfqptr) + // nfq := (*NFQueue)(nfqptr) new_version := version ipver := packet.IPVersion(new_version) ipsz := C.int(ipver.ByteSize()) @@ -187,7 +183,7 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32, // 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].pktch <- &pkt + queues[qid].Packets <- &pkt select { case v = <-pkt.verdict: diff --git a/firewall/interception/nfqueue/packet.go b/firewall/interception/nfqueue/packet.go index 37e2ebd5..a05abc6d 100644 --- a/firewall/interception/nfqueue/packet.go +++ b/firewall/interception/nfqueue/packet.go @@ -5,7 +5,7 @@ package nfqueue import ( "fmt" - "github.com/Safing/safing-core/network/packet" + "github.com/Safing/portmaster/network/packet" ) var ( diff --git a/network/connection.go b/network/connection.go index d0cb84f0..e4ef9268 100644 --- a/network/connection.go +++ b/network/connection.go @@ -7,10 +7,10 @@ import ( "net" "time" - "github.com/Safing/safing-core/database" - "github.com/Safing/safing-core/intel" - "github.com/Safing/safing-core/network/packet" - "github.com/Safing/safing-core/process" + "github.com/Safing/portbase/database" + "github.com/Safing/portmaster/intel" + "github.com/Safing/portmaster/network/packet" + "github.com/Safing/portmaster/process" datastore "github.com/ipfs/go-datastore" ) diff --git a/network/link.go b/network/link.go index b26c0451..d0f43ee7 100644 --- a/network/link.go +++ b/network/link.go @@ -9,9 +9,9 @@ import ( datastore "github.com/ipfs/go-datastore" - "github.com/Safing/safing-core/database" - "github.com/Safing/safing-core/log" - "github.com/Safing/safing-core/network/packet" + "github.com/Safing/portbase/database" + "github.com/Safing/portbase/log" + "github.com/Safing/portmaster/network/packet" ) type FirewallHandler func(pkt packet.Packet, link *Link) diff --git a/portmaster/tunnel.go b/portmaster/tunnel.go index 3269eb51..c820ee42 100644 --- a/portmaster/tunnel.go +++ b/portmaster/tunnel.go @@ -7,8 +7,8 @@ import ( "sync" "time" - "github.com/Safing/safing-core/crypto/random" - "github.com/Safing/safing-core/intel" + "github.com/Safing/portbase/crypto/random" + "github.com/Safing/portmaster/intel" "github.com/miekg/dns" )