Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
xt_connlimit.c
Go to the documentation of this file.
1 /*
2  * netfilter module to limit the number of parallel tcp
3  * connections per IP address.
4  * (c) 2000 Gerd Knorr <[email protected]>
5  * Nov 2002: Martin Bene <[email protected]>:
6  * only ignore TIME_WAIT or gone connections
7  * (C) CC Computer Consultants GmbH, 2007
8  *
9  * based on ...
10  *
11  * Kernel module to match connection tracking information.
12  * GPL (C) 1999 Rusty Russell ([email protected]).
13  */
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 #include <linux/in.h>
16 #include <linux/in6.h>
17 #include <linux/ip.h>
18 #include <linux/ipv6.h>
19 #include <linux/jhash.h>
20 #include <linux/slab.h>
21 #include <linux/list.h>
22 #include <linux/module.h>
23 #include <linux/random.h>
24 #include <linux/skbuff.h>
25 #include <linux/spinlock.h>
26 #include <linux/netfilter/nf_conntrack_tcp.h>
27 #include <linux/netfilter/x_tables.h>
33 
34 /* we will save the tuples of all connections we care about */
36  struct hlist_node node;
39 };
40 
42  struct hlist_head iphash[256];
44 };
45 
46 static u_int32_t connlimit_rnd __read_mostly;
47 
48 static inline unsigned int connlimit_iphash(__be32 addr)
49 {
50  return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF;
51 }
52 
53 static inline unsigned int
54 connlimit_iphash6(const union nf_inet_addr *addr,
55  const union nf_inet_addr *mask)
56 {
57  union nf_inet_addr res;
58  unsigned int i;
59 
60  for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
61  res.ip6[i] = addr->ip6[i] & mask->ip6[i];
62 
63  return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
64 }
65 
66 static inline bool already_closed(const struct nf_conn *conn)
67 {
68  if (nf_ct_protonum(conn) == IPPROTO_TCP)
69  return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT ||
70  conn->proto.tcp.state == TCP_CONNTRACK_CLOSE;
71  else
72  return 0;
73 }
74 
75 static inline unsigned int
76 same_source_net(const union nf_inet_addr *addr,
77  const union nf_inet_addr *mask,
78  const union nf_inet_addr *u3, u_int8_t family)
79 {
80  if (family == NFPROTO_IPV4) {
81  return (addr->ip & mask->ip) == (u3->ip & mask->ip);
82  } else {
83  union nf_inet_addr lh, rh;
84  unsigned int i;
85 
86  for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
87  lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
88  rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
89  }
90 
91  return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
92  }
93 }
94 
95 static int count_them(struct net *net,
96  struct xt_connlimit_data *data,
97  const struct nf_conntrack_tuple *tuple,
98  const union nf_inet_addr *addr,
99  const union nf_inet_addr *mask,
100  u_int8_t family)
101 {
102  const struct nf_conntrack_tuple_hash *found;
103  struct xt_connlimit_conn *conn;
104  struct hlist_node *pos, *n;
105  struct nf_conn *found_ct;
106  struct hlist_head *hash;
107  bool addit = true;
108  int matches = 0;
109 
110  if (family == NFPROTO_IPV6)
111  hash = &data->iphash[connlimit_iphash6(addr, mask)];
112  else
113  hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
114 
115  rcu_read_lock();
116 
117  /* check the saved connections */
118  hlist_for_each_entry_safe(conn, pos, n, hash, node) {
120  &conn->tuple);
121  found_ct = NULL;
122 
123  if (found != NULL)
124  found_ct = nf_ct_tuplehash_to_ctrack(found);
125 
126  if (found_ct != NULL &&
127  nf_ct_tuple_equal(&conn->tuple, tuple) &&
128  !already_closed(found_ct))
129  /*
130  * Just to be sure we have it only once in the list.
131  * We should not see tuples twice unless someone hooks
132  * this into a table without "-p tcp --syn".
133  */
134  addit = false;
135 
136  if (found == NULL) {
137  /* this one is gone */
138  hlist_del(&conn->node);
139  kfree(conn);
140  continue;
141  }
142 
143  if (already_closed(found_ct)) {
144  /*
145  * we do not care about connections which are
146  * closed already -> ditch it
147  */
148  nf_ct_put(found_ct);
149  hlist_del(&conn->node);
150  kfree(conn);
151  continue;
152  }
153 
154  if (same_source_net(addr, mask, &conn->addr, family))
155  /* same source network -> be counted! */
156  ++matches;
157  nf_ct_put(found_ct);
158  }
159 
160  rcu_read_unlock();
161 
162  if (addit) {
163  /* save the new connection in our list */
164  conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
165  if (conn == NULL)
166  return -ENOMEM;
167  conn->tuple = *tuple;
168  conn->addr = *addr;
169  hlist_add_head(&conn->node, hash);
170  ++matches;
171  }
172 
173  return matches;
174 }
175 
176 static bool
177 connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
178 {
179  struct net *net = dev_net(par->in ? par->in : par->out);
180  const struct xt_connlimit_info *info = par->matchinfo;
181  union nf_inet_addr addr;
182  struct nf_conntrack_tuple tuple;
183  const struct nf_conntrack_tuple *tuple_ptr = &tuple;
184  enum ip_conntrack_info ctinfo;
185  const struct nf_conn *ct;
186  int connections;
187 
188  ct = nf_ct_get(skb, &ctinfo);
189  if (ct != NULL)
190  tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
191  else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
192  par->family, &tuple))
193  goto hotdrop;
194 
195  if (par->family == NFPROTO_IPV6) {
196  const struct ipv6hdr *iph = ipv6_hdr(skb);
197  memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ?
198  &iph->daddr : &iph->saddr, sizeof(addr.ip6));
199  } else {
200  const struct iphdr *iph = ip_hdr(skb);
201  addr.ip = (info->flags & XT_CONNLIMIT_DADDR) ?
202  iph->daddr : iph->saddr;
203  }
204 
205  spin_lock_bh(&info->data->lock);
206  connections = count_them(net, info->data, tuple_ptr, &addr,
207  &info->mask, par->family);
208  spin_unlock_bh(&info->data->lock);
209 
210  if (connections < 0)
211  /* kmalloc failed, drop it entirely */
212  goto hotdrop;
213 
214  return (connections > info->limit) ^
215  !!(info->flags & XT_CONNLIMIT_INVERT);
216 
217  hotdrop:
218  par->hotdrop = true;
219  return false;
220 }
221 
222 static int connlimit_mt_check(const struct xt_mtchk_param *par)
223 {
224  struct xt_connlimit_info *info = par->matchinfo;
225  unsigned int i;
226  int ret;
227 
228  if (unlikely(!connlimit_rnd)) {
229  u_int32_t rand;
230 
231  do {
232  get_random_bytes(&rand, sizeof(rand));
233  } while (!rand);
234  cmpxchg(&connlimit_rnd, 0, rand);
235  }
237  if (ret < 0) {
238  pr_info("cannot load conntrack support for "
239  "address family %u\n", par->family);
240  return ret;
241  }
242 
243  /* init private data */
244  info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
245  if (info->data == NULL) {
247  return -ENOMEM;
248  }
249 
250  spin_lock_init(&info->data->lock);
251  for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
252  INIT_HLIST_HEAD(&info->data->iphash[i]);
253 
254  return 0;
255 }
256 
257 static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
258 {
259  const struct xt_connlimit_info *info = par->matchinfo;
260  struct xt_connlimit_conn *conn;
261  struct hlist_node *pos, *n;
262  struct hlist_head *hash = info->data->iphash;
263  unsigned int i;
264 
266 
267  for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
268  hlist_for_each_entry_safe(conn, pos, n, &hash[i], node) {
269  hlist_del(&conn->node);
270  kfree(conn);
271  }
272  }
273 
274  kfree(info->data);
275 }
276 
277 static struct xt_match connlimit_mt_reg __read_mostly = {
278  .name = "connlimit",
279  .revision = 1,
280  .family = NFPROTO_UNSPEC,
281  .checkentry = connlimit_mt_check,
282  .match = connlimit_mt,
283  .matchsize = sizeof(struct xt_connlimit_info),
284  .destroy = connlimit_mt_destroy,
285  .me = THIS_MODULE,
286 };
287 
288 static int __init connlimit_mt_init(void)
289 {
290  return xt_register_match(&connlimit_mt_reg);
291 }
292 
293 static void __exit connlimit_mt_exit(void)
294 {
295  xt_unregister_match(&connlimit_mt_reg);
296 }
297 
298 module_init(connlimit_mt_init);
299 module_exit(connlimit_mt_exit);
300 MODULE_AUTHOR("Jan Engelhardt <[email protected]>");
301 MODULE_DESCRIPTION("Xtables: Number of connections matching");
302 MODULE_LICENSE("GPL");
303 MODULE_ALIAS("ipt_connlimit");
304 MODULE_ALIAS("ip6t_connlimit");