transport: cleanup
[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 clib_error_t *
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   clib_error_t *error;
1252
1253   if (!app_ns)
1254     return clib_error_return_code (0, VNET_API_ERROR_APP_INVALID_NS, 0,
1255                                    "invalid app ns");
1256   if (args->scope > 3)
1257     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
1258                                    "invalid scope");
1259   if (args->transport_proto != TRANSPORT_PROTO_TCP
1260       && args->transport_proto != TRANSPORT_PROTO_UDP)
1261     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
1262                                    "invalid transport proto");
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 ((error = session_rules_table_add_del (srt, &args->table_args)))
1270         {
1271           clib_error_report (error);
1272           return error;
1273         }
1274     }
1275   if (args->scope & SESSION_RULE_SCOPE_LOCAL)
1276     {
1277       clib_memset (&args->table_args.lcl, 0, sizeof (args->table_args.lcl));
1278       args->table_args.lcl.fp_proto = args->table_args.rmt.fp_proto;
1279       args->table_args.lcl_port = 0;
1280       st = app_namespace_get_local_table (app_ns);
1281       srt = &st->session_rules[args->transport_proto];
1282       error = session_rules_table_add_del (srt, &args->table_args);
1283     }
1284   return error;
1285 }
1286
1287 /**
1288  * Mark (global) tables as pertaining to app ns
1289  */
1290 void
1291 session_lookup_set_tables_appns (app_namespace_t * app_ns)
1292 {
1293   session_table_t *st;
1294   u32 fib_index;
1295   u8 fp;
1296
1297   for (fp = 0; fp < ARRAY_LEN (fib_index_to_table_index); fp++)
1298     {
1299       fib_index = app_namespace_get_fib_index (app_ns, fp);
1300       st = session_table_get_for_fib_index (fp, fib_index);
1301       if (st)
1302         st->appns_index = app_namespace_index (app_ns);
1303     }
1304 }
1305
1306 u8 *
1307 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
1308 {
1309   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
1310   u32 is_local = va_arg (*args, u32), app_wrk_index, session_index;
1311   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
1312   session_t *session;
1313   app_worker_t *app_wrk;
1314   const u8 *app_name;
1315   u8 *str = 0;
1316
1317   if (!is_local)
1318     {
1319       session = session_get_from_handle (kvp->value);
1320       app_wrk = app_worker_get (session->app_wrk_index);
1321       app_name = application_name_from_index (app_wrk->app_index);
1322       str = format (0, "[%U] %U:%d->%U:%d", format_transport_proto_short,
1323                     key->proto, format_ip4_address, &key->src,
1324                     clib_net_to_host_u16 (key->src_port), format_ip4_address,
1325                     &key->dst, clib_net_to_host_u16 (key->dst_port));
1326       s = format (s, "%-40v%-30v", str, app_name);
1327     }
1328   else
1329     {
1330       local_session_parse_handle (kvp->value, &app_wrk_index, &session_index);
1331       app_wrk = app_worker_get (app_wrk_index);
1332       app_name = application_name_from_index (app_wrk->app_index);
1333       str = format (0, "[%U] %U:%d", format_transport_proto_short, key->proto,
1334                     format_ip4_address, &key->src,
1335                     clib_net_to_host_u16 (key->src_port));
1336       s = format (s, "%-30v%-30v", str, app_name);
1337     }
1338   return s;
1339 }
1340
1341 typedef struct _ip4_session_table_show_ctx_t
1342 {
1343   vlib_main_t *vm;
1344   u8 is_local;
1345 } ip4_session_table_show_ctx_t;
1346
1347 static int
1348 ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg)
1349 {
1350   ip4_session_table_show_ctx_t *ctx = arg;
1351   vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp,
1352                    ctx->is_local);
1353   return 1;
1354 }
1355
1356 void
1357 session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table,
1358                                    u8 type, u8 is_local)
1359 {
1360   ip4_session_table_show_ctx_t ctx = {
1361     .vm = vm,
1362     .is_local = is_local,
1363   };
1364   if (!is_local)
1365     vlib_cli_output (vm, "%-40s%-30s", "Session", "Application");
1366   else
1367     vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application");
1368   switch (type)
1369     {
1370       /* main table v4 */
1371     case 0:
1372       ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show,
1373                               &ctx);
1374       break;
1375     default:
1376       clib_warning ("not supported");
1377     }
1378 }
1379
1380 static clib_error_t *
1381 session_rule_command_fn (vlib_main_t * vm, unformat_input_t * input,
1382                          vlib_cli_command_t * cmd)
1383 {
1384   u32 proto = ~0, lcl_port, rmt_port, action = 0, lcl_plen = 0, rmt_plen = 0;
1385   u32 appns_index, scope = 0;
1386   ip46_address_t lcl_ip, rmt_ip;
1387   u8 is_ip4 = 1, conn_set = 0;
1388   u8 fib_proto, is_add = 1, *ns_id = 0;
1389   u8 *tag = 0;
1390   app_namespace_t *app_ns;
1391   clib_error_t *error;
1392
1393   clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
1394   clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
1395   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1396     {
1397       if (unformat (input, "del"))
1398         is_add = 0;
1399       else if (unformat (input, "add"))
1400         ;
1401       else if (unformat (input, "appns %_%v%_", &ns_id))
1402         ;
1403       else if (unformat (input, "scope global"))
1404         scope = SESSION_RULE_SCOPE_GLOBAL;
1405       else if (unformat (input, "scope local"))
1406         scope = SESSION_RULE_SCOPE_LOCAL;
1407       else if (unformat (input, "scope all"))
1408         scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL;
1409       else if (unformat (input, "proto %U", unformat_transport_proto, &proto))
1410         ;
1411       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1412                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1413                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1414                          &rmt_port))
1415         {
1416           is_ip4 = 1;
1417           conn_set = 1;
1418         }
1419       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1420                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1421                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1422                          &rmt_port))
1423         {
1424           is_ip4 = 0;
1425           conn_set = 1;
1426         }
1427       else if (unformat (input, "action %d", &action))
1428         ;
1429       else if (unformat (input, "tag %_%v%_", &tag))
1430         ;
1431       else
1432         return clib_error_return (0, "unknown input `%U'",
1433                                   format_unformat_error, input);
1434     }
1435
1436   if (proto == ~0)
1437     {
1438       vlib_cli_output (vm, "proto must be set");
1439       return 0;
1440     }
1441   if (is_add && !conn_set && action == ~0)
1442     {
1443       vlib_cli_output (vm, "connection and action must be set for add");
1444       return 0;
1445     }
1446   if (!is_add && !tag && !conn_set)
1447     {
1448       vlib_cli_output (vm, "connection or tag must be set for delete");
1449       return 0;
1450     }
1451   if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
1452     {
1453       vlib_cli_output (vm, "tag too long (max u64)");
1454       return 0;
1455     }
1456
1457   if (ns_id)
1458     {
1459       app_ns = app_namespace_get_from_id (ns_id);
1460       if (!app_ns)
1461         {
1462           vlib_cli_output (vm, "namespace %v does not exist", ns_id);
1463           return 0;
1464         }
1465     }
1466   else
1467     {
1468       app_ns = app_namespace_get_default ();
1469     }
1470   appns_index = app_namespace_index (app_ns);
1471
1472   fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1473   session_rule_add_del_args_t args = {
1474     .table_args.lcl.fp_addr = lcl_ip,
1475     .table_args.lcl.fp_len = lcl_plen,
1476     .table_args.lcl.fp_proto = fib_proto,
1477     .table_args.rmt.fp_addr = rmt_ip,
1478     .table_args.rmt.fp_len = rmt_plen,
1479     .table_args.rmt.fp_proto = fib_proto,
1480     .table_args.lcl_port = lcl_port,
1481     .table_args.rmt_port = rmt_port,
1482     .table_args.action_index = action,
1483     .table_args.is_add = is_add,
1484     .table_args.tag = tag,
1485     .appns_index = appns_index,
1486     .scope = scope,
1487   };
1488   error = vnet_session_rule_add_del (&args);
1489   vec_free (tag);
1490   return error;
1491 }
1492
1493 /* *INDENT-OFF* */
1494 VLIB_CLI_COMMAND (session_rule_command, static) =
1495 {
1496   .path = "session rule",
1497   .short_help = "session rule [add|del] appns <ns_id> proto <proto> "
1498       "<lcl-ip/plen> <lcl-port> <rmt-ip/plen> <rmt-port> action <action>",
1499   .function = session_rule_command_fn,
1500 };
1501 /* *INDENT-ON* */
1502
1503 void
1504 session_lookup_dump_rules_table (u32 fib_index, u8 fib_proto,
1505                                  u8 transport_proto)
1506 {
1507   vlib_main_t *vm = vlib_get_main ();
1508   session_rules_table_t *srt;
1509   session_table_t *st;
1510   st = session_table_get_for_fib_index (fib_index, fib_proto);
1511   srt = &st->session_rules[transport_proto];
1512   session_rules_table_cli_dump (vm, srt, fib_proto);
1513 }
1514
1515 void
1516 session_lookup_dump_local_rules_table (u32 table_index, u8 fib_proto,
1517                                        u8 transport_proto)
1518 {
1519   vlib_main_t *vm = vlib_get_main ();
1520   session_rules_table_t *srt;
1521   session_table_t *st;
1522   st = session_table_get (table_index);
1523   srt = &st->session_rules[transport_proto];
1524   session_rules_table_cli_dump (vm, srt, fib_proto);
1525 }
1526
1527 static clib_error_t *
1528 show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input,
1529                                vlib_cli_command_t * cmd)
1530 {
1531   u32 transport_proto = ~0, lcl_port, rmt_port, lcl_plen, rmt_plen;
1532   u32 fib_index, scope = 0;
1533   ip46_address_t lcl_ip, rmt_ip;
1534   u8 is_ip4 = 1, show_one = 0;
1535   app_namespace_t *app_ns;
1536   session_rules_table_t *srt;
1537   session_table_t *st;
1538   u8 *ns_id = 0, fib_proto;
1539
1540   clib_memset (&lcl_ip, 0, sizeof (lcl_ip));
1541   clib_memset (&rmt_ip, 0, sizeof (rmt_ip));
1542   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1543     {
1544       if (unformat (input, "%U", unformat_transport_proto, &transport_proto))
1545         ;
1546       else if (unformat (input, "appns %_%v%_", &ns_id))
1547         ;
1548       else if (unformat (input, "scope global"))
1549         scope = 1;
1550       else if (unformat (input, "scope local"))
1551         scope = 2;
1552       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1553                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1554                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1555                          &rmt_port))
1556         {
1557           is_ip4 = 1;
1558           show_one = 1;
1559         }
1560       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1561                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1562                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1563                          &rmt_port))
1564         {
1565           is_ip4 = 0;
1566           show_one = 1;
1567         }
1568       else
1569         return clib_error_return (0, "unknown input `%U'",
1570                                   format_unformat_error, input);
1571     }
1572
1573   if (transport_proto == ~0)
1574     {
1575       vlib_cli_output (vm, "transport proto must be set");
1576       return 0;
1577     }
1578
1579   if (ns_id)
1580     {
1581       app_ns = app_namespace_get_from_id (ns_id);
1582       if (!app_ns)
1583         {
1584           vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
1585           return 0;
1586         }
1587     }
1588   else
1589     {
1590       app_ns = app_namespace_get_default ();
1591     }
1592
1593   if (scope == 1 || scope == 0)
1594     {
1595       fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1596       fib_index = is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
1597       st = session_table_get_for_fib_index (fib_proto, fib_index);
1598     }
1599   else
1600     {
1601       st = app_namespace_get_local_table (app_ns);
1602     }
1603
1604   if (show_one)
1605     {
1606       srt = &st->session_rules[transport_proto];
1607       session_rules_table_show_rule (vm, srt, &lcl_ip, lcl_port, &rmt_ip,
1608                                      rmt_port, is_ip4);
1609       return 0;
1610     }
1611
1612   vlib_cli_output (vm, "%U rules table", format_transport_proto,
1613                    transport_proto);
1614   srt = &st->session_rules[transport_proto];
1615   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4);
1616   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP6);
1617
1618   vec_free (ns_id);
1619   return 0;
1620 }
1621
1622 /* *INDENT-OFF* */
1623 VLIB_CLI_COMMAND (show_session_rules_command, static) =
1624 {
1625   .path = "show session rules",
1626   .short_help = "show session rules [<proto> appns <id> <lcl-ip/plen> "
1627       "<lcl-port> <rmt-ip/plen> <rmt-port> scope <scope>]",
1628   .function = show_session_rules_command_fn,
1629 };
1630 /* *INDENT-ON* */
1631
1632 void
1633 session_lookup_init (void)
1634 {
1635   /*
1636    * Allocate default table and map it to fib_index 0
1637    */
1638   session_table_t *st = session_table_alloc ();
1639   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0);
1640   fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st);
1641   st->active_fib_proto = FIB_PROTOCOL_IP4;
1642   session_table_init (st, FIB_PROTOCOL_IP4);
1643   st = session_table_alloc ();
1644   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0);
1645   fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st);
1646   st->active_fib_proto = FIB_PROTOCOL_IP6;
1647   session_table_init (st, FIB_PROTOCOL_IP6);
1648 }
1649
1650 /*
1651  * fd.io coding-style-patch-verification: ON
1652  *
1653  * Local Variables:
1654  * eval: (c-set-style "gnu")
1655  * End:
1656  */