]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/kernel_netlink.c
*: style for EC replacements
[mirror_frr.git] / zebra / kernel_netlink.c
index d9c663184531acb27f873f4ac1664bed917ceb93..1b713cc8cd62b81a2bc3a06483239b8a5f6cc25b 100644 (file)
 
 #include <zebra.h>
 
+#if defined(HANDLE_NETLINK_FUZZING)
+#include <stdio.h>
+#include <string.h>
+#endif /* HANDLE_NETLINK_FUZZING */
+
 #ifdef HAVE_NETLINK
 
 #include "linklist.h"
@@ -36,6 +41,7 @@
 #include "nexthop.h"
 #include "vrf.h"
 #include "mpls.h"
+#include "lib_errors.h"
 
 #include "zebra/zserv.h"
 #include "zebra/zebra_ns.h"
@@ -46,6 +52,7 @@
 #include "zebra/rt_netlink.h"
 #include "zebra/if_netlink.h"
 #include "zebra/rule_netlink.h"
+#include "zebra/zebra_errors.h"
 
 #ifndef SO_RCVBUFFORCE
 #define SO_RCVBUFFORCE  (33)
@@ -142,9 +149,8 @@ int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup)
         * received some other message in an unexpected
         * way.
         */
-       zlog_err("%s: ignoring message type 0x%04x(%s) NS %u",
-                __PRETTY_FUNCTION__, h->nlmsg_type,
-                nl_msg_type_to_str(h->nlmsg_type), ns_id);
+       zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u", __func__,
+                  h->nlmsg_type, nl_msg_type_to_str(h->nlmsg_type), ns_id);
        return 0;
 }
 
@@ -157,31 +163,33 @@ static int netlink_recvbuf(struct nlsock *nl, uint32_t newsize)
 
        ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
        if (ret < 0) {
-               zlog_err("Can't get %s receive buffer size: %s", nl->name,
-                        safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET,
+                            "Can't get %s receive buffer size: %s", nl->name,
+                            safe_strerror(errno));
                return -1;
        }
 
        /* Try force option (linux >= 2.6.14) and fall back to normal set */
-       if (zserv_privs.change(ZPRIVS_RAISE))
-               zlog_err("routing_socket: Can't raise privileges");
-       ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize,
-                        sizeof(nl_rcvbufsize));
-       if (zserv_privs.change(ZPRIVS_LOWER))
-               zlog_err("routing_socket: Can't lower privileges");
+       frr_elevate_privs(&zserv_privs) {
+               ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE,
+                                &nl_rcvbufsize,
+                                sizeof(nl_rcvbufsize));
+       }
        if (ret < 0)
                ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF,
                                 &nl_rcvbufsize, sizeof(nl_rcvbufsize));
        if (ret < 0) {
-               zlog_err("Can't set %s receive buffer size: %s", nl->name,
-                        safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET,
+                            "Can't set %s receive buffer size: %s", nl->name,
+                            safe_strerror(errno));
                return -1;
        }
 
        ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
        if (ret < 0) {
-               zlog_err("Can't get %s receive buffer size: %s", nl->name,
-                        safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET,
+                            "Can't get %s receive buffer size: %s", nl->name,
+                            safe_strerror(errno));
                return -1;
        }
 
@@ -198,33 +206,26 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
        struct sockaddr_nl snl;
        int sock;
        int namelen;
-       int save_errno;
 
-       if (zserv_privs.change(ZPRIVS_RAISE)) {
-               zlog_err("Can't raise privileges");
-               return -1;
-       }
-
-       sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
-       if (sock < 0) {
-               zlog_err("Can't open %s socket: %s", nl->name,
-                        safe_strerror(errno));
-               return -1;
-       }
+       frr_elevate_privs(&zserv_privs) {
+               sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
+               if (sock < 0) {
+                       zlog_err("Can't open %s socket: %s", nl->name,
+                                safe_strerror(errno));
+                       return -1;
+               }
 
-       memset(&snl, 0, sizeof snl);
-       snl.nl_family = AF_NETLINK;
-       snl.nl_groups = groups;
+               memset(&snl, 0, sizeof snl);
+               snl.nl_family = AF_NETLINK;
+               snl.nl_groups = groups;
 
-       /* Bind the socket to the netlink structure for anything. */
-       ret = bind(sock, (struct sockaddr *)&snl, sizeof snl);
-       save_errno = errno;
-       if (zserv_privs.change(ZPRIVS_LOWER))
-               zlog_err("Can't lower privileges");
+               /* Bind the socket to the netlink structure for anything. */
+               ret = bind(sock, (struct sockaddr *)&snl, sizeof snl);
+       }
 
        if (ret < 0) {
                zlog_err("Can't bind %s socket to group 0x%x: %s", nl->name,
-                        snl.nl_groups, safe_strerror(save_errno));
+                        snl.nl_groups, safe_strerror(errno));
                close(sock);
                return -1;
        }
