master xplshn/aruu / cmd / net / ip.c
  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}