| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 | #include <stdlib.h> | 
 |  | 
 | #include <iplib/iplib.h> | 
 | #include <parlib/parlib.h> | 
 | #include <signal.h> | 
 | #include <stdio.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include <dirent.h> | 
 | #include <fcntl.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 |  | 
 | static uint8_t loopbacknet[IPaddrlen] = { | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0xff, 0xff, | 
 | 	127, 0, 0, 0 | 
 | }; | 
 | static uint8_t loopbackmask[IPaddrlen] = { | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0, 0, 0 | 
 | }; | 
 |  | 
 | #define NFIELD 200 | 
 | #define nelem(x) (sizeof(x) / sizeof(x[0])) | 
 | static struct ipifc **_readoldipifc(char *buf, struct ipifc **l, int index) | 
 | { | 
 | 	char *f[NFIELD]; | 
 | 	int i, n; | 
 | 	struct ipifc *ifc; | 
 | 	struct iplifc *lifc, **ll; | 
 |  | 
 | 	/* allocate new interface */ | 
 | 	*l = ifc = calloc(sizeof(struct ipifc), 1); | 
 | 	if (ifc == NULL) | 
 | 		return l; | 
 | 	l = &ifc->next; | 
 | 	ifc->index = index; | 
 |  | 
 | 	n = tokenize(buf, f, NFIELD); | 
 | 	if (n < 2) | 
 | 		return l; | 
 |  | 
 | 	strncpy(ifc->dev, f[0], sizeof ifc->dev); | 
 | 	ifc->dev[sizeof(ifc->dev) - 1] = 0; | 
 | 	ifc->mtu = strtoul(f[1], NULL, 10); | 
 |  | 
 | 	ll = &ifc->lifc; | 
 | 	for (i = 2; n - i >= 7; i += 7) { | 
 | 		/* allocate new local address */ | 
 | 		*ll = lifc = calloc(sizeof(struct iplifc), 1); | 
 | 		ll = &lifc->next; | 
 |  | 
 | 		parseip(lifc->ip, f[i]); | 
 | 		parseipmask(lifc->mask, f[i + 1]); | 
 | 		parseip(lifc->net, f[i + 2]); | 
 | 		ifc->pktin = strtoul(f[i + 3], NULL, 10); | 
 | 		ifc->pktout = strtoul(f[i + 4], NULL, 10); | 
 | 		ifc->errin = strtoul(f[i + 5], NULL, 10); | 
 | 		ifc->errout = strtoul(f[i + 6], NULL, 10); | 
 | 	} | 
 | 	return l; | 
 | } | 
 |  | 
 | static char *findfield(char *name, char **f, int n) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < n - 1; i++) | 
 | 		if (strcmp(f[i], name) == 0) | 
 | 			return f[i + 1]; | 
 | 	return ""; | 
 | } | 
 |  | 
 | static struct ipifc **_readipifc(char *file, struct ipifc **l, int index) | 
 | { | 
 | 	int i, n, fd, lines; | 
 | 	char buf[4 * 1024]; | 
 | 	char *line[32]; | 
 | 	char *f[64]; | 
 | 	struct ipifc *ifc, **l0; | 
 | 	struct iplifc *lifc, **ll; | 
 |  | 
 | 	/* read the file */ | 
 | 	fd = open(file, O_RDONLY); | 
 | 	if (fd < 0) | 
 | 		return l; | 
 | 	n = 0; | 
 | 	while ((i = read(fd, buf + n, sizeof(buf) - 1 - n)) > 0 && | 
 | 	       n < sizeof(buf) - 1) | 
 | 		n += i; | 
 | 	buf[n] = 0; | 
 | 	close(fd); | 
 |  | 
 | 	if (strncmp(buf, "device", 6) != 0) | 
 | 		return _readoldipifc(buf, l, index); | 
 | 	/* ignore ifcs with no associated device */ | 
 | 	if (strncmp(buf + 6, "  ", 2) == 0) | 
 | 		return l; | 
 | 	/* allocate new interface */ | 
 | 	*l = ifc = calloc(sizeof(struct ipifc), 1); | 
 | 	if (ifc == NULL) | 
 | 		return l; | 
 | 	l0 = l; | 
 | 	l = &ifc->next; | 
 | 	ifc->index = index; | 
 |  | 
 | 	lines = getfields(buf, line, nelem(line), 1, "\n"); | 
 |  | 
 | 	/* pick off device specific info(first line) */ | 
 | 	n = tokenize(line[0], f, nelem(f)); | 
 | 	if (n % 2 != 0) | 
 | 		goto lose; | 
 | 	strncpy(ifc->dev, findfield("device", f, n), sizeof(ifc->dev)); | 
 | 	ifc->dev[sizeof(ifc->dev) - 1] = 0; | 
 | 	if (ifc->dev[0] == 0) { | 
 | 	lose: | 
 | 		free(ifc); | 
 | 		*l0 = NULL; | 
 | 		return l; | 
 | 	} | 
 | 	ifc->mtu = strtoul(findfield("maxtu", f, n), NULL, 10); | 
 | 	ifc->sendra6 = atoi(findfield("sendra", f, n)); | 
 | 	ifc->recvra6 = atoi(findfield("recvra", f, n)); | 
 | 	ifc->rp.mflag = atoi(findfield("mflag", f, n)); | 
 | 	ifc->rp.oflag = atoi(findfield("oflag", f, n)); | 
 | 	ifc->rp.maxraint = atoi(findfield("maxraint", f, n)); | 
 | 	ifc->rp.minraint = atoi(findfield("minraint", f, n)); | 
 | 	ifc->rp.linkmtu = atoi(findfield("linkmtu", f, n)); | 
 | 	ifc->rp.reachtime = atoi(findfield("reachtime", f, n)); | 
 | 	ifc->rp.rxmitra = atoi(findfield("rxmitra", f, n)); | 
 | 	ifc->rp.ttl = atoi(findfield("ttl", f, n)); | 
 | 	ifc->rp.routerlt = atoi(findfield("routerlt", f, n)); | 
 | 	ifc->pktin = strtoul(findfield("pktin", f, n), NULL, 10); | 
 | 	ifc->pktout = strtoul(findfield("pktout", f, n), NULL, 10); | 
 | 	ifc->errin = strtoul(findfield("errin", f, n), NULL, 10); | 
 | 	ifc->errout = strtoul(findfield("errout", f, n), NULL, 10); | 
 |  | 
 | 	/* now read the addresses */ | 
 | 	ll = &ifc->lifc; | 
 | 	for (i = 1; i < lines; i++) { | 
 | 		n = tokenize(line[i], f, nelem(f)); | 
 | 		if (n < 5) | 
 | 			break; | 
 |  | 
 | 		/* allocate new local address */ | 
 | 		*ll = lifc = calloc(sizeof(struct iplifc), 1); | 
 | 		ll = &lifc->next; | 
 |  | 
 | 		parseip(lifc->ip, f[0]); | 
 | 		parseipmask(lifc->mask, f[1]); | 
 | 		parseip(lifc->net, f[2]); | 
 |  | 
 | 		lifc->validlt = strtoul(f[3], NULL, 10); | 
 | 		lifc->preflt = strtoul(f[4], NULL, 10); | 
 | 	} | 
 |  | 
 | 	return l; | 
 | } | 
 |  | 
 | void free_ipifc(struct ipifc *ifc) | 
 | { | 
 | 	struct ipifc *next; | 
 | 	struct iplifc *lnext, *lifc; | 
 |  | 
 | 	if (ifc == NULL) | 
 | 		return; | 
 | 	for (; ifc; ifc = next) { | 
 | 		next = ifc->next; | 
 | 		for (lifc = ifc->lifc; lifc; lifc = lnext) { | 
 | 			lnext = lifc->next; | 
 | 			free(lifc); | 
 | 		} | 
 | 		free(ifc); | 
 | 	} | 
 | } | 
 |  | 
 | /* This will free @ifc when passed in.  Some old Plan 9 programs might rely on | 
 |  * it still (like our ping, netstat, ipconfig).  It usually ends up be a | 
 |  * thread-unsafe disaster. */ | 
 | struct ipifc *readipifc(char *net, struct ipifc *ifc, int index) | 
 | { | 
 | 	int fd, i, n; | 
 | 	struct dir *dir; | 
 | 	char directory[128]; | 
 | 	char buf[128]; | 
 | 	struct ipifc **l; | 
 |  | 
 | 	free_ipifc(ifc); | 
 |  | 
 | 	l = &ifc; | 
 | 	ifc = NULL; | 
 |  | 
 | 	if (net == 0) | 
 | 		net = "/net"; | 
 | 	snprintf(directory, sizeof(directory), "%s/ipifc", net); | 
 |  | 
 | 	if (index >= 0) { | 
 | 		snprintf(buf, sizeof(buf), "%s/%d/status", directory, index); | 
 | 		_readipifc(buf, l, index); | 
 | 	} else { | 
 | 		DIR *d; | 
 | 		struct dirent *de; | 
 | 		d = opendir(directory); | 
 | 		if (!d) | 
 | 			return NULL; | 
 |  | 
 | 		while (de = readdir(d)) { | 
 | 			if (strcmp(de->d_name, "clone") == 0) | 
 | 				continue; | 
 | 			if (strcmp(de->d_name, "stats") == 0) | 
 | 				continue; | 
 | 			snprintf(buf, sizeof(buf), "%s/%s/status", directory, de->d_name); | 
 | 			l = _readipifc(buf, l, atoi(de->d_name)); | 
 | 		} | 
 | 		closedir(d); | 
 | 	} | 
 |  | 
 | 	return ifc; | 
 | } | 
 |  | 
 | /* Gets the local interface that isn't the friggin' loopback address.  When | 
 |  * you're done, free_ipifc(ifc).  Returns 0 on failure. */ | 
 | struct iplifc *get_first_noloop_iplifc(char *net, struct ipifc **ifc) | 
 | { | 
 | 	struct ipifc *nifc; | 
 | 	struct iplifc *lifc; | 
 | 	uint8_t mynet[IPaddrlen]; | 
 |  | 
 | 	*ifc = readipifc(net, NULL, -1); | 
 | 	for (nifc = *ifc; nifc; nifc = nifc->next) { | 
 | 		for (lifc = nifc->lifc; lifc; lifc = lifc->next) { | 
 | 			maskip(lifc->ip, loopbackmask, mynet); | 
 | 			if (ipcmp(mynet, loopbacknet) == 0) | 
 | 				continue; | 
 | 			if (ipcmp(lifc->ip, IPnoaddr) != 0) | 
 | 				return lifc; | 
 | 		} | 
 | 	} | 
 | 	free_ipifc(*ifc); | 
 | 	return 0; | 
 | } |