1 /* 2 * QEMU Bridge Helper 3 * 4 * Copyright IBM, Corp. 2011 5 * 6 * Authors: 7 * Anthony Liguori <aliguori@us.ibm.com> 8 * Richa Marwaha <rmarwah@linux.vnet.ibm.com> 9 * Corey Bryant <coreyb@linux.vnet.ibm.com> 10 * 11 * This work is licensed under the terms of the GNU GPL, version 2. See 12 * the COPYING file in the top-level directory. 13 */ 14 15 /* 16 * Known shortcomings: 17 * - There is no manual page 18 * - The syntax of the ACL file is not documented anywhere 19 * - parse_acl_file() doesn't report fopen() failure properly, fails 20 * to check ferror() after fgets() failure, arbitrarily truncates 21 * long lines, handles whitespace inconsistently, error messages 22 * don't point to the offending file and line, errors in included 23 * files are reported, but otherwise ignored, ... 24 */ 25 26 #include "qemu/osdep.h" 27 28 29 #include <sys/ioctl.h> 30 #include <sys/socket.h> 31 #include <sys/un.h> 32 #include <sys/prctl.h> 33 34 #include <net/if.h> 35 36 #include <linux/sockios.h> 37 38 #ifndef SIOCBRADDIF 39 #include <linux/if_bridge.h> 40 #endif 41 42 #include "qemu/queue.h" 43 44 #include "net/tap-linux.h" 45 46 #ifdef CONFIG_LIBCAP_NG 47 #include <cap-ng.h> 48 #endif 49 50 #define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR "/bridge.conf" 51 52 enum { 53 ACL_ALLOW = 0, 54 ACL_ALLOW_ALL, 55 ACL_DENY, 56 ACL_DENY_ALL, 57 }; 58 59 typedef struct ACLRule { 60 int type; 61 char iface[IFNAMSIZ]; 62 QSIMPLEQ_ENTRY(ACLRule) entry; 63 } ACLRule; 64 65 typedef QSIMPLEQ_HEAD(ACLList, ACLRule) ACLList; 66 67 static void usage(void) 68 { 69 fprintf(stderr, 70 "Usage: qemu-bridge-helper [--use-vnet] --br=bridge --fd=unixfd\n"); 71 } 72 73 static int parse_acl_file(const char *filename, ACLList *acl_list) 74 { 75 FILE *f; 76 char line[4096]; 77 ACLRule *acl_rule; 78 79 f = fopen(filename, "r"); 80 if (f == NULL) { 81 return -1; 82 } 83 84 while (fgets(line, sizeof(line), f) != NULL) { 85 char *ptr = line; 86 char *cmd, *arg, *argend; 87 88 while (g_ascii_isspace(*ptr)) { 89 ptr++; 90 } 91 92 /* skip comments and empty lines */ 93 if (*ptr == '#' || *ptr == 0) { 94 continue; 95 } 96 97 cmd = ptr; 98 arg = strchr(cmd, ' '); 99 if (arg == NULL) { 100 arg = strchr(cmd, '\t'); 101 } 102 103 if (arg == NULL) { 104 fprintf(stderr, "Invalid config line:\n %s\n", line); 105 goto err; 106 } 107 108 *arg = 0; 109 arg++; 110 while (g_ascii_isspace(*arg)) { 111 arg++; 112 } 113 114 argend = arg + strlen(arg); 115 while (arg != argend && g_ascii_isspace(*(argend - 1))) { 116 argend--; 117 } 118 *argend = 0; 119 120 if (!g_str_equal(cmd, "include") && strlen(arg) >= IFNAMSIZ) { 121 fprintf(stderr, "name `%s' too long: %zu\n", arg, strlen(arg)); 122 goto err; 123 } 124 125 if (strcmp(cmd, "deny") == 0) { 126 acl_rule = g_malloc(sizeof(*acl_rule)); 127 if (strcmp(arg, "all") == 0) { 128 acl_rule->type = ACL_DENY_ALL; 129 } else { 130 acl_rule->type = ACL_DENY; 131 snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg); 132 } 133 QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry); 134 } else if (strcmp(cmd, "allow") == 0) { 135 acl_rule = g_malloc(sizeof(*acl_rule)); 136 if (strcmp(arg, "all") == 0) { 137 acl_rule->type = ACL_ALLOW_ALL; 138 } else { 139 acl_rule->type = ACL_ALLOW; 140 snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg); 141 } 142 QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry); 143 } else if (strcmp(cmd, "include") == 0) { 144 /* ignore errors */ 145 parse_acl_file(arg, acl_list); 146 } else { 147 fprintf(stderr, "Unknown command `%s'\n", cmd); 148 goto err; 149 } 150 } 151 152 fclose(f); 153 return 0; 154 155 err: 156 fclose(f); 157 errno = EINVAL; 158 return -1; 159 160 } 161 162 static bool has_vnet_hdr(int fd) 163 { 164 unsigned int features = 0; 165 166 if (ioctl(fd, TUNGETFEATURES, &features) == -1) { 167 return false; 168 } 169 170 if (!(features & IFF_VNET_HDR)) { 171 return false; 172 } 173 174 return true; 175 } 176 177 static void prep_ifreq(struct ifreq *ifr, const char *ifname) 178 { 179 memset(ifr, 0, sizeof(*ifr)); 180 snprintf(ifr->ifr_name, IFNAMSIZ, "%s", ifname); 181 } 182 183 static int send_fd(int c, int fd) 184 { 185 char msgbuf[CMSG_SPACE(sizeof(fd))]; 186 struct msghdr msg = { 187 .msg_control = msgbuf, 188 .msg_controllen = sizeof(msgbuf), 189 }; 190 struct cmsghdr *cmsg; 191 struct iovec iov; 192 char req[1] = { 0x00 }; 193 194 cmsg = CMSG_FIRSTHDR(&msg); 195 cmsg->cmsg_level = SOL_SOCKET; 196 cmsg->cmsg_type = SCM_RIGHTS; 197 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 198 msg.msg_controllen = cmsg->cmsg_len; 199 200 iov.iov_base = req; 201 iov.iov_len = sizeof(req); 202 203 msg.msg_iov = &iov; 204 msg.msg_iovlen = 1; 205 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); 206 207 return sendmsg(c, &msg, 0); 208 } 209 210 #ifdef CONFIG_LIBCAP_NG 211 static int drop_privileges(void) 212 { 213 /* clear all capabilities */ 214 capng_clear(CAPNG_SELECT_BOTH); 215 216 if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, 217 CAP_NET_ADMIN) < 0) { 218 return -1; 219 } 220 221 /* change to calling user's real uid and gid, retaining supplemental 222 * groups and CAP_NET_ADMIN */ 223 if (capng_change_id(getuid(), getgid(), CAPNG_CLEAR_BOUNDING)) { 224 return -1; 225 } 226 227 return 0; 228 } 229 #endif 230 231 int main(int argc, char **argv) 232 { 233 struct ifreq ifr; 234 #ifndef SIOCBRADDIF 235 unsigned long ifargs[4]; 236 #endif 237 int ifindex; 238 int fd = -1, ctlfd = -1, unixfd = -1; 239 int use_vnet = 0; 240 int mtu; 241 const char *bridge = NULL; 242 char iface[IFNAMSIZ]; 243 int index; 244 ACLRule *acl_rule; 245 ACLList acl_list; 246 int access_allowed, access_denied; 247 int ret = EXIT_SUCCESS; 248 249 #ifdef CONFIG_LIBCAP_NG 250 /* if we're run from an suid binary, immediately drop privileges preserving 251 * cap_net_admin */ 252 if (geteuid() == 0 && getuid() != geteuid()) { 253 if (drop_privileges() == -1) { 254 fprintf(stderr, "failed to drop privileges\n"); 255 return 1; 256 } 257 } 258 #endif 259 260 /* parse arguments */ 261 for (index = 1; index < argc; index++) { 262 if (strcmp(argv[index], "--use-vnet") == 0) { 263 use_vnet = 1; 264 } else if (strncmp(argv[index], "--br=", 5) == 0) { 265 bridge = &argv[index][5]; 266 } else if (strncmp(argv[index], "--fd=", 5) == 0) { 267 unixfd = atoi(&argv[index][5]); 268 } else { 269 usage(); 270 return EXIT_FAILURE; 271 } 272 } 273 274 if (bridge == NULL || unixfd == -1) { 275 usage(); 276 return EXIT_FAILURE; 277 } 278 if (strlen(bridge) >= IFNAMSIZ) { 279 fprintf(stderr, "name `%s' too long: %zu\n", bridge, strlen(bridge)); 280 return EXIT_FAILURE; 281 } 282 283 /* parse default acl file */ 284 QSIMPLEQ_INIT(&acl_list); 285 if (parse_acl_file(DEFAULT_ACL_FILE, &acl_list) == -1) { 286 fprintf(stderr, "failed to parse default acl file `%s'\n", 287 DEFAULT_ACL_FILE); 288 ret = EXIT_FAILURE; 289 goto cleanup; 290 } 291 292 /* validate bridge against acl -- default policy is to deny 293 * according acl policy if we have a deny and allow both 294 * then deny should always win over allow 295 */ 296 access_allowed = 0; 297 access_denied = 0; 298 QSIMPLEQ_FOREACH(acl_rule, &acl_list, entry) { 299 switch (acl_rule->type) { 300 case ACL_ALLOW_ALL: 301 access_allowed = 1; 302 break; 303 case ACL_ALLOW: 304 if (strcmp(bridge, acl_rule->iface) == 0) { 305 access_allowed = 1; 306 } 307 break; 308 case ACL_DENY_ALL: 309 access_denied = 1; 310 break; 311 case ACL_DENY: 312 if (strcmp(bridge, acl_rule->iface) == 0) { 313 access_denied = 1; 314 } 315 break; 316 } 317 } 318 319 if ((access_allowed == 0) || (access_denied == 1)) { 320 fprintf(stderr, "access denied by acl file\n"); 321 ret = EXIT_FAILURE; 322 goto cleanup; 323 } 324 325 /* open a socket to use to control the network interfaces */ 326 ctlfd = socket(AF_INET, SOCK_STREAM, 0); 327 if (ctlfd == -1) { 328 fprintf(stderr, "failed to open control socket: %s\n", strerror(errno)); 329 ret = EXIT_FAILURE; 330 goto cleanup; 331 } 332 333 /* open the tap device */ 334 fd = open("/dev/net/tun", O_RDWR); 335 if (fd == -1) { 336 fprintf(stderr, "failed to open /dev/net/tun: %s\n", strerror(errno)); 337 ret = EXIT_FAILURE; 338 goto cleanup; 339 } 340 341 /* request a tap device, disable PI, and add vnet header support if 342 * requested and it's available. */ 343 prep_ifreq(&ifr, "tap%d"); 344 ifr.ifr_flags = IFF_TAP|IFF_NO_PI; 345 if (use_vnet && has_vnet_hdr(fd)) { 346 ifr.ifr_flags |= IFF_VNET_HDR; 347 } 348 349 if (ioctl(fd, TUNSETIFF, &ifr) == -1) { 350 fprintf(stderr, "failed to create tun device: %s\n", strerror(errno)); 351 ret = EXIT_FAILURE; 352 goto cleanup; 353 } 354 355 /* save tap device name */ 356 snprintf(iface, sizeof(iface), "%s", ifr.ifr_name); 357 358 /* get the mtu of the bridge */ 359 prep_ifreq(&ifr, bridge); 360 if (ioctl(ctlfd, SIOCGIFMTU, &ifr) == -1) { 361 fprintf(stderr, "failed to get mtu of bridge `%s': %s\n", 362 bridge, strerror(errno)); 363 ret = EXIT_FAILURE; 364 goto cleanup; 365 } 366 367 /* save mtu */ 368 mtu = ifr.ifr_mtu; 369 370 /* set the mtu of the interface based on the bridge */ 371 prep_ifreq(&ifr, iface); 372 ifr.ifr_mtu = mtu; 373 if (ioctl(ctlfd, SIOCSIFMTU, &ifr) == -1) { 374 fprintf(stderr, "failed to set mtu of device `%s' to %d: %s\n", 375 iface, mtu, strerror(errno)); 376 ret = EXIT_FAILURE; 377 goto cleanup; 378 } 379 380 /* Linux uses the lowest enslaved MAC address as the MAC address of 381 * the bridge. Set MAC address to a high value so that it doesn't 382 * affect the MAC address of the bridge. 383 */ 384 if (ioctl(ctlfd, SIOCGIFHWADDR, &ifr) < 0) { 385 fprintf(stderr, "failed to get MAC address of device `%s': %s\n", 386 iface, strerror(errno)); 387 ret = EXIT_FAILURE; 388 goto cleanup; 389 } 390 ifr.ifr_hwaddr.sa_data[0] = 0xFE; 391 if (ioctl(ctlfd, SIOCSIFHWADDR, &ifr) < 0) { 392 fprintf(stderr, "failed to set MAC address of device `%s': %s\n", 393 iface, strerror(errno)); 394 ret = EXIT_FAILURE; 395 goto cleanup; 396 } 397 398 /* add the interface to the bridge */ 399 prep_ifreq(&ifr, bridge); 400 ifindex = if_nametoindex(iface); 401 #ifndef SIOCBRADDIF 402 ifargs[0] = BRCTL_ADD_IF; 403 ifargs[1] = ifindex; 404 ifargs[2] = 0; 405 ifargs[3] = 0; 406 ifr.ifr_data = (void *)ifargs; 407 ret = ioctl(ctlfd, SIOCDEVPRIVATE, &ifr); 408 #else 409 ifr.ifr_ifindex = ifindex; 410 ret = ioctl(ctlfd, SIOCBRADDIF, &ifr); 411 #endif 412 if (ret == -1) { 413 fprintf(stderr, "failed to add interface `%s' to bridge `%s': %s\n", 414 iface, bridge, strerror(errno)); 415 ret = EXIT_FAILURE; 416 goto cleanup; 417 } 418 419 /* bring the interface up */ 420 prep_ifreq(&ifr, iface); 421 if (ioctl(ctlfd, SIOCGIFFLAGS, &ifr) == -1) { 422 fprintf(stderr, "failed to get interface flags for `%s': %s\n", 423 iface, strerror(errno)); 424 ret = EXIT_FAILURE; 425 goto cleanup; 426 } 427 428 ifr.ifr_flags |= IFF_UP; 429 if (ioctl(ctlfd, SIOCSIFFLAGS, &ifr) == -1) { 430 fprintf(stderr, "failed to bring up interface `%s': %s\n", 431 iface, strerror(errno)); 432 ret = EXIT_FAILURE; 433 goto cleanup; 434 } 435 436 /* write fd to the domain socket */ 437 if (send_fd(unixfd, fd) == -1) { 438 fprintf(stderr, "failed to write fd to unix socket: %s\n", 439 strerror(errno)); 440 ret = EXIT_FAILURE; 441 goto cleanup; 442 } 443 444 /* ... */ 445 446 /* profit! */ 447 448 cleanup: 449 if (fd >= 0) { 450 close(fd); 451 } 452 if (ctlfd >= 0) { 453 close(ctlfd); 454 } 455 while ((acl_rule = QSIMPLEQ_FIRST(&acl_list)) != NULL) { 456 QSIMPLEQ_REMOVE_HEAD(&acl_list, entry); 457 g_free(acl_rule); 458 } 459 460 return ret; 461 } 462