@@ -233,8 +234,8 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
        namelen = sizeof snl;
        ret = getsockname(sock, (struct sockaddr *)&snl, (socklen_t *)&namelen);
        if (ret < 0 || namelen != sizeof snl) {
-               zlog_err("Can't get %s socket name: %s", nl->name,
-                        safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET, "Can't get %s socket name: %s",
+                            nl->name, safe_strerror(errno));
                close(sock);
                return -1;
        }
@@ -285,7 +286,8 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
                 * this message type or not ask for
                 * it to be sent up to us
                 */
-               zlog_err("Unknown netlink nlmsg_type %s(%d) vrf %u\n",
+               flog_err(EC_ZEBRA_UNKNOWN_NLMSG,
+                        "Unknown netlink nlmsg_type %s(%d) vrf %u\n",
                         nl_msg_type_to_str(h->nlmsg_type), h->nlmsg_type,
                         ns_id);
                break;
@@ -293,6 +295,81 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
        return 0;
 }
 
+#if defined(HANDLE_NETLINK_FUZZING)
+/* Using globals here to avoid adding function parameters */
+
+/* Keep distinct filenames for netlink fuzzy collection */
+static unsigned int netlink_file_counter = 1;
+
+/* File name to read fuzzed netlink from */
+static char netlink_fuzz_file[MAXPATHLEN] = "";
+
+/* Flag for whether to read from file or not */
+bool netlink_read;
+
+/**
+ * netlink_read_init() - Starts the message parser
+ * @fname:      Filename to read.
+ */
+void netlink_read_init(const char *fname)
+{
+       snprintf(netlink_fuzz_file, MAXPATHLEN, "%s", fname);
+       /* Creating this fake socket for testing purposes */
+       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+
+       netlink_parse_info(netlink_information_fetch, &zns->netlink, zns, 1, 0);
+}
+
+/**
+ * netlink_write_incoming() - Writes all data received from netlink to a file
+ * @buf:        Data from netlink.
+ * @size:       Size of data.
+ * @counter:    Counter for keeping filenames distinct.
+ */
+static void netlink_write_incoming(const char *buf, const unsigned int size,
+                                  unsigned int counter)
+{
+       char fname[MAXPATHLEN];
+       FILE *f;
+
+       snprintf(fname, MAXPATHLEN, "%s/%s_%u", DAEMON_VTY_DIR, "netlink",
+                counter);
+       frr_elevate_privs(&zserv_privs) {
+               f = fopen(fname, "w");
+       }
+       if (f) {
+               fwrite(buf, 1, size, f);
+               fclose(f);
+       }
+}
+
+/**
+ * netlink_read_file() - Reads netlink data from file
+ * @buf:        Netlink buffer being overwritten.
+ * @fname:      File name to read from.
+ *
+ * Return:      Size of file.
+ */
+static long netlink_read_file(char *buf, const char *fname)
+{
+       FILE *f;
+       long file_bytes = -1;
+
+       frr_elevate_privs(&zserv_privs) {
+               f = fopen(fname, "r");
+       }
+       if (f) {
+               fseek(f, 0, SEEK_END);
+               file_bytes = ftell(f);
+               rewind(f);
+               fread(buf, NL_RCV_PKT_BUF_SIZE, 1, f);
+               fclose(f);
+       }
+       return file_bytes;
+}
+
+#endif /* HANDLE_NETLINK_FUZZING */
+
 static int kernel_read(struct thread *thread)
 {
        struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread);
@@ -376,8 +453,8 @@ static void netlink_install_filter(int sock, __u32 pid)
 
        if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))
            < 0)
