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