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}