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.
*
bond->miimon_next_update = time_msec() + bond->miimon_interval;
}
+ if (bond->monitor) {
+ netdev_monitor_flush(bond->monitor);
+ }
+
/* Enable slaves based on link status and LACP feedback. */
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
bond_link_status_update(slave, tags);
bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis)
{
struct flow hash_flow = *flow;
- hash_flow.vlan_tci = vlan;
+ hash_flow.vlan_tci = htons(vlan);
/* The symmetric quality of this hash function is not required, but
* flow_hash_symmetric_l4 already exists, and is sufficient for our
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,
}
/* Clears 'dp' to "empty" values. */
-void
+static void
dpif_linux_dp_init(struct dpif_linux_dp *dp)
{
memset(dp, 0, sizeof *dp);
* result of the command is expected to be of the same form, which is decoded
* and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the
* reply is no longer needed ('reply' will contain pointers into '*bufp'). */
-int
+static int
dpif_linux_dp_transact(const struct dpif_linux_dp *request,
struct dpif_linux_dp *reply, struct ofpbuf **bufp)
{
/* Obtains information about 'dpif_' and stores it into '*reply' and '*bufp'.
* The caller must free '*bufp' when the reply is no longer needed ('reply'
* will contain pointers into '*bufp'). */
-int
+static int
dpif_linux_dp_get(const struct dpif *dpif_, struct dpif_linux_dp *reply,
struct ofpbuf **bufp)
{
}
/* Clears 'flow' to "empty" values. */
-void
+static void
dpif_linux_flow_init(struct dpif_linux_flow *flow)
{
memset(flow, 0, sizeof *flow);
* result of the command is expected to be a flow also, which is decoded and
* stored in '*reply' and '*bufp'. The caller must free '*bufp' when the reply
* is no longer needed ('reply' will contain pointers into '*bufp'). */
-int
+static int
dpif_linux_flow_transact(const struct dpif_linux_flow *request,
struct dpif_linux_flow *reply, struct ofpbuf **bufp)
{
long long int used; /* Last used time, in monotonic msecs. */
long long int packet_count; /* Number of packets matched. */
long long int byte_count; /* Number of bytes matched. */
- uint16_t tcp_ctl; /* Bitwise-OR of seen tcp_ctl values. */
+ ovs_be16 tcp_ctl; /* Bitwise-OR of seen tcp_ctl values. */
/* Actions. */
struct nlattr *actions;
}
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)
{
struct ip_header *nh = packet->l3;
ovs_be32 ip = nl_attr_get_be32(a);
uint16_t type = nl_attr_type(a);
- uint32_t *field;
+ ovs_be32 *field;
field = type == ODP_ACTION_ATTR_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
if (key->nw_proto == IPPROTO_TCP && packet->l7) {
if (uh->udp_csum) {
uh->udp_csum = recalc_csum32(uh->udp_csum, *field, ip);
if (!uh->udp_csum) {
- uh->udp_csum = 0xffff;
+ uh->udp_csum = htons(0xffff);
}
}
}
if (is_ip(packet, key)) {
uint16_t type = nl_attr_type(a);
ovs_be16 port = nl_attr_get_be16(a);
- uint16_t *field;
+ ovs_be16 *field;
if (key->nw_proto == IPPROTO_TCP && packet->l7) {
struct tcp_header *th = packet->l4;
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,
}
- /* 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;
}
for (i = 0; i < FLOW_N_REGS; i++) {
- if (wc->reg_masks[i] != htonl(UINT32_MAX)) {
+ if (wc->reg_masks[i] != UINT32_MAX) {
return false;
}
}
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. */
BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nd_target) == 16);
BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
-int flow_extract(struct ofpbuf *, uint64_t tun_id, uint16_t in_port,
+int flow_extract(struct ofpbuf *, ovs_be64 tun_id, uint16_t in_port,
struct flow *);
void flow_extract_stats(const struct flow *flow, struct ofpbuf *packet,
struct dpif_flow_stats *);
#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))
/* Sockets used for ioctl operations. */
static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */
-static int af_packet_sock = -1; /* AF_PACKET, SOCK_RAW. */
/* A Netlink routing socket that is not subscribed to any multicast groups. */
static struct nl_sock *rtnl_sock;
const uint8_t[ETH_ADDR_LEN]);
static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
+static int af_packet_sock(void);
static bool
is_netdev_linux_class(const struct netdev_class *netdev_class)
status = af_inet_sock >= 0 ? 0 : errno;
if (status) {
VLOG_ERR("failed to create inet socket: %s", strerror(status));
- } else {
- /* Create AF_PACKET socket. */
- af_packet_sock = socket(AF_PACKET, SOCK_RAW, 0);
- status = af_packet_sock >= 0 ? 0 : errno;
- if (status) {
- VLOG_ERR("failed to create packet socket: %s",
- strerror(status));
- }
- set_nonblocking(af_packet_sock);
}
/* Create rtnetlink socket. */
protocol = (ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL
: ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2
: ethertype);
- netdev->fd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
+ netdev->fd = socket(PF_PACKET, SOCK_RAW,
+ (OVS_FORCE int) htons(protocol));
if (netdev->fd < 0) {
error = errno;
goto error;
struct iovec iov;
int ifindex;
int error;
+ int sock;
+
+ sock = af_packet_sock();
+ if (sock < 0) {
+ return sock;
+ }
error = get_ifindex(netdev_, &ifindex);
if (error) {
msg.msg_controllen = 0;
msg.msg_flags = 0;
- retval = sendmsg(af_packet_sock, &msg, 0);
+ retval = sendmsg(sock, &msg, 0);
} else {
/* Use the netdev's own fd to send to this device. This is
* essential for tap devices, because packets sent to a tap device
const struct netdev_stats *src)
{
COPY_NETDEV_STATS;
+ dst->rx_compressed = 0;
+ dst->tx_compressed = 0;
}
\f
/* Utility functions. */
}
return error;
}
+
+/* Returns an AF_PACKET raw socket or a negative errno value. */
+static int
+af_packet_sock(void)
+{
+ static int sock = INT_MIN;
+
+ if (sock == INT_MIN) {
+ sock = socket(AF_PACKET, SOCK_RAW, 0);
+ if (sock >= 0) {
+ set_nonblocking(sock);
+ } else {
+ sock = -errno;
+ VLOG_ERR("failed to create packet socket: %s", strerror(errno));
+ }
+ }
+
+ return sock;
+}
/* 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. */
static void
nxm_put_eth_dst(struct ofpbuf *b,
- uint32_t wc, const uint8_t value[ETH_ADDR_LEN])
+ flow_wildcards_t wc, const uint8_t value[ETH_ADDR_LEN])
{
switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
case FWW_DL_DST | FWW_ETH_MCAST:
/* 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);
data_len = len - offsetof(struct ofp_packet_in, data);
ds_put_format(string, " data_len=%zu", data_len);
- if (htonl(op->buffer_id) == UINT32_MAX) {
+ if (op->buffer_id == htonl(UINT32_MAX)) {
ds_put_format(string, " (unbuffered)");
if (ntohs(op->total_len) != data_len)
ds_put_format(string, " (***total_len != data_len***)");
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;
VLOG_DEFINE_THIS_MODULE(ofp_util);
+static ovs_be32 normalize_wildcards(const struct ofp_match *);
+
/* Rate limit for OpenFlow message parse errors. These always indicate a bug
* in the peer and so there's not much point in showing a lot of them. */
static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all
* OR'd together. */
-enum {
- WC_INVARIANTS = 0
+static const flow_wildcards_t WC_INVARIANTS = 0
#define WC_INVARIANT_BIT(NAME) | FWW_##NAME
WC_INVARIANT_LIST
#undef WC_INVARIANT_BIT
-};
+;
/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
* 'priority'. */
unsigned int priority, struct cls_rule *rule)
{
struct flow_wildcards *wc = &rule->wc;
- unsigned int ofpfw;
+ uint32_t ofpfw;
ovs_be16 vid, pcp;
/* Initialize rule->priority. */
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
- wc->wildcards = ofpfw & WC_INVARIANTS;
+ wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
/* Wildcard fields that aren't defined by ofp_match or tun_id. */
wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET);
/* 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;
ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
{
const struct flow_wildcards *wc = &rule->wc;
- unsigned int ofpfw;
+ uint32_t ofpfw;
/* Figure out most OpenFlow wildcards. */
- ofpfw = wc->wildcards & WC_INVARIANTS;
+ ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
if (wc->wildcards & FWW_NW_TOS) {
/* 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));
ofputil_decode_msg_type(oh, &type);
if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
/* Standard OpenFlow flow_mod. */
- struct ofp_match match, orig_match;
const struct ofp_flow_mod *ofm;
+ uint16_t priority;
+ ovs_be32 wc;
int error;
/* Dissect the message. */
return error;
}
+ /* Set priority based on original wildcards. Normally we'd allow
+ * ofputil_cls_rule_from_match() to do this for us, but
+ * normalize_wildcards() can put wildcards where the original flow
+ * didn't have them. */
+ priority = ntohs(ofm->priority);
+ if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
+ priority = UINT16_MAX;
+ }
+
/* Normalize ofm->match. If normalization actually changes anything,
* then log the differences. */
- match = ofm->match;
- match.pad1[0] = match.pad2[0] = 0;
- orig_match = match;
- normalize_match(&match);
- if (memcmp(&match, &orig_match, sizeof orig_match)) {
+ wc = normalize_wildcards(&ofm->match);
+ if (wc == ofm->match.wildcards) {
+ ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
+ } else {
+ struct ofp_match match = ofm->match;
+ match.wildcards = wc;
+ ofputil_cls_rule_from_match(&match, priority, &fm->cr);
+
if (!VLOG_DROP_INFO(&bad_ofmsg_rl)) {
- char *old = ofp_match_to_literal_string(&orig_match);
- char *new = ofp_match_to_literal_string(&match);
+ char *pre = ofp_match_to_string(&ofm->match, 1);
+ char *post = ofp_match_to_string(&match, 1);
VLOG_INFO("normalization changed ofp_match, details:");
- VLOG_INFO(" pre: %s", old);
- VLOG_INFO("post: %s", new);
- free(old);
- free(new);
+ VLOG_INFO(" pre: %s", pre);
+ VLOG_INFO("post: %s", post);
+ free(pre);
+ free(post);
}
}
/* Translate the message. */
- ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), &fm->cr);
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);
}
}
-void
-normalize_match(struct ofp_match *m)
+static ovs_be32
+normalize_wildcards(const struct ofp_match *m)
{
- enum { OFPFW_NW = (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO
+ enum { OFPFW_NW = (OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL | OFPFW_NW_PROTO
| OFPFW_NW_TOS) };
enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
- uint32_t wc;
-
- wc = ntohl(m->wildcards) & OFPFW_ALL;
- if (wc & OFPFW_DL_TYPE) {
- m->dl_type = 0;
+ ovs_be32 wc;
- /* Can't sensibly match on network or transport headers if the
- * data link type is unknown. */
- wc |= OFPFW_NW | OFPFW_TP;
- m->nw_src = m->nw_dst = m->nw_proto = m->nw_tos = 0;
- m->tp_src = m->tp_dst = 0;
+ wc = m->wildcards;
+ if (wc & htonl(OFPFW_DL_TYPE)) {
+ wc |= htonl(OFPFW_NW | OFPFW_TP);
} else if (m->dl_type == htons(ETH_TYPE_IP)) {
- if (wc & OFPFW_NW_PROTO) {
- m->nw_proto = 0;
-
- /* Can't sensibly match on transport headers if the network
- * protocol is unknown. */
- wc |= OFPFW_TP;
- m->tp_src = m->tp_dst = 0;
- } else if (m->nw_proto == IPPROTO_TCP ||
- m->nw_proto == IPPROTO_UDP ||
- m->nw_proto == IPPROTO_ICMP) {
- if (wc & OFPFW_TP_SRC) {
- m->tp_src = 0;
- }
- if (wc & OFPFW_TP_DST) {
- m->tp_dst = 0;
- }
- } else {
- /* Transport layer fields will always be extracted as zeros, so we
- * can do an exact-match on those values. */
- wc &= ~OFPFW_TP;
- m->tp_src = m->tp_dst = 0;
- }
- if (wc & OFPFW_NW_SRC_MASK) {
- m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT);
- }
- if (wc & OFPFW_NW_DST_MASK) {
- m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
- }
- if (wc & OFPFW_NW_TOS) {
- m->nw_tos = 0;
- } else {
- m->nw_tos &= IP_DSCP_MASK;
+ if (wc & htonl(OFPFW_NW_PROTO) || (m->nw_proto != IPPROTO_TCP &&
+ m->nw_proto != IPPROTO_UDP &&
+ m->nw_proto != IPPROTO_ICMP)) {
+ wc |= htonl(OFPFW_TP);
}
} else if (m->dl_type == htons(ETH_TYPE_ARP)) {
- if (wc & OFPFW_NW_PROTO) {
- m->nw_proto = 0;
- }
- if (wc & OFPFW_NW_SRC_MASK) {
- m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT);
- }
- if (wc & OFPFW_NW_DST_MASK) {
- m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
- }
- m->tp_src = m->tp_dst = m->nw_tos = 0;
- } else if (m->dl_type == htons(ETH_TYPE_IPV6)) {
- /* Don't normalize IPv6 traffic, since OpenFlow doesn't have a
- * way to express it. */
+ wc |= htonl(OFPFW_TP);
} else {
- /* Network and transport layer fields will always be extracted as
- * zeros, so we can do an exact-match on those values. */
- wc &= ~(OFPFW_NW | OFPFW_TP);
- m->nw_proto = m->nw_src = m->nw_dst = m->nw_tos = 0;
- m->tp_src = m->tp_dst = 0;
- }
- if (wc & OFPFW_DL_SRC) {
- memset(m->dl_src, 0, sizeof m->dl_src);
- }
- if (wc & OFPFW_DL_DST) {
- memset(m->dl_dst, 0, sizeof m->dl_dst);
+ wc |= htonl(OFPFW_NW | OFPFW_TP);
}
- m->wildcards = htonl(wc);
-}
-
-/* Returns a string that describes 'match' in a very literal way, without
- * interpreting its contents except in a very basic fashion. The returned
- * string is intended to be fixed-length, so that it is easy to see differences
- * between two such strings if one is put above another. This is useful for
- * describing changes made by normalize_match().
- *
- * The caller must free the returned string (with free()). */
-char *
-ofp_match_to_literal_string(const struct ofp_match *match)
-{
- return xasprintf("wildcards=%#10"PRIx32" "
- " in_port=%5"PRId16" "
- " dl_src="ETH_ADDR_FMT" "
- " dl_dst="ETH_ADDR_FMT" "
- " dl_vlan=%5"PRId16" "
- " dl_vlan_pcp=%3"PRId8" "
- " dl_type=%#6"PRIx16" "
- " nw_tos=%#4"PRIx8" "
- " nw_proto=%#4"PRIx16" "
- " nw_src=%#10"PRIx32" "
- " nw_dst=%#10"PRIx32" "
- " tp_src=%5"PRId16" "
- " tp_dst=%5"PRId16,
- ntohl(match->wildcards),
- ntohs(match->in_port),
- ETH_ADDR_ARGS(match->dl_src),
- ETH_ADDR_ARGS(match->dl_dst),
- ntohs(match->dl_vlan),
- match->dl_vlan_pcp,
- ntohs(match->dl_type),
- match->nw_tos,
- match->nw_proto,
- ntohl(match->nw_src),
- ntohl(match->nw_dst),
- ntohs(match->tp_src),
- ntohs(match->tp_dst));
+ return wc;
}
static uint32_t
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,
void ofputil_cls_rule_from_match(const struct ofp_match *,
unsigned int priority, struct cls_rule *);
void ofputil_cls_rule_to_match(const struct cls_rule *, struct ofp_match *);
-void normalize_match(struct ofp_match *);
-char *ofp_match_to_literal_string(const struct ofp_match *match);
/* dl_type translation between OpenFlow and 'struct flow' format. */
ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
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
-static int send_packet(struct ofproto_dpif *,
- uint32_t odp_port, uint16_t vlan_tci,
+ /*
+ * 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. */
- ofport->odp_port, 0, &packet);
++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),
- send_packet(ofproto, OFPP_LOCAL, 0, upcall->packet);
++ 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)) {
-/* Sends 'packet' out of port 'odp_port' within 'ofproto'. If 'vlan_tci' is
- * zero the packet will not have any 802.1Q hader; if it is nonzero, then the
- * packet will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'.
- *
++ 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
-send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, uint16_t vlan_tci,
++/* Sends 'packet' out of port 'odp_port' within 'p'.
+ * Returns 0 if successful, otherwise a positive errno value. */
+ static int
- if (vlan_tci != 0) {
- nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
- ntohs(vlan_tci & ~VLAN_CFI));
- }
++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
*
* The log message mentions 'msg_type'. */
static int
-reject_slave_controller(struct ofconn *ofconn, const const char *msg_type)
+reject_slave_controller(struct ofconn *ofconn, const char *msg_type)
{
if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
&& ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
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
- };
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], [ignore], [ignore])
+ [0], [], [stderr])
+ AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d
+/openflowd|INFO|Open vSwitch version/d
+/openflowd|INFO|OpenFlow protocol version/d
+/ofproto|INFO|using datapath ID/d
+/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
])
m4_define([OFPROTO_STOP],
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
-], [0], [stdout], [stderr])
+], [0], [stdout])
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
[[OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
OFPT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
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_CHECK([sed 's/.*|//' stderr], [0], [dnl
-normalization changed ofp_match, details:
- pre: wildcards= 0x3820f8 in_port=65534 dl_src=00:0a:e4:25:6b:b0 dl_dst=00:00:00:00:00:00 dl_vlan= 9 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0
-post: wildcards= 0x3ffff8 in_port=65534 dl_src=00:0a:e4:25:6b:b0 dl_dst=00:00:00:00:00:00 dl_vlan= 9 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0
-normalization changed ofp_match, details:
- pre: wildcards= 0x3820ff in_port= 0 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0
-post: wildcards= 0x3fffff in_port= 0 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0
-normalization changed ofp_match, details:
- pre: wildcards= 0x3820ff in_port= 0 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0
-post: wildcards= 0x3fffff in_port= 0 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0
-])
AT_CLEANUP
AT_SETUP([ovs-ofctl -F nxm parse-flows])
<any>
# in port
- NXM_OF_IN_PORT(fffe)
+ NXM_OF_IN_PORT(0000)
NXM_OF_IN_PORT(fffe)
# eth dst
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;
}
DAEMON_OPTION_ENUMS
};
static struct option long_options[] = {
- {"hub", no_argument, 0, 'H'},
- {"noflow", no_argument, 0, 'n'},
- {"normal", no_argument, 0, 'N'},
- {"wildcard", no_argument, 0, 'w'},
- {"max-idle", required_argument, 0, OPT_MAX_IDLE},
- {"mute", no_argument, 0, OPT_MUTE},
- {"queue", required_argument, 0, 'q'},
- {"port-queue", required_argument, 0, 'Q'},
- {"with-flows", required_argument, 0, OPT_WITH_FLOWS},
- {"unixctl", required_argument, 0, OPT_UNIXCTL},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
+ {"hub", no_argument, NULL, 'H'},
+ {"noflow", no_argument, NULL, 'n'},
+ {"normal", no_argument, NULL, 'N'},
+ {"wildcard", no_argument, NULL, 'w'},
+ {"max-idle", required_argument, NULL, OPT_MAX_IDLE},
+ {"mute", no_argument, NULL, OPT_MUTE},
+ {"queue", required_argument, NULL, 'q'},
+ {"port-queue", required_argument, NULL, 'Q'},
+ {"with-flows", required_argument, NULL, OPT_WITH_FLOWS},
+ {"unixctl", required_argument, NULL, OPT_UNIXCTL},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
STREAM_SSL_LONG_OPTIONS,
- {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT},
- {0, 0, 0, 0},
+ {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
+ {NULL, 0, NULL, 0},
};
char *short_options = long_options_to_short_options(long_options);
#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"
VLOG_OPTION_ENUMS
};
static struct option long_options[] = {
- {"timeout", required_argument, 0, 't'},
- {"strict", no_argument, 0, OPT_STRICT},
- {"flow-format", required_argument, 0, 'F'},
- {"more", no_argument, 0, 'm'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
+ {"timeout", required_argument, NULL, 't'},
+ {"strict", no_argument, NULL, OPT_STRICT},
+ {"flow-format", required_argument, NULL, 'F'},
+ {"more", no_argument, NULL, 'm'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
VLOG_LONG_OPTIONS,
STREAM_SSL_LONG_OPTIONS,
- {0, 0, 0, 0},
+ {NULL, 0, NULL, 0},
};
char *short_options = long_options_to_short_options(long_options);
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;
}
DAEMON_OPTION_ENUMS
};
static struct option long_options[] = {
- {"datapath-id", required_argument, 0, OPT_DATAPATH_ID},
- {"mfr-desc", required_argument, 0, OPT_MFR_DESC},
- {"hw-desc", required_argument, 0, OPT_HW_DESC},
- {"sw-desc", required_argument, 0, OPT_SW_DESC},
- {"serial-desc", required_argument, 0, OPT_SERIAL_DESC},
- {"dp-desc", required_argument, 0, OPT_DP_DESC},
- {"config", required_argument, 0, 'F'},
- {"br-name", required_argument, 0, OPT_BR_NAME},
- {"fail", required_argument, 0, OPT_FAIL_MODE},
- {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
- {"max-idle", required_argument, 0, OPT_MAX_IDLE},
- {"max-backoff", required_argument, 0, OPT_MAX_BACKOFF},
- {"listen", required_argument, 0, 'l'},
- {"snoop", required_argument, 0, OPT_SNOOP},
- {"rate-limit", optional_argument, 0, OPT_RATE_LIMIT},
- {"burst-limit", required_argument, 0, OPT_BURST_LIMIT},
- {"out-of-band", no_argument, 0, OPT_OUT_OF_BAND},
- {"in-band", no_argument, 0, OPT_IN_BAND},
- {"netflow", required_argument, 0, OPT_NETFLOW},
- {"ports", required_argument, 0, OPT_PORTS},
- {"unixctl", required_argument, 0, OPT_UNIXCTL},
- {"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY},
- {"verbose", optional_argument, 0, 'v'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
+ {"datapath-id", required_argument, NULL, OPT_DATAPATH_ID},
+ {"mfr-desc", required_argument, NULL, OPT_MFR_DESC},
+ {"hw-desc", required_argument, NULL, OPT_HW_DESC},
+ {"sw-desc", required_argument, NULL, OPT_SW_DESC},
+ {"serial-desc", required_argument, NULL, OPT_SERIAL_DESC},
+ {"dp-desc", required_argument, NULL, OPT_DP_DESC},
+ {"config", required_argument, NULL, 'F'},
+ {"br-name", required_argument, NULL, OPT_BR_NAME},
+ {"fail", required_argument, NULL, OPT_FAIL_MODE},
+ {"inactivity-probe", required_argument, NULL, OPT_INACTIVITY_PROBE},
+ {"max-idle", required_argument, NULL, OPT_MAX_IDLE},
+ {"max-backoff", required_argument, NULL, OPT_MAX_BACKOFF},
+ {"listen", required_argument, NULL, 'l'},
+ {"snoop", required_argument, NULL, OPT_SNOOP},
+ {"rate-limit", optional_argument, NULL, OPT_RATE_LIMIT},
+ {"burst-limit", required_argument, NULL, OPT_BURST_LIMIT},
+ {"out-of-band", no_argument, NULL, OPT_OUT_OF_BAND},
+ {"in-band", no_argument, NULL, OPT_IN_BAND},
+ {"netflow", required_argument, NULL, OPT_NETFLOW},
+ {"ports", required_argument, NULL, OPT_PORTS},
+ {"unixctl", required_argument, NULL, OPT_UNIXCTL},
+ {"enable-dummy", no_argument, NULL, OPT_ENABLE_DUMMY},
+ {"verbose", optional_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
LEAK_CHECKER_LONG_OPTIONS,
STREAM_SSL_LONG_OPTIONS,
- {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
- {0, 0, 0, 0},
+ {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
+ {NULL, 0, NULL, 0},
};
char *short_options = long_options_to_short_options(long_options);
struct ofproto_controller controller_opts;
}
/* 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();
- eth_addr_nicira_random(br->default_ea);
+
+ /* Derive the default Ethernet address from the bridge's UUID. This should
+ * be unique and it will be stable between ovs-vswitchd runs. */
+ memcpy(br->default_ea, &br_cfg->header_.uuid, ETH_ADDR_LEN);
+ eth_addr_mark_random(br->default_ea);
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();
DAEMON_OPTION_ENUMS
};
static struct option long_options[] = {
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {"mlockall", no_argument, 0, OPT_MLOCKALL},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"mlockall", no_argument, NULL, OPT_MLOCKALL},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
LEAK_CHECKER_LONG_OPTIONS,
STREAM_SSL_LONG_OPTIONS,
- {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT},
- {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
- {"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY},
- {0, 0, 0, 0},
+ {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
+ {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
+ {"enable-dummy", no_argument, NULL, OPT_ENABLE_DUMMY},
+ {NULL, 0, NULL, 0},
};
char *short_options = long_options_to_short_options(long_options);