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