netlink-socket: Add functions for joining and leaving multicast groups.
authorBen Pfaff <blp@nicira.com>
Mon, 10 Jan 2011 00:57:45 +0000 (16:57 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 27 Jan 2011 17:26:05 +0000 (09:26 -0800)
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.

lib/netdev-linux.c
lib/netdev-vport.c
lib/netlink-protocol.h
lib/netlink-socket.c
lib/netlink-socket.h
lib/route-table.c
lib/rtnetlink.c
utilities/nlmon.c
vswitchd/ovs-brcompatd.c
vswitchd/proc-net-compat.c

index 72541c7978c08cce05654d7902da687c2df8c862..e9beebf1a408f1b11d1d75d83b2274371b2cbfb3 100644 (file)
@@ -446,7 +446,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));
index 864945337a91cf6ef866d42bbc1b0b76a689ef7b..bb9e510843b535eb87e9cea5430700f23f962dd5 100644 (file)
@@ -489,7 +489,7 @@ netdev_vport_reset_names(void)
         free(nn);
     }
 
-    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;
index 6281fb2c3af2678ec86587a55d71ecad60efbc78..1b5fa715389f62002f47d38bc375bcb5113d4866 100644 (file)
@@ -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 */
index c9402fd9fa42943c64c24030e889161b83b1a188..8e81da9c56e9d41aca4796c275f8cf9e2a53c601 100644 (file)
@@ -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;
     }
index dc21ce83804965d73236f2c2fa3429bad4906bc1..ad06d8118e8e94d8b7172d39e66ebbb9f3292dd9 100644 (file)
@@ -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);
index e21e80c2d3d93da6b4e3a3cfaa9614dc2f183d04..83a666ef3636ca7c0c15e02c175e0725b7c1bced 100644 (file)
@@ -182,7 +182,7 @@ route_table_reset(void)
     route_map_clear();
     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) {
         VLOG_WARN_RL(&rl, "failed to reset routing table, "
                      "cannot create RTNETLINK_ROUTE socket");
index cc175b41ea5fe05b2c8e51b6a180bb88b1b397d0..6ed85abbba2b5e51f35ca969e75f041f6c9ddb1b 100644 (file)
@@ -89,13 +89,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. */
index 1fb3808085f3f1f1ae48ea76fe6df649c97bf1b5..b6396d50b9da8c89bfa864ac031db7864e59904d 100644 (file)
@@ -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;
 
index 24b28a217d9636631ed3d06733a30b1c143060d3..992b8e21be5566d12f741404dd2f67add10c2656 100644 (file)
@@ -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");
         }
     }
 
index e3b7ee4897c0f95bc81f282fc9fa30cf1d17481b..3b7596ad91b59a79d539b27ea319f4a51788ba3a 100644 (file)
@@ -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;
         }