busybox/networking/libiproute/iplink.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   4 *
   5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   6 */
   7#include <net/if.h>
   8#include <net/if_packet.h>
   9#include <netpacket/packet.h>
  10#include <net/ethernet.h>
  11
  12#include "ip_common.h"  /* #include "libbb.h" is inside */
  13#include "rt_names.h"
  14#include "utils.h"
  15
  16#ifndef IFLA_LINKINFO
  17# define IFLA_LINKINFO 18
  18# define IFLA_INFO_KIND 1
  19#endif
  20
  21/* taken from linux/sockios.h */
  22#define SIOCSIFNAME  0x8923  /* set interface name */
  23
  24/* Exits on error */
  25static int get_ctl_fd(void)
  26{
  27        int fd;
  28
  29        fd = socket(PF_INET, SOCK_DGRAM, 0);
  30        if (fd >= 0)
  31                return fd;
  32        fd = socket(PF_PACKET, SOCK_DGRAM, 0);
  33        if (fd >= 0)
  34                return fd;
  35        return xsocket(PF_INET6, SOCK_DGRAM, 0);
  36}
  37
  38/* Exits on error */
  39static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
  40{
  41        struct ifreq ifr;
  42        int fd;
  43
  44        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  45        fd = get_ctl_fd();
  46        xioctl(fd, SIOCGIFFLAGS, &ifr);
  47        if ((ifr.ifr_flags ^ flags) & mask) {
  48                ifr.ifr_flags &= ~mask;
  49                ifr.ifr_flags |= mask & flags;
  50                xioctl(fd, SIOCSIFFLAGS, &ifr);
  51        }
  52        close(fd);
  53}
  54
  55/* Exits on error */
  56static void do_changename(char *dev, char *newdev)
  57{
  58        struct ifreq ifr;
  59        int fd;
  60
  61        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  62        strncpy_IFNAMSIZ(ifr.ifr_newname, newdev);
  63        fd = get_ctl_fd();
  64        xioctl(fd, SIOCSIFNAME, &ifr);
  65        close(fd);
  66}
  67
  68/* Exits on error */
  69static void set_qlen(char *dev, int qlen)
  70{
  71        struct ifreq ifr;
  72        int s;
  73
  74        s = get_ctl_fd();
  75        memset(&ifr, 0, sizeof(ifr));
  76        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  77        ifr.ifr_qlen = qlen;
  78        xioctl(s, SIOCSIFTXQLEN, &ifr);
  79        close(s);
  80}
  81
  82/* Exits on error */
  83static void set_mtu(char *dev, int mtu)
  84{
  85        struct ifreq ifr;
  86        int s;
  87
  88        s = get_ctl_fd();
  89        memset(&ifr, 0, sizeof(ifr));
  90        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  91        ifr.ifr_mtu = mtu;
  92        xioctl(s, SIOCSIFMTU, &ifr);
  93        close(s);
  94}
  95
  96/* Exits on error */
  97static int get_address(char *dev, int *htype)
  98{
  99        struct ifreq ifr;
 100        struct sockaddr_ll me;
 101        socklen_t alen;
 102        int s;
 103
 104        s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
 105
 106        memset(&ifr, 0, sizeof(ifr));
 107        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
 108        xioctl(s, SIOCGIFINDEX, &ifr);
 109
 110        memset(&me, 0, sizeof(me));
 111        me.sll_family = AF_PACKET;
 112        me.sll_ifindex = ifr.ifr_ifindex;
 113        me.sll_protocol = htons(ETH_P_LOOP);
 114        xbind(s, (struct sockaddr*)&me, sizeof(me));
 115        alen = sizeof(me);
 116        getsockname(s, (struct sockaddr*)&me, &alen);
 117        //never happens:
 118        //if (getsockname(s, (struct sockaddr*)&me, &alen) == -1)
 119        //      bb_perror_msg_and_die("getsockname");
 120        close(s);
 121        *htype = me.sll_hatype;
 122        return me.sll_halen;
 123}
 124
 125/* Exits on error */
 126static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
 127{
 128        int alen;
 129
 130        memset(ifr, 0, sizeof(*ifr));
 131        strncpy_IFNAMSIZ(ifr->ifr_name, dev);
 132        ifr->ifr_hwaddr.sa_family = hatype;
 133
 134        alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
 135        alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla);
 136        if (alen < 0)
 137                exit(EXIT_FAILURE);
 138        if (alen != halen) {
 139                bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
 140        }
 141}
 142
 143/* Exits on error */
 144static void set_address(struct ifreq *ifr, int brd)
 145{
 146        int s;
 147
 148        s = get_ctl_fd();
 149        if (brd)
 150                xioctl(s, SIOCSIFHWBROADCAST, ifr);
 151        else
 152                xioctl(s, SIOCSIFHWADDR, ifr);
 153        close(s);
 154}
 155
 156
 157static void die_must_be_on_off(const char *msg) NORETURN;
 158static void die_must_be_on_off(const char *msg)
 159{
 160        bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg);
 161}
 162
 163/* Return value becomes exitcode. It's okay to not return at all */
 164static int do_set(char **argv)
 165{
 166        char *dev = NULL;
 167        uint32_t mask = 0;
 168        uint32_t flags = 0;
 169        int qlen = -1;
 170        int mtu = -1;
 171        char *newaddr = NULL;
 172        char *newbrd = NULL;
 173        struct ifreq ifr0, ifr1;
 174        char *newname = NULL;
 175        int htype, halen;
 176        static const char keywords[] ALIGN1 =
 177                "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0"
 178                "arp\0""address\0""dev\0";
 179        enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast,
 180                ARG_arp, ARG_addr, ARG_dev };
 181        static const char str_on_off[] ALIGN1 = "on\0""off\0";
 182        enum { PARM_on = 0, PARM_off };
 183        smalluint key;
 184
 185        while (*argv) {
 186                /* substring search ensures that e.g. "addr" and "address"
 187                 * are both accepted */
 188                key = index_in_substrings(keywords, *argv);
 189                if (key == ARG_up) {
 190                        mask |= IFF_UP;
 191                        flags |= IFF_UP;
 192                } else if (key == ARG_down) {
 193                        mask |= IFF_UP;
 194                        flags &= ~IFF_UP;
 195                } else if (key == ARG_name) {
 196                        NEXT_ARG();
 197                        newname = *argv;
 198                } else if (key == ARG_mtu) {
 199                        NEXT_ARG();
 200                        if (mtu != -1)
 201                                duparg("mtu", *argv);
 202                        mtu = get_unsigned(*argv, "mtu");
 203                } else if (key == ARG_qlen) {
 204                        NEXT_ARG();
 205                        if (qlen != -1)
 206                                duparg("qlen", *argv);
 207                        qlen = get_unsigned(*argv, "qlen");
 208                } else if (key == ARG_addr) {
 209                        NEXT_ARG();
 210                        newaddr = *argv;
 211                } else if (key >= ARG_dev) {
 212                        if (key == ARG_dev) {
 213                                NEXT_ARG();
 214                        }
 215                        if (dev)
 216                                duparg2("dev", *argv);
 217                        dev = *argv;
 218                } else {
 219                        int param;
 220                        NEXT_ARG();
 221                        param = index_in_strings(str_on_off, *argv);
 222                        if (key == ARG_multicast) {
 223                                if (param < 0)
 224                                        die_must_be_on_off("multicast");
 225                                mask |= IFF_MULTICAST;
 226                                if (param == PARM_on)
 227                                        flags |= IFF_MULTICAST;
 228                                else
 229                                        flags &= ~IFF_MULTICAST;
 230                        } else if (key == ARG_arp) {
 231                                if (param < 0)
 232                                        die_must_be_on_off("arp");
 233                                mask |= IFF_NOARP;
 234                                if (param == PARM_on)
 235                                        flags &= ~IFF_NOARP;
 236                                else
 237                                        flags |= IFF_NOARP;
 238                        }
 239                }
 240                argv++;
 241        }
 242
 243        if (!dev) {
 244                bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
 245        }
 246
 247        if (newaddr || newbrd) {
 248                halen = get_address(dev, &htype);
 249                if (newaddr) {
 250                        parse_address(dev, htype, halen, newaddr, &ifr0);
 251                        set_address(&ifr0, 0);
 252                }
 253                if (newbrd) {
 254                        parse_address(dev, htype, halen, newbrd, &ifr1);
 255                        set_address(&ifr1, 1);
 256                }
 257        }
 258
 259        if (newname && strcmp(dev, newname)) {
 260                do_changename(dev, newname);
 261                dev = newname;
 262        }
 263        if (qlen != -1) {
 264                set_qlen(dev, qlen);
 265        }
 266        if (mtu != -1) {
 267                set_mtu(dev, mtu);
 268        }
 269        if (mask)
 270                do_chflags(dev, flags, mask);
 271        return 0;
 272}
 273
 274static int ipaddr_list_link(char **argv)
 275{
 276        preferred_family = AF_PACKET;
 277        return ipaddr_list_or_flush(argv, 0);
 278}
 279
 280#ifndef NLMSG_TAIL
 281#define NLMSG_TAIL(nmsg) \
 282        ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
 283#endif
 284/* Return value becomes exitcode. It's okay to not return at all */
 285static int do_change(char **argv, const unsigned rtm)
 286{
 287        static const char keywords[] ALIGN1 =
 288                "link\0""name\0""type\0""dev\0";
 289        enum {
 290                ARG_link,
 291                ARG_name,
 292                ARG_type,
 293                ARG_dev,
 294        };
 295        struct rtnl_handle rth;
 296        struct {
 297                struct nlmsghdr  n;
 298                struct ifinfomsg i;
 299                char             buf[1024];
 300        } req;
 301        smalluint arg;
 302        char *name_str = NULL, *link_str = NULL, *type_str = NULL, *dev_str = NULL;
 303
 304        memset(&req, 0, sizeof(req));
 305
 306        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
 307        req.n.nlmsg_flags = NLM_F_REQUEST;
 308        req.n.nlmsg_type = rtm;
 309        req.i.ifi_family = preferred_family;
 310        if (rtm == RTM_NEWLINK)
 311                req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
 312
 313        while (*argv) {
 314                arg = index_in_substrings(keywords, *argv);
 315                if (arg == ARG_link) {
 316                        NEXT_ARG();
 317                        link_str = *argv;
 318                } else if (arg == ARG_name) {
 319                        NEXT_ARG();
 320                        name_str = *argv;
 321                } else if (arg == ARG_type) {
 322                        NEXT_ARG();
 323                        type_str = *argv;
 324                } else {
 325                        if (arg == ARG_dev) {
 326                                if (dev_str)
 327                                        duparg(*argv, "dev");
 328                                NEXT_ARG();
 329                        }
 330                        dev_str = *argv;
 331                }
 332                argv++;
 333        }
 334        xrtnl_open(&rth);
 335        ll_init_map(&rth);
 336        if (type_str) {
 337                struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
 338
 339                addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
 340                addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
 341                                strlen(type_str));
 342                linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
 343        }
 344        if (rtm != RTM_NEWLINK) {
 345                if (!dev_str)
 346                        return 1; /* Need a device to delete */
 347                req.i.ifi_index = xll_name_to_index(dev_str);
 348        } else {
 349                if (!name_str)
 350                        name_str = dev_str;
 351                if (link_str) {
 352                        int idx = xll_name_to_index(link_str);
 353                        addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
 354                }
 355        }
 356        if (name_str) {
 357                const size_t name_len = strlen(name_str) + 1;
 358                if (name_len < 2 || name_len > IFNAMSIZ)
 359                        invarg(name_str, "name");
 360                addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
 361        }
 362        if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
 363                return 2;
 364        return 0;
 365}
 366
 367/* Return value becomes exitcode. It's okay to not return at all */
 368int FAST_FUNC do_iplink(char **argv)
 369{
 370        static const char keywords[] ALIGN1 =
 371                "add\0""delete\0""set\0""show\0""lst\0""list\0";
 372        if (*argv) {
 373                smalluint key = index_in_substrings(keywords, *argv);
 374                if (key > 5) /* invalid argument */
 375                        bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
 376                argv++;
 377                if (key <= 1) /* add/delete */
 378                        return do_change(argv, key ? RTM_DELLINK : RTM_NEWLINK);
 379                else if (key == 2) /* set */
 380                        return do_set(argv);
 381        }
 382        /* show, lst, list */
 383        return ipaddr_list_link(argv);
 384}
 385