Skip to content

Commit 1197de6

Browse files
kerumetogvisor-bot
authored andcommitted
Implement DUMP message support for NETFILTER sockets.
The nft CLI relies on DUMP messages to initialize its cache. Support for DUMP messages is implemented here for both tables and chains. PiperOrigin-RevId: 800116280
1 parent 05bd51b commit 1197de6

File tree

5 files changed

+220
-37
lines changed

5 files changed

+220
-37
lines changed

pkg/sentry/socket/netlink/netfilter/protocol.go

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,7 @@ func (p *Protocol) updateTable(nft *nftables.NFTables, tab *nftables.Table, attr
201201
// getTable returns a table for the given family.
202202
func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, msgFlags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
203203
if (msgFlags & linux.NLM_F_DUMP) != 0 {
204-
// TODO: b/434242152 - Support dump requests for tables.
205-
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table dump is not currently supported"))
204+
return dumpTables(nft, family, ms)
206205
}
207206

208207
// The table name is required.
@@ -218,18 +217,54 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
218217
return err
219218
}
220219

220+
return fillTableInfo(tab, ms)
221+
}
222+
223+
// dumpTablesForFamily populates the message set with information about all tables
224+
// for a specific address family.
225+
func dumpTablesForFamily(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
226+
for _, tab := range nft.GetAddressFamilyTables(family) {
227+
if err := fillTableInfo(tab, ms); err != nil {
228+
return err
229+
}
230+
}
231+
232+
return nil
233+
}
234+
235+
// dumpTables populates the message set with information about all tables for
236+
// all address families.
237+
func dumpTables(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
238+
// Dumps are multi-part messages.
239+
ms.Multi = true
240+
if family != stack.Unspec {
241+
return dumpTablesForFamily(nft, family, ms)
242+
}
243+
244+
for family := range stack.NumAFs {
245+
if err := dumpTablesForFamily(nft, family, ms); err != nil {
246+
return err
247+
}
248+
}
249+
250+
return nil
251+
}
252+
253+
// fillTableInfo populates the message set with information about a table.
254+
func fillTableInfo(tab *nftables.Table, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
221255
tabName := tab.GetName()
222256
userFlags, err := tab.GetLinuxUserFlagSet()
223257
if err != nil {
224258
return err
225259
}
260+
226261
// From net/netfilter/nf_tables_api.c:nf_tables_gettable
227262
m := ms.AddMessage(linux.NetlinkMessageHeader{
228263
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_NEWTABLE),
229264
})
230265

