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 26 static 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 38 static 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 55 struct nfnl_dump_hook_data { 56 char devname[IFNAMSIZ]; 57 unsigned long headv; 58 u8 hook; 59 }; 60 61 static 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 77 static 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 107 cancel_nest: 108 nla_nest_cancel(nlskb, nest); 109 return -EMSGSIZE; 110 } 111 112 static 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 151 cancel_nest: 152 nla_nest_cancel(nlskb, nest); 153 return -EMSGSIZE; 154 } 155 156 static 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; 235 nla_put_failure: 236 nlmsg_trim(nlskb, nlh); 237 return ret; 238 } 239 240 static const struct nf_hook_entries * 241 nfnl_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 302 static 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 336 done: 337 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb)); 338 rcu_read_unlock(); 339 cb->args[0] = i; 340 return nlskb->len; 341 } 342 343 static 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 387 static int nfnl_hook_dump_stop(struct netlink_callback *cb) 388 { 389 kfree(cb->data); 390 return 0; 391 } 392 393 static 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 415 static 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 424 static 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 431 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK); 432 433 static int __init nfnetlink_hook_init(void) 434 { 435 return nfnetlink_subsys_register(&nfhook_subsys); 436 } 437 438 static void __exit nfnetlink_hook_exit(void) 439 { 440 nfnetlink_subsys_unregister(&nfhook_subsys); 441 } 442 443 module_init(nfnetlink_hook_init); 444 module_exit(nfnetlink_hook_exit); 445 446 MODULE_LICENSE("GPL"); 447 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 448 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks"); 449