static __attribute__()

in bsd/netinet/in.c [640:919]


static __attribute__((noinline)) int
inctl_ifaddr(struct ifnet *ifp, struct in_ifaddr *ia, u_long cmd,
    struct ifreq *ifr)
{
	struct kev_in_data in_event_data;
	struct kev_msg ev_msg;
	struct sockaddr_in addr;
	struct ifaddr *ifa;
	int error = 0;

	VERIFY(ifp != NULL);

	bzero(&in_event_data, sizeof(struct kev_in_data));
	bzero(&ev_msg, sizeof(struct kev_msg));

	switch (cmd) {
	case SIOCGIFADDR:               /* struct ifreq */
		if (ia == NULL) {
			error = EADDRNOTAVAIL;
			break;
		}
		IFA_LOCK(&ia->ia_ifa);
		bcopy(&ia->ia_addr, &ifr->ifr_addr, sizeof(addr));
		IFA_UNLOCK(&ia->ia_ifa);
		break;

	case SIOCSIFADDR:               /* struct ifreq */
		VERIFY(ia != NULL);
		bcopy(&ifr->ifr_addr, &addr, sizeof(addr));
		/*
		 * If this is a new address, the reference count for the
		 * hash table has been taken at creation time above.
		 */
		error = in_ifinit(ifp, ia, &addr, 1);
		if (error == 0) {
			(void) ifnet_notify_address(ifp, AF_INET);
		}
		break;

	case SIOCAIFADDR: {             /* struct {if,in_}aliasreq */
		struct in_aliasreq *ifra = (struct in_aliasreq *)ifr;
		struct sockaddr_in broadaddr, mask;
		int hostIsNew, maskIsNew;

		VERIFY(ia != NULL);
		bcopy(&ifra->ifra_addr, &addr, sizeof(addr));
		bcopy(&ifra->ifra_broadaddr, &broadaddr, sizeof(broadaddr));
		bcopy(&ifra->ifra_mask, &mask, sizeof(mask));

		maskIsNew = 0;
		hostIsNew = 1;
		error = 0;

		IFA_LOCK(&ia->ia_ifa);
		if (ia->ia_addr.sin_family == AF_INET) {
			if (addr.sin_len == 0) {
				addr = ia->ia_addr;
				hostIsNew = 0;
			} else if (addr.sin_addr.s_addr ==
			    ia->ia_addr.sin_addr.s_addr) {
				hostIsNew = 0;
			}
		}
		if (mask.sin_len != 0) {
			IFA_UNLOCK(&ia->ia_ifa);
			in_ifscrub(ifp, ia, 0);
			IFA_LOCK(&ia->ia_ifa);
			ia->ia_sockmask.sin_len = sizeof(struct sockaddr_in);
			ia->ia_sockmask.sin_family = AF_INET;
			ia->ia_sockmask.sin_port = 0;
			ia->ia_sockmask.sin_addr = mask.sin_addr;
			bzero(&ia->ia_sockmask.sin_zero, sizeof(ia->ia_dstaddr.sin_zero));
			ia->ia_subnetmask =
			    ntohl(ia->ia_sockmask.sin_addr.s_addr);
			maskIsNew = 1;
		}
		if ((ifp->if_flags & IFF_POINTOPOINT) &&
		    (broadaddr.sin_family == AF_INET)) {
			IFA_UNLOCK(&ia->ia_ifa);
			in_ifscrub(ifp, ia, 0);
			IFA_LOCK(&ia->ia_ifa);
			ia->ia_dstaddr.sin_family = AF_INET;
			ia->ia_dstaddr.sin_len = sizeof(struct sockaddr_in);
			ia->ia_dstaddr.sin_port = 0;
			ia->ia_dstaddr.sin_addr = broadaddr.sin_addr;
			bzero(&ia->ia_dstaddr.sin_zero, sizeof(ia->ia_dstaddr.sin_zero));
			maskIsNew  = 1; /* We lie; but the effect's the same */
		}
		if (addr.sin_family == AF_INET && (hostIsNew || maskIsNew)) {
			IFA_UNLOCK(&ia->ia_ifa);
			error = in_ifinit(ifp, ia, &addr, 0);
		} else {
			IFA_UNLOCK(&ia->ia_ifa);
		}
		if (error == 0) {
			(void) ifnet_notify_address(ifp, AF_INET);
		}
		IFA_LOCK(&ia->ia_ifa);
		if ((ifp->if_flags & IFF_BROADCAST) &&
		    (broadaddr.sin_family == AF_INET)) {
			ia->ia_broadaddr.sin_family = AF_INET;
			ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in);
			ia->ia_broadaddr.sin_port = 0;
			ia->ia_broadaddr.sin_addr = broadaddr.sin_addr;
			bzero(&ia->ia_broadaddr.sin_zero, sizeof(ia->ia_broadaddr.sin_zero));
		}

		/*
		 * Report event.
		 */
		if ((error == 0) || (error == EEXIST)) {
			ev_msg.vendor_code      = KEV_VENDOR_APPLE;
			ev_msg.kev_class        = KEV_NETWORK_CLASS;
			ev_msg.kev_subclass     = KEV_INET_SUBCLASS;

			if (hostIsNew) {
				ev_msg.event_code = KEV_INET_NEW_ADDR;
			} else {
				ev_msg.event_code = KEV_INET_CHANGED_ADDR;
			}

			if (ia->ia_ifa.ifa_dstaddr) {
				in_event_data.ia_dstaddr =
				    ((struct sockaddr_in *)(void *)ia->
				    ia_ifa.ifa_dstaddr)->sin_addr;
			} else {
				in_event_data.ia_dstaddr.s_addr = INADDR_ANY;
			}
			in_event_data.ia_addr           = ia->ia_addr.sin_addr;
			in_event_data.ia_net            = ia->ia_net;
			in_event_data.ia_netmask        = ia->ia_netmask;
			in_event_data.ia_subnet         = ia->ia_subnet;
			in_event_data.ia_subnetmask     = ia->ia_subnetmask;
			in_event_data.ia_netbroadcast   = ia->ia_netbroadcast;
			IFA_UNLOCK(&ia->ia_ifa);
			(void) strlcpy(&in_event_data.link_data.if_name[0],
			    ifp->if_name, IFNAMSIZ);
			in_event_data.link_data.if_family = ifp->if_family;
			in_event_data.link_data.if_unit = ifp->if_unit;

			ev_msg.dv[0].data_ptr    = &in_event_data;
			ev_msg.dv[0].data_length = sizeof(struct kev_in_data);
			ev_msg.dv[1].data_length = 0;

			dlil_post_complete_msg(ifp, &ev_msg);
		} else {
			IFA_UNLOCK(&ia->ia_ifa);
		}
		break;
	}

