mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 14:25:26 +00:00
netlink-socket: Add functions for joining and leaving multicast groups.
When this library was originally implemented, support for Linux 2.4 was important. The Netlink implementation in Linux only added support for joining and leaving multicast groups after a socket is bound as of Linux 2.6.14, so the library did not support it either. But the current version of Open vSwitch targets Linux 2.6.18 and over, so it's fine to add this support now, and this commit does so. This will be used more extensively in upcoming commits. Reviewed by Justin Pettit.
This commit is contained in:
@@ -446,7 +446,7 @@ netdev_linux_init(void)
|
|||||||
|
|
||||||
/* Create rtnetlink socket. */
|
/* Create rtnetlink socket. */
|
||||||
if (!status) {
|
if (!status) {
|
||||||
status = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
|
status = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
|
||||||
if (status) {
|
if (status) {
|
||||||
VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
|
VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
|
||||||
strerror(status));
|
strerror(status));
|
||||||
|
@@ -489,7 +489,7 @@ netdev_vport_reset_names(void)
|
|||||||
free(nn);
|
free(nn);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
|
error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
|
||||||
if (error) {
|
if (error) {
|
||||||
VLOG_WARN_RL(&rl, "Failed to create NETLINK_ROUTE socket");
|
VLOG_WARN_RL(&rl, "Failed to create NETLINK_ROUTE socket");
|
||||||
return error;
|
return error;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2010 Nicira Networks.
|
* Copyright (c) 2008, 2010, 2011 Nicira Networks.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -86,10 +86,6 @@ struct nlmsgerr
|
|||||||
};
|
};
|
||||||
BUILD_ASSERT_DECL(sizeof(struct nlmsgerr) == 20);
|
BUILD_ASSERT_DECL(sizeof(struct nlmsgerr) == 20);
|
||||||
|
|
||||||
#define NETLINK_ADD_MEMBERSHIP 1
|
|
||||||
#define NETLINK_DROP_MEMBERSHIP 2
|
|
||||||
#define NETLINK_PKTINFO 3
|
|
||||||
|
|
||||||
struct genlmsghdr {
|
struct genlmsghdr {
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
@@ -157,4 +153,11 @@ enum {
|
|||||||
#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
|
#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* These were introduced all together in 2.6.14. (We want our programs to
|
||||||
|
* support the newer kernel features even if compiled with older headers.) */
|
||||||
|
#ifndef NETLINK_ADD_MEMBERSHIP
|
||||||
|
#define NETLINK_ADD_MEMBERSHIP 1
|
||||||
|
#define NETLINK_DROP_MEMBERSHIP 2
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* netlink-protocol.h */
|
#endif /* netlink-protocol.h */
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2009, 2010 Nicira Networks.
|
* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -65,18 +65,9 @@ static void free_pid(uint32_t);
|
|||||||
|
|
||||||
/* Creates a new netlink socket for the given netlink 'protocol'
|
/* Creates a new netlink socket for the given netlink 'protocol'
|
||||||
* (NETLINK_ROUTE, NETLINK_GENERIC, ...). Returns 0 and sets '*sockp' to the
|
* (NETLINK_ROUTE, NETLINK_GENERIC, ...). Returns 0 and sets '*sockp' to the
|
||||||
* new socket if successful, otherwise returns a positive errno value.
|
* new socket if successful, otherwise returns a positive errno value. */
|
||||||
*
|
|
||||||
* If 'multicast_group' is nonzero, the new socket subscribes to the specified
|
|
||||||
* netlink multicast group. (A netlink socket may listen to an arbitrary
|
|
||||||
* number of multicast groups, but so far we only need one at a time.)
|
|
||||||
*
|
|
||||||
* Nonzero 'so_sndbuf' or 'so_rcvbuf' override the kernel default send or
|
|
||||||
* receive buffer size, respectively.
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
nl_sock_create(int protocol, int multicast_group,
|
nl_sock_create(int protocol, struct nl_sock **sockp)
|
||||||
size_t so_sndbuf, size_t so_rcvbuf, struct nl_sock **sockp)
|
|
||||||
{
|
{
|
||||||
struct nl_sock *sock;
|
struct nl_sock *sock;
|
||||||
struct sockaddr_nl local, remote;
|
struct sockaddr_nl local, remote;
|
||||||
@@ -99,29 +90,10 @@ nl_sock_create(int protocol, int multicast_group,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (so_sndbuf != 0
|
|
||||||
&& setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF,
|
|
||||||
&so_sndbuf, sizeof so_sndbuf) < 0) {
|
|
||||||
VLOG_ERR("setsockopt(SO_SNDBUF,%zu): %s", so_sndbuf, strerror(errno));
|
|
||||||
goto error_free_pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (so_rcvbuf != 0
|
|
||||||
&& setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF,
|
|
||||||
&so_rcvbuf, sizeof so_rcvbuf) < 0) {
|
|
||||||
VLOG_ERR("setsockopt(SO_RCVBUF,%zu): %s", so_rcvbuf, strerror(errno));
|
|
||||||
goto error_free_pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bind local address as our selected pid. */
|
/* Bind local address as our selected pid. */
|
||||||
memset(&local, 0, sizeof local);
|
memset(&local, 0, sizeof local);
|
||||||
local.nl_family = AF_NETLINK;
|
local.nl_family = AF_NETLINK;
|
||||||
local.nl_pid = sock->pid;
|
local.nl_pid = sock->pid;
|
||||||
if (multicast_group > 0 && multicast_group <= 32) {
|
|
||||||
/* This method of joining multicast groups is supported by old kernels,
|
|
||||||
* but it only allows 32 multicast groups per protocol. */
|
|
||||||
local.nl_groups |= 1ul << (multicast_group - 1);
|
|
||||||
}
|
|
||||||
if (bind(sock->fd, (struct sockaddr *) &local, sizeof local) < 0) {
|
if (bind(sock->fd, (struct sockaddr *) &local, sizeof local) < 0) {
|
||||||
VLOG_ERR("bind(%"PRIu32"): %s", sock->pid, strerror(errno));
|
VLOG_ERR("bind(%"PRIu32"): %s", sock->pid, strerror(errno));
|
||||||
goto error_free_pid;
|
goto error_free_pid;
|
||||||
@@ -136,23 +108,6 @@ nl_sock_create(int protocol, int multicast_group,
|
|||||||
goto error_free_pid;
|
goto error_free_pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Older kernel headers failed to define this macro. We want our programs
|
|
||||||
* to support the newer kernel features even if compiled with older
|
|
||||||
* headers, so define it ourselves in such a case. */
|
|
||||||
#ifndef NETLINK_ADD_MEMBERSHIP
|
|
||||||
#define NETLINK_ADD_MEMBERSHIP 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This method of joining multicast groups is only supported by newish
|
|
||||||
* kernels, but it allows for an arbitrary number of multicast groups. */
|
|
||||||
if (multicast_group > 32
|
|
||||||
&& setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
|
||||||
&multicast_group, sizeof multicast_group) < 0) {
|
|
||||||
VLOG_ERR("setsockopt(NETLINK_ADD_MEMBERSHIP,%d): %s",
|
|
||||||
multicast_group, strerror(errno));
|
|
||||||
goto error_free_pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
*sockp = sock;
|
*sockp = sock;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -183,6 +138,47 @@ nl_sock_destroy(struct nl_sock *sock)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tries to add 'sock' as a listener for 'multicast_group'. Returns 0 if
|
||||||
|
* successful, otherwise a positive errno value.
|
||||||
|
*
|
||||||
|
* Multicast group numbers are always positive.
|
||||||
|
*
|
||||||
|
* It is not an error to attempt to join a multicast group to which a socket
|
||||||
|
* already belongs. */
|
||||||
|
int
|
||||||
|
nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
|
||||||
|
{
|
||||||
|
if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||||
|
&multicast_group, sizeof multicast_group) < 0) {
|
||||||
|
VLOG_WARN("could not join multicast group %u (%s)",
|
||||||
|
multicast_group, strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tries to make 'sock' stop listening to 'multicast_group'. Returns 0 if
|
||||||
|
* successful, otherwise a positive errno value.
|
||||||
|
*
|
||||||
|
* Multicast group numbers are always positive.
|
||||||
|
*
|
||||||
|
* It is not an error to attempt to leave a multicast group to which a socket
|
||||||
|
* does not belong.
|
||||||
|
*
|
||||||
|
* On success, reading from 'sock' will still return any messages that were
|
||||||
|
* received on 'multicast_group' before the group was left. */
|
||||||
|
int
|
||||||
|
nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
|
||||||
|
{
|
||||||
|
if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
|
||||||
|
&multicast_group, sizeof multicast_group) < 0) {
|
||||||
|
VLOG_WARN("could not leave multicast group %u (%s)",
|
||||||
|
multicast_group, strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
|
/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
|
||||||
* 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, and
|
* 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, and
|
||||||
* nlmsg_pid will be set to 'sock''s pid, before the message is sent.
|
* nlmsg_pid will be set to 'sock''s pid, before the message is sent.
|
||||||
@@ -608,7 +604,7 @@ static int do_lookup_genl_family(const char *name)
|
|||||||
struct nlattr *attrs[ARRAY_SIZE(family_policy)];
|
struct nlattr *attrs[ARRAY_SIZE(family_policy)];
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock);
|
retval = nl_sock_create(NETLINK_GENERIC, &sock);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
return -retval;
|
return -retval;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2009, 2010 Nicira Networks.
|
* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -45,11 +45,12 @@ struct nl_sock;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Netlink sockets. */
|
/* Netlink sockets. */
|
||||||
int nl_sock_create(int protocol, int multicast_group,
|
int nl_sock_create(int protocol, struct nl_sock **);
|
||||||
size_t so_sndbuf, size_t so_rcvbuf,
|
|
||||||
struct nl_sock **);
|
|
||||||
void nl_sock_destroy(struct nl_sock *);
|
void nl_sock_destroy(struct nl_sock *);
|
||||||
|
|
||||||
|
int nl_sock_join_mcgroup(struct nl_sock *, unsigned int multicast_group);
|
||||||
|
int nl_sock_leave_mcgroup(struct nl_sock *, unsigned int multicast_group);
|
||||||
|
|
||||||
int nl_sock_send(struct nl_sock *, const struct ofpbuf *, bool wait);
|
int nl_sock_send(struct nl_sock *, const struct ofpbuf *, bool wait);
|
||||||
int nl_sock_sendv(struct nl_sock *sock, const struct iovec iov[], size_t n_iov,
|
int nl_sock_sendv(struct nl_sock *sock, const struct iovec iov[], size_t n_iov,
|
||||||
bool wait);
|
bool wait);
|
||||||
|
@@ -182,7 +182,7 @@ route_table_reset(void)
|
|||||||
route_map_clear();
|
route_map_clear();
|
||||||
route_table_valid = true;
|
route_table_valid = true;
|
||||||
|
|
||||||
error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
|
error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
|
||||||
if (error) {
|
if (error) {
|
||||||
VLOG_WARN_RL(&rl, "failed to reset routing table, "
|
VLOG_WARN_RL(&rl, "failed to reset routing table, "
|
||||||
"cannot create RTNETLINK_ROUTE socket");
|
"cannot create RTNETLINK_ROUTE socket");
|
||||||
|
@@ -89,13 +89,20 @@ rtnetlink_notifier_register(struct rtnetlink *rtn,
|
|||||||
rtnetlink_notify_func *cb, void *aux)
|
rtnetlink_notify_func *cb, void *aux)
|
||||||
{
|
{
|
||||||
if (!rtn->notify_sock) {
|
if (!rtn->notify_sock) {
|
||||||
int error = nl_sock_create(NETLINK_ROUTE, rtn->multicast_group, 0, 0,
|
struct nl_sock *sock;
|
||||||
&rtn->notify_sock);
|
int error;
|
||||||
|
|
||||||
|
error = nl_sock_create(NETLINK_ROUTE, &sock);
|
||||||
|
if (!error) {
|
||||||
|
error = nl_sock_join_mcgroup(sock, rtn->multicast_group);
|
||||||
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
|
nl_sock_destroy(sock);
|
||||||
VLOG_WARN("could not create rtnetlink socket: %s",
|
VLOG_WARN("could not create rtnetlink socket: %s",
|
||||||
strerror(error));
|
strerror(error));
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
rtn->notify_sock = sock;
|
||||||
} else {
|
} else {
|
||||||
/* Catch up on notification work so that the new notifier won't
|
/* Catch up on notification work so that the new notifier won't
|
||||||
* receive any stale notifications. */
|
* receive any stale notifications. */
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009, 2010 Nicira Networks.
|
* Copyright (c) 2009, 2010, 2011 Nicira Networks.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -45,11 +45,16 @@ main(int argc OVS_UNUSED, char *argv[])
|
|||||||
set_program_name(argv[0]);
|
set_program_name(argv[0]);
|
||||||
vlog_set_levels(NULL, VLF_ANY_FACILITY, VLL_DBG);
|
vlog_set_levels(NULL, VLF_ANY_FACILITY, VLL_DBG);
|
||||||
|
|
||||||
error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &sock);
|
error = nl_sock_create(NETLINK_ROUTE, &sock);
|
||||||
if (error) {
|
if (error) {
|
||||||
ovs_fatal(error, "could not create rtnetlink socket");
|
ovs_fatal(error, "could not create rtnetlink socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error = nl_sock_join_mcgroup(sock, RTNLGRP_LINK);
|
||||||
|
if (error) {
|
||||||
|
ovs_fatal(error, "could not join RTNLGRP_LINK multicast group");
|
||||||
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct ofpbuf *buf;
|
struct ofpbuf *buf;
|
||||||
|
|
||||||
|
@@ -111,7 +111,7 @@ lookup_brc_multicast_group(int *multicast_group)
|
|||||||
struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)];
|
struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)];
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock);
|
retval = nl_sock_create(NETLINK_GENERIC, &sock);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@@ -156,12 +156,17 @@ brc_open(struct nl_sock **sock)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = nl_sock_create(NETLINK_GENERIC, multicast_group, 0, 0, sock);
|
retval = nl_sock_create(NETLINK_GENERIC, sock);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
retval = nl_sock_join_mcgroup(*sock, multicast_group);
|
||||||
|
if (retval) {
|
||||||
|
nl_sock_destroy(*sock);
|
||||||
|
*sock = NULL;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct nl_policy brc_dp_policy[] = {
|
static const struct nl_policy brc_dp_policy[] = {
|
||||||
@@ -1318,8 +1323,16 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prune_timeout) {
|
if (prune_timeout) {
|
||||||
if (nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &rtnl_sock)) {
|
int error;
|
||||||
ovs_fatal(0, "could not create rtnetlink socket");
|
|
||||||
|
error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
|
||||||
|
if (error) {
|
||||||
|
ovs_fatal(error, "could not create rtnetlink socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
error = nl_sock_join_mcgroup(rtnl_sock, RTNLGRP_LINK);
|
||||||
|
if (error) {
|
||||||
|
ovs_fatal(error, "could not join RTNLGRP_LINK multicast group");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2009, 2010 Nicira Networks
|
/* Copyright (c) 2009, 2010, 2011 Nicira Networks
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -59,7 +59,7 @@ proc_net_compat_init(void)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &brc_sock);
|
retval = nl_sock_create(NETLINK_GENERIC, &brc_sock);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user