linux-cp: Fix coverity issue
[vpp.git] / src / plugins / linux-cp / lcp_nl.c
1 /*
2  * Copyright (c) 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 #define _GNU_SOURCE
17 #include <sched.h>
18 #include <fcntl.h>
19
20 #include <linux-cp/lcp_nl.h>
21
22 #include <netlink/route/rule.h>
23 #include <netlink/msg.h>
24 #include <netlink/netlink.h>
25 #include <netlink/socket.h>
26 #include <netlink/route/link.h>
27 #include <netlink/route/route.h>
28 #include <netlink/route/neighbour.h>
29 #include <netlink/route/addr.h>
30
31 #include <vlib/vlib.h>
32 #include <vlib/unix/unix.h>
33 #include <vppinfra/error.h>
34
35 #include <vnet/fib/fib_table.h>
36
37 #include <libmnl/libmnl.h>
38
39 #include <plugins/linux-cp/lcp_interface.h>
40
41 typedef enum nl_event_type_t_
42 {
43   NL_EVENT_READ,
44   NL_EVENT_ERR,
45 } nl_event_type_t;
46
47 typedef struct nl_main
48 {
49
50   struct nl_sock *sk_route;
51   vlib_log_class_t nl_logger;
52   nl_vft_t *nl_vfts;
53   struct nl_cache *nl_caches[LCP_NL_N_OBJS];
54   nl_msg_info_t *nl_msg_queue;
55   uword clib_file_index;
56
57   u32 rx_buf_size;
58   u32 tx_buf_size;
59   u32 batch_size;
60   u32 batch_delay_ms;
61
62 } nl_main_t;
63
64 #define NL_RX_BUF_SIZE_DEF    (1 << 27) /* 128 MB */
65 #define NL_TX_BUF_SIZE_DEF    (1 << 18) /* 256 kB */
66 #define NL_BATCH_SIZE_DEF     (1 << 11) /* 2048 */
67 #define NL_BATCH_DELAY_MS_DEF 50        /* 50 ms, max 20 batch/s */
68
69 static nl_main_t nl_main = {
70   .rx_buf_size = NL_RX_BUF_SIZE_DEF,
71   .tx_buf_size = NL_TX_BUF_SIZE_DEF,
72   .batch_size = NL_BATCH_SIZE_DEF,
73   .batch_delay_ms = NL_BATCH_DELAY_MS_DEF,
74 };
75
76 /* #define foreach_nl_nft_proto  \ */
77 /*   _(IP4, "ip", AF_INT)  \ */
78 /*   _(IP6, "ip6", NFPROTO_IPV6) */
79
80 /* typedef enum nl_nft_proto_t_ */
81 /* { */
82 /* #define _(a,b,c) NL_NFT_PROTO_##a = c, */
83 /*   foreach_nl_nft_proto */
84 /* #undef _ */
85 /* } nl_nft_proto_t; */
86
87 #define FOREACH_VFT(__func, __arg)                                            \
88   {                                                                           \
89     nl_main_t *nm = &nl_main;                                                 \
90     nl_vft_t *__nv;                                                           \
91     vec_foreach (__nv, nm->nl_vfts)                                           \
92       {                                                                       \
93         if (!__nv->__func.cb)                                                 \
94           continue;                                                           \
95                                                                               \
96         if (!__nv->__func.is_mp_safe)                                         \
97           vlib_worker_thread_barrier_sync (vlib_get_main ());                 \
98                                                                               \
99         __nv->__func.cb (__arg);                                              \
100                                                                               \
101         if (!__nv->__func.is_mp_safe)                                         \
102           vlib_worker_thread_barrier_release (vlib_get_main ());              \
103       }                                                                       \
104   }
105
106 #define FOREACH_VFT_CTX(__func, __arg, __ctx)                                 \
107   {                                                                           \
108     nl_main_t *nm = &nl_main;                                                 \
109     nl_vft_t *__nv;                                                           \
110     vec_foreach (__nv, nm->nl_vfts)                                           \
111       {                                                                       \
112         if (!__nv->__func.cb)                                                 \
113           continue;                                                           \
114                                                                               \
115         if (!__nv->__func.is_mp_safe)                                         \
116           vlib_worker_thread_barrier_sync (vlib_get_main ());                 \
117                                                                               \
118         __nv->__func.cb (__arg, __ctx);                                       \
119                                                                               \
120         if (!__nv->__func.is_mp_safe)                                         \
121           vlib_worker_thread_barrier_release (vlib_get_main ());              \
122       }                                                                       \
123   }
124
125 void
126 nl_register_vft (const nl_vft_t *nv)
127 {
128   nl_main_t *nm = &nl_main;
129
130   vec_add1 (nm->nl_vfts, *nv);
131 }
132
133 #define NL_DBG(...)   vlib_log_debug (nl_main.nl_logger, __VA_ARGS__);
134 #define NL_INFO(...)  vlib_log_notice (nl_main.nl_logger, __VA_ARGS__);
135 #define NL_ERROR(...) vlib_log_err (nl_main.nl_logger, __VA_ARGS__);
136
137 static void lcp_nl_open_socket (void);
138 static void lcp_nl_close_socket (void);
139
140 static void
141 nl_route_del (struct rtnl_route *rr, void *arg)
142 {
143   FOREACH_VFT (nvl_rt_route_del, rr);
144 }
145
146 static void
147 nl_route_add (struct rtnl_route *rr, void *arg)
148 {
149   FOREACH_VFT (nvl_rt_route_add, rr);
150 }
151
152 static void
153 nl_neigh_del (struct rtnl_neigh *rn, void *arg)
154 {
155   FOREACH_VFT (nvl_rt_neigh_del, rn);
156 }
157
158 static void
159 nl_neigh_add (struct rtnl_neigh *rn, void *arg)
160 {
161   FOREACH_VFT (nvl_rt_neigh_add, rn);
162 }
163
164 static void
165 nl_link_addr_del (struct rtnl_addr *rla, void *arg)
166 {
167   FOREACH_VFT (nvl_rt_addr_del, rla);
168 }
169
170 static void
171 nl_link_addr_add (struct rtnl_addr *rla, void *arg)
172 {
173   FOREACH_VFT (nvl_rt_addr_add, rla);
174 }
175
176 static void
177 nl_link_del (struct rtnl_link *rl, void *arg)
178 {
179   FOREACH_VFT_CTX (nvl_rt_link_del, rl, arg);
180 }
181
182 static void
183 nl_link_add (struct rtnl_link *rl, void *arg)
184 {
185   FOREACH_VFT_CTX (nvl_rt_link_add, rl, arg);
186 }
187
188 static void
189 nl_route_dispatch (struct nl_object *obj, void *arg)
190 {
191   /* nothing can be done without interface mappings */
192   if (!lcp_itf_num_pairs ())
193     return;
194
195   switch (nl_object_get_msgtype (obj))
196     {
197     case RTM_NEWROUTE:
198       nl_route_add ((struct rtnl_route *) obj, arg);
199       break;
200     case RTM_DELROUTE:
201       nl_route_del ((struct rtnl_route *) obj, arg);
202       break;
203     case RTM_NEWNEIGH:
204       nl_neigh_add ((struct rtnl_neigh *) obj, arg);
205       break;
206     case RTM_DELNEIGH:
207       nl_neigh_del ((struct rtnl_neigh *) obj, arg);
208       break;
209     case RTM_NEWADDR:
210       nl_link_addr_add ((struct rtnl_addr *) obj, arg);
211       break;
212     case RTM_DELADDR:
213       nl_link_addr_del ((struct rtnl_addr *) obj, arg);
214       break;
215     case RTM_NEWLINK:
216       nl_link_add ((struct rtnl_link *) obj, arg);
217       break;
218     case RTM_DELLINK:
219       nl_link_del ((struct rtnl_link *) obj, arg);
220       break;
221     default:
222       NL_INFO ("unhandled: %s", nl_object_get_type (obj));
223       break;
224     }
225 }
226
227 static int
228 nl_route_process_msgs (void)
229 {
230   nl_main_t *nm = &nl_main;
231   nl_msg_info_t *msg_info;
232   int err, n_msgs = 0;
233
234   /* process a batch of messages. break if we hit our limit */
235   vec_foreach (msg_info, nm->nl_msg_queue)
236     {
237       if ((err = nl_msg_parse (msg_info->msg, nl_route_dispatch, msg_info)) <
238           0)
239         NL_ERROR ("Unable to parse object: %s", nl_geterror (err));
240       nlmsg_free (msg_info->msg);
241       if (++n_msgs >= nm->batch_size)
242         break;
243     }
244
245   /* remove the messages we processed from the head of the queue */
246   if (n_msgs)
247     vec_delete (nm->nl_msg_queue, n_msgs, 0);
248
249   NL_INFO ("Processed %u messages", n_msgs);
250
251   return n_msgs;
252 }
253
254 #define DAY_F64 (1.0 * (24 * 60 * 60))
255
256 static uword
257 nl_route_process (vlib_main_t *vm, vlib_node_runtime_t *node,
258                   vlib_frame_t *frame)
259 {
260   nl_main_t *nm = &nl_main;
261   uword event_type;
262   uword *event_data = 0;
263   f64 wait_time = DAY_F64;
264
265   while (1)
266     {
267       /* If we process a batch of messages and stop because we reached the
268        * batch size limit, we want to wake up after the batch delay and
269        * process more. Otherwise we just want to wait for a read event.
270        */
271       vlib_process_wait_for_event_or_clock (vm, wait_time);
272       event_type = vlib_process_get_events (vm, &event_data);
273
274       switch (event_type)
275         {
276         /* process batch of queued messages on timeout or read event signal */
277         case ~0:
278         case NL_EVENT_READ:
279           nl_route_process_msgs ();
280           wait_time = (vec_len (nm->nl_msg_queue) != 0) ?
281                         nm->batch_delay_ms * 1e-3 :
282                         DAY_F64;
283           break;
284
285         /* reopen the socket if there was an error polling/reading it */
286         case NL_EVENT_ERR:
287           lcp_nl_close_socket ();
288           lcp_nl_open_socket ();
289           break;
290
291         default:
292           NL_ERROR ("Unknown event type: %u", (u32) event_type);
293         }
294
295       vec_reset_length (event_data);
296     }
297   return frame->n_vectors;
298 }
299
300 VLIB_REGISTER_NODE (nl_route_process_node, static) = {
301   .function = nl_route_process,
302   .name = "linux-cp-netlink-process",
303   .type = VLIB_NODE_TYPE_PROCESS,
304   .process_log2_n_stack_bytes = 17,
305 };
306
307 static int
308 nl_route_cb (struct nl_msg *msg, void *arg)
309 {
310   nl_main_t *nm = &nl_main;
311   nl_msg_info_t *msg_info = 0;
312
313   /* delay processing - increment ref count and queue for later */
314   vec_add2 (nm->nl_msg_queue, msg_info, 1);
315
316   /* store a timestamp for the message */
317   msg_info->ts = vlib_time_now (vlib_get_main ());
318   msg_info->msg = msg;
319   nlmsg_get (msg);
320
321   /* notify process node */
322   vlib_process_signal_event (vlib_get_main (), nl_route_process_node.index,
323                              NL_EVENT_READ, 0);
324
325   return 0;
326 }
327
328 int
329 lcp_nl_drain_messages (void)
330 {
331   int err;
332   nl_main_t *nm = &nl_main;
333
334   /* Read until there's an error. Unless the error is ENOBUFS, which means
335    * the kernel couldn't send a message due to socket buffer overflow.
336    * Continue reading when that happens.
337    *
338    * libnl translates both ENOBUFS and ENOMEM to NLE_NOMEM. So we need to
339    * check return status and errno to make sure we should keep going.
340    */
341   while ((err = nl_recvmsgs_default (nm->sk_route)) > -1 ||
342          (err == -NLE_NOMEM && errno == ENOBUFS))
343     ;
344
345   /* If there was an error other then EAGAIN, signal process node */
346   if (err != -NLE_AGAIN)
347     vlib_process_signal_event (vlib_get_main (), nl_route_process_node.index,
348                                NL_EVENT_ERR, 0);
349
350   return err;
351 }
352
353 void
354 lcp_nl_pair_add_cb (lcp_itf_pair_t *pair)
355 {
356   lcp_nl_drain_messages ();
357 }
358
359 static clib_error_t *
360 nl_route_read_cb (clib_file_t *f)
361 {
362   int err;
363   err = lcp_nl_drain_messages ();
364   if (err < 0 && err != -NLE_AGAIN)
365     NL_ERROR ("Error reading netlink socket (fd %d): %s (%d)",
366               f->file_descriptor, nl_geterror (err), err);
367
368   return 0;
369 }
370
371 static clib_error_t *
372 nl_route_error_cb (clib_file_t *f)
373 {
374   NL_ERROR ("Error polling netlink socket (fd %d)", f->file_descriptor);
375
376   /* notify process node */
377   vlib_process_signal_event (vlib_get_main (), nl_route_process_node.index,
378                              NL_EVENT_ERR, 0);
379
380   return clib_error_return (0, "Error polling netlink socket %d",
381                             f->file_descriptor);
382 }
383
384 struct nl_cache *
385 lcp_nl_get_cache (lcp_nl_obj_t t)
386 {
387   nl_main_t *nm = &nl_main;
388
389   return nm->nl_caches[t];
390 }
391
392 /* Set the RX buffer size to be used on the netlink socket */
393 void
394 lcp_nl_set_buffer_size (u32 buf_size)
395 {
396   nl_main_t *nm = &nl_main;
397
398   nm->rx_buf_size = buf_size;
399
400   if (nm->sk_route)
401     nl_socket_set_buffer_size (nm->sk_route, nm->rx_buf_size, nm->tx_buf_size);
402 }
403
404 /* Set the batch size - maximum netlink messages to process at one time */
405 void
406 lcp_nl_set_batch_size (u32 batch_size)
407 {
408   nl_main_t *nm = &nl_main;
409
410   nm->batch_size = batch_size;
411 }
412
413 /* Set the batch delay - how long to wait in ms between processing batches */
414 void
415 lcp_nl_set_batch_delay (u32 batch_delay_ms)
416 {
417   nl_main_t *nm = &nl_main;
418
419   nm->batch_delay_ms = batch_delay_ms;
420 }
421
422 static clib_error_t *
423 lcp_itf_pair_config (vlib_main_t *vm, unformat_input_t *input)
424 {
425   u32 buf_size, batch_size, batch_delay_ms;
426
427   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
428     {
429       if (unformat (input, "nl-rx-buffer-size %u", &buf_size))
430         lcp_nl_set_buffer_size (buf_size);
431       else if (unformat (input, "nl-batch-size %u", &batch_size))
432         lcp_nl_set_batch_size (batch_size);
433       else if (unformat (input, "nl-batch-delay-ms %u", &batch_delay_ms))
434         lcp_nl_set_batch_delay (batch_delay_ms);
435       else
436         return clib_error_return (0, "invalid netlink option: %U",
437                                   format_unformat_error, input);
438     }
439
440   return NULL;
441 }
442
443 VLIB_CONFIG_FUNCTION (lcp_itf_pair_config, "linux-nl");
444
445 static void
446 lcp_nl_close_socket (void)
447 {
448   nl_main_t *nm = &nl_main;
449
450   /* delete existing fd from epoll fd set */
451   if (nm->clib_file_index != ~0)
452     {
453       clib_file_main_t *fm = &file_main;
454       clib_file_t *f = clib_file_get (fm, nm->clib_file_index);
455
456       if (f)
457         {
458           NL_INFO ("Stopping poll of fd %u", f->file_descriptor);
459           fm->file_update (f, UNIX_FILE_UPDATE_DELETE);
460         }
461       else
462         /* stored index was not a valid file, reset stored index to ~0 */
463         nm->clib_file_index = ~0;
464     }
465
466   /* If we already created a socket, close/free it */
467   if (nm->sk_route)
468     {
469       NL_INFO ("Closing netlink socket %d", nl_socket_get_fd (nm->sk_route));
470       nl_socket_free (nm->sk_route);
471       nm->sk_route = NULL;
472     }
473 }
474
475 static void
476 lcp_nl_open_socket (void)
477 {
478   nl_main_t *nm = &nl_main;
479   int dest_ns_fd, curr_ns_fd;
480
481   /* Allocate a new socket for both routes and acls
482    * Notifications do not use sequence numbers, disable sequence number
483    * checking.
484    * Define a callback function, which will be called for each notification
485    * received
486    */
487   nm->sk_route = nl_socket_alloc ();
488   nl_socket_disable_seq_check (nm->sk_route);
489
490   dest_ns_fd = lcp_get_default_ns_fd ();
491   if (dest_ns_fd)
492     {
493       curr_ns_fd = open ("/proc/self/ns/net", O_RDONLY);
494       setns (dest_ns_fd, CLONE_NEWNET);
495     }
496
497   nl_connect (nm->sk_route, NETLINK_ROUTE);
498
499   if (dest_ns_fd && curr_ns_fd >= 0)
500     {
501       setns (curr_ns_fd, CLONE_NEWNET);
502       close (curr_ns_fd);
503     }
504
505   /* Subscribe to all the 'routing' notifications on the route socket */
506   nl_socket_add_memberships (nm->sk_route, RTNLGRP_LINK, RTNLGRP_IPV6_IFADDR,
507                              RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE,
508                              RTNLGRP_IPV6_ROUTE, RTNLGRP_NEIGH, RTNLGRP_NOTIFY,
509 #ifdef RTNLGRP_MPLS_ROUTE /* not defined on CentOS/RHEL 7 */
510                              RTNLGRP_MPLS_ROUTE,
511 #endif
512                              RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, 0);
513
514   /* Set socket in nonblocking mode and increase buffer sizes */
515   nl_socket_set_nonblocking (nm->sk_route);
516   nl_socket_set_buffer_size (nm->sk_route, nm->rx_buf_size, nm->tx_buf_size);
517
518   if (nm->clib_file_index == ~0)
519     {
520       clib_file_t rt_file = {
521         .read_function = nl_route_read_cb,
522         .error_function = nl_route_error_cb,
523         .file_descriptor = nl_socket_get_fd (nm->sk_route),
524         .description = format (0, "linux-cp netlink route socket"),
525       };
526
527       nm->clib_file_index = clib_file_add (&file_main, &rt_file);
528       NL_INFO ("Added file %u", nm->clib_file_index);
529     }
530   else
531     /* clib file already created and socket was closed due to error */
532     {
533       clib_file_main_t *fm = &file_main;
534       clib_file_t *f = clib_file_get (fm, nm->clib_file_index);
535
536       f->file_descriptor = nl_socket_get_fd (nm->sk_route);
537       fm->file_update (f, UNIX_FILE_UPDATE_ADD);
538       NL_INFO ("Starting poll of %d", f->file_descriptor);
539     }
540
541   nl_socket_modify_cb (nm->sk_route, NL_CB_VALID, NL_CB_CUSTOM, nl_route_cb,
542                        NULL);
543   NL_INFO ("Opened netlink socket %d", nl_socket_get_fd (nm->sk_route));
544 }
545
546 #include <vnet/plugin/plugin.h>
547 clib_error_t *
548 lcp_nl_init (vlib_main_t *vm)
549 {
550   nl_main_t *nm = &nl_main;
551   lcp_itf_pair_vft_t nl_itf_pair_vft = {
552     .pair_add_fn = lcp_nl_pair_add_cb,
553   };
554
555   nm->clib_file_index = ~0;
556   nm->nl_logger = vlib_log_register_class ("nl", "nl");
557
558   lcp_nl_open_socket ();
559   lcp_itf_pair_register_vft (&nl_itf_pair_vft);
560
561   return (NULL);
562 }
563
564 VLIB_INIT_FUNCTION (lcp_nl_init) = {
565   .runs_after = VLIB_INITS ("lcp_interface_init", "tuntap_init",
566                             "ip_neighbor_init"),
567 };
568
569 #include <vpp/app/version.h>
570 VLIB_PLUGIN_REGISTER () = {
571   .version = VPP_BUILD_VER,
572   .description = "linux Control Plane - Netlink listener",
573   .default_disabled = 1,
574 };
575
576 /*
577  * fd.io coding-style-patch-verification: ON
578  *
579  * Local Variables:
580  * eval: (c-set-style "gnu")
581  * End:
582  */