You know my D wouldn't fit on an entire page. ;)
SORRY, DIDN'T NOTICE MY ACCOUNT WAS MUTED.
ANYWAY, YOU WANT ME TO SPAM SOMETHING DIFFERENT?
HERE YA GO.
/*
- QEMU Bridge Helper *
- Copyright IBM, Corp. 2011 *
- Authors:
- Anthony Liguori [email protected]
- Richa Marwaha [email protected]
- Corey Bryant [email protected] *
- This work is licensed under the terms of the GNU GPL, version 2. See
- the COPYING file in the top-level directory. /
#include "config-host.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef SIOCBRADDIF
#include
#endif
#include "qemu/queue.h"
#include "net/tap-linux.h"
#ifdef CONFIG_LIBCAP
#include
#endif
#define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR "/bridge.conf"
enum { ACL_ALLOW = 0, ACL_ALLOW_ALL, ACL_DENY, ACL_DENY_ALL, };
typedef struct ACLRule { int type; char iface[IFNAMSIZ]; QSIMPLEQ_ENTRY(ACLRule) entry; } ACLRule;
typedef QSIMPLEQ_HEAD(ACLList, ACLRule) ACLList;
static void usage(void) { fprintf(stderr, "Usage: qemu-bridge-helper [--use-vnet] --br=bridge --fd=unixfd\n"); }
static int parse_acl_file(const char filename, ACLList acl_list) { FILE f; char line[4096]; ACLRule acl_rule;
f = fopen(filename, "r");
if (f == NULL) {
return -1;
}
while (fgets(line, sizeof(line), f) != NULL) {
char *ptr = line;
char *cmd, *arg, *argend;
while (isspace(*ptr)) {
ptr++;
}
/* skip comments and empty lines */
if (*ptr == '#' || *ptr == 0) {
continue;
}
cmd = ptr;
arg = strchr(cmd, ' ');
if (arg == NULL) {
arg = strchr(cmd, '\t');
}
if (arg == NULL) {
fprintf(stderr, "Invalid config line:\n %s\n", line);
fclose(f);
errno = EINVAL;
return -1;
}
*arg = 0;
arg++;
while (isspace(*arg)) {
arg++;
}
argend = arg + strlen(arg);
while (arg != argend && isspace(*(argend - 1))) {
argend--;
}
*argend = 0;
if (strcmp(cmd, "deny") == 0) {
acl_rule = g_malloc(sizeof(*acl_rule));
if (strcmp(arg, "all") == 0) {
acl_rule->type = ACL_DENY_ALL;
} else {
acl_rule->type = ACL_DENY;
snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg);
}
QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry);
} else if (strcmp(cmd, "allow") == 0) {
acl_rule = g_malloc(sizeof(*acl_rule));
if (strcmp(arg, "all") == 0) {
acl_rule->type = ACL_ALLOW_ALL;
} else {
acl_rule->type = ACL_ALLOW;
snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg);
}
QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry);
} else if (strcmp(cmd, "include") == 0) {
/* ignore errors */
parse_acl_file(arg, acl_list);
} else {
fprintf(stderr, "Unknown command `%s'\n", cmd);
fclose(f);
errno = EINVAL;
return -1;
}
}
fclose(f);
return 0;
}
static bool has_vnet_hdr(int fd) { unsigned int features = 0;
if (ioctl(fd, TUNGETFEATURES, &features) == -1) {
return false;
}
if (!(features & IFF_VNET_HDR)) {
return false;
}
return true;
}
static void prep_ifreq(struct ifreq ifr, const char ifname) { memset(ifr, 0, sizeof(*ifr)); snprintf(ifr->ifr_name, IFNAMSIZ, "%s", ifname); }
static int send_fd(int c, int fd) { char msgbuf[CMSG_SPACE(sizeof(fd))]; struct msghdr msg = { .msg_control = msgbuf, .msg_controllen = sizeof(msgbuf), }; struct cmsghdr *cmsg; struct iovec iov; char req[1] = { 0x00 };
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
msg.msg_controllen = cmsg->cmsg_len;
iov.iov_base = req;
iov.iov_len = sizeof(req);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
return sendmsg(c, &msg, 0);
}
#ifdef CONFIG_LIBCAP static int drop_privileges(void) { / clear all capabilities / capng_clear(CAPNG_SELECT_BOTH);
if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
CAP_NET_ADMIN) < 0) {
return -1;
}
/* change to calling user's real uid and gid, retaining supplemental
* groups and CAP_NET_ADMIN */
if (capng_change_id(getuid(), getgid(), CAPNG_CLEAR_BOUNDING)) {
return -1;
}
return 0;
}
#endif
int main(int argc, char **argv) { struct ifreq ifr;
#ifndef SIOCBRADDIF unsigned long ifargs[4];
#endif int ifindex; int fd = -1, ctlfd = -1, unixfd = -1; int use_vnet = 0; int mtu; const char bridge = NULL; char iface[IFNAMSIZ]; int index; ACLRule acl_rule; ACLList acl_list; int access_allowed, access_denied; int ret = EXIT_SUCCESS;
#ifdef CONFIG_LIBCAP /* if we're run from an suid binary, immediately drop privileges preserving
* cap_net_admin */
if (geteuid() == 0 && getuid() != geteuid()) {
if (drop_privileges() == -1) {
fprintf(stderr, "failed to drop privileges\n");
return 1;
}
}
#endif
/* parse arguments */
for (index = 1; index < argc; index++) {
if (strcmp(argv[index], "--use-vnet") == 0) {
use_vnet = 1;
} else if (strncmp(argv[index], "--br=", 5) == 0) {
bridge = &argv[index][5];
} else if (strncmp(argv[index], "--fd=", 5) == 0) {
unixfd = atoi(&argv[index][5]);
} else {
usage();
return EXIT_FAILURE;
}
}