wireguard: improve sending WG interface dump details
[vpp.git] / src / plugins / wireguard / wireguard_api.c
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Copyright (c) 2020 Doc.ai and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <vnet/vnet.h>
18 #include <vlibmemory/api.h>
19
20 #include <vnet/format_fns.h>
21 #include <vnet/ip/ip_types_api.h>
22 #include <vlibapi/api.h>
23
24 #include <wireguard/wireguard.api_enum.h>
25 #include <wireguard/wireguard.api_types.h>
26
27 #include <wireguard/wireguard_key.h>
28 #include <wireguard/wireguard.h>
29 #include <wireguard/wireguard_if.h>
30
31 #define REPLY_MSG_ID_BASE wmp->msg_id_base
32 #include <wireguard/wireguard_peer.h>
33 #include <vlibapi/api_helper_macros.h>
34
35 static void
36   vl_api_wireguard_interface_create_t_handler
37   (vl_api_wireguard_interface_create_t * mp)
38 {
39   vl_api_wireguard_interface_create_reply_t *rmp;
40   wg_main_t *wmp = &wg_main;
41   u8 private_key[NOISE_PUBLIC_KEY_LEN];
42   ip_address_t src;
43   u32 sw_if_index = ~0;
44   int rv = 0;
45
46   wg_feature_init (wmp);
47
48   ip_address_decode2 (&mp->interface.src_ip, &src);
49
50   if (mp->generate_key)
51     curve25519_gen_secret (private_key);
52   else
53     clib_memcpy (private_key, mp->interface.private_key, NOISE_PUBLIC_KEY_LEN);
54
55   rv = wg_if_create (ntohl (mp->interface.user_instance), private_key,
56                      ntohs (mp->interface.port), &src, &sw_if_index);
57
58   REPLY_MACRO2(VL_API_WIREGUARD_INTERFACE_CREATE_REPLY,
59   {
60     rmp->sw_if_index = htonl(sw_if_index);
61   });
62 }
63
64 static void
65   vl_api_wireguard_interface_delete_t_handler
66   (vl_api_wireguard_interface_delete_t * mp)
67 {
68   vl_api_wireguard_interface_delete_reply_t *rmp;
69   wg_main_t *wmp = &wg_main;
70   int rv = 0;
71
72   wg_feature_init (wmp);
73
74   VALIDATE_SW_IF_INDEX (mp);
75
76   rv = wg_if_delete (ntohl (mp->sw_if_index));
77
78   BAD_SW_IF_INDEX_LABEL;
79
80   REPLY_MACRO(VL_API_WIREGUARD_INTERFACE_DELETE_REPLY);
81 }
82
83 typedef struct wg_deatils_walk_t_
84 {
85   vl_api_registration_t *reg;
86   u32 context;
87   u8 show_private_key;
88 } wg_deatils_walk_t;
89
90 static walk_rc_t
91 wireguard_if_send_details (index_t wgii, void *data)
92 {
93   vl_api_wireguard_interface_details_t *rmp;
94   wg_deatils_walk_t *ctx = data;
95   const wg_if_t *wgi;
96   const noise_local_t *local;
97
98   wgi = wg_if_get (wgii);
99   local = noise_local_get (wgi->local_idx);
100
101   rmp = vl_msg_api_alloc_zero (sizeof (*rmp));
102   rmp->_vl_msg_id = htons (VL_API_WIREGUARD_INTERFACE_DETAILS +
103                            wg_main.msg_id_base);
104
105   if (ctx->show_private_key)
106     clib_memcpy (rmp->interface.private_key,
107                  local->l_private, NOISE_PUBLIC_KEY_LEN);
108   clib_memcpy (rmp->interface.public_key,
109                local->l_public, NOISE_PUBLIC_KEY_LEN);
110   rmp->interface.sw_if_index = htonl (wgi->sw_if_index);
111   rmp->interface.port = htons (wgi->port);
112   rmp->interface.user_instance = htonl (wgi->user_instance);
113   ip_address_encode2 (&wgi->src_ip, &rmp->interface.src_ip);
114
115   rmp->context = ctx->context;
116
117   vl_api_send_msg (ctx->reg, (u8 *) rmp);
118
119   return (WALK_CONTINUE);
120 }
121
122 static void
123 vl_api_wireguard_interface_dump_t_handler (vl_api_wireguard_interface_dump_t *
124                                            mp)
125 {
126   vl_api_registration_t *reg;
127   wg_main_t *wmp = &wg_main;
128
129   wg_feature_init (wmp);
130
131   reg = vl_api_client_index_to_registration (mp->client_index);
132   if (reg == 0)
133     return;
134
135   wg_deatils_walk_t ctx = {
136     .reg = reg,
137     .context = mp->context,
138     .show_private_key = mp->show_private_key,
139   };
140
141   u32 sw_if_index = ntohl (mp->sw_if_index);
142   if (sw_if_index == ~0)
143     wg_if_walk (wireguard_if_send_details, &ctx);
144   else
145     {
146       index_t wgii = wg_if_find_by_sw_if_index (sw_if_index);
147       if (wgii != INDEX_INVALID)
148         wireguard_if_send_details (wgii, &ctx);
149     }
150 }
151
152 static void
153 vl_api_wireguard_peer_add_t_handler (vl_api_wireguard_peer_add_t * mp)
154 {
155   vl_api_wireguard_peer_add_reply_t *rmp;
156   wg_main_t *wmp = &wg_main;
157   index_t peeri = INDEX_INVALID;
158   int ii, rv = 0;
159
160   ip_address_t endpoint;
161   fib_prefix_t *allowed_ips = NULL;
162
163   VALIDATE_SW_IF_INDEX (&(mp->peer));
164
165   if (0 == mp->peer.n_allowed_ips)
166     {
167       rv = VNET_API_ERROR_INVALID_VALUE;
168       goto done;
169     }
170
171   wg_feature_init (wmp);
172
173   vec_validate (allowed_ips, mp->peer.n_allowed_ips - 1);
174   ip_address_decode2 (&mp->peer.endpoint, &endpoint);
175
176   for (ii = 0; ii < mp->peer.n_allowed_ips; ii++)
177     ip_prefix_decode (&mp->peer.allowed_ips[ii], &allowed_ips[ii]);
178
179   rv = wg_peer_add (ntohl (mp->peer.sw_if_index), mp->peer.public_key,
180                     ntohl (mp->peer.table_id), &ip_addr_46 (&endpoint),
181                     allowed_ips, ntohs (mp->peer.port),
182                     ntohs (mp->peer.persistent_keepalive), &peeri);
183
184   vec_free (allowed_ips);
185 done:
186   BAD_SW_IF_INDEX_LABEL;
187
188   REPLY_MACRO2(VL_API_WIREGUARD_PEER_ADD_REPLY,
189   {
190     rmp->peer_index = ntohl (peeri);
191   });
192 }
193
194 static void
195 vl_api_wireguard_peer_remove_t_handler (vl_api_wireguard_peer_remove_t * mp)
196 {
197   vl_api_wireguard_peer_remove_reply_t *rmp;
198   wg_main_t *wmp = &wg_main;
199   int rv = 0;
200
201   wg_feature_init (wmp);
202
203   rv = wg_peer_remove (ntohl (mp->peer_index));
204
205   REPLY_MACRO(VL_API_WIREGUARD_PEER_REMOVE_REPLY);
206 }
207
208 static walk_rc_t
209 wg_api_send_peers_details (index_t peeri, void *data)
210 {
211   vl_api_wireguard_peers_details_t *rmp;
212   wg_deatils_walk_t *ctx = data;
213   const wg_peer_t *peer;
214   u8 n_allowed_ips;
215   size_t ss;
216
217   if (pool_is_free_index (wg_peer_pool, peeri))
218     return (WALK_CONTINUE);
219
220   peer = wg_peer_get (peeri);
221
222   n_allowed_ips = vec_len (peer->allowed_ips);
223
224   ss = (sizeof (*rmp) + (n_allowed_ips * sizeof (rmp->peer.allowed_ips[0])));
225
226   rmp = vl_msg_api_alloc_zero (ss);
227
228   rmp->_vl_msg_id = htons (VL_API_WIREGUARD_PEERS_DETAILS +
229                            wg_main.msg_id_base);
230
231   rmp->peer.flags = peer->flags;
232   clib_memcpy (rmp->peer.public_key,
233                peer->remote.r_public, NOISE_PUBLIC_KEY_LEN);
234
235   ip_address_encode (&peer->dst.addr, IP46_TYPE_ANY, &rmp->peer.endpoint);
236   rmp->peer.port = htons (peer->dst.port);
237   rmp->peer.n_allowed_ips = n_allowed_ips;
238   rmp->peer.sw_if_index = htonl (peer->wg_sw_if_index);
239
240   int ii;
241   for (ii = 0; ii < n_allowed_ips; ii++)
242     ip_prefix_encode (&peer->allowed_ips[ii], &rmp->peer.allowed_ips[ii]);
243
244   rmp->context = ctx->context;
245
246   vl_api_send_msg (ctx->reg, (u8 *) rmp);
247
248   return (WALK_CONTINUE);
249 }
250
251 static void
252 vl_api_wireguard_peers_dump_t_handler (vl_api_wireguard_peers_dump_t * mp)
253 {
254   vl_api_registration_t *reg;
255   wg_main_t *wmp = &wg_main;
256
257   wg_feature_init (wmp);
258
259   reg = vl_api_client_index_to_registration (mp->client_index);
260   if (reg == NULL)
261     return;
262
263   wg_deatils_walk_t ctx = {
264     .reg = reg,
265     .context = mp->context,
266   };
267
268   if (mp->peer_index == ~0)
269     wg_peer_walk (wg_api_send_peers_details, &ctx);
270   else
271     wg_api_send_peers_details (ntohl (mp->peer_index), &ctx);
272 }
273
274 static vpe_client_registration_t *
275 wg_api_client_lookup (wg_peer_t *peer, u32 client_index)
276 {
277   uword *p;
278   vpe_client_registration_t *api_client = NULL;
279
280   p = hash_get (peer->api_client_by_client_index, client_index);
281   if (p)
282     api_client = vec_elt_at_index (peer->api_clients, p[0]);
283
284   return api_client;
285 }
286
287 static walk_rc_t
288 wg_api_update_peer_api_client (index_t peeri, void *data)
289 {
290   if (pool_is_free_index (wg_peer_pool, peeri))
291     return (WALK_CONTINUE);
292
293   vl_api_want_wireguard_peer_events_t *mp = data;
294   wg_peer_t *peer = wg_peer_get (peeri);
295
296   if (ntohl (mp->sw_if_index) != ~0 &&
297       ntohl (mp->sw_if_index) != peer->wg_sw_if_index)
298     {
299       return (WALK_CONTINUE);
300     }
301
302   vpe_client_registration_t *api_client;
303
304   api_client = wg_api_client_lookup (peer, mp->client_index);
305
306   if (api_client)
307     {
308       if (mp->enable_disable)
309         {
310           return (WALK_CONTINUE);
311         }
312       hash_unset (peer->api_client_by_client_index, api_client->client_index);
313       pool_put (peer->api_clients, api_client);
314     }
315   if (mp->enable_disable)
316     {
317       pool_get (peer->api_clients, api_client);
318       clib_memset (api_client, 0, sizeof (vpe_client_registration_t));
319       api_client->client_index = mp->client_index;
320       api_client->client_pid = mp->pid;
321       hash_set (peer->api_client_by_client_index, mp->client_index,
322                 api_client - peer->api_clients);
323     }
324
325   return (WALK_CONTINUE);
326 }
327
328 static void
329 vl_api_want_wireguard_peer_events_t_handler (
330   vl_api_want_wireguard_peer_events_t *mp)
331 {
332   wg_main_t *wmp = &wg_main;
333   vl_api_want_wireguard_peer_events_reply_t *rmp;
334   int rv = 0;
335
336   wg_feature_init (wmp);
337
338   if (mp->peer_index == ~0)
339     wg_peer_walk (wg_api_update_peer_api_client, mp);
340   else
341     wg_api_update_peer_api_client (ntohl (mp->peer_index), mp);
342
343   REPLY_MACRO (VL_API_WANT_WIREGUARD_PEER_EVENTS_REPLY);
344 }
345
346 void
347 wg_api_send_peer_event (vl_api_registration_t *rp, index_t peer_index,
348                         wg_peer_flags flags)
349 {
350   vl_api_wireguard_peer_event_t *mp = vl_msg_api_alloc (sizeof (*mp));
351   clib_memset (mp, 0, sizeof (*mp));
352
353   mp->_vl_msg_id = htons (VL_API_WIREGUARD_PEER_EVENT + wg_main.msg_id_base);
354   mp->peer_index = htonl (peer_index);
355   mp->flags = flags;
356
357   vl_api_send_msg (rp, (u8 *) mp);
358 }
359
360 void
361 wg_api_peer_event (index_t peeri, wg_peer_flags flags)
362 {
363   wg_peer_t *peer = wg_peer_get (peeri);
364   vpe_client_registration_t *api_client;
365   vl_api_registration_t *rp;
366
367   pool_foreach (api_client, peer->api_clients)
368     {
369       rp = vl_api_client_index_to_registration (api_client->client_index);
370       if (rp)
371         {
372           wg_api_send_peer_event (rp, peeri, flags);
373         }
374     };
375 }
376
377 static void
378 vl_api_wg_set_async_mode_t_handler (vl_api_wg_set_async_mode_t *mp)
379 {
380   wg_main_t *wmp = &wg_main;
381   vl_api_wg_set_async_mode_reply_t *rmp;
382   int rv = 0;
383
384   wg_set_async_mode (mp->async_enable);
385
386   REPLY_MACRO (VL_API_WG_SET_ASYNC_MODE_REPLY);
387 }
388
389 /* set tup the API message handling tables */
390 #include <wireguard/wireguard.api.c>
391 static clib_error_t *
392 wg_api_hookup (vlib_main_t * vm)
393 {
394   wg_main_t *wmp = &wg_main;
395   wmp->msg_id_base = setup_message_id_table ();
396   return 0;
397 }
398
399 VLIB_API_INIT_FUNCTION (wg_api_hookup);
400
401 /*
402  * fd.io coding-style-patch-verification: ON
403  *
404  * Local Variables:
405  * eval: (c-set-style "gnu")
406  * End:
407  */