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