This commit was generated by cvs2svn to compensate for changes in r1650,
[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         int 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, char *lla, struct ifreq *ifr)
220 {
221         int alen;
222
223         memset(ifr, 0, sizeof(*ifr));
224         strncpy(ifr->ifr_name, dev, IFNAMSIZ);
225         ifr->ifr_hwaddr.sa_family = hatype;
226         alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
227         if (alen < 0)
228                 return -1;
229         if (alen != halen) {
230                 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
231                 return -1;
232         }
233         return 0; 
234 }
235
236 static int set_address(struct ifreq *ifr, int brd)
237 {
238         int s;
239
240         s = get_ctl_fd();
241         if (s < 0)
242                 return -1;
243         if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
244                 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
245                 close(s);
246                 return -1;
247         }
248         close(s);
249         return 0; 
250 }
251
252
253 static int do_set(int argc, char **argv)
254 {
255         char *dev = NULL;
256         __u32 mask = 0;
257         __u32 flags = 0;
258         int qlen = -1;
259         int mtu = -1;
260         char *newaddr = NULL;
261         char *newbrd = NULL;
262         struct ifreq ifr0, ifr1;
263         char *newname = NULL;
264         int htype, halen;
265
266         while (argc > 0) {
267                 if (strcmp(*argv, "up") == 0) {
268                         mask |= IFF_UP;
269                         flags |= IFF_UP;
270                 } else if (strcmp(*argv, "down") == 0) {
271                         mask |= IFF_UP;
272                         flags &= ~IFF_UP;
273                 } else if (strcmp(*argv, "name") == 0) {
274                         NEXT_ARG();
275                         newname = *argv;
276                 } else if (matches(*argv, "address") == 0) {
277                         NEXT_ARG();
278                         newaddr = *argv;
279                 } else if (matches(*argv, "broadcast") == 0 ||
280                            strcmp(*argv, "brd") == 0) {
281                         NEXT_ARG();
282                         newbrd = *argv;
283                 } else if (matches(*argv, "txqueuelen") == 0 ||
284                            strcmp(*argv, "qlen") == 0 ||
285                            matches(*argv, "txqlen") == 0) {
286                         NEXT_ARG();
287                         if (qlen != -1)
288                                 duparg("txqueuelen", *argv);
289                         if (get_integer(&qlen,  *argv, 0))
290                                 invarg("Invalid \"txqueuelen\" value\n", *argv);
291                 } else if (strcmp(*argv, "mtu") == 0) {
292                         NEXT_ARG();
293                         if (mtu != -1)
294                                 duparg("mtu", *argv);
295                         if (get_integer(&mtu, *argv, 0))
296                                 invarg("Invalid \"mtu\" value\n", *argv);
297                 } else if (strcmp(*argv, "multicast") == 0) {
298                         NEXT_ARG();
299                         mask |= IFF_MULTICAST;
300                         if (strcmp(*argv, "on") == 0) {
301                                 flags |= IFF_MULTICAST;
302                         } else if (strcmp(*argv, "off") == 0) {
303                                 flags &= ~IFF_MULTICAST;
304                         } else
305                                 return on_off("multicast");
306                 } else if (strcmp(*argv, "allmulticast") == 0) {
307                         NEXT_ARG();
308                         mask |= IFF_ALLMULTI;
309                         if (strcmp(*argv, "on") == 0) {
310                                 flags |= IFF_ALLMULTI;
311                         } else if (strcmp(*argv, "off") == 0) {
312                                 flags &= ~IFF_ALLMULTI;
313                         } else
314                                 return on_off("allmulticast");
315                 } else if (strcmp(*argv, "promisc") == 0) {
316                         NEXT_ARG();
317                         mask |= IFF_PROMISC;
318                         if (strcmp(*argv, "on") == 0) {
319                                 flags |= IFF_PROMISC;
320                         } else if (strcmp(*argv, "off") == 0) {
321                                 flags &= ~IFF_PROMISC;
322                         } else
323                                 return on_off("promisc");
324                 } else if (strcmp(*argv, "trailers") == 0) {
325                         NEXT_ARG();
326                         mask |= IFF_NOTRAILERS;
327                         if (strcmp(*argv, "off") == 0) {
328                                 flags |= IFF_NOTRAILERS;
329                         } else if (strcmp(*argv, "on") == 0) {
330                                 flags &= ~IFF_NOTRAILERS;
331                         } else
332                                 return on_off("trailers");
333                 } else if (strcmp(*argv, "arp") == 0) {
334                         NEXT_ARG();
335                         mask |= IFF_NOARP;
336                         if (strcmp(*argv, "on") == 0) {
337                                 flags &= ~IFF_NOARP;
338                         } else if (strcmp(*argv, "off") == 0) {
339                                 flags |= IFF_NOARP;
340                         } else
341                                 return on_off("noarp");
342 #ifdef IFF_DYNAMIC
343                 } else if (matches(*argv, "dynamic") == 0) {
344                         NEXT_ARG();
345                         mask |= IFF_DYNAMIC;
346                         if (strcmp(*argv, "on") == 0) {
347                                 flags |= IFF_DYNAMIC;
348                         } else if (strcmp(*argv, "off") == 0) {
349                                 flags &= ~IFF_DYNAMIC;
350                         } else
351                                 return on_off("dynamic");
352 #endif
353                 } else {
354                         if (strcmp(*argv, "dev") == 0) {
355                                 NEXT_ARG();
356                         }
357                         if (matches(*argv, "help") == 0)
358                                 usage();
359                         if (dev)
360                                 duparg2("dev", *argv);
361                         dev = *argv;
362                 }
363                 argc--; argv++;
364         }
365
366         if (!dev) {
367                 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
368                 exit(-1);
369         }
370
371         if (newaddr || newbrd) {
372                 halen = get_address(dev, &htype);
373                 if (halen < 0)
374                         return -1;
375                 if (newaddr) {
376                         if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
377                                 return -1;
378                 }
379                 if (newbrd) {
380                         if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
381                                 return -1; 
382                 }
383         }
384
385         if (newname && strcmp(dev, newname)) {
386                 if (do_changename(dev, newname) < 0)
387                         return -1;
388                 dev = newname;
389         }
390         if (qlen != -1) { 
391                 if (set_qlen(dev, qlen) < 0)
392                         return -1; 
393         }
394         if (mtu != -1) { 
395                 if (set_mtu(dev, mtu) < 0)
396                         return -1; 
397         }
398         if (newaddr || newbrd) {
399                 if (newbrd) {
400                         if (set_address(&ifr1, 1) < 0)
401                                 return -1; 
402                 }
403                 if (newaddr) {
404                         if (set_address(&ifr0, 0) < 0)
405                                 return -1;
406                 }
407         }
408         if (mask)
409                 return do_chflags(dev, flags, mask);
410         return 0;
411 }
412
413 int do_iplink(int argc, char **argv)
414 {
415         if (argc > 0) {
416                 if (matches(*argv, "set") == 0)
417                         return do_set(argc-1, argv+1);
418                 if (matches(*argv, "show") == 0 ||
419                     matches(*argv, "lst") == 0 ||
420                     matches(*argv, "list") == 0)
421                         return ipaddr_list_link(argc-1, argv+1);
422                 if (matches(*argv, "help") == 0)
423                         usage();
424         } else
425                 return ipaddr_list_link(0, NULL);
426
427         fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
428         exit(-1);
429 }