1- /* $OpenBSD: ip_output.c,v 1.358 2020/12/20 21:15:47 bluhm Exp $ */
1+ /* $OpenBSD: ip_output.c,v 1.359 2021/01/07 14:51:46 claudio Exp $ */
22/* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */
33
44/*
7373#endif /* IPSEC */
7474
7575int ip_pcbopts (struct mbuf * * , struct mbuf * );
76+ int ip_multicast_if (struct ip_mreqn * , u_int , unsigned int * );
7677int ip_setmoptions (int , struct ip_moptions * * , struct mbuf * , u_int );
7778void ip_mloopback (struct ifnet * , struct mbuf * , struct sockaddr_in * );
7879static __inline u_int16_t __attribute__((__unused__ ))
@@ -1336,6 +1337,51 @@ ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
13361337 return (0 );
13371338}
13381339
1340+ /*
1341+ * Lookup the interface based on the information in the ip_mreqn struct.
1342+ */
1343+ int
1344+ ip_multicast_if (struct ip_mreqn * mreq , u_int rtableid , unsigned int * ifidx )
1345+ {
1346+ struct sockaddr_in sin ;
1347+ struct rtentry * rt ;
1348+
1349+ /*
1350+ * In case userland provides the imr_ifindex use this as interface.
1351+ * If no interface address was provided, use the interface of
1352+ * the route to the given multicast address.
1353+ */
1354+ if (mreq -> imr_ifindex != 0 ) {
1355+ * ifidx = mreq -> imr_ifindex ;
1356+ } else if (mreq -> imr_address .s_addr == INADDR_ANY ) {
1357+ memset (& sin , 0 , sizeof (sin ));
1358+ sin .sin_len = sizeof (sin );
1359+ sin .sin_family = AF_INET ;
1360+ sin .sin_addr = mreq -> imr_multiaddr ;
1361+ rt = rtalloc (sintosa (& sin ), RT_RESOLVE , rtableid );
1362+ if (!rtisvalid (rt )) {
1363+ rtfree (rt );
1364+ return EADDRNOTAVAIL ;
1365+ }
1366+ * ifidx = rt -> rt_ifidx ;
1367+ rtfree (rt );
1368+ } else {
1369+ memset (& sin , 0 , sizeof (sin ));
1370+ sin .sin_len = sizeof (sin );
1371+ sin .sin_family = AF_INET ;
1372+ sin .sin_addr = mreq -> imr_address ;
1373+ rt = rtalloc (sintosa (& sin ), 0 , rtableid );
1374+ if (!rtisvalid (rt ) || !ISSET (rt -> rt_flags , RTF_LOCAL )) {
1375+ rtfree (rt );
1376+ return EADDRNOTAVAIL ;
1377+ }
1378+ * ifidx = rt -> rt_ifidx ;
1379+ rtfree (rt );
1380+ }
1381+
1382+ return 0 ;
1383+ }
1384+
13391385/*
13401386 * Set the IP multicast options in response to user setsockopt().
13411387 */
@@ -1345,12 +1391,12 @@ ip_setmoptions(int optname, struct ip_moptions **imop, struct mbuf *m,
13451391{
13461392 struct in_addr addr ;
13471393 struct in_ifaddr * ia ;
1348- struct ip_mreq * mreq ;
1394+ struct ip_mreqn mreqn ;
13491395 struct ifnet * ifp = NULL ;
13501396 struct ip_moptions * imo = * imop ;
13511397 struct in_multi * * immp ;
1352- struct rtentry * rt ;
13531398 struct sockaddr_in sin ;
1399+ unsigned int ifidx ;
13541400 int i , error = 0 ;
13551401 u_char loop ;
13561402
@@ -1438,63 +1484,41 @@ ip_setmoptions(int optname, struct ip_moptions **imop, struct mbuf *m,
14381484 * Add a multicast group membership.
14391485 * Group must be a valid IP multicast address.
14401486 */
1441- if (m == NULL || m -> m_len != sizeof (struct ip_mreq )) {
1487+ if (m == NULL || !(m -> m_len == sizeof (struct ip_mreq ) ||
1488+ m -> m_len == sizeof (struct ip_mreqn ))) {
14421489 error = EINVAL ;
14431490 break ;
14441491 }
1445- mreq = mtod (m , struct ip_mreq * );
1446- if (!IN_MULTICAST (mreq -> imr_multiaddr .s_addr )) {
1492+ memset (& mreqn , 0 , sizeof (mreqn ));
1493+ memcpy (& mreqn , mtod (m , void * ), m -> m_len );
1494+ if (!IN_MULTICAST (mreqn .imr_multiaddr .s_addr )) {
14471495 error = EINVAL ;
14481496 break ;
14491497 }
1450- /*
1451- * If no interface address was provided, use the interface of
1452- * the route to the given multicast address.
1453- */
1454- if (mreq -> imr_interface .s_addr == INADDR_ANY ) {
1455- memset (& sin , 0 , sizeof (sin ));
1456- sin .sin_len = sizeof (sin );
1457- sin .sin_family = AF_INET ;
1458- sin .sin_addr = mreq -> imr_multiaddr ;
1459- rt = rtalloc (sintosa (& sin ), RT_RESOLVE , rtableid );
1460- if (!rtisvalid (rt )) {
1461- rtfree (rt );
1462- error = EADDRNOTAVAIL ;
1463- break ;
1464- }
1465- } else {
1466- memset (& sin , 0 , sizeof (sin ));
1467- sin .sin_len = sizeof (sin );
1468- sin .sin_family = AF_INET ;
1469- sin .sin_addr = mreq -> imr_interface ;
1470- rt = rtalloc (sintosa (& sin ), 0 , rtableid );
1471- if (!rtisvalid (rt ) || !ISSET (rt -> rt_flags , RTF_LOCAL )) {
1472- rtfree (rt );
1473- error = EADDRNOTAVAIL ;
1474- break ;
1475- }
1476- }
1477- ifp = if_get (rt -> rt_ifidx );
1478- rtfree (rt );
1498+
1499+ error = ip_multicast_if (& mreqn , rtableid , & ifidx );
1500+ if (error )
1501+ break ;
14791502
14801503 /*
14811504 * See if we found an interface, and confirm that it
14821505 * supports multicast.
14831506 */
1507+ ifp = if_get (ifidx );
14841508 if (ifp == NULL || (ifp -> if_flags & IFF_MULTICAST ) == 0 ) {
14851509 error = EADDRNOTAVAIL ;
14861510 if_put (ifp );
14871511 break ;
14881512 }
1513+
14891514 /*
14901515 * See if the membership already exists or if all the
14911516 * membership slots are full.
14921517 */
14931518 for (i = 0 ; i < imo -> imo_num_memberships ; ++ i ) {
1494- if (imo -> imo_membership [i ]-> inm_ifidx
1495- == ifp -> if_index &&
1519+ if (imo -> imo_membership [i ]-> inm_ifidx == ifidx &&
14961520 imo -> imo_membership [i ]-> inm_addr .s_addr
1497- == mreq -> imr_multiaddr .s_addr )
1521+ == mreqn . imr_multiaddr .s_addr )
14981522 break ;
14991523 }
15001524 if (i < imo -> imo_num_memberships ) {
@@ -1506,9 +1530,10 @@ ip_setmoptions(int optname, struct ip_moptions **imop, struct mbuf *m,
15061530 struct in_multi * * nmships , * * omships ;
15071531 size_t newmax ;
15081532 /*
1509- * Resize the vector to next power-of-two minus 1. If the
1510- * size would exceed the maximum then we know we've really
1511- * run out of entries. Otherwise, we reallocate the vector.
1533+ * Resize the vector to next power-of-two minus 1. If
1534+ * the size would exceed the maximum then we know we've
1535+ * really run out of entries. Otherwise, we reallocate
1536+ * the vector.
15121537 */
15131538 nmships = NULL ;
15141539 omships = imo -> imo_membership ;
@@ -1538,7 +1563,7 @@ ip_setmoptions(int optname, struct ip_moptions **imop, struct mbuf *m,
15381563 * address list for the given interface.
15391564 */
15401565 if ((imo -> imo_membership [i ] =
1541- in_addmulti (& mreq -> imr_multiaddr , ifp )) == NULL ) {
1566+ in_addmulti (& mreqn . imr_multiaddr , ifp )) == NULL ) {
15421567 error = ENOBUFS ;
15431568 if_put (ifp );
15441569 break ;
@@ -1552,42 +1577,34 @@ ip_setmoptions(int optname, struct ip_moptions **imop, struct mbuf *m,
15521577 * Drop a multicast group membership.
15531578 * Group must be a valid IP multicast address.
15541579 */
1555- if (m == NULL || m -> m_len != sizeof (struct ip_mreq )) {
1580+ if (m == NULL || !(m -> m_len == sizeof (struct ip_mreq ) ||
1581+ m -> m_len == sizeof (struct ip_mreqn ))) {
15561582 error = EINVAL ;
15571583 break ;
15581584 }
1559- mreq = mtod (m , struct ip_mreq * );
1560- if (!IN_MULTICAST (mreq -> imr_multiaddr .s_addr )) {
1585+ memset (& mreqn , 0 , sizeof (mreqn ));
1586+ memcpy (& mreqn , mtod (m , void * ), m -> m_len );
1587+ if (!IN_MULTICAST (mreqn .imr_multiaddr .s_addr )) {
15611588 error = EINVAL ;
15621589 break ;
15631590 }
1591+
15641592 /*
15651593 * If an interface address was specified, get a pointer
15661594 * to its ifnet structure.
15671595 */
1568- if (mreq -> imr_interface .s_addr == INADDR_ANY )
1569- ifp = NULL ;
1570- else {
1571- memset (& sin , 0 , sizeof (sin ));
1572- sin .sin_len = sizeof (sin );
1573- sin .sin_family = AF_INET ;
1574- sin .sin_addr = mreq -> imr_interface ;
1575- ia = ifatoia (ifa_ifwithaddr (sintosa (& sin ), rtableid ));
1576- if (ia == NULL ) {
1577- error = EADDRNOTAVAIL ;
1578- break ;
1579- }
1580- ifp = ia -> ia_ifp ;
1581- }
1596+ error = ip_multicast_if (& mreqn , rtableid , & ifidx );
1597+ if (error )
1598+ break ;
1599+
15821600 /*
15831601 * Find the membership in the membership array.
15841602 */
15851603 for (i = 0 ; i < imo -> imo_num_memberships ; ++ i ) {
1586- if ((ifp == NULL ||
1587- imo -> imo_membership [i ]-> inm_ifidx ==
1588- ifp -> if_index ) &&
1604+ if ((ifidx == 0 ||
1605+ imo -> imo_membership [i ]-> inm_ifidx == ifidx ) &&
15891606 imo -> imo_membership [i ]-> inm_addr .s_addr ==
1590- mreq -> imr_multiaddr .s_addr )
1607+ mreqn . imr_multiaddr .s_addr )
15911608 break ;
15921609 }
15931610 if (i == imo -> imo_num_memberships ) {
0 commit comments