Annotation of qemu/qemu-bridge-helper.c, revision 1.1.1.1

1.1       root        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