-               zlog_warn("Can't install socket filter: %s\n",
-                         safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET, "Can't install socket filter: %s\n",
+                            safe_strerror(errno));
 }
 
 void netlink_parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta,
@@ -390,8 +467,20 @@ void netlink_parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta,
        }
 }
 
-int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, void *data,
-             unsigned int alen)
+/**
+ * netlink_parse_rtattr_nested() - Parses a nested route attribute
+ * @tb:         Pointer to array for storing rtattr in.
+ * @max:        Max number to store.
+ * @rta:        Pointer to rtattr to look for nested items in.
+ */
+void netlink_parse_rtattr_nested(struct rtattr **tb, int max,
+                                struct rtattr *rta)
+{
+       netlink_parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta));
+}
+
+int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
+             const void *data, unsigned int alen)
 {
        int len;
        struct rtattr *rta;
@@ -415,8 +504,8 @@ int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, void *data,
        return 0;
 }
 
-int rta_addattr_l(struct rtattr *rta, unsigned int maxlen, int type, void *data,
-                 unsigned int alen)
+int rta_addattr_l(struct rtattr *rta, unsigned int maxlen, int type,
+                 const void *data, unsigned int alen)
 {
        unsigned int len;
        struct rtattr *subrta;
@@ -552,8 +641,8 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h)
                         * but noticing it for later.
                         */
                        err_nlh = &err->msg;
-                       zlog_warn("%s: Received %d extended Ack",
-                                 __PRETTY_FUNCTION__, err_nlh->nlmsg_type);
+                       zlog_debug("%s: Received %d extended Ack",
+                                  __PRETTY_FUNCTION__, err_nlh->nlmsg_type);
                }
        }
 
@@ -563,7 +652,8 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h)
                if (is_err)
                        zlog_err("Extended Error: %s", msg);
                else
-                       zlog_warn("Extended Warning: %s", msg);
+                       flog_warn(EC_ZEBRA_NETLINK_EXTENDED_WARNING,
+                                 "Extended Warning: %s", msg);
        }
 }
 
@@ -602,13 +692,25 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
                if (count && read_in >= count)
                        return 0;
 
+#if defined(HANDLE_NETLINK_FUZZING)
+               /* Check if reading and filename is set */
+               if (netlink_read && '\0' != netlink_fuzz_file[0]) {
+                       zlog_debug("Reading netlink fuzz file");
+                       status = netlink_read_file(buf, netlink_fuzz_file);
+                       snl.nl_pid = 0;
+               } else {
+                       status = recvmsg(nl->sock, &msg, 0);
+               }
+#else
                status = recvmsg(nl->sock, &msg, 0);
+#endif /* HANDLE_NETLINK_FUZZING */
                if (status < 0) {
                        if (errno == EINTR)
                                continue;
                        if (errno == EWOULDBLOCK || errno == EAGAIN)
                                break;
-                       zlog_err("%s recvmsg overrun: %s", nl->name,
+                       flog_err(EC_ZEBRA_RECVMSG_OVERRUN,
+                                "%s recvmsg overrun: %s", nl->name,
                                 safe_strerror(errno));
                        /*
                         *  In this case we are screwed.
@@ -620,12 +722,13 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
                }
 
                if (status == 0) {
-                       zlog_err("%s EOF", nl->name);
+                       flog_err_sys(EC_LIB_SOCKET, "%s EOF", nl->name);
                        return -1;
                }
 
                if (msg.msg_namelen != sizeof snl) {
-                       zlog_err("%s sender address length error: length %d",
+                       flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+                                "%s sender address length error: length %d",
                                 nl->name, msg.msg_namelen);
                        return -1;
                }
@@ -636,9 +739,17 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
                        zlog_hexdump(buf, status);
                }
 
+#if defined(HANDLE_NETLINK_FUZZING)
+               if (!netlink_read) {
+                       zlog_debug("Writing incoming netlink message");
+                       netlink_write_incoming(buf, status,
+                                              netlink_file_counter++);
+               }
+#endif /* HANDLE_NETLINK_FUZZING */
+
                read_in++;
                for (h = (struct nlmsghdr *)buf;
-                    NLMSG_OK(h, (unsigned int)status);
+                    (status >= 0 && NLMSG_OK(h, (unsigned int)status));
                     h = NLMSG_NEXT(h, status)) {
                        /* Finish of reading. */
                        if (h->nlmsg_type == NLMSG_DONE)
@@ -689,6 +800,14 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
                                        continue;
                                }
 
+                               if (h->nlmsg_len
+                                   < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                                       flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+                                                "%s error: message truncated",
+                                                nl->name);
+                                       return -1;
+                               }
+
                                /* Deal with errors that occur because of races
                                 * in link handling */
                                if (nl == &zns->netlink_cmd
@@ -737,7 +856,8 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
                                                        err->msg.nlmsg_seq,
                                                        err->msg.nlmsg_pid);
                                } else
