]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: netlink cleanup
authorJakub Urbańczyk <xthaid@gmail.com>
Wed, 24 Jun 2020 18:34:38 +0000 (20:34 +0200)
committerJakub Urbańczyk <xthaid@gmail.com>
Wed, 8 Jul 2020 09:18:06 +0000 (11:18 +0200)
 * Split zebra's netlink code into smaller functions to be more
   generic.

Signed-off-by: Jakub Urbańczyk <xthaid@gmail.com>
zebra/kernel_netlink.c

index 75a8e9f17db29d6bf3167593dbeed83a2cdccc0b..e92f8d4bc0dceb5dfd80046acc996fe13882bf73 100644 (file)
@@ -697,6 +697,204 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h)
        }
 }
 
+/*
+ * netlink_send_msg - send a netlink message of a certain size.
+ *
+ * Returns -1 on error. Otherwise, it returns the number of bytes sent.
+ */
+static int netlink_send_msg(const struct nlsock *nl, void *buf, size_t buflen)
+{
+       struct sockaddr_nl snl;
+       struct iovec iov;
+       struct msghdr msg;
+       int status, save_errno = 0;
+
+       memset(&snl, 0, sizeof(snl));
+       memset(&iov, 0, sizeof(iov));
+       memset(&msg, 0, sizeof(msg));
+
+       iov.iov_base = buf;
+       iov.iov_len = buflen;
+       msg.msg_name = (void *)&snl;
+       msg.msg_namelen = sizeof(snl);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       snl.nl_family = AF_NETLINK;
+
+       /* Send message to netlink interface. */
+       frr_with_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__);
+               zlog_hexdump(buf, buflen);
+       }
+
+       if (status < 0) {
+               flog_err_sys(EC_LIB_SOCKET, "%s error: %s", __func__,
+                            safe_strerror(save_errno));
+               return -1;
+       }
+
+       return status;
+}
+
+/*
+ * netlink_recv_msg - receive a netlink message.
+ *
+ * Returns -1 on error, 0 if read would block or the number of bytes received.
+ */
+static int netlink_recv_msg(const struct nlsock *nl, struct msghdr msg,
+                           void *buf, size_t buflen)
+{
+       struct iovec iov;
+       int status;
+
+       iov.iov_base = buf;
+       iov.iov_len = buflen;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       do {
+#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);
+                       ((struct sockaddr_nl *)msg.msg_name)->nl_pid = 0;
+               } else {
+                       status = recvmsg(nl->sock, &msg, 0);
+               }
+#else
+               status = recvmsg(nl->sock, &msg, 0);
+#endif /* HANDLE_NETLINK_FUZZING */
+       } while (status < 0 && errno == EINTR);
+
+       if (status < 0) {
+               if (errno == EWOULDBLOCK || errno == EAGAIN)
+                       return 0;
+               flog_err(EC_ZEBRA_RECVMSG_OVERRUN, "%s recvmsg overrun: %s",
+                        nl->name, safe_strerror(errno));
+               /*
+                * In this case we are screwed. There is no good way to recover
+                * zebra at this point.
+                */
+               exit(-1);
+       }
+
+       if (status == 0) {
+               flog_err_sys(EC_LIB_SOCKET, "%s EOF", nl->name);
+               return -1;
+       }
+
+       if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+               flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+                        "%s sender address length error: length %d", nl->name,
+                        msg.msg_namelen);
+               return -1;
+       }
+
+       if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_RECV) {
+               zlog_debug("%s: << netlink message dump [recv]", __func__);
+               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 */
+
+       return status;
+}
+
+/*
+ * netlink_parse_error - parse a netlink error message
+ *
+ * Returns 1 if this message is acknowledgement, 0 if this error should be
+ * ignored, -1 otherwise.
+ */
+static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h,
+                              const struct zebra_dplane_info *zns,
+                              bool startup)
+{
+       struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
+       int errnum = err->error;
+       int msg_type = err->msg.nlmsg_type;
+
+       if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+               flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+                        "%s error: message truncated", nl->name);
+               return -1;
+       }
+
+       /*
+        * Parse the extended information before we actually handle it. At this
+        * point in time we do not do anything other than report the issue.
+        */
+       if (h->nlmsg_flags & NLM_F_ACK_TLVS)
+               netlink_parse_extended_ack(h);
+
+       /* If the error field is zero, then this is an ACK. */
+       if (err->error == 0) {
+               if (IS_ZEBRA_DEBUG_KERNEL) {
+                       zlog_debug("%s: %s ACK: type=%s(%u), seq=%u, pid=%u",
+                                  __func__, nl->name,
+                                  nl_msg_type_to_str(err->msg.nlmsg_type),
+                                  err->msg.nlmsg_type, err->msg.nlmsg_seq,
+                                  err->msg.nlmsg_pid);
+               }
+
+               return 1;
+       }
+
+       /* Deal with errors that occur because of races in link handling. */
+       if (zns->is_cmd
+           && ((msg_type == RTM_DELROUTE
+                && (-errnum == ENODEV || -errnum == ESRCH))
+               || (msg_type == RTM_NEWROUTE
+                   && (-errnum == ENETDOWN || -errnum == EEXIST)))) {
+               if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("%s: error: %s type=%s(%u), seq=%u, pid=%u",
+                                  nl->name, safe_strerror(-errnum),
+                                  nl_msg_type_to_str(msg_type), msg_type,
+                                  err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+               return 0;
+       }
+
+       /*
+        * We see RTM_DELNEIGH when shutting down an interface with an IPv4
+        * link-local.  The kernel should have already deleted the neighbor so
+        * do not log these as an error.
+        */
+       if (msg_type == RTM_DELNEIGH
+           || (zns->is_cmd && msg_type == RTM_NEWROUTE
+               && (-errnum == ESRCH || -errnum == ENETUNREACH))) {
+               /*
+                * This is known to happen in some situations, don't log as
+                * error.
+                */
+               if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("%s error: %s, type=%s(%u), seq=%u, pid=%u",
+                                  nl->name, safe_strerror(-errnum),
+                                  nl_msg_type_to_str(msg_type), msg_type,
+                                  err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+       } else {
+               if ((msg_type != RTM_GETNEXTHOP) || !startup)
+                       flog_err(EC_ZEBRA_UNEXPECTED_MESSAGE,
+                                "%s error: %s, type=%s(%u), seq=%u, pid=%u",
+                                nl->name, safe_strerror(-errnum),
+                                nl_msg_type_to_str(msg_type), msg_type,
+                                err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+       }
+
+       return -1;
+}
+
 /*
  * netlink_parse_info
  *
@@ -722,71 +920,19 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
 
        while (1) {
                char buf[NL_RCV_PKT_BUF_SIZE];
-               struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
                struct sockaddr_nl snl;
                struct msghdr msg = {.msg_name = (void *)&snl,
-                                    .msg_namelen = sizeof(snl),
-                                    .msg_iov = &iov,
-                                    .msg_iovlen = 1};
+                                    .msg_namelen = sizeof(snl)};
                struct nlmsghdr *h;
 
                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;
-                       flog_err(EC_ZEBRA_RECVMSG_OVERRUN,
-                                "%s recvmsg overrun: %s", nl->name,
-                                safe_strerror(errno));
-                       /*
-                        *  In this case we are screwed.
-                        *  There is no good way to
-                        *  recover zebra at this point.
-                        */
-                       exit(-1);
-                       continue;
-               }
-
-               if (status == 0) {
-                       flog_err_sys(EC_LIB_SOCKET, "%s EOF", nl->name);
-                       return -1;
-               }
-
-               if (msg.msg_namelen != sizeof(snl)) {
-                       flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
-                                "%s sender address length error: length %d",
-                                nl->name, msg.msg_namelen);
+               status = netlink_recv_msg(nl, msg, buf, sizeof(buf));
+               if (status == -1)
                        return -1;
-               }
-
-               if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_RECV) {
-                       zlog_debug("%s: << netlink message dump [recv]",
-                                  __func__);
-                       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 */
+               else if (status == 0)
+                       break;
 
                read_in++;
                for (h = (struct nlmsghdr *)buf;
@@ -798,112 +944,14 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
 
                        /* Error handling. */
                        if (h->nlmsg_type == NLMSG_ERROR) {
-                               struct nlmsgerr *err =
-                                       (struct nlmsgerr *)NLMSG_DATA(h);
-                               int errnum = err->error;
-                               int msg_type = err->msg.nlmsg_type;
-
-                               if (h->nlmsg_len
-                                   < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
-                                       flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
-                                                "%s error: message truncated",
-                                                nl->name);
-                                       return -1;
-                               }
-
-                               /*
-                                * Parse the extended information before
-                                * we actually handle it.
-                                * At this point in time we do not
-                                * do anything other than report the
-                                * issue.
-                                */
-                               if (h->nlmsg_flags & NLM_F_ACK_TLVS)
-                                       netlink_parse_extended_ack(h);
-
-                               /* If the error field is zero, then this is an
-                                * ACK */
-                               if (err->error == 0) {
-                                       if (IS_ZEBRA_DEBUG_KERNEL) {
-                                               zlog_debug(
-                                                       "%s: %s ACK: type=%s(%u), seq=%u, pid=%u",
-                                                       __func__, nl->name,
-                                                       nl_msg_type_to_str(
-                                                               err->msg.nlmsg_type),
-                                                       err->msg.nlmsg_type,
-                                                       err->msg.nlmsg_seq,
-                                                       err->msg.nlmsg_pid);
-                                       }
-
-                                       /* return if not a multipart message,
-                                        * otherwise continue */
+                               int err = netlink_parse_error(nl, h, zns,
+                                                             startup);
+                               if (err == 1) {
                                        if (!(h->nlmsg_flags & NLM_F_MULTI))
                                                return 0;
                                        continue;
-                               }
-
-                               /* Deal with errors that occur because of races
-                                * in link handling */
-                               if (zns->is_cmd
-                                   && ((msg_type == RTM_DELROUTE
-                                        && (-errnum == ENODEV
-                                            || -errnum == ESRCH))
-                                       || (msg_type == RTM_NEWROUTE
-                                           && (-errnum == ENETDOWN
-                                               || -errnum == EEXIST)))) {
-                                       if (IS_ZEBRA_DEBUG_KERNEL)
-                                               zlog_debug(
-                                                       "%s: error: %s type=%s(%u), seq=%u, pid=%u",
-                                                       nl->name,
-                                                       safe_strerror(-errnum),
-                                                       nl_msg_type_to_str(
-                                                               msg_type),
-                                                       msg_type,
-                                                       err->msg.nlmsg_seq,
-                                                       err->msg.nlmsg_pid);
-                                       return 0;
-                               }
-
-                               /* We see RTM_DELNEIGH when shutting down an
-                                * interface with an IPv4
-                                * link-local.  The kernel should have already
-                                * deleted the neighbor
-                                * so do not log these as an error.
-                                */
-                               if (msg_type == RTM_DELNEIGH
-                                   || (zns->is_cmd && msg_type == RTM_NEWROUTE
-                                       && (-errnum == ESRCH
-                                           || -errnum == ENETUNREACH))) {
-                                       /* This is known to happen in some
-                                        * situations, don't log
-                                        * as error.
-                                        */
-                                       if (IS_ZEBRA_DEBUG_KERNEL)
-                                               zlog_debug(
-                                                       "%s error: %s, type=%s(%u), seq=%u, pid=%u",
-                                                       nl->name,
-                                                       safe_strerror(-errnum),
-                                                       nl_msg_type_to_str(
-                                                               msg_type),
-                                                       msg_type,
-                                                       err->msg.nlmsg_seq,
-                                                       err->msg.nlmsg_pid);
-                               } else {
-                                       if ((msg_type != RTM_GETNEXTHOP)
-                                           || !startup)
-                                               flog_err(
-                                                       EC_ZEBRA_UNEXPECTED_MESSAGE,
-                                                       "%s error: %s, type=%s(%u), seq=%u, pid=%u",
-                                                       nl->name,
-                                                       safe_strerror(-errnum),
-                                                       nl_msg_type_to_str(
-                                                               msg_type),
-                                                       msg_type,
-                                                       err->msg.nlmsg_seq,
-                                                       err->msg.nlmsg_pid);
-                               }
-
-                               return -1;
+                               } else
+                                       return err;
                        }
 
                        /* OK we got netlink message. */
@@ -966,26 +1014,8 @@ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
                      struct nlmsghdr *n,
                      const struct zebra_dplane_info *dp_info, int startup)
 {
-       int status = 0;
-       struct sockaddr_nl snl;
-       struct iovec iov;
-       struct msghdr msg;
-       int save_errno = 0;
        const struct nlsock *nl;
 
-       memset(&snl, 0, sizeof(snl));
-       memset(&iov, 0, sizeof(iov));
-       memset(&msg, 0, sizeof(msg));
-
-       iov.iov_base = n;
-       iov.iov_len = n->nlmsg_len;
-       msg.msg_name = (void *)&snl;
-       msg.msg_namelen = sizeof(snl);
-       msg.msg_iov = &iov;
-       msg.msg_iovlen = 1;
-
-       snl.nl_family = AF_NETLINK;
-
        nl = &(dp_info->nls);
        n->nlmsg_seq = nl->seq;
        n->nlmsg_pid = nl->snl.nl_pid;
@@ -997,22 +1027,8 @@ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
                        n->nlmsg_type, n->nlmsg_len, n->nlmsg_seq,
                        n->nlmsg_flags);
 
