Update dependencies
This commit is contained in:
292
vendor/tailscale.com/util/linuxfw/nftables.go
generated
vendored
Normal file
292
vendor/tailscale.com/util/linuxfw/nftables.go
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// TODO(#8502): add support for more architectures
|
||||
//go:build linux && (arm64 || amd64)
|
||||
|
||||
package linuxfw
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/google/nftables"
|
||||
"github.com/google/nftables/expr"
|
||||
"github.com/google/nftables/xt"
|
||||
"github.com/josharian/native"
|
||||
"golang.org/x/sys/unix"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
// DebugNetfilter prints debug information about netfilter rules to the
|
||||
// provided log function.
|
||||
func DebugNetfilter(logf logger.Logf) error {
|
||||
conn, err := nftables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chains, err := conn.ListChains()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot list chains: %w", err)
|
||||
}
|
||||
|
||||
if len(chains) == 0 {
|
||||
logf("netfilter: no chains")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, chain := range chains {
|
||||
logf("netfilter: table=%s chain=%s", chain.Table.Name, chain.Name)
|
||||
|
||||
rules, err := conn.GetRules(chain.Table, chain)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sort.Slice(rules, func(i, j int) bool {
|
||||
return rules[i].Position < rules[j].Position
|
||||
})
|
||||
|
||||
for i, rule := range rules {
|
||||
logf("netfilter: rule[%d]: pos=%d flags=%d", i, rule.Position, rule.Flags)
|
||||
for _, ex := range rule.Exprs {
|
||||
switch v := ex.(type) {
|
||||
case *expr.Meta:
|
||||
key := cmp.Or(metaKeyNames[v.Key], "UNKNOWN")
|
||||
logf("netfilter: Meta: key=%s source_register=%v register=%d", key, v.SourceRegister, v.Register)
|
||||
|
||||
case *expr.Cmp:
|
||||
op := cmp.Or(cmpOpNames[v.Op], "UNKNOWN")
|
||||
logf("netfilter: Cmp: op=%s register=%d data=%s", op, v.Register, formatMaybePrintable(v.Data))
|
||||
|
||||
case *expr.Counter:
|
||||
// don't print
|
||||
|
||||
case *expr.Verdict:
|
||||
kind := cmp.Or(verdictNames[v.Kind], "UNKNOWN")
|
||||
logf("netfilter: Verdict: kind=%s data=%s", kind, v.Chain)
|
||||
|
||||
case *expr.Target:
|
||||
logf("netfilter: Target: name=%s info=%s", v.Name, printTargetInfo(v.Name, v.Info))
|
||||
|
||||
case *expr.Match:
|
||||
logf("netfilter: Match: name=%s info=%+v", v.Name, printMatchInfo(v.Name, v.Info))
|
||||
|
||||
case *expr.Payload:
|
||||
logf("netfilter: Payload: op=%s src=%d dst=%d base=%s offset=%d len=%d",
|
||||
payloadOperationTypeNames[v.OperationType],
|
||||
v.SourceRegister, v.DestRegister,
|
||||
payloadBaseNames[v.Base],
|
||||
v.Offset, v.Len)
|
||||
// TODO(andrew): csum
|
||||
|
||||
case *expr.Bitwise:
|
||||
var xor string
|
||||
for _, b := range v.Xor {
|
||||
if b != 0 {
|
||||
xor = fmt.Sprintf(" xor=%v", v.Xor)
|
||||
break
|
||||
}
|
||||
}
|
||||
logf("netfilter: Bitwise: src=%d dst=%d len=%d mask=%v%s",
|
||||
v.SourceRegister, v.DestRegister, v.Len, v.Mask, xor)
|
||||
|
||||
default:
|
||||
logf("netfilter: unknown %T: %+v", v, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectNetfilter returns the number of nftables rules present in the system.
|
||||
func detectNetfilter() (int, error) {
|
||||
// Frist try creating a dummy postrouting chain. Emperically, we have
|
||||
// noticed that on some devices there is partial nftables support and the
|
||||
// kernel rejects some chains that are valid on other devices. This is a
|
||||
// workaround to detect that case.
|
||||
//
|
||||
// This specifically allows us to run in on GKE nodes using COS images which
|
||||
// have partial nftables support (as of 2023-10-18). When we try to create a
|
||||
// dummy postrouting chain, we get an error like:
|
||||
// add chain: conn.Receive: netlink receive: no such file or directory
|
||||
nft, err := newNfTablesRunner(logger.Discard)
|
||||
if err != nil {
|
||||
return 0, FWModeNotSupportedError{
|
||||
Mode: FirewallModeNfTables,
|
||||
Err: fmt.Errorf("cannot create nftables runner: %w", err),
|
||||
}
|
||||
}
|
||||
if err := nft.createDummyPostroutingChains(); err != nil {
|
||||
return 0, FWModeNotSupportedError{
|
||||
Mode: FirewallModeNfTables,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := nftables.New()
|
||||
if err != nil {
|
||||
return 0, FWModeNotSupportedError{
|
||||
Mode: FirewallModeNfTables,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
chains, err := conn.ListChains()
|
||||
if err != nil {
|
||||
return 0, FWModeNotSupportedError{
|
||||
Mode: FirewallModeNfTables,
|
||||
Err: fmt.Errorf("cannot list chains: %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
var validRules int
|
||||
for _, chain := range chains {
|
||||
rules, err := conn.GetRules(chain.Table, chain)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
validRules += len(rules)
|
||||
}
|
||||
|
||||
return validRules, nil
|
||||
}
|
||||
|
||||
func printMatchInfo(name string, info xt.InfoAny) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(`{`)
|
||||
|
||||
var handled bool = true
|
||||
switch v := info.(type) {
|
||||
// TODO(andrew): we should support these common types
|
||||
//case *xt.ConntrackMtinfo3:
|
||||
//case *xt.ConntrackMtinfo2:
|
||||
case *xt.Tcp:
|
||||
fmt.Fprintf(&sb, "Src:%s Dst:%s", formatPortRange(v.SrcPorts), formatPortRange(v.DstPorts))
|
||||
if v.Option != 0 {
|
||||
fmt.Fprintf(&sb, " Option:%d", v.Option)
|
||||
}
|
||||
if v.FlagsMask != 0 {
|
||||
fmt.Fprintf(&sb, " FlagsMask:%d", v.FlagsMask)
|
||||
}
|
||||
if v.FlagsCmp != 0 {
|
||||
fmt.Fprintf(&sb, " FlagsCmp:%d", v.FlagsCmp)
|
||||
}
|
||||
if v.InvFlags != 0 {
|
||||
fmt.Fprintf(&sb, " InvFlags:%d", v.InvFlags)
|
||||
}
|
||||
|
||||
case *xt.Udp:
|
||||
fmt.Fprintf(&sb, "Src:%s Dst:%s", formatPortRange(v.SrcPorts), formatPortRange(v.DstPorts))
|
||||
if v.InvFlags != 0 {
|
||||
fmt.Fprintf(&sb, " InvFlags:%d", v.InvFlags)
|
||||
}
|
||||
|
||||
case *xt.AddrType:
|
||||
var sprefix, dprefix string
|
||||
if v.InvertSource {
|
||||
sprefix = "!"
|
||||
}
|
||||
if v.InvertDest {
|
||||
dprefix = "!"
|
||||
}
|
||||
// TODO(andrew): translate source/dest
|
||||
fmt.Fprintf(&sb, "Source:%s%d Dest:%s%d", sprefix, v.Source, dprefix, v.Dest)
|
||||
|
||||
case *xt.AddrTypeV1:
|
||||
// TODO(andrew): translate source/dest
|
||||
fmt.Fprintf(&sb, "Source:%d Dest:%d", v.Source, v.Dest)
|
||||
|
||||
var flags []string
|
||||
for flag, name := range addrTypeFlagNames {
|
||||
if v.Flags&flag != 0 {
|
||||
flags = append(flags, name)
|
||||
}
|
||||
}
|
||||
if len(flags) > 0 {
|
||||
sort.Strings(flags)
|
||||
fmt.Fprintf(&sb, "Flags:%s", strings.Join(flags, ","))
|
||||
}
|
||||
|
||||
default:
|
||||
handled = false
|
||||
}
|
||||
if handled {
|
||||
sb.WriteString(`}`)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
unknown, ok := info.(*xt.Unknown)
|
||||
if !ok {
|
||||
return fmt.Sprintf("(%T)%+v", info, info)
|
||||
}
|
||||
data := []byte(*unknown)
|
||||
|
||||
// Things where upstream has no type
|
||||
handled = true
|
||||
switch name {
|
||||
case "pkttype":
|
||||
if len(data) != 8 {
|
||||
handled = false
|
||||
break
|
||||
}
|
||||
|
||||
pkttype := int(native.Endian.Uint32(data[0:4]))
|
||||
invert := int(native.Endian.Uint32(data[4:8]))
|
||||
var invertPrefix string
|
||||
if invert != 0 {
|
||||
invertPrefix = "!"
|
||||
}
|
||||
|
||||
pkttypeName := packetTypeNames[pkttype]
|
||||
if pkttypeName != "" {
|
||||
fmt.Fprintf(&sb, "PktType:%s%s", invertPrefix, pkttypeName)
|
||||
} else {
|
||||
fmt.Fprintf(&sb, "PktType:%s%d", invertPrefix, pkttype)
|
||||
}
|
||||
|
||||
default:
|
||||
handled = true
|
||||
}
|
||||
|
||||
if !handled {
|
||||
return fmt.Sprintf("(%T)%+v", info, info)
|
||||
}
|
||||
|
||||
sb.WriteString(`}`)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func printTargetInfo(name string, info xt.InfoAny) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(`{`)
|
||||
|
||||
unknown, ok := info.(*xt.Unknown)
|
||||
if !ok {
|
||||
return fmt.Sprintf("(%T)%+v", info, info)
|
||||
}
|
||||
data := []byte(*unknown)
|
||||
|
||||
// Things where upstream has no type
|
||||
switch name {
|
||||
case "LOG":
|
||||
if len(data) != 32 {
|
||||
fmt.Fprintf(&sb, `Error:"bad size; want 32, got %d"`, len(data))
|
||||
break
|
||||
}
|
||||
|
||||
level := data[0]
|
||||
logflags := data[1]
|
||||
prefix := unix.ByteSliceToString(data[2:])
|
||||
fmt.Fprintf(&sb, "Level:%d LogFlags:%d Prefix:%q", level, logflags, prefix)
|
||||
default:
|
||||
return fmt.Sprintf("(%T)%+v", info, info)
|
||||
}
|
||||
|
||||
sb.WriteString(`}`)
|
||||
return sb.String()
|
||||
}
|
||||
Reference in New Issue
Block a user