2262402d52f7f87fd31cc519d85ec7e80bdcdc0f
[vpp.git] / src / plugins / acl / hash_lookup.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <stddef.h>
19 #include <netinet/in.h>
20
21 #include <vlibapi/api.h>
22 #include <vlibmemory/api.h>
23
24 #include <vlib/vlib.h>
25 #include <vnet/vnet.h>
26 #include <vnet/pg/pg.h>
27 #include <vppinfra/error.h>
28 #include <vnet/plugin/plugin.h>
29 #include <acl/acl.h>
30 #include <vppinfra/bihash_48_8.h>
31
32 #include "hash_lookup.h"
33 #include "hash_lookup_private.h"
34
35
36 static inline applied_hash_ace_entry_t **get_applied_hash_aces(acl_main_t *am, int is_input, u32 sw_if_index)
37 {
38   applied_hash_ace_entry_t **applied_hash_aces = is_input ? vec_elt_at_index(am->input_hash_entry_vec_by_sw_if_index, sw_if_index)
39                                                           : vec_elt_at_index(am->output_hash_entry_vec_by_sw_if_index, sw_if_index);
40   return applied_hash_aces;
41 }
42
43
44
45 /*
46  * This returns true if there is indeed a match on the portranges.
47  * With all these levels of indirections, this is not going to be very fast,
48  * so, best use the individual ports or wildcard ports for performance.
49  */
50 static int
51 match_portranges(acl_main_t *am, fa_5tuple_t *match, u32 index)
52 {
53
54   applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, match->pkt.is_input, match->pkt.sw_if_index);
55   applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), index);
56
57   acl_rule_t *r = &(am->acls[pae->acl_index].rules[pae->ace_index]);
58   DBG("PORTMATCH: %d <= %d <= %d && %d <= %d <= %d ?",
59                 r->src_port_or_type_first, match->l4.port[0], r->src_port_or_type_last,
60                 r->dst_port_or_code_first, match->l4.port[1], r->dst_port_or_code_last);
61
62   return ( ((r->src_port_or_type_first <= match->l4.port[0]) && r->src_port_or_type_last >= match->l4.port[0]) &&
63            ((r->dst_port_or_code_first <= match->l4.port[1]) && r->dst_port_or_code_last >= match->l4.port[1]) );
64 }
65
66 static u32
67 multi_acl_match_get_applied_ace_index(acl_main_t *am, fa_5tuple_t *match)
68 {
69   clib_bihash_kv_48_8_t kv;
70   clib_bihash_kv_48_8_t result;
71   fa_5tuple_t *kv_key = (fa_5tuple_t *)kv.key;
72   hash_acl_lookup_value_t *result_val = (hash_acl_lookup_value_t *)&result.value;
73   u64 *pmatch = (u64 *)match;
74   u64 *pmask;
75   u64 *pkey;
76   int mask_type_index;
77   u32 curr_match_index = ~0;
78
79   u32 sw_if_index = match->pkt.sw_if_index;
80   u8 is_input = match->pkt.is_input;
81   applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index);
82   applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index :
83                                                     &am->output_applied_hash_acl_info_by_sw_if_index;
84
85   DBG("TRYING TO MATCH: %016llx %016llx %016llx %016llx %016llx %016llx",
86                pmatch[0], pmatch[1], pmatch[2], pmatch[3], pmatch[4], pmatch[5]);
87
88   for(mask_type_index=0; mask_type_index < pool_len(am->ace_mask_type_pool); mask_type_index++) {
89     if (!clib_bitmap_get(vec_elt_at_index((*applied_hash_acls), sw_if_index)->mask_type_index_bitmap, mask_type_index)) {
90       /* This bit is not set. Avoid trying to match */
91       continue;
92     }
93     ace_mask_type_entry_t *mte = vec_elt_at_index(am->ace_mask_type_pool, mask_type_index);
94     pmatch = (u64 *)match;
95     pmask = (u64 *)&mte->mask;
96     pkey = (u64 *)kv.key;
97     /*
98     * unrolling the below loop results in a noticeable performance increase.
99     int i;
100     for(i=0; i<6; i++) {
101       kv.key[i] = pmatch[i] & pmask[i];
102     }
103     */
104
105     *pkey++ = *pmatch++ & *pmask++;
106     *pkey++ = *pmatch++ & *pmask++;
107     *pkey++ = *pmatch++ & *pmask++;
108     *pkey++ = *pmatch++ & *pmask++;
109     *pkey++ = *pmatch++ & *pmask++;
110     *pkey++ = *pmatch++ & *pmask++;
111
112     kv_key->pkt.mask_type_index_lsb = mask_type_index;
113     DBG("        KEY %3d: %016llx %016llx %016llx %016llx %016llx %016llx", mask_type_index,
114                 kv.key[0], kv.key[1], kv.key[2], kv.key[3], kv.key[4], kv.key[5]);
115     int res = BV (clib_bihash_search) (&am->acl_lookup_hash, &kv, &result);
116     if (res == 0) {
117       DBG("ACL-MATCH! result_val: %016llx", result_val->as_u64);
118       if (result_val->applied_entry_index < curr_match_index) {
119         if (PREDICT_FALSE(result_val->need_portrange_check)) {
120           /*
121            * This is going to be slow, since we can have multiple superset
122            * entries for narrow-ish portranges, e.g.:
123            * 0..42 100..400, 230..60000,
124            * so we need to walk linearly and check if they match.
125            */
126
127           u32 curr_index = result_val->applied_entry_index;
128           while ((curr_index != ~0) && !match_portranges(am, match, curr_index)) {
129             /* while no match and there are more entries, walk... */
130             applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces),curr_index);
131             DBG("entry %d did not portmatch, advancing to %d", curr_index, pae->next_applied_entry_index);
132             curr_index = pae->next_applied_entry_index;
133           }
134           if (curr_index < curr_match_index) {
135             DBG("The index %d is the new candidate in portrange matches.", curr_index);
136             curr_match_index = curr_index;
137           } else {
138             DBG("Curr portmatch index %d is too big vs. current matched one %d", curr_index, curr_match_index);
139           }
140         } else {
141           /* The usual path is here. Found an entry in front of the current candiate - so it's a new one */
142           DBG("This match is the new candidate");
143           curr_match_index = result_val->applied_entry_index;
144           if (!result_val->shadowed) {
145           /* new result is known to not be shadowed, so no point to look up further */
146             break;
147           }
148         }
149       }
150     }
151   }
152   DBG("MATCH-RESULT: %d", curr_match_index);
153   return curr_match_index;
154 }
155
156 static void
157 hashtable_add_del(acl_main_t *am, clib_bihash_kv_48_8_t *kv, int is_add)
158 {
159     DBG("HASH ADD/DEL: %016llx %016llx %016llx %016llx %016llx %016llx %016llx add %d",
160                         kv->key[0], kv->key[1], kv->key[2],
161                         kv->key[3], kv->key[4], kv->key[5], kv->value, is_add);
162     BV (clib_bihash_add_del) (&am->acl_lookup_hash, kv, is_add);
163 }
164
165 static void
166 fill_applied_hash_ace_kv(acl_main_t *am,
167                             applied_hash_ace_entry_t **applied_hash_aces,
168                             u32 sw_if_index, u8 is_input,
169                             u32 new_index, clib_bihash_kv_48_8_t *kv)
170 {
171   fa_5tuple_t *kv_key = (fa_5tuple_t *)kv->key;
172   hash_acl_lookup_value_t *kv_val = (hash_acl_lookup_value_t *)&kv->value;
173   applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), new_index);
174   hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, pae->acl_index);
175
176   memcpy(kv_key, &(vec_elt_at_index(ha->rules, pae->hash_ace_info_index)->match), sizeof(*kv_key));
177   /* initialize the sw_if_index and direction */
178   kv_key->pkt.sw_if_index = sw_if_index;
179   kv_key->pkt.is_input = is_input;
180   kv_val->as_u64 = 0;
181   kv_val->applied_entry_index = new_index;
182   kv_val->need_portrange_check = vec_elt_at_index(ha->rules, pae->hash_ace_info_index)->src_portrange_not_powerof2 ||
183                                    vec_elt_at_index(ha->rules, pae->hash_ace_info_index)->dst_portrange_not_powerof2;
184   /* by default assume all values are shadowed -> check all mask types */
185   kv_val->shadowed = 1;
186 }
187
188 static void
189 add_del_hashtable_entry(acl_main_t *am,
190                             u32 sw_if_index, u8 is_input,
191                             applied_hash_ace_entry_t **applied_hash_aces,
192                             u32 index, int is_add)
193 {
194   clib_bihash_kv_48_8_t kv;
195
196   fill_applied_hash_ace_kv(am, applied_hash_aces, sw_if_index, is_input, index, &kv);
197   hashtable_add_del(am, &kv, is_add);
198 }
199
200
201
202 static void
203 activate_applied_ace_hash_entry(acl_main_t *am,
204                             u32 sw_if_index, u8 is_input,
205                             applied_hash_ace_entry_t **applied_hash_aces,
206                             u32 new_index)
207 {
208   clib_bihash_kv_48_8_t kv;
209   ASSERT(new_index != ~0);
210   applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), new_index);
211   DBG("activate_applied_ace_hash_entry sw_if_index %d is_input %d new_index %d", sw_if_index, is_input, new_index);
212
213   fill_applied_hash_ace_kv(am, applied_hash_aces, sw_if_index, is_input, new_index, &kv);
214
215   DBG("APPLY ADD KY: %016llx %016llx %016llx %016llx %016llx %016llx",
216                         kv.key[0], kv.key[1], kv.key[2],
217                         kv.key[3], kv.key[4], kv.key[5]);
218
219   clib_bihash_kv_48_8_t result;
220   hash_acl_lookup_value_t *result_val = (hash_acl_lookup_value_t *)&result.value;
221   int res = BV (clib_bihash_search) (&am->acl_lookup_hash, &kv, &result);
222   ASSERT(new_index != ~0);
223   ASSERT(new_index < vec_len((*applied_hash_aces)));
224   if (res == 0) {
225     /* There already exists an entry or more. Append at the end. */
226     u32 first_index = result_val->applied_entry_index;
227     ASSERT(first_index != ~0);
228     DBG("A key already exists, with applied entry index: %d", first_index);
229     applied_hash_ace_entry_t *first_pae = vec_elt_at_index((*applied_hash_aces), first_index);
230     u32 last_index = first_pae->tail_applied_entry_index;
231     ASSERT(last_index != ~0);
232     applied_hash_ace_entry_t *last_pae = vec_elt_at_index((*applied_hash_aces), last_index);
233     DBG("...advance to chained entry index: %d", last_index);
234     /* link ourseves in */
235     last_pae->next_applied_entry_index = new_index;
236     pae->prev_applied_entry_index = last_index;
237     /* adjust the pointer to the new tail */
238     first_pae->tail_applied_entry_index = new_index;
239   } else {
240     /* It's the very first entry */
241     hashtable_add_del(am, &kv, 1);
242     ASSERT(new_index != ~0);
243     pae->tail_applied_entry_index = new_index;
244   }
245 }
246
247 static void
248 applied_hash_entries_analyze(acl_main_t *am, applied_hash_ace_entry_t **applied_hash_aces)
249 {
250   /*
251    * Go over the rules and check which ones are shadowed and which aren't.
252    * Naive approach: try to match the match value from every ACE as if it
253    * was a live packet, and see if the resulting match happens earlier in the list.
254    * if it does not match or it is later in the ACL - then the entry is not shadowed.
255    *
256    * This approach fails, an example:
257    *   deny tcp 2001:db8::/32 2001:db8::/32
258    *   permit ip 2001:db8::1/128 2001:db8::2/128
259    */
260 }
261
262 static void *
263 hash_acl_set_heap(acl_main_t *am)
264 {
265   if (0 == am->hash_lookup_mheap) {
266     am->hash_lookup_mheap = mheap_alloc (0 /* use VM */ , am->hash_lookup_mheap_size);
267     mheap_t *h = mheap_header (am->hash_lookup_mheap);
268     h->flags |= MHEAP_FLAG_THREAD_SAFE;
269   }
270   void *oldheap = clib_mem_set_heap(am->hash_lookup_mheap);
271   return oldheap;
272 }
273
274 void
275 acl_plugin_hash_acl_set_validate_heap(acl_main_t *am, int on)
276 {
277   clib_mem_set_heap(hash_acl_set_heap(am));
278   mheap_t *h = mheap_header (am->hash_lookup_mheap);
279   if (on) {
280     h->flags |= MHEAP_FLAG_VALIDATE;
281     h->flags &= ~MHEAP_FLAG_SMALL_OBJECT_CACHE;
282     mheap_validate(h);
283   } else {
284     h->flags &= ~MHEAP_FLAG_VALIDATE;
285     h->flags |= MHEAP_FLAG_SMALL_OBJECT_CACHE;
286   }
287 }
288
289 void
290 acl_plugin_hash_acl_set_trace_heap(acl_main_t *am, int on)
291 {
292   clib_mem_set_heap(hash_acl_set_heap(am));
293   mheap_t *h = mheap_header (am->hash_lookup_mheap);
294   if (on) {
295     h->flags |= MHEAP_FLAG_TRACE;
296   } else {
297     h->flags &= ~MHEAP_FLAG_TRACE;
298   }
299 }
300
301 void
302 hash_acl_apply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index)
303 {
304   int i;
305
306   DBG0("HASH ACL apply: sw_if_index %d is_input %d acl %d", sw_if_index, is_input, acl_index);
307   if (!am->acl_lookup_hash_initialized) {
308     BV (clib_bihash_init) (&am->acl_lookup_hash, "ACL plugin rule lookup bihash",
309                            am->hash_lookup_hash_buckets, am->hash_lookup_hash_memory);
310     am->acl_lookup_hash_initialized = 1;
311   }
312
313   void *oldheap = hash_acl_set_heap(am);
314   if (is_input) {
315     vec_validate(am->input_hash_entry_vec_by_sw_if_index, sw_if_index);
316   } else {
317     vec_validate(am->output_hash_entry_vec_by_sw_if_index, sw_if_index);
318   }
319   vec_validate(am->hash_acl_infos, acl_index);
320   applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index);
321
322   hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index);
323   u32 **hash_acl_applied_sw_if_index = is_input ? &ha->inbound_sw_if_index_list
324                                                 : &ha->outbound_sw_if_index_list;
325
326   int base_offset = vec_len(*applied_hash_aces);
327
328   /* Update the bitmap of the mask types with which the lookup
329      needs to happen for the ACLs applied to this sw_if_index */
330   applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index :
331                                                     &am->output_applied_hash_acl_info_by_sw_if_index;
332   vec_validate((*applied_hash_acls), sw_if_index);
333   applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), sw_if_index);
334
335   /* ensure the list of applied hash acls is initialized and add this acl# to it */
336   u32 index = vec_search(pal->applied_acls, acl_index);
337   if (index != ~0) {
338     clib_warning("BUG: trying to apply twice acl_index %d on sw_if_index %d is_input %d",
339                  acl_index, sw_if_index, is_input);
340     goto done;
341   }
342   vec_add1(pal->applied_acls, acl_index);
343   u32 index2 = vec_search((*hash_acl_applied_sw_if_index), sw_if_index);
344   if (index2 != ~0) {
345     clib_warning("BUG: trying to apply twice acl_index %d on (sw_if_index %d) is_input %d",
346                  acl_index, sw_if_index, is_input);
347     goto done;
348   }
349   vec_add1((*hash_acl_applied_sw_if_index), sw_if_index);
350
351   pal->mask_type_index_bitmap = clib_bitmap_or(pal->mask_type_index_bitmap,
352                                      ha->mask_type_index_bitmap);
353   /*
354    * if the applied ACL is empty, the current code will cause a
355    * different behavior compared to current linear search: an empty ACL will
356    * simply fallthrough to the next ACL, or the default deny in the end.
357    *
358    * This is not a problem, because after vpp-dev discussion,
359    * the consensus was it should not be possible to apply the non-existent
360    * ACL, so the change adding this code also takes care of that.
361    */
362
363   /* expand the applied aces vector by the necessary amount */
364   vec_resize((*applied_hash_aces), vec_len(ha->rules));
365
366   /* add the rules from the ACL to the hash table for lookup and append to the vector*/
367   for(i=0; i < vec_len(ha->rules); i++) {
368     u32 new_index = base_offset + i;
369     applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), new_index);
370     pae->acl_index = acl_index;
371     pae->ace_index = ha->rules[i].ace_index;
372     pae->action = ha->rules[i].action;
373     pae->hitcount = 0;
374     pae->hash_ace_info_index = i;
375     /* we might link it in later */
376     pae->next_applied_entry_index = ~0;
377     pae->prev_applied_entry_index = ~0;
378     pae->tail_applied_entry_index = ~0;
379     activate_applied_ace_hash_entry(am, sw_if_index, is_input, applied_hash_aces, new_index);
380   }
381   applied_hash_entries_analyze(am, applied_hash_aces);
382 done:
383   clib_mem_set_heap (oldheap);
384 }
385
386 static u32
387 find_head_applied_ace_index(applied_hash_ace_entry_t **applied_hash_aces, u32 curr_index)
388 {
389   /*
390    * find back the first entry. Inefficient so might need to be a bit cleverer
391    * if this proves to be a problem..
392    */
393   u32 an_index = curr_index;
394   ASSERT(an_index != ~0);
395   applied_hash_ace_entry_t *head_pae = vec_elt_at_index((*applied_hash_aces), an_index);
396   while(head_pae->prev_applied_entry_index != ~0) {
397     an_index = head_pae->prev_applied_entry_index;
398     ASSERT(an_index != ~0);
399     head_pae = vec_elt_at_index((*applied_hash_aces), an_index);
400   }
401   return an_index;
402 }
403
404 static void
405 move_applied_ace_hash_entry(acl_main_t *am,
406                             u32 sw_if_index, u8 is_input,
407                             applied_hash_ace_entry_t **applied_hash_aces,
408                             u32 old_index, u32 new_index)
409 {
410   ASSERT(old_index != ~0);
411   ASSERT(new_index != ~0);
412   /* move the entry */
413   *vec_elt_at_index((*applied_hash_aces), new_index) = *vec_elt_at_index((*applied_hash_aces), old_index);
414
415   /* update the linkage and hash table if necessary */
416   applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), old_index);
417
418   if (pae->prev_applied_entry_index != ~0) {
419     applied_hash_ace_entry_t *prev_pae = vec_elt_at_index((*applied_hash_aces), pae->prev_applied_entry_index);
420     ASSERT(prev_pae->next_applied_entry_index == old_index);
421     prev_pae->next_applied_entry_index = new_index;
422   } else {
423     /* first entry - so the hash points to it, update */
424     add_del_hashtable_entry(am, sw_if_index, is_input,
425                             applied_hash_aces, new_index, 1);
426     ASSERT(pae->tail_applied_entry_index != ~0);
427   }
428   if (pae->next_applied_entry_index != ~0) {
429     applied_hash_ace_entry_t *next_pae = vec_elt_at_index((*applied_hash_aces), pae->next_applied_entry_index);
430     ASSERT(next_pae->prev_applied_entry_index == old_index);
431     next_pae->prev_applied_entry_index = new_index;
432   } else {
433     /*
434      * Moving the very last entry, so we need to update the tail pointer in the first one.
435      */
436     u32 head_index = find_head_applied_ace_index(applied_hash_aces, old_index);
437     ASSERT(head_index != ~0);
438     applied_hash_ace_entry_t *head_pae = vec_elt_at_index((*applied_hash_aces), head_index);
439
440     ASSERT(head_pae->tail_applied_entry_index == old_index);
441     head_pae->tail_applied_entry_index = new_index;
442   }
443   /* invalidate the old entry */
444   pae->prev_applied_entry_index = ~0;
445   pae->next_applied_entry_index = ~0;
446   pae->tail_applied_entry_index = ~0;
447 }
448
449 static void
450 deactivate_applied_ace_hash_entry(acl_main_t *am,
451                             u32 sw_if_index, u8 is_input,
452                             applied_hash_ace_entry_t **applied_hash_aces,
453                             u32 old_index)
454 {
455   applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), old_index);
456   DBG("UNAPPLY DEACTIVATE: sw_if_index %d is_input %d, applied index %d", sw_if_index, is_input, old_index);
457
458   if (pae->prev_applied_entry_index != ~0) {
459     DBG("UNAPPLY = index %d has prev_applied_entry_index %d", old_index, pae->prev_applied_entry_index);
460     applied_hash_ace_entry_t *prev_pae = vec_elt_at_index((*applied_hash_aces), pae->prev_applied_entry_index);
461     ASSERT(prev_pae->next_applied_entry_index == old_index);
462     prev_pae->next_applied_entry_index = pae->next_applied_entry_index;
463     if (pae->next_applied_entry_index == ~0) {
464       /* it was a last entry we removed, update the pointer on the first one */
465       u32 head_index = find_head_applied_ace_index(applied_hash_aces, old_index);
466       DBG("UNAPPLY = index %d head index to update %d", old_index, head_index);
467       ASSERT(head_index != ~0);
468       applied_hash_ace_entry_t *head_pae = vec_elt_at_index((*applied_hash_aces), head_index);
469
470       ASSERT(head_pae->tail_applied_entry_index == old_index);
471       head_pae->tail_applied_entry_index = pae->prev_applied_entry_index;
472     } else {
473       applied_hash_ace_entry_t *next_pae = vec_elt_at_index((*applied_hash_aces), pae->next_applied_entry_index);
474       next_pae->prev_applied_entry_index = pae->prev_applied_entry_index;
475     }
476   } else {
477     /* It was the first entry. We need either to reset the hash entry or delete it */
478     if (pae->next_applied_entry_index != ~0) {
479       /* the next element becomes the new first one, so needs the tail pointer to be set */
480       applied_hash_ace_entry_t *next_pae = vec_elt_at_index((*applied_hash_aces), pae->next_applied_entry_index);
481       ASSERT(pae->tail_applied_entry_index != ~0);
482       next_pae->tail_applied_entry_index = pae->tail_applied_entry_index;
483       DBG("Resetting the hash table entry from %d to %d, setting tail index to %d", old_index, pae->next_applied_entry_index, pae->tail_applied_entry_index);
484       /* unlink from the next element */
485       next_pae->prev_applied_entry_index = ~0;
486       add_del_hashtable_entry(am, sw_if_index, is_input,
487                               applied_hash_aces, pae->next_applied_entry_index, 1);
488     } else {
489       /* no next entry, so just delete the entry in the hash table */
490       add_del_hashtable_entry(am, sw_if_index, is_input,
491                               applied_hash_aces, old_index, 0);
492     }
493   }
494   /* invalidate the old entry */
495   pae->prev_applied_entry_index = ~0;
496   pae->next_applied_entry_index = ~0;
497   pae->tail_applied_entry_index = ~0;
498 }
499
500
501 static void
502 hash_acl_build_applied_lookup_bitmap(acl_main_t *am, u32 sw_if_index, u8 is_input)
503 {
504   int i;
505   uword *new_lookup_bitmap = 0;
506   applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index
507                                                          : &am->output_applied_hash_acl_info_by_sw_if_index;
508   applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), sw_if_index);
509   for(i=0; i < vec_len(pal->applied_acls); i++) {
510     u32 a_acl_index = *vec_elt_at_index((pal->applied_acls), i);
511     hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, a_acl_index);
512     DBG("Update bitmask = %U or %U (acl_index %d)\n", format_bitmap_hex, new_lookup_bitmap,
513           format_bitmap_hex, ha->mask_type_index_bitmap, a_acl_index);
514     new_lookup_bitmap = clib_bitmap_or(new_lookup_bitmap,
515                                        ha->mask_type_index_bitmap);
516   }
517   uword *old_lookup_bitmap = pal->mask_type_index_bitmap;
518   pal->mask_type_index_bitmap = new_lookup_bitmap;
519   clib_bitmap_free(old_lookup_bitmap);
520 }
521
522 void
523 hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index)
524 {
525   int i;
526
527   DBG0("HASH ACL unapply: sw_if_index %d is_input %d acl %d", sw_if_index, is_input, acl_index);
528   applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index
529                                                          : &am->output_applied_hash_acl_info_by_sw_if_index;
530   applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), sw_if_index);
531
532   hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index);
533   u32 **hash_acl_applied_sw_if_index = is_input ? &ha->inbound_sw_if_index_list
534                                                 : &ha->outbound_sw_if_index_list;
535
536   /* remove this acl# from the list of applied hash acls */
537   u32 index = vec_search(pal->applied_acls, acl_index);
538   if (index == ~0) {
539     clib_warning("BUG: trying to unapply unapplied acl_index %d on sw_if_index %d is_input %d",
540                  acl_index, sw_if_index, is_input);
541     return;
542   }
543   vec_del1(pal->applied_acls, index);
544
545   u32 index2 = vec_search((*hash_acl_applied_sw_if_index), sw_if_index);
546   if (index2 == ~0) {
547     clib_warning("BUG: trying to unapply twice acl_index %d on (sw_if_index %d) is_input %d",
548                  acl_index, sw_if_index, is_input);
549     return;
550   }
551   vec_del1((*hash_acl_applied_sw_if_index), index2);
552
553   applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index);
554
555   for(i=0; i < vec_len((*applied_hash_aces)); i++) {
556     if (vec_elt_at_index(*applied_hash_aces,i)->acl_index == acl_index) {
557       DBG("Found applied ACL#%d at applied index %d", acl_index, i);
558       break;
559     }
560   }
561   if (vec_len((*applied_hash_aces)) <= i) {
562     DBG("Did not find applied ACL#%d at sw_if_index %d", acl_index, sw_if_index);
563     /* we went all the way without finding any entries. Probably a list was empty. */
564     return;
565   }
566
567   void *oldheap = hash_acl_set_heap(am);
568   int base_offset = i;
569   int tail_offset = base_offset + vec_len(ha->rules);
570   int tail_len = vec_len((*applied_hash_aces)) - tail_offset;
571   DBG("base_offset: %d, tail_offset: %d, tail_len: %d", base_offset, tail_offset, tail_len);
572
573   for(i=0; i < vec_len(ha->rules); i ++) {
574     deactivate_applied_ace_hash_entry(am, sw_if_index, is_input,
575                                       applied_hash_aces, base_offset + i);
576   }
577   for(i=0; i < tail_len; i ++) {
578     /* move the entry at tail offset to base offset */
579     /* that is, from (tail_offset+i) -> (base_offset+i) */
580     DBG("UNAPPLY MOVE: sw_if_index %d is_input %d, applied index %d ->", sw_if_index, is_input, tail_offset+i, base_offset + i);
581     move_applied_ace_hash_entry(am, sw_if_index, is_input, applied_hash_aces, tail_offset + i, base_offset + i);
582   }
583   /* trim the end of the vector */
584   _vec_len((*applied_hash_aces)) -= vec_len(ha->rules);
585
586   applied_hash_entries_analyze(am, applied_hash_aces);
587
588   /* After deletion we might not need some of the mask-types anymore... */
589   hash_acl_build_applied_lookup_bitmap(am, sw_if_index, is_input);
590   clib_mem_set_heap (oldheap);
591 }
592
593 /*
594  * Create the applied ACEs and update the hash table,
595  * taking into account that the ACL may not be the last
596  * in the vector of applied ACLs.
597  *
598  * For now, walk from the end of the vector and unapply the ACLs,
599  * then apply the one in question and reapply the rest.
600  */
601
602 void
603 hash_acl_reapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index)
604 {
605   u32 **applied_acls = is_input ? vec_elt_at_index(am->input_acl_vec_by_sw_if_index, sw_if_index)
606                                 : vec_elt_at_index(am->output_acl_vec_by_sw_if_index, sw_if_index);
607   int i;
608   int start_index = vec_search((*applied_acls), acl_index);
609   /*
610    * This function is called after we find out the sw_if_index where ACL is applied.
611    * If the by-sw_if_index vector does not have the ACL#, then it's a bug.
612    */
613   ASSERT(start_index < vec_len(*applied_acls));
614
615   /* unapply all the ACLs till the current one */
616   for(i = vec_len(*applied_acls) - 1; i > start_index; i--) {
617     hash_acl_unapply(am, sw_if_index, is_input, *vec_elt_at_index(*applied_acls, i));
618   }
619   for(i = start_index; i < vec_len(*applied_acls); i++) {
620     hash_acl_apply(am, sw_if_index, is_input, *vec_elt_at_index(*applied_acls, i));
621   }
622 }
623
624 static void
625 make_address_mask(ip46_address_t *addr, u8 is_ipv6, u8 prefix_len)
626 {
627   if (is_ipv6) {
628     ip6_address_mask_from_width(&addr->ip6, prefix_len);
629   } else {
630     /* FIXME: this may not be correct way */
631     ip6_address_mask_from_width(&addr->ip6, prefix_len + 3*32);
632     ip46_address_mask_ip4(addr);
633   }
634 }
635
636 static u8
637 make_port_mask(u16 *portmask, u16 port_first, u16 port_last)
638 {
639   if (port_first == port_last) {
640     *portmask = 0xffff;
641     /* single port is representable by masked value */
642     return 0;
643   }
644   if ((port_first == 0) && (port_last == 65535)) {
645     *portmask = 0;
646     /* wildcard port is representable by a masked value */
647     return 0;
648   }
649
650   /*
651    * For now match all the ports, later
652    * here might be a better optimization which would
653    * pick out bitmaskable portranges.
654    *
655    * However, adding a new mask type potentially
656    * adds a per-packet extra lookup, so the benefit is not clear.
657    */
658   *portmask = 0;
659   /* This port range can't be represented via bitmask exactly. */
660   return 1;
661 }
662
663 static void
664 make_mask_and_match_from_rule(fa_5tuple_t *mask, acl_rule_t *r, hash_ace_info_t *hi, int match_nonfirst_fragment)
665 {
666   memset(mask, 0, sizeof(*mask));
667   memset(&hi->match, 0, sizeof(hi->match));
668   hi->action = r->is_permit;
669
670   /* we will need to be matching based on sw_if_index, direction, and mask_type_index when applied */
671   mask->pkt.sw_if_index = ~0;
672   mask->pkt.is_input = 1;
673   /* we will assign the match of mask_type_index later when we find it*/
674   mask->pkt.mask_type_index_lsb = ~0;
675
676   mask->pkt.is_ip6 = 1;
677   hi->match.pkt.is_ip6 = r->is_ipv6;
678
679   make_address_mask(&mask->addr[0], r->is_ipv6, r->src_prefixlen);
680   hi->match.addr[0] = r->src;
681   make_address_mask(&mask->addr[1], r->is_ipv6, r->dst_prefixlen);
682   hi->match.addr[1] = r->dst;
683
684   if (r->proto != 0) {
685     mask->l4.proto = ~0; /* L4 proto needs to be matched */
686     hi->match.l4.proto = r->proto;
687     if (match_nonfirst_fragment) {
688       /* match the non-first fragments only */
689       mask->pkt.is_nonfirst_fragment = 1;
690       hi->match.pkt.is_nonfirst_fragment = 1;
691     } else {
692       /* Calculate the src/dst port masks and make the src/dst port matches accordingly */
693       hi->src_portrange_not_powerof2 = make_port_mask(&mask->l4.port[0], r->src_port_or_type_first, r->src_port_or_type_last);
694       hi->match.l4.port[0] = r->src_port_or_type_first & mask->l4.port[0];
695       hi->dst_portrange_not_powerof2 = make_port_mask(&mask->l4.port[1], r->dst_port_or_code_first, r->dst_port_or_code_last);
696       hi->match.l4.port[1] = r->dst_port_or_code_first & mask->l4.port[1];
697       /* L4 info must be valid in order to match */
698       mask->pkt.l4_valid = 1;
699       hi->match.pkt.l4_valid = 1;
700       /* And we must set the mask to check that it is an initial fragment */
701       mask->pkt.is_nonfirst_fragment = 1;
702       hi->match.pkt.is_nonfirst_fragment = 0;
703       if ((r->proto == IPPROTO_TCP) && (r->tcp_flags_mask != 0)) {
704         /* if we want to match on TCP flags, they must be masked off as well */
705         mask->pkt.tcp_flags = r->tcp_flags_mask;
706         hi->match.pkt.tcp_flags = r->tcp_flags_value;
707         /* and the flags need to be present within the packet being matched */
708         mask->pkt.tcp_flags_valid = 1;
709         hi->match.pkt.tcp_flags_valid = 1;
710       }
711     }
712   }
713   /* Sanitize the mask and the match */
714   u64 *pmask = (u64 *)mask;
715   u64 *pmatch = (u64 *)&hi->match;
716   int j;
717   for(j=0; j<6; j++) {
718     pmatch[j] = pmatch[j] & pmask[j];
719   }
720 }
721
722 static u32
723 find_mask_type_index(acl_main_t *am, fa_5tuple_t *mask)
724 {
725   ace_mask_type_entry_t *mte;
726   /* *INDENT-OFF* */
727   pool_foreach(mte, am->ace_mask_type_pool,
728   ({
729     if(memcmp(&mte->mask, mask, sizeof(*mask)) == 0)
730       return (mte - am->ace_mask_type_pool);
731   }));
732   /* *INDENT-ON* */
733   return ~0;
734 }
735
736 static u32
737 assign_mask_type_index(acl_main_t *am, fa_5tuple_t *mask)
738 {
739   u32 mask_type_index = find_mask_type_index(am, mask);
740   ace_mask_type_entry_t *mte;
741   if(~0 == mask_type_index) {
742     pool_get_aligned (am->ace_mask_type_pool, mte, CLIB_CACHE_LINE_BYTES);
743     mask_type_index = mte - am->ace_mask_type_pool;
744     clib_memcpy(&mte->mask, mask, sizeof(mte->mask));
745     mte->refcount = 0;
746     /*
747      * We can use only 16 bits, since in the match there is only u16 field.
748      * Realistically, once you go to 64K of mask types, it is a huge
749      * problem anyway, so we might as well stop half way.
750      */
751     ASSERT(mask_type_index < 32768);
752   }
753   mte = am->ace_mask_type_pool + mask_type_index;
754   mte->refcount++;
755   return mask_type_index;
756 }
757
758 static void
759 release_mask_type_index(acl_main_t *am, u32 mask_type_index)
760 {
761   ace_mask_type_entry_t *mte = pool_elt_at_index(am->ace_mask_type_pool, mask_type_index);
762   mte->refcount--;
763   if (mte->refcount == 0) {
764     /* we are not using this entry anymore */
765     pool_put(am->ace_mask_type_pool, mte);
766   }
767 }
768
769 void hash_acl_add(acl_main_t *am, int acl_index)
770 {
771   void *oldheap = hash_acl_set_heap(am);
772   DBG("HASH ACL add : %d", acl_index);
773   int i;
774   acl_list_t *a = &am->acls[acl_index];
775   vec_validate(am->hash_acl_infos, acl_index);
776   hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index);
777   memset(ha, 0, sizeof(*ha));
778
779   /* walk the newly added ACL entries and ensure that for each of them there
780      is a mask type, increment a reference count for that mask type */
781   for(i=0; i < a->count; i++) {
782     hash_ace_info_t ace_info;
783     fa_5tuple_t mask;
784     memset(&ace_info, 0, sizeof(ace_info));
785     ace_info.acl_index = acl_index;
786     ace_info.ace_index = i;
787
788     make_mask_and_match_from_rule(&mask, &a->rules[i], &ace_info, 0);
789     ace_info.mask_type_index = assign_mask_type_index(am, &mask);
790     /* assign the mask type index for matching itself */
791     ace_info.match.pkt.mask_type_index_lsb = ace_info.mask_type_index;
792     DBG("ACE: %d mask_type_index: %d", i, ace_info.mask_type_index);
793     /* Ensure a given index is set in the mask type index bitmap for this ACL */
794     ha->mask_type_index_bitmap = clib_bitmap_set(ha->mask_type_index_bitmap, ace_info.mask_type_index, 1);
795     vec_add1(ha->rules, ace_info);
796     if (am->l4_match_nonfirst_fragment) {
797       /* add the second rule which matches the noninitial fragments with the respective mask */
798       make_mask_and_match_from_rule(&mask, &a->rules[i], &ace_info, 1);
799       ace_info.mask_type_index = assign_mask_type_index(am, &mask);
800       ace_info.match.pkt.mask_type_index_lsb = ace_info.mask_type_index;
801       DBG("ACE: %d (non-initial frags) mask_type_index: %d", i, ace_info.mask_type_index);
802       /* Ensure a given index is set in the mask type index bitmap for this ACL */
803       ha->mask_type_index_bitmap = clib_bitmap_set(ha->mask_type_index_bitmap, ace_info.mask_type_index, 1);
804       vec_add1(ha->rules, ace_info);
805     }
806   }
807   /*
808    * if an ACL is applied somewhere, fill the corresponding lookup data structures.
809    * We need to take care if the ACL is not the last one in the vector of ACLs applied to the interface.
810    */
811   if (acl_index < vec_len(am->input_sw_if_index_vec_by_acl)) {
812     u32 *sw_if_index;
813     vec_foreach(sw_if_index, am->input_sw_if_index_vec_by_acl[acl_index]) {
814       hash_acl_reapply(am, *sw_if_index, 1, acl_index);
815     }
816   }
817   if (acl_index < vec_len(am->output_sw_if_index_vec_by_acl)) {
818     u32 *sw_if_index;
819     vec_foreach(sw_if_index, am->output_sw_if_index_vec_by_acl[acl_index]) {
820       hash_acl_reapply(am, *sw_if_index, 0, acl_index);
821     }
822   }
823   clib_mem_set_heap (oldheap);
824 }
825
826 void hash_acl_delete(acl_main_t *am, int acl_index)
827 {
828   void *oldheap = hash_acl_set_heap(am);
829   DBG0("HASH ACL delete : %d", acl_index);
830   /*
831    * If the ACL is applied somewhere, remove the references of it (call hash_acl_unapply)
832    * this is a different behavior from the linear lookup where an empty ACL is "deny all",
833    *
834    * However, following vpp-dev discussion the ACL that is referenced elsewhere
835    * should not be possible to delete, and the change adding this also adds
836    * the safeguards to that respect, so this is not a problem.
837    *
838    * The part to rememeber is that this routine is called in process of reapplication
839    * during the acl_add_replace() API call - the old acl ruleset is deleted, then
840    * the new one is added, without the change in the applied ACLs - so this case
841    * has to be handled.
842    */
843   hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index);
844   u32 *interface_list_copy = 0;
845   {
846     u32 *sw_if_index;
847     interface_list_copy = vec_dup(ha->inbound_sw_if_index_list);
848     vec_foreach(sw_if_index, interface_list_copy) {
849       hash_acl_unapply(am, *sw_if_index, 1, acl_index);
850     }
851     vec_free(interface_list_copy);
852     interface_list_copy = vec_dup(ha->outbound_sw_if_index_list);
853     vec_foreach(sw_if_index, interface_list_copy) {
854       hash_acl_unapply(am, *sw_if_index, 0, acl_index);
855     }
856   }
857
858   /* walk the mask types for the ACL about-to-be-deleted, and decrease
859    * the reference count, possibly freeing up some of them */
860   int i;
861   for(i=0; i < vec_len(ha->rules); i++) {
862     release_mask_type_index(am, ha->rules[i].mask_type_index);
863   }
864   clib_bitmap_free(ha->mask_type_index_bitmap);
865   vec_free(ha->rules);
866   clib_mem_set_heap (oldheap);
867 }
868
869 u8
870 hash_multi_acl_match_5tuple (u32 sw_if_index, fa_5tuple_t * pkt_5tuple, int is_l2,
871                        int is_ip6, int is_input, u32 * acl_match_p,
872                        u32 * rule_match_p, u32 * trace_bitmap)
873 {
874   acl_main_t *am = &acl_main;
875   applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index);
876   u32 match_index = multi_acl_match_get_applied_ace_index(am, pkt_5tuple);
877   if (match_index < vec_len((*applied_hash_aces))) {
878     applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), match_index);
879     pae->hitcount++;
880     *acl_match_p = pae->acl_index;
881     *rule_match_p = pae->ace_index;
882     return pae->action;
883   }
884   return 0;
885 }
886
887
888 void
889 show_hash_acl_hash (vlib_main_t * vm, acl_main_t *am, u32 verbose)
890 {
891   vlib_cli_output(vm, "\nACL lookup hash table:\n%U\n",
892                   BV (format_bihash), &am->acl_lookup_hash, verbose);
893 }