Skip to content

Commit 7232b29

Browse files
kerumetogvisor-bot
authored andcommitted
Stub for netlink netfilter sockets.
This creates the basic file and structure needed to process ONE netlink message, though implementation details are omitted for now. Future changes will work with filling out the processing of different netlink_netfilter message types (i.e NFT_MSG_NEWTABLE), eventually processing batch messages (NFNL_MSG_BATCH_BEGIN and NFNL_MSG_BATCH_END). PiperOrigin-RevId: 768196902
1 parent 7187343 commit 7232b29

File tree

10 files changed

+354
-0
lines changed

10 files changed

+354
-0
lines changed

pkg/abi/linux/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ go_library(
5656
"netfilter_ipv4.go",
5757
"netfilter_ipv6.go",
5858
"netlink.go",
59+
"netlink_netfilter.go",
5960
"netlink_route.go",
6061
"nf_tables.go",
6162
"poll.go",

pkg/abi/linux/netlink_netfilter.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2025 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package linux
16+
17+
// Group describes Netlink Netfilter groups, from uapi/linux/netfilter/nfnetlink.h.
18+
// Users bind to specific groups to receive processing logs from those groups.
19+
type Group uint16
20+
21+
// Netlink Netfilter groups.
22+
const (
23+
NFNLGPR_NONE Group = iota
24+
NFNLGRP_CONNTRACK_NEW
25+
NFNLGRP_CONNTRACK_UPDATE
26+
NFNLGRP_CONNTRACK_DESTROY
27+
NFNLGRP_CONNTRACK_EXP_NEW
28+
NFNLGRP_CONNTRACK_EXP_UPDATE
29+
NFNLGRP_CONNTRACK_EXP_DESTROY
30+
NFNLGRP_NFTABLES
31+
NFNLGRP_ACCT_QUOTA
32+
NFNLGRP_NFTRACE
33+
__NFNLGRP_MAX
34+
NFNLGRP_MAX = __NFNLGRP_MAX - 1
35+
)
36+
37+
// NetFilterGenMsg describes the netlink netfilter genmsg message, from uapi/linux/netfilter/nfnetlink.h.
38+
type NetFilterGenMsg struct {
39+
Family uint8
40+
Version uint8
41+
ResourceID uint16
42+
}
43+
44+
// SizeOfNetfilterGenMsg is the size of the netlink netfilter genmsg message.
45+
const SizeOfNetfilterGenMsg = 4
46+
47+
// NFNETLINK_V0 is the default version of the netlink netfilter.
48+
const NFNETLINK_V0 = 0
49+
50+
// SubsysID describes Netlink Netfilter subsystem IDs, from uapi/linux/netfilter/nfnetlink.h.
51+
type SubsysID uint16
52+
53+
// Netlink Netfilter subsystem IDs.
54+
const (
55+
NFNL_SUBSYS_NONE SubsysID = iota
56+
NFNL_SUBSYS_CTNETLINK
57+
NFNL_SUBSYS_CTNETLINK_EXP
58+
NFNL_SUBSYS_QUEUE
59+
NFNL_SUBSYS_ULOG
60+
NFNL_SUBSYS_OSF
61+
NFNL_SUBSYS_IPSET
62+
NFNL_SUBSYS_ACCT
63+
NFNL_SUBSYS_CTNETLINK_TIMEOUT
64+
NFNL_SUBSYS_CTHELPER
65+
NFNL_SUBSYS_NFTABLES
66+
NFNL_SUBSYS_NFT_COMPAT
67+
NFNL_SUBSYS_HOOK
68+
)
69+
70+
// NetFilterSubsysID returns the Netfilter Subsystem ID from the netlink message header.
71+
func (hdr *NetlinkMessageHeader) NetFilterSubsysID() SubsysID {
72+
return SubsysID((hdr.Type & 0xff00) >> 8)
73+
}
74+
75+
// NetFilterMsgType returns the Netfilter Message Type from the netlink message header.
76+
func (hdr *NetlinkMessageHeader) NetFilterMsgType() NfTableMsgType {
77+
return NfTableMsgType(hdr.Type & 0x00ff)
78+
}
79+
80+
// Reserved control Netlink Netfilter messages, from uapi/linux/netfilter/nfnetlink.h.
81+
const (
82+
NFNL_MSG_BATCH_BEGIN = NLMSG_MIN_TYPE
83+
NFNL_MSG_BATCH_END = NLMSG_MIN_TYPE + 1
84+
)
85+
86+
// NetlinkBatchAttr describes Netlink Netfilter batch attributes, from uapi/linux/netfilter/nfnetlink.h.
87+
type NetlinkBatchAttr uint16
88+
89+
// Netlink Netfilter batch attributes.
90+
const (
91+
NFNL_BATCH_UNSPEC NetlinkBatchAttr = iota
92+
NFNL_BATCH_GENID
93+
__NFNL_BATCH_MAX
94+
NFNL_BATCH_MAX = __NFNL_BATCH_MAX - 1
95+
)

pkg/abi/linux/nf_tables.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,49 @@ const (
8181
NFT_RETURN int32 = -5
8282
)
8383

84+
// NfTableMsgType values map to operations within the nftables api.
85+
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
86+
type NfTableMsgType uint16
87+
88+
// Netlink Netfilter table message types.
89+
const (
90+
NFT_MSG_NEWTABLE NfTableMsgType = iota
91+
NFT_MSG_GETTABLE
92+
NFT_MSG_DELTABLE
93+
NFT_MSG_NEWCHAIN
94+
NFT_MSG_GETCHAIN
95+
NFT_MSG_DELCHAIN
96+
NFT_MSG_NEWRULE
97+
NFT_MSG_GETRULE
98+
NFT_MSG_DELRULE
99+
NFT_MSG_NEWSET
100+
NFT_MSG_GETSET
101+
NFT_MSG_DELSET
102+
NFT_MSG_NEWSETELEM
103+
NFT_MSG_GETSETELEM
104+
NFT_MSG_DELSETELEM
105+
NFT_MSG_NEWGEN
106+
NFT_MSG_GETGEN
107+
NFT_MSG_TRACE
108+
NFT_MSG_NEWOBJ
109+
NFT_MSG_GETOBJ
110+
NFT_MSG_DELOBJ
111+
NFT_MSG_GETOBJ_RESET
112+
NFT_MSG_NEWFLOWTABLE
113+
NFT_MSG_GETFLOWTABLE
114+
NFT_MSG_DELFLOWTABLE
115+
NFT_MSG_GETRULE_RESET
116+
NFT_MSG_DESTROYTABLE
117+
NFT_MSG_DESTROYCHAIN
118+
NFT_MSG_DESTROYRULE
119+
NFT_MSG_DESTROYSET
120+
NFT_MSG_DESTROYSETELEM
121+
NFT_MSG_DESTROYOBJ
122+
NFT_MSG_DESTROYFLOWTABLE
123+
NFT_MSG_GETSETELEM_RESET
124+
NFT_MSG_MAX
125+
)
126+
84127
// Nf table relational operators.
85128
// Used by the nft comparison operation to compare values in registers.
86129
// These correspond to enum values in include/uapi/linux/netfilter/nf_tables.h.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("//tools:defs.bzl", "go_library")
2+
3+
package(
4+
default_applicable_licenses = ["//:license"],
5+
licenses = ["notice"],
6+
)
7+
8+
go_library(
9+
name = "netfilter",
10+
srcs = ["protocol.go"],
11+
visibility = ["//pkg/sentry:internal"],
12+
deps = [
13+
"//pkg/abi/linux",
14+
"//pkg/context",
15+
"//pkg/sentry/kernel",
16+
"//pkg/sentry/socket/netlink",
17+
"//pkg/sentry/socket/netlink/nlmsg",
18+
"//pkg/syserr",
19+
],
20+
)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2025 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package netfilter provides a NETLINK_NETFILTER socket protocol.
16+
package netfilter
17+
18+
import (
19+
"gvisor.dev/gvisor/pkg/abi/linux"
20+
"gvisor.dev/gvisor/pkg/context"
21+
"gvisor.dev/gvisor/pkg/sentry/kernel"
22+
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
23+
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
24+
"gvisor.dev/gvisor/pkg/syserr"
25+
)
26+
27+
// Protocol implements netlink.Protocol.
28+
//
29+
// +stateify savable
30+
type Protocol struct{}
31+
32+
var _ netlink.Protocol = (*Protocol)(nil)
33+
34+
// NewProtocol creates a NETLINK_NETFILTER netlink.Protocol.
35+
func NewProtocol(t *kernel.Task) (netlink.Protocol, *syserr.Error) {
36+
return &Protocol{}, nil
37+
}
38+
39+
// Protocol implements netlink.Protocol.Protocol.
40+
func (p *Protocol) Protocol() int {
41+
return linux.NETLINK_NETFILTER
42+
}
43+
44+
// CanSend implements netlink.Protocol.CanSend.
45+
// Netfilter sockets should be able to send responses back, namely
46+
// if a user wants to see the current state of the netfilter tables.
47+
func (p *Protocol) CanSend() bool {
48+
return true
49+
}
50+
51+
// ProcessMessage implements netlink.Protocol.ProcessMessage.
52+
func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
53+
hdr := msg.Header()
54+
55+
// Netlink message payloads must be of at least the size of the genmsg. Return early if it is not,
56+
// from linux/net/netfilter/nfnetlink.c.
57+
if netLinkMessagePayloadSize(&hdr) < linux.SizeOfNetfilterGenMsg {
58+
return nil
59+
}
60+
61+
msgType := hdr.NetFilterMsgType()
62+
// TODO: b/421437663 - Match the message type and call the appropriate Nftables function.
63+
switch msgType {
64+
default:
65+
return syserr.ErrInvalidArgument
66+
}
67+
}
68+
69+
// init registers the NETLINK_NETFILTER provider.
70+
func init() {
71+
netlink.RegisterProvider(linux.NETLINK_NETFILTER, NewProtocol)
72+
}
73+
74+
func netLinkMessagePayloadSize(h *linux.NetlinkMessageHeader) int {
75+
return int(h.Length) - linux.NetlinkMessageHeaderSize
76+
}

