diff options
author | Huw Davies <huw@codeweavers.com> | 2016-06-27 15:06:17 -0400 |
---|---|---|
committer | Paul Moore <paul@paul-moore.com> | 2016-06-27 15:06:17 -0400 |
commit | 4fee5242bf41d9ad641d4c1b821e36eb7ba37fbf (patch) | |
tree | 6b79290fc0dbeffe30945235ca86576b652c84dd /net/ipv6 | |
parent | 2e532b702834c07f614caf4489feb691e713232a (diff) |
calipso: Add a label cache.
This works in exactly the same way as the CIPSO label cache.
The idea is to allow the lsm to cache the result of a secattr
lookup so that it doesn't need to perform the lookup for
every skbuff.
It introduces two sysctl controls:
calipso_cache_enable - enables/disables the cache.
calipso_cache_bucket_size - sets the size of a cache bucket.
Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/calipso.c | 264 | ||||
-rw-r--r-- | net/ipv6/sysctl_net_ipv6.c | 19 |
2 files changed, 281 insertions, 2 deletions
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index ea80450efe56..c53b92c617c5 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c | |||
@@ -72,6 +72,255 @@ | |||
72 | static DEFINE_SPINLOCK(calipso_doi_list_lock); | 72 | static DEFINE_SPINLOCK(calipso_doi_list_lock); |
73 | static LIST_HEAD(calipso_doi_list); | 73 | static LIST_HEAD(calipso_doi_list); |
74 | 74 | ||
75 | /* Label mapping cache */ | ||
76 | int calipso_cache_enabled = 1; | ||
77 | int calipso_cache_bucketsize = 10; | ||
78 | #define CALIPSO_CACHE_BUCKETBITS 7 | ||
79 | #define CALIPSO_CACHE_BUCKETS BIT(CALIPSO_CACHE_BUCKETBITS) | ||
80 | #define CALIPSO_CACHE_REORDERLIMIT 10 | ||
81 | struct calipso_map_cache_bkt { | ||
82 | spinlock_t lock; | ||
83 | u32 size; | ||
84 | struct list_head list; | ||
85 | }; | ||
86 | |||
87 | struct calipso_map_cache_entry { | ||
88 | u32 hash; | ||
89 | unsigned char *key; | ||
90 | size_t key_len; | ||
91 | |||
92 | struct netlbl_lsm_cache *lsm_data; | ||
93 | |||
94 | u32 activity; | ||
95 | struct list_head list; | ||
96 | }; | ||
97 | |||
98 | static struct calipso_map_cache_bkt *calipso_cache; | ||
99 | |||
100 | /* Label Mapping Cache Functions | ||
101 | */ | ||
102 | |||
103 | /** | ||
104 | * calipso_cache_entry_free - Frees a cache entry | ||
105 | * @entry: the entry to free | ||
106 | * | ||
107 | * Description: | ||
108 | * This function frees the memory associated with a cache entry including the | ||
109 | * LSM cache data if there are no longer any users, i.e. reference count == 0. | ||
110 | * | ||
111 | */ | ||
112 | static void calipso_cache_entry_free(struct calipso_map_cache_entry *entry) | ||
113 | { | ||
114 | if (entry->lsm_data) | ||
115 | netlbl_secattr_cache_free(entry->lsm_data); | ||
116 | kfree(entry->key); | ||
117 | kfree(entry); | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * calipso_map_cache_hash - Hashing function for the CALIPSO cache | ||
122 | * @key: the hash key | ||
123 | * @key_len: the length of the key in bytes | ||
124 | * | ||
125 | * Description: | ||
126 | * The CALIPSO tag hashing function. Returns a 32-bit hash value. | ||
127 | * | ||
128 | */ | ||
129 | static u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len) | ||
130 | { | ||
131 | return jhash(key, key_len, 0); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * calipso_cache_init - Initialize the CALIPSO cache | ||
136 | * | ||
137 | * Description: | ||
138 | * Initializes the CALIPSO label mapping cache, this function should be called | ||
139 | * before any of the other functions defined in this file. Returns zero on | ||
140 | * success, negative values on error. | ||
141 | * | ||
142 | */ | ||
143 | static int __init calipso_cache_init(void) | ||
144 | { | ||
145 | u32 iter; | ||
146 | |||
147 | calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS, | ||
148 | sizeof(struct calipso_map_cache_bkt), | ||
149 | GFP_KERNEL); | ||
150 | if (!calipso_cache) | ||
151 | return -ENOMEM; | ||
152 | |||
153 | for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { | ||
154 | spin_lock_init(&calipso_cache[iter].lock); | ||
155 | calipso_cache[iter].size = 0; | ||
156 | INIT_LIST_HEAD(&calipso_cache[iter].list); | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /** | ||
163 | * calipso_cache_invalidate - Invalidates the current CALIPSO cache | ||
164 | * | ||
165 | * Description: | ||
166 | * Invalidates and frees any entries in the CALIPSO cache. Returns zero on | ||
167 | * success and negative values on failure. | ||
168 | * | ||
169 | */ | ||
170 | static void calipso_cache_invalidate(void) | ||
171 | { | ||
172 | struct calipso_map_cache_entry *entry, *tmp_entry; | ||
173 | u32 iter; | ||
174 | |||
175 | for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { | ||
176 | spin_lock_bh(&calipso_cache[iter].lock); | ||
177 | list_for_each_entry_safe(entry, | ||
178 | tmp_entry, | ||
179 | &calipso_cache[iter].list, list) { | ||
180 | list_del(&entry->list); | ||
181 | calipso_cache_entry_free(entry); | ||
182 | } | ||
183 | calipso_cache[iter].size = 0; | ||
184 | spin_unlock_bh(&calipso_cache[iter].lock); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * calipso_cache_check - Check the CALIPSO cache for a label mapping | ||
190 | * @key: the buffer to check | ||
191 | * @key_len: buffer length in bytes | ||
192 | * @secattr: the security attribute struct to use | ||
193 | * | ||
194 | * Description: | ||
195 | * This function checks the cache to see if a label mapping already exists for | ||
196 | * the given key. If there is a match then the cache is adjusted and the | ||
197 | * @secattr struct is populated with the correct LSM security attributes. The | ||
198 | * cache is adjusted in the following manner if the entry is not already the | ||
199 | * first in the cache bucket: | ||
200 | * | ||
201 | * 1. The cache entry's activity counter is incremented | ||
202 | * 2. The previous (higher ranking) entry's activity counter is decremented | ||
203 | * 3. If the difference between the two activity counters is geater than | ||
204 | * CALIPSO_CACHE_REORDERLIMIT the two entries are swapped | ||
205 | * | ||
206 | * Returns zero on success, -ENOENT for a cache miss, and other negative values | ||
207 | * on error. | ||
208 | * | ||
209 | */ | ||
210 | static int calipso_cache_check(const unsigned char *key, | ||
211 | u32 key_len, | ||
212 | struct netlbl_lsm_secattr *secattr) | ||
213 | { | ||
214 | u32 bkt; | ||
215 | struct calipso_map_cache_entry *entry; | ||
216 | struct calipso_map_cache_entry *prev_entry = NULL; | ||
217 | u32 hash; | ||
218 | |||
219 | if (!calipso_cache_enabled) | ||
220 | return -ENOENT; | ||
221 | |||
222 | hash = calipso_map_cache_hash(key, key_len); | ||
223 | bkt = hash & (CALIPSO_CACHE_BUCKETS - 1); | ||
224 | spin_lock_bh(&calipso_cache[bkt].lock); | ||
225 | list_for_each_entry(entry, &calipso_cache[bkt].list, list) { | ||
226 | if (entry->hash == hash && | ||
227 | entry->key_len == key_len && | ||
228 | memcmp(entry->key, key, key_len) == 0) { | ||
229 | entry->activity += 1; | ||
230 | atomic_inc(&entry->lsm_data->refcount); | ||
231 | secattr->cache = entry->lsm_data; | ||
232 | secattr->flags |= NETLBL_SECATTR_CACHE; | ||
233 | secattr->type = NETLBL_NLTYPE_CALIPSO; | ||
234 | if (!prev_entry) { | ||
235 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | if (prev_entry->activity > 0) | ||
240 | prev_entry->activity -= 1; | ||
241 | if (entry->activity > prev_entry->activity && | ||
242 | entry->activity - prev_entry->activity > | ||
243 | CALIPSO_CACHE_REORDERLIMIT) { | ||
244 | __list_del(entry->list.prev, entry->list.next); | ||
245 | __list_add(&entry->list, | ||
246 | prev_entry->list.prev, | ||
247 | &prev_entry->list); | ||
248 | } | ||
249 | |||
250 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
251 | return 0; | ||
252 | } | ||
253 | prev_entry = entry; | ||
254 | } | ||
255 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
256 | |||
257 | return -ENOENT; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * calipso_cache_add - Add an entry to the CALIPSO cache | ||
262 | * @calipso_ptr: the CALIPSO option | ||
263 | * @secattr: the packet's security attributes | ||
264 | * | ||
265 | * Description: | ||
266 | * Add a new entry into the CALIPSO label mapping cache. Add the new entry to | ||
267 | * head of the cache bucket's list, if the cache bucket is out of room remove | ||
268 | * the last entry in the list first. It is important to note that there is | ||
269 | * currently no checking for duplicate keys. Returns zero on success, | ||
270 | * negative values on failure. The key stored starts at calipso_ptr + 2, | ||
271 | * i.e. the type and length bytes are not stored, this corresponds to | ||
272 | * calipso_ptr[1] bytes of data. | ||
273 | * | ||
274 | */ | ||
275 | static int calipso_cache_add(const unsigned char *calipso_ptr, | ||
276 | const struct netlbl_lsm_secattr *secattr) | ||
277 | { | ||
278 | int ret_val = -EPERM; | ||
279 | u32 bkt; | ||
280 | struct calipso_map_cache_entry *entry = NULL; | ||
281 | struct calipso_map_cache_entry *old_entry = NULL; | ||
282 | u32 calipso_ptr_len; | ||
283 | |||
284 | if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0) | ||
285 | return 0; | ||
286 | |||
287 | calipso_ptr_len = calipso_ptr[1]; | ||
288 | |||
289 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
290 | if (!entry) | ||
291 | return -ENOMEM; | ||
292 | entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC); | ||
293 | if (!entry->key) { | ||
294 | ret_val = -ENOMEM; | ||
295 | goto cache_add_failure; | ||
296 | } | ||
297 | entry->key_len = calipso_ptr_len; | ||
298 | entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len); | ||
299 | atomic_inc(&secattr->cache->refcount); | ||
300 | entry->lsm_data = secattr->cache; | ||
301 | |||
302 | bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1); | ||
303 | spin_lock_bh(&calipso_cache[bkt].lock); | ||
304 | if (calipso_cache[bkt].size < calipso_cache_bucketsize) { | ||
305 | list_add(&entry->list, &calipso_cache[bkt].list); | ||
306 | calipso_cache[bkt].size += 1; | ||
307 | } else { | ||
308 | old_entry = list_entry(calipso_cache[bkt].list.prev, | ||
309 | struct calipso_map_cache_entry, list); | ||
310 | list_del(&old_entry->list); | ||
311 | list_add(&entry->list, &calipso_cache[bkt].list); | ||
312 | calipso_cache_entry_free(old_entry); | ||
313 | } | ||
314 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
315 | |||
316 | return 0; | ||
317 | |||
318 | cache_add_failure: | ||
319 | if (entry) | ||
320 | calipso_cache_entry_free(entry); | ||
321 | return ret_val; | ||
322 | } | ||
323 | |||
75 | /* DOI List Functions | 324 | /* DOI List Functions |
76 | */ | 325 | */ |
77 | 326 | ||
@@ -789,6 +1038,9 @@ static int calipso_opt_getattr(const unsigned char *calipso, | |||
789 | if (cat_len + 8 > len) | 1038 | if (cat_len + 8 > len) |
790 | return -EINVAL; | 1039 | return -EINVAL; |
791 | 1040 | ||
1041 | if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0) | ||
1042 | return 0; | ||
1043 | |||
792 | doi = get_unaligned_be32(calipso + 2); | 1044 | doi = get_unaligned_be32(calipso + 2); |
793 | rcu_read_lock(); | 1045 | rcu_read_lock(); |
794 | doi_def = calipso_doi_search(doi); | 1046 | doi_def = calipso_doi_search(doi); |
@@ -1191,6 +1443,8 @@ static const struct netlbl_calipso_ops ops = { | |||
1191 | .skbuff_optptr = calipso_skbuff_optptr, | 1443 | .skbuff_optptr = calipso_skbuff_optptr, |
1192 | .skbuff_setattr = calipso_skbuff_setattr, | 1444 | .skbuff_setattr = calipso_skbuff_setattr, |
1193 | .skbuff_delattr = calipso_skbuff_delattr, | 1445 | .skbuff_delattr = calipso_skbuff_delattr, |
1446 | .cache_invalidate = calipso_cache_invalidate, | ||
1447 | .cache_add = calipso_cache_add | ||
1194 | }; | 1448 | }; |
1195 | 1449 | ||
1196 | /** | 1450 | /** |
@@ -1203,11 +1457,17 @@ static const struct netlbl_calipso_ops ops = { | |||
1203 | */ | 1457 | */ |
1204 | int __init calipso_init(void) | 1458 | int __init calipso_init(void) |
1205 | { | 1459 | { |
1206 | netlbl_calipso_ops_register(&ops); | 1460 | int ret_val; |
1207 | return 0; | 1461 | |
1462 | ret_val = calipso_cache_init(); | ||
1463 | if (!ret_val) | ||
1464 | netlbl_calipso_ops_register(&ops); | ||
1465 | return ret_val; | ||
1208 | } | 1466 | } |
1209 | 1467 | ||
1210 | void calipso_exit(void) | 1468 | void calipso_exit(void) |
1211 | { | 1469 | { |
1212 | netlbl_calipso_ops_register(NULL); | 1470 | netlbl_calipso_ops_register(NULL); |
1471 | calipso_cache_invalidate(); | ||
1472 | kfree(calipso_cache); | ||
1213 | } | 1473 | } |
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 45243bbe5253..69c50e737c54 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c | |||
@@ -15,6 +15,9 @@ | |||
15 | #include <net/ipv6.h> | 15 | #include <net/ipv6.h> |
16 | #include <net/addrconf.h> | 16 | #include <net/addrconf.h> |
17 | #include <net/inet_frag.h> | 17 | #include <net/inet_frag.h> |
18 | #ifdef CONFIG_NETLABEL | ||
19 | #include <net/calipso.h> | ||
20 | #endif | ||
18 | 21 | ||
19 | static int one = 1; | 22 | static int one = 1; |
20 | static int auto_flowlabels_min; | 23 | static int auto_flowlabels_min; |
@@ -106,6 +109,22 @@ static struct ctl_table ipv6_rotable[] = { | |||
106 | .proc_handler = proc_dointvec_minmax, | 109 | .proc_handler = proc_dointvec_minmax, |
107 | .extra1 = &one | 110 | .extra1 = &one |
108 | }, | 111 | }, |
112 | #ifdef CONFIG_NETLABEL | ||
113 | { | ||
114 | .procname = "calipso_cache_enable", | ||
115 | .data = &calipso_cache_enabled, | ||
116 | .maxlen = sizeof(int), | ||
117 | .mode = 0644, | ||
118 | .proc_handler = proc_dointvec, | ||
119 | }, | ||
120 | { | ||
121 | .procname = "calipso_cache_bucket_size", | ||
122 | .data = &calipso_cache_bucketsize, | ||
123 | .maxlen = sizeof(int), | ||
124 | .mode = 0644, | ||
125 | .proc_handler = proc_dointvec, | ||
126 | }, | ||
127 | #endif /* CONFIG_NETLABEL */ | ||
109 | { } | 128 | { } |
110 | }; | 129 | }; |
111 | 130 | ||