int in_control(struct socket *so, unsigned long cmd, void *data, struct ifnet *ifp) { struct ifreq *ifr; struct in_ifaddr *ia, *oia; struct ifaddr *ifa; struct in_aliasreq *ifra; struct sockaddr_in oldaddr; int err, newroute, newmask, newhost; unsigned long i; /* Initialize local */ ifr = (struct ifreq *) data; ia = NULL; ifra = (struct in_aliasreq *) data; err = 0; newroute = 0; /* If interface argument sent */ if (ifp != NULL) { /* For all interfaces */ for (ia = in_ifaddr; ia != NULL; ia = ia->ia_next) if (ia->ia_ifp == ifp) break; } /* End if interface argument sent */ /******************************************/ /* Select command part1 - Initialize data */ /******************************************/ switch(cmd) { case SIOCAIFADDR: if (ia == NULL) newroute = 1; /* FALL TRU */ case SIOCDIFADDR: /* If internet family */ if (ifra->ifra_addr.sin_family == AF_INET) { /* For all interface addresses */ for (oia = ia; ia != NULL; ia = ia->ia_next) { if ((ia->ia_ifp == ifp) && (ia->ia_addr.sin_addr.s_addr == ifra->ifra_addr.sin_addr.s_addr)) break; } /* End for all interface addresses */ } /* End if internet family */ if ( (cmd == SIOCDIFADDR) && (ia == NULL) ) return EADDRNOTAVAIL; /* FALL TRU */ case SIOCSIFADDR: case SIOCSIFNETMASK: case SIOCSIFDSTADDR: if ( (so->so_state & SS_PRIV) == 0 ) return EPERM; if (ifp == NULL) panic("in control"); /* If need to alloc */ if (ia == NULL) { oia = mb_alloc(sizeof(struct in_ifaddr), MT_IFADDR, M_WAIT); if (oia == NULL) return ENOBUFS; } /* End if need to alloc */ /* Clear address */ memset(oia, 0, sizeof(struct in_ifaddr)); /* Add address to address list */ ia = in_ifaddr; /* If any addresses exists */ if (ia != NULL) { /* Go to end of address list */ for (; ia->ia_next != NULL; ia = ia->ia_next) continue; /* Set address */ ia->ia_next = oia; } /* End if any addresses exists */ /* Else no addresses exists */ else { /* Set address */ in_ifaddr = oia; } /* End else no addresses exists */ /* Add address to interface address list */ ia = oia; ifa = ifp->if_addrlist; /* If any addresses in interface address list */ if (ifa != NULL) { /* Move to last address */ for ( ; ifa->ifa_next != NULL; ifa = ifa->ifa_next) continue; /* Set address */ ifa->ifa_next = (struct ifaddr *) ia; } /* End if any addresses in interface address list */ /* Else no addresses in interface address list */ else { /* Set address */ ifp->if_addrlist = (struct ifaddr *) ia; } /* End else no addresses in interface address list */ /* Setup address struct */ ia->ia_ifa.ifa_addr = (struct sockaddr *) &ia->ia_addr; ia->ia_ifa.ifa_dstaddr = (struct sockaddr *) &ia->ia_dstaddr; ia->ia_ifa.ifa_netmask = (struct sockaddr *) &ia->ia_netmask; ia->ia_sockmask.sin_len = 8; /* If broadcast available */ if (ifp->if_flags & IFF_BROADCAST) { ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in); ia->ia_broadaddr.sin_family = AF_INET; } /* End if broadcast available */ /* Set interface point in address */ ia->ia_ifp = ifp; if (ifp != loif) in_interfaces++; break; case SIOCSIFBRDADDR: if ( (so->so_state & SS_PRIV) == 0 ) return EPERM; /* FALL TRU */ case SIOCGIFADDR: case SIOCGIFNETMASK: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: if (ia == NULL) return EADDRNOTAVAIL; break; } /* End select command part1 - Initialize data */ /******************************************/ /* Select command part2 - Execute command */ /******************************************/ switch(cmd) { case SIOCGIFADDR: /* Get address */ *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; break; case SIOCGIFBRDADDR: if ( (ifp->if_flags & IFF_BROADCAST) == 0 ) return EINVAL; /* Get address */ *((struct sockaddr_in *) &ifr->ifr_dstaddr) = ia->ia_broadaddr; break; case SIOCGIFDSTADDR: if ( (ifp->if_flags & IFF_POINTOPOINT) == 0 ) return EINVAL; /* Get address */ *((struct sockaddr_in *) &ifr->ifr_dstaddr) = ia->ia_dstaddr; break; case SIOCGIFNETMASK: /* Get mask */ *((struct sockaddr_in *) &ifr->ifr_addr) = ia->ia_sockmask; break; case SIOCSIFDSTADDR: if ( (ifp->if_flags & IFF_POINTOPOINT) == 0 ) return EINVAL; /* Set address */ oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = *(struct sockaddr_in *) &ifr->ifr_dstaddr; /* If interface ioctl fails */ if ( (ifp->if_ioctl != NULL) && (err = ( *ifp->if_ioctl) (ifp, SIOCSIFDSTADDR, ia)) ) { ia->ia_dstaddr = oldaddr; return err; } /* If interface ioctl fails */ /* If route */ if (ia->ia_flags & IFA_ROUTE) { /* Delete old route */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *) &oldaddr; rtinit(&(ia->ia_ifa), RTM_DELETE, RTF_HOST); /* Add new route */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *) &ia->ia_dstaddr; rtinit(&(ia->ia_ifa), RTM_ADD, RTF_HOST | RTF_UP); } /* If route */ break; case SIOCSIFBRDADDR: if ( (ifp->if_flags & IFF_BROADCAST) == 0 ) return EINVAL; /* Set address */ ia->ia_broadaddr = *(struct sockaddr_in *) &ifr->ifr_broadaddr; break; case SIOCSIFADDR: return in_ifinit(ifp, ia, (struct sockaddr_in *) &ifr->ifr_addr, 1); case SIOCSIFNETMASK: i = ifra->ifra_addr.sin_addr.s_addr; ia->ia_sockmask.sin_addr.s_addr = i; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); in_socktrim(&ia->ia_sockmask); break; case SIOCAIFADDR: newmask = 0; newhost = 1; /* If inet family */ if (ia->ia_addr.sin_family == AF_INET) { /* If zero length */ if (ifra->ifra_addr.sin_len == 0) { ifra->ifra_addr = ia->ia_addr; newhost = 0; } /* End if zero length */ /* Else if address match */ else if (ifra->ifra_addr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) { newhost = 0; } /* End else if address match */ } /* End if inet family */ /* If mask length non-zero */ if (ifra->ifra_mask.sin_len) { in_ifscrub(ifp, ia); ia->ia_sockmask = ifra->ifra_mask; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); newmask = 1; } /* End if mask length non-zero */ /* If point-to-point and inet */ if ( (ifp->if_flags & IFF_POINTOPOINT) && (ifra->ifra_dstaddr.sin_family == AF_INET) ) { in_ifscrub(ifp, ia); ia->ia_dstaddr = ifra->ifra_dstaddr; newmask = 1; } /* End if point-to-point and inet */ if ( (ifra->ifra_addr.sin_family == AF_INET) && (newhost || newmask) ) err = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); if ( (err == EEXIST) && !newroute) err = 0; if ( (ifp->if_flags & IFF_BROADCAST) && (ifra->ifra_broadaddr.sin_family == AF_INET) && (ifra->ifra_broadaddr.sin_addr.s_addr) ) ia->ia_broadaddr = ifra->ifra_broadaddr; return err; case SIOCDIFADDR: in_ifscrub(ifp, ia); /* Get interface address list */ ifa = ifp->if_addrlist; /* If the same address list */ if (ifa == (struct ifaddr *) ia) { ifp->if_addrlist = ifa->ifa_next; } /* End if the same address list */ /* Else not the same address list */ else { /* Move until end or match */ while ( (ifa->ifa_next != NULL) && (ifa->ifa_next != (struct ifaddr *) ia) ) ifa = ifa->ifa_next; if (ifa->ifa_next != NULL) ifa->ifa_next = ((struct ifaddr *) ia)->ifa_next; else panic("link inifaddr"); } /* End else not the same address list */ oia = ia; ia = in_ifaddr; /* If old is first element */ if (oia == ia) { in_ifaddr = ia->ia_next; } /* End if old is first element */ /* Else old is not first element */ else { /* Move until end or match */ while ( (ia->ia_next != NULL) && (ia->ia_next != oia) ) ia = ia->ia_next; if (ia->ia_next != NULL) ia->ia_next = oia->ia_next; else panic("unlink inifaddr"); } /* End else old is not first element */ IFAFREE(&oia->ia_ifa); break; default: if ( (ifp == NULL) || (ifp->if_ioctl == NULL) ) return EOPNOTSUPP; return ( *ifp->if_ioctl) (ifp, cmd, data); } /* End select command part2 - Execute command */ return 0; }
ROUTE_ENTRY * routeLookup ( struct sockaddr * pDest, /* IP address reachable with matching route */ ULONG * pMask, /* netmask value, in network byte order */ int protoId /* route source from m2Lib.h, or 0 for any. */ ) { struct radix_node_head * pHead = rt_tables[pDest->sa_family]; struct radix_node * pNode; /* radix tree entry for route */ struct rtentry * pRoute = NULL; /* candidate for matching route */ ROUTE_ENTRY * pMatch = NULL; /* matching route entry */ struct sockaddr_in netmask; /* netmask value, internal format */ char * pNetmask = NULL; /* target netmask value from tree */ struct rtentry * pNextRoute; STATUS result = ERROR; /* OK indicates a successful search */ int s; if (pHead == NULL) { /* No route table exists for the given address family. */ return (NULL); } bzero ( (char *)&netmask, sizeof (struct sockaddr_in)); s = splnet (); /* * If a specific netmask match is required, find the target value * (a pointer to the stored netmask entry) which will determine * whether the match exists. A mask value of zero indicates a * host-specific route, which does not contain a netmask entry. */ if (pMask && *pMask) { /* Setup data structure to search mask's tree for given value. */ netmask.sin_family = AF_INET; netmask.sin_len = sizeof (struct sockaddr_in); netmask.sin_addr.s_addr = *pMask; TOS_SET (&netmask, 0x1f); in_socktrim (&netmask); /* Adjust length field for tree search. */ /* Search for netmask from corresponding tree. */ pNode = rn_addmask (&netmask, 1, pHead->rnh_treetop->rn_off); if (pNode == 0) { /* No route currently uses the specified netmask. */ #ifdef DEBUG logMsg ("routeLookup: requested mask does not exist.\n", 0, 0, 0, 0, 0, 0); #endif splx(s); return (NULL); } pNetmask = pNode->rn_key; } pNode = pHead->rnh_matchaddr ((caddr_t)pDest, pHead); if (pNode && ((pNode->rn_flags & RNF_ROOT) == 0)) { /* Possible match found. Save for later use. */ pRoute = (struct rtentry *)pNode; } else { /* No route matches the given key. */ splx (s); return (NULL); } #ifdef DEBUG logMsg ("routeLookup: candidate for match at %p.\n", pNode, 0, 0, 0, 0, 0); #endif /* * Compare the set of routes available with the initial key * against the desired values. Each entry in the chain of * routes with duplicate keys uses a different netmask value. * * NOTE: The pNode value is not necessarily the start of the * chain. It is the first entry in the chain with a short * enough netmask to produce a match against the destination. */ for ( ; pRoute != NULL; pRoute = pNextRoute) { /* Select the next route candidate in case a test fails. */ pNextRoute = (struct rtentry *) ((struct radix_node *)pRoute)->rn_dupedkey; if (pMask) { /* * Check mask of route against corresponding entry from * the stored netmasks (derived from the given value). */ #ifdef DEBUG logMsg ("routeLookup: checking against specific mask.\n", 0, 0, 0, 0, 0, 0); #endif if (*pMask) { if ( ((struct radix_node *)pRoute)->rn_mask != pNetmask) continue; /* Mask values do not match. */ } else if ( ( (struct sockaddr_in *)pDest)->sin_addr.s_addr) { /* Searching for a host-specific route (no netmask entry). */ if ( ((struct radix_node *)pRoute)->rn_mask != 0) continue; /* Entry is not a host route. */ } } /* * Candidate passed any mask requirements. Search for entries * which match the specified route source. */ #ifdef DEBUG logMsg ("routeLookup: Current mask is OK.\n", 0, 0, 0, 0, 0, 0); #endif if (protoId) { /* Check source of route against specified value. */ #ifdef DEBUG logMsg ("routeLookup: testing protocol ID.\n", 0, 0, 0, 0, 0, 0); #endif for (pMatch = (ROUTE_ENTRY *)pRoute; pMatch != NULL; pMatch = pMatch->diffNode.pFrwd) { if (RT_PROTO_GET(ROUTE_ENTRY_KEY(pMatch)) == protoId) break; } if (pMatch == NULL) /* Route protocol values do not match. */ continue; #ifdef DEBUG logMsg ("routeLookup: Current protocol ID is OK.\n", 0, 0, 0, 0, 0, 0); #endif } else { /* * No route source is specified. Accept the entry * which met any mask criteria. */ pMatch = (ROUTE_ENTRY *)pRoute; } /* The candidate route entry met all criteria. Stop the search. */ result = OK; break; } if (result == OK) { /* Increase the reference count before returning the matching entry. */ pMatch->rtEntry.rt_refcnt++; } else pMatch = NULL; splx (s); return (pMatch); }
int in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, int scrub) { int s, flags, err; unsigned long i; struct sockaddr_in oldaddr; /* Initialize locals */ err = 0; i = ntohl(sin->sin_addr.s_addr); flags = RTF_UP; /* Get processor level */ s = splimp(); /* Store old address */ oldaddr = ia->ia_addr; /* Set new address */ ia->ia_addr = *sin; /* If ioctl function */ if (ifp->if_ioctl != NULL) { /* Initialize interface */ err = ( *ifp->if_ioctl) (ifp, SIOCSIFADDR, ia); if (err) { splx(s); ia->ia_addr = oldaddr; return err; } } /* End if ioctl function */ /* Restore processor level */ splx(s); /* If scrub requested */ if (scrub) { ia->ia_ifa.ifa_addr = (struct sockaddr *) &oldaddr; in_ifscrub(ifp, ia); ia->ia_ifa.ifa_addr = (struct sockaddr *) &ia->ia_addr; } /* End if scrub requested */ /* Setup netmask */ if ( IN_CLASSA(i) ) ia->ia_netmask = IN_CLASSA_NET; else if ( IN_CLASSB(i) ) ia->ia_netmask = IN_CLASSB_NET; else ia->ia_netmask = IN_CLASSC_NET; /* If subnet mask zero */ if (ia->ia_subnetmask == 0) { ia->ia_subnetmask = ia->ia_netmask; ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); } /* End if subnet mask zero */ /* Else subnet mask non-zero */ else { ia->ia_netmask &= ia->ia_subnetmask; } /* End else subnet mask non-zero */ /* Set net */ ia->ia_net = i & ia->ia_netmask; ia->ia_subnet = i & ia->ia_subnetmask; in_socktrim(&ia->ia_sockmask); /* Add route for network */ ia->ia_ifa.ifa_metric = ifp->if_metric; /* If broadcast */ if (ifp->if_flags & IFF_BROADCAST) { ia->ia_broadaddr.sin_addr.s_addr = htonl(ia->ia_subnet | ~ia->ia_subnetmask); ia->ia_netbroadcast.s_addr = htonl(ia->ia_net | ~ia->ia_netmask); } /* End if broadcast */ /* Else if loopback */ else if (ifp->if_flags & IFF_LOOPBACK) { ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; flags |= RTF_HOST; } /* End else if loopback */ /* Else if pointopoint */ else if (ifp->if_flags & IFF_POINTOPOINT) { if (ia->ia_dstaddr.sin_family != AF_INET) return 0; flags |= RTF_HOST; } /* End else if pointopoint */ /* Initialize route */ err = rtinit(&ia->ia_ifa, RTM_ADD, flags); if (err == 0) ia->ia_flags |= IFA_ROUTE; return err; }