This commit was manufactured by cvs2svn to create branch
[iproute2.git] / ip / iplink.c
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
35 static void usage(void) __attribute__((noreturn));
36
37 void iplink_usage(void)
38 {
39         fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n");
40         fprintf(stderr, "                            arp { on | off } |\n");
41         fprintf(stderr, "                            dynamic { on | off } |\n");
42         fprintf(stderr, "                            multicast { on | off } |\n");
43         fprintf(stderr, "                            allmulticast { on | off } |\n");
44         fprintf(stderr, "                            promisc { on | off } |\n");
45         fprintf(stderr, "                            trailers { on | off } |\n");
46         fprintf(stderr, "                            txqueuelen PACKETS |\n");
47         fprintf(stderr, "                            name NEWNAME |\n");
48         fprintf(stderr, "                            address LLADDR | broadcast LLADDR |\n");
49         fprintf(stderr, "                            mtu MTU }\n");
50         fprintf(stderr, "       ip link show [ DEVICE ]\n");
51         exit(-1);
52 }
53
54 static void usage(void)
55 {
56         iplink_usage();
57 }
58
59 static int on_off(char *msg)
60 {
61         fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
62         return -1;
63 }
64
65 static int get_ctl_fd(void)
66 {
67         int s_errno;
68         int fd;
69
70         fd = socket(PF_INET, SOCK_DGRAM, 0);
71         if (fd >= 0)
72                 return fd;
73         s_errno = errno;
74         fd = socket(PF_PACKET, SOCK_DGRAM, 0);
75         if (fd >= 0)
76                 return fd;
77         fd = socket(PF_INET6, SOCK_DGRAM, 0);
78         if (fd >= 0)
79                 return fd;
80         errno = s_errno;
81         perror("Cannot create control socket");
82         return -1;
83 }
84
85 static int do_chflags(const char *dev, __u32 flags, __u32 mask)
86 {
87         struct ifreq ifr;
88         int fd;
89         int err;
90
91         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
92         fd = get_ctl_fd();
93         if (fd < 0)
94                 return -1;
95         err = ioctl(fd, SIOCGIFFLAGS, &ifr);
96         if (err) {
97                 perror("SIOCGIFFLAGS");
98                 close(fd);
99                 return -1;
100         }
101         if ((ifr.ifr_flags^flags)&mask) {
102                 ifr.ifr_flags &= ~mask;
103                 ifr.ifr_flags |= mask&flags;
104                 err = ioctl(fd, SIOCSIFFLAGS, &ifr);
105                 if (err)
106                         perror("SIOCSIFFLAGS");
107         }
108         close(fd);
109         return err;
110 }
111
112 static int do_changename(const char *dev, const char *newdev)
113 {
114         struct ifreq ifr;
115         int fd;
116         int err;
117
118         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
119         strncpy(ifr.ifr_newname, newdev, IFNAMSIZ);
120         fd = get_ctl_fd();
121         if (fd < 0)
122                 return -1;
123         err = ioctl(fd, SIOCSIFNAME, &ifr);
124         if (err) {
125                 perror("SIOCSIFNAME");
126                 close(fd);
127                 return -1;
128         }
129         close(fd);
130         return err;
131 }
132
133 static int set_qlen(const char *dev, int qlen)
134 {
135         struct ifreq ifr;
136         int s;
137
138         s = get_ctl_fd();
139         if (s < 0)
140                 return -1;
141
142         memset(&ifr, 0, sizeof(ifr));
143         strncpy(ifr.ifr_name, dev, IFNAMSIZ); 
144         ifr.ifr_qlen = qlen; 
145         if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
146                 perror("SIOCSIFXQLEN");
147                 close(s);
148                 return -1;
149         }
150         close(s);
151
152         return 0; 
153 }
154
155 static int set_mtu(const char *dev, int mtu)
156 {
157         struct ifreq ifr;
158         int s;
159
160         s = get_ctl_fd();
161         if (s < 0)
162                 return -1;
163
164         memset(&ifr, 0, sizeof(ifr));
165         strncpy(ifr.ifr_name, dev, IFNAMSIZ); 
166         ifr.ifr_mtu = mtu; 
167         if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
168                 perror("SIOCSIFMTU");
169                 close(s);
170                 return -1;
171         }
172         close(s);
173
174         return 0; 
175 }
176
177 static int get_address(const char *dev, int *htype)
178 {
179         struct ifreq ifr;
180         struct sockaddr_ll me;
181         socklen_t alen;
182         int s;
183
184         s = socket(PF_PACKET, SOCK_DGRAM, 0);
185         if (s < 0) { 
186                 perror("socket(PF_PACKET)");
187                 return -1;
188         }
189
190         memset(&ifr, 0, sizeof(ifr));
191         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
192         if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
193                 perror("SIOCGIFINDEX");
194                 close(s);
195                 return -1;
196         }
197
198         memset(&me, 0, sizeof(me));
199         me.sll_family = AF_PACKET;
200         me.sll_ifindex = ifr.ifr_ifindex;
201         me.sll_protocol = htons(ETH_P_LOOP);
202         if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
203                 perror("bind");
204                 close(s);
205                 return -1;
206         }
207
208         alen = sizeof(me);
209         if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
210                 perror("getsockname");
211                 close(s);
212                 return -1;
213         }
214         close(s);
215         *htype = me.sll_hatype;
216         return me.sll_halen;
217 }
218
219 static int parse_address(const char *dev, int hatype, int halen, 
220                 char *lla, struct ifreq *ifr)
221 {
222         int alen;
223
224         memset(ifr, 0, sizeof(*ifr));
225         strncpy(ifr->ifr_name, dev, IFNAMSIZ);
226         ifr->ifr_hwaddr.sa_family = hatype;
227         alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
228         if (alen < 0)
229                 return -1;
230         if (alen != halen) {
231                 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
232                 return -1;
233         }
234         return 0; 
235 }
236
237 static int set_address(struct ifreq *ifr, int brd)
238 {
239         int s;
240
241         s = get_ctl_fd();
242         if (s < 0)
243                 return -1;
244         if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
245                 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
246                 close(s);
247                 return -1;
248         }
249         close(s);
250         return 0; 
251 }
252
253
254 static int do_set(int argc, char **argv)
255 {
256         char *dev = NULL;
257         __u32 mask = 0;
258         __u32 flags = 0;
259         int qlen = -1;
260         int mtu = -1;
261         char *newaddr = NULL;
262         char *newbrd = NULL;
263         struct ifreq ifr0, ifr1;
264         char *newname = NULL;
265         int htype, halen;
266
267         while (argc > 0) {
268                 if (strcmp(*argv, "up") == 0) {
269                         mask |= IFF_UP;
270                         flags |= IFF_UP;
271                 } else if (strcmp(*argv, "down") == 0) {
272                         mask |= IFF_UP;
273                         flags &= ~IFF_UP;
274                 } else if (strcmp(*argv, "name") == 0) {
275                         NEXT_ARG();
276                         newname = *argv;
277                 } else if (matches(*argv, "address") == 0) {
278                         NEXT_ARG();
279                         newaddr = *argv;
280                 } else if (matches(*argv, "broadcast") == 0 ||
281                            strcmp(*argv, "brd") == 0) {
282                         NEXT_ARG();
283                         newbrd = *argv;
284                 } else if (matches(*argv, "txqueuelen") == 0 ||
285                            strcmp(*argv, "qlen") == 0 ||
286                            matches(*argv, "txqlen") == 0) {
287                         NEXT_ARG();
288                         if (qlen != -1)
289                                 duparg("txqueuelen", *argv);
290                         if (get_integer(&qlen,  *argv, 0))
291                                 invarg("Invalid \"txqueuelen\" value\n", *argv);
292                 } else if (strcmp(*argv, "mtu") == 0) {
293                         NEXT_ARG();
294                         if (mtu != -1)
295                                 duparg("mtu", *argv);
296                         if (get_integer(&mtu, *argv, 0))
297                                 invarg("Invalid \"mtu\" value\n", *argv);
298                 } else if (strcmp(*argv, "multicast") == 0) {
299                         NEXT_ARG();
300                         mask |= IFF_MULTICAST;
301                         if (strcmp(*argv, "on") == 0) {
302                                 flags |= IFF_MULTICAST;
303                         } else if (strcmp(*argv, "off") == 0) {
304                                 flags &= ~IFF_MULTICAST;
305                         } else
306                                 return on_off("multicast");
307                 } else if (strcmp(*argv, "allmulticast") == 0) {
308                         NEXT_ARG();
309                         mask |= IFF_ALLMULTI;
310                         if (strcmp(*argv, "on") == 0) {
311                                 flags |= IFF_ALLMULTI;
312                         } else if (strcmp(*argv, "off") == 0) {
313                                 flags &= ~IFF_ALLMULTI;
314                         } else
315                                 return on_off("allmulticast");
316                 } else if (strcmp(*argv, "promisc") == 0) {
317                         NEXT_ARG();
318                         mask |= IFF_PROMISC;
319                         if (strcmp(*argv, "on") == 0) {
320                                 flags |= IFF_PROMISC;
321                         } else if (strcmp(*argv, "off") == 0) {
322                                 flags &= ~IFF_PROMISC;
323                         } else
324                                 return on_off("promisc");
325                 } else if (strcmp(*argv, "trailers") == 0) {
326                         NEXT_ARG();
327                         mask |= IFF_NOTRAILERS;
328                         if (strcmp(*argv, "off") == 0) {
329                                 flags |= IFF_NOTRAILERS;
330                         } else if (strcmp(*argv, "on") == 0) {
331                                 flags &= ~IFF_NOTRAILERS;
332                         } else
333                                 return on_off("trailers");
334                 } else if (strcmp(*argv, "arp") == 0) {
335                         NEXT_ARG();
336                         mask |= IFF_NOARP;
337                         if (strcmp(*argv, "on") == 0) {
338                                 flags &= ~IFF_NOARP;
339                         } else if (strcmp(*argv, "off") == 0) {
340                                 flags |= IFF_NOARP;
341                         } else
342                                 return on_off("noarp");
343 #ifdef IFF_DYNAMIC
344                 } else if (matches(*argv, "dynamic") == 0) {
345                         NEXT_ARG();
346                         mask |= IFF_DYNAMIC;
347                         if (strcmp(*argv, "on") == 0) {
348                                 flags |= IFF_DYNAMIC;
349                         } else if (strcmp(*argv, "off") == 0) {
350                                 flags &= ~IFF_DYNAMIC;
351                         } else
352                                 return on_off("dynamic");
353 #endif
354                 } else {
355                         if (strcmp(*argv, "dev") == 0) {
356                                 NEXT_ARG();
357                         }
358                         if (matches(*argv, "help") == 0)
359                                 usage();
360                         if (dev)
361                                 duparg2("dev", *argv);
362                         dev = *argv;
363                 }
364                 argc--; argv++;
365         }
366
367         if (!dev) {
368                 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
369                 exit(-1);
370         }
371
372         if (newaddr || newbrd) {
373                 halen = get_address(dev, &htype);
374                 if (halen < 0)
375                         return -1;
376                 if (newaddr) {
377                         if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
378                                 return -1;
379                 }
380                 if (newbrd) {
381                         if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
382                                 return -1; 
383                 }
384         }
385
386         if (newname && strcmp(dev, newname)) {
387                 if (do_changename(dev, newname) < 0)
388                         return -1;
389                 dev = newname;
390         }
391         if (qlen != -1) { 
392                 if (set_qlen(dev, qlen) < 0)
393                         return -1; 
394         }
395         if (mtu != -1) { 
396                 if (set_mtu(dev, mtu) < 0)
397                         return -1; 
398         }
399         if (newaddr || newbrd) {
400                 if (newbrd) {
401                         if (set_address(&ifr1, 1) < 0)
402                                 return -1; 
403                 }
404                 if (newaddr) {
405                         if (set_address(&ifr0, 0) < 0)
406                                 return -1;
407                 }
408         }
409         if (mask)
410                 return do_chflags(dev, flags, mask);
411         return 0;
412 }
413
414 int do_iplink(int argc, char **argv)
415 {
416         if (argc > 0) {
417                 if (matches(*argv, "set") == 0)
418                         return do_set(argc-1, argv+1);
419                 if (matches(*argv, "show") == 0 ||
420                     matches(*argv, "lst") == 0 ||
421                     matches(*argv, "list") == 0)
422                         return ipaddr_list_link(argc-1, argv+1);
423                 if (matches(*argv, "help") == 0)
424                         usage();
425         } else
426                 return ipaddr_list_link(0, NULL);
427
428         fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
429         exit(-1);
430 }