-       /* Send message to netlink interface. */
-       frr_with_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__);
-               zlog_hexdump(n, n->nlmsg_len);
-       }
-
-       if (status < 0) {
-               flog_err_sys(EC_LIB_SOCKET, "netlink_talk sendmsg() error: %s",
-                            safe_strerror(save_errno));
+       if (netlink_send_msg(nl, n, n->nlmsg_len) < 0)
                return -1;
-       }
 
        /*
         * Get reply from netlink socket.
@@ -1047,8 +1063,6 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
  */
 int netlink_request(struct nlsock *nl, void *req)
 {
-       int ret;
-       struct sockaddr_nl snl;
        struct nlmsghdr *n = (struct nlmsghdr *)req;
 
        /* Check netlink socket. */
@@ -1062,20 +1076,8 @@ int netlink_request(struct nlsock *nl, void *req)
        n->nlmsg_pid = nl->snl.nl_pid;
        n->nlmsg_seq = ++nl->seq;
 
-       memset(&snl, 0, sizeof(snl));
-       snl.nl_family = AF_NETLINK;
-
-       /* Raise capabilities and send message, then lower capabilities. */
-       frr_with_privs(&zserv_privs) {
-               ret = sendto(nl->sock, req, n->nlmsg_len, 0,
-                            (struct sockaddr *)&snl, sizeof(snl));
-       }
-
-       if (ret < 0) {
-               zlog_err("%s sendto failed: %s", nl->name,
-                        safe_strerror(errno));
+       if (netlink_send_msg(nl, req, n->nlmsg_len) < 0)
                return -1;
-       }
 
        return 0;
 }