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