2020-02-05 22:16:58 +00:00
|
|
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package filter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-04-30 05:49:17 +00:00
|
|
|
"math/bits"
|
|
|
|
"net"
|
2020-02-05 22:16:58 +00:00
|
|
|
"strings"
|
2020-02-29 03:27:17 +00:00
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
"tailscale.com/wgengine/packet"
|
|
|
|
)
|
|
|
|
|
2020-05-11 14:19:17 +00:00
|
|
|
func NewIP(ip net.IP) packet.IP {
|
2020-04-30 05:49:17 +00:00
|
|
|
return packet.NewIP(ip)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Net struct {
|
2020-05-11 14:19:17 +00:00
|
|
|
IP packet.IP
|
|
|
|
Mask packet.IP
|
2020-04-30 05:49:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:19:17 +00:00
|
|
|
func (n Net) Includes(ip packet.IP) bool {
|
2020-04-30 05:49:17 +00:00
|
|
|
return (n.IP & n.Mask) == (ip & n.Mask)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n Net) Bits() int {
|
|
|
|
return 32 - bits.TrailingZeros32(uint32(n.Mask))
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-04-30 05:49:17 +00:00
|
|
|
func (n Net) String() string {
|
|
|
|
b := n.Bits()
|
|
|
|
if b == 32 {
|
|
|
|
return n.IP.String()
|
|
|
|
} else if b == 0 {
|
|
|
|
return "*"
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("%s/%d", n.IP, b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var NetAny = Net{0, 0}
|
2020-05-11 14:19:17 +00:00
|
|
|
var NetNone = Net{^packet.IP(0), ^packet.IP(0)}
|
2020-04-30 05:49:17 +00:00
|
|
|
|
2020-05-11 14:19:17 +00:00
|
|
|
func Netmask(bits int) packet.IP {
|
2020-04-30 10:16:53 +00:00
|
|
|
b := ^uint32((1 << (32 - bits)) - 1)
|
2020-05-11 14:19:17 +00:00
|
|
|
return packet.IP(b)
|
2020-04-30 05:49:17 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
type PortRange struct {
|
|
|
|
First, Last uint16
|
|
|
|
}
|
|
|
|
|
|
|
|
var PortRangeAny = PortRange{0, 65535}
|
|
|
|
|
|
|
|
func (pr PortRange) String() string {
|
|
|
|
if pr.First == 0 && pr.Last == 65535 {
|
|
|
|
return "*"
|
|
|
|
} else if pr.First == pr.Last {
|
|
|
|
return fmt.Sprintf("%d", pr.First)
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("%d-%d", pr.First, pr.Last)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 05:49:17 +00:00
|
|
|
type NetPortRange struct {
|
|
|
|
Net Net
|
2020-02-05 22:16:58 +00:00
|
|
|
Ports PortRange
|
|
|
|
}
|
|
|
|
|
2020-04-30 05:49:17 +00:00
|
|
|
var NetPortRangeAny = NetPortRange{NetAny, PortRangeAny}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-04-30 05:49:17 +00:00
|
|
|
func (ipr NetPortRange) String() string {
|
|
|
|
return fmt.Sprintf("%v:%v", ipr.Net, ipr.Ports)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Match struct {
|
2020-04-30 05:49:17 +00:00
|
|
|
Dsts []NetPortRange
|
|
|
|
Srcs []Net
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-29 03:27:17 +00:00
|
|
|
func (m Match) Clone() (res Match) {
|
2020-04-30 05:49:17 +00:00
|
|
|
if m.Dsts != nil {
|
|
|
|
res.Dsts = append([]NetPortRange{}, m.Dsts...)
|
2020-02-29 03:27:17 +00:00
|
|
|
}
|
2020-04-30 05:49:17 +00:00
|
|
|
if m.Srcs != nil {
|
|
|
|
res.Srcs = append([]Net{}, m.Srcs...)
|
2020-02-29 03:27:17 +00:00
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
func (m Match) String() string {
|
|
|
|
srcs := []string{}
|
2020-04-30 05:49:17 +00:00
|
|
|
for _, src := range m.Srcs {
|
|
|
|
srcs = append(srcs, src.String())
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
dsts := []string{}
|
2020-04-30 05:49:17 +00:00
|
|
|
for _, dst := range m.Dsts {
|
2020-02-05 22:16:58 +00:00
|
|
|
dsts = append(dsts, dst.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
var ss, ds string
|
|
|
|
if len(srcs) == 1 {
|
|
|
|
ss = srcs[0]
|
|
|
|
} else {
|
|
|
|
ss = "[" + strings.Join(srcs, ",") + "]"
|
|
|
|
}
|
|
|
|
if len(dsts) == 1 {
|
|
|
|
ds = dsts[0]
|
|
|
|
} else {
|
|
|
|
ds = "[" + strings.Join(dsts, ",") + "]"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%v=>%v", ss, ds)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Matches []Match
|
|
|
|
|
2020-02-29 03:27:17 +00:00
|
|
|
func (m Matches) Clone() (res Matches) {
|
|
|
|
for _, match := range m {
|
|
|
|
res = append(res, match.Clone())
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:19:17 +00:00
|
|
|
func ipInList(ip packet.IP, netlist []Net) bool {
|
2020-04-30 05:49:17 +00:00
|
|
|
for _, net := range netlist {
|
|
|
|
if net.Includes(ip) {
|
2020-02-05 22:16:58 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func matchIPPorts(mm Matches, q *packet.QDecode) bool {
|
|
|
|
for _, acl := range mm {
|
2020-04-30 05:49:17 +00:00
|
|
|
for _, dst := range acl.Dsts {
|
|
|
|
if !dst.Net.Includes(q.DstIP) {
|
2020-02-05 22:16:58 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if q.DstPort < dst.Ports.First || q.DstPort > dst.Ports.Last {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-30 05:49:17 +00:00
|
|
|
if !ipInList(q.SrcIP, acl.Srcs) {
|
2020-02-05 22:16:58 +00:00
|
|
|
// Skip other dests in this acl, since
|
|
|
|
// the src will never match.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func matchIPWithoutPorts(mm Matches, q *packet.QDecode) bool {
|
|
|
|
for _, acl := range mm {
|
2020-04-30 05:49:17 +00:00
|
|
|
for _, dst := range acl.Dsts {
|
|
|
|
if !dst.Net.Includes(q.DstIP) {
|
2020-02-05 22:16:58 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-04-30 05:49:17 +00:00
|
|
|
if !ipInList(q.SrcIP, acl.Srcs) {
|
2020-02-05 22:16:58 +00:00
|
|
|
// Skip other dests in this acl, since
|
|
|
|
// the src will never match.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|