Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nft_socket: add support for cgroupsv2

Allow to match on the cgroupsv2 id from ancestor level.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+51 -1
+4
include/uapi/linux/netfilter/nf_tables.h
··· 1014 1014 * 1015 1015 * @NFTA_SOCKET_KEY: socket key to match 1016 1016 * @NFTA_SOCKET_DREG: destination register 1017 + * @NFTA_SOCKET_LEVEL: cgroups2 ancestor level (only for cgroupsv2) 1017 1018 */ 1018 1019 enum nft_socket_attributes { 1019 1020 NFTA_SOCKET_UNSPEC, 1020 1021 NFTA_SOCKET_KEY, 1021 1022 NFTA_SOCKET_DREG, 1023 + NFTA_SOCKET_LEVEL, 1022 1024 __NFTA_SOCKET_MAX 1023 1025 }; 1024 1026 #define NFTA_SOCKET_MAX (__NFTA_SOCKET_MAX - 1) ··· 1031 1029 * @NFT_SOCKET_TRANSPARENT: Value of the IP(V6)_TRANSPARENT socket option 1032 1030 * @NFT_SOCKET_MARK: Value of the socket mark 1033 1031 * @NFT_SOCKET_WILDCARD: Whether the socket is zero-bound (e.g. 0.0.0.0 or ::0) 1032 + * @NFT_SOCKET_CGROUPV2: Match on cgroups version 2 1034 1033 */ 1035 1034 enum nft_socket_keys { 1036 1035 NFT_SOCKET_TRANSPARENT, 1037 1036 NFT_SOCKET_MARK, 1038 1037 NFT_SOCKET_WILDCARD, 1038 + NFT_SOCKET_CGROUPV2, 1039 1039 __NFT_SOCKET_MAX 1040 1040 }; 1041 1041 #define NFT_SOCKET_MAX (__NFT_SOCKET_MAX - 1)
+47 -1
net/netfilter/nft_socket.c
··· 9 9 10 10 struct nft_socket { 11 11 enum nft_socket_keys key:8; 12 + u8 level; 12 13 union { 13 14 u8 dreg; 14 15 }; ··· 33 32 return; 34 33 } 35 34 } 35 + 36 + #ifdef CONFIG_CGROUPS 37 + static noinline bool 38 + nft_sock_get_eval_cgroupv2(u32 *dest, const struct nft_pktinfo *pkt, u32 level) 39 + { 40 + struct sock *sk = skb_to_full_sk(pkt->skb); 41 + struct cgroup *cgrp; 42 + 43 + if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk))) 44 + return false; 45 + 46 + cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 47 + if (level > cgrp->level) 48 + return false; 49 + 50 + memcpy(dest, &cgrp->ancestor_ids[level], sizeof(u64)); 51 + 52 + return true; 53 + } 54 + #endif 36 55 37 56 static void nft_socket_eval(const struct nft_expr *expr, 38 57 struct nft_regs *regs, ··· 106 85 } 107 86 nft_socket_wildcard(pkt, regs, sk, dest); 108 87 break; 88 + #ifdef CONFIG_CGROUPS 89 + case NFT_SOCKET_CGROUPV2: 90 + if (!nft_sock_get_eval_cgroupv2(dest, pkt, priv->level)) { 91 + regs->verdict.code = NFT_BREAK; 92 + return; 93 + } 94 + break; 95 + #endif 109 96 default: 110 97 WARN_ON(1); 111 98 regs->verdict.code = NFT_BREAK; ··· 126 97 static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = { 127 98 [NFTA_SOCKET_KEY] = { .type = NLA_U32 }, 128 99 [NFTA_SOCKET_DREG] = { .type = NLA_U32 }, 100 + [NFTA_SOCKET_LEVEL] = { .type = NLA_U32 }, 129 101 }; 130 102 131 103 static int nft_socket_init(const struct nft_ctx *ctx, ··· 134 104 const struct nlattr * const tb[]) 135 105 { 136 106 struct nft_socket *priv = nft_expr_priv(expr); 137 - unsigned int len; 107 + unsigned int len, level; 138 108 139 109 if (!tb[NFTA_SOCKET_DREG] || !tb[NFTA_SOCKET_KEY]) 140 110 return -EINVAL; ··· 159 129 case NFT_SOCKET_MARK: 160 130 len = sizeof(u32); 161 131 break; 132 + #ifdef CONFIG_CGROUPS 133 + case NFT_SOCKET_CGROUPV2: 134 + if (!tb[NFTA_SOCKET_LEVEL]) 135 + return -EINVAL; 136 + 137 + level = ntohl(nla_get_u32(tb[NFTA_SOCKET_LEVEL])); 138 + if (level > 255) 139 + return -EOPNOTSUPP; 140 + 141 + priv->level = level; 142 + len = sizeof(u64); 143 + break; 144 + #endif 162 145 default: 163 146 return -EOPNOTSUPP; 164 147 } ··· 188 145 if (nla_put_u32(skb, NFTA_SOCKET_KEY, htonl(priv->key))) 189 146 return -1; 190 147 if (nft_dump_register(skb, NFTA_SOCKET_DREG, priv->dreg)) 148 + return -1; 149 + if (priv->key == NFT_SOCKET_CGROUPV2 && 150 + nla_put_u32(skb, NFTA_SOCKET_LEVEL, htonl(priv->level))) 191 151 return -1; 192 152 return 0; 193 153 }