session: use session error type instead of vnet error
[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
1050  *
1051  * Lookup logic is similar to that of @ref session_lookup_connection_wt4 but
1052  * this returns a session as opposed to a transport connection and it does not
1053  * try to lookup half-open sessions.
1054  *
1055  * Typically used by dgram connections
1056  */
1057 session_t *
1058 session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
1059                       u16 lcl_port, u16 rmt_port, u8 proto)
1060 {
1061   session_table_t *st;
1062   session_kv4_t kv4;
1063   session_t *s;
1064   u32 action_index;
1065   int rv;
1066
1067   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
1068   if (PREDICT_FALSE (!st))
1069     return 0;
1070
1071   /*
1072    * Lookup session amongst established ones
1073    */
1074   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
1075   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
1076   if (rv == 0)
1077     return session_get_from_handle_safe (kv4.value);
1078
1079   /*
1080    * Check the session rules table
1081    */
1082   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
1083                                               rmt, lcl_port, rmt_port);
1084   if (session_lookup_action_index_is_valid (action_index))
1085     {
1086       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1087         return 0;
1088       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP4,
1089                                                proto);
1090     }
1091
1092   /*
1093    *  If nothing is found, check if any listener is available
1094    */
1095   if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1)))
1096     return s;
1097
1098   return 0;
1099 }
1100
1101 /**
1102  * Lookup connection with ip6 and transport layer information
1103  *
1104  * This is used on the fast path so it needs to be fast. Thereby,
1105  * duplication of code and 'hacks' allowed.
1106  *
1107  * The lookup is incremental and returns whenever something is matched. The
1108  * steps are:
1109  * - Try to find an established session
1110  * - Try to find a half-open connection
1111  * - Try session rules table
1112  * - Try to find a fully-formed or local source wildcarded (listener bound to
1113  *   all interfaces) listener session
1114  * - return 0
1115  *
1116  * @param fib_index     index of the fib wherein the connection was received
1117  * @param lcl           local ip6 address
1118  * @param rmt           remote ip6 address
1119  * @param lcl_port      local port
1120  * @param rmt_port      remote port
1121  * @param proto         transport protocol (e.g., tcp, udp)
1122  * @param thread_index  thread index for request
1123  *
1124  * @return pointer to transport connection, if one is found, 0 otherwise
1125  */
1126 transport_connection_t *
1127 session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
1128                                ip6_address_t * rmt, u16 lcl_port,
1129                                u16 rmt_port, u8 proto, u32 thread_index,
1130                                u8 * result)
1131 {
1132   session_table_t *st;
1133   session_t *s;
1134   session_kv6_t kv6;
1135   u32 action_index;
1136   int rv;
1137
1138   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1139   if (PREDICT_FALSE (!st))
1140     return 0;
1141
1142   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1143   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1144   if (rv == 0)
1145     {
1146       ASSERT ((u32) (kv6.value >> 32) == thread_index);
1147       if (PREDICT_FALSE ((u32) (kv6.value >> 32) != thread_index))
1148         {
1149           *result = SESSION_LOOKUP_RESULT_WRONG_THREAD;
1150           return 0;
1151         }
1152       s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index);
1153       return transport_get_connection (proto, s->connection_index,
1154                                        thread_index);
1155     }
1156
1157   /* Try half-open connections */
1158   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1159   if (rv == 0)
1160     return transport_get_half_open (proto, kv6.value & 0xFFFFFFFF);
1161
1162   /* Check the session rules table */
1163   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1164                                               rmt, lcl_port, rmt_port);
1165   if (session_lookup_action_index_is_valid (action_index))
1166     {
1167       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1168         {
1169           *result = SESSION_LOOKUP_RESULT_FILTERED;
1170           return 0;
1171         }
1172       if ((s = session_lookup_action_to_session (action_index,
1173                                                  FIB_PROTOCOL_IP6, proto)))
1174         return transport_get_listener (proto, s->connection_index);
1175       return 0;
1176     }
1177
1178   /* If nothing is found, check if any listener is available */
1179   s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
1180   if (s)
1181     return transport_get_listener (proto, s->connection_index);
1182
1183   return 0;
1184 }
1185
1186 /**
1187  * Lookup connection with ip6 and transport layer information
1188  *
1189  * Not optimized. This is used on the fast path so it needs to be fast.
1190  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
1191  * to that of @ref session_lookup_connection_wt4
1192  *
1193  * @param fib_index     index of the fib wherein the connection was received
1194  * @param lcl           local ip6 address
1195  * @param rmt           remote ip6 address
1196  * @param lcl_port      local port
1197  * @param rmt_port      remote port
1198  * @param proto         transport protocol (e.g., tcp, udp)
1199  *
1200  * @return pointer to transport connection, if one is found, 0 otherwise
1201  */
1202 transport_connection_t *
1203 session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
1204                             ip6_address_t * rmt, u16 lcl_port, u16 rmt_port,
1205                             u8 proto)
1206 {
1207   session_table_t *st;
1208   session_t *s;
1209   session_kv6_t kv6;
1210   u32 action_index;
1211   int rv;
1212
1213   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1214   if (PREDICT_FALSE (!st))
1215     return 0;
1216
1217   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1218   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1219   if (rv == 0)
1220     {
1221       s = session_get_from_handle (kv6.value);
1222       return transport_get_connection (proto, s->connection_index,
1223                                        s->thread_index);
1224     }
1225
1226   /* Try half-open connections */
1227   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1228   if (rv == 0)
1229     return transport_get_half_open (proto, kv6.value & 0xFFFFFFFF);
1230
1231   /* Check the session rules table */
1232   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1233                                               rmt, lcl_port, rmt_port);
1234   if (session_lookup_action_index_is_valid (action_index))
1235     {
1236       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1237         return 0;
1238       if ((s = session_lookup_action_to_session (action_index,
1239                                                  FIB_PROTOCOL_IP6, proto)))
1240         return transport_get_listener (proto, s->connection_index);
1241       return 0;
1242     }
1243
1244   /* If nothing is found, check if any listener is available */
1245   s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
1246   if (s)
1247     return transport_get_listener (proto, s->connection_index);
1248
1249   return 0;
1250 }
1251
1252 /**
1253  * Lookup session with ip6 and transport layer information
1254  *
1255  * Important note: this may look into another thread's pool table and
1256  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
1257  * if needed as soon as possible.
1258  *
1259  * Lookup logic is similar to that of @ref session_lookup_connection_wt6 but
1260  * this returns a session as opposed to a transport connection and it does not
1261  * try to lookup half-open sessions.
1262  *
1263  * Typically used by dgram connections
1264  */
1265 session_t *
1266 session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
1267                       u16 lcl_port, u16 rmt_port, u8 proto)
1268 {
1269   session_table_t *st;
1270   session_kv6_t kv6;
1271   session_t *s;
1272   u32 action_index;
1273   int rv;
1274
1275   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1276   if (PREDICT_FALSE (!st))
1277     return 0;
1278
1279   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1280   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1281   if (rv == 0)
1282     return session_get_from_handle_safe (kv6.value);
1283
1284   /* Check the session rules table */
1285   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1286                                               rmt, lcl_port, rmt_port);
1287   if (session_lookup_action_index_is_valid (action_index))
1288     {
1289       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1290         return 0;
1291       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP6,
1292                                                proto);
1293     }
1294
1295   /* If nothing is found, check if any listener is available */
1296   if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1)))
1297     return s;
1298   return 0;
1299 }
1300
1301 transport_connection_t *
1302 session_lookup_connection (u32 fib_index, ip46_address_t * lcl,
1303                            ip46_address_t * rmt, u16 lcl_port, u16 rmt_port,
1304                            u8 proto, u8 is_ip4)
1305 {
1306   if (is_ip4)
1307     return session_lookup_connection4 (fib_index, &lcl->ip4, &rmt->ip4,
1308                                        lcl_port, rmt_port, proto);
1309   else
1310     return session_lookup_connection6 (fib_index, &lcl->ip6, &rmt->ip6,
1311                                        lcl_port, rmt_port, proto);
1312 }
1313
1314 session_error_t
1315 vnet_session_rule_add_del (session_rule_add_del_args_t *args)
1316 {
1317   app_namespace_t *app_ns = app_namespace_get (args->appns_index);
1318   session_rules_table_t *srt;
1319   session_table_t *st;
1320   u32 fib_index;
1321   u8 fib_proto;
1322   int rv = 0;
1323
1324   if (!app_ns)
1325     return SESSION_E_INVALID_NS;
1326
1327   if (args->scope > 3)
1328     return SESSION_E_INVALID;
1329
1330   if (args->transport_proto != TRANSPORT_PROTO_TCP
1331       && args->transport_proto != TRANSPORT_PROTO_UDP)
1332     return SESSION_E_INVALID;
1333
1334   if ((args->scope & SESSION_RULE_SCOPE_GLOBAL) || args->scope == 0)
1335     {
1336       fib_proto = args->table_args.rmt.fp_proto;
1337       fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1338       st = session_table_get_for_fib_index (fib_proto, fib_index);
1339       srt = &st->session_rules[args->transport_proto];
1340       if ((rv = session_rules_table_add_del (srt, &args->table_args)))
1341         return rv;
1342     }
1343   if (args->scope & SESSION_RULE_SCOPE_LOCAL)
1344     {
1345       clib_memset (&args->table_args.lcl, 0, sizeof (args->table_args.lcl));
1346       args->table_args.lcl.fp_proto = args->table_args.rmt.fp_proto;
1347       args->table_args.lcl_port = 0;
1348       st = app_namespace_get_local_table (app_ns);
1349       srt = &st->session_rules[args->transport_proto];
1350       rv = session_rules_table_add_del (srt, &args->table_args);
1351     }
1352   return rv;
1353 }
1354
1355 /**
1356  * Mark (global) tables as pertaining to app ns
1357  */
1358 void
1359 session_lookup_set_tables_appns (app_namespace_t * app_ns)
1360 {
1361   session_table_t *st;
1362   u32 fib_index;
1363   u8 fp;
1364
1365   for (fp = 0; fp < ARRAY_LEN (fib_index_to_table_index); fp++)
1366     {
1367       fib_index = app_namespace_get_fib_index (app_ns, fp);
1368       st = session_table_get_or_alloc (fp, fib_index);
1369       if (st)
1370         st->appns_index = app_namespace_index (app_ns);
1371     }
1372 }
1373
1374 u8 *
1375 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
1376 {
1377   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
1378   u32 is_local = va_arg (*args, u32);
1379   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
1380   session_t *session;
1381   app_worker_t *app_wrk;
1382   const u8 *app_name;
1383   u8 *str = 0;
1384
1385   if (!is_local)
1386     {
1387       session = session_get_from_handle (kvp->value);
1388       app_wrk = app_worker_get (session->app_wrk_index);
1389       app_name = application_name_from_index (app_wrk->app_index);
1390       str = format (0, "[%U] %U:%d->%U:%d", format_transport_proto_short,
1391                     key->proto, format_ip4_address, &key->src,
1392                     clib_net_to_host_u16 (key->src_port), format_ip4_address,
1393                     &key->dst, clib_net_to_host_u16 (key->dst_port));
1394       s = format (s, "%-40v%-30v", str, app_name);
1395     }
1396   else
1397     {
1398       session = session_get_from_handle (kvp->value);
1399       app_wrk = app_worker_get (session->app_wrk_index);
1400       app_name = application_name_from_index (app_wrk->app_index);
1401       str = format (0, "[%U] %U:%d", format_transport_proto_short, key->proto,
1402                     format_ip4_address, &key->src,
1403                     clib_net_to_host_u16 (key->src_port));
1404       s = format (s, "%-30v%-30v", str, app_name);
1405     }
1406   return s;
1407 }
1408
1409 typedef struct _ip4_session_table_show_ctx_t
1410 {
1411   vlib_main_t *vm;
1412   u8 is_local;
1413 } ip4_session_table_show_ctx_t;
1414
1415 static int
1416 ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg)
1417 {
1418   ip4_session_table_show_ctx_t *ctx = arg;
1419   vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp,
1420                    ctx->is_local);
1421   return 1;
1422 }
1423
1424 void
1425 session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table,
1426                                    u8 type, u8 is_local)
1427 {
1428   ip4_session_table_show_ctx_t ctx = {
1429     .vm = vm,
1430     .is_local = is_local,
1431   };
1432   if (!is_local)
1433     vlib_cli_output (vm, "%-40s%-30s", "Session", "Application");
1434   else
1435     vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application");
1436   switch (type)
1437     {
1438       /* main table v4 */
1439     case 0:
1440       ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show,
1441                               &ctx);
1442       break;
1443     default:
1444       clib_warning ("not supported");
1445     }
1446 }
1447
1448 static clib_error_t *
1449 session_rule_command_fn (vlib_main_t * vm, unformat_input_t * input,
1450                          vlib_cli_command_t * cmd)
1451 {
1452   u32 proto = ~0, lcl_port, rmt_port, action = 0, lcl_plen = 0, rmt_plen = 0;
1453   clib_error_t *error = 0;
1454   u32 appns_index, scope = 0;
1455   ip46_address_t lcl_ip, rmt_ip;
1456   u8 is_ip4 = 1, conn_set = 0;
1457   u8 fib_proto, is_add = 1, *ns_id = 0;
1458   u8 *tag = 0;
1459   app_namespace_t *app_ns;
1460   int rv;
1461
1462   session_cli_return_if_not_enabled ();
1463
1464   clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
1465   clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
1466   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1467     {
1468       if (unformat (input, "del"))
1469         is_add = 0;
1470       else if (unformat (input, "add"))
1471         ;
1472       else if (unformat (input, "appns %_%v%_", &ns_id))
1473         ;
1474       else if (unformat (input, "scope global"))
1475         scope = SESSION_RULE_SCOPE_GLOBAL;
1476       else if (unformat (input, "scope local"))
1477         scope = SESSION_RULE_SCOPE_LOCAL;
1478       else if (unformat (input, "scope all"))
1479         scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL;
1480       else if (unformat (input, "proto %U", unformat_transport_proto, &proto))
1481         ;
1482       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1483                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1484                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1485                          &rmt_port))
1486         {
1487           is_ip4 = 1;
1488           conn_set = 1;
1489         }
1490       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1491                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1492                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1493                          &rmt_port))
1494         {
1495           is_ip4 = 0;
1496           conn_set = 1;
1497         }
1498       else if (unformat (input, "action %d", &action))
1499         ;
1500       else if (unformat (input, "tag %_%v%_", &tag))
1501         ;
1502       else
1503         {
1504           error = clib_error_return (0, "unknown input `%U'",
1505                                      format_unformat_error, input);
1506           goto done;
1507         }
1508     }
1509
1510   if (proto == ~0)
1511     {
1512       vlib_cli_output (vm, "proto must be set");
1513       goto done;
1514     }
1515   if (is_add && !conn_set && action == ~0)
1516     {
1517       vlib_cli_output (vm, "connection and action must be set for add");
1518       goto done;
1519     }
1520   if (!is_add && !tag && !conn_set)
1521     {
1522       vlib_cli_output (vm, "connection or tag must be set for delete");
1523       goto done;
1524     }
1525   if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
1526     {
1527       vlib_cli_output (vm, "tag too long (max u64)");
1528       goto done;
1529     }
1530
1531   if (ns_id)
1532     {
1533       app_ns = app_namespace_get_from_id (ns_id);
1534       if (!app_ns)
1535         {
1536           vlib_cli_output (vm, "namespace %v does not exist", ns_id);
1537           goto done;
1538         }
1539     }
1540   else
1541     {
1542       app_ns = app_namespace_get_default ();
1543     }
1544   appns_index = app_namespace_index (app_ns);
1545
1546   fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1547   session_rule_add_del_args_t args = {
1548     .transport_proto = proto,
1549     .table_args.lcl.fp_addr = lcl_ip,
1550     .table_args.lcl.fp_len = lcl_plen,
1551     .table_args.lcl.fp_proto = fib_proto,
1552     .table_args.rmt.fp_addr = rmt_ip,
1553     .table_args.rmt.fp_len = rmt_plen,
1554     .table_args.rmt.fp_proto = fib_proto,
1555     .table_args.lcl_port = lcl_port,
1556     .table_args.rmt_port = rmt_port,
1557     .table_args.action_index = action,
1558     .table_args.is_add = is_add,
1559     .table_args.tag = tag,
1560     .appns_index = appns_index,
1561     .scope = scope,
1562   };
1563   if ((rv = vnet_session_rule_add_del (&args)))
1564     error = clib_error_return (0, "rule add del returned %u", rv);
1565
1566 done:
1567   vec_free (ns_id);
1568   vec_free (tag);
1569   return error;
1570 }
1571
1572 /* *INDENT-OFF* */
1573 VLIB_CLI_COMMAND (session_rule_command, static) =
1574 {
1575   .path = "session rule",
1576   .short_help = "session rule [add|del] appns <ns_id> proto <proto> "
1577       "<lcl-ip/plen> <lcl-port> <rmt-ip/plen> <rmt-port> action <action>",
1578   .function = session_rule_command_fn,
1579 };
1580 /* *INDENT-ON* */
1581
1582 void
1583 session_lookup_dump_rules_table (u32 fib_index, u8 fib_proto,
1584                                  u8 transport_proto)
1585 {
1586   vlib_main_t *vm = vlib_get_main ();
1587   session_rules_table_t *srt;
1588   session_table_t *st;
1589   st = session_table_get_for_fib_index (fib_index, fib_proto);
1590   srt = &st->session_rules[transport_proto];
1591   session_rules_table_cli_dump (vm, srt, fib_proto);
1592 }
1593
1594 void
1595 session_lookup_dump_local_rules_table (u32 table_index, u8 fib_proto,
1596                                        u8 transport_proto)
1597 {
1598   vlib_main_t *vm = vlib_get_main ();
1599   session_rules_table_t *srt;
1600   session_table_t *st;
1601   st = session_table_get (table_index);
1602   srt = &st->session_rules[transport_proto];
1603   session_rules_table_cli_dump (vm, srt, fib_proto);
1604 }
1605
1606 static clib_error_t *
1607 show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
1608                                vlib_cli_command_t * cmd)
1609 {
1610   u32 transport_proto = ~0, lcl_port, rmt_port, lcl_plen, rmt_plen;
1611   u32 fib_index, scope = 0;
1612   ip46_address_t lcl_ip, rmt_ip;
1613   u8 is_ip4 = 1, show_one = 0;
1614   app_namespace_t *app_ns;
1615   session_rules_table_t *srt;
1616   session_table_t *st;
1617   u8 *ns_id = 0, fib_proto;
1618
1619   session_cli_return_if_not_enabled ();
1620
1621   clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
1622   clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
1623   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1624     {
1625       if (unformat (input, "%U", unformat_transport_proto, &transport_proto))
1626         ;
1627       else if (unformat (input, "appns %_%v%_", &ns_id))
1628         ;
1629       else if (unformat (input, "scope global"))
1630         scope = 1;
1631       else if (unformat (input, "scope local"))
1632         scope = 2;
1633       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1634                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1635                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1636                          &rmt_port))
1637         {
1638           is_ip4 = 1;
1639           show_one = 1;
1640         }
1641       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1642                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1643                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1644                          &rmt_port))
1645         {
1646           is_ip4 = 0;
1647           show_one = 1;
1648         }
1649       else
1650         return clib_error_return (0, "unknown input `%U'",
1651                                   format_unformat_error, input);
1652     }
1653
1654   if (transport_proto == ~0)
1655     {
1656       vlib_cli_output (vm, "transport proto must be set");
1657       return 0;
1658     }
1659
1660   if (ns_id)
1661     {
1662       app_ns = app_namespace_get_from_id (ns_id);
1663       if (!app_ns)
1664         {
1665           vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
1666           return 0;
1667         }
1668     }
1669   else
1670     {
1671       app_ns = app_namespace_get_default ();
1672     }
1673
1674   if (scope == 1 || scope == 0)
1675     {
1676       fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1677       fib_index = is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
1678       st = session_table_get_for_fib_index (fib_proto, fib_index);
1679     }
1680   else
1681     {
1682       st = app_namespace_get_local_table (app_ns);
1683     }
1684
1685   if (show_one)
1686     {
1687       srt = &st->session_rules[transport_proto];
1688       session_rules_table_show_rule (vm, srt, &lcl_ip, lcl_port, &rmt_ip,
1689                                      rmt_port, is_ip4);
1690       return 0;
1691     }
1692
1693   vlib_cli_output (vm, "%U rules table", format_transport_proto,
1694                    transport_proto);
1695   srt = &st->session_rules[transport_proto];
1696   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4);
1697   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP6);
1698
1699   vec_free (ns_id);
1700   return 0;
1701 }
1702
1703 /* *INDENT-OFF* */
1704 VLIB_CLI_COMMAND (show_session_rules_command, static) =
1705 {
1706   .path = "show session rules",
1707   .short_help = "show session rules [<proto> appns <id> <lcl-ip/plen> "
1708       "<lcl-port> <rmt-ip/plen> <rmt-port> scope <scope>]",
1709   .function = show_session_rules_command_fn,
1710 };
1711 /* *INDENT-ON* */
1712
1713 void
1714 session_lookup_init (void)
1715 {
1716   /*
1717    * Allocate default table and map it to fib_index 0
1718    */
1719   session_table_t *st = session_table_alloc ();
1720   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0);
1721   fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st);
1722   st->active_fib_proto = FIB_PROTOCOL_IP4;
1723   session_table_init (st, FIB_PROTOCOL_IP4);
1724   st = session_table_alloc ();
1725   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0);
1726   fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st);
1727   st->active_fib_proto = FIB_PROTOCOL_IP6;
1728   session_table_init (st, FIB_PROTOCOL_IP6);
1729 }
1730
1731 /*
1732  * fd.io coding-style-patch-verification: ON
1733  *
1734  * Local Variables:
1735  * eval: (c-set-style "gnu")
1736  * End:
1737  */