wireguard: initial implementation of wireguard protocol
[vpp.git] / src / plugins / wireguard / wireguard_cli.c
1 /*
2  * Copyright (c) 2020 Doc.ai 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 <wireguard/wireguard.h>
17 #include <wireguard/wireguard_key.h>
18 #include <wireguard/wireguard_peer.h>
19 #include <wireguard/wireguard_if.h>
20
21 static clib_error_t *
22 wg_if_create_cli (vlib_main_t * vm,
23                   unformat_input_t * input, vlib_cli_command_t * cmd)
24 {
25   unformat_input_t _line_input, *line_input = &_line_input;
26   u8 private_key[NOISE_PUBLIC_KEY_LEN];
27   u32 instance, sw_if_index;
28   ip_address_t src_ip;
29   clib_error_t *error;
30   u8 *private_key_64;
31   u32 port, generate_key = 0;
32   int rv;
33
34   error = NULL;
35   instance = sw_if_index = ~0;
36   private_key_64 = 0;
37   port = 0;
38
39   if (unformat_user (input, unformat_line_input, line_input))
40     {
41       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
42         {
43           if (unformat (line_input, "instance %d", &instance))
44             ;
45           else if (unformat (line_input, "private-key %s", &private_key_64))
46             {
47               if (!(key_from_base64 (private_key_64,
48                                      NOISE_KEY_LEN_BASE64, private_key)))
49                 {
50                   error = clib_error_return (0, "Error parsing private key");
51                   break;
52                 }
53             }
54           else if (unformat (line_input, "listen-port %d", &port))
55             ;
56           else if (unformat (line_input, "port %d", &port))
57             ;
58           else if (unformat (line_input, "generate-key"))
59             generate_key = 1;
60           else
61             if (unformat (line_input, "src %U", unformat_ip_address, &src_ip))
62             ;
63           else
64             {
65               error = clib_error_return (0, "unknown input: %U",
66                                          format_unformat_error, line_input);
67               break;
68             }
69         }
70
71       unformat_free (line_input);
72
73       if (error)
74         return error;
75     }
76
77   if (generate_key)
78     curve25519_gen_secret (private_key);
79
80   rv = wg_if_create (instance, private_key, port, &src_ip, &sw_if_index);
81
82   if (rv)
83     return clib_error_return (0, "wireguard interface create failed");
84
85   vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
86                    sw_if_index);
87   return 0;
88 }
89
90 /*?
91  * Create a Wireguard interface.
92 ?*/
93 /* *INDENT-OFF* */
94 VLIB_CLI_COMMAND (wg_if_create_command, static) = {
95   .path = "wireguard create",
96   .short_help = "wireguard create listen-port <port> "
97     "private-key <key> src <IP> [generate-key]",
98   .function = wg_if_create_cli,
99 };
100 /* *INDENT-ON* */
101
102 static clib_error_t *
103 wg_if_delete_cli (vlib_main_t * vm,
104                   unformat_input_t * input, vlib_cli_command_t * cmd)
105 {
106   vnet_main_t *vnm;
107   u32 sw_if_index;
108   int rv;
109
110   vnm = vnet_get_main ();
111   sw_if_index = ~0;
112
113   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
114     {
115       if (unformat
116           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
117         ;
118       else
119         break;
120     }
121
122   if (~0 != sw_if_index)
123     {
124       rv = wg_if_delete (sw_if_index);
125
126       if (rv)
127         return clib_error_return (0, "wireguard interface delete failed");
128     }
129   else
130     return clib_error_return (0, "no such interface: %U",
131                               format_unformat_error, input);
132
133   return 0;
134 }
135
136 /*?
137  * Delete a Wireguard interface.
138 ?*/
139 /* *INDENT-OFF* */
140 VLIB_CLI_COMMAND (wg_if_delete_command, static) = {
141   .path = "wireguard delete",
142   .short_help = "wireguard delete <interface>",
143   .function = wg_if_delete_cli,
144 };
145 /* *INDENT-ON* */
146
147
148 static clib_error_t *
149 wg_peer_add_command_fn (vlib_main_t * vm,
150                         unformat_input_t * input, vlib_cli_command_t * cmd)
151 {
152   vnet_main_t *vnm = vnet_get_main ();
153   clib_error_t *error = NULL;
154   unformat_input_t _line_input, *line_input = &_line_input;
155
156   u8 *public_key_64 = 0;
157   u8 public_key[NOISE_PUBLIC_KEY_LEN];
158   fib_prefix_t allowed_ip, *allowed_ips = NULL;
159   ip_prefix_t pfx;
160   ip_address_t ip;
161   u32 portDst = 0, table_id = 0;
162   u32 persistent_keepalive = 0;
163   u32 tun_sw_if_index = ~0;
164   u32 peer_index;
165   int rv;
166
167   if (!unformat_user (input, unformat_line_input, line_input))
168     return 0;
169
170   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
171     {
172       if (unformat (line_input, "public-key %s", &public_key_64))
173         {
174           if (!(key_from_base64 (public_key_64,
175                                  NOISE_KEY_LEN_BASE64, public_key)))
176             {
177               error = clib_error_return (0, "Error parsing private key");
178               goto done;
179             }
180         }
181       else if (unformat (line_input, "endpoint %U", unformat_ip_address, &ip))
182         ;
183       else if (unformat (line_input, "table-id %d", &table_id))
184         ;
185       else if (unformat (line_input, "port %d", &portDst))
186         ;
187       else if (unformat (line_input, "persistent-keepalive %d",
188                          &persistent_keepalive))
189         ;
190       else if (unformat (line_input, "allowed-ip %U",
191                          unformat_ip_prefix, &pfx))
192         {
193           ip_prefix_to_fib_prefix (&pfx, &allowed_ip);
194           vec_add1 (allowed_ips, allowed_ip);
195         }
196       else if (unformat (line_input, "%U",
197                          unformat_vnet_sw_interface, vnm, &tun_sw_if_index))
198         ;
199       else
200         {
201           error = clib_error_return (0, "Input error");
202           goto done;
203         }
204     }
205
206   if (AF_IP6 == ip_addr_version (&ip) ||
207       FIB_PROTOCOL_IP6 == allowed_ip.fp_proto)
208     rv = VNET_API_ERROR_INVALID_PROTOCOL;
209   else
210     rv = wg_peer_add (tun_sw_if_index,
211                       public_key,
212                       table_id,
213                       &ip_addr_46 (&ip),
214                       allowed_ips,
215                       portDst, persistent_keepalive, &peer_index);
216
217   switch (rv)
218     {
219     case VNET_API_ERROR_KEY_LENGTH:
220       error = clib_error_return (0, "Error parsing public key");
221       break;
222     case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
223       error = clib_error_return (0, "Peer already exist");
224       break;
225     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
226       error = clib_error_return (0, "Tunnel is not specified");
227       break;
228     case VNET_API_ERROR_LIMIT_EXCEEDED:
229       error = clib_error_return (0, "Max peers limit");
230       break;
231     case VNET_API_ERROR_INIT_FAILED:
232       error = clib_error_return (0, "wireguard device parameters is not set");
233       break;
234     case VNET_API_ERROR_INVALID_PROTOCOL:
235       error = clib_error_return (0, "ipv6 not supported yet");
236       break;
237     }
238
239 done:
240   vec_free (public_key_64);
241   vec_free (allowed_ips);
242   unformat_free (line_input);
243   return error;
244 }
245
246 /* *INDENT-OFF* */
247 VLIB_CLI_COMMAND (wg_peer_add_command, static) =
248 {
249   .path = "wireguard peer add",
250   .short_help = "wireguard peer add <wg_int> public-key <pub_key_other>"
251   "endpoint <ip4_dst> allowed-ip <prefix>"
252   "dst-port [port_dst] persistent-keepalive [keepalive_interval]",
253   .function = wg_peer_add_command_fn,
254 };
255 /* *INDENT-ON* */
256
257 static clib_error_t *
258 wg_peer_remove_command_fn (vlib_main_t * vm,
259                            unformat_input_t * input, vlib_cli_command_t * cmd)
260 {
261   clib_error_t *error = NULL;
262   u32 peer_index;
263   int rv;
264
265   unformat_input_t _line_input, *line_input = &_line_input;
266   if (!unformat_user (input, unformat_line_input, line_input))
267     return 0;
268
269   if (unformat (line_input, "%d", &peer_index))
270     ;
271   else
272     {
273       error = clib_error_return (0, "Input error");
274       goto done;
275     }
276
277   rv = wg_peer_remove (peer_index);
278
279   switch (rv)
280     {
281     case VNET_API_ERROR_KEY_LENGTH:
282       error = clib_error_return (0, "Error parsing public key");
283       break;
284     }
285
286 done:
287   unformat_free (line_input);
288   return error;
289 }
290
291 /* *INDENT-OFF* */
292 VLIB_CLI_COMMAND (wg_peer_remove_command, static) =
293 {
294   .path = "wireguard peer remove",
295   .short_help = "wireguard peer remove <index>",
296   .function = wg_peer_remove_command_fn,
297 };
298 /* *INDENT-ON* */
299
300 static walk_rc_t
301 wg_peer_show_one (index_t peeri, void *arg)
302 {
303   vlib_cli_output (arg, "%U", format_wg_peer, peeri);
304
305   return (WALK_CONTINUE);
306 }
307
308 static clib_error_t *
309 wg_show_peer_command_fn (vlib_main_t * vm,
310                          unformat_input_t * input, vlib_cli_command_t * cmd)
311 {
312   wg_peer_walk (wg_peer_show_one, vm);
313
314   return NULL;
315 }
316
317 /* *INDENT-OFF* */
318 VLIB_CLI_COMMAND (wg_show_peers_command, static) =
319 {
320   .path = "show wireguard peer",
321   .short_help = "show wireguard peer",
322   .function = wg_show_peer_command_fn,
323 };
324 /* *INDENT-ON* */
325
326 static walk_rc_t
327 wg_if_show_one (index_t itfi, void *arg)
328 {
329   vlib_cli_output (arg, "%U", format_wg_if, itfi);
330
331   return (WALK_CONTINUE);
332 }
333
334 static clib_error_t *
335 wg_show_if_command_fn (vlib_main_t * vm,
336                        unformat_input_t * input, vlib_cli_command_t * cmd)
337 {
338   wg_if_walk (wg_if_show_one, vm);
339
340   return NULL;
341 }
342
343 /* *INDENT-OFF* */
344 VLIB_CLI_COMMAND (wg_show_itfs_command, static) =
345 {
346   .path = "show wireguard interface",
347   .short_help = "show wireguard",
348   .function = wg_show_if_command_fn,
349 };
350 /* *INDENT-ON* */
351
352 /*
353  * fd.io coding-style-patch-verification: ON
354  *
355  * Local Variables:
356  * eval: (c-set-style "gnu")
357  * End:
358  */