busybox/networking/libiproute/iplink.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * iplink.c "ip link".
   4 *
   5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
   8 */
   9
  10//#include <sys/ioctl.h>
  11//#include <sys/socket.h>
  12#include <net/if.h>
  13#include <net/if_packet.h>
  14#include <netpacket/packet.h>
  15#include <net/ethernet.h>
  16
  17#include "ip_common.h"  /* #include "libbb.h" is inside */
  18#include "rt_names.h"
  19#include "utils.h"
  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
 116        alen = sizeof(me);
 117        if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
 118                bb_perror_msg_and_die("getsockname");
 119        }
 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""multicast\0"
 178                "arp\0""address\0""dev\0";
 179        enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, 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                }
 193                if (key == ARG_down) {
 194                        mask |= IFF_UP;
 195                        flags &= ~IFF_UP;
 196                }
 197                if (key == ARG_name) {
 198                        NEXT_ARG();
 199                        newname = *argv;
 200                }
 201                if (key == ARG_mtu) {
 202                        NEXT_ARG();
 203                        if (mtu != -1)
 204                                duparg("mtu", *argv);
 205                        mtu = get_unsigned(*argv, "mtu");
 206                }
 207                if (key == ARG_multicast) {
 208                        int param;
 209                        NEXT_ARG();
 210                        mask |= IFF_MULTICAST;
 211                        param = index_in_strings(str_on_off, *argv);
 212                        if (param < 0)
 213                                die_must_be_on_off("multicast");
 214                        if (param == PARM_on)
 215                                flags |= IFF_MULTICAST;
 216                        else
 217                                flags &= ~IFF_MULTICAST;
 218                }
 219                if (key == ARG_arp) {
 220                        int param;
 221                        NEXT_ARG();
 222                        mask |= IFF_NOARP;
 223                        param = index_in_strings(str_on_off, *argv);
 224                        if (param < 0)
 225                                die_must_be_on_off("arp");
 226                        if (param == PARM_on)
 227                                flags &= ~IFF_NOARP;
 228                        else
 229                                flags |= IFF_NOARP;
 230                }
 231                if (key == ARG_addr) {
 232                        NEXT_ARG();
 233                        newaddr = *argv;
 234                }
 235                if (key >= ARG_dev) {
 236                        if (key == ARG_dev) {
 237                                NEXT_ARG();
 238                        }
 239                        if (dev)
 240                                duparg2("dev", *argv);
 241                        dev = *argv;
 242                }
 243                argv++;
 244        }
 245
 246        if (!dev) {
 247                bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
 248        }
 249
 250        if (newaddr || newbrd) {
 251                halen = get_address(dev, &htype);
 252                if (newaddr) {
 253                        parse_address(dev, htype, halen, newaddr, &ifr0);
 254                }
 255                if (newbrd) {
 256                        parse_address(dev, htype, halen, newbrd, &ifr1);
 257                }
 258        }
 259
 260        if (newname && strcmp(dev, newname)) {
 261                do_changename(dev, newname);
 262                dev = newname;
 263        }
 264        if (qlen != -1) {
 265                set_qlen(dev, qlen);
 266        }
 267        if (mtu != -1) {
 268                set_mtu(dev, mtu);
 269        }
 270        if (newaddr || newbrd) {
 271                if (newbrd) {
 272                        set_address(&ifr1, 1);
 273                }
 274                if (newaddr) {
 275                        set_address(&ifr0, 0);
 276                }
 277        }
 278        if (mask)
 279                do_chflags(dev, flags, mask);
 280        return 0;
 281}
 282
 283static int ipaddr_list_link(char **argv)
 284{
 285        preferred_family = AF_PACKET;
 286        return ipaddr_list_or_flush(argv, 0);
 287}
 288
 289/* Return value becomes exitcode. It's okay to not return at all */
 290int do_iplink(char **argv)
 291{
 292        static const char keywords[] ALIGN1 =
 293                "set\0""show\0""lst\0""list\0";
 294        int key;
 295        if (!*argv)
 296                return ipaddr_list_link(argv);
 297        key = index_in_substrings(keywords, *argv);
 298        if (key < 0)
 299                bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
 300        argv++;
 301        if (key == 0) /* set */
 302                return do_set(argv);
 303        /* show, lst, list */
 304        return ipaddr_list_link(argv);
 305}
 306