X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=e93e2a7f0ceee5ac4a8143a213c0490121f9c3c9;hb=d80a55abae13fdee776114a4f2fd5fb29fea87f6;hp=c024963a230600c23c696c8d5dd7939f03ee4248;hpb=b0f7b9b5c98557d159e4a12f125eacbf2a04a25b;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index c024963a2..e93e2a7f0 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,12 +134,17 @@ struct ofmirror { struct ofbundle *out; /* Output port or NULL. */ int out_vlan; /* Output VLAN or -1. */ mirror_mask_t dup_mirrors; /* Bitmap of mirrors with the same output. */ + + /* Counters. */ + int64_t packet_count; /* Number of packets sent. */ + int64_t byte_count; /* Number of bytes sent. */ }; static void mirror_destroy(struct ofmirror *); +static void update_mirror_stats(struct ofproto_dpif *ofproto, + mirror_mask_t mirrors, + uint64_t packets, uint64_t bytes); -/* 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. */ @@ -171,6 +176,18 @@ 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 *); +static struct ofbundle *lookup_input_bundle(struct ofproto_dpif *, + uint16_t in_port, bool warn); + +/* A controller may use OFPP_NONE as the ingress port to indicate that + * it did not arrive on a "real" port. 'ofpp_none_bundle' exists for + * when an input bundle is needed for validation (e.g., mirroring or + * OFPP_NORMAL processing). It is not connected to an 'ofproto' or have + * any 'port' structs, so care must be taken when dealing with it. */ +static struct ofbundle ofpp_none_bundle = { + .name = "OFPP_NONE", + .vlan_mode = PORT_VLAN_TRUNK +}; static void stp_run(struct ofproto_dpif *ofproto); static void stp_wait(struct ofproto_dpif *ofproto); @@ -197,6 +214,9 @@ struct action_xlate_ctx { * we are just revalidating. */ bool may_learn; + /* Cookie of the currently matching rule, or 0. */ + ovs_be64 cookie; + /* If nonnull, called just before executing a resubmit action. * * This is normally null so the client has to set it manually after @@ -213,13 +233,14 @@ struct action_xlate_ctx { bool has_learn; /* Actions include NXAST_LEARN? */ bool has_normal; /* Actions output to OFPP_NORMAL? */ uint16_t nf_output_iface; /* Output interface index for NetFlow. */ + mirror_mask_t mirrors; /* Bitmap of associated mirrors. */ /* 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. */ struct flow base_flow; /* Flow at the last commit. */ - uint32_t original_priority; /* Priority when packet arrived. */ + uint32_t orig_skb_priority; /* Priority when packet arrived. */ uint8_t table_id; /* OpenFlow table ID where flow was found. */ uint32_t sflow_n_outputs; /* Number of output ports. */ uint16_t sflow_odp_port; /* Output port for composing sFlow action. */ @@ -229,6 +250,7 @@ struct action_xlate_ctx { static void action_xlate_ctx_init(struct action_xlate_ctx *, struct ofproto_dpif *, const struct flow *, + ovs_be16 initial_tci, ovs_be64 cookie, const struct ofpbuf *); static struct ofpbuf *xlate_actions(struct action_xlate_ctx *, const union ofp_action *in, size_t n_in); @@ -236,15 +258,14 @@ static struct ofpbuf *xlate_actions(struct action_xlate_ctx *, /* An exact-match instantiation of an OpenFlow flow. * * A facet associates a "struct flow", which represents the Open vSwitch - * userspace idea of an exact-match flow, with a set of datapath actions. - * - * A facet contains one or more subfacets. Each subfacet tracks the datapath's - * idea of the exact-match flow equivalent to the facet. When the kernel - * module (or other dpif implementation) and Open vSwitch userspace agree on - * the definition of a flow key, there is exactly one subfacet per facet. If - * the dpif implementation supports more-specific flow matching than userspace, - * however, a facet can have more than one subfacet, each of which corresponds - * to some distinction in flow that userspace simply doesn't understand. + * userspace idea of an exact-match flow, with one or more subfacets. Each + * subfacet tracks the datapath's idea of the exact-match flow equivalent to + * the facet. When the kernel module (or other dpif implementation) and Open + * vSwitch userspace agree on the definition of a flow key, there is exactly + * one subfacet per facet. If the dpif implementation supports more-specific + * flow matching than userspace, however, a facet can have more than one + * subfacet, each of which corresponds to some distinction in flow that + * userspace simply doesn't understand. * * Flow expiration works in terms of subfacets, so a facet must have at least * one subfacet or it will never expire, leaking memory. */ @@ -277,21 +298,25 @@ struct facet { uint64_t byte_count; /* Number of bytes received. */ /* Resubmit statistics. */ - 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. */ + uint64_t prev_packet_count; /* Number of packets from last stats push. */ + uint64_t prev_byte_count; /* Number of bytes from last stats push. */ + long long int prev_used; /* Used time from last stats push. */ /* Accounting. */ uint64_t accounted_bytes; /* Bytes processed by facet_account(). */ struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */ - /* Datapath actions. */ + /* Properties of datapath actions. + * + * Every subfacet has its own actions because actions can differ slightly + * between splintered and non-splintered subfacets due to the VLAN tag + * being initially different (present vs. absent). All of them have these + * properties in common so we just store one copy of them here. */ bool may_install; /* Reassess actions for every packet? */ bool has_learn; /* Actions include NXAST_LEARN? */ bool has_normal; /* Actions output to OFPP_NORMAL? */ - size_t actions_len; /* Number of bytes in actions[]. */ - struct nlattr *actions; /* Datapath actions. */ tag_type tags; /* Tags that would require revalidation. */ + mirror_mask_t mirrors; /* Bitmap of dependent mirrors. */ }; static struct facet *facet_create(struct rule_dpif *, const struct flow *); @@ -303,16 +328,8 @@ static struct facet *facet_lookup_valid(struct ofproto_dpif *, const struct flow *); static bool facet_revalidate(struct ofproto_dpif *, struct facet *); -static bool execute_controller_action(struct ofproto_dpif *, - const struct flow *, - const struct nlattr *odp_actions, - size_t actions_len, - struct ofpbuf *packet); - 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_reset_counters(struct facet *); @@ -321,7 +338,7 @@ static void facet_account(struct ofproto_dpif *, struct facet *); static bool facet_is_controller_flow(struct facet *); -/* A dpif flow associated with a facet. +/* A dpif flow and actions associated with a facet. * * See also the large comment on struct facet. */ struct subfacet { @@ -344,16 +361,27 @@ struct subfacet { uint64_t dp_packet_count; /* Last known packet count in the datapath. */ uint64_t dp_byte_count; /* Last known byte count in the datapath. */ + /* Datapath actions. + * + * These should be essentially identical for every subfacet in a facet, but + * may differ in trivial ways due to VLAN splinters. */ + size_t actions_len; /* Number of bytes in actions[]. */ + struct nlattr *actions; /* Datapath actions. */ + bool installed; /* Installed in datapath? */ + + /* This value is normally the same as ->facet->flow.vlan_tci. Only VLAN + * splinters can cause it to differ. This value should be removed when + * the VLAN splinters feature is no longer needed. */ + ovs_be16 initial_tci; /* Initial VLAN TCI value. */ }; static struct subfacet *subfacet_create(struct ofproto_dpif *, struct facet *, enum odp_key_fitness, const struct nlattr *key, - size_t key_len); + size_t key_len, ovs_be16 initial_tci); static struct subfacet *subfacet_find(struct ofproto_dpif *, - const struct nlattr *key, size_t key_len, - const struct flow *); + const struct nlattr *key, size_t key_len); static void subfacet_destroy(struct ofproto_dpif *, struct subfacet *); static void subfacet_destroy__(struct ofproto_dpif *, struct subfacet *); static void subfacet_reset_dp_stats(struct subfacet *, @@ -362,6 +390,8 @@ static void subfacet_update_time(struct ofproto_dpif *, struct subfacet *, long long int used); static void subfacet_update_stats(struct ofproto_dpif *, struct subfacet *, const struct dpif_flow_stats *); +static void subfacet_make_actions(struct ofproto_dpif *, struct subfacet *, + const struct ofpbuf *packet); static int subfacet_install(struct ofproto_dpif *, struct subfacet *, const struct nlattr *actions, size_t actions_len, struct dpif_flow_stats *); @@ -378,11 +408,21 @@ struct ofport_dpif { uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */ bool may_enable; /* May be enabled in bonds. */ + /* Spanning tree. */ struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */ enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */ long long int stp_state_entered; struct hmap priorities; /* Map of attached 'priority_to_dscp's. */ + + /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) + * + * This is deprecated. It is only for compatibility with broken device + * drivers in old versions of Linux that do not properly support VLANs when + * VLAN devices are not used. When broken device drivers are no longer in + * widespread use, we will delete these interfaces. */ + uint16_t realdev_ofp_port; + int vlandev_vid; }; /* Node in 'ofport_dpif''s 'priorities' map. Used to maintain a map from @@ -395,6 +435,27 @@ struct priority_to_dscp { uint8_t dscp; /* DSCP bits to mark outgoing traffic with. */ }; +/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) + * + * This is deprecated. It is only for compatibility with broken device drivers + * in old versions of Linux that do not properly support VLANs when VLAN + * devices are not used. When broken device drivers are no longer in + * widespread use, we will delete these interfaces. */ +struct vlan_splinter { + struct hmap_node realdev_vid_node; + struct hmap_node vlandev_node; + uint16_t realdev_ofp_port; + uint16_t vlandev_ofp_port; + int vid; +}; + +static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *, + uint32_t realdev, ovs_be16 vlan_tci); +static uint16_t vsp_vlandev_to_realdev(const struct ofproto_dpif *, + uint16_t vlandev, int *vid); +static void vsp_remove(struct ofport_dpif *); +static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid); + static struct ofport_dpif * ofport_dpif_cast(const struct ofport *ofport) { @@ -424,6 +485,7 @@ struct table_dpif { }; struct ofproto_dpif { + struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */ struct ofproto up; struct dpif *dpif; int max_ports; @@ -455,16 +517,25 @@ struct ofproto_dpif { struct list completions; bool has_bundle_action; /* True when the first bundle action appears. */ + struct netdev_stats stats; /* To account packets generated and consumed in + * userspace. */ /* Spanning tree. */ struct stp *stp; long long int stp_last_tick; + + /* VLAN splinters. */ + struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */ + struct hmap vlandev_map; /* vlandev -> (realdev,vid). */ }; /* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only * for debugging the asynchronous flow_mod implementation.) */ static bool clogged; +/* All existing ofproto_dpif instances, indexed by ->up.name. */ +static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs); + static void ofproto_dpif_unixctl_init(void); static struct ofproto_dpif * @@ -485,10 +556,7 @@ static void update_learning_table(struct ofproto_dpif *, struct ofbundle *); /* Upcalls. */ #define FLOW_MISS_MAX_BATCH 50 - -static void handle_upcall(struct ofproto_dpif *, struct dpif_upcall *); -static void handle_miss_upcalls(struct ofproto_dpif *, - struct dpif_upcall *, size_t n); +static int handle_upcalls(struct ofproto_dpif *, unsigned int max_batch); /* Flow expiration. */ static int expire(struct ofproto_dpif *); @@ -497,11 +565,12 @@ static int expire(struct ofproto_dpif *); static void send_netflow_active_timeouts(struct ofproto_dpif *); /* Utilities. */ -static int send_packet(struct ofproto_dpif *, uint32_t odp_port, - const struct ofpbuf *packet); +static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet); static size_t compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions, const struct flow *, uint32_t odp_port); +static void add_mirror_actions(struct action_xlate_ctx *ctx, + const struct flow *flow); /* Global variables. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -609,7 +678,14 @@ construct(struct ofproto *ofproto_, int *n_tablesp) ofproto->has_bundle_action = false; + hmap_init(&ofproto->vlandev_map); + hmap_init(&ofproto->realdev_vid_map); + + hmap_insert(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node, + hash_string(ofproto->up.name, 0)); + *n_tablesp = N_TABLES; + memset(&ofproto->stats, 0, sizeof ofproto->stats); return 0; } @@ -633,6 +709,7 @@ destruct(struct ofproto *ofproto_) struct classifier *table; int i; + hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node); complete_operations(ofproto); OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { @@ -656,47 +733,56 @@ destruct(struct ofproto *ofproto_) hmap_destroy(&ofproto->facets); hmap_destroy(&ofproto->subfacets); + hmap_destroy(&ofproto->vlandev_map); + hmap_destroy(&ofproto->realdev_vid_map); + dpif_close(ofproto->dpif); } +static int +run_fast(struct ofproto *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + unsigned int work; + + /* Handle one or more batches of upcalls, until there's nothing left to do + * or until we do a fixed total amount of work. + * + * We do work in batches because it can be much cheaper to set up a number + * of flows and fire off their patches all at once. We do multiple batches + * because in some cases handling a packet can cause another packet to be + * queued almost immediately as part of the return flow. Both + * optimizations can make major improvements on some benchmarks and + * presumably for real traffic as well. */ + work = 0; + while (work < FLOW_MISS_MAX_BATCH) { + int retval = handle_upcalls(ofproto, FLOW_MISS_MAX_BATCH - work); + if (retval <= 0) { + return -retval; + } + work += retval; + } + return 0; +} + static int run(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct dpif_upcall misses[FLOW_MISS_MAX_BATCH]; struct ofport_dpif *ofport; struct ofbundle *bundle; - size_t n_misses; - int i; + int error; if (!clogged) { complete_operations(ofproto); } dpif_run(ofproto->dpif); - n_misses = 0; - for (i = 0; i < FLOW_MISS_MAX_BATCH; i++) { - struct dpif_upcall *upcall = &misses[n_misses]; - int error; - - error = dpif_recv(ofproto->dpif, upcall); - if (error) { - if (error == ENODEV && n_misses == 0) { - return error; - } - break; - } - - if (upcall->type == DPIF_UC_MISS) { - /* Handle it later. */ - n_misses++; - } else { - handle_upcall(ofproto, upcall); - } + error = run_fast(ofproto_); + if (error) { + return error; } - handle_miss_upcalls(ofproto, misses, n_misses); - if (timer_expired(&ofproto->next_expiration)) { int delay = expire(ofproto); timer_set_duration(&ofproto->next_expiration, delay); @@ -867,10 +953,11 @@ port_construct(struct ofport *port_) port->stp_port = NULL; port->stp_state = STP_DISABLED; hmap_init(&port->priorities); + port->realdev_ofp_port = 0; + port->vlandev_vid = 0; if (ofproto->sflow) { - dpif_sflow_add_port(ofproto->sflow, port->odp_port, - netdev_get_name(port->up.netdev)); + dpif_sflow_add_port(ofproto->sflow, port_); } return 0; @@ -933,8 +1020,7 @@ set_sflow(struct ofproto *ofproto_, ds = ofproto->sflow = dpif_sflow_create(ofproto->dpif); HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { - dpif_sflow_add_port(ds, ofport->odp_port, - netdev_get_name(ofport->up.netdev)); + dpif_sflow_add_port(ds, &ofport->up); } ofproto->need_revalidate = true; } @@ -1020,8 +1106,7 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " "with unknown MAC", ofproto->up.name, port_num); } else { - send_packet(ofproto_dpif_cast(ofport->up.ofproto), - ofport->odp_port, pkt); + send_packet(ofport, pkt); } } ofpbuf_delete(pkt); @@ -1320,10 +1405,17 @@ set_queues(struct ofport *ofport_, /* Bundles. */ -/* Expires all MAC learning entries associated with 'port' and forces ofproto - * to revalidate every flow. */ +/* Expires all MAC learning entries associated with 'bundle' and forces its + * ofproto to revalidate every flow. + * + * Normally MAC learning entries are removed only from the ofproto associated + * with 'bundle', but if 'all_ofprotos' is true, then the MAC learning entries + * are removed from every ofproto. When patch ports and SLB bonds are in use + * and a VM migration happens and the gratuitous ARPs are somehow lost, this + * avoids a MAC_ENTRY_IDLE_TIME delay before the migrated VM can communicate + * with the host from which it migrated. */ static void -bundle_flush_macs(struct ofbundle *bundle) +bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos) { struct ofproto_dpif *ofproto = bundle->ofproto; struct mac_learning *ml = ofproto->ml; @@ -1332,6 +1424,23 @@ bundle_flush_macs(struct ofbundle *bundle) ofproto->need_revalidate = true; LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) { if (mac->port.p == bundle) { + if (all_ofprotos) { + struct ofproto_dpif *o; + + HMAP_FOR_EACH (o, all_ofproto_dpifs_node, &all_ofproto_dpifs) { + if (o != ofproto) { + struct mac_entry *e; + + e = mac_learning_lookup(o->ml, mac->mac, mac->vlan, + NULL); + if (e) { + tag_set_add(&o->revalidate_set, e->tag); + mac_learning_expire(o->ml, e); + } + } + } + } + mac_learning_expire(ml, mac); } } @@ -1465,7 +1574,7 @@ bundle_destroy(struct ofbundle *bundle) bundle_del_port(port); } - bundle_flush_macs(bundle); + bundle_flush_macs(bundle, true); hmap_remove(&ofproto->bundles, &bundle->hmap_node); free(bundle->name); free(bundle->trunks); @@ -1653,7 +1762,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, /* 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); + bundle_flush_macs(bundle, false); } return 0; @@ -1694,8 +1803,7 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size) pdu_size); memcpy(packet_pdu, pdu, pdu_size); - send_packet(ofproto_dpif_cast(port->up.ofproto), port->odp_port, - &packet); + send_packet(port, &packet); ofpbuf_uninit(&packet); } else { VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface " @@ -1716,13 +1824,16 @@ bundle_send_learning_packets(struct ofbundle *bundle) if (e->port.p != bundle) { struct ofpbuf *learning_packet; struct ofport_dpif *port; + void *port_void; int ret; - learning_packet = bond_compose_learning_packet(bundle->bond, e->mac, - e->vlan, - (void **)&port); - ret = send_packet(ofproto_dpif_cast(port->up.ofproto), - port->odp_port, learning_packet); + /* The assignment to "port" is unnecessary but makes "grep"ing for + * struct ofport_dpif more effective. */ + learning_packet = bond_compose_learning_packet(bundle->bond, + e->mac, e->vlan, + &port_void); + port = port_void; + ret = send_packet(port, learning_packet); ofpbuf_delete(learning_packet); if (ret) { error = ret; @@ -1981,6 +2092,24 @@ mirror_destroy(struct ofmirror *mirror) mirror_update_dups(ofproto); } +static int +mirror_get_stats(struct ofproto *ofproto_, void *aux, + uint64_t *packets, uint64_t *bytes) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + struct ofmirror *mirror = mirror_lookup(ofproto, aux); + + if (!mirror) { + *packets = *bytes = UINT64_MAX; + return 0; + } + + *packets = mirror->packet_count; + *bytes = mirror->byte_count; + + return 0; +} + static int set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans) { @@ -2045,8 +2174,7 @@ port_run(struct ofport_dpif *ofport) ofpbuf_init(&packet, 0); cfm_compose_ccm(ofport->cfm, &packet, ofport->up.opp.hw_addr); - send_packet(ofproto_dpif_cast(ofport->up.ofproto), - ofport->odp_port, &packet); + send_packet(ofport, &packet); ofpbuf_uninit(&packet); } @@ -2126,6 +2254,63 @@ port_del(struct ofproto *ofproto_, uint16_t ofp_port) return error; } +static int +port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + int error; + + error = netdev_get_stats(ofport->up.netdev, stats); + + if (!error && ofport->odp_port == OVSP_LOCAL) { + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + + /* ofproto->stats.tx_packets represents packets that we created + * internally and sent to some port (e.g. packets sent with + * send_packet()). Account for them as if they had come from + * OFPP_LOCAL and got forwarded. */ + + if (stats->rx_packets != UINT64_MAX) { + stats->rx_packets += ofproto->stats.tx_packets; + } + + if (stats->rx_bytes != UINT64_MAX) { + stats->rx_bytes += ofproto->stats.tx_bytes; + } + + /* ofproto->stats.rx_packets represents packets that were received on + * some port and we processed internally and dropped (e.g. STP). + * Account fro them as if they had been forwarded to OFPP_LOCAL. */ + + if (stats->tx_packets != UINT64_MAX) { + stats->tx_packets += ofproto->stats.rx_packets; + } + + if (stats->tx_bytes != UINT64_MAX) { + stats->tx_bytes += ofproto->stats.rx_bytes; + } + } + + return error; +} + +/* Account packets for LOCAL port. */ +static void +ofproto_update_local_port_stats(const struct ofproto *ofproto_, + size_t tx_size, size_t rx_size) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + if (rx_size) { + ofproto->stats.rx_packets++; + ofproto->stats.rx_bytes += rx_size; + } + if (tx_size) { + ofproto->stats.tx_packets++; + ofproto->stats.tx_bytes += tx_size; + } +} + struct port_dump_state { struct dpif_port_dump dump; bool done; @@ -2212,6 +2397,7 @@ struct flow_miss { enum odp_key_fitness key_fitness; const struct nlattr *key; size_t key_len; + ovs_be16 initial_tci; struct list packets; }; @@ -2222,50 +2408,30 @@ struct flow_miss_op { /* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_NO_MATCH to each * OpenFlow controller as necessary according to their individual - * configurations. - * - * If 'clone' is true, the caller retains ownership of 'packet'. Otherwise, - * ownership is transferred to this function. */ + * configurations. */ static void send_packet_in_miss(struct ofproto_dpif *ofproto, struct ofpbuf *packet, - const struct flow *flow, bool clone) + const struct flow *flow) { struct ofputil_packet_in pin; - pin.packet = packet; - pin.in_port = flow->in_port; + pin.packet = packet->data; + pin.packet_len = packet->size; + pin.total_len = packet->size; pin.reason = OFPR_NO_MATCH; + + pin.table_id = 0; + pin.cookie = 0; + pin.buffer_id = 0; /* not yet known */ pin.send_len = 0; /* not used for flow table misses */ - connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow, - clone ? NULL : packet); -} -/* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_ACTION to each - * OpenFlow controller as necessary according to their individual - * configurations. - * - * 'send_len' should be the number of bytes of 'packet' to send to the - * controller, as specified in the action that caused the packet to be sent. - * - * If 'clone' is true, the caller retains ownership of 'upcall->packet'. - * Otherwise, ownership is transferred to this function. */ -static void -send_packet_in_action(struct ofproto_dpif *ofproto, struct ofpbuf *packet, - uint64_t userdata, const struct flow *flow, bool clone) -{ - struct ofputil_packet_in pin; - struct user_action_cookie cookie; + flow_get_metadata(flow, &pin.fmd); - memcpy(&cookie, &userdata, sizeof(cookie)); + /* Registers aren't meaningful on a miss. */ + memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks); - pin.packet = packet; - pin.in_port = flow->in_port; - pin.reason = OFPR_ACTION; - pin.buffer_id = 0; /* not yet known */ - pin.send_len = cookie.data; - connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow, - clone ? NULL : packet); + connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow); } static bool @@ -2301,7 +2467,8 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow, static struct flow_miss * flow_miss_create(struct hmap *todo, const struct flow *flow, enum odp_key_fitness key_fitness, - const struct nlattr *key, size_t key_len) + const struct nlattr *key, size_t key_len, + ovs_be16 initial_tci) { uint32_t hash = flow_hash(flow, 0); struct flow_miss *miss; @@ -2318,6 +2485,7 @@ flow_miss_create(struct hmap *todo, const struct flow *flow, miss->key_fitness = key_fitness; miss->key = key; miss->key_len = key_len; + miss->initial_tci = initial_tci; list_init(&miss->packets); return miss; } @@ -2350,10 +2518,8 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, flow->in_port); } - LIST_FOR_EACH_SAFE (packet, next_packet, list_node, - &miss->packets) { - list_remove(&packet->list_node); - send_packet_in_miss(ofproto, packet, flow, false); + LIST_FOR_EACH (packet, list_node, &miss->packets) { + send_packet_in_miss(ofproto, packet, flow); } return; @@ -2363,9 +2529,14 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, } subfacet = subfacet_create(ofproto, facet, - miss->key_fitness, miss->key, miss->key_len); + miss->key_fitness, miss->key, miss->key_len, + miss->initial_tci); LIST_FOR_EACH_SAFE (packet, next_packet, list_node, &miss->packets) { + struct dpif_flow_stats stats; + struct flow_miss_op *op; + struct dpif_execute *execute; + list_remove(&packet->list_node); ofproto->n_matches++; @@ -2380,29 +2551,37 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, * * See the top-level comment in fail-open.c for more information. */ - send_packet_in_miss(ofproto, packet, flow, true); + send_packet_in_miss(ofproto, packet, flow); } - if (!facet->may_install) { - facet_make_actions(ofproto, facet, packet); + if (!facet->may_install || !subfacet->actions) { + subfacet_make_actions(ofproto, subfacet, packet); } - if (!execute_controller_action(ofproto, &facet->flow, - facet->actions, facet->actions_len, - packet)) { - struct flow_miss_op *op = &ops[(*n_ops)++]; - struct dpif_execute *execute = &op->dpif_op.execute; - op->subfacet = subfacet; - execute->type = DPIF_OP_EXECUTE; - execute->key = miss->key; - execute->key_len = miss->key_len; - execute->actions - = (facet->may_install - ? facet->actions - : xmemdup(facet->actions, facet->actions_len)); - execute->actions_len = facet->actions_len; - execute->packet = packet; + dpif_flow_stats_extract(&facet->flow, packet, &stats); + subfacet_update_stats(ofproto, subfacet, &stats); + + if (flow->vlan_tci != subfacet->initial_tci) { + /* This packet was received on a VLAN splinter port. We added + * a VLAN to the packet to make the packet resemble the flow, + * but the actions were composed assuming that the packet + * contained no VLAN. So, we must remove the VLAN header from + * the packet before trying to execute the actions. */ + eth_pop_vlan(packet); } + + op = &ops[(*n_ops)++]; + execute = &op->dpif_op.execute; + op->subfacet = subfacet; + execute->type = DPIF_OP_EXECUTE; + execute->key = miss->key; + execute->key_len = miss->key_len; + execute->actions = (facet->may_install + ? subfacet->actions + : xmemdup(subfacet->actions, + subfacet->actions_len)); + execute->actions_len = subfacet->actions_len; + execute->packet = packet; } if (facet->may_install && subfacet->key_fitness != ODP_FIT_TOO_LITTLE) { @@ -2414,12 +2593,77 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY; put->key = miss->key; put->key_len = miss->key_len; - put->actions = facet->actions; - put->actions_len = facet->actions_len; + put->actions = subfacet->actions; + put->actions_len = subfacet->actions_len; put->stats = NULL; } } +/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of + * OVS_KEY_ATTR_* attributes in 'key' to a flow structure in 'flow' and returns + * an ODP_FIT_* value that indicates how well 'key' fits our expectations for + * what a flow key should contain. + * + * This function also includes some logic to help make VLAN splinters + * transparent to the rest of the upcall processing logic. In particular, if + * the extracted in_port is a VLAN splinter port, it replaces flow->in_port by + * the "real" port, sets flow->vlan_tci correctly for the VLAN of the VLAN + * splinter port, and pushes a VLAN header onto 'packet' (if it is nonnull). + * + * Sets '*initial_tci' to the VLAN TCI with which the packet was really + * received, that is, the actual VLAN TCI extracted by odp_flow_key_to_flow(). + * (This differs from the value returned in flow->vlan_tci only for packets + * received on VLAN splinters.) + */ +static enum odp_key_fitness +ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto, + const struct nlattr *key, size_t key_len, + struct flow *flow, ovs_be16 *initial_tci, + struct ofpbuf *packet) +{ + enum odp_key_fitness fitness; + uint16_t realdev; + int vid; + + fitness = odp_flow_key_to_flow(key, key_len, flow); + if (fitness == ODP_FIT_ERROR) { + return fitness; + } + *initial_tci = flow->vlan_tci; + + realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid); + if (realdev) { + /* Cause the flow to be processed as if it came in on the real device + * with the VLAN device's VLAN ID. */ + flow->in_port = realdev; + flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI); + if (packet) { + /* Make the packet resemble the flow, so that it gets sent to an + * OpenFlow controller properly, so that it looks correct for + * sFlow, and so that flow_extract() will get the correct vlan_tci + * if it is called on 'packet'. + * + * The allocated space inside 'packet' probably also contains + * 'key', that is, both 'packet' and 'key' are probably part of a + * struct dpif_upcall (see the large comment on that structure + * definition), so pushing data on 'packet' is in general not a + * good idea since it could overwrite 'key' or free it as a side + * effect. However, it's OK in this special case because we know + * that 'packet' is inside a Netlink attribute: pushing 4 bytes + * will just overwrite the 4-byte "struct nlattr", which is fine + * since we don't need that header anymore. */ + eth_push_vlan(packet, flow->vlan_tci); + } + + /* Let the caller know that we can't reproduce 'key' from 'flow'. */ + if (fitness == ODP_FIT_PERFECT) { + fitness = ODP_FIT_TOO_MUCH; + } + } + + return fitness; +} + static void handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, size_t n_upcalls) @@ -2445,19 +2689,26 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) { enum odp_key_fitness fitness; struct flow_miss *miss; + ovs_be16 initial_tci; struct flow flow; /* Obtain metadata and check userspace/kernel agreement on flow match, * then set 'flow''s header pointers. */ - fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + fitness = ofproto_dpif_extract_flow_key(ofproto, + upcall->key, upcall->key_len, + &flow, &initial_tci, + upcall->packet); if (fitness == ODP_FIT_ERROR) { + ofpbuf_delete(upcall->packet); continue; } - flow_extract(upcall->packet, flow.priority, flow.tun_id, + flow_extract(upcall->packet, flow.skb_priority, flow.tun_id, flow.in_port, &flow); /* Handle 802.1ag, LACP, and STP specially. */ if (process_special(ofproto, &flow, upcall->packet)) { + ofproto_update_local_port_stats(&ofproto->up, + 0, upcall->packet->size); ofpbuf_delete(upcall->packet); ofproto->n_matches++; continue; @@ -2465,7 +2716,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, /* Add other packets to a to-do list. */ miss = flow_miss_create(&todo, &flow, fitness, - upcall->key, upcall->key_len); + upcall->key, upcall->key_len, initial_tci); list_push_back(&miss->packets, &upcall->packet->list_node); } @@ -2496,7 +2747,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, switch (op->dpif_op.type) { case DPIF_OP_EXECUTE: execute = &op->dpif_op.execute; - if (op->subfacet->facet->actions != execute->actions) { + if (op->subfacet->actions != execute->actions) { free((struct nlattr *) execute->actions); } ofpbuf_delete((struct ofpbuf *) execute->packet); @@ -2516,45 +2767,72 @@ static void handle_userspace_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) { - struct flow flow; struct user_action_cookie cookie; + enum odp_key_fitness fitness; + ovs_be16 initial_tci; + struct flow flow; memcpy(&cookie, &upcall->userdata, sizeof(cookie)); + fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key, + upcall->key_len, &flow, + &initial_tci, upcall->packet); + if (fitness == ODP_FIT_ERROR) { + ofpbuf_delete(upcall->packet); + return; + } + if (cookie.type == USER_ACTION_COOKIE_SFLOW) { if (ofproto->sflow) { - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie); + dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, + &cookie); } - ofpbuf_delete(upcall->packet); - - } else if (cookie.type == USER_ACTION_COOKIE_CONTROLLER) { - COVERAGE_INC(ofproto_dpif_ctlr_action); - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - send_packet_in_action(ofproto, upcall->packet, upcall->userdata, - &flow, false); } else { VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata); } + ofpbuf_delete(upcall->packet); } -static void -handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +static int +handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch) { - switch (upcall->type) { - case DPIF_UC_ACTION: - handle_userspace_upcall(ofproto, upcall); - break; + struct dpif_upcall misses[FLOW_MISS_MAX_BATCH]; + int n_misses; + int i; - case DPIF_UC_MISS: - /* The caller handles these. */ - NOT_REACHED(); + assert (max_batch <= FLOW_MISS_MAX_BATCH); - case DPIF_N_UC_TYPES: - default: - VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type); - break; + n_misses = 0; + for (i = 0; i < max_batch; i++) { + struct dpif_upcall *upcall = &misses[n_misses]; + int error; + + error = dpif_recv(ofproto->dpif, upcall); + if (error) { + break; + } + + switch (upcall->type) { + case DPIF_UC_ACTION: + handle_userspace_upcall(ofproto, upcall); + break; + + case DPIF_UC_MISS: + /* Handle it later. */ + n_misses++; + break; + + case DPIF_N_UC_TYPES: + default: + VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, + upcall->type); + break; + } } + + handle_miss_upcalls(ofproto, misses, n_misses); + + return i; } /* Flow expiration. */ @@ -2630,16 +2908,9 @@ update_stats(struct ofproto_dpif *p) dpif_flow_dump_start(&dump, p->dpif); while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) { - enum odp_key_fitness fitness; struct subfacet *subfacet; - struct flow flow; - - fitness = odp_flow_key_to_flow(key, key_len, &flow); - if (fitness == ODP_FIT_ERROR) { - continue; - } - subfacet = subfacet_find(p, key, key_len, &flow); + subfacet = subfacet_find(p, key, key_len); if (subfacet && subfacet->installed) { struct facet *facet = subfacet->facet; @@ -2663,9 +2934,18 @@ update_stats(struct ofproto_dpif *p) facet_account(p, facet); facet_push_stats(facet); } else { + if (!VLOG_DROP_WARN(&rl)) { + struct ds s; + + ds_init(&s); + odp_flow_key_format(key, key_len, &s); + VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s)); + ds_destroy(&s); + } + + COVERAGE_INC(facet_unexpected); /* There's a flow in the datapath that we know nothing about, or a * flow that shouldn't be installed but was anyway. Delete it. */ - COVERAGE_INC(facet_unexpected); dpif_flow_del(p->dpif, key, key_len, NULL); } } @@ -2816,9 +3096,6 @@ rule_expire(struct rule_dpif *rule) * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in * the ofproto's classifier table. * - * The facet will initially have no ODP actions. The caller should fix that - * by calling facet_make_actions(). - * * The facet will initially have no subfacets. The caller should create (at * least) one subfacet with subfacet_create(). */ static struct facet * @@ -2843,37 +3120,9 @@ facet_create(struct rule_dpif *rule, const struct flow *flow) static void facet_free(struct facet *facet) { - free(facet->actions); free(facet); } -static bool -execute_controller_action(struct ofproto_dpif *ofproto, - const struct flow *flow, - const struct nlattr *odp_actions, size_t actions_len, - struct ofpbuf *packet) -{ - if (actions_len - && odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE - && NLA_ALIGN(odp_actions->nla_len) == actions_len) { - /* 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. - * - * This optimization will not accidentally catch sFlow - * OVS_ACTION_ATTR_USERSPACE actions, since those are encapsulated - * inside OVS_ACTION_ATTR_SAMPLE. */ - const struct nlattr *nla; - - nla = nl_attr_find_nested(odp_actions, OVS_USERSPACE_ATTR_USERDATA); - send_packet_in_action(ofproto, packet, nl_attr_get_u64(nla), flow, - false); - return true; - } else { - return false; - } -} - /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on * 'packet', which arrived on 'in_port'. * @@ -2887,11 +3136,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, struct ofpbuf key; int error; - if (execute_controller_action(ofproto, flow, odp_actions, actions_len, - packet)) { - return true; - } - ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, flow); @@ -2914,48 +3158,35 @@ facet_remove(struct ofproto_dpif *ofproto, struct facet *facet) { struct subfacet *subfacet, *next_subfacet; + assert(!list_is_empty(&facet->subfacets)); + + /* First uninstall all of the subfacets to get final statistics. */ + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + subfacet_uninstall(ofproto, subfacet); + } + + /* Flush the final stats to the rule. + * + * This might require us to have at least one subfacet around so that we + * can use its actions for accounting in facet_account(), which is why we + * have uninstalled but not yet destroyed the subfacets. */ + facet_flush_stats(ofproto, facet); + + /* Now we're really all done so destroy everything. */ LIST_FOR_EACH_SAFE (subfacet, next_subfacet, list_node, &facet->subfacets) { subfacet_destroy__(ofproto, subfacet); } - - facet_flush_stats(ofproto, facet); hmap_remove(&ofproto->facets, &facet->hmap_node); list_remove(&facet->list_node); facet_free(facet); } -/* Composes the datapath 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->has_learn = ctx.has_learn; - facet->has_normal = ctx.has_normal; - 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 void facet_account(struct ofproto_dpif *ofproto, struct facet *facet) { uint64_t n_bytes; + struct subfacet *subfacet; const struct nlattr *a; unsigned int left; ovs_be16 vlan_tci; @@ -2972,7 +3203,9 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) if (facet->has_learn || facet->has_normal) { struct action_xlate_ctx ctx; - action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL); + action_xlate_ctx_init(&ctx, ofproto, &facet->flow, + facet->flow.vlan_tci, + facet->rule->up.flow_cookie, NULL); ctx.may_learn = true; ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions, facet->rule->up.n_actions)); @@ -2986,9 +3219,15 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) * as a basis. We also need to track the actual VLAN on which the packet * is going to be sent to ensure that it matches the one passed to * bond_choose_output_slave(). (Otherwise, we will account to the wrong - * hash bucket.) */ + * hash bucket.) + * + * We use the actions from an arbitrary subfacet because they should all + * be equally valid for our purpose. */ + subfacet = CONTAINER_OF(list_front(&facet->subfacets), + struct subfacet, list_node); vlan_tci = facet->flow.vlan_tci; - NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) { + NL_ATTR_FOR_EACH_UNSAFE (a, left, + subfacet->actions, subfacet->actions_len) { const struct ovs_action_push_vlan *vlan; struct ofport_dpif *port; @@ -3117,12 +3356,17 @@ facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow) static bool facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) { + struct actions { + struct nlattr *odp_actions; + size_t actions_len; + }; + struct actions *new_actions; + struct action_xlate_ctx ctx; - struct ofpbuf *odp_actions; struct rule_dpif *new_rule; struct subfacet *subfacet; bool actions_changed; - bool flush_stats; + int i; COVERAGE_INC(facet_revalidate); @@ -3139,19 +3383,27 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) * 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 datapath actions changed or the installability changed, * then we need to talk to the datapath. */ - flush_stats = false; + i = 0; + new_actions = NULL; + memset(&ctx, 0, sizeof ctx); LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { - bool should_install = (ctx.may_set_up_flow - && subfacet->key_fitness != ODP_FIT_TOO_LITTLE); + struct ofpbuf *odp_actions; + bool should_install; + + action_xlate_ctx_init(&ctx, ofproto, &facet->flow, + subfacet->initial_tci, new_rule->up.flow_cookie, + NULL); + odp_actions = xlate_actions(&ctx, new_rule->up.actions, + new_rule->up.n_actions); + actions_changed = (subfacet->actions_len != odp_actions->size + || memcmp(subfacet->actions, odp_actions->data, + subfacet->actions_len)); + + should_install = (ctx.may_set_up_flow + && subfacet->key_fitness != ODP_FIT_TOO_LITTLE); if (actions_changed || should_install != subfacet->installed) { if (should_install) { struct dpif_flow_stats stats; @@ -3162,10 +3414,20 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) } else { subfacet_uninstall(ofproto, subfacet); } - flush_stats = true; + + if (!new_actions) { + new_actions = xcalloc(list_size(&facet->subfacets), + sizeof *new_actions); + } + new_actions[i].odp_actions = xmemdup(odp_actions->data, + odp_actions->size); + new_actions[i].actions_len = odp_actions->size; } + + ofpbuf_delete(odp_actions); + i++; } - if (flush_stats) { + if (new_actions) { facet_flush_stats(ofproto, facet); } @@ -3175,10 +3437,18 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) facet->may_install = ctx.may_set_up_flow; facet->has_learn = ctx.has_learn; facet->has_normal = ctx.has_normal; - if (actions_changed) { - free(facet->actions); - facet->actions_len = odp_actions->size; - facet->actions = xmemdup(odp_actions->data, odp_actions->size); + facet->mirrors = ctx.mirrors; + if (new_actions) { + i = 0; + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + if (new_actions[i].odp_actions) { + free(subfacet->actions); + subfacet->actions = new_actions[i].odp_actions; + subfacet->actions_len = new_actions[i].actions_len; + } + i++; + } + free(new_actions); } if (facet->rule != new_rule) { COVERAGE_INC(facet_changed_rule); @@ -3186,11 +3456,9 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) list_push_back(&new_rule->facets, &facet->list_node); facet->rule = new_rule; facet->used = new_rule->up.created; - facet->rs_used = facet->used; + facet->prev_used = facet->used; } - ofpbuf_delete(odp_actions); - return true; } @@ -3214,30 +3482,33 @@ facet_reset_counters(struct facet *facet) { facet->packet_count = 0; facet->byte_count = 0; - facet->rs_packet_count = 0; - facet->rs_byte_count = 0; + facet->prev_packet_count = 0; + facet->prev_byte_count = 0; facet->accounted_bytes = 0; } static void facet_push_stats(struct facet *facet) { - uint64_t rs_packets, rs_bytes; + uint64_t new_packets, new_bytes; - assert(facet->packet_count >= facet->rs_packet_count); - assert(facet->byte_count >= facet->rs_byte_count); - assert(facet->used >= facet->rs_used); + assert(facet->packet_count >= facet->prev_packet_count); + assert(facet->byte_count >= facet->prev_byte_count); + assert(facet->used >= facet->prev_used); - rs_packets = facet->packet_count - facet->rs_packet_count; - rs_bytes = facet->byte_count - facet->rs_byte_count; + new_packets = facet->packet_count - facet->prev_packet_count; + new_bytes = facet->byte_count - facet->prev_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; + if (new_packets || new_bytes || facet->used > facet->prev_used) { + facet->prev_packet_count = facet->packet_count; + facet->prev_byte_count = facet->byte_count; + facet->prev_used = facet->used; flow_push_stats(facet->rule, &facet->flow, - rs_packets, rs_bytes, facet->used); + new_packets, new_bytes, facet->used); + + update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto), + facet->mirrors, new_packets, new_bytes); } } @@ -3261,7 +3532,7 @@ push_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule) } /* Pushes flow statistics to the rules which 'flow' resubmits into given - * 'rule''s actions. */ + * 'rule''s actions and mirrors. */ static void flow_push_stats(const struct rule_dpif *rule, const struct flow *flow, uint64_t packets, uint64_t bytes, @@ -3274,7 +3545,8 @@ flow_push_stats(const struct rule_dpif *rule, push.bytes = bytes; push.used = used; - action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL); + action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, + rule->up.flow_cookie, NULL); push.ctx.resubmit_hook = push_resubmit; ofpbuf_delete(xlate_actions(&push.ctx, rule->up.actions, rule->up.n_actions)); @@ -3304,11 +3576,15 @@ subfacet_find__(struct ofproto_dpif *ofproto, /* Searches 'facet' (within 'ofproto') for a subfacet with the specified * 'key_fitness', 'key', and 'key_len'. Returns the existing subfacet if - * there is one, otherwise creates and returns a new subfacet. */ + * there is one, otherwise creates and returns a new subfacet. + * + * If the returned subfacet is new, then subfacet->actions will be NULL, in + * which case the caller must populate the actions with + * subfacet_make_actions(). */ static struct subfacet * subfacet_create(struct ofproto_dpif *ofproto, struct facet *facet, enum odp_key_fitness key_fitness, - const struct nlattr *key, size_t key_len) + const struct nlattr *key, size_t key_len, ovs_be16 initial_tci) { uint32_t key_hash = odp_flow_key_hash(key, key_len); struct subfacet *subfacet; @@ -3335,6 +3611,7 @@ subfacet_create(struct ofproto_dpif *ofproto, struct facet *facet, subfacet->key_len = key_len; } subfacet->installed = false; + subfacet->initial_tci = initial_tci; return subfacet; } @@ -3343,12 +3620,18 @@ subfacet_create(struct ofproto_dpif *ofproto, struct facet *facet, * 'flow'. Returns the subfacet if one exists, otherwise NULL. */ static struct subfacet * subfacet_find(struct ofproto_dpif *ofproto, - const struct nlattr *key, size_t key_len, - const struct flow *flow) + const struct nlattr *key, size_t key_len) { uint32_t key_hash = odp_flow_key_hash(key, key_len); + enum odp_key_fitness fitness; + struct flow flow; + + fitness = odp_flow_key_to_flow(key, key_len, &flow); + if (fitness == ODP_FIT_ERROR) { + return NULL; + } - return subfacet_find__(ofproto, key, key_len, key_hash, flow); + return subfacet_find__(ofproto, key, key_len, key_hash, &flow); } /* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from @@ -3360,6 +3643,7 @@ subfacet_destroy__(struct ofproto_dpif *ofproto, struct subfacet *subfacet) hmap_remove(&ofproto->subfacets, &subfacet->hmap_node); list_remove(&subfacet->list_node); free(subfacet->key); + free(subfacet->actions); free(subfacet); } @@ -3370,9 +3654,11 @@ subfacet_destroy(struct ofproto_dpif *ofproto, struct subfacet *subfacet) { struct facet *facet = subfacet->facet; - subfacet_destroy__(ofproto, subfacet); - if (list_is_empty(&facet->subfacets)) { + if (list_is_singleton(&facet->subfacets)) { + /* facet_remove() needs at least one subfacet (it will remove it). */ facet_remove(ofproto, facet); + } else { + subfacet_destroy__(ofproto, subfacet); } } @@ -3391,21 +3677,51 @@ subfacet_get_key(struct subfacet *subfacet, struct odputil_keybuf *keybuf, } } -/* Updates 'subfacet''s datapath flow, 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 'subfacet' was last updated. - * - * Returns 0 if successful, otherwise a positive errno value. */ -static int -subfacet_install(struct ofproto_dpif *ofproto, struct subfacet *subfacet, - const struct nlattr *actions, size_t actions_len, - struct dpif_flow_stats *stats) +/* Composes the datapath actions for 'subfacet' based on its rule's actions. */ +static void +subfacet_make_actions(struct ofproto_dpif *p, struct subfacet *subfacet, + const struct ofpbuf *packet) { - struct odputil_keybuf keybuf; - enum dpif_flow_put_flags flags; - struct ofpbuf key; - int ret; + struct facet *facet = subfacet->facet; + const struct rule_dpif *rule = facet->rule; + struct ofpbuf *odp_actions; + struct action_xlate_ctx ctx; + + action_xlate_ctx_init(&ctx, p, &facet->flow, subfacet->initial_tci, + rule->up.flow_cookie, 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->has_learn = ctx.has_learn; + facet->has_normal = ctx.has_normal; + facet->nf_flow.output_iface = ctx.nf_output_iface; + facet->mirrors = ctx.mirrors; + + if (subfacet->actions_len != odp_actions->size + || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) { + free(subfacet->actions); + subfacet->actions_len = odp_actions->size; + subfacet->actions = xmemdup(odp_actions->data, odp_actions->size); + } + + ofpbuf_delete(odp_actions); +} + +/* Updates 'subfacet''s datapath flow, 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 'subfacet' was last updated. + * + * Returns 0 if successful, otherwise a positive errno value. */ +static int +subfacet_install(struct ofproto_dpif *ofproto, struct subfacet *subfacet, + 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) { @@ -3650,7 +3966,8 @@ rule_execute(struct rule *rule_, const struct flow *flow, struct ofpbuf *odp_actions; size_t size; - action_xlate_ctx_init(&ctx, ofproto, flow, packet); + action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, + rule->up.flow_cookie, 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, @@ -3682,18 +3999,27 @@ rule_modify_actions(struct rule *rule_) complete_operation(rule); } -/* Sends 'packet' out of port 'odp_port' within 'ofproto'. +/* Sends 'packet' out 'ofport'. + * May modify 'packet'. * Returns 0 if successful, otherwise a positive errno value. */ static int -send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, - const struct ofpbuf *packet) +send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) { + const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct ofpbuf key, odp_actions; struct odputil_keybuf keybuf; + uint16_t odp_port; struct flow flow; int error; flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow); + odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port, + flow.vlan_tci); + if (odp_port != ofport->odp_port) { + eth_pop_vlan(packet); + flow.vlan_tci = htons(0); + } + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, &flow); @@ -3711,6 +4037,7 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)", ofproto->up.name, odp_port, strerror(error)); } + ofproto_update_local_port_stats(ofport->up.ofproto, packet->size, 0); return error; } @@ -3821,172 +4148,15 @@ fix_sflow_action(struct action_xlate_ctx *ctx) cookie->vlan_tci = base->vlan_tci; } -static void -commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, - const void *key, size_t key_size) -{ - size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); - nl_msg_put_unspec(odp_actions, key_type, key, key_size); - nl_msg_end_nested(odp_actions, offset); -} - -static void -commit_set_tun_id_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (base->tun_id == flow->tun_id) { - return; - } - base->tun_id = flow->tun_id; - - commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID, - &base->tun_id, sizeof(base->tun_id)); -} - -static void -commit_set_ether_addr_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - struct ovs_key_ethernet eth_key; - - if (eth_addr_equals(base->dl_src, flow->dl_src) && - eth_addr_equals(base->dl_dst, flow->dl_dst)) { - return; - } - - memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN); - memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN); - - memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN); - memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN); - - commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET, - ð_key, sizeof(eth_key)); -} - -static void -commit_vlan_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (base->vlan_tci == flow->vlan_tci) { - return; - } - - if (base->vlan_tci & htons(VLAN_CFI)) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } - - if (flow->vlan_tci & htons(VLAN_CFI)) { - struct ovs_action_push_vlan vlan; - - vlan.vlan_tpid = htons(ETH_TYPE_VLAN); - vlan.vlan_tci = flow->vlan_tci; - nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, - &vlan, sizeof vlan); - } - base->vlan_tci = flow->vlan_tci; -} - -static void -commit_set_nw_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - struct ovs_key_ipv4 ipv4_key; - - if (base->dl_type != htons(ETH_TYPE_IP) || - !base->nw_src || !base->nw_dst) { - return; - } - - if (base->nw_src == flow->nw_src && - base->nw_dst == flow->nw_dst && - base->nw_tos == flow->nw_tos && - base->nw_ttl == flow->nw_ttl && - base->nw_frag == flow->nw_frag) { - return; - } - - ipv4_key.ipv4_src = base->nw_src = flow->nw_src; - ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst; - ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos; - ipv4_key.ipv4_ttl = base->nw_ttl = flow->nw_ttl; - ipv4_key.ipv4_proto = base->nw_proto; - ipv4_key.ipv4_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE - : base->nw_frag == FLOW_NW_FRAG_ANY - ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); - - commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4, - &ipv4_key, sizeof(ipv4_key)); -} - -static void -commit_set_port_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (!base->tp_src || !base->tp_dst) { - return; - } - - if (base->tp_src == flow->tp_src && - base->tp_dst == flow->tp_dst) { - return; - } - - if (flow->nw_proto == IPPROTO_TCP) { - struct ovs_key_tcp port_key; - - port_key.tcp_src = base->tp_src = flow->tp_src; - port_key.tcp_dst = base->tp_dst = flow->tp_dst; - - commit_set_action(odp_actions, OVS_KEY_ATTR_TCP, - &port_key, sizeof(port_key)); - - } else if (flow->nw_proto == IPPROTO_UDP) { - struct ovs_key_udp port_key; - - port_key.udp_src = base->tp_src = flow->tp_src; - port_key.udp_dst = base->tp_dst = flow->tp_dst; - - commit_set_action(odp_actions, OVS_KEY_ATTR_UDP, - &port_key, sizeof(port_key)); - } -} - -static void -commit_set_priority_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (base->priority == flow->priority) { - return; - } - base->priority = flow->priority; - - commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY, - &base->priority, sizeof(base->priority)); -} - -static void -commit_odp_actions(struct action_xlate_ctx *ctx) -{ - const struct flow *flow = &ctx->flow; - struct flow *base = &ctx->base_flow; - struct ofpbuf *odp_actions = ctx->odp_actions; - - commit_set_tun_id_action(flow, base, odp_actions); - commit_set_ether_addr_action(flow, base, odp_actions); - commit_vlan_action(flow, base, odp_actions); - commit_set_nw_action(flow, base, odp_actions); - commit_set_port_action(flow, base, odp_actions); - commit_set_priority_action(flow, base, odp_actions); -} - static void compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, bool check_stp) { const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port); uint16_t odp_port = ofp_port_to_odp_port(ofp_port); + ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci; uint8_t flow_nw_tos = ctx->flow.nw_tos; + uint16_t out_port; if (ofport) { struct priority_to_dscp *pdscp; @@ -3996,7 +4166,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, return; } - pdscp = get_priority(ofport, ctx->flow.priority); + pdscp = get_priority(ofport, ctx->flow.skb_priority); if (pdscp) { ctx->flow.nw_tos &= ~IP_DSCP_MASK; ctx->flow.nw_tos |= pdscp->dscp; @@ -4007,11 +4177,18 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, * later and we're pre-populating the flow table. */ } - commit_odp_actions(ctx); - nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); + out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port, + ctx->flow.vlan_tci); + if (out_port != odp_port) { + ctx->flow.vlan_tci = htons(0); + } + commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions); + nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port); + ctx->sflow_odp_port = odp_port; ctx->sflow_n_outputs++; ctx->nf_output_iface = ofp_port; + ctx->flow.vlan_tci = flow_vlan_tci; ctx->flow.nw_tos = flow_nw_tos; } @@ -4060,8 +4237,12 @@ xlate_table_action(struct action_xlate_ctx *ctx, } if (rule) { + ovs_be64 old_cookie = ctx->cookie; + ctx->recurse++; + ctx->cookie = rule->up.flow_cookie; do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx); + ctx->cookie = old_cookie; ctx->recurse--; } @@ -4094,7 +4275,6 @@ flood_packets(struct action_xlate_ctx *ctx, bool all) { struct ofport_dpif *ofport; - commit_odp_actions(ctx); HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) { uint16_t ofp_port = ofport->up.ofp_port; @@ -4113,16 +4293,62 @@ flood_packets(struct action_xlate_ctx *ctx, bool all) } static void -compose_controller_action(struct action_xlate_ctx *ctx, int len) +execute_controller_action(struct action_xlate_ctx *ctx, int len) { - struct user_action_cookie cookie; + struct ofputil_packet_in pin; + struct ofpbuf *packet; - commit_odp_actions(ctx); - cookie.type = USER_ACTION_COOKIE_CONTROLLER; - cookie.data = len; - cookie.n_output = 0; - cookie.vlan_tci = 0; - put_userspace_action(ctx->ofproto, ctx->odp_actions, &ctx->flow, &cookie); + ctx->may_set_up_flow = false; + if (!ctx->packet) { + return; + } + + packet = ofpbuf_clone(ctx->packet); + + if (packet->l2 && packet->l3) { + struct eth_header *eh; + + eth_pop_vlan(packet); + eh = packet->l2; + assert(eh->eth_type == ctx->flow.dl_type); + memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src); + memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst); + + if (ctx->flow.vlan_tci & htons(VLAN_CFI)) { + eth_push_vlan(packet, ctx->flow.vlan_tci); + } + + if (packet->l4) { + if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) { + packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst, + ctx->flow.nw_tos, ctx->flow.nw_ttl); + } + + if (packet->l7) { + if (ctx->flow.nw_proto == IPPROTO_TCP) { + packet_set_tcp_port(packet, ctx->flow.tp_src, + ctx->flow.tp_dst); + } else if (ctx->flow.nw_proto == IPPROTO_UDP) { + packet_set_udp_port(packet, ctx->flow.tp_src, + ctx->flow.tp_dst); + } + } + } + } + + pin.packet = packet->data; + pin.packet_len = packet->size; + pin.reason = OFPR_ACTION; + pin.table_id = ctx->table_id; + pin.cookie = ctx->cookie; + + pin.buffer_id = 0; + pin.send_len = len; + pin.total_len = packet->size; + flow_get_metadata(&ctx->flow, &pin.fmd); + + connmgr_send_packet_in(ctx->ofproto->up.connmgr, &pin, &ctx->flow); + ofpbuf_delete(packet); } static void @@ -4150,7 +4376,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx, flood_packets(ctx, true); break; case OFPP_CONTROLLER: - compose_controller_action(ctx, max_len); + execute_controller_action(ctx, max_len); break; case OFPP_LOCAL: compose_output_action(ctx, OFPP_LOCAL); @@ -4219,10 +4445,10 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, } /* Add datapath actions. */ - flow_priority = ctx->flow.priority; - ctx->flow.priority = priority; + flow_priority = ctx->flow.skb_priority; + ctx->flow.skb_priority = priority; compose_output_action(ctx, ofp_port); - ctx->flow.priority = flow_priority; + ctx->flow.skb_priority = flow_priority; /* Update NetFlow output port. */ if (ctx->nf_output_iface == NF_OUT_DROP) { @@ -4247,7 +4473,7 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx, return; } - ctx->flow.priority = priority; + ctx->flow.skb_priority = priority; } struct xlate_reg_state { @@ -4408,8 +4634,11 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPUTIL_OFPAT_SET_NW_TOS: - ctx->flow.nw_tos &= ~IP_DSCP_MASK; - ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK; + /* OpenFlow 1.0 only supports IPv4. */ + if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) { + ctx->flow.nw_tos &= ~IP_DSCP_MASK; + ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK; + } break; case OFPUTIL_OFPAT_SET_TP_SRC: @@ -4445,7 +4674,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPUTIL_NXAST_POP_QUEUE: - ctx->flow.priority = ctx->original_priority; + ctx->flow.skb_priority = ctx->orig_skb_priority; break; case OFPUTIL_NXAST_REG_MOVE: @@ -4521,10 +4750,15 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, static void action_xlate_ctx_init(struct action_xlate_ctx *ctx, struct ofproto_dpif *ofproto, const struct flow *flow, + ovs_be16 initial_tci, ovs_be64 cookie, const struct ofpbuf *packet) { ctx->ofproto = ofproto; ctx->flow = *flow; + ctx->base_flow = ctx->flow; + ctx->base_flow.tun_id = 0; + ctx->base_flow.vlan_tci = initial_tci; + ctx->cookie = cookie; ctx->packet = packet; ctx->may_learn = packet != NULL; ctx->resubmit_hook = NULL; @@ -4534,6 +4768,8 @@ static struct ofpbuf * xlate_actions(struct action_xlate_ctx *ctx, const union ofp_action *in, size_t n_in) { + struct flow orig_flow = ctx->flow; + COVERAGE_INC(ofproto_dpif_xlate); ctx->odp_actions = ofpbuf_new(512); @@ -4543,10 +4779,9 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->has_learn = false; ctx->has_normal = false; ctx->nf_output_iface = NF_OUT_DROP; + ctx->mirrors = 0; ctx->recurse = 0; - ctx->original_priority = ctx->flow.priority; - ctx->base_flow = ctx->flow; - ctx->base_flow.tun_id = 0; + ctx->orig_skb_priority = ctx->flow.skb_priority; ctx->table_id = 0; ctx->exit = false; @@ -4587,6 +4822,7 @@ xlate_actions(struct action_xlate_ctx *ctx, compose_output_action(ctx, OFPP_LOCAL); } } + add_mirror_actions(ctx, &orig_flow); fix_sflow_action(ctx); } @@ -4633,6 +4869,11 @@ input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid) static bool input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn) { + /* Allow any VID on the OFPP_NONE port. */ + if (in_bundle == &ofpp_none_bundle) { + return true; + } + switch (in_bundle->vlan_mode) { case PORT_VLAN_ACCESS: if (vid) { @@ -4763,34 +5004,6 @@ ofbundle_get_a_port(const struct ofbundle *bundle) struct ofport_dpif, bundle_node); } -static mirror_mask_t -compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan, - const struct ofbundle *in_bundle, - const struct ofbundle *out_bundle) -{ - mirror_mask_t dst_mirrors = 0; - - 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) { - output_normal(ctx, bundle, vlan); - dst_mirrors |= bundle->dst_mirrors; - } - } - ctx->nf_output_iface = NF_OUT_FLOOD; - } else if (out_bundle) { - output_normal(ctx, out_bundle, vlan); - dst_mirrors = out_bundle->dst_mirrors; - } - - return dst_mirrors; -} - static bool vlan_is_mirrored(const struct ofmirror *m, int vlan) { @@ -4839,18 +5052,65 @@ eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN]) } static void -output_mirrors(struct action_xlate_ctx *ctx, - uint16_t vlan, const struct ofbundle *in_bundle, - mirror_mask_t dst_mirrors) +add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow) { struct ofproto_dpif *ofproto = ctx->ofproto; mirror_mask_t mirrors; + struct ofbundle *in_bundle; + uint16_t vlan; + uint16_t vid; + const struct nlattr *a; + size_t left; + + in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port, + ctx->packet != NULL); + if (!in_bundle) { + return; + } + mirrors = in_bundle->src_mirrors; + + /* Drop frames on bundles reserved for mirroring. */ + if (in_bundle->mirror_out) { + if (ctx->packet != NULL) { + 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", + ctx->ofproto->up.name, in_bundle->name); + } + return; + } + + /* Check VLAN. */ + vid = vlan_tci_to_vid(orig_flow->vlan_tci); + if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) { + return; + } + vlan = input_vid_to_vlan(in_bundle, vid); + + /* Look at the output ports to check for destination selections. */ + + NL_ATTR_FOR_EACH (a, left, ctx->odp_actions->data, + ctx->odp_actions->size) { + enum ovs_action_attr type = nl_attr_type(a); + struct ofport_dpif *ofport; + + if (type != OVS_ACTION_ATTR_OUTPUT) { + continue; + } + + ofport = get_odp_port(ofproto, nl_attr_get_u32(a)); + if (ofport && ofport->bundle) { + mirrors |= ofport->bundle->dst_mirrors; + } + } - mirrors = in_bundle->src_mirrors | dst_mirrors; if (!mirrors) { return; } + /* Restore the original packet before adding the mirror actions. */ + ctx->flow = *orig_flow; + while (mirrors) { struct ofmirror *m; @@ -4862,9 +5122,10 @@ output_mirrors(struct action_xlate_ctx *ctx, } mirrors &= ~m->dup_mirrors; + ctx->mirrors |= m->dup_mirrors; if (m->out) { output_normal(ctx, m->out, vlan); - } else if (eth_dst_may_rspan(ctx->flow.dl_dst) + } else if (eth_dst_may_rspan(orig_flow->dl_dst) && vlan != m->out_vlan) { struct ofbundle *bundle; @@ -4878,6 +5139,34 @@ output_mirrors(struct action_xlate_ctx *ctx, } } +static void +update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors, + uint64_t packets, uint64_t bytes) +{ + if (!mirrors) { + return; + } + + for (; mirrors; mirrors &= mirrors - 1) { + struct ofmirror *m; + + m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1]; + + if (!m) { + /* In normal circumstances 'm' will not be NULL. However, + * if mirrors are reconfigured, we can temporarily get out + * of sync in facet_revalidate(). We could "correct" the + * mirror list before reaching here, but doing that would + * not properly account the traffic stats we've currently + * accumulated for previous mirror configuration. */ + continue; + } + + m->packet_count += packets; + m->byte_count += bytes; + } +} + /* 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. */ @@ -4898,6 +5187,11 @@ update_learning_table(struct ofproto_dpif *ofproto, { struct mac_entry *mac; + /* Don't learn the OFPP_NONE port. */ + if (in_bundle == &ofpp_none_bundle) { + return; + } + if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) { return; } @@ -4928,15 +5222,21 @@ update_learning_table(struct ofproto_dpif *ofproto, } } -static struct ofport_dpif * +static struct ofbundle * lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn) { struct ofport_dpif *ofport; + /* Special-case OFPP_NONE, which a controller may use as the ingress + * port for traffic that it is sourcing. */ + if (in_port == OFPP_NONE) { + return &ofpp_none_bundle; + } + /* Find the port and bundle for the received packet. */ ofport = get_ofp_port(ofproto, in_port); if (ofport && ofport->bundle) { - return ofport; + return ofport->bundle; } /* Odd. A few possible reasons here: @@ -5012,25 +5312,23 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, static void xlate_normal(struct action_xlate_ctx *ctx) { - mirror_mask_t dst_mirrors = 0; struct ofport_dpif *in_port; struct ofbundle *in_bundle; - struct ofbundle *out_bundle; struct mac_entry *mac; uint16_t vlan; uint16_t vid; ctx->has_normal = true; - /* Obtain in_port from ctx->flow.in_port. - * - * lookup_input_bundle() also ensures that in_port belongs to a bundle. */ - in_port = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port, + in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port, ctx->packet != NULL); - if (!in_port) { + if (!in_bundle) { return; } - in_bundle = in_port->bundle; + + /* We know 'in_port' exists unless it is "ofpp_none_bundle", + * since lookup_input_bundle() succeeded. */ + in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port); /* Drop malformed frames. */ if (ctx->flow.dl_type == htons(ETH_TYPE_VLAN) && @@ -5063,8 +5361,8 @@ xlate_normal(struct action_xlate_ctx *ctx) vlan = input_vid_to_vlan(in_bundle, vid); /* Check other admissibility requirements. */ - if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) { - output_mirrors(ctx, vlan, in_bundle, 0); + if (in_port && + !is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) { return; } @@ -5077,24 +5375,22 @@ xlate_normal(struct action_xlate_ctx *ctx) 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. */ - ctx->may_set_up_flow = false; - return; + if (mac->port.p != in_bundle) { + output_normal(ctx, mac->port.p, vlan); + } } else { - out_bundle = OFBUNDLE_FLOOD; - } + struct ofbundle *bundle; - /* Don't send packets out their input bundles. */ - if (in_bundle != out_bundle) { - dst_mirrors = compose_dsts(ctx, vlan, in_bundle, out_bundle); + HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) { + if (bundle != in_bundle + && ofbundle_includes_vlan(bundle, vlan) + && bundle->floodable + && !bundle->mirror_out) { + output_normal(ctx, bundle, vlan); + } + } + ctx->nf_output_iface = NF_OUT_FLOOD; } - output_mirrors(ctx, vlan, in_bundle, dst_mirrors); } /* Optimized flow revalidation. @@ -5241,15 +5537,24 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, ofproto->max_ports); if (!error) { struct odputil_keybuf keybuf; - struct action_xlate_ctx ctx; struct ofpbuf *odp_actions; + struct ofproto_push push; struct ofpbuf key; ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, flow); - action_xlate_ctx_init(&ctx, ofproto, flow, packet); - odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions); + action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, 0, + packet); + + /* Ensure that resubmits in 'ofp_actions' get accounted to their + * matching rules. */ + push.packets = 1; + push.bytes = packet->size; + push.used = time_msec(); + push.ctx.resubmit_hook = push_resubmit; + + odp_actions = xlate_actions(&push.ctx, ofp_actions, n_ofp_actions); dpif_execute(ofproto->dpif, key.data, key.size, odp_actions->data, odp_actions->size, packet); ofpbuf_delete(odp_actions); @@ -5298,8 +5603,8 @@ send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet) if (subfacet->installed) { struct dpif_flow_stats stats; - subfacet_install(ofproto, subfacet, facet->actions, - facet->actions_len, &stats); + subfacet_install(ofproto, subfacet, subfacet->actions, + subfacet->actions_len, &stats); subfacet_update_stats(ofproto, subfacet, &stats); } } @@ -5325,19 +5630,24 @@ send_netflow_active_timeouts(struct ofproto_dpif *ofproto) 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); + struct ofproto_dpif *ofproto; + + HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_node, + hash_string(name, 0), &all_ofproto_dpifs) { + if (!strcmp(ofproto->up.name, name)) { + return ofproto; + } + } + return NULL; } static void -ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, - const char *args, void *aux OVS_UNUSED) +ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) { const struct ofproto_dpif *ofproto; - ofproto = ofproto_dpif_lookup(args); + ofproto = ofproto_dpif_lookup(argv[1]); if (!ofproto) { unixctl_command_reply(conn, 501, "no such bridge"); return; @@ -5348,14 +5658,14 @@ ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, } static void -ofproto_unixctl_fdb_show(struct unixctl_conn *conn, - const char *args, void *aux OVS_UNUSED) +ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], 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); + ofproto = ofproto_dpif_lookup(argv[1]); if (!ofproto) { unixctl_command_reply(conn, 501, "no such bridge"); return; @@ -5441,16 +5751,15 @@ trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule) } static void -ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, +ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { - char *dpname, *arg1, *arg2, *arg3, *arg4; - char *args = xstrdup(args_); - char *save_ptr = NULL; + const char *dpname = argv[1]; struct ofproto_dpif *ofproto; struct ofpbuf odp_key; struct ofpbuf *packet; struct rule_dpif *rule; + ovs_be16 initial_tci; struct ds result; struct flow flow; char *s; @@ -5459,76 +5768,69 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, ofpbuf_init(&odp_key, 0); ds_init(&result); - dpname = strtok_r(args, " ", &save_ptr); - arg1 = strtok_r(NULL, " ", &save_ptr); - arg2 = strtok_r(NULL, " ", &save_ptr); - arg3 = strtok_r(NULL, " ", &save_ptr); - arg4 = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */ - if (dpname && arg1 && (!arg2 || !strcmp(arg2, "-generate")) && !arg3) { + ofproto = ofproto_dpif_lookup(dpname); + if (!ofproto) { + unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list " + "for help)"); + goto exit; + } + if (argc == 3 || (argc == 4 && !strcmp(argv[3], "-generate"))) { /* ofproto/trace dpname flow [-generate] */ + const char *flow_s = argv[2]; + const char *generate_s = argv[3]; int error; /* Convert string to datapath key. */ ofpbuf_init(&odp_key, 0); - error = odp_flow_key_from_string(arg1, NULL, &odp_key); + error = odp_flow_key_from_string(flow_s, NULL, &odp_key); if (error) { unixctl_command_reply(conn, 501, "Bad flow syntax"); goto exit; } /* Convert odp_key to flow. */ - error = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); - if (error) { + error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data, + odp_key.size, &flow, + &initial_tci, NULL); + if (error == ODP_FIT_ERROR) { unixctl_command_reply(conn, 501, "Invalid flow"); goto exit; } /* Generate a packet, if requested. */ - if (arg2) { + if (generate_s) { packet = ofpbuf_new(0); flow_compose(packet, &flow); } - } else if (dpname && arg1 && arg2 && arg3 && arg4) { + } else if (argc == 6) { /* ofproto/trace dpname priority tun_id in_port packet */ - uint16_t in_port; - ovs_be64 tun_id; - uint32_t priority; - - priority = atoi(arg1); - tun_id = htonll(strtoull(arg2, NULL, 0)); - in_port = ofp_port_to_odp_port(atoi(arg3)); - - packet = ofpbuf_new(strlen(args) / 2); - arg4 = ofpbuf_put_hex(packet, arg4, NULL); - arg4 += strspn(arg4, " "); - if (*arg4 != '\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"); + const char *priority_s = argv[2]; + const char *tun_id_s = argv[3]; + const char *in_port_s = argv[4]; + const char *packet_s = argv[5]; + uint16_t in_port = ofp_port_to_odp_port(atoi(in_port_s)); + ovs_be64 tun_id = htonll(strtoull(tun_id_s, NULL, 0)); + uint32_t priority = atoi(priority_s); + const char *msg; + + msg = eth_from_hex(packet_s, &packet); + if (msg) { + unixctl_command_reply(conn, 501, msg); goto exit; } ds_put_cstr(&result, "Packet: "); - s = ofp_packet_to_string(packet->data, packet->size, packet->size); + s = ofp_packet_to_string(packet->data, packet->size); ds_put_cstr(&result, s); free(s); flow_extract(packet, priority, tun_id, in_port, &flow); + initial_tci = flow.vlan_tci; } else { 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; - } - ds_put_cstr(&result, "Flow: "); flow_format(&result, &flow); ds_put_char(&result, '\n'); @@ -5541,7 +5843,8 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, trace.result = &result; trace.flow = flow; - action_xlate_ctx_init(&trace.ctx, ofproto, &flow, packet); + action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci, + rule->up.flow_cookie, packet); trace.ctx.resubmit_hook = trace_resubmit; odp_actions = xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions); @@ -5568,20 +5871,19 @@ exit: ds_destroy(&result); ofpbuf_delete(packet); ofpbuf_uninit(&odp_key); - free(args); } static void -ofproto_dpif_clog(struct unixctl_conn *conn OVS_UNUSED, - const char *args_ OVS_UNUSED, void *aux OVS_UNUSED) +ofproto_dpif_clog(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { clogged = true; unixctl_command_reply(conn, 200, NULL); } static void -ofproto_dpif_unclog(struct unixctl_conn *conn OVS_UNUSED, - const char *args_ OVS_UNUSED, void *aux OVS_UNUSED) +ofproto_dpif_unclog(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { clogged = false; unixctl_command_reply(conn, 200, NULL); @@ -5596,15 +5898,160 @@ ofproto_dpif_unixctl_init(void) } registered = true; - unixctl_command_register("ofproto/trace", - "bridge {tun_id in_port packet | odp_flow [-generate]}", - ofproto_unixctl_trace, NULL); - unixctl_command_register("fdb/flush", "bridge", ofproto_unixctl_fdb_flush, - NULL); - unixctl_command_register("fdb/show", "bridge", ofproto_unixctl_fdb_show, - NULL); - unixctl_command_register("ofproto/clog", "", ofproto_dpif_clog, NULL); - unixctl_command_register("ofproto/unclog", "", ofproto_dpif_unclog, NULL); + unixctl_command_register( + "ofproto/trace", + "bridge {tun_id in_port packet | odp_flow [-generate]}", + 2, 5, ofproto_unixctl_trace, NULL); + unixctl_command_register("fdb/flush", "bridge", 1, 1, + ofproto_unixctl_fdb_flush, NULL); + unixctl_command_register("fdb/show", "bridge", 1, 1, + ofproto_unixctl_fdb_show, NULL); + unixctl_command_register("ofproto/clog", "", 0, 0, + ofproto_dpif_clog, NULL); + unixctl_command_register("ofproto/unclog", "", 0, 0, + ofproto_dpif_unclog, NULL); +} + +/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) + * + * This is deprecated. It is only for compatibility with broken device drivers + * in old versions of Linux that do not properly support VLANs when VLAN + * devices are not used. When broken device drivers are no longer in + * widespread use, we will delete these interfaces. */ + +static int +set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto); + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + + if (realdev_ofp_port == ofport->realdev_ofp_port + && vid == ofport->vlandev_vid) { + return 0; + } + + ofproto->need_revalidate = true; + + if (ofport->realdev_ofp_port) { + vsp_remove(ofport); + } + if (realdev_ofp_port && ofport->bundle) { + /* vlandevs are enslaved to their realdevs, so they are not allowed to + * themselves be part of a bundle. */ + bundle_set(ofport->up.ofproto, ofport->bundle, NULL); + } + + ofport->realdev_ofp_port = realdev_ofp_port; + ofport->vlandev_vid = vid; + + if (realdev_ofp_port) { + vsp_add(ofport, realdev_ofp_port, vid); + } + + return 0; +} + +static uint32_t +hash_realdev_vid(uint16_t realdev_ofp_port, int vid) +{ + return hash_2words(realdev_ofp_port, vid); +} + +static uint32_t +vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto, + uint32_t realdev_odp_port, ovs_be16 vlan_tci) +{ + if (!hmap_is_empty(&ofproto->realdev_vid_map)) { + uint16_t realdev_ofp_port = odp_port_to_ofp_port(realdev_odp_port); + int vid = vlan_tci_to_vid(vlan_tci); + const struct vlan_splinter *vsp; + + HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node, + hash_realdev_vid(realdev_ofp_port, vid), + &ofproto->realdev_vid_map) { + if (vsp->realdev_ofp_port == realdev_ofp_port + && vsp->vid == vid) { + return ofp_port_to_odp_port(vsp->vlandev_ofp_port); + } + } + } + return realdev_odp_port; +} + +static struct vlan_splinter * +vlandev_find(const struct ofproto_dpif *ofproto, uint16_t vlandev_ofp_port) +{ + struct vlan_splinter *vsp; + + HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node, hash_int(vlandev_ofp_port, 0), + &ofproto->vlandev_map) { + if (vsp->vlandev_ofp_port == vlandev_ofp_port) { + return vsp; + } + } + + return NULL; +} + +static uint16_t +vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto, + uint16_t vlandev_ofp_port, int *vid) +{ + if (!hmap_is_empty(&ofproto->vlandev_map)) { + const struct vlan_splinter *vsp; + + vsp = vlandev_find(ofproto, vlandev_ofp_port); + if (vsp) { + if (vid) { + *vid = vsp->vid; + } + return vsp->realdev_ofp_port; + } + } + return 0; +} + +static void +vsp_remove(struct ofport_dpif *port) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); + struct vlan_splinter *vsp; + + vsp = vlandev_find(ofproto, port->up.ofp_port); + if (vsp) { + hmap_remove(&ofproto->vlandev_map, &vsp->vlandev_node); + hmap_remove(&ofproto->realdev_vid_map, &vsp->realdev_vid_node); + free(vsp); + + port->realdev_ofp_port = 0; + } else { + VLOG_ERR("missing vlan device record"); + } +} + +static void +vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); + + if (!vsp_vlandev_to_realdev(ofproto, port->up.ofp_port, NULL) + && (vsp_realdev_to_vlandev(ofproto, realdev_ofp_port, htons(vid)) + == realdev_ofp_port)) { + struct vlan_splinter *vsp; + + vsp = xmalloc(sizeof *vsp); + hmap_insert(&ofproto->vlandev_map, &vsp->vlandev_node, + hash_int(port->up.ofp_port, 0)); + hmap_insert(&ofproto->realdev_vid_map, &vsp->realdev_vid_node, + hash_realdev_vid(realdev_ofp_port, vid)); + vsp->realdev_ofp_port = realdev_ofp_port; + vsp->vlandev_ofp_port = port->up.ofp_port; + vsp->vid = vid; + + port->realdev_ofp_port = realdev_ofp_port; + } else { + VLOG_ERR("duplicate vlan device record"); + } } const struct ofproto_class ofproto_dpif_class = { @@ -5616,6 +6063,7 @@ const struct ofproto_class ofproto_dpif_class = { destruct, dealloc, run, + run_fast, wait, flush, get_features, @@ -5629,6 +6077,7 @@ const struct ofproto_class ofproto_dpif_class = { port_query_by_name, port_add, port_del, + port_get_stats, port_dump_start, port_dump_next, port_dump_done, @@ -5659,7 +6108,9 @@ const struct ofproto_class ofproto_dpif_class = { bundle_set, bundle_remove, mirror_set, + mirror_get_stats, set_flood_vlans, is_mirror_output_bundle, forward_bpdu_changed, + set_realdev, };