Cleanup/refactor session layer code
[vpp.git] / src / vnet / session / session_lookup.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /** Generate typed init functions for multiple hash table styles... */
17 #include <vppinfra/bihash_16_8.h>
18 #include <vppinfra/bihash_template.h>
19
20 #include <vppinfra/bihash_template.c>
21
22 #undef __included_bihash_template_h__
23
24 #include <vppinfra/bihash_48_8.h>
25 #include <vppinfra/bihash_template.h>
26
27 #include <vppinfra/bihash_template.c>
28 #include <vnet/session/session_lookup.h>
29 #include <vnet/session/session.h>
30
31 static session_lookup_t session_lookup;
32 extern transport_proto_vft_t *tp_vfts;
33
34 /* *INDENT-OFF* */
35 /* 16 octets */
36 typedef CLIB_PACKED (struct {
37   union
38     {
39       struct
40         {
41           ip4_address_t src;
42           ip4_address_t dst;
43           u16 src_port;
44           u16 dst_port;
45           /* align by making this 4 octets even though its a 1-bit field
46            * NOTE: avoid key overlap with other transports that use 5 tuples for
47            * session identification.
48            */
49           u32 proto;
50         };
51       u64 as_u64[2];
52     };
53 }) v4_connection_key_t;
54
55 typedef CLIB_PACKED (struct {
56   union
57     {
58       struct
59         {
60           /* 48 octets */
61           ip6_address_t src;
62           ip6_address_t dst;
63           u16 src_port;
64           u16 dst_port;
65           u32 proto;
66           u64 unused;
67         };
68       u64 as_u64[6];
69     };
70 }) v6_connection_key_t;
71 /* *INDENT-ON* */
72
73 typedef clib_bihash_kv_16_8_t session_kv4_t;
74 typedef clib_bihash_kv_48_8_t session_kv6_t;
75
76 always_inline void
77 make_v4_ss_kv (session_kv4_t * kv, ip4_address_t * lcl, ip4_address_t * rmt,
78                u16 lcl_port, u16 rmt_port, u8 proto)
79 {
80   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
81
82   key->src.as_u32 = lcl->as_u32;
83   key->dst.as_u32 = rmt->as_u32;
84   key->src_port = lcl_port;
85   key->dst_port = rmt_port;
86   key->proto = proto;
87
88   kv->value = ~0ULL;
89 }
90
91 always_inline void
92 make_v4_listener_kv (session_kv4_t * kv, ip4_address_t * lcl, u16 lcl_port,
93                      u8 proto)
94 {
95   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
96
97   key->src.as_u32 = lcl->as_u32;
98   key->dst.as_u32 = 0;
99   key->src_port = lcl_port;
100   key->dst_port = 0;
101   key->proto = proto;
102
103   kv->value = ~0ULL;
104 }
105
106 always_inline void
107 make_v4_ss_kv_from_tc (session_kv4_t * kv, transport_connection_t * t)
108 {
109   return make_v4_ss_kv (kv, &t->lcl_ip.ip4, &t->rmt_ip.ip4, t->lcl_port,
110                         t->rmt_port, t->proto);
111 }
112
113 always_inline void
114 make_v6_ss_kv (session_kv6_t * kv, ip6_address_t * lcl, ip6_address_t * rmt,
115                u16 lcl_port, u16 rmt_port, u8 proto)
116 {
117   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
118
119   key->src.as_u64[0] = lcl->as_u64[0];
120   key->src.as_u64[1] = lcl->as_u64[1];
121   key->dst.as_u64[0] = rmt->as_u64[0];
122   key->dst.as_u64[1] = rmt->as_u64[1];
123   key->src_port = lcl_port;
124   key->dst_port = rmt_port;
125   key->proto = proto;
126   key->unused = 0;
127
128   kv->value = ~0ULL;
129 }
130
131 always_inline void
132 make_v6_listener_kv (session_kv6_t * kv, ip6_address_t * lcl, u16 lcl_port,
133                      u8 proto)
134 {
135   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
136
137   key->src.as_u64[0] = lcl->as_u64[0];
138   key->src.as_u64[1] = lcl->as_u64[1];
139   key->dst.as_u64[0] = 0;
140   key->dst.as_u64[1] = 0;
141   key->src_port = lcl_port;
142   key->dst_port = 0;
143   key->proto = proto;
144   key->unused = 0;
145
146   kv->value = ~0ULL;
147 }
148
149 always_inline void
150 make_v6_ss_kv_from_tc (session_kv6_t * kv, transport_connection_t * t)
151 {
152   make_v6_ss_kv (kv, &t->lcl_ip.ip6, &t->rmt_ip.ip6, t->lcl_port,
153                  t->rmt_port, t->proto);
154 }
155
156 /*
157  * Session lookup key; (src-ip, dst-ip, src-port, dst-port, session-type)
158  * Value: (owner thread index << 32 | session_index);
159  */
160 void
161 stream_session_table_add_for_tc (transport_connection_t * tc, u64 value)
162 {
163   session_lookup_t *sl = &session_lookup;
164   session_kv4_t kv4;
165   session_kv6_t kv6;
166
167   switch (tc->proto)
168     {
169     case SESSION_TYPE_IP4_UDP:
170     case SESSION_TYPE_IP4_TCP:
171       make_v4_ss_kv_from_tc (&kv4, tc);
172       kv4.value = value;
173       clib_bihash_add_del_16_8 (&sl->v4_session_hash, &kv4, 1 /* is_add */ );
174       break;
175     case SESSION_TYPE_IP6_UDP:
176     case SESSION_TYPE_IP6_TCP:
177       make_v6_ss_kv_from_tc (&kv6, tc);
178       kv6.value = value;
179       clib_bihash_add_del_48_8 (&sl->v6_session_hash, &kv6, 1 /* is_add */ );
180       break;
181     default:
182       clib_warning ("Session type not supported");
183       ASSERT (0);
184     }
185 }
186
187 void
188 stream_session_table_add (session_manager_main_t * smm, stream_session_t * s,
189                           u64 value)
190 {
191   transport_connection_t *tc;
192
193   tc = tp_vfts[s->session_type].get_connection (s->connection_index,
194                                                 s->thread_index);
195   stream_session_table_add_for_tc (tc, value);
196 }
197
198 void
199 stream_session_half_open_table_add (session_type_t sst,
200                                     transport_connection_t * tc, u64 value)
201 {
202   session_lookup_t *sl = &session_lookup;
203   session_kv4_t kv4;
204   session_kv6_t kv6;
205
206   switch (sst)
207     {
208     case SESSION_TYPE_IP4_UDP:
209     case SESSION_TYPE_IP4_TCP:
210       make_v4_ss_kv_from_tc (&kv4, tc);
211       kv4.value = value;
212       clib_bihash_add_del_16_8 (&sl->v4_half_open_hash, &kv4,
213                                 1 /* is_add */ );
214       break;
215     case SESSION_TYPE_IP6_UDP:
216     case SESSION_TYPE_IP6_TCP:
217       make_v6_ss_kv_from_tc (&kv6, tc);
218       kv6.value = value;
219       clib_bihash_add_del_48_8 (&sl->v6_half_open_hash, &kv6,
220                                 1 /* is_add */ );
221       break;
222     default:
223       clib_warning ("Session type not supported");
224       ASSERT (0);
225     }
226 }
227
228 int
229 stream_session_table_del_for_tc (transport_connection_t * tc)
230 {
231   session_lookup_t *sl = &session_lookup;
232   session_kv4_t kv4;
233   session_kv6_t kv6;
234   switch (tc->proto)
235     {
236     case SESSION_TYPE_IP4_UDP:
237     case SESSION_TYPE_IP4_TCP:
238       make_v4_ss_kv_from_tc (&kv4, tc);
239       return clib_bihash_add_del_16_8 (&sl->v4_session_hash, &kv4,
240                                        0 /* is_add */ );
241       break;
242     case SESSION_TYPE_IP6_UDP:
243     case SESSION_TYPE_IP6_TCP:
244       make_v6_ss_kv_from_tc (&kv6, tc);
245       return clib_bihash_add_del_48_8 (&sl->v6_session_hash, &kv6,
246                                        0 /* is_add */ );
247       break;
248     default:
249       clib_warning ("Session type not supported");
250       ASSERT (0);
251     }
252
253   return 0;
254 }
255
256 int
257 stream_session_table_del (stream_session_t * s)
258 {
259   transport_connection_t *ts;
260   ts = tp_vfts[s->session_type].get_connection (s->connection_index,
261                                                 s->thread_index);
262   return stream_session_table_del_for_tc (ts);
263 }
264
265 void
266 stream_session_half_open_table_del (u8 sst, transport_connection_t * tc)
267 {
268   session_lookup_t *sl = &session_lookup;
269   session_kv4_t kv4;
270   session_kv6_t kv6;
271
272   switch (sst)
273     {
274     case SESSION_TYPE_IP4_UDP:
275     case SESSION_TYPE_IP4_TCP:
276       make_v4_ss_kv_from_tc (&kv4, tc);
277       clib_bihash_add_del_16_8 (&sl->v4_half_open_hash, &kv4,
278                                 0 /* is_add */ );
279       break;
280     case SESSION_TYPE_IP6_UDP:
281     case SESSION_TYPE_IP6_TCP:
282       make_v6_ss_kv_from_tc (&kv6, tc);
283       clib_bihash_add_del_48_8 (&sl->v6_half_open_hash, &kv6,
284                                 0 /* is_add */ );
285       break;
286     default:
287       clib_warning ("Session type not supported");
288       ASSERT (0);
289     }
290 }
291
292 stream_session_t *
293 stream_session_lookup_listener4 (ip4_address_t * lcl, u16 lcl_port, u8 proto)
294 {
295   session_lookup_t *sl = &session_lookup;
296   session_kv4_t kv4;
297   int rv;
298
299   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
300   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
301   if (rv == 0)
302     return session_manager_get_listener (proto, (u32) kv4.value);
303
304   /* Zero out the lcl ip */
305   kv4.key[0] = 0;
306   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
307   if (rv == 0)
308     return session_manager_get_listener (proto, (u32) kv4.value);
309
310   return 0;
311 }
312
313 /** Looks up a session based on the 5-tuple passed as argument.
314  *
315  * First it tries to find an established session, if this fails, it tries
316  * finding a listener session if this fails, it tries a lookup with a
317  * wildcarded local source (listener bound to all interfaces)
318  */
319 stream_session_t *
320 stream_session_lookup4 (ip4_address_t * lcl, ip4_address_t * rmt,
321                         u16 lcl_port, u16 rmt_port, u8 proto)
322 {
323   session_lookup_t *sl = &session_lookup;
324   session_kv4_t kv4;
325   stream_session_t *s;
326   int rv;
327
328   /* Lookup session amongst established ones */
329   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
330   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
331   if (rv == 0)
332     return stream_session_get_from_handle (kv4.value);
333
334   /* If nothing is found, check if any listener is available */
335   if ((s = stream_session_lookup_listener4 (lcl, lcl_port, proto)))
336     return s;
337
338   /* Finally, try half-open connections */
339   rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
340   if (rv == 0)
341     return stream_session_get_from_handle (kv4.value);
342   return 0;
343 }
344
345 stream_session_t *
346 stream_session_lookup_listener6 (ip6_address_t * lcl, u16 lcl_port, u8 proto)
347 {
348   session_lookup_t *sl = &session_lookup;
349   session_kv6_t kv6;
350   int rv;
351
352   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
353   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
354   if (rv == 0)
355     return session_manager_get_listener (proto, (u32) kv6.value);
356
357   /* Zero out the lcl ip */
358   kv6.key[0] = kv6.key[1] = 0;
359   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
360   if (rv == 0)
361     return session_manager_get_listener (proto, (u32) kv6.value);
362
363   return 0;
364 }
365
366 /* Looks up a session based on the 5-tuple passed as argument.
367  * First it tries to find an established session, if this fails, it tries
368  * finding a listener session if this fails, it tries a lookup with a
369  * wildcarded local source (listener bound to all interfaces) */
370 stream_session_t *
371 stream_session_lookup6 (ip6_address_t * lcl, ip6_address_t * rmt,
372                         u16 lcl_port, u16 rmt_port, u8 proto)
373 {
374   session_lookup_t *sl = &session_lookup;
375   session_kv6_t kv6;
376   stream_session_t *s;
377   int rv;
378
379   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
380   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
381   if (rv == 0)
382     return stream_session_get_from_handle (kv6.value);
383
384   /* If nothing is found, check if any listener is available */
385   if ((s = stream_session_lookup_listener6 (lcl, lcl_port, proto)))
386     return s;
387
388   /* Finally, try half-open connections */
389   rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
390   if (rv == 0)
391     return stream_session_get_from_handle (kv6.value);
392   return 0;
393 }
394
395 stream_session_t *
396 stream_session_lookup_listener (ip46_address_t * lcl, u16 lcl_port, u8 proto)
397 {
398   switch (proto)
399     {
400     case SESSION_TYPE_IP4_UDP:
401     case SESSION_TYPE_IP4_TCP:
402       return stream_session_lookup_listener4 (&lcl->ip4, lcl_port, proto);
403       break;
404     case SESSION_TYPE_IP6_UDP:
405     case SESSION_TYPE_IP6_TCP:
406       return stream_session_lookup_listener6 (&lcl->ip6, lcl_port, proto);
407       break;
408     }
409   return 0;
410 }
411
412 u64
413 stream_session_half_open_lookup_handle (ip46_address_t * lcl,
414                                         ip46_address_t * rmt, u16 lcl_port,
415                                         u16 rmt_port, u8 proto)
416 {
417   session_lookup_t *sl = &session_lookup;
418   session_kv4_t kv4;
419   session_kv6_t kv6;
420   int rv;
421
422   switch (proto)
423     {
424     case SESSION_TYPE_IP4_UDP:
425     case SESSION_TYPE_IP4_TCP:
426       make_v4_ss_kv (&kv4, &lcl->ip4, &rmt->ip4, lcl_port, rmt_port, proto);
427       rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
428
429       if (rv == 0)
430         return kv4.value;
431
432       return HALF_OPEN_LOOKUP_INVALID_VALUE;
433       break;
434     case SESSION_TYPE_IP6_UDP:
435     case SESSION_TYPE_IP6_TCP:
436       make_v6_ss_kv (&kv6, &lcl->ip6, &rmt->ip6, lcl_port, rmt_port, proto);
437       rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
438
439       if (rv == 0)
440         return kv6.value;
441
442       return HALF_OPEN_LOOKUP_INVALID_VALUE;
443       break;
444     }
445   return HALF_OPEN_LOOKUP_INVALID_VALUE;
446 }
447
448 transport_connection_t *
449 stream_session_half_open_lookup (ip46_address_t * lcl, ip46_address_t * rmt,
450                                  u16 lcl_port, u16 rmt_port, u8 proto)
451 {
452   u64 handle;
453   handle =
454     stream_session_half_open_lookup_handle (lcl, rmt, lcl_port, rmt_port,
455                                             proto);
456   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
457     return tp_vfts[proto].get_half_open (handle & 0xFFFFFFFF);
458   return 0;
459 }
460
461 always_inline stream_session_t *
462 stream_session_get_tsi (u64 ti_and_si, u32 thread_index)
463 {
464   ASSERT ((u32) (ti_and_si >> 32) == thread_index);
465   return pool_elt_at_index (session_manager_main.sessions[thread_index],
466                             ti_and_si & 0xFFFFFFFFULL);
467 }
468
469 transport_connection_t *
470 stream_session_lookup_transport_wt4 (ip4_address_t * lcl, ip4_address_t * rmt,
471                                      u16 lcl_port, u16 rmt_port, u8 proto,
472                                      u32 my_thread_index)
473 {
474   session_lookup_t *sl = &session_lookup;
475   session_kv4_t kv4;
476   stream_session_t *s;
477   int rv;
478
479   /* Lookup session amongst established ones */
480   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
481   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
482   if (rv == 0)
483     {
484       s = stream_session_get_tsi (kv4.value, my_thread_index);
485       return tp_vfts[s->session_type].get_connection (s->connection_index,
486                                                       my_thread_index);
487     }
488
489   /* If nothing is found, check if any listener is available */
490   s = stream_session_lookup_listener4 (lcl, lcl_port, proto);
491   if (s)
492     return tp_vfts[s->session_type].get_listener (s->connection_index);
493
494   /* Finally, try half-open connections */
495   rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
496   if (rv == 0)
497     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
498   return 0;
499 }
500
501 transport_connection_t *
502 stream_session_lookup_transport4 (ip4_address_t * lcl, ip4_address_t * rmt,
503                                   u16 lcl_port, u16 rmt_port, u8 proto)
504 {
505   session_lookup_t *sl = &session_lookup;
506   session_kv4_t kv4;
507   stream_session_t *s;
508   int rv;
509
510   /* Lookup session amongst established ones */
511   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
512   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
513   if (rv == 0)
514     {
515       s = stream_session_get_from_handle (kv4.value);
516       return tp_vfts[s->session_type].get_connection (s->connection_index,
517                                                       s->thread_index);
518     }
519
520   /* If nothing is found, check if any listener is available */
521   s = stream_session_lookup_listener4 (lcl, lcl_port, proto);
522   if (s)
523     return tp_vfts[s->session_type].get_listener (s->connection_index);
524
525   /* Finally, try half-open connections */
526   rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
527   if (rv == 0)
528     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
529   return 0;
530 }
531
532 transport_connection_t *
533 stream_session_lookup_transport_wt6 (ip6_address_t * lcl, ip6_address_t * rmt,
534                                      u16 lcl_port, u16 rmt_port, u8 proto,
535                                      u32 my_thread_index)
536 {
537   session_lookup_t *sl = &session_lookup;
538   stream_session_t *s;
539   session_kv6_t kv6;
540   int rv;
541
542   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
543   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
544   if (rv == 0)
545     {
546       s = stream_session_get_tsi (kv6.value, my_thread_index);
547       return tp_vfts[s->session_type].get_connection (s->connection_index,
548                                                       my_thread_index);
549     }
550
551   /* If nothing is found, check if any listener is available */
552   s = stream_session_lookup_listener6 (lcl, lcl_port, proto);
553   if (s)
554     return tp_vfts[s->session_type].get_listener (s->connection_index);
555
556   /* Finally, try half-open connections */
557   rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
558   if (rv == 0)
559     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
560
561   return 0;
562 }
563
564 transport_connection_t *
565 stream_session_lookup_transport6 (ip6_address_t * lcl, ip6_address_t * rmt,
566                                   u16 lcl_port, u16 rmt_port, u8 proto)
567 {
568   session_lookup_t *sl = &session_lookup;
569   stream_session_t *s;
570   session_kv6_t kv6;
571   int rv;
572
573   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
574   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
575   if (rv == 0)
576     {
577       s = stream_session_get_from_handle (kv6.value);
578       return tp_vfts[s->session_type].get_connection (s->connection_index,
579                                                       s->thread_index);
580     }
581
582   /* If nothing is found, check if any listener is available */
583   s = stream_session_lookup_listener6 (lcl, lcl_port, proto);
584   if (s)
585     return tp_vfts[s->session_type].get_listener (s->connection_index);
586
587   /* Finally, try half-open connections */
588   rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
589   if (rv == 0)
590     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
591
592   return 0;
593 }
594
595 void
596 session_lookup_init (void)
597 {
598   session_lookup_t *sl = &session_lookup;
599   clib_bihash_init_16_8 (&sl->v4_session_hash, "v4 session table",
600                          200000 /* $$$$ config parameter nbuckets */ ,
601                          (64 << 20) /*$$$ config parameter table size */ );
602   clib_bihash_init_48_8 (&sl->v6_session_hash, "v6 session table",
603                          200000 /* $$$$ config parameter nbuckets */ ,
604                          (64 << 20) /*$$$ config parameter table size */ );
605
606   clib_bihash_init_16_8 (&sl->v4_half_open_hash, "v4 half-open table",
607                          200000 /* $$$$ config parameter nbuckets */ ,
608                          (64 << 20) /*$$$ config parameter table size */ );
609   clib_bihash_init_48_8 (&sl->v6_half_open_hash, "v6 half-open table",
610                          200000 /* $$$$ config parameter nbuckets */ ,
611                          (64 << 20) /*$$$ config parameter table size */ );
612 }
613
614 /*
615  * fd.io coding-style-patch-verification: ON
616  *
617  * Local Variables:
618  * eval: (c-set-style "gnu")
619  * End:
620  */