iplink.c revision aba5acdfdb347d2c21fc67d613d83d4430ca3937
1/* 2 * iplink.c "ip link". 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 10 * 11 */ 12 13#include <stdio.h> 14#include <stdlib.h> 15#include <unistd.h> 16#include <syslog.h> 17#include <fcntl.h> 18#include <errno.h> 19#include <sys/socket.h> 20#include <linux/if.h> 21#include <linux/if_packet.h> 22#include <linux/if_ether.h> 23#include <linux/sockios.h> 24#include <netinet/in.h> 25#include <arpa/inet.h> 26#include <string.h> 27#include <sys/ioctl.h> 28#include <linux/sockios.h> 29 30#include "rt_names.h" 31#include "utils.h" 32#include "ip_common.h" 33 34 35static void usage(void) __attribute__((noreturn)); 36 37void iplink_usage(void) 38{ 39 fprintf(stderr, "Usage: ip link set DEVICE { up | down | arp { on | off } |\n"); 40 fprintf(stderr, " dynamic { on | off } |\n"); 41 fprintf(stderr, " multicast { on | off } | txqueuelen PACKETS |\n"); 42 fprintf(stderr, " name NEWNAME |\n"); 43 fprintf(stderr, " address LLADDR | broadcast LLADDR |\n"); 44 fprintf(stderr, " mtu MTU }\n"); 45 fprintf(stderr, " ip link show [ DEVICE ]\n"); 46 exit(-1); 47} 48 49static void usage(void) 50{ 51 iplink_usage(); 52} 53 54static int on_off(char *msg) 55{ 56 fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); 57 return -1; 58} 59 60static int get_ctl_fd(void) 61{ 62 int s_errno; 63 int fd; 64 65 fd = socket(PF_INET, SOCK_DGRAM, 0); 66 if (fd >= 0) 67 return fd; 68 s_errno = errno; 69 fd = socket(PF_PACKET, SOCK_DGRAM, 0); 70 if (fd >= 0) 71 return fd; 72 fd = socket(PF_INET6, SOCK_DGRAM, 0); 73 if (fd >= 0) 74 return fd; 75 errno = s_errno; 76 perror("Cannot create control socket"); 77 return -1; 78} 79 80static int do_chflags(char *dev, __u32 flags, __u32 mask) 81{ 82 struct ifreq ifr; 83 int fd; 84 int err; 85 86 strcpy(ifr.ifr_name, dev); 87 fd = get_ctl_fd(); 88 if (fd < 0) 89 return -1; 90 err = ioctl(fd, SIOCGIFFLAGS, &ifr); 91 if (err) { 92 perror("SIOCGIFFLAGS"); 93 close(fd); 94 return -1; 95 } 96 if ((ifr.ifr_flags^flags)&mask) { 97 ifr.ifr_flags &= ~mask; 98 ifr.ifr_flags |= mask&flags; 99 err = ioctl(fd, SIOCSIFFLAGS, &ifr); 100 if (err) 101 perror("SIOCSIFFLAGS"); 102 } 103 close(fd); 104 return err; 105} 106 107static int do_changename(char *dev, char *newdev) 108{ 109 struct ifreq ifr; 110 int fd; 111 int err; 112 113 strcpy(ifr.ifr_name, dev); 114 strcpy(ifr.ifr_newname, newdev); 115 fd = get_ctl_fd(); 116 if (fd < 0) 117 return -1; 118 err = ioctl(fd, SIOCSIFNAME, &ifr); 119 if (err) { 120 perror("SIOCSIFNAME"); 121 close(fd); 122 return -1; 123 } 124 close(fd); 125 return err; 126} 127 128static int set_qlen(char *dev, int qlen) 129{ 130 struct ifreq ifr; 131 int s; 132 133 s = get_ctl_fd(); 134 if (s < 0) 135 return -1; 136 137 memset(&ifr, 0, sizeof(ifr)); 138 strcpy(ifr.ifr_name, dev); 139 ifr.ifr_qlen = qlen; 140 if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { 141 perror("SIOCSIFXQLEN"); 142 close(s); 143 return -1; 144 } 145 close(s); 146 147 return 0; 148} 149 150static int set_mtu(char *dev, int mtu) 151{ 152 struct ifreq ifr; 153 int s; 154 155 s = get_ctl_fd(); 156 if (s < 0) 157 return -1; 158 159 memset(&ifr, 0, sizeof(ifr)); 160 strcpy(ifr.ifr_name, dev); 161 ifr.ifr_mtu = mtu; 162 if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { 163 perror("SIOCSIFMTU"); 164 close(s); 165 return -1; 166 } 167 close(s); 168 169 return 0; 170} 171 172static int get_address(char *dev, int *htype) 173{ 174 struct ifreq ifr; 175 struct sockaddr_ll me; 176 int alen; 177 int s; 178 179 s = socket(PF_PACKET, SOCK_DGRAM, 0); 180 if (s < 0) { 181 perror("socket(PF_PACKET)"); 182 return -1; 183 } 184 185 memset(&ifr, 0, sizeof(ifr)); 186 strcpy(ifr.ifr_name, dev); 187 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { 188 perror("SIOCGIFINDEX"); 189 close(s); 190 return -1; 191 } 192 193 memset(&me, 0, sizeof(me)); 194 me.sll_family = AF_PACKET; 195 me.sll_ifindex = ifr.ifr_ifindex; 196 me.sll_protocol = htons(ETH_P_LOOP); 197 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { 198 perror("bind"); 199 close(s); 200 return -1; 201 } 202 203 alen = sizeof(me); 204 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { 205 perror("getsockname"); 206 close(s); 207 return -1; 208 } 209 close(s); 210 *htype = me.sll_hatype; 211 return me.sll_halen; 212} 213 214static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) 215{ 216 int alen; 217 218 memset(ifr, 0, sizeof(*ifr)); 219 strcpy(ifr->ifr_name, dev); 220 ifr->ifr_hwaddr.sa_family = hatype; 221 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); 222 if (alen < 0) 223 return -1; 224 if (alen != halen) { 225 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); 226 return -1; 227 } 228 return 0; 229} 230 231static int set_address(struct ifreq *ifr, int brd) 232{ 233 int s; 234 235 s = get_ctl_fd(); 236 if (s < 0) 237 return -1; 238 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { 239 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); 240 close(s); 241 return -1; 242 } 243 close(s); 244 return 0; 245} 246 247 248static int do_set(int argc, char **argv) 249{ 250 char *dev = NULL; 251 __u32 mask = 0; 252 __u32 flags = 0; 253 int qlen = -1; 254 int mtu = -1; 255 char *newaddr = NULL; 256 char *newbrd = NULL; 257 struct ifreq ifr0, ifr1; 258 char *newname = NULL; 259 int htype, halen; 260 261 while (argc > 0) { 262 if (strcmp(*argv, "up") == 0) { 263 mask |= IFF_UP; 264 flags |= IFF_UP; 265 } else if (strcmp(*argv, "down") == 0) { 266 mask |= IFF_UP; 267 flags &= ~IFF_UP; 268 } else if (strcmp(*argv, "name") == 0) { 269 NEXT_ARG(); 270 newname = *argv; 271 } else if (matches(*argv, "address") == 0) { 272 NEXT_ARG(); 273 newaddr = *argv; 274 } else if (matches(*argv, "broadcast") == 0 || 275 strcmp(*argv, "brd") == 0) { 276 NEXT_ARG(); 277 newbrd = *argv; 278 } else if (matches(*argv, "txqueuelen") == 0 || 279 strcmp(*argv, "qlen") == 0 || 280 matches(*argv, "txqlen") == 0) { 281 NEXT_ARG(); 282 if (qlen != -1) 283 duparg("txqueuelen", *argv); 284 if (get_integer(&qlen, *argv, 0)) 285 invarg("Invalid \"txqueuelen\" value\n", *argv); 286 } else if (strcmp(*argv, "mtu") == 0) { 287 NEXT_ARG(); 288 if (mtu != -1) 289 duparg("mtu", *argv); 290 if (get_integer(&mtu, *argv, 0)) 291 invarg("Invalid \"mtu\" value\n", *argv); 292 } else if (strcmp(*argv, "multicast") == 0) { 293 NEXT_ARG(); 294 mask |= IFF_MULTICAST; 295 if (strcmp(*argv, "on") == 0) { 296 flags |= IFF_MULTICAST; 297 } else if (strcmp(*argv, "off") == 0) { 298 flags &= ~IFF_MULTICAST; 299 } else 300 return on_off("multicast"); 301 } else if (strcmp(*argv, "arp") == 0) { 302 NEXT_ARG(); 303 mask |= IFF_NOARP; 304 if (strcmp(*argv, "on") == 0) { 305 flags &= ~IFF_NOARP; 306 } else if (strcmp(*argv, "off") == 0) { 307 flags |= IFF_NOARP; 308 } else 309 return on_off("noarp"); 310#ifdef IFF_DYNAMIC 311 } else if (matches(*argv, "dynamic") == 0) { 312 NEXT_ARG(); 313 mask |= IFF_DYNAMIC; 314 if (strcmp(*argv, "on") == 0) { 315 flags |= IFF_DYNAMIC; 316 } else if (strcmp(*argv, "off") == 0) { 317 flags &= ~IFF_DYNAMIC; 318 } else 319 return on_off("dynamic"); 320#endif 321 } else { 322 if (strcmp(*argv, "dev") == 0) { 323 NEXT_ARG(); 324 } 325 if (matches(*argv, "help") == 0) 326 usage(); 327 if (dev) 328 duparg2("dev", *argv); 329 dev = *argv; 330 } 331 argc--; argv++; 332 } 333 334 if (!dev) { 335 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); 336 exit(-1); 337 } 338 339 if (newaddr || newbrd) { 340 halen = get_address(dev, &htype); 341 if (halen < 0) 342 return -1; 343 if (newaddr) { 344 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) 345 return -1; 346 } 347 if (newbrd) { 348 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) 349 return -1; 350 } 351 } 352 353 if (newname && strcmp(dev, newname)) { 354 if (do_changename(dev, newname) < 0) 355 return -1; 356 dev = newname; 357 } 358 if (qlen != -1) { 359 if (set_qlen(dev, qlen) < 0) 360 return -1; 361 } 362 if (mtu != -1) { 363 if (set_mtu(dev, mtu) < 0) 364 return -1; 365 } 366 if (newaddr || newbrd) { 367 if (newbrd) { 368 if (set_address(&ifr1, 1) < 0) 369 return -1; 370 } 371 if (newaddr) { 372 if (set_address(&ifr0, 0) < 0) 373 return -1; 374 } 375 } 376 if (mask) 377 return do_chflags(dev, flags, mask); 378 return 0; 379} 380 381int do_iplink(int argc, char **argv) 382{ 383 if (argc > 0) { 384 if (matches(*argv, "set") == 0) 385 return do_set(argc-1, argv+1); 386 if (matches(*argv, "show") == 0 || 387 matches(*argv, "lst") == 0 || 388 matches(*argv, "list") == 0) 389 return ipaddr_list_link(argc-1, argv+1); 390 if (matches(*argv, "help") == 0) 391 usage(); 392 } else 393 return ipaddr_list_link(0, NULL); 394 395 fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); 396 exit(-1); 397} 398