Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.15-rc4 391 lines 9.1 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2021 Red Hat GmbH 4 * 5 * Author: Florian Westphal <fw@strlen.de> 6 */ 7 8#include <linux/module.h> 9#include <linux/kernel.h> 10#include <linux/types.h> 11#include <linux/skbuff.h> 12#include <linux/errno.h> 13#include <linux/netlink.h> 14#include <linux/slab.h> 15 16#include <linux/netfilter.h> 17 18#include <linux/netfilter/nfnetlink.h> 19#include <linux/netfilter/nfnetlink_hook.h> 20 21#include <net/netfilter/nf_tables.h> 22#include <net/sock.h> 23 24static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = { 25 [NFNLA_HOOK_HOOKNUM] = { .type = NLA_U32 }, 26 [NFNLA_HOOK_PRIORITY] = { .type = NLA_U32 }, 27 [NFNLA_HOOK_DEV] = { .type = NLA_STRING, 28 .len = IFNAMSIZ - 1 }, 29 [NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING, 30 .len = KSYM_NAME_LEN, }, 31 [NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING, 32 .len = MODULE_NAME_LEN, }, 33 [NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, }, 34}; 35 36static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb, 37 const struct nlmsghdr *nlh, 38 struct netlink_dump_control *c) 39{ 40 int err; 41 42 if (!try_module_get(THIS_MODULE)) 43 return -EINVAL; 44 45 rcu_read_unlock(); 46 err = netlink_dump_start(nlsk, skb, nlh, c); 47 rcu_read_lock(); 48 module_put(THIS_MODULE); 49 50 return err; 51} 52 53struct nfnl_dump_hook_data { 54 char devname[IFNAMSIZ]; 55 unsigned long headv; 56 u8 hook; 57}; 58 59static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb, 60 const struct nfnl_dump_hook_data *ctx, 61 unsigned int seq, 62 const struct nf_hook_ops *ops) 63{ 64 struct net *net = sock_net(nlskb->sk); 65 struct nlattr *nest, *nest2; 66 struct nft_chain *chain; 67 int ret = 0; 68 69 if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES) 70 return 0; 71 72 chain = ops->priv; 73 if (WARN_ON_ONCE(!chain)) 74 return 0; 75 76 if (!nft_is_active(net, chain)) 77 return 0; 78 79 nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO); 80 if (!nest) 81 return -EMSGSIZE; 82 83 ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE, 84 htonl(NFNL_HOOK_TYPE_NFTABLES)); 85 if (ret) 86 goto cancel_nest; 87 88 nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC); 89 if (!nest2) 90 goto cancel_nest; 91 92 ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name); 93 if (ret) 94 goto cancel_nest; 95 96 ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name); 97 if (ret) 98 goto cancel_nest; 99 100 ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family); 101 if (ret) 102 goto cancel_nest; 103 104 nla_nest_end(nlskb, nest2); 105 nla_nest_end(nlskb, nest); 106 return ret; 107 108cancel_nest: 109 nla_nest_cancel(nlskb, nest); 110 return -EMSGSIZE; 111} 112 113static int nfnl_hook_dump_one(struct sk_buff *nlskb, 114 const struct nfnl_dump_hook_data *ctx, 115 const struct nf_hook_ops *ops, 116 int family, unsigned int seq) 117{ 118 u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET); 119 unsigned int portid = NETLINK_CB(nlskb).portid; 120 struct nlmsghdr *nlh; 121 int ret = -EMSGSIZE; 122 u32 hooknum; 123#ifdef CONFIG_KALLSYMS 124 char sym[KSYM_SYMBOL_LEN]; 125 char *module_name; 126#endif 127 nlh = nfnl_msg_put(nlskb, portid, seq, event, 128 NLM_F_MULTI, family, NFNETLINK_V0, 0); 129 if (!nlh) 130 goto nla_put_failure; 131 132#ifdef CONFIG_KALLSYMS 133 ret = snprintf(sym, sizeof(sym), "%ps", ops->hook); 134 if (ret >= sizeof(sym)) { 135 ret = -EINVAL; 136 goto nla_put_failure; 137 } 138 139 module_name = strstr(sym, " ["); 140 if (module_name) { 141 char *end; 142 143 *module_name = '\0'; 144 module_name += 2; 145 end = strchr(module_name, ']'); 146 if (end) { 147 *end = 0; 148 149 ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name); 150 if (ret) 151 goto nla_put_failure; 152 } 153 } 154 155 ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym); 156 if (ret) 157 goto nla_put_failure; 158#endif 159 160 if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS) 161 hooknum = NF_NETDEV_INGRESS; 162 else 163 hooknum = ops->hooknum; 164 165 ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum)); 166 if (ret) 167 goto nla_put_failure; 168 169 ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority)); 170 if (ret) 171 goto nla_put_failure; 172 173 ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops); 174 if (ret) 175 goto nla_put_failure; 176 177 nlmsg_end(nlskb, nlh); 178 return 0; 179nla_put_failure: 180 nlmsg_trim(nlskb, nlh); 181 return ret; 182} 183 184static const struct nf_hook_entries * 185nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev) 186{ 187 const struct nf_hook_entries *hook_head = NULL; 188#ifdef CONFIG_NETFILTER_INGRESS 189 struct net_device *netdev; 190#endif 191 192 switch (pf) { 193 case NFPROTO_IPV4: 194 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4)) 195 return ERR_PTR(-EINVAL); 196 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]); 197 break; 198 case NFPROTO_IPV6: 199 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6)) 200 return ERR_PTR(-EINVAL); 201 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]); 202 break; 203 case NFPROTO_ARP: 204#ifdef CONFIG_NETFILTER_FAMILY_ARP 205 if (hook >= ARRAY_SIZE(net->nf.hooks_arp)) 206 return ERR_PTR(-EINVAL); 207 hook_head = rcu_dereference(net->nf.hooks_arp[hook]); 208#endif 209 break; 210 case NFPROTO_BRIDGE: 211#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE 212 if (hook >= ARRAY_SIZE(net->nf.hooks_bridge)) 213 return ERR_PTR(-EINVAL); 214 hook_head = rcu_dereference(net->nf.hooks_bridge[hook]); 215#endif 216 break; 217#if IS_ENABLED(CONFIG_DECNET) 218 case NFPROTO_DECNET: 219 if (hook >= ARRAY_SIZE(net->nf.hooks_decnet)) 220 return ERR_PTR(-EINVAL); 221 hook_head = rcu_dereference(net->nf.hooks_decnet[hook]); 222 break; 223#endif 224#ifdef CONFIG_NETFILTER_INGRESS 225 case NFPROTO_NETDEV: 226 if (hook != NF_NETDEV_INGRESS) 227 return ERR_PTR(-EOPNOTSUPP); 228 229 if (!dev) 230 return ERR_PTR(-ENODEV); 231 232 netdev = dev_get_by_name_rcu(net, dev); 233 if (!netdev) 234 return ERR_PTR(-ENODEV); 235 236 return rcu_dereference(netdev->nf_hooks_ingress); 237#endif 238 default: 239 return ERR_PTR(-EPROTONOSUPPORT); 240 } 241 242 return hook_head; 243} 244 245static int nfnl_hook_dump(struct sk_buff *nlskb, 246 struct netlink_callback *cb) 247{ 248 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); 249 struct nfnl_dump_hook_data *ctx = cb->data; 250 int err, family = nfmsg->nfgen_family; 251 struct net *net = sock_net(nlskb->sk); 252 struct nf_hook_ops * const *ops; 253 const struct nf_hook_entries *e; 254 unsigned int i = cb->args[0]; 255 256 rcu_read_lock(); 257 258 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname); 259 if (!e) 260 goto done; 261 262 if (IS_ERR(e)) { 263 cb->seq++; 264 goto done; 265 } 266 267 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries) 268 cb->seq++; 269 270 ops = nf_hook_entries_get_hook_ops(e); 271 272 for (; i < e->num_hook_entries; i++) { 273 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family, 274 cb->nlh->nlmsg_seq); 275 if (err) 276 break; 277 } 278 279done: 280 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb)); 281 rcu_read_unlock(); 282 cb->args[0] = i; 283 return nlskb->len; 284} 285 286static int nfnl_hook_dump_start(struct netlink_callback *cb) 287{ 288 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); 289 const struct nlattr * const *nla = cb->data; 290 struct nfnl_dump_hook_data *ctx = NULL; 291 struct net *net = sock_net(cb->skb->sk); 292 u8 family = nfmsg->nfgen_family; 293 char name[IFNAMSIZ] = ""; 294 const void *head; 295 u32 hooknum; 296 297 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM])); 298 if (hooknum > 255) 299 return -EINVAL; 300 301 if (family == NFPROTO_NETDEV) { 302 if (!nla[NFNLA_HOOK_DEV]) 303 return -EINVAL; 304 305 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name)); 306 } 307 308 rcu_read_lock(); 309 /* Not dereferenced; for consistency check only */ 310 head = nfnl_hook_entries_head(family, hooknum, net, name); 311 rcu_read_unlock(); 312 313 if (head && IS_ERR(head)) 314 return PTR_ERR(head); 315 316 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 317 if (!ctx) 318 return -ENOMEM; 319 320 strscpy(ctx->devname, name, sizeof(ctx->devname)); 321 ctx->headv = (unsigned long)head; 322 ctx->hook = hooknum; 323 324 cb->seq = 1; 325 cb->data = ctx; 326 327 return 0; 328} 329 330static int nfnl_hook_dump_stop(struct netlink_callback *cb) 331{ 332 kfree(cb->data); 333 return 0; 334} 335 336static int nfnl_hook_get(struct sk_buff *skb, 337 const struct nfnl_info *info, 338 const struct nlattr * const nla[]) 339{ 340 if (!nla[NFNLA_HOOK_HOOKNUM]) 341 return -EINVAL; 342 343 if (info->nlh->nlmsg_flags & NLM_F_DUMP) { 344 struct netlink_dump_control c = { 345 .start = nfnl_hook_dump_start, 346 .done = nfnl_hook_dump_stop, 347 .dump = nfnl_hook_dump, 348 .module = THIS_MODULE, 349 .data = (void *)nla, 350 }; 351 352 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); 353 } 354 355 return -EOPNOTSUPP; 356} 357 358static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = { 359 [NFNL_MSG_HOOK_GET] = { 360 .call = nfnl_hook_get, 361 .type = NFNL_CB_RCU, 362 .attr_count = NFNLA_HOOK_MAX, 363 .policy = nfnl_hook_nla_policy 364 }, 365}; 366 367static const struct nfnetlink_subsystem nfhook_subsys = { 368 .name = "nfhook", 369 .subsys_id = NFNL_SUBSYS_HOOK, 370 .cb_count = NFNL_MSG_HOOK_MAX, 371 .cb = nfnl_hook_cb, 372}; 373 374MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK); 375 376static int __init nfnetlink_hook_init(void) 377{ 378 return nfnetlink_subsys_register(&nfhook_subsys); 379} 380 381static void __exit nfnetlink_hook_exit(void) 382{ 383 nfnetlink_subsys_unregister(&nfhook_subsys); 384} 385 386module_init(nfnetlink_hook_init); 387module_exit(nfnetlink_hook_exit); 388 389MODULE_LICENSE("GPL"); 390MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 391MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");