diff options
author | Jakub Urbańczyk <xthaid@gmail.com> | 2020-06-24 20:34:38 +0200 |
---|---|---|
committer | Jakub Urbańczyk <xthaid@gmail.com> | 2020-07-08 11:18:06 +0200 |
commit | ae6138bfd8443afd1d2fc59c0a202063cd966376 (patch) | |
tree | 17d89add02c11da398381260b41e9741f2e1a7a6 /zebra/kernel_netlink.c | |
parent | Merge pull request #6689 from mjstapp/doc_topotest_timeouts (diff) | |
download | frr-ae6138bfd8443afd1d2fc59c0a202063cd966376.tar.xz frr-ae6138bfd8443afd1d2fc59c0a202063cd966376.zip |
zebra: netlink cleanup
* Split zebra's netlink code into smaller functions to be more
generic.
Signed-off-by: Jakub Urbańczyk <xthaid@gmail.com>
Diffstat (limited to 'zebra/kernel_netlink.c')
-rw-r--r-- | zebra/kernel_netlink.c | 418 |
1 files changed, 210 insertions, 208 deletions
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 75a8e9f17..e92f8d4bc 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -698,6 +698,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 * * Receive message from netlink interface and pass those information @@ -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; } |