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