I forgot to add ip_mreqn support to IP_MULTICAST_IF and so the
IP_ADD_MEMBERSHIP change is not fixing all the issues I have.

Linux supports calling IP_MULTICAST_IF with a struct in_addr, a struct
ip_mreq, or a struct ip_mreqn. FreeBSD only does the first and last.
I followed the Linux way because doing that was not that hard. In the end
only the imr_ifindex field and the imr_address field need to be checked
and if the imr_ifindex is 0 then just use the old code. If the imr_ifindex
is set then use this for interface index and break early.

Any opinions about this?
-- 
:wq Claudio

Index: netinet/ip_output.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.360
diff -u -p -r1.360 ip_output.c
--- netinet/ip_output.c 11 Jan 2021 13:28:53 -0000      1.360
+++ netinet/ip_output.c 15 Jan 2021 12:20:26 -0000
@@ -1423,11 +1423,40 @@ ip_setmoptions(int optname, struct ip_mo
                /*
                 * Select the interface for outgoing multicast packets.
                 */
-               if (m == NULL || m->m_len != sizeof(struct in_addr)) {
+               if (m == NULL) {
+                       error = EINVAL;
+                       break;
+               }
+               if (m->m_len == sizeof(struct in_addr)) {
+                       addr = *(mtod(m, struct in_addr *));
+               } else if (m->m_len == sizeof(struct ip_mreq) ||
+                   m->m_len == sizeof(struct ip_mreqn)) {
+                       memset(&mreqn, 0, sizeof(mreqn));
+                       memcpy(&mreqn, mtod(m, void *), m->m_len);
+
+                       /*
+                        * If an interface index is given use this
+                        * index to set the imo_ifidx but check first
+                        * that the interface actually exists.
+                        * In the other case just set the addr to
+                        * the imr_address and fall through to the
+                        * regular code.
+                        */
+                       if (mreqn.imr_ifindex != 0) {
+                               ifp = if_get(mreqn.imr_ifindex);
+                               if (ifp == NULL) {
+                                       error = EADDRNOTAVAIL;
+                                       break;
+                               }
+                               imo->imo_ifidx = ifp->if_index;
+                               if_put(ifp);
+                               break;
+                       } else
+                               addr = mreqn.imr_address;
+               } else {
                        error = EINVAL;
                        break;
                }
-               addr = *(mtod(m, struct in_addr *));
                /*
                 * INADDR_ANY is used to remove a previous selection.
                 * When no interface is selected, a default one is

Reply via email to