-                                       zlog_err(
+                                       flog_err(
+                                               EC_ZEBRA_UNEXPECTED_MESSAGE,
                                                "%s error: %s, type=%s(%u), seq=%u, pid=%u",
                                                nl->name,
                                                safe_strerror(-errnum),
@@ -763,26 +883,28 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
                         * other actors besides the kernel
                         */
                        if (snl.nl_pid != 0) {
-                               zlog_err("Ignoring message from pid %u",
-                                        snl.nl_pid);
+                               zlog_debug("Ignoring message from pid %u",
+                                          snl.nl_pid);
                                continue;
                        }
 
                        error = (*filter)(h, zns->ns_id, startup);
                        if (error < 0) {
-                               zlog_err("%s filter function error", nl->name);
-                               zlog_backtrace(LOG_ERR);
+                               zlog_debug("%s filter function error",
+                                          nl->name);
                                ret = error;
                        }
                }
 
                /* After error care. */
                if (msg.msg_flags & MSG_TRUNC) {
-                       zlog_err("%s error: message truncated", nl->name);
+                       flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+                                "%s error: message truncated", nl->name);
                        continue;
                }
                if (status) {
-                       zlog_err("%s error: data remnant size %d", nl->name,
+                       flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+                                "%s error: data remnant size %d", nl->name,
                                 status);
                        return -1;
                }
@@ -807,11 +929,11 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
                 struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns,
                 int startup)
 {
-       int status;
+       int status = 0;
        struct sockaddr_nl snl;
        struct iovec iov;
        struct msghdr msg;
-       int save_errno;
+       int save_errno = 0;
 
        memset(&snl, 0, sizeof snl);
        memset(&iov, 0, sizeof iov);
@@ -829,9 +951,6 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
        n->nlmsg_seq = ++nl->seq;
        n->nlmsg_pid = nl->snl.nl_pid;
 
-       /* Request an acknowledgement by setting NLM_F_ACK */
-       n->nlmsg_flags |= NLM_F_ACK;
-
        if (IS_ZEBRA_DEBUG_KERNEL)
                zlog_debug(
                        "netlink_talk: %s type %s(%u), len=%d seq=%u flags 0x%x",
@@ -840,12 +959,10 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
                        n->nlmsg_flags);
 
        /* Send message to netlink interface. */
-       if (zserv_privs.change(ZPRIVS_RAISE))
-               zlog_err("Can't raise privileges");
-       status = sendmsg(nl->sock, &msg, 0);
-       save_errno = errno;
-       if (zserv_privs.change(ZPRIVS_LOWER))
-               zlog_err("Can't lower privileges");
+       frr_elevate_privs(&zserv_privs) {
+               status = sendmsg(nl->sock, &msg, 0);
+               save_errno = errno;
+       }
 
        if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND) {
                zlog_debug("%s: >> netlink message dump [sent]", __func__);
@@ -853,8 +970,8 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
        }
 
        if (status < 0) {
-               zlog_err("netlink_talk sendmsg() error: %s",
-                        safe_strerror(save_errno));
+               flog_err_sys(EC_LIB_SOCKET, "netlink_talk sendmsg() error: %s",
+                            safe_strerror(save_errno));
                return -1;
        }
 
