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