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 #include "config-host.h" 17 18 #include <stdio.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <unistd.h> 22 #include <string.h> 23 #include <stdlib.h> 24 #include <stdbool.h> 25 #include <ctype.h> 26 #include <glib.h> 27 28 #include <sys/types.h> 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 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 (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 fclose(f); 106 errno = EINVAL; 107 return -1; 108 } 109 110 *arg = 0; 111 arg++; 112 while (isspace(*arg)) { 113 arg++; 114 } 115 116 argend = arg + strlen(arg); 117 while (arg != argend && isspace(*(argend - 1))) { 118 argend--; 119 } 120 *argend = 0; 121 122 if (strcmp(cmd, "deny") == 0) { 123 acl_rule = g_malloc(sizeof(*acl_rule)); 124 if (strcmp(arg, "all") == 0) { 125 acl_rule->type = ACL_DENY_ALL; 126 } else { 127 acl_rule->type = ACL_DENY; 128 snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg); 129 } 130 QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry); 131 } else if (strcmp(cmd, "allow") == 0) { 132 acl_rule = g_malloc(sizeof(*acl_rule)); 133 if (strcmp(arg, "all") == 0) { 134 acl_rule->type = ACL_ALLOW_ALL; 135 } else { 136 acl_rule->type = ACL_ALLOW; 137 snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg); 138 } 139 QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry); 140 } else if (strcmp(cmd, "include") == 0) { 141 /* ignore errors */ 142 parse_acl_file(arg, acl_list); 143 } else { 144 fprintf(stderr, "Unknown command `%s'\n", cmd); 145 fclose(f); 146 errno = EINVAL; 147 return -1; 148 } 149 } 150 151 fclose(f); 152 153 return 0; 154 } 155 156 static bool has_vnet_hdr(int fd) 157 { 158 unsigned int features = 0; 159 160 if (ioctl(fd, TUNGETFEATURES, &features) == -1) { 161 return false; 162 } 163 164 if (!(features & IFF_VNET_HDR)) { 165 return false; 166 } 167 168 return true; 169 } 170 171 static void prep_ifreq(struct ifreq *ifr, const char *ifname) 172 { 173 memset(ifr, 0, sizeof(*ifr)); 174 snprintf(ifr->ifr_name, IFNAMSIZ, "%s", ifname); 175 } 176 177 static int send_fd(int c, int fd) 178 { 179 char msgbuf[CMSG_SPACE(sizeof(fd))]; 180 struct msghdr msg = { 181 .msg_control = msgbuf, 182 .msg_controllen = sizeof(msgbuf), 183 }; 184 struct cmsghdr *cmsg; 185 struct iovec iov; 186 char req[1] = { 0x00 }; 187 188 cmsg = CMSG_FIRSTHDR(&msg); 189 cmsg->cmsg_level = SOL_SOCKET; 190 cmsg->cmsg_type = SCM_RIGHTS; 191 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 192 msg.msg_controllen = cmsg->cmsg_len; 193 194 iov.iov_base = req; 195 iov.iov_len = sizeof(req); 196 197 msg.msg_iov = &iov; 198 msg.msg_iovlen = 1; 199 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); 200 201 return sendmsg(c, &msg, 0); 202 } 203 204 #ifdef CONFIG_LIBCAP 205 static int drop_privileges(void) 206 { 207 /* clear all capabilities */ 208 capng_clear(CAPNG_SELECT_BOTH); 209 210 if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, 211 CAP_NET_ADMIN) < 0) { 212 return -1; 213 } 214 215 /* change to calling user's real uid and gid, retaining supplemental 216 * groups and CAP_NET_ADMIN */ 217 if (capng_change_id(getuid(), getgid(), CAPNG_CLEAR_BOUNDING)) { 218 return -1; 219 } 220 221 return 0; 222 } 223 #endif 224 225 int main(int argc, char **argv) 226 { 227 struct ifreq ifr; 228 #ifndef SIOCBRADDIF 229 unsigned long ifargs[4]; 230 #endif 231 int ifindex; 232 int fd, ctlfd, unixfd = -1; 233 int use_vnet = 0; 234 int mtu; 235 const char *bridge = NULL; 236 char iface[IFNAMSIZ]; 237 int index; 238 ACLRule *acl_rule; 239 ACLList acl_list; 240 int access_allowed, access_denied; 241 int ret = EXIT_SUCCESS; 242 243 #ifdef CONFIG_LIBCAP 244 /* if we're run from an suid binary, immediately drop privileges preserving 245 * cap_net_admin */ 246 if (geteuid() == 0 && getuid() != geteuid()) { 247 if (drop_privileges() == -1) { 248 fprintf(stderr, "failed to drop privileges\n"); 249 return 1; 250 } 251 } 252 #endif 253 254 /* parse arguments */ 255 for (index = 1; index < argc; index++) { 256 if (strcmp(argv[index], "--use-vnet") == 0) { 257 use_vnet = 1; 258 } else if (strncmp(argv[index], "--br=", 5) == 0) { 259 bridge = &argv[index][5]; 260 } else if (strncmp(argv[index], "--fd=", 5) == 0) { 261 unixfd = atoi(&argv[index][5]); 262 } else { 263 usage(); 264 return EXIT_FAILURE; 265 } 266 } 267 268 if (bridge == NULL || unixfd == -1) { 269 usage(); 270 return EXIT_FAILURE; 271 } 272 273 /* parse default acl file */ 274 QSIMPLEQ_INIT(&acl_list); 275 if (parse_acl_file(DEFAULT_ACL_FILE, &acl_list) == -1) { 276 fprintf(stderr, "failed to parse default acl file `%s'\n", 277 DEFAULT_ACL_FILE); 278 ret = EXIT_FAILURE; 279 goto cleanup; 280 } 281 282 /* validate bridge against acl -- default policy is to deny 283 * according acl policy if we have a deny and allow both 284 * then deny should always win over allow 285 */ 286 access_allowed = 0; 287 access_denied = 0; 288 QSIMPLEQ_FOREACH(acl_rule, &acl_list, entry) { 289 switch (acl_rule->type) { 290 case ACL_ALLOW_ALL: 291 access_allowed = 1; 292 break; 293 case ACL_ALLOW: 294 if (strcmp(bridge, acl_rule->iface) == 0) { 295 access_allowed = 1; 296 } 297 break; 298 case ACL_DENY_ALL: 299 access_denied = 1; 300 break; 301 case ACL_DENY: 302 if (strcmp(bridge, acl_rule->iface) == 0) { 303 access_denied = 1; 304 } 305 break; 306 } 307 } 308 309 if ((access_allowed == 0) || (access_denied == 1)) { 310 fprintf(stderr, "access denied by acl file\n"); 311 ret = EXIT_FAILURE; 312 goto cleanup; 313 } 314 315 /* open a socket to use to control the network interfaces */ 316 ctlfd = socket(AF_INET, SOCK_STREAM, 0); 317 if (ctlfd == -1) { 318 fprintf(stderr, "failed to open control socket: %s\n", strerror(errno)); 319 ret = EXIT_FAILURE; 320 goto cleanup; 321 } 322 323 /* open the tap device */ 324 fd = open("/dev/net/tun", O_RDWR); 325 if (fd == -1) { 326 fprintf(stderr, "failed to open /dev/net/tun: %s\n", strerror(errno)); 327 ret = EXIT_FAILURE; 328 goto cleanup; 329 } 330 331 /* request a tap device, disable PI, and add vnet header support if 332 * requested and it's available. */ 333 prep_ifreq(&ifr, "tap%d"); 334 ifr.ifr_flags = IFF_TAP|IFF_NO_PI; 335 if (use_vnet && has_vnet_hdr(fd)) { 336 ifr.ifr_flags |= IFF_VNET_HDR; 337 } 338 339 if (ioctl(fd, TUNSETIFF, &ifr) == -1) { 340 fprintf(stderr, "failed to create tun device: %s\n", strerror(errno)); 341 ret = EXIT_FAILURE; 342 goto cleanup; 343 } 344 345 /* save tap device name */ 346 snprintf(iface, sizeof(iface), "%s", ifr.ifr_name); 347 348 /* get the mtu of the bridge */ 349 prep_ifreq(&ifr, bridge); 350 if (ioctl(ctlfd, SIOCGIFMTU, &ifr) == -1) { 351 fprintf(stderr, "failed to get mtu of bridge `%s': %s\n", 352 bridge, strerror(errno)); 353 ret = EXIT_FAILURE; 354 goto cleanup; 355 } 356 357 /* save mtu */ 358 mtu = ifr.ifr_mtu; 359 360 /* set the mtu of the interface based on the bridge */ 361 prep_ifreq(&ifr, iface); 362 ifr.ifr_mtu = mtu; 363 if (ioctl(ctlfd, SIOCSIFMTU, &ifr) == -1) { 364 fprintf(stderr, "failed to set mtu of device `%s' to %d: %s\n", 365 iface, mtu, strerror(errno)); 366 ret = EXIT_FAILURE; 367 goto cleanup; 368 } 369 370 /* Linux uses the lowest enslaved MAC address as the MAC address of 371 * the bridge. Set MAC address to a high value so that it doesn't 372 * affect the MAC address of the bridge. 373 */ 374 if (ioctl(ctlfd, SIOCGIFHWADDR, &ifr) < 0) { 375 fprintf(stderr, "failed to get MAC address of device `%s': %s\n", 376 iface, strerror(errno)); 377 ret = EXIT_FAILURE; 378 goto cleanup; 379 } 380 ifr.ifr_hwaddr.sa_data[0] = 0xFE; 381 if (ioctl(ctlfd, SIOCSIFHWADDR, &ifr) < 0) { 382 fprintf(stderr, "failed to set MAC address of device `%s': %s\n", 383 iface, strerror(errno)); 384 ret = EXIT_FAILURE; 385 goto cleanup; 386 } 387 388 /* add the interface to the bridge */ 389 prep_ifreq(&ifr, bridge); 390 ifindex = if_nametoindex(iface); 391 #ifndef SIOCBRADDIF 392 ifargs[0] = BRCTL_ADD_IF; 393 ifargs[1] = ifindex; 394 ifargs[2] = 0; 395 ifargs[3] = 0; 396 ifr.ifr_data = (void *)ifargs; 397 ret = ioctl(ctlfd, SIOCDEVPRIVATE, &ifr); 398 #else 399 ifr.ifr_ifindex = ifindex; 400 ret = ioctl(ctlfd, SIOCBRADDIF, &ifr); 401 #endif 402 if (ret == -1) { 403 fprintf(stderr, "failed to add interface `%s' to bridge `%s': %s\n", 404 iface, bridge, strerror(errno)); 405 ret = EXIT_FAILURE; 406 goto cleanup; 407 } 408 409 /* bring the interface up */ 410 prep_ifreq(&ifr, iface); 411 if (ioctl(ctlfd, SIOCGIFFLAGS, &ifr) == -1) { 412 fprintf(stderr, "failed to get interface flags for `%s': %s\n", 413 iface, strerror(errno)); 414 ret = EXIT_FAILURE; 415 goto cleanup; 416 } 417 418 ifr.ifr_flags |= IFF_UP; 419 if (ioctl(ctlfd, SIOCSIFFLAGS, &ifr) == -1) { 420 fprintf(stderr, "failed to bring up interface `%s': %s\n", 421 iface, strerror(errno)); 422 ret = EXIT_FAILURE; 423 goto cleanup; 424 } 425 426 /* write fd to the domain socket */ 427 if (send_fd(unixfd, fd) == -1) { 428 fprintf(stderr, "failed to write fd to unix socket: %s\n", 429 strerror(errno)); 430 ret = EXIT_FAILURE; 431 goto cleanup; 432 } 433 434 /* ... */ 435 436 /* profit! */ 437 438 cleanup: 439 440 while ((acl_rule = QSIMPLEQ_FIRST(&acl_list)) != NULL) { 441 QSIMPLEQ_REMOVE_HEAD(&acl_list, entry); 442 g_free(acl_rule); 443 } 444 445 return ret; 446 } 447