summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/in.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/in.c')
-rw-r--r--freebsd/sys/netinet/in.c58
1 files changed, 33 insertions, 25 deletions
diff --git a/freebsd/sys/netinet/in.c b/freebsd/sys/netinet/in.c
index db1ebda0..c3f172f3 100644
--- a/freebsd/sys/netinet/in.c
+++ b/freebsd/sys/netinet/in.c
@@ -141,20 +141,21 @@ in_localip(struct in_addr in)
int
in_ifhasaddr(struct ifnet *ifp, struct in_addr in)
{
+ struct epoch_tracker et;
struct ifaddr *ifa;
struct in_ifaddr *ia;
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
ia = (struct in_ifaddr *)ifa;
if (ia->ia_addr.sin_addr.s_addr == in.s_addr) {
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
return (1);
}
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
return (0);
}
@@ -192,15 +193,10 @@ int
in_canforward(struct in_addr in)
{
u_long i = ntohl(in.s_addr);
- u_long net;
- if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i) || IN_LINKLOCAL(i))
+ if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i) || IN_LINKLOCAL(i) ||
+ IN_ZERONET(i) || IN_LOOPBACK(i))
return (0);
- if (IN_CLASSA(i)) {
- net = i & IN_CLASSA_NET;
- if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
- return (0);
- }
return (1);
}
@@ -230,6 +226,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
{
struct ifreq *ifr = (struct ifreq *)data;
struct sockaddr_in *addr = (struct sockaddr_in *)&ifr->ifr_addr;
+ struct epoch_tracker et;
struct ifaddr *ifa;
struct in_ifaddr *ia;
int error;
@@ -281,7 +278,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
* address was specified, find that one instead of the
* first one on the interface, if possible.
*/
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
@@ -299,7 +296,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
}
if (ifa == NULL) {
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
return (EADDRNOTAVAIL);
}
@@ -330,7 +327,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
break;
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
return (error);
}
@@ -344,6 +341,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
const struct sockaddr_in *mask = &ifra->ifra_mask;
const struct sockaddr_in *dstaddr = &ifra->ifra_dstaddr;
const int vhid = (cmd == SIOCAIFADDR) ? ifra->ifra_vhid : 0;
+ struct epoch_tracker et;
struct ifaddr *ifa;
struct in_ifaddr *ia;
bool iaIsFirst;
@@ -380,7 +378,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
*/
iaIsFirst = true;
ia = NULL;
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
struct in_ifaddr *it;
@@ -393,7 +391,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0)
ia = it;
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
if (ia != NULL)
(void )in_difaddr_ioctl(cmd, data, ifp, td);
@@ -923,7 +921,7 @@ in_ifscrub_all(void)
IFNET_RLOCK();
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
/* Cannot lock here - lock recursion. */
- /* IF_ADDR_RLOCK(ifp); */
+ /* NET_EPOCH_ENTER(et); */
CK_STAILQ_FOREACH_SAFE(ifa, &ifp->if_addrhead, ifa_link, nifa) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
@@ -939,7 +937,7 @@ in_ifscrub_all(void)
(void)in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr,
ifp, NULL);
}
- /* IF_ADDR_RUNLOCK(ifp); */
+ /* NET_EPOCH_EXIT(et); */
in_purgemaddrs(ifp);
igmp_domifdetach(ifp);
}
@@ -971,6 +969,7 @@ in_ifaddr_broadcast(struct in_addr in, struct in_ifaddr *ia)
int
in_broadcast(struct in_addr in, struct ifnet *ifp)
{
+ struct epoch_tracker et;
struct ifaddr *ifa;
int found;
@@ -984,14 +983,14 @@ in_broadcast(struct in_addr in, struct ifnet *ifp)
* Look through the list of addresses for a match
* with a broadcast address.
*/
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
if (ifa->ifa_addr->sa_family == AF_INET &&
in_ifaddr_broadcast(in, (struct in_ifaddr *)ifa)) {
found = 1;
break;
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
return (found);
}
@@ -1382,15 +1381,13 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add
IF_AFDATA_LOCK_ASSERT(llt->llt_ifp);
KASSERT(l3addr->sa_family == AF_INET,
("sin_family %d", l3addr->sa_family));
- lle = in_lltable_find_dst(llt, sin->sin_addr);
+ KASSERT((flags & (LLE_UNLOCKED | LLE_EXCLUSIVE)) !=
+ (LLE_UNLOCKED | LLE_EXCLUSIVE),
+ ("wrong lle request flags: %#x", flags));
+ lle = in_lltable_find_dst(llt, sin->sin_addr);
if (lle == NULL)
return (NULL);
-
- KASSERT((flags & (LLE_UNLOCKED|LLE_EXCLUSIVE)) !=
- (LLE_UNLOCKED|LLE_EXCLUSIVE),("wrong lle request flags: 0x%X",
- flags));
-
if (flags & LLE_UNLOCKED)
return (lle);
@@ -1399,6 +1396,17 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add
else
LLE_RLOCK(lle);
+ /*
+ * If the afdata lock is not held, the LLE may have been unlinked while
+ * we were blocked on the LLE lock. Check for this case.
+ */
+ if (__predict_false((lle->la_flags & LLE_LINKED) == 0)) {
+ if (flags & LLE_EXCLUSIVE)
+ LLE_WUNLOCK(lle);
+ else
+ LLE_RUNLOCK(lle);
+ return (NULL);
+ }
return (lle);
}