• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #define _GNU_SOURCE
2 #include <errno.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <ifaddrs.h>
7 #include <syscall.h>
8 #include <net/if.h>
9 #include <netinet/in.h>
10 #include <sys/ioctl.h>
11 #include "netlink.h"
12 #include<stdio.h>
13 
14 #define IFADDRS_HASH_SIZE 64
15 #define MAX_IF_NO 10
16 
17 /* getifaddrs() reports hardware addresses with PF_PACKET that implies
18  * struct sockaddr_ll.  But e.g. Infiniband socket address length is
19  * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
20  * to extend ssl_addr - callers should be able to still use it. */
21 struct sockaddr_ll_hack {
22 	unsigned short sll_family, sll_protocol;
23 	int sll_ifindex;
24 	unsigned short sll_hatype;
25 	unsigned char sll_pkttype, sll_halen;
26 	unsigned char sll_addr[24];
27 };
28 
29 union sockany {
30 	struct sockaddr sa;
31 	struct sockaddr_ll_hack ll;
32 	struct sockaddr_in v4;
33 	struct sockaddr_in6 v6;
34 };
35 
36 struct ifaddrs_storage {
37 	struct ifaddrs ifa;
38 	struct ifaddrs_storage *hash_next;
39 	union sockany addr, netmask, ifu;
40 	unsigned int index;
41 	char name[IFNAMSIZ+1];
42 };
43 
44 struct ifaddrs_ctx {
45 	struct ifaddrs_storage *first;
46 	struct ifaddrs_storage *last;
47 	struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
48 };
49 
freeifaddrs(struct ifaddrs * ifp)50 void freeifaddrs(struct ifaddrs *ifp)
51 {
52 	struct ifaddrs *tmp = NULL;
53 	while (ifp) {
54 		if (!ifp->ifa_name)
55 			free(ifp);
56 		if (!ifp->ifa_addr)
57 			free(ifp->ifa_addr);
58 		if (!ifp->ifa_netmask)
59 			free(ifp->ifa_netmask);
60 		tmp = ifp->ifa_next;
61 		free(ifp);
62 		ifp = tmp;
63 	}
64 }
65 
copy_addr(struct sockaddr ** r,int af,union sockany * sa,void * addr,size_t addrlen,int ifindex)66 static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex)
67 {
68 	uint8_t *dst;
69 	int len;
70 
71 	switch (af) {
72 	case AF_INET:
73 		dst = (uint8_t*) &sa->v4.sin_addr;
74 		len = 4;
75 		break;
76 	case AF_INET6:
77 		dst = (uint8_t*) &sa->v6.sin6_addr;
78 		len = 16;
79 		if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
80 			sa->v6.sin6_scope_id = ifindex;
81 		break;
82 	default:
83 		return;
84 	}
85 	if (addrlen < len) return;
86 	sa->sa.sa_family = af;
87 	memcpy(dst, addr, len);
88 	*r = &sa->sa;
89 }
90 
gen_netmask(struct sockaddr ** r,int af,union sockany * sa,int prefixlen)91 static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen)
92 {
93 	uint8_t addr[16] = {0};
94 	int i;
95 
96 	if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr);
97 	i = prefixlen / 8;
98 	memset(addr, 0xff, i);
99 	if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8));
100 	copy_addr(r, af, sa, addr, sizeof(addr), 0);
101 }
102 
copy_lladdr(struct sockaddr ** r,union sockany * sa,void * addr,size_t addrlen,int ifindex,unsigned short hatype)103 static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype)
104 {
105 	if (addrlen > sizeof(sa->ll.sll_addr)) return;
106 	sa->ll.sll_family = AF_PACKET;
107 	sa->ll.sll_ifindex = ifindex;
108 	sa->ll.sll_hatype = hatype;
109 	sa->ll.sll_halen = addrlen;
110 	memcpy(sa->ll.sll_addr, addr, addrlen);
111 	*r = &sa->sa;
112 }
113 
netlink_msg_to_ifaddr(void * pctx,struct nlmsghdr * h)114 static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
115 {
116 	struct ifaddrs_ctx *ctx = pctx;
117 	struct ifaddrs_storage *ifs, *ifs0;
118 	struct ifinfomsg *ifi = NLMSG_DATA(h);
119 	struct ifaddrmsg *ifa = NLMSG_DATA(h);
120 	struct rtattr *rta;
121 	int stats_len = 0;
122 
123 	if (h->nlmsg_type == RTM_NEWLINK) {
124 		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
125 			if (rta->rta_type != IFLA_STATS) continue;
126 			stats_len = RTA_DATALEN(rta);
127 			break;
128 		}
129 	} else {
130 		for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next)
131 			if (ifs0->index == ifa->ifa_index)
132 				break;
133 		if (!ifs0) return 0;
134 	}
135 
136 	ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
137 	if (ifs == 0) return -1;
138 
139 	if (h->nlmsg_type == RTM_NEWLINK) {
140 		ifs->index = ifi->ifi_index;
141 		ifs->ifa.ifa_flags = ifi->ifi_flags;
142 
143 		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
144 			switch (rta->rta_type) {
145 			case IFLA_IFNAME:
146 				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
147 					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
148 					ifs->ifa.ifa_name = ifs->name;
149 				}
150 				break;
151 			case IFLA_ADDRESS:
152 				copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
153 				break;
154 			case IFLA_BROADCAST:
155 				copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
156 				break;
157 			case IFLA_STATS:
158 				ifs->ifa.ifa_data = (void*)(ifs+1);
159 				memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta));
160 				break;
161 			}
162 		}
163 		if (ifs->ifa.ifa_name) {
164 			unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
165 			ifs->hash_next = ctx->hash[bucket];
166 			ctx->hash[bucket] = ifs;
167 		}
168 	} else {
169 		ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
170 		ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
171 		for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
172 			switch (rta->rta_type) {
173 			case IFA_ADDRESS:
174 				/* If ifa_addr is already set we, received an IFA_LOCAL before
175 				 * so treat this as destination address */
176 				if (ifs->ifa.ifa_addr)
177 					copy_addr(&ifs->ifa.ifa_dstaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
178 				else
179 					copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
180 				break;
181 			case IFA_BROADCAST:
182 				copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
183 				break;
184 			case IFA_LOCAL:
185 				/* If ifa_addr is set and we get IFA_LOCAL, assume we have
186 				 * a point-to-point network. Move address to correct field. */
187 				if (ifs->ifa.ifa_addr) {
188 					ifs->ifu = ifs->addr;
189 					ifs->ifa.ifa_dstaddr = &ifs->ifu.sa;
190 					memset(&ifs->addr, 0, sizeof(ifs->addr));
191 				}
192 				copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
193 				break;
194 			case IFA_LABEL:
195 				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
196 					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
197 					ifs->ifa.ifa_name = ifs->name;
198 				}
199 				break;
200 			}
201 		}
202 		if (ifs->ifa.ifa_addr)
203 			gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
204 	}
205 
206 	if (ifs->ifa.ifa_name) {
207 		if (!ctx->first) ctx->first = ifs;
208 		if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa;
209 		ctx->last = ifs;
210 	} else {
211 		free(ifs);
212 	}
213 	return 0;
214 }
215 
216 
ifaddrs_init()217 static struct ifaddrs* ifaddrs_init()
218 {
219 	struct ifaddrs *ifa = NULL;
220 	ifa = malloc(sizeof(struct ifaddrs));
221 	if (!ifa)
222 		return NULL;
223 	ifa->ifa_name = malloc(sizeof(char) *(IF_NAMESIZE + 1));
224 	ifa->ifa_addr = malloc(sizeof(struct sockaddr));
225 	ifa->ifa_netmask = malloc(sizeof(struct sockaddr));
226 	ifa->ifa_next = NULL;
227 	if (!ifa->ifa_name || !ifa->ifa_addr || !ifa->ifa_netmask) {
228 		free(ifa->ifa_name);
229 		free(ifa->ifa_addr);
230 		free(ifa->ifa_netmask);
231 		free(ifa);
232 		return NULL;
233 	}
234 	return ifa;
235 }
236 
getifaddrs(struct ifaddrs ** ifap)237 int getifaddrs(struct ifaddrs **ifap)
238 {
239 	if (ifap == NULL) {
240 		errno = EINVAL;
241 		return -1;
242 	}
243 
244 	int fd, ifno, ret;
245 	struct ifreq ifr[MAX_IF_NO];
246 	struct ifconf ifconfig;
247 	struct ifaddrs *ifstart = NULL;
248 
249 	ifconfig.ifc_buf = ifr;
250 	ifconfig.ifc_len = sizeof(ifr);
251 	if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0)
252 		return -1;
253 	if (ioctl(fd, SIOCGIFCONF, &ifconfig) < 0)
254 		goto error;
255 	if (ifconfig.ifc_len % sizeof(struct ifreq)) {
256 		errno = EINVAL;
257 		goto error;
258 	}
259 	ifno = ifconfig.ifc_len / sizeof(struct ifreq);
260 
261 	if (!(ifstart = ifaddrs_init())) {
262 		errno = ENOMEM;
263 		goto error;
264 	}
265 	struct ifaddrs *ifa = ifstart;
266 
267 	for (int i = 0; i < ifno; i++) {
268 		memcpy(ifa->ifa_name, ifr[i].ifr_name, IF_NAMESIZE);
269 		ifa->ifa_name[IF_NAMESIZE] = '\0';
270 		memcpy(ifa->ifa_addr, &ifr[i].ifr_addr, sizeof(struct sockaddr));
271 		if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) < 0)
272 			goto error;
273 		memcpy(ifa->ifa_netmask, &ifr[i].ifr_netmask, sizeof(struct sockaddr));
274 
275 		if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) < 0)
276 			goto error;
277 		ifa->ifa_flags = ifr[i].ifr_flags;
278 
279 		if (i < ifno - 1) {
280 			ifa->ifa_next = ifaddrs_init();
281 			if (!ifa->ifa_next)	{
282 				errno = ENOMEM;
283 				goto error;
284 			}
285 			ifa = ifa->ifa_next;
286 		}
287 	}
288 
289 	*ifap = ifstart;
290 	return 0;
291 
292 error:
293 	freeifaddrs(ifstart);
294 	__syscall(SYS_close, fd);
295 	return -1;
296 }
297 
298