net/packet: add IPv6 source and destination IPs to Parsed.

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/914/head
David Anderson 4 years ago committed by Dave Anderson
parent d192bd0f86
commit 89894c6930

@ -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()
}

@ -45,8 +45,10 @@ type Parsed struct {
IPVersion uint8 // 4, 6, or 0 IPVersion uint8 // 4, 6, or 0
IPProto IP4Proto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6 IPProto IP4Proto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6
SrcIP IP4 // IP source address (not used for IPv6) SrcIP4 IP4 // IPv4 source address (not used for IPv6)
DstIP IP4 // IP destination 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 SrcPort uint16 // TCP/UDP source port
DstPort uint16 // TCP/UDP destination port DstPort uint16 // TCP/UDP destination port
TCPFlags uint8 // TCP flags (SYN, ACK, etc) TCPFlags uint8 // TCP flags (SYN, ACK, etc)
@ -66,9 +68,9 @@ func (p *Parsed) String() string {
sb := strbuilder.Get() sb := strbuilder.Get()
sb.WriteString(p.IPProto.String()) sb.WriteString(p.IPProto.String())
sb.WriteByte('{') sb.WriteByte('{')
writeIPPort(sb, p.SrcIP, p.SrcPort) writeIPPort(sb, p.SrcIP4, p.SrcPort)
sb.WriteString(" > ") sb.WriteString(" > ")
writeIPPort(sb, p.DstIP, p.DstPort) writeIPPort(sb, p.DstIP4, p.DstPort)
sb.WriteByte('}') sb.WriteByte('}')
return sb.String() return sb.String()
} }
@ -140,8 +142,8 @@ func (q *Parsed) Decode(b []byte) {
} }
// If it's valid IPv4, then the IP addresses are valid // If it's valid IPv4, then the IP addresses are valid
q.SrcIP = IP4(get32(b[12:16])) q.SrcIP4 = IP4(get32(b[12:16]))
q.DstIP = IP4(get32(b[16:20])) q.DstIP4 = IP4(get32(b[16:20]))
q.subofs = int((b[0] & 0x0F) << 2) q.subofs = int((b[0] & 0x0F) << 2)
sub := b[q.subofs:] sub := b[q.subofs:]
@ -229,8 +231,8 @@ func (q *Parsed) IPHeader() IP4Header {
return IP4Header{ return IP4Header{
IPID: ipid, IPID: ipid,
IPProto: q.IPProto, IPProto: q.IPProto,
SrcIP: q.SrcIP, SrcIP: q.SrcIP4,
DstIP: q.DstIP, DstIP: q.DstIP4,
} }
} }

@ -49,8 +49,8 @@ var icmpRequestDecode = Parsed{
IPVersion: 4, IPVersion: 4,
IPProto: ICMP, IPProto: ICMP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 0, SrcPort: 0,
DstPort: 0, DstPort: 0,
} }
@ -75,8 +75,8 @@ var icmpReplyDecode = Parsed{
IPVersion: 4, IPVersion: 4,
IPProto: ICMP, IPProto: ICMP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 0, SrcPort: 0,
DstPort: 0, DstPort: 0,
} }
@ -131,8 +131,8 @@ var tcpPacketDecode = Parsed{
IPVersion: 4, IPVersion: 4,
IPProto: TCP, IPProto: TCP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 123, SrcPort: 123,
DstPort: 567, DstPort: 567,
TCPFlags: TCPSynAck, TCPFlags: TCPSynAck,
@ -159,8 +159,8 @@ var udpRequestDecode = Parsed{
IPVersion: 4, IPVersion: 4,
IPProto: UDP, IPProto: UDP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 123, SrcPort: 123,
DstPort: 567, DstPort: 567,
} }
@ -185,8 +185,8 @@ var udpReplyDecode = Parsed{
length: len(udpReplyBuffer), length: len(udpReplyBuffer),
IPProto: UDP, IPProto: UDP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 567, SrcPort: 567,
DstPort: 123, DstPort: 123,
} }

