4487b1c305f58a6af1035dcd660cebcbd00760ac
[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   make_v4_ss_kv (kv, &t->lcl_ip.ip4, &t->rmt_ip.ip4, t->lcl_port, t->rmt_port,
110                  session_type_from_proto_and_ip (t->transport_proto, 1));
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, t->rmt_port,
153                  session_type_from_proto_and_ip (t->transport_proto, 0));
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   if (tc->is_ip4)
168     {
169       make_v4_ss_kv_from_tc (&kv4, tc);
170       kv4.value = value;
171       clib_bihash_add_del_16_8 (&sl->v4_session_hash, &kv4, 1 /* is_add */ );
172     }
173   else
174     {
175       make_v6_ss_kv_from_tc (&kv6, tc);
176       kv6.value = value;
177       clib_bihash_add_del_48_8 (&sl->v6_session_hash, &kv6, 1 /* is_add */ );
178     }
179 }
180
181 void
182 stream_session_table_add (session_manager_main_t * smm, stream_session_t * s,
183                           u64 value)
184 {
185   transport_connection_t *tc;
186
187   tc = tp_vfts[s->session_type].get_connection (s->connection_index,
188                                                 s->thread_index);
189   stream_session_table_add_for_tc (tc, value);
190 }
191
192 int
193 stream_session_table_del_for_tc (transport_connection_t * tc)
194 {
195   session_lookup_t *sl = &session_lookup;
196   session_kv4_t kv4;
197   session_kv6_t kv6;
198
199   if (tc->is_ip4)
200     {
201       make_v4_ss_kv_from_tc (&kv4, tc);
202       return clib_bihash_add_del_16_8 (&sl->v4_session_hash, &kv4,
203                                        0 /* is_add */ );
204     }
205   else
206     {
207       make_v6_ss_kv_from_tc (&kv6, tc);
208       return clib_bihash_add_del_48_8 (&sl->v6_session_hash, &kv6,
209                                        0 /* is_add */ );
210     }
211
212   return 0;
213 }
214
215 int
216 stream_session_table_del (stream_session_t * s)
217 {
218   transport_connection_t *ts;
219   ts = tp_vfts[s->session_type].get_connection (s->connection_index,
220                                                 s->thread_index);
221   return stream_session_table_del_for_tc (ts);
222 }
223
224
225 void
226 stream_session_half_open_table_add (transport_connection_t * tc, u64 value)
227 {
228   session_lookup_t *sl = &session_lookup;
229   session_kv4_t kv4;
230   session_kv6_t kv6;
231
232   if (tc->is_ip4)
233     {
234       make_v4_ss_kv_from_tc (&kv4, tc);
235       kv4.value = value;
236       (void) clib_bihash_add_del_16_8 (&sl->v4_half_open_hash, &kv4,
237                                        1 /* is_add */ );
238     }
239   else
240     {
241       make_v6_ss_kv_from_tc (&kv6, tc);
242       kv6.value = value;
243       (void) clib_bihash_add_del_48_8 (&sl->v6_half_open_hash, &kv6,
244                                        1 /* is_add */ );
245     }
246 }
247
248 void
249 stream_session_half_open_table_del (transport_connection_t * tc)
250 {
251   session_lookup_t *sl = &session_lookup;
252   session_kv4_t kv4;
253   session_kv6_t kv6;
254
255   if (tc->is_ip4)
256     {
257       make_v4_ss_kv_from_tc (&kv4, tc);
258       clib_bihash_add_del_16_8 (&sl->v4_half_open_hash, &kv4,
259                                 0 /* is_add */ );
260     }
261   else
262     {
263       make_v6_ss_kv_from_tc (&kv6, tc);
264       clib_bihash_add_del_48_8 (&sl->v6_half_open_hash, &kv6,
265                                 0 /* is_add */ );
266     }
267 }
268
269 stream_session_t *
270 stream_session_lookup_listener4 (ip4_address_t * lcl, u16 lcl_port, u8 proto)
271 {
272   session_lookup_t *sl = &session_lookup;
273   session_kv4_t kv4;
274   int rv;
275
276   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
277   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
278   if (rv == 0)
279     return session_manager_get_listener (proto, (u32) kv4.value);
280
281   /* Zero out the lcl ip */
282   kv4.key[0] = 0;
283   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
284   if (rv == 0)
285     return session_manager_get_listener (proto, (u32) kv4.value);
286
287   return 0;
288 }
289
290 /** Looks up a session based on the 5-tuple passed as argument.
291  *
292  * First it tries to find an established session, if this fails, it tries
293  * finding a listener session if this fails, it tries a lookup with a
294  * wildcarded local source (listener bound to all interfaces)
295  */
296 stream_session_t *
297 stream_session_lookup4 (ip4_address_t * lcl, ip4_address_t * rmt,
298                         u16 lcl_port, u16 rmt_port, u8 proto)
299 {
300   session_lookup_t *sl = &session_lookup;
301   session_kv4_t kv4;
302   stream_session_t *s;
303   int rv;
304
305   /* Lookup session amongst established ones */
306   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
307   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
308   if (rv == 0)
309     return stream_session_get_from_handle (kv4.value);
310
311   /* If nothing is found, check if any listener is available */
312   if ((s = stream_session_lookup_listener4 (lcl, lcl_port, proto)))
313     return s;
314
315   /* Finally, try half-open connections */
316   rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
317   if (rv == 0)
318     return stream_session_get_from_handle (kv4.value);
319   return 0;
320 }
321
322 stream_session_t *
323 stream_session_lookup_listener6 (ip6_address_t * lcl, u16 lcl_port, u8 proto)
324 {
325   session_lookup_t *sl = &session_lookup;
326   session_kv6_t kv6;
327   int rv;
328
329   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
330   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
331   if (rv == 0)
332     return session_manager_get_listener (proto, (u32) kv6.value);
333
334   /* Zero out the lcl ip */
335   kv6.key[0] = kv6.key[1] = 0;
336   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
337   if (rv == 0)
338     return session_manager_get_listener (proto, (u32) kv6.value);
339
340   return 0;
341 }
342
343 /* Looks up a session based on the 5-tuple passed as argument.
344  * First it tries to find an established session, if this fails, it tries
345  * finding a listener session if this fails, it tries a lookup with a
346  * wildcarded local source (listener bound to all interfaces) */
347 stream_session_t *
348 stream_session_lookup6 (ip6_address_t * lcl, ip6_address_t * rmt,
349                         u16 lcl_port, u16 rmt_port, u8 proto)
350 {
351   session_lookup_t *sl = &session_lookup;
352   session_kv6_t kv6;
353   stream_session_t *s;
354   int rv;
355
356   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
357   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
358   if (rv == 0)
359     return stream_session_get_from_handle (kv6.value);
360
361   /* If nothing is found, check if any listener is available */
362   if ((s = stream_session_lookup_listener6 (lcl, lcl_port, proto)))
363     return s;
364
365   /* Finally, try half-open connections */
366   rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
367   if (rv == 0)
368     return stream_session_get_from_handle (kv6.value);
369   return 0;
370 }
371
372 stream_session_t *
373 stream_session_lookup_listener (ip46_address_t * lcl, u16 lcl_port, u8 proto)
374 {
375   switch (proto)
376     {
377     case SESSION_TYPE_IP4_UDP:
378     case SESSION_TYPE_IP4_TCP:
379       return stream_session_lookup_listener4 (&lcl->ip4, lcl_port, proto);
380       break;
381     case SESSION_TYPE_IP6_UDP:
382     case SESSION_TYPE_IP6_TCP:
383       return stream_session_lookup_listener6 (&lcl->ip6, lcl_port, proto);
384       break;
385     }
386   return 0;
387 }
388
389 u64
390 stream_session_half_open_lookup_handle (ip46_address_t * lcl,
391                                         ip46_address_t * rmt, u16 lcl_port,
392                                         u16 rmt_port, u8 proto)
393 {
394   session_lookup_t *sl = &session_lookup;
395   session_kv4_t kv4;
396   session_kv6_t kv6;
397   int rv;
398
399   switch (proto)
400     {
401     case SESSION_TYPE_IP4_UDP:
402     case SESSION_TYPE_IP4_TCP:
403       make_v4_ss_kv (&kv4, &lcl->ip4, &rmt->ip4, lcl_port, rmt_port, proto);
404       rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
405
406       if (rv == 0)
407         return kv4.value;
408
409       return HALF_OPEN_LOOKUP_INVALID_VALUE;
410       break;
411     case SESSION_TYPE_IP6_UDP:
412     case SESSION_TYPE_IP6_TCP:
413       make_v6_ss_kv (&kv6, &lcl->ip6, &rmt->ip6, lcl_port, rmt_port, proto);
414       rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
415
416       if (rv == 0)
417         return kv6.value;
418
419       return HALF_OPEN_LOOKUP_INVALID_VALUE;
420       break;
421     }
422   return HALF_OPEN_LOOKUP_INVALID_VALUE;
423 }
424
425 transport_connection_t *
426 stream_session_half_open_lookup (ip46_address_t * lcl, ip46_address_t * rmt,
427                                  u16 lcl_port, u16 rmt_port, u8 proto)
428 {
429   u64 handle;
430   handle =
431     stream_session_half_open_lookup_handle (lcl, rmt, lcl_port, rmt_port,
432                                             proto);
433   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
434     return tp_vfts[proto].get_half_open (handle & 0xFFFFFFFF);
435   return 0;
436 }
437
438 always_inline stream_session_t *
439 stream_session_get_tsi (u64 ti_and_si, u32 thread_index)
440 {
441   ASSERT ((u32) (ti_and_si >> 32) == thread_index);
442   return pool_elt_at_index (session_manager_main.sessions[thread_index],
443                             ti_and_si & 0xFFFFFFFFULL);
444 }
445
446 transport_connection_t *
447 stream_session_lookup_transport_wt4 (ip4_address_t * lcl, ip4_address_t * rmt,
448                                      u16 lcl_port, u16 rmt_port, u8 proto,
449                                      u32 my_thread_index)
450 {
451   session_lookup_t *sl = &session_lookup;
452   session_kv4_t kv4;
453   stream_session_t *s;
454   int rv;
455
456   /* Lookup session amongst established ones */
457   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
458   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
459   if (rv == 0)
460     {
461       s = stream_session_get_tsi (kv4.value, my_thread_index);
462       return tp_vfts[s->session_type].get_connection (s->connection_index,
463                                                       my_thread_index);
464     }
465
466   /* If nothing is found, check if any listener is available */
467   s = stream_session_lookup_listener4 (lcl, lcl_port, proto);
468   if (s)
469     return tp_vfts[s->session_type].get_listener (s->connection_index);
470
471   /* Finally, try half-open connections */
472   rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
473   if (rv == 0)
474     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
475   return 0;
476 }
477
478 transport_connection_t *
479 stream_session_lookup_transport4 (ip4_address_t * lcl, ip4_address_t * rmt,
480                                   u16 lcl_port, u16 rmt_port, u8 proto)
481 {
482   session_lookup_t *sl = &session_lookup;
483   session_kv4_t kv4;
484   stream_session_t *s;
485   int rv;
486
487   /* Lookup session amongst established ones */
488   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
489   rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4);
490   if (rv == 0)
491     {
492       s = stream_session_get_from_handle (kv4.value);
493       return tp_vfts[s->session_type].get_connection (s->connection_index,
494                                                       s->thread_index);
495     }
496
497   /* If nothing is found, check if any listener is available */
498   s = stream_session_lookup_listener4 (lcl, lcl_port, proto);
499   if (s)
500     return tp_vfts[s->session_type].get_listener (s->connection_index);
501
502   /* Finally, try half-open connections */
503   rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4);
504   if (rv == 0)
505     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
506   return 0;
507 }
508
509 transport_connection_t *
510 stream_session_lookup_transport_wt6 (ip6_address_t * lcl, ip6_address_t * rmt,
511                                      u16 lcl_port, u16 rmt_port, u8 proto,
512                                      u32 my_thread_index)
513 {
514   session_lookup_t *sl = &session_lookup;
515   stream_session_t *s;
516   session_kv6_t kv6;
517   int rv;
518
519   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
520   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
521   if (rv == 0)
522     {
523       s = stream_session_get_tsi (kv6.value, my_thread_index);
524       return tp_vfts[s->session_type].get_connection (s->connection_index,
525                                                       my_thread_index);
526     }
527
528   /* If nothing is found, check if any listener is available */
529   s = stream_session_lookup_listener6 (lcl, lcl_port, proto);
530   if (s)
531     return tp_vfts[s->session_type].get_listener (s->connection_index);
532
533   /* Finally, try half-open connections */
534   rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
535   if (rv == 0)
536     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
537
538   return 0;
539 }
540
541 transport_connection_t *
542 stream_session_lookup_transport6 (ip6_address_t * lcl, ip6_address_t * rmt,
543                                   u16 lcl_port, u16 rmt_port, u8 proto)
544 {
545   session_lookup_t *sl = &session_lookup;
546   stream_session_t *s;
547   session_kv6_t kv6;
548   int rv;
549
550   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
551   rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6);
552   if (rv == 0)
553     {
554       s = stream_session_get_from_handle (kv6.value);
555       return tp_vfts[s->session_type].get_connection (s->connection_index,
556                                                       s->thread_index);
557     }
558
559   /* If nothing is found, check if any listener is available */
560   s = stream_session_lookup_listener6 (lcl, lcl_port, proto);
561   if (s)
562     return tp_vfts[s->session_type].get_listener (s->connection_index);
563
564   /* Finally, try half-open connections */
565   rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6);
566   if (rv == 0)
567     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
568
569   return 0;
570 }
571
572 #define foreach_hash_table_parameter            \
573   _(v4,session,buckets,20000)                   \
574   _(v4,session,memory,(64<<20))                 \
575   _(v6,session,buckets,20000)                   \
576   _(v6,session,memory,(64<<20))                 \
577   _(v4,halfopen,buckets,20000)                  \
578   _(v4,halfopen,memory,(64<<20))                \
579   _(v6,halfopen,buckets,20000)                  \
580   _(v6,halfopen,memory,(64<<20))
581
582 void
583 session_lookup_init (void)
584 {
585   session_lookup_t *sl = &session_lookup;
586
587 #define _(af,table,parm,value) \
588   u32 configured_##af##_##table##_table_##parm = value;
589   foreach_hash_table_parameter;
590 #undef _
591
592 #define _(af,table,parm,value)                                          \
593   if (session_manager_main.configured_##af##_##table##_table_##parm)    \
594     configured_##af##_##table##_table_##parm =                          \
595       session_manager_main.configured_##af##_##table##_table_##parm;
596   foreach_hash_table_parameter;
597 #undef _
598
599   clib_bihash_init_16_8 (&sl->v4_session_hash, "v4 session table",
600                          configured_v4_session_table_buckets,
601                          configured_v4_session_table_memory);
602   clib_bihash_init_48_8 (&sl->v6_session_hash, "v6 session table",
603                          configured_v6_session_table_buckets,
604                          configured_v6_session_table_memory);
605   clib_bihash_init_16_8 (&sl->v4_half_open_hash, "v4 half-open table",
606                          configured_v4_halfopen_table_buckets,
607                          configured_v4_halfopen_table_memory);
608   clib_bihash_init_48_8 (&sl->v6_half_open_hash, "v6 half-open table",
609                          configured_v6_halfopen_table_buckets,
610                          configured_v6_halfopen_table_memory);
611 }
612
613 /*
614  * fd.io coding-style-patch-verification: ON
615  *
616  * Local Variables:
617  * eval: (c-set-style "gnu")
618  * End:
619  */