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