Skip to content

Commit c0ba2d2

Browse files
committed
Extend IP_ADD_MEMBERSHIP to also support struct ip_mreqn.
struct ip_mreqn allows to use the interface index to select the interface for multicast packets which makes it possible to use this with unnumbered interfaces. OK dlg@ robert@
1 parent cb197b1 commit c0ba2d2

File tree

2 files changed

+87
-64
lines changed

2 files changed

+87
-64
lines changed

sys/netinet/in.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $OpenBSD: in.h,v 1.138 2020/08/22 17:55:30 gnezdo Exp $ */
1+
/* $OpenBSD: in.h,v 1.139 2021/01/07 14:51:46 claudio Exp $ */
22
/* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */
33

44
/*
@@ -360,6 +360,12 @@ struct ip_mreq {
360360
struct in_addr imr_interface; /* local IP address of interface */
361361
};
362362

363+
struct ip_mreqn {
364+
struct in_addr imr_multiaddr; /* IP multicast address of group */
365+
struct in_addr imr_address; /* local IP address of interface */
366+
int imr_ifindex; /* interface index */
367+
};
368+
363369
/*
364370
* Argument for IP_PORTRANGE:
365371
* - which range to search when port is unspecified at bind() or connect()

sys/netinet/ip_output.c

Lines changed: 80 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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
/*
@@ -73,6 +73,7 @@
7373
#endif /* IPSEC */
7474

7575
int ip_pcbopts(struct mbuf **, struct mbuf *);
76+
int ip_multicast_if(struct ip_mreqn *, u_int, unsigned int *);
7677
int ip_setmoptions(int, struct ip_moptions **, struct mbuf *, u_int);
7778
void ip_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in *);
7879
static __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

Comments
 (0)