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