linux/net/netfilter/nfnetlink.c
<<
>>
Prefs
   1/* Netfilter messages via netlink socket. Allows for user space
   2 * protocol helpers and general trouble making from userspace.
   3 *
   4 * (C) 2001 by Jay Schulist <jschlst@samba.org>,
   5 * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
   6 * (C) 2005,2007 by Pablo Neira Ayuso <pablo@netfilter.org>
   7 *
   8 * Initial netfilter messages via netlink development funded and
   9 * generally made possible by Network Robots, Inc. (www.networkrobots.com)
  10 *
  11 * Further development of this code funded by Astaro AG (http://www.astaro.com)
  12 *
  13 * This software may be used and distributed according to the terms
  14 * of the GNU General Public License, incorporated herein by reference.
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/types.h>
  19#include <linux/socket.h>
  20#include <linux/kernel.h>
  21#include <linux/string.h>
  22#include <linux/sockios.h>
  23#include <linux/net.h>
  24#include <linux/skbuff.h>
  25#include <asm/uaccess.h>
  26#include <net/sock.h>
  27#include <net/netlink.h>
  28#include <linux/init.h>
  29
  30#include <linux/netlink.h>
  31#include <linux/netfilter/nfnetlink.h>
  32
  33MODULE_LICENSE("GPL");
  34MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  35MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
  36
  37static char __initdata nfversion[] = "0.30";
  38
  39static struct {
  40        struct mutex                            mutex;
  41        const struct nfnetlink_subsystem __rcu  *subsys;
  42} table[NFNL_SUBSYS_COUNT];
  43
  44static const int nfnl_group2type[NFNLGRP_MAX+1] = {
  45        [NFNLGRP_CONNTRACK_NEW]         = NFNL_SUBSYS_CTNETLINK,
  46        [NFNLGRP_CONNTRACK_UPDATE]      = NFNL_SUBSYS_CTNETLINK,
  47        [NFNLGRP_CONNTRACK_DESTROY]     = NFNL_SUBSYS_CTNETLINK,
  48        [NFNLGRP_CONNTRACK_EXP_NEW]     = NFNL_SUBSYS_CTNETLINK_EXP,
  49        [NFNLGRP_CONNTRACK_EXP_UPDATE]  = NFNL_SUBSYS_CTNETLINK_EXP,
  50        [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
  51};
  52
  53void nfnl_lock(__u8 subsys_id)
  54{
  55        mutex_lock(&table[subsys_id].mutex);
  56}
  57EXPORT_SYMBOL_GPL(nfnl_lock);
  58
  59void nfnl_unlock(__u8 subsys_id)
  60{
  61        mutex_unlock(&table[subsys_id].mutex);
  62}
  63EXPORT_SYMBOL_GPL(nfnl_unlock);
  64
  65int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
  66{
  67        nfnl_lock(n->subsys_id);
  68        if (table[n->subsys_id].subsys) {
  69                nfnl_unlock(n->subsys_id);
  70                return -EBUSY;
  71        }
  72        rcu_assign_pointer(table[n->subsys_id].subsys, n);
  73        nfnl_unlock(n->subsys_id);
  74
  75        return 0;
  76}
  77EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
  78
  79int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
  80{
  81        nfnl_lock(n->subsys_id);
  82        table[n->subsys_id].subsys = NULL;
  83        nfnl_unlock(n->subsys_id);
  84        synchronize_rcu();
  85        return 0;
  86}
  87EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
  88
  89static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
  90{
  91        u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
  92
  93        if (subsys_id >= NFNL_SUBSYS_COUNT)
  94                return NULL;
  95
  96        return rcu_dereference(table[subsys_id].subsys);
  97}
  98
  99static inline const struct nfnl_callback *
 100nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
 101{
 102        u_int8_t cb_id = NFNL_MSG_TYPE(type);
 103
 104        if (cb_id >= ss->cb_count)
 105                return NULL;
 106
 107        return &ss->cb[cb_id];
 108}
 109
 110int nfnetlink_has_listeners(struct net *net, unsigned int group)
 111{
 112        return netlink_has_listeners(net->nfnl, group);
 113}
 114EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
 115
 116int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
 117                   unsigned int group, int echo, gfp_t flags)
 118{
 119        return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags);
 120}
 121EXPORT_SYMBOL_GPL(nfnetlink_send);
 122
 123int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
 124{
 125        return netlink_set_err(net->nfnl, pid, group, error);
 126}
 127EXPORT_SYMBOL_GPL(nfnetlink_set_err);
 128
 129int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags)
 130{
 131        return netlink_unicast(net->nfnl, skb, pid, flags);
 132}
 133EXPORT_SYMBOL_GPL(nfnetlink_unicast);
 134
 135/* Process one complete nfnetlink message. */
 136static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 137{
 138        struct net *net = sock_net(skb->sk);
 139        const struct nfnl_callback *nc;
 140        const struct nfnetlink_subsystem *ss;
 141        int type, err;
 142
 143        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 144                return -EPERM;
 145
 146        /* All the messages must at least contain nfgenmsg */
 147        if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
 148                return 0;
 149
 150        type = nlh->nlmsg_type;
 151replay:
 152        rcu_read_lock();
 153        ss = nfnetlink_get_subsys(type);
 154        if (!ss) {
 155#ifdef CONFIG_MODULES
 156                rcu_read_unlock();
 157                request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
 158                rcu_read_lock();
 159                ss = nfnetlink_get_subsys(type);
 160                if (!ss)
 161#endif
 162                {
 163                        rcu_read_unlock();
 164                        return -EINVAL;
 165                }
 166        }
 167
 168        nc = nfnetlink_find_client(type, ss);
 169        if (!nc) {
 170                rcu_read_unlock();
 171                return -EINVAL;
 172        }
 173
 174        {
 175                int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
 176                u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
 177                struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
 178                struct nlattr *attr = (void *)nlh + min_len;
 179                int attrlen = nlh->nlmsg_len - min_len;
 180                __u8 subsys_id = NFNL_SUBSYS_ID(type);
 181
 182                err = nla_parse(cda, ss->cb[cb_id].attr_count,
 183                                attr, attrlen, ss->cb[cb_id].policy);
 184                if (err < 0) {
 185                        rcu_read_unlock();
 186                        return err;
 187                }
 188
 189                if (nc->call_rcu) {
 190                        err = nc->call_rcu(net->nfnl, skb, nlh,
 191                                           (const struct nlattr **)cda);
 192                        rcu_read_unlock();
 193                } else {
 194                        rcu_read_unlock();
 195                        nfnl_lock(subsys_id);
 196                        if (rcu_dereference_protected(table[subsys_id].subsys,
 197                                lockdep_is_held(&table[subsys_id].mutex)) != ss ||
 198                            nfnetlink_find_client(type, ss) != nc)
 199                                err = -EAGAIN;
 200                        else if (nc->call)
 201                                err = nc->call(net->nfnl, skb, nlh,
 202                                                   (const struct nlattr **)cda);
 203                        else
 204                                err = -EINVAL;
 205                        nfnl_unlock(subsys_id);
 206                }
 207                if (err == -EAGAIN)
 208                        goto replay;
 209                return err;
 210        }
 211}
 212
 213static void nfnetlink_rcv(struct sk_buff *skb)
 214{
 215        netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
 216}
 217
 218#ifdef CONFIG_MODULES
 219static void nfnetlink_bind(int group)
 220{
 221        const struct nfnetlink_subsystem *ss;
 222        int type = nfnl_group2type[group];
 223
 224        rcu_read_lock();
 225        ss = nfnetlink_get_subsys(type);
 226        if (!ss) {
 227                rcu_read_unlock();
 228                request_module("nfnetlink-subsys-%d", type);
 229                return;
 230        }
 231        rcu_read_unlock();
 232}
 233#endif
 234
 235static int __net_init nfnetlink_net_init(struct net *net)
 236{
 237        struct sock *nfnl;
 238        struct netlink_kernel_cfg cfg = {
 239                .groups = NFNLGRP_MAX,
 240                .input  = nfnetlink_rcv,
 241#ifdef CONFIG_MODULES
 242                .bind   = nfnetlink_bind,
 243#endif
 244        };
 245
 246        nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);
 247        if (!nfnl)
 248                return -ENOMEM;
 249        net->nfnl_stash = nfnl;
 250        rcu_assign_pointer(net->nfnl, nfnl);
 251        return 0;
 252}
 253
 254static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
 255{
 256        struct net *net;
 257
 258        list_for_each_entry(net, net_exit_list, exit_list)
 259                RCU_INIT_POINTER(net->nfnl, NULL);
 260        synchronize_net();
 261        list_for_each_entry(net, net_exit_list, exit_list)
 262                netlink_kernel_release(net->nfnl_stash);
 263}
 264
 265static struct pernet_operations nfnetlink_net_ops = {
 266        .init           = nfnetlink_net_init,
 267        .exit_batch     = nfnetlink_net_exit_batch,
 268};
 269
 270static int __init nfnetlink_init(void)
 271{
 272        int i;
 273
 274        for (i=0; i<NFNL_SUBSYS_COUNT; i++)
 275                mutex_init(&table[i].mutex);
 276
 277        pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
 278        return register_pernet_subsys(&nfnetlink_net_ops);
 279}
 280
 281static void __exit nfnetlink_exit(void)
 282{
 283        pr_info("Removing netfilter NETLINK layer.\n");
 284        unregister_pernet_subsys(&nfnetlink_net_ops);
 285}
 286module_init(nfnetlink_init);
 287module_exit(nfnetlink_exit);
 288