summaryrefslogtreecommitdiffstats
path: root/zebra/kernel_netlink.c
diff options
context:
space:
mode:
authorJakub Urbańczyk <xthaid@gmail.com>2020-06-24 20:34:38 +0200
committerJakub Urbańczyk <xthaid@gmail.com>2020-07-08 11:18:06 +0200
commitae6138bfd8443afd1d2fc59c0a202063cd966376 (patch)
tree17d89add02c11da398381260b41e9741f2e1a7a6 /zebra/kernel_netlink.c
parentMerge pull request #6689 from mjstapp/doc_topotest_timeouts (diff)
downloadfrr-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.c418
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;
}