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}