|
| 1 | +#include <errno.h> |
1 | 2 | #include <ifaddrs.h> |
| 3 | +#include <net/if.h> |
| 4 | +#include <net/if_arp.h> |
| 5 | +#include <netinet/in.h> |
| 6 | +#include <netpacket/packet.h> |
2 | 7 | #include <stdlib.h> |
| 8 | +#include <string.h> |
| 9 | +#include <sys/ioctl.h> |
| 10 | +#include <sys/socket.h> |
| 11 | +#include <unistd.h> |
3 | 12 |
|
4 | | -// TODO |
| 13 | +namespace { |
| 14 | + |
| 15 | +struct ifaddrs *alloc_node(const char *name, size_t addrspace) { |
| 16 | + size_t sz = sizeof(struct ifaddrs) + IF_NAMESIZE + addrspace; |
| 17 | + char *block = (char *)malloc(sz); |
| 18 | + if (!block) |
| 19 | + return nullptr; |
| 20 | + memset(block, 0, sz); |
| 21 | + struct ifaddrs *ifa = (struct ifaddrs *)block; |
| 22 | + char *namep = block + sizeof(struct ifaddrs); |
| 23 | + strlcpy(namep, name, IF_NAMESIZE); |
| 24 | + ifa->ifa_name = namep; |
| 25 | + return ifa; |
| 26 | +} |
| 27 | + |
| 28 | +void *node_addr(struct ifaddrs *ifa) { |
| 29 | + return (char *)ifa + sizeof(struct ifaddrs) + IF_NAMESIZE; |
| 30 | +} |
| 31 | + |
| 32 | +void append(struct ifaddrs **head, struct ifaddrs **tail, struct ifaddrs *ifa) { |
| 33 | + if (*tail) |
| 34 | + (*tail)->ifa_next = ifa; |
| 35 | + else |
| 36 | + *head = ifa; |
| 37 | + *tail = ifa; |
| 38 | +} |
| 39 | + |
| 40 | +int add_iface(int fd, struct ifreq *src, struct ifaddrs **head, |
| 41 | + struct ifaddrs **tail) { |
| 42 | + char name[IF_NAMESIZE]; |
| 43 | + strlcpy(name, src->ifr_name, IF_NAMESIZE); |
| 44 | + |
| 45 | + struct ifreq req; |
| 46 | + short flags = 0; |
| 47 | + memset(&req, 0, sizeof(req)); |
| 48 | + strlcpy(req.ifr_name, name, IF_NAMESIZE); |
| 49 | + if (ioctl(fd, SIOCGIFFLAGS, &req) == 0) |
| 50 | + flags = req.ifr_flags; |
| 51 | + |
| 52 | + struct ifaddrs *ll = alloc_node(name, sizeof(struct sockaddr_ll)); |
| 53 | + if (!ll) |
| 54 | + return -1; |
| 55 | + struct sockaddr_ll *sll = (struct sockaddr_ll *)node_addr(ll); |
| 56 | + sll->sll_family = AF_PACKET; |
| 57 | + sll->sll_hatype = ARPHRD_ETHER; |
| 58 | + sll->sll_halen = 6; |
| 59 | + |
| 60 | + memset(&req, 0, sizeof(req)); |
| 61 | + strlcpy(req.ifr_name, name, IF_NAMESIZE); |
| 62 | + if (ioctl(fd, SIOCGIFINDEX, &req) == 0) |
| 63 | + sll->sll_ifindex = req.ifr_ifindex; |
| 64 | + |
| 65 | + memset(&req, 0, sizeof(req)); |
| 66 | + strlcpy(req.ifr_name, name, IF_NAMESIZE); |
| 67 | + if (ioctl(fd, SIOCGIFHWADDR, &req) == 0) |
| 68 | + memcpy(sll->sll_addr, req.ifr_hwaddr.sa_data, 6); |
| 69 | + |
| 70 | + ll->ifa_addr = (struct sockaddr *)sll; |
| 71 | + ll->ifa_flags = (unsigned int)flags; |
| 72 | + append(head, tail, ll); |
| 73 | + |
| 74 | + struct sockaddr_in *cfg = (struct sockaddr_in *)(void *)&src->ifr_addr; |
| 75 | + if (cfg->sin_family != AF_INET || cfg->sin_addr.s_addr == 0) |
| 76 | + return 0; |
| 77 | + |
| 78 | + struct ifaddrs *in = alloc_node(name, 2 * sizeof(struct sockaddr_in)); |
| 79 | + if (!in) |
| 80 | + return -1; |
| 81 | + struct sockaddr_in *addr = (struct sockaddr_in *)node_addr(in); |
| 82 | + struct sockaddr_in *netmask = addr + 1; |
| 83 | + |
| 84 | + memcpy(addr, cfg, sizeof(struct sockaddr_in)); |
| 85 | + addr->sin_family = AF_INET; |
| 86 | + in->ifa_addr = (struct sockaddr *)addr; |
| 87 | + |
| 88 | + memset(&req, 0, sizeof(req)); |
| 89 | + strlcpy(req.ifr_name, name, IF_NAMESIZE); |
| 90 | + if (ioctl(fd, SIOCGIFNETMASK, &req) == 0) { |
| 91 | + memcpy(netmask, &req.ifr_netmask, sizeof(struct sockaddr_in)); |
| 92 | + netmask->sin_family = AF_INET; |
| 93 | + in->ifa_netmask = (struct sockaddr *)netmask; |
| 94 | + } |
| 95 | + |
| 96 | + in->ifa_flags = (unsigned int)flags; |
| 97 | + append(head, tail, in); |
| 98 | + return 0; |
| 99 | +} |
| 100 | + |
| 101 | +} |
5 | 102 |
|
6 | 103 | int getifaddrs(struct ifaddrs **ifap) { |
7 | 104 | *ifap = nullptr; |
| 105 | + |
| 106 | + int fd = socket(AF_INET, SOCK_DGRAM, 0); |
| 107 | + if (fd < 0) |
| 108 | + return -1; |
| 109 | + |
| 110 | + struct ifconf ifc; |
| 111 | + memset(&ifc, 0, sizeof(ifc)); |
| 112 | + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { |
| 113 | + close(fd); |
| 114 | + return -1; |
| 115 | + } |
| 116 | + |
| 117 | + char *buf = nullptr; |
| 118 | + if (ifc.ifc_len > 0) { |
| 119 | + buf = (char *)malloc((size_t)ifc.ifc_len); |
| 120 | + if (!buf) { |
| 121 | + close(fd); |
| 122 | + errno = ENOMEM; |
| 123 | + return -1; |
| 124 | + } |
| 125 | + ifc.ifc_buf = buf; |
| 126 | + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { |
| 127 | + free(buf); |
| 128 | + close(fd); |
| 129 | + return -1; |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + size_t count = (size_t)ifc.ifc_len / sizeof(struct ifreq); |
| 134 | + struct ifaddrs *head = nullptr, *tail = nullptr; |
| 135 | + |
| 136 | + for (size_t i = 0; i < count; i++) { |
| 137 | + if (add_iface(fd, ((struct ifreq *)buf) + i, &head, &tail) != 0) { |
| 138 | + freeifaddrs(head); |
| 139 | + free(buf); |
| 140 | + close(fd); |
| 141 | + errno = ENOMEM; |
| 142 | + return -1; |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + free(buf); |
| 147 | + close(fd); |
| 148 | + *ifap = head; |
8 | 149 | return 0; |
9 | 150 | } |
10 | 151 |
|
|
0 commit comments