@@ -873,11 +990,11 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n)
 {
        int ret;
        struct sockaddr_nl snl;
-       int save_errno;
 
        /* Check netlink socket. */
        if (nl->sock < 0) {
-               zlog_err("%s socket isn't active.", nl->name);
+               flog_err_sys(EC_LIB_SOCKET, "%s socket isn't active.",
+                            nl->name);
                return -1;
        }
 
@@ -890,21 +1007,14 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n)
        snl.nl_family = AF_NETLINK;
 
        /* Raise capabilities and send message, then lower capabilities. */
-       if (zserv_privs.change(ZPRIVS_RAISE)) {
-               zlog_err("Can't raise privileges");
-               return -1;
+       frr_elevate_privs(&zserv_privs) {
+               ret = sendto(nl->sock, (void *)n, n->nlmsg_len, 0,
+                            (struct sockaddr *)&snl, sizeof snl);
        }
 
-       ret = sendto(nl->sock, (void *)n, n->nlmsg_len, 0,
-                    (struct sockaddr *)&snl, sizeof snl);
-       save_errno = errno;
-
-       if (zserv_privs.change(ZPRIVS_LOWER))
-               zlog_err("Can't lower privileges");
-
        if (ret < 0) {
                zlog_err("%s sendto failed: %s", nl->name,
-                        safe_strerror(save_errno));
+                        safe_strerror(errno));
                return -1;
        }
 
@@ -942,12 +1052,20 @@ void kernel_init(struct zebra_ns *zns)
        snprintf(zns->netlink.name, sizeof(zns->netlink.name),
                 "netlink-listen (NS %u)", zns->ns_id);
        zns->netlink.sock = -1;
-       netlink_socket(&zns->netlink, groups, zns->ns_id);
+       if (netlink_socket(&zns->netlink, groups, zns->ns_id) < 0) {
+               zlog_err("Failure to create %s socket",
+                        zns->netlink.name);
+               exit(-1);
+       }
 
        snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name),
                 "netlink-cmd (NS %u)", zns->ns_id);
        zns->netlink_cmd.sock = -1;
-       netlink_socket(&zns->netlink_cmd, 0, zns->ns_id);
+       if (netlink_socket(&zns->netlink_cmd, 0, zns->ns_id) < 0) {
+               zlog_err("Failure to create %s socket",
+                        zns->netlink_cmd.name);
+               exit(-1);
+       }
 
        /*
         * SOL_NETLINK is not available on all platforms yet
@@ -969,23 +1087,25 @@ void kernel_init(struct zebra_ns *zns)
 #endif
 
        /* Register kernel socket. */
-       if (zns->netlink.sock > 0) {
-               /* Only want non-blocking on the netlink event socket */
-               if (fcntl(zns->netlink.sock, F_SETFL, O_NONBLOCK) < 0)
-                       zlog_err("Can't set %s socket flags: %s",
-                                zns->netlink.name, safe_strerror(errno));
-
-               /* Set receive buffer size if it's set from command line */
-               if (nl_rcvbufsize)
-                       netlink_recvbuf(&zns->netlink, nl_rcvbufsize);
-
-               netlink_install_filter(zns->netlink.sock,
-                                      zns->netlink_cmd.snl.nl_pid);
-               zns->t_netlink = NULL;
-
-               thread_add_read(zebrad.master, kernel_read, zns,
-                               zns->netlink.sock, &zns->t_netlink);
-       }
+       if (fcntl(zns->netlink.sock, F_SETFL, O_NONBLOCK) < 0)
+               flog_err_sys(EC_LIB_SOCKET, "Can't set %s socket flags: %s",
+                            zns->netlink.name, safe_strerror(errno));
+
+       if (fcntl(zns->netlink_cmd.sock, F_SETFL, O_NONBLOCK) < 0)
+               zlog_err("Can't set %s socket error: %s(%d)",
+                        zns->netlink_cmd.name, safe_strerror(errno), errno);
+
+       /* Set receive buffer size if it's set from command line */
+       if (nl_rcvbufsize)
+               netlink_recvbuf(&zns->netlink, nl_rcvbufsize);
+
+       assert(zns->netlink.sock >= 0);
+       netlink_install_filter(zns->netlink.sock,
+                              zns->netlink_cmd.snl.nl_pid);
+       zns->t_netlink = NULL;
+
+       thread_add_read(zebrad.master, kernel_read, zns,
+                       zns->netlink.sock, &zns->t_netlink);
 
        rt_netlink_init();
 }