231266
m.Put(&linux.NetFilterGenMsg{
232-
Family: uint8(family),
267+
Family: uint8(nftables.AfProtocol(tab.GetAddressFamily())),
233268
Version: uint8(linux.NFNETLINK_V0),
234269
// Unused, set to 0.
235270
ResourceID: uint16(0),
@@ -548,8 +583,7 @@ func (p *Protocol) chainParseHook(chain *nftables.Chain, family stack.AddressFam
548583
// getChain fills the message set with information about a chain.
549584
func (p *Protocol) getChain(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, msgFlags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
550585
if (msgFlags & linux.NLM_F_DUMP) != 0 {
551-
// TODO: b/434243967 - Support dump requests for chains.
552-
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Chain dump is not currently supported"))
586+
return dumpChains(nft, family, ms)
553587
}
554588

555589
tabNameBytes, ok := attrs[linux.NFTA_CHAIN_TABLE]
@@ -577,22 +611,57 @@ func (p *Protocol) getChain(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
577611
return err
578612
}
579613

614+
return fillChainInfo(chain, ms)
615+
}
616+
617+
// dumpChainsForFamily populates the message set with information about all
618+
// chains for a specific address family.
619+
func dumpChainsForFamily(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
620+
for _, tab := range nft.GetAddressFamilyTables(family) {
621+
for _, chain := range tab.GetChains() {
622+
if err := fillChainInfo(chain, ms); err != nil {
623+
return err
624+
}
625+
}
626+
}
627+
return nil
628+
}
629+
630+
// dumpChains populates the message set with information chains. If no address
631+
// family is specified, all address families are dumped.
632+
func dumpChains(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
633+
ms.Multi = true
634+
if family != stack.Unspec {
635+
return dumpChainsForFamily(nft, family, ms)
636+
}
637+
638+
for family := range stack.NumAFs {
639+
if err := dumpChainsForFamily(nft, family, ms); err != nil {
640+
return err
641+
}
642+
}
643+
644+
return nil
645+
}
646+
647+
// fillChainInfo populates the message set with information about a chain.
648+
func fillChainInfo(chain *nftables.Chain, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
580649
m := ms.AddMessage(linux.NetlinkMessageHeader{
581650
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_NEWCHAIN),
582651
})
583652

584653
m.Put(&linux.NetFilterGenMsg{
585-
Family: uint8(family),
654+
Family: uint8(nftables.AfProtocol(chain.GetAddressFamily())),
586655
Version: uint8(linux.NFNETLINK_V0),
587656
// Unused, set to 0.
588657
ResourceID: uint16(0),
589658
})
590-
m.PutAttrString(linux.NFTA_CHAIN_TABLE, tabName)
591-
m.PutAttrString(linux.NFTA_CHAIN_NAME, chainName)
659+
m.PutAttrString(linux.NFTA_CHAIN_TABLE, chain.GetTable().GetName())
660+
m.PutAttrString(linux.NFTA_CHAIN_NAME, chain.GetName())
592661
m.PutAttr(linux.NFTA_CHAIN_HANDLE, nlmsg.PutU64(chain.GetHandle()))
593662

594663
if chain.IsBaseChain() {
595-
err := getBaseChainHookInfo(chain, family, m)
664+
err := getBaseChainHookInfo(chain, m)
596665
if err != nil {
597666
return err
598667
}
@@ -884,14 +953,14 @@ func nlaType(hdr linux.NetlinkAttrHeader) uint16 {
884953

885954
// getBaseChainHookInfo creates a NFTA_CHAIN_HOOK attribute with all the
886955
// corresponding nested attributes.
887-
func getBaseChainHookInfo(chain *nftables.Chain, family stack.AddressFamily, m *nlmsg.Message) *syserr.AnnotatedError {
956+
func getBaseChainHookInfo(chain *nftables.Chain, m *nlmsg.Message) *syserr.AnnotatedError {
888957
baseChainInfo := chain.GetBaseChainInfo()
889958
var nestedAttrs nlmsg.NestedAttr
890959

891960
nestedAttrs.PutAttr(linux.NFTA_HOOK_HOOKNUM, nlmsg.PutU32(baseChainInfo.LinuxHookNum))
892961
nestedAttrs.PutAttr(linux.NFTA_HOOK_PRIORITY, nlmsg.PutU32(uint32(baseChainInfo.Priority.GetValue())))
893962

894-
if isNetDevHook(family, baseChainInfo.LinuxHookNum) {
963+
if isNetDevHook(chain.GetAddressFamily(), baseChainInfo.LinuxHookNum) {
895964
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Netdev basechains or basechains attached to Ingress or Egress are not currently supported for getting"))
896965
}
897966

pkg/tcpip/nftables/nftables.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,17 @@ func (nf *NFTables) FlushAddressFamily(family stack.AddressFamily) *syserr.Annot
302302
return nil
303303
}
304304

305+
// GetAddressFamilyTables returns the tables for the given address family.
306+
func (nf *NFTables) GetAddressFamilyTables(family stack.AddressFamily) map[string]*Table {
307+
afFilter := nf.filters[family]
308+
if afFilter == nil {
309+
// An empty map is safe to iterate over.
310+
return nil
311+
}
312+
313+
return afFilter.tables
314+
}
315+
305316
// GetTable validates the inputs and gets a table if it exists, error otherwise.
306317
func (nf *NFTables) GetTable(family stack.AddressFamily, tableName string, portID uint32) (*Table, *syserr.AnnotatedError) {
307318
// Ensures address family is valid.
@@ -649,6 +660,11 @@ func (t *Table) GetChainByHandle(chainHandle uint64) (*Chain, *syserr.AnnotatedE
649660
return c, nil
650661
}
651662

663+
// GetChains returns a map of all chains for the table.
664+
func (t *Table) GetChains() map[string]*Chain {
665+
return t.chains
666+
}
667+
652668
// AddChain makes a new chain for the table. Can return an error if a chain by
653669
// the same name already exists if errorOnDuplicate is true.
654670
func (t *Table) AddChain(name string, info *BaseChainInfo, comment string, errorOnDuplicate bool) (*Chain, *syserr.AnnotatedError) {

pkg/tcpip/nftables/nftables_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,12 +2846,13 @@ func TestEvaluateMetaLoad(t *testing.T) {
28462846
op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_EQ,
28472847
append(numToBE(int(header.IPv6ProtocolNumber), 2), 0, 0)),
28482848
},
2849-
{ // cmd: add rule ip6 tab ch meta nfproto 0x0a
2849+
{ // meta nfproto is only useful in the inet family
2850+
// cmd: add rule inet tab ch meta nfproto 0x0a
28502851
tname: "meta load nfproto test",
28512852
pkt: pkt,
28522853
op1: mustCreateMetaLoad(t, linux.NFT_META_NFPROTO, linux.NFT_REG_4),
28532854
op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ,
2854-
[]byte{AfProtocol(stack.IP6), 0, 0, 0}),
2855+
[]byte{AfProtocol(stack.Inet), 0, 0, 0}),
28552856
},
28562857
{ // cmd: add rule ip6 tab ch meta l4proto 0x6
28572858
tname: "meta load l4proto test",

pkg/tcpip/nftables/nftables_types.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ const (
8787

8888
// addressFamilyProtocols maps address families to their protocol number.
8989
var addressFamilyProtocols = map[stack.AddressFamily]uint8{
90-
stack.IP: linux.NFPROTO_INET,
90+
stack.IP: linux.NFPROTO_IPV4,
9191
stack.IP6: linux.NFPROTO_IPV6,
92-
stack.Inet: linux.NFPROTO_IPV6,
92+
stack.Inet: linux.NFPROTO_INET,
9393
stack.Arp: linux.NFPROTO_ARP,
9494
stack.Bridge: linux.NFPROTO_BRIDGE,
9595
stack.Netdev: linux.NFPROTO_NETDEV,
@@ -930,17 +930,17 @@ func VerdictCodeToString(v uint32) string {
930930
return fmt.Sprintf("invalid verdict: %d", v)
931931
}
932932

933-
// netlinkAFToStackAF maps address families from linux/socket.h to their corresponding
934-
// netfilter address families.
933+
// netlinkAFToStackAF maps address families from linux/netfilter.h to their
934+
// corresponding netfilter address families.
935935
// From linux/include/uapi/linux/netfilter.h
936936
var netlinkAFToStackAF = map[uint8]stack.AddressFamily{
937-
linux.AF_UNSPEC: stack.Unspec,
938-
linux.AF_UNIX: stack.Inet,
939-
linux.AF_INET: stack.IP,
940-
linux.AF_AX25: stack.Arp,
941-
linux.AF_APPLETALK: stack.Netdev,
942-
linux.AF_BRIDGE: stack.Bridge,
943-
linux.AF_INET6: stack.IP6,
937+
linux.NFPROTO_UNSPEC: stack.Unspec,
938+
linux.NFPROTO_INET: stack.Inet,
939+
linux.NFPROTO_IPV4: stack.IP,
940+
linux.NFPROTO_ARP: stack.Arp,
941+
linux.NFPROTO_NETDEV: stack.Netdev,
942+
linux.NFPROTO_BRIDGE: stack.Bridge,
943+
linux.NFPROTO_IPV6: stack.IP6,
944944
}
945945

946946
// AFtoNetlinkAF converts a generic address family to a netfilter address family.

0 commit comments

Comments
 (0)