[ovs-dev] [netlink v4 07/52] netlink-socket: Add functions for joining and leaving multicast groups.

Justin Pettit jpettit at nicira.com
Tue Jan 18 08:42:50 UTC 2011


Looks good.

--Justin


On Jan 11, 2011, at 9:49 PM, Ben Pfaff wrote:

> 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.
> ---
> lib/netdev-linux.c         |    4 +-
> lib/netdev-vport.c         |    2 +-
> lib/netlink-protocol.h     |   13 ++++--
> lib/netlink-socket.c       |   94 +++++++++++++++++++++-----------------------
> lib/netlink-socket.h       |    9 ++--
> lib/rtnetlink.c            |   13 +++++-
> utilities/nlmon.c          |    9 +++-
> vswitchd/ovs-brcompatd.c   |   25 +++++++++---
> vswitchd/proc-net-compat.c |    4 +-
> 9 files changed, 99 insertions(+), 74 deletions(-)
> 
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index 5654dd4..65b22b7 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -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");
>  * you may not use this file except in compliance with the License.
> @@ -445,7 +445,7 @@ netdev_linux_init(void)
> 
>         /* Create rtnetlink socket. */
>         if (!status) {
> -            status = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
> +            status = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
>             if (status) {
>                 VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
>                             strerror(status));
> diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
> index f3985e9..fb2b619 100644
> --- a/lib/netdev-vport.c
> +++ b/lib/netdev-vport.c
> @@ -508,7 +508,7 @@ netdev_vport_reset_name_else_route(bool is_name)
>         }
>     }
> 
> -    error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
> +    error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
>     if (error) {
>         VLOG_WARN_RL(&rl, "Failed to create NETLINK_ROUTE socket");
>         return error;
> diff --git a/lib/netlink-protocol.h b/lib/netlink-protocol.h
> index 6281fb2..1b5fa71 100644
> --- a/lib/netlink-protocol.h
> +++ b/lib/netlink-protocol.h
> @@ -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");
>  * 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);
> 
> -#define NETLINK_ADD_MEMBERSHIP  1
> -#define NETLINK_DROP_MEMBERSHIP 2
> -#define NETLINK_PKTINFO         3
> -
> struct genlmsghdr {
>     uint8_t cmd;
>     uint8_t version;
> @@ -157,4 +153,11 @@ enum {
> #define NLA_TYPE_MASK		~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
> #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 */
> diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c
> index c9402fd..8e81da9 100644
> --- a/lib/netlink-socket.c
> +++ b/lib/netlink-socket.c
> @@ -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");
>  * 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'
>  * (NETLINK_ROUTE, NETLINK_GENERIC, ...).  Returns 0 and sets '*sockp' to the
> - * 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.
> - */
> + * new socket if successful, otherwise returns a positive errno value.  */
> int
> -nl_sock_create(int protocol, int multicast_group,
> -               size_t so_sndbuf, size_t so_rcvbuf, struct nl_sock **sockp)
> +nl_sock_create(int protocol, struct nl_sock **sockp)
> {
>     struct nl_sock *sock;
>     struct sockaddr_nl local, remote;
> @@ -99,29 +90,10 @@ nl_sock_create(int protocol, int multicast_group,
>         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. */
>     memset(&local, 0, sizeof local);
>     local.nl_family = AF_NETLINK;
>     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) {
>         VLOG_ERR("bind(%"PRIu32"): %s", sock->pid, strerror(errno));
>         goto error_free_pid;
> @@ -136,23 +108,6 @@ nl_sock_create(int protocol, int multicast_group,
>         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;
>     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
>  * '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.
> @@ -608,7 +604,7 @@ static int do_lookup_genl_family(const char *name)
>     struct nlattr *attrs[ARRAY_SIZE(family_policy)];
>     int retval;
> 
> -    retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock);
> +    retval = nl_sock_create(NETLINK_GENERIC, &sock);
>     if (retval) {
>         return -retval;
>     }
> diff --git a/lib/netlink-socket.h b/lib/netlink-socket.h
> index dc21ce8..ad06d81 100644
> --- a/lib/netlink-socket.h
> +++ b/lib/netlink-socket.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");
>  * you may not use this file except in compliance with the License.
> @@ -45,11 +45,12 @@ struct nl_sock;
> #endif
> 
> /* Netlink sockets. */
> -int nl_sock_create(int protocol, int multicast_group,
> -                   size_t so_sndbuf, size_t so_rcvbuf,
> -                   struct nl_sock **);
> +int nl_sock_create(int protocol, 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_sendv(struct nl_sock *sock, const struct iovec iov[], size_t n_iov,
>                   bool wait);
> diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c
> index 5d80d72..b92dcd6 100644
> --- a/lib/rtnetlink.c
> +++ b/lib/rtnetlink.c
> @@ -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");
>  * you may not use this file except in compliance with the License.
> @@ -77,13 +77,20 @@ rtnetlink_notifier_register(struct rtnetlink *rtn,
>                             rtnetlink_notify_func *cb, void *aux)
> {
>     if (!rtn->notify_sock) {
> -        int error = nl_sock_create(NETLINK_ROUTE, rtn->multicast_group, 0, 0,
> -                                   &rtn->notify_sock);
> +        struct nl_sock *sock;
> +        int error;
> +
> +        error = nl_sock_create(NETLINK_ROUTE, &sock);
> +        if (!error) {
> +            error = nl_sock_join_mcgroup(sock, rtn->multicast_group);
> +        }
>         if (error) {
> +            nl_sock_destroy(sock);
>             VLOG_WARN("could not create rtnetlink socket: %s",
>                       strerror(error));
>             return error;
>         }
> +        rtn->notify_sock = sock;
>     } else {
>         /* Catch up on notification work so that the new notifier won't
>          * receive any stale notifications. */
> diff --git a/utilities/nlmon.c b/utilities/nlmon.c
> index 1fb3808..b6396d5 100644
> --- a/utilities/nlmon.c
> +++ b/utilities/nlmon.c
> @@ -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");
>  * 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]);
>     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) {
>         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 (;;) {
>         struct ofpbuf *buf;
> 
> diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c
> index bed2747..19e09e7 100644
> --- a/vswitchd/ovs-brcompatd.c
> +++ b/vswitchd/ovs-brcompatd.c
> @@ -1,4 +1,4 @@
> -/* 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");
>  * you may not use this file except in compliance with the License.
> @@ -111,7 +111,7 @@ lookup_brc_multicast_group(int *multicast_group)
>     struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)];
>     int retval;
> 
> -    retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock);
> +    retval = nl_sock_create(NETLINK_GENERIC, &sock);
>     if (retval) {
>         return retval;
>     }
> @@ -156,12 +156,17 @@ brc_open(struct nl_sock **sock)
>         return retval;
>     }
> 
> -    retval = nl_sock_create(NETLINK_GENERIC, multicast_group, 0, 0, sock);
> +    retval = nl_sock_create(NETLINK_GENERIC, sock);
>     if (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[] = {
> @@ -1318,8 +1323,16 @@ main(int argc, char *argv[])
>     }
> 
>     if (prune_timeout) {
> -        if (nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &rtnl_sock)) {
> -            ovs_fatal(0, "could not create rtnetlink socket");
> +        int error;
> +
> +        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");
>         }
>     }
> 
> diff --git a/vswitchd/proc-net-compat.c b/vswitchd/proc-net-compat.c
> index e3b7ee4..3b7596a 100644
> --- a/vswitchd/proc-net-compat.c
> +++ b/vswitchd/proc-net-compat.c
> @@ -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");
>  * you may not use this file except in compliance with the License.
> @@ -59,7 +59,7 @@ proc_net_compat_init(void)
>             return retval;
>         }
> 
> -        retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &brc_sock);
> +        retval = nl_sock_create(NETLINK_GENERIC, &brc_sock);
>         if (retval) {
>             return retval;
>         }
> -- 
> 1.7.1
> 
> 
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev_openvswitch.org





More information about the dev mailing list