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