iplink.c revision aba5acdfdb347d2c21fc67d613d83d4430ca3937
1/*
2 * iplink.c		"ip link".
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <errno.h>
19#include <sys/socket.h>
20#include <linux/if.h>
21#include <linux/if_packet.h>
22#include <linux/if_ether.h>
23#include <linux/sockios.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <string.h>
27#include <sys/ioctl.h>
28#include <linux/sockios.h>
29
30#include "rt_names.h"
31#include "utils.h"
32#include "ip_common.h"
33
34
35static void usage(void) __attribute__((noreturn));
36
37void iplink_usage(void)
38{
39	fprintf(stderr, "Usage: ip link set DEVICE { up | down | arp { on | off } |\n");
40	fprintf(stderr, "	                     dynamic { on | off } |\n");
41	fprintf(stderr, "	                     multicast { on | off } | txqueuelen PACKETS |\n");
42	fprintf(stderr, "	                     name NEWNAME |\n");
43	fprintf(stderr, "	                     address LLADDR | broadcast LLADDR |\n");
44	fprintf(stderr, "	                     mtu MTU }\n");
45	fprintf(stderr, "       ip link show [ DEVICE ]\n");
46	exit(-1);
47}
48
49static void usage(void)
50{
51	iplink_usage();
52}
53
54static int on_off(char *msg)
55{
56	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
57	return -1;
58}
59
60static int get_ctl_fd(void)
61{
62	int s_errno;
63	int fd;
64
65	fd = socket(PF_INET, SOCK_DGRAM, 0);
66	if (fd >= 0)
67		return fd;
68	s_errno = errno;
69	fd = socket(PF_PACKET, SOCK_DGRAM, 0);
70	if (fd >= 0)
71		return fd;
72	fd = socket(PF_INET6, SOCK_DGRAM, 0);
73	if (fd >= 0)
74		return fd;
75	errno = s_errno;
76	perror("Cannot create control socket");
77	return -1;
78}
79
80static int do_chflags(char *dev, __u32 flags, __u32 mask)
81{
82	struct ifreq ifr;
83	int fd;
84	int err;
85
86	strcpy(ifr.ifr_name, dev);
87	fd = get_ctl_fd();
88	if (fd < 0)
89		return -1;
90	err = ioctl(fd, SIOCGIFFLAGS, &ifr);
91	if (err) {
92		perror("SIOCGIFFLAGS");
93		close(fd);
94		return -1;
95	}
96	if ((ifr.ifr_flags^flags)&mask) {
97		ifr.ifr_flags &= ~mask;
98		ifr.ifr_flags |= mask&flags;
99		err = ioctl(fd, SIOCSIFFLAGS, &ifr);
100		if (err)
101			perror("SIOCSIFFLAGS");
102	}
103	close(fd);
104	return err;
105}
106
107static int do_changename(char *dev, char *newdev)
108{
109	struct ifreq ifr;
110	int fd;
111	int err;
112
113	strcpy(ifr.ifr_name, dev);
114	strcpy(ifr.ifr_newname, newdev);
115	fd = get_ctl_fd();
116	if (fd < 0)
117		return -1;
118	err = ioctl(fd, SIOCSIFNAME, &ifr);
119	if (err) {
120		perror("SIOCSIFNAME");
121		close(fd);
122		return -1;
123	}
124	close(fd);
125	return err;
126}
127
128static int set_qlen(char *dev, int qlen)
129{
130	struct ifreq ifr;
131	int s;
132
133	s = get_ctl_fd();
134	if (s < 0)
135		return -1;
136
137	memset(&ifr, 0, sizeof(ifr));
138	strcpy(ifr.ifr_name, dev);
139	ifr.ifr_qlen = qlen;
140	if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
141		perror("SIOCSIFXQLEN");
142		close(s);
143		return -1;
144	}
145	close(s);
146
147	return 0;
148}
149
150static int set_mtu(char *dev, int mtu)
151{
152	struct ifreq ifr;
153	int s;
154
155	s = get_ctl_fd();
156	if (s < 0)
157		return -1;
158
159	memset(&ifr, 0, sizeof(ifr));
160	strcpy(ifr.ifr_name, dev);
161	ifr.ifr_mtu = mtu;
162	if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
163		perror("SIOCSIFMTU");
164		close(s);
165		return -1;
166	}
167	close(s);
168
169	return 0;
170}
171
172static int get_address(char *dev, int *htype)
173{
174	struct ifreq ifr;
175	struct sockaddr_ll me;
176	int alen;
177	int s;
178
179	s = socket(PF_PACKET, SOCK_DGRAM, 0);
180	if (s < 0) {
181		perror("socket(PF_PACKET)");
182		return -1;
183	}
184
185	memset(&ifr, 0, sizeof(ifr));
186	strcpy(ifr.ifr_name, dev);
187	if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
188		perror("SIOCGIFINDEX");
189		close(s);
190		return -1;
191	}
192
193	memset(&me, 0, sizeof(me));
194	me.sll_family = AF_PACKET;
195	me.sll_ifindex = ifr.ifr_ifindex;
196	me.sll_protocol = htons(ETH_P_LOOP);
197	if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
198		perror("bind");
199		close(s);
200		return -1;
201	}
202
203	alen = sizeof(me);
204	if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
205		perror("getsockname");
206		close(s);
207		return -1;
208	}
209	close(s);
210	*htype = me.sll_hatype;
211	return me.sll_halen;
212}
213
214static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
215{
216	int alen;
217
218	memset(ifr, 0, sizeof(*ifr));
219	strcpy(ifr->ifr_name, dev);
220	ifr->ifr_hwaddr.sa_family = hatype;
221	alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
222	if (alen < 0)
223		return -1;
224	if (alen != halen) {
225		fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
226		return -1;
227	}
228	return 0;
229}
230
231static int set_address(struct ifreq *ifr, int brd)
232{
233	int s;
234
235	s = get_ctl_fd();
236	if (s < 0)
237		return -1;
238	if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
239		perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
240		close(s);
241		return -1;
242	}
243	close(s);
244	return 0;
245}
246
247
248static int do_set(int argc, char **argv)
249{
250	char *dev = NULL;
251	__u32 mask = 0;
252	__u32 flags = 0;
253	int qlen = -1;
254	int mtu = -1;
255	char *newaddr = NULL;
256	char *newbrd = NULL;
257	struct ifreq ifr0, ifr1;
258	char *newname = NULL;
259	int htype, halen;
260
261	while (argc > 0) {
262		if (strcmp(*argv, "up") == 0) {
263			mask |= IFF_UP;
264			flags |= IFF_UP;
265		} else if (strcmp(*argv, "down") == 0) {
266			mask |= IFF_UP;
267			flags &= ~IFF_UP;
268		} else if (strcmp(*argv, "name") == 0) {
269			NEXT_ARG();
270			newname = *argv;
271		} else if (matches(*argv, "address") == 0) {
272			NEXT_ARG();
273			newaddr = *argv;
274		} else if (matches(*argv, "broadcast") == 0 ||
275			   strcmp(*argv, "brd") == 0) {
276			NEXT_ARG();
277			newbrd = *argv;
278		} else if (matches(*argv, "txqueuelen") == 0 ||
279			   strcmp(*argv, "qlen") == 0 ||
280			   matches(*argv, "txqlen") == 0) {
281			NEXT_ARG();
282			if (qlen != -1)
283				duparg("txqueuelen", *argv);
284			if (get_integer(&qlen,  *argv, 0))
285				invarg("Invalid \"txqueuelen\" value\n", *argv);
286		} else if (strcmp(*argv, "mtu") == 0) {
287			NEXT_ARG();
288			if (mtu != -1)
289				duparg("mtu", *argv);
290			if (get_integer(&mtu, *argv, 0))
291				invarg("Invalid \"mtu\" value\n", *argv);
292		} else if (strcmp(*argv, "multicast") == 0) {
293			NEXT_ARG();
294			mask |= IFF_MULTICAST;
295			if (strcmp(*argv, "on") == 0) {
296				flags |= IFF_MULTICAST;
297			} else if (strcmp(*argv, "off") == 0) {
298				flags &= ~IFF_MULTICAST;
299			} else
300				return on_off("multicast");
301		} else if (strcmp(*argv, "arp") == 0) {
302			NEXT_ARG();
303			mask |= IFF_NOARP;
304			if (strcmp(*argv, "on") == 0) {
305				flags &= ~IFF_NOARP;
306			} else if (strcmp(*argv, "off") == 0) {
307				flags |= IFF_NOARP;
308			} else
309				return on_off("noarp");
310#ifdef IFF_DYNAMIC
311		} else if (matches(*argv, "dynamic") == 0) {
312			NEXT_ARG();
313			mask |= IFF_DYNAMIC;
314			if (strcmp(*argv, "on") == 0) {
315				flags |= IFF_DYNAMIC;
316			} else if (strcmp(*argv, "off") == 0) {
317				flags &= ~IFF_DYNAMIC;
318			} else
319				return on_off("dynamic");
320#endif
321		} else {
322                        if (strcmp(*argv, "dev") == 0) {
323				NEXT_ARG();
324			}
325			if (matches(*argv, "help") == 0)
326				usage();
327			if (dev)
328				duparg2("dev", *argv);
329			dev = *argv;
330		}
331		argc--; argv++;
332	}
333
334	if (!dev) {
335		fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
336		exit(-1);
337	}
338
339	if (newaddr || newbrd) {
340		halen = get_address(dev, &htype);
341		if (halen < 0)
342			return -1;
343		if (newaddr) {
344			if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
345				return -1;
346		}
347		if (newbrd) {
348			if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
349				return -1;
350		}
351	}
352
353	if (newname && strcmp(dev, newname)) {
354		if (do_changename(dev, newname) < 0)
355			return -1;
356		dev = newname;
357	}
358	if (qlen != -1) {
359		if (set_qlen(dev, qlen) < 0)
360			return -1;
361	}
362	if (mtu != -1) {
363		if (set_mtu(dev, mtu) < 0)
364			return -1;
365	}
366	if (newaddr || newbrd) {
367		if (newbrd) {
368			if (set_address(&ifr1, 1) < 0)
369				return -1;
370		}
371		if (newaddr) {
372			if (set_address(&ifr0, 0) < 0)
373				return -1;
374		}
375	}
376	if (mask)
377		return do_chflags(dev, flags, mask);
378	return 0;
379}
380
381int do_iplink(int argc, char **argv)
382{
383	if (argc > 0) {
384		if (matches(*argv, "set") == 0)
385			return do_set(argc-1, argv+1);
386		if (matches(*argv, "show") == 0 ||
387		    matches(*argv, "lst") == 0 ||
388		    matches(*argv, "list") == 0)
389			return ipaddr_list_link(argc-1, argv+1);
390		if (matches(*argv, "help") == 0)
391			usage();
392	} else
393		return ipaddr_list_link(0, NULL);
394
395	fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
396	exit(-1);
397}
398