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, NFTA_CHAIN_TABLE, chain->table->name); 93 if (ret) 94 goto cancel_nest; 95 96 ret = nla_put_string(nlskb, NFTA_CHAIN_NAME, chain->name); 97 if (ret) 98 goto cancel_nest; 99 100 nla_nest_end(nlskb, nest2); 101 nla_nest_end(nlskb, nest); 102 return ret; 103 104 cancel_nest: 105 nla_nest_cancel(nlskb, nest); 106 return -EMSGSIZE; 107 } 108 109 static int nfnl_hook_dump_one(struct sk_buff *nlskb, 110 const struct nfnl_dump_hook_data *ctx, 111 const struct nf_hook_ops *ops, 112 unsigned int seq) 113 { 114 u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET); 115 unsigned int portid = NETLINK_CB(nlskb).portid; 116 struct nlmsghdr *nlh; 117 int ret = -EMSGSIZE; 118 #ifdef CONFIG_KALLSYMS 119 char sym[KSYM_SYMBOL_LEN]; 120 char *module_name; 121 #endif 122 nlh = nfnl_msg_put(nlskb, portid, seq, event, 123 NLM_F_MULTI, ops->pf, NFNETLINK_V0, 0); 124 if (!nlh) 125 goto nla_put_failure; 126 127 #ifdef CONFIG_KALLSYMS 128 ret = snprintf(sym, sizeof(sym), "%ps", ops->hook); 129 if (ret >= sizeof(sym)) { 130 ret = -EINVAL; 131 goto nla_put_failure; 132 } 133 134 module_name = strstr(sym, " ["); 135 if (module_name) { 136 char *end; 137 138 module_name += 2; 139 end = strchr(module_name, ']'); 140 if (end) { 141 *end = 0; 142 143 ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name); 144 if (ret) 145 goto nla_put_failure; 146 } 147 } 148 149 ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym); 150 if (ret) 151 goto nla_put_failure; 152 #endif 153 154 ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(ops->hooknum)); 155 if (ret) 156 goto nla_put_failure; 157 158 ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority)); 159 if (ret) 160 goto nla_put_failure; 161 162 ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops); 163 if (ret) 164 goto nla_put_failure; 165 166 nlmsg_end(nlskb, nlh); 167 return 0; 168 nla_put_failure: 169 nlmsg_trim(nlskb, nlh); 170 return ret; 171 } 172 173 static const struct nf_hook_entries * 174 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev) 175 { 176 const struct nf_hook_entries *hook_head = NULL; 177 struct net_device *netdev; 178 179 switch (pf) { 180 case NFPROTO_IPV4: 181 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4)) 182 return ERR_PTR(-EINVAL); 183 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]); 184 break; 185 case NFPROTO_IPV6: 186 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6)) 187 return ERR_PTR(-EINVAL); 188 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]); 189 break; 190 case NFPROTO_ARP: 191 #ifdef CONFIG_NETFILTER_FAMILY_ARP 192 if (hook >= ARRAY_SIZE(net->nf.hooks_arp)) 193 return ERR_PTR(-EINVAL); 194 hook_head = rcu_dereference(net->nf.hooks_arp[hook]); 195 #endif 196 break; 197 case NFPROTO_BRIDGE: 198 #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE 199 if (hook >= ARRAY_SIZE(net->nf.hooks_bridge)) 200 return ERR_PTR(-EINVAL); 201 hook_head = rcu_dereference(net->nf.hooks_bridge[hook]); 202 #endif 203 break; 204 #if IS_ENABLED(CONFIG_DECNET) 205 case NFPROTO_DECNET: 206 if (hook >= ARRAY_SIZE(net->nf.hooks_decnet)) 207 return ERR_PTR(-EINVAL); 208 hook_head = rcu_dereference(net->nf.hooks_decnet[hook]); 209 break; 210 #endif 211 #ifdef CONFIG_NETFILTER_INGRESS 212 case NFPROTO_NETDEV: 213 if (hook != NF_NETDEV_INGRESS) 214 return ERR_PTR(-EOPNOTSUPP); 215 216 if (!dev) 217 return ERR_PTR(-ENODEV); 218 219 netdev = dev_get_by_name_rcu(net, dev); 220 if (!netdev) 221 return ERR_PTR(-ENODEV); 222 223 return rcu_dereference(netdev->nf_hooks_ingress); 224 #endif 225 default: 226 return ERR_PTR(-EPROTONOSUPPORT); 227 } 228 229 return hook_head; 230 } 231 232 static int nfnl_hook_dump(struct sk_buff *nlskb, 233 struct netlink_callback *cb) 234 { 235 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); 236 struct nfnl_dump_hook_data *ctx = cb->data; 237 int err, family = nfmsg->nfgen_family; 238 struct net *net = sock_net(nlskb->sk); 239 struct nf_hook_ops * const *ops; 240 const struct nf_hook_entries *e; 241 unsigned int i = cb->args[0]; 242 243 rcu_read_lock(); 244 245 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname); 246 if (!e) 247 goto done; 248 249 if (IS_ERR(e)) { 250 cb->seq++; 251 goto done; 252 } 253 254 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries) 255 cb->seq++; 256 257 ops = nf_hook_entries_get_hook_ops(e); 258 259 for (; i < e->num_hook_entries; i++) { 260 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], cb->seq); 261 if (err) 262 break; 263 } 264 265 done: 266 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb)); 267 rcu_read_unlock(); 268 cb->args[0] = i; 269 return nlskb->len; 270 } 271 272 static int nfnl_hook_dump_start(struct netlink_callback *cb) 273 { 274 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); 275 const struct nlattr * const *nla = cb->data; 276 struct nfnl_dump_hook_data *ctx = NULL; 277 struct net *net = sock_net(cb->skb->sk); 278 u8 family = nfmsg->nfgen_family; 279 char name[IFNAMSIZ] = ""; 280 const void *head; 281 u32 hooknum; 282 283 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM])); 284 if (hooknum > 255) 285 return -EINVAL; 286 287 if (family == NFPROTO_NETDEV) { 288 if (!nla[NFNLA_HOOK_DEV]) 289 return -EINVAL; 290 291 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name)); 292 } 293 294 rcu_read_lock(); 295 /* Not dereferenced; for consistency check only */ 296 head = nfnl_hook_entries_head(family, hooknum, net, name); 297 rcu_read_unlock(); 298 299 if (head && IS_ERR(head)) 300 return PTR_ERR(head); 301 302 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 303 if (!ctx) 304 return -ENOMEM; 305 306 strscpy(ctx->devname, name, sizeof(ctx->devname)); 307 ctx->headv = (unsigned long)head; 308 ctx->hook = hooknum; 309 310 cb->seq = 1; 311 cb->data = ctx; 312 313 return 0; 314 } 315 316 static int nfnl_hook_dump_stop(struct netlink_callback *cb) 317 { 318 kfree(cb->data); 319 return 0; 320 } 321 322 static int nfnl_hook_get(struct sk_buff *skb, 323 const struct nfnl_info *info, 324 const struct nlattr * const nla[]) 325 { 326 if (!nla[NFNLA_HOOK_HOOKNUM]) 327 return -EINVAL; 328 329 if (info->nlh->nlmsg_flags & NLM_F_DUMP) { 330 struct netlink_dump_control c = { 331 .start = nfnl_hook_dump_start, 332 .done = nfnl_hook_dump_stop, 333 .dump = nfnl_hook_dump, 334 .module = THIS_MODULE, 335 .data = (void *)nla, 336 }; 337 338 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); 339 } 340 341 return -EOPNOTSUPP; 342 } 343 344 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = { 345 [NFNL_MSG_HOOK_GET] = { 346 .call = nfnl_hook_get, 347 .type = NFNL_CB_RCU, 348 .attr_count = NFNLA_HOOK_MAX, 349 .policy = nfnl_hook_nla_policy 350 }, 351 }; 352 353 static const struct nfnetlink_subsystem nfhook_subsys = { 354 .name = "nfhook", 355 .subsys_id = NFNL_SUBSYS_HOOK, 356 .cb_count = NFNL_MSG_HOOK_MAX, 357 .cb = nfnl_hook_cb, 358 }; 359 360 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK); 361 362 static int __init nfnetlink_hook_init(void) 363 { 364 return nfnetlink_subsys_register(&nfhook_subsys); 365 } 366 367 static void __exit nfnetlink_hook_exit(void) 368 { 369 nfnetlink_subsys_unregister(&nfhook_subsys); 370 } 371 372 module_init(nfnetlink_hook_init); 373 module_exit(nfnetlink_hook_exit); 374 375 MODULE_LICENSE("GPL"); 376 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 377 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks"); 378