	case SIOCDIFADDR:               /* struct ifreq */
		VERIFY(ia != NULL);
		error = ifnet_ioctl(ifp, PF_INET, SIOCDIFADDR, ia);
		if (error == EOPNOTSUPP) {
			error = 0;
		}
		if (error != 0) {
			break;
		}

		/* Fill out the kernel event information */
		ev_msg.vendor_code      = KEV_VENDOR_APPLE;
		ev_msg.kev_class        = KEV_NETWORK_CLASS;
		ev_msg.kev_subclass     = KEV_INET_SUBCLASS;

		ev_msg.event_code       = KEV_INET_ADDR_DELETED;

		IFA_LOCK(&ia->ia_ifa);
		if (ia->ia_ifa.ifa_dstaddr) {
			in_event_data.ia_dstaddr = ((struct sockaddr_in *)
			    (void *)ia->ia_ifa.ifa_dstaddr)->sin_addr;
		} else {
			in_event_data.ia_dstaddr.s_addr = INADDR_ANY;
		}
		in_event_data.ia_addr           = ia->ia_addr.sin_addr;
		in_event_data.ia_net            = ia->ia_net;
		in_event_data.ia_netmask        = ia->ia_netmask;
		in_event_data.ia_subnet         = ia->ia_subnet;
		in_event_data.ia_subnetmask     = ia->ia_subnetmask;
		in_event_data.ia_netbroadcast   = ia->ia_netbroadcast;
		IFA_UNLOCK(&ia->ia_ifa);
		(void) strlcpy(&in_event_data.link_data.if_name[0],
		    ifp->if_name, IFNAMSIZ);
		in_event_data.link_data.if_family = ifp->if_family;
		in_event_data.link_data.if_unit  = (u_int32_t)ifp->if_unit;

		ev_msg.dv[0].data_ptr    = &in_event_data;
		ev_msg.dv[0].data_length = sizeof(struct kev_in_data);
		ev_msg.dv[1].data_length = 0;

		ifa = &ia->ia_ifa;
		lck_rw_lock_exclusive(in_ifaddr_rwlock);
		/* Release ia_link reference */
		IFA_REMREF(ifa);
		TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link);
		IFA_LOCK(ifa);
		if (IA_IS_HASHED(ia)) {
			in_iahash_remove(ia);
		}
		IFA_UNLOCK(ifa);
		lck_rw_done(in_ifaddr_rwlock);

		/*
		 * in_ifscrub kills the interface route.
		 */
		in_ifscrub(ifp, ia, 0);
		ifnet_lock_exclusive(ifp);
		IFA_LOCK(ifa);
		/* if_detach_ifa() releases ifa_link reference */
		if_detach_ifa(ifp, ifa);
		/* Our reference to this address is dropped at the bottom */
		IFA_UNLOCK(ifa);

		/* invalidate route caches */
		routegenid_inet_update();

		/*
		 * If the interface supports multicast, and no address is left,
		 * remove the "all hosts" multicast group from that interface.
		 */
		if ((ifp->if_flags & IFF_MULTICAST) ||
		    ifp->if_allhostsinm != NULL) {
			TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
				IFA_LOCK(ifa);
				if (ifa->ifa_addr->sa_family == AF_INET) {
					IFA_UNLOCK(ifa);
					break;
				}
				IFA_UNLOCK(ifa);
			}
			ifnet_lock_done(ifp);

			lck_mtx_lock(&ifp->if_addrconfig_lock);
			if (ifa == NULL && ifp->if_allhostsinm != NULL) {
				struct in_multi *inm = ifp->if_allhostsinm;
				ifp->if_allhostsinm = NULL;

				in_delmulti(inm);
				/* release the reference for allhostsinm */
				INM_REMREF(inm);
			}
			lck_mtx_unlock(&ifp->if_addrconfig_lock);
		} else {
			ifnet_lock_done(ifp);
		}

		/* Post the kernel event */
		dlil_post_complete_msg(ifp, &ev_msg);

		/*
		 * See if there is any IPV4 address left and if so,
		 * reconfigure KDP to use current primary address.
		 */
		ifa = ifa_ifpgetprimary(ifp, AF_INET);
		if (ifa != NULL) {
			/*
			 * NOTE: SIOCSIFADDR is defined with struct ifreq
			 * as parameter, but here we are sending it down
			 * to the interface with a pointer to struct ifaddr,
			 * for legacy reasons.
			 */
			error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa);
			if (error == EOPNOTSUPP) {
				error = 0;
			}

			/* Release reference from ifa_ifpgetprimary() */
			IFA_REMREF(ifa);
		}
		(void) ifnet_notify_address(ifp, AF_INET);
		break;

	default:
		VERIFY(0);
		/* NOTREACHED */
	}

	return error;
}