From 89894c6930783de1eec67a18537368badd68e441 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 10 Nov 2020 00:04:27 -0800 Subject: [PATCH] net/packet: add IPv6 source and destination IPs to Parsed. Signed-off-by: David Anderson --- net/packet/ip6.go | 24 ++++++++++++++++++++++++ net/packet/packet.go | 18 ++++++++++-------- net/packet/packet_test.go | 20 ++++++++++---------- wgengine/filter/filter.go | 16 ++++++++-------- wgengine/filter/filter_test.go | 12 ++++++------ wgengine/filter/match4.go | 8 ++++---- wgengine/tstun/tun.go | 2 +- wgengine/userspace.go | 6 +++--- 8 files changed, 66 insertions(+), 40 deletions(-) create mode 100644 net/packet/ip6.go diff --git a/net/packet/ip6.go b/net/packet/ip6.go new file mode 100644 index 000000000..ad5c0a033 --- /dev/null +++ b/net/packet/ip6.go @@ -0,0 +1,24 @@ +package packet + +import ( + "fmt" + + "inet.af/netaddr" +) + +type IP6 [16]byte + +func IP6FromNetaddr(ip netaddr.IP) IP6 { + if !ip.Is6() { + panic(fmt.Sprintf("IP6FromNetaddr called with non-v6 addr %q", ip)) + } + return IP6(ip.As16()) +} + +func (ip IP6) Netaddr() netaddr.IP { + return netaddr.IPFrom16(ip) +} + +func (ip IP6) String() string { + return ip.Netaddr().String() +} diff --git a/net/packet/packet.go b/net/packet/packet.go index c1a510e8a..2b87339f2 100644 --- a/net/packet/packet.go +++ b/net/packet/packet.go @@ -45,8 +45,10 @@ type Parsed struct { IPVersion uint8 // 4, 6, or 0 IPProto IP4Proto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6 - SrcIP IP4 // IP source address (not used for IPv6) - DstIP IP4 // IP destination address (not used for IPv6) + SrcIP4 IP4 // IPv4 source address (not used for IPv6) + DstIP4 IP4 // IPv4 destination address (not used for IPv6) + SrcIP6 IP6 // IPv6 source address (not used for IPv4) + DstIP6 IP6 // IPv6 destination address (not used for IPv4) SrcPort uint16 // TCP/UDP source port DstPort uint16 // TCP/UDP destination port TCPFlags uint8 // TCP flags (SYN, ACK, etc) @@ -66,9 +68,9 @@ func (p *Parsed) String() string { sb := strbuilder.Get() sb.WriteString(p.IPProto.String()) sb.WriteByte('{') - writeIPPort(sb, p.SrcIP, p.SrcPort) + writeIPPort(sb, p.SrcIP4, p.SrcPort) sb.WriteString(" > ") - writeIPPort(sb, p.DstIP, p.DstPort) + writeIPPort(sb, p.DstIP4, p.DstPort) sb.WriteByte('}') return sb.String() } @@ -140,8 +142,8 @@ func (q *Parsed) Decode(b []byte) { } // If it's valid IPv4, then the IP addresses are valid - q.SrcIP = IP4(get32(b[12:16])) - q.DstIP = IP4(get32(b[16:20])) + q.SrcIP4 = IP4(get32(b[12:16])) + q.DstIP4 = IP4(get32(b[16:20])) q.subofs = int((b[0] & 0x0F) << 2) sub := b[q.subofs:] @@ -229,8 +231,8 @@ func (q *Parsed) IPHeader() IP4Header { return IP4Header{ IPID: ipid, IPProto: q.IPProto, - SrcIP: q.SrcIP, - DstIP: q.DstIP, + SrcIP: q.SrcIP4, + DstIP: q.DstIP4, } } diff --git a/net/packet/packet_test.go b/net/packet/packet_test.go index f0e536096..ecbe25a21 100644 --- a/net/packet/packet_test.go +++ b/net/packet/packet_test.go @@ -49,8 +49,8 @@ var icmpRequestDecode = Parsed{ IPVersion: 4, IPProto: ICMP, - SrcIP: NewIP4(net.ParseIP("1.2.3.4")), - DstIP: NewIP4(net.ParseIP("5.6.7.8")), + SrcIP4: NewIP4(net.ParseIP("1.2.3.4")), + DstIP4: NewIP4(net.ParseIP("5.6.7.8")), SrcPort: 0, DstPort: 0, } @@ -75,8 +75,8 @@ var icmpReplyDecode = Parsed{ IPVersion: 4, IPProto: ICMP, - SrcIP: NewIP4(net.ParseIP("1.2.3.4")), - DstIP: NewIP4(net.ParseIP("5.6.7.8")), + SrcIP4: NewIP4(net.ParseIP("1.2.3.4")), + DstIP4: NewIP4(net.ParseIP("5.6.7.8")), SrcPort: 0, DstPort: 0, } @@ -131,8 +131,8 @@ var tcpPacketDecode = Parsed{ IPVersion: 4, IPProto: TCP, - SrcIP: NewIP4(net.ParseIP("1.2.3.4")), - DstIP: NewIP4(net.ParseIP("5.6.7.8")), + SrcIP4: NewIP4(net.ParseIP("1.2.3.4")), + DstIP4: NewIP4(net.ParseIP("5.6.7.8")), SrcPort: 123, DstPort: 567, TCPFlags: TCPSynAck, @@ -159,8 +159,8 @@ var udpRequestDecode = Parsed{ IPVersion: 4, IPProto: UDP, - SrcIP: NewIP4(net.ParseIP("1.2.3.4")), - DstIP: NewIP4(net.ParseIP("5.6.7.8")), + SrcIP4: NewIP4(net.ParseIP("1.2.3.4")), + DstIP4: NewIP4(net.ParseIP("5.6.7.8")), SrcPort: 123, DstPort: 567, } @@ -185,8 +185,8 @@ var udpReplyDecode = Parsed{ length: len(udpReplyBuffer), IPProto: UDP, - SrcIP: NewIP4(net.ParseIP("1.2.3.4")), - DstIP: NewIP4(net.ParseIP("5.6.7.8")), + SrcIP4: NewIP4(net.ParseIP("1.2.3.4")), + DstIP4: NewIP4(net.ParseIP("5.6.7.8")), SrcPort: 567, DstPort: 123, } diff --git a/wgengine/filter/filter.go b/wgengine/filter/filter.go index 91dfc6226..024427d78 100644 --- a/wgengine/filter/filter.go +++ b/wgengine/filter/filter.go @@ -191,8 +191,8 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response { pkt.IPVersion = 4 pkt.IPProto = packet.TCP pkt.TCPFlags = packet.TCPSyn - pkt.SrcIP = packet.IP4FromNetaddr(srcIP) // TODO: IPv6 - pkt.DstIP = packet.IP4FromNetaddr(dstIP) + pkt.SrcIP4 = packet.IP4FromNetaddr(srcIP) // TODO: IPv6 + pkt.DstIP4 = packet.IP4FromNetaddr(dstIP) pkt.SrcPort = 0 pkt.DstPort = dstPort @@ -233,7 +233,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) { // A compromised peer could try to send us packets for // destinations we didn't explicitly advertise. This check is to // prevent that. - if !ip4InList(q.DstIP, f.local4) { + if !ip4InList(q.DstIP4, f.local4) { return Drop, "destination not allowed" } @@ -271,7 +271,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) { return Accept, "tcp ok" } case packet.UDP: - t := tuple{q.SrcIP, q.DstIP, q.SrcPort, q.DstPort} + t := tuple{q.SrcIP4, q.DstIP4, q.SrcPort, q.DstPort} f.state.mu.Lock() _, ok := f.state.lru.Get(t) @@ -292,7 +292,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) { // runIn runs the output-specific part of the filter logic. func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) { if q.IPProto == packet.UDP { - t := tuple{q.DstIP, q.SrcIP, q.DstPort, q.SrcPort} + t := tuple{q.DstIP4, q.SrcIP4, q.DstPort, q.SrcPort} var ti interface{} = t // allocate once, rather than twice inside mutex f.state.mu.Lock() @@ -338,11 +338,11 @@ func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response { f.logRateLimit(rf, q, dir, Drop, "ipv6") return Drop } - if q.DstIP.IsMulticast() { + if q.DstIP4.IsMulticast() { f.logRateLimit(rf, q, dir, Drop, "multicast") return Drop } - if q.DstIP.IsLinkLocalUnicast() { + if q.DstIP4.IsLinkLocalUnicast() { f.logRateLimit(rf, q, dir, Drop, "link-local-unicast") return Drop } @@ -389,7 +389,7 @@ func omitDropLogging(p *packet.Parsed, dir direction) bool { if ipProto == packet.IGMP { return true } - if p.DstIP.IsMulticast() || p.DstIP.IsLinkLocalUnicast() { + if p.DstIP4.IsMulticast() || p.DstIP4.IsLinkLocalUnicast() { return true } case 6: diff --git a/wgengine/filter/filter_test.go b/wgengine/filter/filter_test.go index 5fa9e9fe9..ffec6fa3d 100644 --- a/wgengine/filter/filter_test.go +++ b/wgengine/filter/filter_test.go @@ -168,7 +168,7 @@ func TestFilter(t *testing.T) { t.Errorf("#%d runIn got=%v want=%v packet:%v", i, got, test.want, test.p) } if test.p.IPProto == TCP { - if got := acl.CheckTCP(test.p.SrcIP.Netaddr(), test.p.DstIP.Netaddr(), test.p.DstPort); test.want != got { + if got := acl.CheckTCP(test.p.SrcIP4.Netaddr(), test.p.DstIP4.Netaddr(), test.p.DstPort); test.want != got { t.Errorf("#%d CheckTCP got=%v want=%v packet:%v", i, got, test.want, test.p) } } @@ -315,8 +315,8 @@ func TestPreFilter(t *testing.T) { func parsed(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16) packet.Parsed { return packet.Parsed{ IPProto: proto, - SrcIP: src, - DstIP: dst, + SrcIP4: src, + DstIP4: dst, SrcPort: sport, DstPort: dport, TCPFlags: packet.TCPSyn, @@ -435,19 +435,19 @@ func TestOmitDropLogging(t *testing.T) { }, { name: "v4_multicast_out_low", - pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("224.0.0.0"))}, + pkt: &packet.Parsed{IPVersion: 4, DstIP4: packet.NewIP4(net.ParseIP("224.0.0.0"))}, dir: out, want: true, }, { name: "v4_multicast_out_high", - pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("239.255.255.255"))}, + pkt: &packet.Parsed{IPVersion: 4, DstIP4: packet.NewIP4(net.ParseIP("239.255.255.255"))}, dir: out, want: true, }, { name: "v4_link_local_unicast", - pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("169.254.1.2"))}, + pkt: &packet.Parsed{IPVersion: 4, DstIP4: packet.NewIP4(net.ParseIP("169.254.1.2"))}, dir: out, want: true, }, diff --git a/wgengine/filter/match4.go b/wgengine/filter/match4.go index c0d0a8168..d239b497f 100644 --- a/wgengine/filter/match4.go +++ b/wgengine/filter/match4.go @@ -104,11 +104,11 @@ func newMatches4(ms []Match) (ret matches4) { // any of ms. func (ms matches4) match(q *packet.Parsed) bool { for _, m := range ms { - if !ip4InList(q.SrcIP, m.srcs) { + if !ip4InList(q.SrcIP4, m.srcs) { continue } for _, dst := range m.dsts { - if !dst.net.Contains(q.DstIP) { + if !dst.net.Contains(q.DstIP4) { continue } if !dst.ports.contains(q.DstPort) { @@ -124,11 +124,11 @@ func (ms matches4) match(q *packet.Parsed) bool { // any of ms. func (ms matches4) matchIPsOnly(q *packet.Parsed) bool { for _, m := range ms { - if !ip4InList(q.SrcIP, m.srcs) { + if !ip4InList(q.SrcIP4, m.srcs) { continue } for _, dst := range m.dsts { - if dst.net.Contains(q.DstIP) { + if dst.net.Contains(q.DstIP4) { return true } } diff --git a/wgengine/tstun/tun.go b/wgengine/tstun/tun.go index 40ba756e0..be6940194 100644 --- a/wgengine/tstun/tun.go +++ b/wgengine/tstun/tun.go @@ -283,7 +283,7 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) { p.Decode(buf[offset : offset+n]) if m, ok := t.destIPActivity.Load().(map[packet.IP4]func()); ok { - if fn := m[p.DstIP]; fn != nil { + if fn := m[p.DstIP4]; fn != nil { fn() } } diff --git a/wgengine/userspace.go b/wgengine/userspace.go index b4ffe9065..909a3d956 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -397,7 +397,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.TUN) fil return filter.Drop } - if runtime.GOOS == "darwin" && e.isLocalAddr(p.DstIP) { + if runtime.GOOS == "darwin" && e.isLocalAddr(p.DstIP4) { // macOS NetworkExtension directs packets destined to the // tunnel's local IP address into the tunnel, instead of // looping back within the kernel network stack. We have to @@ -421,10 +421,10 @@ func (e *userspaceEngine) isLocalAddr(ip packet.IP4) bool { // handleDNS is an outbound pre-filter resolving Tailscale domains. func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response { - if p.DstIP == magicDNSIP && p.DstPort == magicDNSPort && p.IPProto == packet.UDP { + if p.DstIP4 == magicDNSIP && p.DstPort == magicDNSPort && p.IPProto == packet.UDP { request := tsdns.Packet{ Payload: append([]byte(nil), p.Payload()...), - Addr: netaddr.IPPort{IP: p.SrcIP.Netaddr(), Port: p.SrcPort}, + Addr: netaddr.IPPort{IP: p.SrcIP4.Netaddr(), Port: p.SrcPort}, } err := e.resolver.EnqueueRequest(request) if err != nil {