Skip to content

Commit 096b420

Browse files
kerumetogvisor-bot
authored andcommitted
Implement GETGEN and a skeleton for GETSET.
This change implements functionality to retrieve the nftables generation id, as well as a skeleton for getting element sets. The nft CLI requires these to be present when adding new rules, but their functionality does not directly impact packet filtering functionality. Updates #11778 PiperOrigin-RevId: 800582677
1 parent 1826627 commit 096b420

File tree

6 files changed

+80
-5
lines changed

6 files changed

+80
-5
lines changed

pkg/abi/linux/nf_tables.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,14 @@ const (
462462
NFT_META_SDIFNAME // Slave device interface name
463463
NFT_META_BRI_BROUTE // Packet br_netfilter_broute bit
464464
)
465+
466+
// Nftables Generation Attributes
467+
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
468+
const (
469+
NFTA_GEN_UNSPEC uint16 = iota
470+
NFTA_GEN_ID
471+
NFTA_GEN_PROC_PID
472+
NFTA_GEN_PROC_NAME
473+
__NFTA_GEN_MAX
474+
NFTA_GEN_MAX = __NFTA_GEN_MAX - 1
475+
)

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

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,25 @@ func fillRuleInfo(rule *nftables.Rule, ms *nlmsg.MessageSet) *syserr.AnnotatedEr
11021102
return nil
11031103
}
11041104

1105+
// getGen returns the generation info for the current nftables instance.
1106+
func (p *Protocol) getGen(nft *nftables.NFTables, task *kernel.Task, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, msgFlags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
1107+
m := ms.AddMessage(linux.NetlinkMessageHeader{
1108+
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_NEWGEN),
1109+
})
1110+
m.Put(&linux.NetFilterGenMsg{
1111+
Family: uint8(nftables.AfProtocol(stack.Unspec)),
1112+
Version: uint8(linux.NFNETLINK_V0),
1113+
// Unused, set to 0.
1114+
ResourceID: uint16(0),
1115+
})
1116+
1117+
m.PutAttr(linux.NFTA_GEN_ID, nlmsg.PutU32(nft.GetGenID()))
1118+
m.PutAttr(linux.NFTA_GEN_PROC_PID, nlmsg.PutU32(uint32(task.ThreadGroup().ID())))
1119+
// TODO - b/434244017: Add support for dumping the process name.
1120+
m.PutAttrString(linux.NFTA_GEN_PROC_NAME, "placeholder")
1121+
return nil
1122+
}
1123+
11051124
// isNetDevHook returns whether the given family and hook number represent a
11061125
// netdev hook, or if the family is inet and is attempting to attach to
11071126
// Ingress or Egress hooks.
@@ -1170,9 +1189,20 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
11701189
}
11711190

11721191
return nil
1173-
case linux.NFT_MSG_GETRULE_RESET,
1174-
linux.NFT_MSG_GETSET, linux.NFT_MSG_GETSETELEM,
1175-
linux.NFT_MSG_GETSETELEM_RESET, linux.NFT_MSG_GETGEN,
1192+
case linux.NFT_MSG_GETGEN:
1193+
if err := p.getGen(nft, kernel.TaskFromContext(ctx), attrs, family, hdr.Flags, ms); err != nil {
1194+
log.Debugf("Nftables get gen error: %s", err)
1195+
return err.GetError()
1196+
}
1197+
return nil
1198+
case linux.NFT_MSG_GETSET:
1199+
// TODO - b/421437663: Implement sets for nftables. This skeleton is
1200+
// left here to satisfy auxiliary calls from the nft CLI not needed
1201+
// for packet filtering functionality.
1202+
ms.Multi = true
1203+
return nil
1204+
case linux.NFT_MSG_GETRULE_RESET, linux.NFT_MSG_GETSETELEM,
1205+
linux.NFT_MSG_GETSETELEM_RESET,
11761206
linux.NFT_MSG_GETOBJ, linux.NFT_MSG_GETOBJ_RESET,
11771207
linux.NFT_MSG_GETFLOWTABLE:
11781208

