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, char *lla, struct ifreq *ifr) 220{ 221 int alen; 222 223 memset(ifr, 0, sizeof(*ifr)); 224 strncpy(ifr->ifr_name, dev, IFNAMSIZ); 225 ifr->ifr_hwaddr.sa_family = hatype; 226 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); 227 if (alen < 0) 228 return -1; 229 if (alen != halen) { 230 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); 231 return -1; 232 } 233 return 0; 234} 235 236static int set_address(struct ifreq *ifr, int brd) 237{ 238 int s; 239 240 s = get_ctl_fd(); 241 if (s < 0) 242 return -1; 243 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { 244 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); 245 close(s); 246 return -1; 247 } 248 close(s); 249 return 0; 250} 251 252 253static int do_set(int argc, char **argv) 254{ 255 char *dev = NULL; 256 __u32 mask = 0; 257 __u32 flags = 0; 258 int qlen = -1; 259 int mtu = -1; 260 char *newaddr = NULL; 261 char *newbrd = NULL; 262 struct ifreq ifr0, ifr1; 263 char *newname = NULL; 264 int htype, halen; 265 266 while (argc > 0) { 267 if (strcmp(*argv, "up") == 0) { 268 mask |= IFF_UP; 269 flags |= IFF_UP; 270 } else if (strcmp(*argv, "down") == 0) { 271 mask |= IFF_UP; 272 flags &= ~IFF_UP; 273 } else if (strcmp(*argv, "name") == 0) { 274 NEXT_ARG(); 275 newname = *argv; 276 } else if (matches(*argv, "address") == 0) { 277 NEXT_ARG(); 278 newaddr = *argv; 279 } else if (matches(*argv, "broadcast") == 0 || 280 strcmp(*argv, "brd") == 0) { 281 NEXT_ARG(); 282 newbrd = *argv; 283 } else if (matches(*argv, "txqueuelen") == 0 || 284 strcmp(*argv, "qlen") == 0 || 285 matches(*argv, "txqlen") == 0) { 286 NEXT_ARG(); 287 if (qlen != -1) 288 duparg("txqueuelen", *argv); 289 if (get_integer(&qlen, *argv, 0)) 290 invarg("Invalid \"txqueuelen\" value\n", *argv); 291 } else if (strcmp(*argv, "mtu") == 0) { 292 NEXT_ARG(); 293 if (mtu != -1) 294 duparg("mtu", *argv); 295 if (get_integer(&mtu, *argv, 0)) 296 invarg("Invalid \"mtu\" value\n", *argv); 297 } else if (strcmp(*argv, "multicast") == 0) { 298 NEXT_ARG(); 299 mask |= IFF_MULTICAST; 300 if (strcmp(*argv, "on") == 0) { 301 flags |= IFF_MULTICAST; 302 } else if (strcmp(*argv, "off") == 0) { 303 flags &= ~IFF_MULTICAST; 304 } else 305 return on_off("multicast"); 306 } else if (strcmp(*argv, "allmulticast") == 0) { 307 NEXT_ARG(); 308 mask |= IFF_ALLMULTI; 309 if (strcmp(*argv, "on") == 0) { 310 flags |= IFF_ALLMULTI; 311 } else if (strcmp(*argv, "off") == 0) { 312 flags &= ~IFF_ALLMULTI; 313 } else 314 return on_off("allmulticast"); 315 } else if (strcmp(*argv, "promisc") == 0) { 316 NEXT_ARG(); 317 mask |= IFF_PROMISC; 318 if (strcmp(*argv, "on") == 0) { 319 flags |= IFF_PROMISC; 320 } else if (strcmp(*argv, "off") == 0) { 321 flags &= ~IFF_PROMISC; 322 } else 323 return on_off("promisc"); 324 } else if (strcmp(*argv, "trailers") == 0) { 325 NEXT_ARG(); 326 mask |= IFF_NOTRAILERS; 327 if (strcmp(*argv, "off") == 0) { 328 flags |= IFF_NOTRAILERS; 329 } else if (strcmp(*argv, "on") == 0) { 330 flags &= ~IFF_NOTRAILERS; 331 } else 332 return on_off("trailers"); 333 } else if (strcmp(*argv, "arp") == 0) { 334 NEXT_ARG(); 335 mask |= IFF_NOARP; 336 if (strcmp(*argv, "on") == 0) { 337 flags &= ~IFF_NOARP; 338 } else if (strcmp(*argv, "off") == 0) { 339 flags |= IFF_NOARP; 340 } else 341 return on_off("noarp"); 342#ifdef IFF_DYNAMIC 343 } else if (matches(*argv, "dynamic") == 0) { 344 NEXT_ARG(); 345 mask |= IFF_DYNAMIC; 346 if (strcmp(*argv, "on") == 0) { 347 flags |= IFF_DYNAMIC; 348 } else if (strcmp(*argv, "off") == 0) { 349 flags &= ~IFF_DYNAMIC; 350 } else 351 return on_off("dynamic"); 352#endif 353 } else { 354 if (strcmp(*argv, "dev") == 0) { 355 NEXT_ARG(); 356 } 357 if (matches(*argv, "help") == 0) 358 usage(); 359 if (dev) 360 duparg2("dev", *argv); 361 dev = *argv; 362 } 363 argc--; argv++; 364 } 365 366 if (!dev) { 367 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); 368 exit(-1); 369 } 370 371 if (newaddr || newbrd) { 372 halen = get_address(dev, &htype); 373 if (halen < 0) 374 return -1; 375 if (newaddr) { 376 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) 377 return -1; 378 } 379 if (newbrd) { 380 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) 381 return -1; 382 } 383 } 384 385 if (newname && strcmp(dev, newname)) { 386 if (do_changename(dev, newname) < 0) 387 return -1; 388 dev = newname; 389 } 390 if (qlen != -1) { 391 if (set_qlen(dev, qlen) < 0) 392 return -1; 393 } 394 if (mtu != -1) { 395 if (set_mtu(dev, mtu) < 0) 396 return -1; 397 } 398 if (newaddr || newbrd) { 399 if (newbrd) { 400 if (set_address(&ifr1, 1) < 0) 401 return -1; 402 } 403 if (newaddr) { 404 if (set_address(&ifr0, 0) < 0) 405 return -1; 406 } 407 } 408 if (mask) 409 return do_chflags(dev, flags, mask); 410 return 0; 411} 412 413int do_iplink(int argc, char **argv) 414{ 415 if (argc > 0) { 416 if (matches(*argv, "set") == 0) 417 return do_set(argc-1, argv+1); 418 if (matches(*argv, "show") == 0 || 419 matches(*argv, "lst") == 0 || 420 matches(*argv, "list") == 0) 421 return ipaddr_list_link(argc-1, argv+1); 422 if (matches(*argv, "help") == 0) 423 usage(); 424 } else 425 return ipaddr_list_link(0, NULL); 426 427 fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); 428 exit(-1); 429} 430