X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=lib%2Fnetlink-socket.c;h=2d2aa29db9a4b253ae78394360b584e40d6240dc;hb=9ed3ba29b36ab99b75d6519660f58d6f5f9bd190;hp=f4bf864ad9df5146e12a9b8ae6962e4b87e08802;hpb=2ad204c8631635a0daf6bb58ece79f89e580e299;p=openvswitch diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c index f4bf864a..2d2aa29d 100644 --- a/lib/netlink-socket.c +++ b/lib/netlink-socket.c @@ -38,7 +38,7 @@ VLOG_DEFINE_THIS_MODULE(netlink_socket); COVERAGE_DEFINE(netlink_overflow); COVERAGE_DEFINE(netlink_received); -COVERAGE_DEFINE(netlink_recv_retry); +COVERAGE_DEFINE(netlink_recv_jumbo); COVERAGE_DEFINE(netlink_send); COVERAGE_DEFINE(netlink_sent); @@ -245,39 +245,6 @@ nl_sock_send(struct nl_sock *sock, const struct ofpbuf *msg, bool wait) return nl_sock_send__(sock, msg, wait); } -/* Tries to send the 'n_iov' chunks of data in 'iov' to the kernel on 'sock' as - * a single Netlink message. (The message must be fully formed and not require - * finalization of its nlmsg_len or nlmsg_pid fields.) - * - * Returns 0 if successful, otherwise a positive errno value. If 'wait' is - * true, then the send will wait until buffer space is ready; otherwise, - * returns EAGAIN if the 'sock' send buffer is full. */ -int -nl_sock_sendv(struct nl_sock *sock, const struct iovec iov[], size_t n_iov, - bool wait) -{ - struct msghdr msg; - int error; - - COVERAGE_INC(netlink_send); - memset(&msg, 0, sizeof msg); - msg.msg_iov = (struct iovec *) iov; - msg.msg_iovlen = n_iov; - do { - int retval; - retval = sendmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT); - error = retval < 0 ? errno : 0; - } while (error == EINTR); - if (error != EAGAIN) { - log_nlmsg(__func__, error, iov[0].iov_base, iov[0].iov_len, - sock->protocol); - if (!error) { - COVERAGE_INC(netlink_sent); - } - } - return error; -} - /* This stress option is useful for testing that OVS properly tolerates * -ENOBUFS on NetLink sockets. Such errors are unavoidable because they can * occur if the kernel cannot temporarily allocate enough GFP_ATOMIC memory to @@ -290,73 +257,68 @@ STRESS_OPTION( static int nl_sock_recv__(struct nl_sock *sock, struct ofpbuf **bufp, bool wait) { - uint8_t tmp; - ssize_t bufsize = 2048; - ssize_t nbytes, nbytes2; - struct ofpbuf *buf; + /* We can't accurately predict the size of the data to be received. Most + * received data will fit in a 2 kB buffer, so we allocate that much space. + * In case the data is actually bigger than that, we make available enough + * additional space to allow Netlink messages to be up to 64 kB long (a + * reasonable figure since that's the maximum length of a Netlink + * attribute). */ + enum { MAX_SIZE = 65536 }; + enum { HEAD_SIZE = 2048 }; + enum { TAIL_SIZE = MAX_SIZE - HEAD_SIZE }; + struct nlmsghdr *nlmsghdr; - struct iovec iov; - struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = NULL, - .msg_controllen = 0, - .msg_flags = 0 - }; + uint8_t tail[TAIL_SIZE]; + struct iovec iov[2]; + struct ofpbuf *buf; + struct msghdr msg; + ssize_t retval; - buf = ofpbuf_new(bufsize); *bufp = NULL; -try_again: - /* Attempt to read the message. We don't know the size of the data - * yet, so we take a guess at 2048. If we're wrong, we keep trying - * and doubling the buffer size each time. - */ - nlmsghdr = ofpbuf_put_uninit(buf, bufsize); - iov.iov_base = nlmsghdr; - iov.iov_len = bufsize; + buf = ofpbuf_new(HEAD_SIZE); + iov[0].iov_base = buf->data; + iov[0].iov_len = HEAD_SIZE; + iov[1].iov_base = tail; + iov[1].iov_len = TAIL_SIZE; + + memset(&msg, 0, sizeof msg); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + do { - nbytes = recvmsg(sock->fd, &msg, (wait ? 0 : MSG_DONTWAIT) | MSG_PEEK); - } while (nbytes < 0 && errno == EINTR); - if (nbytes < 0) { + retval = recvmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT); + } while (retval < 0 && errno == EINTR); + + if (retval < 0) { + int error = errno; + if (error == ENOBUFS) { + /* Socket receive buffer overflow dropped one or more messages that + * the kernel tried to send to us. */ + COVERAGE_INC(netlink_overflow); + } ofpbuf_delete(buf); - return errno; + return error; } + if (msg.msg_flags & MSG_TRUNC) { - COVERAGE_INC(netlink_recv_retry); - bufsize *= 2; - ofpbuf_reinit(buf, bufsize); - goto try_again; + VLOG_ERR_RL(&rl, "truncated message (longer than %d bytes)", MAX_SIZE); + ofpbuf_delete(buf); + return E2BIG; } - buf->size = nbytes; - /* We successfully read the message, so recv again to clear the queue */ - iov.iov_base = &tmp; - iov.iov_len = 1; - do { - nbytes2 = recvmsg(sock->fd, &msg, MSG_DONTWAIT); - } while (nbytes2 < 0 && errno == EINTR); - if (nbytes2 < 0) { - if (errno == ENOBUFS) { - /* The kernel is notifying us that a message it tried to send to us - * was dropped. We have to pass this along to the caller in case - * it wants to retry a request. So kill the buffer, which we can - * re-read next time. */ - COVERAGE_INC(netlink_overflow); - ofpbuf_delete(buf); - return ENOBUFS; - } else { - VLOG_ERR_RL(&rl, "failed to remove nlmsg from socket: %s\n", - strerror(errno)); - } + ofpbuf_put_uninit(buf, MIN(retval, HEAD_SIZE)); + if (retval > HEAD_SIZE) { + COVERAGE_INC(netlink_recv_jumbo); + ofpbuf_put(buf, tail, retval - HEAD_SIZE); } - if (nbytes < sizeof *nlmsghdr + + nlmsghdr = buf->data; + if (retval < sizeof *nlmsghdr || nlmsghdr->nlmsg_len < sizeof *nlmsghdr - || nlmsghdr->nlmsg_len > nbytes) { + || nlmsghdr->nlmsg_len > retval) { VLOG_ERR_RL(&rl, "received invalid nlmsg (%zd bytes < %d)", - bufsize, NLMSG_HDRLEN); + retval, NLMSG_HDRLEN); ofpbuf_delete(buf); return EPROTO; } @@ -718,6 +680,7 @@ static struct hmap genl_families = HMAP_INITIALIZER(&genl_families); static const struct nl_policy family_policy[CTRL_ATTR_MAX + 1] = { [CTRL_ATTR_FAMILY_ID] = {.type = NL_A_U16}, + [CTRL_ATTR_MCAST_GROUPS] = {.type = NL_A_NESTED}, }; static struct genl_family * @@ -763,46 +726,92 @@ genl_family_to_name(uint16_t id) } } -static int do_lookup_genl_family(const char *name) +static int +do_lookup_genl_family(const char *name, struct nlattr **attrs, + struct ofpbuf **replyp) { struct nl_sock *sock; struct ofpbuf request, *reply; - struct nlattr *attrs[ARRAY_SIZE(family_policy)]; - int retval; + int error; - retval = nl_sock_create(NETLINK_GENERIC, &sock); - if (retval) { - return -retval; + *replyp = NULL; + error = nl_sock_create(NETLINK_GENERIC, &sock); + if (error) { + return error; } ofpbuf_init(&request, 0); nl_msg_put_genlmsghdr(&request, 0, GENL_ID_CTRL, NLM_F_REQUEST, CTRL_CMD_GETFAMILY, 1); nl_msg_put_string(&request, CTRL_ATTR_FAMILY_NAME, name); - retval = nl_sock_transact(sock, &request, &reply); + error = nl_sock_transact(sock, &request, &reply); ofpbuf_uninit(&request); - if (retval) { + if (error) { nl_sock_destroy(sock); - return -retval; + return error; } if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN, - family_policy, attrs, ARRAY_SIZE(family_policy))) { + family_policy, attrs, ARRAY_SIZE(family_policy)) + || nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]) == 0) { nl_sock_destroy(sock); ofpbuf_delete(reply); - return -EPROTO; + return EPROTO; } - retval = nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]); - if (retval == 0) { - retval = -EPROTO; - } else { - define_genl_family(retval, name); - } nl_sock_destroy(sock); - ofpbuf_delete(reply); + *replyp = reply; + return 0; +} - return retval; +/* Finds the multicast group called 'group_name' in genl family 'family_name'. + * When successful, writes its result to 'multicast_group' and returns 0. + * Otherwise, clears 'multicast_group' and returns a positive error code. */ +int +nl_lookup_genl_mcgroup(const char *family_name, const char *group_name, + unsigned int *multicast_group) +{ + struct nlattr *family_attrs[ARRAY_SIZE(family_policy)]; + struct ofpbuf all_mcs; + struct ofpbuf *reply; + struct nlattr *mc; + unsigned int left; + int error; + + *multicast_group = 0; + error = do_lookup_genl_family(family_name, family_attrs, &reply); + if (error) { + return error; + } + + nl_attr_get_nested(family_attrs[CTRL_ATTR_MCAST_GROUPS], &all_mcs); + NL_ATTR_FOR_EACH (mc, left, all_mcs.data, all_mcs.size) { + static const struct nl_policy mc_policy[] = { + [CTRL_ATTR_MCAST_GRP_ID] = {.type = NL_A_U32}, + [CTRL_ATTR_MCAST_GRP_NAME] = {.type = NL_A_STRING}, + }; + + struct nlattr *mc_attrs[ARRAY_SIZE(mc_policy)]; + const char *mc_name; + + if (!nl_parse_nested(mc, mc_policy, mc_attrs, ARRAY_SIZE(mc_policy))) { + error = EPROTO; + goto exit; + } + + mc_name = nl_attr_get_string(mc_attrs[CTRL_ATTR_MCAST_GRP_NAME]); + if (!strcmp(group_name, mc_name)) { + *multicast_group = + nl_attr_get_u32(mc_attrs[CTRL_ATTR_MCAST_GRP_ID]); + error = 0; + goto exit; + } + } + error = EPROTO; + +exit: + ofpbuf_delete(reply); + return error; } /* If '*number' is 0, translates the given Generic Netlink family 'name' to a @@ -813,7 +822,19 @@ int nl_lookup_genl_family(const char *name, int *number) { if (*number == 0) { - *number = do_lookup_genl_family(name); + struct nlattr *attrs[ARRAY_SIZE(family_policy)]; + struct ofpbuf *reply; + int error; + + error = do_lookup_genl_family(name, attrs, &reply); + if (!error) { + *number = nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]); + define_genl_family(*number, name); + } else { + *number = -error; + } + ofpbuf_delete(reply); + assert(*number != 0); } return *number > 0 ? 0 : -*number;