Skip to content

Commit a6229c3

Browse files
kerumetogvisor-bot
authored andcommitted
Implement GETRULE functionality for Nftables NETFILTER sockets.
Implements GETRULE functionality for getting information about rules, including dump messages for rules. The nft cli requires this when adding rules. Updates #11778 PiperOrigin-RevId: 800154153
1 parent c647536 commit a6229c3

File tree

5 files changed

+635
-43
lines changed

5 files changed

+635
-43
lines changed

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

Lines changed: 155 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ func dumpTablesForFamily(nft *nftables.NFTables, family stack.AddressFamily, ms
233233
}
234234

235235
// dumpTables populates the message set with information about all tables for
236-
// all address families.
236+
// a given address family or all address families if the family is unspecified.
237237
func dumpTables(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
238238
// Dumps are multi-part messages.
239239
ms.Multi = true
@@ -614,6 +614,23 @@ func (p *Protocol) getChain(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
614614
return fillChainInfo(chain, ms)
615615
}
616616

617+
// getBaseChainHookInfo creates a NFTA_CHAIN_HOOK attribute with all the
618+
// corresponding nested attributes.
619+
func getBaseChainHookInfo(chain *nftables.Chain, m *nlmsg.Message) *syserr.AnnotatedError {
620+
baseChainInfo := chain.GetBaseChainInfo()
621+
var nestedAttrs nlmsg.NestedAttr
622+
623+
nestedAttrs.PutAttr(linux.NFTA_HOOK_HOOKNUM, nlmsg.PutU32(baseChainInfo.LinuxHookNum))
624+
nestedAttrs.PutAttr(linux.NFTA_HOOK_PRIORITY, nlmsg.PutU32(uint32(baseChainInfo.Priority.GetValue())))
625+
626+
if isNetDevHook(chain.GetAddressFamily(), baseChainInfo.LinuxHookNum) {
627+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Netdev basechains or basechains attached to Ingress or Egress are not currently supported for getting"))
628+
}
629+
630+
m.PutNestedAttr(linux.NFTA_CHAIN_HOOK, nestedAttrs)
631+
return nil
632+
}
633+
617634
// dumpChainsForFamily populates the message set with information about all
618635
// chains for a specific address family.
619636
func dumpChainsForFamily(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
@@ -627,8 +644,8 @@ func dumpChainsForFamily(nft *nftables.NFTables, family stack.AddressFamily, ms
627644
return nil
628645
}
629646

630-
// dumpChains populates the message set with information chains. If no address
631-
// family is specified, all address families are dumped.
647+
// dumpChains populates the message set with information about all chains for
648+
// a given address family or all address families if the family is unspecified.
632649
func dumpChains(nft *nftables.NFTables, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
633650
ms.Multi = true
634651
if family != stack.Unspec {
@@ -951,20 +968,137 @@ func nlaType(hdr linux.NetlinkAttrHeader) uint16 {
951968
return hdr.Type & linux.NLA_TYPE_MASK
952969
}
953970

954-
// getBaseChainHookInfo creates a NFTA_CHAIN_HOOK attribute with all the
955-
// corresponding nested attributes.
956-
func getBaseChainHookInfo(chain *nftables.Chain, m *nlmsg.Message) *syserr.AnnotatedError {
957-
baseChainInfo := chain.GetBaseChainInfo()
958-
var nestedAttrs nlmsg.NestedAttr
971+
// getRule returns the rule for the given family and message flags.
972+
func (p *Protocol) getRule(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, msgFlags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
973+
if (msgFlags & linux.NLM_F_DUMP) != 0 {
974+
return dumpRules(nft, attrs, family, ms)
975+
}
959976

960-
nestedAttrs.PutAttr(linux.NFTA_HOOK_HOOKNUM, nlmsg.PutU32(baseChainInfo.LinuxHookNum))
961-
nestedAttrs.PutAttr(linux.NFTA_HOOK_PRIORITY, nlmsg.PutU32(uint32(baseChainInfo.Priority.GetValue())))
977+
tabName, ok := attrs[linux.NFTA_RULE_TABLE]
978+
if !ok {
979+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: NFTA_TABLE attribute is malformed or not found")
980+
}
962981

963-
if isNetDevHook(chain.GetAddressFamily(), baseChainInfo.LinuxHookNum) {
964-
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Netdev basechains or basechains attached to Ingress or Egress are not currently supported for getting"))
982+
// Any process can get any table.
983+
tab, err := nft.GetTable(family, tabName.String(), 0)
984+
if err != nil {
985+
return err
965986
}
966987

967-
m.PutNestedAttr(linux.NFTA_CHAIN_HOOK, nestedAttrs)
988+
chainName, ok := attrs[linux.NFTA_RULE_CHAIN]
989+
if !ok {
990+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: NFTA_CHAIN_NAME attribute is malformed or not found")
991+
}
992+
993+
chain, err := tab.GetChain(chainName.String())
994+
if err != nil {
995+
return err
996+
}
997+
998+
ruleHandleBytes, ok := attrs[linux.NFTA_RULE_HANDLE]
999+
if !ok {
1000+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: NFTA_RULE_HANDLE attribute is malformed or not found")
1001+
}
1002+
1003+
ruleHandle, ok := ruleHandleBytes.Uint64()
1004+
if !ok {
1005+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Rule handle attribute is malformed or not found")
1006+
}
1007+
1008+
ruleHandle = nlmsg.NetToHostU64(ruleHandle)
1009+
rule, err := chain.GetRuleByHandle(ruleHandle)
1010+
if err != nil {
1011+
return err
1012+
}
1013+
1014+
return fillRuleInfo(rule, ms)
1015+
}
1016+
1017+
// dumpRulesForFamily dumps all rules for a given family.
1018+
func dumpRulesForFamily(nft *nftables.NFTables, family stack.AddressFamily, tabName *string, chainName *string, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
1019+
// Linux allows rules to be retrieved for specific tables and chains via
1020+
// attributes, unlike dump operations for tables and chains.
1021+
// From linux/net/netfilter/nf_tables_api.c: nf_tables_dump_rules
1022+
for _, tab := range nft.GetAddressFamilyTables(family) {
1023+
if tabName != nil && tab.GetName() != *tabName {
1024+
continue
1025+
}
1026+
1027+
for _, chain := range tab.GetChains() {
1028+
if chainName != nil && chain.GetName() != *chainName {
1029+
continue
1030+
}
1031+
1032+
for _, rule := range chain.GetRules() {
1033+
if err := fillRuleInfo(rule, ms); err != nil {
1034+
return err
1035+
}
1036+
}
1037+
}
1038+
}
1039+
return nil
1040+
}
1041+
1042+
// dumpRules dumps all rules for a given family or all families if the family
1043+
// is unspecified.
1044+
func dumpRules(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
1045+
ms.Multi = true
1046+
var tabName *string
1047+
var chainName *string
1048+
1049+
if tabNameBytes, ok := attrs[linux.NFTA_RULE_TABLE]; ok {
1050+
attrName := tabNameBytes.String()
1051+
tabName = &attrName
1052+
}
1053+
1054+
if chainNameBytes, ok := attrs[linux.NFTA_RULE_CHAIN]; ok {
1055+
attrName := chainNameBytes.String()
1056+
chainName = &attrName
1057+
}
1058+
if family != stack.Unspec {
1059+
return dumpRulesForFamily(nft, family, tabName, chainName, ms)
1060+
}
1061+
1062+
for family := range stack.NumAFs {
1063+
if err := dumpRulesForFamily(nft, family, tabName, chainName, ms); err != nil {
1064+
return err
1065+
}
1066+
}
1067+
return nil
1068+
}
1069+
1070+
// fillRuleInfo adds the rule information to the message set.
1071+
func fillRuleInfo(rule *nftables.Rule, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
1072+
chain := rule.GetChain()
1073+
m := ms.AddMessage(linux.NetlinkMessageHeader{
1074+
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_NEWCHAIN),
1075+
})
1076+
1077+
m.Put(&linux.NetFilterGenMsg{
1078+
Family: uint8(nftables.AfProtocol(rule.GetAddressFamily())),
1079+
Version: uint8(linux.NFNETLINK_V0),
1080+
// Unused, set to 0.
1081+
ResourceID: uint16(0),
1082+
})
1083+
m.PutAttrString(linux.NFTA_RULE_TABLE, chain.GetTable().GetName())
1084+
m.PutAttrString(linux.NFTA_RULE_CHAIN, chain.GetName())
1085+
m.PutAttr(linux.NFTA_RULE_HANDLE, nlmsg.PutU64(rule.GetHandle()))
1086+
1087+
if (chain.GetFlags() & linux.NFT_CHAIN_HW_OFFLOAD) != 0 {
1088+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Nftables: Hardware offload chains are not supported")
1089+
}
1090+
1091+
// TODO(b/434244017): Add support for dumping expressions. This means
1092+
// expanding the nftables operation interface to include dump operations.
1093+
var exprsData []byte
1094+
// The NLA_F_NESTED flag is explicitly not set here, for backwards
1095+
// compatibility with older kernels.
1096+
// From linux/net/netfilter/nf_tables_api.c: nf_tables_fill_rule_info
1097+
m.PutAttr(linux.NFTA_RULE_EXPRESSIONS, primitive.AsByteSlice(exprsData))
1098+
1099+
if rule.HasUserData() {
1100+
m.PutAttr(linux.NFTA_RULE_USERDATA, primitive.AsByteSlice(rule.GetUserData()))
1101+
}
9681102
return nil
9691103
}
9701104

@@ -1029,7 +1163,14 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
10291163
return err.GetError()
10301164
}
10311165
return nil
1032-
case linux.NFT_MSG_GETRULE, linux.NFT_MSG_GETRULE_RESET,
1166+
case linux.NFT_MSG_GETRULE:
1167+
if err := p.getRule(nft, attrs, family, hdr.Flags, ms); err != nil {
1168+
log.Debugf("Nftables get rule error: %s", err)
1169+
return err.GetError()
1170+
}
1171+
1172+
return nil
1173+
case linux.NFT_MSG_GETRULE_RESET,
10331174
linux.NFT_MSG_GETSET, linux.NFT_MSG_GETSETELEM,
10341175
linux.NFT_MSG_GETSETELEM_RESET, linux.NFT_MSG_GETGEN,
10351176
linux.NFT_MSG_GETOBJ, linux.NFT_MSG_GETOBJ_RESET,

pkg/tcpip/nftables/nftables.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,11 @@ func (c *Chain) SetComment(comment string) {
874874
c.comment = comment
875875
}
876876

877+
// GetRules returns the rules of the chain.
878+
func (c *Chain) GetRules() []*Rule {
879+
return c.rules
880+
}
881+
877882
// RegisterRule assigns the chain to the rule and adds the rule to the chain's
878883
// rule list at the given index.
879884
// Valid indices are -1 (append) and [0, len]. Errors on invalid index.
@@ -1103,6 +1108,11 @@ func (r *Rule) AddOpFromExprInfo(tab *Table, exprInfo ExprInfo) *syserr.Annotate
11031108
return r.addOperation(op)
11041109
}
11051110

1111+
// GetChain returns the chain that the rule is registered to.
1112+
func (r *Rule) GetChain() *Chain {
1113+
return r.chain
1114+
}
1115+
11061116
// GetHandle returns the handle of the rule.
11071117
func (r *Rule) GetHandle() uint64 {
11081118
return r.handle
@@ -1118,11 +1128,21 @@ func (r *Rule) SetUserData(data []byte) *syserr.AnnotatedError {
11181128
return nil
11191129
}
11201130

1131+
// HasUserData returns whether the rule has user data.
1132+
func (r *Rule) HasUserData() bool {
1133+
return r.udata != nil
1134+
}
1135+
11211136
// GetUserData returns the user data of the rule.
11221137
func (r *Rule) GetUserData() []byte {
11231138
return r.udata
11241139
}
11251140

1141+
// GetAddressFamily returns the address family of the rule.
1142+
func (r *Rule) GetAddressFamily() stack.AddressFamily {
1143+
return r.chain.GetAddressFamily()
1144+
}
1145+
11261146
//
11271147
// Private hookFunctionStack functions
11281148
//

0 commit comments

Comments
 (0)