session: add support for application namespacing
[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->transport_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->transport_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 u32
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_INDEX;
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 (u32) 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 (u32) kv6.value;
368     }
369   return SESSION_INVALID_INDEX;
370 }
371
372 u32
373 session_lookup_local_session_endpoint (u32 table_index,
374                                        session_endpoint_t * sep)
375 {
376   session_table_t *st;
377   session_kv4_t kv4;
378   session_kv6_t kv6;
379   int rv;
380
381   st = session_table_get (table_index);
382   if (!st)
383     return SESSION_INVALID_INDEX;
384   if (sep->is_ip4)
385     {
386       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
387                            sep->transport_proto);
388       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
389       if (rv == 0)
390         return (u32) kv4.value;
391
392       /*
393        * Zero out the ip. Logic is that connect to local ips, say
394        * 127.0.0.1:port, can match 0.0.0.0:port
395        */
396       kv4.key[0] = 0;
397       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
398       if (rv == 0)
399         return (u32) kv4.value;
400     }
401   else
402     {
403       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
404                            sep->transport_proto);
405       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
406       if (rv == 0)
407         return (u32) kv6.value;
408
409       /*
410        * Zero out the ip. Same logic as above.
411        */
412       kv6.key[0] = kv6.key[1] = 0;
413       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
414       if (rv == 0)
415         return (u32) kv6.value;
416     }
417   return SESSION_INVALID_INDEX;
418 }
419
420 static stream_session_t *
421 session_lookup_listener4_i (session_table_t * st, ip4_address_t * lcl,
422                             u16 lcl_port, u8 proto)
423 {
424   session_kv4_t kv4;
425   int rv;
426
427   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
428   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
429   if (rv == 0)
430     return session_manager_get_listener (proto, (u32) kv4.value);
431
432   /* Zero out the lcl ip */
433   kv4.key[0] = 0;
434   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
435   if (rv == 0)
436     return session_manager_get_listener (proto, (u32) kv4.value);
437
438   return 0;
439 }
440
441 stream_session_t *
442 session_lookup_listener4 (u32 fib_index, ip4_address_t * lcl, u16 lcl_port,
443                           u8 proto)
444 {
445   session_table_t *st;
446   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
447   if (!st)
448     return 0;
449   return session_lookup_listener4_i (st, lcl, lcl_port, proto);
450 }
451
452 static stream_session_t *
453 session_lookup_listener6_i (session_table_t * st, ip6_address_t * lcl,
454                             u16 lcl_port, u8 proto)
455 {
456   session_kv6_t kv6;
457   int rv;
458
459   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
460   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
461   if (rv == 0)
462     return session_manager_get_listener (proto, (u32) kv6.value);
463
464   /* Zero out the lcl ip */
465   kv6.key[0] = kv6.key[1] = 0;
466   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
467   if (rv == 0)
468     return session_manager_get_listener (proto, (u32) kv6.value);
469
470   return 0;
471 }
472
473 stream_session_t *
474 session_lookup_listener6 (u32 fib_index, ip6_address_t * lcl, u16 lcl_port,
475                           u8 proto)
476 {
477   session_table_t *st;
478   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
479   if (!st)
480     return 0;
481   return session_lookup_listener6_i (st, lcl, lcl_port, proto);
482 }
483
484 stream_session_t *
485 session_lookup_listener (u32 table_index, session_endpoint_t * sep)
486 {
487   session_table_t *st;
488   st = session_table_get (table_index);
489   if (!st)
490     return 0;
491   if (sep->is_ip4)
492     return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port,
493                                        sep->transport_proto);
494   else
495     return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port,
496                                        sep->transport_proto);
497   return 0;
498 }
499
500 int
501 session_lookup_add_half_open (transport_connection_t * tc, u64 value)
502 {
503   session_table_t *st;
504   session_kv4_t kv4;
505   session_kv6_t kv6;
506
507   st = session_table_get_or_alloc_for_connection (tc);
508   if (!st)
509     return 0;
510   if (tc->is_ip4)
511     {
512       make_v4_ss_kv_from_tc (&kv4, tc);
513       kv4.value = value;
514       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
515                                        1 /* is_add */ );
516     }
517   else
518     {
519       make_v6_ss_kv_from_tc (&kv6, tc);
520       kv6.value = value;
521       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
522                                        1 /* is_add */ );
523     }
524 }
525
526 int
527 session_lookup_del_half_open (transport_connection_t * tc)
528 {
529   session_table_t *st;
530   session_kv4_t kv4;
531   session_kv6_t kv6;
532
533   st = session_table_get_for_connection (tc);
534   if (!st)
535     return -1;
536   if (tc->is_ip4)
537     {
538       make_v4_ss_kv_from_tc (&kv4, tc);
539       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
540                                        0 /* is_add */ );
541     }
542   else
543     {
544       make_v6_ss_kv_from_tc (&kv6, tc);
545       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
546                                        0 /* is_add */ );
547     }
548 }
549
550 u64
551 session_lookup_half_open_handle (transport_connection_t * tc)
552 {
553   session_table_t *st;
554   session_kv4_t kv4;
555   session_kv6_t kv6;
556   int rv;
557
558   st = session_table_get_for_fib_index (transport_connection_fib_proto (tc),
559                                         tc->fib_index);
560   if (!st)
561     return HALF_OPEN_LOOKUP_INVALID_VALUE;
562   if (tc->is_ip4)
563     {
564       make_v4_ss_kv (&kv4, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
565                      tc->rmt_port, tc->transport_proto);
566       rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
567       if (rv == 0)
568         return kv4.value;
569     }
570   else
571     {
572       make_v6_ss_kv (&kv6, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
573                      tc->rmt_port, tc->transport_proto);
574       rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
575       if (rv == 0)
576         return kv6.value;
577     }
578   return HALF_OPEN_LOOKUP_INVALID_VALUE;
579 }
580
581 transport_connection_t *
582 session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4)
583 {
584   u32 sst;
585
586   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
587     {
588       sst = session_type_from_proto_and_ip (proto, is_ip4);
589       return tp_vfts[sst].get_half_open (handle & 0xFFFFFFFF);
590     }
591   return 0;
592 }
593
594 /**
595  * Lookup connection with ip4 and transport layer information
596  *
597  * This is used on the fast path so it needs to be fast. Thereby,
598  * duplication of code and 'hacks' allowed.
599  *
600  * The lookup is incremental and returns whenever something is matched. The
601  * steps are:
602  * - Try to find an established session
603  * - Try to find a fully-formed or local source wildcarded (listener bound to
604  *   all interfaces) listener session
605  * - Try to find a half-open connection
606  * - return 0
607  *
608  * @param fib_index     index of fib wherein the connection was received
609  * @param lcl           local ip4 address
610  * @param rmt           remote ip4 address
611  * @param lcl_port      local port
612  * @param rmt_port      remote port
613  * @param proto         transport protocol (e.g., tcp, udp)
614  * @param thread_index  thread index for request
615  *
616  * @return pointer to transport connection, if one is found, 0 otherwise
617  */
618 transport_connection_t *
619 session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl,
620                                ip4_address_t * rmt, u16 lcl_port,
621                                u16 rmt_port, u8 proto, u32 thread_index)
622 {
623   session_table_t *st;
624   session_kv4_t kv4;
625   stream_session_t *s;
626   int rv;
627
628   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
629   if (PREDICT_FALSE (!st))
630     return 0;
631
632   /* Lookup session amongst established ones */
633   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
634   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
635   if (rv == 0)
636     {
637       ASSERT ((u32) (kv4.value >> 32) == thread_index);
638       s = session_get (kv4.value & 0xFFFFFFFFULL, thread_index);
639       return tp_vfts[s->session_type].get_connection (s->connection_index,
640                                                       thread_index);
641     }
642
643   /* If nothing is found, check if any listener is available */
644   s = session_lookup_listener4_i (st, lcl, lcl_port, proto);
645   if (s)
646     return tp_vfts[s->session_type].get_listener (s->connection_index);
647
648   /* Finally, try half-open connections */
649   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
650   if (rv == 0)
651     {
652       u32 sst = session_type_from_proto_and_ip (proto, 1);
653       return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF);
654     }
655   return 0;
656 }
657
658 /**
659  * Lookup connection with ip4 and transport layer information
660  *
661  * Not optimized. This is used on the fast path so it needs to be fast.
662  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
663  * to that of @ref session_lookup_connection_wt4
664  *
665  * @param fib_index     index of the fib wherein the connection was received
666  * @param lcl           local ip4 address
667  * @param rmt           remote ip4 address
668  * @param lcl_port      local port
669  * @param rmt_port      remote port
670  * @param proto         transport protocol (e.g., tcp, udp)
671  *
672  * @return pointer to transport connection, if one is found, 0 otherwise
673  */
674 transport_connection_t *
675 session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl,
676                             ip4_address_t * rmt, u16 lcl_port, u16 rmt_port,
677                             u8 proto)
678 {
679   session_table_t *st;
680   session_kv4_t kv4;
681   stream_session_t *s;
682   int rv;
683
684   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
685   if (PREDICT_FALSE (!st))
686     return 0;
687
688   /* Lookup session amongst established ones */
689   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
690   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
691   if (rv == 0)
692     {
693       s = session_get_from_handle (kv4.value);
694       return tp_vfts[s->session_type].get_connection (s->connection_index,
695                                                       s->thread_index);
696     }
697
698   /* If nothing is found, check if any listener is available */
699   s = session_lookup_listener4_i (st, lcl, lcl_port, proto);
700   if (s)
701     return tp_vfts[s->session_type].get_listener (s->connection_index);
702
703   /* Finally, try half-open connections */
704   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
705   if (rv == 0)
706     {
707       u32 sst = session_type_from_proto_and_ip (proto, 1);
708       return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF);
709     }
710   return 0;
711 }
712
713 /**
714  * Lookup session with ip4 and transport layer information
715  *
716  * Lookup logic is identical to that of @ref session_lookup_connection_wt4 but
717  * this returns a session as opposed to a transport connection;
718  */
719 stream_session_t *
720 session_lookup4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
721                  u16 lcl_port, u16 rmt_port, u8 proto)
722 {
723   session_table_t *st;
724   session_kv4_t kv4;
725   stream_session_t *s;
726   int rv;
727
728   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
729   if (PREDICT_FALSE (!st))
730     return 0;
731
732   /* Lookup session amongst established ones */
733   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
734   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
735   if (rv == 0)
736     return session_get_from_handle (kv4.value);
737
738   /* If nothing is found, check if any listener is available */
739   if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto)))
740     return s;
741
742   /* Finally, try half-open connections */
743   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
744   if (rv == 0)
745     return session_get_from_handle (kv4.value);
746   return 0;
747 }
748
749 /**
750  * Lookup connection with ip6 and transport layer information
751  *
752  * This is used on the fast path so it needs to be fast. Thereby,
753  * duplication of code and 'hacks' allowed.
754  *
755  * The lookup is incremental and returns whenever something is matched. The
756  * steps are:
757  * - Try to find an established session
758  * - Try to find a fully-formed or local source wildcarded (listener bound to
759  *   all interfaces) listener session
760  * - Try to find a half-open connection
761  * - return 0
762  *
763  * @param fib_index     index of the fib wherein the connection was received
764  * @param lcl           local ip6 address
765  * @param rmt           remote ip6 address
766  * @param lcl_port      local port
767  * @param rmt_port      remote port
768  * @param proto         transport protocol (e.g., tcp, udp)
769  * @param thread_index  thread index for request
770  *
771  * @return pointer to transport connection, if one is found, 0 otherwise
772  */
773 transport_connection_t *
774 session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
775                                ip6_address_t * rmt, u16 lcl_port,
776                                u16 rmt_port, u8 proto, u32 thread_index)
777 {
778   session_table_t *st;
779   stream_session_t *s;
780   session_kv6_t kv6;
781   int rv;
782
783   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
784   if (PREDICT_FALSE (!st))
785     return 0;
786
787   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
788   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
789   if (rv == 0)
790     {
791       ASSERT ((u32) (kv6.value >> 32) == thread_index);
792       s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index);
793       return tp_vfts[s->session_type].get_connection (s->connection_index,
794                                                       thread_index);
795     }
796
797   /* If nothing is found, check if any listener is available */
798   s = session_lookup_listener6_i (st, lcl, lcl_port, proto);
799   if (s)
800     return tp_vfts[s->session_type].get_listener (s->connection_index);
801
802   /* Finally, try half-open connections */
803   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
804   if (rv == 0)
805     {
806       u32 sst = session_type_from_proto_and_ip (proto, 1);
807       return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF);
808     }
809
810   return 0;
811 }
812
813 /**
814  * Lookup connection with ip6 and transport layer information
815  *
816  * Not optimized. This is used on the fast path so it needs to be fast.
817  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
818  * to that of @ref session_lookup_connection_wt4
819  *
820  * @param fib_index     index of the fib wherein the connection was received
821  * @param lcl           local ip6 address
822  * @param rmt           remote ip6 address
823  * @param lcl_port      local port
824  * @param rmt_port      remote port
825  * @param proto         transport protocol (e.g., tcp, udp)
826  *
827  * @return pointer to transport connection, if one is found, 0 otherwise
828  */
829 transport_connection_t *
830 session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
831                             ip6_address_t * rmt, u16 lcl_port, u16 rmt_port,
832                             u8 proto)
833 {
834   session_table_t *st;
835   stream_session_t *s;
836   session_kv6_t kv6;
837   int rv;
838
839   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
840   if (PREDICT_FALSE (!st))
841     return 0;
842
843   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
844   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
845   if (rv == 0)
846     {
847       s = session_get_from_handle (kv6.value);
848       return tp_vfts[s->session_type].get_connection (s->connection_index,
849                                                       s->thread_index);
850     }
851
852   /* If nothing is found, check if any listener is available */
853   s = session_lookup_listener6 (fib_index, lcl, lcl_port, proto);
854   if (s)
855     return tp_vfts[s->session_type].get_listener (s->connection_index);
856
857   /* Finally, try half-open connections */
858   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
859   if (rv == 0)
860     {
861       u32 sst = session_type_from_proto_and_ip (proto, 1);
862       return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF);
863     }
864
865   return 0;
866 }
867
868 /**
869  * Lookup session with ip6 and transport layer information
870  *
871  * Lookup logic is identical to that of @ref session_lookup_connection_wt6 but
872  * this returns a session as opposed to a transport connection;
873  */
874 stream_session_t *
875 session_lookup6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
876                  u16 lcl_port, u16 rmt_port, u8 proto)
877 {
878   session_table_t *st;
879   session_kv6_t kv6;
880   stream_session_t *s;
881   int rv;
882
883   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
884   if (PREDICT_FALSE (!st))
885     return 0;
886
887   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
888   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
889   if (rv == 0)
890     return session_get_from_handle (kv6.value);
891
892   /* If nothing is found, check if any listener is available */
893   if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto)))
894     return s;
895
896   /* Finally, try half-open connections */
897   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
898   if (rv == 0)
899     return session_get_from_handle (kv6.value);
900   return 0;
901 }
902
903 u64
904 session_lookup_local_listener_make_handle (session_endpoint_t * sep)
905 {
906   return ((u64) SESSION_LOCAL_TABLE_PREFIX << 32
907           | (u32) sep->port << 16 | (u32) sep->transport_proto << 8
908           | (u32) sep->is_ip4);
909 }
910
911 u8
912 session_lookup_local_is_handle (u64 handle)
913 {
914   if (handle >> 32 == SESSION_LOCAL_TABLE_PREFIX)
915     return 1;
916   return 0;
917 }
918
919 int
920 session_lookup_local_listener_parse_handle (u64 handle,
921                                             session_endpoint_t * sep)
922 {
923   u32 local_table_handle;
924   if (handle >> 32 != SESSION_LOCAL_TABLE_PREFIX)
925     return -1;
926   local_table_handle = handle & 0xFFFFFFFFULL;
927   sep->is_ip4 = local_table_handle & 0xff;
928   local_table_handle >>= 8;
929   sep->transport_proto = local_table_handle & 0xff;
930   sep->port = local_table_handle >> 8;
931   return 0;
932 }
933
934 u8 *
935 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
936 {
937   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
938   u32 is_local = va_arg (*args, u32);
939   u8 *app_name, *str = 0;
940   stream_session_t *session;
941   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
942
943   char *proto = key->proto == TRANSPORT_PROTO_TCP ? "T" : "U";
944   if (!is_local)
945     {
946       session = session_get_from_handle (kvp->value);
947       app_name = application_name_from_index (session->app_index);
948       str = format (0, "[%s] %U:%d->%U:%d", proto, format_ip4_address,
949                     &key->src, clib_net_to_host_u16 (key->src_port),
950                     format_ip4_address, &key->dst,
951                     clib_net_to_host_u16 (key->dst_port));
952       s = format (s, "%-40v%-30v", str, app_name);
953     }
954   else
955     {
956       app_name = application_name_from_index (kvp->value);
957       str = format (0, "[%s] %U:%d", proto, format_ip4_address,
958                     &key->src, clib_net_to_host_u16 (key->src_port));
959       s = format (s, "%-30v%-30v", str, app_name);
960     }
961   vec_free (app_name);
962   return s;
963 }
964
965 typedef struct _ip4_session_table_show_ctx_t
966 {
967   vlib_main_t *vm;
968   u8 is_local;
969 } ip4_session_table_show_ctx_t;
970
971 static int
972 ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg)
973 {
974   ip4_session_table_show_ctx_t *ctx = arg;
975   vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp,
976                    ctx->is_local);
977   return 1;
978 }
979
980 void
981 session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table,
982                                    u8 type, u8 is_local)
983 {
984   ip4_session_table_show_ctx_t ctx = {
985     .vm = vm,
986     .is_local = is_local,
987   };
988   if (!is_local)
989     vlib_cli_output (vm, "%-40s%-30s", "Session", "Application");
990   else
991     vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application");
992   switch (type)
993     {
994       /* main table v4 */
995     case 0:
996       ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show,
997                               &ctx);
998       break;
999     default:
1000       clib_warning ("not supported");
1001     }
1002 }
1003
1004 void
1005 session_lookup_init (void)
1006 {
1007   /*
1008    * Allocate default table and map it to fib_index 0
1009    */
1010   session_table_t *st = session_table_alloc ();
1011   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0);
1012   fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st);
1013   session_table_init (st);
1014   st = session_table_alloc ();
1015   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0);
1016   fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st);
1017   session_table_init (st);
1018 }
1019
1020 /*
1021  * fd.io coding-style-patch-verification: ON
1022  *
1023  * Local Variables:
1024  * eval: (c-set-style "gnu")
1025  * End:
1026  */