Blame view

net/netfilter/nf_conntrack_extend.c 4.86 KB
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /* Structure dynamic extension infrastructure
   * Copyright (C) 2004 Rusty Russell IBM Corporation
   * Copyright (C) 2007 Netfilter Core Team <coreteam@netfilter.org>
   * Copyright (C) 2007 USAGI/WIDE Project <http://www.linux-ipv6.org>
   *
   *      This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   */
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/rcupdate.h>
  #include <linux/slab.h>
  #include <linux/skbuff.h>
  #include <net/netfilter/nf_conntrack_extend.h>
0906a372f   Arnd Bergmann   net/netfilter: __...
18
  static struct nf_ct_ext_type __rcu *nf_ct_ext_types[NF_CT_EXT_NUM];
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
19
  static DEFINE_MUTEX(nf_ct_ext_type_mutex);
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
20
21
22
23
  void __nf_ct_ext_destroy(struct nf_conn *ct)
  {
  	unsigned int i;
  	struct nf_ct_ext_type *t;
ee92d3786   Changli Gao   netfilter: nf_con...
24
  	struct nf_ct_ext *ext = ct->ext;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
25
26
  
  	for (i = 0; i < NF_CT_EXT_NUM; i++) {
ee92d3786   Changli Gao   netfilter: nf_con...
27
  		if (!__nf_ct_ext_exist(ext, i))
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  			continue;
  
  		rcu_read_lock();
  		t = rcu_dereference(nf_ct_ext_types[i]);
  
  		/* Here the nf_ct_ext_type might have been unregisterd.
  		 * I.e., it has responsible to cleanup private
  		 * area in all conntracks when it is unregisterd.
  		 */
  		if (t && t->destroy)
  			t->destroy(ct);
  		rcu_read_unlock();
  	}
  }
  EXPORT_SYMBOL(__nf_ct_ext_destroy);
  
  static void *
  nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp)
  {
019f692ea   Pekka Enberg   [NETFILTER]: nf_c...
47
  	unsigned int off, len;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
48
  	struct nf_ct_ext_type *t;
15cdeadaa   Eric Dumazet   netfilter: fix a ...
49
  	size_t alloc_size;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
50
51
52
53
54
55
  
  	rcu_read_lock();
  	t = rcu_dereference(nf_ct_ext_types[id]);
  	BUG_ON(t == NULL);
  	off = ALIGN(sizeof(struct nf_ct_ext), t->align);
  	len = off + t->len;
15cdeadaa   Eric Dumazet   netfilter: fix a ...
56
  	alloc_size = t->alloc_size;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
57
  	rcu_read_unlock();
15cdeadaa   Eric Dumazet   netfilter: fix a ...
58
  	*ext = kzalloc(alloc_size, gfp);
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
59
60
61
62
63
  	if (!*ext)
  		return NULL;
  
  	(*ext)->offset[id] = off;
  	(*ext)->len = len;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
64
65
66
  
  	return (void *)(*ext) + off;
  }
