session: Add sock_name option to add_ns
[vpp.git] / src / vnet / session / session_lookup.c
1 /*
2  * Copyright (c) 2017-2019 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  * Network namespace index (i.e., fib index) to session lookup table. We
34  * should have one per network protocol type but for now we only support IP4/6
35  */
36 static u32 *fib_index_to_table_index[2];
37
38 /* *INDENT-OFF* */
39 /* 16 octets */
40 typedef CLIB_PACKED (struct {
41   union
42     {
43       struct
44         {
45           ip4_address_t src;
46           ip4_address_t dst;
47           u16 src_port;
48           u16 dst_port;
49           /* align by making this 4 octets even though its a 1-bit field
50            * NOTE: avoid key overlap with other transports that use 5 tuples for
51            * session identification.
52            */
53           u32 proto;
54         };
55       u64 as_u64[2];
56     };
57 }) v4_connection_key_t;
58
59 typedef CLIB_PACKED (struct {
60   union
61     {
62       struct
63         {
64           /* 48 octets */
65           ip6_address_t src;
66           ip6_address_t dst;
67           u16 src_port;
68           u16 dst_port;
69           u32 proto;
70           u64 unused;
71         };
72       u64 as_u64[6];
73     };
74 }) v6_connection_key_t;
75 /* *INDENT-ON* */
76
77 typedef clib_bihash_kv_16_8_t session_kv4_t;
78 typedef clib_bihash_kv_48_8_t session_kv6_t;
79
80 always_inline void
81 make_v4_ss_kv (session_kv4_t * kv, ip4_address_t * lcl, ip4_address_t * rmt,
82                u16 lcl_port, u16 rmt_port, u8 proto)
83 {
84   kv->key[0] = (u64) rmt->as_u32 << 32 | (u64) lcl->as_u32;
85   kv->key[1] = (u64) proto << 32 | (u64) rmt_port << 16 | (u64) lcl_port;
86   kv->value = ~0ULL;
87 }
88
89 always_inline void
90 make_v4_listener_kv (session_kv4_t * kv, ip4_address_t * lcl, u16 lcl_port,
91                      u8 proto)
92 {
93   kv->key[0] = (u64) lcl->as_u32;
94   kv->key[1] = (u64) proto << 32 | (u64) lcl_port;
95   kv->value = ~0ULL;
96 }
97
98 always_inline void
99 make_v4_proxy_kv (session_kv4_t * kv, ip4_address_t * lcl, u8 proto)
100 {
101   kv->key[0] = (u64) lcl->as_u32;
102   kv->key[1] = (u64) proto << 32;
103   kv->value = ~0ULL;
104 }
105
106 always_inline void
107 make_v4_ss_kv_from_tc (session_kv4_t * kv, transport_connection_t * tc)
108 {
109   make_v4_ss_kv (kv, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
110                  tc->rmt_port, tc->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   kv->key[0] = lcl->as_u64[0];
118   kv->key[1] = lcl->as_u64[1];
119   kv->key[2] = rmt->as_u64[0];
120   kv->key[3] = rmt->as_u64[1];
121   kv->key[4] = (u64) proto << 32 | (u64) rmt_port << 16 | (u64) lcl_port;
122   kv->key[5] = 0;
123   kv->value = ~0ULL;
124 }
125
126 always_inline void
127 make_v6_listener_kv (session_kv6_t * kv, ip6_address_t * lcl, u16 lcl_port,
128                      u8 proto)
129 {
130   kv->key[0] = lcl->as_u64[0];
131   kv->key[1] = lcl->as_u64[1];
132   kv->key[2] = 0;
133   kv->key[3] = 0;
134   kv->key[4] = (u64) proto << 32 | (u64) lcl_port;
135   kv->key[5] = 0;
136   kv->value = ~0ULL;
137 }
138
139 always_inline void
140 make_v6_proxy_kv (session_kv6_t * kv, ip6_address_t * lcl, u8 proto)
141 {
142   kv->key[0] = lcl->as_u64[0];
143   kv->key[1] = lcl->as_u64[1];
144   kv->key[2] = 0;
145   kv->key[3] = 0;
146   kv->key[4] = (u64) proto << 32;
147   kv->key[5] = 0;
148   kv->value = ~0ULL;
149 }
150
151 always_inline void
152 make_v6_ss_kv_from_tc (session_kv6_t * kv, transport_connection_t * tc)
153 {
154   make_v6_ss_kv (kv, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
155                  tc->rmt_port, tc->proto);
156 }
157
158 static session_table_t *
159 session_table_get_or_alloc (u8 fib_proto, u32 fib_index)
160 {
161   session_table_t *st;
162   u32 table_index;
163   ASSERT (fib_index != ~0);
164   if (vec_len (fib_index_to_table_index[fib_proto]) > fib_index &&
165       fib_index_to_table_index[fib_proto][fib_index] != ~0)
166     {
167       table_index = fib_index_to_table_index[fib_proto][fib_index];
168       return session_table_get (table_index);
169     }
170   else
171     {
172       st = session_table_alloc ();
173       table_index = session_table_index (st);
174       vec_validate_init_empty (fib_index_to_table_index[fib_proto], fib_index,
175                                ~0);
176       fib_index_to_table_index[fib_proto][fib_index] = table_index;
177       st->active_fib_proto = fib_proto;
178       session_table_init (st, fib_proto);
179       return st;
180     }
181 }
182
183 static session_table_t *
184 session_table_get_or_alloc_for_connection (transport_connection_t * tc)
185 {
186   u32 fib_proto;
187   fib_proto = transport_connection_fib_proto (tc);
188   return session_table_get_or_alloc (fib_proto, tc->fib_index);
189 }
190
191 static session_table_t *
192 session_table_get_for_connection (transport_connection_t * tc)
193 {
194   u32 fib_proto = transport_connection_fib_proto (tc);
195   if (vec_len (fib_index_to_table_index[fib_proto]) <= tc->fib_index)
196     return 0;
197   return
198     session_table_get (fib_index_to_table_index[fib_proto][tc->fib_index]);
199 }
200
201 static session_table_t *
202 session_table_get_for_fib_index (u32 fib_proto, u32 fib_index)
203 {
204   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
205     return 0;
206   return session_table_get (fib_index_to_table_index[fib_proto][fib_index]);
207 }
208
209 u32
210 session_lookup_get_index_for_fib (u32 fib_proto, u32 fib_index)
211 {
212   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
213     return SESSION_TABLE_INVALID_INDEX;
214   return fib_index_to_table_index[fib_proto][fib_index];
215 }
216
217 u32
218 session_lookup_get_or_alloc_index_for_fib (u32 fib_proto, u32 fib_index)
219 {
220   session_table_t *st;
221   st = session_table_get_or_alloc (fib_proto, fib_index);
222   return session_table_index (st);
223 }
224
225 /**
226  * Add transport connection to a session table
227  *
228  * Session lookup 5-tuple (src-ip, dst-ip, src-port, dst-port, session-type)
229  * is added to requested session table.
230  *
231  * @param tc            transport connection to be added
232  * @param value         value to be stored
233  *
234  * @return non-zero if failure
235  */
236 int
237 session_lookup_add_connection (transport_connection_t * tc, u64 value)
238 {
239   session_table_t *st;
240   session_kv4_t kv4;
241   session_kv6_t kv6;
242
243   st = session_table_get_or_alloc_for_connection (tc);
244   if (!st)
245     return -1;
246   if (tc->is_ip4)
247     {
248       make_v4_ss_kv_from_tc (&kv4, tc);
249       kv4.value = value;
250       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4,
251                                        1 /* is_add */ );
252     }
253   else
254     {
255       make_v6_ss_kv_from_tc (&kv6, tc);
256       kv6.value = value;
257       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6,
258                                        1 /* is_add */ );
259     }
260 }
261
262 int
263 session_lookup_add_session_endpoint (u32 table_index,
264                                      session_endpoint_t * sep, u64 value)
265 {
266   session_table_t *st;
267   session_kv4_t kv4;
268   session_kv6_t kv6;
269
270   st = session_table_get (table_index);
271   if (!st)
272     return -1;
273   if (sep->is_ip4)
274     {
275       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
276                            sep->transport_proto);
277       kv4.value = value;
278       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 1);
279     }
280   else
281     {
282       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
283                            sep->transport_proto);
284       kv6.value = value;
285       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 1);
286     }
287 }
288
289 int
290 session_lookup_del_session_endpoint (u32 table_index,
291                                      session_endpoint_t * sep)
292 {
293   session_table_t *st;
294   session_kv4_t kv4;
295   session_kv6_t kv6;
296
297   st = session_table_get (table_index);
298   if (!st)
299     return -1;
300   if (sep->is_ip4)
301     {
302       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
303                            sep->transport_proto);
304       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 0);
305     }
306   else
307     {
308       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
309                            sep->transport_proto);
310       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 0);
311     }
312 }
313
314 int
315 session_lookup_del_session_endpoint2 (session_endpoint_t * sep)
316 {
317   fib_protocol_t fib_proto;
318   session_table_t *st;
319   session_kv4_t kv4;
320   session_kv6_t kv6;
321
322   fib_proto = sep->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
323   st = session_table_get_for_fib_index (fib_proto, sep->fib_index);
324   if (!st)
325     return -1;
326   if (sep->is_ip4)
327     {
328       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
329                            sep->transport_proto);
330       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 0);
331     }
332   else
333     {
334       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
335                            sep->transport_proto);
336       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 0);
337     }
338 }
339
340 /**
341  * Delete transport connection from session table
342  *
343  * @param table_index   session table index
344  * @param tc            transport connection to be removed
345  *
346  * @return non-zero if failure
347  */
348 int
349 session_lookup_del_connection (transport_connection_t * tc)
350 {
351   session_table_t *st;
352   session_kv4_t kv4;
353   session_kv6_t kv6;
354
355   st = session_table_get_for_connection (tc);
356   if (!st)
357     return -1;
358   if (tc->is_ip4)
359     {
360       make_v4_ss_kv_from_tc (&kv4, tc);
361       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4,
362                                        0 /* is_add */ );
363     }
364   else
365     {
366       make_v6_ss_kv_from_tc (&kv6, tc);
367       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6,
368                                        0 /* is_add */ );
369     }
370 }
371
372 int
373 session_lookup_del_session (session_t * s)
374 {
375   transport_connection_t *ts;
376   ts = transport_get_connection (session_get_transport_proto (s),
377                                  s->connection_index, s->thread_index);
378   if (!ts || (ts->flags & TRANSPORT_CONNECTION_F_NO_LOOKUP))
379     return 0;
380   return session_lookup_del_connection (ts);
381 }
382
383 static u8
384 session_lookup_action_index_is_valid (u32 action_index)
385 {
386   if (action_index == SESSION_RULES_TABLE_ACTION_ALLOW
387       || action_index == SESSION_RULES_TABLE_INVALID_INDEX)
388     return 0;
389   return 1;
390 }
391
392 static u64
393 session_lookup_action_to_handle (u32 action_index)
394 {
395   switch (action_index)
396     {
397     case SESSION_RULES_TABLE_ACTION_DROP:
398       return SESSION_DROP_HANDLE;
399     case SESSION_RULES_TABLE_ACTION_ALLOW:
400     case SESSION_RULES_TABLE_INVALID_INDEX:
401       return SESSION_INVALID_HANDLE;
402     default:
403       /* application index */
404       return action_index;
405     }
406 }
407
408 static session_t *
409 session_lookup_app_listen_session (u32 app_index, u8 fib_proto,
410                                    u8 transport_proto)
411 {
412   application_t *app;
413   app = application_get_if_valid (app_index);
414   if (!app)
415     return 0;
416
417   return app_worker_first_listener (application_get_default_worker (app),
418                                     fib_proto, transport_proto);
419 }
420
421 static session_t *
422 session_lookup_action_to_session (u32 action_index, u8 fib_proto,
423                                   u8 transport_proto)
424 {
425   u32 app_index;
426   app_index = session_lookup_action_to_handle (action_index);
427   /* Nothing sophisticated for now, action index is app index */
428   return session_lookup_app_listen_session (app_index, fib_proto,
429                                             transport_proto);
430 }
431
432 /** UNUSED */
433 session_t *
434 session_lookup_rules_table_session4 (session_table_t * st, u8 proto,
435                                      ip4_address_t * lcl, u16 lcl_port,
436                                      ip4_address_t * rmt, u16 rmt_port)
437 {
438   session_rules_table_t *srt = &st->session_rules[proto];
439   u32 action_index, app_index;
440   action_index = session_rules_table_lookup4 (srt, lcl, rmt, lcl_port,
441                                               rmt_port);
442   app_index = session_lookup_action_to_handle (action_index);
443   /* Nothing sophisticated for now, action index is app index */
444   return session_lookup_app_listen_session (app_index, FIB_PROTOCOL_IP4,
445                                             proto);
446 }
447
448 /** UNUSED */
449 session_t *
450 session_lookup_rules_table_session6 (session_table_t * st, u8 proto,
451                                      ip6_address_t * lcl, u16 lcl_port,
452                                      ip6_address_t * rmt, u16 rmt_port)
453 {
454   session_rules_table_t *srt = &st->session_rules[proto];
455   u32 action_index, app_index;
456   action_index = session_rules_table_lookup6 (srt, lcl, rmt, lcl_port,
457                                               rmt_port);
458   app_index = session_lookup_action_to_handle (action_index);
459   return session_lookup_app_listen_session (app_index, FIB_PROTOCOL_IP6,
460                                             proto);
461 }
462
463 /**
464  * Lookup listener for session endpoint in table
465  *
466  * @param table_index table where the endpoint should be looked up
467  * @param sep session endpoint to be looked up
468  * @param use_rules flag that indicates if the session rules of the table
469  *                  should be used
470  * @return invalid handle if nothing is found, the handle of a valid listener
471  *         or an action derived handle if a rule is hit
472  */
473 u64
474 session_lookup_endpoint_listener (u32 table_index, session_endpoint_t * sep,
475                                   u8 use_rules)
476 {
477   session_rules_table_t *srt;
478   session_table_t *st;
479   u32 ai;
480   int rv;
481
482   st = session_table_get (table_index);
483   if (!st)
484     return SESSION_INVALID_HANDLE;
485   if (sep->is_ip4)
486     {
487       session_kv4_t kv4;
488       ip4_address_t lcl4;
489
490       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
491                            sep->transport_proto);
492       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
493       if (rv == 0)
494         return kv4.value;
495       if (use_rules)
496         {
497           clib_memset (&lcl4, 0, sizeof (lcl4));
498           srt = &st->session_rules[sep->transport_proto];
499           ai = session_rules_table_lookup4 (srt, &lcl4, &sep->ip.ip4, 0,
500                                             sep->port);
501           if (session_lookup_action_index_is_valid (ai))
502             return session_lookup_action_to_handle (ai);
503         }
504     }
505   else
506     {
507       session_kv6_t kv6;
508       ip6_address_t lcl6;
509
510       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
511                            sep->transport_proto);
512       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
513       if (rv == 0)
514         return kv6.value;
515
516       if (use_rules)
517         {
518           clib_memset (&lcl6, 0, sizeof (lcl6));
519           srt = &st->session_rules[sep->transport_proto];
520           ai = session_rules_table_lookup6 (srt, &lcl6, &sep->ip.ip6, 0,
521                                             sep->port);
522           if (session_lookup_action_index_is_valid (ai))
523             return session_lookup_action_to_handle (ai);
524         }
525     }
526   return SESSION_INVALID_HANDLE;
527 }
528
529 /**
530  * Look up endpoint in local session table
531  *
532  * The result, for now, is an application index and it may in the future
533  * be extended to a more complicated "action object". The only action we
534  * emulate now is "drop" and for that we return a special app index.
535  *
536  * Lookup logic is to check in order:
537  * - the rules in the table (connect acls)
538  * - session sub-table for a listener
539  * - session sub-table for a local listener (zeroed addr)
540  *
541  * @param table_index table where the lookup should be done
542  * @param sep session endpoint to be looked up
543  * @return session handle that can be interpreted as an adjacency
544  */
545 u64
546 session_lookup_local_endpoint (u32 table_index, session_endpoint_t * sep)
547 {
548   session_rules_table_t *srt;
549   session_table_t *st;
550   u32 ai;
551   int rv;
552
553   st = session_table_get (table_index);
554   if (!st)
555     return SESSION_INVALID_INDEX;
556   ASSERT (st->is_local);
557
558   if (sep->is_ip4)
559     {
560       session_kv4_t kv4;
561       ip4_address_t lcl4;
562
563       /*
564        * Check if endpoint has special rules associated
565        */
566       clib_memset (&lcl4, 0, sizeof (lcl4));
567       srt = &st->session_rules[sep->transport_proto];
568       ai = session_rules_table_lookup4 (srt, &lcl4, &sep->ip.ip4, 0,
569                                         sep->port);
570       if (session_lookup_action_index_is_valid (ai))
571         return session_lookup_action_to_handle (ai);
572
573       /*
574        * Check if session endpoint is a listener
575        */
576       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
577                            sep->transport_proto);
578       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
579       if (rv == 0)
580         return kv4.value;
581
582       /*
583        * Zero out the ip. Logic is that connect to local ips, say
584        * 127.0.0.1:port, can match 0.0.0.0:port
585        */
586       if (ip4_is_local_host (&sep->ip.ip4))
587         {
588           kv4.key[0] = 0;
589           rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
590           if (rv == 0)
591             return kv4.value;
592         }
593       else
594         {
595           kv4.key[0] = 0;
596         }
597
598       /*
599        * Zero out the port and check if we have proxy
600        */
601       kv4.key[1] = 0;
602       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
603       if (rv == 0)
604         return kv4.value;
605     }
606   else
607     {
608       session_kv6_t kv6;
609       ip6_address_t lcl6;
610
611       clib_memset (&lcl6, 0, sizeof (lcl6));
612       srt = &st->session_rules[sep->transport_proto];
613       ai = session_rules_table_lookup6 (srt, &lcl6, &sep->ip.ip6, 0,
614                                         sep->port);
615       if (session_lookup_action_index_is_valid (ai))
616         return session_lookup_action_to_handle (ai);
617
618       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
619                            sep->transport_proto);
620       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
621       if (rv == 0)
622         return kv6.value;
623
624       /*
625        * Zero out the ip. Same logic as above.
626        */
627
628       if (ip6_is_local_host (&sep->ip.ip6))
629         {
630           kv6.key[0] = kv6.key[1] = 0;
631           rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
632           if (rv == 0)
633             return kv6.value;
634         }
635       else
636         {
637           kv6.key[0] = kv6.key[1] = 0;
638         }
639
640       /*
641        * Zero out the port. Same logic as above.
642        */
643       kv6.key[4] = kv6.key[5] = 0;
644       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
645       if (rv == 0)
646         return kv6.value;
647     }
648   return SESSION_INVALID_HANDLE;
649 }
650
651 static inline session_t *
652 session_lookup_listener4_i (session_table_t * st, ip4_address_t * lcl,
653                             u16 lcl_port, u8 proto, u8 use_wildcard)
654 {
655   session_kv4_t kv4;
656   int rv;
657
658   /*
659    * First, try a fully formed listener
660    */
661   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
662   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
663   if (rv == 0)
664     return listen_session_get ((u32) kv4.value);
665
666   /*
667    * Zero out the lcl ip and check if any 0/0 port binds have been done
668    */
669   if (use_wildcard)
670     {
671       kv4.key[0] = 0;
672       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
673       if (rv == 0)
674         return listen_session_get ((u32) kv4.value);
675     }
676   else
677     {
678       kv4.key[0] = 0;
679     }
680
681   /*
682    * Zero out port and check if we have a proxy set up for our ip
683    */
684   make_v4_proxy_kv (&kv4, lcl, proto);
685   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
686   if (rv == 0)
687     return listen_session_get ((u32) kv4.value);
688
689   return 0;
690 }
691
692 session_t *
693 session_lookup_listener4 (u32 fib_index, ip4_address_t * lcl, u16 lcl_port,
694                           u8 proto, u8 use_wildcard)
695 {
696   session_table_t *st;
697   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
698   if (!st)
699     return 0;
700   return session_lookup_listener4_i (st, lcl, lcl_port, proto, use_wildcard);
701 }
702
703 static session_t *
704 session_lookup_listener6_i (session_table_t * st, ip6_address_t * lcl,
705                             u16 lcl_port, u8 proto, u8 ip_wildcard)
706 {
707   session_kv6_t kv6;
708   int rv;
709
710   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
711   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
712   if (rv == 0)
713     return listen_session_get ((u32) kv6.value);
714
715   /* Zero out the lcl ip */
716   if (ip_wildcard)
717     {
718       kv6.key[0] = kv6.key[1] = 0;
719       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
720       if (rv == 0)
721         return listen_session_get ((u32) kv6.value);
722     }
723   else
724     {
725       kv6.key[0] = kv6.key[1] = 0;
726     }
727
728   make_v6_proxy_kv (&kv6, lcl, proto);
729   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
730   if (rv == 0)
731     return listen_session_get ((u32) kv6.value);
732   return 0;
733 }
734
735 session_t *
736 session_lookup_listener6 (u32 fib_index, ip6_address_t * lcl, u16 lcl_port,
737                           u8 proto, u8 use_wildcard)
738 {
739   session_table_t *st;
740   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
741   if (!st)
742     return 0;
743   return session_lookup_listener6_i (st, lcl, lcl_port, proto, use_wildcard);
744 }
745
746 /**
747  * Lookup listener, exact or proxy (inaddr_any:0) match
748  */
749 session_t *
750 session_lookup_listener (u32 table_index, session_endpoint_t * sep)
751 {
752   session_table_t *st;
753   st = session_table_get (table_index);
754   if (!st)
755     return 0;
756   if (sep->is_ip4)
757     return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port,
758                                        sep->transport_proto, 0);
759   else
760     return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port,
761                                        sep->transport_proto, 0);
762   return 0;
763 }
764
765 /**
766  * Lookup listener wildcard match
767  */
768 session_t *
769 session_lookup_listener_wildcard (u32 table_index, session_endpoint_t * sep)
770 {
771   session_table_t *st;
772   st = session_table_get (table_index);
773   if (!st)
774     return 0;
775   if (sep->is_ip4)
776     return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port,
777                                        sep->transport_proto,
778                                        1 /* use_wildcard */ );
779   else
780     return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port,
781                                        sep->transport_proto,
782                                        1 /* use_wildcard */ );
783   return 0;
784 }
785
786 int
787 session_lookup_add_half_open (transport_connection_t * tc, u64 value)
788 {
789   session_table_t *st;
790   session_kv4_t kv4;
791   session_kv6_t kv6;
792
793   st = session_table_get_or_alloc_for_connection (tc);
794   if (!st)
795     return 0;
796   if (tc->is_ip4)
797     {
798       make_v4_ss_kv_from_tc (&kv4, tc);
799       kv4.value = value;
800       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
801                                        1 /* is_add */ );
802     }
803   else
804     {
805       make_v6_ss_kv_from_tc (&kv6, tc);
806       kv6.value = value;
807       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
808                                        1 /* is_add */ );
809     }
810 }
811
812 int
813 session_lookup_del_half_open (transport_connection_t * tc)
814 {
815   session_table_t *st;
816   session_kv4_t kv4;
817   session_kv6_t kv6;
818
819   st = session_table_get_for_connection (tc);
820   if (!st)
821     return -1;
822   if (tc->is_ip4)
823     {
824       make_v4_ss_kv_from_tc (&kv4, tc);
825       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
826                                        0 /* is_add */ );
827     }
828   else
829     {
830       make_v6_ss_kv_from_tc (&kv6, tc);
831       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
832                                        0 /* is_add */ );
833     }
834 }
835
836 u64
837 session_lookup_half_open_handle (transport_connection_t * tc)
838 {
839   session_table_t *st;
840   session_kv4_t kv4;
841   session_kv6_t kv6;
842   int rv;
843
844   st = session_table_get_for_fib_index (transport_connection_fib_proto (tc),
845                                         tc->fib_index);
846   if (!st)
847     return HALF_OPEN_LOOKUP_INVALID_VALUE;
848   if (tc->is_ip4)
849     {
850       make_v4_ss_kv (&kv4, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
851                      tc->rmt_port, tc->proto);
852       rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
853       if (rv == 0)
854         return kv4.value;
855     }
856   else
857     {
858       make_v6_ss_kv (&kv6, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
859                      tc->rmt_port, tc->proto);
860       rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
861       if (rv == 0)
862         return kv6.value;
863     }
864   return HALF_OPEN_LOOKUP_INVALID_VALUE;
865 }
866
867 transport_connection_t *
868 session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4)
869 {
870   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
871     {
872       u32 sst = session_type_from_proto_and_ip (proto, is_ip4);
873       return transport_get_half_open (sst, handle & 0xFFFFFFFF);
874     }
875   return 0;
876 }
877
878 /**
879  * Lookup connection with ip4 and transport layer information
880  *
881  * This is used on the fast path so it needs to be fast. Thereby,
882  * duplication of code and 'hacks' allowed.
883  *
884  * The lookup is incremental and returns whenever something is matched. The
885  * steps are:
886  * - Try to find an established session
887  * - Try to find a half-open connection
888  * - Try session rules table
889  * - Try to find a fully-formed or local source wildcarded (listener bound to
890  *   all interfaces) listener session
891  * - return 0
892  *
893  * @param fib_index     index of fib wherein the connection was received
894  * @param lcl           local ip4 address
895  * @param rmt           remote ip4 address
896  * @param lcl_port      local port
897  * @param rmt_port      remote port
898  * @param proto         transport protocol (e.g., tcp, udp)
899  * @param thread_index  thread index for request
900  * @param is_filtered   return flag that indicates if connection was filtered.
901  *
902  * @return pointer to transport connection, if one is found, 0 otherwise
903  */
904 transport_connection_t *
905 session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl,
906                                ip4_address_t * rmt, u16 lcl_port,
907                                u16 rmt_port, u8 proto, u32 thread_index,
908                                u8 * result)
909 {
910   session_table_t *st;
911   session_kv4_t kv4;
912   session_t *s;
913   u32 action_index;
914   int rv;
915
916   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
917   if (PREDICT_FALSE (!st))
918     return 0;
919
920   /*
921    * Lookup session amongst established ones
922    */
923   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
924   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
925   if (rv == 0)
926     {
927       if (PREDICT_FALSE ((u32) (kv4.value >> 32) != thread_index))
928         {
929           *result = SESSION_LOOKUP_RESULT_WRONG_THREAD;
930           return 0;
931         }
932       s = session_get (kv4.value & 0xFFFFFFFFULL, thread_index);
933       return transport_get_connection (proto, s->connection_index,
934                                        thread_index);
935     }
936
937   /*
938    * Try half-open connections
939    */
940   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
941   if (rv == 0)
942     return transport_get_half_open (proto, kv4.value & 0xFFFFFFFF);
943
944   /*
945    * Check the session rules table
946    */
947   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
948                                               rmt, lcl_port, rmt_port);
949   if (session_lookup_action_index_is_valid (action_index))
950     {
951       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
952         {
953           *result = SESSION_LOOKUP_RESULT_FILTERED;
954           return 0;
955         }
956       if ((s = session_lookup_action_to_session (action_index,
957                                                  FIB_PROTOCOL_IP4, proto)))
958         return transport_get_listener (proto, s->connection_index);
959       return 0;
960     }
961
962   /*
963    * If nothing is found, check if any listener is available
964    */
965   s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1);
966   if (s)
967     return transport_get_listener (proto, s->connection_index);
968
969   return 0;
970 }
971
972 /**
973  * Lookup connection with ip4 and transport layer information
974  *
975  * Not optimized. Lookup logic is identical to that of
976  * @ref session_lookup_connection_wt4
977  *
978  * @param fib_index     index of the fib wherein the connection was received
979  * @param lcl           local ip4 address
980  * @param rmt           remote ip4 address
981  * @param lcl_port      local port
982  * @param rmt_port      remote port
983  * @param proto         transport protocol (e.g., tcp, udp)
984  *
985  * @return pointer to transport connection, if one is found, 0 otherwise
986  */
987 transport_connection_t *
988 session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl,
989                             ip4_address_t * rmt, u16 lcl_port, u16 rmt_port,
990                             u8 proto)
991 {
992   session_table_t *st;
993   session_kv4_t kv4;
994   session_t *s;
995   u32 action_index;
996   int rv;
997
998   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
999   if (PREDICT_FALSE (!st))
1000     return 0;
1001
1002   /*
1003    * Lookup session amongst established ones
1004    */
1005   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
1006   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
1007   if (rv == 0)
1008     {
1009       s = session_get_from_handle (kv4.value);
1010       return transport_get_connection (proto, s->connection_index,
1011                                        s->thread_index);
1012     }
1013
1014   /*
1015    * Try half-open connections
1016    */
1017   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
1018   if (rv == 0)
1019     return transport_get_half_open (proto, kv4.value & 0xFFFFFFFF);
1020
1021   /*
1022    * Check the session rules table
1023    */
1024   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
1025                                               rmt, lcl_port, rmt_port);
1026   if (session_lookup_action_index_is_valid (action_index))
1027     {
1028       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1029         return 0;
1030       if ((s = session_lookup_action_to_session (action_index,
1031                                                  FIB_PROTOCOL_IP4, proto)))
1032         return transport_get_listener (proto, s->connection_index);
1033       return 0;
1034     }
1035
1036   /*
1037    * If nothing is found, check if any listener is available
1038    */
1039   s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1);
1040   if (s)
1041     return transport_get_listener (proto, s->connection_index);
1042
1043   return 0;
1044 }
1045
1046 /**
1047  * Lookup session with ip4 and transport layer information
1048  *
1049  * Important note: this may look into another thread's pool table and
1050  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
1051  * if needed as soon as possible.
1052  *
1053  * Lookup logic is similar to that of @ref session_lookup_connection_wt4 but
1054  * this returns a session as opposed to a transport connection and it does not
1055  * try to lookup half-open sessions.
1056  *
1057  * Typically used by dgram connections
1058  */
1059 session_t *
1060 session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
1061                       u16 lcl_port, u16 rmt_port, u8 proto)
1062 {
1063   session_table_t *st;
1064   session_kv4_t kv4;
1065   session_t *s;
1066   u32 action_index;
1067   int rv;
1068
1069   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
1070   if (PREDICT_FALSE (!st))
1071     return 0;
1072
1073   /*
1074    * Lookup session amongst established ones
1075    */
1076   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
1077   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
1078   if (rv == 0)
1079     return session_get_from_handle_safe (kv4.value);
1080
1081   /*
1082    * Check the session rules table
1083    */
1084   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
1085                                               rmt, lcl_port, rmt_port);
1086   if (session_lookup_action_index_is_valid (action_index))
1087     {
1088       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1089         return 0;
1090       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP4,
1091                                                proto);
1092     }
1093
1094   /*
1095    *  If nothing is found, check if any listener is available
1096    */
1097   if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1)))
1098     return s;
1099
1100   return 0;
1101 }
1102
1103 /**
1104  * Lookup connection with ip6 and transport layer information
1105  *
1106  * This is used on the fast path so it needs to be fast. Thereby,
1107  * duplication of code and 'hacks' allowed.
1108  *
1109  * The lookup is incremental and returns whenever something is matched. The
1110  * steps are:
1111  * - Try to find an established session
1112  * - Try to find a half-open connection
1113  * - Try session rules table
1114  * - Try to find a fully-formed or local source wildcarded (listener bound to
1115  *   all interfaces) listener session
1116  * - return 0
1117  *
1118  * @param fib_index     index of the fib wherein the connection was received
1119  * @param lcl           local ip6 address
1120  * @param rmt           remote ip6 address
1121  * @param lcl_port      local port
1122  * @param rmt_port      remote port
1123  * @param proto         transport protocol (e.g., tcp, udp)
1124  * @param thread_index  thread index for request
1125  *
1126  * @return pointer to transport connection, if one is found, 0 otherwise
1127  */
1128 transport_connection_t *
1129 session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
1130                                ip6_address_t * rmt, u16 lcl_port,
1131                                u16 rmt_port, u8 proto, u32 thread_index,
1132                                u8 * result)
1133 {
1134   session_table_t *st;
1135   session_t *s;
1136   session_kv6_t kv6;
1137   u32 action_index;
1138   int rv;
1139
1140   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1141   if (PREDICT_FALSE (!st))
1142     return 0;
1143
1144   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1145   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1146   if (rv == 0)
1147     {
1148       ASSERT ((u32) (kv6.value >> 32) == thread_index);
1149       if (PREDICT_FALSE ((u32) (kv6.value >> 32) != thread_index))
1150         {
1151           *result = SESSION_LOOKUP_RESULT_WRONG_THREAD;
1152           return 0;
1153         }
1154       s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index);
1155       return transport_get_connection (proto, s->connection_index,
1156                                        thread_index);
1157     }
1158
1159   /* Try half-open connections */
1160   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1161   if (rv == 0)
1162     return transport_get_half_open (proto, kv6.value & 0xFFFFFFFF);
1163
1164   /* Check the session rules table */
1165   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1166                                               rmt, lcl_port, rmt_port);
1167   if (session_lookup_action_index_is_valid (action_index))
1168     {
1169       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1170         {
1171           *result = SESSION_LOOKUP_RESULT_FILTERED;
1172           return 0;
1173         }
1174       if ((s = session_lookup_action_to_session (action_index,
1175                                                  FIB_PROTOCOL_IP6, proto)))
1176         return transport_get_listener (proto, s->connection_index);
1177       return 0;
1178     }
1179
1180   /* If nothing is found, check if any listener is available */
1181   s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
1182   if (s)
1183     return transport_get_listener (proto, s->connection_index);
1184
1185   return 0;
1186 }
1187
1188 /**
1189  * Lookup connection with ip6 and transport layer information
1190  *
1191  * Not optimized. This is used on the fast path so it needs to be fast.
1192  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
1193  * to that of @ref session_lookup_connection_wt4
1194  *
1195  * @param fib_index     index of the fib wherein the connection was received
1196  * @param lcl           local ip6 address
1197  * @param rmt           remote ip6 address
1198  * @param lcl_port      local port
1199  * @param rmt_port      remote port
1200  * @param proto         transport protocol (e.g., tcp, udp)
1201  *
1202  * @return pointer to transport connection, if one is found, 0 otherwise
1203  */
1204 transport_connection_t *
1205 session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
1206                             ip6_address_t * rmt, u16 lcl_port, u16 rmt_port,
1207                             u8 proto)
1208 {
1209   session_table_t *st;
1210   session_t *s;
1211   session_kv6_t kv6;
1212   u32 action_index;
1213   int rv;
1214
1215   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1216   if (PREDICT_FALSE (!st))
1217     return 0;
1218
1219   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1220   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1221   if (rv == 0)
1222     {
1223       s = session_get_from_handle (kv6.value);
1224       return transport_get_connection (proto, s->connection_index,
1225                                        s->thread_index);
1226     }
1227
1228   /* Try half-open connections */
1229   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1230   if (rv == 0)
1231     return transport_get_half_open (proto, kv6.value & 0xFFFFFFFF);
1232
1233   /* Check the session rules table */
1234   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1235                                               rmt, lcl_port, rmt_port);
1236   if (session_lookup_action_index_is_valid (action_index))
1237     {
1238       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1239         return 0;
1240       if ((s = session_lookup_action_to_session (action_index,
1241                                                  FIB_PROTOCOL_IP6, proto)))
1242         return transport_get_listener (proto, s->connection_index);
1243       return 0;
1244     }
1245
1246   /* If nothing is found, check if any listener is available */
1247   s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
1248   if (s)
1249     return transport_get_listener (proto, s->connection_index);
1250
1251   return 0;
1252 }
1253
1254 /**
1255  * Lookup session with ip6 and transport layer information
1256  *
1257  * Important note: this may look into another thread's pool table and
1258  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
1259  * if needed as soon as possible.
1260  *
1261  * Lookup logic is similar to that of @ref session_lookup_connection_wt6 but
1262  * this returns a session as opposed to a transport connection and it does not
1263  * try to lookup half-open sessions.
1264  *
1265  * Typically used by dgram connections
1266  */
1267 session_t *
1268 session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
1269                       u16 lcl_port, u16 rmt_port, u8 proto)
1270 {
1271   session_table_t *st;
1272   session_kv6_t kv6;
1273   session_t *s;
1274   u32 action_index;
1275   int rv;
1276
1277   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1278   if (PREDICT_FALSE (!st))
1279     return 0;
1280
1281   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1282   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1283   if (rv == 0)
1284     return session_get_from_handle_safe (kv6.value);
1285
1286   /* Check the session rules table */
1287   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1288                                               rmt, lcl_port, rmt_port);
1289   if (session_lookup_action_index_is_valid (action_index))
1290     {
1291       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1292         return 0;
1293       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP6,
1294                                                proto);
1295     }
1296
1297   /* If nothing is found, check if any listener is available */
1298   if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1)))
1299     return s;
1300   return 0;
1301 }
1302
1303 transport_connection_t *
1304 session_lookup_connection (u32 fib_index, ip46_address_t * lcl,
1305                            ip46_address_t * rmt, u16 lcl_port, u16 rmt_port,
1306                            u8 proto, u8 is_ip4)
1307 {
1308   if (is_ip4)
1309     return session_lookup_connection4 (fib_index, &lcl->ip4, &rmt->ip4,
1310                                        lcl_port, rmt_port, proto);
1311   else
1312     return session_lookup_connection6 (fib_index, &lcl->ip6, &rmt->ip6,
1313                                        lcl_port, rmt_port, proto);
1314 }
1315
1316 int
1317 vnet_session_rule_add_del (session_rule_add_del_args_t * args)
1318 {
1319   app_namespace_t *app_ns = app_namespace_get (args->appns_index);
1320   session_rules_table_t *srt;
1321   session_table_t *st;
1322   u32 fib_index;
1323   u8 fib_proto;
1324   int rv = 0;
1325
1326   if (!app_ns)
1327     return VNET_API_ERROR_APP_INVALID_NS;
1328
1329   if (args->scope > 3)
1330     return VNET_API_ERROR_INVALID_VALUE;
1331
1332   if (args->transport_proto != TRANSPORT_PROTO_TCP
1333       && args->transport_proto != TRANSPORT_PROTO_UDP)
1334     return VNET_API_ERROR_INVALID_VALUE;
1335
1336   if ((args->scope & SESSION_RULE_SCOPE_GLOBAL) || args->scope == 0)
1337     {
1338       fib_proto = args->table_args.rmt.fp_proto;
1339       fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1340       st = session_table_get_for_fib_index (fib_proto, fib_index);
1341       srt = &st->session_rules[args->transport_proto];
1342       if ((rv = session_rules_table_add_del (srt, &args->table_args)))
1343         return rv;
1344     }
1345   if (args->scope & SESSION_RULE_SCOPE_LOCAL)
1346     {
1347       clib_memset (&args->table_args.lcl, 0, sizeof (args->table_args.lcl));
1348       args->table_args.lcl.fp_proto = args->table_args.rmt.fp_proto;
1349       args->table_args.lcl_port = 0;
1350       st = app_namespace_get_local_table (app_ns);
1351       srt = &st->session_rules[args->transport_proto];
1352       rv = session_rules_table_add_del (srt, &args->table_args);
1353     }
1354   return rv;
1355 }
1356
1357 /**
1358  * Mark (global) tables as pertaining to app ns
1359  */
1360 void
1361 session_lookup_set_tables_appns (app_namespace_t * app_ns)
1362 {
1363   session_table_t *st;
1364   u32 fib_index;
1365   u8 fp;
1366
1367   for (fp = 0; fp < ARRAY_LEN (fib_index_to_table_index); fp++)
1368     {
1369       fib_index = app_namespace_get_fib_index (app_ns, fp);
1370       st = session_table_get_or_alloc (fp, fib_index);
1371       if (st)
1372         st->appns_index = app_namespace_index (app_ns);
1373     }
1374 }
1375
1376 u8 *
1377 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
1378 {
1379   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
1380   u32 is_local = va_arg (*args, u32);
1381   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
1382   session_t *session;
1383   app_worker_t *app_wrk;
1384   const u8 *app_name;
1385   u8 *str = 0;
1386
1387   if (!is_local)
1388     {
1389       session = session_get_from_handle (kvp->value);
1390       app_wrk = app_worker_get (session->app_wrk_index);
1391       app_name = application_name_from_index (app_wrk->app_index);
1392       str = format (0, "[%U] %U:%d->%U:%d", format_transport_proto_short,
1393                     key->proto, format_ip4_address, &key->src,
1394                     clib_net_to_host_u16 (key->src_port), format_ip4_address,
1395                     &key->dst, clib_net_to_host_u16 (key->dst_port));
1396       s = format (s, "%-40v%-30v", str, app_name);
1397     }
1398   else
1399     {
1400       session = session_get_from_handle (kvp->value);
1401       app_wrk = app_worker_get (session->app_wrk_index);
1402       app_name = application_name_from_index (app_wrk->app_index);
1403       str = format (0, "[%U] %U:%d", format_transport_proto_short, key->proto,
1404                     format_ip4_address, &key->src,
1405                     clib_net_to_host_u16 (key->src_port));
1406       s = format (s, "%-30v%-30v", str, app_name);
1407     }
1408   return s;
1409 }
1410
1411 typedef struct _ip4_session_table_show_ctx_t
1412 {
1413   vlib_main_t *vm;
1414   u8 is_local;
1415 } ip4_session_table_show_ctx_t;
1416
1417 static int
1418 ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg)
1419 {
1420   ip4_session_table_show_ctx_t *ctx = arg;
1421   vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp,
1422                    ctx->is_local);
1423   return 1;
1424 }
1425
1426 void
1427 session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table,
1428                                    u8 type, u8 is_local)
1429 {
1430   ip4_session_table_show_ctx_t ctx = {
1431     .vm = vm,
1432     .is_local = is_local,
1433   };
1434   if (!is_local)
1435     vlib_cli_output (vm, "%-40s%-30s", "Session", "Application");
1436   else
1437     vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application");
1438   switch (type)
1439     {
1440       /* main table v4 */
1441     case 0:
1442       ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show,
1443                               &ctx);
1444       break;
1445     default:
1446       clib_warning ("not supported");
1447     }
1448 }
1449
1450 static clib_error_t *
1451 session_rule_command_fn (vlib_main_t * vm, unformat_input_t * input,
1452                          vlib_cli_command_t * cmd)
1453 {
1454   u32 proto = ~0, lcl_port, rmt_port, action = 0, lcl_plen = 0, rmt_plen = 0;
1455   clib_error_t *error = 0;
1456   u32 appns_index, scope = 0;
1457   ip46_address_t lcl_ip, rmt_ip;
1458   u8 is_ip4 = 1, conn_set = 0;
1459   u8 fib_proto, is_add = 1, *ns_id = 0;
1460   u8 *tag = 0;
1461   app_namespace_t *app_ns;
1462   int rv;
1463
1464   session_cli_return_if_not_enabled ();
1465
1466   clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
1467   clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
1468   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1469     {
1470       if (unformat (input, "del"))
1471         is_add = 0;
1472       else if (unformat (input, "add"))
1473         ;
1474       else if (unformat (input, "appns %_%v%_", &ns_id))
1475         ;
1476       else if (unformat (input, "scope global"))
1477         scope = SESSION_RULE_SCOPE_GLOBAL;
1478       else if (unformat (input, "scope local"))
1479         scope = SESSION_RULE_SCOPE_LOCAL;
1480       else if (unformat (input, "scope all"))
1481         scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL;
1482       else if (unformat (input, "proto %U", unformat_transport_proto, &proto))
1483         ;
1484       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1485                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1486                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1487                          &rmt_port))
1488         {
1489           is_ip4 = 1;
1490           conn_set = 1;
1491         }
1492       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1493                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1494                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1495                          &rmt_port))
1496         {
1497           is_ip4 = 0;
1498           conn_set = 1;
1499         }
1500       else if (unformat (input, "action %d", &action))
1501         ;
1502       else if (unformat (input, "tag %_%v%_", &tag))
1503         ;
1504       else
1505         {
1506           error = clib_error_return (0, "unknown input `%U'",
1507                                      format_unformat_error, input);
1508           goto done;
1509         }
1510     }
1511
1512   if (proto == ~0)
1513     {
1514       vlib_cli_output (vm, "proto must be set");
1515       goto done;
1516     }
1517   if (is_add && !conn_set && action == ~0)
1518     {
1519       vlib_cli_output (vm, "connection and action must be set for add");
1520       goto done;
1521     }
1522   if (!is_add && !tag && !conn_set)
1523     {
1524       vlib_cli_output (vm, "connection or tag must be set for delete");
1525       goto done;
1526     }
1527   if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
1528     {
1529       vlib_cli_output (vm, "tag too long (max u64)");
1530       goto done;
1531     }
1532
1533   if (ns_id)
1534     {
1535       app_ns = app_namespace_get_from_id (ns_id);
1536       if (!app_ns)
1537         {
1538           vlib_cli_output (vm, "namespace %v does not exist", ns_id);
1539           goto done;
1540         }
1541     }
1542   else
1543     {
1544       app_ns = app_namespace_get_default ();
1545     }
1546   appns_index = app_namespace_index (app_ns);
1547
1548   fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1549   session_rule_add_del_args_t args = {
1550     .transport_proto = proto,
1551     .table_args.lcl.fp_addr = lcl_ip,
1552     .table_args.lcl.fp_len = lcl_plen,
1553     .table_args.lcl.fp_proto = fib_proto,
1554     .table_args.rmt.fp_addr = rmt_ip,
1555     .table_args.rmt.fp_len = rmt_plen,
1556     .table_args.rmt.fp_proto = fib_proto,
1557     .table_args.lcl_port = lcl_port,
1558     .table_args.rmt_port = rmt_port,
1559     .table_args.action_index = action,
1560     .table_args.is_add = is_add,
1561     .table_args.tag = tag,
1562     .appns_index = appns_index,
1563     .scope = scope,
1564   };
1565   if ((rv = vnet_session_rule_add_del (&args)))
1566     error = clib_error_return (0, "rule add del returned %u", rv);
1567
1568 done:
1569   vec_free (ns_id);
1570   vec_free (tag);
1571   return error;
1572 }
1573
1574 /* *INDENT-OFF* */
1575 VLIB_CLI_COMMAND (session_rule_command, static) =
1576 {
1577   .path = "session rule",
1578   .short_help = "session rule [add|del] appns <ns_id> proto <proto> "
1579       "<lcl-ip/plen> <lcl-port> <rmt-ip/plen> <rmt-port> action <action>",
1580   .function = session_rule_command_fn,
1581 };
1582 /* *INDENT-ON* */
1583
1584 void
1585 session_lookup_dump_rules_table (u32 fib_index, u8 fib_proto,
1586                                  u8 transport_proto)
1587 {
1588   vlib_main_t *vm = vlib_get_main ();
1589   session_rules_table_t *srt;
1590   session_table_t *st;
1591   st = session_table_get_for_fib_index (fib_index, fib_proto);
1592   srt = &st->session_rules[transport_proto];
1593   session_rules_table_cli_dump (vm, srt, fib_proto);
1594 }
1595
1596 void
1597 session_lookup_dump_local_rules_table (u32 table_index, u8 fib_proto,
1598                                        u8 transport_proto)
1599 {
1600   vlib_main_t *vm = vlib_get_main ();
1601   session_rules_table_t *srt;
1602   session_table_t *st;
1603   st = session_table_get (table_index);
1604   srt = &st->session_rules[transport_proto];
1605   session_rules_table_cli_dump (vm, srt, fib_proto);
1606 }
1607
1608 static clib_error_t *
1609 show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
1610                                vlib_cli_command_t * cmd)
1611 {
1612   u32 transport_proto = ~0, lcl_port, rmt_port, lcl_plen, rmt_plen;
1613   u32 fib_index, scope = 0;
1614   ip46_address_t lcl_ip, rmt_ip;
1615   u8 is_ip4 = 1, show_one = 0;
1616   app_namespace_t *app_ns;
1617   session_rules_table_t *srt;
1618   session_table_t *st;
1619   u8 *ns_id = 0, fib_proto;
1620
1621   session_cli_return_if_not_enabled ();
1622
1623   clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
1624   clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
1625   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1626     {
1627       if (unformat (input, "%U", unformat_transport_proto, &transport_proto))
1628         ;
1629       else if (unformat (input, "appns %_%v%_", &ns_id))
1630         ;
1631       else if (unformat (input, "scope global"))
1632         scope = 1;
1633       else if (unformat (input, "scope local"))
1634         scope = 2;
1635       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1636                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1637                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1638                          &rmt_port))
1639         {
1640           is_ip4 = 1;
1641           show_one = 1;
1642         }
1643       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1644                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1645                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1646                          &rmt_port))
1647         {
1648           is_ip4 = 0;
1649           show_one = 1;
1650         }
1651       else
1652         return clib_error_return (0, "unknown input `%U'",
1653                                   format_unformat_error, input);
1654     }
1655
1656   if (transport_proto == ~0)
1657     {
1658       vlib_cli_output (vm, "transport proto must be set");
1659       return 0;
1660     }
1661
1662   if (ns_id)
1663     {
1664       app_ns = app_namespace_get_from_id (ns_id);
1665       if (!app_ns)
1666         {
1667           vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
1668           return 0;
1669         }
1670     }
1671   else
1672     {
1673       app_ns = app_namespace_get_default ();
1674     }
1675
1676   if (scope == 1 || scope == 0)
1677     {
1678       fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1679       fib_index = is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
1680       st = session_table_get_for_fib_index (fib_proto, fib_index);
1681     }
1682   else
1683     {
1684       st = app_namespace_get_local_table (app_ns);
1685     }
1686
1687   if (show_one)
1688     {
1689       srt = &st->session_rules[transport_proto];
1690       session_rules_table_show_rule (vm, srt, &lcl_ip, lcl_port, &rmt_ip,
1691                                      rmt_port, is_ip4);
1692       return 0;
1693     }
1694
1695   vlib_cli_output (vm, "%U rules table", format_transport_proto,
1696                    transport_proto);
1697   srt = &st->session_rules[transport_proto];
1698   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4);
1699   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP6);
1700
1701   vec_free (ns_id);
1702   return 0;
1703 }
1704
1705 /* *INDENT-OFF* */
1706 VLIB_CLI_COMMAND (show_session_rules_command, static) =
1707 {
1708   .path = "show session rules",
1709   .short_help = "show session rules [<proto> appns <id> <lcl-ip/plen> "
1710       "<lcl-port> <rmt-ip/plen> <rmt-port> scope <scope>]",
1711   .function = show_session_rules_command_fn,
1712 };
1713 /* *INDENT-ON* */
1714
1715 void
1716 session_lookup_init (void)
1717 {
1718   /*
1719    * Allocate default table and map it to fib_index 0
1720    */
1721   session_table_t *st = session_table_alloc ();
1722   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0);
1723   fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st);
1724   st->active_fib_proto = FIB_PROTOCOL_IP4;
1725   session_table_init (st, FIB_PROTOCOL_IP4);
1726   st = session_table_alloc ();
1727   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0);
1728   fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st);
1729   st->active_fib_proto = FIB_PROTOCOL_IP6;
1730   session_table_init (st, FIB_PROTOCOL_IP6);
1731 }
1732
1733 /*
1734  * fd.io coding-style-patch-verification: ON
1735  *
1736  * Local Variables:
1737  * eval: (c-set-style "gnu")
1738  * End:
1739  */