| #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; |
| } |
| } |