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 24static void on_off(const char *msg) ATTRIBUTE_NORETURN; 25static void on_off(const char *msg) 26{ 27 bb_error_msg_and_die("error: argument of \"%s\" must be \"on\" or \"off\"", msg); 28} 29 30/* Exits on error */ 31static 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; 41 return xsocket(PF_INET6, SOCK_DGRAM, 0); 42} 43 44/* Exits on error */ 45static void do_chflags(char *dev, uint32_t flags, uint32_t mask) 46{ 47 struct ifreq ifr; 48 int fd; 49 50 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 51 fd = get_ctl_fd(); 52 xioctl(fd, SIOCGIFFLAGS, &ifr); 53 if ((ifr.ifr_flags ^ flags) & mask) { 54 ifr.ifr_flags &= ~mask; 55 ifr.ifr_flags |= mask & flags; 56 xioctl(fd, SIOCSIFFLAGS, &ifr); 57 } 58 close(fd); 59} 60 61/* Exits on error */ 62static void do_changename(char *dev, char *newdev) 63{ 64 struct ifreq ifr; 65 int fd; 66 67 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 68 strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname)); 69 fd = get_ctl_fd(); 70 xioctl(fd, SIOCSIFNAME, &ifr); 71 close(fd); 72} 73 74/* Exits on error */ 75static void set_qlen(char *dev, int qlen) 76{ 77 struct ifreq ifr; 78 int s; 79 80 s = get_ctl_fd(); 81 memset(&ifr, 0, sizeof(ifr)); 82 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 83 ifr.ifr_qlen = qlen; 84 xioctl(s, SIOCSIFTXQLEN, &ifr); 85 close(s); 86} 87 88/* Exits on error */ 89static void set_mtu(char *dev, int mtu) 90{ 91 struct ifreq ifr; 92 int s; 93 94 s = get_ctl_fd(); 95 memset(&ifr, 0, sizeof(ifr)); 96 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 97 ifr.ifr_mtu = mtu; 98 xioctl(s, SIOCSIFMTU, &ifr); 99 close(s); 100} 101 102/* Exits on error */ 103static 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 110 s = xsocket(PF_PACKET, SOCK_DGRAM, 0); 111 112 memset(&ifr, 0, sizeof(ifr)); 113 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 114 xioctl(s, SIOCGIFINDEX, &ifr); 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); 120 xbind(s, (struct sockaddr*)&me, sizeof(me)); 121 122 alen = sizeof(me); 123 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { 124 bb_perror_msg_and_die("getsockname"); 125 } 126 close(s); 127 *htype = me.sll_hatype; 128 return me.sll_halen; 129} 130 131/* Exits on error */ 132static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) 133{ 134 int alen; 135 136 memset(ifr, 0, sizeof(*ifr)); 137 strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name)); 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) 141 exit(1); 142 if (alen != halen) { 143 bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen); 144 } 145} 146 147/* Exits on error */ 148static void set_address(struct ifreq *ifr, int brd) 149{ 150 int s; 151 152 s = get_ctl_fd(); 153 if (brd) 154 xioctl(s, SIOCSIFHWBROADCAST, ifr); 155 else 156 xioctl(s, SIOCSIFHWADDR, ifr); 157 close(s); 158} 159 160 161/* Return value becomes exitcode. It's okay to not return at all */ 162static int do_set(int argc, char **argv) 163{ 164 char *dev = NULL; 165 uint32_t mask = 0; 166 uint32_t flags = 0; 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; 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; 180 181 while (argc > 0) { 182 key = index_in_strings(keywords, *argv) + 1; 183 if (key == ARG_up) { 184 mask |= IFF_UP; 185 flags |= IFF_UP; 186 } else if (key == ARG_down) { 187 mask |= IFF_UP; 188 flags &= ~IFF_UP; 189 } else if (key == ARG_name) { 190 NEXT_ARG(); 191 newname = *argv; 192 } else if (key == ARG_mtu) { 193 NEXT_ARG(); 194 if (mtu != -1) 195 duparg("mtu", *argv); 196 if (get_integer(&mtu, *argv, 0)) 197 invarg(*argv, "mtu"); 198 } else if (key == ARG_multicast) { 199 NEXT_ARG(); 200 mask |= IFF_MULTICAST; 201 key = index_in_strings(keywords, *argv) + 1; 202 if (key == PARM_on) { 203 flags |= IFF_MULTICAST; 204 } else if (key == PARM_off) { 205 flags &= ~IFF_MULTICAST; 206 } else 207 on_off("multicast"); 208 } else if (key == ARG_arp) { 209 NEXT_ARG(); 210 mask |= IFF_NOARP; 211 key = index_in_strings(keywords, *argv) + 1; 212 if (key == PARM_on) { 213 flags &= ~IFF_NOARP; 214 } else if (key == PARM_off) { 215 flags |= IFF_NOARP; 216 } else 217 on_off("arp"); 218 } else if (key == ARG_addr) { 219 NEXT_ARG(); 220 newaddr = *argv; 221 } else { 222 if (key == ARG_dev) { 223 NEXT_ARG(); 224 } 225 if (dev) 226 duparg2("dev", *argv); 227 dev = *argv; 228 } 229 argc--; argv++; 230 } 231 232 if (!dev) { 233 bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\""); 234 } 235 236 if (newaddr || newbrd) { 237 halen = get_address(dev, &htype); 238 if (newaddr) { 239 parse_address(dev, htype, halen, newaddr, &ifr0); 240 } 241 if (newbrd) { 242 parse_address(dev, htype, halen, newbrd, &ifr1); 243 } 244 } 245 246 if (newname && strcmp(dev, newname)) { 247 do_changename(dev, newname); 248 dev = newname; 249 } 250 if (qlen != -1) { 251 set_qlen(dev, qlen); 252 } 253 if (mtu != -1) { 254 set_mtu(dev, mtu); 255 } 256 if (newaddr || newbrd) { 257 if (newbrd) { 258 set_address(&ifr1, 1); 259 } 260 if (newaddr) { 261 set_address(&ifr0, 0); 262 } 263 } 264 if (mask) 265 do_chflags(dev, flags, mask); 266 return 0; 267} 268 269static 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 275/* Return value becomes exitcode. It's okay to not return at all */ 276int do_iplink(int argc, char **argv) 277{ 278 static const char keywords[] ALIGN1 = 279 "set\0""show\0""lst\0""list\0"; 280 smalluint key; 281 if (argc <= 0) 282 return ipaddr_list_link(0, NULL); 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); 291} 292