| #define _LARGEFILE64_SOURCE /* needed to use lseek64 */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <fcntl.h> | 
 | #include <parlib/arch/arch.h> | 
 | #include <unistd.h> | 
 | #include <errno.h> | 
 | #include <dirent.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include <dirent.h> | 
 | #include <arpa/inet.h> | 
 | #include <sys/socket.h> | 
 | #include <netdb.h> | 
 | #include <ifaddrs.h> | 
 | #include <ros/syscall.h> | 
 | #include <ros/fs.h> | 
 | #include <iplib/iplib.h> | 
 | #include <netpacket/packet.h> | 
 |  | 
 | /* Given a list of ifas (possibly null), prepend ifas for ethernet devices. | 
 |  * Returns the new head of the list (also possibly null). */ | 
 | static struct ifaddrs *get_ether_addrs(struct ifaddrs *ifa) | 
 | { | 
 | 	struct ifaddrs *new_ifa; | 
 | 	struct sockaddr_ll *sa_ll; | 
 | 	DIR *net; | 
 | 	struct dirent *d; | 
 | 	int addr_fd; | 
 | 	char path[MAX_PATH_LEN]; | 
 | 	/* 6 is known everywhere, defined nowhere. So is 6 * 2 */ | 
 | 	char etheraddr[12]; | 
 | 	#define SIZE_OF_ETHER 5 | 
 |  | 
 | 	net = opendir("/net"); | 
 | 	if (net == NULL) | 
 | 		return ifa; | 
 |  | 
 | 	for (d = readdir(net); d; d = readdir(net)) { | 
 | 		if (strncmp(d->d_name, "ether", SIZE_OF_ETHER)) | 
 | 			continue; | 
 | 		snprintf(path, sizeof(path), "/net/%s/addr", d->d_name); | 
 | 		addr_fd = open(path, O_RDONLY); | 
 | 		if (addr_fd < 0) | 
 | 			continue; | 
 | 		if (read(addr_fd, etheraddr, | 
 | 			 sizeof(etheraddr)) < sizeof(etheraddr)) { | 
 | 			fprintf(stderr, "Read addr from %s: %r", d->d_name); | 
 | 			close(addr_fd); | 
 | 			continue; | 
 | 		} | 
 | 		/* getifaddrs is a stupid design as it only admits of | 
 | 		 * one address per interface.  Don't even bother | 
 | 		 * filling in ifa_{addr,netmask}. They're allowed to | 
 | 		 * be NULL.  Broadaddr need be set IFF a bit is set | 
 | 		 * in the flags field. We don't set either one. | 
 | 		 */ | 
 | 		new_ifa = calloc(sizeof(*ifa), 1); | 
 | 		new_ifa->ifa_next = ifa; | 
 | 		ifa = new_ifa; | 
 | 		ifa->ifa_name = strdup(d->d_name); | 
 | 		sa_ll = calloc(sizeof(struct sockaddr_ll), 1); | 
 | 		ifa->ifa_addr = (struct sockaddr*)sa_ll; | 
 | 		sa_ll->sll_family = AF_PACKET; | 
 | 		/* TODO: could set protocol and hatype, if we ever get the | 
 | 		 * headers for the options.  Probably not worth it. */ | 
 | 		sa_ll->sll_ifindex = atoi(&ifa->ifa_name[SIZE_OF_ETHER]); | 
 | 		sa_ll->sll_halen = 6; | 
 | 		for (int i = 0; i < 6; i++) | 
 | 			sscanf(ðeraddr[i * 2], "%02x", &sa_ll->sll_addr[i]); | 
 | 		close(addr_fd); | 
 | 	} | 
 | 	closedir(net); | 
 | 	return ifa; | 
 | } | 
 |  | 
 | /* Given a list of ifas (possibly null), prepend an ifa for the given lifc. */ | 
 | static struct ifaddrs *get_lifc_addr(struct ifaddrs *ifa, struct iplifc *lifc) | 
 | { | 
 | 	struct ifaddrs *new_ifa; | 
 | 	struct sockaddr_in *sa_in, *mask_in; | 
 | 	struct sockaddr_in6 *sa_in6, *mask_in6; | 
 |  | 
 | 	if (!ipcmp(lifc->ip, IPnoaddr)) | 
 | 		return ifa; | 
 | 	new_ifa = calloc(sizeof(*ifa), 1); | 
 | 	new_ifa->ifa_next = ifa; | 
 | 	ifa = new_ifa; | 
 | 	ifa->ifa_name = NULL; | 
 | 	if (isv4(lifc->ip)) { | 
 | 		sa_in = calloc(sizeof(struct sockaddr_in), 1); | 
 | 		sa_in->sin_family = AF_INET; | 
 | 		ifa->ifa_addr = (struct sockaddr*)sa_in; | 
 | 		v6tov4((uint8_t*)&sa_in->sin_addr, lifc->ip); | 
 |  | 
 | 		mask_in = calloc(sizeof(struct sockaddr_in), 1); | 
 | 		mask_in->sin_family = AF_INET; | 
 | 		ifa->ifa_netmask = (struct sockaddr*)mask_in; | 
 | 		/* The V4 mask is the last four bytes of the full v6 mask */ | 
 | 		memcpy((uint8_t*)&mask_in->sin_addr, &lifc->mask[12], | 
 | 		       sizeof(struct in_addr)); | 
 | 	} else { | 
 | 		/* TODO: check this when we have a v6 system */ | 
 | 		sa_in6 = calloc(sizeof(struct sockaddr_in6), 1); | 
 | 		sa_in6->sin6_family = AF_INET6; | 
 | 		ifa->ifa_addr = (struct sockaddr*)sa_in6; | 
 | 		memcpy(&sa_in6->sin6_addr, lifc->ip, sizeof(struct in6_addr)); | 
 |  | 
 | 		mask_in6 = calloc(sizeof(struct sockaddr_in6), 1); | 
 | 		mask_in6->sin6_family = AF_INET6; | 
 | 		ifa->ifa_netmask = (struct sockaddr*)mask_in6; | 
 | 		memcpy((uint8_t*)&mask_in6->sin6_addr, lifc->mask, | 
 | 		       sizeof(struct in6_addr)); | 
 | 	} | 
 | 	return ifa; | 
 | } | 
 |  | 
 | /* Given a list of ifas (possibly null), prepend ifas for inet devices. | 
 |  * Returns the new head of the list (also possibly null). */ | 
 | static struct ifaddrs *get_inet_addrs(struct ifaddrs *ifa) | 
 | { | 
 | 	struct ipifc *ifc, *ifc_i; | 
 | 	struct iplifc *lifc_i; | 
 |  | 
 | 	/* This gives us a list of ipifcs (actual interfaces), each of which has | 
 | 	 * a list of lifcs (local interface, including the IP addr). */ | 
 | 	ifc = readipifc(NULL, NULL, -1); | 
 | 	for (ifc_i = ifc; ifc_i; ifc_i = ifc_i->next) { | 
 | 		for (lifc_i = ifc_i->lifc; lifc_i; lifc_i = lifc_i->next) | 
 | 			ifa = get_lifc_addr(ifa, lifc_i); | 
 | 	} | 
 | 	free_ipifc(ifc); | 
 | 	return ifa; | 
 | } | 
 |  | 
 | int getifaddrs(struct ifaddrs **ifap) | 
 | { | 
 | 	struct ifaddrs *ifa = NULL; | 
 |  | 
 | 	*ifap = NULL; | 
 | 	ifa = get_ether_addrs(ifa); | 
 | 	ifa = get_inet_addrs(ifa); | 
 | 	*ifap = ifa; | 
 | 	return 0; | 
 | } | 
 |  | 
 | void freeifaddrs(struct ifaddrs *ifa) | 
 | { | 
 | 	struct ifaddrs *next = ifa; | 
 |  | 
 | 	while (ifa) { | 
 | 		next = ifa->ifa_next; | 
 |  | 
 | 		free(ifa->ifa_name); | 
 | 		free(ifa->ifa_addr); | 
 | 		free(ifa->ifa_netmask); | 
 | 		free(ifa); | 
 |  | 
 | 		ifa = next; | 
 | 	} | 
 | } |