Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.23-rc9 313 lines 8.2 kB view raw
1/* 2 * netfilter module to limit the number of parallel tcp 3 * connections per IP address. 4 * (c) 2000 Gerd Knorr <kraxel@bytesex.org> 5 * Nov 2002: Martin Bene <martin.bene@icomedias.com>: 6 * only ignore TIME_WAIT or gone connections 7 * Copyright © Jan Engelhardt <jengelh@gmx.de>, 2007 8 * 9 * based on ... 10 * 11 * Kernel module to match connection tracking information. 12 * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au). 13 */ 14#include <linux/in.h> 15#include <linux/in6.h> 16#include <linux/ip.h> 17#include <linux/ipv6.h> 18#include <linux/jhash.h> 19#include <linux/list.h> 20#include <linux/module.h> 21#include <linux/random.h> 22#include <linux/skbuff.h> 23#include <linux/spinlock.h> 24#include <linux/netfilter/nf_conntrack_tcp.h> 25#include <linux/netfilter/x_tables.h> 26#include <linux/netfilter/xt_connlimit.h> 27#include <net/netfilter/nf_conntrack.h> 28#include <net/netfilter/nf_conntrack_core.h> 29#include <net/netfilter/nf_conntrack_tuple.h> 30 31/* we will save the tuples of all connections we care about */ 32struct xt_connlimit_conn { 33 struct list_head list; 34 struct nf_conntrack_tuple tuple; 35}; 36 37struct xt_connlimit_data { 38 struct list_head iphash[256]; 39 spinlock_t lock; 40}; 41 42static u_int32_t connlimit_rnd; 43static bool connlimit_rnd_inited; 44 45static inline unsigned int connlimit_iphash(__be32 addr) 46{ 47 if (unlikely(!connlimit_rnd_inited)) { 48 get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd)); 49 connlimit_rnd_inited = true; 50 } 51 return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF; 52} 53 54static inline unsigned int 55connlimit_iphash6(const union nf_conntrack_address *addr, 56 const union nf_conntrack_address *mask) 57{ 58 union nf_conntrack_address res; 59 unsigned int i; 60 61 if (unlikely(!connlimit_rnd_inited)) { 62 get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd)); 63 connlimit_rnd_inited = true; 64 } 65 66 for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) 67 res.ip6[i] = addr->ip6[i] & mask->ip6[i]; 68 69 return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF; 70} 71 72static inline bool already_closed(const struct nf_conn *conn) 73{ 74 u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum; 75 76 if (proto == IPPROTO_TCP) 77 return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT; 78 else 79 return 0; 80} 81 82static inline unsigned int 83same_source_net(const union nf_conntrack_address *addr, 84 const union nf_conntrack_address *mask, 85 const union nf_conntrack_address *u3, unsigned int family) 86{ 87 if (family == AF_INET) { 88 return (addr->ip & mask->ip) == (u3->ip & mask->ip); 89 } else { 90 union nf_conntrack_address lh, rh; 91 unsigned int i; 92 93 for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) { 94 lh.ip6[i] = addr->ip6[i] & mask->ip6[i]; 95 rh.ip6[i] = u3->ip6[i] & mask->ip6[i]; 96 } 97 98 return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0; 99 } 100} 101 102static int count_them(struct xt_connlimit_data *data, 103 const struct nf_conntrack_tuple *tuple, 104 const union nf_conntrack_address *addr, 105 const union nf_conntrack_address *mask, 106 const struct xt_match *match) 107{ 108 struct nf_conntrack_tuple_hash *found; 109 struct xt_connlimit_conn *conn; 110 struct xt_connlimit_conn *tmp; 111 struct nf_conn *found_ct; 112 struct list_head *hash; 113 bool addit = true; 114 int matches = 0; 115 116 117 if (match->family == AF_INET6) 118 hash = &data->iphash[connlimit_iphash6(addr, mask)]; 119 else 120 hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)]; 121 122 read_lock_bh(&nf_conntrack_lock); 123 124 /* check the saved connections */ 125 list_for_each_entry_safe(conn, tmp, hash, list) { 126 found = __nf_conntrack_find(&conn->tuple, NULL); 127 found_ct = NULL; 128 129 if (found != NULL) 130 found_ct = nf_ct_tuplehash_to_ctrack(found); 131 132 if (found_ct != NULL && 133 nf_ct_tuple_equal(&conn->tuple, tuple) && 134 !already_closed(found_ct)) 135 /* 136 * Just to be sure we have it only once in the list. 137 * We should not see tuples twice unless someone hooks 138 * this into a table without "-p tcp --syn". 139 */ 140 addit = false; 141 142 if (found == NULL) { 143 /* this one is gone */ 144 list_del(&conn->list); 145 kfree(conn); 146 continue; 147 } 148 149 if (already_closed(found_ct)) { 150 /* 151 * we do not care about connections which are 152 * closed already -> ditch it 153 */ 154 list_del(&conn->list); 155 kfree(conn); 156 continue; 157 } 158 159 if (same_source_net(addr, mask, &conn->tuple.src.u3, 160 match->family)) 161 /* same source network -> be counted! */ 162 ++matches; 163 } 164 165 read_unlock_bh(&nf_conntrack_lock); 166 167 if (addit) { 168 /* save the new connection in our list */ 169 conn = kzalloc(sizeof(*conn), GFP_ATOMIC); 170 if (conn == NULL) 171 return -ENOMEM; 172 conn->tuple = *tuple; 173 list_add(&conn->list, hash); 174 ++matches; 175 } 176 177 return matches; 178} 179 180static bool connlimit_match(const struct sk_buff *skb, 181 const struct net_device *in, 182 const struct net_device *out, 183 const struct xt_match *match, 184 const void *matchinfo, int offset, 185 unsigned int protoff, bool *hotdrop) 186{ 187 const struct xt_connlimit_info *info = matchinfo; 188 union nf_conntrack_address addr, mask; 189 struct nf_conntrack_tuple tuple; 190 const struct nf_conntrack_tuple *tuple_ptr = &tuple; 191 enum ip_conntrack_info ctinfo; 192 const struct nf_conn *ct; 193 int connections; 194 195 ct = nf_ct_get(skb, &ctinfo); 196 if (ct != NULL) 197 tuple_ptr = &ct->tuplehash[0].tuple; 198 else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), 199 match->family, &tuple)) 200 goto hotdrop; 201 202 if (match->family == AF_INET6) { 203 const struct ipv6hdr *iph = ipv6_hdr(skb); 204 memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr)); 205 memcpy(&mask.ip6, info->v6_mask, sizeof(info->v6_mask)); 206 } else { 207 const struct iphdr *iph = ip_hdr(skb); 208 addr.ip = iph->saddr; 209 mask.ip = info->v4_mask; 210 } 211 212 spin_lock_bh(&info->data->lock); 213 connections = count_them(info->data, tuple_ptr, &addr, &mask, match); 214 spin_unlock_bh(&info->data->lock); 215 216 if (connections < 0) { 217 /* kmalloc failed, drop it entirely */ 218 *hotdrop = true; 219 return false; 220 } 221 222 return (connections > info->limit) ^ info->inverse; 223 224 hotdrop: 225 *hotdrop = true; 226 return false; 227} 228 229static bool connlimit_check(const char *tablename, const void *ip, 230 const struct xt_match *match, void *matchinfo, 231 unsigned int hook_mask) 232{ 233 struct xt_connlimit_info *info = matchinfo; 234 unsigned int i; 235 236 if (nf_ct_l3proto_try_module_get(match->family) < 0) { 237 printk(KERN_WARNING "cannot load conntrack support for " 238 "address family %u\n", match->family); 239 return false; 240 } 241 242 /* init private data */ 243 info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL); 244 if (info->data == NULL) { 245 nf_ct_l3proto_module_put(match->family); 246 return false; 247 } 248 249 spin_lock_init(&info->data->lock); 250 for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) 251 INIT_LIST_HEAD(&info->data->iphash[i]); 252 253 return true; 254} 255 256static void connlimit_destroy(const struct xt_match *match, void *matchinfo) 257{ 258 struct xt_connlimit_info *info = matchinfo; 259 struct xt_connlimit_conn *conn; 260 struct xt_connlimit_conn *tmp; 261 struct list_head *hash = info->data->iphash; 262 unsigned int i; 263 264 nf_ct_l3proto_module_put(match->family); 265 266 for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) { 267 list_for_each_entry_safe(conn, tmp, &hash[i], list) { 268 list_del(&conn->list); 269 kfree(conn); 270 } 271 } 272 273 kfree(info->data); 274} 275 276static struct xt_match connlimit_reg[] __read_mostly = { 277 { 278 .name = "connlimit", 279 .family = AF_INET, 280 .checkentry = connlimit_check, 281 .match = connlimit_match, 282 .matchsize = sizeof(struct xt_connlimit_info), 283 .destroy = connlimit_destroy, 284 .me = THIS_MODULE, 285 }, 286 { 287 .name = "connlimit", 288 .family = AF_INET6, 289 .checkentry = connlimit_check, 290 .match = connlimit_match, 291 .matchsize = sizeof(struct xt_connlimit_info), 292 .destroy = connlimit_destroy, 293 .me = THIS_MODULE, 294 }, 295}; 296 297static int __init xt_connlimit_init(void) 298{ 299 return xt_register_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg)); 300} 301 302static void __exit xt_connlimit_exit(void) 303{ 304 xt_unregister_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg)); 305} 306 307module_init(xt_connlimit_init); 308module_exit(xt_connlimit_exit); 309MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>"); 310MODULE_DESCRIPTION("netfilter xt_connlimit match module"); 311MODULE_LICENSE("GPL"); 312MODULE_ALIAS("ipt_connlimit"); 313MODULE_ALIAS("ip6t_connlimit");