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