title: Need IPv6 Multicast C code that works on iOS 9
body: <p>Apple now requires iOS 9 apps to be IPv6 compliant.  We're mostly OK, except for a bit of code which sends a UDP broadcast - this now fails in iOS 9.</p>
  
  <p>Everything I read tells me that UDP multicast is the right way to do this in IPv6.  I've found some example code, but it doesn't work on any version of iOS or Mac OS X I've tried.</p>
  
  <p>This code is being called from a C/C++ lib inside our program - difficult to make a callback into Swift, Obj-C, Java, etc.  And this code will be shared by a Mac OS X and Android version of our app.  One would think it's possible to do IPv6 multicast in C in any POSIX environment!</p>
  
  <p>In the sample below, execution succeeds up to the final sendto() call, which actually sends the UDP message.  That sendto() fails, with errno set to EBROKENPIPE (22) after the failure.</p>
  
  <p>My best guess is that I'm missing some required setsockopt() call, or am using the wrong multicast address.  Right now, I'm stumped.</p>
  
  <p>Here's the function call I'm making (to multicast "Is anybody out there?" on UDP port 4031):</p>
  
  <pre><code>char *msg = "Is anybody out there?";
  err = multicast_udp_msg ( "FF01::1111", 4031, msg, strlen(msg) );
  </code></pre>
  
  <p>Here's the code that is being called:</p>
  
  <pre><code>// Multicasts a message on a specific UDP port.
  // myhost - IPv6 address on which to multicast the message (i.e., ourself)
  // port - UDP port on which to broadcast the mssage
  // msg - message contents to broadcast
  // msgsize - length of message in bytes
  // Return value is zero if successful, or nonzero on error.
  
  int multicast_udp_msg ( char *myhost, short port, char *msg, size_t msgsize )
  {
      int        sockfd, n;
      char    service[16] = { 0 };
      int        err = 0;
      struct addrinfo hints = { 0 }, *res, *ressave;
      struct sockaddr_storage addr = { 0 };
  
      hints.ai_family = AF_INET6;
      hints.ai_socktype = SOCK_DGRAM;
  
      sprintf ( service, "%hd", port );
      n = getaddrinfo ( myhost, service, &amp;hints, &amp;res );
      if ( n &lt; 0 )
      {
          fprintf(stderr, "getaddrinfo error:: [%s]\n", gai_strerror(n));
          return -1;
      }
  
      ressave = res;
  
      sockfd = socket ( res-&gt;ai_family, res-&gt;ai_socktype, res-&gt;ai_protocol );
      if ( sockfd &gt;= 0 )
      {
          memcpy ( &amp;addr, res-&gt;ai_addr, sizeof ( addr ) );
          if ( joinGroup ( sockfd, 0, 8, &amp;addr ) == 0 )
              if ( bind ( sockfd, res-&gt;ai_addr, res-&gt;ai_addrlen ) == 0 )
                  if ( sendto ( sockfd, msg, msgsize, 0, (struct sockaddr *) &amp;addr, sizeof ( addr ) ) &lt; 0 )
                      err = errno;
  
          close ( sockfd );
  
          res = res-&gt;ai_next;
      }
  
      freeaddrinfo ( ressave );
      return err;
  }
  
  int
  joinGroup(int sockfd, int loopBack, int mcastTTL,
           struct sockaddr_storage *addr)
  {
      int r1, r2, r3, retval;
  
      retval=-1;
  
      switch (addr-&gt;ss_family) {
          case AF_INET: {
              struct ip_mreq      mreq;
  
              mreq.imr_multiaddr.s_addr=
              ((struct sockaddr_in *)addr)-&gt;sin_addr.s_addr;
              mreq.imr_interface.s_addr= INADDR_ANY;
  
              r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
                             &amp;loopBack, sizeof(loopBack));
              if (r1&lt;0)
                  perror("joinGroup:: IP_MULTICAST_LOOP:: ");
  
              r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
                             &amp;mcastTTL, sizeof(mcastTTL));
              if (r2&lt;0)
                  perror("joinGroup:: IP_MULTICAST_TTL:: ");
  
              r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                             (const void *)&amp;mreq, sizeof(mreq));
              if (r3&lt;0)
                  perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");
  
          } break;
  
          case AF_INET6: {
              struct ipv6_mreq    mreq6;
  
              memcpy(&amp;mreq6.ipv6mr_multiaddr,
                     &amp;(((struct sockaddr_in6 *)addr)-&gt;sin6_addr),
                     sizeof(struct in6_addr));
  
              mreq6.ipv6mr_interface= 0; // cualquier interfaz
  
              r1= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
                             &amp;loopBack, sizeof(loopBack));
              if (r1&lt;0)
                  perror("joinGroup:: IPV6_MULTICAST_LOOP:: ");
  
              r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                             &amp;mcastTTL, sizeof(mcastTTL));
              if (r2&lt;0)
                  perror("joinGroup:: IPV6_MULTICAST_HOPS::  ");
  
              r3= setsockopt(sockfd, IPPROTO_IPV6,
                             IPV6_JOIN_GROUP, &amp;mreq6, sizeof(mreq6));
              if (r3&lt;0)
                  perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");
  
          } break;
  
          default:
              r1=r2=r3=-1;
      }
  
      if ((r1&gt;=0) &amp;&amp; (r2&gt;=0) &amp;&amp; (r3&gt;=0))
          retval=0;
  
      return retval;
  }
  </code></pre>
  
  <p>Thoughts welcome!</p>
  
  <p>-Tim</p>