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 <linux/init.h>
  28
  29#include <net/netlink.h>
  30#include <linux/netfilter/nfnetlink.h>
  31
  32MODULE_LICENSE("GPL");
  33MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  34MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
  35
  36static char __initdata nfversion[] = "0.30";
  37
  38static struct {
  39        struct mutex                            mutex;
  40        const struct nfnetlink_subsystem __rcu  *subsys;
  41} table[NFNL_SUBSYS_COUNT];
  42
  43static const int nfnl_group2type[NFNLGRP_MAX+1] = {
  44        [NFNLGRP_CONNTRACK_NEW]         = NFNL_SUBSYS_CTNETLINK,
  45        [NFNLGRP_CONNTRACK_UPDATE]      = NFNL_SUBSYS_CTNETLINK,
  46        [NFNLGRP_CONNTRACK_DESTROY]     = NFNL_SUBSYS_CTNETLINK,
  47        [NFNLGRP_CONNTRACK_EXP_NEW]     = NFNL_SUBSYS_CTNETLINK_EXP,
  48        [NFNLGRP_CONNTRACK_EXP_UPDATE]  = NFNL_SUBSYS_CTNETLINK_EXP,
  49        [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
  50};
  51
  52void nfnl_lock(__u8 subsys_id)
  53{
  54        mutex_lock(&table[subsys_id].mutex);
  55}
  56EXPORT_SYMBOL_GPL(nfnl_lock);
  57
  58void nfnl_unlock(__u8 subsys_id)
  59{
  60        mutex_unlock(&table[subsys_id].mutex);
  61}
  62EXPORT_SYMBOL_GPL(nfnl_unlock);
  63
  64int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
  65{
  66        nfnl_lock(n->subsys_id);
  67        if (table[n->subsys_id].subsys) {
  68                nfnl_unlock(n->subsys_id);
  69                return -EBUSY;
  70        }
  71        rcu_assign_pointer(table[n->subsys_id].subsys, n);
  72        nfnl_unlock(n->subsys_id);
  73
  74        return 0;
  75}
  76EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
  77
  78int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
  79{
  80        nfnl_lock(n->subsys_id);
  81        table[n->subsys_id].subsys = NULL;
  82        nfnl_unlock(n->subsys_id);
  83        synchronize_rcu();
  84        return 0;
  85}
  86EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
  87
  88static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
  89{
  90        u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
  91
  92        if (subsys_id >= NFNL_SUBSYS_COUNT)
  93                return NULL;
  94
  95        return rcu_dereference(table[subsys_id].subsys);
  96}
  97
  98static inline const struct nfnl_callback *
  99nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
 100{
 101        u_int8_t cb_id = NFNL_MSG_TYPE(type);
 102
 103        if (cb_id >= ss->cb_count)
 104                return NULL;
 105
 106        return &ss->cb[cb_id];
 107}
 108
 109int nfnetlink_has_listeners(struct net *net, unsigned int group)
 110{
 111        return netlink_has_listeners(net->nfnl, group);
 112}
 113EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
 114
 115struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size,
 116                                    u32 dst_portid, gfp_t gfp_mask)
 117{
 118        return netlink_alloc_skb(net->nfnl, size, dst_portid, gfp_mask);
 119}
 120EXPORT_SYMBOL_GPL(nfnetlink_alloc_skb);
 121
 122int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
 123                   unsigned int group, int echo, gfp_t flags)
 124{
 125        return nlmsg_notify(net->nfnl, skb, portid, group, echo, flags);
 126}
 127EXPORT_SYMBOL_GPL(nfnetlink_send);
 128
 129int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error)
 130{
 131        return netlink_set_err(net->nfnl, portid, group, error);
 132}
 133EXPORT_SYMBOL_GPL(nfnetlink_set_err);
 134
 135int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
 136                      int flags)
 137{
 138        return netlink_unicast(net->nfnl, skb, portid, flags);
 139}
 140EXPORT_SYMBOL_GPL(nfnetlink_unicast);
 141
 142/* Process one complete nfnetlink message. */
 143static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 144{
 145        struct net *net = sock_net(skb->sk);
 146        const struct nfnl_callback *nc;
 147        const struct nfnetlink_subsystem *ss;
 148        int type, err;
 149
 150        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 151                return -EPERM;
 152
 153        /* All the messages must at least contain nfgenmsg */
 154        if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
 155                return 0;
 156
 157        type = nlh->nlmsg_type;
 158replay:
 159        rcu_read_lock();
 160        ss = nfnetlink_get_subsys(type);
 161        if (!ss) {
 162#ifdef CONFIG_MODULES
 163                rcu_read_unlock();
 164                request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
 165                rcu_read_lock();
 166                ss = nfnetlink_get_subsys(type);
 167                if (!ss)
 168#endif
 169                {
 170                        rcu_read_unlock();
 171                        return -EINVAL;
 172                }
 173        }
 174
 175        nc = nfnetlink_find_client(type, ss);
 176        if (!nc) {
 177                rcu_read_unlock();
 178                return -EINVAL;
 179        }
 180
 181        {
 182                int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
 183                u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
 184                struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
 185                struct nlattr *attr = (void *)nlh + min_len;
 186                int attrlen = nlh->nlmsg_len - min_len;
 187                __u8 subsys_id = NFNL_SUBSYS_ID(type);
 188
 189                err = nla_parse(cda, ss->cb[cb_id].attr_count,
 190                                attr, attrlen, ss->cb[cb_id].policy);
 191                if (err < 0) {
 192                        rcu_read_unlock();
 193                        return err;
 194                }
 195
 196                if (nc->call_rcu) {
 197                        err = nc->call_rcu(net->nfnl, skb, nlh,
 198                                           (const struct nlattr **)cda);
 199                        rcu_read_unlock();
 200                } else {
 201                        rcu_read_unlock();
 202                        nfnl_lock(subsys_id);
 203                        if (rcu_dereference_protected(table[subsys_id].subsys,
 204                                lockdep_is_held(&table[subsys_id].mutex)) != ss ||
 205                            nfnetlink_find_client(type, ss) != nc)
 206                                err = -EAGAIN;
 207                        else if (nc->call)
 208                                err = nc->call(net->nfnl, skb, nlh,
 209                                                   (const struct nlattr **)cda);
 210                        else
 211                                err = -EINVAL;
 212                        nfnl_unlock(subsys_id);
 213                }
 214                if (err == -EAGAIN)
 215                        goto replay;
 216                return err;
 217        }
 218}
 219
 220static void nfnetlink_rcv(struct sk_buff *skb)
 221{
 222        netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
 223}
 224
 225#ifdef CONFIG_MODULES
 226static void nfnetlink_bind(int group)
 227{
 228        const struct nfnetlink_subsystem *ss;
 229        int type = nfnl_group2type[group];
 230
 231        rcu_read_lock();
 232        ss = nfnetlink_get_subsys(type);
 233        if (!ss) {
 234                rcu_read_unlock();
 235                request_module("nfnetlink-subsys-%d", type);
 236                return;
 237        }
 238        rcu_read_unlock();
 239}
 240#endif
 241
 242static int __net_init nfnetlink_net_init(struct net *net)
 243{
 244        struct sock *nfnl;
 245        struct netlink_kernel_cfg cfg = {
 246                .groups = NFNLGRP_MAX,
 247                .input  = nfnetlink_rcv,
 248#ifdef CONFIG_MODULES
 249                .bind   = nfnetlink_bind,
 250#endif
 251        };
 252
 253        nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);
 254        if (!nfnl)
 255                return -ENOMEM;
 256        net->nfnl_stash = nfnl;
 257        rcu_assign_pointer(net->nfnl, nfnl);
 258        return 0;
 259}
 260
 261static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
 262{
 263        struct net *net;
 264
 265        list_for_each_entry(net, net_exit_list, exit_list)
 266                RCU_INIT_POINTER(net->nfnl, NULL);
 267        synchronize_net();
 268        list_for_each_entry(net, net_exit_list, exit_list)
 269                netlink_kernel_release(net->nfnl_stash);
 270}
 271
 272static struct pernet_operations nfnetlink_net_ops = {
 273        .init           = nfnetlink_net_init,
 274        .exit_batch     = nfnetlink_net_exit_batch,
 275};
 276
 277static int __init nfnetlink_init(void)
 278{
 279        int i;
 280
 281        for (i=0; i<NFNL_SUBSYS_COUNT; i++)
 282                mutex_init(&table[i].mutex);
 283
 284        pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
 285        return register_pernet_subsys(&nfnetlink_net_ops);
 286}
 287
 288static void __exit nfnetlink_exit(void)
 289{
 290        pr_info("Removing netfilter NETLINK layer.\n");
 291        unregister_pernet_subsys(&nfnetlink_net_ops);
 292}
 293module_init(nfnetlink_init);
 294module_exit(nfnetlink_exit);
 295
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.