iplink.c revision ca78b0e7d4042be7f7283d551d433b76da7f312b
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 <dlfcn.h> 19#include <errno.h> 20#include <sys/socket.h> 21#include <linux/if.h> 22#include <linux/if_packet.h> 23#include <linux/if_ether.h> 24#include <linux/sockios.h> 25#include <netinet/in.h> 26#include <arpa/inet.h> 27#include <string.h> 28#include <sys/ioctl.h> 29#include <linux/sockios.h> 30 31#include "rt_names.h" 32#include "utils.h" 33#include "ip_common.h" 34 35#define IPLINK_IOCTL_COMPAT 1 36 37static void usage(void) __attribute__((noreturn)); 38 39void iplink_usage(void) 40{ 41 fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n"); 42 fprintf(stderr, " arp { on | off } |\n"); 43 fprintf(stderr, " dynamic { on | off } |\n"); 44 fprintf(stderr, " multicast { on | off } |\n"); 45 fprintf(stderr, " allmulticast { on | off } |\n"); 46 fprintf(stderr, " promisc { on | off } |\n"); 47 fprintf(stderr, " trailers { on | off } |\n"); 48 fprintf(stderr, " txqueuelen PACKETS |\n"); 49 fprintf(stderr, " name NEWNAME |\n"); 50 fprintf(stderr, " address LLADDR | broadcast LLADDR |\n"); 51 fprintf(stderr, " mtu MTU }\n"); 52 fprintf(stderr, " ip link show [ DEVICE ]\n"); 53 exit(-1); 54} 55 56static void usage(void) 57{ 58 iplink_usage(); 59} 60 61static int on_off(char *msg) 62{ 63 fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); 64 return -1; 65} 66 67static void *BODY; /* cached dlopen(NULL) handle */ 68static struct link_util *linkutil_list; 69 70struct link_util *get_link_kind(const char *id) 71{ 72 void *dlh; 73 char buf[256]; 74 struct link_util *l; 75 76 for (l = linkutil_list; l; l = l->next) 77 if (strcmp(l->id, id) == 0) 78 return l; 79 80 snprintf(buf, sizeof(buf), "/usr/lib/ip/link_%s.so", id); 81 dlh = dlopen(buf, RTLD_LAZY); 82 if (dlh == NULL) { 83 /* look in current binary, only open once */ 84 dlh = BODY; 85 if (dlh == NULL) { 86 dlh = BODY = dlopen(NULL, RTLD_LAZY); 87 if (dlh == NULL) 88 return NULL; 89 } 90 } 91 92 snprintf(buf, sizeof(buf), "%s_link_util", id); 93 l = dlsym(dlh, buf); 94 if (l == NULL) 95 return NULL; 96 97 l->next = linkutil_list; 98 linkutil_list = l; 99 return l; 100} 101 102#if IPLINK_IOCTL_COMPAT 103static int have_rtnl_newlink = -1; 104 105static int accept_msg(const struct sockaddr_nl *who, 106 struct nlmsghdr *n, void *arg) 107{ 108 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n); 109 110 if (n->nlmsg_type == NLMSG_ERROR && err->error == -EOPNOTSUPP) 111 have_rtnl_newlink = 0; 112 else 113 have_rtnl_newlink = 1; 114 return -1; 115} 116 117static int iplink_have_newlink(void) 118{ 119 struct { 120 struct nlmsghdr n; 121 struct ifinfomsg i; 122 char buf[1024]; 123 } req; 124 125 if (have_rtnl_newlink < 0) { 126 memset(&req, 0, sizeof(req)); 127 128 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 129 req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; 130 req.n.nlmsg_type = RTM_NEWLINK; 131 req.i.ifi_family = AF_UNSPEC; 132 133 rtnl_send(&rth, (char *)&req.n, req.n.nlmsg_len); 134 rtnl_listen(&rth, accept_msg, NULL); 135 } 136 return have_rtnl_newlink; 137} 138#else /* IPLINK_IOCTL_COMPAT */ 139static int iplink_have_newlink(void) 140{ 141 return 1; 142} 143#endif /* ! IPLINK_IOCTL_COMPAT */ 144 145static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) 146{ 147 int qlen = -1; 148 int mtu = -1; 149 int len; 150 char abuf[32]; 151 char *dev = NULL; 152 char *name = NULL; 153 char *link = NULL; 154 char *type = NULL; 155 struct link_util *lu = NULL; 156 struct { 157 struct nlmsghdr n; 158 struct ifinfomsg i; 159 char buf[1024]; 160 } req; 161 162 memset(&req, 0, sizeof(req)); 163 164 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 165 req.n.nlmsg_flags = NLM_F_REQUEST|flags; 166 req.n.nlmsg_type = cmd; 167 req.i.ifi_family = preferred_family; 168 169 while (argc > 0) { 170 if (strcmp(*argv, "up") == 0) { 171 req.i.ifi_change |= IFF_UP; 172 req.i.ifi_flags |= IFF_UP; 173 } else if (strcmp(*argv, "down") == 0) { 174 req.i.ifi_change |= IFF_UP; 175 req.i.ifi_flags &= ~IFF_UP; 176 } else if (strcmp(*argv, "name") == 0) { 177 NEXT_ARG(); 178 name = *argv; 179 } else if (matches(*argv, "link") == 0) { 180 NEXT_ARG(); 181 link = *argv; 182 } else if (matches(*argv, "address") == 0) { 183 NEXT_ARG(); 184 len = ll_addr_a2n(abuf, sizeof(abuf), *argv); 185 addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len); 186 } else if (matches(*argv, "broadcast") == 0 || 187 strcmp(*argv, "brd") == 0) { 188 NEXT_ARG(); 189 len = ll_addr_a2n(abuf, sizeof(abuf), *argv); 190 addattr_l(&req.n, sizeof(req), IFLA_BROADCAST, abuf, len); 191 } else if (matches(*argv, "txqueuelen") == 0 || 192 strcmp(*argv, "qlen") == 0 || 193 matches(*argv, "txqlen") == 0) { 194 NEXT_ARG(); 195 if (qlen != -1) 196 duparg("txqueuelen", *argv); 197 if (get_integer(&qlen, *argv, 0)) 198 invarg("Invalid \"txqueuelen\" value\n", *argv); 199 addattr_l(&req.n, sizeof(req), IFLA_TXQLEN, &qlen, 4); 200 } else if (strcmp(*argv, "mtu") == 0) { 201 NEXT_ARG(); 202 if (mtu != -1) 203 duparg("mtu", *argv); 204 if (get_integer(&mtu, *argv, 0)) 205 invarg("Invalid \"mtu\" value\n", *argv); 206 addattr_l(&req.n, sizeof(req), IFLA_MTU, &mtu, 4); 207 } else if (strcmp(*argv, "multicast") == 0) { 208 NEXT_ARG(); 209 req.i.ifi_change |= IFF_MULTICAST; 210 if (strcmp(*argv, "on") == 0) { 211 req.i.ifi_flags |= IFF_MULTICAST; 212 } else if (strcmp(*argv, "off") == 0) { 213 req.i.ifi_flags &= ~IFF_MULTICAST; 214 } else 215 return on_off("multicast"); 216 } else if (strcmp(*argv, "allmulticast") == 0) { 217 NEXT_ARG(); 218 req.i.ifi_change |= IFF_ALLMULTI; 219 if (strcmp(*argv, "on") == 0) { 220 req.i.ifi_flags |= IFF_ALLMULTI; 221 } else if (strcmp(*argv, "off") == 0) { 222 req.i.ifi_flags &= ~IFF_ALLMULTI; 223 } else 224 return on_off("allmulticast"); 225 } else if (strcmp(*argv, "promisc") == 0) { 226 NEXT_ARG(); 227 req.i.ifi_change |= IFF_PROMISC; 228 if (strcmp(*argv, "on") == 0) { 229 req.i.ifi_flags |= IFF_PROMISC; 230 } else if (strcmp(*argv, "off") == 0) { 231 req.i.ifi_flags &= ~IFF_PROMISC; 232 } else 233 return on_off("promisc"); 234 } else if (strcmp(*argv, "trailers") == 0) { 235 NEXT_ARG(); 236 req.i.ifi_change |= IFF_NOTRAILERS; 237 if (strcmp(*argv, "off") == 0) { 238 req.i.ifi_flags |= IFF_NOTRAILERS; 239 } else if (strcmp(*argv, "on") == 0) { 240 req.i.ifi_flags &= ~IFF_NOTRAILERS; 241 } else 242 return on_off("trailers"); 243 } else if (strcmp(*argv, "arp") == 0) { 244 NEXT_ARG(); 245 req.i.ifi_change |= IFF_NOARP; 246 if (strcmp(*argv, "on") == 0) { 247 req.i.ifi_flags &= ~IFF_NOARP; 248 } else if (strcmp(*argv, "off") == 0) { 249 req.i.ifi_flags |= IFF_NOARP; 250 } else 251 return on_off("noarp"); 252#ifdef IFF_DYNAMIC 253 } else if (matches(*argv, "dynamic") == 0) { 254 NEXT_ARG(); 255 req.i.ifi_change |= IFF_DYNAMIC; 256 if (strcmp(*argv, "on") == 0) { 257 req.i.ifi_flags |= IFF_DYNAMIC; 258 } else if (strcmp(*argv, "off") == 0) { 259 req.i.ifi_flags &= ~IFF_DYNAMIC; 260 } else 261 return on_off("dynamic"); 262#endif 263 } else if (matches(*argv, "type") == 0) { 264 NEXT_ARG(); 265 type = *argv; 266 argc--; argv++; 267 break; 268 } else { 269 if (strcmp(*argv, "dev") == 0) { 270 NEXT_ARG(); 271 } 272 if (dev) 273 duparg2("dev", *argv); 274 dev = *argv; 275 } 276 argc--; argv++; 277 } 278 279 ll_init_map(&rth); 280 281 if (type) { 282 struct rtattr *linkinfo = NLMSG_TAIL(&req.n); 283 addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); 284 addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, 285 strlen(type)); 286 287 lu = get_link_kind(type); 288 if (lu && argc) { 289 struct rtattr * data = NLMSG_TAIL(&req.n); 290 addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); 291 292 if (lu->parse_opt && 293 lu->parse_opt(lu, argc, argv, &req.n)) 294 return -1; 295 296 data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; 297 } else if (argc) { 298 if (matches(*argv, "help") == 0) 299 usage(); 300 fprintf(stderr, "Garbage instead of arguments \"%s ...\". " 301 "Try \"ip link help\".\n", *argv); 302 return -1; 303 } 304 linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; 305 } 306 307 if (!(flags & NLM_F_CREATE)) { 308 if (!dev) { 309 fprintf(stderr, "Not enough information: \"dev\" " 310 "argument is required.\n"); 311 exit(-1); 312 } 313 314 req.i.ifi_index = ll_name_to_index(dev); 315 if (req.i.ifi_index == 0) { 316 fprintf(stderr, "Cannot find device \"%s\"\n", dev); 317 return -1; 318 } 319 } else { 320 /* Allow "ip link add dev" and "ip link add name" */ 321 if (!name) 322 name = dev; 323 324 if (link) { 325 int ifindex; 326 327 ifindex = ll_name_to_index(link); 328 if (ifindex == 0) { 329 fprintf(stderr, "Cannot find device \"%s\"\n", 330 link); 331 return -1; 332 } 333 addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4); 334 } 335 } 336 337 if (name) { 338 len = strlen(name) + 1; 339 if (len == 1) 340 invarg("\"\" is not a valid device identifier\n", "name"); 341 if (len > IFNAMSIZ) 342 invarg("\"name\" too long\n", name); 343 addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len); 344 } 345 346 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) 347 exit(2); 348 349 return 0; 350} 351 352#if IPLINK_IOCTL_COMPAT 353static int get_ctl_fd(void) 354{ 355 int s_errno; 356 int fd; 357 358 fd = socket(PF_INET, SOCK_DGRAM, 0); 359 if (fd >= 0) 360 return fd; 361 s_errno = errno; 362 fd = socket(PF_PACKET, SOCK_DGRAM, 0); 363 if (fd >= 0) 364 return fd; 365 fd = socket(PF_INET6, SOCK_DGRAM, 0); 366 if (fd >= 0) 367 return fd; 368 errno = s_errno; 369 perror("Cannot create control socket"); 370 return -1; 371} 372 373static int do_chflags(const char *dev, __u32 flags, __u32 mask) 374{ 375 struct ifreq ifr; 376 int fd; 377 int err; 378 379 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 380 fd = get_ctl_fd(); 381 if (fd < 0) 382 return -1; 383 err = ioctl(fd, SIOCGIFFLAGS, &ifr); 384 if (err) { 385 perror("SIOCGIFFLAGS"); 386 close(fd); 387 return -1; 388 } 389 if ((ifr.ifr_flags^flags)&mask) { 390 ifr.ifr_flags &= ~mask; 391 ifr.ifr_flags |= mask&flags; 392 err = ioctl(fd, SIOCSIFFLAGS, &ifr); 393 if (err) 394 perror("SIOCSIFFLAGS"); 395 } 396 close(fd); 397 return err; 398} 399 400static int do_changename(const char *dev, const char *newdev) 401{ 402 struct ifreq ifr; 403 int fd; 404 int err; 405 406 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 407 strncpy(ifr.ifr_newname, newdev, IFNAMSIZ); 408 fd = get_ctl_fd(); 409 if (fd < 0) 410 return -1; 411 err = ioctl(fd, SIOCSIFNAME, &ifr); 412 if (err) { 413 perror("SIOCSIFNAME"); 414 close(fd); 415 return -1; 416 } 417 close(fd); 418 return err; 419} 420 421static int set_qlen(const char *dev, int qlen) 422{ 423 struct ifreq ifr; 424 int s; 425 426 s = get_ctl_fd(); 427 if (s < 0) 428 return -1; 429 430 memset(&ifr, 0, sizeof(ifr)); 431 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 432 ifr.ifr_qlen = qlen; 433 if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { 434 perror("SIOCSIFXQLEN"); 435 close(s); 436 return -1; 437 } 438 close(s); 439 440 return 0; 441} 442 443static int set_mtu(const char *dev, int mtu) 444{ 445 struct ifreq ifr; 446 int s; 447 448 s = get_ctl_fd(); 449 if (s < 0) 450 return -1; 451 452 memset(&ifr, 0, sizeof(ifr)); 453 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 454 ifr.ifr_mtu = mtu; 455 if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { 456 perror("SIOCSIFMTU"); 457 close(s); 458 return -1; 459 } 460 close(s); 461 462 return 0; 463} 464 465static int get_address(const char *dev, int *htype) 466{ 467 struct ifreq ifr; 468 struct sockaddr_ll me; 469 socklen_t alen; 470 int s; 471 472 s = socket(PF_PACKET, SOCK_DGRAM, 0); 473 if (s < 0) { 474 perror("socket(PF_PACKET)"); 475 return -1; 476 } 477 478 memset(&ifr, 0, sizeof(ifr)); 479 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 480 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { 481 perror("SIOCGIFINDEX"); 482 close(s); 483 return -1; 484 } 485 486 memset(&me, 0, sizeof(me)); 487 me.sll_family = AF_PACKET; 488 me.sll_ifindex = ifr.ifr_ifindex; 489 me.sll_protocol = htons(ETH_P_LOOP); 490 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { 491 perror("bind"); 492 close(s); 493 return -1; 494 } 495 496 alen = sizeof(me); 497 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { 498 perror("getsockname"); 499 close(s); 500 return -1; 501 } 502 close(s); 503 *htype = me.sll_hatype; 504 return me.sll_halen; 505} 506 507static int parse_address(const char *dev, int hatype, int halen, 508 char *lla, struct ifreq *ifr) 509{ 510 int alen; 511 512 memset(ifr, 0, sizeof(*ifr)); 513 strncpy(ifr->ifr_name, dev, IFNAMSIZ); 514 ifr->ifr_hwaddr.sa_family = hatype; 515 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); 516 if (alen < 0) 517 return -1; 518 if (alen != halen) { 519 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); 520 return -1; 521 } 522 return 0; 523} 524 525static int set_address(struct ifreq *ifr, int brd) 526{ 527 int s; 528 529 s = get_ctl_fd(); 530 if (s < 0) 531 return -1; 532 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { 533 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); 534 close(s); 535 return -1; 536 } 537 close(s); 538 return 0; 539} 540 541 542static int do_set(int argc, char **argv) 543{ 544 char *dev = NULL; 545 __u32 mask = 0; 546 __u32 flags = 0; 547 int qlen = -1; 548 int mtu = -1; 549 char *newaddr = NULL; 550 char *newbrd = NULL; 551 struct ifreq ifr0, ifr1; 552 char *newname = NULL; 553 int htype, halen; 554 555 while (argc > 0) { 556 if (strcmp(*argv, "up") == 0) { 557 mask |= IFF_UP; 558 flags |= IFF_UP; 559 } else if (strcmp(*argv, "down") == 0) { 560 mask |= IFF_UP; 561 flags &= ~IFF_UP; 562 } else if (strcmp(*argv, "name") == 0) { 563 NEXT_ARG(); 564 newname = *argv; 565 } else if (matches(*argv, "address") == 0) { 566 NEXT_ARG(); 567 newaddr = *argv; 568 } else if (matches(*argv, "broadcast") == 0 || 569 strcmp(*argv, "brd") == 0) { 570 NEXT_ARG(); 571 newbrd = *argv; 572 } else if (matches(*argv, "txqueuelen") == 0 || 573 strcmp(*argv, "qlen") == 0 || 574 matches(*argv, "txqlen") == 0) { 575 NEXT_ARG(); 576 if (qlen != -1) 577 duparg("txqueuelen", *argv); 578 if (get_integer(&qlen, *argv, 0)) 579 invarg("Invalid \"txqueuelen\" value\n", *argv); 580 } else if (strcmp(*argv, "mtu") == 0) { 581 NEXT_ARG(); 582 if (mtu != -1) 583 duparg("mtu", *argv); 584 if (get_integer(&mtu, *argv, 0)) 585 invarg("Invalid \"mtu\" value\n", *argv); 586 } else if (strcmp(*argv, "multicast") == 0) { 587 NEXT_ARG(); 588 mask |= IFF_MULTICAST; 589 if (strcmp(*argv, "on") == 0) { 590 flags |= IFF_MULTICAST; 591 } else if (strcmp(*argv, "off") == 0) { 592 flags &= ~IFF_MULTICAST; 593 } else 594 return on_off("multicast"); 595 } else if (strcmp(*argv, "allmulticast") == 0) { 596 NEXT_ARG(); 597 mask |= IFF_ALLMULTI; 598 if (strcmp(*argv, "on") == 0) { 599 flags |= IFF_ALLMULTI; 600 } else if (strcmp(*argv, "off") == 0) { 601 flags &= ~IFF_ALLMULTI; 602 } else 603 return on_off("allmulticast"); 604 } else if (strcmp(*argv, "promisc") == 0) { 605 NEXT_ARG(); 606 mask |= IFF_PROMISC; 607 if (strcmp(*argv, "on") == 0) { 608 flags |= IFF_PROMISC; 609 } else if (strcmp(*argv, "off") == 0) { 610 flags &= ~IFF_PROMISC; 611 } else 612 return on_off("promisc"); 613 } else if (strcmp(*argv, "trailers") == 0) { 614 NEXT_ARG(); 615 mask |= IFF_NOTRAILERS; 616 if (strcmp(*argv, "off") == 0) { 617 flags |= IFF_NOTRAILERS; 618 } else if (strcmp(*argv, "on") == 0) { 619 flags &= ~IFF_NOTRAILERS; 620 } else 621 return on_off("trailers"); 622 } else if (strcmp(*argv, "arp") == 0) { 623 NEXT_ARG(); 624 mask |= IFF_NOARP; 625 if (strcmp(*argv, "on") == 0) { 626 flags &= ~IFF_NOARP; 627 } else if (strcmp(*argv, "off") == 0) { 628 flags |= IFF_NOARP; 629 } else 630 return on_off("noarp"); 631#ifdef IFF_DYNAMIC 632 } else if (matches(*argv, "dynamic") == 0) { 633 NEXT_ARG(); 634 mask |= IFF_DYNAMIC; 635 if (strcmp(*argv, "on") == 0) { 636 flags |= IFF_DYNAMIC; 637 } else if (strcmp(*argv, "off") == 0) { 638 flags &= ~IFF_DYNAMIC; 639 } else 640 return on_off("dynamic"); 641#endif 642 } else { 643 if (strcmp(*argv, "dev") == 0) { 644 NEXT_ARG(); 645 } 646 if (matches(*argv, "help") == 0) 647 usage(); 648 if (dev) 649 duparg2("dev", *argv); 650 dev = *argv; 651 } 652 argc--; argv++; 653 } 654 655 if (!dev) { 656 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); 657 exit(-1); 658 } 659 660 if (newaddr || newbrd) { 661 halen = get_address(dev, &htype); 662 if (halen < 0) 663 return -1; 664 if (newaddr) { 665 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) 666 return -1; 667 } 668 if (newbrd) { 669 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) 670 return -1; 671 } 672 } 673 674 if (newname && strcmp(dev, newname)) { 675 if (strlen(newname) == 0) 676 invarg("\"\" is not a valid device identifier\n", "name"); 677 if (do_changename(dev, newname) < 0) 678 return -1; 679 dev = newname; 680 } 681 if (qlen != -1) { 682 if (set_qlen(dev, qlen) < 0) 683 return -1; 684 } 685 if (mtu != -1) { 686 if (set_mtu(dev, mtu) < 0) 687 return -1; 688 } 689 if (newaddr || newbrd) { 690 if (newbrd) { 691 if (set_address(&ifr1, 1) < 0) 692 return -1; 693 } 694 if (newaddr) { 695 if (set_address(&ifr0, 0) < 0) 696 return -1; 697 } 698 } 699 if (mask) 700 return do_chflags(dev, flags, mask); 701 return 0; 702} 703#endif /* IPLINK_IOCTL_COMPAT */ 704 705int do_iplink(int argc, char **argv) 706{ 707 if (argc > 0) { 708 if (iplink_have_newlink()) { 709 if (matches(*argv, "add") == 0) 710 return iplink_modify(RTM_NEWLINK, 711 NLM_F_CREATE|NLM_F_EXCL, 712 argc-1, argv+1); 713 if (matches(*argv, "set") == 0 || 714 matches(*argv, "change") == 0) 715 return iplink_modify(RTM_NEWLINK, 0, 716 argc-1, argv+1); 717 if (matches(*argv, "replace") == 0) 718 return iplink_modify(RTM_NEWLINK, 719 NLM_F_CREATE|NLM_F_REPLACE, 720 argc-1, argv+1); 721 if (matches(*argv, "delete") == 0) 722 return iplink_modify(RTM_DELLINK, 0, 723 argc-1, argv+1); 724 } else { 725#if IPLINK_IOCTL_COMPAT 726 if (matches(*argv, "set") == 0) 727 return do_set(argc-1, argv+1); 728#endif 729 } 730 if (matches(*argv, "show") == 0 || 731 matches(*argv, "lst") == 0 || 732 matches(*argv, "list") == 0) 733 return ipaddr_list_link(argc-1, argv+1); 734 if (matches(*argv, "help") == 0) 735 usage(); 736 } else 737 return ipaddr_list_link(0, NULL); 738 739 fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); 740 exit(-1); 741} 742