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