a686eb201cda5f8cb19850101a984a5dde4c7769
[platform/upstream/busybox.git] / networking / libiproute / iplink.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
4  *                      Patrick McHardy <kaber@trash.net>
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7  */
8 #include <net/if.h>
9 #include <net/if_packet.h>
10 #include <netpacket/packet.h>
11 #include <netinet/if_ether.h>
12
13 #include <linux/if_vlan.h>
14 #include "ip_common.h"  /* #include "libbb.h" is inside */
15 #include "rt_names.h"
16 #include "utils.h"
17
18 #undef  ETH_P_8021AD
19 #define ETH_P_8021AD            0x88A8
20 #undef  VLAN_FLAG_REORDER_HDR
21 #define VLAN_FLAG_REORDER_HDR   0x1
22 #undef  VLAN_FLAG_GVRP
23 #define VLAN_FLAG_GVRP          0x2
24 #undef  VLAN_FLAG_LOOSE_BINDING
25 #define VLAN_FLAG_LOOSE_BINDING 0x4
26 #undef  VLAN_FLAG_MVRP
27 #define VLAN_FLAG_MVRP          0x8
28 #undef  IFLA_VLAN_PROTOCOL
29 #define IFLA_VLAN_PROTOCOL      5
30
31 #ifndef IFLA_LINKINFO
32 # define IFLA_LINKINFO 18
33 # define IFLA_INFO_KIND 1
34 #endif
35
36 /* taken from linux/sockios.h */
37 #define SIOCSIFNAME  0x8923  /* set interface name */
38
39 /* Exits on error */
40 static int get_ctl_fd(void)
41 {
42         int fd;
43
44         fd = socket(PF_INET, SOCK_DGRAM, 0);
45         if (fd >= 0)
46                 return fd;
47         fd = socket(PF_PACKET, SOCK_DGRAM, 0);
48         if (fd >= 0)
49                 return fd;
50         return xsocket(PF_INET6, SOCK_DGRAM, 0);
51 }
52
53 /* Exits on error */
54 static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
55 {
56         struct ifreq ifr;
57         int fd;
58
59         strncpy_IFNAMSIZ(ifr.ifr_name, dev);
60         fd = get_ctl_fd();
61         xioctl(fd, SIOCGIFFLAGS, &ifr);
62         if ((ifr.ifr_flags ^ flags) & mask) {
63                 ifr.ifr_flags &= ~mask;
64                 ifr.ifr_flags |= mask & flags;
65                 xioctl(fd, SIOCSIFFLAGS, &ifr);
66         }
67         close(fd);
68 }
69
70 /* Exits on error */
71 static void do_changename(char *dev, char *newdev)
72 {
73         struct ifreq ifr;
74         int fd;
75
76         strncpy_IFNAMSIZ(ifr.ifr_name, dev);
77         strncpy_IFNAMSIZ(ifr.ifr_newname, newdev);
78         fd = get_ctl_fd();
79         xioctl(fd, SIOCSIFNAME, &ifr);
80         close(fd);
81 }
82
83 /* Exits on error */
84 static void set_qlen(char *dev, int qlen)
85 {
86         struct ifreq ifr;
87         int s;
88
89         s = get_ctl_fd();
90         memset(&ifr, 0, sizeof(ifr));
91         strncpy_IFNAMSIZ(ifr.ifr_name, dev);
92         ifr.ifr_qlen = qlen;
93         xioctl(s, SIOCSIFTXQLEN, &ifr);
94         close(s);
95 }
96
97 /* Exits on error */
98 static void set_mtu(char *dev, int mtu)
99 {
100         struct ifreq ifr;
101         int s;
102
103         s = get_ctl_fd();
104         memset(&ifr, 0, sizeof(ifr));
105         strncpy_IFNAMSIZ(ifr.ifr_name, dev);
106         ifr.ifr_mtu = mtu;
107         xioctl(s, SIOCSIFMTU, &ifr);
108         close(s);
109 }
110
111 /* Exits on error */
112 static int get_address(char *dev, int *htype)
113 {
114         struct ifreq ifr;
115         struct sockaddr_ll me;
116         socklen_t alen;
117         int s;
118
119         s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
120
121         memset(&ifr, 0, sizeof(ifr));
122         strncpy_IFNAMSIZ(ifr.ifr_name, dev);
123         xioctl(s, SIOCGIFINDEX, &ifr);
124
125         memset(&me, 0, sizeof(me));
126         me.sll_family = AF_PACKET;
127         me.sll_ifindex = ifr.ifr_ifindex;
128         me.sll_protocol = htons(ETH_P_LOOP);
129         xbind(s, (struct sockaddr*)&me, sizeof(me));
130         alen = sizeof(me);
131         getsockname(s, (struct sockaddr*)&me, &alen);
132         //never happens:
133         //if (getsockname(s, (struct sockaddr*)&me, &alen) == -1)
134         //      bb_perror_msg_and_die("getsockname");
135         close(s);
136         *htype = me.sll_hatype;
137         return me.sll_halen;
138 }
139
140 /* Exits on error */
141 static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
142 {
143         int alen;
144
145         memset(ifr, 0, sizeof(*ifr));
146         strncpy_IFNAMSIZ(ifr->ifr_name, dev);
147         ifr->ifr_hwaddr.sa_family = hatype;
148
149         alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
150         alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla);
151         if (alen < 0)
152                 exit(EXIT_FAILURE);
153         if (alen != halen) {
154                 bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
155         }
156 }
157
158 /* Exits on error */
159 static void set_address(struct ifreq *ifr, int brd)
160 {
161         int s;
162
163         s = get_ctl_fd();
164         if (brd)
165                 xioctl(s, SIOCSIFHWBROADCAST, ifr);
166         else
167                 xioctl(s, SIOCSIFHWADDR, ifr);
168         close(s);
169 }
170
171
172 static void die_must_be_on_off(const char *msg) NORETURN;
173 static void die_must_be_on_off(const char *msg)
174 {
175         bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg);
176 }
177
178 /* Return value becomes exitcode. It's okay to not return at all */
179 static int do_set(char **argv)
180 {
181         char *dev = NULL;
182         uint32_t mask = 0;
183         uint32_t flags = 0;
184         int qlen = -1;
185         int mtu = -1;
186         char *newaddr = NULL;
187         char *newbrd = NULL;
188         struct ifreq ifr0, ifr1;
189         char *newname = NULL;
190         int htype, halen;
191         static const char keywords[] ALIGN1 =
192                 "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0"
193                 "arp\0""address\0""dev\0";
194         enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast,
195                 ARG_arp, ARG_addr, ARG_dev };
196         static const char str_on_off[] ALIGN1 = "on\0""off\0";
197         enum { PARM_on = 0, PARM_off };
198         smalluint key;
199
200         while (*argv) {
201                 /* substring search ensures that e.g. "addr" and "address"
202                  * are both accepted */
203                 key = index_in_substrings(keywords, *argv);
204                 if (key == ARG_up) {
205                         mask |= IFF_UP;
206                         flags |= IFF_UP;
207                 } else if (key == ARG_down) {
208                         mask |= IFF_UP;
209                         flags &= ~IFF_UP;
210                 } else if (key == ARG_name) {
211                         NEXT_ARG();
212                         newname = *argv;
213                 } else if (key == ARG_mtu) {
214                         NEXT_ARG();
215                         if (mtu != -1)
216                                 duparg("mtu", *argv);
217                         mtu = get_unsigned(*argv, "mtu");
218                 } else if (key == ARG_qlen) {
219                         NEXT_ARG();
220                         if (qlen != -1)
221                                 duparg("qlen", *argv);
222                         qlen = get_unsigned(*argv, "qlen");
223                 } else if (key == ARG_addr) {
224                         NEXT_ARG();
225                         newaddr = *argv;
226                 } else if (key >= ARG_dev) {
227                         if (key == ARG_dev) {
228                                 NEXT_ARG();
229                         }
230                         if (dev)
231                                 duparg2("dev", *argv);
232                         dev = *argv;
233                 } else {
234                         int param;
235                         NEXT_ARG();
236                         param = index_in_strings(str_on_off, *argv);
237                         if (key == ARG_multicast) {
238                                 if (param < 0)
239                                         die_must_be_on_off("multicast");
240                                 mask |= IFF_MULTICAST;
241                                 if (param == PARM_on)
242                                         flags |= IFF_MULTICAST;
243                                 else
244                                         flags &= ~IFF_MULTICAST;
245                         } else if (key == ARG_arp) {
246                                 if (param < 0)
247                                         die_must_be_on_off("arp");
248                                 mask |= IFF_NOARP;
249                                 if (param == PARM_on)
250                                         flags &= ~IFF_NOARP;
251                                 else
252                                         flags |= IFF_NOARP;
253                         }
254                 }
255                 argv++;
256         }
257
258         if (!dev) {
259                 bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
260         }
261
262         if (newaddr || newbrd) {
263                 halen = get_address(dev, &htype);
264                 if (newaddr) {
265                         parse_address(dev, htype, halen, newaddr, &ifr0);
266                         set_address(&ifr0, 0);
267                 }
268                 if (newbrd) {
269                         parse_address(dev, htype, halen, newbrd, &ifr1);
270                         set_address(&ifr1, 1);
271                 }
272         }
273
274         if (newname && strcmp(dev, newname)) {
275                 do_changename(dev, newname);
276                 dev = newname;
277         }
278         if (qlen != -1) {
279                 set_qlen(dev, qlen);
280         }
281         if (mtu != -1) {
282                 set_mtu(dev, mtu);
283         }
284         if (mask)
285                 do_chflags(dev, flags, mask);
286         return 0;
287 }
288
289 static int ipaddr_list_link(char **argv)
290 {
291         preferred_family = AF_PACKET;
292         return ipaddr_list_or_flush(argv, 0);
293 }
294
295 static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
296 {
297         static const char keywords[] ALIGN1 =
298                 "id\0"
299                 "protocol\0"
300                 "reorder_hdr\0"
301                 "gvrp\0"
302                 "mvrp\0"
303                 "loose_binding\0"
304         ;
305         static const char protocols[] ALIGN1 =
306                 "802.1q\0"
307                 "802.1ad\0"
308         ;
309         static const char str_on_off[] ALIGN1 =
310                 "on\0"
311                 "off\0"
312         ;
313         enum {
314                 ARG_id = 0,
315                 ARG_reorder_hdr,
316                 ARG_gvrp,
317                 ARG_mvrp,
318                 ARG_loose_binding,
319                 ARG_protocol,
320         };
321         enum {
322                 PROTO_8021Q = 0,
323                 PROTO_8021AD,
324         };
325         enum {
326                 PARM_on = 0,
327                 PARM_off
328         };
329         int arg;
330         uint16_t id, proto;
331         struct ifla_vlan_flags flags = {};
332
333         while (*argv) {
334                 arg = index_in_substrings(keywords, *argv);
335                 if (arg < 0)
336                         invarg(*argv, "type vlan");
337
338                 NEXT_ARG();
339                 if (arg == ARG_id) {
340                         id = get_u16(*argv, "id");
341                         addattr_l(n, size, IFLA_VLAN_ID, &id, sizeof(id));
342                 } else if (arg == ARG_protocol) {
343                         arg = index_in_substrings(protocols, *argv);
344                         if (arg == PROTO_8021Q)
345                                 proto = ETH_P_8021Q;
346                         else if (arg == PROTO_8021AD)
347                                 proto = ETH_P_8021AD;
348                         else
349                                 bb_error_msg_and_die("unknown VLAN encapsulation protocol '%s'",
350                                                                      *argv);
351                         addattr_l(n, size, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto));
352                 } else {
353                         int param = index_in_strings(str_on_off, *argv);
354                         if (param < 0)
355                                 die_must_be_on_off(nth_string(keywords, arg));
356
357                         if (arg == ARG_reorder_hdr) {
358                                 flags.mask |= VLAN_FLAG_REORDER_HDR;
359                                 flags.flags &= ~VLAN_FLAG_REORDER_HDR;
360                                 if (param == PARM_on)
361                                         flags.flags |= VLAN_FLAG_REORDER_HDR;
362                         } else if (arg == ARG_gvrp) {
363                                 flags.mask |= VLAN_FLAG_GVRP;
364                                 flags.flags &= ~VLAN_FLAG_GVRP;
365                                 if (param == PARM_on)
366                                         flags.flags |= VLAN_FLAG_GVRP;
367                         } else if (arg == ARG_mvrp) {
368                                 flags.mask |= VLAN_FLAG_MVRP;
369                                 flags.flags &= ~VLAN_FLAG_MVRP;
370                                 if (param == PARM_on)
371                                         flags.flags |= VLAN_FLAG_MVRP;
372                         } else { /*if (arg == ARG_loose_binding) */
373                                 flags.mask |= VLAN_FLAG_LOOSE_BINDING;
374                                 flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
375                                 if (param == PARM_on)
376                                         flags.flags |= VLAN_FLAG_LOOSE_BINDING;
377                         }
378                 }
379                 argv++;
380         }
381
382         if (flags.mask)
383                 addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
384 }
385
386 #ifndef NLMSG_TAIL
387 #define NLMSG_TAIL(nmsg) \
388         ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
389 #endif
390 /* Return value becomes exitcode. It's okay to not return at all */
391 static int do_add_or_delete(char **argv, const unsigned rtm)
392 {
393         static const char keywords[] ALIGN1 =
394                 "link\0""name\0""type\0""dev\0";
395         enum {
396                 ARG_link,
397                 ARG_name,
398                 ARG_type,
399                 ARG_dev,
400         };
401         struct rtnl_handle rth;
402         struct {
403                 struct nlmsghdr  n;
404                 struct ifinfomsg i;
405                 char             buf[1024];
406         } req;
407         smalluint arg;
408         char *name_str = NULL, *link_str = NULL, *type_str = NULL, *dev_str = NULL;
409
410         memset(&req, 0, sizeof(req));
411
412         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
413         req.n.nlmsg_flags = NLM_F_REQUEST;
414         req.n.nlmsg_type = rtm;
415         req.i.ifi_family = preferred_family;
416         if (rtm == RTM_NEWLINK)
417                 req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
418
419         while (*argv) {
420                 arg = index_in_substrings(keywords, *argv);
421                 if (arg == ARG_type) {
422                         NEXT_ARG();
423                         type_str = *argv++;
424                         break;
425                 }
426                 if (arg == ARG_link) {
427                         NEXT_ARG();
428                         link_str = *argv;
429                 } else if (arg == ARG_name) {
430                         NEXT_ARG();
431                         name_str = *argv;
432                 } else {
433                         if (arg == ARG_dev) {
434                                 if (dev_str)
435                                         duparg(*argv, "dev");
436                                 NEXT_ARG();
437                         }
438                         dev_str = *argv;
439                 }
440                 argv++;
441         }
442         xrtnl_open(&rth);
443         ll_init_map(&rth);
444         if (type_str) {
445                 struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
446
447                 addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
448                 addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
449                                 strlen(type_str));
450
451                 if (*argv) {
452                         struct rtattr *data = NLMSG_TAIL(&req.n);
453                         addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
454
455                         if (strcmp(type_str, "vlan") == 0)
456                                 vlan_parse_opt(argv, &req.n, sizeof(req));
457
458                         data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
459                 }
460
461                 linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
462         }
463         if (rtm != RTM_NEWLINK) {
464                 if (!dev_str)
465                         return 1; /* Need a device to delete */
466                 req.i.ifi_index = xll_name_to_index(dev_str);
467         } else {
468                 if (!name_str)
469                         name_str = dev_str;
470                 if (link_str) {
471                         int idx = xll_name_to_index(link_str);
472                         addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
473                 }
474         }
475         if (name_str) {
476                 const size_t name_len = strlen(name_str) + 1;
477                 if (name_len < 2 || name_len > IFNAMSIZ)
478                         invarg(name_str, "name");
479                 addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
480         }
481         if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
482                 return 2;
483         return 0;
484 }
485
486 /* Return value becomes exitcode. It's okay to not return at all */
487 int FAST_FUNC do_iplink(char **argv)
488 {
489         static const char keywords[] ALIGN1 =
490                 "add\0""delete\0""set\0""show\0""lst\0""list\0";
491         if (*argv) {
492                 int key = index_in_substrings(keywords, *argv);
493                 if (key < 0) /* invalid argument */
494                         invarg(*argv, applet_name);
495                 argv++;
496                 if (key <= 1) /* add/delete */
497                         return do_add_or_delete(argv, key ? RTM_DELLINK : RTM_NEWLINK);
498                 if (key == 2) /* set */
499                         return do_set(argv);
500         }
501         /* show, lst, list */
502         return ipaddr_list_link(argv);
503 }