/* 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));
 
         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;
 
 /*
- * 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.
 };
 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;
 #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 */
 
 /*
- * 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.
 
 /* 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;
         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;
         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;
 
     }
 }
 
+/* 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.
     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;
     }
 
 /*
- * 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.
 #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);
 
     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");
 
                             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. */
 
 /*
- * 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.
     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;
 
 
     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;
     }
         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[] = {
     }
 
     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");
         }
     }
 
 
-/* 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.
             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;
         }