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/kallsyms.h>
  10#include <linux/kernel.h>
  11#include <linux/types.h>
  12#include <linux/skbuff.h>
  13#include <linux/errno.h>
  14#include <linux/netlink.h>
  15#include <linux/slab.h>
  16
  17#include <linux/netfilter.h>
  18
  19#include <linux/netfilter/nfnetlink.h>
  20#include <linux/netfilter/nfnetlink_hook.h>
  21
  22#include <net/netfilter/nf_tables.h>
  23#include <net/sock.h>
  24
  25static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
  26        [NFNLA_HOOK_HOOKNUM]    = { .type = NLA_U32 },
  27        [NFNLA_HOOK_PRIORITY]   = { .type = NLA_U32 },
  28        [NFNLA_HOOK_DEV]        = { .type = NLA_STRING,
  29                                    .len = IFNAMSIZ - 1 },
  30        [NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
  31                                       .len = KSYM_NAME_LEN, },
  32        [NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
  33                                     .len = MODULE_NAME_LEN, },
  34        [NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
  35};
  36
  37static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
  38                                     const struct nlmsghdr *nlh,
  39                                     struct netlink_dump_control *c)
  40{
  41        int err;
  42
  43        if (!try_module_get(THIS_MODULE))
  44                return -EINVAL;
  45
  46        rcu_read_unlock();
  47        err = netlink_dump_start(nlsk, skb, nlh, c);
  48        rcu_read_lock();
  49        module_put(THIS_MODULE);
  50
  51        return err;
  52}
  53
  54struct nfnl_dump_hook_data {
  55        char devname[IFNAMSIZ];
  56        unsigned long headv;
  57        u8 hook;
  58};
  59
  60static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
  61                                        const struct nfnl_dump_hook_data *ctx,
  62                                        unsigned int seq,
  63                                        const struct nf_hook_ops *ops)
  64{
  65        struct net *net = sock_net(nlskb->sk);
  66        struct nlattr *nest, *nest2;
  67        struct nft_chain *chain;
  68        int ret = 0;
  69
  70        if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
  71                return 0;
  72
  73        chain = ops->priv;
  74        if (WARN_ON_ONCE(!chain))
  75                return 0;
  76
  77        if (!nft_is_active(net, chain))
  78                return 0;
  79
  80        nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
  81        if (!nest)
  82                return -EMSGSIZE;
  83
  84        ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
  85                           htonl(NFNL_HOOK_TYPE_NFTABLES));
  86        if (ret)
  87                goto cancel_nest;
  88
  89        nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
  90        if (!nest2)
  91                goto cancel_nest;
  92
  93        ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
  94        if (ret)
  95                goto cancel_nest;
  96
  97        ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
  98        if (ret)
  99                goto cancel_nest;
 100
 101        ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
 102        if (ret)
 103                goto cancel_nest;
 104
 105        nla_nest_end(nlskb, nest2);
 106        nla_nest_end(nlskb, nest);
 107        return ret;
 108
 109cancel_nest:
 110        nla_nest_cancel(nlskb, nest);
 111        return -EMSGSIZE;
 112}
 113
 114static int nfnl_hook_dump_one(struct sk_buff *nlskb,
 115                              const struct nfnl_dump_hook_data *ctx,
 116                              const struct nf_hook_ops *ops,
 117                              int family, unsigned int seq)
 118{
 119        u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
 120        unsigned int portid = NETLINK_CB(nlskb).portid;
 121        struct nlmsghdr *nlh;
 122        int ret = -EMSGSIZE;
 123        u32 hooknum;
 124#ifdef CONFIG_KALLSYMS
 125        char sym[KSYM_SYMBOL_LEN];
 126        char *module_name;
 127#endif
 128        nlh = nfnl_msg_put(nlskb, portid, seq, event,
 129                           NLM_F_MULTI, family, NFNETLINK_V0, 0);
 130        if (!nlh)
 131                goto nla_put_failure;
 132
 133#ifdef CONFIG_KALLSYMS
 134        ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
 135        if (ret >= sizeof(sym)) {
 136                ret = -EINVAL;
 137                goto nla_put_failure;
 138        }
 139
 140        module_name = strstr(sym, " [");
 141        if (module_name) {
 142                char *end;
 143
 144                *module_name = '\0';
 145                module_name += 2;
 146                end = strchr(module_name, ']');
 147                if (end) {
 148                        *end = 0;
 149
 150                        ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
 151                        if (ret)
 152                                goto nla_put_failure;
 153                }
 154        }
 155
 156        ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
 157        if (ret)
 158                goto nla_put_failure;
 159#endif
 160
 161        if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
 162                hooknum = NF_NETDEV_INGRESS;
 163        else
 164                hooknum = ops->hooknum;
 165
 166        ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
 167        if (ret)
 168                goto nla_put_failure;
 169
 170        ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
 171        if (ret)
 172                goto nla_put_failure;
 173
 174        ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
 175        if (ret)
 176                goto nla_put_failure;
 177
 178        nlmsg_end(nlskb, nlh);
 179        return 0;
 180nla_put_failure:
 181        nlmsg_trim(nlskb, nlh);
 182        return ret;
 183}
 184
 185static const struct nf_hook_entries *
 186nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
 187{
 188        const struct nf_hook_entries *hook_head = NULL;
 189#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
 190        struct net_device *netdev;
 191#endif
 192
 193        switch (pf) {
 194        case NFPROTO_IPV4:
 195                if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
 196                        return ERR_PTR(-EINVAL);
 197                hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
 198                break;
 199        case NFPROTO_IPV6:
 200                if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
 201                        return ERR_PTR(-EINVAL);
 202                hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
 203                break;
 204        case NFPROTO_ARP:
 205#ifdef CONFIG_NETFILTER_FAMILY_ARP
 206                if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
 207                        return ERR_PTR(-EINVAL);
 208                hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
 209#endif
 210                break;
 211        case NFPROTO_BRIDGE:
 212#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
 213                if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
 214                        return ERR_PTR(-EINVAL);
 215                hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
 216#endif
 217                break;
 218#if IS_ENABLED(CONFIG_DECNET)
 219        case NFPROTO_DECNET:
 220                if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
 221                        return ERR_PTR(-EINVAL);
 222                hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
 223                break;
 224#endif
 225#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
 226        case NFPROTO_NETDEV:
 227                if (hook >= NF_NETDEV_NUMHOOKS)
 228                        return ERR_PTR(-EOPNOTSUPP);
 229
 230                if (!dev)
 231                        return ERR_PTR(-ENODEV);
 232
 233                netdev = dev_get_by_name_rcu(net, dev);
 234                if (!netdev)
 235                        return ERR_PTR(-ENODEV);
 236
 237#ifdef CONFIG_NETFILTER_INGRESS
 238                if (hook == NF_NETDEV_INGRESS)
 239                        return rcu_dereference(netdev->nf_hooks_ingress);
 240#endif
 241#ifdef CONFIG_NETFILTER_EGRESS
 242                if (hook == NF_NETDEV_EGRESS)
 243                        return rcu_dereference(netdev->nf_hooks_egress);
 244#endif
 245                fallthrough;
 246#endif
 247        default:
 248                return ERR_PTR(-EPROTONOSUPPORT);
 249        }
 250
 251        return hook_head;
 252}
 253
 254static int nfnl_hook_dump(struct sk_buff *nlskb,
 255                          struct netlink_callback *cb)
 256{
 257        struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
 258        struct nfnl_dump_hook_data *ctx = cb->data;
 259        int err, family = nfmsg->nfgen_family;
 260        struct net *net = sock_net(nlskb->sk);
 261        struct nf_hook_ops * const *ops;
 262        const struct nf_hook_entries *e;
 263        unsigned int i = cb->args[0];
 264
 265        rcu_read_lock();
 266
 267        e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
 268        if (!e)
 269                goto done;
 270
 271        if (IS_ERR(e)) {
 272                cb->seq++;
 273                goto done;
 274        }
 275
 276        if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
 277                cb->seq++;
 278
 279        ops = nf_hook_entries_get_hook_ops(e);
 280
 281        for (; i < e->num_hook_entries; i++) {
 282                err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
 283                                         cb->nlh->nlmsg_seq);
 284                if (err)
 285                        break;
 286        }
 287
 288done:
 289        nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
 290        rcu_read_unlock();
 291        cb->args[0] = i;
 292        return nlskb->len;
 293}
 294
 295static int nfnl_hook_dump_start(struct netlink_callback *cb)
 296{
 297        const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
 298        const struct nlattr * const *nla = cb->data;
 299        struct nfnl_dump_hook_data *ctx = NULL;
 300        struct net *net = sock_net(cb->skb->sk);
 301        u8 family = nfmsg->nfgen_family;
 302        char name[IFNAMSIZ] = "";
 303        const void *head;
 304        u32 hooknum;
 305
 306        hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
 307        if (hooknum > 255)
 308                return -EINVAL;
 309
 310        if (family == NFPROTO_NETDEV) {
 311                if (!nla[NFNLA_HOOK_DEV])
 312                        return -EINVAL;
 313
 314                nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
 315        }
 316
 317        rcu_read_lock();
 318        /* Not dereferenced; for consistency check only */
 319        head = nfnl_hook_entries_head(family, hooknum, net, name);
 320        rcu_read_unlock();
 321
 322        if (head && IS_ERR(head))
 323                return PTR_ERR(head);
 324
 325        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 326        if (!ctx)
 327                return -ENOMEM;
 328
 329        strscpy(ctx->devname, name, sizeof(ctx->devname));
 330        ctx->headv = (unsigned long)head;
 331        ctx->hook = hooknum;
 332
 333        cb->seq = 1;
 334        cb->data = ctx;
 335
 336        return 0;
 337}
 338
 339static int nfnl_hook_dump_stop(struct netlink_callback *cb)
 340{
 341        kfree(cb->data);
 342        return 0;
 343}
 344
 345static int nfnl_hook_get(struct sk_buff *skb,
 346                         const struct nfnl_info *info,
 347                         const struct nlattr * const nla[])
 348{
 349        if (!nla[NFNLA_HOOK_HOOKNUM])
 350                return -EINVAL;
 351
 352        if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
 353                struct netlink_dump_control c = {
 354                        .start = nfnl_hook_dump_start,
 355                        .done = nfnl_hook_dump_stop,
 356                        .dump = nfnl_hook_dump,
 357                        .module = THIS_MODULE,
 358                        .data = (void *)nla,
 359                };
 360
 361                return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
 362        }
 363
 364        return -EOPNOTSUPP;
 365}
 366
 367static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
 368        [NFNL_MSG_HOOK_GET] = {
 369                .call           = nfnl_hook_get,
 370                .type           = NFNL_CB_RCU,
 371                .attr_count     = NFNLA_HOOK_MAX,
 372                .policy         = nfnl_hook_nla_policy
 373        },
 374};
 375
 376static const struct nfnetlink_subsystem nfhook_subsys = {
 377        .name                           = "nfhook",
 378        .subsys_id                      = NFNL_SUBSYS_HOOK,
 379        .cb_count                       = NFNL_MSG_HOOK_MAX,
 380        .cb                             = nfnl_hook_cb,
 381};
 382
 383MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
 384
 385static int __init nfnetlink_hook_init(void)
 386{
 387        return nfnetlink_subsys_register(&nfhook_subsys);
 388}
 389
 390static void __exit nfnetlink_hook_exit(void)
 391{
 392        nfnetlink_subsys_unregister(&nfhook_subsys);
 393}
 394
 395module_init(nfnetlink_hook_init);
 396module_exit(nfnetlink_hook_exit);
 397
 398MODULE_LICENSE("GPL");
 399MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
 400MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");
 401