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 72541c7..e9beebf 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 8649453..bb9e510 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 6281fb2..1b5fa71 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 c9402fd..8e81da9 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 dc21ce8..ad06d81 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 e21e80c..83a666e 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 cc175b4..6ed85ab 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 1fb3808..b6396d5 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 24b28a2..992b8e2 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 e3b7ee4..3b7596a 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;
         }