Type: string
_Description: Controller location:
Specify how the OpenFlow switch should connect to the OpenFlow controller.
- The value should be in form "ssl:HOST[:PORT]" to connect to the controller
- over SSL (recommended for security) or "tcp:HOST[:PORT]" to connect over
+ The value should be in form "ssl:IP[:PORT]" to connect to the controller
+ over SSL (recommended for security) or "tcp:IP[:PORT]" to connect over
cleartext TCP.
Template: openvswitch-switch/controller-vconn-error
Type: error
_Description: The controller location is invalid.
- The controller location must be specifed as "ssl:HOST[:PORT]" to
+ The controller location must be specifed as "ssl:IP[:PORT]" to
connect to the controller over SSL (recommended for security) or
- "tcp:HOST[:PORT]" to connect over cleartext TCP.
+ "tcp:IP[:PORT]" to connect over cleartext TCP.
Template: openvswitch-switch/pki-uri
Type: string
configure_ssl
;;
*)
- echo "$default: CONTROLLER must be in the form 'ssl:HOST[:PORT]' or 'tcp:HOST[:PORT]' when not in discovery mode" >&2
+ echo "$default: CONTROLLER must be in the form 'ssl:IP[:PORT]' or 'tcp:IP[:PORT]' when not in discovery mode" >&2
echo "Run ovs-switch-setup (in the openvswitch-switch-config package) or edit /etc/default/openvswitch-switch to configure" >&2
exit 1
esac
# CONTROLLER: Location of controller.
# One of the following formats:
-# tcp:HOST[:PORT] via TCP to PORT (default: 6633) on HOST
-# ssl:HOST[:PORT] via SSL to PORT (default: 6633) on HOST
+# tcp:IP[:PORT] via TCP to PORT (default: 6633) at IP
+# ssl:IP[:PORT] via SSL to PORT (default: 6633) at IP
# The default below assumes that the controller is running locally.
# This setting has no effect when MODE is set to 'discovery'.
#CONTROLLER="tcp:127.0.0.1"
#: ../openvswitch-switch-config.templates:10001
msgid ""
"Specify how the OpenFlow switch should connect to the OpenFlow controller. "
-"The value should be in form \"ssl:HOST[:PORT]\" to connect to the controller "
-"over SSL (recommended for security) or \"tcp:HOST[:PORT]\" to connect over "
+"The value should be in form \"ssl:IP[:PORT]\" to connect to the controller "
+"over SSL (recommended for security) or \"tcp:IP[:PORT]\" to connect over "
"cleartext TCP."
msgstr ""
#. Description
#: ../openvswitch-switch-config.templates:11001
msgid ""
-"The controller location must be specifed as \"ssl:HOST[:PORT]\" to connect "
-"to the controller over SSL (recommended for security) or \"tcp:HOST[:PORT]\" "
+"The controller location must be specifed as \"ssl:IP[:PORT]\" to connect "
+"to the controller over SSL (recommended for security) or \"tcp:IP[:PORT]\" "
"to connect over cleartext TCP."
msgstr ""
free(pattern);
}
+/* Fills 'svec' with all of the key-value pairs that match shell glob pattern
+ * 'pattern'. The caller must first initialize 'svec'. */
+void
+cfg_get_matches(struct svec *svec, const char *pattern_, ...)
+{
+ char *pattern;
+ char **p;
+
+ FORMAT_KEY(pattern_, pattern);
+
+ for (p = cfg.names; *p; p++) {
+ if (!fnmatch(pattern, *p, 0)) {
+ svec_add(svec, *p);
+ }
+ }
+
+ free(pattern);
+}
+
/* Fills 'svec' with all of the key-value pairs that have sections that
* begin with 'section'. The caller must first initialize 'svec'. */
void
void cfg_del_entry(const char *key, ...) PRINTF_FORMAT(1, 2);
void cfg_del_section(const char *key, ...) PRINTF_FORMAT(1, 2);
void cfg_del_match(const char *pattern, ...) PRINTF_FORMAT(1, 2);
+void cfg_get_matches(struct svec *svec, const char *pattern, ...)
+ PRINTF_FORMAT(2, 3);
void cfg_get_section(struct svec *svec, const char *key, ...)
PRINTF_FORMAT(2, 3);
return max_fds;
}
-/* Translates 'host_name', which may be a DNS name or an IP address, into a
- * numeric IP address in '*addr'. Returns 0 if successful, otherwise a
- * positive errno value. */
+/* Translates 'host_name', which must be a string representation of an IP
+ * address, into a numeric IP address in '*addr'. Returns 0 if successful,
+ * otherwise a positive errno value. */
int
lookup_ip(const char *host_name, struct in_addr *addr)
{
if (!inet_aton(host_name, addr)) {
- struct hostent *he = gethostbyname(host_name);
- if (he == NULL) {
- struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_ERR_RL(&rl, "gethostbyname(%s): %s", host_name,
- (h_errno == HOST_NOT_FOUND ? "host not found"
- : h_errno == TRY_AGAIN ? "try again"
- : h_errno == NO_RECOVERY ? "non-recoverable error"
- : h_errno == NO_ADDRESS ? "no address"
- : "unknown error"));
- return ENOENT;
- }
- addr->s_addr = *(uint32_t *) he->h_addr;
+ struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name);
+ return ENOENT;
}
return 0;
}
printf("\n");
if (active) {
printf("Active OpenFlow connection methods:\n");
- printf(" tcp:HOST[:PORT] "
- "PORT (default: %d) on remote TCP HOST\n", OFP_TCP_PORT);
+ printf(" tcp:IP[:PORT] "
+ "PORT (default: %d) at remote IP\n", OFP_TCP_PORT);
#ifdef HAVE_OPENSSL
- printf(" ssl:HOST[:PORT] "
- "SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT);
+ printf(" ssl:IP[:PORT] "
+ "SSL PORT (default: %d) at remote IP\n", OFP_SSL_PORT);
#endif
printf(" unix:FILE Unix domain socket named FILE\n");
}
Listens for connections from OpenFlow switches on the Unix domain
server socket named \fIfile\fR.
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR. The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
-
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+options are mandatory when this form is used.
+
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
.TP
\fBunix:\fIfile\fR
are supported:
.RS
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR. The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
-
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+options are mandatory when this form is used.
+
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
.TP
\fBunix:\fIfile\fR
the OpenFlow controller. It takes one of the following forms:
.RS
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR. The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+options are mandatory when this form is used.
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
.TP
\fBunix:\fIfile\fR
is given, the default is in-band control.
.TP
-\fB--netflow=\fIhost\fB:\fIport\fR
-Configures the given UDP \fIport\fR on the specified IP \fIhost\fR as
-a recipient of NetFlow messages for expired flows.
+\fB--netflow=\fIip\fB:\fIport\fR
+Configures the given UDP \fIport\fR on the specified IP \fIip\fR as
+a recipient of NetFlow messages for expired flows. The \fIip\fR must
+be specified numerically, not as a DNS name.
This option may be specified multiple times to configure additional
NetFlow collectors.
if (br->ml) {
const struct mac_entry *e;
LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+ if (e->port < 0 || e->port >= br->n_ports) {
+ continue;
+ }
ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n",
- e->port, e->vlan, ETH_ADDR_ARGS(e->mac),
- mac_entry_age(e));
+ br->ports[e->port]->ifaces[0]->dp_ifidx,
+ e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
}
}
unixctl_command_reply(conn, 200, ds_cstr(&ds));
}
/* Get all the interfaces for 'bridge' as 'ifaces', breaking bonded interfaces
- * down into their constituent parts. */
+ * down into their constituent parts.
+ *
+ * If 'vlan' < 0, all interfaces on 'bridge' are reported. If 'vlan' == 0,
+ * then only interfaces for trunk ports or ports with implicit VLAN 0 are
+ * reported. If 'vlan' > 0, only interfaces with implict VLAN 'vlan' are
+ * reported. */
static void
-get_bridge_ifaces(const char *bridge, struct svec *ifaces)
+get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan)
{
struct svec ports;
int i;
cfg_get_all_keys(&ports, "bridge.%s.port", bridge);
for (i = 0; i < ports.n; i++) {
const char *port_name = ports.names[i];
+ if (vlan >= 0) {
+ int port_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name);
+ if (port_vlan < 0) {
+ port_vlan = 0;
+ }
+ if (vlan != port_vlan) {
+ continue;
+ }
+ }
if (cfg_has_section("bonding.%s", port_name)) {
struct svec slaves;
svec_init(&slaves);
struct svec ifaces;
/* Check that each bridge interface exists. */
- get_bridge_ifaces(br_name, &ifaces);
+ get_bridge_ifaces(br_name, &ifaces, -1);
for (j = 0; j < ifaces.n; j++) {
const char *iface_name = ifaces.names[j];
enum netdev_flags flags;
return error;
}
+/* Returns the name of the bridge that contains a port named 'port_name', as a
+ * malloc'd string that the caller must free, or a null pointer if no bridge
+ * contains a port named 'port_name'. */
+static char *
+get_bridge_containing_port(const char *port_name)
+{
+ struct svec matches;
+ const char *start, *end;
+
+ svec_init(&matches);
+ cfg_get_matches(&matches, "bridge.*.port=%s", port_name);
+ if (!matches.n) {
+ return 0;
+ }
+
+ start = matches.names[0] + strlen("bridge.");
+ end = strstr(start, ".port=");
+ assert(end);
+ return xmemdup0(start, end - start);
+}
+
static int
handle_fdb_query_cmd(struct ofpbuf *buffer)
{
int n_local_macs;
int i;
+ /* Impedance matching between the vswitchd and Linux kernel notions of what
+ * a bridge is. The kernel only handles a single VLAN per bridge, but
+ * vswitchd can deal with all the VLANs on a single bridge. We have to
+ * pretend that the former is the case even though the latter is the
+ * implementation. */
+ const char *linux_bridge; /* Name used by brctl. */
+ char *ovs_bridge; /* Name used by ovs-vswitchd. */
+ int br_vlan; /* VLAN tag. */
+ struct svec ifaces;
+
struct ofpbuf query_data;
char *unixctl_command;
uint64_t count, skip;
- const char *br_name;
- struct svec ifaces;
char *output;
char *save_ptr;
uint32_t seq;
int error;
/* Parse the command received from brcompat_mod. */
- error = parse_command(buffer, &seq, &br_name, NULL, &count, &skip);
+ error = parse_command(buffer, &seq, &linux_bridge, NULL, &count, &skip);
if (error) {
return error;
}
+ /* Figure out vswitchd bridge and VLAN. */
+ cfg_read();
+ if (bridge_exists(linux_bridge)) {
+ /* Bridge name is the same. We are interested in VLAN 0. */
+ ovs_bridge = xstrdup(linux_bridge);
+ br_vlan = 0;
+ } else {
+ /* No such Open vSwitch bridge 'linux_bridge', but there might be an
+ * internal port named 'linux_bridge' on some other bridge
+ * 'ovs_bridge'. If so then we are interested in the VLAN assigned to
+ * port 'linux_bridge' on the bridge named 'ovs_bridge'. */
+ const char *port_name = linux_bridge;
+
+ ovs_bridge = get_bridge_containing_port(port_name);
+ br_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name);
+ if (!ovs_bridge || br_vlan < 0) {
+ free(ovs_bridge);
+ send_reply(seq, ENODEV, NULL);
+ return error;
+ }
+ }
+
/* Fetch the forwarding database using ovs-appctl. */
- unixctl_command = xasprintf("fdb/show %s", br_name);
+ unixctl_command = xasprintf("fdb/show %s", ovs_bridge);
error = execute_appctl_command(unixctl_command, &output);
free(unixctl_command);
if (error) {
+ free(ovs_bridge);
send_reply(seq, error, NULL);
return error;
}
/* Fetch the MAC address for each interface on the bridge, so that we can
* fill in the is_local field in the response. */
- cfg_read();
- get_bridge_ifaces(br_name, &ifaces);
+ get_bridge_ifaces(ovs_bridge, &ifaces, br_vlan);
local_macs = xmalloc(ifaces.n * sizeof *local_macs);
n_local_macs = 0;
for (i = 0; i < ifaces.n; i++) {
continue;
}
+ if (vlan != br_vlan) {
+ continue;
+ }
+
if (skip > 0) {
skip--;
continue;
send_reply(seq, 0, &query_data);
ofpbuf_uninit(&query_data);
+ free(ovs_bridge);
return 0;
}
IP flows, such as the principals involved and duration. A bridge may be
configured to send NetFlow v5 records to NetFlow collectors when flows
end. To enable, define the key \fBnetflow.\fIbridge\fB.host\fR for each
-collector in the form \fIhost\fB:\fIport\fR. Records from \fIbridge\fR
-will be sent to each \fIhost\fR on UDP \fIport\fR.
+collector in the form \fIip\fB:\fIport\fR. Records from \fIbridge\fR
+will be sent to each \fIip\fR on UDP \fIport\fR. The \fIip\fR must
+be specified numerically, not as a DNS name.
The NetFlow messages will use the datapath index for the engine type and id.
This can be overridden with the \fBnetflow.\fIbridge\fB.engine-type\fR and
functionality is enabled by setting the key \fBmgmt.controller\fR to one
of the following values:
.
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR. SSL must be configured when this form is used (see \fBSSL
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+SSL must be configured when this form is used (see \fBSSL
Configuration\fR, below).
.
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
.PP
The maximum time between attempts to connect to the controller may be
specified in integral seconds with the \fBmgmt.max-backoff\fR key. The
.RE
.
.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR. SSL must be configured when this form is used (see \fBSSL
+\fBssl:\fIip\fR[\fB:\fIport\fR]
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+SSL must be configured when this form is used (see \fBSSL
Configuration\fR, below).
.
.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+\fBtcp:\fIip\fR[\fB:\fIport\fR]
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
.
.TP
\fBunix:\fIfile\fR
for physdev in physdevs:
down_netdev(physdev)
+ # If we are bringing up a bond, remove IP addresses from the
+ # slaves (because we are implicitly being asked to take them down).
+ #
+ # Conversely, if we are bringing up an interface that has bond
+ # masters, remove IP addresses from the bond master (because we
+ # are implicitly being asked to take it down).
+ for bond_pif in bond_slaves + bond_masters:
+ run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0'])
+
# Remove all keys related to pif and any bond masters linked to PIF.
del_ports = [ipdev] + physdevs + bond_masters
if vlan_slave and bond_master:
# Update /etc/issue (which contains the IP address of the management interface)
os.system("/sbin/update-issue")
+
+ if bond_slaves:
+ # There seems to be a race somewhere: without this sleep, using
+ # XenCenter to create a bond that becomes the management interface
+ # fails with "The underlying connection was closed: A connection that
+ # was expected to be kept alive was closed by the server." on every
+ # second or third try, even though /var/log/messages doesn't show
+ # anything unusual.
+ #
+ # The race is probably present even without vswitch, but bringing up a
+ # bond without vswitch involves a built-in pause of 10 seconds or more
+ # to wait for the bond to transition from learning to forwarding state.
+ time.sleep(5)
def action_down(pif):
rec = db.get_pif_record(pif)
if self.processname == None:
self.processname = name
+ def version(self):
+ try:
+ output = ShellPipe(["service", self.name, "version"]).Stdout()
+ except StandardError, e:
+ log.error("version retrieval error: " + str(e))
+ return "<unknown>"
+ for line in output:
+ if self.processname in line:
+ return line.split()[-1]
+ return "<unknown>"
+
def status(self):
try:
output = ShellPipe(["service", self.name, "status"]).Stdout()
return "<unknown>"
if len(output) == 0:
return "<unknown>"
- for l in output:
- if self.processname not in l:
+ for line in output:
+ if self.processname not in line:
continue
- elif "running" in l:
+ elif "running" in line:
return "Running"
- elif "stop" in l:
+ elif "stop" in line:
return "Stopped"
else:
return "<unknown>"
inPane.NewLine()
- versionStr = data.host.other_config({}).get("vSwitchVersion", "<Unknown>")
- inPane.AddStatusField(Lang("Version", 20), versionStr)
+ inPane.AddStatusField(Lang("Version", 20),
+ VSwitchService.Inst("vswitch", "ovs-vswitchd").version())
inPane.NewLine()