session: support local sessions and deprecate redirects
[vpp.git] / src / vnet / session / session_lookup.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /** Generate typed init functions for multiple hash table styles... */
17 #include <vppinfra/bihash_16_8.h>
18 #include <vppinfra/bihash_template.h>
19
20 #include <vppinfra/bihash_template.c>
21
22 #undef __included_bihash_template_h__
23
24 #include <vppinfra/bihash_48_8.h>
25 #include <vppinfra/bihash_template.h>
26
27 #include <vppinfra/bihash_template.c>
28 #include <vnet/session/session_lookup.h>
29 #include <vnet/session/session.h>
30 #include <vnet/session/application.h>
31
32 /**
33  * External vector of per transport virtual functions table
34  */
35 extern transport_proto_vft_t *tp_vfts;
36
37 /**
38  * Network namespace index (i.e., fib index) to session lookup table. We
39  * should have one per network protocol type but for now we only support IP4/6
40  */
41 static u32 *fib_index_to_table_index[2];
42
43 /* *INDENT-OFF* */
44 /* 16 octets */
45 typedef CLIB_PACKED (struct {
46   union
47     {
48       struct
49         {
50           ip4_address_t src;
51           ip4_address_t dst;
52           u16 src_port;
53           u16 dst_port;
54           /* align by making this 4 octets even though its a 1-bit field
55            * NOTE: avoid key overlap with other transports that use 5 tuples for
56            * session identification.
57            */
58           u32 proto;
59         };
60       u64 as_u64[2];
61     };
62 }) v4_connection_key_t;
63
64 typedef CLIB_PACKED (struct {
65   union
66     {
67       struct
68         {
69           /* 48 octets */
70           ip6_address_t src;
71           ip6_address_t dst;
72           u16 src_port;
73           u16 dst_port;
74           u32 proto;
75           u64 unused;
76         };
77       u64 as_u64[6];
78     };
79 }) v6_connection_key_t;
80 /* *INDENT-ON* */
81
82 typedef clib_bihash_kv_16_8_t session_kv4_t;
83 typedef clib_bihash_kv_48_8_t session_kv6_t;
84
85 always_inline void
86 make_v4_ss_kv (session_kv4_t * kv, ip4_address_t * lcl, ip4_address_t * rmt,
87                u16 lcl_port, u16 rmt_port, u8 proto)
88 {
89   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
90
91   key->src.as_u32 = lcl->as_u32;
92   key->dst.as_u32 = rmt->as_u32;
93   key->src_port = lcl_port;
94   key->dst_port = rmt_port;
95   key->proto = proto;
96
97   kv->value = ~0ULL;
98 }
99
100 always_inline void
101 make_v4_listener_kv (session_kv4_t * kv, ip4_address_t * lcl, u16 lcl_port,
102                      u8 proto)
103 {
104   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
105
106   key->src.as_u32 = lcl->as_u32;
107   key->dst.as_u32 = 0;
108   key->src_port = lcl_port;
109   key->dst_port = 0;
110   key->proto = proto;
111
112   kv->value = ~0ULL;
113 }
114
115 always_inline void
116 make_v4_proxy_kv (session_kv4_t * kv, ip4_address_t * lcl, u8 proto)
117 {
118   v4_connection_key_t *key = (v4_connection_key_t *) kv->key;
119
120   key->src.as_u32 = lcl->as_u32;
121   key->dst.as_u32 = 0;
122   key->src_port = 0;
123   key->dst_port = 0;
124   key->proto = proto;
125
126   kv->value = ~0ULL;
127 }
128
129 always_inline void
130 make_v4_ss_kv_from_tc (session_kv4_t * kv, transport_connection_t * tc)
131 {
132   make_v4_ss_kv (kv, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
133                  tc->rmt_port, tc->proto);
134 }
135
136 always_inline void
137 make_v6_ss_kv (session_kv6_t * kv, ip6_address_t * lcl, ip6_address_t * rmt,
138                u16 lcl_port, u16 rmt_port, u8 proto)
139 {
140   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
141
142   key->src.as_u64[0] = lcl->as_u64[0];
143   key->src.as_u64[1] = lcl->as_u64[1];
144   key->dst.as_u64[0] = rmt->as_u64[0];
145   key->dst.as_u64[1] = rmt->as_u64[1];
146   key->src_port = lcl_port;
147   key->dst_port = rmt_port;
148   key->proto = proto;
149   key->unused = 0;
150
151   kv->value = ~0ULL;
152 }
153
154 always_inline void
155 make_v6_listener_kv (session_kv6_t * kv, ip6_address_t * lcl, u16 lcl_port,
156                      u8 proto)
157 {
158   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
159
160   key->src.as_u64[0] = lcl->as_u64[0];
161   key->src.as_u64[1] = lcl->as_u64[1];
162   key->dst.as_u64[0] = 0;
163   key->dst.as_u64[1] = 0;
164   key->src_port = lcl_port;
165   key->dst_port = 0;
166   key->proto = proto;
167   key->unused = 0;
168
169   kv->value = ~0ULL;
170 }
171
172 always_inline void
173 make_v6_proxy_kv (session_kv6_t * kv, ip6_address_t * lcl, u8 proto)
174 {
175   v6_connection_key_t *key = (v6_connection_key_t *) kv->key;
176
177   key->src.as_u64[0] = lcl->as_u64[0];
178   key->src.as_u64[1] = lcl->as_u64[1];
179   key->dst.as_u64[0] = 0;
180   key->dst.as_u64[1] = 0;
181   key->src_port = 0;
182   key->dst_port = 0;
183   key->proto = proto;
184   key->unused = 0;
185
186   kv->value = ~0ULL;
187 }
188
189 always_inline void
190 make_v6_ss_kv_from_tc (session_kv6_t * kv, transport_connection_t * tc)
191 {
192   make_v6_ss_kv (kv, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
193                  tc->rmt_port, tc->proto);
194 }
195
196 static session_table_t *
197 session_table_get_or_alloc (u8 fib_proto, u8 fib_index)
198 {
199   session_table_t *st;
200   u32 table_index;
201   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
202     {
203       st = session_table_alloc ();
204       table_index = session_table_index (st);
205       vec_validate (fib_index_to_table_index[fib_proto], fib_index);
206       fib_index_to_table_index[fib_proto][fib_index] = table_index;
207       st->active_fib_proto = fib_proto;
208       session_table_init (st, fib_proto);
209       return st;
210     }
211   else
212     {
213       table_index = fib_index_to_table_index[fib_proto][fib_index];
214       return session_table_get (table_index);
215     }
216 }
217
218 static session_table_t *
219 session_table_get_or_alloc_for_connection (transport_connection_t * tc)
220 {
221   u32 fib_proto;
222   fib_proto = transport_connection_fib_proto (tc);
223   return session_table_get_or_alloc (fib_proto, tc->fib_index);
224 }
225
226 static session_table_t *
227 session_table_get_for_connection (transport_connection_t * tc)
228 {
229   u32 fib_proto = transport_connection_fib_proto (tc);
230   if (vec_len (fib_index_to_table_index[fib_proto]) <= tc->fib_index)
231     return 0;
232   return
233     session_table_get (fib_index_to_table_index[fib_proto][tc->fib_index]);
234 }
235
236 static session_table_t *
237 session_table_get_for_fib_index (u32 fib_proto, u32 fib_index)
238 {
239   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
240     return 0;
241   return session_table_get (fib_index_to_table_index[fib_proto][fib_index]);
242 }
243
244 u32
245 session_lookup_get_index_for_fib (u32 fib_proto, u32 fib_index)
246 {
247   if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index)
248     return SESSION_TABLE_INVALID_INDEX;
249   return fib_index_to_table_index[fib_proto][fib_index];
250 }
251
252 /**
253  * Add transport connection to a session table
254  *
255  * Session lookup 5-tuple (src-ip, dst-ip, src-port, dst-port, session-type)
256  * is added to requested session table.
257  *
258  * @param tc            transport connection to be added
259  * @param value         value to be stored
260  *
261  * @return non-zero if failure
262  */
263 int
264 session_lookup_add_connection (transport_connection_t * tc, u64 value)
265 {
266   session_table_t *st;
267   session_kv4_t kv4;
268   session_kv6_t kv6;
269
270   st = session_table_get_or_alloc_for_connection (tc);
271   if (!st)
272     return -1;
273   if (tc->is_ip4)
274     {
275       make_v4_ss_kv_from_tc (&kv4, tc);
276       kv4.value = value;
277       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4,
278                                        1 /* is_add */ );
279     }
280   else
281     {
282       make_v6_ss_kv_from_tc (&kv6, tc);
283       kv6.value = value;
284       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6,
285                                        1 /* is_add */ );
286     }
287 }
288
289 int
290 session_lookup_add_session_endpoint (u32 table_index,
291                                      session_endpoint_t * sep, u64 value)
292 {
293   session_table_t *st;
294   session_kv4_t kv4;
295   session_kv6_t kv6;
296
297   st = session_table_get (table_index);
298   if (!st)
299     return -1;
300   if (sep->is_ip4)
301     {
302       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
303                            sep->transport_proto);
304       kv4.value = value;
305       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 1);
306     }
307   else
308     {
309       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
310                            sep->transport_proto);
311       kv6.value = value;
312       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 1);
313     }
314 }
315
316 int
317 session_lookup_del_session_endpoint (u32 table_index,
318                                      session_endpoint_t * sep)
319 {
320   session_table_t *st;
321   session_kv4_t kv4;
322   session_kv6_t kv6;
323
324   st = session_table_get (table_index);
325   if (!st)
326     return -1;
327   if (sep->is_ip4)
328     {
329       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
330                            sep->transport_proto);
331       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 0);
332     }
333   else
334     {
335       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
336                            sep->transport_proto);
337       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 0);
338     }
339 }
340
341 /**
342  * Delete transport connection from session table
343  *
344  * @param table_index   session table index
345  * @param tc            transport connection to be removed
346  *
347  * @return non-zero if failure
348  */
349 int
350 session_lookup_del_connection (transport_connection_t * tc)
351 {
352   session_table_t *st;
353   session_kv4_t kv4;
354   session_kv6_t kv6;
355
356   st = session_table_get_for_connection (tc);
357   if (!st)
358     return -1;
359   if (tc->is_ip4)
360     {
361       make_v4_ss_kv_from_tc (&kv4, tc);
362       return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4,
363                                        0 /* is_add */ );
364     }
365   else
366     {
367       make_v6_ss_kv_from_tc (&kv6, tc);
368       return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6,
369                                        0 /* is_add */ );
370     }
371 }
372
373 int
374 session_lookup_del_session (stream_session_t * s)
375 {
376   transport_proto_t tp = session_get_transport_proto (s);
377   transport_connection_t *ts;
378   ts = tp_vfts[tp].get_connection (s->connection_index, s->thread_index);
379   return session_lookup_del_connection (ts);
380 }
381
382 static u8
383 session_lookup_action_index_is_valid (u32 action_index)
384 {
385   if (action_index == SESSION_RULES_TABLE_ACTION_ALLOW
386       || action_index == SESSION_RULES_TABLE_INVALID_INDEX)
387     return 0;
388   return 1;
389 }
390
391 static u64
392 session_lookup_action_to_handle (u32 action_index)
393 {
394   switch (action_index)
395     {
396     case SESSION_RULES_TABLE_ACTION_DROP:
397       return SESSION_DROP_HANDLE;
398     case SESSION_RULES_TABLE_ACTION_ALLOW:
399     case SESSION_RULES_TABLE_INVALID_INDEX:
400       return SESSION_INVALID_HANDLE;
401     default:
402       /* application index */
403       return action_index;
404     }
405 }
406
407 static stream_session_t *
408 session_lookup_app_listen_session (u32 app_index, u8 fib_proto,
409                                    u8 transport_proto)
410 {
411   application_t *app;
412   app = application_get_if_valid (app_index);
413   if (!app)
414     return 0;
415
416   return application_first_listener (app, fib_proto, transport_proto);
417 }
418
419 static stream_session_t *
420 session_lookup_action_to_session (u32 action_index, u8 fib_proto,
421                                   u8 transport_proto)
422 {
423   u32 app_index;
424   app_index = session_lookup_action_to_handle (action_index);
425   /* Nothing sophisticated for now, action index is app index */
426   return session_lookup_app_listen_session (app_index, fib_proto,
427                                             transport_proto);
428 }
429
430 /** UNUSED */
431 stream_session_t *
432 session_lookup_rules_table_session4 (session_table_t * st, u8 proto,
433                                      ip4_address_t * lcl, u16 lcl_port,
434                                      ip4_address_t * rmt, u16 rmt_port)
435 {
436   session_rules_table_t *srt = &st->session_rules[proto];
437   u32 action_index, app_index;
438   action_index = session_rules_table_lookup4 (srt, lcl, rmt, lcl_port,
439                                               rmt_port);
440   app_index = session_lookup_action_to_handle (action_index);
441   /* Nothing sophisticated for now, action index is app index */
442   return session_lookup_app_listen_session (app_index, FIB_PROTOCOL_IP4,
443                                             proto);
444 }
445
446 /** UNUSED */
447 stream_session_t *
448 session_lookup_rules_table_session6 (session_table_t * st, u8 proto,
449                                      ip6_address_t * lcl, u16 lcl_port,
450                                      ip6_address_t * rmt, u16 rmt_port)
451 {
452   session_rules_table_t *srt = &st->session_rules[proto];
453   u32 action_index, app_index;
454   action_index = session_rules_table_lookup6 (srt, lcl, rmt, lcl_port,
455                                               rmt_port);
456   app_index = session_lookup_action_to_handle (action_index);
457   return session_lookup_app_listen_session (app_index, FIB_PROTOCOL_IP6,
458                                             proto);
459 }
460
461 /**
462  * Lookup listener for session endpoint in table
463  *
464  * @param table_index table where the endpoint should be looked up
465  * @param sep session endpoint to be looked up
466  * @param use_rules flag that indicates if the session rules of the table
467  *                  should be used
468  * @return invalid handle if nothing is found, the handle of a valid listener
469  *         or an action derived handle if a rule is hit
470  */
471 u64
472 session_lookup_endpoint_listener (u32 table_index, session_endpoint_t * sep,
473                                   u8 use_rules)
474 {
475   session_rules_table_t *srt;
476   session_table_t *st;
477   u32 ai;
478   int rv;
479
480   st = session_table_get (table_index);
481   if (!st)
482     return SESSION_INVALID_HANDLE;
483   if (sep->is_ip4)
484     {
485       session_kv4_t kv4;
486       ip4_address_t lcl4;
487
488       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
489                            sep->transport_proto);
490       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
491       if (rv == 0)
492         return kv4.value;
493       if (use_rules)
494         {
495           memset (&lcl4, 0, sizeof (lcl4));
496           srt = &st->session_rules[sep->transport_proto];
497           ai = session_rules_table_lookup4 (srt, &lcl4, &sep->ip.ip4, 0,
498                                             sep->port);
499           if (session_lookup_action_index_is_valid (ai))
500             return session_lookup_action_to_handle (ai);
501         }
502     }
503   else
504     {
505       session_kv6_t kv6;
506       ip6_address_t lcl6;
507
508       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
509                            sep->transport_proto);
510       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
511       if (rv == 0)
512         return kv6.value;
513
514       if (use_rules)
515         {
516           memset (&lcl6, 0, sizeof (lcl6));
517           srt = &st->session_rules[sep->transport_proto];
518           ai = session_rules_table_lookup6 (srt, &lcl6, &sep->ip.ip6, 0,
519                                             sep->port);
520           if (session_lookup_action_index_is_valid (ai))
521             return session_lookup_action_to_handle (ai);
522         }
523     }
524   return SESSION_INVALID_HANDLE;
525 }
526
527 /**
528  * Look up endpoint in local session table
529  *
530  * The result, for now, is an application index and it may in the future
531  * be extended to a more complicated "action object". The only action we
532  * emulate now is "drop" and for that we return a special app index.
533  *
534  * Lookup logic is to check in order:
535  * - the rules in the table (connect acls)
536  * - session sub-table for a listener
537  * - session sub-table for a local listener (zeroed addr)
538  *
539  * @param table_index table where the lookup should be done
540  * @param sep session endpoint to be looked up
541  * @return session handle that can be interpreted as an adjacency
542  */
543 u64
544 session_lookup_local_endpoint (u32 table_index, session_endpoint_t * sep)
545 {
546   session_rules_table_t *srt;
547   session_table_t *st;
548   u32 ai;
549   int rv;
550
551   st = session_table_get (table_index);
552   if (!st)
553     return SESSION_INVALID_INDEX;
554   ASSERT (st->is_local);
555
556   if (sep->is_ip4)
557     {
558       session_kv4_t kv4;
559       ip4_address_t lcl4;
560
561       /*
562        * Check if endpoint has special rules associated
563        */
564       memset (&lcl4, 0, sizeof (lcl4));
565       srt = &st->session_rules[sep->transport_proto];
566       ai = session_rules_table_lookup4 (srt, &lcl4, &sep->ip.ip4, 0,
567                                         sep->port);
568       if (session_lookup_action_index_is_valid (ai))
569         return session_lookup_action_to_handle (ai);
570
571       /*
572        * Check if session endpoint is a listener
573        */
574       make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port,
575                            sep->transport_proto);
576       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
577       if (rv == 0)
578         return kv4.value;
579
580       /*
581        * Zero out the ip. Logic is that connect to local ips, say
582        * 127.0.0.1:port, can match 0.0.0.0:port
583        */
584       kv4.key[0] = 0;
585       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
586       if (rv == 0)
587         return kv4.value;
588
589       /*
590        * Zero out the port and check if we have proxy
591        */
592       kv4.key[1] = 0;
593       rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
594       if (rv == 0)
595         return kv4.value;
596     }
597   else
598     {
599       session_kv6_t kv6;
600       ip6_address_t lcl6;
601
602       memset (&lcl6, 0, sizeof (lcl6));
603       srt = &st->session_rules[sep->transport_proto];
604       ai = session_rules_table_lookup6 (srt, &lcl6, &sep->ip.ip6, 0,
605                                         sep->port);
606       if (session_lookup_action_index_is_valid (ai))
607         return session_lookup_action_to_handle (ai);
608
609       make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port,
610                            sep->transport_proto);
611       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
612       if (rv == 0)
613         return kv6.value;
614
615       /*
616        * Zero out the ip. Same logic as above.
617        */
618       kv6.key[0] = kv6.key[1] = 0;
619       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
620       if (rv == 0)
621         return kv6.value;
622
623       /*
624        * Zero out the port. Same logic as above.
625        */
626       kv6.key[4] = kv6.key[5] = 0;
627       rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
628       if (rv == 0)
629         return kv6.value;
630     }
631   return SESSION_INVALID_HANDLE;
632 }
633
634 static stream_session_t *
635 session_lookup_listener4_i (session_table_t * st, ip4_address_t * lcl,
636                             u16 lcl_port, u8 proto)
637 {
638   session_kv4_t kv4;
639   int rv;
640   session_type_t session_type;
641
642   /*
643    * First, try a fully formed listener
644    */
645   session_type = session_type_from_proto_and_ip (proto, 1);
646   make_v4_listener_kv (&kv4, lcl, lcl_port, proto);
647   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
648   if (rv == 0)
649     return session_manager_get_listener (session_type, (u32) kv4.value);
650
651   /*
652    * Zero out the lcl ip and check if any 0/0 port binds have been done
653    */
654   kv4.key[0] = 0;
655   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
656   if (rv == 0)
657     return session_manager_get_listener (session_type, (u32) kv4.value);
658
659   /*
660    * Zero out port and check if we have a proxy set up for our ip
661    */
662   make_v4_proxy_kv (&kv4, lcl, proto);
663   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
664   if (rv == 0)
665     return session_manager_get_listener (session_type, (u32) kv4.value);
666
667   return 0;
668 }
669
670 stream_session_t *
671 session_lookup_listener4 (u32 fib_index, ip4_address_t * lcl, u16 lcl_port,
672                           u8 proto)
673 {
674   session_table_t *st;
675   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
676   if (!st)
677     return 0;
678   return session_lookup_listener4_i (st, lcl, lcl_port, proto);
679 }
680
681 static stream_session_t *
682 session_lookup_listener6_i (session_table_t * st, ip6_address_t * lcl,
683                             u16 lcl_port, u8 proto)
684 {
685   session_kv6_t kv6;
686   int rv;
687   session_type_t session_type;
688
689   session_type = session_type_from_proto_and_ip (proto, 0);
690   make_v6_listener_kv (&kv6, lcl, lcl_port, proto);
691   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
692   if (rv == 0)
693     return session_manager_get_listener (session_type, (u32) kv6.value);
694
695   /* Zero out the lcl ip */
696   kv6.key[0] = kv6.key[1] = 0;
697   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
698   if (rv == 0)
699     return session_manager_get_listener (session_type, (u32) kv6.value);
700
701   make_v6_proxy_kv (&kv6, lcl, proto);
702   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
703   if (rv == 0)
704     return session_manager_get_listener (session_type, (u32) kv6.value);
705   return 0;
706 }
707
708 stream_session_t *
709 session_lookup_listener6 (u32 fib_index, ip6_address_t * lcl, u16 lcl_port,
710                           u8 proto)
711 {
712   session_table_t *st;
713   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
714   if (!st)
715     return 0;
716   return session_lookup_listener6_i (st, lcl, lcl_port, proto);
717 }
718
719 stream_session_t *
720 session_lookup_listener (u32 table_index, session_endpoint_t * sep)
721 {
722   session_table_t *st;
723   st = session_table_get (table_index);
724   if (!st)
725     return 0;
726   if (sep->is_ip4)
727     return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port,
728                                        sep->transport_proto);
729   else
730     return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port,
731                                        sep->transport_proto);
732   return 0;
733 }
734
735 int
736 session_lookup_add_half_open (transport_connection_t * tc, u64 value)
737 {
738   session_table_t *st;
739   session_kv4_t kv4;
740   session_kv6_t kv6;
741
742   st = session_table_get_or_alloc_for_connection (tc);
743   if (!st)
744     return 0;
745   if (tc->is_ip4)
746     {
747       make_v4_ss_kv_from_tc (&kv4, tc);
748       kv4.value = value;
749       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
750                                        1 /* is_add */ );
751     }
752   else
753     {
754       make_v6_ss_kv_from_tc (&kv6, tc);
755       kv6.value = value;
756       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
757                                        1 /* is_add */ );
758     }
759 }
760
761 int
762 session_lookup_del_half_open (transport_connection_t * tc)
763 {
764   session_table_t *st;
765   session_kv4_t kv4;
766   session_kv6_t kv6;
767
768   st = session_table_get_for_connection (tc);
769   if (!st)
770     return -1;
771   if (tc->is_ip4)
772     {
773       make_v4_ss_kv_from_tc (&kv4, tc);
774       return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4,
775                                        0 /* is_add */ );
776     }
777   else
778     {
779       make_v6_ss_kv_from_tc (&kv6, tc);
780       return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6,
781                                        0 /* is_add */ );
782     }
783 }
784
785 u64
786 session_lookup_half_open_handle (transport_connection_t * tc)
787 {
788   session_table_t *st;
789   session_kv4_t kv4;
790   session_kv6_t kv6;
791   int rv;
792
793   st = session_table_get_for_fib_index (transport_connection_fib_proto (tc),
794                                         tc->fib_index);
795   if (!st)
796     return HALF_OPEN_LOOKUP_INVALID_VALUE;
797   if (tc->is_ip4)
798     {
799       make_v4_ss_kv (&kv4, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port,
800                      tc->rmt_port, tc->proto);
801       rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
802       if (rv == 0)
803         return kv4.value;
804     }
805   else
806     {
807       make_v6_ss_kv (&kv6, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port,
808                      tc->rmt_port, tc->proto);
809       rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
810       if (rv == 0)
811         return kv6.value;
812     }
813   return HALF_OPEN_LOOKUP_INVALID_VALUE;
814 }
815
816 transport_connection_t *
817 session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4)
818 {
819   u32 sst;
820
821   if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE)
822     {
823       sst = session_type_from_proto_and_ip (proto, is_ip4);
824       return tp_vfts[sst].get_half_open (handle & 0xFFFFFFFF);
825     }
826   return 0;
827 }
828
829 /**
830  * Lookup connection with ip4 and transport layer information
831  *
832  * This is used on the fast path so it needs to be fast. Thereby,
833  * duplication of code and 'hacks' allowed.
834  *
835  * The lookup is incremental and returns whenever something is matched. The
836  * steps are:
837  * - Try to find an established session
838  * - Try to find a half-open connection
839  * - Try session rules table
840  * - Try to find a fully-formed or local source wildcarded (listener bound to
841  *   all interfaces) listener session
842  * - return 0
843  *
844  * @param fib_index     index of fib wherein the connection was received
845  * @param lcl           local ip4 address
846  * @param rmt           remote ip4 address
847  * @param lcl_port      local port
848  * @param rmt_port      remote port
849  * @param proto         transport protocol (e.g., tcp, udp)
850  * @param thread_index  thread index for request
851  * @param is_filtered   return flag that indicates if connection was filtered.
852  *
853  * @return pointer to transport connection, if one is found, 0 otherwise
854  */
855 transport_connection_t *
856 session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl,
857                                ip4_address_t * rmt, u16 lcl_port,
858                                u16 rmt_port, u8 proto, u32 thread_index,
859                                u8 * is_filtered)
860 {
861   session_table_t *st;
862   session_kv4_t kv4;
863   stream_session_t *s;
864   u32 action_index;
865   int rv;
866
867   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
868   if (PREDICT_FALSE (!st))
869     return 0;
870
871   /*
872    * Lookup session amongst established ones
873    */
874   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
875   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
876   if (rv == 0)
877     {
878       ASSERT ((u32) (kv4.value >> 32) == thread_index);
879       s = session_get (kv4.value & 0xFFFFFFFFULL, thread_index);
880       return tp_vfts[proto].get_connection (s->connection_index,
881                                             thread_index);
882     }
883
884   /*
885    * Try half-open connections
886    */
887   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
888   if (rv == 0)
889     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
890
891   /*
892    * Check the session rules table
893    */
894   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
895                                               rmt, lcl_port, rmt_port);
896   if (session_lookup_action_index_is_valid (action_index))
897     {
898       if ((*is_filtered = (action_index == SESSION_RULES_TABLE_ACTION_DROP)))
899         return 0;
900       if ((s = session_lookup_action_to_session (action_index,
901                                                  FIB_PROTOCOL_IP4, proto)))
902         return tp_vfts[proto].get_listener (s->connection_index);
903       return 0;
904     }
905
906   /*
907    * If nothing is found, check if any listener is available
908    */
909   s = session_lookup_listener4_i (st, lcl, lcl_port, proto);
910   if (s)
911     return tp_vfts[proto].get_listener (s->connection_index);
912
913   return 0;
914 }
915
916 /**
917  * Lookup connection with ip4 and transport layer information
918  *
919  * Not optimized. This is used on the fast path so it needs to be fast.
920  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
921  * to that of @ref session_lookup_connection_wt4
922  *
923  * @param fib_index     index of the fib wherein the connection was received
924  * @param lcl           local ip4 address
925  * @param rmt           remote ip4 address
926  * @param lcl_port      local port
927  * @param rmt_port      remote port
928  * @param proto         transport protocol (e.g., tcp, udp)
929  *
930  * @return pointer to transport connection, if one is found, 0 otherwise
931  */
932 transport_connection_t *
933 session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl,
934                             ip4_address_t * rmt, u16 lcl_port, u16 rmt_port,
935                             u8 proto)
936 {
937   session_table_t *st;
938   session_kv4_t kv4;
939   stream_session_t *s;
940   u32 action_index;
941   int rv;
942
943   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
944   if (PREDICT_FALSE (!st))
945     return 0;
946
947   /*
948    * Lookup session amongst established ones
949    */
950   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
951   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
952   if (rv == 0)
953     {
954       s = session_get_from_handle (kv4.value);
955       return tp_vfts[proto].get_connection (s->connection_index,
956                                             s->thread_index);
957     }
958
959   /*
960    * Try half-open connections
961    */
962   rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4);
963   if (rv == 0)
964     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
965
966   /*
967    * Check the session rules table
968    */
969   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
970                                               rmt, lcl_port, rmt_port);
971   if (session_lookup_action_index_is_valid (action_index))
972     {
973       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
974         return 0;
975       if ((s = session_lookup_action_to_session (action_index,
976                                                  FIB_PROTOCOL_IP4, proto)))
977         return tp_vfts[proto].get_listener (s->connection_index);
978       return 0;
979     }
980
981   /*
982    * If nothing is found, check if any listener is available
983    */
984   s = session_lookup_listener4_i (st, lcl, lcl_port, proto);
985   if (s)
986     return tp_vfts[proto].get_listener (s->connection_index);
987
988   return 0;
989 }
990
991 /**
992  * Lookup session with ip4 and transport layer information
993  *
994  * Important note: this may look into another thread's pool table and
995  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
996  * if needed as soon as possible.
997  *
998  * Lookup logic is similar to that of @ref session_lookup_connection_wt4 but
999  * this returns a session as opposed to a transport connection and it does not
1000  * try to lookup half-open sessions.
1001  *
1002  * Typically used by dgram connections
1003  */
1004 stream_session_t *
1005 session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt,
1006                       u16 lcl_port, u16 rmt_port, u8 proto)
1007 {
1008   session_table_t *st;
1009   session_kv4_t kv4;
1010   stream_session_t *s;
1011   u32 action_index;
1012   int rv;
1013
1014   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index);
1015   if (PREDICT_FALSE (!st))
1016     return 0;
1017
1018   /*
1019    * Lookup session amongst established ones
1020    */
1021   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
1022   rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4);
1023   if (rv == 0)
1024     return session_get_from_handle_safe (kv4.value);
1025
1026   /*
1027    * Check the session rules table
1028    */
1029   action_index = session_rules_table_lookup4 (&st->session_rules[proto], lcl,
1030                                               rmt, lcl_port, rmt_port);
1031   if (session_lookup_action_index_is_valid (action_index))
1032     {
1033       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1034         return 0;
1035       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP4,
1036                                                proto);
1037     }
1038
1039   /*
1040    *  If nothing is found, check if any listener is available
1041    */
1042   if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto)))
1043     return s;
1044
1045   return 0;
1046 }
1047
1048 /**
1049  * Lookup connection with ip6 and transport layer information
1050  *
1051  * This is used on the fast path so it needs to be fast. Thereby,
1052  * duplication of code and 'hacks' allowed.
1053  *
1054  * The lookup is incremental and returns whenever something is matched. The
1055  * steps are:
1056  * - Try to find an established session
1057  * - Try to find a half-open connection
1058  * - Try session rules table
1059  * - Try to find a fully-formed or local source wildcarded (listener bound to
1060  *   all interfaces) listener session
1061  * - return 0
1062  *
1063  * @param fib_index     index of the fib wherein the connection was received
1064  * @param lcl           local ip6 address
1065  * @param rmt           remote ip6 address
1066  * @param lcl_port      local port
1067  * @param rmt_port      remote port
1068  * @param proto         transport protocol (e.g., tcp, udp)
1069  * @param thread_index  thread index for request
1070  *
1071  * @return pointer to transport connection, if one is found, 0 otherwise
1072  */
1073 transport_connection_t *
1074 session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl,
1075                                ip6_address_t * rmt, u16 lcl_port,
1076                                u16 rmt_port, u8 proto, u32 thread_index,
1077                                u8 * is_filtered)
1078 {
1079   session_table_t *st;
1080   stream_session_t *s;
1081   session_kv6_t kv6;
1082   u32 action_index;
1083   int rv;
1084
1085   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1086   if (PREDICT_FALSE (!st))
1087     return 0;
1088
1089   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1090   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1091   if (rv == 0)
1092     {
1093       ASSERT ((u32) (kv6.value >> 32) == thread_index);
1094       s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index);
1095       return tp_vfts[proto].get_connection (s->connection_index,
1096                                             thread_index);
1097     }
1098
1099   /* Try half-open connections */
1100   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1101   if (rv == 0)
1102     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
1103
1104   /* Check the session rules table */
1105   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1106                                               rmt, lcl_port, rmt_port);
1107   if (session_lookup_action_index_is_valid (action_index))
1108     {
1109       if ((*is_filtered = (action_index == SESSION_RULES_TABLE_ACTION_DROP)))
1110         return 0;
1111       if ((s = session_lookup_action_to_session (action_index,
1112                                                  FIB_PROTOCOL_IP6, proto)))
1113         return tp_vfts[proto].get_listener (s->connection_index);
1114       return 0;
1115     }
1116
1117   /* If nothing is found, check if any listener is available */
1118   s = session_lookup_listener6_i (st, lcl, lcl_port, proto);
1119   if (s)
1120     return tp_vfts[proto].get_listener (s->connection_index);
1121
1122   return 0;
1123 }
1124
1125 /**
1126  * Lookup connection with ip6 and transport layer information
1127  *
1128  * Not optimized. This is used on the fast path so it needs to be fast.
1129  * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical
1130  * to that of @ref session_lookup_connection_wt4
1131  *
1132  * @param fib_index     index of the fib wherein the connection was received
1133  * @param lcl           local ip6 address
1134  * @param rmt           remote ip6 address
1135  * @param lcl_port      local port
1136  * @param rmt_port      remote port
1137  * @param proto         transport protocol (e.g., tcp, udp)
1138  *
1139  * @return pointer to transport connection, if one is found, 0 otherwise
1140  */
1141 transport_connection_t *
1142 session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl,
1143                             ip6_address_t * rmt, u16 lcl_port, u16 rmt_port,
1144                             u8 proto)
1145 {
1146   session_table_t *st;
1147   stream_session_t *s;
1148   session_kv6_t kv6;
1149   u32 action_index;
1150   int rv;
1151
1152   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1153   if (PREDICT_FALSE (!st))
1154     return 0;
1155
1156   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1157   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1158   if (rv == 0)
1159     {
1160       s = session_get_from_handle (kv6.value);
1161       return tp_vfts[proto].get_connection (s->connection_index,
1162                                             s->thread_index);
1163     }
1164
1165   /* Try half-open connections */
1166   rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6);
1167   if (rv == 0)
1168     return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
1169
1170   /* Check the session rules table */
1171   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1172                                               rmt, lcl_port, rmt_port);
1173   if (session_lookup_action_index_is_valid (action_index))
1174     {
1175       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1176         return 0;
1177       if ((s = session_lookup_action_to_session (action_index,
1178                                                  FIB_PROTOCOL_IP6, proto)))
1179         return tp_vfts[proto].get_listener (s->connection_index);
1180       return 0;
1181     }
1182
1183   /* If nothing is found, check if any listener is available */
1184   s = session_lookup_listener6 (fib_index, lcl, lcl_port, proto);
1185   if (s)
1186     return tp_vfts[proto].get_listener (s->connection_index);
1187
1188   return 0;
1189 }
1190
1191 /**
1192  * Lookup session with ip6 and transport layer information
1193  *
1194  * Important note: this may look into another thread's pool table and
1195  * register as 'peeker'. Caller should call @ref session_pool_remove_peeker as
1196  * if needed as soon as possible.
1197  *
1198  * Lookup logic is similar to that of @ref session_lookup_connection_wt6 but
1199  * this returns a session as opposed to a transport connection and it does not
1200  * try to lookup half-open sessions.
1201  *
1202  * Typically used by dgram connections
1203  */
1204 stream_session_t *
1205 session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt,
1206                       u16 lcl_port, u16 rmt_port, u8 proto)
1207 {
1208   session_table_t *st;
1209   session_kv6_t kv6;
1210   stream_session_t *s;
1211   u32 action_index;
1212   int rv;
1213
1214   st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index);
1215   if (PREDICT_FALSE (!st))
1216     return 0;
1217
1218   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
1219   rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6);
1220   if (rv == 0)
1221     return session_get_from_handle_safe (kv6.value);
1222
1223   /* Check the session rules table */
1224   action_index = session_rules_table_lookup6 (&st->session_rules[proto], lcl,
1225                                               rmt, lcl_port, rmt_port);
1226   if (session_lookup_action_index_is_valid (action_index))
1227     {
1228       if (action_index == SESSION_RULES_TABLE_ACTION_DROP)
1229         return 0;
1230       return session_lookup_action_to_session (action_index, FIB_PROTOCOL_IP6,
1231                                                proto);
1232     }
1233
1234   /* If nothing is found, check if any listener is available */
1235   if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto)))
1236     return s;
1237   return 0;
1238 }
1239
1240 clib_error_t *
1241 vnet_session_rule_add_del (session_rule_add_del_args_t * args)
1242 {
1243   app_namespace_t *app_ns = app_namespace_get (args->appns_index);
1244   session_rules_table_t *srt;
1245   session_table_t *st;
1246   u32 fib_index;
1247   u8 fib_proto;
1248   clib_error_t *error;
1249
1250   if (!app_ns)
1251     return clib_error_return_code (0, VNET_API_ERROR_APP_INVALID_NS, 0,
1252                                    "invalid app ns");
1253   if (args->scope > 3)
1254     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
1255                                    "invalid scope");
1256   if (args->transport_proto != TRANSPORT_PROTO_TCP
1257       && args->transport_proto != TRANSPORT_PROTO_UDP)
1258     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
1259                                    "invalid transport proto");
1260   if ((args->scope & SESSION_RULE_SCOPE_GLOBAL) || args->scope == 0)
1261     {
1262       fib_proto = args->table_args.rmt.fp_proto;
1263       fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1264       st = session_table_get_for_fib_index (fib_proto, fib_index);
1265       srt = &st->session_rules[args->transport_proto];
1266       if ((error = session_rules_table_add_del (srt, &args->table_args)))
1267         {
1268           clib_error_report (error);
1269           return error;
1270         }
1271     }
1272   if (args->scope & SESSION_RULE_SCOPE_LOCAL)
1273     {
1274       memset (&args->table_args.lcl, 0, sizeof (args->table_args.lcl));
1275       args->table_args.lcl.fp_proto = args->table_args.rmt.fp_proto;
1276       args->table_args.lcl_port = 0;
1277       st = app_namespace_get_local_table (app_ns);
1278       srt = &st->session_rules[args->transport_proto];
1279       error = session_rules_table_add_del (srt, &args->table_args);
1280     }
1281   return error;
1282 }
1283
1284 /**
1285  * Mark (global) tables as pertaining to app ns
1286  */
1287 void
1288 session_lookup_set_tables_appns (app_namespace_t * app_ns)
1289 {
1290   session_table_t *st;
1291   u32 fib_index;
1292   u8 fp;
1293
1294   for (fp = 0; fp < ARRAY_LEN (fib_index_to_table_index); fp++)
1295     {
1296       fib_index = app_namespace_get_fib_index (app_ns, fp);
1297       st = session_table_get_for_fib_index (fp, fib_index);
1298       if (st)
1299         st->appns_index = app_namespace_index (app_ns);
1300     }
1301 }
1302
1303 u8 *
1304 format_ip4_session_lookup_kvp (u8 * s, va_list * args)
1305 {
1306   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
1307   u32 is_local = va_arg (*args, u32);
1308   u8 *app_name, *str = 0;
1309   stream_session_t *session;
1310   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
1311
1312   if (!is_local)
1313     {
1314       session = session_get_from_handle (kvp->value);
1315       app_name = application_name_from_index (session->app_index);
1316       str = format (0, "[%U] %U:%d->%U:%d", format_transport_proto_short,
1317                     key->proto, format_ip4_address, &key->src,
1318                     clib_net_to_host_u16 (key->src_port), format_ip4_address,
1319                     &key->dst, clib_net_to_host_u16 (key->dst_port));
1320       s = format (s, "%-40v%-30v", str, app_name);
1321     }
1322   else
1323     {
1324       app_name = application_name_from_index (kvp->value);
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  */