iplink.c revision 7b5657545dc246ae37690d660597e8fa37040205
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 |\n"); 40 fprintf(stderr, " arp { on | off } |\n"); 41 fprintf(stderr, " dynamic { on | off } |\n"); 42 fprintf(stderr, " multicast { on | off } |\n"); 43 fprintf(stderr, " allmulticast { on | off } |\n"); 44 fprintf(stderr, " promisc { on | off } |\n"); 45 fprintf(stderr, " trailers { on | off } |\n"); 46 fprintf(stderr, " txqueuelen PACKETS |\n"); 47 fprintf(stderr, " name NEWNAME |\n"); 48 fprintf(stderr, " address LLADDR | broadcast LLADDR |\n"); 49 fprintf(stderr, " mtu MTU }\n"); 50 fprintf(stderr, " ip link show [ DEVICE ]\n"); 51 exit(-1); 52} 53 54static void usage(void) 55{ 56 iplink_usage(); 57} 58 59static int on_off(char *msg) 60{ 61 fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); 62 return -1; 63} 64 65static int get_ctl_fd(void) 66{ 67 int s_errno; 68 int fd; 69 70 fd = socket(PF_INET, SOCK_DGRAM, 0); 71 if (fd >= 0) 72 return fd; 73 s_errno = errno; 74 fd = socket(PF_PACKET, SOCK_DGRAM, 0); 75 if (fd >= 0) 76 return fd; 77 fd = socket(PF_INET6, SOCK_DGRAM, 0); 78 if (fd >= 0) 79 return fd; 80 errno = s_errno; 81 perror("Cannot create control socket"); 82 return -1; 83} 84 85static int do_chflags(const char *dev, __u32 flags, __u32 mask) 86{ 87 struct ifreq ifr; 88 int fd; 89 int err; 90 91 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 92 fd = get_ctl_fd(); 93 if (fd < 0) 94 return -1; 95 err = ioctl(fd, SIOCGIFFLAGS, &ifr); 96 if (err) { 97 perror("SIOCGIFFLAGS"); 98 close(fd); 99 return -1; 100 } 101 if ((ifr.ifr_flags^flags)&mask) { 102 ifr.ifr_flags &= ~mask; 103 ifr.ifr_flags |= mask&flags; 104 err = ioctl(fd, SIOCSIFFLAGS, &ifr); 105 if (err) 106 perror("SIOCSIFFLAGS"); 107 } 108 close(fd); 109 return err; 110} 111 112static int do_changename(const char *dev, const char *newdev) 113{ 114 struct ifreq ifr; 115 int fd; 116 int err; 117 118 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 119 strncpy(ifr.ifr_newname, newdev, IFNAMSIZ); 120 fd = get_ctl_fd(); 121 if (fd < 0) 122 return -1; 123 err = ioctl(fd, SIOCSIFNAME, &ifr); 124 if (err) { 125 perror("SIOCSIFNAME"); 126 close(fd); 127 return -1; 128 } 129 close(fd); 130 return err; 131} 132 133static int set_qlen(const char *dev, int qlen) 134{ 135 struct ifreq ifr; 136 int s; 137 138 s = get_ctl_fd(); 139 if (s < 0) 140 return -1; 141 142 memset(&ifr, 0, sizeof(ifr)); 143 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 144 ifr.ifr_qlen = qlen; 145 if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { 146 perror("SIOCSIFXQLEN"); 147 close(s); 148 return -1; 149 } 150 close(s); 151 152 return 0; 153} 154 155static int set_mtu(const char *dev, int mtu) 156{ 157 struct ifreq ifr; 158 int s; 159 160 s = get_ctl_fd(); 161 if (s < 0) 162 return -1; 163 164 memset(&ifr, 0, sizeof(ifr)); 165 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 166 ifr.ifr_mtu = mtu; 167 if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { 168 perror("SIOCSIFMTU"); 169 close(s); 170 return -1; 171 } 172 close(s); 173 174 return 0; 175} 176 177static int get_address(const char *dev, int *htype) 178{ 179 struct ifreq ifr; 180 struct sockaddr_ll me; 181 socklen_t alen; 182 int s; 183 184 s = socket(PF_PACKET, SOCK_DGRAM, 0); 185 if (s < 0) { 186 perror("socket(PF_PACKET)"); 187 return -1; 188 } 189 190 memset(&ifr, 0, sizeof(ifr)); 191 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 192 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { 193 perror("SIOCGIFINDEX"); 194 close(s); 195 return -1; 196 } 197 198 memset(&me, 0, sizeof(me)); 199 me.sll_family = AF_PACKET; 200 me.sll_ifindex = ifr.ifr_ifindex; 201 me.sll_protocol = htons(ETH_P_LOOP); 202 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { 203 perror("bind"); 204 close(s); 205 return -1; 206 } 207 208 alen = sizeof(me); 209 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { 210 perror("getsockname"); 211 close(s); 212 return -1; 213 } 214 close(s); 215 *htype = me.sll_hatype; 216 return me.sll_halen; 217} 218 219static int parse_address(const char *dev, int hatype, int halen, 220 char *lla, struct ifreq *ifr) 221{ 222 int alen; 223 224 memset(ifr, 0, sizeof(*ifr)); 225 strncpy(ifr->ifr_name, dev, IFNAMSIZ); 226 ifr->ifr_hwaddr.sa_family = hatype; 227 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); 228 if (alen < 0) 229 return -1; 230 if (alen != halen) { 231 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); 232 return -1; 233 } 234 return 0; 235} 236 237static int set_address(struct ifreq *ifr, int brd) 238{ 239 int s; 240 241 s = get_ctl_fd(); 242 if (s < 0) 243 return -1; 244 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { 245 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); 246 close(s); 247 return -1; 248 } 249 close(s); 250 return 0; 251} 252 253 254static int do_set(int argc, char **argv) 255{ 256 char *dev = NULL; 257 __u32 mask = 0; 258 __u32 flags = 0; 259 int qlen = -1; 260 int mtu = -1; 261 char *newaddr = NULL; 262 char *newbrd = NULL; 263 struct ifreq ifr0, ifr1; 264 char *newname = NULL; 265 int htype, halen; 266 267 while (argc > 0) { 268 if (strcmp(*argv, "up") == 0) { 269 mask |= IFF_UP; 270 flags |= IFF_UP; 271 } else if (strcmp(*argv, "down") == 0) { 272 mask |= IFF_UP; 273 flags &= ~IFF_UP; 274 } else if (strcmp(*argv, "name") == 0) { 275 NEXT_ARG(); 276 newname = *argv; 277 } else if (matches(*argv, "address") == 0) { 278 NEXT_ARG(); 279 newaddr = *argv; 280 } else if (matches(*argv, "broadcast") == 0 || 281 strcmp(*argv, "brd") == 0) { 282 NEXT_ARG(); 283 newbrd = *argv; 284 } else if (matches(*argv, "txqueuelen") == 0 || 285 strcmp(*argv, "qlen") == 0 || 286 matches(*argv, "txqlen") == 0) { 287 NEXT_ARG(); 288 if (qlen != -1) 289 duparg("txqueuelen", *argv); 290 if (get_integer(&qlen, *argv, 0)) 291 invarg("Invalid \"txqueuelen\" value\n", *argv); 292 } else if (strcmp(*argv, "mtu") == 0) { 293 NEXT_ARG(); 294 if (mtu != -1) 295 duparg("mtu", *argv); 296 if (get_integer(&mtu, *argv, 0)) 297 invarg("Invalid \"mtu\" value\n", *argv); 298 } else if (strcmp(*argv, "multicast") == 0) { 299 NEXT_ARG(); 300 mask |= IFF_MULTICAST; 301 if (strcmp(*argv, "on") == 0) { 302 flags |= IFF_MULTICAST; 303 } else if (strcmp(*argv, "off") == 0) { 304 flags &= ~IFF_MULTICAST; 305 } else 306 return on_off("multicast"); 307 } else if (strcmp(*argv, "allmulticast") == 0) { 308 NEXT_ARG(); 309 mask |= IFF_ALLMULTI; 310 if (strcmp(*argv, "on") == 0) { 311 flags |= IFF_ALLMULTI; 312 } else if (strcmp(*argv, "off") == 0) { 313 flags &= ~IFF_ALLMULTI; 314 } else 315 return on_off("allmulticast"); 316 } else if (strcmp(*argv, "promisc") == 0) { 317 NEXT_ARG(); 318 mask |= IFF_PROMISC; 319 if (strcmp(*argv, "on") == 0) { 320 flags |= IFF_PROMISC; 321 } else if (strcmp(*argv, "off") == 0) { 322 flags &= ~IFF_PROMISC; 323 } else 324 return on_off("promisc"); 325 } else if (strcmp(*argv, "trailers") == 0) { 326 NEXT_ARG(); 327 mask |= IFF_NOTRAILERS; 328 if (strcmp(*argv, "off") == 0) { 329 flags |= IFF_NOTRAILERS; 330 } else if (strcmp(*argv, "on") == 0) { 331 flags &= ~IFF_NOTRAILERS; 332 } else 333 return on_off("trailers"); 334 } else if (strcmp(*argv, "arp") == 0) { 335 NEXT_ARG(); 336 mask |= IFF_NOARP; 337 if (strcmp(*argv, "on") == 0) { 338 flags &= ~IFF_NOARP; 339 } else if (strcmp(*argv, "off") == 0) { 340 flags |= IFF_NOARP; 341 } else 342 return on_off("noarp"); 343#ifdef IFF_DYNAMIC 344 } else if (matches(*argv, "dynamic") == 0) { 345 NEXT_ARG(); 346 mask |= IFF_DYNAMIC; 347 if (strcmp(*argv, "on") == 0) { 348 flags |= IFF_DYNAMIC; 349 } else if (strcmp(*argv, "off") == 0) { 350 flags &= ~IFF_DYNAMIC; 351 } else 352 return on_off("dynamic"); 353#endif 354 } else { 355 if (strcmp(*argv, "dev") == 0) { 356 NEXT_ARG(); 357 } 358 if (matches(*argv, "help") == 0) 359 usage(); 360 if (dev) 361 duparg2("dev", *argv); 362 dev = *argv; 363 } 364 argc--; argv++; 365 } 366 367 if (!dev) { 368 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); 369 exit(-1); 370 } 371 372 if (newaddr || newbrd) { 373 halen = get_address(dev, &htype); 374 if (halen < 0) 375 return -1; 376 if (newaddr) { 377 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) 378 return -1; 379 } 380 if (newbrd) { 381 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) 382 return -1; 383 } 384 } 385 386 if (newname && strcmp(dev, newname)) { 387 if (do_changename(dev, newname) < 0) 388 return -1; 389 dev = newname; 390 } 391 if (qlen != -1) { 392 if (set_qlen(dev, qlen) < 0) 393 return -1; 394 } 395 if (mtu != -1) { 396 if (set_mtu(dev, mtu) < 0) 397 return -1; 398 } 399 if (newaddr || newbrd) { 400 if (newbrd) { 401 if (set_address(&ifr1, 1) < 0) 402 return -1; 403 } 404 if (newaddr) { 405 if (set_address(&ifr0, 0) < 0) 406 return -1; 407 } 408 } 409 if (mask) 410 return do_chflags(dev, flags, mask); 411 return 0; 412} 413 414int do_iplink(int argc, char **argv) 415{ 416 if (argc > 0) { 417 if (matches(*argv, "set") == 0) 418 return do_set(argc-1, argv+1); 419 if (matches(*argv, "show") == 0 || 420 matches(*argv, "lst") == 0 || 421 matches(*argv, "list") == 0) 422 return ipaddr_list_link(argc-1, argv+1); 423 if (matches(*argv, "help") == 0) 424 usage(); 425 } else 426 return ipaddr_list_link(0, NULL); 427 428 fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); 429 exit(-1); 430} 431