diff options
Diffstat (limited to 'extras/router-plugin/devices/rtnetlink/rtnl.c')
-rw-r--r-- | extras/router-plugin/devices/rtnetlink/rtnl.c | 661 |
1 files changed, 0 insertions, 661 deletions
diff --git a/extras/router-plugin/devices/rtnetlink/rtnl.c b/extras/router-plugin/devices/rtnetlink/rtnl.c deleted file mode 100644 index 9c4757b17..000000000 --- a/extras/router-plugin/devices/rtnetlink/rtnl.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include <sched.h> - -#include <vlib/unix/unix.h> -#include <vlib/vlib.h> -#include <vppinfra/error.h> - -#include <fcntl.h> -#include <float.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <sys/socket.h> - -#include <errno.h> -#include <string.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include "netns.h" -#include "rtnl.h" - -#undef DBL_MAX -#define DBL_MAX 1000000000.0 - -typedef enum -{ - RTNL_E_OPEN, - RTNL_E_CLOSE, - RTNL_E_READ, -} rtnl_event_t; - -typedef enum -{ - RTNL_S_INIT, - RTNL_S_SYNC, - RTNL_S_READY, -} rtnl_state_t; - -typedef enum -{ - RTNL_SS_OPENING, - RTNL_SS_LINK, - RTNL_SS_ADDR, - RTNL_SS_ROUTE4, - RTNL_SS_ROUTE6, - RTNL_SS_NEIGH, -} rtnl_sync_state_t; - -typedef struct -{ - rtnl_stream_t stream; - rtnl_state_t state; - rtnl_sync_state_t sync_state; - int ns_fd; - int rtnl_socket; - u32 unix_index; - u32 rtnl_seq; - f64 timeout; -} rtnl_ns_t; - -typedef struct -{ - f64 now; - rtnl_ns_t *streams; -} rtnl_main_t; - -static rtnl_main_t rtnl_main; -static vlib_node_registration_t rtnl_process_node; - -#define RTNL_BUFFSIZ 16384 -#define RTNL_DUMP_TIMEOUT 1 - -static inline u32 -grpmask (u32 g) -{ - ASSERT (g <= 31); - if (g) - { - return 1 << (g - 1); - } - else - return 0; -} - -u8 * -format_rtnl_nsname2path (u8 *s, va_list *args) -{ - char *nsname = va_arg (*args, char *); - if (!nsname || !strlen (nsname)) - { - return format (s, "/proc/self/ns/net"); - } - else if (strpbrk (nsname, "/") != NULL) - { - return format (s, "%s", nsname); - } - else - { - return format (s, "/var/run/netns/%s", nsname); - } -} - -static_always_inline void -rtnl_schedule_timeout (rtnl_ns_t *ns, f64 when) -{ - ns->timeout = when; -} - -static_always_inline void -rtnl_cancel_timeout (rtnl_ns_t *ns) -{ - ns->timeout = DBL_MAX; -} - -static clib_error_t * -rtnl_read_cb (struct clib_file *f) -{ - rtnl_main_t *rm = &rtnl_main; - vlib_main_t *vm = vlib_get_main (); - rtnl_ns_t *ns = &rm->streams[f->private_data]; - vlib_process_signal_event (vm, rtnl_process_node.index, RTNL_E_READ, - (uword) (ns - rm->streams)); - return 0; -} - -int -rtnl_dump_request (rtnl_ns_t *ns, int type, void *req, size_t len) -{ - struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; - struct nlmsghdr nlh = { - .nlmsg_len = NLMSG_LENGTH (len), - .nlmsg_type = type, - .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, - .nlmsg_pid = 0, - .nlmsg_seq = ++ns->rtnl_seq, - }; - struct iovec iov[2] = { { .iov_base = &nlh, .iov_len = sizeof (nlh) }, - { .iov_base = req, .iov_len = len } }; - struct msghdr msg = { - .msg_name = &nladdr, - .msg_namelen = sizeof (nladdr), - .msg_iov = iov, - .msg_iovlen = 2, - }; - if (sendmsg (ns->rtnl_socket, &msg, 0) < 0) - { - clib_warning ("sendmsg error: %s", strerror (errno)); - return -1; - } - return 0; -} - -static void -rtnl_socket_close (rtnl_ns_t *ns) -{ - clib_file_del (&file_main, &file_main.file_pool[ns->unix_index]); - close (ns->rtnl_socket); -} - -struct rtnl_thread_exec -{ - int fd; - void *(*fn) (void *); - void *arg; - void **ret; -}; - -static void * -rtnl_exec_in_thread_fn (void *p) -{ - struct rtnl_thread_exec *ex = (struct rtnl_thread_exec *) p; - if (setns (ex->fd, 0)) - return (void *) ((uword) (-errno)); - - *ex->ret = ex->fn (ex->arg); - return NULL; -} - -static int -rtnl_exec_in_namespace_byfd (int fd, void *(*fn) (void *), void *arg, - void **ret) -{ - pthread_t thread; - void *thread_ret; - struct rtnl_thread_exec ex = { .fd = fd, .fn = fn, .arg = arg, .ret = ret }; - if (pthread_create (&thread, NULL, rtnl_exec_in_thread_fn, &ex)) - return -errno; - - if (pthread_join (thread, &thread_ret)) - return -errno; - - if (thread_ret) - return (int) ((uword) thread_ret); - - return 0; -} - -int -rtnl_exec_in_namespace (u32 stream_index, void *(*fn) (void *), void *arg, - void **ret) -{ - rtnl_main_t *rm = &rtnl_main; - if (pool_is_free_index (rm->streams, stream_index)) - return -EBADR; - - rtnl_ns_t *ns = pool_elt_at_index (rm->streams, stream_index); - return rtnl_exec_in_namespace_byfd (ns->ns_fd, fn, arg, ret); -} - -int -rtnl_exec_in_namespace_by_name (char *nsname, void *(*fn) (void *), void *arg, - void **ret) -{ - int fd; - u8 *s = format ((u8 *) 0, "%U", format_rtnl_nsname2path, nsname); - - if ((fd = open ((char *) s, O_RDONLY)) < 0) - { - vec_free (s); - return -errno; - } - - int r = rtnl_exec_in_namespace_byfd (fd, fn, arg, ret); - vec_free (s); - close (fd); - return r; -} - -/* this function is run by the second thread */ -static void * -rtnl_thread_fn (void *p) -{ - rtnl_ns_t *ns = (rtnl_ns_t *) p; - if (setns (ns->ns_fd, 0)) - { - clib_warning ("setns(%d, %d) error %d", ns->ns_fd, CLONE_NEWNET, errno); - return (void *) -1; - } - - if ((ns->rtnl_socket = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) - { - clib_warning ("Cannot open socket"); - return (void *) -2; - } - - return NULL; -} - -static int -rtnl_socket_open (rtnl_ns_t *ns) -{ - rtnl_main_t *rm = &rtnl_main; - pthread_t thread; - void *thread_ret; - if (pthread_create (&thread, NULL, rtnl_thread_fn, ns)) - { - clib_warning ("Can't create opening thread"); - return -1; - } - - if (pthread_join (thread, &thread_ret)) - { - clib_warning ("Can't join opening thread"); - return -2; - } - - if (thread_ret) - { - clib_warning ("Could not open netlink socket"); - return -3; - } - - struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, - .nl_pad = 0, - .nl_pid = 0, - /*add mpls message group*/ - .nl_groups = grpmask (RTNLGRP_LINK) | grpmask (RTNLGRP_IPV6_IFADDR) | - grpmask (RTNLGRP_IPV4_IFADDR) | grpmask (RTNLGRP_IPV4_ROUTE) | - grpmask (RTNLGRP_IPV6_ROUTE) | grpmask (RTNLGRP_NEIGH) | - grpmask (RTNLGRP_NOTIFY) /* | grpmask(RTNLGRP_MPLS_ROUTE)*/, - }; - - if (bind (ns->rtnl_socket, (struct sockaddr *) &addr, sizeof (addr))) - { - close (ns->rtnl_socket); - return -3; - } - - clib_file_t template = { 0 }; - template.read_function = rtnl_read_cb; - template.file_descriptor = ns->rtnl_socket; - template.private_data = (uword) (ns - rm->streams); - ns->unix_index = clib_file_add (&file_main, &template); - return 0; -} - -static int -rtnl_rcv_error (rtnl_ns_t *ns, struct nlmsghdr *hdr, int *error) -{ - struct nlmsgerr *err = NLMSG_DATA (hdr); - size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN (sizeof (*hdr)); - if (datalen < sizeof (*err)) - return -1; - - *error = err->error; - return 0; -} - -static void -rtnl_sync_reset (rtnl_ns_t *ns) -{ - if (ns->sync_state == RTNL_SS_OPENING) - return; - - rtnl_socket_close (ns); - ns->sync_state = RTNL_SS_OPENING; -} - -static void -rtnl_sync_done (rtnl_ns_t *ns) -{ - rtnl_main_t *rm = &rtnl_main; - struct ifaddrmsg addrmsg; - struct rtmsg rtmsg; - struct ndmsg ndmsg; - switch (ns->sync_state) - { - case RTNL_SS_OPENING: - // Cannot happen here - break; - case RTNL_SS_LINK: - memset (&addrmsg, 0, sizeof (addrmsg)); - addrmsg.ifa_family = AF_UNSPEC; - if (rtnl_dump_request (ns, RTM_GETADDR, &addrmsg, sizeof (addrmsg))) - { - rtnl_sync_reset (ns); - rtnl_schedule_timeout (ns, rm->now + 1); - return; - } - rtnl_schedule_timeout (ns, rm->now + RTNL_DUMP_TIMEOUT); - ns->sync_state = RTNL_SS_ADDR; - break; - case RTNL_SS_ADDR: - case RTNL_SS_ROUTE4: - memset (&rtmsg, 0, sizeof (rtmsg)); - rtmsg.rtm_family = (ns->sync_state == RTNL_SS_ADDR) ? AF_INET : AF_INET6; - rtmsg.rtm_table = RT_TABLE_UNSPEC; - if (rtnl_dump_request (ns, RTM_GETROUTE, &rtmsg, sizeof (rtmsg))) - { - rtnl_sync_reset (ns); - rtnl_schedule_timeout (ns, rm->now + 1); - return; - } - rtnl_schedule_timeout (ns, rm->now + RTNL_DUMP_TIMEOUT); - ns->sync_state = - (ns->sync_state == RTNL_SS_ADDR) ? RTNL_SS_ROUTE4 : RTNL_SS_ROUTE6; - break; - case RTNL_SS_ROUTE6: - memset (&ndmsg, 0, sizeof (ndmsg)); - ndmsg.ndm_family = AF_UNSPEC; - if (rtnl_dump_request (ns, RTM_GETNEIGH, &ndmsg, sizeof (ndmsg))) - { - rtnl_sync_reset (ns); - rtnl_schedule_timeout (ns, rm->now + 1); - return; - } - rtnl_schedule_timeout (ns, rm->now + RTNL_DUMP_TIMEOUT); - ns->sync_state = RTNL_SS_NEIGH; - break; - case RTNL_SS_NEIGH: - ns->state = RTNL_S_READY; - ns->sync_state = 0; - rtnl_cancel_timeout (ns); - break; - } -} - -static void -rtnl_sync_timeout (rtnl_ns_t *ns) -{ - rtnl_main_t *rm = &rtnl_main; - struct ifinfomsg imsg = {}; - switch (ns->sync_state) - { - case RTNL_SS_OPENING: - if (rtnl_socket_open (ns)) - { - rtnl_schedule_timeout (ns, rm->now + 10); - return; - } - imsg.ifi_family = AF_UNSPEC; - if (rtnl_dump_request (ns, RTM_GETLINK, &imsg, sizeof (imsg))) - { - rtnl_sync_reset (ns); - rtnl_schedule_timeout (ns, rm->now + 10); - } - ns->sync_state = RTNL_SS_LINK; - rtnl_schedule_timeout (ns, rm->now + 2); - break; - case RTNL_SS_LINK: - case RTNL_SS_ADDR: - case RTNL_SS_ROUTE4: - case RTNL_SS_ROUTE6: - case RTNL_SS_NEIGH: - // Timeout happened while synchronizing - rtnl_sync_reset (ns); - rtnl_schedule_timeout (ns, rm->now + 1); - break; - } -} - -static int -rtnl_ns_recv (rtnl_ns_t *ns, struct nlmsghdr *hdr) -{ - rtnl_main_t *rm = &rtnl_main; - int ret, error = 0; - - if (ns->state == RTNL_S_SYNC && ((hdr->nlmsg_flags & RTM_F_NOTIFY) || - (hdr->nlmsg_seq != (ns->rtnl_seq)))) - { - clib_warning ( - "Received notification while in sync. Restart synchronization."); - rtnl_sync_reset (ns); - rtnl_schedule_timeout (ns, rm->now); - } - - switch (hdr->nlmsg_type) - { - case NLMSG_DONE: - rtnl_sync_done (ns); - break; - case NLMSG_ERROR: - if ((ret = rtnl_rcv_error (ns, hdr, &error))) - return ret; - break; - case RTM_NEWROUTE: - case RTM_DELROUTE: - case RTM_NEWLINK: - case RTM_DELLINK: - case RTM_NEWADDR: - case RTM_DELADDR: - case RTM_NEWNEIGH: - case RTM_DELNEIGH: - if (ns->stream.recv_message) - ns->stream.recv_message (hdr, ns->stream.opaque); - break; - default: - clib_warning ("Unknown rtnetlink type %d", hdr->nlmsg_type); - break; - } - return 0; -} - -static void -rtnl_process_open (rtnl_ns_t *ns) -{ - rtnl_main_t *rm = &rtnl_main; - if (ns->state != RTNL_S_INIT) - return; - - ns->state = RTNL_S_SYNC; - ns->sync_state = RTNL_SS_OPENING; - rtnl_schedule_timeout (ns, rm->now); -} - -static void -rtnl_process_close (rtnl_ns_t *ns) -{ - rtnl_main_t *rm = &rtnl_main; - if (ns->state == RTNL_S_INIT) - return; - - rtnl_socket_close (ns); - close (ns->ns_fd); - pool_put (rm->streams, ns); -} - -static int -rtnl_process_read (rtnl_ns_t *ns) -{ - uint8_t buff[RTNL_BUFFSIZ]; - ssize_t len; - struct nlmsghdr *hdr; - while (1) - { - if ((len = recv (ns->rtnl_socket, buff, RTNL_BUFFSIZ, MSG_DONTWAIT)) < 0) - { - if (errno != EAGAIN) - { - clib_warning ("rtnetlink recv error (%d) [%s]: %s", - ns->rtnl_socket, ns->stream.name, - strerror (errno)); - return -1; - } - return 0; - } - - for (hdr = (struct nlmsghdr *) buff; len > 0; - len -= NLMSG_ALIGN (hdr->nlmsg_len), - hdr = (struct nlmsghdr *) (((uint8_t *) hdr) + - NLMSG_ALIGN (hdr->nlmsg_len))) - { - if ((sizeof (*hdr) > (size_t) len) || - (hdr->nlmsg_len > (size_t) len)) - { - clib_warning ("rtnetlink buffer too small (%d Vs %d)", - (int) hdr->nlmsg_len, (int) len); - return -1; - } - if (rtnl_ns_recv (ns, hdr)) - return -1; - } - } - return 0; -} - -static void -rtnl_process_timeout (rtnl_ns_t *ns) -{ - switch (ns->state) - { - case RTNL_S_SYNC: - rtnl_sync_timeout (ns); - break; - case RTNL_S_INIT: - case RTNL_S_READY: - clib_warning ("Should not happen"); - break; - } -} - -static uword -rtnl_process (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) -{ - rtnl_main_t *rm = &rtnl_main; - uword event_type; - uword *event_data = 0; - rm->now = vlib_time_now (vm); - f64 timeout = DBL_MAX; - rtnl_ns_t *ns; - - // Setting up - while (1) - { - vlib_process_wait_for_event_or_clock (vm, timeout - rm->now); - event_type = vlib_process_get_events (vm, &event_data); - rm->now = vlib_time_now (vm); - - if (event_type == ~0) - { // Clock event or no event - pool_foreach (ns, rm->streams, { - if (ns->timeout < rm->now) - { - ns->timeout = DBL_MAX; - rtnl_process_timeout (ns); - } - }) - ; - } - else - { - rtnl_ns_t *ns; - uword *d; - vec_foreach (d, event_data) - { - ns = &rm->streams[d[0]]; - switch (event_type) - { - case RTNL_E_CLOSE: - rtnl_process_close (ns); - break; - case RTNL_E_OPEN: - rtnl_process_open (ns); - break; - case RTNL_E_READ: - rtnl_process_read (ns); - break; - } - } - } - - vec_reset_length (event_data); - - timeout = DBL_MAX; - pool_foreach (ns, rm->streams, { - if (ns->timeout < timeout) - timeout = ns->timeout; - }) - ; - } - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (rtnl_process_node, static) = { - .function = rtnl_process, - .name = "rtnl-process", - .type = VLIB_NODE_TYPE_PROCESS, -}; - -u32 -rtnl_stream_open (rtnl_stream_t *template) -{ - vlib_main_t *vm = vlib_get_main (); - rtnl_main_t *rm = &rtnl_main; - rtnl_ns_t *ns; - int fd; - u8 *s = format ((u8 *) 0, "%U", format_rtnl_nsname2path, template->name); - vec_add1 (s, 0); - - if ((fd = open ((char *) s, O_RDONLY)) < 0) - { - clib_unix_warning ("open stream %s: ", s); - vec_free (s); - return ~0; - } - - vec_free (s); - pool_get (rm->streams, ns); - ns->state = RTNL_S_INIT; - ns->ns_fd = fd; - ns->stream = *template; - vlib_process_signal_event (vm, rtnl_process_node.index, RTNL_E_OPEN, - (uword) (ns - rm->streams)); - return ns - rm->streams; -} - -void -rtnl_stream_close (u32 stream_index) -{ - vlib_main_t *vm = vlib_get_main (); - rtnl_main_t *rm = &rtnl_main; - ASSERT (!pool_is_free_index (rm->streams, stream_index)); - vlib_process_signal_event (vm, rtnl_process_node.index, RTNL_E_CLOSE, - stream_index); -} - -clib_error_t * -rtnl_init (vlib_main_t *vm) -{ - rtnl_main_t *rm = &rtnl_main; - rm->streams = 0; - return 0; -} - -VLIB_INIT_FUNCTION (rtnl_init); |