(Porting OVS to other kinds of platforms is likely to be more
difficult.)
-Open vSwitch Architectural Overview
------------------------------------
-The following diagram shows the conceptual architecture of Open
-vSwitch from a porter's perspective.
- _ _
- | +-------------------+ |
- | | ovs-vswitchd | |Generic
- | +-------------------+ |code
- userspace | | ofproto | _|
- | +---------+---------+ _
- | | netdev |dpif/wdp | |
- |_ +---||----+----||---+ |Code that
- _ || || |may need
- | +---||-----+---||---+ |porting
- | | |datapath| _|
- kernel | | +--------+
- | | |
- |_ +-------||----------+
- ||
- physical
- NIC
-
-Some of the components are generic. Modulo bugs, these components
-should not need to be modified as part of a port:
-
- - Near the top of the diagram, "ofproto" is the library in Open vSwitch
- that contains the core OpenFlow protocol implementation and switching
- functionality. It is built from source files in the "ofproto"
- directory.
-
- - Above ofproto, "ovs-vswitchd", the main Open vSwitch userspace
- program, is the primary client for ofproto. It is built
- from source files in the "vswitchd" directory of the Open
- vSwitch distribution.
-
- ovs-vswitchd is the most sophisticated of ofproto's clients, but
- ofproto can have other clients as well. Notably, ovs-openflowd,
- in the utilities directory, is much simpler (though less
- capable) than ovs-vswitchd, and it may be easier to get up and
- running as part of a port.
-
-The other components require attention during a port:
-
- - "dpif" or "wdp" is what ofproto uses to directly monitor and
- control a "datapath", which is the term used in OVS for a
- collection of physical or virtual ports that are exposed over
- OpenFlow as a single switch. A datapath implements a flow
- table.
-
- - "netdev" is the interface to "network devices", e.g. eth0 on
- Linux. ofproto expects that every port exposed by a datapath
- has a corresponding netdev that it can open with netdev_open().
+Vocabulary
+----------
-The following sections talk about these components in more detail.
+For historical reasons, different words are used for essentially the
+same concept in different areas of the Open vSwitch source tree. Here
+is a concordance, indexed by the area of the source tree:
-Which Branch?
--------------
-
-The architectural diagram shows "dpif" and "wdp" as alternatives.
-These alternatives correspond to the "master" and "wdp" branches,
-respectively, of the Open vSwitch Git repository at
-git://openvswitch.org/openvswitch. Both of these branches currently
-represent reasonable porting targets for different purposes:
-
- - The "master" branch is more mature and better tested. Open
- vSwitch releases are made from this branch, and most OVS
- development and testing occurs on this branch.
-
- - The "wdp" branch has a software architecture that can take
- advantage of hardware with support for wildcards (e.g. TCAMs or
- similar). This branch has known important bugs, but is the basis
- of a few ongoing hardware projects, so we expect the quality to
- improve rapidly.
-
-Since its architecture is better, in the medium to long term we will
-fix the problems in the "wdp" branch and merge it into "master".
-
-In porting OVS, the major difference between the two branches is the
-form of the flow table in the datapath:
-
- - On "master", the "dpif" datapath interface maintains a simple
- flow table, one that does not support any kind of wildcards.
- This flow table essentially acts as a cache. When a packet
- arrives on an interface, the datapath looks for it in this
- exact-match table. If there is a match, then it performs the
- associated actions. If there is no match, the datapath passes
- the packet up to "ofproto", which maintains a flow table that
- supports wildcards. If the packet matches in this flow table,
- then ofproto executes its actions and inserts a new exact-match
- entry into the dpif flow table. (Otherwise, ofproto sends the
- packet to the OpenFlow controller, if one is configured.)
-
- Thus, on the "master" branch, the datapath has little
- opportunity to take advantage of hardware support for wildcards,
- since it is only ever presented with exact-match flow entries.
-
- - On "wdp", the "wdp" datapath interface maintains a flow table
- similar to that of OpenFlow, one that supports wildcards. Thus,
- a wdp datapath can take advantage of hardware support for
- wildcards, since it is free to implement the flow table any way
- it likes.
-
-The following sections describe the two datapath interfaces in a
-little more detail.
-
-dpif: The "master" Branch Datapath
-----------------------------------
-
-struct dpif_class, in lib/dpif-provider.h, defines the
-interfaces required to implement a dpif for new hardware or
-software. That structure contains many function pointers, each
-of which has a comment that is meant to describe its behavior in
-detail. If the requirements are unclear, please report this as
-a bug and we will clarify.
+ datapath/ vport ---
+ vswitchd/ iface port
+ ofproto/ port bundle
+ lib/bond.c slave bond
+ lib/lacp.c slave lacp
+ lib/netdev.c netdev ---
+ database Interface Port
-There are two existing dpif implementations that may serve as
-useful examples during a port:
- * lib/dpif-linux.c is a Linux-specific dpif implementation that
- talks to an Open vSwitch-specific kernel module (whose sources
- are in the "datapath" directory). The kernel module performs
- all of the switching work, passing packets that do not match any
- flow table entry up to userspace. This dpif implementation is
- essentially a wrapper around calls to "ioctl".
+Open vSwitch Architectural Overview
+-----------------------------------
- * lib/dpif-netdev.c is a generic dpif implementation that performs
- all switching internally. It delegates most of its work to the
- "netdev" library (described below). Using dpif-netdev, instead
- of writing a new dpif, can be a simple way to get OVS up and
- running on new platforms, but other solutions are likely to
- yield higher performance.
+The following diagram shows the very high-level architecture of Open
+vSwitch from a porter's perspective.
-"wdp": The "wdp" Branch Datapath
---------------------------------
+ +-------------------+
+ | ovs-vswitchd |<-->ovsdb-server
+ +-------------------+
+ | ofproto |<-->OpenFlow controllers
+ +--------+-+--------+
+ | netdev | | ofproto|
+ +--------+ |provider|
+ | netdev | +--------+
+ |provider|
+ +--------+
+
+Some of the components are generic. Modulo bugs or inadequacies,
+these components should not need to be modified as part of a port:
+
+ - "ovs-vswitchd" is the main Open vSwitch userspace program, in
+ vswitchd/. It reads the desired Open vSwitch configuration from
+ the ovsdb-server program over an IPC channel and passes this
+ configuration down to the "ofproto" library. It also passes
+ certain status and statistical information from ofproto back
+ into the database.
+
+ - "ofproto" is the Open vSwitch library, in ofproto/, that
+ implements an OpenFlow switch. It talks to OpenFlow controllers
+ over the network and to switch hardware or software to an
+ "ofproto provider", explained further below.
+
+ - "netdev" is the Open vSwitch library, in lib/netdev.c, that
+ abstracts interacting with network devices, that is, Ethernet
+ interfaces. The netdev library is a thin layer over "netdev
+ provider" code, explained further below.
+
+The other components may need attention during a port. You will
+almost certainly have to implement a "netdev provider". Depending on
+the type of port you are doing and the desired performance, you may
+also have to implement an "ofproto provider" or a lower-level
+component called a "dpif" provider.
-struct wdp_class, in ofproto/wdp-provider.h, defines the interfaces
-required to implement a wdp ("wildcarded datapath") for new hardware
-or software. That structure contains many function pointers, each of
-which has a comment that is meant to describe its behavior in detail.
-If the requirements are unclear, please report this as a bug and we
-will clarify.
+The following sections talk about these components in more detail.
-The wdp interface is preliminary. Please let us know if it seems
-unsuitable for your purpose. We will try to improve it.
-There is currently only one wdp implementation:
+Writing a netdev Provider
+-------------------------
- * ofproto/wdp-xflow.c is an adaptation of "master" branch code
- that breaks wildcarded flows up into exact-match flows in the
- same way that ofproto always does on the "master" branch. It
- delegates its work to exact-match datapath implementations whose
- interfaces are identical to "master" branch datapaths, except
- that names have been changed from "dpif" to "xfif" ("exact-match
- flow interface") and similar.
+A "netdev provider" implements an operating system and hardware
+specific interface to "network devices", e.g. eth0 on Linux. Open
+vSwitch must be able to open each port on a switch as a netdev, so you
+will need to implement a "netdev provider" that works with your switch
+hardware and software.
-"netdev": Interface to network devices
---------------------------------------
+struct netdev_class, in lib/netdev-provider.h, defines the interfaces
+required to implement a netdev. That structure contains many function
+pointers, each of which has a comment that is meant to describe its
+behavior in detail. If the requirements are unclear, please report
+this as a bug.
-The netdev interface can be roughly divided into functionality for the
-following purposes:
+The netdev interface can be divided into a few rough categories:
* Functions required to properly implement OpenFlow features. For
example, OpenFlow requires the ability to report the Ethernet
table. These functions must be implemented if the corresponding
OVS features are to work, but may be omitted initially.
- * Functions that may be needed in some implementations but not
- others. The dpif-netdev described above, for example, needs to
- be able to send and receive packets on a netdev.
-
-struct netdev_class, in lib/netdev-provider.h, defines the interfaces
-required to implement a netdev. That structure contains many function
-pointers, each of which has a comment that is meant to describe its
-behavior in detail. If the requirements are unclear, please report
-this as a bug and we will clarify.
+ * Functions needed in some implementations but not in others. For
+ example, most kinds of ports (see below) do not need
+ functionality to receive packets from a network device.
The existing netdev implementations may serve as useful examples
during a port:
network devices, using Linux kernel calls. It may be a good
place to start for full-featured netdev implementations.
- * lib/netdev-vport.c provides support for "virtual ports"
+ * lib/netdev-vport.c provides support for "virtual ports"
implemented by the Open vSwitch datapath module for the Linux
kernel. This may serve as a model for minimal netdev
implementations.
+ * lib/netdev-dummy.c is a fake netdev implementation useful only
+ for testing.
+
+
+Porting Strategies
+------------------
+
+After a netdev provider has been implemented for a system's network
+devices, you may choose among three basic porting strategies.
+
+The lowest-effort strategy is to use the "userspace switch"
+implementation built into Open vSwitch. This ought to work, without
+writing any more code, as long as the netdev provider that you
+implemented supports receiving packets. It yields poor performance,
+however, because every packet passes through the ovs-vswitchd process.
+See INSTALL.userspace for instructions on how to configure a userspace
+switch.
+
+If the userspace switch is not the right choice for your port, then
+you will have to write more code. You may implement either an
+"ofproto provider" or a "dpif provider". Which you should choose
+depends on a few different factors:
+
+ * Only an ofproto provider can take full advantage of hardware
+ with built-in support for wildcards (e.g. an ACL table or a
+ TCAM).
+
+ * A dpif provider can take advantage of the Open vSwitch built-in
+ implementations of bonding, LACP, 802.1ag, 802.1Q VLANs, and
+ other features. An ofproto provider has to provide its own
+ implementations, if the hardware can support them at all.
+
+ * A dpif provider is usually easier to implement.
+
+The following sections describe how to implement each kind of port.
+
+
+ofproto Providers
+-----------------
+
+An "ofproto provider" is what ofproto uses to directly monitor and
+control an OpenFlow-capable switch. struct ofproto_class, in
+ofproto/private.h, defines the interfaces to implement a ofproto
+provider for new hardware or software. That structure contains many
+function pointers, each of which has a comment that is meant to
+describe its behavior in detail. If the requirements are unclear,
+please report this as a bug.
+
+The ofproto provider interface is preliminary. Please let us know if
+it seems unsuitable for your purpose. We will try to improve it.
+
+
+Writing a dpif Provider
+-----------------------
+
+Open vSwitch has a built-in ofproto provider named "ofproto-dpif",
+which is built on top of a library for manipulating datapaths, called
+"dpif". A "datapath" is a simple flow table, one that supports only
+exact-match flows, that is, flows without wildcards. When a packet
+arrives on a network device, the datapath looks for it in this
+exact-match table. If there is a match, then it performs the
+associated actions. If there is no match, the datapath passes the
+packet up to ofproto-dpif, which maintains an OpenFlow flow table
+(that supports wildcards). If the packet matches in this flow table,
+then ofproto-dpif executes its actions and inserts a new exact-match
+entry into the dpif flow table. (Otherwise, ofproto-dpif passes the
+packet up to ofproto to send the packet to the OpenFlow controller, if
+one is configured.)
+
+The "dpif" library in turn delegates much of its functionality to a
+"dpif provider". The following diagram shows how dpif providers fit
+into the Open vSwitch architecture:
+
+ _
+ | +-------------------+
+ | | ovs-vswitchd |<-->ovsdb-server
+ | +-------------------+
+ | | ofproto |<-->OpenFlow controllers
+ | +--------+-+--------+
+ | | netdev | |ofproto-|
+ userspace | +--------+ | dpif |
+ | | netdev | +--------+
+ | |provider| | dpif |
+ | +---||---+ +--------+
+ | || | dpif |
+ | || |provider|
+ |_ || +---||---+
+ || ||
+ _ +---||-----+---||---+
+ | | |datapath|
+ kernel | | +--------+
+ | | |
+ |_ +--------||---------+
+ ||
+ physical
+ NIC
+
+struct dpif_class, in lib/dpif-provider.h, defines the interfaces
+required to implement a dpif provider for new hardware or software.
+That structure contains many function pointers, each of which has a
+comment that is meant to describe its behavior in detail. If the
+requirements are unclear, please report this as a bug.
+
+There are two existing dpif implementations that may serve as
+useful examples during a port:
+
+ * lib/dpif-linux.c is a Linux-specific dpif implementation that
+ talks to an Open vSwitch-specific kernel module (whose sources
+ are in the "datapath" directory). The kernel module performs
+ all of the switching work, passing packets that do not match any
+ flow table entry up to userspace. This dpif implementation is
+ essentially a wrapper around calls into the kernel module.
+
+ * lib/dpif-netdev.c is a generic dpif implementation that performs
+ all switching internally. This is how the Open vSwitch
+ userspace switch is implemented.
+
+
Miscellaneous Notes
-------------------
+Open vSwitch source code uses uint16_t, uint32_t, and uint64_t as
+fixed-width types in host byte order, and ovs_be16, ovs_be32, and
+ovs_be64 as fixed-width types in network byte order. Each of the
+latter is equivalent to the one of the former, but the difference in
+name makes the intended use obvious.
+
+ovs-vswitchd is the most sophisticated of ofproto's clients, but
+ofproto can have other clients as well. ovs-openflowd, in the
+utilities directory, is much simpler than ovs-vswitchd. It may be
+easier to initially bring up ovs-openflowd as part of a port.
+
lib/entropy.c assumes that it can obtain high-quality random number
seeds at startup by reading from /dev/urandom. You will need to
modify it if this is not true on your platform.
vswitchd/system-stats.c only knows how to obtain some statistics on
Linux. Optionally you may implement them for your platform as well.
+
Questions
---------
NXFMFC_HARDWARE = 0x100,
/* A nonexistent table ID was specified in the "command" field of struct
- * ofp_flow_mod, when the nxt_flow_mod_table_id extension is enabled.
- * (This extension is not yet implemented on this branch of Open
- * vSwitch.) */
+ * ofp_flow_mod, when the nxt_flow_mod_table_id extension is enabled. */
NXFMFC_BAD_TABLE_ID = 0x101
};
\f
NXT_ROLE_REQUEST,
NXT_ROLE_REPLY,
+ /* Use the upper 8 bits of the 'command' member in struct ofp_flow_mod to
+ * designate the table to which a flow is to be added? See the big comment
+ * on struct nxt_flow_mod_table_id for more information.
+ *
+ * A screwup caused this extension to be assigned the same value as
+ * NXT_SET_FLOW_FORMAT (see below). The two extensions do have different
+ * lengths, so they can still be distinguished. */
+ NXT_FLOW_MOD_TABLE_ID,
+
/* Flexible flow specification (aka NXM = Nicira Extended Match). */
- NXT_SET_FLOW_FORMAT, /* Set flow format. */
+ NXT_SET_FLOW_FORMAT = NXT_FLOW_MOD_TABLE_ID, /* Set flow format. */
NXT_FLOW_MOD, /* Analogous to OFPT_FLOW_MOD. */
NXT_FLOW_REMOVED /* Analogous to OFPT_FLOW_REMOVED. */
};
NXST_AGGREGATE /* Analogous to OFPST_AGGREGATE. */
};
+/* This command enables or disables an Open vSwitch extension that allows a
+ * controller to specify the OpenFlow table to which a flow should be added,
+ * instead of having the switch decide which table is most appropriate as
+ * required by OpenFlow 1.0. By default, the extension is disabled.
+ *
+ * When this feature is enabled, Open vSwitch treats struct ofp_flow_mod's
+ * 16-bit 'command' member as two separate fields. The upper 8 bits are used
+ * as the table ID, the lower 8 bits specify the command as usual. A table ID
+ * of 0xff is treated like a wildcarded table ID.
+ *
+ * The specific treatment of the table ID depends on the type of flow mod:
+ *
+ * - OFPFC_ADD: Given a specific table ID, the flow is always placed in that
+ * table. If an identical flow already exists in that table only, then it
+ * is replaced. If the flow cannot be placed in the specified table,
+ * either because the table is full or because the table cannot support
+ * flows of the given type, the switch replies with an
+ * OFPFMFC_ALL_TABLES_FULL error. (A controller can distinguish these
+ * cases by comparing the current and maximum number of entries reported
+ * in ofp_table_stats.)
+ *
+ * If the table ID is wildcarded, the switch picks an appropriate table
+ * itself. If an identical flow already exist in the selected flow table,
+ * then it is replaced. The choice of table might depend on the flows
+ * that are already in the switch; for example, if one table fills up then
+ * the switch might fall back to another one.
+ *
+ * - OFPFC_MODIFY, OFPFC_DELETE: Given a specific table ID, only flows
+ * within that table are matched and modified or deleted. If the table ID
+ * is wildcarded, flows within any table may be matched and modified or
+ * deleted.
+ *
+ * - OFPFC_MODIFY_STRICT, OFPFC_DELETE_STRICT: Given a specific table ID,
+ * only a flow within that table may be matched and modified or deleted.
+ * If the table ID is wildcarded and exactly one flow within any table
+ * matches, then it is modified or deleted; if flows in more than one
+ * table match, then none is modified or deleted.
+ */
+struct nxt_flow_mod_table_id {
+ struct ofp_header header;
+ uint32_t vendor; /* NX_VENDOR_ID. */
+ uint32_t subtype; /* NXT_FLOW_MOD_TABLE_ID. */
+ uint8_t set; /* Nonzero to enable, zero to disable. */
+ uint8_t pad[7];
+};
+OFP_ASSERT(sizeof(struct nxt_flow_mod_table_id) == 24);
+
/* Configures the "role" of the sending controller. The default role is:
*
* - Other (NX_ROLE_OTHER), which allows the controller access to all
lib/hash.h \
lib/hmap.c \
lib/hmap.h \
+ lib/hmapx.c \
+ lib/hmapx.h \
lib/json.c \
lib/json.h \
lib/jsonrpc.c \
lib/vconn-stream.c \
lib/vconn.c \
lib/vconn.h \
+ lib/vlan-bitmap.c \
+ lib/vlan-bitmap.h \
lib/vlog.c \
lib/vlog.h
nodist_lib_libopenvswitch_a_SOURCES = \
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
return xzalloc(bitmap_n_bytes(n_bits));
}
+static inline unsigned long *
+bitmap_clone(const unsigned long *bitmap, size_t n_bits)
+{
+ return xmemdup(bitmap, bitmap_n_bytes(n_bits));
+}
+
static inline void
bitmap_free(unsigned long *bitmap)
{
return revalidate;
}
+static void
+bond_slave_set_netdev__(struct bond *bond, struct bond_slave *slave,
+ struct netdev *netdev)
+{
+ if (slave->netdev != netdev) {
+ if (bond->monitor) {
+ if (slave->netdev) {
+ netdev_monitor_remove(bond->monitor, slave->netdev);
+ }
+ netdev_monitor_add(bond->monitor, netdev);
+ }
+ slave->netdev = netdev;
+ }
+}
+
/* Registers 'slave_' as a slave of 'bond'. The 'slave_' pointer is an
* arbitrary client-provided pointer that uniquely identifies a slave within a
* bond. If 'slave_' already exists within 'bond' then this function
bond->bond_revalidate = true;
}
- if (slave->netdev != netdev) {
- if (bond->monitor) {
- if (slave->netdev) {
- netdev_monitor_remove(bond->monitor, slave->netdev);
- }
- netdev_monitor_add(bond->monitor, netdev);
- }
- slave->netdev = netdev;
- }
+ bond_slave_set_netdev__(bond, slave, netdev);
free(slave->name);
slave->name = xstrdup(netdev_get_name(netdev));
}
+/* Updates the network device to be used with 'slave_' to 'netdev'.
+ *
+ * This is useful if the caller closes and re-opens the network device
+ * registered with bond_slave_register() but doesn't need to change anything
+ * else. */
+void
+bond_slave_set_netdev(struct bond *bond, void *slave_, struct netdev *netdev)
+{
+ struct bond_slave *slave = bond_slave_lookup(bond, slave_);
+ if (slave) {
+ bond_slave_set_netdev__(bond, slave, netdev);
+ }
+}
+
/* Unregisters 'slave_' from 'bond'. If 'bond' does not contain such a slave
* then this function has no effect.
*
bool bond_reconfigure(struct bond *, const struct bond_settings *);
void bond_slave_register(struct bond *, void *slave_,
uint32_t stable_id, struct netdev *);
+void bond_slave_set_netdev(struct bond *, void *slave_, struct netdev *);
void bond_slave_unregister(struct bond *, const void *slave);
void bond_run(struct bond *, struct tag_set *, bool lacp_negotiated);
}
void
-cls_rule_set_in_port(struct cls_rule *rule, uint16_t odp_port)
+cls_rule_set_in_port(struct cls_rule *rule, uint16_t ofp_port)
{
rule->wc.wildcards &= ~FWW_IN_PORT;
- rule->flow.in_port = odp_port;
+ rule->flow.in_port = ofp_port;
}
void
break;
}
if (!(w & FWW_IN_PORT)) {
- ds_put_format(s, "in_port=%"PRIu16",",
- odp_port_to_ofp_port(f->in_port));
+ ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
}
if (wc->vlan_tci_mask) {
ovs_be16 vid_mask = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
* rule, even rules that cannot have any effect because the new rule matches a
* superset of their flows and has higher priority. */
struct cls_rule *
-classifier_insert(struct classifier *cls, struct cls_rule *rule)
+classifier_replace(struct classifier *cls, struct cls_rule *rule)
{
struct cls_rule *old_rule;
struct cls_table *table;
return old_rule;
}
+/* Inserts 'rule' into 'cls'. Until 'rule' is removed from 'cls', the caller
+ * must not modify or free it.
+ *
+ * 'cls' must not contain an identical rule (including wildcards, values of
+ * fixed fields, and priority). Use classifier_find_rule_exactly() to find
+ * such a rule. */
+void
+classifier_insert(struct classifier *cls, struct cls_rule *rule)
+{
+ struct cls_rule *displaced_rule = classifier_replace(cls, rule);
+ assert(!displaced_rule);
+}
+
/* Removes 'rule' from 'cls'. It is the caller's responsibility to free
* 'rule', if this is desirable. */
void
/* Finds and returns a rule in 'cls' with exactly the same priority and
* matching criteria as 'target'. Returns a null pointer if 'cls' doesn't
- * contain an exact match.
- *
- * Priority is ignored for exact-match rules (because OpenFlow 1.0 always
- * treats exact-match rules as highest priority). */
+ * contain an exact match. */
struct cls_rule *
classifier_find_rule_exactly(const struct classifier *cls,
const struct cls_rule *target)
}
head = find_equal(table, &target->flow, flow_hash(&target->flow, 0));
- if (flow_wildcards_is_exact(&target->wc)) {
- return head;
- }
FOR_EACH_RULE_IN_LIST (rule, head) {
if (target->priority >= rule->priority) {
return target->priority == rule->priority ? rule : NULL;
void classifier_destroy(struct classifier *);
bool classifier_is_empty(const struct classifier *);
int classifier_count(const struct classifier *);
-struct cls_rule *classifier_insert(struct classifier *, struct cls_rule *);
+void classifier_insert(struct classifier *, struct cls_rule *);
+struct cls_rule *classifier_replace(struct classifier *, struct cls_rule *);
void classifier_remove(struct classifier *, struct cls_rule *);
struct cls_rule *classifier_lookup(const struct classifier *,
const struct flow *);
const struct dpif_class dpif_linux_class = {
"system",
- NULL, /* run */
- NULL, /* wait */
dpif_linux_enumerate,
dpif_linux_open,
dpif_linux_close,
dpif_linux_destroy,
+ NULL, /* run */
+ NULL, /* wait */
dpif_linux_get_stats,
dpif_linux_get_drop_frags,
dpif_linux_set_drop_frags,
}
static void
-dp_netdev_run(void)
+dpif_netdev_run(struct dpif *dpif)
{
- struct shash_node *node;
+ struct dp_netdev *dp = get_dp_netdev(dpif);
+ struct dp_netdev_port *port;
struct ofpbuf packet;
ofpbuf_init(&packet, DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + max_mtu);
- SHASH_FOR_EACH (node, &dp_netdevs) {
- struct dp_netdev *dp = node->data;
- struct dp_netdev_port *port;
-
- LIST_FOR_EACH (port, node, &dp->port_list) {
- int error;
-
- /* Reset packet contents. */
- ofpbuf_clear(&packet);
- ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
- error = netdev_recv(port->netdev, &packet);
- if (!error) {
- dp_netdev_port_input(dp, port, &packet);
- } else if (error != EAGAIN && error != EOPNOTSUPP) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
- netdev_get_name(port->netdev), strerror(error));
- }
+ LIST_FOR_EACH (port, node, &dp->port_list) {
+ int error;
+
+ /* Reset packet contents. */
+ ofpbuf_clear(&packet);
+ ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
+
+ error = netdev_recv(port->netdev, &packet);
+ if (!error) {
+ dp_netdev_port_input(dp, port, &packet);
+ } else if (error != EAGAIN && error != EOPNOTSUPP) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
+ netdev_get_name(port->netdev), strerror(error));
}
}
ofpbuf_uninit(&packet);
}
static void
-dp_netdev_wait(void)
+dpif_netdev_wait(struct dpif *dpif)
{
- struct shash_node *node;
-
- SHASH_FOR_EACH (node, &dp_netdevs) {
- struct dp_netdev *dp = node->data;
- struct dp_netdev_port *port;
+ struct dp_netdev *dp = get_dp_netdev(dpif);
+ struct dp_netdev_port *port;
- LIST_FOR_EACH (port, node, &dp->port_list) {
- netdev_recv_wait(port->netdev);
- }
+ LIST_FOR_EACH (port, node, &dp->port_list) {
+ netdev_recv_wait(port->netdev);
}
}
-
static void
dp_netdev_strip_vlan(struct ofpbuf *packet)
{
const struct dpif_class dpif_netdev_class = {
"netdev",
- dp_netdev_run,
- dp_netdev_wait,
NULL, /* enumerate */
dpif_netdev_open,
dpif_netdev_close,
dpif_netdev_destroy,
+ dpif_netdev_run,
+ dpif_netdev_wait,
dpif_netdev_get_stats,
dpif_netdev_get_drop_frags,
dpif_netdev_set_drop_frags,
* the type assumed if no type is specified when opening a dpif. */
const char *type;
- /* Performs periodic work needed by dpifs of this class, if any is
- * necessary. */
- void (*run)(void);
-
- /* Arranges for poll_block() to wake up if the "run" member function needs
- * to be called. */
- void (*wait)(void);
-
/* Enumerates the names of all known created datapaths, if possible, into
* 'all_dps'. The caller has already initialized 'all_dps' and other dpif
* classes might already have added names to it.
* the 'close' member function. */
int (*destroy)(struct dpif *dpif);
+ /* Performs periodic work needed by 'dpif', if any is necessary. */
+ void (*run)(struct dpif *dpif);
+
+ /* Arranges for poll_block() to wake up if the "run" member function needs
+ * to be called for 'dpif'. */
+ void (*wait)(struct dpif *dpif);
+
/* Retrieves statistics for 'dpif' into 'stats'. */
int (*get_stats)(const struct dpif *dpif, struct odp_stats *stats);
}
}
-/* Performs periodic work needed by all the various kinds of dpifs.
- *
- * If your program opens any dpifs, it must call both this function and
- * netdev_run() within its main poll loop. */
-void
-dp_run(void)
-{
- struct shash_node *node;
- SHASH_FOR_EACH(node, &dpif_classes) {
- const struct registered_dpif_class *registered_class = node->data;
- if (registered_class->dpif_class->run) {
- registered_class->dpif_class->run();
- }
- }
-}
-
-/* Arranges for poll_block() to wake up when dp_run() needs to be called.
- *
- * If your program opens any dpifs, it must call both this function and
- * netdev_wait() within its main poll loop. */
-void
-dp_wait(void)
-{
- struct shash_node *node;
- SHASH_FOR_EACH(node, &dpif_classes) {
- const struct registered_dpif_class *registered_class = node->data;
- if (registered_class->dpif_class->wait) {
- registered_class->dpif_class->wait();
- }
- }
-}
-
/* Registers a new datapath provider. After successful registration, new
* datapaths of that type can be opened using dpif_open(). */
int
return error;
}
-/* Parses 'datapath name', which is of the form type@name into its
- * component pieces. 'name' and 'type' must be freed by the caller. */
+/* Parses 'datapath_name_', which is of the form [type@]name into its
+ * component pieces. 'name' and 'type' must be freed by the caller.
+ *
+ * The returned 'type' is normalized, as if by dpif_normalize_type(). */
void
dp_parse_name(const char *datapath_name_, char **name, char **type)
{
if (separator) {
*separator = '\0';
*type = datapath_name;
- *name = xstrdup(separator + 1);
+ *name = xstrdup(dpif_normalize_type(separator + 1));
} else {
*name = datapath_name;
- *type = NULL;
+ *type = xstrdup(dpif_normalize_type(NULL));
}
}
dp_initialize();
- if (!type || *type == '\0') {
- type = "system";
- }
+ type = dpif_normalize_type(type);
registered_class = shash_find_data(&dpif_classes, type);
if (!registered_class) {
}
}
+/* Performs periodic work needed by 'dpif'. */
+void
+dpif_run(struct dpif *dpif)
+{
+ if (dpif->dpif_class->run) {
+ dpif->dpif_class->run(dpif);
+ }
+}
+
+/* Arranges for poll_block() to wake up when dp_run() needs to be called for
+ * 'dpif'. */
+void
+dpif_wait(struct dpif *dpif)
+{
+ if (dpif->dpif_class->wait) {
+ dpif->dpif_class->wait(dpif);
+ }
+}
+
/* Returns the name of datapath 'dpif' prefixed with the type
* (for use in log messages). */
const char *
return dpif->base_name;
}
+/* Returns the fully spelled out name for the given datapath 'type'.
+ *
+ * Normalized type string can be compared with strcmp(). Unnormalized type
+ * string might be the same even if they have different spellings. */
+const char *
+dpif_normalize_type(const char *type)
+{
+ return type && type[0] ? type : "system";
+}
+
/* Destroys the datapath that 'dpif' is connected to, first removing all of its
* ports. After calling this function, it does not make sense to pass 'dpif'
* to any functions other than dpif_name() or dpif_close(). */
} else {
memset(port, 0, sizeof *port);
- /* Log level is DBG here because all the current callers are interested
- * in whether 'dpif' actually has a port 'devname', so that it's not an
- * issue worth logging if it doesn't. */
- VLOG_DBG_RL(&error_rl, "%s: failed to query port %s: %s",
- dpif_name(dpif), devname, strerror(error));
+ /* For ENOENT or ENODEV we use DBG level because the caller is probably
+ * interested in whether 'dpif' actually has a port 'devname', so that
+ * it's not an issue worth logging if it doesn't. Other errors are
+ * uncommon and more likely to indicate a real problem. */
+ VLOG_RL(&error_rl,
+ error == ENOENT || error == ENODEV ? VLL_DBG : VLL_WARN,
+ "%s: failed to query port %s: %s",
+ dpif_name(dpif), devname, strerror(error));
}
return error;
}
struct sset;
struct dpif_class;
-void dp_run(void);
-void dp_wait(void);
-
int dp_register_provider(const struct dpif_class *);
int dp_unregister_provider(const char *type);
void dp_enumerate_types(struct sset *types);
+const char *dpif_normalize_type(const char *);
int dp_enumerate_names(const char *type, struct sset *names);
void dp_parse_name(const char *datapath_name, char **name, char **type);
int dpif_create_and_open(const char *name, const char *type, struct dpif **);
void dpif_close(struct dpif *);
+void dpif_run(struct dpif *);
+void dpif_wait(struct dpif *);
+
const char *dpif_name(const struct dpif *);
const char *dpif_base_name(const struct dpif *);
}
-/* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port.
+/* Initializes 'flow' members from 'packet', 'tun_id', and 'ofp_in_port'.
* Initializes 'packet' header pointers as follows:
*
* - packet->l2 to the start of the Ethernet header.
* present and has a correct length, and otherwise NULL.
*/
int
-flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
+flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t ofp_in_port,
struct flow *flow)
{
struct ofpbuf b = *packet;
memset(flow, 0, sizeof *flow);
flow->tun_id = tun_id;
- flow->in_port = in_port;
+ flow->in_port = ofp_in_port;
packet->l2 = b.data;
packet->l3 = NULL;
uint32_t regs[FLOW_N_REGS]; /* Registers. */
ovs_be32 nw_src; /* IPv4 source address. */
ovs_be32 nw_dst; /* IPv4 destination address. */
- uint16_t in_port; /* Input switch port. */
+ uint16_t in_port; /* OpenFlow port number of input port. */
ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
ovs_be16 dl_type; /* Ethernet frame type. */
ovs_be16 tp_src; /* TCP/UDP source port. */
#define FWW_TP_DST ((OVS_FORCE flow_wildcards_t) (1 << 7))
/* Same meanings as corresponding OFPFW_* bits, but differ in value. */
#define FWW_NW_TOS ((OVS_FORCE flow_wildcards_t) (1 << 1))
-/* No corresponding OFPFW_* or OVSFW_* bits. */
+/* No corresponding OFPFW_* bits. */
#define FWW_ETH_MCAST ((OVS_FORCE flow_wildcards_t) (1 << 8))
/* multicast bit only */
#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 9))
--- /dev/null
+/*
+ * Copyright (c) 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 "hmapx.h"
+
+#include <assert.h>
+
+#include "hash.h"
+
+static struct hmapx_node *
+hmapx_find__(const struct hmapx *map, const void *data, size_t hash)
+{
+ struct hmapx_node *node;
+
+ HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash, &map->map) {
+ if (node->data == data) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+static struct hmapx_node *
+hmapx_add__(struct hmapx *map, void *data, size_t hash)
+{
+ struct hmapx_node *node = xmalloc(sizeof *node);
+ node->data = data;
+ hmap_insert(&map->map, &node->hmap_node, hash);
+ return node;
+}
+
+/* Initializes 'map' as an empty set of pointers. */
+void
+hmapx_init(struct hmapx *map)
+{
+ hmap_init(&map->map);
+}
+
+/* Destroys 'map'. */
+void
+hmapx_destroy(struct hmapx *map)
+{
+ if (map) {
+ hmapx_clear(map);
+ hmap_destroy(&map->map);
+ }
+}
+
+/* Initializes 'map' to contain the same pointers as 'orig'. */
+void
+hmapx_clone(struct hmapx *map, const struct hmapx *orig)
+{
+ struct hmapx_node *node;
+
+ hmapx_init(map);
+ HMAP_FOR_EACH (node, hmap_node, &orig->map) {
+ hmapx_add__(map, node->data, node->hmap_node.hash);
+ }
+}
+
+/* Exchanges the contents of 'a' and 'b'. */
+void
+hmapx_swap(struct hmapx *a, struct hmapx *b)
+{
+ hmap_swap(&a->map, &b->map);
+}
+
+/* Adjusts 'map' so that it is still valid after it has been moved around in
+ * memory (e.g. due to realloc()). */
+void
+hmapx_moved(struct hmapx *map)
+{
+ hmap_moved(&map->map);
+}
+
+/* Returns true if 'map' contains no nodes, false if it contains at least one
+ * node. */
+bool
+hmapx_is_empty(const struct hmapx *map)
+{
+ return hmap_is_empty(&map->map);
+}
+
+/* Returns the number of nodes in 'map'. */
+size_t
+hmapx_count(const struct hmapx *map)
+{
+ return hmap_count(&map->map);
+}
+
+/* Adds 'data' to 'map'. If 'data' is new, returns the new hmapx_node;
+ * otherwise (if a 'data' already existed in 'map'), returns NULL. */
+struct hmapx_node *
+hmapx_add(struct hmapx *map, void *data)
+{
+ uint32_t hash = hash_pointer(data, 0);
+ return (hmapx_find__(map, data, hash)
+ ? NULL
+ : hmapx_add__(map, data, hash));
+}
+
+/* Adds 'data' to 'map'. Assert-fails if 'data' was already in 'map'. */
+void
+hmapx_add_assert(struct hmapx *map, void *data)
+{
+ bool added OVS_UNUSED = hmapx_add(map, data);
+ assert(added);
+}
+
+/* Removes all of the nodes from 'map'. */
+void
+hmapx_clear(struct hmapx *map)
+{
+ struct hmapx_node *node, *next;
+
+ HMAPX_FOR_EACH_SAFE (node, next, map) {
+ hmapx_delete(map, node);
+ }
+}
+
+/* Deletes 'node' from 'map' and frees 'node'. */
+void
+hmapx_delete(struct hmapx *map, struct hmapx_node *node)
+{
+ hmap_remove(&map->map, &node->hmap_node);
+ free(node);
+}
+
+/* Searches for 'data' in 'map'. If found, deletes it and returns true. If
+ * not found, returns false without modifying 'map'. */
+bool
+hmapx_find_and_delete(struct hmapx *map, const void *data)
+{
+ struct hmapx_node *node = hmapx_find(map, data);
+ if (node) {
+ hmapx_delete(map, node);
+ }
+ return node != NULL;
+}
+
+/* Searches for 'data' in 'map' and deletes it. Assert-fails if 'data' is not
+ * in 'map'. */
+void
+hmapx_find_and_delete_assert(struct hmapx *map, const void *data)
+{
+ bool deleted OVS_UNUSED = hmapx_find_and_delete(map, data);
+ assert(deleted);
+}
+
+/* Searches for 'data' in 'map'. Returns its node, if found, otherwise a null
+ * pointer. */
+struct hmapx_node *
+hmapx_find(const struct hmapx *map, const void *data)
+{
+ return hmapx_find__(map, data, hash_pointer(data, 0));
+}
+
+/* Returns true if 'map' contains 'data', false otherwise. */
+bool
+hmapx_contains(const struct hmapx *map, const void *data)
+{
+ return hmapx_find(map, data) != NULL;
+}
+
+/* Returns true if 'a' and 'b' contain the same pointers, false otherwise. */
+bool
+hmapx_equals(const struct hmapx *a, const struct hmapx *b)
+{
+ struct hmapx_node *node;
+
+ if (hmapx_count(a) != hmapx_count(b)) {
+ return false;
+ }
+
+ HMAP_FOR_EACH (node, hmap_node, &a->map) {
+ if (!hmapx_find__(b, node->data, node->hmap_node.hash)) {
+ return false;
+ }
+ }
+
+ return true;
+}
--- /dev/null
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef HMAPX_H
+#define HMAPX_H
+
+#include "hmap.h"
+
+struct hmapx_node {
+ struct hmap_node hmap_node;
+ void *data;
+};
+
+/* A set of "void *" pointers. */
+struct hmapx {
+ struct hmap map;
+};
+
+#define HMAPX_INITIALIZER(HMAPX) { HMAP_INITIALIZER(&(HMAPX)->map) }
+
+/* Basics. */
+void hmapx_init(struct hmapx *);
+void hmapx_destroy(struct hmapx *);
+void hmapx_clone(struct hmapx *, const struct hmapx *);
+void hmapx_swap(struct hmapx *, struct hmapx *);
+void hmapx_moved(struct hmapx *);
+
+/* Count. */
+bool hmapx_is_empty(const struct hmapx *);
+size_t hmapx_count(const struct hmapx *);
+
+/* Insertion. */
+struct hmapx_node *hmapx_add(struct hmapx *, void *);
+void hmapx_add_assert(struct hmapx *, void *);
+
+/* Deletion. */
+void hmapx_clear(struct hmapx *);
+void hmapx_delete(struct hmapx *, struct hmapx_node *);
+bool hmapx_find_and_delete(struct hmapx *, const void *);
+void hmapx_find_and_delete_assert(struct hmapx *, const void *);
+
+/* Search. */
+struct hmapx_node *hmapx_find(const struct hmapx *, const void *);
+bool hmapx_contains(const struct hmapx *, const void *);
+bool hmapx_equals(const struct hmapx *, const struct hmapx *);
+
+/* Iteration. */
+
+/* Iterates through every hmapx_node in HMAPX. */
+#define HMAPX_FOR_EACH(NODE, HMAPX) \
+ HMAP_FOR_EACH(NODE, hmap_node, &(HMAPX)->map)
+
+/* Safe when NODE may be freed (not needed when NODE may be removed from the
+ * hash map but its members remain accessible and intact). */
+#define HMAPX_FOR_EACH_SAFE(NODE, NEXT, HMAPX) \
+ HMAP_FOR_EACH_SAFE(NODE, NEXT, hmap_node, &(HMAPX)->map)
+
+#endif /* hmapx.h */
case OFPUTIL_OFPST_AGGREGATE_REPLY:
case OFPUTIL_NXT_ROLE_REQUEST:
case OFPUTIL_NXT_ROLE_REPLY:
+ case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
case OFPUTIL_NXT_SET_FLOW_FORMAT:
case OFPUTIL_NXT_FLOW_MOD:
case OFPUTIL_NXT_FLOW_REMOVED:
#include "tag.h"
#include "timeval.h"
#include "util.h"
+#include "vlan-bitmap.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(mac_learning);
{
if (ml) {
bitmap_free(ml->flood_vlans);
+ free(ml);
}
- free(ml);
}
/* Provides a bitmap of VLANs which have learning disabled, that is, VLANs on
- * which all packets are flooded. It takes ownership of the bitmap. Returns
- * true if the set has changed from the previous value. */
+ * which all packets are flooded. Returns true if the set has changed from the
+ * previous value. */
bool
-mac_learning_set_flood_vlans(struct mac_learning *ml, unsigned long *bitmap)
+mac_learning_set_flood_vlans(struct mac_learning *ml,
+ const unsigned long *bitmap)
{
- bool ret = (bitmap == NULL
- ? ml->flood_vlans != NULL
- : (ml->flood_vlans == NULL
- || !bitmap_equal(bitmap, ml->flood_vlans, 4096)));
-
- bitmap_free(ml->flood_vlans);
- ml->flood_vlans = bitmap;
-
- return ret;
+ if (vlan_bitmap_equal(ml->flood_vlans, bitmap)) {
+ return false;
+ } else {
+ bitmap_free(ml->flood_vlans);
+ ml->flood_vlans = vlan_bitmap_clone(bitmap);
+ return true;
+ }
}
static bool
is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
{
- return !(ml->flood_vlans && bitmap_is_set(ml->flood_vlans, vlan));
+ return vlan_bitmap_contains(ml->flood_vlans, vlan);
}
/* Returns true if 'src_mac' may be learned on 'vlan' for 'ml'.
/* Configuration. */
bool mac_learning_set_flood_vlans(struct mac_learning *,
- unsigned long *bitmap);
+ const unsigned long *bitmap);
/* Learning. */
bool mac_learning_may_learn(const struct mac_learning *,
const struct netdev_stats *src)
{
COPY_NETDEV_STATS;
+ dst->rx_compressed = 0;
+ dst->tx_compressed = 0;
}
\f
/* Utility functions. */
/* Metadata. */
case NFI_NXM_OF_IN_PORT:
flow->in_port = ntohs(get_unaligned_be16(value));
- if (flow->in_port == OFPP_LOCAL) {
- flow->in_port = ODPP_LOCAL;
- }
return 0;
/* Ethernet header. */
/* Metadata. */
if (!(wc & FWW_IN_PORT)) {
uint16_t in_port = flow->in_port;
- if (in_port == ODPP_LOCAL) {
- in_port = OFPP_LOCAL;
- }
nxm_put_16(b, NXM_OF_IN_PORT, htons(in_port));
}
{
switch (src->index) {
case NFI_NXM_OF_IN_PORT:
- return flow->in_port == ODPP_LOCAL ? OFPP_LOCAL : flow->in_port;
+ return flow->in_port;
case NFI_NXM_OF_ETH_DST:
return eth_addr_to_uint64(flow->dl_dst);
nl_msg_put_be64(buf, ODP_KEY_ATTR_TUN_ID, flow->tun_id);
}
- nl_msg_put_u32(buf, ODP_KEY_ATTR_IN_PORT, flow->in_port);
+ nl_msg_put_u32(buf, ODP_KEY_ATTR_IN_PORT,
+ ofp_port_to_odp_port(flow->in_port));
eth_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ETHERNET,
sizeof *eth_key);
if (nl_attr_get_u32(nla) >= UINT16_MAX) {
return EINVAL;
}
- flow->in_port = nl_attr_get_u32(nla);
+ flow->in_port = odp_port_to_ofp_port(nl_attr_get_u32(nla));
break;
case TRANSITION(ODP_KEY_ATTR_IN_PORT, ODP_KEY_ATTR_ETHERNET):
if (!parse_port_name(value, &port_no)) {
port_no = atoi(value);
}
- if (port_no == OFPP_LOCAL) {
- port_no = ODPP_LOCAL;
- }
cls_rule_set_in_port(rule, port_no);
break;
* man page) into 'pf'. If 'actions' is specified, an action must be in
* 'string' and may be expanded or reallocated. */
void
-parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
- struct ofpbuf *actions, char *string)
+parse_ofp_str(struct flow_mod *fm, struct ofpbuf *actions, char *string)
{
char *save_ptr = NULL;
char *name;
- if (table_idx) {
- *table_idx = 0xff;
- }
cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
fm->cookie = htonll(0);
+ fm->table_id = 0xff;
fm->command = UINT16_MAX;
fm->idle_timeout = OFP_FLOW_PERMANENT;
fm->hard_timeout = OFP_FLOW_PERMANENT;
ovs_fatal(0, "field %s missing value", name);
}
- if (table_idx && !strcmp(name, "table")) {
- *table_idx = atoi(value);
+ if (!strcmp(name, "table")) {
+ fm->table_id = atoi(value);
} else if (!strcmp(name, "out_port")) {
fm->out_port = atoi(value);
} else if (!strcmp(name, "priority")) {
* flow. */
void
parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
- char *string, uint16_t command)
+ bool *flow_mod_table_id, char *string, uint16_t command)
{
bool is_del = command == OFPFC_DELETE || command == OFPFC_DELETE_STRICT;
enum nx_flow_format min_format, next_format;
struct flow_mod fm;
ofpbuf_init(&actions, 64);
- parse_ofp_str(&fm, NULL, is_del ? NULL : &actions, string);
+ parse_ofp_str(&fm, is_del ? NULL : &actions, string);
fm.command = command;
min_format = ofputil_min_flow_format(&fm.cr);
*cur_format = next_format;
}
- ofm = ofputil_encode_flow_mod(&fm, *cur_format);
+ if (fm.table_id != 0xff && !*flow_mod_table_id) {
+ struct ofpbuf *sff = ofputil_make_flow_mod_table_id(true);
+ list_push_back(packets, &sff->list_node);
+ *flow_mod_table_id = true;
+ }
+
+ ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id);
list_push_back(packets, &ofm->list_node);
ofpbuf_uninit(&actions);
* 'stream' and the command is always OFPFC_ADD. Returns false if end-of-file
* is reached before reading a flow, otherwise true. */
bool
-parse_ofp_flow_mod_file(struct list *packets, enum nx_flow_format *cur,
+parse_ofp_flow_mod_file(struct list *packets,
+ enum nx_flow_format *cur, bool *flow_mod_table_id,
FILE *stream, uint16_t command)
{
struct ds s;
ds_init(&s);
ok = ds_get_preprocessed_line(&s, stream) == 0;
if (ok) {
- parse_ofp_flow_mod_str(packets, cur, ds_cstr(&s), command);
+ parse_ofp_flow_mod_str(packets, cur, flow_mod_table_id,
+ ds_cstr(&s), command);
}
ds_destroy(&s);
bool aggregate, char *string)
{
struct flow_mod fm;
- uint8_t table_id;
- parse_ofp_str(&fm, &table_id, NULL, string);
+ parse_ofp_str(&fm, NULL, string);
fsr->aggregate = aggregate;
fsr->match = fm.cr;
fsr->out_port = fm.out_port;
- fsr->table_id = table_id;
+ fsr->table_id = fm.table_id;
}
struct list;
struct ofpbuf;
-void parse_ofp_str(struct flow_mod *, uint8_t *table_idx,
- struct ofpbuf *actions, char *string);
+void parse_ofp_str(struct flow_mod *, struct ofpbuf *actions, char *string);
-void parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur,
+void parse_ofp_flow_mod_str(struct list *packets,
+ enum nx_flow_format *cur, bool *flow_mod_table_id,
char *string, uint16_t command);
-bool parse_ofp_flow_mod_file(struct list *packets, enum nx_flow_format *cur,
+bool parse_ofp_flow_mod_file(struct list *packets,
+ enum nx_flow_format *cur, bool *flow_mod_table_id,
FILE *, uint16_t command);
void parse_ofp_flow_stats_request_str(struct flow_stats_request *,
bool need_priority;
int error;
- error = ofputil_decode_flow_mod(&fm, oh);
+ error = ofputil_decode_flow_mod(&fm, oh, true);
if (error) {
ofp_print_error(s, error);
return;
default:
ds_put_format(s, "cmd:%d", fm.command);
}
+ if (fm.table_id != 0) {
+ ds_put_format(s, " table_id:%d", fm.table_id);
+ }
ds_put_char(s, ' ');
if (verbosity >= 3 && code == OFPUTIL_OFPT_FLOW_MOD) {
}
}
+static void
+ofp_print_nxt_flow_mod_table_id(struct ds *string,
+ const struct nxt_flow_mod_table_id *nfmti)
+{
+ ds_put_format(string, " %s", nfmti->set ? "enable" : "disable");
+}
+
static void
ofp_print_nxt_set_flow_format(struct ds *string,
const struct nxt_set_flow_format *nsff)
ofp_print_nxt_role_message(string, msg);
break;
+ case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
+ ofp_print_nxt_flow_mod_table_id(string, msg);
+ break;
+
case OFPUTIL_NXT_SET_FLOW_FORMAT:
ofp_print_nxt_set_flow_format(string, msg);
break;
/* Initialize most of rule->flow. */
rule->flow.nw_src = match->nw_src;
rule->flow.nw_dst = match->nw_dst;
- rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
- : ntohs(match->in_port));
+ rule->flow.in_port = ntohs(match->in_port);
rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type);
rule->flow.tp_src = match->tp_src;
rule->flow.tp_dst = match->tp_dst;
/* Compose most of the match structure. */
match->wildcards = htonl(ofpfw);
- match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL
- : rule->flow.in_port);
+ match->in_port = htons(rule->flow.in_port);
memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
ofputil_decode_vendor(const struct ofp_header *oh,
const struct ofputil_msg_type **typep)
{
+ BUILD_ASSERT_DECL(sizeof(struct nxt_set_flow_format)
+ != sizeof(struct nxt_flow_mod_table_id));
+
static const struct ofputil_msg_type nxt_messages[] = {
{ OFPUTIL_NXT_ROLE_REQUEST,
NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
}
nh = (const struct nicira_header *) oh;
+
+ if (nh->subtype == htonl(NXT_FLOW_MOD_TABLE_ID)
+ && oh->length == htons(sizeof(struct nxt_flow_mod_table_id))) {
+ /* NXT_SET_FLOW_FORMAT and NXT_FLOW_MOD_TABLE_ID accidentally have the
+ * same value but different lengths. ofputil_lookup_openflow_message()
+ * doesn't support this case, so special case it here. */
+ static const struct ofputil_msg_type nxt_flow_mod_table_id =
+ { OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
+ NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID",
+ sizeof(struct nxt_flow_mod_table_id), 0 };
+
+ *typep = &nxt_flow_mod_table_id;
+ return 0;
+ }
+
return ofputil_lookup_openflow_message(&nxt_category, ntohl(nh->subtype),
ntohs(oh->length), typep);
}
return msg;
}
+/* Returns an OpenFlow message that can be used to turn the flow_mod_table_id
+ * extension on or off (according to 'flow_mod_table_id'). */
+struct ofpbuf *
+ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
+{
+ struct nxt_flow_mod_table_id *nfmti;
+ struct ofpbuf *msg;
+
+ nfmti = make_nxmsg(sizeof *nfmti, NXT_FLOW_MOD_TABLE_ID, &msg);
+ nfmti->set = flow_mod_table_id;
+ return msg;
+}
+
/* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code.
*
+ * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
+ * enabled, false otherwise.
+ *
* Does not validate the flow_mod actions. */
int
-ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
+ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
+ bool flow_mod_table_id)
{
const struct ofputil_msg_type *type;
+ uint16_t command;
struct ofpbuf b;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
/* Translate the message. */
fm->cookie = ofm->cookie;
- fm->command = ntohs(ofm->command);
+ command = ntohs(ofm->command);
fm->idle_timeout = ntohs(ofm->idle_timeout);
fm->hard_timeout = ntohs(ofm->hard_timeout);
fm->buffer_id = ntohl(ofm->buffer_id);
/* Translate the message. */
fm->cookie = nfm->cookie;
- fm->command = ntohs(nfm->command);
+ command = ntohs(nfm->command);
fm->idle_timeout = ntohs(nfm->idle_timeout);
fm->hard_timeout = ntohs(nfm->hard_timeout);
fm->buffer_id = ntohl(nfm->buffer_id);
NOT_REACHED();
}
+ if (flow_mod_table_id) {
+ fm->command = command & 0xff;
+ fm->table_id = command >> 8;
+ } else {
+ fm->command = command;
+ fm->table_id = 0xff;
+ }
+
return 0;
}
/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
- * 'flow_format' and returns the message. */
+ * 'flow_format' and returns the message.
+ *
+ * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
+ * enabled, false otherwise. */
struct ofpbuf *
ofputil_encode_flow_mod(const struct flow_mod *fm,
- enum nx_flow_format flow_format)
+ enum nx_flow_format flow_format,
+ bool flow_mod_table_id)
{
size_t actions_len = fm->n_actions * sizeof *fm->actions;
struct ofpbuf *msg;
+ uint16_t command;
+
+ command = (flow_mod_table_id
+ ? (fm->command & 0xff) | (fm->table_id << 8)
+ : fm->command);
if (flow_format == NXFF_OPENFLOW10) {
struct ofp_flow_mod *ofm;
nfm = msg->data;
nfm->cookie = fm->cookie;
- nfm->command = htons(fm->command);
+ nfm->command = htons(command);
nfm->idle_timeout = htons(fm->idle_timeout);
nfm->hard_timeout = htons(fm->hard_timeout);
nfm->priority = htons(fm->cr.priority);
OFPUTIL_NXT_ROLE_REQUEST,
OFPUTIL_NXT_ROLE_REPLY,
OFPUTIL_NXT_SET_FLOW_FORMAT,
+ OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
OFPUTIL_NXT_FLOW_MOD,
OFPUTIL_NXT_FLOW_REMOVED,
struct ofpbuf *ofputil_make_set_flow_format(enum nx_flow_format);
+/* NXT_FLOW_MOD_TABLE_ID extension. */
+struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
+
/* Flow format independent flow_mod. */
struct flow_mod {
struct cls_rule cr;
ovs_be64 cookie;
+ uint8_t table_id;
uint16_t command;
uint16_t idle_timeout;
uint16_t hard_timeout;
size_t n_actions;
};
-int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *);
+int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *,
+ bool flow_mod_table_id);
struct ofpbuf *ofputil_encode_flow_mod(const struct flow_mod *,
- enum nx_flow_format);
+ enum nx_flow_format,
+ bool flow_mod_table_id);
/* Flow stats or aggregate stats request, independent of flow format. */
struct flow_stats_request {
--- /dev/null
+/* Copyright (c) 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 "vlan-bitmap.h"
+
+/* Allocates and returns a new 4096-bit bitmap that has 1-bit in positions in
+ * the 'n_vlans' bits indicated in 'vlans' and 0-bits everywhere else. Returns
+ * a null pointer if there are no (valid) VLANs in 'vlans'. */
+unsigned long *
+vlan_bitmap_from_array(const int64_t *vlans, size_t n_vlans)
+{
+ unsigned long *b;
+ size_t i, n;
+
+ if (!n_vlans) {
+ return NULL;
+ }
+
+ b = bitmap_allocate(4096);
+ n = 0;
+ for (i = 0; i < n_vlans; i++) {
+ int64_t vlan = vlans[i];
+
+ if (vlan >= 0 && vlan < 4096) {
+ bitmap_set1(b, vlan);
+ n++;
+ }
+ }
+
+ if (!n) {
+ free(b);
+ return NULL;
+ }
+
+ return b;
+}
+
+/* Returns true if 'a' and 'b' are the same: either both null or both the same
+ * 4096-bit bitmap.
+ *
+ * (We assume that a nonnull bitmap is not all 0-bits.) */
+bool
+vlan_bitmap_equal(const unsigned long *a, const unsigned long *b)
+{
+ return (!a && !b) || (a && b && bitmap_equal(a, b, 4096));
+}
--- /dev/null
+/* Copyright (c) 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.
+ */
+
+#ifndef VLAN_BITMAP_H
+#define VLAN_BITMAP_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bitmap.h"
+
+/* A "VLAN bitmap" is a 4096-bit bitmap that represents a set. A 1-bit
+ * indicates that the respective VLAN is a member of the set, a 0-bit indicates
+ * that it is not. There is one wrinkle: NULL indicates that every VLAN is a
+ * member of the set.
+ *
+ * This is empirically a useful data structure. */
+
+unsigned long *vlan_bitmap_from_array(const int64_t *vlans, size_t n_vlans);
+bool vlan_bitmap_equal(const unsigned long *a, const unsigned long *b);
+
+/* Returns true if 'vid', in the range [0,4095], is a member of 'vlans'. */
+static inline bool
+vlan_bitmap_contains(const unsigned long *vlans, uint16_t vid)
+{
+ return !vlans || bitmap_is_set(vlans, vid);
+}
+
+/* Returns a new copy of 'vlans'. */
+static inline unsigned long *
+vlan_bitmap_clone(const unsigned long *vlans)
+{
+ return vlans ? bitmap_clone(vlans, 4096) : NULL;
+}
+
+#endif /* lib/vlan-bitmap.h */
ofproto/fail-open.h \
ofproto/in-band.c \
ofproto/in-band.h \
+ ofproto/names.c \
ofproto/netflow.c \
ofproto/netflow.h \
ofproto/ofproto.c \
ofproto/ofproto.h \
+ ofproto/ofproto-dpif.c \
ofproto/ofproto-sflow.c \
ofproto/ofproto-sflow.h \
ofproto/pktbuf.c \
ofproto/pktbuf.h \
ofproto/pinsched.c \
- ofproto/pinsched.h
+ ofproto/pinsched.h \
+ ofproto/private.h
EXTRA_DIST += ofproto/ofproto-unixctl.man
#include <stdlib.h>
#include "coverage.h"
-#include "dpif.h"
#include "fail-open.h"
#include "in-band.h"
#include "odp-util.h"
#include "pinsched.h"
#include "poll-loop.h"
#include "pktbuf.h"
+#include "private.h"
#include "rconn.h"
#include "shash.h"
#include "timeval.h"
struct rconn *rconn; /* OpenFlow connection. */
enum ofconn_type type; /* Type. */
enum nx_flow_format flow_format; /* Currently selected flow format. */
+ bool flow_mod_table_id; /* NXT_FLOW_MOD_TABLE_ID enabled? */
/* OFPT_PACKET_IN related data. */
struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */
const struct ofproto_controller *controllers,
size_t n_controllers)
{
+ bool had_controllers = connmgr_has_controllers(mgr);
struct shash new_controllers;
struct ofconn *ofconn, *next_ofconn;
struct ofservice *ofservice, *next_ofservice;
update_in_band_remotes(mgr);
update_fail_open(mgr);
+ if (had_controllers != connmgr_has_controllers(mgr)) {
+ ofproto_flush_flows(mgr->ofproto);
+ }
}
/* Drops the connections between 'mgr' and all of its primary and secondary
ofconn->flow_format = flow_format;
}
+/* Returns true if the NXT_FLOW_MOD_TABLE_ID extension is enabled, false
+ * otherwise.
+ *
+ * By default the extension is not enabled. */
+bool
+ofconn_get_flow_mod_table_id(const struct ofconn *ofconn)
+{
+ return ofconn->flow_mod_table_id;
+}
+
+/* Enables or disables (according to 'enable') the NXT_FLOW_MOD_TABLE_ID
+ * extension on 'ofconn'. */
+void
+ofconn_set_flow_mod_table_id(struct ofconn *ofconn, bool enable)
+{
+ ofconn->flow_mod_table_id = enable;
+}
+
/* Returns the default miss send length for 'ofconn'. */
int
ofconn_get_miss_send_len(const struct ofconn *ofconn)
ofconn->rconn = rconn;
ofconn->type = type;
ofconn->flow_format = NXFF_OPENFLOW10;
+ ofconn->flow_mod_table_id = false;
ofconn->role = NX_ROLE_OTHER;
ofconn->packet_in_counter = rconn_packet_counter_create ();
ofconn->pktbuf = NULL;
\f
/* Sending asynchronous messages. */
-static void schedule_packet_in(struct ofconn *, const struct dpif_upcall *,
+static void schedule_packet_in(struct ofconn *, struct ofputil_packet_in,
const struct flow *, struct ofpbuf *rw_packet);
/* Sends an OFPT_PORT_STATUS message with 'opp' and 'reason' to appropriate
}
}
-/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an
- * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
- * their individual configurations.
+/* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as
+ * necessary according to their individual configurations.
*
* 'rw_packet' may be NULL. Otherwise, 'rw_packet' must contain the same data
- * as upcall->packet. (rw_packet == upcall->packet is also valid.) Ownership
- * of 'rw_packet' is transferred to this function. */
+ * as pin->packet. (rw_packet == pin->packet is also valid.) Ownership of
+ * 'rw_packet' is transferred to this function. */
void
-connmgr_send_packet_in(struct connmgr *mgr, const struct dpif_upcall *upcall,
+connmgr_send_packet_in(struct connmgr *mgr,
+ const struct ofputil_packet_in *pin,
const struct flow *flow, struct ofpbuf *rw_packet)
{
struct ofconn *ofconn, *prev;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
if (ofconn_receives_async_msgs(ofconn)) {
if (prev) {
- schedule_packet_in(prev, upcall, flow, NULL);
+ schedule_packet_in(prev, *pin, flow, NULL);
}
prev = ofconn;
}
}
if (prev) {
- schedule_packet_in(prev, upcall, flow, rw_packet);
+ schedule_packet_in(prev, *pin, flow, rw_packet);
} else {
ofpbuf_delete(rw_packet);
}
ofconn->packet_in_counter, 100);
}
-/* Takes 'upcall', whose packet has the flow specified by 'flow', composes an
+/* Takes 'pin', whose packet has the flow specified by 'flow', composes an
* OpenFlow packet-in message from it, and passes it to 'ofconn''s packet
* scheduler for sending.
*
* 'rw_packet' may be NULL. Otherwise, 'rw_packet' must contain the same data
- * as upcall->packet. (rw_packet == upcall->packet is also valid.) Ownership
- * of 'rw_packet' is transferred to this function. */
+ * as pin->packet. (rw_packet == pin->packet is also valid.) Ownership of
+ * 'rw_packet' is transferred to this function. */
static void
-schedule_packet_in(struct ofconn *ofconn, const struct dpif_upcall *upcall,
+schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin,
const struct flow *flow, struct ofpbuf *rw_packet)
{
struct connmgr *mgr = ofconn->connmgr;
- struct ofputil_packet_in pin;
-
- /* Figure out the easy parts. */
- pin.packet = upcall->packet;
- pin.in_port = odp_port_to_ofp_port(flow->in_port);
- pin.reason = upcall->type == DPIF_UC_MISS ? OFPR_NO_MATCH : OFPR_ACTION;
/* Get OpenFlow buffer_id. */
- if (upcall->type == DPIF_UC_ACTION) {
+ if (pin.reason == OFPR_ACTION) {
pin.buffer_id = UINT32_MAX;
} else if (mgr->fail_open && fail_open_is_active(mgr->fail_open)) {
pin.buffer_id = pktbuf_get_null();
} else if (!ofconn->pktbuf) {
pin.buffer_id = UINT32_MAX;
} else {
- pin.buffer_id = pktbuf_save(ofconn->pktbuf, upcall->packet,
- flow->in_port);
+ pin.buffer_id = pktbuf_save(ofconn->pktbuf, pin.packet, flow->in_port);
}
/* Figure out how much of the packet to send. */
- pin.send_len = upcall->packet->size;
+ if (pin.reason == OFPR_NO_MATCH) {
+ pin.send_len = pin.packet->size;
+ } else {
+ /* Caller should have initialized 'send_len' to 'max_len' specified in
+ * struct ofp_action_output. */
+ }
if (pin.buffer_id != UINT32_MAX) {
pin.send_len = MIN(pin.send_len, ofconn->miss_send_len);
}
- if (upcall->type == DPIF_UC_ACTION) {
- pin.send_len = MIN(pin.send_len, upcall->userdata);
- }
/* Make OFPT_PACKET_IN and hand over to packet scheduler. It might
* immediately call into do_send_packet_in() or it might buffer it for a
* while (until a later call to pinsched_run()). */
- pinsched_send(ofconn->schedulers[upcall->type == DPIF_UC_MISS ? 0 : 1],
+ pinsched_send(ofconn->schedulers[pin.reason == OFPR_NO_MATCH ? 0 : 1],
flow->in_port, ofputil_encode_packet_in(&pin, rw_packet),
do_send_packet_in, ofconn);
}
void
connmgr_set_fail_mode(struct connmgr *mgr, enum ofproto_fail_mode fail_mode)
{
- mgr->fail_mode = fail_mode;
- update_fail_open(mgr);
+ if (mgr->fail_mode != fail_mode) {
+ mgr->fail_mode = fail_mode;
+ update_fail_open(mgr);
+ if (!connmgr_has_controllers(mgr)) {
+ ofproto_flush_flows(mgr->ofproto);
+ }
+ }
}
\f
/* Fail-open implementation. */
if (mgr->fail_open) {
fail_open_flushed(mgr->fail_open);
}
+
+ /* If there are no controllers and we're in standalone mode, set up a flow
+ * that matches every packet and directs them to OFPP_NORMAL (which goes to
+ * us). Otherwise, the switch is in secure mode and we won't pass any
+ * traffic until a controller has been defined and it tells us to do so. */
+ if (!connmgr_has_controllers(mgr)
+ && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
+ union ofp_action action;
+ struct cls_rule rule;
+
+ memset(&action, 0, sizeof action);
+ action.type = htons(OFPAT_OUTPUT);
+ action.output.len = htons(sizeof action);
+ action.output.port = htons(OFPP_NORMAL);
+ cls_rule_init_catchall(&rule, 0);
+ ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
+ }
}
\f
/* Creates a new ofservice for 'target' in 'mgr'. Returns 0 if successful,
#include "openflow/nicira-ext.h"
#include "openvswitch/types.h"
-struct dpif_upcall;
struct ofconn;
struct ofputil_flow_removed;
+struct ofputil_packet_in;
struct sset;
/* ofproto supports two kinds of OpenFlow connections:
enum nx_flow_format ofconn_get_flow_format(struct ofconn *);
void ofconn_set_flow_format(struct ofconn *, enum nx_flow_format);
+bool ofconn_get_flow_mod_table_id(const struct ofconn *);
+void ofconn_set_flow_mod_table_id(struct ofconn *, bool enable);
+
int ofconn_get_miss_send_len(const struct ofconn *);
void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len);
uint8_t reason);
void connmgr_send_flow_removed(struct connmgr *,
const struct ofputil_flow_removed *);
-void connmgr_send_packet_in(struct connmgr *, const struct dpif_upcall *,
+void connmgr_send_packet_in(struct connmgr *, const struct ofputil_packet_in *,
const struct flow *, struct ofpbuf *rw_packet);
/* Fail-open settings. */
#include "ofproto.h"
#include "pktbuf.h"
#include "poll-loop.h"
+#include "private.h"
#include "rconn.h"
#include "timeval.h"
#include "vconn.h"
#include "openflow/openflow.h"
#include "packets.h"
#include "poll-loop.h"
+#include "private.h"
#include "timeval.h"
#include "vlog.h"
--- /dev/null
+/*
+ * Copyright (c) 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 "ofproto/ofproto.h"
+
+#include "dpif.h"
+
+/* This function is in a separate file because it is the only ofproto function
+ * required by ovs-ofctl, which allows ovs-ofctl to avoid linking in extra
+ * baggage. */
+
+/* Parses 'ofproto_name', which is of the form [type@]name into component
+ * pieces that are suitable for passing to ofproto_create(). The caller must
+ * free 'name' and 'type'. */
+void
+ofproto_parse_name(const char *ofproto_name, char **name, char **type)
+{
+ dp_parse_name(ofproto_name, name, type);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 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 "ofproto/private.h"
+
+#include <errno.h>
+
+#include "autopath.h"
+#include "bond.h"
+#include "byte-order.h"
+#include "connmgr.h"
+#include "coverage.h"
+#include "cfm.h"
+#include "dpif.h"
+#include "dynamic-string.h"
+#include "fail-open.h"
+#include "hmapx.h"
+#include "lacp.h"
+#include "mac-learning.h"
+#include "multipath.h"
+#include "netdev.h"
+#include "netlink.h"
+#include "nx-match.h"
+#include "odp-util.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "ofp-print.h"
+#include "ofproto-sflow.h"
+#include "poll-loop.h"
+#include "timer.h"
+#include "unaligned.h"
+#include "unixctl.h"
+#include "vlan-bitmap.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
+
+COVERAGE_DEFINE(ofproto_dpif_ctlr_action);
+COVERAGE_DEFINE(ofproto_dpif_expired);
+COVERAGE_DEFINE(ofproto_dpif_no_packet_in);
+COVERAGE_DEFINE(ofproto_dpif_xlate);
+COVERAGE_DEFINE(facet_changed_rule);
+COVERAGE_DEFINE(facet_invalidated);
+COVERAGE_DEFINE(facet_revalidate);
+COVERAGE_DEFINE(facet_unexpected);
+
+/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
+ * flow translation. */
+#define MAX_RESUBMIT_RECURSION 16
+
+struct ofport_dpif;
+struct ofproto_dpif;
+
+struct rule_dpif {
+ struct rule up;
+
+ long long int used; /* Time last used; time created if not used. */
+
+ /* These statistics:
+ *
+ * - Do include packets and bytes from facets that have been deleted or
+ * whose own statistics have been folded into the rule.
+ *
+ * - Do include packets and bytes sent "by hand" that were accounted to
+ * the rule without any facet being involved (this is a rare corner
+ * case in rule_execute()).
+ *
+ * - Do not include packet or bytes that can be obtained from any facet's
+ * packet_count or byte_count member or that can be obtained from the
+ * datapath by, e.g., dpif_flow_get() for any facet.
+ */
+ uint64_t packet_count; /* Number of packets received. */
+ uint64_t byte_count; /* Number of bytes received. */
+
+ struct list facets; /* List of "struct facet"s. */
+};
+
+static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
+{
+ return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
+}
+
+static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *ofproto,
+ const struct flow *flow);
+
+#define MAX_MIRRORS 32
+typedef uint32_t mirror_mask_t;
+#define MIRROR_MASK_C(X) UINT32_C(X)
+BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
+struct ofmirror {
+ struct ofproto_dpif *ofproto; /* Owning ofproto. */
+ size_t idx; /* In ofproto's "mirrors" array. */
+ void *aux; /* Key supplied by ofproto's client. */
+ char *name; /* Identifier for log messages. */
+
+ /* Selection criteria. */
+ struct hmapx srcs; /* Contains "struct ofbundle *"s. */
+ struct hmapx dsts; /* Contains "struct ofbundle *"s. */
+ unsigned long *vlans; /* Bitmap of chosen VLANs, NULL selects all. */
+
+ /* Output (mutually exclusive). */
+ struct ofbundle *out; /* Output port or NULL. */
+ int out_vlan; /* Output VLAN or -1. */
+};
+
+static void mirror_destroy(struct ofmirror *);
+
+/* A group of one or more OpenFlow ports. */
+#define OFBUNDLE_FLOOD ((struct ofbundle *) 1)
+struct ofbundle {
+ struct ofproto_dpif *ofproto; /* Owning ofproto. */
+ struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
+ void *aux; /* Key supplied by ofproto's client. */
+ char *name; /* Identifier for log messages. */
+
+ /* Configuration. */
+ struct list ports; /* Contains "struct ofport"s. */
+ int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
+ unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
+ * NULL if all VLANs are trunked. */
+ struct lacp *lacp; /* LACP if LACP is enabled, otherwise NULL. */
+ struct bond *bond; /* Nonnull iff more than one port. */
+
+ /* Status. */
+ bool floodable; /* True if no port has OFPPC_NO_FLOOD set. */
+
+ /* Port mirroring info. */
+ mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */
+ mirror_mask_t dst_mirrors; /* Mirrors triggered when packet sent. */
+ mirror_mask_t mirror_out; /* Mirrors that output to this bundle. */
+};
+
+static void bundle_remove(struct ofport *);
+static void bundle_destroy(struct ofbundle *);
+static void bundle_del_port(struct ofport_dpif *);
+static void bundle_run(struct ofbundle *);
+static void bundle_wait(struct ofbundle *);
+
+struct action_xlate_ctx {
+/* action_xlate_ctx_init() initializes these members. */
+
+ /* The ofproto. */
+ struct ofproto_dpif *ofproto;
+
+ /* Flow to which the OpenFlow actions apply. xlate_actions() will modify
+ * this flow when actions change header fields. */
+ struct flow flow;
+
+ /* The packet corresponding to 'flow', or a null pointer if we are
+ * revalidating without a packet to refer to. */
+ const struct ofpbuf *packet;
+
+ /* If nonnull, called just before executing a resubmit action.
+ *
+ * This is normally null so the client has to set it manually after
+ * calling action_xlate_ctx_init(). */
+ void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *);
+
+/* xlate_actions() initializes and uses these members. The client might want
+ * to look at them after it returns. */
+
+ struct ofpbuf *odp_actions; /* Datapath actions. */
+ tag_type tags; /* Tags associated with OFPP_NORMAL actions. */
+ bool may_set_up_flow; /* True ordinarily; false if the actions must
+ * be reassessed for every packet. */
+ uint16_t nf_output_iface; /* Output interface index for NetFlow. */
+
+/* xlate_actions() initializes and uses these members, but the client has no
+ * reason to look at them. */
+
+ int recurse; /* Recursion level, via xlate_table_action. */
+ int last_pop_priority; /* Offset in 'odp_actions' just past most
+ * recent ODP_ACTION_ATTR_SET_PRIORITY. */
+};
+
+static void action_xlate_ctx_init(struct action_xlate_ctx *,
+ struct ofproto_dpif *, const struct flow *,
+ const struct ofpbuf *);
+static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
+ const union ofp_action *in, size_t n_in);
+
+/* An exact-match instantiation of an OpenFlow flow. */
+struct facet {
+ long long int used; /* Time last used; time created if not used. */
+
+ /* These statistics:
+ *
+ * - Do include packets and bytes sent "by hand", e.g. with
+ * dpif_execute().
+ *
+ * - Do include packets and bytes that were obtained from the datapath
+ * when a flow was deleted (e.g. dpif_flow_del()) or when its
+ * statistics were reset (e.g. dpif_flow_put() with
+ * DPIF_FP_ZERO_STATS).
+ *
+ * - Do not include any packets or bytes that can currently be obtained
+ * from the datapath by, e.g., dpif_flow_get().
+ */
+ uint64_t packet_count; /* Number of packets received. */
+ uint64_t byte_count; /* Number of bytes received. */
+
+ uint64_t dp_packet_count; /* Last known packet count in the datapath. */
+ uint64_t dp_byte_count; /* Last known byte count in the datapath. */
+
+ uint64_t rs_packet_count; /* Packets pushed to resubmit children. */
+ uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */
+ long long int rs_used; /* Used time pushed to resubmit children. */
+
+ /* Number of bytes passed to account_cb. This may include bytes that can
+ * currently obtained from the datapath (thus, it can be greater than
+ * byte_count). */
+ uint64_t accounted_bytes;
+
+ struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */
+ struct list list_node; /* In owning rule's 'facets' list. */
+ struct rule_dpif *rule; /* Owning rule. */
+ struct flow flow; /* Exact-match flow. */
+ bool installed; /* Installed in datapath? */
+ bool may_install; /* True ordinarily; false if actions must
+ * be reassessed for every packet. */
+ size_t actions_len; /* Number of bytes in actions[]. */
+ struct nlattr *actions; /* Datapath actions. */
+ tag_type tags; /* Tags. */
+ struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
+};
+
+static struct facet *facet_create(struct rule_dpif *, const struct flow *,
+ const struct ofpbuf *packet);
+static void facet_remove(struct ofproto_dpif *, struct facet *);
+static void facet_free(struct facet *);
+
+static struct facet *facet_find(struct ofproto_dpif *, const struct flow *);
+static struct facet *facet_lookup_valid(struct ofproto_dpif *,
+ const struct flow *);
+static bool facet_revalidate(struct ofproto_dpif *, struct facet *);
+
+static void facet_execute(struct ofproto_dpif *, struct facet *,
+ struct ofpbuf *packet);
+
+static int facet_put__(struct ofproto_dpif *, struct facet *,
+ const struct nlattr *actions, size_t actions_len,
+ struct dpif_flow_stats *);
+static void facet_install(struct ofproto_dpif *, struct facet *,
+ bool zero_stats);
+static void facet_uninstall(struct ofproto_dpif *, struct facet *);
+static void facet_flush_stats(struct ofproto_dpif *, struct facet *);
+
+static void facet_make_actions(struct ofproto_dpif *, struct facet *,
+ const struct ofpbuf *packet);
+static void facet_update_time(struct ofproto_dpif *, struct facet *,
+ long long int used);
+static void facet_update_stats(struct ofproto_dpif *, struct facet *,
+ const struct dpif_flow_stats *);
+static void facet_push_stats(struct facet *);
+static void facet_account(struct ofproto_dpif *, struct facet *,
+ uint64_t extra_bytes);
+
+static bool facet_is_controller_flow(struct facet *);
+
+static void flow_push_stats(const struct rule_dpif *,
+ struct flow *, uint64_t packets, uint64_t bytes,
+ long long int used);
+
+struct ofport_dpif {
+ struct ofport up;
+
+ uint32_t odp_port;
+ struct ofbundle *bundle; /* Bundle that contains this port, if any. */
+ struct list bundle_node; /* In struct ofbundle's "ports" list. */
+ struct cfm *cfm; /* Connectivity Fault Management, if any. */
+ tag_type tag; /* Tag associated with this port. */
+ uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */
+};
+
+static struct ofport_dpif *
+ofport_dpif_cast(const struct ofport *ofport)
+{
+ assert(ofport->ofproto->ofproto_class == &ofproto_dpif_class);
+ return ofport ? CONTAINER_OF(ofport, struct ofport_dpif, up) : NULL;
+}
+
+static void port_run(struct ofport_dpif *);
+static void port_wait(struct ofport_dpif *);
+static int set_cfm(struct ofport *, const struct cfm *,
+ const uint16_t *remote_mps, size_t n_remote_mps);
+
+struct ofproto_dpif {
+ struct ofproto up;
+ struct dpif *dpif;
+ int max_ports;
+
+ /* Statistics. */
+ uint64_t n_matches;
+
+ /* Bridging. */
+ struct netflow *netflow;
+ struct ofproto_sflow *sflow;
+ struct hmap bundles; /* Contains "struct ofbundle"s. */
+ struct mac_learning *ml;
+ struct ofmirror *mirrors[MAX_MIRRORS];
+ bool has_bonded_bundles;
+
+ /* Expiration. */
+ struct timer next_expiration;
+
+ /* Facets. */
+ struct hmap facets;
+ bool need_revalidate;
+ struct tag_set revalidate_set;
+};
+
+static void ofproto_dpif_unixctl_init(void);
+
+static struct ofproto_dpif *
+ofproto_dpif_cast(const struct ofproto *ofproto)
+{
+ assert(ofproto->ofproto_class == &ofproto_dpif_class);
+ return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
+}
+
+static struct ofport_dpif *get_ofp_port(struct ofproto_dpif *,
+ uint16_t ofp_port);
+static struct ofport_dpif *get_odp_port(struct ofproto_dpif *,
+ uint32_t odp_port);
+
+/* Packet processing. */
+static void update_learning_table(struct ofproto_dpif *,
+ const struct flow *, int vlan,
+ struct ofbundle *);
+static bool is_admissible(struct ofproto_dpif *, const struct flow *,
+ bool have_packet, tag_type *, int *vlanp,
+ struct ofbundle **in_bundlep);
+static void handle_upcall(struct ofproto_dpif *, struct dpif_upcall *);
+
+/* Flow expiration. */
+static int expire(struct ofproto_dpif *);
+
+/* Utilities. */
+static int send_packet(struct ofproto_dpif *, uint32_t odp_port,
+ const struct ofpbuf *packet);
+
+/* Global variables. */
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+\f
+/* Factory functions. */
+
+static void
+enumerate_types(struct sset *types)
+{
+ dp_enumerate_types(types);
+}
+
+static int
+enumerate_names(const char *type, struct sset *names)
+{
+ return dp_enumerate_names(type, names);
+}
+
+static int
+del(const char *type, const char *name)
+{
+ struct dpif *dpif;
+ int error;
+
+ error = dpif_open(name, type, &dpif);
+ if (!error) {
+ error = dpif_delete(dpif);
+ dpif_close(dpif);
+ }
+ return error;
+}
+\f
+/* Basic life-cycle. */
+
+static struct ofproto *
+alloc(void)
+{
+ struct ofproto_dpif *ofproto = xmalloc(sizeof *ofproto);
+ return &ofproto->up;
+}
+
+static void
+dealloc(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ free(ofproto);
+}
+
+static int
+construct(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ const char *name = ofproto->up.name;
+ int error;
+ int i;
+
+ error = dpif_create_and_open(name, ofproto->up.type, &ofproto->dpif);
+ if (error) {
+ VLOG_ERR("failed to open datapath %s: %s", name, strerror(error));
+ return error;
+ }
+
+ ofproto->max_ports = dpif_get_max_ports(ofproto->dpif);
+ ofproto->n_matches = 0;
+
+ error = dpif_recv_set_mask(ofproto->dpif,
+ ((1u << DPIF_UC_MISS) |
+ (1u << DPIF_UC_ACTION) |
+ (1u << DPIF_UC_SAMPLE)));
+ if (error) {
+ VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error));
+ dpif_close(ofproto->dpif);
+ return error;
+ }
+ dpif_flow_flush(ofproto->dpif);
+ dpif_recv_purge(ofproto->dpif);
+
+ ofproto->netflow = NULL;
+ ofproto->sflow = NULL;
+ hmap_init(&ofproto->bundles);
+ ofproto->ml = mac_learning_create();
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ ofproto->mirrors[i] = NULL;
+ }
+ ofproto->has_bonded_bundles = false;
+
+ timer_set_duration(&ofproto->next_expiration, 1000);
+
+ hmap_init(&ofproto->facets);
+ ofproto->need_revalidate = false;
+ tag_set_init(&ofproto->revalidate_set);
+
+ ofproto->up.tables = xmalloc(sizeof *ofproto->up.tables);
+ classifier_init(&ofproto->up.tables[0]);
+ ofproto->up.n_tables = 1;
+
+ ofproto_dpif_unixctl_init();
+
+ return 0;
+}
+
+static void
+destruct(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ int i;
+
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ mirror_destroy(ofproto->mirrors[i]);
+ }
+
+ netflow_destroy(ofproto->netflow);
+ ofproto_sflow_destroy(ofproto->sflow);
+ hmap_destroy(&ofproto->bundles);
+ mac_learning_destroy(ofproto->ml);
+
+ hmap_destroy(&ofproto->facets);
+
+ dpif_close(ofproto->dpif);
+}
+
+static int
+run(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct ofport_dpif *ofport;
+ struct ofbundle *bundle;
+ int i;
+
+ dpif_run(ofproto->dpif);
+
+ for (i = 0; i < 50; i++) {
+ struct dpif_upcall packet;
+ int error;
+
+ error = dpif_recv(ofproto->dpif, &packet);
+ if (error) {
+ if (error == ENODEV) {
+ /* Datapath destroyed. */
+ return error;
+ }
+ break;
+ }
+
+ handle_upcall(ofproto, &packet);
+ }
+
+ if (timer_expired(&ofproto->next_expiration)) {
+ int delay = expire(ofproto);
+ timer_set_duration(&ofproto->next_expiration, delay);
+ }
+
+ if (ofproto->netflow) {
+ netflow_run(ofproto->netflow);
+ }
+ if (ofproto->sflow) {
+ ofproto_sflow_run(ofproto->sflow);
+ }
+
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ port_run(ofport);
+ }
+ HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+ bundle_run(bundle);
+ }
+
+ /* Now revalidate if there's anything to do. */
+ if (ofproto->need_revalidate
+ || !tag_set_is_empty(&ofproto->revalidate_set)) {
+ struct tag_set revalidate_set = ofproto->revalidate_set;
+ bool revalidate_all = ofproto->need_revalidate;
+ struct facet *facet, *next;
+
+ /* Clear the revalidation flags. */
+ tag_set_init(&ofproto->revalidate_set);
+ ofproto->need_revalidate = false;
+
+ HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
+ if (revalidate_all
+ || tag_set_intersects(&revalidate_set, facet->tags)) {
+ facet_revalidate(ofproto, facet);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void
+wait(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct ofport_dpif *ofport;
+ struct ofbundle *bundle;
+
+ dpif_wait(ofproto->dpif);
+ dpif_recv_wait(ofproto->dpif);
+ if (ofproto->sflow) {
+ ofproto_sflow_wait(ofproto->sflow);
+ }
+ if (!tag_set_is_empty(&ofproto->revalidate_set)) {
+ poll_immediate_wake();
+ }
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ port_wait(ofport);
+ }
+ HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+ bundle_wait(bundle);
+ }
+ if (ofproto->need_revalidate) {
+ /* Shouldn't happen, but if it does just go around again. */
+ VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
+ poll_immediate_wake();
+ } else {
+ timer_wait(&ofproto->next_expiration);
+ }
+}
+
+static void
+flush(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct facet *facet, *next_facet;
+
+ HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
+ /* Mark the facet as not installed so that facet_remove() doesn't
+ * bother trying to uninstall it. There is no point in uninstalling it
+ * individually since we are about to blow away all the facets with
+ * dpif_flow_flush(). */
+ facet->installed = false;
+ facet->dp_packet_count = 0;
+ facet->dp_byte_count = 0;
+ facet_remove(ofproto, facet);
+ }
+ dpif_flow_flush(ofproto->dpif);
+}
+
+static void
+get_features(struct ofproto *ofproto_ OVS_UNUSED,
+ bool *arp_match_ip, uint32_t *actions)
+{
+ *arp_match_ip = true;
+ *actions = ((1u << OFPAT_OUTPUT) |
+ (1u << OFPAT_SET_VLAN_VID) |
+ (1u << OFPAT_SET_VLAN_PCP) |
+ (1u << OFPAT_STRIP_VLAN) |
+ (1u << OFPAT_SET_DL_SRC) |
+ (1u << OFPAT_SET_DL_DST) |
+ (1u << OFPAT_SET_NW_SRC) |
+ (1u << OFPAT_SET_NW_DST) |
+ (1u << OFPAT_SET_NW_TOS) |
+ (1u << OFPAT_SET_TP_SRC) |
+ (1u << OFPAT_SET_TP_DST) |
+ (1u << OFPAT_ENQUEUE));
+}
+
+static void
+get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct odp_stats s;
+
+ strcpy(ots->name, "classifier");
+
+ dpif_get_dp_stats(ofproto->dpif, &s);
+ put_32aligned_be64(&ots->lookup_count, htonll(s.n_hit + s.n_missed));
+ put_32aligned_be64(&ots->matched_count,
+ htonll(s.n_hit + ofproto->n_matches));
+}
+
+static int
+set_netflow(struct ofproto *ofproto_,
+ const struct netflow_options *netflow_options)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ if (netflow_options) {
+ if (!ofproto->netflow) {
+ ofproto->netflow = netflow_create();
+ }
+ return netflow_set_options(ofproto->netflow, netflow_options);
+ } else {
+ netflow_destroy(ofproto->netflow);
+ ofproto->netflow = NULL;
+ return 0;
+ }
+}
+
+static struct ofport *
+port_alloc(void)
+{
+ struct ofport_dpif *port = xmalloc(sizeof *port);
+ return &port->up;
+}
+
+static void
+port_dealloc(struct ofport *port_)
+{
+ struct ofport_dpif *port = ofport_dpif_cast(port_);
+ free(port);
+}
+
+static int
+port_construct(struct ofport *port_)
+{
+ struct ofport_dpif *port = ofport_dpif_cast(port_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+
+ port->odp_port = ofp_port_to_odp_port(port->up.ofp_port);
+ port->bundle = NULL;
+ port->cfm = NULL;
+ port->tag = tag_create_random();
+
+ if (ofproto->sflow) {
+ ofproto_sflow_add_port(ofproto->sflow, port->odp_port,
+ netdev_get_name(port->up.netdev));
+ }
+
+ return 0;
+}
+
+static void
+port_destruct(struct ofport *port_)
+{
+ struct ofport_dpif *port = ofport_dpif_cast(port_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+
+ bundle_remove(port_);
+ set_cfm(port_, NULL, NULL, 0);
+ if (ofproto->sflow) {
+ ofproto_sflow_del_port(ofproto->sflow, port->odp_port);
+ }
+}
+
+static void
+port_modified(struct ofport *port_)
+{
+ struct ofport_dpif *port = ofport_dpif_cast(port_);
+
+ if (port->bundle && port->bundle->bond) {
+ bond_slave_set_netdev(port->bundle->bond, port, port->up.netdev);
+ }
+}
+
+static void
+port_reconfigured(struct ofport *port_, ovs_be32 old_config)
+{
+ struct ofport_dpif *port = ofport_dpif_cast(port_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ ovs_be32 changed = old_config ^ port->up.opp.config;
+
+ if (changed & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP |
+ OFPPC_NO_FWD | OFPPC_NO_FLOOD)) {
+ ofproto->need_revalidate = true;
+ }
+}
+
+static int
+set_sflow(struct ofproto *ofproto_,
+ const struct ofproto_sflow_options *sflow_options)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct ofproto_sflow *os = ofproto->sflow;
+ if (sflow_options) {
+ if (!os) {
+ struct ofport_dpif *ofport;
+
+ os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif);
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ ofproto_sflow_add_port(os, ofport->odp_port,
+ netdev_get_name(ofport->up.netdev));
+ }
+ }
+ ofproto_sflow_set_options(os, sflow_options);
+ } else {
+ ofproto_sflow_destroy(os);
+ ofproto->sflow = NULL;
+ }
+ return 0;
+}
+
+static int
+set_cfm(struct ofport *ofport_, const struct cfm *cfm,
+ const uint16_t *remote_mps, size_t n_remote_mps)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ int error;
+
+ if (!cfm) {
+ error = 0;
+ } else {
+ if (!ofport->cfm) {
+ ofport->cfm = cfm_create();
+ }
+
+ ofport->cfm->mpid = cfm->mpid;
+ ofport->cfm->interval = cfm->interval;
+ memcpy(ofport->cfm->maid, cfm->maid, CCM_MAID_LEN);
+
+ cfm_update_remote_mps(ofport->cfm, remote_mps, n_remote_mps);
+
+ if (cfm_configure(ofport->cfm)) {
+ return 0;
+ }
+
+ error = EINVAL;
+ }
+ cfm_destroy(ofport->cfm);
+ ofport->cfm = NULL;
+ return error;
+}
+
+static int
+get_cfm(struct ofport *ofport_, const struct cfm **cfmp)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ *cfmp = ofport->cfm;
+ return 0;
+}
+\f
+/* Bundles. */
+
+/* Expires all MAC learning entries associated with 'port' and forces ofproto
+ * to revalidate every flow. */
+static void
+bundle_flush_macs(struct ofbundle *bundle)
+{
+ struct ofproto_dpif *ofproto = bundle->ofproto;
+ struct mac_learning *ml = ofproto->ml;
+ struct mac_entry *mac, *next_mac;
+
+ ofproto->need_revalidate = true;
+ LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
+ if (mac->port.p == bundle) {
+ mac_learning_expire(ml, mac);
+ }
+ }
+}
+
+static struct ofbundle *
+bundle_lookup(const struct ofproto_dpif *ofproto, void *aux)
+{
+ struct ofbundle *bundle;
+
+ HMAP_FOR_EACH_IN_BUCKET (bundle, hmap_node, hash_pointer(aux, 0),
+ &ofproto->bundles) {
+ if (bundle->aux == aux) {
+ return bundle;
+ }
+ }
+ return NULL;
+}
+
+/* Looks up each of the 'n_auxes' pointers in 'auxes' as bundles and adds the
+ * ones that are found to 'bundles'. */
+static void
+bundle_lookup_multiple(struct ofproto_dpif *ofproto,
+ void **auxes, size_t n_auxes,
+ struct hmapx *bundles)
+{
+ size_t i;
+
+ hmapx_init(bundles);
+ for (i = 0; i < n_auxes; i++) {
+ struct ofbundle *bundle = bundle_lookup(ofproto, auxes[i]);
+ if (bundle) {
+ hmapx_add(bundles, bundle);
+ }
+ }
+}
+
+static void
+bundle_del_port(struct ofport_dpif *port)
+{
+ struct ofbundle *bundle = port->bundle;
+
+ bundle->ofproto->need_revalidate = true;
+
+ list_remove(&port->bundle_node);
+ port->bundle = NULL;
+
+ if (bundle->lacp) {
+ lacp_slave_unregister(bundle->lacp, port);
+ }
+ if (bundle->bond) {
+ bond_slave_unregister(bundle->bond, port);
+ }
+
+ bundle->floodable = true;
+ LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
+ if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) {
+ bundle->floodable = false;
+ }
+ }
+}
+
+static bool
+bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
+ struct lacp_slave_settings *lacp,
+ uint32_t bond_stable_id)
+{
+ struct ofport_dpif *port;
+
+ port = get_ofp_port(bundle->ofproto, ofp_port);
+ if (!port) {
+ return false;
+ }
+
+ if (port->bundle != bundle) {
+ bundle->ofproto->need_revalidate = true;
+ if (port->bundle) {
+ bundle_del_port(port);
+ }
+
+ port->bundle = bundle;
+ list_push_back(&bundle->ports, &port->bundle_node);
+ if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) {
+ bundle->floodable = false;
+ }
+ }
+ if (lacp) {
+ lacp_slave_register(bundle->lacp, port, lacp);
+ }
+
+ port->bond_stable_id = bond_stable_id;
+
+ return true;
+}
+
+static void
+bundle_destroy(struct ofbundle *bundle)
+{
+ struct ofproto_dpif *ofproto;
+ struct ofport_dpif *port, *next_port;
+ int i;
+
+ if (!bundle) {
+ return;
+ }
+
+ ofproto = bundle->ofproto;
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ struct ofmirror *m = ofproto->mirrors[i];
+ if (m) {
+ if (m->out == bundle) {
+ mirror_destroy(m);
+ } else if (hmapx_find_and_delete(&m->srcs, bundle)
+ || hmapx_find_and_delete(&m->dsts, bundle)) {
+ ofproto->need_revalidate = true;
+ }
+ }
+ }
+
+ LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
+ bundle_del_port(port);
+ }
+
+ bundle_flush_macs(bundle);
+ hmap_remove(&ofproto->bundles, &bundle->hmap_node);
+ free(bundle->name);
+ free(bundle->trunks);
+ lacp_destroy(bundle->lacp);
+ bond_destroy(bundle->bond);
+ free(bundle);
+}
+
+static int
+bundle_set(struct ofproto *ofproto_, void *aux,
+ const struct ofproto_bundle_settings *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ bool need_flush = false;
+ const unsigned long *trunks;
+ struct ofport_dpif *port;
+ struct ofbundle *bundle;
+ size_t i;
+ bool ok;
+
+ if (!s) {
+ bundle_destroy(bundle_lookup(ofproto, aux));
+ return 0;
+ }
+
+ assert(s->n_slaves == 1 || s->bond != NULL);
+ assert((s->lacp != NULL) == (s->lacp_slaves != NULL));
+
+ bundle = bundle_lookup(ofproto, aux);
+ if (!bundle) {
+ bundle = xmalloc(sizeof *bundle);
+
+ bundle->ofproto = ofproto;
+ hmap_insert(&ofproto->bundles, &bundle->hmap_node,
+ hash_pointer(aux, 0));
+ bundle->aux = aux;
+ bundle->name = NULL;
+
+ list_init(&bundle->ports);
+ bundle->vlan = -1;
+ bundle->trunks = NULL;
+ bundle->lacp = NULL;
+ bundle->bond = NULL;
+
+ bundle->floodable = true;
+
+ bundle->src_mirrors = 0;
+ bundle->dst_mirrors = 0;
+ bundle->mirror_out = 0;
+ }
+
+ if (!bundle->name || strcmp(s->name, bundle->name)) {
+ free(bundle->name);
+ bundle->name = xstrdup(s->name);
+ }
+
+ /* LACP. */
+ if (s->lacp) {
+ if (!bundle->lacp) {
+ bundle->lacp = lacp_create();
+ }
+ lacp_configure(bundle->lacp, s->lacp);
+ } else {
+ lacp_destroy(bundle->lacp);
+ bundle->lacp = NULL;
+ }
+
+ /* Update set of ports. */
+ ok = true;
+ for (i = 0; i < s->n_slaves; i++) {
+ if (!bundle_add_port(bundle, s->slaves[i],
+ s->lacp ? &s->lacp_slaves[i] : NULL,
+ s->bond_stable_ids ? s->bond_stable_ids[i] : 0)) {
+ ok = false;
+ }
+ }
+ if (!ok || list_size(&bundle->ports) != s->n_slaves) {
+ struct ofport_dpif *next_port;
+
+ LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
+ for (i = 0; i < s->n_slaves; i++) {
+ if (s->slaves[i] == port->up.ofp_port) {
+ goto found;
+ }
+ }
+
+ bundle_del_port(port);
+ found: ;
+ }
+ }
+ assert(list_size(&bundle->ports) <= s->n_slaves);
+
+ if (list_is_empty(&bundle->ports)) {
+ bundle_destroy(bundle);
+ return EINVAL;
+ }
+
+ /* Set VLAN tag. */
+ if (s->vlan != bundle->vlan) {
+ bundle->vlan = s->vlan;
+ need_flush = true;
+ }
+
+ /* Get trunked VLANs. */
+ trunks = s->vlan == -1 ? NULL : s->trunks;
+ if (!vlan_bitmap_equal(trunks, bundle->trunks)) {
+ free(bundle->trunks);
+ bundle->trunks = vlan_bitmap_clone(trunks);
+ need_flush = true;
+ }
+
+ /* Bonding. */
+ if (!list_is_short(&bundle->ports)) {
+ bundle->ofproto->has_bonded_bundles = true;
+ if (bundle->bond) {
+ if (bond_reconfigure(bundle->bond, s->bond)) {
+ ofproto->need_revalidate = true;
+ }
+ } else {
+ bundle->bond = bond_create(s->bond);
+ ofproto->need_revalidate = true;
+ }
+
+ LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
+ bond_slave_register(bundle->bond, port, port->bond_stable_id,
+ port->up.netdev);
+ }
+ } else {
+ bond_destroy(bundle->bond);
+ bundle->bond = NULL;
+ }
+
+ /* If we changed something that would affect MAC learning, un-learn
+ * everything on this port and force flow revalidation. */
+ if (need_flush) {
+ bundle_flush_macs(bundle);
+ }
+
+ return 0;
+}
+
+static void
+bundle_remove(struct ofport *port_)
+{
+ struct ofport_dpif *port = ofport_dpif_cast(port_);
+ struct ofbundle *bundle = port->bundle;
+
+ if (bundle) {
+ bundle_del_port(port);
+ if (list_is_empty(&bundle->ports)) {
+ bundle_destroy(bundle);
+ } else if (list_is_short(&bundle->ports)) {
+ bond_destroy(bundle->bond);
+ bundle->bond = NULL;
+ }
+ }
+}
+
+static void
+send_pdu_cb(void *port_, const struct lacp_pdu *pdu)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+ struct ofport_dpif *port = port_;
+ uint8_t ea[ETH_ADDR_LEN];
+ int error;
+
+ error = netdev_get_etheraddr(port->up.netdev, ea);
+ if (!error) {
+ struct lacp_pdu *packet_pdu;
+ struct ofpbuf packet;
+
+ ofpbuf_init(&packet, 0);
+ packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
+ sizeof *packet_pdu);
+ *packet_pdu = *pdu;
+ error = netdev_send(port->up.netdev, &packet);
+ if (error) {
+ VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
+ "(%s)", port->bundle->name,
+ netdev_get_name(port->up.netdev), strerror(error));
+ }
+ ofpbuf_uninit(&packet);
+ } else {
+ VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
+ "%s (%s)", port->bundle->name,
+ netdev_get_name(port->up.netdev), strerror(error));
+ }
+}
+
+static void
+bundle_send_learning_packets(struct ofbundle *bundle)
+{
+ struct ofproto_dpif *ofproto = bundle->ofproto;
+ int error, n_packets, n_errors;
+ struct mac_entry *e;
+
+ error = n_packets = n_errors = 0;
+ LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
+ if (e->port.p != bundle) {
+ int ret = bond_send_learning_packet(bundle->bond, e->mac, e->vlan);
+ if (ret) {
+ error = ret;
+ n_errors++;
+ }
+ n_packets++;
+ }
+ }
+
+ if (n_errors) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
+ "packets, last error was: %s",
+ bundle->name, n_errors, n_packets, strerror(error));
+ } else {
+ VLOG_DBG("bond %s: sent %d gratuitous learning packets",
+ bundle->name, n_packets);
+ }
+}
+
+static void
+bundle_run(struct ofbundle *bundle)
+{
+ if (bundle->lacp) {
+ lacp_run(bundle->lacp, send_pdu_cb);
+ }
+ if (bundle->bond) {
+ struct ofport_dpif *port;
+
+ LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
+ bool may_enable = lacp_slave_may_enable(bundle->lacp, port);
+ bond_slave_set_lacp_may_enable(bundle->bond, port, may_enable);
+ }
+
+ bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
+ lacp_negotiated(bundle->lacp));
+ if (bond_should_send_learning_packets(bundle->bond)) {
+ bundle_send_learning_packets(bundle);
+ }
+ }
+}
+
+static void
+bundle_wait(struct ofbundle *bundle)
+{
+ if (bundle->lacp) {
+ lacp_wait(bundle->lacp);
+ }
+ if (bundle->bond) {
+ bond_wait(bundle->bond);
+ }
+}
+\f
+/* Mirrors. */
+
+static int
+mirror_scan(struct ofproto_dpif *ofproto)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_MIRRORS; idx++) {
+ if (!ofproto->mirrors[idx]) {
+ return idx;
+ }
+ }
+ return -1;
+}
+
+static struct ofmirror *
+mirror_lookup(struct ofproto_dpif *ofproto, void *aux)
+{
+ int i;
+
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ struct ofmirror *mirror = ofproto->mirrors[i];
+ if (mirror && mirror->aux == aux) {
+ return mirror;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+mirror_set(struct ofproto *ofproto_, void *aux,
+ const struct ofproto_mirror_settings *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ mirror_mask_t mirror_bit;
+ struct ofbundle *bundle;
+ struct ofmirror *mirror;
+ struct ofbundle *out;
+ struct hmapx srcs; /* Contains "struct ofbundle *"s. */
+ struct hmapx dsts; /* Contains "struct ofbundle *"s. */
+ int out_vlan;
+
+ mirror = mirror_lookup(ofproto, aux);
+ if (!s) {
+ mirror_destroy(mirror);
+ return 0;
+ }
+ if (!mirror) {
+ int idx;
+
+ idx = mirror_scan(ofproto);
+ if (idx < 0) {
+ VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
+ "cannot create %s",
+ ofproto->up.name, MAX_MIRRORS, s->name);
+ return EFBIG;
+ }
+
+ mirror = ofproto->mirrors[idx] = xzalloc(sizeof *mirror);
+ mirror->ofproto = ofproto;
+ mirror->idx = idx;
+ mirror->out_vlan = -1;
+ mirror->name = NULL;
+ }
+
+ if (!mirror->name || strcmp(s->name, mirror->name)) {
+ free(mirror->name);
+ mirror->name = xstrdup(s->name);
+ }
+
+ /* Get the new configuration. */
+ if (s->out_bundle) {
+ out = bundle_lookup(ofproto, s->out_bundle);
+ if (!out) {
+ mirror_destroy(mirror);
+ return EINVAL;
+ }
+ out_vlan = -1;
+ } else {
+ out = NULL;
+ out_vlan = s->out_vlan;
+ }
+ bundle_lookup_multiple(ofproto, s->srcs, s->n_srcs, &srcs);
+ bundle_lookup_multiple(ofproto, s->dsts, s->n_dsts, &dsts);
+
+ /* If the configuration has not changed, do nothing. */
+ if (hmapx_equals(&srcs, &mirror->srcs)
+ && hmapx_equals(&dsts, &mirror->dsts)
+ && vlan_bitmap_equal(mirror->vlans, s->src_vlans)
+ && mirror->out == out
+ && mirror->out_vlan == out_vlan)
+ {
+ hmapx_destroy(&srcs);
+ hmapx_destroy(&dsts);
+ return 0;
+ }
+
+ hmapx_swap(&srcs, &mirror->srcs);
+ hmapx_destroy(&srcs);
+
+ hmapx_swap(&dsts, &mirror->dsts);
+ hmapx_destroy(&dsts);
+
+ free(mirror->vlans);
+ mirror->vlans = vlan_bitmap_clone(s->src_vlans);
+
+ mirror->out = out;
+ mirror->out_vlan = out_vlan;
+
+ /* Update bundles. */
+ mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
+ HMAP_FOR_EACH (bundle, hmap_node, &mirror->ofproto->bundles) {
+ if (hmapx_contains(&mirror->srcs, bundle)) {
+ bundle->src_mirrors |= mirror_bit;
+ } else {
+ bundle->src_mirrors &= ~mirror_bit;
+ }
+
+ if (hmapx_contains(&mirror->dsts, bundle)) {
+ bundle->dst_mirrors |= mirror_bit;
+ } else {
+ bundle->dst_mirrors &= ~mirror_bit;
+ }
+
+ if (mirror->out == bundle) {
+ bundle->mirror_out |= mirror_bit;
+ } else {
+ bundle->mirror_out &= ~mirror_bit;
+ }
+ }
+
+ ofproto->need_revalidate = true;
+ mac_learning_flush(ofproto->ml);
+
+ return 0;
+}
+
+static void
+mirror_destroy(struct ofmirror *mirror)
+{
+ struct ofproto_dpif *ofproto;
+ mirror_mask_t mirror_bit;
+ struct ofbundle *bundle;
+
+ if (!mirror) {
+ return;
+ }
+
+ ofproto = mirror->ofproto;
+ ofproto->need_revalidate = true;
+ mac_learning_flush(ofproto->ml);
+
+ mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
+ HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+ bundle->src_mirrors &= ~mirror_bit;
+ bundle->dst_mirrors &= ~mirror_bit;
+ bundle->mirror_out &= ~mirror_bit;
+ }
+
+ hmapx_destroy(&mirror->srcs);
+ hmapx_destroy(&mirror->dsts);
+ free(mirror->vlans);
+
+ ofproto->mirrors[mirror->idx] = NULL;
+ free(mirror->name);
+ free(mirror);
+}
+
+static int
+set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ if (mac_learning_set_flood_vlans(ofproto->ml, flood_vlans)) {
+ ofproto->need_revalidate = true;
+ mac_learning_flush(ofproto->ml);
+ }
+ return 0;
+}
+
+static bool
+is_mirror_output_bundle(struct ofproto *ofproto_, void *aux)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct ofbundle *bundle = bundle_lookup(ofproto, aux);
+ return bundle && bundle->mirror_out != 0;
+}
+\f
+/* Ports. */
+
+static struct ofport_dpif *
+get_ofp_port(struct ofproto_dpif *ofproto, uint16_t ofp_port)
+{
+ struct ofport *ofport = ofproto_get_port(&ofproto->up, ofp_port);
+ return ofport ? ofport_dpif_cast(ofport) : NULL;
+}
+
+static struct ofport_dpif *
+get_odp_port(struct ofproto_dpif *ofproto, uint32_t odp_port)
+{
+ return get_ofp_port(ofproto, odp_port_to_ofp_port(odp_port));
+}
+
+static void
+ofproto_port_from_dpif_port(struct ofproto_port *ofproto_port,
+ struct dpif_port *dpif_port)
+{
+ ofproto_port->name = dpif_port->name;
+ ofproto_port->type = dpif_port->type;
+ ofproto_port->ofp_port = odp_port_to_ofp_port(dpif_port->port_no);
+}
+
+static void
+port_run(struct ofport_dpif *ofport)
+{
+ if (ofport->cfm) {
+ cfm_run(ofport->cfm);
+
+ if (cfm_should_send_ccm(ofport->cfm)) {
+ struct ofpbuf packet;
+ struct ccm *ccm;
+
+ ofpbuf_init(&packet, 0);
+ ccm = eth_compose(&packet, eth_addr_ccm, ofport->up.opp.hw_addr,
+ ETH_TYPE_CFM, sizeof *ccm);
+ cfm_compose_ccm(ofport->cfm, ccm);
+ send_packet(ofproto_dpif_cast(ofport->up.ofproto),
+ ofport->odp_port, &packet);
+ ofpbuf_uninit(&packet);
+ }
+ }
+}
+
+static void
+port_wait(struct ofport_dpif *ofport)
+{
+ if (ofport->cfm) {
+ cfm_wait(ofport->cfm);
+ }
+}
+
+static int
+port_query_by_name(const struct ofproto *ofproto_, const char *devname,
+ struct ofproto_port *ofproto_port)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct dpif_port dpif_port;
+ int error;
+
+ error = dpif_port_query_by_name(ofproto->dpif, devname, &dpif_port);
+ if (!error) {
+ ofproto_port_from_dpif_port(ofproto_port, &dpif_port);
+ }
+ return error;
+}
+
+static int
+port_add(struct ofproto *ofproto_, struct netdev *netdev, uint16_t *ofp_portp)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ uint16_t odp_port;
+ int error;
+
+ error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
+ if (!error) {
+ *ofp_portp = odp_port_to_ofp_port(odp_port);
+ }
+ return error;
+}
+
+static int
+port_del(struct ofproto *ofproto_, uint16_t ofp_port)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ int error;
+
+ error = dpif_port_del(ofproto->dpif, ofp_port_to_odp_port(ofp_port));
+ if (!error) {
+ struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
+ if (ofport) {
+ /* The caller is going to close ofport->up.netdev. If this is a
+ * bonded port, then the bond is using that netdev, so remove it
+ * from the bond. The client will need to reconfigure everything
+ * after deleting ports, so then the slave will get re-added. */
+ bundle_remove(&ofport->up);
+ }
+ }
+ return error;
+}
+
+struct port_dump_state {
+ struct dpif_port_dump dump;
+ bool done;
+};
+
+static int
+port_dump_start(const struct ofproto *ofproto_, void **statep)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct port_dump_state *state;
+
+ *statep = state = xmalloc(sizeof *state);
+ dpif_port_dump_start(&state->dump, ofproto->dpif);
+ state->done = false;
+ return 0;
+}
+
+static int
+port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
+ struct ofproto_port *port)
+{
+ struct port_dump_state *state = state_;
+ struct dpif_port dpif_port;
+
+ if (dpif_port_dump_next(&state->dump, &dpif_port)) {
+ ofproto_port_from_dpif_port(port, &dpif_port);
+ return 0;
+ } else {
+ int error = dpif_port_dump_done(&state->dump);
+ state->done = true;
+ return error ? error : EOF;
+ }
+}
+
+static int
+port_dump_done(const struct ofproto *ofproto_ OVS_UNUSED, void *state_)
+{
+ struct port_dump_state *state = state_;
+
+ if (!state->done) {
+ dpif_port_dump_done(&state->dump);
+ }
+ free(state);
+ return 0;
+}
+
+static int
+port_poll(const struct ofproto *ofproto_, char **devnamep)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ return dpif_port_poll(ofproto->dpif, devnamep);
+}
+
+static void
+port_poll_wait(const struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ dpif_port_poll_wait(ofproto->dpif);
+}
+
+static int
+port_is_lacp_current(const struct ofport *ofport_)
+{
+ const struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ return (ofport->bundle && ofport->bundle->lacp
+ ? lacp_slave_is_current(ofport->bundle->lacp, ofport)
+ : -1);
+}
+\f
+/* Upcall handling. */
+
+/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an
+ * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
+ * their individual configurations.
+ *
+ * If 'clone' is true, the caller retains ownership of 'upcall->packet'.
+ * Otherwise, ownership is transferred to this function. */
+static void
+send_packet_in(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall,
+ const struct flow *flow, bool clone)
+{
+ struct ofputil_packet_in pin;
+
+ pin.packet = upcall->packet;
+ pin.in_port = flow->in_port;
+ pin.reason = upcall->type == DPIF_UC_MISS ? OFPR_NO_MATCH : OFPR_ACTION;
+ pin.buffer_id = 0; /* not yet known */
+ pin.send_len = upcall->userdata;
+ connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow,
+ clone ? NULL : upcall->packet);
+}
+
+static bool
+process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
+ const struct ofpbuf *packet)
+{
+ if (cfm_should_process_flow(flow)) {
+ struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
+ if (ofport && ofport->cfm) {
+ cfm_process_heartbeat(ofport->cfm, packet);
+ }
+ return true;
+ } else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
+ struct ofport_dpif *port = get_ofp_port(ofproto, flow->in_port);
+ if (port && port->bundle && port->bundle->lacp) {
+ const struct lacp_pdu *pdu = parse_lacp_packet(packet);
+ if (pdu) {
+ lacp_process_pdu(port->bundle->lacp, port, pdu);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+handle_miss_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
+{
+ struct facet *facet;
+ struct flow flow;
+
+ /* Obtain in_port and tun_id, at least. */
+ odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+
+ /* Set header pointers in 'flow'. */
+ flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
+
+ /* Handle 802.1ag and LACP. */
+ if (process_special(ofproto, &flow, upcall->packet)) {
+ ofpbuf_delete(upcall->packet);
+ ofproto->n_matches++;
+ return;
+ }
+
+ /* Check with in-band control to see if this packet should be sent
+ * to the local port regardless of the flow table. */
+ if (connmgr_msg_in_hook(ofproto->up.connmgr, &flow, upcall->packet)) {
+ send_packet(ofproto, OFPP_LOCAL, upcall->packet);
+ }
+
+ facet = facet_lookup_valid(ofproto, &flow);
+ if (!facet) {
+ struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow);
+ if (!rule) {
+ /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
+ struct ofport_dpif *port = get_ofp_port(ofproto, flow.in_port);
+ if (port) {
+ if (port->up.opp.config & htonl(OFPPC_NO_PACKET_IN)) {
+ COVERAGE_INC(ofproto_dpif_no_packet_in);
+ /* XXX install 'drop' flow entry */
+ ofpbuf_delete(upcall->packet);
+ return;
+ }
+ } else {
+ VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16,
+ flow.in_port);
+ }
+
+ send_packet_in(ofproto, upcall, &flow, false);
+ return;
+ }
+
+ facet = facet_create(rule, &flow, upcall->packet);
+ } else if (!facet->may_install) {
+ /* The facet is not installable, that is, we need to process every
+ * packet, so process the current packet's actions into 'facet'. */
+ facet_make_actions(ofproto, facet, upcall->packet);
+ }
+
+ if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
+ /*
+ * Extra-special case for fail-open mode.
+ *
+ * We are in fail-open mode and the packet matched the fail-open rule,
+ * but we are connected to a controller too. We should send the packet
+ * up to the controller in the hope that it will try to set up a flow
+ * and thereby allow us to exit fail-open.
+ *
+ * See the top-level comment in fail-open.c for more information.
+ */
+ send_packet_in(ofproto, upcall, &flow, true);
+ }
+
+ facet_execute(ofproto, facet, upcall->packet);
+ facet_install(ofproto, facet, false);
+ ofproto->n_matches++;
+}
+
+static void
+handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
+{
+ struct flow flow;
+
+ switch (upcall->type) {
+ case DPIF_UC_ACTION:
+ COVERAGE_INC(ofproto_dpif_ctlr_action);
+ odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+ send_packet_in(ofproto, upcall, &flow, false);
+ break;
+
+ case DPIF_UC_SAMPLE:
+ if (ofproto->sflow) {
+ odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+ ofproto_sflow_received(ofproto->sflow, upcall, &flow);
+ }
+ ofpbuf_delete(upcall->packet);
+ break;
+
+ case DPIF_UC_MISS:
+ handle_miss_upcall(ofproto, upcall);
+ break;
+
+ case DPIF_N_UC_TYPES:
+ default:
+ VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
+ break;
+ }
+}
+\f
+/* Flow expiration. */
+
+static int facet_max_idle(const struct ofproto_dpif *);
+static void update_stats(struct ofproto_dpif *);
+static void rule_expire(struct rule_dpif *);
+static void expire_facets(struct ofproto_dpif *, int dp_max_idle);
+
+/* This function is called periodically by run(). Its job is to collect
+ * updates for the flows that have been installed into the datapath, most
+ * importantly when they last were used, and then use that information to
+ * expire flows that have not been used recently.
+ *
+ * Returns the number of milliseconds after which it should be called again. */
+static int
+expire(struct ofproto_dpif *ofproto)
+{
+ struct rule_dpif *rule, *next_rule;
+ struct cls_cursor cursor;
+ int dp_max_idle;
+
+ /* Update stats for each flow in the datapath. */
+ update_stats(ofproto);
+
+ /* Expire facets that have been idle too long. */
+ dp_max_idle = facet_max_idle(ofproto);
+ expire_facets(ofproto, dp_max_idle);
+
+ /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
+ cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL);
+ CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
+ rule_expire(rule);
+ }
+
+ /* All outstanding data in existing flows has been accounted, so it's a
+ * good time to do bond rebalancing. */
+ if (ofproto->has_bonded_bundles) {
+ struct ofbundle *bundle;
+
+ HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+ if (bundle->bond) {
+ bond_rebalance(bundle->bond, &ofproto->revalidate_set);
+ }
+ }
+ }
+
+ return MIN(dp_max_idle, 1000);
+}
+
+/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
+ *
+ * This function also pushes statistics updates to rules which each facet
+ * resubmits into. Generally these statistics will be accurate. However, if a
+ * facet changes the rule it resubmits into at some time in between
+ * update_stats() runs, it is possible that statistics accrued to the
+ * old rule will be incorrectly attributed to the new rule. This could be
+ * avoided by calling update_stats() whenever rules are created or
+ * deleted. However, the performance impact of making so many calls to the
+ * datapath do not justify the benefit of having perfectly accurate statistics.
+ */
+static void
+update_stats(struct ofproto_dpif *p)
+{
+ const struct dpif_flow_stats *stats;
+ struct dpif_flow_dump dump;
+ const struct nlattr *key;
+ size_t key_len;
+
+ dpif_flow_dump_start(&dump, p->dpif);
+ while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
+ struct facet *facet;
+ struct flow flow;
+
+ if (odp_flow_key_to_flow(key, key_len, &flow)) {
+ struct ds s;
+
+ ds_init(&s);
+ odp_flow_key_format(key, key_len, &s);
+ VLOG_WARN_RL(&rl, "failed to convert ODP flow key to flow: %s",
+ ds_cstr(&s));
+ ds_destroy(&s);
+
+ continue;
+ }
+ facet = facet_find(p, &flow);
+
+ if (facet && facet->installed) {
+
+ if (stats->n_packets >= facet->dp_packet_count) {
+ uint64_t extra = stats->n_packets - facet->dp_packet_count;
+ facet->packet_count += extra;
+ } else {
+ VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
+ }
+
+ if (stats->n_bytes >= facet->dp_byte_count) {
+ facet->byte_count += stats->n_bytes - facet->dp_byte_count;
+ } else {
+ VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
+ }
+
+ facet->dp_packet_count = stats->n_packets;
+ facet->dp_byte_count = stats->n_bytes;
+
+ facet_update_time(p, facet, stats->used);
+ facet_account(p, facet, stats->n_bytes);
+ facet_push_stats(facet);
+ } else {
+ /* There's a flow in the datapath that we know nothing about.
+ * Delete it. */
+ COVERAGE_INC(facet_unexpected);
+ dpif_flow_del(p->dpif, key, key_len, NULL);
+ }
+ }
+ dpif_flow_dump_done(&dump);
+}
+
+/* Calculates and returns the number of milliseconds of idle time after which
+ * facets should expire from the datapath and we should fold their statistics
+ * into their parent rules in userspace. */
+static int
+facet_max_idle(const struct ofproto_dpif *ofproto)
+{
+ /*
+ * Idle time histogram.
+ *
+ * Most of the time a switch has a relatively small number of facets. When
+ * this is the case we might as well keep statistics for all of them in
+ * userspace and to cache them in the kernel datapath for performance as
+ * well.
+ *
+ * As the number of facets increases, the memory required to maintain
+ * statistics about them in userspace and in the kernel becomes
+ * significant. However, with a large number of facets it is likely that
+ * only a few of them are "heavy hitters" that consume a large amount of
+ * bandwidth. At this point, only heavy hitters are worth caching in the
+ * kernel and maintaining in userspaces; other facets we can discard.
+ *
+ * The technique used to compute the idle time is to build a histogram with
+ * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each facet
+ * that is installed in the kernel gets dropped in the appropriate bucket.
+ * After the histogram has been built, we compute the cutoff so that only
+ * the most-recently-used 1% of facets (but at least 1000 flows) are kept
+ * cached. At least the most-recently-used bucket of facets is kept, so
+ * actually an arbitrary number of facets can be kept in any given
+ * expiration run (though the next run will delete most of those unless
+ * they receive additional data).
+ *
+ * This requires a second pass through the facets, in addition to the pass
+ * made by update_stats(), because the former function never looks
+ * at uninstallable facets.
+ */
+ enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) };
+ enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
+ int buckets[N_BUCKETS] = { 0 };
+ struct facet *facet;
+ int total, bucket;
+ long long int now;
+ int i;
+
+ total = hmap_count(&ofproto->facets);
+ if (total <= 1000) {
+ return N_BUCKETS * BUCKET_WIDTH;
+ }
+
+ /* Build histogram. */
+ now = time_msec();
+ HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ long long int idle = now - facet->used;
+ int bucket = (idle <= 0 ? 0
+ : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
+ : (unsigned int) idle / BUCKET_WIDTH);
+ buckets[bucket]++;
+ }
+
+ /* Find the first bucket whose flows should be expired. */
+ for (bucket = 0; bucket < N_BUCKETS; bucket++) {
+ if (buckets[bucket]) {
+ int subtotal = 0;
+ do {
+ subtotal += buckets[bucket++];
+ } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100));
+ break;
+ }
+ }
+
+ if (VLOG_IS_DBG_ENABLED()) {
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_cstr(&s, "keep");
+ for (i = 0; i < N_BUCKETS; i++) {
+ if (i == bucket) {
+ ds_put_cstr(&s, ", drop");
+ }
+ if (buckets[i]) {
+ ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
+ }
+ }
+ VLOG_INFO("%s: %s (msec:count)", ofproto->up.name, ds_cstr(&s));
+ ds_destroy(&s);
+ }
+
+ return bucket * BUCKET_WIDTH;
+}
+
+static void
+facet_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet)
+{
+ if (ofproto->netflow && !facet_is_controller_flow(facet) &&
+ netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) {
+ struct ofexpired expired;
+
+ if (facet->installed) {
+ struct dpif_flow_stats stats;
+
+ facet_put__(ofproto, facet, facet->actions, facet->actions_len,
+ &stats);
+ facet_update_stats(ofproto, facet, &stats);
+ }
+
+ expired.flow = facet->flow;
+ expired.packet_count = facet->packet_count;
+ expired.byte_count = facet->byte_count;
+ expired.used = facet->used;
+ netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
+ }
+}
+
+static void
+expire_facets(struct ofproto_dpif *ofproto, int dp_max_idle)
+{
+ long long int cutoff = time_msec() - dp_max_idle;
+ struct facet *facet, *next_facet;
+
+ HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
+ facet_active_timeout(ofproto, facet);
+ if (facet->used < cutoff) {
+ facet_remove(ofproto, facet);
+ }
+ }
+}
+
+/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
+ * then delete it entirely. */
+static void
+rule_expire(struct rule_dpif *rule)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct facet *facet, *next_facet;
+ long long int now;
+ uint8_t reason;
+
+ /* Has 'rule' expired? */
+ now = time_msec();
+ if (rule->up.hard_timeout
+ && now > rule->up.created + rule->up.hard_timeout * 1000) {
+ reason = OFPRR_HARD_TIMEOUT;
+ } else if (rule->up.idle_timeout && list_is_empty(&rule->facets)
+ && now > rule->used + rule->up.idle_timeout * 1000) {
+ reason = OFPRR_IDLE_TIMEOUT;
+ } else {
+ return;
+ }
+
+ COVERAGE_INC(ofproto_dpif_expired);
+
+ /* Update stats. (This is a no-op if the rule expired due to an idle
+ * timeout, because that only happens when the rule has no facets left.) */
+ LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
+ facet_remove(ofproto, facet);
+ }
+
+ /* Get rid of the rule. */
+ ofproto_rule_expire(&rule->up, reason);
+}
+\f
+/* Facets. */
+
+/* Creates and returns a new facet owned by 'rule', given a 'flow' and an
+ * example 'packet' within that flow.
+ *
+ * The caller must already have determined that no facet with an identical
+ * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in
+ * the ofproto's classifier table. */
+static struct facet *
+facet_create(struct rule_dpif *rule, const struct flow *flow,
+ const struct ofpbuf *packet)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct facet *facet;
+
+ facet = xzalloc(sizeof *facet);
+ facet->used = time_msec();
+ hmap_insert(&ofproto->facets, &facet->hmap_node, flow_hash(flow, 0));
+ list_push_back(&rule->facets, &facet->list_node);
+ facet->rule = rule;
+ facet->flow = *flow;
+ netflow_flow_init(&facet->nf_flow);
+ netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
+
+ facet_make_actions(ofproto, facet, packet);
+
+ return facet;
+}
+
+static void
+facet_free(struct facet *facet)
+{
+ free(facet->actions);
+ free(facet);
+}
+
+/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
+ * 'packet', which arrived on 'in_port'.
+ *
+ * Takes ownership of 'packet'. */
+static bool
+execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
+ const struct nlattr *odp_actions, size_t actions_len,
+ struct ofpbuf *packet)
+{
+ if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
+ && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) {
+ /* As an optimization, avoid a round-trip from userspace to kernel to
+ * userspace. This also avoids possibly filling up kernel packet
+ * buffers along the way. */
+ struct dpif_upcall upcall;
+
+ upcall.type = DPIF_UC_ACTION;
+ upcall.packet = packet;
+ upcall.key = NULL;
+ upcall.key_len = 0;
+ upcall.userdata = nl_attr_get_u64(odp_actions);
+ upcall.sample_pool = 0;
+ upcall.actions = NULL;
+ upcall.actions_len = 0;
+
+ send_packet_in(ofproto, &upcall, flow, false);
+
+ return true;
+ } else {
+ int error;
+
+ error = dpif_execute(ofproto->dpif, odp_actions, actions_len, packet);
+ ofpbuf_delete(packet);
+ return !error;
+ }
+}
+
+/* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s
+ * statistics appropriately. 'packet' must have at least sizeof(struct
+ * ofp_packet_in) bytes of headroom.
+ *
+ * For correct results, 'packet' must actually be in 'facet''s flow; that is,
+ * applying flow_extract() to 'packet' would yield the same flow as
+ * 'facet->flow'.
+ *
+ * 'facet' must have accurately composed ODP actions; that is, it must not be
+ * in need of revalidation.
+ *
+ * Takes ownership of 'packet'. */
+static void
+facet_execute(struct ofproto_dpif *ofproto, struct facet *facet,
+ struct ofpbuf *packet)
+{
+ struct dpif_flow_stats stats;
+
+ assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
+
+ flow_extract_stats(&facet->flow, packet, &stats);
+ stats.used = time_msec();
+ if (execute_odp_actions(ofproto, &facet->flow,
+ facet->actions, facet->actions_len, packet)) {
+ facet_update_stats(ofproto, facet, &stats);
+ }
+}
+
+/* Remove 'facet' from 'ofproto' and free up the associated memory:
+ *
+ * - If 'facet' was installed in the datapath, uninstalls it and updates its
+ * rule's statistics, via facet_uninstall().
+ *
+ * - Removes 'facet' from its rule and from ofproto->facets.
+ */
+static void
+facet_remove(struct ofproto_dpif *ofproto, struct facet *facet)
+{
+ facet_uninstall(ofproto, facet);
+ facet_flush_stats(ofproto, facet);
+ hmap_remove(&ofproto->facets, &facet->hmap_node);
+ list_remove(&facet->list_node);
+ facet_free(facet);
+}
+
+/* Composes the ODP actions for 'facet' based on its rule's actions. */
+static void
+facet_make_actions(struct ofproto_dpif *p, struct facet *facet,
+ const struct ofpbuf *packet)
+{
+ const struct rule_dpif *rule = facet->rule;
+ struct ofpbuf *odp_actions;
+ struct action_xlate_ctx ctx;
+
+ action_xlate_ctx_init(&ctx, p, &facet->flow, packet);
+ odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
+ facet->tags = ctx.tags;
+ facet->may_install = ctx.may_set_up_flow;
+ facet->nf_flow.output_iface = ctx.nf_output_iface;
+
+ if (facet->actions_len != odp_actions->size
+ || memcmp(facet->actions, odp_actions->data, odp_actions->size)) {
+ free(facet->actions);
+ facet->actions_len = odp_actions->size;
+ facet->actions = xmemdup(odp_actions->data, odp_actions->size);
+ }
+
+ ofpbuf_delete(odp_actions);
+}
+
+static int
+facet_put__(struct ofproto_dpif *ofproto, struct facet *facet,
+ const struct nlattr *actions, size_t actions_len,
+ struct dpif_flow_stats *stats)
+{
+ struct odputil_keybuf keybuf;
+ enum dpif_flow_put_flags flags;
+ struct ofpbuf key;
+
+ flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+ if (stats) {
+ flags |= DPIF_FP_ZERO_STATS;
+ facet->dp_packet_count = 0;
+ facet->dp_byte_count = 0;
+ }
+
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&key, &facet->flow);
+
+ return dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
+ actions, actions_len, stats);
+}
+
+/* If 'facet' is installable, inserts or re-inserts it into 'p''s datapath. If
+ * 'zero_stats' is true, clears any existing statistics from the datapath for
+ * 'facet'. */
+static void
+facet_install(struct ofproto_dpif *p, struct facet *facet, bool zero_stats)
+{
+ struct dpif_flow_stats stats;
+
+ if (facet->may_install
+ && !facet_put__(p, facet, facet->actions, facet->actions_len,
+ zero_stats ? &stats : NULL)) {
+ facet->installed = true;
+ }
+}
+
+static void
+facet_account(struct ofproto_dpif *ofproto,
+ struct facet *facet, uint64_t extra_bytes)
+{
+ uint64_t total_bytes, n_bytes;
+ struct ofbundle *in_bundle;
+ const struct nlattr *a;
+ tag_type dummy = 0;
+ unsigned int left;
+ int vlan;
+
+ total_bytes = facet->byte_count + extra_bytes;
+ if (total_bytes <= facet->accounted_bytes) {
+ return;
+ }
+ n_bytes = total_bytes - facet->accounted_bytes;
+ facet->accounted_bytes = total_bytes;
+
+ /* Test that 'tags' is nonzero to ensure that only flows that include an
+ * OFPP_NORMAL action are used for learning and bond slave rebalancing.
+ * This works because OFPP_NORMAL always sets a nonzero tag value.
+ *
+ * Feed information from the active flows back into the learning table to
+ * ensure that table is always in sync with what is actually flowing
+ * through the datapath. */
+ if (!facet->tags
+ || !is_admissible(ofproto, &facet->flow, false, &dummy,
+ &vlan, &in_bundle)) {
+ return;
+ }
+
+ update_learning_table(ofproto, &facet->flow, vlan, in_bundle);
+
+ if (!ofproto->has_bonded_bundles) {
+ return;
+ }
+ NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) {
+ if (nl_attr_type(a) == ODP_ACTION_ATTR_OUTPUT) {
+ struct ofport_dpif *port;
+
+ port = get_odp_port(ofproto, nl_attr_get_u32(a));
+ if (port && port->bundle && port->bundle->bond) {
+ bond_account(port->bundle->bond, &facet->flow, vlan, n_bytes);
+ }
+ }
+ }
+}
+
+/* If 'rule' is installed in the datapath, uninstalls it. */
+static void
+facet_uninstall(struct ofproto_dpif *p, struct facet *facet)
+{
+ if (facet->installed) {
+ struct odputil_keybuf keybuf;
+ struct dpif_flow_stats stats;
+ struct ofpbuf key;
+
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&key, &facet->flow);
+
+ if (!dpif_flow_del(p->dpif, key.data, key.size, &stats)) {
+ facet_update_stats(p, facet, &stats);
+ }
+ facet->installed = false;
+ facet->dp_packet_count = 0;
+ facet->dp_byte_count = 0;
+ } else {
+ assert(facet->dp_packet_count == 0);
+ assert(facet->dp_byte_count == 0);
+ }
+}
+
+/* Returns true if the only action for 'facet' is to send to the controller.
+ * (We don't report NetFlow expiration messages for such facets because they
+ * are just part of the control logic for the network, not real traffic). */
+static bool
+facet_is_controller_flow(struct facet *facet)
+{
+ return (facet
+ && facet->rule->up.n_actions == 1
+ && action_outputs_to_port(&facet->rule->up.actions[0],
+ htons(OFPP_CONTROLLER)));
+}
+
+/* Folds all of 'facet''s statistics into its rule. Also updates the
+ * accounting ofhook and emits a NetFlow expiration if appropriate. All of
+ * 'facet''s statistics in the datapath should have been zeroed and folded into
+ * its packet and byte counts before this function is called. */
+static void
+facet_flush_stats(struct ofproto_dpif *ofproto, struct facet *facet)
+{
+ assert(!facet->dp_byte_count);
+ assert(!facet->dp_packet_count);
+
+ facet_push_stats(facet);
+ facet_account(ofproto, facet, 0);
+
+ if (ofproto->netflow && !facet_is_controller_flow(facet)) {
+ struct ofexpired expired;
+ expired.flow = facet->flow;
+ expired.packet_count = facet->packet_count;
+ expired.byte_count = facet->byte_count;
+ expired.used = facet->used;
+ netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
+ }
+
+ facet->rule->packet_count += facet->packet_count;
+ facet->rule->byte_count += facet->byte_count;
+
+ /* Reset counters to prevent double counting if 'facet' ever gets
+ * reinstalled. */
+ facet->packet_count = 0;
+ facet->byte_count = 0;
+ facet->rs_packet_count = 0;
+ facet->rs_byte_count = 0;
+ facet->accounted_bytes = 0;
+
+ netflow_flow_clear(&facet->nf_flow);
+}
+
+/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
+ * Returns it if found, otherwise a null pointer.
+ *
+ * The returned facet might need revalidation; use facet_lookup_valid()
+ * instead if that is important. */
+static struct facet *
+facet_find(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct facet *facet;
+
+ HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, flow_hash(flow, 0),
+ &ofproto->facets) {
+ if (flow_equal(flow, &facet->flow)) {
+ return facet;
+ }
+ }
+
+ return NULL;
+}
+
+/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
+ * Returns it if found, otherwise a null pointer.
+ *
+ * The returned facet is guaranteed to be valid. */
+static struct facet *
+facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct facet *facet = facet_find(ofproto, flow);
+
+ /* The facet we found might not be valid, since we could be in need of
+ * revalidation. If it is not valid, don't return it. */
+ if (facet
+ && ofproto->need_revalidate
+ && !facet_revalidate(ofproto, facet)) {
+ COVERAGE_INC(facet_invalidated);
+ return NULL;
+ }
+
+ return facet;
+}
+
+/* Re-searches 'ofproto''s classifier for a rule matching 'facet':
+ *
+ * - If the rule found is different from 'facet''s current rule, moves
+ * 'facet' to the new rule and recompiles its actions.
+ *
+ * - If the rule found is the same as 'facet''s current rule, leaves 'facet'
+ * where it is and recompiles its actions anyway.
+ *
+ * - If there is none, destroys 'facet'.
+ *
+ * Returns true if 'facet' still exists, false if it has been destroyed. */
+static bool
+facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet)
+{
+ struct action_xlate_ctx ctx;
+ struct ofpbuf *odp_actions;
+ struct rule_dpif *new_rule;
+ bool actions_changed;
+
+ COVERAGE_INC(facet_revalidate);
+
+ /* Determine the new rule. */
+ new_rule = rule_dpif_lookup(ofproto, &facet->flow);
+ if (!new_rule) {
+ /* No new rule, so delete the facet. */
+ facet_remove(ofproto, facet);
+ return false;
+ }
+
+ /* Calculate new ODP actions.
+ *
+ * We do not modify any 'facet' state yet, because we might need to, e.g.,
+ * emit a NetFlow expiration and, if so, we need to have the old state
+ * around to properly compose it. */
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL);
+ odp_actions = xlate_actions(&ctx,
+ new_rule->up.actions, new_rule->up.n_actions);
+ actions_changed = (facet->actions_len != odp_actions->size
+ || memcmp(facet->actions, odp_actions->data,
+ facet->actions_len));
+
+ /* If the ODP actions changed or the installability changed, then we need
+ * to talk to the datapath. */
+ if (actions_changed || ctx.may_set_up_flow != facet->installed) {
+ if (ctx.may_set_up_flow) {
+ struct dpif_flow_stats stats;
+
+ facet_put__(ofproto, facet,
+ odp_actions->data, odp_actions->size, &stats);
+ facet_update_stats(ofproto, facet, &stats);
+ } else {
+ facet_uninstall(ofproto, facet);
+ }
+
+ /* The datapath flow is gone or has zeroed stats, so push stats out of
+ * 'facet' into 'rule'. */
+ facet_flush_stats(ofproto, facet);
+ }
+
+ /* Update 'facet' now that we've taken care of all the old state. */
+ facet->tags = ctx.tags;
+ facet->nf_flow.output_iface = ctx.nf_output_iface;
+ facet->may_install = ctx.may_set_up_flow;
+ if (actions_changed) {
+ free(facet->actions);
+ facet->actions_len = odp_actions->size;
+ facet->actions = xmemdup(odp_actions->data, odp_actions->size);
+ }
+ if (facet->rule != new_rule) {
+ COVERAGE_INC(facet_changed_rule);
+ list_remove(&facet->list_node);
+ list_push_back(&new_rule->facets, &facet->list_node);
+ facet->rule = new_rule;
+ facet->used = new_rule->up.created;
+ facet->rs_used = facet->used;
+ }
+
+ ofpbuf_delete(odp_actions);
+
+ return true;
+}
+
+/* Updates 'facet''s used time. Caller is responsible for calling
+ * facet_push_stats() to update the flows which 'facet' resubmits into. */
+static void
+facet_update_time(struct ofproto_dpif *ofproto, struct facet *facet,
+ long long int used)
+{
+ if (used > facet->used) {
+ facet->used = used;
+ if (used > facet->rule->used) {
+ facet->rule->used = used;
+ }
+ netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
+ }
+}
+
+/* Folds the statistics from 'stats' into the counters in 'facet'.
+ *
+ * Because of the meaning of a facet's counters, it only makes sense to do this
+ * if 'stats' are not tracked in the datapath, that is, if 'stats' represents a
+ * packet that was sent by hand or if it represents statistics that have been
+ * cleared out of the datapath. */
+static void
+facet_update_stats(struct ofproto_dpif *ofproto, struct facet *facet,
+ const struct dpif_flow_stats *stats)
+{
+ if (stats->n_packets || stats->used > facet->used) {
+ facet_update_time(ofproto, facet, stats->used);
+ facet->packet_count += stats->n_packets;
+ facet->byte_count += stats->n_bytes;
+ facet_push_stats(facet);
+ netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
+ }
+}
+
+static void
+facet_push_stats(struct facet *facet)
+{
+ uint64_t rs_packets, rs_bytes;
+
+ assert(facet->packet_count >= facet->rs_packet_count);
+ assert(facet->byte_count >= facet->rs_byte_count);
+ assert(facet->used >= facet->rs_used);
+
+ rs_packets = facet->packet_count - facet->rs_packet_count;
+ rs_bytes = facet->byte_count - facet->rs_byte_count;
+
+ if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
+ facet->rs_packet_count = facet->packet_count;
+ facet->rs_byte_count = facet->byte_count;
+ facet->rs_used = facet->used;
+
+ flow_push_stats(facet->rule, &facet->flow,
+ rs_packets, rs_bytes, facet->used);
+ }
+}
+
+struct ofproto_push {
+ struct action_xlate_ctx ctx;
+ uint64_t packets;
+ uint64_t bytes;
+ long long int used;
+};
+
+static void
+push_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
+{
+ struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
+
+ if (rule) {
+ rule->packet_count += push->packets;
+ rule->byte_count += push->bytes;
+ rule->used = MAX(push->used, rule->used);
+ }
+}
+
+/* Pushes flow statistics to the rules which 'flow' resubmits into given
+ * 'rule''s actions. */
+static void
+flow_push_stats(const struct rule_dpif *rule,
+ struct flow *flow, uint64_t packets, uint64_t bytes,
+ long long int used)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct ofproto_push push;
+
+ push.packets = packets;
+ push.bytes = bytes;
+ push.used = used;
+
+ action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL);
+ push.ctx.resubmit_hook = push_resubmit;
+ ofpbuf_delete(xlate_actions(&push.ctx,
+ rule->up.actions, rule->up.n_actions));
+}
+\f
+/* Rules. */
+
+static struct rule_dpif *
+rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ return rule_dpif_cast(rule_from_cls_rule(
+ classifier_lookup(&ofproto->up.tables[0],
+ flow)));
+}
+
+static struct rule *
+rule_alloc(void)
+{
+ struct rule_dpif *rule = xmalloc(sizeof *rule);
+ return &rule->up;
+}
+
+static void
+rule_dealloc(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ free(rule);
+}
+
+static int
+rule_construct(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct rule_dpif *old_rule;
+ int error;
+
+ error = validate_actions(rule->up.actions, rule->up.n_actions,
+ &rule->up.cr.flow, ofproto->max_ports);
+ if (error) {
+ return error;
+ }
+
+ old_rule = rule_dpif_cast(rule_from_cls_rule(classifier_find_rule_exactly(
+ &ofproto->up.tables[0],
+ &rule->up.cr)));
+ if (old_rule) {
+ ofproto_rule_destroy(&old_rule->up);
+ }
+
+ rule->used = rule->up.created;
+ rule->packet_count = 0;
+ rule->byte_count = 0;
+ list_init(&rule->facets);
+ classifier_insert(&ofproto->up.tables[0], &rule->up.cr);
+
+ ofproto->need_revalidate = true;
+
+ return 0;
+}
+
+static void
+rule_destruct(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct facet *facet, *next_facet;
+
+ classifier_remove(&ofproto->up.tables[0], &rule->up.cr);
+ LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
+ facet_revalidate(ofproto, facet);
+ }
+ ofproto->need_revalidate = true;
+}
+
+static void
+rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct facet *facet;
+
+ /* Start from historical data for 'rule' itself that are no longer tracked
+ * in facets. This counts, for example, facets that have expired. */
+ *packets = rule->packet_count;
+ *bytes = rule->byte_count;
+
+ /* Add any statistics that are tracked by facets. This includes
+ * statistical data recently updated by ofproto_update_stats() as well as
+ * stats for packets that were executed "by hand" via dpif_execute(). */
+ LIST_FOR_EACH (facet, list_node, &rule->facets) {
+ *packets += facet->packet_count;
+ *bytes += facet->byte_count;
+ }
+}
+
+static int
+rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct action_xlate_ctx ctx;
+ struct ofpbuf *odp_actions;
+ struct facet *facet;
+ size_t size;
+
+ /* First look for a related facet. If we find one, account it to that. */
+ facet = facet_lookup_valid(ofproto, flow);
+ if (facet && facet->rule == rule) {
+ facet_execute(ofproto, facet, packet);
+ return 0;
+ }
+
+ /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
+ * create a new facet for it and use that. */
+ if (rule_dpif_lookup(ofproto, flow) == rule) {
+ facet = facet_create(rule, flow, packet);
+ facet_execute(ofproto, facet, packet);
+ facet_install(ofproto, facet, true);
+ return 0;
+ }
+
+ /* We can't account anything to a facet. If we were to try, then that
+ * facet would have a non-matching rule, busting our invariants. */
+ action_xlate_ctx_init(&ctx, ofproto, flow, packet);
+ odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
+ size = packet->size;
+ if (execute_odp_actions(ofproto, flow, odp_actions->data,
+ odp_actions->size, packet)) {
+ rule->used = time_msec();
+ rule->packet_count++;
+ rule->byte_count += size;
+ flow_push_stats(rule, flow, 1, size, rule->used);
+ }
+ ofpbuf_delete(odp_actions);
+
+ return 0;
+}
+
+static int
+rule_modify_actions(struct rule *rule_,
+ const union ofp_action *actions, size_t n_actions)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ int error;
+
+ error = validate_actions(actions, n_actions, &rule->up.cr.flow,
+ ofproto->max_ports);
+ if (!error) {
+ ofproto->need_revalidate = true;
+ }
+ return error;
+}
+\f
+/* Sends 'packet' out of port 'odp_port' within 'p'.
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port,
+ const struct ofpbuf *packet)
+{
+ struct ofpbuf odp_actions;
+ int error;
+
+ ofpbuf_init(&odp_actions, 32);
+ nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
+ error = dpif_execute(ofproto->dpif, odp_actions.data, odp_actions.size,
+ packet);
+ ofpbuf_uninit(&odp_actions);
+
+ if (error) {
+ VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
+ ofproto->up.name, odp_port, strerror(error));
+ }
+ return error;
+}
+\f
+/* OpenFlow to ODP action translation. */
+
+static void do_xlate_actions(const union ofp_action *in, size_t n_in,
+ struct action_xlate_ctx *ctx);
+static bool xlate_normal(struct action_xlate_ctx *);
+
+static void
+add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
+{
+ const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
+ uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
+
+ if (ofport) {
+ if (ofport->up.opp.config & htonl(OFPPC_NO_FWD)) {
+ /* Forwarding disabled on port. */
+ return;
+ }
+ } else {
+ /*
+ * We don't have an ofport record for this port, but it doesn't hurt to
+ * allow forwarding to it anyhow. Maybe such a port will appear later
+ * and we're pre-populating the flow table.
+ */
+ }
+
+ nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
+ ctx->nf_output_iface = ofp_port;
+}
+
+static void
+xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
+{
+ if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
+ struct rule_dpif *rule;
+ uint16_t old_in_port;
+
+ /* Look up a flow with 'in_port' as the input port. Then restore the
+ * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
+ * have surprising behavior). */
+ old_in_port = ctx->flow.in_port;
+ ctx->flow.in_port = in_port;
+ rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow);
+ ctx->flow.in_port = old_in_port;
+
+ if (ctx->resubmit_hook) {
+ ctx->resubmit_hook(ctx, rule);
+ }
+
+ if (rule) {
+ ctx->recurse++;
+ do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
+ ctx->recurse--;
+ }
+ } else {
+ static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+ VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
+ MAX_RESUBMIT_RECURSION);
+ }
+}
+
+static void
+flood_packets(struct ofproto_dpif *ofproto,
+ uint16_t ofp_in_port, ovs_be32 mask,
+ uint16_t *nf_output_iface, struct ofpbuf *odp_actions)
+{
+ struct ofport_dpif *ofport;
+
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ uint16_t ofp_port = ofport->up.ofp_port;
+ if (ofp_port != ofp_in_port && !(ofport->up.opp.config & mask)) {
+ nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT,
+ ofport->odp_port);
+ }
+ }
+ *nf_output_iface = NF_OUT_FLOOD;
+}
+
+static void
+xlate_output_action__(struct action_xlate_ctx *ctx,
+ uint16_t port, uint16_t max_len)
+{
+ uint16_t prev_nf_output_iface = ctx->nf_output_iface;
+
+ ctx->nf_output_iface = NF_OUT_DROP;
+
+ switch (port) {
+ case OFPP_IN_PORT:
+ add_output_action(ctx, ctx->flow.in_port);
+ break;
+ case OFPP_TABLE:
+ xlate_table_action(ctx, ctx->flow.in_port);
+ break;
+ case OFPP_NORMAL:
+ xlate_normal(ctx);
+ break;
+ case OFPP_FLOOD:
+ flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(OFPPC_NO_FLOOD),
+ &ctx->nf_output_iface, ctx->odp_actions);
+ break;
+ case OFPP_ALL:
+ flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0),
+ &ctx->nf_output_iface, ctx->odp_actions);
+ break;
+ case OFPP_CONTROLLER:
+ nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len);
+ break;
+ case OFPP_LOCAL:
+ add_output_action(ctx, OFPP_LOCAL);
+ break;
+ default:
+ if (port != ctx->flow.in_port) {
+ add_output_action(ctx, port);
+ }
+ break;
+ }
+
+ if (prev_nf_output_iface == NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_FLOOD;
+ } else if (ctx->nf_output_iface == NF_OUT_DROP) {
+ ctx->nf_output_iface = prev_nf_output_iface;
+ } else if (prev_nf_output_iface != NF_OUT_DROP &&
+ ctx->nf_output_iface != NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_MULTI;
+ }
+}
+
+static void
+xlate_output_action(struct action_xlate_ctx *ctx,
+ const struct ofp_action_output *oao)
+{
+ xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
+}
+
+/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
+ * optimization, because we're going to add another action that sets the
+ * priority immediately after, or because there are no actions following the
+ * pop. */
+static void
+remove_pop_action(struct action_xlate_ctx *ctx)
+{
+ if (ctx->odp_actions->size == ctx->last_pop_priority) {
+ ctx->odp_actions->size -= NLA_ALIGN(NLA_HDRLEN);
+ ctx->last_pop_priority = -1;
+ }
+}
+
+static void
+add_pop_action(struct action_xlate_ctx *ctx)
+{
+ if (ctx->odp_actions->size != ctx->last_pop_priority) {
+ nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
+ ctx->last_pop_priority = ctx->odp_actions->size;
+ }
+}
+
+static void
+xlate_enqueue_action(struct action_xlate_ctx *ctx,
+ const struct ofp_action_enqueue *oae)
+{
+ uint16_t ofp_port, odp_port;
+ uint32_t priority;
+ int error;
+
+ error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
+ &priority);
+ if (error) {
+ /* Fall back to ordinary output action. */
+ xlate_output_action__(ctx, ntohs(oae->port), 0);
+ return;
+ }
+
+ /* Figure out ODP output port. */
+ ofp_port = ntohs(oae->port);
+ if (ofp_port == OFPP_IN_PORT) {
+ ofp_port = ctx->flow.in_port;
+ }
+ odp_port = ofp_port_to_odp_port(ofp_port);
+
+ /* Add ODP actions. */
+ remove_pop_action(ctx);
+ nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
+ add_output_action(ctx, odp_port);
+ add_pop_action(ctx);
+
+ /* Update NetFlow output port. */
+ if (ctx->nf_output_iface == NF_OUT_DROP) {
+ ctx->nf_output_iface = odp_port;
+ } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_MULTI;
+ }
+}
+
+static void
+xlate_set_queue_action(struct action_xlate_ctx *ctx,
+ const struct nx_action_set_queue *nasq)
+{
+ uint32_t priority;
+ int error;
+
+ error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
+ &priority);
+ if (error) {
+ /* Couldn't translate queue to a priority, so ignore. A warning
+ * has already been logged. */
+ return;
+ }
+
+ remove_pop_action(ctx);
+ nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
+}
+
+static void
+xlate_set_dl_tci(struct action_xlate_ctx *ctx)
+{
+ ovs_be16 tci = ctx->flow.vlan_tci;
+ if (!(tci & htons(VLAN_CFI))) {
+ nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
+ } else {
+ nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
+ tci & ~htons(VLAN_CFI));
+ }
+}
+
+struct xlate_reg_state {
+ ovs_be16 vlan_tci;
+ ovs_be64 tun_id;
+};
+
+static void
+save_reg_state(const struct action_xlate_ctx *ctx,
+ struct xlate_reg_state *state)
+{
+ state->vlan_tci = ctx->flow.vlan_tci;
+ state->tun_id = ctx->flow.tun_id;
+}
+
+static void
+update_reg_state(struct action_xlate_ctx *ctx,
+ const struct xlate_reg_state *state)
+{
+ if (ctx->flow.vlan_tci != state->vlan_tci) {
+ xlate_set_dl_tci(ctx);
+ }
+ if (ctx->flow.tun_id != state->tun_id) {
+ nl_msg_put_be64(ctx->odp_actions,
+ ODP_ACTION_ATTR_SET_TUNNEL, ctx->flow.tun_id);
+ }
+}
+
+static void
+xlate_autopath(struct action_xlate_ctx *ctx,
+ const struct nx_action_autopath *naa)
+{
+ uint16_t ofp_port = ntohl(naa->id);
+ struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
+
+ if (!port || !port->bundle) {
+ ofp_port = OFPP_NONE;
+ } else if (port->bundle->bond) {
+ /* Autopath does not support VLAN hashing. */
+ struct ofport_dpif *slave = bond_choose_output_slave(
+ port->bundle->bond, &ctx->flow, OFP_VLAN_NONE, &ctx->tags);
+ if (slave) {
+ ofp_port = slave->up.ofp_port;
+ }
+ }
+ autopath_execute(naa, &ctx->flow, ofp_port);
+}
+
+static void
+xlate_nicira_action(struct action_xlate_ctx *ctx,
+ const struct nx_action_header *nah)
+{
+ const struct nx_action_resubmit *nar;
+ const struct nx_action_set_tunnel *nast;
+ const struct nx_action_set_queue *nasq;
+ const struct nx_action_multipath *nam;
+ const struct nx_action_autopath *naa;
+ enum nx_action_subtype subtype = ntohs(nah->subtype);
+ struct xlate_reg_state state;
+ ovs_be64 tun_id;
+
+ assert(nah->vendor == htonl(NX_VENDOR_ID));
+ switch (subtype) {
+ case NXAST_RESUBMIT:
+ nar = (const struct nx_action_resubmit *) nah;
+ xlate_table_action(ctx, ntohs(nar->in_port));
+ break;
+
+ case NXAST_SET_TUNNEL:
+ nast = (const struct nx_action_set_tunnel *) nah;
+ tun_id = htonll(ntohl(nast->tun_id));
+ nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
+ ctx->flow.tun_id = tun_id;
+ break;
+
+ case NXAST_DROP_SPOOFED_ARP:
+ if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
+ nl_msg_put_flag(ctx->odp_actions,
+ ODP_ACTION_ATTR_DROP_SPOOFED_ARP);
+ }
+ break;
+
+ case NXAST_SET_QUEUE:
+ nasq = (const struct nx_action_set_queue *) nah;
+ xlate_set_queue_action(ctx, nasq);
+ break;
+
+ case NXAST_POP_QUEUE:
+ add_pop_action(ctx);
+ break;
+
+ case NXAST_REG_MOVE:
+ save_reg_state(ctx, &state);
+ nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
+ &ctx->flow);
+ update_reg_state(ctx, &state);
+ break;
+
+ case NXAST_REG_LOAD:
+ save_reg_state(ctx, &state);
+ nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
+ &ctx->flow);
+ update_reg_state(ctx, &state);
+ break;
+
+ case NXAST_NOTE:
+ /* Nothing to do. */
+ break;
+
+ case NXAST_SET_TUNNEL64:
+ tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
+ nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
+ ctx->flow.tun_id = tun_id;
+ break;
+
+ case NXAST_MULTIPATH:
+ nam = (const struct nx_action_multipath *) nah;
+ multipath_execute(nam, &ctx->flow);
+ break;
+
+ case NXAST_AUTOPATH:
+ naa = (const struct nx_action_autopath *) nah;
+ xlate_autopath(ctx, naa);
+ break;
+
+ /* If you add a new action here that modifies flow data, don't forget to
+ * update the flow key in ctx->flow at the same time. */
+
+ case NXAST_SNAT__OBSOLETE:
+ default:
+ VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) subtype);
+ break;
+ }
+}
+
+static void
+do_xlate_actions(const union ofp_action *in, size_t n_in,
+ struct action_xlate_ctx *ctx)
+{
+ const struct ofport_dpif *port;
+ struct actions_iterator iter;
+ const union ofp_action *ia;
+
+ port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
+ if (port
+ && port->up.opp.config & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
+ port->up.opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
+ ? htonl(OFPPC_NO_RECV_STP)
+ : htonl(OFPPC_NO_RECV))) {
+ /* Drop this flow. */
+ return;
+ }
+
+ for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
+ enum ofp_action_type type = ntohs(ia->type);
+ const struct ofp_action_dl_addr *oada;
+
+ switch (type) {
+ case OFPAT_OUTPUT:
+ xlate_output_action(ctx, &ia->output);
+ break;
+
+ case OFPAT_SET_VLAN_VID:
+ ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+ ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
+ xlate_set_dl_tci(ctx);
+ break;
+
+ case OFPAT_SET_VLAN_PCP:
+ ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ ctx->flow.vlan_tci |= htons(
+ (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+ xlate_set_dl_tci(ctx);
+ break;
+
+ case OFPAT_STRIP_VLAN:
+ ctx->flow.vlan_tci = htons(0);
+ xlate_set_dl_tci(ctx);
+ break;
+
+ case OFPAT_SET_DL_SRC:
+ oada = ((struct ofp_action_dl_addr *) ia);
+ nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
+ oada->dl_addr, ETH_ADDR_LEN);
+ memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPAT_SET_DL_DST:
+ oada = ((struct ofp_action_dl_addr *) ia);
+ nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
+ oada->dl_addr, ETH_ADDR_LEN);
+ memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPAT_SET_NW_SRC:
+ nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_SRC,
+ ia->nw_addr.nw_addr);
+ ctx->flow.nw_src = ia->nw_addr.nw_addr;
+ break;
+
+ case OFPAT_SET_NW_DST:
+ nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_DST,
+ ia->nw_addr.nw_addr);
+ ctx->flow.nw_dst = ia->nw_addr.nw_addr;
+ break;
+
+ case OFPAT_SET_NW_TOS:
+ nl_msg_put_u8(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_TOS,
+ ia->nw_tos.nw_tos);
+ ctx->flow.nw_tos = ia->nw_tos.nw_tos;
+ break;
+
+ case OFPAT_SET_TP_SRC:
+ nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_SRC,
+ ia->tp_port.tp_port);
+ ctx->flow.tp_src = ia->tp_port.tp_port;
+ break;
+
+ case OFPAT_SET_TP_DST:
+ nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_DST,
+ ia->tp_port.tp_port);
+ ctx->flow.tp_dst = ia->tp_port.tp_port;
+ break;
+
+ case OFPAT_VENDOR:
+ xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
+ break;
+
+ case OFPAT_ENQUEUE:
+ xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+ break;
+
+ default:
+ VLOG_DBG_RL(&rl, "unknown action type %d", (int) type);
+ break;
+ }
+ }
+}
+
+static void
+action_xlate_ctx_init(struct action_xlate_ctx *ctx,
+ struct ofproto_dpif *ofproto, const struct flow *flow,
+ const struct ofpbuf *packet)
+{
+ ctx->ofproto = ofproto;
+ ctx->flow = *flow;
+ ctx->packet = packet;
+ ctx->resubmit_hook = NULL;
+}
+
+static struct ofpbuf *
+xlate_actions(struct action_xlate_ctx *ctx,
+ const union ofp_action *in, size_t n_in)
+{
+ COVERAGE_INC(ofproto_dpif_xlate);
+
+ ctx->odp_actions = ofpbuf_new(512);
+ ctx->tags = 0;
+ ctx->may_set_up_flow = true;
+ ctx->nf_output_iface = NF_OUT_DROP;
+ ctx->recurse = 0;
+ ctx->last_pop_priority = -1;
+
+ if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
+ ctx->may_set_up_flow = false;
+ } else {
+ do_xlate_actions(in, n_in, ctx);
+ }
+
+ remove_pop_action(ctx);
+
+ /* Check with in-band control to see if we're allowed to set up this
+ * flow. */
+ if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
+ ctx->odp_actions->data,
+ ctx->odp_actions->size)) {
+ ctx->may_set_up_flow = false;
+ }
+
+ return ctx->odp_actions;
+}
+\f
+/* OFPP_NORMAL implementation. */
+
+struct dst {
+ struct ofport_dpif *port;
+ uint16_t vlan;
+};
+
+struct dst_set {
+ struct dst builtin[32];
+ struct dst *dsts;
+ size_t n, allocated;
+};
+
+static void dst_set_init(struct dst_set *);
+static void dst_set_add(struct dst_set *, const struct dst *);
+static void dst_set_free(struct dst_set *);
+
+static struct ofport_dpif *ofbundle_get_a_port(const struct ofbundle *);
+
+static bool
+set_dst(struct action_xlate_ctx *ctx, struct dst *dst,
+ const struct ofbundle *in_bundle, const struct ofbundle *out_bundle)
+{
+ dst->vlan = (out_bundle->vlan >= 0 ? OFP_VLAN_NONE
+ : in_bundle->vlan >= 0 ? in_bundle->vlan
+ : ctx->flow.vlan_tci == 0 ? OFP_VLAN_NONE
+ : vlan_tci_to_vid(ctx->flow.vlan_tci));
+
+ dst->port = (!out_bundle->bond
+ ? ofbundle_get_a_port(out_bundle)
+ : bond_choose_output_slave(out_bundle->bond, &ctx->flow,
+ dst->vlan, &ctx->tags));
+
+ return dst->port != NULL;
+}
+
+static int
+mirror_mask_ffs(mirror_mask_t mask)
+{
+ BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
+ return ffs(mask);
+}
+
+static void
+dst_set_init(struct dst_set *set)
+{
+ set->dsts = set->builtin;
+ set->n = 0;
+ set->allocated = ARRAY_SIZE(set->builtin);
+}
+
+static void
+dst_set_add(struct dst_set *set, const struct dst *dst)
+{
+ if (set->n >= set->allocated) {
+ size_t new_allocated;
+ struct dst *new_dsts;
+
+ new_allocated = set->allocated * 2;
+ new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
+ memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
+
+ dst_set_free(set);
+
+ set->dsts = new_dsts;
+ set->allocated = new_allocated;
+ }
+ set->dsts[set->n++] = *dst;
+}
+
+static void
+dst_set_free(struct dst_set *set)
+{
+ if (set->dsts != set->builtin) {
+ free(set->dsts);
+ }
+}
+
+static bool
+dst_is_duplicate(const struct dst_set *set, const struct dst *test)
+{
+ size_t i;
+ for (i = 0; i < set->n; i++) {
+ if (set->dsts[i].vlan == test->vlan
+ && set->dsts[i].port == test->port) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan)
+{
+ return bundle->vlan < 0 && vlan_bitmap_contains(bundle->trunks, vlan);
+}
+
+static bool
+ofbundle_includes_vlan(const struct ofbundle *bundle, uint16_t vlan)
+{
+ return vlan == bundle->vlan || ofbundle_trunks_vlan(bundle, vlan);
+}
+
+/* Returns an arbitrary interface within 'bundle'. */
+static struct ofport_dpif *
+ofbundle_get_a_port(const struct ofbundle *bundle)
+{
+ return CONTAINER_OF(list_front(&bundle->ports),
+ struct ofport_dpif, bundle_node);
+}
+
+static void
+compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan,
+ const struct ofbundle *in_bundle,
+ const struct ofbundle *out_bundle, struct dst_set *set)
+{
+ struct dst dst;
+
+ if (out_bundle == OFBUNDLE_FLOOD) {
+ struct ofbundle *bundle;
+
+ HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
+ if (bundle != in_bundle
+ && ofbundle_includes_vlan(bundle, vlan)
+ && bundle->floodable
+ && !bundle->mirror_out
+ && set_dst(ctx, &dst, in_bundle, bundle)) {
+ dst_set_add(set, &dst);
+ }
+ }
+ ctx->nf_output_iface = NF_OUT_FLOOD;
+ } else if (out_bundle && set_dst(ctx, &dst, in_bundle, out_bundle)) {
+ dst_set_add(set, &dst);
+ ctx->nf_output_iface = dst.port->odp_port;
+ }
+}
+
+static bool
+vlan_is_mirrored(const struct ofmirror *m, int vlan)
+{
+ return vlan_bitmap_contains(m->vlans, vlan);
+}
+
+static void
+compose_mirror_dsts(struct action_xlate_ctx *ctx,
+ uint16_t vlan, const struct ofbundle *in_bundle,
+ struct dst_set *set)
+{
+ struct ofproto_dpif *ofproto = ctx->ofproto;
+ mirror_mask_t mirrors;
+ int flow_vlan;
+ size_t i;
+
+ mirrors = in_bundle->src_mirrors;
+ for (i = 0; i < set->n; i++) {
+ mirrors |= set->dsts[i].port->bundle->dst_mirrors;
+ }
+
+ if (!mirrors) {
+ return;
+ }
+
+ flow_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci);
+ if (flow_vlan == 0) {
+ flow_vlan = OFP_VLAN_NONE;
+ }
+
+ while (mirrors) {
+ struct ofmirror *m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
+ if (vlan_is_mirrored(m, vlan)) {
+ struct dst dst;
+
+ if (m->out) {
+ if (set_dst(ctx, &dst, in_bundle, m->out)
+ && !dst_is_duplicate(set, &dst)) {
+ dst_set_add(set, &dst);
+ }
+ } else {
+ struct ofbundle *bundle;
+
+ HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+ if (ofbundle_includes_vlan(bundle, m->out_vlan)
+ && set_dst(ctx, &dst, in_bundle, bundle))
+ {
+ if (bundle->vlan < 0) {
+ dst.vlan = m->out_vlan;
+ }
+ if (dst_is_duplicate(set, &dst)) {
+ continue;
+ }
+
+ /* Use the vlan tag on the original flow instead of
+ * the one passed in the vlan parameter. This ensures
+ * that we compare the vlan from before any implicit
+ * tagging tags place. This is necessary because
+ * dst->vlan is the final vlan, after removing implicit
+ * tags. */
+ if (bundle == in_bundle && dst.vlan == flow_vlan) {
+ /* Don't send out input port on same VLAN. */
+ continue;
+ }
+ dst_set_add(set, &dst);
+ }
+ }
+ }
+ }
+ mirrors &= mirrors - 1;
+ }
+}
+
+static void
+compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan,
+ const struct ofbundle *in_bundle,
+ const struct ofbundle *out_bundle)
+{
+ uint16_t initial_vlan, cur_vlan;
+ const struct dst *dst;
+ struct dst_set set;
+
+ dst_set_init(&set);
+ compose_dsts(ctx, vlan, in_bundle, out_bundle, &set);
+ compose_mirror_dsts(ctx, vlan, in_bundle, &set);
+
+ /* Output all the packets we can without having to change the VLAN. */
+ initial_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci);
+ if (initial_vlan == 0) {
+ initial_vlan = OFP_VLAN_NONE;
+ }
+ for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
+ if (dst->vlan != initial_vlan) {
+ continue;
+ }
+ nl_msg_put_u32(ctx->odp_actions,
+ ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port);
+ }
+
+ /* Then output the rest. */
+ cur_vlan = initial_vlan;
+ for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
+ if (dst->vlan == initial_vlan) {
+ continue;
+ }
+ if (dst->vlan != cur_vlan) {
+ if (dst->vlan == OFP_VLAN_NONE) {
+ nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
+ } else {
+ ovs_be16 tci;
+ tci = htons(dst->vlan & VLAN_VID_MASK);
+ tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK);
+ nl_msg_put_be16(ctx->odp_actions,
+ ODP_ACTION_ATTR_SET_DL_TCI, tci);
+ }
+ cur_vlan = dst->vlan;
+ }
+ nl_msg_put_u32(ctx->odp_actions,
+ ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port);
+ }
+
+ dst_set_free(&set);
+}
+
+/* Returns the effective vlan of a packet, taking into account both the
+ * 802.1Q header and implicitly tagged ports. A value of 0 indicates that
+ * the packet is untagged and -1 indicates it has an invalid header and
+ * should be dropped. */
+static int
+flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow,
+ struct ofbundle *in_bundle, bool have_packet)
+{
+ int vlan = vlan_tci_to_vid(flow->vlan_tci);
+ if (in_bundle->vlan >= 0) {
+ if (vlan) {
+ if (have_packet) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
+ "packet received on port %s configured with "
+ "implicit VLAN %"PRIu16,
+ ofproto->up.name, vlan,
+ in_bundle->name, in_bundle->vlan);
+ }
+ return -1;
+ }
+ vlan = in_bundle->vlan;
+ } else {
+ if (!ofbundle_includes_vlan(in_bundle, vlan)) {
+ if (have_packet) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
+ "packet received on port %s not configured for "
+ "trunking VLAN %d",
+ ofproto->up.name, vlan, in_bundle->name, vlan);
+ }
+ return -1;
+ }
+ }
+
+ return vlan;
+}
+
+/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
+ * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to
+ * indicate this; newer upstream kernels use gratuitous ARP requests. */
+static bool
+is_gratuitous_arp(const struct flow *flow)
+{
+ return (flow->dl_type == htons(ETH_TYPE_ARP)
+ && eth_addr_is_broadcast(flow->dl_dst)
+ && (flow->nw_proto == ARP_OP_REPLY
+ || (flow->nw_proto == ARP_OP_REQUEST
+ && flow->nw_src == flow->nw_dst)));
+}
+
+static void
+update_learning_table(struct ofproto_dpif *ofproto,
+ const struct flow *flow, int vlan,
+ struct ofbundle *in_bundle)
+{
+ struct mac_entry *mac;
+
+ if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) {
+ return;
+ }
+
+ mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan);
+ if (is_gratuitous_arp(flow)) {
+ /* We don't want to learn from gratuitous ARP packets that are
+ * reflected back over bond slaves so we lock the learning table. */
+ if (!in_bundle->bond) {
+ mac_entry_set_grat_arp_lock(mac);
+ } else if (mac_entry_is_grat_arp_locked(mac)) {
+ return;
+ }
+ }
+
+ if (mac_entry_is_new(mac) || mac->port.p != in_bundle) {
+ /* The log messages here could actually be useful in debugging,
+ * so keep the rate limit relatively high. */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
+ VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
+ "on port %s in VLAN %d",
+ ofproto->up.name, ETH_ADDR_ARGS(flow->dl_src),
+ in_bundle->name, vlan);
+
+ mac->port.p = in_bundle;
+ tag_set_add(&ofproto->revalidate_set,
+ mac_learning_changed(ofproto->ml, mac));
+ }
+}
+
+/* Determines whether packets in 'flow' within 'br' should be forwarded or
+ * dropped. Returns true if they may be forwarded, false if they should be
+ * dropped.
+ *
+ * If 'have_packet' is true, it indicates that the caller is processing a
+ * received packet. If 'have_packet' is false, then the caller is just
+ * revalidating an existing flow because configuration has changed. Either
+ * way, 'have_packet' only affects logging (there is no point in logging errors
+ * during revalidation).
+ *
+ * Sets '*in_portp' to the input port. This will be a null pointer if
+ * flow->in_port does not designate a known input port (in which case
+ * is_admissible() returns false).
+ *
+ * When returning true, sets '*vlanp' to the effective VLAN of the input
+ * packet, as returned by flow_get_vlan().
+ *
+ * May also add tags to '*tags', although the current implementation only does
+ * so in one special case.
+ */
+static bool
+is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
+ bool have_packet,
+ tag_type *tags, int *vlanp, struct ofbundle **in_bundlep)
+{
+ struct ofport_dpif *in_port;
+ struct ofbundle *in_bundle;
+ int vlan;
+
+ /* Find the port and bundle for the received packet. */
+ in_port = get_ofp_port(ofproto, flow->in_port);
+ *in_bundlep = in_bundle = in_port ? in_port->bundle : NULL;
+ if (!in_port || !in_bundle) {
+ /* No interface? Something fishy... */
+ if (have_packet) {
+ /* Odd. A few possible reasons here:
+ *
+ * - We deleted a port but there are still a few packets queued up
+ * from it.
+ *
+ * - Someone externally added a port (e.g. "ovs-dpctl add-if") that
+ * we don't know about.
+ *
+ * - Packet arrived on the local port but the local port is not
+ * part of a bundle.
+ */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
+ "port %"PRIu16,
+ ofproto->up.name, flow->in_port);
+ }
+ return false;
+ }
+ *vlanp = vlan = flow_get_vlan(ofproto, flow, in_bundle, have_packet);
+ if (vlan < 0) {
+ return false;
+ }
+
+ /* Drop frames for reserved multicast addresses. */
+ if (eth_addr_is_reserved(flow->dl_dst)) {
+ return false;
+ }
+
+ /* Drop frames on bundles reserved for mirroring. */
+ if (in_bundle->mirror_out) {
+ if (have_packet) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
+ "%s, which is reserved exclusively for mirroring",
+ ofproto->up.name, in_bundle->name);
+ }
+ return false;
+ }
+
+ if (in_bundle->bond) {
+ struct mac_entry *mac;
+
+ switch (bond_check_admissibility(in_bundle->bond, in_port,
+ flow->dl_dst, tags)) {
+ case BV_ACCEPT:
+ break;
+
+ case BV_DROP:
+ return false;
+
+ case BV_DROP_IF_MOVED:
+ mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL);
+ if (mac && mac->port.p != in_bundle &&
+ (!is_gratuitous_arp(flow)
+ || mac_entry_is_grat_arp_locked(mac))) {
+ return false;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+/* If the composed actions may be applied to any packet in the given 'flow',
+ * returns true. Otherwise, the actions should only be applied to 'packet', or
+ * not at all, if 'packet' was NULL. */
+static bool
+xlate_normal(struct action_xlate_ctx *ctx)
+{
+ struct ofbundle *in_bundle;
+ struct ofbundle *out_bundle;
+ struct mac_entry *mac;
+ int vlan;
+
+ /* Check whether we should drop packets in this flow. */
+ if (!is_admissible(ctx->ofproto, &ctx->flow, ctx->packet != NULL,
+ &ctx->tags, &vlan, &in_bundle)) {
+ out_bundle = NULL;
+ goto done;
+ }
+
+ /* Learn source MAC (but don't try to learn from revalidation). */
+ if (ctx->packet) {
+ update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
+ }
+
+ /* Determine output bundle. */
+ mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
+ &ctx->tags);
+ if (mac) {
+ out_bundle = mac->port.p;
+ } else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) {
+ /* If we are revalidating but don't have a learning entry then eject
+ * the flow. Installing a flow that floods packets opens up a window
+ * of time where we could learn from a packet reflected on a bond and
+ * blackhole packets before the learning table is updated to reflect
+ * the correct port. */
+ return false;
+ } else {
+ out_bundle = OFBUNDLE_FLOOD;
+ }
+
+ /* Don't send packets out their input bundles. */
+ if (in_bundle == out_bundle) {
+ out_bundle = NULL;
+ }
+
+done:
+ if (in_bundle) {
+ compose_actions(ctx, vlan, in_bundle, out_bundle);
+ }
+
+ return true;
+}
+\f
+static bool
+get_drop_frags(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ bool drop_frags;
+
+ dpif_get_drop_frags(ofproto->dpif, &drop_frags);
+ return drop_frags;
+}
+
+static void
+set_drop_frags(struct ofproto *ofproto_, bool drop_frags)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ dpif_set_drop_frags(ofproto->dpif, drop_frags);
+}
+
+static int
+packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
+ const struct flow *flow,
+ const union ofp_action *ofp_actions, size_t n_ofp_actions)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ int error;
+
+ error = validate_actions(ofp_actions, n_ofp_actions, flow,
+ ofproto->max_ports);
+ if (!error) {
+ struct action_xlate_ctx ctx;
+ struct ofpbuf *odp_actions;
+
+ action_xlate_ctx_init(&ctx, ofproto, flow, packet);
+ odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions);
+ dpif_execute(ofproto->dpif, odp_actions->data, odp_actions->size,
+ packet);
+ ofpbuf_delete(odp_actions);
+ }
+ return error;
+}
+
+static void
+get_netflow_ids(const struct ofproto *ofproto_,
+ uint8_t *engine_type, uint8_t *engine_id)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ dpif_get_netflow_ids(ofproto->dpif, engine_type, engine_id);
+}
+\f
+static struct ofproto_dpif *
+ofproto_dpif_lookup(const char *name)
+{
+ struct ofproto *ofproto = ofproto_lookup(name);
+ return (ofproto && ofproto->ofproto_class == &ofproto_dpif_class
+ ? ofproto_dpif_cast(ofproto)
+ : NULL);
+}
+
+static void
+ofproto_unixctl_fdb_show(struct unixctl_conn *conn,
+ const char *args, void *aux OVS_UNUSED)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ const struct ofproto_dpif *ofproto;
+ const struct mac_entry *e;
+
+ ofproto = ofproto_dpif_lookup(args);
+ if (!ofproto) {
+ unixctl_command_reply(conn, 501, "no such bridge");
+ return;
+ }
+
+ ds_put_cstr(&ds, " port VLAN MAC Age\n");
+ LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
+ struct ofbundle *bundle = e->port.p;
+ ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n",
+ ofbundle_get_a_port(bundle)->odp_port,
+ e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
+ }
+ unixctl_command_reply(conn, 200, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
+struct ofproto_trace {
+ struct action_xlate_ctx ctx;
+ struct flow flow;
+ struct ds *result;
+};
+
+static void
+trace_format_rule(struct ds *result, int level, const struct rule *rule)
+{
+ ds_put_char_multiple(result, '\t', level);
+ if (!rule) {
+ ds_put_cstr(result, "No match\n");
+ return;
+ }
+
+ ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
+ ntohll(rule->flow_cookie));
+ cls_rule_format(&rule->cr, result);
+ ds_put_char(result, '\n');
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_cstr(result, "OpenFlow ");
+ ofp_print_actions(result, (const struct ofp_action_header *) rule->actions,
+ rule->n_actions * sizeof *rule->actions);
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_format_flow(struct ds *result, int level, const char *title,
+ struct ofproto_trace *trace)
+{
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s: ", title);
+ if (flow_equal(&trace->ctx.flow, &trace->flow)) {
+ ds_put_cstr(result, "unchanged");
+ } else {
+ flow_format(result, &trace->ctx.flow);
+ trace->flow = trace->ctx.flow;
+ }
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
+{
+ struct ofproto_trace *trace = CONTAINER_OF(ctx, struct ofproto_trace, ctx);
+ struct ds *result = trace->result;
+
+ ds_put_char(result, '\n');
+ trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
+ trace_format_rule(result, ctx->recurse + 1, &rule->up);
+}
+
+static void
+ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
+ void *aux OVS_UNUSED)
+{
+ char *dpname, *in_port_s, *tun_id_s, *packet_s;
+ char *args = xstrdup(args_);
+ char *save_ptr = NULL;
+ struct ofproto_dpif *ofproto;
+ struct ofpbuf packet;
+ struct rule_dpif *rule;
+ struct ds result;
+ struct flow flow;
+ uint16_t in_port;
+ ovs_be64 tun_id;
+ char *s;
+
+ ofpbuf_init(&packet, strlen(args) / 2);
+ ds_init(&result);
+
+ dpname = strtok_r(args, " ", &save_ptr);
+ tun_id_s = strtok_r(NULL, " ", &save_ptr);
+ in_port_s = strtok_r(NULL, " ", &save_ptr);
+ packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
+ if (!dpname || !in_port_s || !packet_s) {
+ unixctl_command_reply(conn, 501, "Bad command syntax");
+ goto exit;
+ }
+
+ ofproto = ofproto_dpif_lookup(dpname);
+ if (!ofproto) {
+ unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list "
+ "for help)");
+ goto exit;
+ }
+
+ tun_id = htonll(strtoull(tun_id_s, NULL, 0));
+ in_port = ofp_port_to_odp_port(atoi(in_port_s));
+
+ packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
+ packet_s += strspn(packet_s, " ");
+ if (*packet_s != '\0') {
+ unixctl_command_reply(conn, 501, "Trailing garbage in command");
+ goto exit;
+ }
+ if (packet.size < ETH_HEADER_LEN) {
+ unixctl_command_reply(conn, 501, "Packet data too short for Ethernet");
+ goto exit;
+ }
+
+ ds_put_cstr(&result, "Packet: ");
+ s = ofp_packet_to_string(packet.data, packet.size, packet.size);
+ ds_put_cstr(&result, s);
+ free(s);
+
+ flow_extract(&packet, tun_id, in_port, &flow);
+ ds_put_cstr(&result, "Flow: ");
+ flow_format(&result, &flow);
+ ds_put_char(&result, '\n');
+
+ rule = rule_dpif_lookup(ofproto, &flow);
+ trace_format_rule(&result, 0, &rule->up);
+ if (rule) {
+ struct ofproto_trace trace;
+ struct ofpbuf *odp_actions;
+
+ trace.result = &result;
+ trace.flow = flow;
+ action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet);
+ trace.ctx.resubmit_hook = trace_resubmit;
+ odp_actions = xlate_actions(&trace.ctx,
+ rule->up.actions, rule->up.n_actions);
+
+ ds_put_char(&result, '\n');
+ trace_format_flow(&result, 0, "Final flow", &trace);
+ ds_put_cstr(&result, "Datapath actions: ");
+ format_odp_actions(&result, odp_actions->data, odp_actions->size);
+ ofpbuf_delete(odp_actions);
+ }
+
+ unixctl_command_reply(conn, 200, ds_cstr(&result));
+
+exit:
+ ds_destroy(&result);
+ ofpbuf_uninit(&packet);
+ free(args);
+}
+
+static void
+ofproto_dpif_unixctl_init(void)
+{
+ static bool registered;
+ if (registered) {
+ return;
+ }
+ registered = true;
+
+ unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL);
+ unixctl_command_register("fdb/show", ofproto_unixctl_fdb_show, NULL);
+}
+\f
+const struct ofproto_class ofproto_dpif_class = {
+ enumerate_types,
+ enumerate_names,
+ del,
+ alloc,
+ construct,
+ destruct,
+ dealloc,
+ run,
+ wait,
+ flush,
+ get_features,
+ get_tables,
+ port_alloc,
+ port_construct,
+ port_destruct,
+ port_dealloc,
+ port_modified,
+ port_reconfigured,
+ port_query_by_name,
+ port_add,
+ port_del,
+ port_dump_start,
+ port_dump_next,
+ port_dump_done,
+ port_poll,
+ port_poll_wait,
+ port_is_lacp_current,
+ rule_alloc,
+ rule_construct,
+ rule_destruct,
+ rule_dealloc,
+ rule_get_stats,
+ rule_execute,
+ rule_modify_actions,
+ get_drop_frags,
+ set_drop_frags,
+ packet_out,
+ set_netflow,
+ get_netflow_ids,
+ set_sflow,
+ set_cfm,
+ get_cfm,
+ bundle_set,
+ bundle_remove,
+ mirror_set,
+ set_flood_vlans,
+ is_mirror_output_bundle,
+};
#include "ofproto.h"
#include <errno.h>
#include <inttypes.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <netinet/in.h>
#include <stdbool.h>
#include <stdlib.h>
-#include "autopath.h"
#include "byte-order.h"
-#include "cfm.h"
#include "classifier.h"
#include "connmgr.h"
#include "coverage.h"
-#include "dpif.h"
#include "dynamic-string.h"
-#include "fail-open.h"
#include "hash.h"
#include "hmap.h"
-#include "in-band.h"
-#include "mac-learning.h"
-#include "multipath.h"
#include "netdev.h"
-#include "netflow.h"
-#include "netlink.h"
#include "nx-match.h"
-#include "odp-util.h"
#include "ofp-print.h"
#include "ofp-util.h"
-#include "ofproto-sflow.h"
#include "ofpbuf.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
-#include "openvswitch/datapath-protocol.h"
#include "packets.h"
#include "pinsched.h"
#include "pktbuf.h"
#include "poll-loop.h"
-#include "rconn.h"
+#include "private.h"
#include "shash.h"
#include "sset.h"
-#include "stream-ssl.h"
-#include "tag.h"
-#include "timer.h"
#include "timeval.h"
#include "unaligned.h"
#include "unixctl.h"
-#include "vconn.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(ofproto);
-COVERAGE_DEFINE(facet_changed_rule);
-COVERAGE_DEFINE(facet_revalidate);
-COVERAGE_DEFINE(odp_overflow);
COVERAGE_DEFINE(ofproto_agg_request);
-COVERAGE_DEFINE(ofproto_costly_flags);
-COVERAGE_DEFINE(ofproto_ctlr_action);
-COVERAGE_DEFINE(ofproto_del_rule);
COVERAGE_DEFINE(ofproto_error);
-COVERAGE_DEFINE(ofproto_expiration);
-COVERAGE_DEFINE(ofproto_expired);
COVERAGE_DEFINE(ofproto_flows_req);
COVERAGE_DEFINE(ofproto_flush);
-COVERAGE_DEFINE(ofproto_invalidated);
COVERAGE_DEFINE(ofproto_no_packet_in);
-COVERAGE_DEFINE(ofproto_ofp2odp);
-COVERAGE_DEFINE(ofproto_packet_in);
COVERAGE_DEFINE(ofproto_packet_out);
COVERAGE_DEFINE(ofproto_queue_req);
COVERAGE_DEFINE(ofproto_recv_openflow);
COVERAGE_DEFINE(ofproto_reinit_ports);
-COVERAGE_DEFINE(ofproto_unexpected_rule);
COVERAGE_DEFINE(ofproto_uninstallable);
COVERAGE_DEFINE(ofproto_update_port);
-/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
- * flow translation. */
-#define MAX_RESUBMIT_RECURSION 16
+static void ofport_destroy__(struct ofport *);
+static void ofport_destroy(struct ofport *);
-struct rule;
+static int rule_create(struct ofproto *, const struct cls_rule *,
+ const union ofp_action *, size_t n_actions,
+ uint16_t idle_timeout, uint16_t hard_timeout,
+ ovs_be64 flow_cookie, bool send_flow_removed,
+ struct rule **rulep);
-struct ofport {
- struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */
- struct netdev *netdev;
- struct ofp_phy_port opp;
- uint16_t odp_port;
- struct cfm *cfm; /* Connectivity Fault Management, if any. */
-};
+static uint64_t pick_datapath_id(const struct ofproto *);
+static uint64_t pick_fallback_dpid(void);
-static void ofport_free(struct ofport *);
-static void ofport_run(struct ofproto *, struct ofport *);
-static void ofport_wait(struct ofport *);
+static void ofproto_destroy__(struct ofproto *);
+static void ofproto_flush_flows__(struct ofproto *);
-struct action_xlate_ctx {
-/* action_xlate_ctx_init() initializes these members. */
+static void ofproto_rule_destroy__(struct rule *);
+static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
- /* The ofproto. */
- struct ofproto *ofproto;
+static void handle_openflow(struct ofconn *, struct ofpbuf *);
- /* Flow to which the OpenFlow actions apply. xlate_actions() will modify
- * this flow when actions change header fields. */
- struct flow flow;
+static void update_port(struct ofproto *, const char *devname);
+static int init_ports(struct ofproto *);
+static void reinit_ports(struct ofproto *);
- /* The packet corresponding to 'flow', or a null pointer if we are
- * revalidating without a packet to refer to. */
- const struct ofpbuf *packet;
+static void ofproto_unixctl_init(void);
- /* If nonnull, called just before executing a resubmit action.
- *
- * This is normally null so the client has to set it manually after
- * calling action_xlate_ctx_init(). */
- void (*resubmit_hook)(struct action_xlate_ctx *, struct rule *);
+/* All registered ofproto classes, in probe order. */
+static const struct ofproto_class **ofproto_classes;
+static size_t n_ofproto_classes;
+static size_t allocated_ofproto_classes;
- /* If true, the speciality of 'flow' should be checked before executing
- * its actions. If special_cb returns false on 'flow' rendered
- * uninstallable and no actions will be executed. */
- bool check_special;
+/* Map from datapath name to struct ofproto, for use by unixctl commands. */
+static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
-/* xlate_actions() initializes and uses these members. The client might want
- * to look at them after it returns. */
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- struct ofpbuf *odp_actions; /* Datapath actions. */
- tag_type tags; /* Tags associated with OFPP_NORMAL actions. */
- bool may_set_up_flow; /* True ordinarily; false if the actions must
- * be reassessed for every packet. */
- uint16_t nf_output_iface; /* Output interface index for NetFlow. */
+static void
+ofproto_initialize(void)
+{
+ static bool inited;
-/* xlate_actions() initializes and uses these members, but the client has no
- * reason to look at them. */
+ if (!inited) {
+ inited = true;
+ ofproto_class_register(&ofproto_dpif_class);
+ }
+}
- int recurse; /* Recursion level, via xlate_table_action. */
- int last_pop_priority; /* Offset in 'odp_actions' just past most
- * recent ODP_ACTION_ATTR_SET_PRIORITY. */
-};
+/* 'type' should be a normalized datapath type, as returned by
+ * ofproto_normalize_type(). Returns the corresponding ofproto_class
+ * structure, or a null pointer if there is none registered for 'type'. */
+static const struct ofproto_class *
+ofproto_class_find__(const char *type)
+{
+ size_t i;
-static void action_xlate_ctx_init(struct action_xlate_ctx *,
- struct ofproto *, const struct flow *,
- const struct ofpbuf *);
-static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
- const union ofp_action *in, size_t n_in);
-
-/* An OpenFlow flow. */
-struct rule {
- long long int used; /* Time last used; time created if not used. */
- long long int created; /* Creation time. */
-
- /* These statistics:
- *
- * - Do include packets and bytes from facets that have been deleted or
- * whose own statistics have been folded into the rule.
- *
- * - Do include packets and bytes sent "by hand" that were accounted to
- * the rule without any facet being involved (this is a rare corner
- * case in rule_execute()).
- *
- * - Do not include packet or bytes that can be obtained from any facet's
- * packet_count or byte_count member or that can be obtained from the
- * datapath by, e.g., dpif_flow_get() for any facet.
- */
- uint64_t packet_count; /* Number of packets received. */
- uint64_t byte_count; /* Number of bytes received. */
-
- ovs_be64 flow_cookie; /* Controller-issued identifier. */
-
- struct cls_rule cr; /* In owning ofproto's classifier. */
- uint16_t idle_timeout; /* In seconds from time of last use. */
- uint16_t hard_timeout; /* In seconds from time of creation. */
- bool send_flow_removed; /* Send a flow removed message? */
- int n_actions; /* Number of elements in actions[]. */
- union ofp_action *actions; /* OpenFlow actions. */
- struct list facets; /* List of "struct facet"s. */
-};
+ ofproto_initialize();
+ for (i = 0; i < n_ofproto_classes; i++) {
+ const struct ofproto_class *class = ofproto_classes[i];
+ struct sset types;
+ bool found;
-static struct rule *rule_from_cls_rule(const struct cls_rule *);
-static bool rule_is_hidden(const struct rule *);
-
-static struct rule *rule_create(const struct cls_rule *,
- const union ofp_action *, size_t n_actions,
- uint16_t idle_timeout, uint16_t hard_timeout,
- ovs_be64 flow_cookie, bool send_flow_removed);
-static void rule_destroy(struct ofproto *, struct rule *);
-static void rule_free(struct rule *);
-
-static struct rule *rule_lookup(struct ofproto *, const struct flow *);
-static void rule_insert(struct ofproto *, struct rule *);
-static void rule_remove(struct ofproto *, struct rule *);
-
-static void rule_send_removed(struct ofproto *, struct rule *, uint8_t reason);
-static void rule_get_stats(const struct rule *, uint64_t *packets,
- uint64_t *bytes);
-
-/* An exact-match instantiation of an OpenFlow flow. */
-struct facet {
- long long int used; /* Time last used; time created if not used. */
-
- /* These statistics:
- *
- * - Do include packets and bytes sent "by hand", e.g. with
- * dpif_execute().
- *
- * - Do include packets and bytes that were obtained from the datapath
- * when a flow was deleted (e.g. dpif_flow_del()) or when its
- * statistics were reset (e.g. dpif_flow_put() with
- * DPIF_FP_ZERO_STATS).
- *
- * - Do not include any packets or bytes that can currently be obtained
- * from the datapath by, e.g., dpif_flow_get().
- */
- uint64_t packet_count; /* Number of packets received. */
- uint64_t byte_count; /* Number of bytes received. */
-
- uint64_t dp_packet_count; /* Last known packet count in the datapath. */
- uint64_t dp_byte_count; /* Last known byte count in the datapath. */
-
- uint64_t rs_packet_count; /* Packets pushed to resubmit children. */
- uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */
- long long int rs_used; /* Used time pushed to resubmit children. */
-
- /* Number of bytes passed to account_cb. This may include bytes that can
- * currently obtained from the datapath (thus, it can be greater than
- * byte_count). */
- uint64_t accounted_bytes;
-
- struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */
- struct list list_node; /* In owning rule's 'facets' list. */
- struct rule *rule; /* Owning rule. */
- struct flow flow; /* Exact-match flow. */
- bool installed; /* Installed in datapath? */
- bool may_install; /* True ordinarily; false if actions must
- * be reassessed for every packet. */
- size_t actions_len; /* Number of bytes in actions[]. */
- struct nlattr *actions; /* Datapath actions. */
- tag_type tags; /* Tags (set only by hooks). */
- struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
-};
+ sset_init(&types);
+ class->enumerate_types(&types);
+ found = sset_contains(&types, type);
+ sset_destroy(&types);
-static struct facet *facet_create(struct ofproto *, struct rule *,
- const struct flow *,
- const struct ofpbuf *packet);
-static void facet_remove(struct ofproto *, struct facet *);
-static void facet_free(struct facet *);
-
-static struct facet *facet_lookup_valid(struct ofproto *, const struct flow *);
-static bool facet_revalidate(struct ofproto *, struct facet *);
-
-static void facet_install(struct ofproto *, struct facet *, bool zero_stats);
-static void facet_uninstall(struct ofproto *, struct facet *);
-static void facet_flush_stats(struct ofproto *, struct facet *);
-
-static void facet_make_actions(struct ofproto *, struct facet *,
- const struct ofpbuf *packet);
-static void facet_reset_dp_stats(struct facet *, struct dpif_flow_stats *);
-static void facet_update_stats(struct ofproto *, struct facet *,
- const struct dpif_flow_stats *);
-static void facet_push_stats(struct ofproto *, struct facet *);
-
-static void send_packet_in(struct ofproto *, struct dpif_upcall *,
- const struct flow *, bool clone);
-
-struct ofproto {
- /* Settings. */
- uint64_t datapath_id; /* Datapath ID. */
- uint64_t fallback_dpid; /* Datapath ID if no better choice found. */
- char *mfr_desc; /* Manufacturer. */
- char *hw_desc; /* Hardware. */
- char *sw_desc; /* Software version. */
- char *serial_desc; /* Serial number. */
- char *dp_desc; /* Datapath description. */
-
- /* Datapath. */
- struct dpif *dpif;
- struct netdev_monitor *netdev_monitor;
- struct hmap ports; /* Contains "struct ofport"s. */
- struct shash port_by_name;
- uint32_t max_ports;
-
- /* Configuration. */
- struct netflow *netflow;
- struct ofproto_sflow *sflow;
-
- /* Flow table. */
- struct classifier cls;
- struct timer next_expiration;
-
- /* Facets. */
- struct hmap facets;
- bool need_revalidate;
- struct tag_set revalidate_set;
-
- /* OpenFlow connections. */
- struct connmgr *connmgr;
-
- /* Hooks for ovs-vswitchd. */
- const struct ofhooks *ofhooks;
- void *aux;
-
- /* Used by default ofhooks. */
- struct mac_learning *ml;
-};
+ if (found) {
+ return class;
+ }
+ }
+ VLOG_WARN("unknown datapath type %s", type);
+ return NULL;
+}
-/* Map from dpif name to struct ofproto, for use by unixctl commands. */
-static struct shash all_ofprotos = SHASH_INITIALIZER(&all_ofprotos);
+/* Registers a new ofproto class. After successful registration, new ofprotos
+ * of that type can be created using ofproto_create(). */
+int
+ofproto_class_register(const struct ofproto_class *new_class)
+{
+ size_t i;
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ for (i = 0; i < n_ofproto_classes; i++) {
+ if (ofproto_classes[i] == new_class) {
+ return EEXIST;
+ }
+ }
-static const struct ofhooks default_ofhooks;
+ if (n_ofproto_classes >= allocated_ofproto_classes) {
+ ofproto_classes = x2nrealloc(ofproto_classes,
+ &allocated_ofproto_classes,
+ sizeof *ofproto_classes);
+ }
+ ofproto_classes[n_ofproto_classes++] = new_class;
+ return 0;
+}
-static uint64_t pick_datapath_id(const struct ofproto *);
-static uint64_t pick_fallback_dpid(void);
+/* Unregisters a datapath provider. 'type' must have been previously
+ * registered and not currently be in use by any ofprotos. After
+ * unregistration new datapaths of that type cannot be opened using
+ * ofproto_create(). */
+int
+ofproto_class_unregister(const struct ofproto_class *class)
+{
+ size_t i;
-static void ofproto_flush_flows__(struct ofproto *);
-static int ofproto_expire(struct ofproto *);
-static void flow_push_stats(struct ofproto *, const struct rule *,
- struct flow *, uint64_t packets, uint64_t bytes,
- long long int used);
+ for (i = 0; i < n_ofproto_classes; i++) {
+ if (ofproto_classes[i] == class) {
+ for (i++; i < n_ofproto_classes; i++) {
+ ofproto_classes[i - 1] = ofproto_classes[i];
+ }
+ n_ofproto_classes--;
+ return 0;
+ }
+ }
+ VLOG_WARN("attempted to unregister an ofproto class that is not "
+ "registered");
+ return EAFNOSUPPORT;
+}
-static void handle_upcall(struct ofproto *, struct dpif_upcall *);
+/* Clears 'types' and enumerates all registered ofproto types into it. The
+ * caller must first initialize the sset. */
+void
+ofproto_enumerate_types(struct sset *types)
+{
+ size_t i;
-static void handle_openflow(struct ofconn *, struct ofpbuf *);
+ ofproto_initialize();
+ for (i = 0; i < n_ofproto_classes; i++) {
+ ofproto_classes[i]->enumerate_types(types);
+ }
+}
-static struct ofport *get_port(const struct ofproto *, uint16_t odp_port);
-static void update_port(struct ofproto *, const char *devname);
-static int init_ports(struct ofproto *);
-static void reinit_ports(struct ofproto *);
+/* Returns the fully spelled out name for the given ofproto 'type'.
+ *
+ * Normalized type string can be compared with strcmp(). Unnormalized type
+ * string might be the same even if they have different spellings. */
+const char *
+ofproto_normalize_type(const char *type)
+{
+ return type && type[0] ? type : "system";
+}
-static void ofproto_unixctl_init(void);
+/* Clears 'names' and enumerates the names of all known created ofprotos with
+ * the given 'type'. The caller must first initialize the sset. Returns 0 if
+ * successful, otherwise a positive errno value.
+ *
+ * Some kinds of datapaths might not be practically enumerable. This is not
+ * considered an error. */
+int
+ofproto_enumerate_names(const char *type, struct sset *names)
+{
+ const struct ofproto_class *class = ofproto_class_find__(type);
+ return class ? class->enumerate_names(type, names) : EAFNOSUPPORT;
+ }
int
-ofproto_create(const char *datapath, const char *datapath_type,
- const struct ofhooks *ofhooks, void *aux,
+ofproto_create(const char *datapath_name, const char *datapath_type,
struct ofproto **ofprotop)
{
- char local_name[IF_NAMESIZE];
- struct ofproto *p;
- struct dpif *dpif;
+ const struct ofproto_class *class;
+ struct ofproto *ofproto;
int error;
*ofprotop = NULL;
+ ofproto_initialize();
ofproto_unixctl_init();
- /* Connect to datapath and start listening for messages. */
- error = dpif_open(datapath, datapath_type, &dpif);
- if (error) {
- VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
- return error;
- }
- error = dpif_recv_set_mask(dpif,
- ((1u << DPIF_UC_MISS) |
- (1u << DPIF_UC_ACTION) |
- (1u << DPIF_UC_SAMPLE)));
- if (error) {
- VLOG_ERR("failed to listen on datapath %s: %s",
- datapath, strerror(error));
- dpif_close(dpif);
- return error;
+ datapath_type = ofproto_normalize_type(datapath_type);
+ class = ofproto_class_find__(datapath_type);
+ if (!class) {
+ VLOG_WARN("could not create datapath %s of unknown type %s",
+ datapath_name, datapath_type);
+ return EAFNOSUPPORT;
}
- dpif_flow_flush(dpif);
- dpif_recv_purge(dpif);
- error = dpif_port_get_name(dpif, ODPP_LOCAL,
- local_name, sizeof local_name);
+ ofproto = class->alloc();
+ if (!ofproto) {
+ VLOG_ERR("failed to allocate datapath %s of type %s",
+ datapath_name, datapath_type);
+ return ENOMEM;
+ }
+
+ /* Initialize. */
+ memset(ofproto, 0, sizeof *ofproto);
+ ofproto->ofproto_class = class;
+ ofproto->name = xstrdup(datapath_name);
+ ofproto->type = xstrdup(datapath_type);
+ hmap_insert(&all_ofprotos, &ofproto->hmap_node,
+ hash_string(ofproto->name, 0));
+ ofproto->datapath_id = 0;
+ ofproto->fallback_dpid = pick_fallback_dpid();
+ ofproto->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
+ ofproto->hw_desc = xstrdup(DEFAULT_HW_DESC);
+ ofproto->sw_desc = xstrdup(DEFAULT_SW_DESC);
+ ofproto->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
+ ofproto->dp_desc = xstrdup(DEFAULT_DP_DESC);
+ ofproto->netdev_monitor = netdev_monitor_create();
+ hmap_init(&ofproto->ports);
+ shash_init(&ofproto->port_by_name);
+ ofproto->tables = NULL;
+ ofproto->n_tables = 0;
+ ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
+
+ error = ofproto->ofproto_class->construct(ofproto);
if (error) {
- VLOG_ERR("%s: cannot get name of datapath local port (%s)",
- datapath, strerror(error));
+ VLOG_ERR("failed to open datapath %s: %s",
+ datapath_name, strerror(error));
+ ofproto_destroy__(ofproto);
return error;
}
+ assert(ofproto->n_tables > 0);
- /* Initialize settings. */
- p = xzalloc(sizeof *p);
- p->fallback_dpid = pick_fallback_dpid();
- p->datapath_id = p->fallback_dpid;
- p->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
- p->hw_desc = xstrdup(DEFAULT_HW_DESC);
- p->sw_desc = xstrdup(DEFAULT_SW_DESC);
- p->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
- p->dp_desc = xstrdup(DEFAULT_DP_DESC);
-
- /* Initialize datapath. */
- p->dpif = dpif;
- p->netdev_monitor = netdev_monitor_create();
- hmap_init(&p->ports);
- shash_init(&p->port_by_name);
- p->max_ports = dpif_get_max_ports(dpif);
-
- /* Initialize submodules. */
- p->netflow = NULL;
- p->sflow = NULL;
-
- /* Initialize flow table. */
- classifier_init(&p->cls);
- timer_set_duration(&p->next_expiration, 1000);
-
- /* Initialize facet table. */
- hmap_init(&p->facets);
- p->need_revalidate = false;
- tag_set_init(&p->revalidate_set);
-
- /* Initialize hooks. */
- if (ofhooks) {
- p->ofhooks = ofhooks;
- p->aux = aux;
- p->ml = NULL;
- } else {
- p->ofhooks = &default_ofhooks;
- p->aux = p;
- p->ml = mac_learning_create();
- }
-
- /* Pick final datapath ID. */
- p->datapath_id = pick_datapath_id(p);
- VLOG_INFO("using datapath ID %016"PRIx64, p->datapath_id);
-
- shash_add_once(&all_ofprotos, dpif_name(p->dpif), p);
+ ofproto->datapath_id = pick_datapath_id(ofproto);
+ VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
+ init_ports(ofproto);
- /* Initialize OpenFlow connections. */
- p->connmgr = connmgr_create(p, datapath, local_name);
-
- *ofprotop = p;
+ *ofprotop = ofproto;
return 0;
}
ofproto_set_netflow(struct ofproto *ofproto,
const struct netflow_options *nf_options)
{
- if (nf_options && !sset_is_empty(&nf_options->collectors)) {
- if (!ofproto->netflow) {
- ofproto->netflow = netflow_create();
- }
- return netflow_set_options(ofproto->netflow, nf_options);
+ if (nf_options && sset_is_empty(&nf_options->collectors)) {
+ nf_options = NULL;
+ }
+
+ if (ofproto->ofproto_class->set_netflow) {
+ return ofproto->ofproto_class->set_netflow(ofproto, nf_options);
} else {
- netflow_destroy(ofproto->netflow);
- ofproto->netflow = NULL;
- return 0;
+ return nf_options ? EOPNOTSUPP : 0;
}
}
-void
+int
ofproto_set_sflow(struct ofproto *ofproto,
const struct ofproto_sflow_options *oso)
{
- struct ofproto_sflow *os = ofproto->sflow;
- if (oso) {
- if (!os) {
- struct ofport *ofport;
+ if (oso && sset_is_empty(&oso->targets)) {
+ oso = NULL;
+ }
- os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif);
- HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) {
- ofproto_sflow_add_port(os, ofport->odp_port,
- netdev_get_name(ofport->netdev));
- }
- }
- ofproto_sflow_set_options(os, oso);
+ if (ofproto->ofproto_class->set_sflow) {
+ return ofproto->ofproto_class->set_sflow(ofproto, oso);
} else {
- ofproto_sflow_destroy(os);
- ofproto->sflow = NULL;
+ return oso ? EOPNOTSUPP : 0;
}
}
\f
/* Connectivity Fault Management configuration. */
-/* Clears the CFM configuration from 'port_no' on 'ofproto'. */
+/* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */
void
-ofproto_iface_clear_cfm(struct ofproto *ofproto, uint32_t port_no)
+ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port)
{
- struct ofport *ofport = get_port(ofproto, port_no);
- if (ofport && ofport->cfm){
- cfm_destroy(ofport->cfm);
- ofport->cfm = NULL;
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ if (ofport && ofproto->ofproto_class->set_cfm) {
+ ofproto->ofproto_class->set_cfm(ofport, NULL, NULL, 0);
}
}
-/* Configures connectivity fault management on 'port_no' in 'ofproto'. Takes
+/* Configures connectivity fault management on 'ofp_port' in 'ofproto'. Takes
* basic configuration from the configuration members in 'cfm', and the set of
* remote maintenance points from the 'n_remote_mps' elements in 'remote_mps'.
* Ignores the statistics members of 'cfm'.
*
- * This function has no effect if 'ofproto' does not have a port 'port_no'. */
+ * This function has no effect if 'ofproto' does not have a port 'ofp_port'. */
void
-ofproto_iface_set_cfm(struct ofproto *ofproto, uint32_t port_no,
- const struct cfm *cfm,
- const uint16_t *remote_mps, size_t n_remote_mps)
+ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
+ const struct cfm *cfm,
+ const uint16_t *remote_mps, size_t n_remote_mps)
{
struct ofport *ofport;
+ int error;
- ofport = get_port(ofproto, port_no);
+ ofport = ofproto_get_port(ofproto, ofp_port);
if (!ofport) {
- VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu32,
- dpif_name(ofproto->dpif), port_no);
+ VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu16,
+ ofproto->name, ofp_port);
return;
}
- if (!ofport->cfm) {
- ofport->cfm = cfm_create();
+ error = (ofproto->ofproto_class->set_cfm
+ ? ofproto->ofproto_class->set_cfm(ofport, cfm,
+ remote_mps, n_remote_mps)
+ : EOPNOTSUPP);
+ if (error) {
+ VLOG_WARN("%s: CFM configuration on port %"PRIu16" (%s) failed (%s)",
+ ofproto->name, ofp_port, netdev_get_name(ofport->netdev),
+ strerror(error));
}
+}
- ofport->cfm->mpid = cfm->mpid;
- ofport->cfm->interval = cfm->interval;
- memcpy(ofport->cfm->maid, cfm->maid, CCM_MAID_LEN);
+/* Returns the connectivity fault management object associated with 'ofp_port'
+ * within 'ofproto', or a null pointer if 'ofproto' does not have a port
+ * 'ofp_port' or if that port does not have CFM configured. The caller must
+ * not modify or destroy the returned object. */
+const struct cfm *
+ofproto_port_get_cfm(struct ofproto *ofproto, uint16_t ofp_port)
+{
+ struct ofport *ofport;
+ const struct cfm *cfm;
- cfm_update_remote_mps(ofport->cfm, remote_mps, n_remote_mps);
+ ofport = ofproto_get_port(ofproto, ofp_port);
+ return (ofport
+ && ofproto->ofproto_class->get_cfm
+ && !ofproto->ofproto_class->get_cfm(ofport, &cfm)) ? cfm : NULL;
+}
- if (!cfm_configure(ofport->cfm)) {
- VLOG_WARN("%s: CFM configuration on port %"PRIu32" (%s) failed",
- dpif_name(ofproto->dpif), port_no,
- netdev_get_name(ofport->netdev));
- cfm_destroy(ofport->cfm);
- ofport->cfm = NULL;
- }
+/* Checks the status of LACP negotiation for 'ofp_port' within ofproto.
+ * Returns 1 if LACP partner information for 'ofp_port' is up-to-date,
+ * 0 if LACP partner information is not current (generally indicating a
+ * connectivity problem), or -1 if LACP is not enabled on 'ofp_port'. */
+int
+ofproto_port_is_lacp_current(struct ofproto *ofproto, uint16_t ofp_port)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ return (ofport && ofproto->ofproto_class->port_is_lacp_current
+ ? ofproto->ofproto_class->port_is_lacp_current(ofport)
+ : -1);
}
+\f
+/* Bundles. */
-/* Returns the connectivity fault management object associated with 'port_no'
- * within 'ofproto', or a null pointer if 'ofproto' does not have a port
- * 'port_no' or if that port does not have CFM configured. The caller must not
- * modify or destroy the returned object. */
-const struct cfm *
-ofproto_iface_get_cfm(struct ofproto *ofproto, uint32_t port_no)
+/* Registers a "bundle" associated with client data pointer 'aux' in 'ofproto'.
+ * A bundle is the same concept as a Port in OVSDB, that is, it consists of one
+ * or more "slave" devices (Interfaces, in OVSDB) along with a VLAN
+ * configuration plus, if there is more than one slave, a bonding
+ * configuration.
+ *
+ * If 'aux' is already registered then this function updates its configuration
+ * to 's'. Otherwise, this function registers a new bundle.
+ *
+ * Bundles only affect the NXAST_AUTOPATH action and output to the OFPP_NORMAL
+ * port. */
+int
+ofproto_bundle_register(struct ofproto *ofproto, void *aux,
+ const struct ofproto_bundle_settings *s)
+{
+ return (ofproto->ofproto_class->bundle_set
+ ? ofproto->ofproto_class->bundle_set(ofproto, aux, s)
+ : EOPNOTSUPP);
+}
+
+/* Unregisters the bundle registered on 'ofproto' with auxiliary data 'aux'.
+ * If no such bundle has been registered, this has no effect. */
+int
+ofproto_bundle_unregister(struct ofproto *ofproto, void *aux)
{
- struct ofport *ofport = get_port(ofproto, port_no);
- return ofport ? ofport->cfm : NULL;
+ return ofproto_bundle_register(ofproto, aux, NULL);
}
+
\f
-uint64_t
-ofproto_get_datapath_id(const struct ofproto *ofproto)
+/* Registers a mirror associated with client data pointer 'aux' in 'ofproto'.
+ * If 'aux' is already registered then this function updates its configuration
+ * to 's'. Otherwise, this function registers a new mirror.
+ *
+ * Mirrors affect only the treatment of packets output to the OFPP_NORMAL
+ * port. */
+int
+ofproto_mirror_register(struct ofproto *ofproto, void *aux,
+ const struct ofproto_mirror_settings *s)
{
- return ofproto->datapath_id;
+ return (ofproto->ofproto_class->mirror_set
+ ? ofproto->ofproto_class->mirror_set(ofproto, aux, s)
+ : EOPNOTSUPP);
}
-bool
-ofproto_has_primary_controller(const struct ofproto *ofproto)
+/* Unregisters the mirror registered on 'ofproto' with auxiliary data 'aux'.
+ * If no mirror has been registered, this has no effect. */
+int
+ofproto_mirror_unregister(struct ofproto *ofproto, void *aux)
{
- return connmgr_has_controllers(ofproto->connmgr);
+ return ofproto_mirror_register(ofproto, aux, NULL);
}
-enum ofproto_fail_mode
-ofproto_get_fail_mode(const struct ofproto *p)
+/* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs on
+ * which all packets are flooded, instead of using MAC learning. If
+ * 'flood_vlans' is NULL, then MAC learning applies to all VLANs.
+ *
+ * Flood VLANs affect only the treatment of packets output to the OFPP_NORMAL
+ * port. */
+int
+ofproto_set_flood_vlans(struct ofproto *ofproto, unsigned long *flood_vlans)
{
- return connmgr_get_fail_mode(p->connmgr);
+ return (ofproto->ofproto_class->set_flood_vlans
+ ? ofproto->ofproto_class->set_flood_vlans(ofproto, flood_vlans)
+ : EOPNOTSUPP);
}
+/* Returns true if 'aux' is a registered bundle that is currently in use as the
+ * output for a mirror. */
+bool
+ofproto_is_mirror_output_bundle(struct ofproto *ofproto, void *aux)
+{
+ return (ofproto->ofproto_class->is_mirror_output_bundle
+ ? ofproto->ofproto_class->is_mirror_output_bundle(ofproto, aux)
+ : false);
+}
+\f
bool
ofproto_has_snoops(const struct ofproto *ofproto)
{
connmgr_get_snoops(ofproto->connmgr, snoops);
}
+static void
+ofproto_destroy__(struct ofproto *ofproto)
+{
+ size_t i;
+
+ connmgr_destroy(ofproto->connmgr);
+
+ hmap_remove(&all_ofprotos, &ofproto->hmap_node);
+ free(ofproto->name);
+ free(ofproto->mfr_desc);
+ free(ofproto->hw_desc);
+ free(ofproto->sw_desc);
+ free(ofproto->serial_desc);
+ free(ofproto->dp_desc);
+ netdev_monitor_destroy(ofproto->netdev_monitor);
+ hmap_destroy(&ofproto->ports);
+ shash_destroy(&ofproto->port_by_name);
+
+ for (i = 0; i < ofproto->n_tables; i++) {
+ classifier_destroy(&ofproto->tables[i]);
+ }
+ free(ofproto->tables);
+
+ ofproto->ofproto_class->dealloc(ofproto);
+}
+
void
ofproto_destroy(struct ofproto *p)
{
return;
}
- shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif));
-
ofproto_flush_flows__(p);
- connmgr_destroy(p->connmgr);
- classifier_destroy(&p->cls);
- hmap_destroy(&p->facets);
-
- dpif_close(p->dpif);
- netdev_monitor_destroy(p->netdev_monitor);
HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) {
- hmap_remove(&p->ports, &ofport->hmap_node);
- ofport_free(ofport);
+ ofport_destroy(ofport);
}
- shash_destroy(&p->port_by_name);
-
- netflow_destroy(p->netflow);
- ofproto_sflow_destroy(p->sflow);
- mac_learning_destroy(p->ml);
-
- free(p->mfr_desc);
- free(p->hw_desc);
- free(p->sw_desc);
- free(p->serial_desc);
- free(p->dp_desc);
-
- hmap_destroy(&p->ports);
-
- free(p);
+ p->ofproto_class->destruct(p);
+ ofproto_destroy__(p);
}
+/* Destroys the datapath with the respective 'name' and 'type'. With the Linux
+ * kernel datapath, for example, this destroys the datapath in the kernel, and
+ * with the netdev-based datapath, it tears down the data structures that
+ * represent the datapath.
+ *
+ * The datapath should not be currently open as an ofproto. */
int
-ofproto_run(struct ofproto *p)
+ofproto_delete(const char *name, const char *type)
{
- int error = ofproto_run1(p);
- if (!error) {
- error = ofproto_run2(p, false);
- }
- return error;
+ const struct ofproto_class *class = ofproto_class_find__(type);
+ return (!class ? EAFNOSUPPORT
+ : !class->del ? EACCES
+ : class->del(type, name));
}
static void
}
int
-ofproto_run1(struct ofproto *p)
+ofproto_run(struct ofproto *p)
{
- struct ofport *ofport;
char *devname;
int error;
- int i;
- if (shash_is_empty(&p->port_by_name)) {
- init_ports(p);
+ error = p->ofproto_class->run(p);
+ if (error == ENODEV) {
+ /* Someone destroyed the datapath behind our back. The caller
+ * better destroy us and give up, because we're just going to
+ * spin from here on out. */
+ static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
+ p->name);
+ return ENODEV;
}
- for (i = 0; i < 50; i++) {
- struct dpif_upcall packet;
-
- error = dpif_recv(p->dpif, &packet);
- if (error) {
- if (error == ENODEV) {
- /* Someone destroyed the datapath behind our back. The caller
- * better destroy us and give up, because we're just going to
- * spin from here on out. */
- static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
- dpif_name(p->dpif));
- return ENODEV;
- }
- break;
+ if (p->ofproto_class->port_poll) {
+ while ((error = p->ofproto_class->port_poll(p, &devname)) != EAGAIN) {
+ process_port_change(p, error, devname);
}
-
- handle_upcall(p, &packet);
- }
-
- while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
- process_port_change(p, error, devname);
}
while ((error = netdev_monitor_poll(p->netdev_monitor,
&devname)) != EAGAIN) {
process_port_change(p, error, devname);
}
- HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
- ofport_run(p, ofport);
- }
-
connmgr_run(p->connmgr, handle_openflow);
- if (timer_expired(&p->next_expiration)) {
- int delay = ofproto_expire(p);
- timer_set_duration(&p->next_expiration, delay);
- COVERAGE_INC(ofproto_expiration);
- }
-
- if (p->netflow) {
- netflow_run(p->netflow);
- }
- if (p->sflow) {
- ofproto_sflow_run(p->sflow);
- }
-
- return 0;
-}
-
-int
-ofproto_run2(struct ofproto *p, bool revalidate_all)
-{
- /* Figure out what we need to revalidate now, if anything. */
- struct tag_set revalidate_set = p->revalidate_set;
- if (p->need_revalidate) {
- revalidate_all = true;
- }
-
- /* Clear the revalidation flags. */
- tag_set_init(&p->revalidate_set);
- p->need_revalidate = false;
-
- /* Now revalidate if there's anything to do. */
- if (revalidate_all || !tag_set_is_empty(&revalidate_set)) {
- struct facet *facet, *next;
-
- HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &p->facets) {
- if (revalidate_all
- || tag_set_intersects(&revalidate_set, facet->tags)) {
- facet_revalidate(p, facet);
- }
- }
- }
-
return 0;
}
void
ofproto_wait(struct ofproto *p)
{
- struct ofport *ofport;
-
- HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
- ofport_wait(ofport);
+ p->ofproto_class->wait(p);
+ if (p->ofproto_class->port_poll_wait) {
+ p->ofproto_class->port_poll_wait(p);
}
- dpif_recv_wait(p->dpif);
- dpif_port_poll_wait(p->dpif);
netdev_monitor_poll_wait(p->netdev_monitor);
- if (p->sflow) {
- ofproto_sflow_wait(p->sflow);
- }
- if (!tag_set_is_empty(&p->revalidate_set)) {
- poll_immediate_wake();
- }
- if (p->need_revalidate) {
- /* Shouldn't happen, but if it does just go around again. */
- VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
- poll_immediate_wake();
- } else {
- timer_wait(&p->next_expiration);
- }
connmgr_wait(p->connmgr);
}
-void
-ofproto_revalidate(struct ofproto *ofproto, tag_type tag)
-{
- tag_set_add(&ofproto->revalidate_set, tag);
-}
-
-struct tag_set *
-ofproto_get_revalidate_set(struct ofproto *ofproto)
-{
- return &ofproto->revalidate_set;
-}
-
bool
ofproto_is_alive(const struct ofproto *p)
{
shash_destroy(info);
}
-/* Deletes port number 'odp_port' from the datapath for 'ofproto'.
+/* Makes a deep copy of 'old' into 'port'. */
+void
+ofproto_port_clone(struct ofproto_port *port, const struct ofproto_port *old)
+{
+ port->name = xstrdup(old->name);
+ port->type = xstrdup(old->type);
+ port->ofp_port = old->ofp_port;
+}
+
+/* Frees memory allocated to members of 'ofproto_port'.
*
- * This is almost the same as calling dpif_port_del() directly on the
- * datapath, but it also makes 'ofproto' close its open netdev for the port
- * (if any). This makes it possible to create a new netdev of a different
- * type under the same name, which otherwise the netdev library would refuse
- * to do because of the conflict. (The netdev would eventually get closed on
- * the next trip through ofproto_run(), but this interface is more direct.)
+ * Do not call this function on an ofproto_port obtained from
+ * ofproto_port_dump_next(): that function retains ownership of the data in the
+ * ofproto_port. */
+void
+ofproto_port_destroy(struct ofproto_port *ofproto_port)
+{
+ free(ofproto_port->name);
+ free(ofproto_port->type);
+}
+
+/* Initializes 'dump' to begin dumping the ports in an ofproto.
*
- * Returns 0 if successful, otherwise a positive errno. */
+ * This function provides no status indication. An error status for the entire
+ * dump operation is provided when it is completed by calling
+ * ofproto_port_dump_done().
+ */
+void
+ofproto_port_dump_start(struct ofproto_port_dump *dump,
+ const struct ofproto *ofproto)
+{
+ dump->ofproto = ofproto;
+ dump->error = ofproto->ofproto_class->port_dump_start(ofproto,
+ &dump->state);
+}
+
+/* Attempts to retrieve another port from 'dump', which must have been created
+ * with ofproto_port_dump_start(). On success, stores a new ofproto_port into
+ * 'port' and returns true. On failure, returns false.
+ *
+ * Failure might indicate an actual error or merely that the last port has been
+ * dumped. An error status for the entire dump operation is provided when it
+ * is completed by calling ofproto_port_dump_done().
+ *
+ * The ofproto owns the data stored in 'port'. It will remain valid until at
+ * least the next time 'dump' is passed to ofproto_port_dump_next() or
+ * ofproto_port_dump_done(). */
+bool
+ofproto_port_dump_next(struct ofproto_port_dump *dump,
+ struct ofproto_port *port)
+{
+ const struct ofproto *ofproto = dump->ofproto;
+
+ if (dump->error) {
+ return false;
+ }
+
+ dump->error = ofproto->ofproto_class->port_dump_next(ofproto, dump->state,
+ port);
+ if (dump->error) {
+ ofproto->ofproto_class->port_dump_done(ofproto, dump->state);
+ return false;
+ }
+ return true;
+}
+
+/* Completes port table dump operation 'dump', which must have been created
+ * with ofproto_port_dump_start(). Returns 0 if the dump operation was
+ * error-free, otherwise a positive errno value describing the problem. */
int
-ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port)
+ofproto_port_dump_done(struct ofproto_port_dump *dump)
{
- struct ofport *ofport = get_port(ofproto, odp_port);
- const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
+ const struct ofproto *ofproto = dump->ofproto;
+ if (!dump->error) {
+ dump->error = ofproto->ofproto_class->port_dump_done(ofproto,
+ dump->state);
+ }
+ return dump->error == EOF ? 0 : dump->error;
+}
+
+/* Attempts to add 'netdev' as a port on 'ofproto'. If successful, returns 0
+ * and sets '*ofp_portp' to the new port's OpenFlow port number (if 'ofp_portp'
+ * is non-null). On failure, returns a positive errno value and sets
+ * '*ofp_portp' to OFPP_NONE (if 'ofp_portp' is non-null). */
+int
+ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
+ uint16_t *ofp_portp)
+{
+ uint16_t ofp_port;
int error;
- error = dpif_port_del(ofproto->dpif, odp_port);
- if (error) {
- VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)",
- dpif_name(ofproto->dpif), odp_port, name, strerror(error));
- } else if (ofport) {
- /* 'name' is the netdev's name and update_port() is going to close the
- * netdev. Just in case update_port() refers to 'name' after it
- * destroys 'ofport', make a copy of it around the update_port()
- * call. */
- char *devname = xstrdup(name);
- update_port(ofproto, devname);
- free(devname);
+ error = ofproto->ofproto_class->port_add(ofproto, netdev, &ofp_port);
+ if (!error) {
+ update_port(ofproto, netdev_get_name(netdev));
+ }
+ if (ofp_portp) {
+ *ofp_portp = error ? OFPP_NONE : ofp_port;
}
return error;
}
-/* Checks if 'ofproto' thinks 'odp_port' should be included in floods. Returns
- * true if 'odp_port' exists and should be included, false otherwise. */
-bool
-ofproto_port_is_floodable(struct ofproto *ofproto, uint16_t odp_port)
+/* Looks up a port named 'devname' in 'ofproto'. On success, returns 0 and
+ * initializes '*port' appropriately; on failure, returns a positive errno
+ * value.
+ *
+ * The caller owns the data in 'ofproto_port' and must free it with
+ * ofproto_port_destroy() when it is no longer needed. */
+int
+ofproto_port_query_by_name(const struct ofproto *ofproto, const char *devname,
+ struct ofproto_port *port)
{
- struct ofport *ofport = get_port(ofproto, odp_port);
- return ofport && !(ofport->opp.config & htonl(OFPPC_NO_FLOOD));
+ int error;
+
+ error = ofproto->ofproto_class->port_query_by_name(ofproto, devname, port);
+ if (error) {
+ memset(port, 0, sizeof *port);
+ }
+ return error;
}
-/* Sends 'packet' out of port 'port_no' within 'p'.
- *
- * Returns 0 if successful, otherwise a positive errno value. */
+/* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
+ * Returns 0 if successful, otherwise a positive errno. */
int
-ofproto_send_packet(struct ofproto *ofproto,
- uint32_t port_no, const struct ofpbuf *packet)
+ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
{
- struct ofpbuf odp_actions;
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
int error;
- ofpbuf_init(&odp_actions, 32);
- nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, port_no);
- error = dpif_execute(ofproto->dpif, odp_actions.data, odp_actions.size,
- packet);
- ofpbuf_uninit(&odp_actions);
-
- if (error) {
- VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
- dpif_name(ofproto->dpif), port_no, strerror(error));
+ error = ofproto->ofproto_class->port_del(ofproto, ofp_port);
+ if (!error && ofport) {
+ /* 'name' is the netdev's name and update_port() is going to close the
+ * netdev. Just in case update_port() refers to 'name' after it
+ * destroys 'ofport', make a copy of it around the update_port()
+ * call. */
+ char *devname = xstrdup(name);
+ update_port(ofproto, devname);
+ free(devname);
}
return error;
}
-/* Adds a flow to the OpenFlow flow table in 'p' that matches 'cls_rule' and
+/* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and
* performs the 'n_actions' actions in 'actions'. The new flow will not
* timeout.
*
* (0...65535, inclusive) then the flow will be visible to OpenFlow
* controllers; otherwise, it will be hidden.
*
- * The caller retains ownership of 'cls_rule' and 'actions'. */
+ * The caller retains ownership of 'cls_rule' and 'actions'.
+ *
+ * This is a helper function for in-band control and fail-open. */
void
ofproto_add_flow(struct ofproto *p, const struct cls_rule *cls_rule,
const union ofp_action *actions, size_t n_actions)
{
struct rule *rule;
- rule = rule_create(cls_rule, actions, n_actions, 0, 0, 0, false);
- rule_insert(p, rule);
+ rule_create(p, cls_rule, actions, n_actions, 0, 0, 0, false, &rule);
}
+/* Searches for a rule with matching criteria exactly equal to 'target' in
+ * ofproto's table 0 and, if it finds one, deletes it.
+ *
+ * This is a helper function for in-band control and fail-open. */
void
ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
{
struct rule *rule;
- rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls,
- target));
- if (rule) {
- rule_remove(ofproto, rule);
- }
+ rule = rule_from_cls_rule(classifier_find_rule_exactly(
+ &ofproto->tables[0], target));
+ ofproto_rule_destroy(rule);
}
static void
ofproto_flush_flows__(struct ofproto *ofproto)
{
- struct facet *facet, *next_facet;
- struct rule *rule, *next_rule;
- struct cls_cursor cursor;
+ size_t i;
COVERAGE_INC(ofproto_flush);
- HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
- /* Mark the facet as not installed so that facet_remove() doesn't
- * bother trying to uninstall it. There is no point in uninstalling it
- * individually since we are about to blow away all the facets with
- * dpif_flow_flush(). */
- facet->installed = false;
- facet->dp_packet_count = 0;
- facet->dp_byte_count = 0;
- facet_remove(ofproto, facet);
+ if (ofproto->ofproto_class->flush) {
+ ofproto->ofproto_class->flush(ofproto);
}
- cls_cursor_init(&cursor, &ofproto->cls, NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
- rule_remove(ofproto, rule);
- }
+ for (i = 0; i < ofproto->n_tables; i++) {
+ struct rule *rule, *next_rule;
+ struct cls_cursor cursor;
- dpif_flow_flush(ofproto->dpif);
+ cls_cursor_init(&cursor, &ofproto->tables[i], NULL);
+ CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+ ofproto_rule_destroy(rule);
+ }
+ }
}
+/* Deletes all of the flows from all of ofproto's flow tables, then
+ * reintroduces rules required by in-band control and fail open. */
void
ofproto_flush_flows(struct ofproto *ofproto)
{
static void
reinit_ports(struct ofproto *p)
{
- struct dpif_port_dump dump;
+ struct ofproto_port_dump dump;
struct sset devnames;
struct ofport *ofport;
- struct dpif_port dpif_port;
+ struct ofproto_port ofproto_port;
const char *devname;
COVERAGE_INC(ofproto_reinit_ports);
HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
sset_add(&devnames, netdev_get_name(ofport->netdev));
}
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
- sset_add(&devnames, dpif_port.name);
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
+ sset_add(&devnames, ofproto_port.name);
}
SSET_FOR_EACH (devname, &devnames) {
sset_destroy(&devnames);
}
-/* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev
- * cannot be opened. On success, also fills in 'opp'. */
+/* Opens and returns a netdev for 'ofproto_port', or a null pointer if the
+ * netdev cannot be opened. On success, also fills in 'opp'. */
static struct netdev *
-ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp)
+ofport_open(const struct ofproto_port *ofproto_port, struct ofp_phy_port *opp)
{
uint32_t curr, advertised, supported, peer;
struct netdev_options netdev_options;
int error;
memset(&netdev_options, 0, sizeof netdev_options);
- netdev_options.name = dpif_port->name;
- netdev_options.type = dpif_port->type;
+ netdev_options.name = ofproto_port->name;
+ netdev_options.type = ofproto_port->type;
netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
error = netdev_open(&netdev_options, &netdev);
if (error) {
VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
"cannot be opened (%s)",
- dpif_port->name, dpif_port->port_no,
- dpif_port->name, strerror(error));
+ ofproto_port->name, ofproto_port->ofp_port,
+ ofproto_port->name, strerror(error));
return NULL;
}
netdev_get_flags(netdev, &flags);
netdev_get_features(netdev, &curr, &advertised, &supported, &peer);
- opp->port_no = htons(odp_port_to_ofp_port(dpif_port->port_no));
+ opp->port_no = htons(ofproto_port->ofp_port);
netdev_get_etheraddr(netdev, opp->hw_addr);
- ovs_strzcpy(opp->name, dpif_port->name, sizeof opp->name);
+ ovs_strzcpy(opp->name, ofproto_port->name, sizeof opp->name);
opp->config = flags & NETDEV_UP ? 0 : htonl(OFPPC_PORT_DOWN);
opp->state = netdev_get_carrier(netdev) ? 0 : htonl(OFPPS_LINK_DOWN);
opp->curr = htonl(curr);
return netdev;
}
-static bool
-ofport_conflicts(const struct ofproto *p, const struct dpif_port *dpif_port)
-{
- if (get_port(p, dpif_port->port_no)) {
- VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
- dpif_port->port_no);
- return true;
- } else if (shash_find(&p->port_by_name, dpif_port->name)) {
- VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
- dpif_port->name);
- return true;
- } else {
- return false;
- }
-}
-
/* Returns true if most fields of 'a' and 'b' are equal. Differences in name,
* port number, and 'config' bits other than OFPPC_PORT_DOWN are
* disregarded. */
{
const char *netdev_name = netdev_get_name(netdev);
struct ofport *ofport;
-
- connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD);
+ int error;
/* Create ofport. */
- ofport = xmalloc(sizeof *ofport);
+ ofport = p->ofproto_class->port_alloc();
+ if (!ofport) {
+ error = ENOMEM;
+ goto error;
+ }
+ ofport->ofproto = p;
ofport->netdev = netdev;
ofport->opp = *opp;
- ofport->odp_port = ofp_port_to_odp_port(ntohs(opp->port_no));
- ofport->cfm = NULL;
+ ofport->ofp_port = ntohs(opp->port_no);
/* Add port to 'p'. */
netdev_monitor_add(p->netdev_monitor, ofport->netdev);
- hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->odp_port, 0));
+ hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
shash_add(&p->port_by_name, netdev_name, ofport);
- if (p->sflow) {
- ofproto_sflow_add_port(p->sflow, ofport->odp_port, netdev_name);
+
+ /* Let the ofproto_class initialize its private data. */
+ error = p->ofproto_class->port_construct(ofport);
+ if (error) {
+ goto error;
+ }
+ connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD);
+ return;
+
+error:
+ VLOG_WARN_RL(&rl, "%s: could not add port %s (%s)",
+ p->name, netdev_name, strerror(error));
+ if (ofport) {
+ ofport_destroy__(ofport);
+ } else {
+ netdev_close(netdev);
}
}
/* Removes 'ofport' from 'p' and destroys it. */
static void
-ofport_remove(struct ofproto *p, struct ofport *ofport)
+ofport_remove(struct ofport *ofport)
{
- connmgr_send_port_status(p->connmgr, &ofport->opp, OFPPR_DELETE);
-
- netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
- hmap_remove(&p->ports, &ofport->hmap_node);
- shash_delete(&p->port_by_name,
- shash_find(&p->port_by_name,
- netdev_get_name(ofport->netdev)));
- if (p->sflow) {
- ofproto_sflow_del_port(p->sflow, ofport->odp_port);
- }
-
- ofport_free(ofport);
+ connmgr_send_port_status(ofport->ofproto->connmgr, &ofport->opp,
+ OFPPR_DELETE);
+ ofport_destroy(ofport);
}
/* If 'ofproto' contains an ofport named 'name', removes it from 'ofproto' and
{
struct ofport *port = shash_find_data(&ofproto->port_by_name, name);
if (port) {
- ofport_remove(ofproto, port);
+ ofport_remove(port);
}
}
* Does not handle a name or port number change. The caller must implement
* such a change as a delete followed by an add. */
static void
-ofport_modified(struct ofproto *ofproto, struct ofport *port,
- struct netdev *netdev, struct ofp_phy_port *opp)
+ofport_modified(struct ofport *port, struct ofp_phy_port *opp)
{
memcpy(port->opp.hw_addr, opp->hw_addr, ETH_ADDR_LEN);
port->opp.config = ((port->opp.config & ~htonl(OFPPC_PORT_DOWN))
port->opp.supported = opp->supported;
port->opp.peer = opp->peer;
- netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
- netdev_monitor_add(ofproto->netdev_monitor, netdev);
-
- netdev_close(port->netdev);
- port->netdev = netdev;
-
- connmgr_send_port_status(ofproto->connmgr, &port->opp, OFPPR_MODIFY);
+ connmgr_send_port_status(port->ofproto->connmgr, &port->opp, OFPPR_MODIFY);
}
-static void
-ofport_run(struct ofproto *ofproto, struct ofport *ofport)
+void
+ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port)
{
- if (ofport->cfm) {
- cfm_run(ofport->cfm);
-
- if (cfm_should_send_ccm(ofport->cfm)) {
- struct ofpbuf packet;
- struct ccm *ccm;
-
- ofpbuf_init(&packet, 0);
- ccm = eth_compose(&packet, eth_addr_ccm, ofport->opp.hw_addr,
- ETH_TYPE_CFM, sizeof *ccm);
- cfm_compose_ccm(ofport->cfm, ccm);
- ofproto_send_packet(ofproto, ofport->odp_port, &packet);
- ofpbuf_uninit(&packet);
+ struct ofport *port = ofproto_get_port(ofproto, ofp_port);
+ if (port) {
+ if (port->ofproto->ofproto_class->set_cfm) {
+ port->ofproto->ofproto_class->set_cfm(port, NULL, NULL, 0);
+ }
+ if (port->ofproto->ofproto_class->bundle_remove) {
+ port->ofproto->ofproto_class->bundle_remove(port);
}
}
}
static void
-ofport_wait(struct ofport *ofport)
+ofport_destroy__(struct ofport *port)
{
- if (ofport->cfm) {
- cfm_wait(ofport->cfm);
- }
+ struct ofproto *ofproto = port->ofproto;
+ const char *name = netdev_get_name(port->netdev);
+
+ netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
+ hmap_remove(&ofproto->ports, &port->hmap_node);
+ shash_delete(&ofproto->port_by_name,
+ shash_find(&ofproto->port_by_name, name));
+
+ netdev_close(port->netdev);
+ ofproto->ofproto_class->port_dealloc(port);
}
static void
-ofport_free(struct ofport *ofport)
+ofport_destroy(struct ofport *port)
{
- if (ofport) {
- cfm_destroy(ofport->cfm);
- netdev_close(ofport->netdev);
- free(ofport);
- }
+ if (port) {
+ port->ofproto->ofproto_class->port_destruct(port);
+ ofport_destroy__(port);
+ }
}
-static struct ofport *
-get_port(const struct ofproto *ofproto, uint16_t odp_port)
+struct ofport *
+ofproto_get_port(const struct ofproto *ofproto, uint16_t ofp_port)
{
struct ofport *port;
HMAP_FOR_EACH_IN_BUCKET (port, hmap_node,
- hash_int(odp_port, 0), &ofproto->ports) {
- if (port->odp_port == odp_port) {
+ hash_int(ofp_port, 0), &ofproto->ports) {
+ if (port->ofp_port == ofp_port) {
return port;
}
}
static void
update_port(struct ofproto *ofproto, const char *name)
{
- struct dpif_port dpif_port;
+ struct ofproto_port ofproto_port;
struct ofp_phy_port opp;
struct netdev *netdev;
struct ofport *port;
COVERAGE_INC(ofproto_update_port);
/* Fetch 'name''s location and properties from the datapath. */
- netdev = (!dpif_port_query_by_name(ofproto->dpif, name, &dpif_port)
- ? ofport_open(&dpif_port, &opp)
+ netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
+ ? ofport_open(&ofproto_port, &opp)
: NULL);
if (netdev) {
- port = get_port(ofproto, dpif_port.port_no);
+ port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
if (port && !strcmp(netdev_get_name(port->netdev), name)) {
+ struct netdev *old_netdev = port->netdev;
+
/* 'name' hasn't changed location. Any properties changed? */
if (!ofport_equal(&port->opp, &opp)) {
- ofport_modified(ofproto, port, netdev, &opp);
- } else {
- netdev_close(netdev);
+ ofport_modified(port, &opp);
+ }
+
+ /* Install the newly opened netdev in case it has changed.
+ * Don't close the old netdev yet in case port_modified has to
+ * remove a retained reference to it.*/
+ netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
+ netdev_monitor_add(ofproto->netdev_monitor, netdev);
+ port->netdev = netdev;
+
+ if (port->ofproto->ofproto_class->port_modified) {
+ port->ofproto->ofproto_class->port_modified(port);
}
+
+ netdev_close(old_netdev);
} else {
/* If 'port' is nonnull then its name differs from 'name' and thus
* we should delete it. If we think there's a port named 'name'
* then its port number must be wrong now so delete it too. */
if (port) {
- ofport_remove(ofproto, port);
+ ofport_remove(port);
}
ofport_remove_with_name(ofproto, name);
ofport_install(ofproto, netdev, &opp);
/* Any port named 'name' is gone now. */
ofport_remove_with_name(ofproto, name);
}
- dpif_port_destroy(&dpif_port);
+ ofproto_port_destroy(&ofproto_port);
}
static int
init_ports(struct ofproto *p)
{
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
-
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
- if (!ofport_conflicts(p, &dpif_port)) {
+ struct ofproto_port_dump dump;
+ struct ofproto_port ofproto_port;
+
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
+ uint16_t ofp_port = ofproto_port.ofp_port;
+ if (ofproto_get_port(p, ofp_port)) {
+ VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
+ ofp_port);
+ } else if (shash_find(&p->port_by_name, ofproto_port.name)) {
+ VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
+ ofproto_port.name);
+ } else {
struct ofp_phy_port opp;
struct netdev *netdev;
- netdev = ofport_open(&dpif_port, &opp);
+ netdev = ofport_open(&ofproto_port, &opp);
if (netdev) {
ofport_install(p, netdev, &opp);
}
return 0;
}
\f
-/* Returns true if 'rule' should be hidden from the controller.
- *
- * Rules with priority higher than UINT16_MAX are set up by ofproto itself
- * (e.g. by in-band control) and are intentionally hidden from the
- * controller. */
-static bool
-rule_is_hidden(const struct rule *rule)
-{
- return rule->cr.priority > UINT16_MAX;
-}
-
-/* Creates and returns a new rule initialized as specified.
- *
- * The caller is responsible for inserting the rule into the classifier (with
- * rule_insert()). */
-static struct rule *
-rule_create(const struct cls_rule *cls_rule,
+/* Creates a new rule initialized as specified, inserts it into 'ofproto''s
+ * flow table, and stores the new rule into '*rulep'. Returns 0 on success,
+ * otherwise a positive errno value or OpenFlow error code. */
+static int
+rule_create(struct ofproto *ofproto, const struct cls_rule *cls_rule,
const union ofp_action *actions, size_t n_actions,
uint16_t idle_timeout, uint16_t hard_timeout,
- ovs_be64 flow_cookie, bool send_flow_removed)
+ ovs_be64 flow_cookie, bool send_flow_removed,
+ struct rule **rulep)
{
- struct rule *rule = xzalloc(sizeof *rule);
+ struct rule *rule;
+ int error;
+
+ rule = ofproto->ofproto_class->rule_alloc();
+ if (!rule) {
+ error = ENOMEM;
+ goto error;
+ }
+
+ rule->ofproto = ofproto;
rule->cr = *cls_rule;
+ rule->flow_cookie = flow_cookie;
+ rule->created = time_msec();
rule->idle_timeout = idle_timeout;
rule->hard_timeout = hard_timeout;
- rule->flow_cookie = flow_cookie;
- rule->used = rule->created = time_msec();
rule->send_flow_removed = send_flow_removed;
- list_init(&rule->facets);
if (n_actions > 0) {
- rule->n_actions = n_actions;
rule->actions = xmemdup(actions, n_actions * sizeof *actions);
+ } else {
+ rule->actions = NULL;
}
+ rule->n_actions = n_actions;
- return rule;
-}
+ error = ofproto->ofproto_class->rule_construct(rule);
+ if (error) {
+ ofproto_rule_destroy__(rule);
+ goto error;
+ }
-static struct rule *
-rule_from_cls_rule(const struct cls_rule *cls_rule)
-{
- return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
+ *rulep = rule;
+ return 0;
+
+error:
+ VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
+ ofproto->name, strerror(error));
+ *rulep = NULL;
+ return error;
}
static void
-rule_free(struct rule *rule)
+ofproto_rule_destroy__(struct rule *rule)
{
free(rule->actions);
- free(rule);
+ rule->ofproto->ofproto_class->rule_dealloc(rule);
}
-/* Destroys 'rule' and iterates through all of its facets and revalidates them,
- * destroying any that no longer has a rule (which is probably all of them).
- *
- * The caller must have already removed 'rule' from the classifier. */
-static void
-rule_destroy(struct ofproto *ofproto, struct rule *rule)
+/* Destroys 'rule' and removes it from the flow table and the datapath. */
+void
+ofproto_rule_destroy(struct rule *rule)
{
- struct facet *facet, *next_facet;
- LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
- facet_revalidate(ofproto, facet);
+ if (rule) {
+ rule->ofproto->ofproto_class->rule_destruct(rule);
+ ofproto_rule_destroy__(rule);
}
- rule_free(rule);
}
/* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
return false;
}
-/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'.
- *
- * Takes ownership of 'packet'. */
-static bool
-execute_odp_actions(struct ofproto *ofproto, const struct flow *flow,
- const struct nlattr *odp_actions, size_t actions_len,
- struct ofpbuf *packet)
-{
- if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
- && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) {
- /* As an optimization, avoid a round-trip from userspace to kernel to
- * userspace. This also avoids possibly filling up kernel packet
- * buffers along the way. */
- struct dpif_upcall upcall;
-
- upcall.type = DPIF_UC_ACTION;
- upcall.packet = packet;
- upcall.key = NULL;
- upcall.key_len = 0;
- upcall.userdata = nl_attr_get_u64(odp_actions);
- upcall.sample_pool = 0;
- upcall.actions = NULL;
- upcall.actions_len = 0;
-
- send_packet_in(ofproto, &upcall, flow, false);
-
- return true;
- } else {
- int error;
-
- error = dpif_execute(ofproto->dpif, odp_actions, actions_len, packet);
- ofpbuf_delete(packet);
- return !error;
- }
-}
-
-/* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s
+/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
* statistics appropriately. 'packet' must have at least sizeof(struct
* ofp_packet_in) bytes of headroom.
*
- * For correct results, 'packet' must actually be in 'facet''s flow; that is,
- * applying flow_extract() to 'packet' would yield the same flow as
- * 'facet->flow'.
- *
- * 'facet' must have accurately composed ODP actions; that is, it must not be
- * in need of revalidation.
- *
- * Takes ownership of 'packet'. */
-static void
-facet_execute(struct ofproto *ofproto, struct facet *facet,
- struct ofpbuf *packet)
-{
- struct dpif_flow_stats stats;
-
- assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
-
- flow_extract_stats(&facet->flow, packet, &stats);
- stats.used = time_msec();
- if (execute_odp_actions(ofproto, &facet->flow,
- facet->actions, facet->actions_len, packet)) {
- facet_update_stats(ofproto, facet, &stats);
- }
-}
-
-/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
- * statistics (or the statistics for one of its facets) appropriately.
- * 'packet' must have at least sizeof(struct ofp_packet_in) bytes of headroom.
- *
* 'packet' doesn't necessarily have to match 'rule'. 'rule' will be credited
* with statistics for 'packet' either way.
*
* Takes ownership of 'packet'. */
-static void
-rule_execute(struct ofproto *ofproto, struct rule *rule, uint16_t in_port,
- struct ofpbuf *packet)
-{
- struct action_xlate_ctx ctx;
- struct ofpbuf *odp_actions;
- struct facet *facet;
- struct flow flow;
- size_t size;
-
- assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
-
- flow_extract(packet, 0, in_port, &flow);
-
- /* First look for a related facet. If we find one, account it to that. */
- facet = facet_lookup_valid(ofproto, &flow);
- if (facet && facet->rule == rule) {
- facet_execute(ofproto, facet, packet);
- return;
- }
-
- /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
- * create a new facet for it and use that. */
- if (rule_lookup(ofproto, &flow) == rule) {
- facet = facet_create(ofproto, rule, &flow, packet);
- facet_execute(ofproto, facet, packet);
- facet_install(ofproto, facet, true);
- return;
- }
-
- /* We can't account anything to a facet. If we were to try, then that
- * facet would have a non-matching rule, busting our invariants. */
- action_xlate_ctx_init(&ctx, ofproto, &flow, packet);
- odp_actions = xlate_actions(&ctx, rule->actions, rule->n_actions);
- size = packet->size;
- if (execute_odp_actions(ofproto, &flow, odp_actions->data,
- odp_actions->size, packet)) {
- rule->used = time_msec();
- rule->packet_count++;
- rule->byte_count += size;
- flow_push_stats(ofproto, rule, &flow, 1, size, rule->used);
- }
- ofpbuf_delete(odp_actions);
-}
-
-/* Inserts 'rule' into 'p''s flow table. */
-static void
-rule_insert(struct ofproto *p, struct rule *rule)
-{
- struct rule *displaced_rule;
-
- displaced_rule = rule_from_cls_rule(classifier_insert(&p->cls, &rule->cr));
- if (displaced_rule) {
- rule_destroy(p, displaced_rule);
- }
- p->need_revalidate = true;
-}
-
-/* Creates and returns a new facet within 'ofproto' owned by 'rule', given a
- * 'flow' and an example 'packet' within that flow.
- *
- * The caller must already have determined that no facet with an identical
- * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in
- * 'ofproto''s classifier table. */
-static struct facet *
-facet_create(struct ofproto *ofproto, struct rule *rule,
- const struct flow *flow, const struct ofpbuf *packet)
-{
- struct facet *facet;
-
- facet = xzalloc(sizeof *facet);
- facet->used = time_msec();
- hmap_insert(&ofproto->facets, &facet->hmap_node, flow_hash(flow, 0));
- list_push_back(&rule->facets, &facet->list_node);
- facet->rule = rule;
- facet->flow = *flow;
- netflow_flow_init(&facet->nf_flow);
- netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
-
- facet_make_actions(ofproto, facet, packet);
-
- return facet;
-}
-
-static void
-facet_free(struct facet *facet)
-{
- free(facet->actions);
- free(facet);
-}
-
-/* Remove 'rule' from 'ofproto' and free up the associated memory:
- *
- * - Removes 'rule' from the classifier.
- *
- * - If 'rule' has facets, revalidates them (and possibly uninstalls and
- * destroys them), via rule_destroy().
- */
-static void
-rule_remove(struct ofproto *ofproto, struct rule *rule)
-{
- COVERAGE_INC(ofproto_del_rule);
- ofproto->need_revalidate = true;
- classifier_remove(&ofproto->cls, &rule->cr);
- rule_destroy(ofproto, rule);
-}
-
-/* Remove 'facet' from 'ofproto' and free up the associated memory:
- *
- * - If 'facet' was installed in the datapath, uninstalls it and updates its
- * rule's statistics, via facet_uninstall().
- *
- * - Removes 'facet' from its rule and from ofproto->facets.
- */
-static void
-facet_remove(struct ofproto *ofproto, struct facet *facet)
-{
- facet_uninstall(ofproto, facet);
- facet_flush_stats(ofproto, facet);
- hmap_remove(&ofproto->facets, &facet->hmap_node);
- list_remove(&facet->list_node);
- facet_free(facet);
-}
-
-/* Composes the ODP actions for 'facet' based on its rule's actions. */
-static void
-facet_make_actions(struct ofproto *p, struct facet *facet,
- const struct ofpbuf *packet)
-{
- const struct rule *rule = facet->rule;
- struct ofpbuf *odp_actions;
- struct action_xlate_ctx ctx;
-
- action_xlate_ctx_init(&ctx, p, &facet->flow, packet);
- odp_actions = xlate_actions(&ctx, rule->actions, rule->n_actions);
- facet->tags = ctx.tags;
- facet->may_install = ctx.may_set_up_flow;
- facet->nf_flow.output_iface = ctx.nf_output_iface;
-
- if (facet->actions_len != odp_actions->size
- || memcmp(facet->actions, odp_actions->data, odp_actions->size)) {
- free(facet->actions);
- facet->actions_len = odp_actions->size;
- facet->actions = xmemdup(odp_actions->data, odp_actions->size);
- }
-
- ofpbuf_delete(odp_actions);
-}
-
-/* Updates 'facet''s flow in the datapath setting its actions to 'actions_len'
- * bytes of actions in 'actions'. If 'stats' is non-null, statistics counters
- * in the datapath will be zeroed and 'stats' will be updated with traffic new
- * since 'facet' was last updated.
- *
- * Returns 0 if successful, otherwise a positive errno value.*/
-static int
-facet_put__(struct ofproto *ofproto, struct facet *facet,
- const struct nlattr *actions, size_t actions_len,
- struct dpif_flow_stats *stats)
-{
- struct odputil_keybuf keybuf;
- enum dpif_flow_put_flags flags;
- struct ofpbuf key;
- int ret;
-
- flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
- if (stats) {
- flags |= DPIF_FP_ZERO_STATS;
- }
-
- ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
- odp_flow_key_from_flow(&key, &facet->flow);
-
- ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
- actions, actions_len, stats);
-
- if (stats) {
- facet_reset_dp_stats(facet, stats);
- }
-
- return ret;
-}
-
-/* If 'facet' is installable, inserts or re-inserts it into 'p''s datapath. If
- * 'zero_stats' is true, clears any existing statistics from the datapath for
- * 'facet'. */
-static void
-facet_install(struct ofproto *p, struct facet *facet, bool zero_stats)
-{
- struct dpif_flow_stats stats;
-
- if (facet->may_install
- && !facet_put__(p, facet, facet->actions, facet->actions_len,
- zero_stats ? &stats : NULL)) {
- facet->installed = true;
- }
-}
-
-/* Ensures that the bytes in 'facet', plus 'extra_bytes', have been passed up
- * to the accounting hook function in the ofhooks structure. */
-static void
-facet_account(struct ofproto *ofproto,
- struct facet *facet, uint64_t extra_bytes)
-{
- uint64_t total_bytes = facet->byte_count + extra_bytes;
-
- if (ofproto->ofhooks->account_flow_cb
- && total_bytes > facet->accounted_bytes)
- {
- ofproto->ofhooks->account_flow_cb(
- &facet->flow, facet->tags, facet->actions, facet->actions_len,
- total_bytes - facet->accounted_bytes, ofproto->aux);
- facet->accounted_bytes = total_bytes;
- }
-}
-
-/* If 'rule' is installed in the datapath, uninstalls it. */
-static void
-facet_uninstall(struct ofproto *p, struct facet *facet)
-{
- if (facet->installed) {
- struct odputil_keybuf keybuf;
- struct dpif_flow_stats stats;
- struct ofpbuf key;
- int error;
-
- ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
- odp_flow_key_from_flow(&key, &facet->flow);
-
- error = dpif_flow_del(p->dpif, key.data, key.size, &stats);
- facet_reset_dp_stats(facet, &stats);
- if (!error) {
- facet_update_stats(p, facet, &stats);
- }
-
- facet->installed = false;
- } else {
- assert(facet->dp_packet_count == 0);
- assert(facet->dp_byte_count == 0);
- }
-}
-
-/* Returns true if the only action for 'facet' is to send to the controller.
- * (We don't report NetFlow expiration messages for such facets because they
- * are just part of the control logic for the network, not real traffic). */
-static bool
-facet_is_controller_flow(struct facet *facet)
-{
- return (facet
- && facet->rule->n_actions == 1
- && action_outputs_to_port(&facet->rule->actions[0],
- htons(OFPP_CONTROLLER)));
-}
-
-/* Resets 'facet''s datapath statistics counters. This should be called when
- * 'facet''s statistics are cleared in the datapath. If 'stats' is non-null,
- * it should contain the statistics returned by dpif when 'facet' was reset in
- * the datapath. 'stats' will be modified to only included statistics new
- * since 'facet' was last updated. */
-static void
-facet_reset_dp_stats(struct facet *facet, struct dpif_flow_stats *stats)
-{
- if (stats && facet->dp_packet_count <= stats->n_packets
- && facet->dp_byte_count <= stats->n_bytes) {
- stats->n_packets -= facet->dp_packet_count;
- stats->n_bytes -= facet->dp_byte_count;
- }
-
- facet->dp_packet_count = 0;
- facet->dp_byte_count = 0;
-}
-
-/* Folds all of 'facet''s statistics into its rule. Also updates the
- * accounting ofhook and emits a NetFlow expiration if appropriate. All of
- * 'facet''s statistics in the datapath should have been zeroed and folded into
- * its packet and byte counts before this function is called. */
-static void
-facet_flush_stats(struct ofproto *ofproto, struct facet *facet)
-{
- assert(!facet->dp_byte_count);
- assert(!facet->dp_packet_count);
-
- facet_push_stats(ofproto, facet);
- facet_account(ofproto, facet, 0);
-
- if (ofproto->netflow && !facet_is_controller_flow(facet)) {
- struct ofexpired expired;
- expired.flow = facet->flow;
- expired.packet_count = facet->packet_count;
- expired.byte_count = facet->byte_count;
- expired.used = facet->used;
- netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
- }
-
- facet->rule->packet_count += facet->packet_count;
- facet->rule->byte_count += facet->byte_count;
-
- /* Reset counters to prevent double counting if 'facet' ever gets
- * reinstalled. */
- facet->packet_count = 0;
- facet->byte_count = 0;
- facet->rs_packet_count = 0;
- facet->rs_byte_count = 0;
- facet->accounted_bytes = 0;
-
- netflow_flow_clear(&facet->nf_flow);
-}
-
-/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
- * Returns it if found, otherwise a null pointer.
- *
- * The returned facet might need revalidation; use facet_lookup_valid()
- * instead if that is important. */
-static struct facet *
-facet_find(struct ofproto *ofproto, const struct flow *flow)
-{
- struct facet *facet;
-
- HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, flow_hash(flow, 0),
- &ofproto->facets) {
- if (flow_equal(flow, &facet->flow)) {
- return facet;
- }
- }
-
- return NULL;
-}
-
-/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
- * Returns it if found, otherwise a null pointer.
- *
- * The returned facet is guaranteed to be valid. */
-static struct facet *
-facet_lookup_valid(struct ofproto *ofproto, const struct flow *flow)
-{
- struct facet *facet = facet_find(ofproto, flow);
-
- /* The facet we found might not be valid, since we could be in need of
- * revalidation. If it is not valid, don't return it. */
- if (facet
- && ofproto->need_revalidate
- && !facet_revalidate(ofproto, facet)) {
- COVERAGE_INC(ofproto_invalidated);
- return NULL;
- }
-
- return facet;
-}
-
-/* Re-searches 'ofproto''s classifier for a rule matching 'facet':
- *
- * - If the rule found is different from 'facet''s current rule, moves
- * 'facet' to the new rule and recompiles its actions.
- *
- * - If the rule found is the same as 'facet''s current rule, leaves 'facet'
- * where it is and recompiles its actions anyway.
- *
- * - If there is none, destroys 'facet'.
- *
- * Returns true if 'facet' still exists, false if it has been destroyed. */
-static bool
-facet_revalidate(struct ofproto *ofproto, struct facet *facet)
-{
- struct action_xlate_ctx ctx;
- struct ofpbuf *odp_actions;
- struct rule *new_rule;
- bool actions_changed;
-
- COVERAGE_INC(facet_revalidate);
-
- /* Determine the new rule. */
- new_rule = rule_lookup(ofproto, &facet->flow);
- if (!new_rule) {
- /* No new rule, so delete the facet. */
- facet_remove(ofproto, facet);
- return false;
- }
-
- /* Calculate new ODP actions.
- *
- * We do not modify any 'facet' state yet, because we might need to, e.g.,
- * emit a NetFlow expiration and, if so, we need to have the old state
- * around to properly compose it. */
- action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL);
- odp_actions = xlate_actions(&ctx, new_rule->actions, new_rule->n_actions);
- actions_changed = (facet->actions_len != odp_actions->size
- || memcmp(facet->actions, odp_actions->data,
- facet->actions_len));
-
- /* If the ODP actions changed or the installability changed, then we need
- * to talk to the datapath. */
- if (actions_changed || ctx.may_set_up_flow != facet->installed) {
- if (ctx.may_set_up_flow) {
- struct dpif_flow_stats stats;
-
- facet_put__(ofproto, facet,
- odp_actions->data, odp_actions->size, &stats);
- facet_update_stats(ofproto, facet, &stats);
- } else {
- facet_uninstall(ofproto, facet);
- }
-
- /* The datapath flow is gone or has zeroed stats, so push stats out of
- * 'facet' into 'rule'. */
- facet_flush_stats(ofproto, facet);
- }
-
- /* Update 'facet' now that we've taken care of all the old state. */
- facet->tags = ctx.tags;
- facet->nf_flow.output_iface = ctx.nf_output_iface;
- facet->may_install = ctx.may_set_up_flow;
- if (actions_changed) {
- free(facet->actions);
- facet->actions_len = odp_actions->size;
- facet->actions = xmemdup(odp_actions->data, odp_actions->size);
- }
- if (facet->rule != new_rule) {
- COVERAGE_INC(facet_changed_rule);
- list_remove(&facet->list_node);
- list_push_back(&new_rule->facets, &facet->list_node);
- facet->rule = new_rule;
- facet->used = new_rule->created;
- facet->rs_used = facet->used;
- }
-
- ofpbuf_delete(odp_actions);
-
- return true;
-}
-\f
-static void
-send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
- int error)
-{
- struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
- if (buf) {
- COVERAGE_INC(ofproto_error);
- ofconn_send_reply(ofconn, buf);
- }
-}
-
-static int
-handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- ofconn_send_reply(ofconn, make_echo_reply(oh));
- return 0;
-}
-
-static int
-handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofp_switch_features *osf;
- struct ofpbuf *buf;
- struct ofport *port;
-
- osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
- osf->datapath_id = htonll(ofproto->datapath_id);
- osf->n_buffers = htonl(pktbuf_capacity());
- osf->n_tables = 2;
- osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
- OFPC_PORT_STATS | OFPC_ARP_MATCH_IP);
- osf->actions = htonl((1u << OFPAT_OUTPUT) |
- (1u << OFPAT_SET_VLAN_VID) |
- (1u << OFPAT_SET_VLAN_PCP) |
- (1u << OFPAT_STRIP_VLAN) |
- (1u << OFPAT_SET_DL_SRC) |
- (1u << OFPAT_SET_DL_DST) |
- (1u << OFPAT_SET_NW_SRC) |
- (1u << OFPAT_SET_NW_DST) |
- (1u << OFPAT_SET_NW_TOS) |
- (1u << OFPAT_SET_TP_SRC) |
- (1u << OFPAT_SET_TP_DST) |
- (1u << OFPAT_ENQUEUE));
-
- HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- ofpbuf_put(buf, &port->opp, sizeof port->opp);
- }
-
- ofconn_send_reply(ofconn, buf);
- return 0;
-}
-
-static int
-handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofpbuf *buf;
- struct ofp_switch_config *osc;
- uint16_t flags;
- bool drop_frags;
-
- /* Figure out flags. */
- dpif_get_drop_frags(ofproto->dpif, &drop_frags);
- flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
-
- /* Send reply. */
- osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
- osc->flags = htons(flags);
- osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
- ofconn_send_reply(ofconn, buf);
-
- return 0;
-}
-
-static int
-handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- uint16_t flags = ntohs(osc->flags);
-
- if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
- && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
- switch (flags & OFPC_FRAG_MASK) {
- case OFPC_FRAG_NORMAL:
- dpif_set_drop_frags(ofproto->dpif, false);
- break;
- case OFPC_FRAG_DROP:
- dpif_set_drop_frags(ofproto->dpif, true);
- break;
- default:
- VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
- osc->flags);
- break;
- }
- }
-
- ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
-
- return 0;
-}
-
-static void do_xlate_actions(const union ofp_action *in, size_t n_in,
- struct action_xlate_ctx *ctx);
-
-static void
-add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
-{
- const struct ofport *ofport = get_port(ctx->ofproto, port);
-
- if (ofport) {
- if (ofport->opp.config & htonl(OFPPC_NO_FWD)) {
- /* Forwarding disabled on port. */
- return;
- }
- } else {
- /*
- * We don't have an ofport record for this port, but it doesn't hurt to
- * allow forwarding to it anyhow. Maybe such a port will appear later
- * and we're pre-populating the flow table.
- */
- }
-
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, port);
- ctx->nf_output_iface = port;
-}
-
-static struct rule *
-rule_lookup(struct ofproto *ofproto, const struct flow *flow)
-{
- return rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
-}
-
-static void
-xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
-{
- if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
- uint16_t old_in_port;
- struct rule *rule;
-
- /* Look up a flow with 'in_port' as the input port. Then restore the
- * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
- * have surprising behavior). */
- old_in_port = ctx->flow.in_port;
- ctx->flow.in_port = in_port;
- rule = rule_lookup(ctx->ofproto, &ctx->flow);
- ctx->flow.in_port = old_in_port;
-
- if (ctx->resubmit_hook) {
- ctx->resubmit_hook(ctx, rule);
- }
-
- if (rule) {
- ctx->recurse++;
- do_xlate_actions(rule->actions, rule->n_actions, ctx);
- ctx->recurse--;
- }
- } else {
- static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
-
- VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
- MAX_RESUBMIT_RECURSION);
- }
-}
-
-static void
-flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, ovs_be32 mask,
- uint16_t *nf_output_iface, struct ofpbuf *odp_actions)
-{
- struct ofport *ofport;
-
- HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) {
- uint16_t odp_port = ofport->odp_port;
- if (odp_port != odp_in_port && !(ofport->opp.config & mask)) {
- nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
- }
- }
- *nf_output_iface = NF_OUT_FLOOD;
-}
-
-static void
-xlate_output_action__(struct action_xlate_ctx *ctx,
- uint16_t port, uint16_t max_len)
-{
- uint16_t odp_port;
- uint16_t prev_nf_output_iface = ctx->nf_output_iface;
-
- ctx->nf_output_iface = NF_OUT_DROP;
-
- switch (port) {
- case OFPP_IN_PORT:
- add_output_action(ctx, ctx->flow.in_port);
- break;
- case OFPP_TABLE:
- xlate_table_action(ctx, ctx->flow.in_port);
- break;
- case OFPP_NORMAL:
- if (!ctx->ofproto->ofhooks->normal_cb(&ctx->flow, ctx->packet,
- ctx->odp_actions, &ctx->tags,
- &ctx->nf_output_iface,
- ctx->ofproto->aux)) {
- COVERAGE_INC(ofproto_uninstallable);
- ctx->may_set_up_flow = false;
- }
- break;
- case OFPP_FLOOD:
- flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(OFPPC_NO_FLOOD),
- &ctx->nf_output_iface, ctx->odp_actions);
- break;
- case OFPP_ALL:
- flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0),
- &ctx->nf_output_iface, ctx->odp_actions);
- break;
- case OFPP_CONTROLLER:
- nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len);
- break;
- case OFPP_LOCAL:
- add_output_action(ctx, ODPP_LOCAL);
- break;
- default:
- odp_port = ofp_port_to_odp_port(port);
- if (odp_port != ctx->flow.in_port) {
- add_output_action(ctx, odp_port);
- }
- break;
- }
-
- if (prev_nf_output_iface == NF_OUT_FLOOD) {
- ctx->nf_output_iface = NF_OUT_FLOOD;
- } else if (ctx->nf_output_iface == NF_OUT_DROP) {
- ctx->nf_output_iface = prev_nf_output_iface;
- } else if (prev_nf_output_iface != NF_OUT_DROP &&
- ctx->nf_output_iface != NF_OUT_FLOOD) {
- ctx->nf_output_iface = NF_OUT_MULTI;
- }
-}
-
-static void
-xlate_output_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_output *oao)
-{
- xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
-}
-
-/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
- * optimization, because we're going to add another action that sets the
- * priority immediately after, or because there are no actions following the
- * pop. */
-static void
-remove_pop_action(struct action_xlate_ctx *ctx)
-{
- if (ctx->odp_actions->size == ctx->last_pop_priority) {
- ctx->odp_actions->size -= NLA_ALIGN(NLA_HDRLEN);
- ctx->last_pop_priority = -1;
- }
-}
-
-static void
-add_pop_action(struct action_xlate_ctx *ctx)
-{
- if (ctx->odp_actions->size != ctx->last_pop_priority) {
- nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
- ctx->last_pop_priority = ctx->odp_actions->size;
- }
-}
-
-static void
-xlate_enqueue_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_enqueue *oae)
-{
- uint16_t ofp_port, odp_port;
- uint32_t priority;
- int error;
-
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
- &priority);
- if (error) {
- /* Fall back to ordinary output action. */
- xlate_output_action__(ctx, ntohs(oae->port), 0);
- return;
- }
-
- /* Figure out ODP output port. */
- ofp_port = ntohs(oae->port);
- if (ofp_port != OFPP_IN_PORT) {
- odp_port = ofp_port_to_odp_port(ofp_port);
- } else {
- odp_port = ctx->flow.in_port;
- }
-
- /* Add ODP actions. */
- remove_pop_action(ctx);
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
- add_output_action(ctx, odp_port);
- add_pop_action(ctx);
-
- /* Update NetFlow output port. */
- if (ctx->nf_output_iface == NF_OUT_DROP) {
- ctx->nf_output_iface = odp_port;
- } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
- ctx->nf_output_iface = NF_OUT_MULTI;
- }
-}
-
-static void
-xlate_set_queue_action(struct action_xlate_ctx *ctx,
- const struct nx_action_set_queue *nasq)
+static int
+rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet)
{
- uint32_t priority;
- int error;
-
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
- &priority);
- if (error) {
- /* Couldn't translate queue to a priority, so ignore. A warning
- * has already been logged. */
- return;
- }
+ struct flow flow;
- remove_pop_action(ctx);
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
-}
+ assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
-static void
-xlate_set_dl_tci(struct action_xlate_ctx *ctx)
-{
- ovs_be16 tci = ctx->flow.vlan_tci;
- if (!(tci & htons(VLAN_CFI))) {
- nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
- } else {
- nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
- tci & ~htons(VLAN_CFI));
- }
+ flow_extract(packet, 0, in_port, &flow);
+ return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
}
-struct xlate_reg_state {
- ovs_be16 vlan_tci;
- ovs_be64 tun_id;
-};
-
-static void
-save_reg_state(const struct action_xlate_ctx *ctx,
- struct xlate_reg_state *state)
+/* Returns true if 'rule' should be hidden from the controller.
+ *
+ * Rules with priority higher than UINT16_MAX are set up by ofproto itself
+ * (e.g. by in-band control) and are intentionally hidden from the
+ * controller. */
+static bool
+rule_is_hidden(const struct rule *rule)
{
- state->vlan_tci = ctx->flow.vlan_tci;
- state->tun_id = ctx->flow.tun_id;
+ return rule->cr.priority > UINT16_MAX;
}
-
+\f
static void
-update_reg_state(struct action_xlate_ctx *ctx,
- const struct xlate_reg_state *state)
+send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
+ int error)
{
- if (ctx->flow.vlan_tci != state->vlan_tci) {
- xlate_set_dl_tci(ctx);
- }
- if (ctx->flow.tun_id != state->tun_id) {
- nl_msg_put_be64(ctx->odp_actions,
- ODP_ACTION_ATTR_SET_TUNNEL, ctx->flow.tun_id);
+ struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
+ if (buf) {
+ COVERAGE_INC(ofproto_error);
+ ofconn_send_reply(ofconn, buf);
}
}
-static void
-xlate_nicira_action(struct action_xlate_ctx *ctx,
- const struct nx_action_header *nah)
-{
- const struct nx_action_resubmit *nar;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- enum nx_action_subtype subtype = ntohs(nah->subtype);
- const struct ofhooks *ofhooks = ctx->ofproto->ofhooks;
- struct xlate_reg_state state;
- uint16_t autopath_port;
- ovs_be64 tun_id;
-
- assert(nah->vendor == htonl(NX_VENDOR_ID));
- switch (subtype) {
- case NXAST_RESUBMIT:
- nar = (const struct nx_action_resubmit *) nah;
- xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
- break;
-
- case NXAST_SET_TUNNEL:
- nast = (const struct nx_action_set_tunnel *) nah;
- tun_id = htonll(ntohl(nast->tun_id));
- nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
- ctx->flow.tun_id = tun_id;
- break;
-
- case NXAST_DROP_SPOOFED_ARP:
- if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
- nl_msg_put_flag(ctx->odp_actions,
- ODP_ACTION_ATTR_DROP_SPOOFED_ARP);
- }
- break;
-
- case NXAST_SET_QUEUE:
- nasq = (const struct nx_action_set_queue *) nah;
- xlate_set_queue_action(ctx, nasq);
- break;
-
- case NXAST_POP_QUEUE:
- add_pop_action(ctx);
- break;
-
- case NXAST_REG_MOVE:
- save_reg_state(ctx, &state);
- nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
- &ctx->flow);
- update_reg_state(ctx, &state);
- break;
-
- case NXAST_REG_LOAD:
- save_reg_state(ctx, &state);
- nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
- &ctx->flow);
- update_reg_state(ctx, &state);
- break;
-
- case NXAST_NOTE:
- /* Nothing to do. */
- break;
-
- case NXAST_SET_TUNNEL64:
- tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
- nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
- ctx->flow.tun_id = tun_id;
- break;
-
- case NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) nah;
- multipath_execute(nam, &ctx->flow);
- break;
-
- case NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *) nah;
- autopath_port = (ofhooks->autopath_cb
- ? ofhooks->autopath_cb(&ctx->flow, ntohl(naa->id),
- &ctx->tags, ctx->ofproto->aux)
- : OFPP_NONE);
- autopath_execute(naa, &ctx->flow, autopath_port);
- break;
-
- /* If you add a new action here that modifies flow data, don't forget to
- * update the flow key in ctx->flow at the same time. */
-
- case NXAST_SNAT__OBSOLETE:
- default:
- VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) subtype);
- break;
- }
+static int
+handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ ofconn_send_reply(ofconn, make_echo_reply(oh));
+ return 0;
}
-static void
-do_xlate_actions(const union ofp_action *in, size_t n_in,
- struct action_xlate_ctx *ctx)
+static int
+handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
- struct actions_iterator iter;
- const union ofp_action *ia;
- const struct ofport *port;
-
- port = get_port(ctx->ofproto, ctx->flow.in_port);
- if (port && port->opp.config & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
- port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
- ? htonl(OFPPC_NO_RECV_STP)
- : htonl(OFPPC_NO_RECV))) {
- /* Drop this flow. */
- return;
- }
-
- for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
- enum ofp_action_type type = ntohs(ia->type);
- const struct ofp_action_dl_addr *oada;
-
- switch (type) {
- case OFPAT_OUTPUT:
- xlate_output_action(ctx, &ia->output);
- break;
-
- case OFPAT_SET_VLAN_VID:
- ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
- ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
- xlate_set_dl_tci(ctx);
- break;
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct ofp_switch_features *osf;
+ struct ofpbuf *buf;
+ struct ofport *port;
+ bool arp_match_ip;
+ uint32_t actions;
- case OFPAT_SET_VLAN_PCP:
- ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
- ctx->flow.vlan_tci |= htons(
- (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
- xlate_set_dl_tci(ctx);
- break;
+ ofproto->ofproto_class->get_features(ofproto, &arp_match_ip, &actions);
+ assert(actions & (1 << OFPAT_OUTPUT)); /* sanity check */
- case OFPAT_STRIP_VLAN:
- ctx->flow.vlan_tci = htons(0);
- xlate_set_dl_tci(ctx);
- break;
+ osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
+ osf->datapath_id = htonll(ofproto->datapath_id);
+ osf->n_buffers = htonl(pktbuf_capacity());
+ osf->n_tables = ofproto->n_tables;
+ osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
+ OFPC_PORT_STATS);
+ if (arp_match_ip) {
+ osf->capabilities |= htonl(OFPC_ARP_MATCH_IP);
+ }
+ osf->actions = htonl(actions);
- case OFPAT_SET_DL_SRC:
- oada = ((struct ofp_action_dl_addr *) ia);
- nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
- oada->dl_addr, ETH_ADDR_LEN);
- memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
- break;
+ HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
+ ofpbuf_put(buf, &port->opp, sizeof port->opp);
+ }
- case OFPAT_SET_DL_DST:
- oada = ((struct ofp_action_dl_addr *) ia);
- nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
- oada->dl_addr, ETH_ADDR_LEN);
- memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
- break;
+ ofconn_send_reply(ofconn, buf);
+ return 0;
+}
- case OFPAT_SET_NW_SRC:
- nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_SRC,
- ia->nw_addr.nw_addr);
- ctx->flow.nw_src = ia->nw_addr.nw_addr;
- break;
+static int
+handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct ofpbuf *buf;
+ struct ofp_switch_config *osc;
+ uint16_t flags;
+ bool drop_frags;
- case OFPAT_SET_NW_DST:
- nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_DST,
- ia->nw_addr.nw_addr);
- ctx->flow.nw_dst = ia->nw_addr.nw_addr;
- break;
+ /* Figure out flags. */
+ drop_frags = ofproto->ofproto_class->get_drop_frags(ofproto);
+ flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
- case OFPAT_SET_NW_TOS:
- nl_msg_put_u8(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_TOS,
- ia->nw_tos.nw_tos);
- ctx->flow.nw_tos = ia->nw_tos.nw_tos;
- break;
+ /* Send reply. */
+ osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
+ osc->flags = htons(flags);
+ osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
+ ofconn_send_reply(ofconn, buf);
- case OFPAT_SET_TP_SRC:
- nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_SRC,
- ia->tp_port.tp_port);
- ctx->flow.tp_src = ia->tp_port.tp_port;
- break;
+ return 0;
+}
- case OFPAT_SET_TP_DST:
- nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_DST,
- ia->tp_port.tp_port);
- ctx->flow.tp_dst = ia->tp_port.tp_port;
- break;
+static int
+handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
+{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ uint16_t flags = ntohs(osc->flags);
- case OFPAT_VENDOR:
- xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
+ if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
+ && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
+ switch (flags & OFPC_FRAG_MASK) {
+ case OFPC_FRAG_NORMAL:
+ ofproto->ofproto_class->set_drop_frags(ofproto, false);
break;
-
- case OFPAT_ENQUEUE:
- xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+ case OFPC_FRAG_DROP:
+ ofproto->ofproto_class->set_drop_frags(ofproto, true);
break;
-
default:
- VLOG_DBG_RL(&rl, "unknown action type %d", (int) type);
+ VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
+ osc->flags);
break;
}
}
-}
-
-static void
-action_xlate_ctx_init(struct action_xlate_ctx *ctx,
- struct ofproto *ofproto, const struct flow *flow,
- const struct ofpbuf *packet)
-{
- ctx->ofproto = ofproto;
- ctx->flow = *flow;
- ctx->packet = packet;
- ctx->resubmit_hook = NULL;
- ctx->check_special = true;
-}
-
-static void
-ofproto_process_cfm(struct ofproto *ofproto, const struct flow *flow,
- const struct ofpbuf *packet)
-{
- struct ofport *ofport;
-
- ofport = get_port(ofproto, flow->in_port);
- if (ofport && ofport->cfm) {
- cfm_process_heartbeat(ofport->cfm, packet);
- }
-}
-
-static struct ofpbuf *
-xlate_actions(struct action_xlate_ctx *ctx,
- const union ofp_action *in, size_t n_in)
-{
- COVERAGE_INC(ofproto_ofp2odp);
-
- ctx->odp_actions = ofpbuf_new(512);
- ctx->tags = 0;
- ctx->may_set_up_flow = true;
- ctx->nf_output_iface = NF_OUT_DROP;
- ctx->recurse = 0;
- ctx->last_pop_priority = -1;
-
- if (ctx->check_special && cfm_should_process_flow(&ctx->flow)) {
- if (ctx->packet) {
- ofproto_process_cfm(ctx->ofproto, &ctx->flow, ctx->packet);
- }
- ctx->may_set_up_flow = false;
- } else if (ctx->check_special
- && ctx->ofproto->ofhooks->special_cb
- && !ctx->ofproto->ofhooks->special_cb(&ctx->flow, ctx->packet,
- ctx->ofproto->aux)) {
- ctx->may_set_up_flow = false;
- } else {
- do_xlate_actions(in, n_in, ctx);
- }
- remove_pop_action(ctx);
-
- /* Check with in-band control to see if we're allowed to set up this
- * flow. */
- if (!connmgr_may_set_up_flow(ctx->ofproto->connmgr, &ctx->flow,
- ctx->odp_actions->data,
- ctx->odp_actions->size)) {
- ctx->may_set_up_flow = false;
- }
+ ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
- return ctx->odp_actions;
+ return 0;
}
/* Checks whether 'ofconn' is a slave controller. If so, returns an OpenFlow
struct ofp_packet_out *opo;
struct ofpbuf payload, *buffer;
union ofp_action *ofp_actions;
- struct action_xlate_ctx ctx;
- struct ofpbuf *odp_actions;
struct ofpbuf request;
struct flow flow;
size_t n_ofp_actions;
buffer = NULL;
}
- /* Extract flow, check actions. */
- flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)),
- &flow);
- error = validate_actions(ofp_actions, n_ofp_actions, &flow, p->max_ports);
- if (error) {
- goto exit;
- }
-
- /* Send. */
- action_xlate_ctx_init(&ctx, p, &flow, &payload);
- odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions);
- dpif_execute(p->dpif, odp_actions->data, odp_actions->size, &payload);
- ofpbuf_delete(odp_actions);
-
-exit:
+ /* Send out packet. */
+ flow_extract(&payload, 0, ntohs(opo->in_port), &flow);
+ error = p->ofproto_class->packet_out(p, &payload, &flow,
+ ofp_actions, n_ofp_actions);
ofpbuf_delete(buffer);
- return 0;
+
+ return error;
}
static void
-update_port_config(struct ofproto *p, struct ofport *port,
- ovs_be32 config, ovs_be32 mask)
+update_port_config(struct ofport *port, ovs_be32 config, ovs_be32 mask)
{
+ ovs_be32 old_config = port->opp.config;
+
mask &= config ^ port->opp.config;
if (mask & htonl(OFPPC_PORT_DOWN)) {
if (config & htonl(OFPPC_PORT_DOWN)) {
netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
}
}
-#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | \
- OFPPC_NO_FWD | OFPPC_NO_FLOOD)
- if (mask & htonl(REVALIDATE_BITS)) {
- COVERAGE_INC(ofproto_costly_flags);
- port->opp.config ^= mask & htonl(REVALIDATE_BITS);
- p->need_revalidate = true;
- }
-#undef REVALIDATE_BITS
- if (mask & htonl(OFPPC_NO_PACKET_IN)) {
- port->opp.config ^= htonl(OFPPC_NO_PACKET_IN);
+
+ port->opp.config ^= mask & (htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP |
+ OFPPC_NO_FLOOD | OFPPC_NO_FWD |
+ OFPPC_NO_PACKET_IN));
+ if (port->opp.config != old_config) {
+ port->ofproto->ofproto_class->port_reconfigured(port, old_config);
}
}
return error;
}
- port = get_port(p, ofp_port_to_odp_port(ntohs(opm->port_no)));
+ port = ofproto_get_port(p, ntohs(opm->port_no));
if (!port) {
return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
} else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
} else {
- update_port_config(p, port, opm->config, opm->mask);
+ update_port_config(port, opm->config, opm->mask);
if (opm->advertise) {
netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
}
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofp_table_stats *ots;
struct ofpbuf *msg;
+ size_t i;
- msg = start_ofp_stats_reply(request, sizeof *ots * 2);
+ msg = start_ofp_stats_reply(request, sizeof *ots * p->n_tables);
- /* Classifier table. */
- ots = append_ofp_stats_reply(sizeof *ots, ofconn, &msg);
- memset(ots, 0, sizeof *ots);
- strcpy(ots->name, "classifier");
- ots->wildcards = htonl(OFPFW_ALL);
- ots->max_entries = htonl(1024 * 1024); /* An arbitrary big number. */
- ots->active_count = htonl(classifier_count(&p->cls));
- put_32aligned_be64(&ots->lookup_count, htonll(0)); /* XXX */
- put_32aligned_be64(&ots->matched_count, htonll(0)); /* XXX */
+ ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables);
+ for (i = 0; i < p->n_tables; i++) {
+ ots[i].table_id = i;
+ sprintf(ots[i].name, "table%d", i);
+ ots[i].wildcards = htonl(OFPFW_ALL);
+ ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
+ ots[i].active_count = htonl(classifier_count(&p->tables[i]));
+ }
+
+ p->ofproto_class->get_tables(p, ots);
ofconn_send_reply(ofconn, msg);
return 0;
msg = start_ofp_stats_reply(oh, sizeof *ops * 16);
if (psr->port_no != htons(OFPP_NONE)) {
- port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no)));
+ port = ofproto_get_port(p, ntohs(psr->port_no));
if (port) {
append_port_stat(port, ofconn, &msg);
}
put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
ovs_be16 out_port, struct ofpbuf **replyp)
{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofp_flow_stats *ofs;
uint64_t packet_count, byte_count;
size_t act_len, len;
act_len = sizeof *rule->actions * rule->n_actions;
len = offsetof(struct ofp_flow_stats, actions) + act_len;
- rule_get_stats(rule, &packet_count, &byte_count);
+ ofproto->ofproto_class->rule_get_stats(rule, &packet_count, &byte_count);
ofs = append_ofp_stats_reply(len, ofconn, replyp);
ofs->length = htons(len);
- ofs->table_id = 0;
+ ofs->table_id = rule->table_id;
ofs->pad = 0;
ofputil_cls_rule_to_match(&rule->cr, &ofs->match);
put_32aligned_be64(&ofs->cookie, rule->flow_cookie);
}
}
-static bool
-is_valid_table(uint8_t table_id)
+static struct classifier *
+first_matching_table(struct ofproto *ofproto, uint8_t table_id)
{
- if (table_id == 0 || table_id == 0xff) {
- return true;
+ if (table_id == 0xff) {
+ return &ofproto->tables[0];
+ } else if (table_id < ofproto->n_tables) {
+ return &ofproto->tables[table_id];
} else {
/* It would probably be better to reply with an error but there doesn't
* seem to be any appropriate value, so that might just be
* confusing. */
VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8,
table_id);
- return false;
+ return NULL;
}
}
+static struct classifier *
+next_matching_table(struct ofproto *ofproto,
+ struct classifier *cls, uint8_t table_id)
+{
+ return (table_id == 0xff && cls != &ofproto->tables[ofproto->n_tables - 1]
+ ? cls + 1
+ : NULL);
+}
+
+/* Assigns CLS to each classifier table, in turn, that matches TABLE_ID in
+ * OFPROTO:
+ *
+ * - If TABLE_ID is 0xff, this iterates over every classifier table in
+ * OFPROTO.
+ *
+ * - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates
+ * only once, for that table.
+ *
+ * - Otherwise, TABLE_ID isn't valid for OFPROTO, so ofproto logs a warning
+ * and does not enter the loop at all.
+ *
+ * All parameters are evaluated multiple times.
+ */
+#define FOR_EACH_MATCHING_TABLE(CLS, TABLE_ID, OFPROTO) \
+ for ((CLS) = first_matching_table(OFPROTO, TABLE_ID); \
+ (CLS) != NULL; \
+ (CLS) = next_matching_table(OFPROTO, CLS, TABLE_ID))
+
static int
handle_flow_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
const struct ofp_flow_stats_request *fsr = ofputil_stats_body(oh);
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct classifier *cls;
+ struct cls_rule target;
struct ofpbuf *reply;
COVERAGE_INC(ofproto_flows_req);
reply = start_ofp_stats_reply(oh, 1024);
- if (is_valid_table(fsr->table_id)) {
+ ofputil_cls_rule_from_match(&fsr->match, 0, &target);
+ FOR_EACH_MATCHING_TABLE (cls, fsr->table_id, ofproto) {
struct cls_cursor cursor;
- struct cls_rule target;
struct rule *rule;
- ofputil_cls_rule_from_match(&fsr->match, 0, &target);
- cls_cursor_init(&cursor, &ofproto->cls, &target);
+ cls_cursor_init(&cursor, cls, &target);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
put_ofp_flow_stats(ofconn, rule, fsr->out_port, &reply);
}
return;
}
- rule_get_stats(rule, &packet_count, &byte_count);
+ rule->ofproto->ofproto_class->rule_get_stats(rule,
+ &packet_count, &byte_count);
act_len = sizeof *rule->actions * rule->n_actions;
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct nx_flow_stats_request *nfsr;
+ struct classifier *cls;
struct cls_rule target;
struct ofpbuf *reply;
struct ofpbuf b;
COVERAGE_INC(ofproto_flows_req);
reply = start_nxstats_reply(&nfsr->nsm, 1024);
- if (is_valid_table(nfsr->table_id)) {
+ FOR_EACH_MATCHING_TABLE (cls, nfsr->table_id, ofproto) {
struct cls_cursor cursor;
struct rule *rule;
- cls_cursor_init(&cursor, &ofproto->cls, &target);
+ cls_cursor_init(&cursor, cls, &target);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
put_nx_flow_stats(ofconn, rule, nfsr->out_port, &reply);
}
uint64_t packet_count, byte_count;
size_t act_len = sizeof *rule->actions * rule->n_actions;
- rule_get_stats(rule, &packet_count, &byte_count);
+ rule->ofproto->ofproto_class->rule_get_stats(rule,
+ &packet_count, &byte_count);
+ if (rule->table_id != 0) {
+ ds_put_format(results, "table_id=%"PRIu8", ", rule->table_id);
+ }
ds_put_format(results, "duration=%llds, ",
(time_msec() - rule->created) / 1000);
- ds_put_format(results, "idle=%.3fs, ", (time_msec() - rule->used) / 1000.0);
ds_put_format(results, "priority=%u, ", rule->cr.priority);
ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
void
ofproto_get_all_flows(struct ofproto *p, struct ds *results)
{
- struct cls_cursor cursor;
- struct rule *rule;
+ struct classifier *cls;
+
+ for (cls = &p->tables[0]; cls < &p->tables[p->n_tables]; cls++) {
+ struct cls_cursor cursor;
+ struct rule *rule;
- cls_cursor_init(&cursor, &p->cls, NULL);
- CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
- flow_stats_ds(rule, results);
+ cls_cursor_init(&cursor, cls, NULL);
+ CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+ flow_stats_ds(rule, results);
+ }
}
}
+/* Obtains the NetFlow engine type and engine ID for 'ofproto' into
+ * '*engine_type' and '*engine_id', respectively. */
+void
+ofproto_get_netflow_ids(const struct ofproto *ofproto,
+ uint8_t *engine_type, uint8_t *engine_id)
+{
+ ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id);
+}
+
static void
query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
ovs_be16 out_port, uint8_t table_id,
{
uint64_t total_packets = 0;
uint64_t total_bytes = 0;
+ struct classifier *cls;
int n_flows = 0;
COVERAGE_INC(ofproto_agg_request);
- if (is_valid_table(table_id)) {
+ FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
struct cls_cursor cursor;
struct rule *rule;
- cls_cursor_init(&cursor, &ofproto->cls, target);
+ cls_cursor_init(&cursor, cls, target);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) {
uint64_t packet_count;
uint64_t byte_count;
- rule_get_stats(rule, &packet_count, &byte_count);
+ ofproto->ofproto_class->rule_get_stats(rule, &packet_count,
+ &byte_count);
total_packets += packet_count;
total_bytes += byte_count;
HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
handle_queue_stats_for_port(port, queue_id, &cbdata);
}
- } else if (port_no < ofproto->max_ports) {
- port = get_port(ofproto, ofp_port_to_odp_port(port_no));
+ } else if (port_no < OFPP_MAX) {
+ port = ofproto_get_port(ofproto, port_no);
if (port) {
handle_queue_stats_for_port(port, queue_id, &cbdata);
}
return 0;
}
-/* Updates 'facet''s used time. Caller is responsible for calling
- * facet_push_stats() to update the flows which 'facet' resubmits into. */
-static void
-facet_update_time(struct ofproto *ofproto, struct facet *facet,
- long long int used)
-{
- if (used > facet->used) {
- facet->used = used;
- if (used > facet->rule->used) {
- facet->rule->used = used;
- }
- netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
- }
-}
-
-/* Folds the statistics from 'stats' into the counters in 'facet'.
- *
- * Because of the meaning of a facet's counters, it only makes sense to do this
- * if 'stats' are not tracked in the datapath, that is, if 'stats' represents a
- * packet that was sent by hand or if it represents statistics that have been
- * cleared out of the datapath. */
-static void
-facet_update_stats(struct ofproto *ofproto, struct facet *facet,
- const struct dpif_flow_stats *stats)
-{
- if (stats->n_packets || stats->used > facet->used) {
- facet_update_time(ofproto, facet, stats->used);
- facet->packet_count += stats->n_packets;
- facet->byte_count += stats->n_bytes;
- facet_push_stats(ofproto, facet);
- netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
- }
-}
-
-static void
-facet_push_stats(struct ofproto *ofproto, struct facet *facet)
-{
- uint64_t rs_packets, rs_bytes;
-
- assert(facet->packet_count >= facet->rs_packet_count);
- assert(facet->byte_count >= facet->rs_byte_count);
- assert(facet->used >= facet->rs_used);
-
- rs_packets = facet->packet_count - facet->rs_packet_count;
- rs_bytes = facet->byte_count - facet->rs_byte_count;
-
- if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
- facet->rs_packet_count = facet->packet_count;
- facet->rs_byte_count = facet->byte_count;
- facet->rs_used = facet->used;
-
- flow_push_stats(ofproto, facet->rule, &facet->flow,
- rs_packets, rs_bytes, facet->used);
- }
-}
-
-struct ofproto_push {
- struct action_xlate_ctx ctx;
- uint64_t packets;
- uint64_t bytes;
- long long int used;
-};
-
-static void
-push_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
-{
- struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
-
- if (rule) {
- rule->packet_count += push->packets;
- rule->byte_count += push->bytes;
- rule->used = MAX(push->used, rule->used);
- }
-}
-
-/* Pushes flow statistics to the rules which 'flow' resubmits into given
- * 'rule''s actions. */
-static void
-flow_push_stats(struct ofproto *ofproto, const struct rule *rule,
- struct flow *flow, uint64_t packets, uint64_t bytes,
- long long int used)
-{
- struct ofproto_push push;
-
- push.packets = packets;
- push.bytes = bytes;
- push.used = used;
-
- action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL);
- push.ctx.resubmit_hook = push_resubmit;
- ofpbuf_delete(xlate_actions(&push.ctx, rule->actions, rule->n_actions));
-}
-
/* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT
* in which no matching flow already exists in the flow table.
*
struct ofpbuf *packet;
struct rule *rule;
uint16_t in_port;
+ int buf_err;
int error;
- if (fm->flags & OFPFF_CHECK_OVERLAP
- && classifier_rule_overlaps(&p->cls, &fm->cr)) {
- return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+ if (fm->flags & OFPFF_CHECK_OVERLAP) {
+ struct classifier *cls;
+
+ FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+ if (classifier_rule_overlaps(cls, &fm->cr)) {
+ return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+ }
+ }
}
- error = 0;
- if (fm->buffer_id != UINT32_MAX) {
- error = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id,
- &packet, &in_port);
- } else {
- packet = NULL;
- in_port = UINT16_MAX;
+ buf_err = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id, &packet, &in_port);
+ error = rule_create(p, &fm->cr, fm->actions, fm->n_actions,
+ fm->idle_timeout, fm->hard_timeout, fm->cookie,
+ fm->flags & OFPFF_SEND_FLOW_REM, &rule);
+ if (error) {
+ ofpbuf_delete(packet);
+ return error;
}
- rule = rule_create(&fm->cr, fm->actions, fm->n_actions,
- fm->idle_timeout, fm->hard_timeout, fm->cookie,
- fm->flags & OFPFF_SEND_FLOW_REM);
- rule_insert(p, rule);
if (packet) {
- rule_execute(p, rule, in_port, packet);
+ assert(!buf_err);
+ return rule_execute(rule, in_port, packet);
}
- return error;
+ return buf_err;
}
-static struct rule *
-find_flow_strict(struct ofproto *p, const struct flow_mod *fm)
+/* Searches 'p' for an exact match for 'fm', in the table or tables indicated
+ * by fm->table_id. Returns 0 if no match was found, 1 if exactly one match
+ * was found, 2 if more than one match was found. If exactly one match is
+ * found, sets '*rulep' to the match, otherwise to NULL.
+ *
+ * This implements the rules for "strict" matching explained in the comment on
+ * struct nxt_flow_mod_table_id in nicira-ext.h.
+ *
+ * Ignores hidden rules. */
+static int
+find_flow_strict(struct ofproto *p, const struct flow_mod *fm,
+ struct rule **rulep)
{
- return rule_from_cls_rule(classifier_find_rule_exactly(&p->cls, &fm->cr));
+ struct classifier *cls;
+
+ *rulep = NULL;
+ FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+ struct rule *rule;
+
+ rule = rule_from_cls_rule(classifier_find_rule_exactly(cls, &fm->cr));
+ if (rule && !rule_is_hidden(rule)) {
+ if (*rulep) {
+ *rulep = NULL;
+ return 2;
+ }
+ *rulep = rule;
+ }
+ }
+ return *rulep != NULL;
}
static int
send_buffered_packet(struct ofconn *ofconn,
struct rule *rule, uint32_t buffer_id)
{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofpbuf *packet;
uint16_t in_port;
int error;
return error;
}
- rule_execute(ofproto, rule, in_port, packet);
-
- return 0;
+ return rule_execute(rule, in_port, packet);
}
\f
/* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
struct rule *match;
};
-static int modify_flow(struct ofproto *, const struct flow_mod *,
- struct rule *);
+static int modify_flow(const struct flow_mod *, struct rule *);
/* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code as
* encoded by ofp_mkerr() on failure.
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct rule *match = NULL;
- struct cls_cursor cursor;
- struct rule *rule;
+ struct classifier *cls;
+ int error;
+
+ error = 0;
+ FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+ struct cls_cursor cursor;
+ struct rule *rule;
- cls_cursor_init(&cursor, &p->cls, &fm->cr);
- CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
- if (!rule_is_hidden(rule)) {
- match = rule;
- modify_flow(p, fm, rule);
+ cls_cursor_init(&cursor, cls, &fm->cr);
+ CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+ if (!rule_is_hidden(rule)) {
+ int retval = modify_flow(fm, rule);
+ if (!retval) {
+ match = rule;
+ } else {
+ error = retval;
+ }
+ }
}
}
- if (match) {
+ if (error) {
+ return error;
+ } else if (match) {
/* This credits the packet to whichever flow happened to match last.
* That's weird. Maybe we should do a lookup for the flow that
* actually matches the packet? Who knows. */
modify_flow_strict(struct ofconn *ofconn, struct flow_mod *fm)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
- struct rule *rule = find_flow_strict(p, fm);
- if (rule && !rule_is_hidden(rule)) {
- modify_flow(p, fm, rule);
- return send_buffered_packet(ofconn, rule, fm->buffer_id);
- } else {
+ struct rule *rule;
+ int error;
+
+ switch (find_flow_strict(p, fm, &rule)) {
+ case 0:
return add_flow(ofconn, fm);
+
+ case 1:
+ error = modify_flow(fm, rule);
+ if (!error) {
+ error = send_buffered_packet(ofconn, rule, fm->buffer_id);
+ }
+ return error;
+
+ case 2:
+ return 0;
+
+ default:
+ NOT_REACHED();
}
}
/* Implements core of OFPFC_MODIFY and OFPFC_MODIFY_STRICT where 'rule' has
- * been identified as a flow in 'p''s flow table to be modified, by changing
- * the rule's actions to match those in 'ofm' (which is followed by 'n_actions'
- * ofp_action[] structures). */
+ * been identified as a flow to be modified, by changing the rule's actions to
+ * match those in 'ofm' (which is followed by 'n_actions' ofp_action[]
+ * structures). */
static int
-modify_flow(struct ofproto *p, const struct flow_mod *fm, struct rule *rule)
+modify_flow(const struct flow_mod *fm, struct rule *rule)
{
size_t actions_len = fm->n_actions * sizeof *rule->actions;
+ int error;
- rule->flow_cookie = fm->cookie;
-
- /* If the actions are the same, do nothing. */
if (fm->n_actions == rule->n_actions
&& (!fm->n_actions
|| !memcmp(fm->actions, rule->actions, actions_len))) {
- return 0;
+ error = 0;
+ } else {
+ error = rule->ofproto->ofproto_class->rule_modify_actions(
+ rule, fm->actions, fm->n_actions);
+ if (!error) {
+ free(rule->actions);
+ rule->actions = (fm->n_actions
+ ? xmemdup(fm->actions, actions_len)
+ : NULL);
+ rule->n_actions = fm->n_actions;
+ }
}
- /* Replace actions. */
- free(rule->actions);
- rule->actions = fm->n_actions ? xmemdup(fm->actions, actions_len) : NULL;
- rule->n_actions = fm->n_actions;
-
- p->need_revalidate = true;
+ if (!error) {
+ rule->flow_cookie = fm->cookie;
+ }
- return 0;
+ return error;
}
\f
/* OFPFC_DELETE implementation. */
-static void delete_flow(struct ofproto *, struct rule *, ovs_be16 out_port);
+static void delete_flow(struct rule *, ovs_be16 out_port);
/* Implements OFPFC_DELETE. */
static void
delete_flows_loose(struct ofproto *p, const struct flow_mod *fm)
{
- struct rule *rule, *next_rule;
- struct cls_cursor cursor;
+ struct classifier *cls;
+
+ FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+ struct rule *rule, *next_rule;
+ struct cls_cursor cursor;
- cls_cursor_init(&cursor, &p->cls, &fm->cr);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
- delete_flow(p, rule, htons(fm->out_port));
+ cls_cursor_init(&cursor, cls, &fm->cr);
+ CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+ delete_flow(rule, htons(fm->out_port));
+ }
}
}
static void
delete_flow_strict(struct ofproto *p, struct flow_mod *fm)
{
- struct rule *rule = find_flow_strict(p, fm);
- if (rule) {
- delete_flow(p, rule, htons(fm->out_port));
+ struct rule *rule;
+ if (find_flow_strict(p, fm, &rule) == 1) {
+ delete_flow(rule, htons(fm->out_port));
}
}
* 'out_port' is htons(OFPP_NONE) or if 'rule' actually outputs to the
* specified 'out_port'. */
static void
-delete_flow(struct ofproto *p, struct rule *rule, ovs_be16 out_port)
+delete_flow(struct rule *rule, ovs_be16 out_port)
{
if (rule_is_hidden(rule)) {
return;
return;
}
- rule_send_removed(p, rule, OFPRR_DELETE);
- rule_remove(p, rule);
+ ofproto_rule_send_removed(rule, OFPRR_DELETE);
+ ofproto_rule_destroy(rule);
+}
+
+static void
+ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
+{
+ struct ofputil_flow_removed fr;
+
+ if (rule_is_hidden(rule) || !rule->send_flow_removed) {
+ return;
+ }
+
+ fr.rule = rule->cr;
+ fr.cookie = rule->flow_cookie;
+ fr.reason = reason;
+ calc_flow_duration__(rule->created, &fr.duration_sec, &fr.duration_nsec);
+ fr.idle_timeout = rule->idle_timeout;
+ rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
+ &fr.byte_count);
+
+ connmgr_send_flow_removed(rule->ofproto->connmgr, &fr);
+}
+
+/* Sends an OpenFlow "flow removed" message with the given 'reason' (either
+ * OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its
+ * ofproto.
+ *
+ * ofproto implementation ->run() functions should use this function to expire
+ * OpenFlow flows. */
+void
+ofproto_rule_expire(struct rule *rule, uint8_t reason)
+{
+ assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT);
+ ofproto_rule_send_removed(rule, reason);
+ ofproto_rule_destroy(rule);
}
\f
static int
return error;
}
- error = ofputil_decode_flow_mod(&fm, oh);
+ error = ofputil_decode_flow_mod(&fm, oh,
+ ofconn_get_flow_mod_table_id(ofconn));
if (error) {
return error;
}
return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
}
- error = validate_actions(fm.actions, fm.n_actions,
- &fm.cr.flow, p->max_ports);
- if (error) {
- return error;
- }
-
switch (fm.command) {
case OFPFC_ADD:
return add_flow(ofconn, &fm);
return 0;
default:
+ if (fm.command > 0xff) {
+ VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
+ "flow_mod_table_id extension is not enabled");
+ }
return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
}
}
return 0;
}
+static int
+handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
+ const struct ofp_header *oh)
+{
+ const struct nxt_flow_mod_table_id *msg
+ = (const struct nxt_flow_mod_table_id *) oh;
+
+ ofconn_set_flow_mod_table_id(ofconn, msg->set != 0);
+ return 0;
+}
+
static int
handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
{
case OFPUTIL_NXT_ROLE_REQUEST:
return handle_role_request(ofconn, oh);
+ case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
+ return handle_nxt_flow_mod_table_id(ofconn, oh);
+
case OFPUTIL_NXT_SET_FLOW_FORMAT:
return handle_nxt_set_flow_format(ofconn, oh);
COVERAGE_INC(ofproto_recv_openflow);
}
\f
-static void
-handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall)
-{
- struct facet *facet;
- struct flow flow;
-
- /* Obtain in_port and tun_id, at least. */
- odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
-
- /* Set header pointers in 'flow'. */
- flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
-
- if (cfm_should_process_flow(&flow)) {
- ofproto_process_cfm(p, &flow, upcall->packet);
- ofpbuf_delete(upcall->packet);
- return;
- } else if (p->ofhooks->special_cb
- && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) {
- ofpbuf_delete(upcall->packet);
- return;
- }
-
- /* Check with in-band control to see if this packet should be sent
- * to the local port regardless of the flow table. */
- if (connmgr_msg_in_hook(p->connmgr, &flow, upcall->packet)) {
- ofproto_send_packet(p, ODPP_LOCAL, upcall->packet);
- }
-
- facet = facet_lookup_valid(p, &flow);
- if (!facet) {
- struct rule *rule = rule_lookup(p, &flow);
- if (!rule) {
- /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
- struct ofport *port = get_port(p, flow.in_port);
- if (port) {
- if (port->opp.config & htonl(OFPPC_NO_PACKET_IN)) {
- COVERAGE_INC(ofproto_no_packet_in);
- /* XXX install 'drop' flow entry */
- ofpbuf_delete(upcall->packet);
- return;
- }
- } else {
- VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16,
- flow.in_port);
- }
-
- COVERAGE_INC(ofproto_packet_in);
- send_packet_in(p, upcall, &flow, false);
- return;
- }
-
- facet = facet_create(p, rule, &flow, upcall->packet);
- } else if (!facet->may_install) {
- /* The facet is not installable, that is, we need to process every
- * packet, so process the current packet's actions into 'facet'. */
- facet_make_actions(p, facet, upcall->packet);
- }
-
- if (facet->rule->cr.priority == FAIL_OPEN_PRIORITY) {
- /*
- * Extra-special case for fail-open mode.
- *
- * We are in fail-open mode and the packet matched the fail-open rule,
- * but we are connected to a controller too. We should send the packet
- * up to the controller in the hope that it will try to set up a flow
- * and thereby allow us to exit fail-open.
- *
- * See the top-level comment in fail-open.c for more information.
- */
- send_packet_in(p, upcall, &flow, true);
- }
-
- facet_execute(p, facet, upcall->packet);
- facet_install(p, facet, false);
-}
-
-static void
-handle_upcall(struct ofproto *p, struct dpif_upcall *upcall)
-{
- struct flow flow;
-
- switch (upcall->type) {
- case DPIF_UC_ACTION:
- COVERAGE_INC(ofproto_ctlr_action);
- odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
- send_packet_in(p, upcall, &flow, false);
- break;
-
- case DPIF_UC_SAMPLE:
- if (p->sflow) {
- odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
- ofproto_sflow_received(p->sflow, upcall, &flow);
- }
- ofpbuf_delete(upcall->packet);
- break;
-
- case DPIF_UC_MISS:
- handle_miss_upcall(p, upcall);
- break;
-
- case DPIF_N_UC_TYPES:
- default:
- VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
- break;
- }
-}
-\f
-/* Flow expiration. */
-
-static int ofproto_dp_max_idle(const struct ofproto *);
-static void ofproto_update_stats(struct ofproto *);
-static void rule_expire(struct ofproto *, struct rule *);
-static void ofproto_expire_facets(struct ofproto *, int dp_max_idle);
-
-/* This function is called periodically by ofproto_run(). Its job is to
- * collect updates for the flows that have been installed into the datapath,
- * most importantly when they last were used, and then use that information to
- * expire flows that have not been used recently.
- *
- * Returns the number of milliseconds after which it should be called again. */
-static int
-ofproto_expire(struct ofproto *ofproto)
-{
- struct rule *rule, *next_rule;
- struct cls_cursor cursor;
- int dp_max_idle;
-
- /* Update stats for each flow in the datapath. */
- ofproto_update_stats(ofproto);
-
- /* Expire facets that have been idle too long. */
- dp_max_idle = ofproto_dp_max_idle(ofproto);
- ofproto_expire_facets(ofproto, dp_max_idle);
-
- /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
- cls_cursor_init(&cursor, &ofproto->cls, NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
- rule_expire(ofproto, rule);
- }
-
- /* Let the hook know that we're at a stable point: all outstanding data
- * in existing flows has been accounted to the account_cb. Thus, the
- * hook can now reasonably do operations that depend on having accurate
- * flow volume accounting (currently, that's just bond rebalancing). */
- if (ofproto->ofhooks->account_checkpoint_cb) {
- ofproto->ofhooks->account_checkpoint_cb(ofproto->aux);
- }
-
- return MIN(dp_max_idle, 1000);
-}
-
-/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
- *
- * This function also pushes statistics updates to rules which each facet
- * resubmits into. Generally these statistics will be accurate. However, if a
- * facet changes the rule it resubmits into at some time in between
- * ofproto_update_stats() runs, it is possible that statistics accrued to the
- * old rule will be incorrectly attributed to the new rule. This could be
- * avoided by calling ofproto_update_stats() whenever rules are created or
- * deleted. However, the performance impact of making so many calls to the
- * datapath do not justify the benefit of having perfectly accurate statistics.
- */
-static void
-ofproto_update_stats(struct ofproto *p)
-{
- const struct dpif_flow_stats *stats;
- struct dpif_flow_dump dump;
- const struct nlattr *key;
- size_t key_len;
-
- dpif_flow_dump_start(&dump, p->dpif);
- while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
- struct facet *facet;
- struct flow flow;
-
- if (odp_flow_key_to_flow(key, key_len, &flow)) {
- struct ds s;
-
- ds_init(&s);
- odp_flow_key_format(key, key_len, &s);
- VLOG_WARN_RL(&rl, "failed to convert ODP flow key to flow: %s",
- ds_cstr(&s));
- ds_destroy(&s);
-
- continue;
- }
- facet = facet_find(p, &flow);
-
- if (facet && facet->installed) {
-
- if (stats->n_packets >= facet->dp_packet_count) {
- facet->packet_count += stats->n_packets - facet->dp_packet_count;
- } else {
- VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
- }
-
- if (stats->n_bytes >= facet->dp_byte_count) {
- facet->byte_count += stats->n_bytes - facet->dp_byte_count;
- } else {
- VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
- }
-
- facet->dp_packet_count = stats->n_packets;
- facet->dp_byte_count = stats->n_bytes;
-
- facet_update_time(p, facet, stats->used);
- facet_account(p, facet, stats->n_bytes);
- facet_push_stats(p, facet);
- } else {
- /* There's a flow in the datapath that we know nothing about.
- * Delete it. */
- COVERAGE_INC(ofproto_unexpected_rule);
- dpif_flow_del(p->dpif, key, key_len, NULL);
- }
- }
- dpif_flow_dump_done(&dump);
-}
-
-/* Calculates and returns the number of milliseconds of idle time after which
- * facets should expire from the datapath and we should fold their statistics
- * into their parent rules in userspace. */
-static int
-ofproto_dp_max_idle(const struct ofproto *ofproto)
-{
- /*
- * Idle time histogram.
- *
- * Most of the time a switch has a relatively small number of facets. When
- * this is the case we might as well keep statistics for all of them in
- * userspace and to cache them in the kernel datapath for performance as
- * well.
- *
- * As the number of facets increases, the memory required to maintain
- * statistics about them in userspace and in the kernel becomes
- * significant. However, with a large number of facets it is likely that
- * only a few of them are "heavy hitters" that consume a large amount of
- * bandwidth. At this point, only heavy hitters are worth caching in the
- * kernel and maintaining in userspaces; other facets we can discard.
- *
- * The technique used to compute the idle time is to build a histogram with
- * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each facet
- * that is installed in the kernel gets dropped in the appropriate bucket.
- * After the histogram has been built, we compute the cutoff so that only
- * the most-recently-used 1% of facets (but at least 1000 flows) are kept
- * cached. At least the most-recently-used bucket of facets is kept, so
- * actually an arbitrary number of facets can be kept in any given
- * expiration run (though the next run will delete most of those unless
- * they receive additional data).
- *
- * This requires a second pass through the facets, in addition to the pass
- * made by ofproto_update_stats(), because the former function never looks
- * at uninstallable facets.
- */
- enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) };
- enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
- int buckets[N_BUCKETS] = { 0 };
- struct facet *facet;
- int total, bucket;
- long long int now;
- int i;
-
- total = hmap_count(&ofproto->facets);
- if (total <= 1000) {
- return N_BUCKETS * BUCKET_WIDTH;
- }
-
- /* Build histogram. */
- now = time_msec();
- HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
- long long int idle = now - facet->used;
- int bucket = (idle <= 0 ? 0
- : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
- : (unsigned int) idle / BUCKET_WIDTH);
- buckets[bucket]++;
- }
-
- /* Find the first bucket whose flows should be expired. */
- for (bucket = 0; bucket < N_BUCKETS; bucket++) {
- if (buckets[bucket]) {
- int subtotal = 0;
- do {
- subtotal += buckets[bucket++];
- } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100));
- break;
- }
- }
-
- if (VLOG_IS_DBG_ENABLED()) {
- struct ds s;
-
- ds_init(&s);
- ds_put_cstr(&s, "keep");
- for (i = 0; i < N_BUCKETS; i++) {
- if (i == bucket) {
- ds_put_cstr(&s, ", drop");
- }
- if (buckets[i]) {
- ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
- }
- }
- VLOG_INFO("%s: %s (msec:count)",
- dpif_name(ofproto->dpif), ds_cstr(&s));
- ds_destroy(&s);
- }
-
- return bucket * BUCKET_WIDTH;
-}
-
-static void
-facet_active_timeout(struct ofproto *ofproto, struct facet *facet)
-{
- if (ofproto->netflow && !facet_is_controller_flow(facet) &&
- netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) {
- struct ofexpired expired;
-
- if (facet->installed) {
- struct dpif_flow_stats stats;
-
- facet_put__(ofproto, facet, facet->actions, facet->actions_len,
- &stats);
- facet_update_stats(ofproto, facet, &stats);
- }
-
- expired.flow = facet->flow;
- expired.packet_count = facet->packet_count;
- expired.byte_count = facet->byte_count;
- expired.used = facet->used;
- netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
- }
-}
-
-static void
-ofproto_expire_facets(struct ofproto *ofproto, int dp_max_idle)
-{
- long long int cutoff = time_msec() - dp_max_idle;
- struct facet *facet, *next_facet;
-
- HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
- facet_active_timeout(ofproto, facet);
- if (facet->used < cutoff) {
- facet_remove(ofproto, facet);
- }
- }
-}
-
-/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
- * then delete it entirely. */
-static void
-rule_expire(struct ofproto *ofproto, struct rule *rule)
-{
- struct facet *facet, *next_facet;
- long long int now;
- uint8_t reason;
-
- /* Has 'rule' expired? */
- now = time_msec();
- if (rule->hard_timeout
- && now > rule->created + rule->hard_timeout * 1000) {
- reason = OFPRR_HARD_TIMEOUT;
- } else if (rule->idle_timeout && list_is_empty(&rule->facets)
- && now >rule->used + rule->idle_timeout * 1000) {
- reason = OFPRR_IDLE_TIMEOUT;
- } else {
- return;
- }
-
- COVERAGE_INC(ofproto_expired);
-
- /* Update stats. (This is a no-op if the rule expired due to an idle
- * timeout, because that only happens when the rule has no facets left.) */
- LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
- facet_remove(ofproto, facet);
- }
-
- /* Get rid of the rule. */
- if (!rule_is_hidden(rule)) {
- rule_send_removed(ofproto, rule, reason);
- }
- rule_remove(ofproto, rule);
-}
-\f
-static void
-rule_send_removed(struct ofproto *p, struct rule *rule, uint8_t reason)
-{
- struct ofputil_flow_removed fr;
-
- if (!rule->send_flow_removed) {
- return;
- }
-
- fr.rule = rule->cr;
- fr.cookie = rule->flow_cookie;
- fr.reason = reason;
- calc_flow_duration__(rule->created, &fr.duration_sec, &fr.duration_nsec);
- fr.idle_timeout = rule->idle_timeout;
- fr.packet_count = rule->packet_count;
- fr.byte_count = rule->byte_count;
-
- connmgr_send_flow_removed(p->connmgr, &fr);
-}
-
-/* Obtains statistics for 'rule' and stores them in '*packets' and '*bytes'.
- * The returned statistics include statistics for all of 'rule''s facets. */
-static void
-rule_get_stats(const struct rule *rule, uint64_t *packets, uint64_t *bytes)
-{
- uint64_t p, b;
- struct facet *facet;
-
- /* Start from historical data for 'rule' itself that are no longer tracked
- * in facets. This counts, for example, facets that have expired. */
- p = rule->packet_count;
- b = rule->byte_count;
-
- /* Add any statistics that are tracked by facets. This includes
- * statistical data recently updated by ofproto_update_stats() as well as
- * stats for packets that were executed "by hand" via dpif_execute(). */
- LIST_FOR_EACH (facet, list_node, &rule->facets) {
- p += facet->packet_count;
- b += facet->byte_count;
- }
-
- *packets = p;
- *bytes = b;
-}
-
-/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an
- * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
- * their individual configurations.
- *
- * If 'clone' is true, the caller retains ownership of 'upcall->packet'.
- * Otherwise, ownership is transferred to this function. */
-static void
-send_packet_in(struct ofproto *ofproto, struct dpif_upcall *upcall,
- const struct flow *flow, bool clone)
-{
- connmgr_send_packet_in(ofproto->connmgr, upcall, flow,
- clone ? NULL : upcall->packet);
-}
-
static uint64_t
pick_datapath_id(const struct ofproto *ofproto)
{
const struct ofport *port;
- port = get_port(ofproto, ODPP_LOCAL);
+ port = ofproto_get_port(ofproto, OFPP_LOCAL);
if (port) {
uint8_t ea[ETH_ADDR_LEN];
int error;
return eth_addr_to_uint64(ea);
}
\f
-static void
-ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED,
- void *aux OVS_UNUSED)
-{
- const struct shash_node *node;
- struct ds results;
-
- ds_init(&results);
- SHASH_FOR_EACH (node, &all_ofprotos) {
- ds_put_format(&results, "%s\n", node->name);
- }
- unixctl_command_reply(conn, 200, ds_cstr(&results));
- ds_destroy(&results);
-}
-
-struct ofproto_trace {
- struct action_xlate_ctx ctx;
- struct flow flow;
- struct ds *result;
-};
+/* unixctl commands. */
-static void
-trace_format_rule(struct ds *result, int level, const struct rule *rule)
+struct ofproto *
+ofproto_lookup(const char *name)
{
- ds_put_char_multiple(result, '\t', level);
- if (!rule) {
- ds_put_cstr(result, "No match\n");
- return;
- }
-
- ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
- ntohll(rule->flow_cookie));
- cls_rule_format(&rule->cr, result);
- ds_put_char(result, '\n');
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_cstr(result, "OpenFlow ");
- ofp_print_actions(result, (const struct ofp_action_header *) rule->actions,
- rule->n_actions * sizeof *rule->actions);
- ds_put_char(result, '\n');
-}
+ struct ofproto *ofproto;
-static void
-trace_format_flow(struct ds *result, int level, const char *title,
- struct ofproto_trace *trace)
-{
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- if (flow_equal(&trace->ctx.flow, &trace->flow)) {
- ds_put_cstr(result, "unchanged");
- } else {
- flow_format(result, &trace->ctx.flow);
- trace->flow = trace->ctx.flow;
+ HMAP_FOR_EACH_WITH_HASH (ofproto, hmap_node, hash_string(name, 0),
+ &all_ofprotos) {
+ if (!strcmp(ofproto->name, name)) {
+ return ofproto;
+ }
}
- ds_put_char(result, '\n');
-}
-
-static void
-trace_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
-{
- struct ofproto_trace *trace = CONTAINER_OF(ctx, struct ofproto_trace, ctx);
- struct ds *result = trace->result;
-
- ds_put_char(result, '\n');
- trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
- trace_format_rule(result, ctx->recurse + 1, rule);
+ return NULL;
}
static void
-ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
- void *aux OVS_UNUSED)
+ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED,
+ void *aux OVS_UNUSED)
{
- char *dpname, *in_port_s, *tun_id_s, *packet_s;
- char *args = xstrdup(args_);
- char *save_ptr = NULL;
struct ofproto *ofproto;
- struct ofpbuf packet;
- struct rule *rule;
- struct ds result;
- struct flow flow;
- uint16_t in_port;
- ovs_be64 tun_id;
- char *s;
-
- ofpbuf_init(&packet, strlen(args) / 2);
- ds_init(&result);
-
- dpname = strtok_r(args, " ", &save_ptr);
- tun_id_s = strtok_r(NULL, " ", &save_ptr);
- in_port_s = strtok_r(NULL, " ", &save_ptr);
- packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
- if (!dpname || !in_port_s || !packet_s) {
- unixctl_command_reply(conn, 501, "Bad command syntax");
- goto exit;
- }
-
- ofproto = shash_find_data(&all_ofprotos, dpname);
- if (!ofproto) {
- unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list "
- "for help)");
- goto exit;
- }
-
- tun_id = htonll(strtoull(tun_id_s, NULL, 0));
- in_port = ofp_port_to_odp_port(atoi(in_port_s));
-
- packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
- packet_s += strspn(packet_s, " ");
- if (*packet_s != '\0') {
- unixctl_command_reply(conn, 501, "Trailing garbage in command");
- goto exit;
- }
- if (packet.size < ETH_HEADER_LEN) {
- unixctl_command_reply(conn, 501, "Packet data too short for Ethernet");
- goto exit;
- }
-
- ds_put_cstr(&result, "Packet: ");
- s = ofp_packet_to_string(packet.data, packet.size, packet.size);
- ds_put_cstr(&result, s);
- free(s);
-
- flow_extract(&packet, tun_id, in_port, &flow);
- ds_put_cstr(&result, "Flow: ");
- flow_format(&result, &flow);
- ds_put_char(&result, '\n');
+ struct ds results;
- rule = rule_lookup(ofproto, &flow);
- trace_format_rule(&result, 0, rule);
- if (rule) {
- struct ofproto_trace trace;
- struct ofpbuf *odp_actions;
-
- trace.result = &result;
- trace.flow = flow;
- action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet);
- trace.ctx.resubmit_hook = trace_resubmit;
- odp_actions = xlate_actions(&trace.ctx,
- rule->actions, rule->n_actions);
-
- ds_put_char(&result, '\n');
- trace_format_flow(&result, 0, "Final flow", &trace);
- ds_put_cstr(&result, "Datapath actions: ");
- format_odp_actions(&result, odp_actions->data, odp_actions->size);
- ofpbuf_delete(odp_actions);
+ ds_init(&results);
+ HMAP_FOR_EACH (ofproto, hmap_node, &all_ofprotos) {
+ ds_put_format(&results, "%s\n", ofproto->name);
}
-
- unixctl_command_reply(conn, 200, ds_cstr(&result));
-
-exit:
- ds_destroy(&result);
- ofpbuf_uninit(&packet);
- free(args);
+ unixctl_command_reply(conn, 200, ds_cstr(&results));
+ ds_destroy(&results);
}
static void
registered = true;
unixctl_command_register("ofproto/list", ofproto_unixctl_list, NULL);
- unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL);
-}
-\f
-static bool
-default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
- struct ofpbuf *odp_actions, tag_type *tags,
- uint16_t *nf_output_iface, void *ofproto_)
-{
- struct ofproto *ofproto = ofproto_;
- struct mac_entry *dst_mac;
-
- /* Drop frames for reserved multicast addresses. */
- if (eth_addr_is_reserved(flow->dl_dst)) {
- return true;
- }
-
- /* Learn source MAC (but don't try to learn from revalidation). */
- if (packet != NULL
- && mac_learning_may_learn(ofproto->ml, flow->dl_src, 0)) {
- struct mac_entry *src_mac;
-
- src_mac = mac_learning_insert(ofproto->ml, flow->dl_src, 0);
- if (mac_entry_is_new(src_mac) || src_mac->port.i != flow->in_port) {
- /* The log messages here could actually be useful in debugging,
- * so keep the rate limit relatively high. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
- VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
- ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
-
- ofproto_revalidate(ofproto,
- mac_learning_changed(ofproto->ml, src_mac));
- src_mac->port.i = flow->in_port;
- }
- }
-
- /* Determine output port. */
- dst_mac = mac_learning_lookup(ofproto->ml, flow->dl_dst, 0, tags);
- if (!dst_mac) {
- flood_packets(ofproto, flow->in_port, htonl(OFPPC_NO_FLOOD),
- nf_output_iface, odp_actions);
- } else {
- int out_port = dst_mac->port.i;
- if (out_port != flow->in_port) {
- nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, out_port);
- *nf_output_iface = out_port;
- } else {
- /* Drop. */
- }
- }
-
- return true;
}
-
-static const struct ofhooks default_ofhooks = {
- default_normal_ofhook_cb,
- NULL,
- NULL,
- NULL,
- NULL
-};
extern "C" {
#endif
+struct cfm;
struct cls_rule;
-struct nlattr;
-struct ofhooks;
+struct netdev;
struct ofproto;
struct shash;
#define DEFAULT_SERIAL_DESC "None"
#define DEFAULT_DP_DESC "None"
+void ofproto_enumerate_types(struct sset *types);
+const char *ofproto_normalize_type(const char *);
+
+int ofproto_enumerate_names(const char *type, struct sset *names);
+void ofproto_parse_name(const char *name, char **dp_name, char **dp_type);
+
int ofproto_create(const char *datapath, const char *datapath_type,
- const struct ofhooks *, void *aux,
struct ofproto **ofprotop);
void ofproto_destroy(struct ofproto *);
+int ofproto_delete(const char *name, const char *type);
+
int ofproto_run(struct ofproto *);
-int ofproto_run1(struct ofproto *);
-int ofproto_run2(struct ofproto *, bool revalidate_all);
void ofproto_wait(struct ofproto *);
bool ofproto_is_alive(const struct ofproto *);
-int ofproto_port_del(struct ofproto *, uint16_t odp_port);
-bool ofproto_port_is_floodable(struct ofproto *, uint16_t odp_port);
+/* A port within an OpenFlow switch.
+ *
+ * 'name' and 'type' are suitable for passing to netdev_open(). */
+struct ofproto_port {
+ char *name; /* Network device name, e.g. "eth0". */
+ char *type; /* Network device type, e.g. "system". */
+ uint16_t ofp_port; /* OpenFlow port number. */
+};
+void ofproto_port_clone(struct ofproto_port *, const struct ofproto_port *);
+void ofproto_port_destroy(struct ofproto_port *);
+
+struct ofproto_port_dump {
+ const struct ofproto *ofproto;
+ int error;
+ void *state;
+};
+void ofproto_port_dump_start(struct ofproto_port_dump *,
+ const struct ofproto *);
+bool ofproto_port_dump_next(struct ofproto_port_dump *, struct ofproto_port *);
+int ofproto_port_dump_done(struct ofproto_port_dump *);
+
+/* Iterates through each OFPROTO_PORT in OFPROTO, using DUMP as state.
+ *
+ * Arguments all have pointer type.
+ *
+ * If you break out of the loop, then you need to free the dump structure by
+ * hand using ofproto_port_dump_done(). */
+#define OFPROTO_PORT_FOR_EACH(OFPROTO_PORT, DUMP, OFPROTO) \
+ for (ofproto_port_dump_start(DUMP, OFPROTO); \
+ (ofproto_port_dump_next(DUMP, OFPROTO_PORT) \
+ ? true \
+ : (ofproto_port_dump_done(DUMP), false)); \
+ )
+
+int ofproto_port_add(struct ofproto *, struct netdev *, uint16_t *ofp_portp);
+int ofproto_port_del(struct ofproto *, uint16_t ofp_port);
+
+int ofproto_port_query_by_name(const struct ofproto *, const char *devname,
+ struct ofproto_port *);
/* Top-level configuration. */
void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
int ofproto_set_snoops(struct ofproto *, const struct sset *snoops);
int ofproto_set_netflow(struct ofproto *,
const struct netflow_options *nf_options);
-void ofproto_set_sflow(struct ofproto *, const struct ofproto_sflow_options *);
+int ofproto_set_sflow(struct ofproto *, const struct ofproto_sflow_options *);
-/* Configuration of individual interfaces. */
-struct cfm;
+/* Configuration of ports. */
+
+void ofproto_port_unregister(struct ofproto *, uint16_t ofp_port);
+
+void ofproto_port_clear_cfm(struct ofproto *, uint16_t ofp_port);
+void ofproto_port_set_cfm(struct ofproto *, uint16_t ofp_port,
+ const struct cfm *,
+ const uint16_t *remote_mps, size_t n_remote_mps);
+const struct cfm *ofproto_port_get_cfm(struct ofproto *, uint16_t ofp_port);
+int ofproto_port_is_lacp_current(struct ofproto *, uint16_t ofp_port);
-void ofproto_iface_clear_cfm(struct ofproto *, uint32_t port_no);
-void ofproto_iface_set_cfm(struct ofproto *, uint32_t port_no,
- const struct cfm *,
- const uint16_t *remote_mps, size_t n_remote_mps);
-const struct cfm *ofproto_iface_get_cfm(struct ofproto *, uint32_t port_no);
+/* Configuration of bundles. */
+struct ofproto_bundle_settings {
+ char *name; /* For use in log messages. */
+
+ uint16_t *slaves; /* OpenFlow port numbers for slaves. */
+ size_t n_slaves;
+
+ int vlan; /* VLAN if access port, -1 if trunk port. */
+ unsigned long *trunks; /* vlan_bitmap, NULL to trunk all VLANs. */
+
+ struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
+ uint32_t *bond_stable_ids; /* Array of n_slaves elements. */
+
+ struct lacp_settings *lacp; /* Nonnull to enable LACP. */
+ struct lacp_slave_settings *lacp_slaves; /* Array of n_slaves elements. */
+};
+
+int ofproto_bundle_register(struct ofproto *, void *aux,
+ const struct ofproto_bundle_settings *);
+int ofproto_bundle_unregister(struct ofproto *, void *aux);
+
+/* Configuration of mirrors. */
+struct ofproto_mirror_settings {
+ /* Name for log messages. */
+ char *name;
+
+ /* Bundles that select packets for mirroring upon ingress. */
+ void **srcs; /* A set of registered ofbundle handles. */
+ size_t n_srcs;
+
+ /* Bundles that select packets for mirroring upon egress. */
+ void **dsts; /* A set of registered ofbundle handles. */
+ size_t n_dsts;
+
+ /* VLANs of packets to select for mirroring. */
+ unsigned long *src_vlans; /* vlan_bitmap, NULL selects all VLANs. */
+
+ /* Output (mutually exclusive). */
+ void *out_bundle; /* A registered ofbundle handle or NULL. */
+ uint16_t out_vlan; /* Output VLAN, only if out_bundle is NULL. */
+};
+
+int ofproto_mirror_register(struct ofproto *, void *aux,
+ const struct ofproto_mirror_settings *);
+int ofproto_mirror_unregister(struct ofproto *, void *aux);
+
+int ofproto_set_flood_vlans(struct ofproto *, unsigned long *flood_vlans);
+bool ofproto_is_mirror_output_bundle(struct ofproto *, void *aux);
/* Configuration querying. */
-uint64_t ofproto_get_datapath_id(const struct ofproto *);
-bool ofproto_has_primary_controller(const struct ofproto *);
-enum ofproto_fail_mode ofproto_get_fail_mode(const struct ofproto *);
-void ofproto_get_listeners(const struct ofproto *, struct sset *);
bool ofproto_has_snoops(const struct ofproto *);
void ofproto_get_snoops(const struct ofproto *, struct sset *);
void ofproto_get_all_flows(struct ofproto *p, struct ds *);
-
-/* Functions for use by ofproto implementation modules, not by clients. */
-int ofproto_send_packet(struct ofproto *, uint32_t port_no,
- const struct ofpbuf *);
-void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
- const union ofp_action *, size_t n_actions);
-void ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
-void ofproto_flush_flows(struct ofproto *);
-
-/* Hooks for ovs-vswitchd. */
-struct ofhooks {
- bool (*normal_cb)(const struct flow *, const struct ofpbuf *packet,
- struct ofpbuf *odp_actions, tag_type *,
- uint16_t *nf_output_iface, void *aux);
- bool (*special_cb)(const struct flow *flow, const struct ofpbuf *packet,
- void *aux);
- void (*account_flow_cb)(const struct flow *, tag_type tags,
- const struct nlattr *odp_actions,
- size_t actions_len,
- uint64_t n_bytes, void *aux);
- void (*account_checkpoint_cb)(void *aux);
-
- uint16_t (*autopath_cb)(const struct flow *, uint32_t id,
- tag_type *, void *aux);
-};
-void ofproto_revalidate(struct ofproto *, tag_type);
-struct tag_set *ofproto_get_revalidate_set(struct ofproto *);
+void ofproto_get_netflow_ids(const struct ofproto *,
+ uint8_t *engine_type, uint8_t *engine_id);
void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *);
void ofproto_free_ofproto_controller_info(struct shash *);
/* Otherwise queue it up for the periodic callback to drain out. */
struct pinqueue *q;
- /* We are called with a buffer obtained from dpif_recv() that has much
- * more allocated space than actual content most of the time. Since
- * we're going to store the packet for some time, free up that
+ /* We might be called with a buffer obtained from dpif_recv() that has
+ * much more allocated space than actual content most of the time.
+ * Since we're going to store the packet for some time, free up that
* otherwise wasted space. */
ofpbuf_trim(packet);
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
struct packet *p;
int error;
+ if (id == UINT32_MAX) {
+ error = 0;
+ goto error;
+ }
+
if (!pb) {
VLOG_WARN_RL(&rl, "attempt to send buffered packet via connection "
"without buffers");
"if the switch was recently in fail-open mode)", id);
error = 0;
}
+error:
*bufferp = NULL;
*in_port = UINT16_MAX;
return error;
--- /dev/null
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef OFPROTO_PRIVATE_H
+#define OFPROTO_PRIVATE_H 1
+
+/* Definitions for use within ofproto. */
+
+#include "ofproto/ofproto.h"
+#include "classifier.h"
+#include "list.h"
+#include "shash.h"
+#include "timeval.h"
+
+/* An OpenFlow switch.
+ *
+ * With few exceptions, ofproto implementations may look at these fields but
+ * should not modify them. */
+struct ofproto {
+ const struct ofproto_class *ofproto_class;
+ char *type; /* Datapath type. */
+ char *name; /* Datapath name. */
+ struct hmap_node hmap_node; /* In global 'all_ofprotos' hmap. */
+
+ /* Settings. */
+ uint64_t fallback_dpid; /* Datapath ID if no better choice found. */
+ uint64_t datapath_id; /* Datapath ID. */
+ char *mfr_desc; /* Manufacturer. */
+ char *hw_desc; /* Hardware. */
+ char *sw_desc; /* Software version. */
+ char *serial_desc; /* Serial number. */
+ char *dp_desc; /* Datapath description. */
+
+ /* Datapath. */
+ struct netdev_monitor *netdev_monitor;
+ struct hmap ports; /* Contains "struct ofport"s. */
+ struct shash port_by_name;
+
+ /* Flow tables. */
+ struct classifier *tables; /* Each classifier contains "struct rule"s. */
+ int n_tables;
+
+ /* OpenFlow connections. */
+ struct connmgr *connmgr;
+};
+
+struct ofproto *ofproto_lookup(const char *name);
+struct ofport *ofproto_get_port(const struct ofproto *, uint16_t ofp_port);
+
+/* An OpenFlow port within a "struct ofproto".
+ *
+ * With few exceptions, ofproto implementations may look at these fields but
+ * should not modify them. */
+struct ofport {
+ struct ofproto *ofproto; /* The ofproto that contains this port. */
+ struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */
+ struct netdev *netdev;
+ struct ofp_phy_port opp;
+ uint16_t ofp_port; /* OpenFlow port number. */
+};
+
+/* An OpenFlow flow within a "struct ofproto".
+ *
+ * With few exceptions, ofproto implementations may look at these fields but
+ * should not modify them. */
+struct rule {
+ struct ofproto *ofproto; /* The ofproto that contains this rule. */
+ struct cls_rule cr; /* In owning ofproto's classifier. */
+
+ ovs_be64 flow_cookie; /* Controller-issued identifier. */
+
+ long long int created; /* Creation time. */
+ uint16_t idle_timeout; /* In seconds from time of last use. */
+ uint16_t hard_timeout; /* In seconds from time of creation. */
+ uint8_t table_id; /* Index in ofproto's 'tables' array. */
+ bool send_flow_removed; /* Send a flow removed message? */
+
+ union ofp_action *actions; /* OpenFlow actions. */
+ int n_actions; /* Number of elements in actions[]. */
+};
+
+static inline struct rule *
+rule_from_cls_rule(const struct cls_rule *cls_rule)
+{
+ return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
+}
+
+void ofproto_rule_expire(struct rule *, uint8_t reason);
+void ofproto_rule_destroy(struct rule *);
+
+/* ofproto class structure, to be defined by each ofproto implementation.
+ *
+ *
+ * Data Structures
+ * ===============
+ *
+ * These functions work primarily with three different kinds of data
+ * structures:
+ *
+ * - "struct ofproto", which represents an OpenFlow switch.
+ *
+ * - "struct ofport", which represents a port within an ofproto.
+ *
+ * - "struct rule", which represents an OpenFlow flow within an ofproto.
+ *
+ * Each of these data structures contains all of the implementation-independent
+ * generic state for the respective concept, called the "base" state. None of
+ * them contains any extra space for ofproto implementations to use. Instead,
+ * each implementation is expected to declare its own data structure that
+ * contains an instance of the generic data structure plus additional
+ * implementation-specific members, called the "derived" state. The
+ * implementation can use casts or (preferably) the CONTAINER_OF macro to
+ * obtain access to derived state given only a pointer to the embedded generic
+ * data structure.
+ *
+ *
+ * Life Cycle
+ * ==========
+ *
+ * Four stylized functions accompany each of these data structures:
+ *
+ * "alloc" "construct" "destruct" "dealloc"
+ * ------------ ---------------- --------------- --------------
+ * ofproto ->alloc ->construct ->destruct ->dealloc
+ * ofport ->port_alloc ->port_construct ->port_destruct ->port_dealloc
+ * rule ->rule_alloc ->rule_construct ->rule_destruct ->rule_dealloc
+ *
+ * Any instance of a given data structure goes through the following life
+ * cycle:
+ *
+ * 1. The client calls the "alloc" function to obtain raw memory. If "alloc"
+ * fails, skip all the other steps.
+ *
+ * 2. The client initializes all of the data structure's base state. If this
+ * fails, skip to step 7.
+ *
+ * 3. The client calls the "construct" function. The implementation
+ * initializes derived state. It may refer to the already-initialized
+ * base state. If "construct" fails, skip to step 6.
+ *
+ * 4. The data structure is now initialized and in use.
+ *
+ * 5. When the data structure is no longer needed, the client calls the
+ * "destruct" function. The implementation uninitializes derived state.
+ * The base state has not been uninitialized yet, so the implementation
+ * may still refer to it.
+ *
+ * 6. The client uninitializes all of the data structure's base state.
+ *
+ * 7. The client calls the "dealloc" to free the raw memory. The
+ * implementation must not refer to base or derived state in the data
+ * structure, because it has already been uninitialized.
+ *
+ * Each "alloc" function allocates and returns a new instance of the respective
+ * data structure. The "alloc" function is not given any information about the
+ * use of the new data structure, so it cannot perform much initialization.
+ * Its purpose is just to ensure that the new data structure has enough room
+ * for base and derived state. It may return a null pointer if memory is not
+ * available, in which case none of the other functions is called.
+ *
+ * Each "construct" function initializes derived state in its respective data
+ * structure. When "construct" is called, all of the base state has already
+ * been initialized, so the "construct" function may refer to it. The
+ * "construct" function is allowed to fail, in which case the client calls the
+ * "dealloc" function (but not the "destruct" function).
+ *
+ * Each "destruct" function uninitializes and frees derived state in its
+ * respective data structure. When "destruct" is called, the base state has
+ * not yet been uninitialized, so the "destruct" function may refer to it. The
+ * "destruct" function is not allowed to fail.
+ *
+ * Each "dealloc" function frees raw memory that was allocated by the the
+ * "alloc" function. The memory's base and derived members might not have ever
+ * been initialized (but if "construct" returned successfully, then it has been
+ * "destruct"ed already). The "dealloc" function is not allowed to fail.
+ *
+ *
+ * Conventions
+ * ===========
+ *
+ * Most of these functions return 0 if they are successful or a positive error
+ * code on failure. Depending on the function, valid error codes are either
+ * errno values or OpenFlow error codes constructed with ofp_mkerr().
+ *
+ * Most of these functions are expected to execute synchronously, that is, to
+ * block as necessary to obtain a result. Thus, these functions may return
+ * EAGAIN (or EWOULDBLOCK or EINPROGRESS) only where the function descriptions
+ * explicitly say those errors are a possibility. We may relax this
+ * requirement in the future if and when we encounter performance problems. */
+struct ofproto_class {
+/* ## ----------------- ## */
+/* ## Factory Functions ## */
+/* ## ----------------- ## */
+
+ /* Enumerates the types of all support ofproto types into 'types'. The
+ * caller has already initialized 'types' and other ofproto classes might
+ * already have added names to it. */
+ void (*enumerate_types)(struct sset *types);
+
+ /* Enumerates the names of all existing datapath of the specified 'type'
+ * into 'names' 'all_dps'. The caller has already initialized 'names' as
+ * an empty sset.
+ *
+ * 'type' is one of the types enumerated by ->enumerate_types().
+ *
+ * Returns 0 if successful, otherwise a positive errno value.
+ */
+ int (*enumerate_names)(const char *type, struct sset *names);
+
+ /* Deletes the datapath with the specified 'type' and 'name'. The caller
+ * should have closed any open ofproto with this 'type' and 'name'; this
+ * function is allowed to fail if that is not the case.
+ *
+ * 'type' is one of the types enumerated by ->enumerate_types().
+ * 'name' is one of the names enumerated by ->enumerate_names() for 'type'.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.
+ */
+ int (*del)(const char *type, const char *name);
+
+/* ## --------------------------- ## */
+/* ## Top-Level ofproto Functions ## */
+/* ## --------------------------- ## */
+
+ /* Life-cycle functions for an "ofproto" (see "Life Cycle" above).
+ *
+ * ->construct() should not modify most base members of the ofproto. In
+ * particular, the client will initialize the ofproto's 'ports' member
+ * after construction is complete.
+ *
+ * ->construct() should initialize the base 'n_tables' member to the number
+ * of flow tables supported by the datapath (between 1 and 255, inclusive),
+ * initialize the base 'tables' member with space for one classifier per
+ * table, and initialize each classifier with classifier_init. Each flow
+ * table should be initially empty, so ->construct() should delete flows
+ * from the underlying datapath, if necessary, rather than populating the
+ * tables.
+ *
+ * Only one ofproto instance needs to be supported for any given datapath.
+ * If a datapath is already open as part of one "ofproto", then another
+ * attempt to "construct" the same datapath as part of another ofproto is
+ * allowed to fail with an error.
+ *
+ * ->construct() returns 0 if successful, otherwise a positive errno
+ * value. */
+ struct ofproto *(*alloc)(void);
+ int (*construct)(struct ofproto *ofproto);
+ void (*destruct)(struct ofproto *ofproto);
+ void (*dealloc)(struct ofproto *ofproto);
+
+ /* Performs any periodic activity required by 'ofproto'. It should:
+ *
+ * - Call connmgr_send_packet_in() for each received packet that missed
+ * in the OpenFlow flow table or that had a OFPP_CONTROLLER output
+ * action.
+ *
+ * - Call ofproto_rule_expire() for each OpenFlow flow that has reached
+ * its hard_timeout or idle_timeout, to expire the flow.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. The ENODEV
+ * return value specifically means that the datapath underlying 'ofproto'
+ * has been destroyed (externally, e.g. by an admin running ovs-dpctl).
+ */
+ int (*run)(struct ofproto *ofproto);
+
+ /* Causes the poll loop to wake up when 'ofproto''s 'run' function needs to
+ * be called, e.g. by calling the timer or fd waiting functions in
+ * poll-loop.h. */
+ void (*wait)(struct ofproto *ofproto);
+
+ /* Every "struct rule" in 'ofproto' is about to be deleted, one by one.
+ * This function may prepare for that, for example by clearing state in
+ * advance. It should *not* actually delete any "struct rule"s from
+ * 'ofproto', only prepare for it.
+ *
+ * This function is optional; it's really just for optimization in case
+ * it's cheaper to delete all the flows from your hardware in a single pass
+ * than to do it one by one. */
+ void (*flush)(struct ofproto *ofproto);
+
+ /* Helper for the OpenFlow OFPT_FEATURES_REQUEST request.
+ *
+ * The implementation should store true in '*arp_match_ip' if the switch
+ * supports matching IP addresses inside ARP requests and replies, false
+ * otherwise.
+ *
+ * The implementation should store in '*actions' a bitmap of the supported
+ * OpenFlow actions: the bit with value (1 << n) should be set to 1 if the
+ * implementation supports the action with value 'n', and to 0 otherwise.
+ * For example, if the implementation supports the OFPAT_OUTPUT and
+ * OFPAT_ENQUEUE actions, but no others, it would set '*actions' to (1 <<
+ * OFPAT_OUTPUT) | (1 << OFPAT_ENQUEUE). Vendor actions are not included
+ * in '*actions'. */
+ void (*get_features)(struct ofproto *ofproto,
+ bool *arp_match_ip, uint32_t *actions);
+
+ /* Helper for the OpenFlow OFPST_TABLE statistics request.
+ *
+ * The 'ots' array contains 'ofproto->n_tables' elements. Each element is
+ * initialized as:
+ *
+ * - 'table_id' to the array index.
+ *
+ * - 'name' to "table#" where # is the table ID.
+ *
+ * - 'wildcards' to OFPFW_ALL.
+ *
+ * - 'max_entries' to 1,000,000.
+ *
+ * - 'active_count' to the classifier_count() for the table.
+ *
+ * - 'lookup_count' and 'matched_count' to 0.
+ *
+ * The implementation should update any members in each element for which
+ * it has better values:
+ *
+ * - 'name' to a more meaningful name.
+ *
+ * - 'wildcards' to the set of wildcards actually supported by the table
+ * (if it doesn't support all OpenFlow wildcards).
+ *
+ * - 'max_entries' to the maximum number of flows actually supported by
+ * the hardware.
+ *
+ * - 'lookup_count' to the number of packets looked up in this flow table
+ * so far.
+ *
+ * - 'matched_count' to the number of packets looked up in this flow
+ * table so far that matched one of the flow entries.
+ *
+ * Keep in mind that all of the members of struct ofp_table_stats are in
+ * network byte order.
+ */
+ void (*get_tables)(struct ofproto *ofproto, struct ofp_table_stats *ots);
+
+/* ## ---------------- ## */
+/* ## ofport Functions ## */
+/* ## ---------------- ## */
+
+ /* Life-cycle functions for a "struct ofport" (see "Life Cycle" above).
+ *
+ * ->port_construct() should not modify any base members of the ofport.
+ *
+ * ofports are managed by the base ofproto code. The ofproto
+ * implementation should only create and destroy them in response to calls
+ * to these functions. The base ofproto code will create and destroy
+ * ofports in the following situations:
+ *
+ * - Just after the ->construct() function is called, the base ofproto
+ * iterates over all of the implementation's ports, using
+ * ->port_dump_start() and related functions, and constructs an ofport
+ * for each dumped port.
+ *
+ * - If ->port_poll() reports that a specific port has changed, then the
+ * base ofproto will query that port with ->port_query_by_name() and
+ * construct or destruct ofports as necessary to reflect the updated
+ * set of ports.
+ *
+ * - If ->port_poll() returns ENOBUFS to report an unspecified port set
+ * change, then the base ofproto will iterate over all of the
+ * implementation's ports, in the same way as at ofproto
+ * initialization, and construct and destruct ofports to reflect all of
+ * the changes.
+ *
+ * ->port_construct() returns 0 if successful, otherwise a positive errno
+ * value.
+ */
+ struct ofport *(*port_alloc)(void);
+ int (*port_construct)(struct ofport *ofport);
+ void (*port_destruct)(struct ofport *ofport);
+ void (*port_dealloc)(struct ofport *ofport);
+
+ /* Called after 'ofport->netdev' is replaced by a new netdev object. If
+ * the ofproto implementation uses the ofport's netdev internally, then it
+ * should switch to using the new one. The old one has been closed.
+ *
+ * An ofproto implementation that doesn't need to do anything in this
+ * function may use a null pointer. */
+ void (*port_modified)(struct ofport *ofport);
+
+ /* Called after an OpenFlow OFPT_PORT_MOD request changes a port's
+ * configuration. 'ofport->opp.config' contains the new configuration.
+ * 'old_config' contains the previous configuration.
+ *
+ * The caller implements OFPPC_PORT_DOWN using netdev functions to turn
+ * NETDEV_UP on and off, so this function doesn't have to do anything for
+ * that bit (and it won't be called if that is the only bit that
+ * changes). */
+ void (*port_reconfigured)(struct ofport *ofport, ovs_be32 old_config);
+
+ /* Looks up a port named 'devname' in 'ofproto'. On success, initializes
+ * '*port' appropriately.
+ *
+ * The caller owns the data in 'port' and must free it with
+ * ofproto_port_destroy() when it is no longer needed. */
+ int (*port_query_by_name)(const struct ofproto *ofproto,
+ const char *devname, struct ofproto_port *port);
+
+ /* Attempts to add 'netdev' as a port on 'ofproto'. Returns 0 if
+ * successful, otherwise a positive errno value. If successful, sets
+ * '*ofp_portp' to the new port's port number.
+ *
+ * It doesn't matter whether the new port will be returned by a later call
+ * to ->port_poll(); the implementation may do whatever is more
+ * convenient. */
+ int (*port_add)(struct ofproto *ofproto, struct netdev *netdev,
+ uint16_t *ofp_portp);
+
+ /* Deletes port number 'ofp_port' from the datapath for 'ofproto'. Returns
+ * 0 if successful, otherwise a positive errno value.
+ *
+ * It doesn't matter whether the new port will be returned by a later call
+ * to ->port_poll(); the implementation may do whatever is more
+ * convenient. */
+ int (*port_del)(struct ofproto *ofproto, uint16_t ofp_port);
+
+ /* Attempts to begin dumping the ports in 'ofproto'. On success, returns 0
+ * and initializes '*statep' with any data needed for iteration. On
+ * failure, returns a positive errno value. */
+ int (*port_dump_start)(const struct ofproto *ofproto, void **statep);
+
+ /* Attempts to retrieve another port from 'ofproto' for 'state', which was
+ * initialized by a successful call to the 'port_dump_start' function for
+ * 'ofproto'. On success, stores a new ofproto_port into 'port' and
+ * returns 0. Returns EOF if the end of the port table has been reached,
+ * or a positive errno value on error. This function will not be called
+ * again once it returns nonzero once for a given iteration (but the
+ * 'port_dump_done' function will be called afterward).
+ *
+ * The ofproto provider retains ownership of the data stored in 'port'. It
+ * must remain valid until at least the next call to 'port_dump_next' or
+ * 'port_dump_done' for 'state'. */
+ int (*port_dump_next)(const struct ofproto *ofproto, void *state,
+ struct ofproto_port *port);
+
+ /* Releases resources from 'ofproto' for 'state', which was initialized by
+ * a successful call to the 'port_dump_start' function for 'ofproto'. */
+ int (*port_dump_done)(const struct ofproto *ofproto, void *state);
+
+ /* Polls for changes in the set of ports in 'ofproto'. If the set of ports
+ * in 'ofproto' has changed, then this function should do one of the
+ * following:
+ *
+ * - Preferably: store the name of the device that was added to or deleted
+ * from 'ofproto' in '*devnamep' and return 0. The caller is responsible
+ * for freeing '*devnamep' (with free()) when it no longer needs it.
+ *
+ * - Alternatively: return ENOBUFS, without indicating the device that was
+ * added or deleted.
+ *
+ * Occasional 'false positives', in which the function returns 0 while
+ * indicating a device that was not actually added or deleted or returns
+ * ENOBUFS without any change, are acceptable.
+ *
+ * The purpose of 'port_poll' is to let 'ofproto' know about changes made
+ * externally to the 'ofproto' object, e.g. by a system administrator via
+ * ovs-dpctl. Therefore, it's OK, and even preferable, for port_poll() to
+ * not report changes made through calls to 'port_add' or 'port_del' on the
+ * same 'ofproto' object. (But it's OK for it to report them too, just
+ * slightly less efficient.)
+ *
+ * If the set of ports in 'ofproto' has not changed, returns EAGAIN. May
+ * also return other positive errno values to indicate that something has
+ * gone wrong.
+ *
+ * If the set of ports in a datapath is fixed, or if the only way that the
+ * set of ports in a datapath can change is through ->port_add() and
+ * ->port_del(), then this function may be a null pointer.
+ */
+ int (*port_poll)(const struct ofproto *ofproto, char **devnamep);
+
+ /* Arranges for the poll loop to wake up when ->port_poll() will return a
+ * value other than EAGAIN.
+ *
+ * If the set of ports in a datapath is fixed, or if the only way that the
+ * set of ports in a datapath can change is through ->port_add() and
+ * ->port_del(), or if the poll loop will always wake up anyway when
+ * ->port_poll() will return a value other than EAGAIN, then this function
+ * may be a null pointer.
+ */
+ void (*port_poll_wait)(const struct ofproto *ofproto);
+
+ /* Checks the status of LACP negotiation for 'port'. Returns 1 if LACP
+ * partner information for 'port' is up-to-date, 0 if LACP partner
+ * information is not current (generally indicating a connectivity
+ * problem), or -1 if LACP is not enabled on 'port'.
+ *
+ * This function may be a null pointer if the ofproto implementation does
+ * not support LACP. */
+ int (*port_is_lacp_current)(const struct ofport *port);
+
+/* ## ----------------------- ## */
+/* ## OpenFlow Rule Functions ## */
+/* ## ----------------------- ## */
+
+ /* Life-cycle functions for a "struct rule" (see "Life Cycle" above).
+ *
+ * ->rule_construct() should first check whether the rule is acceptable:
+ *
+ * - Validate that the matching rule in 'rule->cr' is supported by the
+ * datapath. If not, then return an OpenFlow error code (as returned
+ * by ofp_mkerr()).
+ *
+ * For example, if the datapath does not support registers, then it
+ * should return an error if 'rule->cr' does not wildcard all
+ * registers.
+ *
+ * - Validate that 'rule->actions' and 'rule->n_actions' are well-formed
+ * OpenFlow actions that can be correctly implemented by the datapath.
+ * If not, then return an OpenFlow error code (as returned by
+ * ofp_mkerr()).
+ *
+ * The validate_actions() function (in ofp-util.c) can be useful as a
+ * model for action validation, but it accepts all of the OpenFlow
+ * actions that OVS understands. If your ofproto implementation only
+ * implements a subset of those, then you should implement your own
+ * action validation.
+ *
+ * If the rule is acceptable, then ->rule_construct() should modify the
+ * flow table:
+ *
+ * - If there was already a rule with exactly the same matching criteria
+ * and priority in the classifier, then it should destroy it (with
+ * ofproto_rule_destroy()).
+ *
+ * To the greatest extent possible, the old rule should be destroyed
+ * only if inserting the new rule succeeds; that is, ->rule_construct()
+ * should be transactional.
+ *
+ * The function classifier_find_rule_exactly() can locate such a rule.
+ *
+ * - Insert the new rule into the ofproto's 'cls' classifier, and into
+ * the datapath flow table.
+ *
+ * The function classifier_insert() inserts a rule into the classifier.
+ *
+ * Other than inserting 'rule->cr' into the classifier, ->rule_construct()
+ * should not modify any base members of struct rule.
+ *
+ * ->rule_destruct() should remove 'rule' from the ofproto's 'cls'
+ * classifier (e.g. with classifier_remove()) and from the datapath flow
+ * table. */
+ struct rule *(*rule_alloc)(void);
+ int (*rule_construct)(struct rule *rule);
+ void (*rule_destruct)(struct rule *rule);
+ void (*rule_dealloc)(struct rule *rule);
+
+ /* Obtains statistics for 'rule', storing the number of packets that have
+ * matched it in '*packet_count' and the number of bytes in those packets
+ * in '*byte_count'. */
+ void (*rule_get_stats)(struct rule *rule, uint64_t *packet_count,
+ uint64_t *byte_count);
+
+ /* Applies the actions in 'rule' to 'packet'. (This implements sending
+ * buffered packets for OpenFlow OFPT_FLOW_MOD commands.)
+ *
+ * Takes ownership of 'packet' (so it should eventually free it, with
+ * ofpbuf_delete()).
+ *
+ * 'flow' reflects the flow information for 'packet'. All of the
+ * information in 'flow' is extracted from 'packet', except for
+ * flow->tun_id and flow->in_port, which are assigned the correct values
+ * for the incoming packet. The register values are zeroed.
+ *
+ * The statistics for 'packet' should be included in 'rule'.
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error code (as returned
+ * by ofp_mkerr()). */
+ int (*rule_execute)(struct rule *rule, struct flow *flow,
+ struct ofpbuf *packet);
+
+ /* Validates that the 'n' elements in 'actions' are well-formed OpenFlow
+ * actions that can be correctly implemented by the datapath. If not, then
+ * return an OpenFlow error code (as returned by ofp_mkerr()). If so,
+ * then update the datapath to implement the new actions and return 0.
+ *
+ * When this function runs, 'rule' still has its original actions. If this
+ * function returns 0, then the caller will update 'rule' with the new
+ * actions and free the old ones. */
+ int (*rule_modify_actions)(struct rule *rule,
+ const union ofp_action *actions, size_t n);
+
+ /* These functions implement the OpenFlow IP fragment handling policy. By
+ * default ('drop_frags' == false), an OpenFlow switch should treat IP
+ * fragments the same way as other packets (although TCP and UDP port
+ * numbers cannot be determined). With 'drop_frags' == true, the switch
+ * should drop all IP fragments without passing them through the flow
+ * table. */
+ bool (*get_drop_frags)(struct ofproto *ofproto);
+ void (*set_drop_frags)(struct ofproto *ofproto, bool drop_frags);
+
+ /* Implements the OpenFlow OFPT_PACKET_OUT command. The datapath should
+ * execute the 'n_actions' in the 'actions' array on 'packet'.
+ *
+ * The caller retains ownership of 'packet', so ->packet_out() should not
+ * modify or free it.
+ *
+ * This function must validate that the 'n_actions' elements in 'actions'
+ * are well-formed OpenFlow actions that can be correctly implemented by
+ * the datapath. If not, then it should return an OpenFlow error code (as
+ * returned by ofp_mkerr()).
+ *
+ * 'flow' reflects the flow information for 'packet'. All of the
+ * information in 'flow' is extracted from 'packet', except for
+ * flow->in_port, which is taken from the OFPT_PACKET_OUT message.
+ * flow->tun_id and its register values are zeroed.
+ *
+ * 'packet' is not matched against the OpenFlow flow table, so its
+ * statistics should not be included in OpenFlow flow statistics.
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error code (as returned
+ * by ofp_mkerr()). */
+ int (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
+ const struct flow *flow,
+ const union ofp_action *actions,
+ size_t n_actions);
+
+/* ## ------------------------- ## */
+/* ## OFPP_NORMAL configuration ## */
+/* ## ------------------------- ## */
+
+ /* Configures NetFlow on 'ofproto' according to the options in
+ * 'netflow_options', or turns off NetFlow if 'netflow_options' is NULL.
+ *
+ * EOPNOTSUPP as a return value indicates that 'ofproto' does not support
+ * sFlow, as does a null pointer. */
+ int (*set_netflow)(struct ofproto *ofproto,
+ const struct netflow_options *netflow_options);
+
+ void (*get_netflow_ids)(const struct ofproto *ofproto,
+ uint8_t *engine_type, uint8_t *engine_id);
+
+ /* Configures sFlow on 'ofproto' according to the options in
+ * 'sflow_options', or turns off sFlow if 'sflow_options' is NULL.
+ *
+ * EOPNOTSUPP as a return value indicates that 'ofproto' does not support
+ * sFlow, as does a null pointer. */
+ int (*set_sflow)(struct ofproto *ofproto,
+ const struct ofproto_sflow_options *sflow_options);
+
+ /* Configures connectivity fault management on 'ofport'.
+ *
+ * If 'cfm' is nonnull, takes basic configuration from the configuration
+ * members in 'cfm', and the set of remote maintenance points from the
+ * 'n_remote_mps' elements in 'remote_mps'. Ignores the statistics members
+ * of 'cfm'.
+ *
+ * If 'cfm' is null, removes any connectivity fault management
+ * configuration from 'ofport'.
+ *
+ * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+ * support CFM, as does a null pointer. */
+ int (*set_cfm)(struct ofport *ofport, const struct cfm *cfm,
+ const uint16_t *remote_mps, size_t n_remote_mps);
+
+ /* Stores the connectivity fault management object associated with 'ofport'
+ * in '*cfmp'. Stores a null pointer in '*cfmp' if CFM is not configured
+ * on 'ofport'. The caller must not modify or destroy the returned object.
+ *
+ * This function may be NULL if this ofproto_class does not support CFM. */
+ int (*get_cfm)(struct ofport *ofport, const struct cfm **cfmp);
+
+ /* If 's' is nonnull, this function registers a "bundle" associated with
+ * client data pointer 'aux' in 'ofproto'. A bundle is the same concept as
+ * a Port in OVSDB, that is, it consists of one or more "slave" devices
+ * (Interfaces, in OVSDB) along with VLAN and LACP configuration and, if
+ * there is more than one slave, a bonding configuration. If 'aux' is
+ * already registered then this function updates its configuration to 's'.
+ * Otherwise, this function registers a new bundle.
+ *
+ * If 's' is NULL, this function unregisters the bundle registered on
+ * 'ofproto' associated with client data pointer 'aux'. If no such bundle
+ * has been registered, this has no effect.
+ *
+ * This function affects only the behavior of the NXAST_AUTOPATH action and
+ * output to the OFPP_NORMAL port. An implementation that does not support
+ * it at all may set it to NULL or return EOPNOTSUPP. An implementation
+ * that supports only a subset of the functionality should implement what
+ * it can and return 0. */
+ int (*bundle_set)(struct ofproto *ofproto, void *aux,
+ const struct ofproto_bundle_settings *s);
+
+ /* If 'port' is part of any bundle, removes it from that bundle. If the
+ * bundle now has no ports, deletes the bundle. If the bundle now has only
+ * one port, deconfigures the bundle's bonding configuration. */
+ void (*bundle_remove)(struct ofport *ofport);
+
+ /* If 's' is nonnull, this function registers a mirror associated with
+ * client data pointer 'aux' in 'ofproto'. A mirror is the same concept as
+ * a Mirror in OVSDB. If 'aux' is already registered then this function
+ * updates its configuration to 's'. Otherwise, this function registers a
+ * new mirror.
+ *
+ * If 's' is NULL, this function unregisters the mirror registered on
+ * 'ofproto' associated with client data pointer 'aux'. If no such mirror
+ * has been registered, this has no effect.
+ *
+ * This function affects only the behavior of the OFPP_NORMAL action. An
+ * implementation that does not support it at all may set it to NULL or
+ * return EOPNOTSUPP. An implementation that supports only a subset of the
+ * functionality should implement what it can and return 0. */
+ int (*mirror_set)(struct ofproto *ofproto, void *aux,
+ const struct ofproto_mirror_settings *s);
+
+ /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs
+ * on which all packets are flooded, instead of using MAC learning. If
+ * 'flood_vlans' is NULL, then MAC learning applies to all VLANs.
+ *
+ * This function affects only the behavior of the OFPP_NORMAL action. An
+ * implementation that does not support it may set it to NULL or return
+ * EOPNOTSUPP. */
+ int (*set_flood_vlans)(struct ofproto *ofproto,
+ unsigned long *flood_vlans);
+
+ /* Returns true if 'aux' is a registered bundle that is currently in use as
+ * the output for a mirror. */
+ bool (*is_mirror_output_bundle)(struct ofproto *ofproto, void *aux);
+};
+
+extern const struct ofproto_class ofproto_dpif_class;
+
+int ofproto_class_register(const struct ofproto_class *);
+int ofproto_class_unregister(const struct ofproto_class *);
+
+void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
+ const union ofp_action *, size_t n_actions);
+void ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
+void ofproto_flush_flows(struct ofproto *);
+
+#endif /* ofproto/private.h */
trap 'kill `cat ovs-openflowd.pid`' 0
AT_CAPTURE_FILE([ovs-openflowd.log])
AT_CHECK(
- [ovs-openflowd --detach --pidfile --enable-dummy --log-file dummy@br0 none --datapath-id=fedcba9876543210 $1],
+ [ovs-openflowd --detach --pidfile --enable-dummy --log-file --fail=closed dummy@br0 none --datapath-id=fedcba9876543210 $1],
[0], [], [stderr])
AT_CHECK([[sed < stderr '
/vlog|INFO|opened log file/d
AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
AT_CHECK([STRIP_XIDS stdout], [0], [dnl
OFPT_FEATURES_REPLY: ver:0x1, dpid:fedcba9876543210
-n_tables:2, n_buffers:256
+n_tables:1, n_buffers:256
features: capabilities:0x87, actions:0xfff
LOCAL(br0): addr:aa:55:aa:55:00:00, config: 0x1, state:0x1
OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
AT_CHECK_UNQUOTED([STRIP_XIDS stdout], [0], [dnl
OFPT_FEATURES_REPLY: ver:0x1, dpid:fedcba9876543210
-n_tables:2, n_buffers:256
+n_tables:1, n_buffers:256
features: capabilities:0x87, actions:0xfff
LOCAL(br0): addr:aa:55:aa:55:00:00, config: $config, state:$state
OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
AT_CHECK([echo 'in_port=1,actions=0' | ovs-ofctl add-flows br0 -])
AT_CHECK([ovs-ofctl add-flow br0 in_port=0,actions=1])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
+ cookie=0x0, duration=?s, table_id=0, n_packets=0, n_bytes=0, in_port=0 actions=output:1
cookie=0x0, duration=?s, table_id=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
- cookie=0x0, duration=?s, table_id=0, n_packets=0, n_bytes=0, in_port=65534 actions=output:1
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl del-flows br0])
tun_id=0x1234,cookie=0x5678,actions=flood
actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, NXM_NX_REG0[0..12])
-actions=drop
+table=1,actions=drop
tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt
NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD
NXT_FLOW_MOD: ADD actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
NXT_FLOW_MOD: ADD actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
-NXT_FLOW_MOD: ADD actions=drop
-NXT_FLOW_MOD: ADD tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
+NXT_FLOW_MOD_TABLE_ID: enable
+NXT_FLOW_MOD: ADD table_id:1 actions=drop
+NXT_FLOW_MOD: ADD table_id:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
]])
AT_CLEANUP
<any>
# in port
-NXM_OF_IN_PORT(fffe)
+NXM_OF_IN_PORT(0000)
NXM_OF_IN_PORT(fffe)
# eth dst
static ovs_be64 tun_id_values[] = {
0,
CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
-static uint16_t in_port_values[] = { 1, ODPP_LOCAL };
+static uint16_t in_port_values[] = { 1, OFPP_LOCAL };
static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
static ovs_be16 dl_type_values[]
= { CONSTANT_HTONS(ETH_TYPE_IP), CONSTANT_HTONS(ETH_TYPE_ARP) };
tcls_init(&tcls);
tcls_rule = tcls_insert(&tcls, rule);
- assert(!classifier_insert(&cls, &rule->cls_rule));
+ classifier_insert(&cls, &rule->cls_rule);
check_tables(&cls, 1, 1, 0);
compare_classifiers(&cls, &tcls);
classifier_init(&cls);
tcls_init(&tcls);
tcls_insert(&tcls, rule1);
- assert(!classifier_insert(&cls, &rule1->cls_rule));
+ classifier_insert(&cls, &rule1->cls_rule);
check_tables(&cls, 1, 1, 0);
compare_classifiers(&cls, &tcls);
tcls_destroy(&tcls);
tcls_init(&tcls);
tcls_insert(&tcls, rule2);
assert(test_rule_from_cls_rule(
- classifier_insert(&cls, &rule2->cls_rule)) == rule1);
+ classifier_replace(&cls, &rule2->cls_rule)) == rule1);
free(rule1);
check_tables(&cls, 1, 1, 0);
compare_classifiers(&cls, &tcls);
tcls_rules[j] = tcls_insert(&tcls, rules[j]);
displaced_rule = test_rule_from_cls_rule(
- classifier_insert(&cls, &rules[j]->cls_rule));
+ classifier_replace(&cls, &rules[j]->cls_rule));
if (pri_rules[pris[j]] >= 0) {
int k = pri_rules[pris[j]];
assert(displaced_rule != NULL);
rules[i] = make_rule(wcf, priority, value_pats[i]);
tcls_rules[i] = tcls_insert(&tcls, rules[i]);
- assert(!classifier_insert(&cls, &rules[i]->cls_rule));
+ classifier_insert(&cls, &rules[i]->cls_rule);
check_tables(&cls, 1, i + 1, 0);
compare_classifiers(&cls, &tcls);
int value_pat = rand() & ((1u << CLS_N_FIELDS) - 1);
rule = make_rule(wcf, priority, value_pat);
tcls_insert(&tcls, rule);
- assert(!classifier_insert(&cls, &rule->cls_rule));
+ classifier_insert(&cls, &rule->cls_rule);
check_tables(&cls, -1, i + 1, -1);
compare_classifiers(&cls, &tcls);
}
utilities_ovs_dpctl_LDADD = lib/libopenvswitch.a
utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c
-utilities_ovs_ofctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_ofctl_LDADD = \
+ ofproto/libofproto.a \
+ lib/libopenvswitch.a \
+ $(SSL_LIBS)
utilities_ovs_openflowd_SOURCES = utilities/ovs-openflowd.c
utilities_ovs_openflowd_LDADD = \
cfg.mode = (action_normal ? LSW_NORMAL
: learn_macs ? LSW_LEARN
: LSW_FLOOD);
+ cfg.exact_flows = exact_flows;
cfg.max_idle = set_up_flows ? max_idle : -1;
cfg.default_flows = &default_flows;
cfg.default_queue = default_queue;
read_flow_file(const char *name)
{
enum nx_flow_format flow_format;
+ bool flow_mod_table_id;
FILE *stream;
stream = fopen(optarg, "r");
}
flow_format = NXFF_OPENFLOW10;
- while (parse_ofp_flow_mod_file(&default_flows, &flow_format, stream,
- OFPFC_ADD)) {
+ flow_mod_table_id = false;
+ while (parse_ofp_flow_mod_file(&default_flows,
+ &flow_format, &flow_mod_table_id,
+ stream, OFPFC_ADD)) {
continue;
}
When \fBdl_type\fR and \fBnw_proto\fR take other values, the values of
these settings are ignored (see \fBFlow Syntax\fR above).
.
+.IP \fBtable=\fInumber\fR
+If specified, limits the flow manipulation and flow dump commands to
+only apply to the table with the given \fInumber\fR.
+\fInumber\fR is a number between 0 and 254, inclusive.
+.
+Behavior varies if \fBtable\fR is not specified. For flow table
+modification commands without \fB\-\-strict\fR, the switch will choose
+the table for these commands to operate on. For flow table
+modification commands with \fB\-\-strict\fR, the command will operate
+on any single matching flow in any table; it will do nothing if there
+are matches in more than one table. The \fBdump-flows\fR and
+\fBdump-aggregate\fR commands will gather statistics about flows from
+all tables.
+.IP
+When this field is specified in \fBadd-flow\fR, \fBadd-flows\fR,
+\fBmod-flows\fR and \fBdel-flows\fR commands, it activates a Nicira
+extension to OpenFlow, which as of this writing is only known to be
+implemented by Open vSwitch.
+.
.PP
The following shorthand notations are also available:
.
The \fBdump\-flows\fR and \fBdump\-aggregate\fR commands support an
additional optional field:
.
-.IP \fBtable=\fInumber\fR
-If specified, limits the flows about which statistics are gathered to
-those in the table with the given \fInumber\fR. Tables are numbered
-as shown by the \fBdump\-tables\fR command.
-.
-If this field is not specified, or if \fInumber\fR is given as
-\fB255\fR, statistics are gathered about flows from all tables.
-.
.SS "Table Entry Output"
.
The \fBdump\-tables\fR and \fBdump\-aggregate\fR commands print information
#include "command-line.h"
#include "compiler.h"
#include "dirs.h"
-#include "dpif.h"
#include "dynamic-string.h"
#include "netlink.h"
#include "nx-match.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "ofproto/ofproto.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
#include "random.h"
open_vconn__(const char *name, const char *default_suffix,
struct vconn **vconnp)
{
- struct dpif *dpif;
+ char *datapath_name, *datapath_type, *socket_name;
+ char *bridge_path;
struct stat s;
- char *bridge_path, *datapath_name, *datapath_type;
bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
- dp_parse_name(name, &datapath_name, &datapath_type);
+
+ ofproto_parse_name(name, &datapath_name, &datapath_type);
+ socket_name = xasprintf("%s/%s.%s",
+ ovs_rundir(), datapath_name, default_suffix);
+ free(datapath_name);
+ free(datapath_type);
if (strstr(name, ":")) {
run(vconn_open_block(name, OFP_VERSION, vconnp),
open_vconn_socket(name, vconnp);
} else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) {
open_vconn_socket(bridge_path, vconnp);
- } else if (!dpif_open(datapath_name, datapath_type, &dpif)) {
- char dpif_name[IF_NAMESIZE + 1];
- char *socket_name;
-
- run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name),
- "obtaining name of %s", dpif_name);
- dpif_close(dpif);
- if (strcmp(dpif_name, name)) {
- VLOG_DBG("datapath %s is named %s", name, dpif_name);
- }
-
- socket_name = xasprintf("%s/%s.%s",
- ovs_rundir(), dpif_name, default_suffix);
- if (stat(socket_name, &s)) {
- ovs_fatal(errno, "cannot connect to %s: stat failed on %s",
- name, socket_name);
- } else if (!S_ISSOCK(s.st_mode)) {
+ } else if (!stat(socket_name, &s)) {
+ if (!S_ISSOCK(s.st_mode)) {
ovs_fatal(0, "cannot connect to %s: %s is not a socket",
name, socket_name);
}
-
open_vconn_socket(socket_name, vconnp);
- free(socket_name);
} else {
ovs_fatal(0, "%s is not a valid connection method", name);
}
- free(datapath_name);
- free(datapath_type);
free(bridge_path);
+ free(socket_name);
}
static void
do_flow_mod_file__(int argc OVS_UNUSED, char *argv[], uint16_t command)
{
enum nx_flow_format flow_format;
+ bool flow_mod_table_id;
struct list requests;
struct vconn *vconn;
FILE *file;
list_init(&requests);
flow_format = set_initial_format_for_flow_mod(&requests);
+ flow_mod_table_id = false;
open_vconn(argv[1], &vconn);
- while (parse_ofp_flow_mod_file(&requests, &flow_format, file, command)) {
+ while (parse_ofp_flow_mod_file(&requests, &flow_format, &flow_mod_table_id,
+ file, command)) {
check_final_format_for_flow_mod(flow_format);
transact_multiple_noreply(vconn, &requests);
}
do_flow_mod__(int argc, char *argv[], uint16_t command)
{
enum nx_flow_format flow_format;
+ bool flow_mod_table_id;
struct list requests;
struct vconn *vconn;
list_init(&requests);
flow_format = set_initial_format_for_flow_mod(&requests);
+ flow_mod_table_id = false;
- parse_ofp_flow_mod_str(&requests, &flow_format, argc > 2 ? argv[2] : "",
- command);
+ parse_ofp_flow_mod_str(&requests, &flow_format, &flow_mod_table_id,
+ argc > 2 ? argv[2] : "", command);
check_final_format_for_flow_mod(flow_format);
open_vconn(argv[1], &vconn);
fte->rule = *rule;
fte->versions[index] = version;
- old = fte_from_cls_rule(classifier_insert(cls, &fte->rule));
+ old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
if (old) {
fte_version_free(old->versions[index]);
fte->versions[!index] = old->versions[!index];
enum nx_flow_format min_ff;
struct ofpbuf actions;
struct flow_mod fm;
- uint8_t table_idx;
ofpbuf_init(&actions, 64);
- parse_ofp_str(&fm, &table_idx, &actions, ds_cstr(&s));
+ parse_ofp_str(&fm, &actions, ds_cstr(&s));
version = xmalloc(sizeof *version);
version->cookie = fm.cookie;
fm.cr = fte->rule;
fm.cookie = version->cookie;
+ fm.table_id = 0xff;
fm.command = command;
fm.idle_timeout = version->idle_timeout;
fm.hard_timeout = version->hard_timeout;
fm.n_actions = 0;
}
- ofm = ofputil_encode_flow_mod(&fm, flow_format);
+ ofm = ofputil_encode_flow_mod(&fm, flow_format, false);
list_push_back(packets, &ofm->list_node);
}
do_parse_flow(int argc OVS_UNUSED, char *argv[])
{
enum nx_flow_format flow_format;
+ bool flow_mod_table_id;
struct list packets;
flow_format = NXFF_OPENFLOW10;
if (preferred_flow_format > 0) {
flow_format = preferred_flow_format;
}
+ flow_mod_table_id = false;
list_init(&packets);
- parse_ofp_flow_mod_str(&packets, &flow_format, argv[1], OFPFC_ADD);
+ parse_ofp_flow_mod_str(&packets, &flow_format, &flow_mod_table_id,
+ argv[1], OFPFC_ADD);
print_packet_list(&packets);
}
do_parse_flows(int argc OVS_UNUSED, char *argv[])
{
enum nx_flow_format flow_format;
+ bool flow_mod_table_id;
struct list packets;
FILE *file;
if (preferred_flow_format > 0) {
flow_format = preferred_flow_format;
}
+ flow_mod_table_id = false;
list_init(&packets);
- while (parse_ofp_flow_mod_file(&packets, &flow_format, file, OFPFC_ADD)) {
+ while (parse_ofp_flow_mod_file(&packets, &flow_format, &flow_mod_table_id,
+ file, OFPFC_ADD)) {
print_packet_list(&packets);
}
fclose(file);
#include "compiler.h"
#include "daemon.h"
#include "dirs.h"
-#include "dpif.h"
#include "dummy.h"
#include "leak-checker.h"
#include "list.h"
struct ofproto *ofproto;
struct ofsettings s;
int error;
- struct dpif *dpif;
struct netflow_options nf_options;
const char *port;
bool 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);
+ error = ofproto_create(s.dp_name, s.dp_type, &ofproto);
if (error) {
- VLOG_FATAL("could not create datapath (%s)", strerror(error));
+ VLOG_FATAL("could not initialize OpenFlow switch (%s)",
+ strerror(error));
}
/* Add ports to the datapath if requested by the user. */
port, strerror(error));
}
- error = dpif_port_add(dpif, netdev, NULL);
+ error = ofproto_port_add(ofproto, netdev, NULL);
if (error) {
VLOG_FATAL("failed to add %s as a port (%s)",
port, strerror(error));
netdev_close(netdev);
}
- /* Start OpenFlow processing. */
- error = ofproto_create(s.dp_name, s.dp_type, NULL, NULL, &ofproto);
- if (error) {
- VLOG_FATAL("could not initialize openflow switch (%s)",
- strerror(error));
- }
+ /* Configure OpenFlow switch. */
if (s.datapath_id) {
ofproto_set_datapath_id(ofproto, s.datapath_id);
}
VLOG_FATAL("unrecoverable datapath error (%s)", strerror(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);
+ ofproto_destroy(ofproto);
return 0;
}
}
/* Local vconns. */
- dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
+ ofproto_parse_name(argv[0], &s->dp_name, &s->dp_type);
/* Figure out controller names. */
s->run_forever = false;
#include <config.h>
#include "bridge.h"
-#include "byte-order.h"
#include <assert.h>
#include <errno.h>
-#include <arpa/inet.h>
-#include <ctype.h>
#include <inttypes.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <openflow/openflow.h>
-#include <signal.h>
#include <stdlib.h>
-#include <strings.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
#include "bitmap.h"
#include "bond.h"
#include "cfm.h"
-#include "classifier.h"
#include "coverage.h"
#include "daemon.h"
#include "dirs.h"
-#include "dpif.h"
#include "dynamic-string.h"
-#include "flow.h"
#include "hash.h"
#include "hmap.h"
#include "jsonrpc.h"
#include "lacp.h"
#include "list.h"
-#include "mac-learning.h"
#include "netdev.h"
-#include "netlink.h"
-#include "odp-util.h"
#include "ofp-print.h"
#include "ofpbuf.h"
-#include "ofproto/netflow.h"
#include "ofproto/ofproto.h"
-#include "ovsdb-data.h"
-#include "packets.h"
#include "poll-loop.h"
-#include "process.h"
#include "sha1.h"
#include "shash.h"
#include "socket-util.h"
#include "stream-ssl.h"
#include "sset.h"
-#include "svec.h"
#include "system-stats.h"
#include "timeval.h"
#include "util.h"
#include "unixctl.h"
-#include "vconn.h"
#include "vswitchd/vswitch-idl.h"
#include "xenserver.h"
#include "vlog.h"
#include "sflow_api.h"
+#include "vlan-bitmap.h"
VLOG_DEFINE_THIS_MODULE(bridge);
-COVERAGE_DEFINE(bridge_flush);
-COVERAGE_DEFINE(bridge_process_flow);
COVERAGE_DEFINE(bridge_reconfigure);
-struct dst {
- struct iface *iface;
- uint16_t vlan;
-};
-
-struct dst_set {
- struct dst builtin[32];
- struct dst *dsts;
- size_t n, allocated;
-};
-
-static void dst_set_init(struct dst_set *);
-static void dst_set_add(struct dst_set *, const struct dst *);
-static void dst_set_free(struct dst_set *);
-
struct iface {
/* These members are always valid. */
struct list port_elem; /* Element in struct port's "ifaces" list. */
+ struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */
struct port *port; /* Containing port. */
char *name; /* Host network device name. */
tag_type tag; /* Tag associated with this interface. */
/* These members are valid only after bridge_reconfigure() causes them to
* be initialized. */
- struct hmap_node dp_ifidx_node; /* In struct bridge's "ifaces" hmap. */
- int dp_ifidx; /* Index within kernel datapath. */
+ struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */
+ int ofp_port; /* OpenFlow port number, -1 if unknown. */
struct netdev *netdev; /* Network device. */
const char *type; /* Usually same as cfg->type. */
const struct ovsrec_interface *cfg;
};
-#define MAX_MIRRORS 32
-typedef uint32_t mirror_mask_t;
-#define MIRROR_MASK_C(X) UINT32_C(X)
-BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
struct mirror {
+ struct uuid uuid; /* UUID of this "mirror" record in database. */
+ struct hmap_node hmap_node; /* In struct bridge's "mirrors" hmap. */
struct bridge *bridge;
- size_t idx;
char *name;
- struct uuid uuid; /* UUID of this "mirror" record in database. */
-
- /* Selection criteria. */
- struct sset src_ports; /* Source port names. */
- struct sset dst_ports; /* Destination port names. */
- int *vlans;
- size_t n_vlans;
-
- /* Output. */
- struct port *out_port;
- int out_vlan;
};
-#define FLOOD_PORT ((struct port *) 1) /* The 'flood' output port. */
struct port {
struct bridge *bridge;
struct hmap_node hmap_node; /* Element in struct bridge's "ports" hmap. */
char *name;
- int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
- unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
- * NULL if all VLANs are trunked. */
const struct ovsrec_port *cfg;
/* An ordinary bridge port has 1 interface.
* A bridge port for bonding has at least 2 interfaces. */
struct list ifaces; /* List of "struct iface"s. */
-
- struct lacp *lacp; /* NULL if LACP is not enabled. */
-
- /* Bonding info. */
- struct bond *bond;
-
- /* Port mirroring info. */
- mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */
- mirror_mask_t dst_mirrors; /* Mirrors triggered when packet sent. */
- bool is_mirror_output_port; /* Does port mirroring send frames here? */
};
struct bridge {
- struct list node; /* Node in global list of bridges. */
+ struct hmap_node node; /* In 'all_bridges'. */
char *name; /* User-specified arbitrary name. */
- struct mac_learning *ml; /* MAC learning table. */
+ char *type; /* Datapath type. */
uint8_t ea[ETH_ADDR_LEN]; /* Bridge Ethernet Address. */
uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
const struct ovsrec_bridge *cfg;
/* OpenFlow switch processing. */
struct ofproto *ofproto; /* OpenFlow switch. */
- /* Kernel datapath information. */
- struct dpif *dpif; /* Datapath. */
- struct hmap ifaces; /* "struct iface"s indexed by dp_ifidx. */
-
/* Bridge ports. */
struct hmap ports; /* "struct port"s indexed by name. */
- struct shash iface_by_name; /* "struct iface"s indexed by name. */
-
- /* Bonding. */
- bool has_bonded_ports;
-
- /* Flow tracking. */
- bool flush;
+ struct hmap ifaces; /* "struct iface"s indexed by ofp_port. */
+ struct hmap iface_by_name; /* "struct iface"s indexed by name. */
/* Port mirroring. */
- struct mirror *mirrors[MAX_MIRRORS];
+ struct hmap mirrors; /* "struct mirror" indexed by UUID. */
/* Synthetic local port if necessary. */
struct ovsrec_port synth_local_port;
struct ovsrec_interface *synth_local_ifacep;
};
-/* List of all bridges. */
-static struct list all_bridges = LIST_INITIALIZER(&all_bridges);
+/* All bridges, indexed by name. */
+static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges);
/* OVSDB IDL used to obtain configuration. */
static struct ovsdb_idl *idl;
#define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
static long long int db_limiter = LLONG_MIN;
-static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
+static void add_del_bridges(const struct ovsrec_open_vswitch *);
+static void bridge_del_ofprotos(void);
+static bool bridge_add_ofprotos(struct bridge *);
+static void bridge_create(const struct ovsrec_bridge *);
static void bridge_destroy(struct bridge *);
static struct bridge *bridge_lookup(const char *name);
static unixctl_cb_func bridge_unixctl_dump_flows;
static unixctl_cb_func bridge_unixctl_reconnect;
-static int bridge_run_one(struct bridge *);
static size_t bridge_get_controllers(const struct bridge *br,
struct ovsrec_controller ***controllersp);
-static void bridge_reconfigure_one(struct bridge *);
-static void bridge_reconfigure_remotes(struct bridge *,
- const struct sockaddr_in *managers,
- size_t n_managers);
-static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces);
-static void bridge_fetch_dp_ifaces(struct bridge *);
-static void bridge_flush(struct bridge *);
+static void bridge_add_del_ports(struct bridge *);
+static void bridge_add_ofproto_ports(struct bridge *);
+static void bridge_del_ofproto_ports(struct bridge *);
+static void bridge_refresh_ofp_port(struct bridge *);
+static void bridge_configure_datapath_id(struct bridge *);
+static void bridge_configure_netflow(struct bridge *);
+static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
+static void bridge_configure_remotes(struct bridge *,
+ const struct sockaddr_in *managers,
+ size_t n_managers);
static void bridge_pick_local_hw_addr(struct bridge *,
uint8_t ea[ETH_ADDR_LEN],
struct iface **hw_addr_iface);
const uint8_t bridge_ea[ETH_ADDR_LEN],
struct iface *hw_addr_iface);
static uint64_t dpid_from_hash(const void *, size_t nbytes);
+static bool bridge_has_bond_fake_iface(const struct bridge *,
+ const char *name);
+static bool port_is_bond_fake_iface(const struct port *);
-static unixctl_cb_func bridge_unixctl_fdb_show;
static unixctl_cb_func cfm_unixctl_show;
static unixctl_cb_func qos_unixctl_show;
-static void port_run(struct port *);
-static void port_wait(struct port *);
-static struct port *port_create(struct bridge *, const char *name);
-static void port_reconfigure(struct port *, const struct ovsrec_port *);
-static void port_del_ifaces(struct port *, const struct ovsrec_port *);
+static struct port *port_create(struct bridge *, const struct ovsrec_port *);
+static void port_add_ifaces(struct port *);
+static void port_del_ifaces(struct port *);
static void port_destroy(struct port *);
static struct port *port_lookup(const struct bridge *, const char *name);
-static struct iface *port_get_an_iface(const struct port *);
-static struct port *port_from_dp_ifidx(const struct bridge *,
- uint16_t dp_ifidx);
-static void port_reconfigure_lacp(struct port *);
-static void port_reconfigure_bond(struct port *);
-static void port_send_learning_packets(struct port *);
-
-static void mirror_create(struct bridge *, struct ovsrec_mirror *);
+static void port_configure(struct port *);
+static struct lacp_settings *port_configure_lacp(struct port *,
+ struct lacp_settings *);
+static void port_configure_bond(struct port *, struct bond_settings *,
+ uint32_t *bond_stable_ids);
+
+static void bridge_configure_mirrors(struct bridge *);
+static struct mirror *mirror_create(struct bridge *,
+ const struct ovsrec_mirror *);
static void mirror_destroy(struct mirror *);
-static void mirror_reconfigure(struct bridge *);
-static void mirror_reconfigure_one(struct mirror *, struct ovsrec_mirror *);
-static bool vlan_is_mirrored(const struct mirror *, int vlan);
+static bool mirror_configure(struct mirror *, const struct ovsrec_mirror *);
+static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *);
static struct iface *iface_create(struct port *port,
const struct ovsrec_interface *if_cfg);
static void iface_destroy(struct iface *);
static struct iface *iface_lookup(const struct bridge *, const char *name);
static struct iface *iface_find(const char *name);
-static struct iface *iface_from_dp_ifidx(const struct bridge *,
- uint16_t dp_ifidx);
+static struct iface *iface_from_ofp_port(const struct bridge *,
+ uint16_t ofp_port);
static void iface_set_mac(struct iface *);
static void iface_set_ofport(const struct ovsrec_interface *, int64_t ofport);
-static void iface_update_qos(struct iface *, const struct ovsrec_qos *);
-static void iface_update_cfm(struct iface *);
+static void iface_configure_qos(struct iface *, const struct ovsrec_qos *);
+static void iface_configure_cfm(struct iface *);
static bool iface_refresh_cfm_stats(struct iface *iface);
static bool iface_get_carrier(const struct iface *);
static bool iface_is_synthetic(const struct iface *);
struct shash *);
static void shash_to_ovs_idl_map(struct shash *,
char ***keys, char ***values, size_t *n);
-
-/* Hooks into ofproto processing. */
-static struct ofhooks bridge_ofhooks;
\f
/* Public functions. */
ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids);
/* Register unixctl commands. */
- unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
unixctl_command_register("cfm/show", cfm_unixctl_show, NULL);
unixctl_command_register("qos/show", qos_unixctl_show, NULL);
unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows,
{
struct bridge *br, *next_br;
- LIST_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
+ HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
bridge_destroy(br);
}
ovsdb_idl_destroy(idl);
}
-/* Performs configuration that is only necessary once at ovs-vswitchd startup,
- * but for which the ovs-vswitchd configuration 'cfg' is required. */
-static void
-bridge_configure_once(const struct ovsrec_open_vswitch *cfg)
-{
- static bool already_configured_once;
- struct sset bridge_names;
- struct sset dpif_names, dpif_types;
- const char *type;
- size_t i;
-
- /* Only do this once per ovs-vswitchd run. */
- if (already_configured_once) {
- return;
- }
- already_configured_once = true;
-
- stats_timer = time_msec() + STATS_INTERVAL;
-
- /* Get all the configured bridges' names from 'cfg' into 'bridge_names'. */
- sset_init(&bridge_names);
- for (i = 0; i < cfg->n_bridges; i++) {
- sset_add(&bridge_names, cfg->bridges[i]->name);
- }
-
- /* Iterate over all system dpifs and delete any of them that do not appear
- * in 'cfg'. */
- sset_init(&dpif_names);
- sset_init(&dpif_types);
- dp_enumerate_types(&dpif_types);
- SSET_FOR_EACH (type, &dpif_types) {
- const char *name;
-
- dp_enumerate_names(type, &dpif_names);
-
- /* Delete each dpif whose name is not in 'bridge_names'. */
- SSET_FOR_EACH (name, &dpif_names) {
- if (!sset_contains(&bridge_names, name)) {
- struct dpif *dpif;
- int retval;
-
- retval = dpif_open(name, type, &dpif);
- if (!retval) {
- dpif_delete(dpif);
- dpif_close(dpif);
- }
- }
- }
- }
- sset_destroy(&bridge_names);
- sset_destroy(&dpif_names);
- sset_destroy(&dpif_types);
-}
-
-/* Callback for iterate_and_prune_ifaces(). */
-static bool
-check_iface(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED)
-{
- if (!iface->netdev) {
- /* We already reported a related error, don't bother duplicating it. */
- return false;
- }
-
- if (iface->dp_ifidx < 0) {
- VLOG_ERR("%s interface not in %s, dropping",
- iface->name, dpif_name(br->dpif));
- return false;
- }
-
- VLOG_DBG("%s has interface %s on port %d", dpif_name(br->dpif),
- iface->name, iface->dp_ifidx);
- return true;
-}
-
-/* Callback for iterate_and_prune_ifaces(). */
-static bool
-set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
- void *aux OVS_UNUSED)
-{
- /* Set policing attributes. */
- netdev_set_policing(iface->netdev,
- iface->cfg->ingress_policing_rate,
- iface->cfg->ingress_policing_burst);
-
- /* Set MAC address of internal interfaces other than the local
- * interface. */
- iface_set_mac(iface);
-
- return true;
-}
-
-/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
- * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
- * deletes from 'br' any ports that no longer have any interfaces. */
-static void
-iterate_and_prune_ifaces(struct bridge *br,
- bool (*cb)(struct bridge *, struct iface *,
- void *aux),
- void *aux)
-{
- struct port *port, *next_port;
-
- HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
- struct iface *iface, *next_iface;
-
- LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
- if (!cb(br, iface, aux)) {
- iface_set_ofport(iface->cfg, -1);
- iface_destroy(iface);
- }
- }
-
- if (list_is_empty(&port->ifaces)) {
- VLOG_WARN("%s port has no interfaces, dropping", port->name);
- port_destroy(port);
- }
- }
-}
-
/* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP
* addresses and ports into '*managersp' and '*n_managersp'. The caller is
* responsible for freeing '*managersp' (with free()).
static void
bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
{
- struct shash old_br, new_br;
- struct shash_node *node;
- struct bridge *br, *next;
struct sockaddr_in *managers;
- size_t n_managers;
- size_t i;
+ struct bridge *br, *next;
int sflow_bridge_number;
+ size_t n_managers;
COVERAGE_INC(bridge_reconfigure);
- collect_in_band_managers(ovs_cfg, &managers, &n_managers);
-
- /* Collect old and new bridges. */
- shash_init(&old_br);
- shash_init(&new_br);
- LIST_FOR_EACH (br, node, &all_bridges) {
- shash_add(&old_br, br->name, br);
+ /* Create and destroy "struct bridge"s, "struct port"s, and "struct
+ * iface"s according to 'ovs_cfg', with only very minimal configuration
+ * otherwise.
+ *
+ * This is purely an update to bridge data structures. Nothing is pushed
+ * down to ofproto or lower layers. */
+ add_del_bridges(ovs_cfg);
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ bridge_add_del_ports(br);
}
- for (i = 0; i < ovs_cfg->n_bridges; i++) {
- const struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i];
- if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
- VLOG_WARN("more than one bridge named %s", br_cfg->name);
+
+ /* Delete all datapaths and datapath ports that are no longer configured.
+ *
+ * The kernel will reject any attempt to add a given port to a datapath if
+ * that port already belongs to a different datapath, so we must do all
+ * port deletions before any port additions. A datapath always has a
+ * "local port" so we must delete not-configured datapaths too. */
+ bridge_del_ofprotos();
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ if (br->ofproto) {
+ bridge_del_ofproto_ports(br);
}
}
- /* Get rid of deleted bridges and add new bridges. */
- LIST_FOR_EACH_SAFE (br, next, node, &all_bridges) {
- struct ovsrec_bridge *br_cfg = shash_find_data(&new_br, br->name);
- if (br_cfg) {
- br->cfg = br_cfg;
- } else {
+ /* Create datapaths and datapath ports that are missing.
+ *
+ * After this is done, we have our final set of bridges, ports, and
+ * interfaces. Every "struct bridge" has an ofproto, every "struct port"
+ * has at least one iface, every "struct iface" has a valid ofp_port and
+ * netdev. */
+ HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+ if (!br->ofproto && !bridge_add_ofprotos(br)) {
bridge_destroy(br);
}
}
- SHASH_FOR_EACH (node, &new_br) {
- const char *br_name = node->name;
- const struct ovsrec_bridge *br_cfg = node->data;
- br = shash_find_data(&old_br, br_name);
- if (br) {
- /* If the bridge datapath type has changed, we need to tear it
- * down and recreate. */
- if (strcmp(br->cfg->datapath_type, br_cfg->datapath_type)) {
- bridge_destroy(br);
- bridge_create(br_cfg);
- }
- } else {
- bridge_create(br_cfg);
- }
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ bridge_refresh_ofp_port(br);
+ bridge_add_ofproto_ports(br);
}
- shash_destroy(&old_br);
- shash_destroy(&new_br);
- /* Reconfigure all bridges. */
- LIST_FOR_EACH (br, node, &all_bridges) {
- bridge_reconfigure_one(br);
- }
+ /* Complete the configuration. */
+ sflow_bridge_number = 0;
+ collect_in_band_managers(ovs_cfg, &managers, &n_managers);
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ struct port *port;
- /* Add and delete ports on all datapaths.
- *
- * The kernel will reject any attempt to add a given port to a datapath if
- * that port already belongs to a different datapath, so we must do all
- * port deletions before any port additions. */
- LIST_FOR_EACH (br, node, &all_bridges) {
- struct dpif_port_dump dump;
- struct shash want_ifaces;
- struct dpif_port dpif_port;
-
- bridge_get_all_ifaces(br, &want_ifaces);
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- if (!shash_find(&want_ifaces, dpif_port.name)
- && strcmp(dpif_port.name, br->name)) {
- int retval = dpif_port_del(br->dpif, dpif_port.port_no);
- if (retval) {
- VLOG_WARN("failed to remove %s interface from %s: %s",
- dpif_port.name, dpif_name(br->dpif),
- strerror(retval));
- }
+ HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+ struct iface *iface;
+
+ port_configure(port);
+
+ HMAP_FOR_EACH (iface, ofp_port_node, &br->ifaces) {
+ iface_configure_cfm(iface);
+ iface_configure_qos(iface, port->cfg->qos);
+ iface_set_mac(iface);
}
}
- shash_destroy(&want_ifaces);
+ bridge_configure_mirrors(br);
+ bridge_configure_datapath_id(br);
+ bridge_configure_remotes(br, managers, n_managers);
+ bridge_configure_netflow(br);
+ bridge_configure_sflow(br, &sflow_bridge_number);
}
- LIST_FOR_EACH (br, node, &all_bridges) {
- struct shash cur_ifaces, want_ifaces;
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
+ free(managers);
- /* Get the set of interfaces currently in this datapath. */
- shash_init(&cur_ifaces);
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- struct dpif_port *port_info = xmalloc(sizeof *port_info);
- dpif_port_clone(port_info, &dpif_port);
- shash_add(&cur_ifaces, dpif_port.name, port_info);
- }
+ /* ovs-vswitchd has completed initialization, so allow the process that
+ * forked us to exit successfully. */
+ daemonize_complete();
+}
- /* Get the set of interfaces we want on this datapath. */
- bridge_get_all_ifaces(br, &want_ifaces);
+/* Iterate over all ofprotos and delete any of them that do not have a
+ * configured bridge or that are the wrong type. */
+static void
+bridge_del_ofprotos(void)
+{
+ struct sset names;
+ struct sset types;
+ const char *type;
- hmap_clear(&br->ifaces);
- SHASH_FOR_EACH (node, &want_ifaces) {
- const char *if_name = node->name;
- struct iface *iface = node->data;
- struct dpif_port *dpif_port;
- const char *type;
- int error;
+ sset_init(&names);
+ sset_init(&types);
+ ofproto_enumerate_types(&types);
+ SSET_FOR_EACH (type, &types) {
+ const char *name;
- type = iface ? iface->type : "internal";
- dpif_port = shash_find_data(&cur_ifaces, if_name);
-
- /* If we have a port or a netdev already, and it's not the type we
- * want, then delete the port (if any) and close the netdev (if
- * any). */
- if ((dpif_port && strcmp(dpif_port->type, type))
- || (iface && iface->netdev
- && strcmp(type, netdev_get_type(iface->netdev)))) {
- if (dpif_port) {
- error = ofproto_port_del(br->ofproto, dpif_port->port_no);
- if (error) {
- continue;
- }
- dpif_port = NULL;
- }
- if (iface) {
- if (iface->port->bond) {
- /* The bond has a pointer to the netdev, so remove it
- * from the bond before closing the netdev. The slave
- * will get added back to the bond later, after a new
- * netdev is available. */
- bond_slave_unregister(iface->port->bond, iface);
- }
- netdev_close(iface->netdev);
- iface->netdev = NULL;
- }
+ ofproto_enumerate_names(type, &names);
+ SSET_FOR_EACH (name, &names) {
+ struct bridge *br = bridge_lookup(name);
+ if (!br || strcmp(type, br->type)) {
+ ofproto_delete(name, type);
}
+ }
+ }
+ sset_destroy(&names);
+ sset_destroy(&types);
+}
- /* If the port doesn't exist or we don't have the netdev open,
- * we need to do more work. */
- if (!dpif_port || (iface && !iface->netdev)) {
- struct netdev_options options;
- struct netdev *netdev;
- struct shash args;
-
- /* First open the network device. */
- options.name = if_name;
- options.type = type;
- options.args = &args;
- options.ethertype = NETDEV_ETH_TYPE_NONE;
+static bool
+bridge_add_ofprotos(struct bridge *br)
+{
+ int error = ofproto_create(br->name, br->type, &br->ofproto);
+ if (error) {
+ VLOG_ERR("failed to create bridge %s: %s", br->name, strerror(error));
+ return false;
+ }
+ return true;
+}
- shash_init(&args);
- if (iface) {
- shash_from_ovs_idl_map(iface->cfg->key_options,
- iface->cfg->value_options,
- iface->cfg->n_options, &args);
- }
- error = netdev_open(&options, &netdev);
- shash_destroy(&args);
+static void
+port_configure(struct port *port)
+{
+ const struct ovsrec_port *cfg = port->cfg;
+ struct bond_settings bond_settings;
+ struct lacp_settings lacp_settings;
+ struct ofproto_bundle_settings s;
+ struct iface *iface;
- if (error) {
- VLOG_WARN("could not open network device %s (%s)",
- if_name, strerror(error));
- continue;
- }
+ /* Get name. */
+ s.name = port->name;
- /* Then add the port if we haven't already. */
- if (!dpif_port) {
- error = dpif_port_add(br->dpif, netdev, NULL);
- if (error) {
- netdev_close(netdev);
- if (error == EFBIG) {
- VLOG_ERR("ran out of valid port numbers on %s",
- dpif_name(br->dpif));
- break;
- } else {
- VLOG_WARN("failed to add %s interface to %s: %s",
- if_name, dpif_name(br->dpif),
- strerror(error));
- continue;
- }
- }
- }
+ /* Get slaves. */
+ s.n_slaves = 0;
+ s.slaves = xmalloc(list_size(&port->ifaces) * sizeof *s.slaves);
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ s.slaves[s.n_slaves++] = iface->ofp_port;
+ }
- /* Update 'iface'. */
- if (iface) {
- iface->netdev = netdev;
- }
- } else if (iface && iface->netdev) {
- struct shash args;
-
- shash_init(&args);
- shash_from_ovs_idl_map(iface->cfg->key_options,
- iface->cfg->value_options,
- iface->cfg->n_options, &args);
- netdev_set_config(iface->netdev, &args);
- shash_destroy(&args);
+ /* Get VLAN tag. */
+ s.vlan = -1;
+ if (cfg->tag) {
+ if (list_is_short(&port->ifaces)) {
+ if (*cfg->tag >= 0 && *cfg->tag <= 4095) {
+ s.vlan = *cfg->tag;
+ VLOG_DBG("port %s: assigning VLAN tag %d", port->name, s.vlan);
}
+ } else {
+ /* It's possible that bonded, VLAN-tagged ports make sense. Maybe
+ * they even work as-is. But they have not been tested. */
+ VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
+ port->name);
}
- shash_destroy(&want_ifaces);
+ }
- SHASH_FOR_EACH (node, &cur_ifaces) {
- struct dpif_port *port_info = node->data;
- dpif_port_destroy(port_info);
- free(port_info);
- }
- shash_destroy(&cur_ifaces);
+ /* Get VLAN trunks. */
+ s.trunks = NULL;
+ if (s.vlan < 0 && cfg->n_trunks) {
+ s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
+ } else if (s.vlan >= 0 && cfg->n_trunks) {
+ VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
+ port->name);
}
- sflow_bridge_number = 0;
- LIST_FOR_EACH (br, node, &all_bridges) {
- uint8_t ea[ETH_ADDR_LEN];
- uint64_t dpid;
- struct iface *local_iface;
- struct iface *hw_addr_iface;
- char *dpid_string;
- bridge_fetch_dp_ifaces(br);
+ /* Get LACP settings. */
+ s.lacp = port_configure_lacp(port, &lacp_settings);
+ if (s.lacp) {
+ size_t i = 0;
- /* Delete interfaces that cannot be opened.
- *
- * From this point forward we are guaranteed that every "struct iface"
- * has nonnull 'netdev' and correct 'dp_ifidx'. */
- iterate_and_prune_ifaces(br, check_iface, NULL);
-
- /* Pick local port hardware address, datapath ID. */
- bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
- local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
- if (local_iface) {
- int error = netdev_set_etheraddr(local_iface->netdev, ea);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
- "Ethernet address: %s",
- br->name, strerror(error));
- }
+ s.lacp_slaves = xmalloc(s.n_slaves * sizeof *s.lacp_slaves);
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ iface_configure_lacp(iface, &s.lacp_slaves[i++]);
}
- memcpy(br->ea, ea, ETH_ADDR_LEN);
-
- dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
- ofproto_set_datapath_id(br->ofproto, dpid);
+ } else {
+ s.lacp_slaves = NULL;
+ }
- dpid_string = xasprintf("%016"PRIx64, dpid);
- ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
- free(dpid_string);
+ /* Get bond settings. */
+ if (s.n_slaves > 1) {
+ s.bond = &bond_settings;
+ s.bond_stable_ids = xmalloc(s.n_slaves * sizeof *s.bond_stable_ids);
+ port_configure_bond(port, &bond_settings, s.bond_stable_ids);
+ } else {
+ s.bond = NULL;
+ s.bond_stable_ids = NULL;
+ }
- /* Set NetFlow configuration on this bridge. */
- if (br->cfg->netflow) {
- struct ovsrec_netflow *nf_cfg = br->cfg->netflow;
- struct netflow_options opts;
+ /* Register. */
+ ofproto_bundle_register(port->bridge->ofproto, port, &s);
- memset(&opts, 0, sizeof opts);
+ /* Clean up. */
+ free(s.trunks);
+ free(s.lacp_slaves);
+ free(s.bond_stable_ids);
+}
- dpif_get_netflow_ids(br->dpif, &opts.engine_type, &opts.engine_id);
- if (nf_cfg->engine_type) {
- opts.engine_type = *nf_cfg->engine_type;
- }
- if (nf_cfg->engine_id) {
- opts.engine_id = *nf_cfg->engine_id;
- }
+/* Pick local port hardware address and datapath ID for 'br'. */
+static void
+bridge_configure_datapath_id(struct bridge *br)
+{
+ uint8_t ea[ETH_ADDR_LEN];
+ uint64_t dpid;
+ struct iface *local_iface;
+ struct iface *hw_addr_iface;
+ char *dpid_string;
- opts.active_timeout = nf_cfg->active_timeout;
- if (!opts.active_timeout) {
- opts.active_timeout = -1;
- } else if (opts.active_timeout < 0) {
- VLOG_WARN("bridge %s: active timeout interval set to negative "
- "value, using default instead (%d seconds)", br->name,
- NF_ACTIVE_TIMEOUT_DEFAULT);
- opts.active_timeout = -1;
- }
+ bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
+ local_iface = iface_from_ofp_port(br, OFPP_LOCAL);
+ if (local_iface) {
+ int error = netdev_set_etheraddr(local_iface->netdev, ea);
+ if (error) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
+ "Ethernet address: %s",
+ br->name, strerror(error));
+ }
+ }
+ memcpy(br->ea, ea, ETH_ADDR_LEN);
- opts.add_id_to_iface = nf_cfg->add_id_to_interface;
- if (opts.add_id_to_iface) {
- if (opts.engine_id > 0x7f) {
- VLOG_WARN("bridge %s: netflow port mangling may conflict "
- "with another vswitch, choose an engine id less "
- "than 128", br->name);
- }
- if (hmap_count(&br->ports) > 508) {
- VLOG_WARN("bridge %s: netflow port mangling will conflict "
- "with another port when more than 508 ports are "
- "used", br->name);
- }
- }
+ dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
+ ofproto_set_datapath_id(br->ofproto, dpid);
- sset_init(&opts.collectors);
- sset_add_array(&opts.collectors,
- nf_cfg->targets, nf_cfg->n_targets);
- if (ofproto_set_netflow(br->ofproto, &opts)) {
- VLOG_ERR("bridge %s: problem setting netflow collectors",
- br->name);
- }
- sset_destroy(&opts.collectors);
- } else {
- ofproto_set_netflow(br->ofproto, NULL);
- }
+ dpid_string = xasprintf("%016"PRIx64, dpid);
+ ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
+ free(dpid_string);
+}
- /* Set sFlow configuration on this bridge. */
- if (br->cfg->sflow) {
- const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow;
- struct ovsrec_controller **controllers;
- struct ofproto_sflow_options oso;
- size_t n_controllers;
+/* Set NetFlow configuration on 'br'. */
+static void
+bridge_configure_netflow(struct bridge *br)
+{
+ struct ovsrec_netflow *cfg = br->cfg->netflow;
+ struct netflow_options opts;
- memset(&oso, 0, sizeof oso);
+ if (!cfg) {
+ ofproto_set_netflow(br->ofproto, NULL);
+ return;
+ }
- sset_init(&oso.targets);
- sset_add_array(&oso.targets,
- sflow_cfg->targets, sflow_cfg->n_targets);
+ memset(&opts, 0, sizeof opts);
- oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
- if (sflow_cfg->sampling) {
- oso.sampling_rate = *sflow_cfg->sampling;
- }
+ /* Get default NetFlow configuration from datapath.
+ * Apply overrides from 'cfg'. */
+ ofproto_get_netflow_ids(br->ofproto, &opts.engine_type, &opts.engine_id);
+ if (cfg->engine_type) {
+ opts.engine_type = *cfg->engine_type;
+ }
+ if (cfg->engine_id) {
+ opts.engine_id = *cfg->engine_id;
+ }
- oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL;
- if (sflow_cfg->polling) {
- oso.polling_interval = *sflow_cfg->polling;
- }
+ /* Configure active timeout interval. */
+ opts.active_timeout = cfg->active_timeout;
+ if (!opts.active_timeout) {
+ opts.active_timeout = -1;
+ } else if (opts.active_timeout < 0) {
+ VLOG_WARN("bridge %s: active timeout interval set to negative "
+ "value, using default instead (%d seconds)", br->name,
+ NF_ACTIVE_TIMEOUT_DEFAULT);
+ opts.active_timeout = -1;
+ }
- oso.header_len = SFL_DEFAULT_HEADER_SIZE;
- if (sflow_cfg->header) {
- oso.header_len = *sflow_cfg->header;
- }
+ /* Add engine ID to interface number to disambiguate bridgs? */
+ opts.add_id_to_iface = cfg->add_id_to_interface;
+ if (opts.add_id_to_iface) {
+ if (opts.engine_id > 0x7f) {
+ VLOG_WARN("bridge %s: NetFlow port mangling may conflict with "
+ "another vswitch, choose an engine id less than 128",
+ br->name);
+ }
+ if (hmap_count(&br->ports) > 508) {
+ VLOG_WARN("bridge %s: NetFlow port mangling will conflict with "
+ "another port when more than 508 ports are used",
+ br->name);
+ }
+ }
- oso.sub_id = sflow_bridge_number++;
- oso.agent_device = sflow_cfg->agent;
+ /* Collectors. */
+ sset_init(&opts.collectors);
+ sset_add_array(&opts.collectors, cfg->targets, cfg->n_targets);
- oso.control_ip = NULL;
- n_controllers = bridge_get_controllers(br, &controllers);
- for (i = 0; i < n_controllers; i++) {
- if (controllers[i]->local_ip) {
- oso.control_ip = controllers[i]->local_ip;
- break;
- }
- }
- ofproto_set_sflow(br->ofproto, &oso);
+ /* Configure. */
+ if (ofproto_set_netflow(br->ofproto, &opts)) {
+ VLOG_ERR("bridge %s: problem setting netflow collectors", br->name);
+ }
+ sset_destroy(&opts.collectors);
+}
- sset_destroy(&oso.targets);
- } else {
- ofproto_set_sflow(br->ofproto, NULL);
- }
+/* Set sFlow configuration on 'br'. */
+static void
+bridge_configure_sflow(struct bridge *br, int *sflow_bridge_number)
+{
+ const struct ovsrec_sflow *cfg = br->cfg->sflow;
+ struct ovsrec_controller **controllers;
+ struct ofproto_sflow_options oso;
+ size_t n_controllers;
+ size_t i;
- /* Update the controller and related settings. It would be more
- * straightforward to call this from bridge_reconfigure_one(), but we
- * can't do it there for two reasons. First, and most importantly, at
- * that point we don't know the dp_ifidx of any interfaces that have
- * been added to the bridge (because we haven't actually added them to
- * the datapath). Second, at that point we haven't set the datapath ID
- * yet; when a controller is configured, resetting the datapath ID will
- * immediately disconnect from the controller, so it's better to set
- * the datapath ID before the controller. */
- bridge_reconfigure_remotes(br, managers, n_managers);
+ if (!cfg) {
+ ofproto_set_sflow(br->ofproto, NULL);
+ return;
}
- LIST_FOR_EACH (br, node, &all_bridges) {
- struct port *port;
- br->has_bonded_ports = false;
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
+ memset(&oso, 0, sizeof oso);
- port_reconfigure_lacp(port);
- port_reconfigure_bond(port);
+ sset_init(&oso.targets);
+ sset_add_array(&oso.targets, cfg->targets, cfg->n_targets);
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- iface_update_qos(iface, port->cfg->qos);
- }
- }
+ oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
+ if (cfg->sampling) {
+ oso.sampling_rate = *cfg->sampling;
}
- LIST_FOR_EACH (br, node, &all_bridges) {
- iterate_and_prune_ifaces(br, set_iface_properties, NULL);
+
+ oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL;
+ if (cfg->polling) {
+ oso.polling_interval = *cfg->polling;
}
- /* Some reconfiguration operations require the bridge to have been run at
- * least once. */
- LIST_FOR_EACH (br, node, &all_bridges) {
- struct iface *iface;
+ oso.header_len = SFL_DEFAULT_HEADER_SIZE;
+ if (cfg->header) {
+ oso.header_len = *cfg->header;
+ }
- bridge_run_one(br);
+ oso.sub_id = (*sflow_bridge_number)++;
+ oso.agent_device = cfg->agent;
- HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
- iface_update_cfm(iface);
+ oso.control_ip = NULL;
+ n_controllers = bridge_get_controllers(br, &controllers);
+ for (i = 0; i < n_controllers; i++) {
+ if (controllers[i]->local_ip) {
+ oso.control_ip = controllers[i]->local_ip;
+ break;
}
}
+ ofproto_set_sflow(br->ofproto, &oso);
- free(managers);
+ sset_destroy(&oso.targets);
+}
- /* ovs-vswitchd has completed initialization, so allow the process that
- * forked us to exit successfully. */
- daemonize_complete();
+static bool
+bridge_has_bond_fake_iface(const struct bridge *br, const char *name)
+{
+ const struct port *port = port_lookup(br, name);
+ return port && port_is_bond_fake_iface(port);
}
-static const char *
-get_ovsrec_key_value(const struct ovsdb_idl_row *row,
- const struct ovsdb_idl_column *column,
- const char *key)
+static bool
+port_is_bond_fake_iface(const struct port *port)
{
- const struct ovsdb_datum *datum;
- union ovsdb_atom atom;
- unsigned int idx;
+ return port->cfg->bond_fake_iface && !list_is_short(&port->ifaces);
+}
- datum = ovsdb_idl_get(row, column, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
+static void
+add_del_bridges(const struct ovsrec_open_vswitch *cfg)
+{
+ struct bridge *br, *next;
+ struct shash new_br;
+ size_t i;
+
+ /* Collect new bridges' names and types. */
+ shash_init(&new_br);
+ for (i = 0; i < cfg->n_bridges; i++) {
+ const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
+ if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
+ VLOG_WARN("bridge %s specified twice", br_cfg->name);
+ }
+ }
+
+ /* Get rid of deleted bridges or those whose types have changed.
+ * Update 'cfg' of bridges that still exist. */
+ HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+ br->cfg = shash_find_data(&new_br, br->name);
+ if (!br->cfg || strcmp(br->type, ofproto_normalize_type(
+ br->cfg->datapath_type))) {
+ bridge_destroy(br);
+ }
+ }
+
+ /* Add new bridges. */
+ for (i = 0; i < cfg->n_bridges; i++) {
+ const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
+ struct bridge *br = bridge_lookup(br_cfg->name);
+ if (!br) {
+ bridge_create(br_cfg);
+ }
+ }
+
+ shash_destroy(&new_br);
+}
+
+/* Delete each ofproto port on 'br' that doesn't have a corresponding "struct
+ * iface".
+ *
+ * The kernel will reject any attempt to add a given port to a datapath if that
+ * port already belongs to a different datapath, so we must do all port
+ * deletions before any port additions. */
+static void
+bridge_del_ofproto_ports(struct bridge *br)
+{
+ struct ofproto_port_dump dump;
+ struct ofproto_port ofproto_port;
+
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ const char *name = ofproto_port.name;
+ struct iface *iface;
+ const char *type;
+ int error;
+
+ /* Ignore the local port. We can't change it anyhow. */
+ if (!strcmp(name, br->name)) {
+ continue;
+ }
+
+ /* Get the type that 'ofproto_port' should have (ordinarily the
+ * type of its corresponding iface) or NULL if it should be
+ * deleted. */
+ iface = iface_lookup(br, name);
+ type = (iface ? iface->type
+ : bridge_has_bond_fake_iface(br, name) ? "internal"
+ : NULL);
+
+ /* If it's the wrong type then delete the ofproto port. */
+ if (type
+ && !strcmp(ofproto_port.type, type)
+ && (!iface || !iface->netdev
+ || !strcmp(netdev_get_type(iface->netdev), type))) {
+ continue;
+ }
+ error = ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
+ if (error) {
+ VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
+ br->name, name, strerror(error));
+ }
+ if (iface) {
+ netdev_close(iface->netdev);
+ iface->netdev = NULL;
+ }
+ }
+}
+
+static void
+iface_set_ofp_port(struct iface *iface, int ofp_port)
+{
+ struct bridge *br = iface->port->bridge;
+
+ assert(iface->ofp_port < 0 && ofp_port >= 0);
+ iface->ofp_port = ofp_port;
+ hmap_insert(&br->ifaces, &iface->ofp_port_node, hash_int(ofp_port, 0));
+ iface_set_ofport(iface->cfg, ofp_port);
+}
+
+static void
+bridge_refresh_ofp_port(struct bridge *br)
+{
+ struct ofproto_port_dump dump;
+ struct ofproto_port ofproto_port;
+ struct port *port;
+
+ /* Clear all the "ofp_port"es. */
+ hmap_clear(&br->ifaces);
+ HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+ struct iface *iface;
+
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ iface->ofp_port = -1;
+ }
+ }
+
+ /* Obtain the correct "ofp_port"s from ofproto. */
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ struct iface *iface = iface_lookup(br, ofproto_port.name);
+ if (iface) {
+ if (iface->ofp_port >= 0) {
+ VLOG_WARN("bridge %s: interface %s reported twice",
+ br->name, ofproto_port.name);
+ } else if (iface_from_ofp_port(br, ofproto_port.ofp_port)) {
+ VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
+ br->name, ofproto_port.ofp_port);
+ } else {
+ iface_set_ofp_port(iface, ofproto_port.ofp_port);
+ }
+ }
+ }
+}
+
+/* Add an ofproto port for any "struct iface" that doesn't have one.
+ * Delete any "struct iface" for which this fails.
+ * Delete any "struct port" that thereby ends up with no ifaces. */
+static void
+bridge_add_ofproto_ports(struct bridge *br)
+{
+ struct port *port, *next_port;
+
+ HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
+ struct iface *iface, *next_iface;
+ struct ofproto_port ofproto_port;
+
+ LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
+ struct shash args;
+ int error;
+
+ /* Open the netdev or reconfigure it. */
+ shash_init(&args);
+ shash_from_ovs_idl_map(iface->cfg->key_options,
+ iface->cfg->value_options,
+ iface->cfg->n_options, &args);
+ if (!iface->netdev) {
+ struct netdev_options options;
+ options.name = iface->name;
+ options.type = iface->type;
+ options.args = &args;
+ options.ethertype = NETDEV_ETH_TYPE_NONE;
+ error = netdev_open(&options, &iface->netdev);
+ } else {
+ error = netdev_set_config(iface->netdev, &args);
+ }
+ shash_destroy(&args);
+ if (error) {
+ VLOG_WARN("could not %s network device %s (%s)",
+ iface->netdev ? "reconfigure" : "open",
+ iface->name, strerror(error));
+ }
+
+ /* Add the port, if necessary. */
+ if (iface->netdev && iface->ofp_port < 0) {
+ uint16_t ofp_port;
+ int error;
+
+ error = ofproto_port_add(br->ofproto, iface->netdev,
+ &ofp_port);
+ if (!error) {
+ iface_set_ofp_port(iface, ofp_port);
+ } else {
+ netdev_close(iface->netdev);
+ iface->netdev = NULL;
+ }
+ }
+
+ /* Delete the iface if */
+ if (iface->netdev && iface->ofp_port >= 0) {
+ VLOG_DBG("bridge %s: interface %s is on port %d",
+ br->name, iface->name, iface->ofp_port);
+ } else {
+ if (iface->netdev) {
+ VLOG_ERR("bridge %s: missing %s interface, dropping",
+ br->name, iface->name);
+ } else {
+ /* We already reported a related error, don't bother
+ * duplicating it. */
+ }
+ iface_set_ofport(iface->cfg, -1);
+ iface_destroy(iface);
+ }
+ }
+ if (list_is_empty(&port->ifaces)) {
+ VLOG_WARN("%s port has no interfaces, dropping", port->name);
+ port_destroy(port);
+ continue;
+ }
+
+ /* Add bond fake iface if necessary. */
+ if (port_is_bond_fake_iface(port)) {
+ if (ofproto_port_query_by_name(br->ofproto, port->name,
+ &ofproto_port)) {
+ struct netdev_options options;
+ struct netdev *netdev;
+ int error;
+
+ options.name = port->name;
+ options.type = "internal";
+ options.args = NULL;
+ options.ethertype = NETDEV_ETH_TYPE_NONE;
+ error = netdev_open(&options, &netdev);
+ if (!error) {
+ ofproto_port_add(br->ofproto, netdev, NULL);
+ netdev_close(netdev);
+ } else {
+ VLOG_WARN("could not open network device %s (%s)",
+ port->name, strerror(error));
+ }
+ } else {
+ /* Already exists, nothing to do. */
+ ofproto_port_destroy(&ofproto_port);
+ }
+ }
+ }
+}
+
+static const char *
+get_ovsrec_key_value(const struct ovsdb_idl_row *row,
+ const struct ovsdb_idl_column *column,
+ const char *key)
+{
+ const struct ovsdb_datum *datum;
+ union ovsdb_atom atom;
+ unsigned int idx;
+
+ datum = ovsdb_idl_get(row, column, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
atom.string = (char *) key;
idx = ovsdb_datum_find_key(datum, &atom, OVSDB_TYPE_STRING);
return idx == UINT_MAX ? NULL : datum->values[idx].string;
struct iface *iface;
/* Mirror output ports don't participate. */
- if (port->is_mirror_output_port) {
+ if (ofproto_is_mirror_output_bundle(br->ofproto, port)) {
continue;
}
/* The local port doesn't count (since we're trying to choose its
* MAC address anyway). */
- if (iface->dp_ifidx == ODPP_LOCAL) {
+ if (iface->ofp_port == OFPP_LOCAL) {
continue;
}
ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
}
-
ovsrec_interface_set_link_state(iface->cfg,
iface_get_carrier(iface) ? "up" : "down");
size_t i;
mon = iface->cfg->monitor;
- cfm = ofproto_iface_get_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+ cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
if (!cfm || !mon) {
return false;
static bool
iface_refresh_lacp_stats(struct iface *iface)
{
- bool *db_current = iface->cfg->lacp_current;
- bool changed = false;
+ struct ofproto *ofproto = iface->port->bridge->ofproto;
+ int old = iface->cfg->lacp_current ? *iface->cfg->lacp_current : -1;
+ int new = ofproto_port_is_lacp_current(ofproto, iface->ofp_port);
- if (iface->port->lacp) {
- bool current = lacp_slave_is_current(iface->port->lacp, iface);
-
- if (!db_current || *db_current != current) {
- changed = true;
- ovsrec_interface_set_lacp_current(iface->cfg, ¤t, 1);
- }
- } else if (db_current) {
- changed = true;
- ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
+ if (old != new) {
+ bool current = new;
+ ovsrec_interface_set_lacp_current(iface->cfg, ¤t, new >= 0);
}
-
- return changed;
+ return old != new;
}
static void
/* Let each bridge do the work that it needs to do. */
datapath_destroyed = false;
- LIST_FOR_EACH (br, node, &all_bridges) {
- int error = bridge_run_one(br);
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ int error = ofproto_run(br->ofproto);
if (error) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_ERR_RL(&rl, "bridge %s: datapath was destroyed externally, "
if (cfg) {
struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl);
- bridge_configure_once(cfg);
bridge_reconfigure(cfg);
ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
struct ovsdb_idl_txn *txn;
txn = ovsdb_idl_txn_create(idl);
- LIST_FOR_EACH (br, node, &all_bridges) {
+ HMAP_FOR_EACH (br, node, &all_bridges) {
struct port *port;
HMAP_FOR_EACH (port, hmap_node, &br->ports) {
bool changed = false;
txn = ovsdb_idl_txn_create(idl);
- LIST_FOR_EACH (br, node, &all_bridges) {
+ HMAP_FOR_EACH (br, node, &all_bridges) {
struct port *port;
HMAP_FOR_EACH (port, hmap_node, &br->ports) {
{
struct bridge *br;
- LIST_FOR_EACH (br, node, &all_bridges) {
- struct port *port;
-
+ HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_wait(br->ofproto);
- mac_learning_wait(br->ml);
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- port_wait(port);
- }
}
ovsdb_idl_wait(idl);
poll_timer_wait_until(stats_timer);
poll_timer_wait_until(db_limiter);
}
}
-
-/* Forces 'br' to revalidate all of its flows. This is appropriate when 'br''s
- * configuration changes. */
-static void
-bridge_flush(struct bridge *br)
-{
- COVERAGE_INC(bridge_flush);
- br->flush = true;
-}
-\f
-/* Bridge unixctl user interface functions. */
-static void
-bridge_unixctl_fdb_show(struct unixctl_conn *conn,
- const char *args, void *aux OVS_UNUSED)
-{
- struct ds ds = DS_EMPTY_INITIALIZER;
- const struct bridge *br;
- const struct mac_entry *e;
-
- br = bridge_lookup(args);
- if (!br) {
- unixctl_command_reply(conn, 501, "no such bridge");
- return;
- }
-
- ds_put_cstr(&ds, " port VLAN MAC Age\n");
- LIST_FOR_EACH (e, lru_node, &br->ml->lrus) {
- struct port *port = e->port.p;
- ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n",
- port_get_an_iface(port)->dp_ifidx,
- e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
- }
- unixctl_command_reply(conn, 200, ds_cstr(&ds));
- ds_destroy(&ds);
-}
\f
/* CFM unixctl user interface functions. */
static void
return;
}
- cfm = ofproto_iface_get_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+ cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
if (!cfm) {
unixctl_command_reply(conn, 501, "CFM not enabled");
}
\f
/* Bridge reconfiguration functions. */
-static struct bridge *
+static void
bridge_create(const struct ovsrec_bridge *br_cfg)
{
struct bridge *br;
- int error;
assert(!bridge_lookup(br_cfg->name));
br = xzalloc(sizeof *br);
- error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type,
- &br->dpif);
- if (error) {
- free(br);
- return NULL;
- }
-
- error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks,
- br, &br->ofproto);
- if (error) {
- VLOG_ERR("failed to create switch %s: %s", br_cfg->name,
- strerror(error));
- dpif_delete(br->dpif);
- dpif_close(br->dpif);
- free(br);
- return NULL;
- }
-
br->name = xstrdup(br_cfg->name);
+ br->type = xstrdup(ofproto_normalize_type(br_cfg->datapath_type));
br->cfg = br_cfg;
- br->ml = mac_learning_create();
/* Derive the default Ethernet address from the bridge's UUID. This should
* be unique and it will be stable between ovs-vswitchd runs. */
hmap_init(&br->ports);
hmap_init(&br->ifaces);
- shash_init(&br->iface_by_name);
+ hmap_init(&br->iface_by_name);
+ hmap_init(&br->mirrors);
- br->flush = false;
-
- list_push_back(&all_bridges, &br->node);
-
- VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
-
- return br;
+ hmap_insert(&all_bridges, &br->node, hash_string(br->name, 0));
}
static void
bridge_destroy(struct bridge *br)
{
if (br) {
- struct port *port, *next;
- int error;
- int i;
+ struct mirror *mirror, *next_mirror;
+ struct port *port, *next_port;
- HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
+ HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
port_destroy(port);
}
- for (i = 0; i < MAX_MIRRORS; i++) {
- mirror_destroy(br->mirrors[i]);
+ HMAP_FOR_EACH_SAFE (mirror, next_mirror, hmap_node, &br->mirrors) {
+ mirror_destroy(mirror);
}
- list_remove(&br->node);
+ hmap_remove(&all_bridges, &br->node);
ofproto_destroy(br->ofproto);
- error = dpif_delete(br->dpif);
- if (error && error != ENOENT) {
- VLOG_ERR("failed to delete %s: %s",
- dpif_name(br->dpif), strerror(error));
- }
- dpif_close(br->dpif);
- mac_learning_destroy(br->ml);
hmap_destroy(&br->ifaces);
hmap_destroy(&br->ports);
- shash_destroy(&br->iface_by_name);
- free(br->synth_local_iface.type);
+ hmap_destroy(&br->iface_by_name);
+ hmap_destroy(&br->mirrors);
free(br->name);
+ free(br->type);
free(br);
}
}
{
struct bridge *br;
- LIST_FOR_EACH (br, node, &all_bridges) {
+ HMAP_FOR_EACH_WITH_HASH (br, node, hash_string(name, 0), &all_bridges) {
if (!strcmp(br->name, name)) {
return br;
}
}
ofproto_reconnect_controllers(br->ofproto);
} else {
- LIST_FOR_EACH (br, node, &all_bridges) {
+ HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_reconnect_controllers(br->ofproto);
}
}
unixctl_command_reply(conn, 200, NULL);
}
-static int
-bridge_run_one(struct bridge *br)
-{
- struct port *port;
- int error;
-
- error = ofproto_run1(br->ofproto);
- if (error) {
- return error;
- }
-
- mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- port_run(port);
- }
-
- error = ofproto_run2(br->ofproto, br->flush);
- br->flush = false;
-
- return error;
-}
-
static size_t
bridge_get_controllers(const struct bridge *br,
struct ovsrec_controller ***controllersp)
return n_controllers;
}
+/* Adds and deletes "struct port"s and "struct iface"s under 'br' to match
+ * those configured in 'br->cfg'. */
static void
-bridge_reconfigure_one(struct bridge *br)
+bridge_add_del_ports(struct bridge *br)
{
- enum ofproto_fail_mode fail_mode;
struct port *port, *next;
struct shash_node *node;
struct shash new_ports;
br->name, name);
}
}
- if (!shash_find(&new_ports, br->name)) {
- struct dpif_port dpif_port;
- char *type;
-
+ if (bridge_get_controllers(br, NULL)
+ && !shash_find(&new_ports, br->name)) {
VLOG_WARN("bridge %s: no port named %s, synthesizing one",
br->name, br->name);
- dpif_port_query_by_number(br->dpif, ODPP_LOCAL, &dpif_port);
- type = xstrdup(dpif_port.type ? dpif_port.type : "internal");
- dpif_port_destroy(&dpif_port);
-
br->synth_local_port.interfaces = &br->synth_local_ifacep;
br->synth_local_port.n_interfaces = 1;
br->synth_local_port.name = br->name;
br->synth_local_iface.name = br->name;
- free(br->synth_local_iface.type);
- br->synth_local_iface.type = type;
+ br->synth_local_iface.type = "internal";
br->synth_local_ifacep = &br->synth_local_iface;
}
/* Get rid of deleted ports.
- * Get rid of deleted interfaces on ports that still exist. */
+ * Get rid of deleted interfaces on ports that still exist.
+ * Update 'cfg' of ports that still exist. */
HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
- const struct ovsrec_port *port_cfg;
-
- port_cfg = shash_find_data(&new_ports, port->name);
- if (!port_cfg) {
+ port->cfg = shash_find_data(&new_ports, port->name);
+ if (!port->cfg) {
port_destroy(port);
} else {
- port_del_ifaces(port, port_cfg);
+ port_del_ifaces(port);
}
}
/* Create new ports.
- * Add new interfaces to existing ports.
- * Reconfigure existing ports. */
+ * Add new interfaces to existing ports. */
SHASH_FOR_EACH (node, &new_ports) {
struct port *port = port_lookup(br, node->name);
if (!port) {
- port = port_create(br, node->name);
+ struct ovsrec_port *cfg = node->data;
+ port = port_create(br, cfg);
}
-
- port_reconfigure(port, node->data);
+ port_add_ifaces(port);
if (list_is_empty(&port->ifaces)) {
VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
br->name, port->name);
}
}
shash_destroy(&new_ports);
-
- /* Set the fail-mode */
- fail_mode = !br->cfg->fail_mode
- || !strcmp(br->cfg->fail_mode, "standalone")
- ? OFPROTO_FAIL_STANDALONE
- : OFPROTO_FAIL_SECURE;
- if (ofproto_get_fail_mode(br->ofproto) != fail_mode
- && !ofproto_has_primary_controller(br->ofproto)) {
- ofproto_flush_flows(br->ofproto);
- }
- ofproto_set_fail_mode(br->ofproto, fail_mode);
-
- /* Delete all flows if we're switching from connected to standalone or vice
- * versa. (XXX Should we delete all flows if we are switching from one
- * controller to another?) */
-
- /* Configure OpenFlow controller connection snooping. */
- if (!ofproto_has_snoops(br->ofproto)) {
- struct sset snoops;
-
- sset_init(&snoops);
- sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop",
- ovs_rundir(), br->name));
- ofproto_set_snoops(br->ofproto, &snoops);
- sset_destroy(&snoops);
- }
-
- mirror_reconfigure(br);
}
/* Initializes 'oc' appropriately as a management service controller for
struct in_addr ip;
/* If there's no local interface or no IP address, give up. */
- local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
+ local_iface = iface_from_ofp_port(br, OFPP_LOCAL);
if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
return;
}
}
static void
-bridge_reconfigure_remotes(struct bridge *br,
- const struct sockaddr_in *managers,
- size_t n_managers)
+bridge_configure_remotes(struct bridge *br,
+ const struct sockaddr_in *managers, size_t n_managers)
{
const char *disable_ib_str, *queue_id_str;
bool disable_in_band = false;
struct ovsrec_controller **controllers;
size_t n_controllers;
- bool had_primary;
+
+ enum ofproto_fail_mode fail_mode;
struct ofproto_controller *ocs;
size_t n_ocs;
} else {
ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
}
- had_primary = ofproto_has_primary_controller(br->ofproto);
n_controllers = bridge_get_controllers(br, &controllers);
if (!strncmp(c->target, "punix:", 6)
|| !strncmp(c->target, "unix:", 5)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- /* Prevent remote ovsdb-server users from accessing arbitrary Unix
- * domain sockets and overwriting arbitrary local files. */
- VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller "
- "\"%s\" due to possibility for remote exploit",
- dpif_name(br->dpif), c->target);
- continue;
- }
-
- bridge_configure_local_iface_netdev(br, c);
- bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]);
- if (disable_in_band) {
- ocs[n_ocs].band = OFPROTO_OUT_OF_BAND;
- }
- n_ocs++;
- }
-
- ofproto_set_controllers(br->ofproto, ocs, n_ocs);
- free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
- free(ocs);
-
- if (had_primary != ofproto_has_primary_controller(br->ofproto)) {
- ofproto_flush_flows(br->ofproto);
- }
-
- /* If there are no controllers and the bridge is in standalone
- * mode, set up a flow that matches every packet and directs
- * them to OFPP_NORMAL (which goes to us). Otherwise, the
- * switch is in secure mode and we won't pass any traffic until
- * a controller has been defined and it tells us to do so. */
- if (!n_controllers
- && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
- union ofp_action action;
- struct cls_rule rule;
-
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
- cls_rule_init_catchall(&rule, 0);
- ofproto_add_flow(br->ofproto, &rule, &action, 1);
- }
-}
-
-static void
-bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces)
-{
- struct port *port;
-
- shash_init(ifaces);
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- shash_add_once(ifaces, iface->name, iface);
- }
- if (!list_is_short(&port->ifaces) && port->cfg->bond_fake_iface) {
- shash_add_once(ifaces, port->name, NULL);
- }
- }
-}
-
-/* For robustness, in case the administrator moves around datapath ports behind
- * our back, we re-check all the datapath port numbers here.
- *
- * This function will set the 'dp_ifidx' members of interfaces that have
- * disappeared to -1, so only call this function from a context where those
- * 'struct iface's will be removed from the bridge. Otherwise, the -1
- * 'dp_ifidx'es will cause trouble later when we try to send them to the
- * datapath, which doesn't support UINT16_MAX+1 ports. */
-static void
-bridge_fetch_dp_ifaces(struct bridge *br)
-{
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
- struct port *port;
-
- /* Reset all interface numbers. */
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- iface->dp_ifidx = -1;
- }
- }
- hmap_clear(&br->ifaces);
-
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- struct iface *iface = iface_lookup(br, dpif_port.name);
- if (iface) {
- if (iface->dp_ifidx >= 0) {
- VLOG_WARN("%s reported interface %s twice",
- dpif_name(br->dpif), dpif_port.name);
- } else if (iface_from_dp_ifidx(br, dpif_port.port_no)) {
- VLOG_WARN("%s reported interface %"PRIu16" twice",
- dpif_name(br->dpif), dpif_port.port_no);
- } else {
- iface->dp_ifidx = dpif_port.port_no;
- hmap_insert(&br->ifaces, &iface->dp_ifidx_node,
- hash_int(iface->dp_ifidx, 0));
- }
-
- iface_set_ofport(iface->cfg,
- (iface->dp_ifidx >= 0
- ? odp_port_to_ofp_port(iface->dp_ifidx)
- : -1));
- }
- }
-}
-\f
-/* Bridge packet processing functions. */
-
-static bool
-set_dst(struct dst *dst, const struct flow *flow,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags)
-{
- dst->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
- : in_port->vlan >= 0 ? in_port->vlan
- : flow->vlan_tci == 0 ? OFP_VLAN_NONE
- : vlan_tci_to_vid(flow->vlan_tci));
-
- dst->iface = (!out_port->bond
- ? port_get_an_iface(out_port)
- : bond_choose_output_slave(out_port->bond, flow,
- dst->vlan, tags));
-
- return dst->iface != NULL;
-}
-
-static int
-mirror_mask_ffs(mirror_mask_t mask)
-{
- BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
- return ffs(mask);
-}
-
-static void
-dst_set_init(struct dst_set *set)
-{
- set->dsts = set->builtin;
- set->n = 0;
- set->allocated = ARRAY_SIZE(set->builtin);
-}
-
-static void
-dst_set_add(struct dst_set *set, const struct dst *dst)
-{
- if (set->n >= set->allocated) {
- size_t new_allocated;
- struct dst *new_dsts;
-
- new_allocated = set->allocated * 2;
- new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
- memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
-
- dst_set_free(set);
-
- set->dsts = new_dsts;
- set->allocated = new_allocated;
- }
- set->dsts[set->n++] = *dst;
-}
-
-static void
-dst_set_free(struct dst_set *set)
-{
- if (set->dsts != set->builtin) {
- free(set->dsts);
- }
-}
-
-static bool
-dst_is_duplicate(const struct dst_set *set, const struct dst *test)
-{
- size_t i;
- for (i = 0; i < set->n; i++) {
- if (set->dsts[i].vlan == test->vlan
- && set->dsts[i].iface == test->iface) {
- return true;
- }
- }
- return false;
-}
-
-static bool
-port_trunks_vlan(const struct port *port, uint16_t vlan)
-{
- return (port->vlan < 0
- && (!port->trunks || bitmap_is_set(port->trunks, vlan)));
-}
-
-static bool
-port_includes_vlan(const struct port *port, uint16_t vlan)
-{
- return vlan == port->vlan || port_trunks_vlan(port, vlan);
-}
-
-static bool
-port_is_floodable(const struct port *port)
-{
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- if (!ofproto_port_is_floodable(port->bridge->ofproto,
- iface->dp_ifidx)) {
- return false;
- }
- }
- return true;
-}
-
-/* Returns an arbitrary interface within 'port'. */
-static struct iface *
-port_get_an_iface(const struct port *port)
-{
- return CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
-}
-
-static void
-compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- struct dst_set *set, tag_type *tags, uint16_t *nf_output_iface)
-{
- struct dst dst;
-
- if (out_port == FLOOD_PORT) {
- struct port *port;
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- if (port != in_port
- && port_is_floodable(port)
- && port_includes_vlan(port, vlan)
- && !port->is_mirror_output_port
- && set_dst(&dst, flow, in_port, port, tags)) {
- dst_set_add(set, &dst);
- }
- }
- *nf_output_iface = NF_OUT_FLOOD;
- } else if (out_port && set_dst(&dst, flow, in_port, out_port, tags)) {
- dst_set_add(set, &dst);
- *nf_output_iface = dst.iface->dp_ifidx;
- }
-}
-
-static void
-compose_mirror_dsts(const struct bridge *br, const struct flow *flow,
- uint16_t vlan, const struct port *in_port,
- struct dst_set *set, tag_type *tags)
-{
- mirror_mask_t mirrors;
- int flow_vlan;
- size_t i;
-
- mirrors = in_port->src_mirrors;
- for (i = 0; i < set->n; i++) {
- mirrors |= set->dsts[i].iface->port->dst_mirrors;
- }
-
- if (!mirrors) {
- return;
- }
-
- flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
- if (flow_vlan == 0) {
- flow_vlan = OFP_VLAN_NONE;
- }
-
- while (mirrors) {
- struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1];
- if (!m->n_vlans || vlan_is_mirrored(m, vlan)) {
- struct dst dst;
-
- if (m->out_port) {
- if (set_dst(&dst, flow, in_port, m->out_port, tags)
- && !dst_is_duplicate(set, &dst)) {
- dst_set_add(set, &dst);
- }
- } else {
- struct port *port;
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- if (port_includes_vlan(port, m->out_vlan)
- && set_dst(&dst, flow, in_port, port, tags))
- {
- if (port->vlan < 0) {
- dst.vlan = m->out_vlan;
- }
- if (dst_is_duplicate(set, &dst)) {
- continue;
- }
-
- /* Use the vlan tag on the original flow instead of
- * the one passed in the vlan parameter. This ensures
- * that we compare the vlan from before any implicit
- * tagging tags place. This is necessary because
- * dst->vlan is the final vlan, after removing implicit
- * tags. */
- if (port == in_port && dst.vlan == flow_vlan) {
- /* Don't send out input port on same VLAN. */
- continue;
- }
- dst_set_add(set, &dst);
- }
- }
- }
- }
- mirrors &= mirrors - 1;
- }
-}
-
-static void
-compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags, struct ofpbuf *actions,
- uint16_t *nf_output_iface)
-{
- uint16_t initial_vlan, cur_vlan;
- const struct dst *dst;
- struct dst_set set;
-
- dst_set_init(&set);
- compose_dsts(br, flow, vlan, in_port, out_port, &set, tags,
- nf_output_iface);
- compose_mirror_dsts(br, flow, vlan, in_port, &set, tags);
-
- /* Output all the packets we can without having to change the VLAN. */
- initial_vlan = vlan_tci_to_vid(flow->vlan_tci);
- if (initial_vlan == 0) {
- initial_vlan = OFP_VLAN_NONE;
- }
- for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
- if (dst->vlan != initial_vlan) {
- continue;
- }
- nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
- }
-
- /* Then output the rest. */
- cur_vlan = initial_vlan;
- for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
- if (dst->vlan == initial_vlan) {
- continue;
- }
- if (dst->vlan != cur_vlan) {
- if (dst->vlan == OFP_VLAN_NONE) {
- nl_msg_put_flag(actions, ODP_ACTION_ATTR_STRIP_VLAN);
- } else {
- ovs_be16 tci;
- tci = htons(dst->vlan & VLAN_VID_MASK);
- tci |= flow->vlan_tci & htons(VLAN_PCP_MASK);
- nl_msg_put_be16(actions, ODP_ACTION_ATTR_SET_DL_TCI, tci);
- }
- cur_vlan = dst->vlan;
- }
- nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
- }
-
- dst_set_free(&set);
-}
-
-/* Returns the effective vlan of a packet, taking into account both the
- * 802.1Q header and implicitly tagged ports. A value of 0 indicates that
- * the packet is untagged and -1 indicates it has an invalid header and
- * should be dropped. */
-static int flow_get_vlan(struct bridge *br, const struct flow *flow,
- struct port *in_port, bool have_packet)
-{
- int vlan = vlan_tci_to_vid(flow->vlan_tci);
- if (in_port->vlan >= 0) {
- if (vlan) {
- if (have_packet) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
- "packet received on port %s configured with "
- "implicit VLAN %"PRIu16,
- br->name, vlan, in_port->name, in_port->vlan);
- }
- return -1;
- }
- vlan = in_port->vlan;
- } else {
- if (!port_includes_vlan(in_port, vlan)) {
- if (have_packet) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
- "packet received on port %s not configured for "
- "trunking VLAN %d",
- br->name, vlan, in_port->name, vlan);
- }
- return -1;
- }
- }
-
- return vlan;
-}
-
-/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
- * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to
- * indicate this; newer upstream kernels use gratuitous ARP requests. */
-static bool
-is_gratuitous_arp(const struct flow *flow)
-{
- return (flow->dl_type == htons(ETH_TYPE_ARP)
- && eth_addr_is_broadcast(flow->dl_dst)
- && (flow->nw_proto == ARP_OP_REPLY
- || (flow->nw_proto == ARP_OP_REQUEST
- && flow->nw_src == flow->nw_dst)));
-}
-
-static void
-update_learning_table(struct bridge *br, const struct flow *flow, int vlan,
- struct port *in_port)
-{
- struct mac_entry *mac;
-
- if (!mac_learning_may_learn(br->ml, flow->dl_src, vlan)) {
- return;
- }
-
- mac = mac_learning_insert(br->ml, flow->dl_src, vlan);
- if (is_gratuitous_arp(flow)) {
- /* We don't want to learn from gratuitous ARP packets that are
- * reflected back over bond slaves so we lock the learning table. */
- if (!in_port->bond) {
- mac_entry_set_grat_arp_lock(mac);
- } else if (mac_entry_is_grat_arp_locked(mac)) {
- return;
- }
- }
-
- if (mac_entry_is_new(mac) || mac->port.p != in_port) {
- /* The log messages here could actually be useful in debugging,
- * so keep the rate limit relatively high. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
- VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
- "on port %s in VLAN %d",
- br->name, ETH_ADDR_ARGS(flow->dl_src),
- in_port->name, vlan);
-
- mac->port.p = in_port;
- ofproto_revalidate(br->ofproto, mac_learning_changed(br->ml, mac));
- }
-}
-
-/* Determines whether packets in 'flow' within 'br' should be forwarded or
- * dropped. Returns true if they may be forwarded, false if they should be
- * dropped.
- *
- * If 'have_packet' is true, it indicates that the caller is processing a
- * received packet. If 'have_packet' is false, then the caller is just
- * revalidating an existing flow because configuration has changed. Either
- * way, 'have_packet' only affects logging (there is no point in logging errors
- * during revalidation).
- *
- * Sets '*in_portp' to the input port. This will be a null pointer if
- * flow->in_port does not designate a known input port (in which case
- * is_admissible() returns false).
- *
- * When returning true, sets '*vlanp' to the effective VLAN of the input
- * packet, as returned by flow_get_vlan().
- *
- * May also add tags to '*tags', although the current implementation only does
- * so in one special case.
- */
-static bool
-is_admissible(struct bridge *br, const struct flow *flow, bool have_packet,
- tag_type *tags, int *vlanp, struct port **in_portp)
-{
- struct iface *in_iface;
- struct port *in_port;
- int vlan;
-
- /* Find the interface and port structure for the received packet. */
- in_iface = iface_from_dp_ifidx(br, flow->in_port);
- if (!in_iface) {
- /* No interface? Something fishy... */
- if (have_packet) {
- /* Odd. A few possible reasons here:
- *
- * - We deleted an interface but there are still a few packets
- * queued up from it.
- *
- * - Someone externally added an interface (e.g. with "ovs-dpctl
- * add-if") that we don't know about.
- *
- * - Packet arrived on the local port but the local port is not
- * one of our bridge ports.
- */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
- "interface %"PRIu16, br->name, flow->in_port);
- }
-
- *in_portp = NULL;
- return false;
- }
- *in_portp = in_port = in_iface->port;
- *vlanp = vlan = flow_get_vlan(br, flow, in_port, have_packet);
- if (vlan < 0) {
- return false;
- }
-
- /* Drop frames for reserved multicast addresses. */
- if (eth_addr_is_reserved(flow->dl_dst)) {
- return false;
- }
-
- /* Drop frames on ports reserved for mirroring. */
- if (in_port->is_mirror_output_port) {
- if (have_packet) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
- "%s, which is reserved exclusively for mirroring",
- br->name, in_port->name);
- }
- return false;
- }
-
- if (in_port->bond) {
- struct mac_entry *mac;
-
- switch (bond_check_admissibility(in_port->bond, in_iface,
- flow->dl_dst, tags)) {
- case BV_ACCEPT:
- break;
-
- case BV_DROP:
- return false;
-
- case BV_DROP_IF_MOVED:
- mac = mac_learning_lookup(br->ml, flow->dl_src, vlan, NULL);
- if (mac && mac->port.p != in_port &&
- (!is_gratuitous_arp(flow)
- || mac_entry_is_grat_arp_locked(mac))) {
- return false;
- }
- break;
- }
- }
-
- return true;
-}
-
-/* If the composed actions may be applied to any packet in the given 'flow',
- * returns true. Otherwise, the actions should only be applied to 'packet', or
- * not at all, if 'packet' was NULL. */
-static bool
-process_flow(struct bridge *br, const struct flow *flow,
- const struct ofpbuf *packet, struct ofpbuf *actions,
- tag_type *tags, uint16_t *nf_output_iface)
-{
- struct port *in_port;
- struct port *out_port;
- struct mac_entry *mac;
- int vlan;
-
- /* Check whether we should drop packets in this flow. */
- if (!is_admissible(br, flow, packet != NULL, tags, &vlan, &in_port)) {
- out_port = NULL;
- goto done;
- }
-
- /* Learn source MAC (but don't try to learn from revalidation). */
- if (packet) {
- update_learning_table(br, flow, vlan, in_port);
- }
-
- /* Determine output port. */
- mac = mac_learning_lookup(br->ml, flow->dl_dst, vlan, tags);
- if (mac) {
- out_port = mac->port.p;
- } else if (!packet && !eth_addr_is_multicast(flow->dl_dst)) {
- /* If we are revalidating but don't have a learning entry then
- * eject the flow. Installing a flow that floods packets opens
- * up a window of time where we could learn from a packet reflected
- * on a bond and blackhole packets before the learning table is
- * updated to reflect the correct port. */
- return false;
- } else {
- out_port = FLOOD_PORT;
- }
-
- /* Don't send packets out their input ports. */
- if (in_port == out_port) {
- out_port = NULL;
- }
-
-done:
- if (in_port) {
- compose_actions(br, flow, vlan, in_port, out_port, tags, actions,
- nf_output_iface);
- }
-
- return true;
-}
-
-static bool
-bridge_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
- struct ofpbuf *actions, tag_type *tags,
- uint16_t *nf_output_iface, void *br_)
-{
- struct bridge *br = br_;
-
- COVERAGE_INC(bridge_process_flow);
- return process_flow(br, flow, packet, actions, tags, nf_output_iface);
-}
-
-static bool
-bridge_special_ofhook_cb(const struct flow *flow,
- const struct ofpbuf *packet, void *br_)
-{
- struct iface *iface;
- struct bridge *br = br_;
-
- iface = iface_from_dp_ifidx(br, flow->in_port);
-
- if (flow->dl_type == htons(ETH_TYPE_LACP)) {
- if (iface && iface->port->lacp && packet) {
- const struct lacp_pdu *pdu = parse_lacp_packet(packet);
- if (pdu) {
- lacp_process_pdu(iface->port->lacp, iface, pdu);
- }
- }
- return false;
- }
-
- return true;
-}
-
-static void
-bridge_account_flow_ofhook_cb(const struct flow *flow, tag_type tags,
- const struct nlattr *actions,
- size_t actions_len,
- uint64_t n_bytes, void *br_)
-{
- struct bridge *br = br_;
- const struct nlattr *a;
- struct port *in_port;
- tag_type dummy = 0;
- unsigned int left;
- int vlan;
-
- /* Feed information from the active flows back into the learning table to
- * ensure that table is always in sync with what is actually flowing
- * through the datapath.
- *
- * We test that 'tags' is nonzero to ensure that only flows that include an
- * OFPP_NORMAL action are used for learning. This works because
- * bridge_normal_ofhook_cb() always sets a nonzero tag value. */
- if (tags && is_admissible(br, flow, false, &dummy, &vlan, &in_port)) {
- update_learning_table(br, flow, vlan, in_port);
- }
-
- /* Account for bond slave utilization. */
- if (!br->has_bonded_ports) {
- return;
- }
- NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
- if (nl_attr_type(a) == ODP_ACTION_ATTR_OUTPUT) {
- struct port *out_port = port_from_dp_ifidx(br, nl_attr_get_u32(a));
- if (out_port && out_port->bond) {
- uint16_t vlan = (flow->vlan_tci
- ? vlan_tci_to_vid(flow->vlan_tci)
- : OFP_VLAN_NONE);
- bond_account(out_port->bond, flow, vlan, n_bytes);
- }
- }
- }
-}
-
-static void
-bridge_account_checkpoint_ofhook_cb(void *br_)
-{
- struct bridge *br = br_;
- struct port *port;
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- if (port->bond) {
- bond_rebalance(port->bond,
- ofproto_get_revalidate_set(br->ofproto));
- }
- }
-}
-
-static uint16_t
-bridge_autopath_ofhook_cb(const struct flow *flow, uint32_t ofp_port,
- tag_type *tags, void *br_)
-{
- struct bridge *br = br_;
- uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
- struct port *port = port_from_dp_ifidx(br, odp_port);
- uint16_t ret;
-
- if (!port) {
- ret = ODPP_NONE;
- } else if (list_is_short(&port->ifaces)) {
- ret = odp_port;
- } else {
- struct iface *iface;
-
- /* Autopath does not support VLAN hashing. */
- iface = bond_choose_output_slave(port->bond, flow,
- OFP_VLAN_NONE, tags);
- ret = iface ? iface->dp_ifidx : ODPP_NONE;
- }
-
- return odp_port_to_ofp_port(ret);
-}
-
-static struct ofhooks bridge_ofhooks = {
- bridge_normal_ofhook_cb,
- bridge_special_ofhook_cb,
- bridge_account_flow_ofhook_cb,
- bridge_account_checkpoint_ofhook_cb,
- bridge_autopath_ofhook_cb,
-};
-\f
-/* Port functions. */
-
-static void
-lacp_send_pdu_cb(void *iface_, const struct lacp_pdu *pdu)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
- struct iface *iface = iface_;
- uint8_t ea[ETH_ADDR_LEN];
- int error;
-
- error = netdev_get_etheraddr(iface->netdev, ea);
- if (!error) {
- struct lacp_pdu *packet_pdu;
- struct ofpbuf packet;
-
- ofpbuf_init(&packet, 0);
- packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
- sizeof *packet_pdu);
- *packet_pdu = *pdu;
- error = netdev_send(iface->netdev, &packet);
- if (error) {
- VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
- "(%s)", iface->port->name, iface->name,
- strerror(error));
- }
- ofpbuf_uninit(&packet);
- } else {
- VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
- "%s (%s)", iface->port->name, iface->name,
- strerror(error));
- }
-}
-
-static void
-port_run(struct port *port)
-{
- if (port->lacp) {
- lacp_run(port->lacp, lacp_send_pdu_cb);
- }
-
- if (port->bond) {
- struct iface *iface;
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- bool may_enable = lacp_slave_may_enable(port->lacp, iface);
- bond_slave_set_lacp_may_enable(port->bond, iface, may_enable);
+ /* Prevent remote ovsdb-server users from accessing arbitrary Unix
+ * domain sockets and overwriting arbitrary local files. */
+ VLOG_ERR_RL(&rl, "bridge %s: not adding Unix domain socket "
+ "controller \"%s\" due to possibility for remote "
+ "exploit", br->name, c->target);
+ continue;
}
- bond_run(port->bond,
- ofproto_get_revalidate_set(port->bridge->ofproto),
- lacp_negotiated(port->lacp));
- if (bond_should_send_learning_packets(port->bond)) {
- port_send_learning_packets(port);
+ bridge_configure_local_iface_netdev(br, c);
+ bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]);
+ if (disable_in_band) {
+ ocs[n_ocs].band = OFPROTO_OUT_OF_BAND;
}
+ n_ocs++;
}
-}
-static void
-port_wait(struct port *port)
-{
- if (port->lacp) {
- lacp_wait(port->lacp);
- }
+ ofproto_set_controllers(br->ofproto, ocs, n_ocs);
+ free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
+ free(ocs);
+
+ /* Set the fail-mode. */
+ fail_mode = !br->cfg->fail_mode
+ || !strcmp(br->cfg->fail_mode, "standalone")
+ ? OFPROTO_FAIL_STANDALONE
+ : OFPROTO_FAIL_SECURE;
+ ofproto_set_fail_mode(br->ofproto, fail_mode);
+
+ /* Configure OpenFlow controller connection snooping. */
+ if (!ofproto_has_snoops(br->ofproto)) {
+ struct sset snoops;
- if (port->bond) {
- bond_wait(port->bond);
+ sset_init(&snoops);
+ sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop",
+ ovs_rundir(), br->name));
+ ofproto_set_snoops(br->ofproto, &snoops);
+ sset_destroy(&snoops);
}
}
+\f
+/* Port functions. */
static struct port *
-port_create(struct bridge *br, const char *name)
+port_create(struct bridge *br, const struct ovsrec_port *cfg)
{
struct port *port;
port = xzalloc(sizeof *port);
port->bridge = br;
- port->vlan = -1;
- port->trunks = NULL;
- port->name = xstrdup(name);
+ port->name = xstrdup(cfg->name);
+ port->cfg = cfg;
list_init(&port->ifaces);
hmap_insert(&br->ports, &port->hmap_node, hash_string(port->name, 0));
VLOG_INFO("created port %s on bridge %s", port->name, br->name);
- bridge_flush(br);
return port;
}
return value ? value : default_value;
}
+/* Deletes interfaces from 'port' that are no longer configured for it. */
static void
-port_del_ifaces(struct port *port, const struct ovsrec_port *cfg)
+port_del_ifaces(struct port *port)
{
struct iface *iface, *next;
struct sset new_ifaces;
/* Collect list of new interfaces. */
sset_init(&new_ifaces);
- for (i = 0; i < cfg->n_interfaces; i++) {
- const char *name = cfg->interfaces[i]->name;
- sset_add(&new_ifaces, name);
+ for (i = 0; i < port->cfg->n_interfaces; i++) {
+ const char *name = port->cfg->interfaces[i]->name;
+ const char *type = port->cfg->interfaces[i]->name;
+ if (strcmp(type, "null")) {
+ sset_add(&new_ifaces, name);
+ }
}
/* Get rid of deleted interfaces. */
sset_destroy(&new_ifaces);
}
-/* Expires all MAC learning entries associated with 'port' and forces ofproto
- * to revalidate every flow. */
+/* Adds new interfaces to 'port' and updates 'type' and 'cfg' members of
+ * existing ones. */
static void
-port_flush_macs(struct port *port)
+port_add_ifaces(struct port *port)
{
- struct bridge *br = port->bridge;
- struct mac_learning *ml = br->ml;
- struct mac_entry *mac, *next_mac;
+ struct shash new_ifaces;
+ struct shash_node *node;
+ size_t i;
- bridge_flush(br);
- LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
- if (mac->port.p == port) {
- mac_learning_expire(ml, mac);
+ /* Collect new ifaces. */
+ shash_init(&new_ifaces);
+ for (i = 0; i < port->cfg->n_interfaces; i++) {
+ const struct ovsrec_interface *cfg = port->cfg->interfaces[i];
+ if (strcmp(cfg->type, "null")
+ && !shash_add_once(&new_ifaces, cfg->name, cfg)) {
+ VLOG_WARN("port %s: %s specified twice as port interface",
+ port->name, cfg->name);
+ iface_set_ofport(cfg, -1);
}
}
-}
-
-static void
-port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
-{
- struct sset new_ifaces;
- bool need_flush = false;
- unsigned long *trunks;
- int vlan;
- size_t i;
-
- port->cfg = cfg;
- /* Add new interfaces and update 'cfg' member of existing ones. */
- sset_init(&new_ifaces);
- for (i = 0; i < cfg->n_interfaces; i++) {
- const struct ovsrec_interface *if_cfg = cfg->interfaces[i];
+ /* Create new interfaces.
+ * Update interface types and 'cfg' members. */
+ SHASH_FOR_EACH (node, &new_ifaces) {
+ const struct ovsrec_interface *cfg = node->data;
+ const char *iface_name = node->name;
struct iface *iface;
- const char *type;
- if (!sset_add(&new_ifaces, if_cfg->name)) {
- VLOG_WARN("port %s: %s specified twice as port interface",
- port->name, if_cfg->name);
- iface_set_ofport(if_cfg, -1);
- continue;
+ iface = iface_lookup(port->bridge, iface_name);
+ if (!iface) {
+ iface = iface_create(port, cfg);
+ } else {
+ iface->cfg = cfg;
}
/* Determine interface type. The local port always has type
* "internal". Other ports take their type from the database and
* default to "system" if none is specified. */
- type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
- : if_cfg->type[0] ? if_cfg->type
- : "system");
-
- iface = iface_lookup(port->bridge, if_cfg->name);
- if (!strcmp(type, "null")) {
- iface_destroy(iface);
- continue;
- } else if (iface) {
- if (iface->port != port) {
- VLOG_ERR("bridge %s: %s interface is on multiple ports, "
- "removing from %s",
- port->bridge->name, if_cfg->name, iface->port->name);
- continue;
- }
- iface->cfg = if_cfg;
- } else {
- iface = iface_create(port, if_cfg);
- }
-
- iface->type = type;
- }
- sset_destroy(&new_ifaces);
-
- /* Get VLAN tag. */
- vlan = -1;
- if (cfg->tag) {
- if (list_is_short(&port->ifaces)) {
- vlan = *cfg->tag;
- if (vlan >= 0 && vlan <= 4095) {
- VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan);
- } else {
- vlan = -1;
- }
- } else {
- /* It's possible that bonded, VLAN-tagged ports make sense. Maybe
- * they even work as-is. But they have not been tested. */
- VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
- port->name);
- }
- }
- if (port->vlan != vlan) {
- port->vlan = vlan;
- need_flush = true;
- }
-
- /* Get trunked VLANs. */
- trunks = NULL;
- if (vlan < 0 && cfg->n_trunks) {
- size_t n_errors;
-
- trunks = bitmap_allocate(4096);
- n_errors = 0;
- for (i = 0; i < cfg->n_trunks; i++) {
- int trunk = cfg->trunks[i];
- if (trunk >= 0) {
- bitmap_set1(trunks, trunk);
- } else {
- n_errors++;
- }
- }
- if (n_errors) {
- VLOG_ERR("port %s: invalid values for %zu trunk VLANs",
- port->name, cfg->n_trunks);
- }
- if (n_errors == cfg->n_trunks) {
- VLOG_ERR("port %s: no valid trunks, trunking all VLANs",
- port->name);
- bitmap_free(trunks);
- trunks = NULL;
- }
- } else if (vlan >= 0 && cfg->n_trunks) {
- VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
- port->name);
- }
- if (trunks == NULL
- ? port->trunks != NULL
- : port->trunks == NULL || !bitmap_equal(trunks, port->trunks, 4096)) {
- need_flush = true;
- }
- bitmap_free(port->trunks);
- port->trunks = trunks;
-
- if (need_flush) {
- port_flush_macs(port);
+ iface->type = (!strcmp(iface_name, port->bridge->name) ? "internal"
+ : cfg->type[0] ? cfg->type
+ : "system");
}
+ shash_destroy(&new_ifaces);
}
static void
if (port) {
struct bridge *br = port->bridge;
struct iface *iface, *next;
- int i;
- for (i = 0; i < MAX_MIRRORS; i++) {
- struct mirror *m = br->mirrors[i];
- if (m && m->out_port == port) {
- mirror_destroy(m);
- }
+ if (br->ofproto) {
+ ofproto_bundle_unregister(br->ofproto, port);
}
LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) {
VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
- bond_destroy(port->bond);
- lacp_destroy(port->lacp);
- port_flush_macs(port);
-
- bitmap_free(port->trunks);
free(port->name);
free(port);
}
}
-static struct port *
-port_from_dp_ifidx(const struct bridge *br, uint16_t dp_ifidx)
-{
- struct iface *iface = iface_from_dp_ifidx(br, dp_ifidx);
- return iface ? iface->port : NULL;
-}
-
static struct port *
port_lookup(const struct bridge *br, const char *name)
{
}
}
-static void
-iface_reconfigure_lacp(struct iface *iface)
-{
- struct lacp_slave_settings s;
- int priority, portid, key;
-
- portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
- priority = atoi(get_interface_other_config(iface->cfg,
- "lacp-port-priority", "0"));
- key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
- "0"));
-
- if (portid <= 0 || portid > UINT16_MAX) {
- portid = iface->dp_ifidx;
- }
-
- if (priority <= 0 || priority > UINT16_MAX) {
- priority = UINT16_MAX;
- }
-
- if (key < 0 || key > UINT16_MAX) {
- key = 0;
- }
-
- s.name = iface->name;
- s.id = portid;
- s.priority = priority;
- s.key = key;
- lacp_slave_register(iface->port->lacp, iface, &s);
-}
-
-static void
-port_reconfigure_lacp(struct port *port)
+static struct lacp_settings *
+port_configure_lacp(struct port *port, struct lacp_settings *s)
{
- static struct lacp_settings s;
- struct iface *iface;
- uint8_t sysid[ETH_ADDR_LEN];
- const char *sysid_str;
const char *lacp_time;
long long int custom_time;
int priority;
- if (!enable_lacp(port, &s.active)) {
- lacp_destroy(port->lacp);
- port->lacp = NULL;
- return;
- }
-
- sysid_str = get_port_other_config(port->cfg, "lacp-system-id", NULL);
- if (sysid_str && eth_addr_from_string(sysid_str, sysid)) {
- memcpy(s.id, sysid, ETH_ADDR_LEN);
- } else {
- memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
+ if (!enable_lacp(port, &s->active)) {
+ return NULL;
}
- s.name = port->name;
+ s->name = port->name;
+ memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN);
/* Prefer bondable links if unspecified. */
priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
"0"));
- s.priority = (priority > 0 && priority <= UINT16_MAX
- ? priority
- : UINT16_MAX - !list_is_short(&port->ifaces));
+ s->priority = (priority > 0 && priority <= UINT16_MAX
+ ? priority
+ : UINT16_MAX - !list_is_short(&port->ifaces));
+
+ s->heartbeat = !strcmp(get_port_other_config(port->cfg,
+ "lacp-heartbeat",
+ "false"), "true");
- s.heartbeat = !strcmp(get_port_other_config(port->cfg,
- "lacp-heartbeat",
- "false"), "true");
lacp_time = get_port_other_config(port->cfg, "lacp-time", "slow");
custom_time = atoi(lacp_time);
if (!strcmp(lacp_time, "fast")) {
- s.lacp_time = LACP_TIME_FAST;
+ s->lacp_time = LACP_TIME_FAST;
} else if (!strcmp(lacp_time, "slow")) {
- s.lacp_time = LACP_TIME_SLOW;
+ s->lacp_time = LACP_TIME_SLOW;
} else if (custom_time > 0) {
- s.lacp_time = LACP_TIME_CUSTOM;
- s.custom_time = custom_time;
+ s->lacp_time = LACP_TIME_CUSTOM;
+ s->custom_time = custom_time;
} else {
- s.lacp_time = LACP_TIME_SLOW;
+ s->lacp_time = LACP_TIME_SLOW;
}
- if (!port->lacp) {
- port->lacp = lacp_create();
+ return s;
+}
+
+static void
+iface_configure_lacp(struct iface *iface, struct lacp_slave_settings *s)
+{
+ int priority, portid, key;
+
+ portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
+ priority = atoi(get_interface_other_config(iface->cfg,
+ "lacp-port-priority", "0"));
+ key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
+ "0"));
+
+ if (portid <= 0 || portid > UINT16_MAX) {
+ portid = iface->ofp_port;
}
- lacp_configure(port->lacp, &s);
+ if (priority <= 0 || priority > UINT16_MAX) {
+ priority = UINT16_MAX;
+ }
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- iface_reconfigure_lacp(iface);
+ if (key < 0 || key > UINT16_MAX) {
+ key = 0;
}
+
+ s->name = iface->name;
+ s->id = portid;
+ s->priority = priority;
+ s->key = key;
}
static void
-port_reconfigure_bond(struct port *port)
+port_configure_bond(struct port *port, struct bond_settings *s,
+ uint32_t *bond_stable_ids)
{
- struct bond_settings s;
const char *detect_s;
struct iface *iface;
+ size_t i;
- if (list_is_short(&port->ifaces)) {
- /* Not a bonded port. */
- bond_destroy(port->bond);
- port->bond = NULL;
- return;
- }
-
- port->bridge->has_bonded_ports = true;
-
- s.name = port->name;
- s.balance = BM_SLB;
+ s->name = port->name;
+ s->balance = BM_SLB;
if (port->cfg->bond_mode
- && !bond_mode_from_string(&s.balance, port->cfg->bond_mode)) {
+ && !bond_mode_from_string(&s->balance, port->cfg->bond_mode)) {
VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s",
port->name, port->cfg->bond_mode,
- bond_mode_to_string(s.balance));
+ bond_mode_to_string(s->balance));
}
- s.detect = BLSM_CARRIER;
+ s->detect = BLSM_CARRIER;
detect_s = get_port_other_config(port->cfg, "bond-detect-mode", NULL);
- if (detect_s && !bond_detect_mode_from_string(&s.detect, detect_s)) {
+ if (detect_s && !bond_detect_mode_from_string(&s->detect, detect_s)) {
VLOG_WARN("port %s: unsupported bond-detect-mode %s, "
"defaulting to %s",
- port->name, detect_s, bond_detect_mode_to_string(s.detect));
+ port->name, detect_s, bond_detect_mode_to_string(s->detect));
}
- s.miimon_interval = atoi(
+ s->miimon_interval = atoi(
get_port_other_config(port->cfg, "bond-miimon-interval", "200"));
- if (s.miimon_interval < 100) {
- s.miimon_interval = 100;
+ if (s->miimon_interval < 100) {
+ s->miimon_interval = 100;
}
- s.up_delay = MAX(0, port->cfg->bond_updelay);
- s.down_delay = MAX(0, port->cfg->bond_downdelay);
- s.basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0"));
-
- s.rebalance_interval = atoi(
+ s->up_delay = MAX(0, port->cfg->bond_updelay);
+ s->down_delay = MAX(0, port->cfg->bond_downdelay);
+ s->basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0"));
+ s->rebalance_interval = atoi(
get_port_other_config(port->cfg, "bond-rebalance-interval", "10000"));
- if (s.rebalance_interval < 1000) {
- s.rebalance_interval = 1000;
+ if (s->rebalance_interval < 1000) {
+ s->rebalance_interval = 1000;
}
- s.fake_iface = port->cfg->bond_fake_iface;
-
- if (!port->bond) {
- port->bond = bond_create(&s);
- } else {
- if (bond_reconfigure(port->bond, &s)) {
- bridge_flush(port->bridge);
- }
- }
+ s->fake_iface = port->cfg->bond_fake_iface;
+ i = 0;
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
long long stable_id;
stable_id = atoll(get_interface_other_config(iface->cfg,
"bond-stable-id", "0"));
-
if (stable_id <= 0 || stable_id >= UINT32_MAX) {
- stable_id = odp_port_to_ofp_port(iface->dp_ifidx);
+ stable_id = iface->ofp_port;
}
-
- bond_slave_register(iface->port->bond, iface, stable_id,
- iface->netdev);
- }
-}
-
-static void
-port_send_learning_packets(struct port *port)
-{
- struct bridge *br = port->bridge;
- int error, n_packets, n_errors;
- struct mac_entry *e;
-
- error = n_packets = n_errors = 0;
- LIST_FOR_EACH (e, lru_node, &br->ml->lrus) {
- if (e->port.p != port) {
- int ret = bond_send_learning_packet(port->bond, e->mac, e->vlan);
- if (ret) {
- error = ret;
- n_errors++;
- }
- n_packets++;
- }
- }
-
- if (n_errors) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
- "packets, last error was: %s",
- port->name, n_errors, n_packets, strerror(error));
- } else {
- VLOG_DBG("bond %s: sent %d gratuitous learning packets",
- port->name, n_packets);
+ bond_stable_ids[i++] = stable_id;
}
}
\f
iface = xzalloc(sizeof *iface);
iface->port = port;
iface->name = xstrdup(name);
- iface->dp_ifidx = -1;
+ iface->ofp_port = -1;
iface->tag = tag_create_random();
iface->netdev = NULL;
iface->cfg = if_cfg;
- shash_add_assert(&br->iface_by_name, iface->name, iface);
+ hmap_insert(&br->iface_by_name, &iface->name_node, hash_string(name, 0));
list_push_back(&port->ifaces, &iface->port_elem);
VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
- bridge_flush(br);
-
return iface;
}
struct port *port = iface->port;
struct bridge *br = port->bridge;
- if (port->bond) {
- bond_slave_unregister(port->bond, iface);
- }
-
- if (port->lacp) {
- lacp_slave_unregister(port->lacp, iface);
+ if (br->ofproto && iface->ofp_port >= 0) {
+ ofproto_port_unregister(br->ofproto, iface->ofp_port);
}
- shash_find_and_delete_assert(&br->iface_by_name, iface->name);
-
- if (iface->dp_ifidx >= 0) {
- hmap_remove(&br->ifaces, &iface->dp_ifidx_node);
+ if (iface->ofp_port >= 0) {
+ hmap_remove(&br->ifaces, &iface->ofp_port_node);
}
list_remove(&iface->port_elem);
+ hmap_remove(&br->iface_by_name, &iface->name_node);
netdev_close(iface->netdev);
free(iface->name);
free(iface);
-
- bridge_flush(port->bridge);
}
}
static struct iface *
iface_lookup(const struct bridge *br, const char *name)
{
- return shash_find_data(&br->iface_by_name, name);
+ struct iface *iface;
+
+ HMAP_FOR_EACH_WITH_HASH (iface, name_node, hash_string(name, 0),
+ &br->iface_by_name) {
+ if (!strcmp(iface->name, name)) {
+ return iface;
+ }
+ }
+
+ return NULL;
}
static struct iface *
{
const struct bridge *br;
- LIST_FOR_EACH (br, node, &all_bridges) {
+ HMAP_FOR_EACH (br, node, &all_bridges) {
struct iface *iface = iface_lookup(br, name);
if (iface) {
}
static struct iface *
-iface_from_dp_ifidx(const struct bridge *br, uint16_t dp_ifidx)
+iface_from_ofp_port(const struct bridge *br, uint16_t ofp_port)
{
struct iface *iface;
- HMAP_FOR_EACH_IN_BUCKET (iface, dp_ifidx_node,
- hash_int(dp_ifidx, 0), &br->ifaces) {
- if (iface->dp_ifidx == dp_ifidx) {
+ HMAP_FOR_EACH_IN_BUCKET (iface, ofp_port_node,
+ hash_int(ofp_port, 0), &br->ifaces) {
+ if (iface->ofp_port == ofp_port) {
return iface;
}
}
if (!strcmp(iface->type, "internal")
&& iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
- if (iface->dp_ifidx == ODPP_LOCAL) {
+ if (iface->ofp_port == OFPP_LOCAL) {
VLOG_ERR("interface %s: ignoring mac in Interface record "
"(use Bridge record to set local port's mac)",
iface->name);
}
static void
-iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
+iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
{
if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) {
netdev_set_qos(iface->netdev, NULL, NULL);
shash_destroy(&details);
}
}
+
+ netdev_set_policing(iface->netdev,
+ iface->cfg->ingress_policing_rate,
+ iface->cfg->ingress_policing_burst);
}
static void
-iface_update_cfm(struct iface *iface)
+iface_configure_cfm(struct iface *iface)
{
size_t i;
struct cfm cfm;
mon = iface->cfg->monitor;
if (!mon) {
- ofproto_iface_clear_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+ ofproto_port_clear_cfm(iface->port->bridge->ofproto, iface->ofp_port);
return;
}
remote_mps[i] = mon->remote_mps[i]->mpid;
}
- ofproto_iface_set_cfm(iface->port->bridge->ofproto, iface->dp_ifidx,
- &cfm, remote_mps, mon->n_remote_mps);
+ ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port,
+ &cfm, remote_mps, mon->n_remote_mps);
free(remote_mps);
}
static struct mirror *
mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid)
{
- int i;
+ struct mirror *m;
- for (i = 0; i < MAX_MIRRORS; i++) {
- struct mirror *m = br->mirrors[i];
- if (m && uuid_equals(uuid, &m->uuid)) {
+ HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, uuid_hash(uuid), &br->mirrors) {
+ if (uuid_equals(uuid, &m->uuid)) {
return m;
}
}
}
static void
-mirror_reconfigure(struct bridge *br)
+bridge_configure_mirrors(struct bridge *br)
{
- unsigned long *rspan_vlans;
- struct port *port;
- int i;
+ const struct ovsdb_datum *mc;
+ unsigned long *flood_vlans;
+ struct mirror *m, *next;
+ size_t i;
/* Get rid of deleted mirrors. */
- for (i = 0; i < MAX_MIRRORS; i++) {
- struct mirror *m = br->mirrors[i];
- if (m) {
- const struct ovsdb_datum *mc;
- union ovsdb_atom atom;
-
- mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
- atom.uuid = br->mirrors[i]->uuid;
- if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
- mirror_destroy(m);
- }
+ mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
+ HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mirrors) {
+ union ovsdb_atom atom;
+
+ atom.uuid = m->uuid;
+ if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
+ mirror_destroy(m);
}
}
/* Add new mirrors and reconfigure existing ones. */
for (i = 0; i < br->cfg->n_mirrors; i++) {
- struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
+ const struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
struct mirror *m = mirror_find_by_uuid(br, &cfg->header_.uuid);
- if (m) {
- mirror_reconfigure_one(m, cfg);
- } else {
- mirror_create(br, cfg);
+ if (!m) {
+ m = mirror_create(br, cfg);
}
- }
-
- /* Update port reserved status. */
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- port->is_mirror_output_port = false;
- }
- for (i = 0; i < MAX_MIRRORS; i++) {
- struct mirror *m = br->mirrors[i];
- if (m && m->out_port) {
- m->out_port->is_mirror_output_port = true;
+ if (!mirror_configure(m, cfg)) {
+ mirror_destroy(m);
}
}
/* Update flooded vlans (for RSPAN). */
- rspan_vlans = NULL;
- if (br->cfg->n_flood_vlans) {
- rspan_vlans = bitmap_allocate(4096);
-
- for (i = 0; i < br->cfg->n_flood_vlans; i++) {
- int64_t vlan = br->cfg->flood_vlans[i];
- if (vlan >= 0 && vlan < 4096) {
- bitmap_set1(rspan_vlans, vlan);
- VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64,
- br->name, vlan);
- } else {
- VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN",
- br->name, vlan);
- }
- }
- }
- if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) {
- bridge_flush(br);
- mac_learning_flush(br->ml);
- }
+ flood_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans,
+ br->cfg->n_flood_vlans);
+ ofproto_set_flood_vlans(br->ofproto, flood_vlans);
+ bitmap_free(flood_vlans);
}
-static void
-mirror_create(struct bridge *br, struct ovsrec_mirror *cfg)
+static struct mirror *
+mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg)
{
struct mirror *m;
- size_t i;
- for (i = 0; ; i++) {
- if (i >= MAX_MIRRORS) {
- VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
- "cannot create %s", br->name, MAX_MIRRORS, cfg->name);
- return;
- }
- if (!br->mirrors[i]) {
- break;
- }
- }
-
- VLOG_INFO("created port mirror %s on bridge %s", cfg->name, br->name);
- bridge_flush(br);
- mac_learning_flush(br->ml);
-
- br->mirrors[i] = m = xzalloc(sizeof *m);
+ m = xzalloc(sizeof *m);
m->uuid = cfg->header_.uuid;
+ hmap_insert(&br->mirrors, &m->hmap_node, uuid_hash(&m->uuid));
m->bridge = br;
- m->idx = i;
m->name = xstrdup(cfg->name);
- sset_init(&m->src_ports);
- sset_init(&m->dst_ports);
- m->vlans = NULL;
- m->n_vlans = 0;
- m->out_vlan = -1;
- m->out_port = NULL;
- mirror_reconfigure_one(m, cfg);
+ return m;
}
static void
{
if (m) {
struct bridge *br = m->bridge;
- struct port *port;
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- port->src_mirrors &= ~(MIRROR_MASK_C(1) << m->idx);
- port->dst_mirrors &= ~(MIRROR_MASK_C(1) << m->idx);
+ if (br->ofproto) {
+ ofproto_mirror_unregister(br->ofproto, m);
}
- sset_destroy(&m->src_ports);
- sset_destroy(&m->dst_ports);
- free(m->vlans);
-
- m->bridge->mirrors[m->idx] = NULL;
+ hmap_remove(&br->mirrors, &m->hmap_node);
free(m->name);
free(m);
-
- bridge_flush(br);
- mac_learning_flush(br->ml);
}
}
static void
-mirror_collect_ports(struct mirror *m, struct ovsrec_port **ports, int n_ports,
- struct sset *names)
+mirror_collect_ports(struct mirror *m,
+ struct ovsrec_port **in_ports, int n_in_ports,
+ void ***out_portsp, size_t *n_out_portsp)
{
+ void **out_ports = xmalloc(n_in_ports * sizeof *out_ports);
+ size_t n_out_ports = 0;
size_t i;
- for (i = 0; i < n_ports; i++) {
- const char *name = ports[i]->name;
- if (port_lookup(m->bridge, name)) {
- sset_add(names, name);
+ for (i = 0; i < n_in_ports; i++) {
+ const char *name = in_ports[i]->name;
+ struct port *port = port_lookup(m->bridge, name);
+ if (port) {
+ out_ports[n_out_ports++] = port;
} else {
VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent "
"port %s", m->bridge->name, m->name, name);
}
}
-}
-
-static size_t
-mirror_collect_vlans(struct mirror *m, const struct ovsrec_mirror *cfg,
- int **vlans)
-{
- size_t n_vlans;
- size_t i;
-
- *vlans = xmalloc(sizeof **vlans * cfg->n_select_vlan);
- n_vlans = 0;
- for (i = 0; i < cfg->n_select_vlan; i++) {
- int64_t vlan = cfg->select_vlan[i];
- if (vlan < 0 || vlan > 4095) {
- VLOG_WARN("bridge %s: mirror %s selects invalid VLAN %"PRId64,
- m->bridge->name, m->name, vlan);
- } else {
- (*vlans)[n_vlans++] = vlan;
- }
- }
- return n_vlans;
+ *out_portsp = out_ports;
+ *n_out_portsp = n_out_ports;
}
static bool
-vlan_is_mirrored(const struct mirror *m, int vlan)
-{
- size_t i;
-
- for (i = 0; i < m->n_vlans; i++) {
- if (m->vlans[i] == vlan) {
- return true;
- }
- }
- return false;
-}
-
-static void
-mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
+mirror_configure(struct mirror *m, const struct ovsrec_mirror *cfg)
{
- struct sset src_ports, dst_ports;
- mirror_mask_t mirror_bit;
- struct port *out_port;
- struct port *port;
- int out_vlan;
- size_t n_vlans;
- int *vlans;
+ struct ofproto_mirror_settings s;
/* Set name. */
if (strcmp(cfg->name, m->name)) {
free(m->name);
m->name = xstrdup(cfg->name);
}
+ s.name = m->name;
- /* Get output port. */
+ /* Get output port or VLAN. */
if (cfg->output_port) {
- out_port = port_lookup(m->bridge, cfg->output_port->name);
- if (!out_port) {
+ s.out_bundle = port_lookup(m->bridge, cfg->output_port->name);
+ if (!s.out_bundle) {
VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge",
m->bridge->name, m->name);
- mirror_destroy(m);
- return;
+ return false;
}
- out_vlan = -1;
+ s.out_vlan = UINT16_MAX;
if (cfg->output_vlan) {
VLOG_ERR("bridge %s: mirror %s specifies both output port and "
m->bridge->name, m->name);
}
} else if (cfg->output_vlan) {
- out_port = NULL;
- out_vlan = *cfg->output_vlan;
+ /* The database should prevent invalid VLAN values. */
+ s.out_bundle = NULL;
+ s.out_vlan = *cfg->output_vlan;
} else {
VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring",
m->bridge->name, m->name);
- mirror_destroy(m);
- return;
+ return false;
}
- sset_init(&src_ports);
- sset_init(&dst_ports);
+ /* Get port selection. */
if (cfg->select_all) {
+ size_t n_ports = hmap_count(&m->bridge->ports);
+ void **ports = xmalloc(n_ports * sizeof *ports);
+ struct port *port;
+ size_t i;
+
+ i = 0;
HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
- sset_add(&src_ports, port->name);
- sset_add(&dst_ports, port->name);
+ ports[i++] = port;
}
- vlans = NULL;
- n_vlans = 0;
+
+ s.srcs = ports;
+ s.n_srcs = n_ports;
+
+ s.dsts = ports;
+ s.n_dsts = n_ports;
} else {
- /* Get ports, and drop duplicates and ports that don't exist. */
+ /* Get ports, dropping ports that don't exist.
+ * The IDL ensures that there are no duplicates. */
mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port,
- &src_ports);
+ &s.srcs, &s.n_srcs);
mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port,
- &dst_ports);
-
- /* Get all the vlans, and drop duplicate and invalid vlans. */
- n_vlans = mirror_collect_vlans(m, cfg, &vlans);
- }
-
- /* Update mirror data. */
- if (!sset_equals(&m->src_ports, &src_ports)
- || !sset_equals(&m->dst_ports, &dst_ports)
- || m->n_vlans != n_vlans
- || memcmp(m->vlans, vlans, sizeof *vlans * n_vlans)
- || m->out_port != out_port
- || m->out_vlan != out_vlan) {
- bridge_flush(m->bridge);
- mac_learning_flush(m->bridge->ml);
- }
- sset_swap(&m->src_ports, &src_ports);
- sset_swap(&m->dst_ports, &dst_ports);
- free(m->vlans);
- m->vlans = vlans;
- m->n_vlans = n_vlans;
- m->out_port = out_port;
- m->out_vlan = out_vlan;
-
- /* Update ports. */
- mirror_bit = MIRROR_MASK_C(1) << m->idx;
- HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
- if (sset_contains(&m->src_ports, port->name)) {
- port->src_mirrors |= mirror_bit;
- } else {
- port->src_mirrors &= ~mirror_bit;
- }
+ &s.dsts, &s.n_dsts);
- if (sset_contains(&m->dst_ports, port->name)) {
- port->dst_mirrors |= mirror_bit;
- } else {
- port->dst_mirrors &= ~mirror_bit;
- }
}
+ /* Get VLAN selection. */
+ s.src_vlans = vlan_bitmap_from_array(cfg->select_vlan, cfg->n_select_vlan);
+
+ /* Configure. */
+ ofproto_mirror_register(m->bridge->ofproto, m, &s);
+
/* Clean up. */
- sset_destroy(&src_ports);
- sset_destroy(&dst_ports);
+ if (s.srcs != s.dsts) {
+ free(s.dsts);
+ }
+ free(s.srcs);
+ free(s.src_vlans);
+
+ return true;
}
#include "command-line.h"
#include "compiler.h"
#include "daemon.h"
-#include "dpif.h"
#include "dummy.h"
#include "leak-checker.h"
#include "netdev.h"
}
bridge_run();
unixctl_server_run(unixctl);
- dp_run();
netdev_run();
signal_wait(sighup);
bridge_wait();
unixctl_server_wait(unixctl);
- dp_wait();
netdev_wait();
if (exiting) {
poll_immediate_wake();