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