740c5a6d533819701a4386c22fd24d6e37055ff0
[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 #include <vnet/session/application.h>
31
32 /**
33  * External vector of per transport virtual functions table
34  */
35 extern transport_proto_vft_t *tp_vfts;
36
37 /**
38  * Network namespace index (i.e., fib index) to session lookup table. We
39  * should have one per network protocol type but for now we only support IP4/6
40  */
41 static u32 *fib_index_to_table_index[2];
42
43 /* *INDENT-OFF* */
44 /* 16 octets */
45 typedef CLIB_PACKED (struct {
46   union
47     {
48       struct
49         {
50           ip4_address_t src;
51           ip4_address_t dst;
52           u16 src_port;
53           u16 dst_port;
54           /* align by making this 4 octets even though its a 1-bit field
55            * NOTE: avoid key overlap with other transports that use 5 tuples for
56            * session identification.
57            */
58           u32 proto;
59         };
60       u64 as_u64[2];
61     };
62 }) v4_connection_key_t;
63
64 typedef CLIB_PACKED (struct {
65   union
66     {
67       struct
68         {
69           /* 48 octets */
70           ip6_address_t src;
71           ip6_address_t dst;
72           u16 src_port;
73           u16 dst_port;
74           u32 proto;
75           u64 unused;
76         };
77       u64 as_u64[6];
78     };
79 }) v6_connection_key_t;
80 /* *INDENT-ON* */
81
82 typedef clib_bihash_kv_16_8_t session_kv4_t;
83 typedef clib_bihash_kv_48_8_t session_kv6_t;
84
85 always_inline void
86 make_v4_ss_kv (session_kv4_t * kv, ip4_address_t * lcl, ip4_address_t * rmt,
87                u16 lcl_port, u16 rmt_port, u8 proto)
88 {
89   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
90
91   key->src.as_u32 = lcl->as_u32;
92   key->dst.as_u32 = rmt->as_u32;
93   key->src_port = lcl_port;
94   key->dst_port = rmt_port;
95   key->proto = proto;
96
97   kv->value = ~0ULL;
98 }
99
100 always_inline void
101 make_v4_listener_kv (session_kv4_t * kv, ip4_address_t * lcl, u16 lcl_port,
102                      u8 proto)
103 {
104   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
105
106   key->src.as_u32 = lcl->as_u32;
107   key->dst.as_u32 = 0;
108   key->src_port = lcl_port;
109   key->dst_port = 0;
110   key->proto = proto;
111
112   kv->value = ~0ULL;
113 }
114
115 always_inline void
116 make_v4_ss_kv_from_tc (session_kv4_t * kv, transport_connection_t * t)
117 {
118   make_v4_ss_kv (kv, &t->lcl_ip.ip4, &t->rmt_ip.ip4, t->lcl_port, t->rmt_port,
119                  session_type_from_proto_and_ip (t->proto, 1));
120 }
121
122 always_inline void
123 make_v6_ss_kv (session_kv6_t * kv, ip6_address_t * lcl, ip6_address_t * rmt,
124                u16 lcl_port, u16 rmt_port, u8 proto)
125 {
126   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
127
128   key->src.as_u64[0] = lcl->as_u64[0];
129   key->src.as_u64[1] = lcl->as_u64[1];
130   key->dst.as_u64[0] = rmt->as_u64[0];
131   key->dst.as_u64[1] = rmt->as_u64[1];
132   key->src_port = lcl_port;
133   key->dst_port = rmt_port;
134   key->proto = proto;
135   key->unused = 0;
136
137   kv->value = ~0ULL;
138 }
139
140 always_inline void
141 make_v6_listener_kv (session_kv6_t * kv, ip6_address_t * lcl, u16 lcl_port,
142                      u8 proto)
143 {
144   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
145
146   key->src.as_u64[0] = lcl->as_u64[0];
147   key->src.as_u64[1] = lcl->as_u64[1];
148   key->dst.as_u64[0] = 0;
149   key->dst.as_u64[1] = 0;
150   key->src_port = lcl_port;
151   key->dst_port = 0;
152   key->proto = proto;
153   key->unused = 0;
154
155   kv->value = ~0ULL;
156 }
157
158 always_inline void
159 make_v6_ss_kv_from_tc (session_kv6_t * kv, transport_connection_t * t)
160 {
161   make_v6_ss_kv (kv, &t->lcl_ip.ip6, &t->rmt_ip.ip6, t->lcl_port, t->rmt_port,
162                  session_type_from_proto_and_ip (t->proto, 0));
163 }
164
165
166 static session_table_t *
167 session_table_get_or_alloc_for_connection (transport_connection_t * tc)
168 {
169   session_table_t *st;
170   u32 table_index, fib_proto = transport_connection_fib_proto (tc);
171   if (vec_len (fib_index_to_table_index[fib_proto]) <= tc->fib_index)
172     {
173       st = session_table_alloc ();
174       table_index = session_table_index (st);
175       vec_validate (fib_index_to_table_index[fib_proto], tc->fib_index);
176       fib_index_to_table_index[fib_proto][tc->fib_index] = table_index;
177       return st;
178     }
179   else
180     {
181       table_index = fib_index_to_table_index[fib_proto][tc->fib_index];
182       return session_table_get (table_index);
183     }
184 }
185
186 static session_table_t *
187 session_table_get_for_connection (transport_connection_t * tc)
188 {
189   u32 fib_proto = transport_connection_fib_proto (tc);
190   if (vec_len (fib_index_to_table_index[fib_proto]) <= tc->fib_index)
191     return 0;
192   return
193     session_table_get (fib_index_to_table_index[fib_proto][tc->fib_index]);
194 }
195
196 static session_table_t *
197 session_table_get_for_fib_index (u32 fib_proto, u32 fib_index)
198 {
199   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
200     return 0;
201   return session_table_get (fib_index_to_table_index[fib_proto][fib_index]);
202 }
203
204 u32
205 session_lookup_get_index_for_fib (u32 fib_proto, u32 fib_index)
206 {
207   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
208     return SESSION_TABLE_INVALID_INDEX;
209   return fib_index_to_table_index[fib_proto][fib_index];
210 }
211
212 /**
213  * Add transport connection to a session table
214  *
215  * Session lookup 5-tuple (src-ip, dst-ip, src-port, dst-port, session-type)
216  * is added to requested session table.
217  *
218  * @param tc            transport connection to be added
219  * @param value         value to be stored
220  *
221  * @return non-zero if failure
222  */
223 int
224 session_lookup_add_connection (transport_connection_t * tc, u64 value)
225 {
226   session_table_t *st;
227   session_kv4_t kv4;
228   session_kv6_t kv6;
229
230   st = session_table_get_or_alloc_for_connection (tc);
231   if (!st)
232     return -1;
233   if (tc->is_ip4)
234     {
235       make_v4_ss_kv_from_tc (&kv4, tc);
236       kv4.value = value;
237       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4,
238                                        1 /* is_add */ );
239     }
240   else
241     {
242       make_v6_ss_kv_from_tc (&kv6, tc);
243       kv6.value = value;
244       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6,
245                                        1 /* is_add */ );
246     }
247 }
248
249 int
250 session_lookup_add_session_endpoint (u32 table_index,
251                                      session_endpoint_t * sep, u64 value)
252 {
253   session_table_t *st;
254   session_kv4_t kv4;
255   session_kv6_t kv6;
256
257   st = session_table_get (table_index);
258   if (!st)
259     return -1;
260   if (sep->is_ip4)
261     {
262       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
263                            sep->transport_proto);
264       kv4.value = value;
265       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 1);
266     }
267   else
268     {
269       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
270                            sep->transport_proto);
271       kv6.value = value;
272       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 1);
273     }
274 }
275
276 int
277 session_lookup_del_session_endpoint (u32 table_index,
278                                      session_endpoint_t * sep)
279 {
280   session_table_t *st;
281   session_kv4_t kv4;
282   session_kv6_t kv6;
283
284   st = session_table_get (table_index);
285   if (!st)
286     return -1;
287   if (sep->is_ip4)
288     {
289       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
290                            sep->transport_proto);
291       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 0);
292     }
293   else
294     {
295       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
296                            sep->transport_proto);
297       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 0);
298     }
299 }
300
301 /**
302  * Delete transport connection from session table
303  *
304  * @param table_index   session table index
305  * @param tc            transport connection to be removed
306  *
307  * @return non-zero if failure
308  */
309 int
310 session_lookup_del_connection (transport_connection_t * tc)
311 {
312   session_table_t *st;
313   session_kv4_t kv4;
314   session_kv6_t kv6;
315
316   st = session_table_get_for_connection (tc);
317   if (!st)
318     return -1;
319   if (tc->is_ip4)
320     {
321       make_v4_ss_kv_from_tc (&kv4, tc);
322       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4,
323                                        0 /* is_add */ );
324     }
325   else
326     {
327       make_v6_ss_kv_from_tc (&kv6, tc);
328       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6,
329                                        0 /* is_add */ );
330     }
331 }
332
333 int
334 session_lookup_del_session (stream_session_t * s)
335 {
336   transport_connection_t *ts;
337   ts = tp_vfts[s->session_type].get_connection (s->connection_index,
338                                                 s->thread_index);
339   return session_lookup_del_connection (ts);
340 }
341
342 u64
343 session_lookup_session_endpoint (u32 table_index, session_endpoint_t * sep)
344 {
345   session_table_t *st;
346   session_kv4_t kv4;
347   session_kv6_t kv6;
348   int rv;
349
350   st = session_table_get (table_index);
351   if (!st)
352     return SESSION_INVALID_HANDLE;
353   if (sep->is_ip4)
354     {
355       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
356                            sep->transport_proto);
357       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
358       if (rv == 0)
359         return kv4.value;
360     }
361   else
362     {
363       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
364                            sep->transport_proto);
365       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
366       if (rv == 0)
367         return kv6.value;
368     }
369   return SESSION_INVALID_HANDLE;
370 }
371
372 stream_session_t *
373 session_lookup_global_session_endpoint (session_endpoint_t * sep)
374 {
375   session_table_t *st;
376   session_kv4_t kv4;
377   session_kv6_t kv6;
378   u8 fib_proto;
379   u32 table_index;
380   int rv;
381
382   fib_proto = session_endpoint_fib_proto (sep);
383   table_index = session_lookup_get_index_for_fib (fib_proto, sep->fib_index);
384   st = session_table_get (table_index);
385   if (!st)
386     return 0;
387   if (sep->is_ip4)
388     {
389       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
390                            sep->transport_proto);
391       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
392       if (rv == 0)
393         return session_get_from_handle (kv4.value);
394     }
395   else
396     {
397       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
398                            sep->transport_proto);
399       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
400       if (rv == 0)
401         return session_get_from_handle (kv6.value);
402     }
403   return 0;
404 }
405
406 u32
407 session_lookup_local_session_endpoint (u32 table_index,
408                                        session_endpoint_t * sep)
409 {
410   session_table_t *st;
411   session_kv4_t kv4;
412   session_kv6_t kv6;
413   int rv;
414
415   st = session_table_get (table_index);
416   if (!st)
417     return SESSION_INVALID_INDEX;
418   if (sep->is_ip4)
419     {
420       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
421                            sep->transport_proto);
422       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
423       if (rv == 0)
424         return (u32) kv4.value;
425
426       /*
427        * Zero out the ip. Logic is that connect to local ips, say
428        * 127.0.0.1:port, can match 0.0.0.0:port
429        */
430       kv4.key[0] = 0;
431       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
432       if (rv == 0)
433         return (u32) kv4.value;
434     }
435   else
436     {
437       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
438                            sep->transport_proto);
439       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
440       if (rv == 0)
441         return (u32) kv6.value;
442
443       /*
444        * Zero out the ip. Same logic as above.
445        */
446       kv6.key[0] = kv6.key[1] = 0;
447       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
448       if (rv == 0)
449         return (u32) kv6.value;
450     }
451   return SESSION_INVALID_INDEX;
452 }
453
454 static stream_session_t *
455 session_lookup_listener4_i (session_table_t * st, ip4_address_t * lcl,
456                             u16 lcl_port, u8 proto)
457 {
458   session_kv4_t kv4;
459   int rv;
460
461   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
462   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
463   if (rv == 0)
464     return session_manager_get_listener (proto, (u32) kv4.value);
465
466   /* Zero out the lcl ip */
467   kv4.key[0] = 0;
468   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
469   if (rv == 0)
470     return session_manager_get_listener (proto, (u32) kv4.value);
471
472   return 0;
473 }
474
475 stream_session_t *
476 session_lookup_listener4 (u32 fib_index, ip4_address_t * lcl, u16 lcl_port,
477                           u8 proto)
478 {
479   session_table_t *st;
480   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
481   if (!st)
482     return 0;
483   return session_lookup_listener4_i (st, lcl, lcl_port, proto);
484 }
485
486 static stream_session_t *
487 session_lookup_listener6_i (session_table_t * st, ip6_address_t * lcl,
488                             u16 lcl_port, u8 proto)
489 {
490   session_kv6_t kv6;
491   int rv;
492
493   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
494   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
495   if (rv == 0)
496     return session_manager_get_listener (proto, (u32) kv6.value);
497
498   /* Zero out the lcl ip */
499   kv6.key[0] = kv6.key[1] = 0;
500   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
501   if (rv == 0)
502     return session_manager_get_listener (proto, (u32) kv6.value);
503
504   return 0;
505 }
506
507 stream_session_t *
508 session_lookup_listener6 (u32 fib_index, ip6_address_t * lcl, u16 lcl_port,
509                           u8 proto)
510 {
511   session_table_t *st;
512   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
513   if (!st)
514     return 0;
515   return session_lookup_listener6_i (st, lcl, lcl_port, proto);
516 }
517
518 stream_session_t *
519 session_lookup_listener (u32 table_index, session_endpoint_t * sep)
520 {
521   session_table_t *st;
522   st = session_table_get (table_index);
523   if (!st)
524     return 0;
525   if (sep->is_ip4)
526     return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port,
527                                        sep->transport_proto);
528   else
529     return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port,
530                                        sep->transport_proto);
531   return 0;
532 }
533
534 int
535 session_lookup_add_half_open (transport_connection_t * tc, u64 value)
536 {
537   session_table_t *st;
538   session_kv4_t kv4;
539   session_kv6_t kv6;
540
541   st = session_table_get_or_alloc_for_connection (tc);
542   if (!st)
543     return 0;
544   if (tc->is_ip4)
545     {
546       make_v4_ss_kv_from_tc (&kv4, tc);
547       kv4.value = value;
548       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
549                                        1 /* is_add */ );
550     }
551   else
552     {
553       make_v6_ss_kv_from_tc (&kv6, tc);
554       kv6.value = value;
555       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
556                                        1 /* is_add */ );
557     }
558 }
559
560 int
561 session_lookup_del_half_open (transport_connection_t * tc)
562 {
563   session_table_t *st;
564   session_kv4_t kv4;
565   session_kv6_t kv6;
566
567   st = session_table_get_for_connection (tc);
568   if (!st)
569     return -1;
570   if (tc->is_ip4)
571     {
572       make_v4_ss_kv_from_tc (&kv4, tc);
573       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
574                                        0 /* is_add */ );
575     }
576   else
577     {
578       make_v6_ss_kv_from_tc (&kv6, tc);
579       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
580                                        0 /* is_add */ );
581     }
582 }
583
584 u64
585 session_lookup_half_open_handle (transport_connection_t * tc)
586 {
587   session_table_t *st;
588   session_kv4_t kv4;
589   session_kv6_t kv6;
590   int rv;
591
592   st = session_table_get_for_fib_index (transport_connection_fib_proto (tc),
593                                         tc->fib_index);
594   if (!st)
595     return HALF_OPEN_LOOKUP_INVALID_VALUE;
596   if (tc->is_ip4)
597     {
598       make_v4_ss_kv (&kv4, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
599                      tc->rmt_port, tc->proto);
600       rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
601       if (rv == 0)
602         return kv4.value;
603     }
604   else
605     {
606       make_v6_ss_kv (&kv6, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
607                      tc->rmt_port, tc->proto);
608       rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
609       if (rv == 0)
610         return kv6.value;
611     }
612   return HALF_OPEN_LOOKUP_INVALID_VALUE;
613 }
614
615 transport_connection_t *
616 session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4)
617 {
618   u32 sst;
619
620   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
621     {
622       sst = session_type_from_proto_and_ip (proto, is_ip4);
623       return tp_vfts[sst].get_half_open (handle & 0xFFFFFFFF);
624     }
625   return 0;
626 }
627
628 /**
629  * Lookup connection with ip4 and transport layer information
630  *
631  * This is used on the fast path so it needs to be fast. Thereby,
632  * duplication of code and 'hacks' allowed.
633  *
634  * The lookup is incremental and returns whenever something is matched. The
635  * steps are:
636  * - Try to find an established session
637  * - Try to find a fully-formed or local source wildcarded (listener bound to
638  *   all interfaces) listener session
639  * - Try to find a half-open connection
640  * - return 0
641  *
642  * @param fib_index     index of fib wherein the connection was received
643  * @param lcl           local ip4 address
644  * @param rmt           remote ip4 address
645  * @param lcl_port      local port
646  * @param rmt_port      remote port
647  * @param proto         transport protocol (e.g., tcp, udp)
648  * @param thread_index  thread index for request
649  *
650  * @return pointer to transport connection, if one is found, 0 otherwise
651  */
652 transport_connection_t *
653 session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl,
654                                ip4_address_t * rmt, u16 lcl_port,
655                                u16 rmt_port, u8 proto, u32 thread_index)
656 {
657   session_table_t *st;
658   session_kv4_t kv4;
659   stream_session_t *s;
660   int rv;
661
662   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
663   if (PREDICT_FALSE (!st))
664     return 0;
665
666   /* Lookup session amongst established ones */
667   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
668   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
669   if (rv == 0)
670     {
671       ASSERT ((u32) (kv4.value >> 32) == thread_index);
672       s = session_get (kv4.value & 0xFFFFFFFFULL, thread_index);
673       return tp_vfts[s->session_type].get_connection (s->connection_index,
674                                                       thread_index);
675     }
676
677   /* If nothing is found, check if any listener is available */
678   s = session_lookup_listener4_i (st, lcl, lcl_port, proto);
679   if (s)
680     return tp_vfts[s->session_type].get_listener (s->connection_index);
681
682   /* Finally, try half-open connections */
683   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
684   if (rv == 0)
685     {
686       u32 sst = session_type_from_proto_and_ip (proto, 1);
687       return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF);
688     }
689   return 0;
690 }
691
692 /**
693  * Lookup connection with ip4 and transport layer information
694  *
695  * Not optimized. This is used on the fast path so it needs to be fast.
696  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
697  * to that of @ref session_lookup_connection_wt4
698  *
699  * @param fib_index     index of the fib wherein the connection was received
700  * @param lcl           local ip4 address
701  * @param rmt           remote ip4 address
702  * @param lcl_port      local port
703  * @param rmt_port      remote port
704  * @param proto         transport protocol (e.g., tcp, udp)
705  *
706  * @return pointer to transport connection, if one is found, 0 otherwise
707  */
708 transport_connection_t *
709 session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl,
710                             ip4_address_t * rmt, u16 lcl_port, u16 rmt_port,
711                             u8 proto)
712 {
713   session_table_t *st;
714   session_kv4_t kv4;
715   stream_session_t *s;
716   int rv;
717
718   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
719   if (PREDICT_FALSE (!st))
720     return 0;
721
722   /* Lookup session amongst established ones */
723   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
724   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
725   if (rv == 0)
726     {
727       s = session_get_from_handle (kv4.value);
728       return tp_vfts[s->session_type].get_connection (s->connection_index,
729                                                       s->thread_index);
730     }
731
732   /* If nothing is found, check if any listener is available */
733   s = session_lookup_listener4_i (st, lcl, lcl_port, proto);
734   if (s)
735     return tp_vfts[s->session_type].get_listener (s->connection_index);
736
737   /* Finally, try half-open connections */
738   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
739   if (rv == 0)
740     {
741       u32 sst = session_type_from_proto_and_ip (proto, 1);
742       return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF);
743     }
744   return 0;
745 }
746
747 /**
748  * Lookup session with ip4 and transport layer information
749  *
750  * Important note: this may look into another thread's pool table and
751  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
752  * if needed as soon as possible.
753  *
754  * Lookup logic is similar to that of @ref session_lookup_connection_wt4 but
755  * this returns a session as opposed to a transport connection and it does not
756  * try to lookup half-open sessions.
757  *
758  * Typically used by dgram connections
759  */
760 stream_session_t *
761 session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
762                       u16 lcl_port, u16 rmt_port, u8 proto)
763 {
764   session_table_t *st;
765   session_kv4_t kv4;
766   stream_session_t *s;
767   int rv;
768
769   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
770   if (PREDICT_FALSE (!st))
771     return 0;
772
773   /* Lookup session amongst established ones */
774   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
775   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
776   if (rv == 0)
777     return session_get_from_handle_safe (kv4.value);
778
779   /* If nothing is found, check if any listener is available */
780   if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto)))
781     return s;
782   return 0;
783 }
784
785 /**
786  * Lookup connection with ip6 and transport layer information
787  *
788  * This is used on the fast path so it needs to be fast. Thereby,
789  * duplication of code and 'hacks' allowed.
790  *
791  * The lookup is incremental and returns whenever something is matched. The
792  * steps are:
793  * - Try to find an established session
794  * - Try to find a fully-formed or local source wildcarded (listener bound to
795  *   all interfaces) listener session
796  * - Try to find a half-open connection
797  * - return 0
798  *
799  * @param fib_index     index of the fib wherein the connection was received
800  * @param lcl           local ip6 address
801  * @param rmt           remote ip6 address
802  * @param lcl_port      local port
803  * @param rmt_port      remote port
804  * @param proto         transport protocol (e.g., tcp, udp)
805  * @param thread_index  thread index for request
806  *
807  * @return pointer to transport connection, if one is found, 0 otherwise
808  */
809 transport_connection_t *
810 session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
811                                ip6_address_t * rmt, u16 lcl_port,
812                                u16 rmt_port, u8 proto, u32 thread_index)
813 {
814   session_table_t *st;
815   stream_session_t *s;
816   session_kv6_t kv6;
817   int rv;
818
819   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
820   if (PREDICT_FALSE (!st))
821     return 0;
822
823   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
824   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
825   if (rv == 0)
826     {
827       ASSERT ((u32) (kv6.value >> 32) == thread_index);
828       s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index);
829       return tp_vfts[s->session_type].get_connection (s->connection_index,
830                                                       thread_index);
831     }
832
833   /* If nothing is found, check if any listener is available */
834   s = session_lookup_listener6_i (st, lcl, lcl_port, proto);
835   if (s)
836     return tp_vfts[s->session_type].get_listener (s->connection_index);
837
838   /* Finally, try half-open connections */
839   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
840   if (rv == 0)
841     {
842       u32 sst = session_type_from_proto_and_ip (proto, 1);
843       return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF);
844     }
845
846   return 0;
847 }
848
849 /**
850  * Lookup connection with ip6 and transport layer information
851  *
852  * Not optimized. This is used on the fast path so it needs to be fast.
853  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
854  * to that of @ref session_lookup_connection_wt4
855  *
856  * @param fib_index     index of the fib wherein the connection was received
857  * @param lcl           local ip6 address
858  * @param rmt           remote ip6 address
859  * @param lcl_port      local port
860  * @param rmt_port      remote port
861  * @param proto         transport protocol (e.g., tcp, udp)
862  *
863  * @return pointer to transport connection, if one is found, 0 otherwise
864  */
865 transport_connection_t *
866 session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
867                             ip6_address_t * rmt, u16 lcl_port, u16 rmt_port,
868                             u8 proto)
869 {
870   session_table_t *st;
871   stream_session_t *s;
872   session_kv6_t kv6;
873   int rv;
874
875   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
876   if (PREDICT_FALSE (!st))
877     return 0;
878
879   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
880   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
881   if (rv == 0)
882     {
883       s = session_get_from_handle (kv6.value);
884       return tp_vfts[s->session_type].get_connection (s->connection_index,
885                                                       s->thread_index);
886     }
887
888   /* If nothing is found, check if any listener is available */
889   s = session_lookup_listener6 (fib_index, lcl, lcl_port, proto);
890   if (s)
891     return tp_vfts[s->session_type].get_listener (s->connection_index);
892
893   /* Finally, try half-open connections */
894   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
895   if (rv == 0)
896     {
897       u32 sst = session_type_from_proto_and_ip (proto, 1);
898       return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF);
899     }
900
901   return 0;
902 }
903
904 /**
905  * Lookup session with ip6 and transport layer information
906  *
907  * Important note: this may look into another thread's pool table and
908  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
909  * if needed as soon as possible.
910  *
911  * Lookup logic is similar to that of @ref session_lookup_connection_wt6 but
912  * this returns a session as opposed to a transport connection and it does not
913  * try to lookup half-open sessions.
914  *
915  * Typically used by dgram connections
916  */
917 stream_session_t *
918 session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
919                       u16 lcl_port, u16 rmt_port, u8 proto)
920 {
921   session_table_t *st;
922   session_kv6_t kv6;
923   stream_session_t *s;
924   int rv;
925
926   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
927   if (PREDICT_FALSE (!st))
928     return 0;
929
930   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
931   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
932   if (rv == 0)
933     return session_get_from_handle_safe (kv6.value);
934
935   /* If nothing is found, check if any listener is available */
936   if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto)))
937     return s;
938   return 0;
939 }
940
941 u64
942 session_lookup_local_listener_make_handle (session_endpoint_t * sep)
943 {
944   return ((u64) SESSION_LOCAL_TABLE_PREFIX << 32
945           | (u32) sep->port << 16 | (u32) sep->transport_proto << 8
946           | (u32) sep->is_ip4);
947 }
948
949 u8
950 session_lookup_local_is_handle (u64 handle)
951 {
952   if (handle >> 32 == SESSION_LOCAL_TABLE_PREFIX)
953     return 1;
954   return 0;
955 }
956
957 int
958 session_lookup_local_listener_parse_handle (u64 handle,
959                                             session_endpoint_t * sep)
960 {
961   u32 local_table_handle;
962   if (handle >> 32 != SESSION_LOCAL_TABLE_PREFIX)
963     return -1;
964   local_table_handle = handle & 0xFFFFFFFFULL;
965   sep->is_ip4 = local_table_handle & 0xff;
966   local_table_handle >>= 8;
967   sep->transport_proto = local_table_handle & 0xff;
968   sep->port = local_table_handle >> 8;
969   return 0;
970 }
971
972 u8 *
973 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
974 {
975   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
976   u32 is_local = va_arg (*args, u32);
977   u8 *app_name, *str = 0;
978   stream_session_t *session;
979   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
980
981   char *proto = key->proto == TRANSPORT_PROTO_TCP ? "T" : "U";
982   if (!is_local)
983     {
984       session = session_get_from_handle (kvp->value);
985       app_name = application_name_from_index (session->app_index);
986       str = format (0, "[%s] %U:%d->%U:%d", proto, format_ip4_address,
987                     &key->src, clib_net_to_host_u16 (key->src_port),
988                     format_ip4_address, &key->dst,
989                     clib_net_to_host_u16 (key->dst_port));
990       s = format (s, "%-40v%-30v", str, app_name);
991     }
992   else
993     {
994       app_name = application_name_from_index (kvp->value);
995       str = format (0, "[%s] %U:%d", proto, format_ip4_address,
996                     &key->src, clib_net_to_host_u16 (key->src_port));
997       s = format (s, "%-30v%-30v", str, app_name);
998     }
999   vec_free (app_name);
1000   return s;
1001 }
1002
1003 typedef struct _ip4_session_table_show_ctx_t
1004 {
1005   vlib_main_t *vm;
1006   u8 is_local;
1007 } ip4_session_table_show_ctx_t;
1008
1009 static int
1010 ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg)
1011 {
1012   ip4_session_table_show_ctx_t *ctx = arg;
1013   vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp,
1014                    ctx->is_local);
1015   return 1;
1016 }
1017
1018 void
1019 session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table,
1020                                    u8 type, u8 is_local)
1021 {
1022   ip4_session_table_show_ctx_t ctx = {
1023     .vm = vm,
1024     .is_local = is_local,
1025   };
1026   if (!is_local)
1027     vlib_cli_output (vm, "%-40s%-30s", "Session", "Application");
1028   else
1029     vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application");
1030   switch (type)
1031     {
1032       /* main table v4 */
1033     case 0:
1034       ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show,
1035                               &ctx);
1036       break;
1037     default:
1038       clib_warning ("not supported");
1039     }
1040 }
1041
1042 void
1043 session_lookup_init (void)
1044 {
1045   /*
1046    * Allocate default table and map it to fib_index 0
1047    */
1048   session_table_t *st = session_table_alloc ();
1049   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0);
1050   fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st);
1051   session_table_init (st);
1052   st = session_table_alloc ();
1053   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0);
1054   fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st);
1055   session_table_init (st);
1056 }
1057
1058 /*
1059  * fd.io coding-style-patch-verification: ON
1060  *
1061  * Local Variables:
1062  * eval: (c-set-style "gnu")
1063  * End:
1064  */