#ifndef lint static char *sccsid = "@(#)subnet.c 1.5 (Berkeley) 1/4/88"; #endif #include "../common/conf.h" #ifdef SUBNET #include #include #include #ifndef NETMASK #include #endif #include /* * The following routines provide a general interface for * subnet support. Like the library function "inet_netof", * which returns the standard (i.e., non-subnet) network * portion of an internet address, "inet_snetof" returns * the subnetwork portion -- if there is one. If there * isn't, it returns 0. * * Subnets, under 4.3, are specific to a given set of * machines -- right down to the network interfaces. * Because of this, the function "getifconf" must be * called first. This routine builds a table listing * all the (internet) interfaces present on a machine, * along with their subnet masks. Then when inet_snetof * is called, it can quickly scan this table. * * Unfortunately, there "ain't no graceful way" to handle * certain situations. For example, the kernel permits * arbitrary subnet bits -- that is, you could have a * 22 bit network field and a 10 bit subnet field. * However, due to braindamage at the user level, in * such sterling routines as getnetbyaddr, you need to * have a subnet mask which is an even multiple of 8. * Unless you are running with class C subnets, in which * case it should be a multiple of 4. Because of this rot, * if you have non-multiples of 4 bits of subnet, you should * define DAMAGED_NETMASK when you compile. This will round * things off to a multiple of 8 bits. * * Finally, you may want subnet support even if your system doesn't * support the ioctls to get subnet mask information. If you want * such a thing, you can define NETMASK to be a constant that is * the subnet mask for your network. * * And don't *even* get me started on how the definitions of the inet_foo() * routines changed between 4.2 and 4.3, making internet addresses * be unsigned long vs. struct in_addr. Don't blame me if this * won't lint... */ /* * One structure for each interface, containing * the network number and subnet mask, stored in HBO. */ struct in_if { u_long i_net; /* Network number, shifted right */ u_long i_subnetmask; /* Subnet mask for this if */ int i_bitshift; /* How many bits right for outside */ }; /* * Table (eventually, once we malloc) of * internet interface subnet informaiton. */ static struct in_if *in_ifsni; static int if_count; /* * Get the network interface configuration, * and squirrel away the network numbers and * subnet masks of each interface. Return * number of interfaces found, or -1 on error. * N.B.: don't call this more than once... */ getifconf() { #ifndef NETMASK register int i, j; int s; struct ifconf ifc; char buf[1024]; register struct ifreq *ifr; u_long inet_netof(); u_long addr; /* * Find out how many interfaces we have, and malloc * room for information about each one. */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return (-1); ifc.ifc_buf = buf; ifc.ifc_len = sizeof (buf); if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { (void) close(s); return (-1); } /* * if_count here is the count of possible * interfaces we may be interested in... actual * interfaces may be less (some may not be internet, * not all are necessarily up, etc.) */ if_count = ifc.ifc_len / sizeof (struct ifreq); in_ifsni = (struct in_if *) malloc(if_count * sizeof (struct in_if)); if (in_ifsni == 0) { (void) close(s); return (-1); } for (i = j = 0; i < if_count; ++i) { ifr = &ifc.ifc_req[i]; if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) continue; if ((ifr->ifr_flags & IFF_UP) == 0) continue; if (ioctl(s, SIOCGIFADDR, ifr) < 0) continue; if (ifr->ifr_addr.sa_family != AF_INET) continue; addr = (*(struct sockaddr_in *)&ifr->ifr_addr).sin_addr.s_addr; in_ifsni[j].i_net = inet_netof(addr); if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) continue; in_ifsni[j].i_subnetmask = ntohl((*(struct sockaddr_in *)&ifr->ifr_addr).sin_addr.s_addr); /* * The following should "never happen". But under SunOs * 3.4, along with the rest of their broken networking code, * SIOCGIFNETMASK can get a netmask which is 0. There * really isn't anything that "right" that we can do * about it, so we'll set their subnet mask to be their * *net*work mask. Which may or may not be right. */ if (in_ifsni[j].i_subnetmask == 0) { addr = ntohl(addr); if (IN_CLASSA(addr)) in_ifsni[j].i_subnetmask = IN_CLASSA_NET; else if (IN_CLASSB(addr)) in_ifsni[j].i_subnetmask = IN_CLASSB_NET; else if (IN_CLASSC(addr)) in_ifsni[j].i_subnetmask = IN_CLASSC_NET; else /* what to do ... */ in_ifsni[j].i_subnetmask = IN_CLASSC_NET; } else in_ifsni[j].i_bitshift = bsr(in_ifsni[j].i_subnetmask); j++; } if_count = j; (void) close(s); return (if_count); #else /* hard-coded subnets */ if_count = 1; in_ifsni = (struct in_if *) malloc(if_count * sizeof (struct in_if)); if (in_ifsni == 0) { return (-1); } in_ifsni[0].i_net = 0; in_ifsni[0].i_subnetmask = NETMASK; in_ifsni[0].i_bitshift = bsr(in_ifsni[0].i_subnetmask); return (if_count); #endif } /* * Return the (sub)network number from an internet address. * "in" is in NBO, return value in host byte order. * If "in" is not a subnet, return 0. */ u_long inet_snetof(in) u_long in; { register int j; register u_long i = ntohl(in); register u_long net; u_long inet_netof(), inet_lnaof(); net = inet_netof(in); /* * Check whether network is a subnet; * if so, return subnet number. */ for (j = 0; j < if_count; ++j) #ifdef NETMASK if (1) { #else if (net == in_ifsni[j].i_net) { #endif net = i & in_ifsni[j].i_subnetmask; if (inet_lnaof(htonl(net)) == 0) return (0); else return (net >> in_ifsni[j].i_bitshift); } return (0); } /* * Return the number of bits required to * shift right a mask into a getnetent-able entitity. */ bsr(mask) register int mask; { register int count = 0; if (mask == 0) /* "never happen", except with SunOs 3.4 */ return (0); while ((mask & 1) == 0) { ++count; mask >>= 1; } #ifdef DAMAGED_NETMASK count /= 8; /* XXX gag retch puke barf */ count *= 8; #endif return (count); } #endif