mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-13 19:39:28 +00:00
zebra: netlink cleanup
* Split zebra's netlink code into smaller functions to be more generic. Signed-off-by: Jakub Urbańczyk <xthaid@gmail.com>
This commit is contained in:
parent
5b76e76515
commit
ae6138bfd8
@ -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);
|
||||
status = netlink_recv_msg(nl, msg, buf, sizeof(buf));
|
||||
if (status == -1)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user