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