session: rules tables
[vpp.git] / src / vnet / session / session_test.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/session/application_namespace.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/application.h>
19 #include <vnet/session/session.h>
20 #include <vnet/session/session_rules_table.h>
21
22 #define SESSION_TEST_I(_cond, _comment, _args...)               \
23 ({                                                              \
24   int _evald = (_cond);                                         \
25   if (!(_evald)) {                                              \
26     fformat(stderr, "FAIL:%d: " _comment "\n",                  \
27             __LINE__, ##_args);                                 \
28   } else {                                                      \
29     fformat(stderr, "PASS:%d: " _comment "\n",                  \
30             __LINE__, ##_args);                                 \
31   }                                                             \
32   _evald;                                                       \
33 })
34
35 #define SESSION_TEST(_cond, _comment, _args...)                 \
36 {                                                               \
37     if (!SESSION_TEST_I(_cond, _comment, ##_args)) {            \
38         return 1;                                               \
39     }                                                           \
40 }
41
42 void
43 dummy_session_reset_callback (stream_session_t * s)
44 {
45   clib_warning ("called...");
46 }
47
48 int
49 dummy_session_connected_callback (u32 app_index, u32 api_context,
50                                   stream_session_t * s, u8 is_fail)
51 {
52   clib_warning ("called...");
53   return -1;
54 }
55
56 int
57 dummy_add_segment_callback (u32 client_index, const u8 * seg_name,
58                             u32 seg_size)
59 {
60   clib_warning ("called...");
61   return -1;
62 }
63
64 int
65 dummy_redirect_connect_callback (u32 client_index, void *mp)
66 {
67   return VNET_API_ERROR_SESSION_REDIRECT;
68 }
69
70 void
71 dummy_session_disconnect_callback (stream_session_t * s)
72 {
73   clib_warning ("called...");
74 }
75
76 int
77 dummy_session_accept_callback (stream_session_t * s)
78 {
79   clib_warning ("called...");
80   return -1;
81 }
82
83 int
84 dummy_server_rx_callback (stream_session_t * s)
85 {
86   clib_warning ("called...");
87   return -1;
88 }
89
90 /* *INDENT-OFF* */
91 static session_cb_vft_t dummy_session_cbs = {
92   .session_reset_callback = dummy_session_reset_callback,
93   .session_connected_callback = dummy_session_connected_callback,
94   .session_accept_callback = dummy_session_accept_callback,
95   .session_disconnect_callback = dummy_session_disconnect_callback,
96   .builtin_server_rx_callback = dummy_server_rx_callback,
97   .redirect_connect_callback = dummy_redirect_connect_callback,
98 };
99 /* *INDENT-ON* */
100
101 static int
102 session_test_namespace (vlib_main_t * vm, unformat_input_t * input)
103 {
104   u64 options[SESSION_OPTIONS_N_OPTIONS], dummy_secret = 1234;
105   u32 server_index, server_st_index, server_local_st_index;
106   u32 dummy_port = 1234, local_listener, client_index;
107   u32 dummy_api_context = 4321, dummy_client_api_index = 1234;
108   u32 dummy_server_api_index = ~0, sw_if_index = 0;
109   session_endpoint_t server_sep = SESSION_ENDPOINT_NULL;
110   session_endpoint_t client_sep = SESSION_ENDPOINT_NULL;
111   session_endpoint_t intf_sep = SESSION_ENDPOINT_NULL;
112   clib_error_t *error = 0;
113   u8 *ns_id = format (0, "appns1"), intf_mac[6];
114   app_namespace_t *app_ns;
115   u8 segment_name[128];
116   application_t *server;
117   stream_session_t *s;
118   int code;
119
120   server_sep.is_ip4 = 1;
121   server_sep.port = dummy_port;
122   client_sep.is_ip4 = 1;
123   client_sep.port = dummy_port;
124   memset (options, 0, sizeof (options));
125   memset (intf_mac, 0, sizeof (intf_mac));
126
127   options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_BUILTIN_APP;
128   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ACCEPT_REDIRECT;
129   vnet_app_attach_args_t attach_args = {
130     .api_client_index = ~0,
131     .options = options,
132     .namespace_id = 0,
133     .session_cb_vft = &dummy_session_cbs,
134     .segment_name = segment_name,
135   };
136
137   vnet_bind_args_t bind_args = {
138     .sep = server_sep,
139     .app_index = 0,
140   };
141
142   vnet_connect_args_t connect_args = {
143     .sep = client_sep,
144     .app_index = 0,
145     .api_context = 0,
146   };
147
148   vnet_unbind_args_t unbind_args = {
149     .handle = bind_args.handle,
150     .app_index = 0,
151   };
152
153   vnet_app_detach_args_t detach_args = {
154     .app_index = 0,
155   };
156
157   ip4_address_t intf_addr = {
158     .as_u32 = clib_host_to_net_u32 (0x06000105),
159   };
160
161   intf_sep.ip.ip4 = intf_addr;
162   intf_sep.is_ip4 = 1;
163   intf_sep.port = dummy_port;
164
165   /*
166    * Insert namespace and lookup
167    */
168
169   vnet_app_namespace_add_del_args_t ns_args = {
170     .ns_id = ns_id,
171     .secret = dummy_secret,
172     .sw_if_index = APP_NAMESPACE_INVALID_INDEX,
173     .is_add = 1
174   };
175   error = vnet_app_namespace_add_del (&ns_args);
176   SESSION_TEST ((error == 0), "app ns insertion should succeed: %d",
177                 clib_error_get_code (error));
178
179   app_ns = app_namespace_get_from_id (ns_id);
180   SESSION_TEST ((app_ns != 0), "should find ns %v status", ns_id);
181   SESSION_TEST ((app_ns->ns_secret == dummy_secret), "secret should be %d",
182                 dummy_secret);
183   SESSION_TEST ((app_ns->sw_if_index == APP_NAMESPACE_INVALID_INDEX),
184                 "sw_if_index should be invalid");
185
186   /*
187    * Try application attach with wrong secret
188    */
189
190   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
191   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
192   options[APP_OPTIONS_NAMESPACE_SECRET] = dummy_secret - 1;
193   attach_args.namespace_id = ns_id;
194   attach_args.api_client_index = dummy_server_api_index;
195
196   error = vnet_application_attach (&attach_args);
197   SESSION_TEST ((error != 0), "app attachment should fail");
198   code = clib_error_get_code (error);
199   SESSION_TEST ((code == VNET_API_ERROR_APP_WRONG_NS_SECRET),
200                 "code should be wrong ns secret: %d", code);
201
202   /*
203    * Attach server with global default scope
204    */
205   options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
206   options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
207   options[APP_OPTIONS_NAMESPACE_SECRET] = 0;
208   attach_args.namespace_id = 0;
209   attach_args.api_client_index = dummy_server_api_index;
210   error = vnet_application_attach (&attach_args);
211   SESSION_TEST ((error == 0), "server attachment should work");
212   server_index = attach_args.app_index;
213   server = application_get (server_index);
214   SESSION_TEST ((server->ns_index == 0),
215                 "server should be in the default ns");
216
217   bind_args.app_index = server_index;
218   error = vnet_bind (&bind_args);
219   SESSION_TEST ((error == 0), "server bind should work");
220
221   server_st_index = application_session_table (server, FIB_PROTOCOL_IP4);
222   s = session_lookup_listener (server_st_index, &server_sep);
223   SESSION_TEST ((s != 0), "listener should exist in global table");
224   SESSION_TEST ((s->app_index == server_index), "app_index should be that of "
225                 "the server");
226   server_local_st_index = application_local_session_table (server);
227   SESSION_TEST ((server_local_st_index == APP_INVALID_INDEX),
228                 "server shouldn't have access to local table");
229
230   unbind_args.app_index = server_index;
231   unbind_args.handle = bind_args.handle;
232   error = vnet_unbind (&unbind_args);
233   SESSION_TEST ((error == 0), "unbind should work");
234
235   s = session_lookup_listener (server_st_index, &server_sep);
236   SESSION_TEST ((s == 0), "listener should not exist in global table");
237
238   detach_args.app_index = server_index;
239   vnet_application_detach (&detach_args);
240
241   /*
242    * Attach server with local and global scope
243    */
244   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
245   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
246   options[APP_OPTIONS_NAMESPACE_SECRET] = dummy_secret;
247   attach_args.namespace_id = ns_id;
248   attach_args.api_client_index = dummy_server_api_index;
249   error = vnet_application_attach (&attach_args);
250   SESSION_TEST ((error == 0), "server attachment should work");
251   server_index = attach_args.app_index;
252   server = application_get (server_index);
253   SESSION_TEST ((server->ns_index == app_namespace_index (app_ns)),
254                 "server should be in the right ns");
255
256   bind_args.app_index = server_index;
257   error = vnet_bind (&bind_args);
258   SESSION_TEST ((error == 0), "bind should work");
259   server_st_index = application_session_table (server, FIB_PROTOCOL_IP4);
260   s = session_lookup_listener (server_st_index, &server_sep);
261   SESSION_TEST ((s != 0), "listener should exist in global table");
262   SESSION_TEST ((s->app_index == server_index), "app_index should be that of "
263                 "the server");
264   server_local_st_index = application_local_session_table (server);
265   local_listener =
266     session_lookup_local_session_endpoint (server_local_st_index,
267                                            &server_sep);
268   SESSION_TEST ((local_listener != SESSION_INVALID_INDEX),
269                 "listener should exist in local table");
270
271   /*
272    * Try client connect with 1) local scope 2) global scope
273    */
274   options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
275   attach_args.api_client_index = dummy_client_api_index;
276   error = vnet_application_attach (&attach_args);
277   SESSION_TEST ((error == 0), "client attachment should work");
278   client_index = attach_args.app_index;
279   connect_args.api_context = dummy_api_context;
280   connect_args.app_index = client_index;
281   error = vnet_connect (&connect_args);
282   SESSION_TEST ((error != 0), "client connect should return error code");
283   code = clib_error_get_code (error);
284   SESSION_TEST ((code == VNET_API_ERROR_INVALID_VALUE),
285                 "error code should be invalid value (zero ip)");
286   connect_args.sep.ip.ip4.as_u8[0] = 127;
287   error = vnet_connect (&connect_args);
288   SESSION_TEST ((error != 0), "client connect should return error code");
289   code = clib_error_get_code (error);
290   SESSION_TEST ((code == VNET_API_ERROR_SESSION_REDIRECT),
291                 "error code should be redirect");
292   detach_args.app_index = client_index;
293   vnet_application_detach (&detach_args);
294
295   options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
296   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
297   attach_args.api_client_index = dummy_client_api_index;
298   error = vnet_application_attach (&attach_args);
299   SESSION_TEST ((error == 0), "client attachment should work");
300   error = vnet_connect (&connect_args);
301   SESSION_TEST ((error != 0), "client connect should return error code");
302   code = clib_error_get_code (error);
303   SESSION_TEST ((code == VNET_API_ERROR_SESSION_CONNECT),
304                 "error code should be connect (nothing in local scope)");
305   detach_args.app_index = client_index;
306   vnet_application_detach (&detach_args);
307
308   /*
309    * Unbind and detach server and then re-attach with local scope only
310    */
311   unbind_args.handle = bind_args.handle;
312   unbind_args.app_index = server_index;
313   error = vnet_unbind (&unbind_args);
314   SESSION_TEST ((error == 0), "unbind should work");
315
316   s = session_lookup_listener (server_st_index, &server_sep);
317   SESSION_TEST ((s == 0), "listener should not exist in global table");
318   local_listener =
319     session_lookup_local_session_endpoint (server_local_st_index,
320                                            &server_sep);
321   SESSION_TEST ((s == 0), "listener should not exist in local table");
322
323   detach_args.app_index = server_index;
324   vnet_application_detach (&detach_args);
325
326   options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
327   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
328   attach_args.api_client_index = dummy_server_api_index;
329   error = vnet_application_attach (&attach_args);
330   SESSION_TEST ((error == 0), "app attachment should work");
331   server_index = attach_args.app_index;
332   server = application_get (server_index);
333   SESSION_TEST ((server->ns_index == app_namespace_index (app_ns)),
334                 "app should be in the right ns");
335
336   bind_args.app_index = server_index;
337   error = vnet_bind (&bind_args);
338   SESSION_TEST ((error == 0), "bind should work");
339
340   server_st_index = application_session_table (server, FIB_PROTOCOL_IP4);
341   s = session_lookup_listener (server_st_index, &server_sep);
342   SESSION_TEST ((s == 0), "listener should not exist in global table");
343   server_local_st_index = application_local_session_table (server);
344   local_listener =
345     session_lookup_local_session_endpoint (server_local_st_index,
346                                            &server_sep);
347   SESSION_TEST ((local_listener != SESSION_INVALID_INDEX),
348                 "listener should exist in local table");
349
350   unbind_args.handle = bind_args.handle;
351   error = vnet_unbind (&unbind_args);
352   SESSION_TEST ((error == 0), "unbind should work");
353
354   local_listener =
355     session_lookup_local_session_endpoint (server_local_st_index,
356                                            &server_sep);
357   SESSION_TEST ((local_listener == SESSION_INVALID_INDEX),
358                 "listener should not exist in local table");
359
360   /*
361    * Client attach + connect in default ns with local scope
362    */
363   options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
364   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
365   attach_args.namespace_id = 0;
366   attach_args.api_client_index = dummy_client_api_index;
367   vnet_application_attach (&attach_args);
368   error = vnet_connect (&connect_args);
369   SESSION_TEST ((error != 0), "client connect should return error code");
370   code = clib_error_get_code (error);
371   SESSION_TEST ((code == VNET_API_ERROR_SESSION_CONNECT),
372                 "error code should be connect (not in same ns)");
373   detach_args.app_index = client_index;
374   vnet_application_detach (&detach_args);
375
376   /*
377    * Detach server
378    */
379   detach_args.app_index = server_index;
380   vnet_application_detach (&detach_args);
381
382   /*
383    * Create loopback interface
384    */
385   if (vnet_create_loopback_interface (&sw_if_index, intf_mac, 0, 0))
386     {
387       clib_warning ("couldn't create loopback. stopping the test!");
388       return 0;
389     }
390   vnet_sw_interface_set_flags (vnet_get_main (), sw_if_index,
391                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);
392   ip4_add_del_interface_address (vlib_get_main (), sw_if_index, &intf_addr,
393                                  24, 0);
394
395   /*
396    * Update namespace
397    */
398   ns_args.sw_if_index = sw_if_index;
399   error = vnet_app_namespace_add_del (&ns_args);
400   SESSION_TEST ((error == 0), "app ns insertion should succeed: %d",
401                 clib_error_get_code (error));
402
403   /*
404    * Attach server with local and global scope
405    */
406   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
407   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
408   options[APP_OPTIONS_NAMESPACE_SECRET] = dummy_secret;
409   attach_args.namespace_id = ns_id;
410   attach_args.api_client_index = dummy_server_api_index;
411   error = vnet_application_attach (&attach_args);
412   SESSION_TEST ((error == 0), "server attachment should work");
413   server_index = attach_args.app_index;
414
415   bind_args.app_index = server_index;
416   error = vnet_bind (&bind_args);
417   server_st_index = application_session_table (server, FIB_PROTOCOL_IP4);
418   s = session_lookup_listener (server_st_index, &server_sep);
419   SESSION_TEST ((s == 0), "zero listener should not exist in global table");
420
421   s = session_lookup_listener (server_st_index, &intf_sep);
422   SESSION_TEST ((s != 0), "intf listener should exist in global table");
423   SESSION_TEST ((s->app_index == server_index), "app_index should be that of "
424                 "the server");
425   server_local_st_index = application_local_session_table (server);
426   local_listener =
427     session_lookup_local_session_endpoint (server_local_st_index,
428                                            &server_sep);
429   SESSION_TEST ((local_listener != SESSION_INVALID_INDEX),
430                 "zero listener should exist in local table");
431   detach_args.app_index = server_index;
432   vnet_application_detach (&detach_args);
433
434   /*
435    * Cleanup
436    */
437   vec_free (ns_id);
438   vnet_delete_loopback_interface (sw_if_index);
439   return 0;
440 }
441
442 static int
443 session_test_rule_table (vlib_main_t * vm, unformat_input_t * input)
444 {
445   session_rules_table_t _srt, *srt = &_srt;
446   u16 lcl_port = 1234, rmt_port = 4321;
447   u32 action_index = 1, res;
448   ip4_address_t lcl_lkup, rmt_lkup;
449   clib_error_t *error;
450   int verbose = 0;
451
452   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
453     {
454       if (unformat (input, "verbose"))
455         verbose = 1;
456       else
457         {
458           vlib_cli_output (vm, "parse error: '%U'", format_unformat_error,
459                            input);
460           return -1;
461         }
462     }
463
464   memset (srt, 0, sizeof (*srt));
465   session_rules_table_init (srt);
466
467   ip4_address_t lcl_ip = {
468     .as_u32 = clib_host_to_net_u32 (0x01020304),
469   };
470   ip4_address_t rmt_ip = {
471     .as_u32 = clib_host_to_net_u32 (0x05060708),
472   };
473   ip4_address_t lcl_ip2 = {
474     .as_u32 = clib_host_to_net_u32 (0x02020202),
475   };
476   ip4_address_t rmt_ip2 = {
477     .as_u32 = clib_host_to_net_u32 (0x06060606),
478   };
479   ip4_address_t lcl_ip3 = {
480     .as_u32 = clib_host_to_net_u32 (0x03030303),
481   };
482   ip4_address_t rmt_ip3 = {
483     .as_u32 = clib_host_to_net_u32 (0x07070707),
484   };
485   fib_prefix_t lcl_pref = {
486     .fp_addr.ip4.as_u32 = lcl_ip.as_u32,
487     .fp_len = 16,
488     .fp_proto = FIB_PROTOCOL_IP4,
489   };
490   fib_prefix_t rmt_pref = {
491     .fp_addr.ip4.as_u32 = rmt_ip.as_u32,
492     .fp_len = 16,
493     .fp_proto = FIB_PROTOCOL_IP4,
494   };
495
496   session_rule_table_add_del_args_t args = {
497     .lcl = lcl_pref,
498     .rmt = rmt_pref,
499     .lcl_port = lcl_port,
500     .rmt_port = rmt_port,
501     .action_index = action_index++,
502     .is_add = 1,
503   };
504   error = session_rules_table_add_del (srt, &args);
505   SESSION_TEST ((error == 0), "Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action %d",
506                 action_index - 1);
507
508   res =
509     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
510                                  lcl_port, rmt_port);
511   SESSION_TEST ((res == 1),
512                 "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 1: %d",
513                 res);
514
515   /*
516    * Add 1.2.3.4/24 1234 5.6.7.8/16 4321 and 1.2.3.4/24 1234 5.6.7.8/24 4321
517    */
518   args.lcl.fp_addr.ip4 = lcl_ip;
519   args.lcl.fp_len = 24;
520   args.action_index = action_index++;
521   error = session_rules_table_add_del (srt, &args);
522   SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/16 4321 action %d",
523                 action_index - 1);
524   args.rmt.fp_addr.ip4 = rmt_ip;
525   args.rmt.fp_len = 24;
526   args.action_index = action_index++;
527   error = session_rules_table_add_del (srt, &args);
528   SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/24 4321 action %d",
529                 action_index - 1);
530
531   /*
532    * Add 2.2.2.2/24 1234 6.6.6.6/16 4321 and 3.3.3.3/24 1234 7.7.7.7/16 4321
533    */
534   args.lcl.fp_addr.ip4 = lcl_ip2;
535   args.lcl.fp_len = 24;
536   args.rmt.fp_addr.ip4 = rmt_ip2;
537   args.rmt.fp_len = 16;
538   args.action_index = action_index++;
539   error = session_rules_table_add_del (srt, &args);
540   SESSION_TEST ((error == 0), "Add 2.2.2.2/24 1234 6.6.6.6/16 4321 action %d",
541                 action_index - 1);
542   args.lcl.fp_addr.ip4 = lcl_ip3;
543   args.rmt.fp_addr.ip4 = rmt_ip3;
544   args.action_index = action_index++;
545   error = session_rules_table_add_del (srt, &args);
546   SESSION_TEST ((error == 0), "Add 3.3.3.3/24 1234 7.7.7.7/16 4321 action %d",
547                 action_index - 1);
548
549   /*
550    * Add again 3.3.3.3/24 1234 7.7.7.7/16 4321
551    */
552   args.lcl.fp_addr.ip4 = lcl_ip3;
553   args.rmt.fp_addr.ip4 = rmt_ip3;
554   args.action_index = action_index++;
555   error = session_rules_table_add_del (srt, &args);
556   SESSION_TEST ((error == 0), "overwrite 3.3.3.3/24 1234 7.7.7.7/16 4321 "
557                 "action %d", action_index - 1);
558
559   /*
560    * Lookup 1.2.3.4/32 1234 5.6.7.8/32 4321, 1.2.2.4/32 1234 5.6.7.9/32 4321
561    * and  3.3.3.3 1234 7.7.7.7 4321
562    */
563   res =
564     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
565                                  lcl_port, rmt_port);
566   SESSION_TEST ((res == 3),
567                 "Lookup 1.2.3.4 1234 5.6.7.8 4321 action " "should be 3: %d",
568                 res);
569
570   lcl_lkup.as_u32 = clib_host_to_net_u32 (0x01020204);
571   rmt_lkup.as_u32 = clib_host_to_net_u32 (0x05060709);
572   res =
573     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_lkup,
574                                  &rmt_lkup, lcl_port, rmt_port);
575   SESSION_TEST ((res == 1),
576                 "Lookup 1.2.2.4 1234 5.6.7.9 4321, action " "should be 1: %d",
577                 res);
578
579   res =
580     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip3, &rmt_ip3,
581                                  lcl_port, rmt_port);
582   SESSION_TEST ((res == 6),
583                 "Lookup 3.3.3.3 1234 7.7.7.7 4321, action "
584                 "should be 6 (updated): %d", res);
585
586   /*
587    * Add 1.2.3.4/24 * 5.6.7.8/24 *
588    * Lookup 1.2.3.4 1234 5.6.7.8 4321 and 1.2.3.4 1235 5.6.7.8 4321
589    */
590   args.lcl.fp_addr.ip4 = lcl_ip;
591   args.rmt.fp_addr.ip4 = rmt_ip;
592   args.lcl.fp_len = 24;
593   args.rmt.fp_len = 24;
594   args.lcl_port = 0;
595   args.rmt_port = 0;
596   args.action_index = action_index++;
597   error = session_rules_table_add_del (srt, &args);
598   SESSION_TEST ((error == 0), "Add 1.2.3.4/24 * 5.6.7.8/24 * action %d",
599                 action_index - 1);
600   res =
601     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
602                                  lcl_port, rmt_port);
603   SESSION_TEST ((res == 7),
604                 "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should"
605                 " be 7 (lpm dst): %d", res);
606   res =
607     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
608                                  lcl_port + 1, rmt_port);
609   SESSION_TEST ((res == 7),
610                 "Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 7: %d",
611                 res);
612
613   /*
614    * Del 1.2.3.4/24 * 5.6.7.8/24 *
615    * Add 1.2.3.4/16 * 5.6.7.8/16 * and 1.2.3.4/24 1235 5.6.7.8/24 4321
616    * Lookup 1.2.3.4 1234 5.6.7.8 4321, 1.2.3.4 1235 5.6.7.8 4321 and
617    * 1.2.3.4 1235 5.6.7.8 4322
618    */
619   args.is_add = 0;
620   error = session_rules_table_add_del (srt, &args);
621   SESSION_TEST ((error == 0), "Del 1.2.3.4/24 * 5.6.7.8/24 *");
622
623   args.lcl.fp_addr.ip4 = lcl_ip;
624   args.rmt.fp_addr.ip4 = rmt_ip;
625   args.lcl.fp_len = 16;
626   args.rmt.fp_len = 16;
627   args.lcl_port = 0;
628   args.rmt_port = 0;
629   args.action_index = action_index++;
630   args.is_add = 1;
631   error = session_rules_table_add_del (srt, &args);
632   SESSION_TEST ((error == 0), "Add 1.2.3.4/16 * 5.6.7.8/16 * action %d",
633                 action_index - 1);
634
635   args.lcl.fp_addr.ip4 = lcl_ip;
636   args.rmt.fp_addr.ip4 = rmt_ip;
637   args.lcl.fp_len = 24;
638   args.rmt.fp_len = 24;
639   args.lcl_port = lcl_port + 1;
640   args.rmt_port = rmt_port;
641   args.action_index = action_index++;
642   args.is_add = 1;
643   error = session_rules_table_add_del (srt, &args);
644   SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1235 5.6.7.8/24 4321 action %d",
645                 action_index - 1);
646
647   if (verbose)
648     session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4,
649                                   TRANSPORT_PROTO_TCP);
650
651   res =
652     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
653                                  lcl_port, rmt_port);
654   SESSION_TEST ((res == 3),
655                 "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d",
656                 res);
657   res =
658     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
659                                  lcl_port + 1, rmt_port);
660   SESSION_TEST ((res == 9),
661                 "Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 9: %d",
662                 res);
663   res =
664     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
665                                  lcl_port + 1, rmt_port + 1);
666   SESSION_TEST ((res == 8),
667                 "Lookup 1.2.3.4 1235 5.6.7.8 4322, action should " "be 8: %d",
668                 res);
669
670   /*
671    * Delete 1.2.0.0/16 1234 5.6.0.0/16 4321 and 1.2.0.0/16 * 5.6.0.0/16 *
672    * Lookup 1.2.3.4 1234 5.6.7.8 4321
673    */
674   args.lcl_port = 1234;
675   args.rmt_port = 4321;
676   args.lcl.fp_len = 16;
677   args.rmt.fp_len = 16;
678   args.is_add = 0;
679   error = session_rules_table_add_del (srt, &args);
680   SESSION_TEST ((error == 0), "Del 1.2.0.0/16 1234 5.6.0.0/16 4321");
681   res =
682     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
683                                  lcl_port, rmt_port);
684   SESSION_TEST ((res == 3),
685                 "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d",
686                 res);
687
688   args.lcl_port = 0;
689   args.rmt_port = 0;
690   args.is_add = 0;
691   error = session_rules_table_add_del (srt, &args);
692   SESSION_TEST ((error == 0), "Del 1.2.0.0/16 * 5.6.0.0/16 *");
693   res =
694     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
695                                  lcl_port, rmt_port);
696   SESSION_TEST ((res == 3),
697                 "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d",
698                 res);
699
700   /*
701    * Delete 1.2.3.4/24 1234 5.6.7.5/24
702    */
703   args.lcl.fp_addr.ip4 = lcl_ip;
704   args.rmt.fp_addr.ip4 = rmt_ip;
705   args.lcl.fp_len = 24;
706   args.rmt.fp_len = 24;
707   args.lcl_port = 1234;
708   args.rmt_port = 4321;
709   args.is_add = 0;
710   error = session_rules_table_add_del (srt, &args);
711   SESSION_TEST ((error == 0), "Del 1.2.3.4/24 1234 5.6.7.5/24");
712   res =
713     session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip,
714                                  lcl_port, rmt_port);
715   SESSION_TEST ((res == 2), "Action should be 2: %d", res);
716
717   return 0;
718 }
719
720 static int
721 session_test_rules (vlib_main_t * vm, unformat_input_t * input)
722 {
723   session_endpoint_t server_sep = SESSION_ENDPOINT_NULL;
724   u64 options[SESSION_OPTIONS_N_OPTIONS];
725   u16 lcl_port = 1234, rmt_port = 4321;
726   u32 server_index, app_index;
727   u32 dummy_server_api_index = ~0;
728   transport_connection_t *tc;
729   u32 dummy_port = 1111;
730   clib_error_t *error = 0;
731   u8 segment_name[128];
732   stream_session_t *listener, *s;
733   app_namespace_t *default_ns = app_namespace_get_default ();
734   u32 local_ns_index = default_ns->local_table_index;
735
736   server_sep.is_ip4 = 1;
737   server_sep.port = dummy_port;
738   memset (options, 0, sizeof (options));
739
740   vnet_app_attach_args_t attach_args = {
741     .api_client_index = ~0,
742     .options = options,
743     .namespace_id = 0,
744     .session_cb_vft = &dummy_session_cbs,
745     .segment_name = segment_name,
746   };
747
748   vnet_bind_args_t bind_args = {
749     .sep = server_sep,
750     .app_index = 0,
751   };
752
753   /*
754    * Attach server with global and local default scope
755    */
756   options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_BUILTIN_APP;
757   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ACCEPT_REDIRECT;
758   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
759   options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
760   attach_args.namespace_id = 0;
761   attach_args.api_client_index = dummy_server_api_index;
762   error = vnet_application_attach (&attach_args);
763   SESSION_TEST ((error == 0), "server attached");
764   server_index = attach_args.app_index;
765
766   bind_args.app_index = server_index;
767   error = vnet_bind (&bind_args);
768   SESSION_TEST ((error == 0), "server bound to %U/%d", format_ip46_address,
769                 &server_sep.ip, 1, server_sep.port);
770   listener = listen_session_get_from_handle (bind_args.handle);
771   ip4_address_t lcl_ip = {
772     .as_u32 = clib_host_to_net_u32 (0x01020304),
773   };
774   ip4_address_t rmt_ip = {
775     .as_u32 = clib_host_to_net_u32 (0x05060708),
776   };
777   fib_prefix_t lcl_pref = {
778     .fp_addr.ip4.as_u32 = lcl_ip.as_u32,
779     .fp_len = 16,
780     .fp_proto = FIB_PROTOCOL_IP4,
781   };
782   fib_prefix_t rmt_pref = {
783     .fp_addr.ip4.as_u32 = rmt_ip.as_u32,
784     .fp_len = 16,
785     .fp_proto = FIB_PROTOCOL_IP4,
786   };
787
788   tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4,
789                                       &rmt_pref.fp_addr.ip4, lcl_port,
790                                       rmt_port, TRANSPORT_PROTO_TCP, 0);
791   SESSION_TEST ((tc == 0), "optimized lookup should not work (port)");
792
793   /*
794    * Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action server_index
795    */
796   session_rule_add_del_args_t args = {
797     .table_args.lcl = lcl_pref,
798     .table_args.rmt = rmt_pref,
799     .table_args.lcl_port = lcl_port,
800     .table_args.rmt_port = rmt_port,
801     .table_args.action_index = server_index,
802     .table_args.is_add = 1,
803     .appns_index = 0,
804   };
805   error = vnet_session_rule_add_del (&args);
806   SESSION_TEST ((error == 0), "Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action %d",
807                 args.table_args.action_index);
808
809   tc = session_lookup_connection4 (0, &lcl_pref.fp_addr.ip4,
810                                    &rmt_pref.fp_addr.ip4, lcl_port, rmt_port,
811                                    TRANSPORT_PROTO_TCP);
812   SESSION_TEST ((tc->c_index == listener->connection_index),
813                 "optimized lookup should return the listener");
814   tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4,
815                                       &rmt_pref.fp_addr.ip4, lcl_port,
816                                       rmt_port, TRANSPORT_PROTO_TCP, 0);
817   SESSION_TEST ((tc->c_index == listener->connection_index),
818                 "lookup should return the listener");
819   s = session_lookup_safe4 (0, &lcl_pref.fp_addr.ip4, &rmt_pref.fp_addr.ip4,
820                             lcl_port, rmt_port, TRANSPORT_PROTO_TCP);
821   SESSION_TEST ((s->connection_index == listener->connection_index),
822                 "safe lookup should return the listener");
823   session_endpoint_t sep = {
824     .ip = rmt_pref.fp_addr,
825     .is_ip4 = 1,
826     .port = rmt_port,
827     .transport_proto = TRANSPORT_PROTO_TCP,
828   };
829   app_index = session_lookup_local_session_endpoint (local_ns_index, &sep);
830   SESSION_TEST ((app_index != server_index), "local session endpoint lookup "
831                 "should not work (global scope)");
832
833   tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4,
834                                       &rmt_pref.fp_addr.ip4, lcl_port + 1,
835                                       rmt_port, TRANSPORT_PROTO_TCP, 0);
836   SESSION_TEST ((tc == 0),
837                 "optimized lookup for wrong lcl port + 1 should not" " work");
838
839   /*
840    * Add 1.2.3.4/16 * 5.6.7.8/16 4321
841    */
842   args.table_args.lcl_port = 0;
843   args.scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL;
844   error = vnet_session_rule_add_del (&args);
845   SESSION_TEST ((error == 0), "Add 1.2.3.4/16 * 5.6.7.8/16 4321 action %d",
846                 args.table_args.action_index);
847   tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4,
848                                       &rmt_pref.fp_addr.ip4, lcl_port + 1,
849                                       rmt_port, TRANSPORT_PROTO_TCP, 0);
850   SESSION_TEST ((tc->c_index == listener->connection_index),
851                 "optimized lookup for lcl port + 1 should work");
852   app_index = session_lookup_local_session_endpoint (local_ns_index, &sep);
853   SESSION_TEST ((app_index != server_index), "local session endpoint lookup "
854                 "should not work (constrained lcl ip)");
855
856   /*
857    * Add local scope rule for 0/0 * 5.6.7.8/16 4321 action server_index
858    */
859   args.table_args.lcl.fp_len = 0;
860   error = vnet_session_rule_add_del (&args);
861   SESSION_TEST ((error == 0), "Add * * 5.6.7.8/16 4321 action %d",
862                 args.table_args.action_index);
863   app_index = session_lookup_local_session_endpoint (local_ns_index, &sep);
864   SESSION_TEST ((app_index == server_index), "local session endpoint lookup "
865                 "should work");
866
867   /*
868    * Delete 0/0 * 5.6.7.8/16 4321, 1.2.3.4/16 * 5.6.7.8/16 4321 and
869    * 1.2.3.4/16 1234 5.6.7.8/16 4321
870    */
871   args.table_args.is_add = 0;
872   error = vnet_session_rule_add_del (&args);
873   SESSION_TEST ((error == 0), "Del 0/0 * 5.6.7.8/16 4321");
874   app_index = session_lookup_local_session_endpoint (local_ns_index, &sep);
875   SESSION_TEST ((app_index != server_index), "local session endpoint lookup "
876                 "should not work (removed)");
877
878   args.table_args.is_add = 0;
879   args.table_args.lcl = lcl_pref;
880   error = vnet_session_rule_add_del (&args);
881   SESSION_TEST ((error == 0), "Del 1.2.3.4/16 * 5.6.7.8/16 4321");
882   tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4,
883                                       &rmt_pref.fp_addr.ip4, lcl_port + 1,
884                                       rmt_port, TRANSPORT_PROTO_TCP, 0);
885   SESSION_TEST ((tc == 0), "optimized lookup for lcl port + 1 should not "
886                 "work (del)");
887
888   args.table_args.is_add = 0;
889   args.table_args.lcl_port = 1234;
890   error = vnet_session_rule_add_del (&args);
891   SESSION_TEST ((error == 0), "Del 1.2.3.4/16 1234 5.6.7.8/16 4321");
892   tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4,
893                                       &rmt_pref.fp_addr.ip4, lcl_port,
894                                       rmt_port, TRANSPORT_PROTO_TCP, 0);
895   SESSION_TEST ((tc == 0), "optimized lookup should not work (del)");
896   return 0;
897 }
898
899 static clib_error_t *
900 session_test (vlib_main_t * vm,
901               unformat_input_t * input, vlib_cli_command_t * cmd_arg)
902 {
903   int res = 0;
904
905   vnet_session_enable_disable (vm, 1);
906
907   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
908     {
909       if (unformat (input, "namespace"))
910         {
911           res = session_test_namespace (vm, input);
912         }
913       else if (unformat (input, "rules-table"))
914         res = session_test_rule_table (vm, input);
915       else if (unformat (input, "rules"))
916         res = session_test_rules (vm, input);
917       else
918         break;
919     }
920
921   if (res)
922     return clib_error_return (0, "Session unit test failed");
923   return 0;
924 }
925
926 /* *INDENT-OFF* */
927 VLIB_CLI_COMMAND (tcp_test_command, static) =
928 {
929   .path = "test session",
930   .short_help = "internal session unit tests",
931   .function = session_test,
932 };
933 /* *INDENT-ON* */
934
935 /*
936  * fd.io coding-style-patch-verification: ON
937  *
938  * Local Variables:
939  * eval: (c-set-style "gnu")
940  * End:
941  */