Rework kube-proxy into LB plugin
[vpp.git] / src / plugins / lb / cli.c
1 /*
2  * Copyright (c) 2016 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 <lb/lb.h>
17 #include <lb/util.h>
18
19 static clib_error_t *
20 lb_vip_command_fn (vlib_main_t * vm,
21               unformat_input_t * input, vlib_cli_command_t * cmd)
22 {
23   unformat_input_t _line_input, *line_input = &_line_input;
24   lb_vip_add_args_t args;
25   u8 del = 0;
26   int ret;
27   u32 encap = 0;
28   u32 dscp = ~0;
29   u32 srv_type = LB_SRV_TYPE_CLUSTERIP;
30   u32 port = 0;
31   u32 target_port = 0;
32   u32 node_port = 0;
33   clib_error_t *error = 0;
34
35   args.new_length = 1024;
36
37   if (!unformat_user (input, unformat_line_input, line_input))
38     return 0;
39
40   if (!unformat(line_input, "%U", unformat_ip46_prefix, &(args.prefix),
41                 &(args.plen), IP46_TYPE_ANY, &(args.plen))) {
42     error = clib_error_return (0, "invalid vip prefix: '%U'",
43                                format_unformat_error, line_input);
44     goto done;
45   }
46
47   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
48   {
49     if (unformat(line_input, "new_len %d", &(args.new_length)))
50       ;
51     else if (unformat(line_input, "del"))
52       del = 1;
53     else if (unformat(line_input, "encap gre4"))
54       encap = LB_ENCAP_TYPE_GRE4;
55     else if (unformat(line_input, "encap gre6"))
56       encap = LB_ENCAP_TYPE_GRE6;
57     else if (unformat(line_input, "encap l3dsr"))
58       encap = LB_ENCAP_TYPE_L3DSR;
59     else if (unformat(line_input, "encap nat4"))
60       encap = LB_ENCAP_TYPE_NAT4;
61     else if (unformat(line_input, "encap nat6"))
62       encap = LB_ENCAP_TYPE_NAT6;
63     else if (unformat(line_input, "dscp %d", &dscp))
64       ;
65     else if (unformat(line_input, "type clusterip"))
66       srv_type = LB_SRV_TYPE_CLUSTERIP;
67     else if (unformat(line_input, "type nodeport"))
68       srv_type = LB_SRV_TYPE_NODEPORT;
69     else if (unformat(line_input, "port %d", &port))
70       ;
71     else if (unformat(line_input, "target_port %d", &target_port))
72       ;
73     else if (unformat(line_input, "node_port %d", &node_port))
74       ;
75     else {
76       error = clib_error_return (0, "parse error: '%U'",
77                                 format_unformat_error, line_input);
78       goto done;
79     }
80   }
81
82   if ((encap != LB_ENCAP_TYPE_L3DSR) && (dscp != ~0))
83     {
84       error = clib_error_return(0, "lb_vip_add error: "
85                                 "should not configure dscp for none L3DSR.");
86       goto done;
87     }
88
89   if ((encap == LB_ENCAP_TYPE_L3DSR) && (dscp >= 64))
90     {
91       error = clib_error_return(0, "lb_vip_add error: "
92                                 "dscp for L3DSR should be less than 64.");
93       goto done;
94     }
95
96   if (ip46_prefix_is_ip4(&(args.prefix), (args.plen)))
97     {
98       if (encap == LB_ENCAP_TYPE_GRE4)
99         args.type = LB_VIP_TYPE_IP4_GRE4;
100       else if (encap == LB_ENCAP_TYPE_GRE6)
101         args.type = LB_VIP_TYPE_IP4_GRE6;
102       else if (encap == LB_ENCAP_TYPE_L3DSR)
103         args.type = LB_VIP_TYPE_IP4_L3DSR;
104       else if (encap == LB_ENCAP_TYPE_NAT4)
105         args.type = LB_VIP_TYPE_IP4_NAT4;
106       else if (encap == LB_ENCAP_TYPE_NAT6)
107         {
108           error = clib_error_return(0, "currently does not support NAT46");
109           goto done;
110         }
111     }
112   else
113     {
114       if (encap == LB_ENCAP_TYPE_GRE4)
115         args.type = LB_VIP_TYPE_IP6_GRE4;
116       else if (encap == LB_ENCAP_TYPE_GRE6)
117         args.type = LB_VIP_TYPE_IP6_GRE6;
118       else if (encap == LB_ENCAP_TYPE_NAT6)
119         args.type = LB_VIP_TYPE_IP6_NAT6;
120       else if (encap == LB_ENCAP_TYPE_NAT4)
121         {
122           error = clib_error_return(0, "currently does not support NAT64");
123           goto done;
124         }
125     }
126
127   lb_garbage_collection();
128
129   u32 index;
130   if (!del) {
131     if (encap == LB_ENCAP_TYPE_L3DSR) {
132         args.encap_args.dscp = (u8)(dscp & 0x3F);
133       }
134       else if ((encap == LB_ENCAP_TYPE_NAT4)
135                || (encap == LB_ENCAP_TYPE_NAT6))
136         {
137           args.encap_args.srv_type = (u8) srv_type;
138           args.encap_args.port = (u16) port;
139           args.encap_args.target_port = (u16) target_port;
140           args.encap_args.node_port = (u16) node_port;
141         }
142
143     if ((ret = lb_vip_add(args, &index))) {
144       error = clib_error_return (0, "lb_vip_add error %d", ret);
145       goto done;
146     } else {
147       vlib_cli_output(vm, "lb_vip_add ok %d", index);
148     }
149   } else {
150     if ((ret = lb_vip_find_index(&(args.prefix), args.plen, &index))) {
151       error = clib_error_return (0, "lb_vip_find_index error %d", ret);
152       goto done;
153     } else if ((ret = lb_vip_del(index))) {
154       error = clib_error_return (0, "lb_vip_del error %d", ret);
155       goto done;
156     }
157   }
158
159 done:
160   unformat_free (line_input);
161
162   return error;
163 }
164
165 VLIB_CLI_COMMAND (lb_vip_command, static) =
166 {
167   .path = "lb vip",
168   .short_help = "lb vip <prefix> [encap (gre6|gre4|l3dsr|nat4|nat6)] "
169       "[dscp <n>] "
170       "[type (nodeport|clusterip) port <n> target_port <n> node_port <n>] "
171       "[new_len <n>] [del]",
172   .function = lb_vip_command_fn,
173 };
174
175 static clib_error_t *
176 lb_as_command_fn (vlib_main_t * vm,
177               unformat_input_t * input, vlib_cli_command_t * cmd)
178 {
179   unformat_input_t _line_input, *line_input = &_line_input;
180   ip46_address_t vip_prefix, as_addr;
181   u8 vip_plen;
182   ip46_address_t *as_array = 0;
183   u32 vip_index;
184   u8 del = 0;
185   int ret;
186   clib_error_t *error = 0;
187
188   if (!unformat_user (input, unformat_line_input, line_input))
189     return 0;
190
191   if (!unformat(line_input, "%U", unformat_ip46_prefix, &vip_prefix, &vip_plen, IP46_TYPE_ANY)) {
192     error = clib_error_return (0, "invalid as address: '%U'",
193                                format_unformat_error, line_input);
194     goto done;
195   }
196
197   if ((ret = lb_vip_find_index(&vip_prefix, vip_plen, &vip_index))) {
198     error = clib_error_return (0, "lb_vip_find_index error %d", ret);
199     goto done;
200   }
201
202   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
203   {
204     if (unformat(line_input, "%U", unformat_ip46_address, &as_addr, IP46_TYPE_ANY)) {
205       vec_add1(as_array, as_addr);
206     } else if (unformat(line_input, "del")) {
207       del = 1;
208     } else {
209       error = clib_error_return (0, "parse error: '%U'",
210                                  format_unformat_error, line_input);
211       goto done;
212     }
213   }
214
215   if (!vec_len(as_array)) {
216     error = clib_error_return (0, "No AS address provided");
217     goto done;
218   }
219
220   lb_garbage_collection();
221   clib_warning("vip index is %d", vip_index);
222
223   if (del) {
224     if ((ret = lb_vip_del_ass(vip_index, as_array, vec_len(as_array)))) {
225       error = clib_error_return (0, "lb_vip_del_ass error %d", ret);
226       goto done;
227     }
228   } else {
229     if ((ret = lb_vip_add_ass(vip_index, as_array, vec_len(as_array)))) {
230       error = clib_error_return (0, "lb_vip_add_ass error %d", ret);
231       goto done;
232     }
233   }
234
235 done:
236   unformat_free (line_input);
237   vec_free(as_array);
238
239   return error;
240 }
241
242 VLIB_CLI_COMMAND (lb_as_command, static) =
243 {
244   .path = "lb as",
245   .short_help = "lb as <vip-prefix> [<address> [<address> [...]]] [del]",
246   .function = lb_as_command_fn,
247 };
248
249 static clib_error_t *
250 lb_conf_command_fn (vlib_main_t * vm,
251               unformat_input_t * input, vlib_cli_command_t * cmd)
252 {
253   lb_main_t *lbm = &lb_main;
254   unformat_input_t _line_input, *line_input = &_line_input;
255   ip4_address_t ip4 = lbm->ip4_src_address;
256   ip6_address_t ip6 = lbm->ip6_src_address;
257   u32 per_cpu_sticky_buckets = lbm->per_cpu_sticky_buckets;
258   u32 per_cpu_sticky_buckets_log2 = 0;
259   u32 flow_timeout = lbm->flow_timeout;
260   int ret;
261   clib_error_t *error = 0;
262
263   if (!unformat_user (input, unformat_line_input, line_input))
264     return 0;
265
266   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
267   {
268     if (unformat(line_input, "ip4-src-address %U", unformat_ip4_address, &ip4))
269       ;
270     else if (unformat(line_input, "ip6-src-address %U", unformat_ip6_address, &ip6))
271       ;
272     else if (unformat(line_input, "buckets %d", &per_cpu_sticky_buckets))
273       ;
274     else if (unformat(line_input, "buckets-log2 %d", &per_cpu_sticky_buckets_log2)) {
275       if (per_cpu_sticky_buckets_log2 >= 32)
276         return clib_error_return (0, "buckets-log2 value is too high");
277       per_cpu_sticky_buckets = 1 << per_cpu_sticky_buckets_log2;
278     } else if (unformat(line_input, "timeout %d", &flow_timeout))
279       ;
280     else {
281       error = clib_error_return (0, "parse error: '%U'",
282                                  format_unformat_error, line_input);
283       goto done;
284     }
285   }
286
287   lb_garbage_collection();
288
289   if ((ret = lb_conf(&ip4, &ip6, per_cpu_sticky_buckets, flow_timeout))) {
290     error = clib_error_return (0, "lb_conf error %d", ret);
291     goto done;
292   }
293
294 done:
295   unformat_free (line_input);
296
297   return error;
298 }
299
300 VLIB_CLI_COMMAND (lb_conf_command, static) =
301 {
302   .path = "lb conf",
303   .short_help = "lb conf [ip4-src-address <addr>] [ip6-src-address <addr>] [buckets <n>] [timeout <s>]",
304   .function = lb_conf_command_fn,
305 };
306
307 static clib_error_t *
308 lb_show_command_fn (vlib_main_t * vm,
309               unformat_input_t * input, vlib_cli_command_t * cmd)
310 {
311   vlib_cli_output(vm, "%U", format_lb_main);
312   return NULL;
313 }
314
315
316 VLIB_CLI_COMMAND (lb_show_command, static) =
317 {
318   .path = "show lb",
319   .short_help = "show lb",
320   .function = lb_show_command_fn,
321 };
322
323 static clib_error_t *
324 lb_show_vips_command_fn (vlib_main_t * vm,
325               unformat_input_t * input, vlib_cli_command_t * cmd)
326 {
327   unformat_input_t line_input;
328   lb_main_t *lbm = &lb_main;
329   lb_vip_t *vip;
330   u8 verbose = 0;
331
332   if (!unformat_user (input, unformat_line_input, &line_input))
333       return 0;
334
335   if (unformat(&line_input, "verbose"))
336     verbose = 1;
337
338   pool_foreach(vip, lbm->vips, {
339       vlib_cli_output(vm, "%U\n", verbose?format_lb_vip_detailed:format_lb_vip, vip);
340   });
341
342   unformat_free (&line_input);
343   return NULL;
344 }
345
346 VLIB_CLI_COMMAND (lb_show_vips_command, static) =
347 {
348   .path = "show lb vips",
349   .short_help = "show lb vips [verbose]",
350   .function = lb_show_vips_command_fn,
351 };
352
353 static clib_error_t *
354 lb_set_interface_nat_command_fn (vlib_main_t * vm,
355                                  unformat_input_t * input,
356                                  vlib_cli_command_t * cmd,
357                                  u8 is_nat6)
358 {
359   unformat_input_t _line_input, *line_input = &_line_input;
360   vnet_main_t * vnm = vnet_get_main();
361   clib_error_t * error = 0;
362   u32 * sw_if_index = 0;
363   u32 * inside_sw_if_indices = 0;
364   int is_del = 0;
365
366   /* Get a line of input. */
367   if (!unformat_user (input, unformat_line_input, line_input))
368     return 0;
369
370   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
371     {
372       if (unformat (line_input, "in %U", unformat_vnet_sw_interface,
373                     vnm, sw_if_index))
374         vec_add1 (inside_sw_if_indices, *sw_if_index);
375       else if (unformat (line_input, "del"))
376         is_del = 1;
377       else
378         {
379           error = clib_error_return (0, "unknown input '%U'",
380             format_unformat_error, line_input);
381           goto done;
382         }
383     }
384
385     vec_foreach (sw_if_index, inside_sw_if_indices)
386     {
387       if (!is_nat6)
388         {
389           if (lb_nat4_interface_add_del (*sw_if_index, is_del))
390             {
391               error = clib_error_return(
392                   0, "%s %U failed", is_del ? "del" : "add",
393                   format_vnet_sw_interface_name, vnm,
394                   vnet_get_sw_interface (vnm, *sw_if_index));
395               goto done;
396             }
397         }
398       else
399         {
400           if (lb_nat6_interface_add_del (*sw_if_index, is_del))
401             {
402               error = clib_error_return(
403                   0, "%s %U failed", is_del ? "del" : "add",
404                   format_vnet_sw_interface_name, vnm,
405                   vnet_get_sw_interface (vnm, *sw_if_index));
406               goto done;
407             }
408         }
409     }
410
411 done:
412   unformat_free (line_input);
413   vec_free (inside_sw_if_indices);
414
415   return error;
416 }
417
418 static clib_error_t *
419 lb_set_interface_nat4_command_fn (vlib_main_t * vm,
420                                   unformat_input_t * input,
421                                   vlib_cli_command_t * cmd)
422 {
423   return lb_set_interface_nat_command_fn(vm, input, cmd, 0);
424 }
425
426 VLIB_CLI_COMMAND (lb_set_interface_nat4_command, static) = {
427   .path = "lb set interface nat4",
428   .function = lb_set_interface_nat4_command_fn,
429   .short_help = "lb set interface nat4 in <intfc> [del]",
430 };
431
432 static clib_error_t *
433 lb_set_interface_nat6_command_fn (vlib_main_t * vm,
434                                   unformat_input_t * input,
435                                   vlib_cli_command_t * cmd)
436 {
437   return lb_set_interface_nat_command_fn(vm, input, cmd, 1);
438 }
439
440 VLIB_CLI_COMMAND (lb_set_interface_nat6_command, static) = {
441   .path = "lb set interface nat6",
442   .function = lb_set_interface_nat6_command_fn,
443   .short_help = "lb set interface nat6 in <intfc> [del]",
444 };
445
446 static clib_error_t *
447 lb_flowtable_flush_command_fn (vlib_main_t * vm,
448               unformat_input_t * input, vlib_cli_command_t * cmd)
449 {
450   u32 thread_index;
451   vlib_thread_main_t *tm = vlib_get_thread_main();
452   lb_main_t *lbm = &lb_main;
453
454   for(thread_index = 0; thread_index < tm->n_vlib_mains; thread_index++ ) {
455     lb_hash_t *h = lbm->per_cpu[thread_index].sticky_ht;
456     if (h != NULL) {
457         u32 i;
458         lb_hash_bucket_t *b;
459
460         lb_hash_foreach_entry(h, b, i) {
461             vlib_refcount_add(&lbm->as_refcount, thread_index, b->value[i], -1);
462             vlib_refcount_add(&lbm->as_refcount, thread_index, 0, 1);
463         }
464
465         lb_hash_free(h);
466         lbm->per_cpu[thread_index].sticky_ht = 0;
467     }
468   }
469
470   return NULL;
471 }
472
473 /*
474  * flush all lb flowtables
475  * This is indented for debug and unit-tests purposes only
476  */
477 VLIB_CLI_COMMAND (lb_flowtable_flush_command, static) =
478 {
479   .path = "test lb flowtable flush",
480   .short_help = "test lb flowtable flush",
481   .function = lb_flowtable_flush_command_fn,
482 };