X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=7df04501f38746317cf54258f349949b90a2b184;hb=34582733d9aad82bba60f4bf986b62d58412502a;hp=90ce71559d10dd56748148aeb6ca3cf939190646;hpb=94639963bf275a41e42277116205da0e9b457580;p=cascardo%2Fovs.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 90ce71559..7df04501f 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -110,6 +110,28 @@ struct rule_dpif { static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes); static struct rule_dpif *rule_dpif_cast(const struct rule *); +struct group_dpif { + struct ofgroup up; + + /* 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 subfacet. + */ + struct ovs_mutex stats_mutex; + uint64_t packet_count OVS_GUARDED; /* Number of packets received. */ + uint64_t byte_count OVS_GUARDED; /* Number of bytes received. */ + struct bucket_counter *bucket_stats OVS_GUARDED; /* Bucket statistics. */ +}; + struct ofbundle { struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */ struct ofproto_dpif *ofproto; /* Owning ofproto. */ @@ -263,7 +285,7 @@ struct facet { /* Accounting. */ uint64_t accounted_bytes; /* Bytes processed by facet_account(). */ struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */ - uint8_t tcp_flags; /* TCP flags seen for this 'rule'. */ + uint16_t tcp_flags; /* TCP flags seen for this 'rule'. */ struct xlate_out xout; @@ -310,6 +332,7 @@ struct ofport_dpif { struct bfd *bfd; /* BFD, if any. */ bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* This port is a tunnel. */ + bool is_layer3; /* This is a layer 3 port. */ long long int carrier_seq; /* Carrier status changes. */ struct ofport_dpif *peer; /* Peer if patch port. */ @@ -515,7 +538,9 @@ ofproto_dpif_cast(const struct ofproto *ofproto) static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port); static void ofproto_trace(struct ofproto_dpif *, const struct flow *, - const struct ofpbuf *packet, struct ds *); + const struct ofpbuf *packet, + const struct ofpact[], size_t ofpacts_len, + struct ds *); /* Upcalls. */ static void handle_upcalls(struct dpif_backer *); @@ -545,11 +570,11 @@ ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto, * Takes ownership of 'pin' and pin->packet. */ void ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto, - struct ofputil_packet_in *pin) + struct ofproto_packet_in *pin) { if (!guarded_list_push_back(&ofproto->pins, &pin->list_node, 1024)) { COVERAGE_INC(packet_in_overflow); - free(CONST_CAST(void *, pin->packet)); + free(CONST_CAST(void *, pin->up.packet)); free(pin); } } @@ -802,8 +827,8 @@ type_run(const char *type) ofport->up.netdev, ofport->cfm, ofport->bfd, ofport->peer, stp_port, ofport->qdscp, ofport->n_qdscp, - ofport->up.pp.config, ofport->is_tunnel, - ofport->may_enable); + ofport->up.pp.config, ofport->up.pp.state, + ofport->is_tunnel, ofport->may_enable); } ovs_rwlock_unlock(&xlate_rwlock); @@ -1211,7 +1236,6 @@ construct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct shash_node *node, *next; - uint32_t max_ports; int error; error = open_dpif_backer(ofproto->up.type, &ofproto->backer); @@ -1219,9 +1243,6 @@ construct(struct ofproto *ofproto_) return error; } - max_ports = dpif_get_max_ports(ofproto->backer->dpif); - ofproto_init_max_ports(ofproto_, MIN(max_ports, ofp_to_u16(OFPP_MAX))); - ofproto->netflow = NULL; ofproto->sflow = NULL; ofproto->ipfix = NULL; @@ -1233,7 +1254,7 @@ construct(struct ofproto *ofproto_) ovs_mutex_init(&ofproto->stats_mutex); ovs_mutex_init(&ofproto->vsp_mutex); - classifier_init(&ofproto->facets); + classifier_init(&ofproto->facets, NULL); ofproto->consistency_rl = LLONG_MIN; guarded_list_init(&ofproto->pins); @@ -1359,7 +1380,7 @@ destruct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct rule_dpif *rule, *next_rule; - struct ofputil_packet_in *pin, *next_pin; + struct ofproto_packet_in *pin, *next_pin; struct facet *facet, *next_facet; struct cls_cursor cursor; struct oftable *table; @@ -1397,7 +1418,7 @@ destruct(struct ofproto *ofproto_) guarded_list_pop_all(&ofproto->pins, &pins); LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) { list_remove(&pin->list_node); - free(CONST_CAST(void *, pin->packet)); + free(CONST_CAST(void *, pin->up.packet)); free(pin); } guarded_list_destroy(&ofproto->pins); @@ -1428,7 +1449,7 @@ static int run_fast(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct ofputil_packet_in *pin, *next_pin; + struct ofproto_packet_in *pin, *next_pin; struct list pins; /* Do not perform any periodic activity required by 'ofproto' while @@ -1441,7 +1462,7 @@ run_fast(struct ofproto *ofproto_) LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) { connmgr_send_packet_in(ofproto->up.connmgr, pin); list_remove(&pin->list_node); - free(CONST_CAST(void *, pin->packet)); + free(CONST_CAST(void *, pin->up.packet)); free(pin); } @@ -1505,14 +1526,14 @@ run(struct ofproto *ofproto_) if (time_msec() >= ofproto->consistency_rl && !classifier_is_empty(&ofproto->facets) && !ofproto->backer->need_revalidate) { - struct cls_table *table; + struct cls_subtable *table; struct cls_rule *cr; struct facet *facet; ofproto->consistency_rl = time_msec() + 250; - table = CONTAINER_OF(hmap_random_node(&ofproto->facets.tables), - struct cls_table, hmap_node); + table = CONTAINER_OF(hmap_random_node(&ofproto->facets.subtables), + struct cls_subtable, hmap_node); cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule, hmap_node); facet = CONTAINER_OF(cr, struct facet, cr); @@ -1688,6 +1709,7 @@ port_construct(struct ofport *port_) port->realdev_ofp_port = 0; port->vlandev_vid = 0; port->carrier_seq = netdev_get_carrier_resets(netdev); + port->is_layer3 = netdev_vport_is_layer3(netdev); if (netdev_vport_is_patch(netdev)) { /* By bailing out here, we don't submit the port to the sFlow module @@ -1931,6 +1953,7 @@ get_cfm_status(const struct ofport *ofport_, if (ofport->cfm) { status->faults = cfm_get_fault(ofport->cfm); + status->flap_count = cfm_get_flap_count(ofport->cfm); status->remote_opstate = cfm_get_opup(ofport->cfm); status->health = cfm_get_health(ofport->cfm); cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps); @@ -2284,6 +2307,7 @@ bundle_update(struct ofbundle *bundle) bundle->floodable = true; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD + || port->is_layer3 || !stp_forward_in_state(port->stp_state)) { bundle->floodable = false; break; @@ -2331,6 +2355,7 @@ bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port, port->bundle = bundle; list_push_back(&bundle->ports, &port->bundle_node); if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD + || port->is_layer3 || !stp_forward_in_state(port->stp_state)) { bundle->floodable = false; } @@ -4304,9 +4329,15 @@ rule_dpif_credit_stats(struct rule_dpif *rule, } bool -rule_dpif_fail_open(const struct rule_dpif *rule) +rule_dpif_is_fail_open(const struct rule_dpif *rule) { - return rule->up.cr.priority == FAIL_OPEN_PRIORITY; + return is_fail_open_rule(&rule->up); +} + +bool +rule_dpif_is_table_miss(const struct rule_dpif *rule) +{ + return rule_is_table_miss(&rule->up); } ovs_be64 @@ -4783,6 +4814,137 @@ rule_modify_actions(struct rule *rule_, bool reset_counters) complete_operation(rule); } + +static struct group_dpif *group_dpif_cast(const struct ofgroup *group) +{ + return group ? CONTAINER_OF(group, struct group_dpif, up) : NULL; +} + +static struct ofgroup * +group_alloc(void) +{ + struct group_dpif *group = xzalloc(sizeof *group); + return &group->up; +} + +static void +group_dealloc(struct ofgroup *group_) +{ + struct group_dpif *group = group_dpif_cast(group_); + free(group); +} + +static void +group_construct_stats(struct group_dpif *group) + OVS_REQUIRES(group->stats_mutex) +{ + group->packet_count = 0; + group->byte_count = 0; + if (!group->bucket_stats) { + group->bucket_stats = xcalloc(group->up.n_buckets, + sizeof *group->bucket_stats); + } else { + memset(group->bucket_stats, 0, group->up.n_buckets * + sizeof *group->bucket_stats); + } +} + +static enum ofperr +group_construct(struct ofgroup *group_) +{ + struct group_dpif *group = group_dpif_cast(group_); + ovs_mutex_init(&group->stats_mutex); + ovs_mutex_lock(&group->stats_mutex); + group_construct_stats(group); + ovs_mutex_unlock(&group->stats_mutex); + return 0; +} + +static void +group_destruct__(struct group_dpif *group) + OVS_REQUIRES(group->stats_mutex) +{ + free(group->bucket_stats); + group->bucket_stats = NULL; +} + +static void +group_destruct(struct ofgroup *group_) +{ + struct group_dpif *group = group_dpif_cast(group_); + ovs_mutex_lock(&group->stats_mutex); + group_destruct__(group); + ovs_mutex_unlock(&group->stats_mutex); + ovs_mutex_destroy(&group->stats_mutex); +} + +static enum ofperr +group_modify(struct ofgroup *group_, struct ofgroup *victim_) +{ + struct group_dpif *group = group_dpif_cast(group_); + struct group_dpif *victim = group_dpif_cast(victim_); + + ovs_mutex_lock(&group->stats_mutex); + if (victim->up.n_buckets < group->up.n_buckets) { + group_destruct__(group); + } + group_construct_stats(group); + ovs_mutex_unlock(&group->stats_mutex); + + return 0; +} + +static enum ofperr +group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs) +{ + struct group_dpif *group = group_dpif_cast(group_); + + /* Start from historical data for 'group' itself that are no longer tracked + * in facets. This counts, for example, facets that have expired. */ + ovs_mutex_lock(&group->stats_mutex); + ogs->packet_count = group->packet_count; + ogs->byte_count = group->byte_count; + memcpy(ogs->bucket_stats, group->bucket_stats, + group->up.n_buckets * sizeof *group->bucket_stats); + ovs_mutex_unlock(&group->stats_mutex); + + return 0; +} + +bool +group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id, + struct group_dpif **group) + OVS_TRY_RDLOCK(true, (*group)->up.rwlock) +{ + struct ofgroup *ofgroup; + bool found; + + *group = NULL; + found = ofproto_group_lookup(&ofproto->up, group_id, &ofgroup); + *group = found ? group_dpif_cast(ofgroup) : NULL; + + return found; +} + +void +group_dpif_release(struct group_dpif *group) + OVS_RELEASES(group->up.rwlock) +{ + ofproto_group_release(&group->up); +} + +void +group_dpif_get_buckets(const struct group_dpif *group, + const struct list **buckets) +{ + *buckets = &group->up.buckets; +} + +enum ofp11_group_type +group_dpif_get_type(const struct group_dpif *group) +{ + return group->up.type; +} /* Sends 'packet' out 'ofport'. * May modify 'packet'. @@ -5079,7 +5241,7 @@ trace_format_regs(struct ds *result, int level, const char *title, ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s:", title); for (i = 0; i < FLOW_N_REGS; i++) { - ds_put_format(result, " reg%zu=0x%"PRIx32, i, trace->flow.regs[i]); + ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]); } ds_put_char(result, '\n'); } @@ -5120,37 +5282,39 @@ trace_report(struct xlate_in *xin, const char *s, int recurse) ds_put_char(result, '\n'); } -static void -ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], - void *aux OVS_UNUSED) +/* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following + * forms are supported: + * + * - [dpname] odp_flow [-generate | packet] + * - bridge br_flow [-generate | packet] + * + * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure + * returns a nonnull error message. */ +static const char * +parse_flow_and_packet(int argc, const char *argv[], + struct ofproto_dpif **ofprotop, struct flow *flow, + struct ofpbuf **packetp) { const struct dpif_backer *backer = NULL; - struct ofproto_dpif *ofproto; - struct ofpbuf odp_key, odp_mask; + const char *error = NULL; + struct simap port_names = SIMAP_INITIALIZER(&port_names); struct ofpbuf *packet; - struct ds result; - struct flow flow; - struct simap port_names; - char *s; + struct ofpbuf odp_key; + struct ofpbuf odp_mask; - packet = NULL; - backer = NULL; - ds_init(&result); ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); - simap_init(&port_names); /* Handle "-generate" or a hex string as the last argument. */ if (!strcmp(argv[argc - 1], "-generate")) { packet = ofpbuf_new(0); argc--; } else { - const char *error = eth_from_hex(argv[argc - 1], &packet); + error = eth_from_hex(argv[argc - 1], &packet); if (!error) { argc--; } else if (argc == 4) { /* The 3-argument form must end in "-generate' or a hex string. */ - unixctl_command_reply_error(conn, error); goto exit; } } @@ -5167,12 +5331,15 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], dp_type = argv[1]; } backer = shash_find_data(&all_dpif_backers, dp_type); - } else { + } else if (argc == 2) { struct shash_node *node; if (shash_count(&all_dpif_backers) == 1) { node = shash_first(&all_dpif_backers); backer = node->data; } + } else { + error = "Syntax error"; + goto exit; } if (backer && backer->dpif) { struct dpif_port dpif_port; @@ -5187,96 +5354,220 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], * bridge is specified. If function odp_flow_key_from_string() * returns 0, the flow is a odp_flow. If function * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */ - if (!odp_flow_from_string(argv[argc - 1], &port_names, &odp_key, &odp_mask)) { + if (!odp_flow_from_string(argv[argc - 1], &port_names, + &odp_key, &odp_mask)) { if (!backer) { - unixctl_command_reply_error(conn, "Cannot find the datapath"); + error = "Cannot find the datapath"; goto exit; } - if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, &flow, - NULL, &ofproto, NULL)) { - unixctl_command_reply_error(conn, "Invalid datapath flow"); + if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, flow, + NULL, ofprotop, NULL)) { + error = "Invalid datapath flow"; goto exit; } - ds_put_format(&result, "Bridge: %s\n", ofproto->up.name); - } else if (!parse_ofp_exact_flow(&flow, NULL, argv[argc - 1], NULL)) { + } else if (!parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL)) { if (argc != 3) { - unixctl_command_reply_error(conn, "Must specify bridge name"); + error = "Must specify bridge name"; goto exit; } - ofproto = ofproto_dpif_lookup(argv[1]); - if (!ofproto) { - unixctl_command_reply_error(conn, "Unknown bridge name"); + *ofprotop = ofproto_dpif_lookup(argv[1]); + if (!*ofprotop) { + error = "Unknown bridge name"; goto exit; } } else { - unixctl_command_reply_error(conn, "Bad flow syntax"); + error = "Bad flow syntax"; goto exit; } /* Generate a packet, if requested. */ if (packet) { if (!packet->size) { - flow_compose(packet, &flow); + flow_compose(packet, flow); } else { - union flow_in_port in_port_; - - in_port_ = flow.in_port; - ds_put_cstr(&result, "Packet: "); - s = ofp_packet_to_string(packet->data, packet->size); - ds_put_cstr(&result, s); - free(s); + union flow_in_port in_port = flow->in_port; /* Use the metadata from the flow and the packet argument * to reconstruct the flow. */ - flow_extract(packet, flow.skb_priority, flow.pkt_mark, NULL, - &in_port_, &flow); + flow_extract(packet, flow->skb_priority, flow->pkt_mark, NULL, + &in_port, flow); } } - ofproto_trace(ofproto, &flow, packet, &result); - unixctl_command_reply(conn, ds_cstr(&result)); + error = NULL; exit: - ds_destroy(&result); - ofpbuf_delete(packet); + if (error) { + ofpbuf_delete(packet); + packet = NULL; + } + *packetp = packet; ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); simap_destroy(&port_names); + return error; +} + +static void +ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], + void *aux OVS_UNUSED) +{ + struct ofproto_dpif *ofproto; + struct ofpbuf *packet; + const char *error; + struct flow flow; + + error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet); + if (!error) { + struct ds result; + + ds_init(&result); + ofproto_trace(ofproto, &flow, packet, NULL, 0, &result); + unixctl_command_reply(conn, ds_cstr(&result)); + ds_destroy(&result); + ofpbuf_delete(packet); + } else { + unixctl_command_reply_error(conn, error); + } +} + +static void +ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + enum ofputil_protocol usable_protocols; + struct ofproto_dpif *ofproto; + bool enforce_consistency; + struct ofpbuf ofpacts; + struct ofpbuf *packet; + struct ds result; + struct flow flow; + uint16_t in_port; + + /* Three kinds of error return values! */ + enum ofperr retval; + const char *error; + char *rw_error; + + packet = NULL; + ds_init(&result); + ofpbuf_init(&ofpacts, 0); + + /* Parse actions. */ + rw_error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols); + if (rw_error) { + unixctl_command_reply_error(conn, rw_error); + free(rw_error); + goto exit; + } + + /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of + * consistency between the flow and the actions. With -consistent, we + * enforce consistency even for a flow supported in OpenFlow 1.0. */ + if (!strcmp(argv[1], "-consistent")) { + enforce_consistency = true; + argv++; + argc--; + } else { + enforce_consistency = false; + } + + error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet); + if (error) { + unixctl_command_reply_error(conn, error); + goto exit; + } + + /* Do the same checks as handle_packet_out() in ofproto.c. + * + * We pass a 'table_id' of 0 to ofproto_check_ofpacts(), which isn't + * strictly correct because these actions aren't in any table, but it's OK + * because it 'table_id' is used only to check goto_table instructions, but + * packet-outs take a list of actions and therefore it can't include + * instructions. + * + * We skip the "meter" check here because meter is an instruction, not an + * action, and thus cannot appear in ofpacts. */ + in_port = ofp_to_u16(flow.in_port.ofp_port); + if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) { + unixctl_command_reply_error(conn, "invalid in_port"); + goto exit; + } + if (enforce_consistency) { + retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow, + u16_to_ofp(ofproto->up.max_ports), + 0, 0, usable_protocols); + } else { + retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow, + u16_to_ofp(ofproto->up.max_ports), 0, 0, + &usable_protocols); + } + + if (retval) { + ds_clear(&result); + ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval)); + unixctl_command_reply_error(conn, ds_cstr(&result)); + goto exit; + } + + ofproto_trace(ofproto, &flow, packet, ofpacts.data, ofpacts.size, &result); + unixctl_command_reply(conn, ds_cstr(&result)); + +exit: + ds_destroy(&result); + ofpbuf_delete(packet); + ofpbuf_uninit(&ofpacts); } +/* Implements a "trace" through 'ofproto''s flow table, appending a textual + * description of the results to 'ds'. + * + * The trace follows a packet with the specified 'flow' through the flow + * table. 'packet' may be nonnull to trace an actual packet, with consequent + * side effects (if it is nonnull then its flow must be 'flow'). + * + * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to + * trace, otherwise the actions are determined by a flow table lookup. */ static void ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, - const struct ofpbuf *packet, struct ds *ds) + const struct ofpbuf *packet, + const struct ofpact ofpacts[], size_t ofpacts_len, + struct ds *ds) { struct rule_dpif *rule; struct flow_wildcards wc; + ds_put_format(ds, "Bridge: %s\n", ofproto->up.name); ds_put_cstr(ds, "Flow: "); flow_format(ds, flow); ds_put_char(ds, '\n'); flow_wildcards_init_catchall(&wc); - rule_dpif_lookup(ofproto, flow, &wc, &rule); + if (ofpacts) { + rule = NULL; + } else { + rule_dpif_lookup(ofproto, flow, &wc, &rule); - trace_format_rule(ds, 0, rule); - if (rule == ofproto->miss_rule) { - ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n"); - } else if (rule == ofproto->no_packet_in_rule) { - ds_put_cstr(ds, "\nNo match, packets dropped because " - "OFPPC_NO_PACKET_IN is set on in_port.\n"); - } else if (rule == ofproto->drop_frags_rule) { - ds_put_cstr(ds, "\nPackets dropped because they are IP fragments " - "and the fragment handling mode is \"drop\".\n"); + trace_format_rule(ds, 0, rule); + if (rule == ofproto->miss_rule) { + ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n"); + } else if (rule == ofproto->no_packet_in_rule) { + ds_put_cstr(ds, "\nNo match, packets dropped because " + "OFPPC_NO_PACKET_IN is set on in_port.\n"); + } else if (rule == ofproto->drop_frags_rule) { + ds_put_cstr(ds, "\nPackets dropped because they are IP fragments " + "and the fragment handling mode is \"drop\".\n"); + } } - if (rule) { + if (rule || ofpacts) { uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions; struct trace_ctx trace; struct match match; - uint8_t tcp_flags; + uint16_t tcp_flags; tcp_flags = packet ? packet_get_tcp_flags(packet, flow) : 0; trace.result = ds; @@ -5284,6 +5575,10 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub); xlate_in_init(&trace.xin, ofproto, flow, rule, tcp_flags, packet); + if (ofpacts) { + trace.xin.ofpacts = ofpacts; + trace.xin.ofpacts_len = ofpacts_len; + } trace.xin.resubmit_hook = trace_resubmit; trace.xin.report_hook = trace_report; @@ -5439,7 +5734,7 @@ dpif_show_backer(const struct dpif_backer *backer, struct ds *ds) ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n", dpif_name(backer->dpif), n_hit, n_missed); - ds_put_format(ds, "\tflows: cur: %zu, avg: %u, max: %u," + ds_put_format(ds, "\tflows: cur: %"PRIuSIZE", avg: %u, max: %u," " life span: %lldms\n", hmap_count(&backer->subfacets), backer->avg_n_subfacet, backer->max_n_subfacet, backer->avg_subfacet_life); @@ -5550,7 +5845,7 @@ ofproto_unixctl_dpif_dump_megaflows(struct unixctl_conn *conn, CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { cls_rule_format(&facet->cr, &ds); ds_put_cstr(&ds, ", "); - ds_put_format(&ds, "n_subfacets:%zu, ", list_size(&facet->subfacets)); + ds_put_format(&ds, "n_subfacets:%"PRIuSIZE", ", list_size(&facet->subfacets)); ds_put_format(&ds, "used:%.3fs, ", (now - facet->used) / 1000.0); ds_put_cstr(&ds, "Datapath actions: "); if (facet->xout.slow) { @@ -5717,8 +6012,12 @@ ofproto_dpif_unixctl_init(void) unixctl_command_register( "ofproto/trace", - "[dp_name]|bridge odp_flow|br_flow [-generate|packet]", + "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]", 1, 3, ofproto_unixctl_trace, NULL); + unixctl_command_register( + "ofproto/trace-packet-out", + "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions", + 2, 6, ofproto_unixctl_trace_actions, NULL); unixctl_command_register("fdb/flush", "[bridge]", 0, 1, ofproto_unixctl_fdb_flush, NULL); unixctl_command_register("fdb/show", "bridge", 1, 1, @@ -6070,10 +6369,10 @@ const struct ofproto_class ofproto_dpif_class = { NULL, /* meter_set */ NULL, /* meter_get */ NULL, /* meter_del */ - NULL, /* group_alloc */ - NULL, /* group_construct */ - NULL, /* group_destruct */ - NULL, /* group_dealloc */ - NULL, /* group_modify */ - NULL, /* group_get_stats */ + group_alloc, /* group_alloc */ + group_construct, /* group_construct */ + group_destruct, /* group_destruct */ + group_dealloc, /* group_dealloc */ + group_modify, /* group_modify */ + group_get_stats, /* group_get_stats */ };