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 #include "qemu-queue.h" 39 40 #include "net/tap-linux.h" 41 42 #ifdef CONFIG_LIBCAP 43 #include <cap-ng.h> 44 #endif 45 46 #define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR "/bridge.conf" 47 48 enum { 49 ACL_ALLOW = 0, 50 ACL_ALLOW_ALL, 51 ACL_DENY, 52 ACL_DENY_ALL, 53 }; 54 55 typedef struct ACLRule { 56 int type; 57 char iface[IFNAMSIZ]; 58 QSIMPLEQ_ENTRY(ACLRule) entry; 59 } ACLRule; 60 61 typedef QSIMPLEQ_HEAD(ACLList, ACLRule) ACLList; 62 63 static void usage(void) 64 { 65 fprintf(stderr, 66 "Usage: qemu-bridge-helper [--use-vnet] --br=bridge --fd=unixfd\n"); 67 } 68 69 static int parse_acl_file(const char *filename, ACLList *acl_list) 70 { 71 FILE *f; 72 char line[4096]; 73 ACLRule *acl_rule; 74 75 f = fopen(filename, "r"); 76 if (f == NULL) { 77 return -1; 78 } 79 80 while (fgets(line, sizeof(line), f) != NULL) { 81 char *ptr = line; 82 char *cmd, *arg, *argend; 83 84 while (isspace(*ptr)) { 85 ptr++; 86 } 87 88 /* skip comments and empty lines */ 89 if (*ptr == '#' || *ptr == 0) { 90 continue; 91 } 92 93 cmd = ptr; 94 arg = strchr(cmd, ' '); 95 if (arg == NULL) { 96 arg = strchr(cmd, '\t'); 97 } 98 99 if (arg == NULL) { 100 fprintf(stderr, "Invalid config line:\n %s\n", line); 101 fclose(f); 102 errno = EINVAL; 103 return -1; 104 } 105 106 *arg = 0; 107 arg++; 108 while (isspace(*arg)) { 109 arg++; 110 } 111 112 argend = arg + strlen(arg); 113 while (arg != argend && isspace(*(argend - 1))) { 114 argend--; 115 } 116 *argend = 0; 117 118 if (strcmp(cmd, "deny") == 0) { 119 acl_rule = g_malloc(sizeof(*acl_rule)); 120 if (strcmp(arg, "all") == 0) { 121 acl_rule->type = ACL_DENY_ALL; 122 } else { 123 acl_rule->type = ACL_DENY; 124 snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg); 125 } 126 QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry); 127 } else if (strcmp(cmd, "allow") == 0) { 128 acl_rule = g_malloc(sizeof(*acl_rule)); 129 if (strcmp(arg, "all") == 0) { 130 acl_rule->type = ACL_ALLOW_ALL; 131 } else { 132 acl_rule->type = ACL_ALLOW; 133 snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg); 134 } 135 QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry); 136 } else if (strcmp(cmd, "include") == 0) { 137 /* ignore errors */ 138 parse_acl_file(arg, acl_list); 139 } else { 140 fprintf(stderr, "Unknown command `%s'\n", cmd); 141 fclose(f); 142 errno = EINVAL; 143 return -1; 144 } 145 } 146 147 fclose(f); 148 149 return 0; 150 } 151 152 static bool has_vnet_hdr(int fd) 153 { 154 unsigned int features = 0; 155 156 if (ioctl(fd, TUNGETFEATURES, &features) == -1) { 157 return false; 158 } 159 160 if (!(features & IFF_VNET_HDR)) { 161 return false; 162 } 163 164 return true; 165 } 166 167 static void prep_ifreq(struct ifreq *ifr, const char *ifname) 168 { 169 memset(ifr, 0, sizeof(*ifr)); 170 snprintf(ifr->ifr_name, IFNAMSIZ, "%s", ifname); 171 } 172 173 static int send_fd(int c, int fd) 174 { 175 char msgbuf[CMSG_SPACE(sizeof(fd))]; 176 struct msghdr msg = { 177 .msg_control = msgbuf, 178 .msg_controllen = sizeof(msgbuf), 179 }; 180 struct cmsghdr *cmsg; 181 struct iovec iov; 182 char req[1] = { 0x00 }; 183 184 cmsg = CMSG_FIRSTHDR(&msg); 185 cmsg->cmsg_level = SOL_SOCKET; 186 cmsg->cmsg_type = SCM_RIGHTS; 187 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 188 msg.msg_controllen = cmsg->cmsg_len; 189 190 iov.iov_base = req; 191 iov.iov_len = sizeof(req); 192 193 msg.msg_iov = &iov; 194 msg.msg_iovlen = 1; 195 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); 196 197 return sendmsg(c, &msg, 0); 198 } 199 200 #ifdef CONFIG_LIBCAP 201 static int drop_privileges(void) 202 { 203 /* clear all capabilities */ 204 capng_clear(CAPNG_SELECT_BOTH); 205 206 if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, 207 CAP_NET_ADMIN) < 0) { 208 return -1; 209 } 210 211 /* change to calling user's real uid and gid, retaining supplemental 212 * groups and CAP_NET_ADMIN */ 213 if (capng_change_id(getuid(), getgid(), CAPNG_CLEAR_BOUNDING)) { 214 return -1; 215 } 216 217 return 0; 218 } 219 #endif 220 221 int main(int argc, char **argv) 222 { 223 struct ifreq ifr; 224 int fd, ctlfd, unixfd = -1; 225 int use_vnet = 0; 226 int mtu; 227 const char *bridge = NULL; 228 char iface[IFNAMSIZ]; 229 int index; 230 ACLRule *acl_rule; 231 ACLList acl_list; 232 int access_allowed, access_denied; 233 int ret = EXIT_SUCCESS; 234 235 #ifdef CONFIG_LIBCAP 236 /* if we're run from an suid binary, immediately drop privileges preserving 237 * cap_net_admin */ 238 if (geteuid() == 0 && getuid() != geteuid()) { 239 if (drop_privileges() == -1) { 240 fprintf(stderr, "failed to drop privileges\n"); 241 return 1; 242 } 243 } 244 #endif 245 246 /* parse arguments */ 247 for (index = 1; index < argc; index++) { 248 if (strcmp(argv[index], "--use-vnet") == 0) { 249 use_vnet = 1; 250 } else if (strncmp(argv[index], "--br=", 5) == 0) { 251 bridge = &argv[index][5]; 252 } else if (strncmp(argv[index], "--fd=", 5) == 0) { 253 unixfd = atoi(&argv[index][5]); 254 } else { 255 usage(); 256 return EXIT_FAILURE; 257 } 258 } 259 260 if (bridge == NULL || unixfd == -1) { 261 usage(); 262 return EXIT_FAILURE; 263 } 264 265 /* parse default acl file */ 266 QSIMPLEQ_INIT(&acl_list); 267 if (parse_acl_file(DEFAULT_ACL_FILE, &acl_list) == -1) { 268 fprintf(stderr, "failed to parse default acl file `%s'\n", 269 DEFAULT_ACL_FILE); 270 ret = EXIT_FAILURE; 271 goto cleanup; 272 } 273 274 /* validate bridge against acl -- default policy is to deny 275 * according acl policy if we have a deny and allow both 276 * then deny should always win over allow 277 */ 278 access_allowed = 0; 279 access_denied = 0; 280 QSIMPLEQ_FOREACH(acl_rule, &acl_list, entry) { 281 switch (acl_rule->type) { 282 case ACL_ALLOW_ALL: 283 access_allowed = 1; 284 break; 285 case ACL_ALLOW: 286 if (strcmp(bridge, acl_rule->iface) == 0) { 287 access_allowed = 1; 288 } 289 break; 290 case ACL_DENY_ALL: 291 access_denied = 1; 292 break; 293 case ACL_DENY: 294 if (strcmp(bridge, acl_rule->iface) == 0) { 295 access_denied = 1; 296 } 297 break; 298 } 299 } 300 301 if ((access_allowed == 0) || (access_denied == 1)) { 302 fprintf(stderr, "access denied by acl file\n"); 303 ret = EXIT_FAILURE; 304 goto cleanup; 305 } 306 307 /* open a socket to use to control the network interfaces */ 308 ctlfd = socket(AF_INET, SOCK_STREAM, 0); 309 if (ctlfd == -1) { 310 fprintf(stderr, "failed to open control socket: %s\n", strerror(errno)); 311 ret = EXIT_FAILURE; 312 goto cleanup; 313 } 314 315 /* open the tap device */ 316 fd = open("/dev/net/tun", O_RDWR); 317 if (fd == -1) { 318 fprintf(stderr, "failed to open /dev/net/tun: %s\n", strerror(errno)); 319 ret = EXIT_FAILURE; 320 goto cleanup; 321 } 322 323 /* request a tap device, disable PI, and add vnet header support if 324 * requested and it's available. */ 325 prep_ifreq(&ifr, "tap%d"); 326 ifr.ifr_flags = IFF_TAP|IFF_NO_PI; 327 if (use_vnet && has_vnet_hdr(fd)) { 328 ifr.ifr_flags |= IFF_VNET_HDR; 329 } 330 331 if (ioctl(fd, TUNSETIFF, &ifr) == -1) { 332 fprintf(stderr, "failed to create tun device: %s\n", strerror(errno)); 333 ret = EXIT_FAILURE; 334 goto cleanup; 335 } 336 337 /* save tap device name */ 338 snprintf(iface, sizeof(iface), "%s", ifr.ifr_name); 339 340 /* get the mtu of the bridge */ 341 prep_ifreq(&ifr, bridge); 342 if (ioctl(ctlfd, SIOCGIFMTU, &ifr) == -1) { 343 fprintf(stderr, "failed to get mtu of bridge `%s': %s\n", 344 bridge, strerror(errno)); 345 ret = EXIT_FAILURE; 346 goto cleanup; 347 } 348 349 /* save mtu */ 350 mtu = ifr.ifr_mtu; 351 352 /* set the mtu of the interface based on the bridge */ 353 prep_ifreq(&ifr, iface); 354 ifr.ifr_mtu = mtu; 355 if (ioctl(ctlfd, SIOCSIFMTU, &ifr) == -1) { 356 fprintf(stderr, "failed to set mtu of device `%s' to %d: %s\n", 357 iface, mtu, strerror(errno)); 358 ret = EXIT_FAILURE; 359 goto cleanup; 360 } 361 362 /* add the interface to the bridge */ 363 prep_ifreq(&ifr, bridge); 364 ifr.ifr_ifindex = if_nametoindex(iface); 365 366 if (ioctl(ctlfd, SIOCBRADDIF, &ifr) == -1) { 367 fprintf(stderr, "failed to add interface `%s' to bridge `%s': %s\n", 368 iface, bridge, strerror(errno)); 369 ret = EXIT_FAILURE; 370 goto cleanup; 371 } 372 373 /* bring the interface up */ 374 prep_ifreq(&ifr, iface); 375 if (ioctl(ctlfd, SIOCGIFFLAGS, &ifr) == -1) { 376 fprintf(stderr, "failed to get interface flags for `%s': %s\n", 377 iface, strerror(errno)); 378 ret = EXIT_FAILURE; 379 goto cleanup; 380 } 381 382 ifr.ifr_flags |= IFF_UP; 383 if (ioctl(ctlfd, SIOCSIFFLAGS, &ifr) == -1) { 384 fprintf(stderr, "failed to bring up interface `%s': %s\n", 385 iface, strerror(errno)); 386 ret = EXIT_FAILURE; 387 goto cleanup; 388 } 389 390 /* write fd to the domain socket */ 391 if (send_fd(unixfd, fd) == -1) { 392 fprintf(stderr, "failed to write fd to unix socket: %s\n", 393 strerror(errno)); 394 ret = EXIT_FAILURE; 395 goto cleanup; 396 } 397 398 /* ... */ 399 400 /* profit! */ 401 402 cleanup: 403 404 while ((acl_rule = QSIMPLEQ_FIRST(&acl_list)) != NULL) { 405 QSIMPLEQ_REMOVE_HEAD(&acl_list, entry); 406 g_free(acl_rule); 407 } 408 409 return ret; 410 } 411