3a80286a9cb21d263de390bd7029d9ea8a631fee
[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   session_type_t session_type;
656
657   /*
658    * First, try a fully formed listener
659    */
660   session_type = session_type_from_proto_and_ip (proto, 1);
661   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
662   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
663   if (rv == 0)
664     return session_manager_get_listener (session_type, (u32) kv4.value);
665
666   /*
667    * Zero out the lcl ip and check if any 0/0 port binds have been done
668    */
669   if (use_wildcard)
670     {
671       kv4.key[0] = 0;
672       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
673       if (rv == 0)
674         return session_manager_get_listener (session_type, (u32) kv4.value);
675     }
676   else
677     {
678       kv4.key[0] = 0;
679     }
680
681   /*
682    * Zero out port and check if we have a proxy set up for our ip
683    */
684   make_v4_proxy_kv (&kv4, lcl, proto);
685   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
686   if (rv == 0)
687     return session_manager_get_listener (session_type, (u32) kv4.value);
688
689   return 0;
690 }
691
692 stream_session_t *
693 session_lookup_listener4 (u32 fib_index, ip4_address_t * lcl, u16 lcl_port,
694                           u8 proto)
695 {
696   session_table_t *st;
697   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
698   if (!st)
699     return 0;
700   return session_lookup_listener4_i (st, lcl, lcl_port, proto, 0);
701 }
702
703 static stream_session_t *
704 session_lookup_listener6_i (session_table_t * st, ip6_address_t * lcl,
705                             u16 lcl_port, u8 proto, u8 ip_wildcard)
706 {
707   session_kv6_t kv6;
708   int rv;
709   session_type_t session_type;
710
711   session_type = session_type_from_proto_and_ip (proto, 0);
712   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
713   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
714   if (rv == 0)
715     return session_manager_get_listener (session_type, (u32) kv6.value);
716
717   /* Zero out the lcl ip */
718   if (ip_wildcard)
719     {
720       kv6.key[0] = kv6.key[1] = 0;
721       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
722       if (rv == 0)
723         return session_manager_get_listener (session_type, (u32) kv6.value);
724     }
725   else
726     {
727       kv6.key[0] = kv6.key[1] = 0;
728     }
729
730   make_v6_proxy_kv (&kv6, lcl, proto);
731   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
732   if (rv == 0)
733     return session_manager_get_listener (session_type, (u32) kv6.value);
734   return 0;
735 }
736
737 stream_session_t *
738 session_lookup_listener6 (u32 fib_index, ip6_address_t * lcl, u16 lcl_port,
739                           u8 proto)
740 {
741   session_table_t *st;
742   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
743   if (!st)
744     return 0;
745   return session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
746 }
747
748 /**
749  * Lookup listener, exact or proxy (inaddr_any:0) match
750  */
751 stream_session_t *
752 session_lookup_listener (u32 table_index, session_endpoint_t * sep)
753 {
754   session_table_t *st;
755   st = session_table_get (table_index);
756   if (!st)
757     return 0;
758   if (sep->is_ip4)
759     return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port,
760                                        sep->transport_proto, 0);
761   else
762     return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port,
763                                        sep->transport_proto, 0);
764   return 0;
765 }
766
767 int
768 session_lookup_add_half_open (transport_connection_t * tc, u64 value)
769 {
770   session_table_t *st;
771   session_kv4_t kv4;
772   session_kv6_t kv6;
773
774   st = session_table_get_or_alloc_for_connection (tc);
775   if (!st)
776     return 0;
777   if (tc->is_ip4)
778     {
779       make_v4_ss_kv_from_tc (&kv4, tc);
780       kv4.value = value;
781       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
782                                        1 /* is_add */ );
783     }
784   else
785     {
786       make_v6_ss_kv_from_tc (&kv6, tc);
787       kv6.value = value;
788       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
789                                        1 /* is_add */ );
790     }
791 }
792
793 int
794 session_lookup_del_half_open (transport_connection_t * tc)
795 {
796   session_table_t *st;
797   session_kv4_t kv4;
798   session_kv6_t kv6;
799
800   st = session_table_get_for_connection (tc);
801   if (!st)
802     return -1;
803   if (tc->is_ip4)
804     {
805       make_v4_ss_kv_from_tc (&kv4, tc);
806       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
807                                        0 /* is_add */ );
808     }
809   else
810     {
811       make_v6_ss_kv_from_tc (&kv6, tc);
812       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
813                                        0 /* is_add */ );
814     }
815 }
816
817 u64
818 session_lookup_half_open_handle (transport_connection_t * tc)
819 {
820   session_table_t *st;
821   session_kv4_t kv4;
822   session_kv6_t kv6;
823   int rv;
824
825   st = session_table_get_for_fib_index (transport_connection_fib_proto (tc),
826                                         tc->fib_index);
827   if (!st)
828     return HALF_OPEN_LOOKUP_INVALID_VALUE;
829   if (tc->is_ip4)
830     {
831       make_v4_ss_kv (&kv4, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
832                      tc->rmt_port, tc->proto);
833       rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
834       if (rv == 0)
835         return kv4.value;
836     }
837   else
838     {
839       make_v6_ss_kv (&kv6, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
840                      tc->rmt_port, tc->proto);
841       rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
842       if (rv == 0)
843         return kv6.value;
844     }
845   return HALF_OPEN_LOOKUP_INVALID_VALUE;
846 }
847
848 transport_connection_t *
849 session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4)
850 {
851   u32 sst;
852
853   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
854     {
855       sst = session_type_from_proto_and_ip (proto, is_ip4);
856       return tp_vfts[sst].get_half_open (handle & 0xFFFFFFFF);
857     }
858   return 0;
859 }
860
861 /**
862  * Lookup connection with ip4 and transport layer information
863  *
864  * This is used on the fast path so it needs to be fast. Thereby,
865  * duplication of code and 'hacks' allowed.
866  *
867  * The lookup is incremental and returns whenever something is matched. The
868  * steps are:
869  * - Try to find an established session
870  * - Try to find a half-open connection
871  * - Try session rules table
872  * - Try to find a fully-formed or local source wildcarded (listener bound to
873  *   all interfaces) listener session
874  * - return 0
875  *
876  * @param fib_index     index of fib wherein the connection was received
877  * @param lcl           local ip4 address
878  * @param rmt           remote ip4 address
879  * @param lcl_port      local port
880  * @param rmt_port      remote port
881  * @param proto         transport protocol (e.g., tcp, udp)
882  * @param thread_index  thread index for request
883  * @param is_filtered   return flag that indicates if connection was filtered.
884  *
885  * @return pointer to transport connection, if one is found, 0 otherwise
886  */
887 transport_connection_t *
888 session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl,
889                                ip4_address_t * rmt, u16 lcl_port,
890                                u16 rmt_port, u8 proto, u32 thread_index,
891                                u8 * is_filtered)
892 {
893   session_table_t *st;
894   session_kv4_t kv4;
895   stream_session_t *s;
896   u32 action_index;
897   int rv;
898
899   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
900   if (PREDICT_FALSE (!st))
901     return 0;
902
903   /*
904    * Lookup session amongst established ones
905    */
906   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
907   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
908   if (rv == 0)
909     {
910       ASSERT ((u32) (kv4.value >> 32) == thread_index);
911       s = session_get (kv4.value & 0xFFFFFFFFULL, thread_index);
912       return tp_vfts[proto].get_connection (s->connection_index,
913                                             thread_index);
914     }
915
916   /*
917    * Try half-open connections
918    */
919   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
920   if (rv == 0)
921     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
922
923   /*
924    * Check the session rules table
925    */
926   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
927                                               rmt, lcl_port, rmt_port);
928   if (session_lookup_action_index_is_valid (action_index))
929     {
930       if ((*is_filtered = (action_index == SESSION_RULES_TABLE_ACTION_DROP)))
931         return 0;
932       if ((s = session_lookup_action_to_session (action_index,
933                                                  FIB_PROTOCOL_IP4, proto)))
934         return tp_vfts[proto].get_listener (s->connection_index);
935       return 0;
936     }
937
938   /*
939    * If nothing is found, check if any listener is available
940    */
941   s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1);
942   if (s)
943     return tp_vfts[proto].get_listener (s->connection_index);
944
945   return 0;
946 }
947
948 /**
949  * Lookup connection with ip4 and transport layer information
950  *
951  * Not optimized. This is used on the fast path so it needs to be fast.
952  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
953  * to that of @ref session_lookup_connection_wt4
954  *
955  * @param fib_index     index of the fib wherein the connection was received
956  * @param lcl           local ip4 address
957  * @param rmt           remote ip4 address
958  * @param lcl_port      local port
959  * @param rmt_port      remote port
960  * @param proto         transport protocol (e.g., tcp, udp)
961  *
962  * @return pointer to transport connection, if one is found, 0 otherwise
963  */
964 transport_connection_t *
965 session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl,
966                             ip4_address_t * rmt, u16 lcl_port, u16 rmt_port,
967                             u8 proto)
968 {
969   session_table_t *st;
970   session_kv4_t kv4;
971   stream_session_t *s;
972   u32 action_index;
973   int rv;
974
975   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
976   if (PREDICT_FALSE (!st))
977     return 0;
978
979   /*
980    * Lookup session amongst established ones
981    */
982   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
983   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
984   if (rv == 0)
985     {
986       s = session_get_from_handle (kv4.value);
987       return tp_vfts[proto].get_connection (s->connection_index,
988                                             s->thread_index);
989     }
990
991   /*
992    * Try half-open connections
993    */
994   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
995   if (rv == 0)
996     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
997
998   /*
999    * Check the session rules table
1000    */
1001   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
1002                                               rmt, lcl_port, rmt_port);
1003   if (session_lookup_action_index_is_valid (action_index))
1004     {
1005       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1006         return 0;
1007       if ((s = session_lookup_action_to_session (action_index,
1008                                                  FIB_PROTOCOL_IP4, proto)))
1009         return tp_vfts[proto].get_listener (s->connection_index);
1010       return 0;
1011     }
1012
1013   /*
1014    * If nothing is found, check if any listener is available
1015    */
1016   s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1);
1017   if (s)
1018     return tp_vfts[proto].get_listener (s->connection_index);
1019
1020   return 0;
1021 }
1022
1023 /**
1024  * Lookup session with ip4 and transport layer information
1025  *
1026  * Important note: this may look into another thread's pool table and
1027  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
1028  * if needed as soon as possible.
1029  *
1030  * Lookup logic is similar to that of @ref session_lookup_connection_wt4 but
1031  * this returns a session as opposed to a transport connection and it does not
1032  * try to lookup half-open sessions.
1033  *
1034  * Typically used by dgram connections
1035  */
1036 stream_session_t *
1037 session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
1038                       u16 lcl_port, u16 rmt_port, u8 proto)
1039 {
1040   session_table_t *st;
1041   session_kv4_t kv4;
1042   stream_session_t *s;
1043   u32 action_index;
1044   int rv;
1045
1046   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
1047   if (PREDICT_FALSE (!st))
1048     return 0;
1049
1050   /*
1051    * Lookup session amongst established ones
1052    */
1053   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
1054   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
1055   if (rv == 0)
1056     return session_get_from_handle_safe (kv4.value);
1057
1058   /*
1059    * Check the session rules table
1060    */
1061   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
1062                                               rmt, lcl_port, rmt_port);
1063   if (session_lookup_action_index_is_valid (action_index))
1064     {
1065       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1066         return 0;
1067       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP4,
1068                                                proto);
1069     }
1070
1071   /*
1072    *  If nothing is found, check if any listener is available
1073    */
1074   if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto, 1)))
1075     return s;
1076
1077   return 0;
1078 }
1079
1080 /**
1081  * Lookup connection with ip6 and transport layer information
1082  *
1083  * This is used on the fast path so it needs to be fast. Thereby,
1084  * duplication of code and 'hacks' allowed.
1085  *
1086  * The lookup is incremental and returns whenever something is matched. The
1087  * steps are:
1088  * - Try to find an established session
1089  * - Try to find a half-open connection
1090  * - Try session rules table
1091  * - Try to find a fully-formed or local source wildcarded (listener bound to
1092  *   all interfaces) listener session
1093  * - return 0
1094  *
1095  * @param fib_index     index of the fib wherein the connection was received
1096  * @param lcl           local ip6 address
1097  * @param rmt           remote ip6 address
1098  * @param lcl_port      local port
1099  * @param rmt_port      remote port
1100  * @param proto         transport protocol (e.g., tcp, udp)
1101  * @param thread_index  thread index for request
1102  *
1103  * @return pointer to transport connection, if one is found, 0 otherwise
1104  */
1105 transport_connection_t *
1106 session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
1107                                ip6_address_t * rmt, u16 lcl_port,
1108                                u16 rmt_port, u8 proto, u32 thread_index,
1109                                u8 * is_filtered)
1110 {
1111   session_table_t *st;
1112   stream_session_t *s;
1113   session_kv6_t kv6;
1114   u32 action_index;
1115   int rv;
1116
1117   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1118   if (PREDICT_FALSE (!st))
1119     return 0;
1120
1121   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1122   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1123   if (rv == 0)
1124     {
1125       ASSERT ((u32) (kv6.value >> 32) == thread_index);
1126       s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index);
1127       return tp_vfts[proto].get_connection (s->connection_index,
1128                                             thread_index);
1129     }
1130
1131   /* Try half-open connections */
1132   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1133   if (rv == 0)
1134     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
1135
1136   /* Check the session rules table */
1137   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1138                                               rmt, lcl_port, rmt_port);
1139   if (session_lookup_action_index_is_valid (action_index))
1140     {
1141       if ((*is_filtered = (action_index == SESSION_RULES_TABLE_ACTION_DROP)))
1142         return 0;
1143       if ((s = session_lookup_action_to_session (action_index,
1144                                                  FIB_PROTOCOL_IP6, proto)))
1145         return tp_vfts[proto].get_listener (s->connection_index);
1146       return 0;
1147     }
1148
1149   /* If nothing is found, check if any listener is available */
1150   s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
1151   if (s)
1152     return tp_vfts[proto].get_listener (s->connection_index);
1153
1154   return 0;
1155 }
1156
1157 /**
1158  * Lookup connection with ip6 and transport layer information
1159  *
1160  * Not optimized. This is used on the fast path so it needs to be fast.
1161  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
1162  * to that of @ref session_lookup_connection_wt4
1163  *
1164  * @param fib_index     index of the fib wherein the connection was received
1165  * @param lcl           local ip6 address
1166  * @param rmt           remote ip6 address
1167  * @param lcl_port      local port
1168  * @param rmt_port      remote port
1169  * @param proto         transport protocol (e.g., tcp, udp)
1170  *
1171  * @return pointer to transport connection, if one is found, 0 otherwise
1172  */
1173 transport_connection_t *
1174 session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
1175                             ip6_address_t * rmt, u16 lcl_port, u16 rmt_port,
1176                             u8 proto)
1177 {
1178   session_table_t *st;
1179   stream_session_t *s;
1180   session_kv6_t kv6;
1181   u32 action_index;
1182   int rv;
1183
1184   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1185   if (PREDICT_FALSE (!st))
1186     return 0;
1187
1188   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1189   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1190   if (rv == 0)
1191     {
1192       s = session_get_from_handle (kv6.value);
1193       return tp_vfts[proto].get_connection (s->connection_index,
1194                                             s->thread_index);
1195     }
1196
1197   /* Try half-open connections */
1198   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1199   if (rv == 0)
1200     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
1201
1202   /* Check the session rules table */
1203   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1204                                               rmt, lcl_port, rmt_port);
1205   if (session_lookup_action_index_is_valid (action_index))
1206     {
1207       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1208         return 0;
1209       if ((s = session_lookup_action_to_session (action_index,
1210                                                  FIB_PROTOCOL_IP6, proto)))
1211         return tp_vfts[proto].get_listener (s->connection_index);
1212       return 0;
1213     }
1214
1215   /* If nothing is found, check if any listener is available */
1216   s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1);
1217   if (s)
1218     return tp_vfts[proto].get_listener (s->connection_index);
1219
1220   return 0;
1221 }
1222
1223 /**
1224  * Lookup session with ip6 and transport layer information
1225  *
1226  * Important note: this may look into another thread's pool table and
1227  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
1228  * if needed as soon as possible.
1229  *
1230  * Lookup logic is similar to that of @ref session_lookup_connection_wt6 but
1231  * this returns a session as opposed to a transport connection and it does not
1232  * try to lookup half-open sessions.
1233  *
1234  * Typically used by dgram connections
1235  */
1236 stream_session_t *
1237 session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
1238                       u16 lcl_port, u16 rmt_port, u8 proto)
1239 {
1240   session_table_t *st;
1241   session_kv6_t kv6;
1242   stream_session_t *s;
1243   u32 action_index;
1244   int rv;
1245
1246   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1247   if (PREDICT_FALSE (!st))
1248     return 0;
1249
1250   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1251   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1252   if (rv == 0)
1253     return session_get_from_handle_safe (kv6.value);
1254
1255   /* Check the session rules table */
1256   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1257                                               rmt, lcl_port, rmt_port);
1258   if (session_lookup_action_index_is_valid (action_index))
1259     {
1260       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1261         return 0;
1262       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP6,
1263                                                proto);
1264     }
1265
1266   /* If nothing is found, check if any listener is available */
1267   if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto, 1)))
1268     return s;
1269   return 0;
1270 }
1271
1272 clib_error_t *
1273 vnet_session_rule_add_del (session_rule_add_del_args_t * args)
1274 {
1275   app_namespace_t *app_ns = app_namespace_get (args->appns_index);
1276   session_rules_table_t *srt;
1277   session_table_t *st;
1278   u32 fib_index;
1279   u8 fib_proto;
1280   clib_error_t *error;
1281
1282   if (!app_ns)
1283     return clib_error_return_code (0, VNET_API_ERROR_APP_INVALID_NS, 0,
1284                                    "invalid app ns");
1285   if (args->scope > 3)
1286     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
1287                                    "invalid scope");
1288   if (args->transport_proto != TRANSPORT_PROTO_TCP
1289       && args->transport_proto != TRANSPORT_PROTO_UDP)
1290     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
1291                                    "invalid transport proto");
1292   if ((args->scope & SESSION_RULE_SCOPE_GLOBAL) || args->scope == 0)
1293     {
1294       fib_proto = args->table_args.rmt.fp_proto;
1295       fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1296       st = session_table_get_for_fib_index (fib_proto, fib_index);
1297       srt = &st->session_rules[args->transport_proto];
1298       if ((error = session_rules_table_add_del (srt, &args->table_args)))
1299         {
1300           clib_error_report (error);
1301           return error;
1302         }
1303     }
1304   if (args->scope & SESSION_RULE_SCOPE_LOCAL)
1305     {
1306       memset (&args->table_args.lcl, 0, sizeof (args->table_args.lcl));
1307       args->table_args.lcl.fp_proto = args->table_args.rmt.fp_proto;
1308       args->table_args.lcl_port = 0;
1309       st = app_namespace_get_local_table (app_ns);
1310       srt = &st->session_rules[args->transport_proto];
1311       error = session_rules_table_add_del (srt, &args->table_args);
1312     }
1313   return error;
1314 }
1315
1316 /**
1317  * Mark (global) tables as pertaining to app ns
1318  */
1319 void
1320 session_lookup_set_tables_appns (app_namespace_t * app_ns)
1321 {
1322   session_table_t *st;
1323   u32 fib_index;
1324   u8 fp;
1325
1326   for (fp = 0; fp < ARRAY_LEN (fib_index_to_table_index); fp++)
1327     {
1328       fib_index = app_namespace_get_fib_index (app_ns, fp);
1329       st = session_table_get_for_fib_index (fp, fib_index);
1330       if (st)
1331         st->appns_index = app_namespace_index (app_ns);
1332     }
1333 }
1334
1335 u8 *
1336 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
1337 {
1338   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
1339   u32 is_local = va_arg (*args, u32);
1340   u8 *app_name, *str = 0;
1341   stream_session_t *session;
1342   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
1343
1344   if (!is_local)
1345     {
1346       session = session_get_from_handle (kvp->value);
1347       app_name = application_name_from_index (session->app_index);
1348       str = format (0, "[%U] %U:%d->%U:%d", format_transport_proto_short,
1349                     key->proto, format_ip4_address, &key->src,
1350                     clib_net_to_host_u16 (key->src_port), format_ip4_address,
1351                     &key->dst, clib_net_to_host_u16 (key->dst_port));
1352       s = format (s, "%-40v%-30v", str, app_name);
1353     }
1354   else
1355     {
1356       app_name = application_name_from_index (kvp->value);
1357       str = format (0, "[%U] %U:%d", format_transport_proto_short, key->proto,
1358                     format_ip4_address, &key->src,
1359                     clib_net_to_host_u16 (key->src_port));
1360       s = format (s, "%-30v%-30v", str, app_name);
1361     }
1362   vec_free (app_name);
1363   return s;
1364 }
1365
1366 typedef struct _ip4_session_table_show_ctx_t
1367 {
1368   vlib_main_t *vm;
1369   u8 is_local;
1370 } ip4_session_table_show_ctx_t;
1371
1372 static int
1373 ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg)
1374 {
1375   ip4_session_table_show_ctx_t *ctx = arg;
1376   vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp,
1377                    ctx->is_local);
1378   return 1;
1379 }
1380
1381 void
1382 session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table,
1383                                    u8 type, u8 is_local)
1384 {
1385   ip4_session_table_show_ctx_t ctx = {
1386     .vm = vm,
1387     .is_local = is_local,
1388   };
1389   if (!is_local)
1390     vlib_cli_output (vm, "%-40s%-30s", "Session", "Application");
1391   else
1392     vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application");
1393   switch (type)
1394     {
1395       /* main table v4 */
1396     case 0:
1397       ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show,
1398                               &ctx);
1399       break;
1400     default:
1401       clib_warning ("not supported");
1402     }
1403 }
1404
1405 static clib_error_t *
1406 session_rule_command_fn (vlib_main_t * vm, unformat_input_t * input,
1407                          vlib_cli_command_t * cmd)
1408 {
1409   u32 proto = ~0, lcl_port, rmt_port, action = 0, lcl_plen = 0, rmt_plen = 0;
1410   u32 appns_index, scope = 0;
1411   ip46_address_t lcl_ip, rmt_ip;
1412   u8 is_ip4 = 1, conn_set = 0;
1413   u8 fib_proto, is_add = 1, *ns_id = 0;
1414   u8 *tag = 0;
1415   app_namespace_t *app_ns;
1416   clib_error_t *error;
1417
1418   memset (&lcl_ip, 0, sizeof (lcl_ip));
1419   memset (&rmt_ip, 0, sizeof (rmt_ip));
1420   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1421     {
1422       if (unformat (input, "del"))
1423         is_add = 0;
1424       else if (unformat (input, "add"))
1425         ;
1426       else if (unformat (input, "appns %_%v%_", &ns_id))
1427         ;
1428       else if (unformat (input, "scope global"))
1429         scope = SESSION_RULE_SCOPE_GLOBAL;
1430       else if (unformat (input, "scope local"))
1431         scope = SESSION_RULE_SCOPE_LOCAL;
1432       else if (unformat (input, "scope all"))
1433         scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL;
1434       else if (unformat (input, "proto %U", unformat_transport_proto, &proto))
1435         ;
1436       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1437                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1438                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1439                          &rmt_port))
1440         {
1441           is_ip4 = 1;
1442           conn_set = 1;
1443         }
1444       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1445                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1446                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1447                          &rmt_port))
1448         {
1449           is_ip4 = 0;
1450           conn_set = 1;
1451         }
1452       else if (unformat (input, "action %d", &action))
1453         ;
1454       else if (unformat (input, "tag %_%v%_", &tag))
1455         ;
1456       else
1457         return clib_error_return (0, "unknown input `%U'",
1458                                   format_unformat_error, input);
1459     }
1460
1461   if (proto == ~0)
1462     {
1463       vlib_cli_output (vm, "proto must be set");
1464       return 0;
1465     }
1466   if (is_add && !conn_set && action == ~0)
1467     {
1468       vlib_cli_output (vm, "connection and action must be set for add");
1469       return 0;
1470     }
1471   if (!is_add && !tag && !conn_set)
1472     {
1473       vlib_cli_output (vm, "connection or tag must be set for delete");
1474       return 0;
1475     }
1476   if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
1477     {
1478       vlib_cli_output (vm, "tag too long (max u64)");
1479       return 0;
1480     }
1481
1482   if (ns_id)
1483     {
1484       app_ns = app_namespace_get_from_id (ns_id);
1485       if (!app_ns)
1486         {
1487           vlib_cli_output (vm, "namespace %v does not exist", ns_id);
1488           return 0;
1489         }
1490     }
1491   else
1492     {
1493       app_ns = app_namespace_get_default ();
1494     }
1495   appns_index = app_namespace_index (app_ns);
1496
1497   fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1498   session_rule_add_del_args_t args = {
1499     .table_args.lcl.fp_addr = lcl_ip,
1500     .table_args.lcl.fp_len = lcl_plen,
1501     .table_args.lcl.fp_proto = fib_proto,
1502     .table_args.rmt.fp_addr = rmt_ip,
1503     .table_args.rmt.fp_len = rmt_plen,
1504     .table_args.rmt.fp_proto = fib_proto,
1505     .table_args.lcl_port = lcl_port,
1506     .table_args.rmt_port = rmt_port,
1507     .table_args.action_index = action,
1508     .table_args.is_add = is_add,
1509     .table_args.tag = tag,
1510     .appns_index = appns_index,
1511     .scope = scope,
1512   };
1513   error = vnet_session_rule_add_del (&args);
1514   vec_free (tag);
1515   return error;
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   memset (&lcl_ip, 0, sizeof (lcl_ip));
1566   memset (&rmt_ip, 0, sizeof (rmt_ip));
1567   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1568     {
1569       if (unformat (input, "%U", unformat_transport_proto, &transport_proto))
1570         ;
1571       else if (unformat (input, "appns %_%v%_", &ns_id))
1572         ;
1573       else if (unformat (input, "scope global"))
1574         scope = 1;
1575       else if (unformat (input, "scope local"))
1576         scope = 2;
1577       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address,
1578                          &lcl_ip.ip4, &lcl_plen, &lcl_port,
1579                          unformat_ip4_address, &rmt_ip.ip4, &rmt_plen,
1580                          &rmt_port))
1581         {
1582           is_ip4 = 1;
1583           show_one = 1;
1584         }
1585       else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address,
1586                          &lcl_ip.ip6, &lcl_plen, &lcl_port,
1587                          unformat_ip6_address, &rmt_ip.ip6, &rmt_plen,
1588                          &rmt_port))
1589         {
1590           is_ip4 = 0;
1591           show_one = 1;
1592         }
1593       else
1594         return clib_error_return (0, "unknown input `%U'",
1595                                   format_unformat_error, input);
1596     }
1597
1598   if (transport_proto == ~0)
1599     {
1600       vlib_cli_output (vm, "transport proto must be set");
1601       return 0;
1602     }
1603
1604   if (ns_id)
1605     {
1606       app_ns = app_namespace_get_from_id (ns_id);
1607       if (!app_ns)
1608         {
1609           vlib_cli_output (vm, "appns %v doesn't exist", ns_id);
1610           return 0;
1611         }
1612     }
1613   else
1614     {
1615       app_ns = app_namespace_get_default ();
1616     }
1617
1618   if (scope == 1 || scope == 0)
1619     {
1620       fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
1621       fib_index = is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
1622       st = session_table_get_for_fib_index (fib_proto, fib_index);
1623     }
1624   else
1625     {
1626       st = app_namespace_get_local_table (app_ns);
1627     }
1628
1629   if (show_one)
1630     {
1631       srt = &st->session_rules[transport_proto];
1632       session_rules_table_show_rule (vm, srt, &lcl_ip, lcl_port, &rmt_ip,
1633                                      rmt_port, is_ip4);
1634       return 0;
1635     }
1636
1637   vlib_cli_output (vm, "%U rules table", format_transport_proto,
1638                    transport_proto);
1639   srt = &st->session_rules[transport_proto];
1640   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4);
1641   session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP6);
1642
1643   vec_free (ns_id);
1644   return 0;
1645 }
1646
1647 /* *INDENT-OFF* */
1648 VLIB_CLI_COMMAND (show_session_rules_command, static) =
1649 {
1650   .path = "show session rules",
1651   .short_help = "show session rules [<proto> appns <id> <lcl-ip/plen> "
1652       "<lcl-port> <rmt-ip/plen> <rmt-port> scope <scope>]",
1653   .function = show_session_rules_command_fn,
1654 };
1655 /* *INDENT-ON* */
1656
1657 void
1658 session_lookup_init (void)
1659 {
1660   /*
1661    * Allocate default table and map it to fib_index 0
1662    */
1663   session_table_t *st = session_table_alloc ();
1664   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0);
1665   fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st);
1666   st->active_fib_proto = FIB_PROTOCOL_IP4;
1667   session_table_init (st, FIB_PROTOCOL_IP4);
1668   st = session_table_alloc ();
1669   vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0);
1670   fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st);
1671   st->active_fib_proto = FIB_PROTOCOL_IP6;
1672   session_table_init (st, FIB_PROTOCOL_IP6);
1673 }
1674
1675 /*
1676  * fd.io coding-style-patch-verification: ON
1677  *
1678  * Local Variables:
1679  * eval: (c-set-style "gnu")
1680  * End:
1681  */