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