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