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