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