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