pkg/tcpip/nftables/nftables.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,12 @@ func NewNFTables(clock tcpip.Clock, rng rand.RNG) *NFTables {
243243
if rng.Reader == nil {
244244
panic("nftables state must be initialized with a non-nil random number generator")
245245
}
246-
return &NFTables{clock: clock, startTime: clock.Now(), rng: rng, tableHandleCounter: atomicbitops.Uint64{}}
246+
return &NFTables{clock: clock, startTime: clock.Now(), rng: rng, tableHandleCounter: atomicbitops.Uint64{}, genid: 1}
247+
}
248+
249+
// GetGenID returns the generation ID for the NFTables object.
250+
func (nf *NFTables) GetGenID() uint32 {
251+
return nf.genid
247252
}
248253

249254
// Flush clears entire ruleset and all data for all address families

pkg/tcpip/nftables/nftables_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const (
8787

8888
// addressFamilyProtocols maps address families to their protocol number.
8989
var addressFamilyProtocols = map[stack.AddressFamily]uint8{
90+
stack.Unspec: linux.NFPROTO_UNSPEC,
9091
stack.IP: linux.NFPROTO_IPV4,
9192
stack.IP6: linux.NFPROTO_IPV6,
9293
stack.Inet: linux.NFPROTO_INET,
@@ -243,6 +244,7 @@ type NFTables struct {
243244
rng rand.RNG // Random number generator.
244245
tableHandleCounter atomicbitops.Uint64 // Table handle counter.
245246
Mu nfTablesRWMutex // Mutex for tableHandles.
247+
genid uint32 // Generation ID for nftables.
246248
}
247249

248250
// Ensures NFTables implements the NFTablesInterface.
@@ -1260,4 +1262,5 @@ func (nf *NFTables) DeepCopy() *NFTables {
12601262
// with the tables of the passed in NFTables struct.
12611263
func (nf *NFTables) ReplaceNFTables(nftCopy *NFTables) {
12621264
nf.filters = nftCopy.filters
1265+
nf.genid++
12631266
}

test/syscalls/linux/socket_netlink_netfilter.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,31 @@ TEST_F(NetlinkNetfilterTest, GetRuleDumpTableChainSpecified) {
34013401
EXPECT_EQ(rules_found, 0);
34023402
}
34033403

3404+
TEST_F(NetlinkNetfilterTest, GetGenerationID) {
3405+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
3406+
SKIP_IF(!IsRunningOnGvisor());
3407+
FileDescriptor fd =
3408+
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_NETFILTER));
3409+
3410+
std::vector<char> get_gen_request =
3411+
NlReq("getgen req inet").Seq(kSeq).Build();
3412+
3413+
ASSERT_NO_ERRNO(NetlinkRequestResponse(
3414+
fd, get_gen_request.data(), get_gen_request.size(),
3415+
[&](const struct nlmsghdr* hdr) {
3416+
const struct nfattr* gen_id_attr =
3417+
FindNfAttr(hdr, nullptr, NFTA_GEN_ID);
3418+
EXPECT_NE(gen_id_attr, nullptr);
3419+
// Although the generation ID is initialized to 1, this number gets
3420+
// incremented on successful NETFILTER nftables batch requests.
3421+
// Thus, we simply check that is is greater than 1 here.
3422+
uint32_t gen_id =
3423+
ntohl(*(reinterpret_cast<uint32_t*>(NFA_DATA(gen_id_attr))));
3424+
EXPECT_GE(gen_id, 1);
3425+
},
3426+
false));
3427+
}
3428+
34043429
} // namespace
34053430

34063431
} // namespace testing

test/syscalls/linux/socket_netlink_netfilter_util.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,8 @@ bool NlReq::MsgTypeToken(const std::string& token) {
472472
{"deltable", NFT_MSG_DELTABLE}, {"destroytable", NFT_MSG_DESTROYTABLE},
473473
{"newchain", NFT_MSG_NEWCHAIN}, {"getchain", NFT_MSG_GETCHAIN},
474474
{"delchain", NFT_MSG_DELCHAIN}, {"destroychain", NFT_MSG_DESTROYCHAIN},
475-
{"newrule", NFT_MSG_NEWRULE}, {"getrule", NFT_MSG_GETRULE}};
475+
{"newrule", NFT_MSG_NEWRULE}, {"getrule", NFT_MSG_GETRULE},
476+
{"getgen", NFT_MSG_GETGEN}};
476477
auto it = token_to_msg_type.find(token);
477478
if (it != token_to_msg_type.end()) {
478479
EXPECT_FALSE(msg_type_set_) << "Message type already set: " << msg_type_;

0 commit comments

Comments
 (0)