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 24 static 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 36 static 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 53 struct nfnl_dump_hook_data { 54 char devname[IFNAMSIZ]; 55 unsigned long headv; 56 u8 hook; 57 }; 58 59 static 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 108 cancel_nest: 109 nla_nest_cancel(nlskb, nest); 110 return -EMSGSIZE; 111 } 112 113 static 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; 179 nla_put_failure: 180 nlmsg_trim(nlskb, nlh); 181 return ret; 182 } 183 184 static const struct nf_hook_entries * 185 nfnl_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 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS) 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 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS) 225 case NFPROTO_NETDEV: 226 if (hook >= NF_NETDEV_NUMHOOKS) 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 #ifdef CONFIG_NETFILTER_INGRESS 237 if (hook == NF_NETDEV_INGRESS) 238 return rcu_dereference(netdev->nf_hooks_ingress); 239 #endif 240 #ifdef CONFIG_NETFILTER_EGRESS 241 if (hook == NF_NETDEV_EGRESS) 242 return rcu_dereference(netdev->nf_hooks_egress); 243 #endif 244 fallthrough; 245 #endif 246 default: 247 return ERR_PTR(-EPROTONOSUPPORT); 248 } 249 250 return hook_head; 251 } 252 253 static int nfnl_hook_dump(struct sk_buff *nlskb, 254 struct netlink_callback *cb) 255 { 256 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); 257 struct nfnl_dump_hook_data *ctx = cb->data; 258 int err, family = nfmsg->nfgen_family; 259 struct net *net = sock_net(nlskb->sk); 260 struct nf_hook_ops * const *ops; 261 const struct nf_hook_entries *e; 262 unsigned int i = cb->args[0]; 263 264 rcu_read_lock(); 265 266 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname); 267 if (!e) 268 goto done; 269 270 if (IS_ERR(e)) { 271 cb->seq++; 272 goto done; 273 } 274 275 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries) 276 cb->seq++; 277 278 ops = nf_hook_entries_get_hook_ops(e); 279 280 for (; i < e->num_hook_entries; i++) { 281 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family, 282 cb->nlh->nlmsg_seq); 283 if (err) 284 break; 285 } 286 287 done: 288 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb)); 289 rcu_read_unlock(); 290 cb->args[0] = i; 291 return nlskb->len; 292 } 293 294 static int nfnl_hook_dump_start(struct netlink_callback *cb) 295 { 296 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); 297 const struct nlattr * const *nla = cb->data; 298 struct nfnl_dump_hook_data *ctx = NULL; 299 struct net *net = sock_net(cb->skb->sk); 300 u8 family = nfmsg->nfgen_family; 301 char name[IFNAMSIZ] = ""; 302 const void *head; 303 u32 hooknum; 304 305 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM])); 306 if (hooknum > 255) 307 return -EINVAL; 308 309 if (family == NFPROTO_NETDEV) { 310 if (!nla[NFNLA_HOOK_DEV]) 311 return -EINVAL; 312 313 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name)); 314 } 315 316 rcu_read_lock(); 317 /* Not dereferenced; for consistency check only */ 318 head = nfnl_hook_entries_head(family, hooknum, net, name); 319 rcu_read_unlock(); 320 321 if (head && IS_ERR(head)) 322 return PTR_ERR(head); 323 324 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 325 if (!ctx) 326 return -ENOMEM; 327 328 strscpy(ctx->devname, name, sizeof(ctx->devname)); 329 ctx->headv = (unsigned long)head; 330 ctx->hook = hooknum; 331 332 cb->seq = 1; 333 cb->data = ctx; 334 335 return 0; 336 } 337 338 static int nfnl_hook_dump_stop(struct netlink_callback *cb) 339 { 340 kfree(cb->data); 341 return 0; 342 } 343 344 static int nfnl_hook_get(struct sk_buff *skb, 345 const struct nfnl_info *info, 346 const struct nlattr * const nla[]) 347 { 348 if (!nla[NFNLA_HOOK_HOOKNUM]) 349 return -EINVAL; 350 351 if (info->nlh->nlmsg_flags & NLM_F_DUMP) { 352 struct netlink_dump_control c = { 353 .start = nfnl_hook_dump_start, 354 .done = nfnl_hook_dump_stop, 355 .dump = nfnl_hook_dump, 356 .module = THIS_MODULE, 357 .data = (void *)nla, 358 }; 359 360 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); 361 } 362 363 return -EOPNOTSUPP; 364 } 365 366 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = { 367 [NFNL_MSG_HOOK_GET] = { 368 .call = nfnl_hook_get, 369 .type = NFNL_CB_RCU, 370 .attr_count = NFNLA_HOOK_MAX, 371 .policy = nfnl_hook_nla_policy 372 }, 373 }; 374 375 static const struct nfnetlink_subsystem nfhook_subsys = { 376 .name = "nfhook", 377 .subsys_id = NFNL_SUBSYS_HOOK, 378 .cb_count = NFNL_MSG_HOOK_MAX, 379 .cb = nfnl_hook_cb, 380 }; 381 382 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK); 383 384 static int __init nfnetlink_hook_init(void) 385 { 386 return nfnetlink_subsys_register(&nfhook_subsys); 387 } 388 389 static void __exit nfnetlink_hook_exit(void) 390 { 391 nfnetlink_subsys_unregister(&nfhook_subsys); 392 } 393 394 module_init(nfnetlink_hook_init); 395 module_exit(nfnetlink_hook_exit); 396 397 MODULE_LICENSE("GPL"); 398 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 399 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks"); 400