File:  [Qemu by Fabrice Bellard] / qemu / qemu-bridge-helper.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 19:35:01 2018 UTC (2 years, 3 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu1101, HEAD
qemu 1.1.1

    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: }

unix.superglobalmegacorp.com