diff --git a/sys/netinet/in.c b/sys/netinet/in.c index c91df4a2744..9edd74768cb 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -234,16 +234,42 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * in_lifaddr_ioctl() and ifp->if_ioctl(). */ switch (cmd) { - case SIOCAIFADDR: - case SIOCDIFADDR: case SIOCGIFADDR: case SIOCGIFBRDADDR: case SIOCGIFDSTADDR: case SIOCGIFNETMASK: + case SIOCDIFADDR: + break; + case SIOCAIFADDR: + /* + * ifra_addr must be present and be of INET family. + * ifra_broadaddr and ifra_mask are optional. + */ + if (ifra->ifra_addr.sin_len != sizeof(struct sockaddr_in) || + ifra->ifra_addr.sin_family != AF_INET) + return (EINVAL); + if (ifra->ifra_broadaddr.sin_len != 0 && + (ifra->ifra_broadaddr.sin_len != sizeof(struct sockaddr_in) || + ifra->ifra_broadaddr.sin_family != AF_INET)) + return (EINVAL); +#if 0 + /* + * ifconfig(8) historically doesn't set af_family for mask + * for unknown reason. + */ + if (ifra->ifra_mask.sin_len != 0 && + (ifra->ifra_mask.sin_len != sizeof(struct sockaddr_in) || + ifra->ifra_mask.sin_family != AF_INET)) + return (EINVAL); +#endif + break; case SIOCSIFADDR: case SIOCSIFBRDADDR: case SIOCSIFDSTADDR: case SIOCSIFNETMASK: + if (ifr->ifr_addr.sa_family != AF_INET || + ifr->ifr_addr.sa_len != sizeof(struct sockaddr_in)) + return (EINVAL); break; case SIOCALIFADDR: @@ -349,7 +375,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, switch (cmd) { case SIOCAIFADDR: case SIOCDIFADDR: - if (ifra->ifra_addr.sin_family == AF_INET) { + { struct in_ifaddr *oia; IN_IFADDR_RLOCK(); @@ -506,7 +532,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, goto out; case SIOCSIFNETMASK: - ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr; + ia->ia_sockmask = *(struct sockaddr_in *)&ifr->ifr_addr; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); goto out; @@ -514,14 +540,12 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, maskIsNew = 0; hostIsNew = 1; error = 0; - if (ia->ia_addr.sin_family == AF_INET) { - if (ifra->ifra_addr.sin_len == 0) { - ifra->ifra_addr = ia->ia_addr; - hostIsNew = 0; - } else if (ifra->ifra_addr.sin_addr.s_addr == - ia->ia_addr.sin_addr.s_addr) - hostIsNew = 0; - } + if (ifra->ifra_addr.sin_len == 0) { + ifra->ifra_addr = ia->ia_addr; + hostIsNew = 0; + } else if (ifra->ifra_addr.sin_addr.s_addr == + ia->ia_addr.sin_addr.s_addr) + hostIsNew = 0; if (ifra->ifra_mask.sin_len) { /* * QL: XXX @@ -552,7 +576,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, break; if ((ifp->if_flags & IFF_BROADCAST) && - (ifra->ifra_broadaddr.sin_family == AF_INET)) + ifra->ifra_broadaddr.sin_len) ia->ia_broadaddr = ifra->ifra_broadaddr; if (error == 0) { ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); @@ -608,31 +632,26 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, IN_IFADDR_WLOCK(); TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link); - if (ia->ia_addr.sin_family == AF_INET) { - struct in_ifaddr *if_ia; - LIST_REMOVE(ia, ia_hash); - IN_IFADDR_WUNLOCK(); - /* - * If this is the last IPv4 address configured on this - * interface, leave the all-hosts group. - * No state-change report need be transmitted. - */ - if_ia = NULL; - IFP_TO_IA(ifp, if_ia); - if (if_ia == NULL) { - ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); - IN_MULTI_LOCK(); - if (ii->ii_allhosts) { - (void)in_leavegroup_locked(ii->ii_allhosts, - NULL); - ii->ii_allhosts = NULL; - } - IN_MULTI_UNLOCK(); - } else - ifa_free(&if_ia->ia_ifa); + LIST_REMOVE(ia, ia_hash); + IN_IFADDR_WUNLOCK(); + /* + * If this is the last IPv4 address configured on this + * interface, leave the all-hosts group. + * No state-change report need be transmitted. + */ + IFP_TO_IA(ifp, iap); + if (iap == NULL) { + ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); + IN_MULTI_LOCK(); + if (ii->ii_allhosts) { + (void)in_leavegroup_locked(ii->ii_allhosts, NULL); + ii->ii_allhosts = NULL; + } + IN_MULTI_UNLOCK(); } else - IN_IFADDR_WUNLOCK(); + ifa_free(&iap->ia_ifa); + ifa_free(&ia->ia_ifa); /* in_ifaddrhead */ out: if (ia != NULL) @@ -828,50 +847,29 @@ in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, int scrub) { register u_long i = ntohl(sin->sin_addr.s_addr); - struct sockaddr_in oldaddr; int flags = RTF_UP, error = 0; - oldaddr = ia->ia_addr; - if (oldaddr.sin_family == AF_INET) + if (scrub) + in_scrubprefix(ia, LLE_STATIC); + + IN_IFADDR_WLOCK(); + if (ia->ia_addr.sin_family == AF_INET) LIST_REMOVE(ia, ia_hash); ia->ia_addr = *sin; - if (ia->ia_addr.sin_family == AF_INET) { - IN_IFADDR_WLOCK(); - LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), - ia, ia_hash); - IN_IFADDR_WUNLOCK(); - } + LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), + ia, ia_hash); + IN_IFADDR_WUNLOCK(); + /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ - if (ifp->if_ioctl != NULL) { - error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); - if (error) { + if (ifp->if_ioctl != NULL && + (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) != 0) /* LIST_REMOVE(ia, ia_hash) is done in in_control */ - ia->ia_addr = oldaddr; - IN_IFADDR_WLOCK(); - if (ia->ia_addr.sin_family == AF_INET) - LIST_INSERT_HEAD(INADDR_HASH( - ia->ia_addr.sin_addr.s_addr), ia, ia_hash); - else - /* - * If oldaddr family is not AF_INET (e.g. - * interface has been just created) in_control - * does not call LIST_REMOVE, and we end up - * with bogus ia entries in hash - */ - LIST_REMOVE(ia, ia_hash); - IN_IFADDR_WUNLOCK(); return (error); - } - } - if (scrub) { - ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in_ifscrub(ifp, ia, LLE_STATIC); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - } + /* * Be compatible with network classes, if netmask isn't supplied, * guess it based on classes. @@ -916,11 +914,9 @@ in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, if (ia->ia_addr.sin_addr.s_addr == INADDR_ANY) return (0); - if (ifp->if_flags & IFF_POINTOPOINT) { - if (ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) + if (ifp->if_flags & IFF_POINTOPOINT && + ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) return (0); - } - /* * add a loopback route to self