runsc/boot/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ go_library(
9494
"//pkg/sentry/socket/hostinet",
9595
"//pkg/sentry/socket/netfilter",
9696
"//pkg/sentry/socket/netlink",
97+
"//pkg/sentry/socket/netlink/netfilter",
9798
"//pkg/sentry/socket/netlink/route",
9899
"//pkg/sentry/socket/netlink/uevent",
99100
"//pkg/sentry/socket/netstack",

runsc/boot/loader.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import (
9090

9191
// Include other supported socket providers.
9292
_ "gvisor.dev/gvisor/pkg/sentry/socket/netlink"
93+
_ "gvisor.dev/gvisor/pkg/sentry/socket/netlink/netfilter"
9394
_ "gvisor.dev/gvisor/pkg/sentry/socket/netlink/route"
9495
_ "gvisor.dev/gvisor/pkg/sentry/socket/netlink/uevent"
9596
_ "gvisor.dev/gvisor/pkg/sentry/socket/unix"

test/syscalls/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,11 @@ syscall_test(
937937
test = "//test/syscalls/linux:socket_netlink_route_test",
938938
)
939939

940+
syscall_test(
941+
add_hostinet = True,
942+
test = "//test/syscalls/linux:socket_netlink_netfilter_test",
943+
)
944+
940945
syscall_test(
941946
add_hostinet = True,
942947
test = "//test/syscalls/linux:socket_netlink_uevent_test",

test/syscalls/linux/BUILD

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3495,6 +3495,23 @@ cc_binary(
34953495
],
34963496
)
34973497

3498+
cc_binary(
3499+
name = "socket_netlink_netfilter_test",
3500+
testonly = 1,
3501+
srcs = ["socket_netlink_netfilter.cc"],
3502+
linkstatic = 1,
3503+
malloc = "//test/util:errno_safe_allocator",
3504+
deps = select_gtest() + [
3505+
":socket_netlink_util",
3506+
"//test/util:file_descriptor",
3507+
"//test/util:posix_error",
3508+
"//test/util:socket_util",
3509+
"//test/util:test_main",
3510+
"//test/util:test_util",
3511+
"@com_google_absl//absl/strings:str_format",
3512+
],
3513+
)
3514+
34983515
cc_binary(
34993516
name = "socket_netlink_uevent_test",
35003517
testonly = 1,
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2025 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <linux/netfilter/nfnetlink.h>
16+
#include <linux/netlink.h>
17+
#include <sys/socket.h>
18+
19+
#include <functional>
20+
#include <string>
21+
#include <tuple>
22+
23+
#include "gmock/gmock.h"
24+
#include "gtest/gtest.h"
25+
#include "absl/strings/str_format.h"
26+
#include "test/syscalls/linux/socket_netlink_util.h"
27+
#include "test/util/file_descriptor.h"
28+
#include "test/util/posix_error.h"
29+
#include "test/util/socket_util.h"
30+
#include "test/util/test_util.h"
31+
32+
// Tests for NETLINK_NETFILTER sockets.
33+
34+
namespace gvisor {
35+
namespace testing {
36+
37+
namespace {
38+
39+
using SockOptTest = ::testing::TestWithParam<
40+
std::tuple<int, std::function<bool(int)>, std::string>>;
41+
42+
TEST_P(SockOptTest, GetSockOpt) {
43+
int sockopt = std::get<0>(GetParam());
44+
auto verifier = std::get<1>(GetParam());
45+
std::string verifier_description = std::get<2>(GetParam());
46+
47+
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
48+
Socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER));
49+
50+
int res;
51+
socklen_t len = sizeof(res);
52+
53+
EXPECT_THAT(getsockopt(fd.get(), SOL_SOCKET, sockopt, &res, &len),
54+
SyscallSucceeds());
55+
56+
EXPECT_EQ(len, sizeof(res));
57+
EXPECT_TRUE(verifier(res)) << absl::StrFormat(
58+
"getsockopt(%d, SOL_SOCKET, %d, &res, &len) => res=%d was unexpected, "
59+
"expected %s",
60+
fd.get(), sockopt, res, verifier_description);
61+
}
62+
63+
std::function<bool(int)> IsPositive() {
64+
return [](int val) { return val > 0; };
65+
}
66+
67+
std::function<bool(int)> IsEqual(int target) {
68+
return [target](int val) { return val == target; };
69+
}
70+
71+
INSTANTIATE_TEST_SUITE_P(
72+
NetlinkNetfilterTest, SockOptTest,
73+
::testing::Values(
74+
std::make_tuple(SO_SNDBUF, IsPositive(), "positive send buffer size"),
75+
std::make_tuple(SO_RCVBUF, IsPositive(),
76+
"positive receive buffer size"),
77+
std::make_tuple(SO_TYPE, IsEqual(SOCK_RAW),
78+
absl::StrFormat("SOCK_RAW (%d)", SOCK_RAW)),
79+
std::make_tuple(SO_DOMAIN, IsEqual(AF_NETLINK),
80+
absl::StrFormat("AF_NETLINK (%d)", AF_NETLINK)),
81+
std::make_tuple(SO_PROTOCOL, IsEqual(NETLINK_NETFILTER),
82+
absl::StrFormat("NETLINK_NETFILTER (%d)",
83+
NETLINK_NETFILTER)),
84+
std::make_tuple(SO_PASSCRED, IsEqual(0), "0")));
85+
86+
// Netlink sockets must be SOCK_DGRAM or SOCK_RAW.
87+
TEST(NetlinkNetfilterTest, CanCreateSocket) {
88+
FileDescriptor fd =
89+
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_NETFILTER));
90+
EXPECT_THAT(fd.get(), SyscallSucceeds());
91+
}
92+
} // namespace
93+
94+
} // namespace testing
95+
} // namespace gvisor

0 commit comments

Comments
 (0)