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