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