From a8293044f1dfa00a93462d167bbf0d0e1408f8e6 Mon Sep 17 00:00:00 2001 From: Aaron Turner Date: Sat, 6 Nov 2021 16:48:51 -0700 Subject: [PATCH] BPF filter uses src net - use: src net x.x.x.x/y in BPF filter #71 - Improved debug output with spew - tweak sample config file for freebsd/pfSense - release v0.0.8 --- CHANGELOG.md | 9 +++++ Makefile | 2 +- cmd/interfaces.go | 2 +- cmd/listen.go | 13 ++++--- cmd/utils.go | 37 ++++++++++++++++++- go.mod | 3 +- go.sum | 1 - .../pfSense/usr/local/etc/udp-proxy-2020.conf | 2 +- 8 files changed, 56 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb46d28..ae1c4c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## v0.0.8 - 2021-11-07 + Changed: - Use FreeBSD 12.2 for building binaries (pfSense 2.5.x) @@ -10,6 +12,13 @@ Changed: - Update Makefile targets and improve `make help` - Release binaries no longer end in `-static` - Release binaries are now stripped and about 33% of their previous sizes + - Improve debug output + +Fixed: + +- Now use 'src net x.x.x.x/y' in the BPF filter to restrict packets we forward + for platforms like Netgate SG5100 where pcap.SetDirection() doesn't work + to prevent infinite loops #71 ## v0.0.7 - 2021-05-23 diff --git a/Makefile b/Makefile index 86da1cd..f191484 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ GOARCH ?= $(shell uname -m | sed -E 's/x86_64/amd64/') BUILDINFOSDET ?= UDP_PROXY_2020_ARGS ?= -PROJECT_VERSION := 0.0.7 +PROJECT_VERSION := 0.0.8 DOCKER_REPO := synfinatic PROJECT_NAME := udp-proxy-2020 PROJECT_TAG := $(shell git describe --tags 2>/dev/null $(git rev-list --tags --max-count=1)) diff --git a/cmd/interfaces.go b/cmd/interfaces.go index 09ab4c5..692d85b 100644 --- a/cmd/interfaces.go +++ b/cmd/interfaces.go @@ -50,7 +50,7 @@ func initializeInterface(l *Listen) { } // set our BPF filter - bpf_filter := buildBPFFilter(l.ports) + bpf_filter := buildBPFFilter(l.ports, Interfaces[l.iname].Addresses, l.promisc) log.Debugf("%s: applying BPF Filter: %s", l.iname, bpf_filter) err = l.handle.SetBPFFilter(bpf_filter) if err != nil { diff --git a/cmd/listen.go b/cmd/listen.go index 07f2a96..6551540 100644 --- a/cmd/listen.go +++ b/cmd/listen.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/davecgh/go-spew/spew" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" @@ -89,7 +90,7 @@ func newListener(netif *net.Interface, promisc bool, ports []int32, to time.Dura sendpkt: make(chan Send, SendBufferSize), clients: clients, } - log.Debugf("Listen: %v", new) + log.Debugf("Listen: %s", spew.Sdump(new)) return new } @@ -223,7 +224,7 @@ func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopb // UDP payload if err := payload.SerializeTo(buffer, opts); err != nil { - log.Fatalf("can't serialize payload: %v", payload) + log.Fatalf("can't serialize payload: %s", spew.Sdump(payload)) } // UDP checksums can't be calculated via SerializeOptions @@ -237,7 +238,7 @@ func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopb } if err := new_udp.SerializeTo(buffer, opts); err != nil { - log.Fatalf("can't serialize UDP header: %v", udp) + log.Fatalf("can't serialize UDP header: %s", spew.Sdump(udp)) } // IPv4 header @@ -257,7 +258,7 @@ func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopb Options: ip4.Options, } if err := new_ip4.SerializeTo(buffer, csum_opts); err != nil { - log.Fatalf("can't serialize IP header: %v", new_ip4) + log.Fatalf("can't serialize IP header: %s", spew.Sdump(new_ip4)) } // Add our L2 header to the buffer @@ -267,7 +268,7 @@ func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopb Family: layers.ProtocolFamilyIPv4, } if err := loop.SerializeTo(buffer, opts); err != nil { - log.Fatalf("can't serialize Loop header: %v", loop) + log.Fatalf("can't serialize Loop header: %s", spew.Sdump(loop)) } case layers.LinkTypeEthernet.String(): // build a new ethernet header @@ -278,7 +279,7 @@ func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopb EthernetType: layers.EthernetTypeIPv4, } if err := new_eth.SerializeTo(buffer, opts); err != nil { - log.Fatalf("can't serialize Eth header: %v", new_eth) + log.Fatalf("can't serialize Eth header: %s", spew.Sdump(new_eth)) } case layers.LinkTypeRaw.String(): // no L2 header diff --git a/cmd/utils.go b/cmd/utils.go index 2e7e87d..3de5474 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -2,9 +2,12 @@ package main import ( "fmt" - log "github.com/sirupsen/logrus" + "net" "strings" "time" + + "github.com/google/gopacket/pcap" + log "github.com/sirupsen/logrus" ) // Check to see if the string is in the slice @@ -28,7 +31,7 @@ func stringPrefixInSlice(a string, list []string) bool { } // takes a list of ports and builds our BPF filter -func buildBPFFilter(ports []int32) string { +func buildBPFFilter(ports []int32, addresses []pcap.InterfaceAddress, promisc bool) string { if len(ports) < 1 { log.Fatal("--port must be specified one or more times") } @@ -42,6 +45,24 @@ func buildBPFFilter(ports []int32) string { } else { bpf_filter = bpf_filters[0] } + + // add filter to accept only traffic with a src IP matching the interface + // This should avoid network loops with NIC/drivers which do not honor the + // pcap.SetDirection() call. + networks := []string{} + for _, addr := range addresses { + if net, err := getNetwork(addr); err == nil { + if maskLen, _ := addr.Netmask.Size(); maskLen > 0 { + networks = append(networks, fmt.Sprintf("src net %s", net)) + } + } + } + var networkFilter string + if len(networks) >= 1 { + networkFilter = strings.Join(networks, " or ") + bpf_filter = fmt.Sprintf("(%s) and (%s)", bpf_filter, networkFilter) + } + return bpf_filter } @@ -53,3 +74,15 @@ func parseTimeout(timeout int64) time.Duration { } return to } + +// takes a net.IP and returns x.x.x.x/len format +func getNetwork(addr pcap.InterfaceAddress) (string, error) { + var ip4 net.IP + if ip4 = addr.IP.To4(); ip4 == nil { + return "", fmt.Errorf("Unable to getNetwork for IPv6 address: %s", addr.IP.String()) + } + + len, _ := addr.Netmask.Size() + mask := net.CIDRMask(len, 32) + return fmt.Sprintf("%s/%d", ip4.Mask(mask), len), nil +} diff --git a/go.mod b/go.mod index 9a13421..0e82eb9 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ module github.com/synfinatic/udp-proxy-2020 -go 1.14 +go 1.16 require ( + github.com/davecgh/go-spew v1.1.1 github.com/google/gopacket v1.1.18 github.com/sirupsen/logrus v1.7.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 83ec5ce..523820e 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2eP golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/startup-scripts/pfSense/usr/local/etc/udp-proxy-2020.conf b/startup-scripts/pfSense/usr/local/etc/udp-proxy-2020.conf index e316725..1c34fcc 100644 --- a/startup-scripts/pfSense/usr/local/etc/udp-proxy-2020.conf +++ b/startup-scripts/pfSense/usr/local/etc/udp-proxy-2020.conf @@ -1,2 +1,2 @@ -# change the port and add --dev {dev-name} as many times as needed. +# change the port and add --interface {dev-name} as many times as needed. udp_vars="--port 9003 --interface lagg0,lagg0.200,lagg0.400,ovpns2 --logfile /var/log/udp-proxy-2020.log"