$OpenBSD: patch-lib_sockopt_c,v 1.2 2016/03/22 12:57:54 jca Exp $

Revert 69bf3a39; does not work on at least (Open/Net)BSD.
This can be put back if we get RFC3678 support.

--- lib/sockopt.c.orig	Wed Mar 16 00:17:17 2016
+++ lib/sockopt.c	Mon Mar 21 01:42:18 2016
@@ -197,7 +197,7 @@ setsockopt_ipv6_tclass(int sock, int tclass)
 
 /*
  * Process multicast socket options for IPv4 in an OS-dependent manner.
- * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
+ * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP.
  *
  * Many operating systems have a limit on the number of groups that
  * can be joined per socket (where each group and local address
@@ -217,132 +217,121 @@ setsockopt_ipv6_tclass(int sock, int tclass)
  * allow leaves, or implicitly leave all groups joined to down interfaces.
  */
 int
-setsockopt_ipv4_multicast(int sock,
+setsockopt_multicast_ipv4(int sock, 
 			int optname, 
+			struct in_addr if_addr /* required */,
 			unsigned int mcast_addr,
-			ifindex_t ifindex)
+			ifindex_t ifindex /* optional: if non-zero, may be
+						  used instead of if_addr */)
 {
-#ifdef HAVE_RFC3678
-  struct group_req gr;
-  struct sockaddr_in *si;
-  int ret;
-  memset (&gr, 0, sizeof(gr));
-  si = (struct sockaddr_in *)&gr.gr_group;
-  gr.gr_interface = ifindex;
-  si->sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
-  si->sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-  si->sin_addr.s_addr = mcast_addr;
-  ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ? 
-    MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
-  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
-    {
-      setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
-      ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr));
-    }
-  return ret;
 
-#elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
+#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+  /* This is better because it uses ifindex directly */
   struct ip_mreqn mreqn;
   int ret;
   
-  assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
-  memset (&mreqn, 0, sizeof(mreqn));
-
-  mreqn.imr_multiaddr.s_addr = mcast_addr;
-  mreqn.imr_ifindex = ifindex;
-  
-  ret = setsockopt(sock, IPPROTO_IP, optname,
-                   (void *)&mreqn, sizeof(mreqn));
-  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+  switch (optname)
     {
-      /* see above: handle possible problem when interface comes back up */
-      char buf[1][INET_ADDRSTRLEN];
-      zlog_info("setsockopt_ipv4_multicast attempting to drop and "
-                "re-add (fd %d, mcast %s, ifindex %u)",
-                sock,
-                inet_ntop(AF_INET, &mreqn.imr_multiaddr,
-                          buf[0], sizeof(buf[0])), ifindex);
-      setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
-                 (void *)&mreqn, sizeof(mreqn));
-      ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                       (void *)&mreqn, sizeof(mreqn));
+    case IP_MULTICAST_IF:
+    case IP_ADD_MEMBERSHIP:
+    case IP_DROP_MEMBERSHIP:
+      memset (&mreqn, 0, sizeof(mreqn));
+
+      if (mcast_addr)
+	mreqn.imr_multiaddr.s_addr = mcast_addr;
+      
+      if (ifindex)
+	mreqn.imr_ifindex = ifindex;
+      else
+	mreqn.imr_address = if_addr;
+      
+      ret = setsockopt(sock, IPPROTO_IP, optname,
+		       (void *)&mreqn, sizeof(mreqn));
+      if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+        {
+	  /* see above: handle possible problem when interface comes back up */
+	  char buf[2][INET_ADDRSTRLEN];
+	  zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
+		    "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
+		    sock,
+		    inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])),
+		    inet_ntop(AF_INET, &mreqn.imr_multiaddr,
+			      buf[1], sizeof(buf[1])), ifindex);
+	  setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+		     (void *)&mreqn, sizeof(mreqn));
+	  ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+			   (void *)&mreqn, sizeof(mreqn));
+        }
+      return ret;
+      break;
+
+    default:
+      /* Can out and give an understandable error */
+      errno = EINVAL;
+      return -1;
+      break;
     }
-  return ret;
 
   /* Example defines for another OS, boilerplate off other code in this
      function, AND handle optname as per other sections for consistency !! */
   /* #elif  defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
   /* Add your favourite OS here! */
 
-#elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */ 
+#else /* #if OS_TYPE */ 
   /* standard BSD API */
 
   struct in_addr m;
   struct ip_mreq mreq;
   int ret;
 
-  assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
+#ifdef HAVE_BSD_STRUCT_IP_MREQ_HACK
+  if (ifindex)
+    m.s_addr = htonl(ifindex);
+  else
+#endif
+    m = if_addr;
 
-  m.s_addr = htonl(ifindex);
-
-  memset (&mreq, 0, sizeof(mreq));
-  mreq.imr_multiaddr.s_addr = mcast_addr;
-  mreq.imr_interface = m;
-  
-  ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
-  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+  switch (optname)
     {
-      /* see above: handle possible problem when interface comes back up */
-      char buf[1][INET_ADDRSTRLEN];
-      zlog_info("setsockopt_ipv4_multicast attempting to drop and "
-                "re-add (fd %d, mcast %s, ifindex %u)",
-                sock,
-                inet_ntop(AF_INET, &mreq.imr_multiaddr,
-                          buf[0], sizeof(buf[0])), ifindex);
-      setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
-                  (void *)&mreq, sizeof(mreq));
-      ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                        (void *)&mreq, sizeof(mreq));
-    }
-  return ret;
+    case IP_MULTICAST_IF:
+      return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m)); 
+      break;
 
-#else
-  #error "Unsupported multicast API"
+    case IP_ADD_MEMBERSHIP:
+    case IP_DROP_MEMBERSHIP:
+      memset (&mreq, 0, sizeof(mreq));
+      mreq.imr_multiaddr.s_addr = mcast_addr;
+      mreq.imr_interface = m;
+      
+      ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
+      if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+        {
+	  /* see above: handle possible problem when interface comes back up */
+	  char buf[2][INET_ADDRSTRLEN];
+	  zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
+		    "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
+		    sock,
+		    inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])),
+		    inet_ntop(AF_INET, &mreq.imr_multiaddr,
+			      buf[1], sizeof(buf[1])), ifindex);
+	  setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+		      (void *)&mreq, sizeof(mreq));
+	  ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+	  		    (void *)&mreq, sizeof(mreq));
+        }
+      return ret;
+      break;
+      
+    default:
+      /* Can out and give an understandable error */
+      errno = EINVAL;
+      return -1;
+      break;
+    }
 #endif /* #if OS_TYPE */
 
 }
 
-/*
- * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
- */
-int
-setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex)
-{
-
-#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
-  struct ip_mreqn mreqn;
-  memset (&mreqn, 0, sizeof(mreqn));
-
-  mreqn.imr_ifindex = ifindex;
-  return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn));
-
-  /* Example defines for another OS, boilerplate off other code in this
-     function */
-  /* #elif  defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
-  /* Add your favourite OS here! */
-#elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
-  struct in_addr m;
-
-  m.s_addr = htonl(ifindex);
-
-  return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m));
-#else
-  #error "Unsupported multicast API"
-#endif
-}
-  
 static int
 setsockopt_ipv4_ifindex (int sock, ifindex_t val)
 {
