Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2021 Red Hat GmbH
0004  *
0005  * Author: Florian Westphal <fw@strlen.de>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/kallsyms.h>
0010 #include <linux/kernel.h>
0011 #include <linux/types.h>
0012 #include <linux/skbuff.h>
0013 #include <linux/errno.h>
0014 #include <linux/netlink.h>
0015 #include <linux/slab.h>
0016 
0017 #include <linux/netfilter.h>
0018 
0019 #include <linux/netfilter/nfnetlink.h>
0020 #include <linux/netfilter/nfnetlink_hook.h>
0021 
0022 #include <net/netfilter/nf_tables.h>
0023 #include <net/sock.h>
0024 
0025 static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
0026     [NFNLA_HOOK_HOOKNUM]    = { .type = NLA_U32 },
0027     [NFNLA_HOOK_PRIORITY]   = { .type = NLA_U32 },
0028     [NFNLA_HOOK_DEV]    = { .type = NLA_STRING,
0029                     .len = IFNAMSIZ - 1 },
0030     [NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
0031                        .len = KSYM_NAME_LEN, },
0032     [NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
0033                      .len = MODULE_NAME_LEN, },
0034     [NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
0035 };
0036 
0037 static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
0038                      const struct nlmsghdr *nlh,
0039                      struct netlink_dump_control *c)
0040 {
0041     int err;
0042 
0043     if (!try_module_get(THIS_MODULE))
0044         return -EINVAL;
0045 
0046     rcu_read_unlock();
0047     err = netlink_dump_start(nlsk, skb, nlh, c);
0048     rcu_read_lock();
0049     module_put(THIS_MODULE);
0050 
0051     return err;
0052 }
0053 
0054 struct nfnl_dump_hook_data {
0055     char devname[IFNAMSIZ];
0056     unsigned long headv;
0057     u8 hook;
0058 };
0059 
0060 static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
0061                     const struct nfnl_dump_hook_data *ctx,
0062                     unsigned int seq,
0063                     const struct nf_hook_ops *ops)
0064 {
0065     struct net *net = sock_net(nlskb->sk);
0066     struct nlattr *nest, *nest2;
0067     struct nft_chain *chain;
0068     int ret = 0;
0069 
0070     if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
0071         return 0;
0072 
0073     chain = ops->priv;
0074     if (WARN_ON_ONCE(!chain))
0075         return 0;
0076 
0077     if (!nft_is_active(net, chain))
0078         return 0;
0079 
0080     nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
0081     if (!nest)
0082         return -EMSGSIZE;
0083 
0084     ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
0085                htonl(NFNL_HOOK_TYPE_NFTABLES));
0086     if (ret)
0087         goto cancel_nest;
0088 
0089     nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
0090     if (!nest2)
0091         goto cancel_nest;
0092 
0093     ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
0094     if (ret)
0095         goto cancel_nest;
0096 
0097     ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
0098     if (ret)
0099         goto cancel_nest;
0100 
0101     ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
0102     if (ret)
0103         goto cancel_nest;
0104 
0105     nla_nest_end(nlskb, nest2);
0106     nla_nest_end(nlskb, nest);
0107     return ret;
0108 
0109 cancel_nest:
0110     nla_nest_cancel(nlskb, nest);
0111     return -EMSGSIZE;
0112 }
0113 
0114 static int nfnl_hook_dump_one(struct sk_buff *nlskb,
0115                   const struct nfnl_dump_hook_data *ctx,
0116                   const struct nf_hook_ops *ops,
0117                   int family, unsigned int seq)
0118 {
0119     u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
0120     unsigned int portid = NETLINK_CB(nlskb).portid;
0121     struct nlmsghdr *nlh;
0122     int ret = -EMSGSIZE;
0123     u32 hooknum;
0124 #ifdef CONFIG_KALLSYMS
0125     char sym[KSYM_SYMBOL_LEN];
0126     char *module_name;
0127 #endif
0128     nlh = nfnl_msg_put(nlskb, portid, seq, event,
0129                NLM_F_MULTI, family, NFNETLINK_V0, 0);
0130     if (!nlh)
0131         goto nla_put_failure;
0132 
0133 #ifdef CONFIG_KALLSYMS
0134     ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
0135     if (ret >= sizeof(sym)) {
0136         ret = -EINVAL;
0137         goto nla_put_failure;
0138     }
0139 
0140     module_name = strstr(sym, " [");
0141     if (module_name) {
0142         char *end;
0143 
0144         *module_name = '\0';
0145         module_name += 2;
0146         end = strchr(module_name, ']');
0147         if (end) {
0148             *end = 0;
0149 
0150             ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
0151             if (ret)
0152                 goto nla_put_failure;
0153         }
0154     }
0155 
0156     ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
0157     if (ret)
0158         goto nla_put_failure;
0159 #endif
0160 
0161     if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
0162         hooknum = NF_NETDEV_INGRESS;
0163     else
0164         hooknum = ops->hooknum;
0165 
0166     ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
0167     if (ret)
0168         goto nla_put_failure;
0169 
0170     ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
0171     if (ret)
0172         goto nla_put_failure;
0173 
0174     ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
0175     if (ret)
0176         goto nla_put_failure;
0177 
0178     nlmsg_end(nlskb, nlh);
0179     return 0;
0180 nla_put_failure:
0181     nlmsg_trim(nlskb, nlh);
0182     return ret;
0183 }
0184 
0185 static const struct nf_hook_entries *
0186 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
0187 {
0188     const struct nf_hook_entries *hook_head = NULL;
0189 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
0190     struct net_device *netdev;
0191 #endif
0192 
0193     switch (pf) {
0194     case NFPROTO_IPV4:
0195         if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
0196             return ERR_PTR(-EINVAL);
0197         hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
0198         break;
0199     case NFPROTO_IPV6:
0200         if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
0201             return ERR_PTR(-EINVAL);
0202         hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
0203         break;
0204     case NFPROTO_ARP:
0205 #ifdef CONFIG_NETFILTER_FAMILY_ARP
0206         if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
0207             return ERR_PTR(-EINVAL);
0208         hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
0209 #endif
0210         break;
0211     case NFPROTO_BRIDGE:
0212 #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
0213         if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
0214             return ERR_PTR(-EINVAL);
0215         hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
0216 #endif
0217         break;
0218 #if IS_ENABLED(CONFIG_DECNET)
0219     case NFPROTO_DECNET:
0220         if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
0221             return ERR_PTR(-EINVAL);
0222         hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
0223         break;
0224 #endif
0225 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
0226     case NFPROTO_NETDEV:
0227         if (hook >= NF_NETDEV_NUMHOOKS)
0228             return ERR_PTR(-EOPNOTSUPP);
0229 
0230         if (!dev)
0231             return ERR_PTR(-ENODEV);
0232 
0233         netdev = dev_get_by_name_rcu(net, dev);
0234         if (!netdev)
0235             return ERR_PTR(-ENODEV);
0236 
0237 #ifdef CONFIG_NETFILTER_INGRESS
0238         if (hook == NF_NETDEV_INGRESS)
0239             return rcu_dereference(netdev->nf_hooks_ingress);
0240 #endif
0241 #ifdef CONFIG_NETFILTER_EGRESS
0242         if (hook == NF_NETDEV_EGRESS)
0243             return rcu_dereference(netdev->nf_hooks_egress);
0244 #endif
0245         fallthrough;
0246 #endif
0247     default:
0248         return ERR_PTR(-EPROTONOSUPPORT);
0249     }
0250 
0251     return hook_head;
0252 }
0253 
0254 static int nfnl_hook_dump(struct sk_buff *nlskb,
0255               struct netlink_callback *cb)
0256 {
0257     struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
0258     struct nfnl_dump_hook_data *ctx = cb->data;
0259     int err, family = nfmsg->nfgen_family;
0260     struct net *net = sock_net(nlskb->sk);
0261     struct nf_hook_ops * const *ops;
0262     const struct nf_hook_entries *e;
0263     unsigned int i = cb->args[0];
0264 
0265     rcu_read_lock();
0266 
0267     e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
0268     if (!e)
0269         goto done;
0270 
0271     if (IS_ERR(e)) {
0272         cb->seq++;
0273         goto done;
0274     }
0275 
0276     if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
0277         cb->seq++;
0278 
0279     ops = nf_hook_entries_get_hook_ops(e);
0280 
0281     for (; i < e->num_hook_entries; i++) {
0282         err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
0283                      cb->nlh->nlmsg_seq);
0284         if (err)
0285             break;
0286     }
0287 
0288 done:
0289     nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
0290     rcu_read_unlock();
0291     cb->args[0] = i;
0292     return nlskb->len;
0293 }
0294 
0295 static int nfnl_hook_dump_start(struct netlink_callback *cb)
0296 {
0297     const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
0298     const struct nlattr * const *nla = cb->data;
0299     struct nfnl_dump_hook_data *ctx = NULL;
0300     struct net *net = sock_net(cb->skb->sk);
0301     u8 family = nfmsg->nfgen_family;
0302     char name[IFNAMSIZ] = "";
0303     const void *head;
0304     u32 hooknum;
0305 
0306     hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
0307     if (hooknum > 255)
0308         return -EINVAL;
0309 
0310     if (family == NFPROTO_NETDEV) {
0311         if (!nla[NFNLA_HOOK_DEV])
0312             return -EINVAL;
0313 
0314         nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
0315     }
0316 
0317     rcu_read_lock();
0318     /* Not dereferenced; for consistency check only */
0319     head = nfnl_hook_entries_head(family, hooknum, net, name);
0320     rcu_read_unlock();
0321 
0322     if (head && IS_ERR(head))
0323         return PTR_ERR(head);
0324 
0325     ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
0326     if (!ctx)
0327         return -ENOMEM;
0328 
0329     strscpy(ctx->devname, name, sizeof(ctx->devname));
0330     ctx->headv = (unsigned long)head;
0331     ctx->hook = hooknum;
0332 
0333     cb->seq = 1;
0334     cb->data = ctx;
0335 
0336     return 0;
0337 }
0338 
0339 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
0340 {
0341     kfree(cb->data);
0342     return 0;
0343 }
0344 
0345 static int nfnl_hook_get(struct sk_buff *skb,
0346              const struct nfnl_info *info,
0347              const struct nlattr * const nla[])
0348 {
0349     if (!nla[NFNLA_HOOK_HOOKNUM])
0350         return -EINVAL;
0351 
0352     if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
0353         struct netlink_dump_control c = {
0354             .start = nfnl_hook_dump_start,
0355             .done = nfnl_hook_dump_stop,
0356             .dump = nfnl_hook_dump,
0357             .module = THIS_MODULE,
0358             .data = (void *)nla,
0359         };
0360 
0361         return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
0362     }
0363 
0364     return -EOPNOTSUPP;
0365 }
0366 
0367 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
0368     [NFNL_MSG_HOOK_GET] = {
0369         .call       = nfnl_hook_get,
0370         .type       = NFNL_CB_RCU,
0371         .attr_count = NFNLA_HOOK_MAX,
0372         .policy     = nfnl_hook_nla_policy
0373     },
0374 };
0375 
0376 static const struct nfnetlink_subsystem nfhook_subsys = {
0377     .name               = "nfhook",
0378     .subsys_id          = NFNL_SUBSYS_HOOK,
0379     .cb_count           = NFNL_MSG_HOOK_MAX,
0380     .cb             = nfnl_hook_cb,
0381 };
0382 
0383 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
0384 
0385 static int __init nfnetlink_hook_init(void)
0386 {
0387     return nfnetlink_subsys_register(&nfhook_subsys);
0388 }
0389 
0390 static void __exit nfnetlink_hook_exit(void)
0391 {
0392     nfnetlink_subsys_unregister(&nfhook_subsys);
0393 }
0394 
0395 module_init(nfnetlink_hook_init);
0396 module_exit(nfnetlink_hook_exit);
0397 
0398 MODULE_LICENSE("GPL");
0399 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
0400 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");