//go:build linux package tun import ( "net/netip" "github.com/sagernet/nftables" "github.com/sagernet/nftables/expr" "go4.org/netipx" ) func nftablesIfname(n string) []byte { b := make([]byte, 16) copy(b, n+"\x00") return b } func nftablesCreateExcludeDestinationIPSet( nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain, id uint32, name string, family nftables.TableFamily, invert bool, ) { exprs := []expr.Any{ &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(family)}, }, } if family == nftables.TableFamilyIPv4 { exprs = append(exprs, &expr.Payload{ OperationType: expr.PayloadLoad, DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 16, Len: 4, }, ) } else { exprs = append(exprs, &expr.Payload{ OperationType: expr.PayloadLoad, DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 24, Len: 16, }, ) } exprs = append(exprs, &expr.Lookup{ SourceRegister: 1, SetID: id, SetName: name, Invert: invert, }, &expr.Counter{}, &expr.Verdict{ Kind: expr.VerdictReturn, }) nft.AddRule(&nftables.Rule{ Table: table, Chain: chain, Exprs: exprs, }) } func nftablesCreateIPSet( nft *nftables.Conn, table *nftables.Table, id uint32, name string, family nftables.TableFamily, setList []*netipx.IPSet, prefixList []netip.Prefix, appendDefault bool, update bool, ) (*nftables.Set, error) { var builder netipx.IPSetBuilder for _, prefix := range prefixList { builder.AddPrefix(prefix) } for _, set := range setList { builder.AddSet(set) } ipSet, err := builder.IPSet() if err != nil { return nil, err } ipRanges := ipSet.Ranges() setElements := make([]nftables.SetElement, 0, len(ipRanges)) for _, rr := range ipRanges { if (family == nftables.TableFamilyIPv4) != rr.From().Is4() { continue } endAddr := rr.To().Next() if !endAddr.IsValid() { endAddr = rr.From() } setElements = append(setElements, nftables.SetElement{ Key: rr.From().AsSlice(), }) setElements = append(setElements, nftables.SetElement{ Key: endAddr.AsSlice(), IntervalEnd: true, }) } if len(prefixList) == 0 && appendDefault { if family == nftables.TableFamilyIPv4 { setElements = append(setElements, nftables.SetElement{ Key: netip.IPv4Unspecified().AsSlice(), }, nftables.SetElement{ Key: netip.IPv4Unspecified().AsSlice(), IntervalEnd: true, }) } else { setElements = append(setElements, nftables.SetElement{ Key: netip.IPv6Unspecified().AsSlice(), }, nftables.SetElement{ Key: netip.IPv6Unspecified().AsSlice(), IntervalEnd: true, }) } } var keyType nftables.SetDatatype if family == nftables.TableFamilyIPv4 { keyType = nftables.TypeIPAddr } else { keyType = nftables.TypeIP6Addr } mySet := &nftables.Set{ Table: table, ID: id, Name: name, Interval: true, KeyType: keyType, } if id == 0 { mySet.Anonymous = true mySet.Constant = true } if id == 0 { err := nft.AddSet(mySet, setElements) if err != nil { return nil, err } return mySet, nil } else if update { nft.FlushSet(mySet) } else { err := nft.AddSet(mySet, nil) if err != nil { return nil, err } } for len(setElements) > 0 { toAdd := setElements if len(toAdd) > 1000 { toAdd = toAdd[:1000] } setElements = setElements[len(toAdd):] err := nft.SetAddElements(mySet, toAdd) if err != nil { return nil, err } err = nft.Flush() if err != nil { return nil, err } } return mySet, nil }