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