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