2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-13 14:07:02 +00:00
Files
openvswitch/utilities/ovs-openflowd.c
Ben Pfaff 8274ae959a daemon: Define daemon options enums the same way as other option enums.
Other modules that accept options use this style and I don't see a reason
for the daemon code to be different.  The style used by the daemon code
until now runs the risk of ending up with conflicting values accidentally,
which would be confusing.
2011-02-07 12:50:19 -08:00

574 lines
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2008, 2009, 2010, 2011 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 <assert.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "command-line.h"
#include "compiler.h"
#include "daemon.h"
#include "dirs.h"
#include "dpif.h"
#include "dummy.h"
#include "leak-checker.h"
#include "list.h"
#include "netdev.h"
#include "ofpbuf.h"
#include "ofproto/ofproto.h"
#include "openflow/openflow.h"
#include "packets.h"
#include "poll-loop.h"
#include "rconn.h"
#include "stream-ssl.h"
#include "svec.h"
#include "timeval.h"
#include "unixctl.h"
#include "util.h"
#include "vconn.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(openflowd);
/* Settings that may be configured by the user. */
struct ofsettings {
const char *unixctl_path; /* File name for unixctl socket. */
/* Controller configuration. */
struct ofproto_controller *controllers;
size_t n_controllers;
enum ofproto_fail_mode fail_mode;
bool run_forever; /* Continue running even with no controller? */
/* Datapath. */
uint64_t datapath_id; /* Datapath ID. */
char *dp_name; /* Name of local datapath. */
char *dp_type; /* Type of local datapath. */
struct svec ports; /* Set of ports to add to datapath (if any). */
/* Description strings. */
const char *mfr_desc; /* Manufacturer. */
const char *hw_desc; /* Hardware. */
const char *sw_desc; /* Software version. */
const char *serial_desc; /* Serial number. */
const char *dp_desc; /* Datapath description. */
/* Related vconns and network devices. */
struct svec snoops; /* Listen for controller snooping conns. */
/* Failure behavior. */
int max_idle; /* Idle time for flows in fail-open mode. */
/* NetFlow. */
struct svec netflow; /* NetFlow targets. */
};
static unixctl_cb_func ovs_openflowd_exit;
static void parse_options(int argc, char *argv[], struct ofsettings *);
static void usage(void) NO_RETURN;
int
main(int argc, char *argv[])
{
struct unixctl_server *unixctl;
struct ofproto *ofproto;
struct ofsettings s;
int error;
struct dpif *dpif;
struct netflow_options nf_options;
bool exiting;
proctitle_init(argc, argv);
set_program_name(argv[0]);
parse_options(argc, argv, &s);
signal(SIGPIPE, SIG_IGN);
die_if_already_running();
daemonize_start();
/* Start listening for ovs-appctl requests. */
error = unixctl_server_create(s.unixctl_path, &unixctl);
if (error) {
exit(EXIT_FAILURE);
}
unixctl_command_register("exit", ovs_openflowd_exit, &exiting);
VLOG_INFO("Open vSwitch version %s", VERSION BUILDNR);
VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION);
error = dpif_create_and_open(s.dp_name, s.dp_type, &dpif);
if (error) {
ovs_fatal(error, "could not create datapath");
}
/* Add ports to the datapath if requested by the user. */
if (s.ports.n) {
const char *port;
size_t i;
SVEC_FOR_EACH (i, port, &s.ports) {
struct netdev *netdev;
error = netdev_open_default(port, &netdev);
if (error) {
ovs_fatal(error, "%s: failed to open network device", port);
}
error = dpif_port_add(dpif, netdev, NULL);
if (error) {
ovs_fatal(error, "failed to add %s as a port", port);
}
netdev_close(netdev);
}
}
/* Start OpenFlow processing. */
error = ofproto_create(s.dp_name, s.dp_type, NULL, NULL, &ofproto);
if (error) {
ovs_fatal(error, "could not initialize openflow switch");
}
if (s.datapath_id) {
ofproto_set_datapath_id(ofproto, s.datapath_id);
}
ofproto_set_desc(ofproto, s.mfr_desc, s.hw_desc, s.sw_desc,
s.serial_desc, s.dp_desc);
error = ofproto_set_snoops(ofproto, &s.snoops);
if (error) {
ovs_fatal(error,
"failed to configure controller snooping connections");
}
memset(&nf_options, 0, sizeof nf_options);
nf_options.collectors = s.netflow;
error = ofproto_set_netflow(ofproto, &nf_options);
if (error) {
ovs_fatal(error, "failed to configure NetFlow collectors");
}
ofproto_set_controllers(ofproto, s.controllers, s.n_controllers);
ofproto_set_fail_mode(ofproto, s.fail_mode);
daemonize_complete();
exiting = false;
while (!exiting && (s.run_forever || ofproto_is_alive(ofproto))) {
error = ofproto_run(ofproto);
if (error) {
ovs_fatal(error, "unrecoverable datapath error");
}
unixctl_server_run(unixctl);
dp_run();
netdev_run();
ofproto_wait(ofproto);
unixctl_server_wait(unixctl);
dp_wait();
netdev_wait();
if (exiting) {
poll_immediate_wake();
}
poll_block();
}
dpif_close(dpif);
return 0;
}
static void
ovs_openflowd_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED,
void *exiting_)
{
bool *exiting = exiting_;
*exiting = true;
unixctl_command_reply(conn, 200, NULL);
}
/* User interface. */
static void
parse_options(int argc, char *argv[], struct ofsettings *s)
{
enum {
OPT_DATAPATH_ID = UCHAR_MAX + 1,
OPT_MFR_DESC,
OPT_HW_DESC,
OPT_SW_DESC,
OPT_SERIAL_DESC,
OPT_DP_DESC,
OPT_ACCEPT_VCONN,
OPT_NO_RESOLV_CONF,
OPT_BR_NAME,
OPT_FAIL_MODE,
OPT_INACTIVITY_PROBE,
OPT_MAX_IDLE,
OPT_MAX_BACKOFF,
OPT_SNOOP,
OPT_RATE_LIMIT,
OPT_BURST_LIMIT,
OPT_BOOTSTRAP_CA_CERT,
OPT_OUT_OF_BAND,
OPT_IN_BAND,
OPT_NETFLOW,
OPT_PORTS,
OPT_UNIXCTL,
OPT_ENABLE_DUMMY,
VLOG_OPTION_ENUMS,
LEAK_CHECKER_OPTION_ENUMS,
DAEMON_OPTION_ENUMS
};
static struct option long_options[] = {
{"datapath-id", required_argument, 0, OPT_DATAPATH_ID},
{"mfr-desc", required_argument, 0, OPT_MFR_DESC},
{"hw-desc", required_argument, 0, OPT_HW_DESC},
{"sw-desc", required_argument, 0, OPT_SW_DESC},
{"serial-desc", required_argument, 0, OPT_SERIAL_DESC},
{"dp-desc", required_argument, 0, OPT_DP_DESC},
{"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
{"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
{"config", required_argument, 0, 'F'},
{"br-name", required_argument, 0, OPT_BR_NAME},
{"fail", required_argument, 0, OPT_FAIL_MODE},
{"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
{"max-idle", required_argument, 0, OPT_MAX_IDLE},
{"max-backoff", required_argument, 0, OPT_MAX_BACKOFF},
{"listen", required_argument, 0, 'l'},
{"snoop", required_argument, 0, OPT_SNOOP},
{"rate-limit", optional_argument, 0, OPT_RATE_LIMIT},
{"burst-limit", required_argument, 0, OPT_BURST_LIMIT},
{"out-of-band", no_argument, 0, OPT_OUT_OF_BAND},
{"in-band", no_argument, 0, OPT_IN_BAND},
{"netflow", required_argument, 0, OPT_NETFLOW},
{"ports", required_argument, 0, OPT_PORTS},
{"unixctl", required_argument, 0, OPT_UNIXCTL},
{"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY},
{"verbose", optional_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
LEAK_CHECKER_LONG_OPTIONS,
#ifdef HAVE_OPENSSL
STREAM_SSL_LONG_OPTIONS
{"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
#endif
{0, 0, 0, 0},
};
char *short_options = long_options_to_short_options(long_options);
struct ofproto_controller controller_opts;
struct svec controllers;
int i;
/* Set defaults that we can figure out before parsing options. */
controller_opts.target = NULL;
controller_opts.max_backoff = 8;
controller_opts.probe_interval = 5;
controller_opts.band = OFPROTO_IN_BAND;
controller_opts.accept_re = NULL;
controller_opts.update_resolv_conf = true;
controller_opts.rate_limit = 0;
controller_opts.burst_limit = 0;
s->unixctl_path = NULL;
s->fail_mode = OFPROTO_FAIL_STANDALONE;
s->datapath_id = 0;
s->mfr_desc = NULL;
s->hw_desc = NULL;
s->sw_desc = NULL;
s->serial_desc = NULL;
s->dp_desc = NULL;
svec_init(&controllers);
svec_init(&s->snoops);
s->max_idle = 0;
svec_init(&s->netflow);
svec_init(&s->ports);
for (;;) {
int c;
c = getopt_long(argc, argv, short_options, long_options, NULL);
if (c == -1) {
break;
}
switch (c) {
case OPT_DATAPATH_ID:
if (!dpid_from_string(optarg, &s->datapath_id)) {
ovs_fatal(0, "argument to --datapath-id must be "
"exactly 16 hex digits and may not be all-zero");
}
break;
case OPT_MFR_DESC:
s->mfr_desc = optarg;
break;
case OPT_HW_DESC:
s->hw_desc = optarg;
break;
case OPT_SW_DESC:
s->sw_desc = optarg;
break;
case OPT_SERIAL_DESC:
s->serial_desc = optarg;
break;
case OPT_DP_DESC:
s->dp_desc = optarg;
break;
case OPT_ACCEPT_VCONN:
controller_opts.accept_re = optarg;
break;
case OPT_NO_RESOLV_CONF:
controller_opts.update_resolv_conf = false;
break;
case OPT_FAIL_MODE:
if (!strcmp(optarg, "open") || !strcmp(optarg, "standalone")) {
s->fail_mode = OFPROTO_FAIL_STANDALONE;
} else if (!strcmp(optarg, "closed")
|| !strcmp(optarg, "secure")) {
s->fail_mode = OFPROTO_FAIL_SECURE;
} else {
ovs_fatal(0, "--fail argument must be \"standalone\" "
"or \"secure\"");
}
break;
case OPT_INACTIVITY_PROBE:
controller_opts.probe_interval = atoi(optarg);
if (controller_opts.probe_interval < 5) {
ovs_fatal(0, "--inactivity-probe argument must be at least 5");
}
break;
case OPT_MAX_IDLE:
if (!strcmp(optarg, "permanent")) {
s->max_idle = OFP_FLOW_PERMANENT;
} else {
s->max_idle = atoi(optarg);
if (s->max_idle < 1 || s->max_idle > 65535) {
ovs_fatal(0, "--max-idle argument must be between 1 and "
"65535 or the word 'permanent'");
}
}
break;
case OPT_MAX_BACKOFF:
controller_opts.max_backoff = atoi(optarg);
if (controller_opts.max_backoff < 1) {
ovs_fatal(0, "--max-backoff argument must be at least 1");
} else if (controller_opts.max_backoff > 3600) {
controller_opts.max_backoff = 3600;
}
break;
case OPT_RATE_LIMIT:
if (optarg) {
controller_opts.rate_limit = atoi(optarg);
if (controller_opts.rate_limit < 1) {
ovs_fatal(0, "--rate-limit argument must be at least 1");
}
} else {
controller_opts.rate_limit = 1000;
}
break;
case OPT_BURST_LIMIT:
controller_opts.burst_limit = atoi(optarg);
if (controller_opts.burst_limit < 1) {
ovs_fatal(0, "--burst-limit argument must be at least 1");
}
break;
case OPT_OUT_OF_BAND:
controller_opts.band = OFPROTO_OUT_OF_BAND;
break;
case OPT_IN_BAND:
controller_opts.band = OFPROTO_IN_BAND;
break;
case OPT_NETFLOW:
svec_add(&s->netflow, optarg);
break;
case 'l':
svec_add(&controllers, optarg);
break;
case OPT_SNOOP:
svec_add(&s->snoops, optarg);
break;
case OPT_PORTS:
svec_split(&s->ports, optarg, ",");
break;
case OPT_UNIXCTL:
s->unixctl_path = optarg;
break;
case OPT_ENABLE_DUMMY:
dummy_enable();
break;
case 'h':
usage();
case 'V':
OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION);
exit(EXIT_SUCCESS);
DAEMON_OPTION_HANDLERS
VLOG_OPTION_HANDLERS
LEAK_CHECKER_OPTION_HANDLERS
#ifdef HAVE_OPENSSL
STREAM_SSL_OPTION_HANDLERS
case OPT_BOOTSTRAP_CA_CERT:
stream_ssl_set_ca_cert_file(optarg, true);
break;
#endif
case '?':
exit(EXIT_FAILURE);
default:
abort();
}
}
free(short_options);
argc -= optind;
argv += optind;
if (argc < 1) {
ovs_fatal(0, "need at least one non-option arguments; "
"use --help for usage");
}
/* Set accept_controller_regex. */
if (!controller_opts.accept_re) {
controller_opts.accept_re
= stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*";
}
/* Rate limiting. */
if (controller_opts.rate_limit && controller_opts.rate_limit < 100) {
VLOG_WARN("Rate limit set to unusually low value %d",
controller_opts.rate_limit);
}
/* Local vconns. */
dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
/* Figure out controller names. */
s->run_forever = false;
if (!controllers.n) {
svec_add_nocopy(&controllers, xasprintf("punix:%s/%s.mgmt",
ovs_rundir(), s->dp_name));
}
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "none")) {
s->run_forever = true;
} else {
svec_add(&controllers, argv[i]);
}
}
if (argc < 2) {
svec_add(&controllers, "discover");
}
/* Set up controllers. */
s->n_controllers = controllers.n;
s->controllers = xmalloc(s->n_controllers * sizeof *s->controllers);
for (i = 0; i < s->n_controllers; i++) {
s->controllers[i] = controller_opts;
s->controllers[i].target = controllers.names[i];
}
/* Sanity check. */
if (controller_opts.band == OFPROTO_OUT_OF_BAND) {
for (i = 0; i < s->n_controllers; i++) {
if (!strcmp(s->controllers[i].target, "discover")) {
ovs_fatal(0, "Cannot perform discovery with out-of-band "
"control");
}
}
}
}
static void
usage(void)
{
printf("%s: an OpenFlow switch implementation.\n"
"usage: %s [OPTIONS] [TYPE@]DATAPATH [CONTROLLER...]\n"
"where DATAPATH is a local datapath (e.g. \"dp0\")\n"
"optionally with an explicit TYPE (default: \"system\").\n"
"Each CONTROLLER is an active OpenFlow connection method. If\n"
"none is given, ovs-openflowd performs controller discovery.\n",
program_name, program_name);
vconn_usage(true, true, true);
printf("\nOpenFlow options:\n"
" -d, --datapath-id=ID Use ID as the OpenFlow switch ID\n"
" (ID must consist of 16 hex digits)\n"
" --mfr-desc=MFR Identify manufacturer as MFR\n"
" --hw-desc=HW Identify hardware as HW\n"
" --sw-desc=SW Identify software as SW\n"
" --serial-desc=SERIAL Identify serial number as SERIAL\n"
" --dp-desc=DP_DESC Identify dp description as DP_DESC\n"
"\nController discovery options:\n"
" --accept-vconn=REGEX accept matching discovered controllers\n"
" --no-resolv-conf do not update /etc/resolv.conf\n"
"\nNetworking options:\n"
" --fail=open|closed when controller connection fails:\n"
" closed: drop all packets\n"
" open (default): act as learning switch\n"
" --inactivity-probe=SECS time between inactivity probes\n"
" --max-idle=SECS max idle for flows set up by switch\n"
" --max-backoff=SECS max time between controller connection\n"
" attempts (default: 8 seconds)\n"
" -l, --listen=METHOD allow management connections on METHOD\n"
" (a passive OpenFlow connection method)\n"
" --snoop=METHOD allow controller snooping on METHOD\n"
" (a passive OpenFlow connection method)\n"
" --out-of-band controller connection is out-of-band\n"
" --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");
daemon_usage();
vlog_usage();
printf("\nOther options:\n"
" --unixctl=SOCKET override default control socket name\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
leak_checker_usage();
exit(EXIT_SUCCESS);
}