68b80f113   Patrick McHardy   netfilter: nf_nat...
67
68
69
70
71
  static void __nf_ct_ext_free_rcu(struct rcu_head *head)
  {
  	struct nf_ct_ext *ext = container_of(head, struct nf_ct_ext, rcu);
  	kfree(ext);
  }
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
72
73
  void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
  {
ee92d3786   Changli Gao   netfilter: nf_con...
74
  	struct nf_ct_ext *old, *new;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
75
76
  	int i, newlen, newoff;
  	struct nf_ct_ext_type *t;
55871d047   Patrick McHardy   [NETFILTER]: nf_c...
77
78
  	/* Conntrack must not be confirmed to avoid races on reallocation. */
  	NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
ee92d3786   Changli Gao   netfilter: nf_con...
79
80
  	old = ct->ext;
  	if (!old)
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
81
  		return nf_ct_ext_create(&ct->ext, id, gfp);
ee92d3786   Changli Gao   netfilter: nf_con...
82
  	if (__nf_ct_ext_exist(old, id))
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
83
84
85
86
87
  		return NULL;
  
  	rcu_read_lock();
  	t = rcu_dereference(nf_ct_ext_types[id]);
  	BUG_ON(t == NULL);
ee92d3786   Changli Gao   netfilter: nf_con...
88
  	newoff = ALIGN(old->len, t->align);
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
89
90
  	newlen = newoff + t->len;
  	rcu_read_unlock();
ee92d3786   Changli Gao   netfilter: nf_con...
91
  	new = __krealloc(old, newlen, gfp);
31d8519c9   Pekka Enberg   netfilter: nf_con...
92
93
  	if (!new)
  		return NULL;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
94

ee92d3786   Changli Gao   netfilter: nf_con...
95
  	if (new != old) {
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
96
  		for (i = 0; i < NF_CT_EXT_NUM; i++) {
ee92d3786   Changli Gao   netfilter: nf_con...
97
  			if (!__nf_ct_ext_exist(old, i))
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
98
99
100
101
102
  				continue;
  
  			rcu_read_lock();
  			t = rcu_dereference(nf_ct_ext_types[i]);
  			if (t && t->move)
86577c661   Patrick McHardy   [NETFILTER]: nf_c...
103
  				t->move((void *)new + new->offset[i],
ee92d3786   Changli Gao   netfilter: nf_con...
104
  					(void *)old + old->offset[i]);
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
105
106
  			rcu_read_unlock();
  		}
ee92d3786   Changli Gao   netfilter: nf_con...
107
  		call_rcu(&old->rcu, __nf_ct_ext_free_rcu);
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
108
109
  		ct->ext = new;
  	}
6c64825bf   Patrick McHardy   netfilter: nf_con...
110
111
112
113
  	new->offset[id] = newoff;
  	new->len = newlen;
  	memset((void *)new + newoff, 0, newlen - newoff);
  	return (void *)new + newoff;
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  }
  EXPORT_SYMBOL(__nf_ct_ext_add);
  
  static void update_alloc_size(struct nf_ct_ext_type *type)
  {
  	int i, j;
  	struct nf_ct_ext_type *t1, *t2;
  	enum nf_ct_ext_id min = 0, max = NF_CT_EXT_NUM - 1;
  
  	/* unnecessary to update all types */
  	if ((type->flags & NF_CT_EXT_F_PREALLOC) == 0) {
  		min = type->id;
  		max = type->id;
  	}
  
  	/* This assumes that extended areas in conntrack for the types
  	   whose NF_CT_EXT_F_PREALLOC bit set are allocated in order */
  	for (i = min; i <= max; i++) {
  		t1 = nf_ct_ext_types[i];
  		if (!t1)
  			continue;
  
  		t1->alloc_size = sizeof(struct nf_ct_ext)
  				 + ALIGN(sizeof(struct nf_ct_ext), t1->align)
  				 + t1->len;
  		for (j = 0; j < NF_CT_EXT_NUM; j++) {
  			t2 = nf_ct_ext_types[j];
  			if (t2 == NULL || t2 == t1 ||
  			    (t2->flags & NF_CT_EXT_F_PREALLOC) == 0)
  				continue;
  
  			t1->alloc_size = ALIGN(t1->alloc_size, t2->align)
  					 + t2->len;
  		}
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  	}
  }
  
  /* This MUST be called in process context. */
  int nf_ct_extend_register(struct nf_ct_ext_type *type)
  {
  	int ret = 0;
  
  	mutex_lock(&nf_ct_ext_type_mutex);
  	if (nf_ct_ext_types[type->id]) {
  		ret = -EBUSY;
  		goto out;
  	}
  
  	/* This ensures that nf_ct_ext_create() can allocate enough area
  	   before updating alloc_size */
  	type->alloc_size = ALIGN(sizeof(struct nf_ct_ext), type->align)
  			   + type->len;
  	rcu_assign_pointer(nf_ct_ext_types[type->id], type);
  	update_alloc_size(type);
  out:
  	mutex_unlock(&nf_ct_ext_type_mutex);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(nf_ct_extend_register);
  
  /* This MUST be called in process context. */
  void nf_ct_extend_unregister(struct nf_ct_ext_type *type)
  {
  	mutex_lock(&nf_ct_ext_type_mutex);
  	rcu_assign_pointer(nf_ct_ext_types[type->id], NULL);
  	update_alloc_size(type);
  	mutex_unlock(&nf_ct_ext_type_mutex);
308ff823e   Jesper Dangaard Brouer   nf_conntrack: Use...
181
  	rcu_barrier(); /* Wait for completion of call_rcu()'s */
ecfab2c9f   Yasuyuki Kozakai   [NETFILTER]: nf_c...
182
183
  }
  EXPORT_SYMBOL_GPL(nf_ct_extend_unregister);