net/packet: add IPv6 source and destination IPs to Parsed.
Signed-off-by: David Anderson <danderson@tailscale.com>pull/914/head
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…
Reference in New Issue