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