@ -191,8 +191,8 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
pkt.IPVersion = 4 pkt.IPVersion = 4
pkt.IPProto = packet.TCP pkt.IPProto = packet.TCP
pkt.TCPFlags = packet.TCPSyn pkt.TCPFlags = packet.TCPSyn
pkt.SrcIP = packet.IP4FromNetaddr(srcIP) // TODO: IPv6 pkt.SrcIP4 = packet.IP4FromNetaddr(srcIP) // TODO: IPv6
pkt.DstIP = packet.IP4FromNetaddr(dstIP) pkt.DstIP4 = packet.IP4FromNetaddr(dstIP)
pkt.SrcPort = 0 pkt.SrcPort = 0
pkt.DstPort = dstPort 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 // A compromised peer could try to send us packets for
// destinations we didn't explicitly advertise. This check is to // destinations we didn't explicitly advertise. This check is to
// prevent that. // prevent that.
if !ip4InList(q.DstIP, f.local4) { if !ip4InList(q.DstIP4, f.local4) {
return Drop, "destination not allowed" return Drop, "destination not allowed"
} }
@ -271,7 +271,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) {
return Accept, "tcp ok" return Accept, "tcp ok"
} }
case packet.UDP: 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() f.state.mu.Lock()
_, ok := f.state.lru.Get(t) _, 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. // runIn runs the output-specific part of the filter logic.
func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) { func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) {
if q.IPProto == packet.UDP { 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 var ti interface{} = t // allocate once, rather than twice inside mutex
f.state.mu.Lock() 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") f.logRateLimit(rf, q, dir, Drop, "ipv6")
return Drop return Drop
} }
if q.DstIP.IsMulticast() { if q.DstIP4.IsMulticast() {
f.logRateLimit(rf, q, dir, Drop, "multicast") f.logRateLimit(rf, q, dir, Drop, "multicast")
return Drop return Drop
} }
if q.DstIP.IsLinkLocalUnicast() { if q.DstIP4.IsLinkLocalUnicast() {
f.logRateLimit(rf, q, dir, Drop, "link-local-unicast") f.logRateLimit(rf, q, dir, Drop, "link-local-unicast")
return Drop return Drop
} }
@ -389,7 +389,7 @@ func omitDropLogging(p *packet.Parsed, dir direction) bool {
if ipProto == packet.IGMP { if ipProto == packet.IGMP {
return true return true
} }
if p.DstIP.IsMulticast() || p.DstIP.IsLinkLocalUnicast() { if p.DstIP4.IsMulticast() || p.DstIP4.IsLinkLocalUnicast() {
return true return true
} }
case 6: case 6:

@ -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) t.Errorf("#%d runIn got=%v want=%v packet:%v", i, got, test.want, test.p)
} }
if test.p.IPProto == TCP { 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) 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 { func parsed(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16) packet.Parsed {
return packet.Parsed{ return packet.Parsed{
IPProto: proto, IPProto: proto,
SrcIP: src, SrcIP4: src,
DstIP: dst, DstIP4: dst,
SrcPort: sport, SrcPort: sport,
DstPort: dport, DstPort: dport,
TCPFlags: packet.TCPSyn, TCPFlags: packet.TCPSyn,
@ -435,19 +435,19 @@ func TestOmitDropLogging(t *testing.T) {
}, },
{ {
name: "v4_multicast_out_low", 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, dir: out,
want: true, want: true,
}, },
{ {
name: "v4_multicast_out_high", 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, dir: out,
want: true, want: true,
}, },
{ {
name: "v4_link_local_unicast", 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, dir: out,
want: true, want: true,
}, },

@ -104,11 +104,11 @@ func newMatches4(ms []Match) (ret matches4) {
// any of ms. // any of ms.
func (ms matches4) match(q *packet.Parsed) bool { func (ms matches4) match(q *packet.Parsed) bool {
for _, m := range ms { for _, m := range ms {
if !ip4InList(q.SrcIP, m.srcs) { if !ip4InList(q.SrcIP4, m.srcs) {
continue continue
} }
for _, dst := range m.dsts { for _, dst := range m.dsts {
if !dst.net.Contains(q.DstIP) { if !dst.net.Contains(q.DstIP4) {
continue continue
} }
if !dst.ports.contains(q.DstPort) { if !dst.ports.contains(q.DstPort) {
@ -124,11 +124,11 @@ func (ms matches4) match(q *packet.Parsed) bool {
// any of ms. // any of ms.
func (ms matches4) matchIPsOnly(q *packet.Parsed) bool { func (ms matches4) matchIPsOnly(q *packet.Parsed) bool {
for _, m := range ms { for _, m := range ms {
if !ip4InList(q.SrcIP, m.srcs) { if !ip4InList(q.SrcIP4, m.srcs) {
continue continue
} }
for _, dst := range m.dsts { for _, dst := range m.dsts {
if dst.net.Contains(q.DstIP) { if dst.net.Contains(q.DstIP4) {
return true return true
} }
} }

@ -283,7 +283,7 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) {
p.Decode(buf[offset : offset+n]) p.Decode(buf[offset : offset+n])
if m, ok := t.destIPActivity.Load().(map[packet.IP4]func()); ok { 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() fn()
} }
} }

@ -397,7 +397,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.TUN) fil
return filter.Drop 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 // macOS NetworkExtension directs packets destined to the
// tunnel's local IP address into the tunnel, instead of // tunnel's local IP address into the tunnel, instead of
// looping back within the kernel network stack. We have to // 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. // handleDNS is an outbound pre-filter resolving Tailscale domains.
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response { 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{ request := tsdns.Packet{
Payload: append([]byte(nil), p.Payload()...), 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) err := e.resolver.EnqueueRequest(request)
if err != nil { if err != nil {

Loading…
Cancel
Save