mirror of
https://github.com/openvswitch/ovs
synced 2025-09-07 09:46:20 +00:00
ofproto: Drop remote command execution feature.
At one point Nicira had deployment plans for which adding a remote command execution feature to the OpenFlow stack made a lot of sense. We no longer have those plans, as far as I know, and leaving the feature in seems like a huge potential security hole. So this commit blows away the entire feature.
This commit is contained in:
@@ -39,7 +39,6 @@ EXTRA_DIST = INSTALL.bridge \
|
||||
bin_PROGRAMS =
|
||||
sbin_PROGRAMS =
|
||||
bin_SCRIPTS =
|
||||
dist_commands_DATA =
|
||||
dist_man_MANS =
|
||||
dist_pkgdata_SCRIPTS =
|
||||
dist_sbin_SCRIPTS =
|
||||
|
3
debian/automake.mk
vendored
3
debian/automake.mk
vendored
@@ -1,7 +1,5 @@
|
||||
EXTRA_DIST += \
|
||||
debian/changelog \
|
||||
debian/commands/reconfigure \
|
||||
debian/commands/update \
|
||||
debian/compat \
|
||||
debian/control \
|
||||
debian/control.modules.in \
|
||||
@@ -60,5 +58,6 @@ EXTRA_DIST += \
|
||||
debian/ovs-switch-setup.8 \
|
||||
debian/po/POTFILES.in \
|
||||
debian/po/templates.pot \
|
||||
debian/reconfigure \
|
||||
debian/rules \
|
||||
debian/rules.modules
|
||||
|
4
debian/commands/update
vendored
4
debian/commands/update
vendored
@@ -1,4 +0,0 @@
|
||||
#! /bin/sh
|
||||
set -e
|
||||
apt-get update -qy
|
||||
apt-get upgrade -qy
|
2
debian/openvswitch-switch.install
vendored
2
debian/openvswitch-switch.install
vendored
@@ -4,5 +4,3 @@ _debian/utilities/ovs-dpctl usr/sbin
|
||||
_debian/utilities/ovs-kill usr/sbin
|
||||
_debian/utilities/ovs-vsctl usr/sbin
|
||||
_debian/vswitchd/ovs-vswitchd usr/sbin
|
||||
debian/commands/* usr/share/openvswitch/commands
|
||||
debian/openvswitch/usr/share/openvswitch/commands/* usr/share/openvswitch/commands
|
||||
|
1
debian/openvswitch-switchui.install
vendored
1
debian/openvswitch-switchui.install
vendored
@@ -1,2 +1,3 @@
|
||||
_debian/extras/ezio/ezio-term usr/sbin
|
||||
_debian/extras/ezio/ovs-switchui usr/bin
|
||||
debian/reconfigure usr/share/openvswitch-switchui
|
||||
|
@@ -2079,7 +2079,7 @@ save_config(const struct svec *settings)
|
||||
}
|
||||
|
||||
svec_init(&argv);
|
||||
svec_add(&argv, "/usr/share/openvswitch/commands/reconfigure");
|
||||
svec_add(&argv, "/usr/share/openvswitch-switchui/reconfigure");
|
||||
svec_append(&argv, settings);
|
||||
svec_terminate(&argv);
|
||||
ok = run_and_report_failure(argv.names, "Save failed");
|
||||
|
@@ -44,14 +44,11 @@ enum nicira_type {
|
||||
/* Get configuration of action. */
|
||||
NXT_ACT_GET_CONFIG,
|
||||
|
||||
/* Remote command execution. The request body is a sequence of strings
|
||||
* delimited by null bytes. The first string is a command name.
|
||||
* Subsequent strings are command arguments. */
|
||||
NXT_COMMAND_REQUEST,
|
||||
/* No longer used. */
|
||||
NXT_COMMAND_REQUEST__OBSOLETE,
|
||||
|
||||
/* Remote command execution reply, sent when the command's execution
|
||||
* completes. The reply body is struct nx_command_reply. */
|
||||
NXT_COMMAND_REPLY,
|
||||
/* No longer used. */
|
||||
NXT_COMMAND_REPLY__OBSOLETE,
|
||||
|
||||
/* No longer used. */
|
||||
NXT_FLOW_END_CONFIG__OBSOLETE,
|
||||
@@ -97,24 +94,4 @@ struct nx_action_header {
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct nx_action_header) == 16);
|
||||
|
||||
/* Status bits for NXT_COMMAND_REPLY. */
|
||||
enum {
|
||||
NXT_STATUS_EXITED = 1 << 31, /* Exited normally. */
|
||||
NXT_STATUS_SIGNALED = 1 << 30, /* Exited due to signal. */
|
||||
NXT_STATUS_UNKNOWN = 1 << 29, /* Exited for unknown reason. */
|
||||
NXT_STATUS_COREDUMP = 1 << 28, /* Exited with core dump. */
|
||||
NXT_STATUS_ERROR = 1 << 27, /* Command could not be executed. */
|
||||
NXT_STATUS_STARTED = 1 << 26, /* Command was started. */
|
||||
NXT_STATUS_EXITSTATUS = 0xff, /* Exit code mask if NXT_STATUS_EXITED. */
|
||||
NXT_STATUS_TERMSIG = 0xff, /* Signal number if NXT_STATUS_SIGNALED. */
|
||||
};
|
||||
|
||||
/* NXT_COMMAND_REPLY. */
|
||||
struct nx_command_reply {
|
||||
struct nicira_header nxh;
|
||||
uint32_t status; /* Status bits defined above. */
|
||||
/* Followed by any number of bytes of process output. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct nx_command_reply) == 20);
|
||||
|
||||
#endif /* openflow/nicira-ext.h */
|
||||
|
@@ -32,7 +32,6 @@ VLOG_MODULE(dpif)
|
||||
VLOG_MODULE(dpif_linux)
|
||||
VLOG_MODULE(dpif_netdev)
|
||||
VLOG_MODULE(dpctl)
|
||||
VLOG_MODULE(executer)
|
||||
VLOG_MODULE(ezio_term)
|
||||
VLOG_MODULE(fail_open)
|
||||
VLOG_MODULE(fatal_signal)
|
||||
|
@@ -11,8 +11,6 @@ ofproto_libofproto_a_SOURCES = \
|
||||
ofproto/collectors.h \
|
||||
ofproto/discovery.c \
|
||||
ofproto/discovery.h \
|
||||
ofproto/executer.c \
|
||||
ofproto/executer.h \
|
||||
ofproto/fail-open.c \
|
||||
ofproto/fail-open.h \
|
||||
ofproto/in-band.c \
|
||||
@@ -27,5 +25,3 @@ ofproto_libofproto_a_SOURCES = \
|
||||
ofproto/pinsched.h \
|
||||
ofproto/status.c \
|
||||
ofproto/status.h
|
||||
|
||||
include ofproto/commands/automake.mk
|
||||
|
@@ -1,3 +0,0 @@
|
||||
commandsdir = ${pkgdatadir}/commands
|
||||
dist_commands_SCRIPTS = \
|
||||
ofproto/commands/reboot
|
@@ -1,3 +0,0 @@
|
||||
#! /bin/sh
|
||||
ovs-kill --force --signal=USR1 ovs-switchui.pid
|
||||
reboot
|
@@ -1,512 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Nicira Networks.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "executer.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "dirs.h"
|
||||
#include "dynamic-string.h"
|
||||
#include "fatal-signal.h"
|
||||
#include "openflow/nicira-ext.h"
|
||||
#include "ofpbuf.h"
|
||||
#include "openflow/openflow.h"
|
||||
#include "poll-loop.h"
|
||||
#include "rconn.h"
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
#include "vconn.h"
|
||||
|
||||
#define THIS_MODULE VLM_executer
|
||||
#include "vlog.h"
|
||||
|
||||
#define MAX_CHILDREN 8
|
||||
|
||||
struct child {
|
||||
/* Information about child process. */
|
||||
char *name; /* argv[0] passed to child. */
|
||||
pid_t pid; /* Child's process ID. */
|
||||
|
||||
/* For sending a reply to the controller when the child dies. */
|
||||
struct rconn *rconn;
|
||||
uint32_t xid; /* Transaction ID used by controller. */
|
||||
|
||||
/* We read up to MAX_OUTPUT bytes of output and send them back to the
|
||||
* controller when the child dies. */
|
||||
#define MAX_OUTPUT 4096
|
||||
int output_fd; /* FD from which to read child's output. */
|
||||
uint8_t *output; /* Output data. */
|
||||
size_t output_size; /* Number of bytes of output data so far. */
|
||||
};
|
||||
|
||||
struct executer {
|
||||
/* Settings. */
|
||||
char *command_acl; /* Command white/blacklist, as shell globs. */
|
||||
char *command_dir; /* Directory that contains commands. */
|
||||
|
||||
/* Children. */
|
||||
struct child children[MAX_CHILDREN];
|
||||
size_t n_children;
|
||||
};
|
||||
|
||||
/* File descriptors for waking up when a child dies. */
|
||||
static int signal_fds[2] = {-1, -1};
|
||||
|
||||
static void send_child_status(struct rconn *, uint32_t xid, uint32_t status,
|
||||
const void *data, size_t size);
|
||||
static void send_child_message(struct rconn *, uint32_t xid, uint32_t status,
|
||||
const char *message);
|
||||
|
||||
/* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
|
||||
* access control list in the format described for --command-acl in
|
||||
* ovs-openflowd(8). */
|
||||
static bool
|
||||
executer_is_permitted(const char *acl_, const char *cmd)
|
||||
{
|
||||
char *acl, *save_ptr, *pattern;
|
||||
bool allowed, denied;
|
||||
|
||||
/* Verify that 'cmd' consists only of alphanumerics plus _ or -. */
|
||||
if (cmd[strspn(cmd, "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")] != '\0') {
|
||||
VLOG_WARN("rejecting command name \"%s\" that contain forbidden "
|
||||
"characters", cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check 'cmd' against 'acl'. */
|
||||
acl = xstrdup(acl_);
|
||||
save_ptr = acl;
|
||||
allowed = denied = false;
|
||||
while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
|
||||
if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
|
||||
allowed = true;
|
||||
} else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
|
||||
denied = true;
|
||||
}
|
||||
}
|
||||
free(acl);
|
||||
|
||||
/* Check the command white/blacklisted state. */
|
||||
if (allowed && !denied) {
|
||||
VLOG_INFO("permitting command execution: \"%s\" is whitelisted", cmd);
|
||||
} else if (allowed && denied) {
|
||||
VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
|
||||
"and whitelisted", cmd);
|
||||
} else if (!allowed) {
|
||||
VLOG_WARN("denying command execution: \"%s\" is not whitelisted", cmd);
|
||||
} else if (denied) {
|
||||
VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
|
||||
}
|
||||
return allowed && !denied;
|
||||
}
|
||||
|
||||
int
|
||||
executer_handle_request(struct executer *e, struct rconn *rconn,
|
||||
struct nicira_header *request)
|
||||
{
|
||||
char **argv;
|
||||
char *args;
|
||||
char *exec_file = NULL;
|
||||
int max_fds;
|
||||
struct stat s;
|
||||
size_t args_size;
|
||||
size_t argc;
|
||||
size_t i;
|
||||
pid_t pid;
|
||||
int output_fds[2];
|
||||
|
||||
/* Verify limit on children not exceeded.
|
||||
* XXX should probably kill children when the connection drops? */
|
||||
if (e->n_children >= MAX_CHILDREN) {
|
||||
send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
|
||||
"too many child processes");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy argument buffer, adding a null terminator at the end. Now every
|
||||
* argument is null-terminated, instead of being merely null-delimited. */
|
||||
args_size = ntohs(request->header.length) - sizeof *request;
|
||||
args = xmemdup0((const void *) (request + 1), args_size);
|
||||
|
||||
/* Count arguments. */
|
||||
argc = 0;
|
||||
for (i = 0; i <= args_size; i++) {
|
||||
argc += args[i] == '\0';
|
||||
}
|
||||
|
||||
/* Set argv[*] to point to each argument. */
|
||||
argv = xmalloc((argc + 1) * sizeof *argv);
|
||||
argv[0] = args;
|
||||
for (i = 1; i < argc; i++) {
|
||||
argv[i] = strchr(argv[i - 1], '\0') + 1;
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
/* Check permissions. */
|
||||
if (!executer_is_permitted(e->command_acl, argv[0])) {
|
||||
send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
|
||||
"command not allowed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Find the executable. */
|
||||
exec_file = xasprintf("%s/%s", e->command_dir, argv[0]);
|
||||
if (stat(exec_file, &s)) {
|
||||
VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
|
||||
send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
|
||||
"command not allowed");
|
||||
goto done;
|
||||
}
|
||||
if (!S_ISREG(s.st_mode)) {
|
||||
VLOG_WARN("\"%s\" is not a regular file", exec_file);
|
||||
send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
|
||||
"command not allowed");
|
||||
goto done;
|
||||
}
|
||||
argv[0] = exec_file;
|
||||
|
||||
/* Arrange to capture output. */
|
||||
if (pipe(output_fds)) {
|
||||
VLOG_WARN("pipe failed: %s", strerror(errno));
|
||||
send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
|
||||
"internal error (pipe)");
|
||||
goto done;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
/* Running in child.
|
||||
* XXX should run in new process group so that we can signal all
|
||||
* subprocesses at once? Would also want to catch fatal signals and
|
||||
* kill them at the same time though. */
|
||||
fatal_signal_fork();
|
||||
dup2(get_null_fd(), 0);
|
||||
dup2(output_fds[1], 1);
|
||||
dup2(get_null_fd(), 2);
|
||||
max_fds = get_max_fds();
|
||||
for (i = 3; i < max_fds; i++) {
|
||||
close(i);
|
||||
}
|
||||
if (chdir(e->command_dir)) {
|
||||
printf("could not change directory to \"%s\": %s",
|
||||
e->command_dir, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
execv(argv[0], argv);
|
||||
printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (pid > 0) {
|
||||
/* Running in parent. */
|
||||
struct child *child;
|
||||
|
||||
VLOG_INFO("started \"%s\" subprocess", argv[0]);
|
||||
send_child_status(rconn, request->header.xid, NXT_STATUS_STARTED,
|
||||
NULL, 0);
|
||||
child = &e->children[e->n_children++];
|
||||
child->name = xstrdup(argv[0]);
|
||||
child->pid = pid;
|
||||
child->rconn = rconn;
|
||||
child->xid = request->header.xid;
|
||||
child->output_fd = output_fds[0];
|
||||
child->output = xmalloc(MAX_OUTPUT);
|
||||
child->output_size = 0;
|
||||
set_nonblocking(output_fds[0]);
|
||||
close(output_fds[1]);
|
||||
} else {
|
||||
VLOG_WARN("fork failed: %s", strerror(errno));
|
||||
send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
|
||||
"internal error (fork)");
|
||||
close(output_fds[0]);
|
||||
close(output_fds[1]);
|
||||
}
|
||||
|
||||
done:
|
||||
free(exec_file);
|
||||
free(args);
|
||||
free(argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_child_status(struct rconn *rconn, uint32_t xid, uint32_t status,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
if (rconn) {
|
||||
struct nx_command_reply *r;
|
||||
struct ofpbuf *buffer;
|
||||
|
||||
r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
|
||||
r->nxh.vendor = htonl(NX_VENDOR_ID);
|
||||
r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
|
||||
r->status = htonl(status);
|
||||
ofpbuf_put(buffer, data, size);
|
||||
update_openflow_length(buffer);
|
||||
if (rconn_send(rconn, buffer, NULL)) {
|
||||
ofpbuf_delete(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_child_message(struct rconn *rconn, uint32_t xid, uint32_t status,
|
||||
const char *message)
|
||||
{
|
||||
send_child_status(rconn, xid, status, message, strlen(message));
|
||||
}
|
||||
|
||||
/* 'child' died with 'status' as its return code. Deal with it. */
|
||||
static void
|
||||
child_terminated(struct child *child, int status)
|
||||
{
|
||||
struct ds ds;
|
||||
uint32_t ofp_status;
|
||||
|
||||
/* Log how it terminated. */
|
||||
ds_init(&ds);
|
||||
if (WIFEXITED(status)) {
|
||||
ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
const char *name = NULL;
|
||||
#ifdef HAVE_STRSIGNAL
|
||||
name = strsignal(WTERMSIG(status));
|
||||
#endif
|
||||
ds_put_format(&ds, "by signal %d", WTERMSIG(status));
|
||||
if (name) {
|
||||
ds_put_format(&ds, " (%s)", name);
|
||||
}
|
||||
}
|
||||
if (WCOREDUMP(status)) {
|
||||
ds_put_cstr(&ds, " (core dumped)");
|
||||
}
|
||||
VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
|
||||
child->name, (long int) child->pid, ds_cstr(&ds));
|
||||
ds_destroy(&ds);
|
||||
|
||||
/* Send a status message back to the controller that requested the
|
||||
* command. */
|
||||
if (WIFEXITED(status)) {
|
||||
ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
|
||||
} else {
|
||||
ofp_status = NXT_STATUS_UNKNOWN;
|
||||
}
|
||||
if (WCOREDUMP(status)) {
|
||||
ofp_status |= NXT_STATUS_COREDUMP;
|
||||
}
|
||||
send_child_status(child->rconn, child->xid, ofp_status,
|
||||
child->output, child->output_size);
|
||||
}
|
||||
|
||||
/* Read output from 'child' and append it to its output buffer. */
|
||||
static void
|
||||
poll_child(struct child *child)
|
||||
{
|
||||
ssize_t n;
|
||||
|
||||
if (child->output_fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
n = read(child->output_fd, child->output + child->output_size,
|
||||
MAX_OUTPUT - child->output_size);
|
||||
} while (n < 0 && errno == EINTR);
|
||||
if (n > 0) {
|
||||
child->output_size += n;
|
||||
if (child->output_size < MAX_OUTPUT) {
|
||||
return;
|
||||
}
|
||||
} else if (n < 0 && errno == EAGAIN) {
|
||||
return;
|
||||
}
|
||||
close(child->output_fd);
|
||||
child->output_fd = -1;
|
||||
}
|
||||
|
||||
void
|
||||
executer_run(struct executer *e)
|
||||
{
|
||||
char buffer[MAX_CHILDREN];
|
||||
size_t i;
|
||||
|
||||
if (!e->n_children) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read output from children. */
|
||||
for (i = 0; i < e->n_children; i++) {
|
||||
struct child *child = &e->children[i];
|
||||
poll_child(child);
|
||||
}
|
||||
|
||||
/* If SIGCHLD was received, reap dead children. */
|
||||
if (read(signal_fds[0], buffer, sizeof buffer) <= 0) {
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
/* Get dead child in 'pid' and its return code in 'status'. */
|
||||
pid = waitpid(WAIT_ANY, &status, WNOHANG);
|
||||
if (pid < 0 && errno == EINTR) {
|
||||
continue;
|
||||
} else if (pid <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find child with given 'pid' and drop it from the list. */
|
||||
for (i = 0; i < e->n_children; i++) {
|
||||
struct child *child = &e->children[i];
|
||||
if (child->pid == pid) {
|
||||
poll_child(child);
|
||||
child_terminated(child, status);
|
||||
free(child->name);
|
||||
free(child->output);
|
||||
*child = e->children[--e->n_children];
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
|
||||
found:;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
executer_wait(struct executer *e)
|
||||
{
|
||||
if (e->n_children) {
|
||||
size_t i;
|
||||
|
||||
/* Wake up on SIGCHLD. */
|
||||
poll_fd_wait(signal_fds[0], POLLIN);
|
||||
|
||||
/* Wake up when we get output from a child. */
|
||||
for (i = 0; i < e->n_children; i++) {
|
||||
struct child *child = &e->children[i];
|
||||
if (child->output_fd >= 0) {
|
||||
poll_fd_wait(child->output_fd, POLLIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
executer_rconn_closing(struct executer *e, struct rconn *rconn)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* If any of our children was connected to 'r', then disconnect it so we
|
||||
* don't try to reference a dead connection when the process terminates
|
||||
* later.
|
||||
* XXX kill the children started by 'r'? */
|
||||
for (i = 0; i < e->n_children; i++) {
|
||||
if (e->children[i].rconn == rconn) {
|
||||
e->children[i].rconn = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sigchld_handler(int signr UNUSED)
|
||||
{
|
||||
ignore(write(signal_fds[1], "", 1));
|
||||
}
|
||||
|
||||
int
|
||||
executer_create(const char *command_acl, const char *command_dir,
|
||||
struct executer **executerp)
|
||||
{
|
||||
struct executer *e;
|
||||
struct sigaction sa;
|
||||
|
||||
*executerp = NULL;
|
||||
if (signal_fds[0] == -1) {
|
||||
/* Make sure we can get a fd for /dev/null. */
|
||||
int null_fd = get_null_fd();
|
||||
if (null_fd < 0) {
|
||||
return -null_fd;
|
||||
}
|
||||
|
||||
/* Create pipe for notifying us that SIGCHLD was invoked. */
|
||||
if (pipe(signal_fds)) {
|
||||
VLOG_ERR("pipe failed: %s", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
set_nonblocking(signal_fds[0]);
|
||||
set_nonblocking(signal_fds[1]);
|
||||
}
|
||||
|
||||
/* Set up signal handler. */
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_handler = sigchld_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
|
||||
if (sigaction(SIGCHLD, &sa, NULL)) {
|
||||
VLOG_ERR("sigaction(SIGCHLD) failed: %s", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
e = xzalloc(sizeof *e);
|
||||
e->command_acl = xstrdup(command_acl);
|
||||
e->command_dir = (command_dir
|
||||
? xstrdup(command_dir)
|
||||
: xasprintf("%s/commands", ovs_pkgdatadir));
|
||||
e->n_children = 0;
|
||||
*executerp = e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
executer_destroy(struct executer *e)
|
||||
{
|
||||
if (e) {
|
||||
size_t i;
|
||||
|
||||
free(e->command_acl);
|
||||
free(e->command_dir);
|
||||
for (i = 0; i < e->n_children; i++) {
|
||||
struct child *child = &e->children[i];
|
||||
|
||||
free(child->name);
|
||||
kill(child->pid, SIGHUP);
|
||||
/* We don't own child->rconn. */
|
||||
free(child->output);
|
||||
free(child);
|
||||
}
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
executer_set_acl(struct executer *e, const char *acl, const char *dir)
|
||||
{
|
||||
free(e->command_acl);
|
||||
e->command_acl = xstrdup(acl);
|
||||
free(e->command_dir);
|
||||
e->command_dir = xstrdup(dir);
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Nicira Networks.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef EXECUTER_H
|
||||
#define EXECUTER_H 1
|
||||
|
||||
struct executer;
|
||||
struct nicira_header;
|
||||
struct rconn;
|
||||
|
||||
int executer_create(const char *acl, const char *dir, struct executer **);
|
||||
void executer_set_acl(struct executer *, const char *acl, const char *dir);
|
||||
void executer_destroy(struct executer *);
|
||||
void executer_run(struct executer *);
|
||||
void executer_wait(struct executer *);
|
||||
void executer_rconn_closing(struct executer *, struct rconn *);
|
||||
int executer_handle_request(struct executer *, struct rconn *,
|
||||
struct nicira_header *);
|
||||
|
||||
#endif /* executer.h */
|
@@ -27,7 +27,6 @@
|
||||
#include "discovery.h"
|
||||
#include "dpif.h"
|
||||
#include "dynamic-string.h"
|
||||
#include "executer.h"
|
||||
#include "fail-open.h"
|
||||
#include "in-band.h"
|
||||
#include "mac-learning.h"
|
||||
@@ -207,7 +206,6 @@ struct ofproto {
|
||||
struct discovery *discovery;
|
||||
struct fail_open *fail_open;
|
||||
struct pinsched *miss_sched, *action_sched;
|
||||
struct executer *executer;
|
||||
struct netflow *netflow;
|
||||
|
||||
/* Flow table. */
|
||||
@@ -314,7 +312,6 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
|
||||
p->discovery = NULL;
|
||||
p->fail_open = NULL;
|
||||
p->miss_sched = p->action_sched = NULL;
|
||||
p->executer = NULL;
|
||||
p->netflow = NULL;
|
||||
|
||||
/* Initialize flow table. */
|
||||
@@ -603,24 +600,6 @@ ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp)
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ofproto_set_remote_execution(struct ofproto *ofproto, const char *command_acl,
|
||||
const char *command_dir)
|
||||
{
|
||||
if (command_acl) {
|
||||
if (!ofproto->executer) {
|
||||
return executer_create(command_acl, command_dir,
|
||||
&ofproto->executer);
|
||||
} else {
|
||||
executer_set_acl(ofproto->executer, command_acl, command_dir);
|
||||
}
|
||||
} else {
|
||||
executer_destroy(ofproto->executer);
|
||||
ofproto->executer = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ofproto_get_datapath_id(const struct ofproto *ofproto)
|
||||
{
|
||||
@@ -716,7 +695,6 @@ ofproto_destroy(struct ofproto *p)
|
||||
fail_open_destroy(p->fail_open);
|
||||
pinsched_destroy(p->miss_sched);
|
||||
pinsched_destroy(p->action_sched);
|
||||
executer_destroy(p->executer);
|
||||
netflow_destroy(p->netflow);
|
||||
|
||||
switch_status_unregister(p->ss_cat);
|
||||
@@ -812,9 +790,6 @@ ofproto_run1(struct ofproto *p)
|
||||
}
|
||||
pinsched_run(p->miss_sched, send_packet_in_miss, p);
|
||||
pinsched_run(p->action_sched, send_packet_in_action, p);
|
||||
if (p->executer) {
|
||||
executer_run(p->executer);
|
||||
}
|
||||
|
||||
LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
|
||||
&p->all_conns) {
|
||||
@@ -923,9 +898,6 @@ ofproto_wait(struct ofproto *p)
|
||||
}
|
||||
pinsched_wait(p->miss_sched);
|
||||
pinsched_wait(p->action_sched);
|
||||
if (p->executer) {
|
||||
executer_wait(p->executer);
|
||||
}
|
||||
if (!tag_set_is_empty(&p->revalidate_set)) {
|
||||
poll_immediate_wake();
|
||||
}
|
||||
@@ -1330,10 +1302,6 @@ ofconn_create(struct ofproto *p, struct rconn *rconn)
|
||||
static void
|
||||
ofconn_destroy(struct ofconn *ofconn, struct ofproto *p)
|
||||
{
|
||||
if (p->executer) {
|
||||
executer_rconn_closing(p->executer, ofconn->rconn);
|
||||
}
|
||||
|
||||
list_remove(&ofconn->node);
|
||||
rconn_destroy(ofconn->rconn);
|
||||
rconn_packet_counter_destroy(ofconn->packet_in_counter);
|
||||
@@ -3030,12 +2998,6 @@ handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
|
||||
case NXT_ACT_GET_CONFIG:
|
||||
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
|
||||
|
||||
case NXT_COMMAND_REQUEST:
|
||||
if (p->executer) {
|
||||
return executer_handle_request(p->executer, ofconn->rconn, msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case NXT_MGMT:
|
||||
return handle_ofmp(p, ofconn, msg);
|
||||
}
|
||||
|
@@ -65,8 +65,6 @@ int ofproto_set_netflow(struct ofproto *,
|
||||
void ofproto_set_failure(struct ofproto *, bool fail_open);
|
||||
void ofproto_set_rate_limit(struct ofproto *, int rate_limit, int burst_limit);
|
||||
int ofproto_set_stp(struct ofproto *, bool enable_stp);
|
||||
int ofproto_set_remote_execution(struct ofproto *, const char *command_acl,
|
||||
const char *command_dir);
|
||||
|
||||
/* Configuration querying. */
|
||||
uint64_t ofproto_get_datapath_id(const struct ofproto *);
|
||||
|
@@ -164,15 +164,6 @@ flow expirations.
|
||||
This command may be useful for debugging switch or controller
|
||||
implementations.
|
||||
|
||||
.TP
|
||||
\fBexecute \fIswitch command \fR[\fIarg\fR...]
|
||||
Sends a request to \fIswitch\fR to execute \fIcommand\fR along with
|
||||
each \fIarg\fR, if any, then waits for the command to complete and
|
||||
reports its completion status on \fBstderr\fR and its output, if any,
|
||||
on \fBstdout\fR. The set of available commands and their argument is
|
||||
switch-dependent. (This command uses a Nicira extension to OpenFlow
|
||||
that may not be available on all switches.)
|
||||
|
||||
.SS "OpenFlow Switch and Controller Commands"
|
||||
|
||||
The following commands, like those in the previous section, may be
|
||||
|
@@ -164,7 +164,6 @@ usage(void)
|
||||
" mod-flows SWITCH FLOW modify actions of matching FLOWs\n"
|
||||
" del-flows SWITCH [FLOW] delete matching FLOWs\n"
|
||||
" monitor SWITCH MISSLEN EXP print packets received from SWITCH\n"
|
||||
" execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
|
||||
"\nFor OpenFlow switches and controllers:\n"
|
||||
" probe VCONN probe whether VCONN is up\n"
|
||||
" ping VCONN [N] latency of N-byte echos\n"
|
||||
@@ -1163,71 +1162,6 @@ do_benchmark(int argc UNUSED, char *argv[])
|
||||
count * message_size / (duration / 1000.0));
|
||||
}
|
||||
|
||||
static void
|
||||
do_execute(int argc, char *argv[])
|
||||
{
|
||||
struct vconn *vconn;
|
||||
struct ofpbuf *request;
|
||||
struct nicira_header *nicira;
|
||||
struct nx_command_reply *ncr;
|
||||
uint32_t xid;
|
||||
int i;
|
||||
|
||||
nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
|
||||
xid = nicira->header.xid;
|
||||
nicira->vendor = htonl(NX_VENDOR_ID);
|
||||
nicira->subtype = htonl(NXT_COMMAND_REQUEST);
|
||||
ofpbuf_put(request, argv[2], strlen(argv[2]));
|
||||
for (i = 3; i < argc; i++) {
|
||||
ofpbuf_put_zeros(request, 1);
|
||||
ofpbuf_put(request, argv[i], strlen(argv[i]));
|
||||
}
|
||||
update_openflow_length(request);
|
||||
|
||||
open_vconn(argv[1], &vconn);
|
||||
run(vconn_send_block(vconn, request), "send");
|
||||
|
||||
for (;;) {
|
||||
struct ofpbuf *reply;
|
||||
uint32_t status;
|
||||
|
||||
run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
|
||||
if (reply->size < sizeof *ncr) {
|
||||
ovs_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
|
||||
reply->size, sizeof *ncr);
|
||||
}
|
||||
ncr = reply->data;
|
||||
if (ncr->nxh.header.type != OFPT_VENDOR
|
||||
|| ncr->nxh.vendor != htonl(NX_VENDOR_ID)
|
||||
|| ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
|
||||
ovs_fatal(0, "reply is invalid");
|
||||
}
|
||||
|
||||
status = ntohl(ncr->status);
|
||||
if (status & NXT_STATUS_STARTED) {
|
||||
/* Wait for a second reply. */
|
||||
continue;
|
||||
} else if (status & NXT_STATUS_EXITED) {
|
||||
fprintf(stderr, "process terminated normally with exit code %d",
|
||||
status & NXT_STATUS_EXITSTATUS);
|
||||
} else if (status & NXT_STATUS_SIGNALED) {
|
||||
fprintf(stderr, "process terminated by signal %d",
|
||||
status & NXT_STATUS_TERMSIG);
|
||||
} else if (status & NXT_STATUS_ERROR) {
|
||||
fprintf(stderr, "error executing command");
|
||||
} else {
|
||||
fprintf(stderr, "process terminated for unknown reason");
|
||||
}
|
||||
if (status & NXT_STATUS_COREDUMP) {
|
||||
fprintf(stderr, " (core dumped)");
|
||||
}
|
||||
putc('\n', stderr);
|
||||
|
||||
fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_help(int argc UNUSED, char *argv[] UNUSED)
|
||||
{
|
||||
@@ -1251,7 +1185,6 @@ static const struct command all_commands[] = {
|
||||
{ "probe", 1, 1, do_probe },
|
||||
{ "ping", 1, 2, do_ping },
|
||||
{ "benchmark", 3, 3, do_benchmark },
|
||||
{ "execute", 2, INT_MAX, do_execute },
|
||||
{ "help", 0, INT_MAX, do_help },
|
||||
{ NULL, 0, 0, NULL },
|
||||
};
|
||||
|
@@ -388,34 +388,6 @@ specified on \fB--rate-limit\fR.
|
||||
|
||||
This option takes effect only when \fB--rate-limit\fR is also specified.
|
||||
|
||||
.SS "Remote Command Execution Options"
|
||||
|
||||
.TP
|
||||
\fB--command-acl=\fR[\fB!\fR]\fIglob\fR[\fB,\fR[\fB!\fR]\fIglob\fR...]
|
||||
Configures the commands that remote OpenFlow connections are allowed
|
||||
to invoke using (e.g.) \fBovs\-ofctl execute\fR. The argument is a
|
||||
comma-separated sequence of shell glob patterns. A glob pattern
|
||||
specified without a leading \fB!\fR is a ``whitelist'' that specifies
|
||||
a set of commands that are that may be invoked, whereas a pattern that
|
||||
does begin with \fB!\fR is a ``blacklist'' that specifies commands
|
||||
that may not be invoked. To be permitted, a command name must be
|
||||
whitelisted and must not be blacklisted;
|
||||
e.g. \fB--command-acl=up*,!upgrade\fR would allow any command whose name
|
||||
begins with \fBup\fR except for the command named \fBupgrade\fR.
|
||||
Command names that include characters other than upper- and lower-case
|
||||
English letters, digits, and the underscore and hyphen characters are
|
||||
unconditionally disallowed.
|
||||
|
||||
When the whitelist and blacklist permit a command name, \fBovs\-openflowd\fR
|
||||
looks for a program with the same name as the command in the commands
|
||||
directory (see below). Other directories are not searched.
|
||||
|
||||
.TP
|
||||
\fB--command-dir=\fIdirectory\fR
|
||||
Sets the directory searched for remote command execution to
|
||||
\fBdirectory\fR. The default directory is
|
||||
\fB@pkgdatadir@/commands\fR.
|
||||
|
||||
.SS "Datapath Options"
|
||||
.
|
||||
.IP "\fB\-\-ports=\fIport\fR[\fB,\fIport\fR...]"
|
||||
|
@@ -94,10 +94,6 @@ struct ofsettings {
|
||||
/* Spanning tree protocol. */
|
||||
bool enable_stp;
|
||||
|
||||
/* Remote command execution. */
|
||||
char *command_acl; /* Command white/blacklist, as shell globs. */
|
||||
char *command_dir; /* Directory that contains commands. */
|
||||
|
||||
/* Management. */
|
||||
uint64_t mgmt_id; /* Management ID. */
|
||||
|
||||
@@ -206,11 +202,6 @@ main(int argc, char *argv[])
|
||||
if (error) {
|
||||
ovs_fatal(error, "failed to configure STP");
|
||||
}
|
||||
error = ofproto_set_remote_execution(ofproto, s.command_acl,
|
||||
s.command_dir);
|
||||
if (error) {
|
||||
ovs_fatal(error, "failed to configure remote command execution");
|
||||
}
|
||||
if (!s.discovery) {
|
||||
error = ofproto_set_controller(ofproto, s.controller_name);
|
||||
if (error) {
|
||||
@@ -265,8 +256,6 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
|
||||
OPT_NO_STP,
|
||||
OPT_OUT_OF_BAND,
|
||||
OPT_IN_BAND,
|
||||
OPT_COMMAND_ACL,
|
||||
OPT_COMMAND_DIR,
|
||||
OPT_NETFLOW,
|
||||
OPT_MGMT_ID,
|
||||
OPT_PORTS,
|
||||
@@ -295,8 +284,6 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
|
||||
{"no-stp", no_argument, 0, OPT_NO_STP},
|
||||
{"out-of-band", no_argument, 0, OPT_OUT_OF_BAND},
|
||||
{"in-band", no_argument, 0, OPT_IN_BAND},
|
||||
{"command-acl", required_argument, 0, OPT_COMMAND_ACL},
|
||||
{"command-dir", required_argument, 0, OPT_COMMAND_DIR},
|
||||
{"netflow", required_argument, 0, OPT_NETFLOW},
|
||||
{"mgmt-id", required_argument, 0, OPT_MGMT_ID},
|
||||
{"ports", required_argument, 0, OPT_PORTS},
|
||||
@@ -332,8 +319,6 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
|
||||
s->accept_controller_re = NULL;
|
||||
s->enable_stp = false;
|
||||
s->in_band = true;
|
||||
s->command_acl = "";
|
||||
s->command_dir = NULL;
|
||||
svec_init(&s->netflow);
|
||||
s->mgmt_id = 0;
|
||||
svec_init(&s->ports);
|
||||
@@ -449,16 +434,6 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
|
||||
s->in_band = true;
|
||||
break;
|
||||
|
||||
case OPT_COMMAND_ACL:
|
||||
s->command_acl = (s->command_acl[0]
|
||||
? xasprintf("%s,%s", s->command_acl, optarg)
|
||||
: optarg);
|
||||
break;
|
||||
|
||||
case OPT_COMMAND_DIR:
|
||||
s->command_dir = optarg;
|
||||
break;
|
||||
|
||||
case OPT_NETFLOW:
|
||||
svec_add(&s->netflow, optarg);
|
||||
break;
|
||||
@@ -584,10 +559,7 @@ usage(void)
|
||||
" --netflow=HOST:PORT configure NetFlow output target\n"
|
||||
"\nRate-limiting of \"packet-in\" messages to the controller:\n"
|
||||
" --rate-limit[=PACKETS] max rate, in packets/s (default: 1000)\n"
|
||||
" --burst-limit=BURST limit on packet credit for idle time\n"
|
||||
"\nRemote command execution options:\n"
|
||||
" --command-acl=[!]GLOB[,[!]GLOB...] set allowed/denied commands\n"
|
||||
" --command-dir=DIR set command dir (default: %s/commands)\n",
|
||||
" --burst-limit=BURST limit on packet credit for idle time\n",
|
||||
ovs_pkgdatadir);
|
||||
daemon_usage();
|
||||
vlog_usage();
|
||||
|
@@ -1456,8 +1456,6 @@ bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg,
|
||||
rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0;
|
||||
burst_limit = c->controller_burst_limit ? *c->controller_burst_limit : 0;
|
||||
ofproto_set_rate_limit(br->ofproto, rate_limit, burst_limit);
|
||||
|
||||
ofproto_set_remote_execution(br->ofproto, NULL, NULL); /* XXX */
|
||||
} else {
|
||||
union ofp_action action;
|
||||
flow_t flow;
|
||||
|
Reference in New Issue
Block a user