master xplshn/aruu / cmd / net / ip.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <arpa/inet.h>
  4#include <ctype.h>
  5#include <errno.h>
  6#include <net/if.h>
  7#include <stdio.h>
  8#include <stdlib.h>
  9#include <string.h>
 10#include <sys/socket.h>
 11#include <sys/types.h>
 12#include <unistd.h>
 13
 14#include "util.h"
 15
 16#if FEATURE_IP_ROUTE_ADD_DEL
 17static void
 18prefix_to_mask(int prefix, char *mask_str, size_t mask_str_len)
 19{
 20  unsigned long  mask;
 21  struct in_addr addr;
 22
 23  if (prefix < 0)
 24    prefix = 32;
 25  if (prefix > 32)
 26    eprintf("invalid prefix: %d\n", prefix);
 27
 28  if (prefix == 0) {
 29    mask = 0;
 30  } else {
 31    mask = 0xffffffffUL << (32 - prefix);
 32  }
 33  addr.s_addr = htonl(mask);
 34  strlcpy(mask_str, inet_ntoa(addr), mask_str_len);
 35}
 36#endif
 37
 38static void
 39usage(void)
 40{
 41  eprintf("usage: %s [addr | link | route] [args...]\n", argv0);
 42}
 43
 44static void
 45parse_mac(const char *str, unsigned char mac[6])
 46{
 47  unsigned int val;
 48  int          i;
 49  const char  *p = str;
 50
 51  for (i = 0; i < 6; i++) {
 52    if (i > 0) {
 53      if (*p != ':')
 54        eprintf("invalid mac address: %s\n", str);
 55      p++;
 56    }
 57    if (sscanf(p, "%2x", &val) != 1)
 58      eprintf("invalid mac address: %s\n", str);
 59    mac[i] = val;
 60    p += 2;
 61  }
 62}
 63
 64static void
 65print_link_iface(const struct NetInterface *iface)
 66{
 67  printf("%d: %s: mtu %d ", iface->metric, iface->name, iface->mtu);
 68  if (iface->flags & IFF_UP)
 69    printf("UP ");
 70  if (iface->flags & IFF_LOOPBACK)
 71    printf("LOOPBACK ");
 72  if (iface->flags & IFF_POINTOPOINT)
 73    printf("POINTOPOINT ");
 74  if (iface->flags & IFF_RUNNING)
 75    printf("RUNNING ");
 76  printf("\n");
 77  if (iface->has_mac) {
 78    printf(
 79        "    link/ether %02x:%02x:%02x:%02x:%02x:%02x\n",
 80        iface->mac[0],
 81        iface->mac[1],
 82        iface->mac[2],
 83        iface->mac[3],
 84        iface->mac[4],
 85        iface->mac[5]
 86    );
 87  }
 88}
 89
 90static void
 91print_addr_iface(const struct NetInterface *iface)
 92{
 93  char ipv6_str[INET6_ADDRSTRLEN];
 94
 95  print_link_iface(iface);
 96  if (iface->has_ipv4) {
 97    printf(
 98        "    inet %s netmask %s\n",
 99        inet_ntoa(iface->ipv4_addr.sin_addr),
100        inet_ntoa(iface->ipv4_mask.sin_addr)
101    );
102  }
103  if (iface->has_ipv6) {
104    if (inet_ntop(AF_INET6, &iface->ipv6_addr.sin6_addr, ipv6_str, sizeof(ipv6_str))) {
105      printf("    inet6 %s\n", ipv6_str);
106    }
107  }
108}
109
110static int
111do_addr(int argc, char *argv[])
112{
113  struct NetInterface *ifaces = NULL;
114  char                *cmd, *ip, *dev, *slash;
115  int                  count  = 0;
116  int                  prefix = -1;
117  int                  i, r;
118
119  if (argc == 0)
120    cmd = "show";
121  else
122    cmd = argv[0];
123
124  if (strcmp(cmd, "show") == 0 || strcmp(cmd, "list") == 0) {
125    dev = NULL;
126    if (argc > 1) {
127      if (strcmp(argv[1], "dev") == 0 && argc > 2)
128        dev = argv[2];
129      else
130        dev = argv[1];
131    }
132    if (net_get_interfaces(&ifaces, &count) < 0)
133      eprintf("net_get_interfaces:");
134    for (i = 0; i < count; i++) {
135      if (!dev || strcmp(ifaces[i].name, dev) == 0)
136        print_addr_iface(&ifaces[i]);
137    }
138    free(ifaces);
139    return 0;
140  }
141
142  if (strcmp(cmd, "add") == 0 || strcmp(cmd, "del") == 0) {
143    if (argc < 4)
144      eprintf(
145          "usage: ip addr %s <ip>/<prefix> dev "
146          "<interface>\n",
147          cmd
148      );
149    ip  = argv[1];
150    dev = NULL;
151    for (i = 2; i < argc; i++) {
152      if (strcmp(argv[i], "dev") == 0 && i + 1 < argc) {
153        dev = argv[i + 1];
154        break;
155      }
156    }
157    if (!dev)
158      eprintf("missing dev parameter\n");
159
160    slash = strchr(ip, '/');
161    if (slash) {
162      *slash = '\0';
163      prefix = atoi(slash + 1);
164    }
165
166    if (strcmp(cmd, "add") == 0)
167      r = net_add_addr(dev, ip, prefix);
168    else
169      r = net_del_addr(dev, ip, prefix);
170
171    if (r < 0)
172      eprintf("net_%s_addr:", cmd);
173    return 0;
174  }
175
176#if FEATURE_IP_ADDR_FLUSH
177  if (strcmp(cmd, "flush") == 0) {
178    dev = NULL;
179    for (i = 1; i < argc; i++) {
180      if (strcmp(argv[i], "dev") == 0 && i + 1 < argc) {
181        dev = argv[i + 1];
182        break;
183      }
184    }
185    if (!dev)
186      eprintf("missing dev parameter for flush\n");
187    if (net_flush_addrs(dev) < 0)
188      eprintf("net_flush_addrs:");
189    return 0;
190  }
191#endif
192
193  usage();
194  return 1;
195}
196
197static int
198do_link(int argc, char *argv[])
199{
200  struct NetInterface *ifaces = NULL;
201  char                *cmd, *dev, *mac_str;
202  int                  count = 0;
203  int                  i, mtu;
204  unsigned char        mac[6];
205
206  if (argc == 0)
207    cmd = "show";
208  else
209    cmd = argv[0];
210
211  if (strcmp(cmd, "show") == 0 || strcmp(cmd, "list") == 0) {
212    dev = NULL;
213    if (argc > 1) {
214      if (strcmp(argv[1], "dev") == 0 && argc > 2)
215        dev = argv[2];
216      else
217        dev = argv[1];
218    }
219    if (net_get_interfaces(&ifaces, &count) < 0)
220      eprintf("net_get_interfaces:");
221    for (i = 0; i < count; i++) {
222      if (!dev || strcmp(ifaces[i].name, dev) == 0)
223        print_link_iface(&ifaces[i]);
224    }
225    free(ifaces);
226    return 0;
227  }
228
229  if (strcmp(cmd, "set") == 0) {
230    if (argc < 3)
231      eprintf(
232          "usage: ip link set <interface> [up | down | "
233          "mtu <mtu> | address <mac>]\n"
234      );
235    dev = argv[1];
236    for (i = 2; i < argc; i++) {
237      if (strcmp(argv[i], "up") == 0) {
238        if (net_set_flags(dev, IFF_UP, 1) < 0)
239          eprintf("net_set_flags up:");
240      } else if (strcmp(argv[i], "down") == 0) {
241        if (net_set_flags(dev, IFF_UP, 0) < 0)
242          eprintf("net_set_flags down:");
243      } else if (strcmp(argv[i], "mtu") == 0 && i + 1 < argc) {
244        mtu = atoi(argv[i + 1]);
245        if (net_set_mtu(dev, mtu) < 0)
246          eprintf("net_set_mtu:");
247        i++;
248      } else if (strcmp(argv[i], "address") == 0 && i + 1 < argc) {
249        mac_str = argv[i + 1];
250        parse_mac(mac_str, mac);
251        if (net_set_mac(dev, mac) < 0)
252          eprintf("net_set_mac:");
253        i++;
254#if FEATURE_IP_LINK_SET
255#ifdef IFF_NOARP
256      } else if (strcmp(argv[i], "arp") == 0 && i + 1 < argc) {
257        int set = (strcmp(argv[i + 1], "off") == 0);
258        if (net_set_flags(dev, IFF_NOARP, set) < 0)
259          eprintf("net_set_flags arp:");
260        i++;
261#endif
262#ifdef IFF_MULTICAST
263      } else if (strcmp(argv[i], "multicast") == 0 && i + 1 < argc) {
264        int set = (strcmp(argv[i + 1], "on") == 0);
265        if (net_set_flags(dev, IFF_MULTICAST, set) < 0)
266          eprintf("net_set_flags multicast:");
267        i++;
268#endif
269#ifdef IFF_ALLMULTI
270      } else if (strcmp(argv[i], "allmulticast") == 0 && i + 1 < argc) {
271        int set = (strcmp(argv[i + 1], "on") == 0);
272        if (net_set_flags(dev, IFF_ALLMULTI, set) < 0)
273          eprintf("net_set_flags allmulticast:");
274        i++;
275#endif
276#ifdef IFF_PROMISC
277      } else if (strcmp(argv[i], "promisc") == 0 && i + 1 < argc) {
278        int set = (strcmp(argv[i + 1], "on") == 0);
279        if (net_set_flags(dev, IFF_PROMISC, set) < 0)
280          eprintf("net_set_flags promisc:");
281        i++;
282#endif
283      } else if (strcmp(argv[i], "name") == 0 && i + 1 < argc) {
284        if (net_set_name(dev, argv[i + 1]) < 0)
285          eprintf("net_set_name:");
286        dev = argv[i + 1];
287        i++;
288      } else if (strcmp(argv[i], "txqueuelen") == 0 && i + 1 < argc) {
289        int qlen = atoi(argv[i + 1]);
290        if (net_set_txqueuelen(dev, qlen) < 0)
291          eprintf("net_set_txqueuelen:");
292        i++;
293#endif
294      }
295    }
296    return 0;
297  }
298
299  usage();
300  return 1;
301}
302
303static int
304do_route(int argc, char *argv[])
305{
306  char       *cmd, *dst, *slash;
307  const char *gateway = NULL, *dev = NULL;
308  int         prefix_len = -1, metric = -1, i;
309  char        mask_str[32];
310
311  if (argc == 0)
312    cmd = "show";
313  else
314    cmd = argv[0];
315
316  if (strcmp(cmd, "show") == 0 || strcmp(cmd, "list") == 0) {
317    if (net_show_routes() < 0)
318      eprintf("net_show_routes:");
319    return 0;
320  }
321
322#if FEATURE_IP_ROUTE_ADD_DEL
323  if (strcmp(cmd, "add") == 0 || strcmp(cmd, "del") == 0) {
324    if (argc < 2)
325      eprintf(
326          "usage: ip route %s <prefix> [via <gateway>] "
327          "[dev <device>] [metric <metric>]\n",
328          cmd
329      );
330
331    dst   = argv[1];
332    slash = strchr(dst, '/');
333    if (slash) {
334      *slash     = '\0';
335      prefix_len = atoi(slash + 1);
336    } else if (strcmp(dst, "default") == 0) {
337      prefix_len = 0;
338    }
339
340    prefix_to_mask(prefix_len, mask_str, sizeof(mask_str));
341
342    for (i = 2; i < argc; i++) {
343      if (strcmp(argv[i], "via") == 0 && i + 1 < argc) {
344        gateway = argv[i + 1];
345        i++;
346      } else if (strcmp(argv[i], "dev") == 0 && i + 1 < argc) {
347        dev = argv[i + 1];
348        i++;
349      } else if (strcmp(argv[i], "metric") == 0 && i + 1 < argc) {
350        metric = atoi(argv[i + 1]);
351        i++;
352      }
353    }
354
355    if (strcmp(cmd, "add") == 0) {
356      if (net_add_route(dst, gateway, mask_str, dev, metric) < 0)
357        eprintf("net_add_route:");
358    } else {
359      if (net_del_route(dst, gateway, mask_str, dev, metric) < 0)
360        eprintf("net_del_route:");
361    }
362    return 0;
363  }
364#endif
365
366  usage();
367  return 1;
368}
369
370// ?man ip: show or configure routing and devices
371// ?man arguments: [addr | link | route] [args ...]
372// ?man configure and view network devices, routing, and tunnels
373// ?man ## COMMANDS
374// ?man ### addr [show | list [dev <device>]]
375// ?man show interface addresses
376// ?man ### addr add <ip>/<prefix> dev <device>
377// ?man add address to interface
378// ?man ### addr del <ip>/<prefix> dev <device>
379// ?man delete address from interface
380// ?man ### addr flush dev <device>
381// ?man flush all addresses from interface
382// ?man ### link [show | list [dev <device>]]
383// ?man show interface link states
384// ?man ### link set <interface> [up | down | mtu <mtu> | address <mac> | arp
385// on|off | multicast on|off | allmulticast on|off | promisc on|off | name
386// <newname> | txqueuelen <qlen>] ?man configure interface settings ?man ###
387// route [show | list] ?man show routing table ?man ### route add <prefix> [via
388// <gateway>] [dev <device>] [metric <metric>] ?man add route to routing table
389// ?man ### route del <prefix> [via <gateway>] [dev <device>] [metric <metric>]
390// ?man delete route from routing table
391int
392main(int argc, char *argv[])
393{
394  char *obj;
395
396  ARGBEGIN
397  {
398    default:
399      usage();
400  }
401  ARGEND
402
403  if (argc == 0)
404    usage();
405
406  obj = argv[0];
407
408  if (strcmp(obj, "addr") == 0 || strcmp(obj, "address") == 0) {
409    return do_addr(argc - 1, argv + 1);
410  } else if (strcmp(obj, "link") == 0) {
411    return do_link(argc - 1, argv + 1);
412  } else if (strcmp(obj, "route") == 0) {
413    return do_route(argc - 1, argv + 1);
414  }
415
416  usage();
417
418  if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
419    return 1;
420
421  return 0;
422}