[1765] | 1 | /* vi: set sw=4 ts=4: */
|
---|
[821] | 2 | /*
|
---|
[1765] | 3 | * iplink.c "ip link".
|
---|
[821] | 4 | *
|
---|
[1765] | 5 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
---|
[821] | 6 | *
|
---|
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
|
---|
| 8 | */
|
---|
| 9 |
|
---|
[1765] | 10 | //#include <sys/ioctl.h>
|
---|
| 11 | //#include <sys/socket.h>
|
---|
[821] | 12 | #include <net/if.h>
|
---|
| 13 | #include <net/if_packet.h>
|
---|
| 14 | #include <netpacket/packet.h>
|
---|
| 15 | #include <net/ethernet.h>
|
---|
| 16 |
|
---|
[1765] | 17 | #include "ip_common.h" /* #include "libbb.h" is inside */
|
---|
[821] | 18 | #include "rt_names.h"
|
---|
| 19 | #include "utils.h"
|
---|
| 20 |
|
---|
[1765] | 21 | /* taken from linux/sockios.h */
|
---|
[821] | 22 | #define SIOCSIFNAME 0x8923 /* set interface name */
|
---|
| 23 |
|
---|
[1765] | 24 | static void on_off(const char *msg) ATTRIBUTE_NORETURN;
|
---|
| 25 | static void on_off(const char *msg)
|
---|
[821] | 26 | {
|
---|
[1765] | 27 | bb_error_msg_and_die("error: argument of \"%s\" must be \"on\" or \"off\"", msg);
|
---|
[821] | 28 | }
|
---|
| 29 |
|
---|
[1765] | 30 | /* Exits on error */
|
---|
[821] | 31 | static int get_ctl_fd(void)
|
---|
| 32 | {
|
---|
| 33 | int fd;
|
---|
| 34 |
|
---|
| 35 | fd = socket(PF_INET, SOCK_DGRAM, 0);
|
---|
| 36 | if (fd >= 0)
|
---|
| 37 | return fd;
|
---|
| 38 | fd = socket(PF_PACKET, SOCK_DGRAM, 0);
|
---|
| 39 | if (fd >= 0)
|
---|
| 40 | return fd;
|
---|
[1765] | 41 | return xsocket(PF_INET6, SOCK_DGRAM, 0);
|
---|
[821] | 42 | }
|
---|
| 43 |
|
---|
[1765] | 44 | /* Exits on error */
|
---|
| 45 | static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
|
---|
[821] | 46 | {
|
---|
| 47 | struct ifreq ifr;
|
---|
| 48 | int fd;
|
---|
| 49 |
|
---|
[1765] | 50 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
|
---|
[821] | 51 | fd = get_ctl_fd();
|
---|
[1765] | 52 | xioctl(fd, SIOCGIFFLAGS, &ifr);
|
---|
| 53 | if ((ifr.ifr_flags ^ flags) & mask) {
|
---|
[821] | 54 | ifr.ifr_flags &= ~mask;
|
---|
[1765] | 55 | ifr.ifr_flags |= mask & flags;
|
---|
| 56 | xioctl(fd, SIOCSIFFLAGS, &ifr);
|
---|
[821] | 57 | }
|
---|
| 58 | close(fd);
|
---|
| 59 | }
|
---|
| 60 |
|
---|
[1765] | 61 | /* Exits on error */
|
---|
| 62 | static void do_changename(char *dev, char *newdev)
|
---|
[821] | 63 | {
|
---|
| 64 | struct ifreq ifr;
|
---|
| 65 | int fd;
|
---|
| 66 |
|
---|
[1765] | 67 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
|
---|
| 68 | strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname));
|
---|
[821] | 69 | fd = get_ctl_fd();
|
---|
[1765] | 70 | xioctl(fd, SIOCSIFNAME, &ifr);
|
---|
[821] | 71 | close(fd);
|
---|
| 72 | }
|
---|
| 73 |
|
---|
[1765] | 74 | /* Exits on error */
|
---|
| 75 | static void set_qlen(char *dev, int qlen)
|
---|
[821] | 76 | {
|
---|
| 77 | struct ifreq ifr;
|
---|
| 78 | int s;
|
---|
| 79 |
|
---|
| 80 | s = get_ctl_fd();
|
---|
| 81 | memset(&ifr, 0, sizeof(ifr));
|
---|
[1765] | 82 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
|
---|
[821] | 83 | ifr.ifr_qlen = qlen;
|
---|
[1765] | 84 | xioctl(s, SIOCSIFTXQLEN, &ifr);
|
---|
[821] | 85 | close(s);
|
---|
| 86 | }
|
---|
| 87 |
|
---|
[1765] | 88 | /* Exits on error */
|
---|
| 89 | static void set_mtu(char *dev, int mtu)
|
---|
[821] | 90 | {
|
---|
| 91 | struct ifreq ifr;
|
---|
| 92 | int s;
|
---|
| 93 |
|
---|
| 94 | s = get_ctl_fd();
|
---|
| 95 | memset(&ifr, 0, sizeof(ifr));
|
---|
[1765] | 96 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
|
---|
[821] | 97 | ifr.ifr_mtu = mtu;
|
---|
[1765] | 98 | xioctl(s, SIOCSIFMTU, &ifr);
|
---|
[821] | 99 | close(s);
|
---|
| 100 | }
|
---|
| 101 |
|
---|
[1765] | 102 | /* Exits on error */
|
---|
[821] | 103 | static int get_address(char *dev, int *htype)
|
---|
| 104 | {
|
---|
| 105 | struct ifreq ifr;
|
---|
| 106 | struct sockaddr_ll me;
|
---|
| 107 | socklen_t alen;
|
---|
| 108 | int s;
|
---|
| 109 |
|
---|
[1765] | 110 | s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
|
---|
[821] | 111 |
|
---|
| 112 | memset(&ifr, 0, sizeof(ifr));
|
---|
[1765] | 113 | strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
|
---|
| 114 | xioctl(s, SIOCGIFINDEX, &ifr);
|
---|
[821] | 115 |
|
---|
| 116 | memset(&me, 0, sizeof(me));
|
---|
| 117 | me.sll_family = AF_PACKET;
|
---|
| 118 | me.sll_ifindex = ifr.ifr_ifindex;
|
---|
| 119 | me.sll_protocol = htons(ETH_P_LOOP);
|
---|
[1765] | 120 | xbind(s, (struct sockaddr*)&me, sizeof(me));
|
---|
[821] | 121 |
|
---|
| 122 | alen = sizeof(me);
|
---|
| 123 | if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
|
---|
[1765] | 124 | bb_perror_msg_and_die("getsockname");
|
---|
[821] | 125 | }
|
---|
| 126 | close(s);
|
---|
| 127 | *htype = me.sll_hatype;
|
---|
| 128 | return me.sll_halen;
|
---|
| 129 | }
|
---|
| 130 |
|
---|
[1765] | 131 | /* Exits on error */
|
---|
| 132 | static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
|
---|
[821] | 133 | {
|
---|
| 134 | int alen;
|
---|
| 135 |
|
---|
| 136 | memset(ifr, 0, sizeof(*ifr));
|
---|
[1765] | 137 | strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name));
|
---|
[821] | 138 | ifr->ifr_hwaddr.sa_family = hatype;
|
---|
| 139 | alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), 14, lla);
|
---|
| 140 | if (alen < 0)
|
---|
[1765] | 141 | exit(1);
|
---|
[821] | 142 | if (alen != halen) {
|
---|
[1765] | 143 | bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
|
---|
[821] | 144 | }
|
---|
| 145 | }
|
---|
| 146 |
|
---|
[1765] | 147 | /* Exits on error */
|
---|
| 148 | static void set_address(struct ifreq *ifr, int brd)
|
---|
[821] | 149 | {
|
---|
| 150 | int s;
|
---|
| 151 |
|
---|
| 152 | s = get_ctl_fd();
|
---|
[1765] | 153 | if (brd)
|
---|
| 154 | xioctl(s, SIOCSIFHWBROADCAST, ifr);
|
---|
| 155 | else
|
---|
| 156 | xioctl(s, SIOCSIFHWADDR, ifr);
|
---|
[821] | 157 | close(s);
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 |
|
---|
[1765] | 161 | /* Return value becomes exitcode. It's okay to not return at all */
|
---|
[821] | 162 | static int do_set(int argc, char **argv)
|
---|
| 163 | {
|
---|
| 164 | char *dev = NULL;
|
---|
[1765] | 165 | uint32_t mask = 0;
|
---|
| 166 | uint32_t flags = 0;
|
---|
[821] | 167 | int qlen = -1;
|
---|
| 168 | int mtu = -1;
|
---|
| 169 | char *newaddr = NULL;
|
---|
| 170 | char *newbrd = NULL;
|
---|
| 171 | struct ifreq ifr0, ifr1;
|
---|
| 172 | char *newname = NULL;
|
---|
| 173 | int htype, halen;
|
---|
[1765] | 174 | static const char keywords[] ALIGN1 =
|
---|
| 175 | "up\0""down\0""name\0""mtu\0""multicast\0""arp\0""addr\0""dev\0"
|
---|
| 176 | "on\0""off\0";
|
---|
| 177 | enum { ARG_up = 1, ARG_down, ARG_name, ARG_mtu, ARG_multicast, ARG_arp,
|
---|
| 178 | ARG_addr, ARG_dev, PARM_on, PARM_off };
|
---|
| 179 | smalluint key;
|
---|
[821] | 180 |
|
---|
| 181 | while (argc > 0) {
|
---|
[1765] | 182 | key = index_in_strings(keywords, *argv) + 1;
|
---|
| 183 | if (key == ARG_up) {
|
---|
[821] | 184 | mask |= IFF_UP;
|
---|
| 185 | flags |= IFF_UP;
|
---|
[1765] | 186 | } else if (key == ARG_down) {
|
---|
[821] | 187 | mask |= IFF_UP;
|
---|
| 188 | flags &= ~IFF_UP;
|
---|
[1765] | 189 | } else if (key == ARG_name) {
|
---|
[821] | 190 | NEXT_ARG();
|
---|
| 191 | newname = *argv;
|
---|
[1765] | 192 | } else if (key == ARG_mtu) {
|
---|
[821] | 193 | NEXT_ARG();
|
---|
| 194 | if (mtu != -1)
|
---|
| 195 | duparg("mtu", *argv);
|
---|
| 196 | if (get_integer(&mtu, *argv, 0))
|
---|
| 197 | invarg(*argv, "mtu");
|
---|
[1765] | 198 | } else if (key == ARG_multicast) {
|
---|
[821] | 199 | NEXT_ARG();
|
---|
| 200 | mask |= IFF_MULTICAST;
|
---|
[1765] | 201 | key = index_in_strings(keywords, *argv) + 1;
|
---|
| 202 | if (key == PARM_on) {
|
---|
[821] | 203 | flags |= IFF_MULTICAST;
|
---|
[1765] | 204 | } else if (key == PARM_off) {
|
---|
[821] | 205 | flags &= ~IFF_MULTICAST;
|
---|
| 206 | } else
|
---|
[1765] | 207 | on_off("multicast");
|
---|
| 208 | } else if (key == ARG_arp) {
|
---|
[821] | 209 | NEXT_ARG();
|
---|
| 210 | mask |= IFF_NOARP;
|
---|
[1765] | 211 | key = index_in_strings(keywords, *argv) + 1;
|
---|
| 212 | if (key == PARM_on) {
|
---|
[821] | 213 | flags &= ~IFF_NOARP;
|
---|
[1765] | 214 | } else if (key == PARM_off) {
|
---|
[821] | 215 | flags |= IFF_NOARP;
|
---|
| 216 | } else
|
---|
[1765] | 217 | on_off("arp");
|
---|
| 218 | } else if (key == ARG_addr) {
|
---|
[821] | 219 | NEXT_ARG();
|
---|
| 220 | newaddr = *argv;
|
---|
| 221 | } else {
|
---|
[1765] | 222 | if (key == ARG_dev) {
|
---|
[821] | 223 | NEXT_ARG();
|
---|
| 224 | }
|
---|
| 225 | if (dev)
|
---|
| 226 | duparg2("dev", *argv);
|
---|
| 227 | dev = *argv;
|
---|
| 228 | }
|
---|
| 229 | argc--; argv++;
|
---|
| 230 | }
|
---|
| 231 |
|
---|
| 232 | if (!dev) {
|
---|
[1765] | 233 | bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
|
---|
[821] | 234 | }
|
---|
| 235 |
|
---|
| 236 | if (newaddr || newbrd) {
|
---|
| 237 | halen = get_address(dev, &htype);
|
---|
| 238 | if (newaddr) {
|
---|
[1765] | 239 | parse_address(dev, htype, halen, newaddr, &ifr0);
|
---|
[821] | 240 | }
|
---|
| 241 | if (newbrd) {
|
---|
[1765] | 242 | parse_address(dev, htype, halen, newbrd, &ifr1);
|
---|
[821] | 243 | }
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | if (newname && strcmp(dev, newname)) {
|
---|
[1765] | 247 | do_changename(dev, newname);
|
---|
[821] | 248 | dev = newname;
|
---|
| 249 | }
|
---|
| 250 | if (qlen != -1) {
|
---|
[1765] | 251 | set_qlen(dev, qlen);
|
---|
[821] | 252 | }
|
---|
| 253 | if (mtu != -1) {
|
---|
[1765] | 254 | set_mtu(dev, mtu);
|
---|
[821] | 255 | }
|
---|
| 256 | if (newaddr || newbrd) {
|
---|
| 257 | if (newbrd) {
|
---|
[1765] | 258 | set_address(&ifr1, 1);
|
---|
[821] | 259 | }
|
---|
| 260 | if (newaddr) {
|
---|
[1765] | 261 | set_address(&ifr0, 0);
|
---|
[821] | 262 | }
|
---|
| 263 | }
|
---|
| 264 | if (mask)
|
---|
[1765] | 265 | do_chflags(dev, flags, mask);
|
---|
[821] | 266 | return 0;
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 | static int ipaddr_list_link(int argc, char **argv)
|
---|
| 270 | {
|
---|
| 271 | preferred_family = AF_PACKET;
|
---|
| 272 | return ipaddr_list_or_flush(argc, argv, 0);
|
---|
| 273 | }
|
---|
| 274 |
|
---|
[1765] | 275 | /* Return value becomes exitcode. It's okay to not return at all */
|
---|
[821] | 276 | int do_iplink(int argc, char **argv)
|
---|
| 277 | {
|
---|
[1765] | 278 | static const char keywords[] ALIGN1 =
|
---|
| 279 | "set\0""show\0""lst\0""list\0";
|
---|
| 280 | smalluint key;
|
---|
| 281 | if (argc <= 0)
|
---|
[821] | 282 | return ipaddr_list_link(0, NULL);
|
---|
[1765] | 283 | key = index_in_substrings(keywords, *argv) + 1;
|
---|
| 284 | if (key == 0)
|
---|
| 285 | bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
|
---|
| 286 | argc--; argv++;
|
---|
| 287 | if (key == 1) /* set */
|
---|
| 288 | return do_set(argc, argv);
|
---|
| 289 | else /* show, lst, list */
|
---|
| 290 | return ipaddr_list_link(argc, argv);
|
---|
[821] | 291 | }
|
---|