master xplshn/aruu / shared / libutil / net.c
  1#include "../util.h"
  2
  3#include <arpa/inet.h>
  4#include <ctype.h>
  5#include <errno.h>
  6#include <ifaddrs.h>
  7#include <net/if.h>
  8#include <net/route.h>
  9#include <stdio.h>
 10#include <stdlib.h>
 11#include <string.h>
 12#include <sys/ioctl.h>
 13#include <sys/socket.h>
 14#include <unistd.h>
 15
 16#ifdef __linux__
 17#include <netpacket/packet.h>
 18#else
 19#include <net/if_dl.h>
 20#include <sys/sysctl.h>
 21#endif
 22
 23static void
 24get_mtu_metric(const char *name, int *mtu, int *metric)
 25{
 26	struct ifreq ifr;
 27	int sock;
 28
 29	*mtu = 0;
 30	*metric = 0;
 31	sock = socket(AF_INET, SOCK_DGRAM, 0);
 32	if (sock < 0)
 33		return;
 34	memset(&ifr, 0, sizeof(ifr));
 35	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
 36	if (ioctl(sock, SIOCGIFMTU, &ifr) >= 0)
 37		*mtu = ifr.ifr_mtu;
 38	if (ioctl(sock, SIOCGIFMETRIC, &ifr) >= 0)
 39		*metric = ifr.ifr_metric;
 40	close(sock);
 41}
 42
 43int
 44net_get_interfaces(struct NetInterface **ifaces, int *count)
 45{
 46	struct ifaddrs *ifaddr, *ifa;
 47	struct NetInterface *list = NULL;
 48	struct NetInterface *iface;
 49	int found_count = 0;
 50	int i;
 51
 52	if (getifaddrs(&ifaddr) < 0)
 53		return -1;
 54
 55	for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
 56		iface = NULL;
 57		for (i = 0; i < found_count; i++) {
 58			if (strcmp(list[i].name, ifa->ifa_name) == 0) {
 59				iface = &list[i];
 60				break;
 61			}
 62		}
 63		if (!iface) {
 64			list = reallocarray(list, found_count + 1, sizeof(*list));
 65			if (!list) {
 66				freeifaddrs(ifaddr);
 67				return -1;
 68			}
 69			iface = &list[found_count];
 70			memset(iface, 0, sizeof(*iface));
 71			strlcpy(iface->name, ifa->ifa_name, sizeof(iface->name));
 72			iface->flags = ifa->ifa_flags;
 73			get_mtu_metric(iface->name, &iface->mtu, &iface->metric);
 74			found_count++;
 75		}
 76
 77		if (!ifa->ifa_addr)
 78			continue;
 79
 80		if (ifa->ifa_addr->sa_family == AF_INET) {
 81			memcpy(&iface->ipv4_addr, ifa->ifa_addr, sizeof(struct sockaddr_in));
 82			iface->has_ipv4 = 1;
 83			if (ifa->ifa_netmask)
 84				memcpy(&iface->ipv4_mask, ifa->ifa_netmask, sizeof(struct sockaddr_in));
 85			if (ifa->ifa_dstaddr)
 86				memcpy(&iface->ipv4_brd, ifa->ifa_dstaddr, sizeof(struct sockaddr_in));
 87		} else if (ifa->ifa_addr->sa_family == AF_INET6) {
 88			memcpy(&iface->ipv6_addr, ifa->ifa_addr, sizeof(struct sockaddr_in6));
 89			iface->has_ipv6 = 1;
 90			/* scope id could be set here from sin6_scope_id */
 91		}
 92#ifdef __linux__
 93		else if (ifa->ifa_addr->sa_family == AF_PACKET) {
 94			struct sockaddr_ll *sll = (struct sockaddr_ll *)ifa->ifa_addr;
 95			if (sll->sll_halen == 6) {
 96				memcpy(iface->mac, sll->sll_addr, 6);
 97				iface->has_mac = 1;
 98			}
 99		}
100#else
101		else if (ifa->ifa_addr->sa_family == AF_LINK) {
102			struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
103			if (sdl->sdl_alen == 6) {
104				memcpy(iface->mac, LLADDR(sdl), 6);
105				iface->has_mac = 1;
106			}
107		}
108#endif
109	}
110
111	freeifaddrs(ifaddr);
112	*ifaces = list;
113	*count = found_count;
114	return 0;
115}
116
117#ifdef __linux__
118int
119net_get_stats(const char *ifname, struct NetStats *stats)
120{
121	FILE *fp;
122	char line[256];
123	char *p, *name;
124	int found = 0;
125
126	memset(stats, 0, sizeof(*stats));
127	fp = fopen("/proc/net/dev", "r");
128	if (!fp)
129		return -1;
130
131	while (fgets(line, sizeof(line), fp)) {
132		p = strchr(line, ':');
133		if (p) {
134			*p = '\0';
135			name = line;
136			while (isspace(*name))
137				name++;
138			if (strcmp(name, ifname) == 0) {
139				if (sscanf(p + 1, "%llu %llu %llu %llu %*u %*u %*u %*u %llu %llu %llu %llu",
140				           &stats->rx_bytes, &stats->rx_packets, &stats->rx_errs, &stats->rx_drop,
141				           &stats->tx_bytes, &stats->tx_packets, &stats->tx_errs, &stats->tx_drop) == 8) {
142					found = 1;
143				}
144				break;
145			}
146		}
147	}
148	fclose(fp);
149	return found ? 0 : -1;
150}
151#else
152int
153net_get_stats(const char *ifname, struct NetStats *stats)
154{
155	struct ifaddrs *ifaddr, *ifa;
156	int found = 0;
157
158	memset(stats, 0, sizeof(*stats));
159	if (getifaddrs(&ifaddr) < 0)
160		return -1;
161
162	for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
163		if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_LINK &&
164		    strcmp(ifa->ifa_name, ifname) == 0) {
165			struct if_data *ifd = (struct if_data *)ifa->ifa_data;
166			if (ifd) {
167				stats->rx_bytes = ifd->ifi_ibytes;
168				stats->rx_packets = ifd->ifi_ipackets;
169				stats->rx_errs = ifd->ifi_ierrors;
170				stats->rx_drop = ifd->ifi_iqdrops;
171				stats->tx_bytes = ifd->ifi_obytes;
172				stats->tx_packets = ifd->ifi_opackets;
173				stats->tx_errs = ifd->ifi_oerrors;
174				stats->tx_drop = 0;
175				found = 1;
176				break;
177			}
178		}
179	}
180	freeifaddrs(ifaddr);
181	return found ? 0 : -1;
182}
183#endif
184
185#ifdef __linux__
186int
187net_set_txqueuelen(const char *name, int qlen)
188{
189	struct ifreq ifr;
190	int sock;
191
192	sock = socket(AF_INET, SOCK_DGRAM, 0);
193	if (sock < 0)
194		return -1;
195	memset(&ifr, 0, sizeof(ifr));
196	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
197	ifr.ifr_qlen = qlen;
198	if (ioctl(sock, SIOCSIFTXQLEN, &ifr) < 0) {
199		close(sock);
200		return -1;
201	}
202	close(sock);
203	return 0;
204}
205#else
206int
207net_set_txqueuelen(const char *name, int qlen)
208{
209	(void)name;
210	(void)qlen;
211	return -1;
212}
213#endif
214
215int
216net_set_flags(const char *name, unsigned int flags, int set)
217{
218	struct ifreq ifr;
219	int sock;
220
221	sock = socket(AF_INET, SOCK_DGRAM, 0);
222	if (sock < 0)
223		return -1;
224	memset(&ifr, 0, sizeof(ifr));
225	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
226	if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
227		close(sock);
228		return -1;
229	}
230	if (set)
231		ifr.ifr_flags |= flags;
232	else
233		ifr.ifr_flags &= ~flags;
234	if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
235		close(sock);
236		return -1;
237	}
238	close(sock);
239	return 0;
240}
241
242int
243net_set_mtu(const char *name, int mtu)
244{
245	struct ifreq ifr;
246	int sock;
247
248	sock = socket(AF_INET, SOCK_DGRAM, 0);
249	if (sock < 0)
250		return -1;
251	memset(&ifr, 0, sizeof(ifr));
252	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
253	ifr.ifr_mtu = mtu;
254	if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
255		close(sock);
256		return -1;
257	}
258	close(sock);
259	return 0;
260}
261
262int
263net_set_mac(const char *name, const unsigned char mac[6])
264{
265#ifdef __linux__
266	struct ifreq ifr;
267	int sock;
268
269	sock = socket(AF_INET, SOCK_DGRAM, 0);
270	if (sock < 0)
271		return -1;
272	memset(&ifr, 0, sizeof(ifr));
273	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
274	ifr.ifr_hwaddr.sa_family = 1;
275	memcpy(ifr.ifr_hwaddr.sa_data, mac, 6);
276	if (ioctl(sock, SIOCSIFHWADDR, &ifr) < 0) {
277		close(sock);
278		return -1;
279	}
280	close(sock);
281	return 0;
282#else
283	(void)name;
284	(void)mac;
285	return -1;
286#endif
287}
288
289int
290net_add_addr(const char *name, const char *addr, int prefix)
291{
292	struct ifreq ifr;
293	struct sockaddr_in *sin;
294	int sock;
295	unsigned int mask;
296
297	sock = socket(AF_INET, SOCK_DGRAM, 0);
298	if (sock < 0)
299		return -1;
300
301	memset(&ifr, 0, sizeof(ifr));
302	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
303
304	sin = (struct sockaddr_in *)&ifr.ifr_addr;
305	sin->sin_family = AF_INET;
306	if (inet_pton(AF_INET, addr, &sin->sin_addr) <= 0) {
307		close(sock);
308		return -1;
309	}
310
311	if (ioctl(sock, SIOCSIFADDR, &ifr) < 0) {
312		close(sock);
313		return -1;
314	}
315
316	if (prefix >= 0) {
317		mask = 0;
318		if (prefix > 0)
319			mask = htonl(~0U << (32 - prefix));
320		sin = (struct sockaddr_in *)&ifr.ifr_netmask;
321		sin->sin_family = AF_INET;
322		sin->sin_addr.s_addr = mask;
323		if (ioctl(sock, SIOCSIFNETMASK, &ifr) < 0) {
324			close(sock);
325			return -1;
326		}
327	}
328
329	memset(&ifr, 0, sizeof(ifr));
330	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
331	if (ioctl(sock, SIOCGIFFLAGS, &ifr) >= 0) {
332		ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
333		ioctl(sock, SIOCSIFFLAGS, &ifr);
334	}
335
336	close(sock);
337	return 0;
338}
339
340int
341net_del_addr(const char *name, const char *addr, int prefix)
342{
343	struct ifreq ifr;
344	struct sockaddr_in *sin;
345	int sock;
346
347	(void)prefix;
348	sock = socket(AF_INET, SOCK_DGRAM, 0);
349	if (sock < 0)
350		return -1;
351
352	memset(&ifr, 0, sizeof(ifr));
353	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
354
355	sin = (struct sockaddr_in *)&ifr.ifr_addr;
356	sin->sin_family = AF_INET;
357	if (inet_pton(AF_INET, addr, &sin->sin_addr) <= 0) {
358		close(sock);
359		return -1;
360	}
361
362	if (ioctl(sock, SIOCDIFADDR, &ifr) < 0) {
363		close(sock);
364		return -1;
365	}
366	close(sock);
367	return 0;
368}
369
370int
371net_show_routes(void)
372{
373#ifdef __linux__
374	FILE *fp;
375	char line[256];
376	char iface[16], dest[16], gateway[16], mask[16];
377	unsigned int d, g, m, flags, metric;
378
379	fp = fopen("/proc/net/route", "r");
380	if (!fp)
381		return -1;
382
383	printf("%-10s %-15s %-15s %-15s %-6s %-6s\n", "Iface", "Destination", "Gateway", "Genmask", "Flags", "Metric");
384	if (fgets(line, sizeof(line), fp)) {
385		while (fgets(line, sizeof(line), fp)) {
386			if (sscanf(line, "%15s %x %x %x %*u %*u %u %x", iface, &d, &g, &m, &metric, &flags) == 6) {
387				struct in_addr dest_addr, gw_addr, mask_addr;
388				dest_addr.s_addr = d;
389				gw_addr.s_addr = g;
390				mask_addr.s_addr = m;
391				strlcpy(dest, inet_ntoa(dest_addr), sizeof(dest));
392				strlcpy(gateway, inet_ntoa(gw_addr), sizeof(gateway));
393				strlcpy(mask, inet_ntoa(mask_addr), sizeof(mask));
394				printf("%-10s %-15s %-15s %-15s 0x%04X %-6u\n", iface, dest, gateway, mask, flags, metric);
395			}
396		}
397	}
398	fclose(fp);
399	return 0;
400#else
401	printf("route listing not implemented on this OS\n");
402	return 0;
403#endif
404}
405
406int
407net_add_route(const char *dst, const char *gateway, const char *mask, const char *dev, int metric)
408{
409	struct rtentry rt;
410	struct sockaddr_in *sin;
411	int sock;
412
413	sock = socket(AF_INET, SOCK_DGRAM, 0);
414	if (sock < 0)
415		return -1;
416
417	memset(&rt, 0, sizeof(rt));
418
419	sin = (struct sockaddr_in *)&rt.rt_dst;
420	sin->sin_family = AF_INET;
421	if (strcmp(dst, "default") == 0) {
422		sin->sin_addr.s_addr = INADDR_ANY;
423	} else {
424		if (inet_pton(AF_INET, dst, &sin->sin_addr) <= 0) {
425			close(sock);
426			return -1;
427		}
428	}
429
430	if (gateway) {
431		sin = (struct sockaddr_in *)&rt.rt_gateway;
432		sin->sin_family = AF_INET;
433		if (inet_pton(AF_INET, gateway, &sin->sin_addr) <= 0) {
434			close(sock);
435			return -1;
436		}
437		rt.rt_flags |= RTF_GATEWAY;
438	}
439
440	if (mask) {
441		sin = (struct sockaddr_in *)&rt.rt_genmask;
442		sin->sin_family = AF_INET;
443		if (inet_pton(AF_INET, mask, &sin->sin_addr) <= 0) {
444			close(sock);
445			return -1;
446		}
447	}
448
449	rt.rt_flags |= RTF_UP;
450	if (dev)
451		rt.rt_dev = (char *)dev;
452	if (metric >= 0)
453		rt.rt_metric = metric + 1;
454
455	if (ioctl(sock, SIOCADDRT, &rt) < 0) {
456		close(sock);
457		return -1;
458	}
459
460	close(sock);
461	return 0;
462}
463
464int
465net_del_route(const char *dst, const char *gateway, const char *mask, const char *dev, int metric)
466{
467	struct rtentry rt;
468	struct sockaddr_in *sin;
469	int sock;
470
471	sock = socket(AF_INET, SOCK_DGRAM, 0);
472	if (sock < 0)
473		return -1;
474
475	memset(&rt, 0, sizeof(rt));
476
477	sin = (struct sockaddr_in *)&rt.rt_dst;
478	sin->sin_family = AF_INET;
479	if (strcmp(dst, "default") == 0) {
480		sin->sin_addr.s_addr = INADDR_ANY;
481	} else {
482		if (inet_pton(AF_INET, dst, &sin->sin_addr) <= 0) {
483			close(sock);
484			return -1;
485		}
486	}
487
488	if (gateway) {
489		sin = (struct sockaddr_in *)&rt.rt_gateway;
490		sin->sin_family = AF_INET;
491		if (inet_pton(AF_INET, gateway, &sin->sin_addr) <= 0) {
492			close(sock);
493			return -1;
494		}
495		rt.rt_flags |= RTF_GATEWAY;
496	}
497
498	if (mask) {
499		sin = (struct sockaddr_in *)&rt.rt_genmask;
500		sin->sin_family = AF_INET;
501		if (inet_pton(AF_INET, mask, &sin->sin_addr) <= 0) {
502			close(sock);
503			return -1;
504		}
505	}
506
507	rt.rt_flags |= RTF_UP;
508	if (dev)
509		rt.rt_dev = (char *)dev;
510	if (metric >= 0)
511		rt.rt_metric = metric + 1;
512
513	if (ioctl(sock, SIOCDELRT, &rt) < 0) {
514		close(sock);
515		return -1;
516	}
517
518	close(sock);
519	return 0;
520}
521
522int
523net_flush_addrs(const char *dev)
524{
525	struct NetInterface *ifaces = NULL;
526	int count = 0, i, r = 0;
527
528	if (net_get_interfaces(&ifaces, &count) < 0)
529		return -1;
530
531	for (i = 0; i < count; i++) {
532		if (strcmp(ifaces[i].name, dev) == 0) {
533			if (ifaces[i].has_ipv4) {
534				char addr_str[16];
535				struct sockaddr_in *sin = &ifaces[i].ipv4_addr;
536				inet_ntop(AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str));
537				if (net_del_addr(dev, addr_str, -1) < 0)
538					r = -1;
539			}
540		}
541	}
542	free(ifaces);
543	return r;
544}
545
546int
547net_set_name(const char *name, const char *newname)
548{
549#ifdef __linux__
550	struct ifreq ifr;
551	int sock;
552
553	sock = socket(AF_INET, SOCK_DGRAM, 0);
554	if (sock < 0)
555		return -1;
556	memset(&ifr, 0, sizeof(ifr));
557	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
558	strlcpy(ifr.ifr_newname, newname, sizeof(ifr.ifr_newname));
559	if (ioctl(sock, SIOCSIFNAME, &ifr) < 0) {
560		close(sock);
561		return -1;
562	}
563	close(sock);
564	return 0;
565#else
566	(void)name;
567	(void)newname;
568	return -1;
569#endif
570}