X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=72ec8dd9dc7d147d1cacabf200940d492b0e35cc;hb=8fec720d1371b5a15abdc02e173c746f2b8088d9;hp=97f140eea341cf11e266b2dc8acb40a08fb38f9a;hpb=c7af6a8de73b2c4d2c65c0b66a752ed4c7e51755;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 97f140ee..72ec8dd9 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -25,9 +25,9 @@ #include #include #include "byte-order.h" +#include "cfm.h" #include "classifier.h" #include "coverage.h" -#include "discovery.h" #include "dpif.h" #include "dynamic-string.h" #include "fail-open.h" @@ -54,7 +54,6 @@ #include "poll-loop.h" #include "rconn.h" #include "shash.h" -#include "status.h" #include "stream-ssl.h" #include "svec.h" #include "tag.h" @@ -102,9 +101,12 @@ struct ofport { struct netdev *netdev; struct ofp_phy_port opp; /* In host byte order. */ uint16_t odp_port; + struct cfm *cfm; /* Connectivity Fault Management, if any. */ }; static void ofport_free(struct ofport *); +static void ofport_run(struct ofproto *, struct ofport *); +static void ofport_wait(struct ofport *); static void hton_ofp_phy_port(struct ofp_phy_port *); struct action_xlate_ctx { @@ -333,8 +335,6 @@ struct ofconn { /* type == OFCONN_PRIMARY only. */ enum nx_role role; /* Role. */ struct hmap_node hmap_node; /* In struct ofproto's "controllers" map. */ - struct discovery *discovery; /* Controller discovery object, if enabled. */ - struct status_category *ss; /* Switch status category. */ enum ofproto_band band; /* In-band or out-of-band? */ }; @@ -373,7 +373,6 @@ struct ofproto { uint32_t max_ports; /* Configuration. */ - struct switch_status *switch_status; struct fail_open *fail_open; struct netflow *netflow; struct ofproto_sflow *sflow; @@ -488,7 +487,6 @@ ofproto_create(const char *datapath, const char *datapath_type, p->max_ports = dpif_get_max_ports(dpif); /* Initialize submodules. */ - p->switch_status = switch_status_create(p); p->fail_open = NULL; p->netflow = NULL; p->sflow = NULL; @@ -548,82 +546,47 @@ ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id) } } -static bool -is_discovery_controller(const struct ofproto_controller *c) -{ - return !strcmp(c->target, "discover"); -} - -static bool -is_in_band_controller(const struct ofproto_controller *c) -{ - return is_discovery_controller(c) || c->band == OFPROTO_IN_BAND; -} - /* Creates a new controller in 'ofproto'. Some of the settings are initially * drawn from 'c', but update_controller() needs to be called later to finish * the new ofconn's configuration. */ static void add_controller(struct ofproto *ofproto, const struct ofproto_controller *c) { - struct discovery *discovery; + char *name = ofconn_make_name(ofproto, c->target); struct ofconn *ofconn; - if (is_discovery_controller(c)) { - int error = discovery_create(c->accept_re, c->update_resolv_conf, - ofproto->dpif, ofproto->switch_status, - &discovery); - if (error) { - return; - } - } else { - discovery = NULL; - } - ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_PRIMARY); ofconn->pktbuf = pktbuf_create(); ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; - if (discovery) { - ofconn->discovery = discovery; - } else { - char *name = ofconn_make_name(ofproto, c->target); - rconn_connect(ofconn->rconn, c->target, name); - free(name); - } + rconn_connect(ofconn->rconn, c->target, name); hmap_insert(&ofproto->controllers, &ofconn->hmap_node, hash_string(c->target, 0)); + + free(name); } /* Reconfigures 'ofconn' to match 'c'. This function cannot update an ofconn's - * target or turn discovery on or off (these are done by creating new ofconns - * and deleting old ones), but it can update the rest of an ofconn's - * settings. */ + * target (this is done by creating new ofconns and deleting old ones), but it + * can update the rest of an ofconn's settings. */ static void update_controller(struct ofconn *ofconn, const struct ofproto_controller *c) { int probe_interval; - ofconn->band = (is_in_band_controller(c) - ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND); + ofconn->band = c->band; rconn_set_max_backoff(ofconn->rconn, c->max_backoff); probe_interval = c->probe_interval ? MAX(c->probe_interval, 5) : 0; rconn_set_probe_interval(ofconn->rconn, probe_interval); - if (ofconn->discovery) { - discovery_set_update_resolv_conf(ofconn->discovery, - c->update_resolv_conf); - discovery_set_accept_controller_re(ofconn->discovery, c->accept_re); - } - ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit); } static const char * ofconn_get_target(const struct ofconn *ofconn) { - return ofconn->discovery ? "discover" : rconn_get_target(ofconn->rconn); + return rconn_get_target(ofconn->rconn); } static struct ofconn * @@ -646,7 +609,6 @@ update_in_band_remotes(struct ofproto *ofproto) const struct ofconn *ofconn; struct sockaddr_in *addrs; size_t max_addrs, n_addrs; - bool discovery; size_t i; /* Allocate enough memory for as many remotes as we could possibly have. */ @@ -655,7 +617,6 @@ update_in_band_remotes(struct ofproto *ofproto) n_addrs = 0; /* Add all the remotes. */ - discovery = false; HMAP_FOR_EACH (ofconn, hmap_node, &ofproto->controllers) { struct sockaddr_in *sin = &addrs[n_addrs]; @@ -668,23 +629,15 @@ update_in_band_remotes(struct ofproto *ofproto) sin->sin_port = rconn_get_remote_port(ofconn->rconn); n_addrs++; } - if (ofconn->discovery) { - discovery = true; - } } for (i = 0; i < ofproto->n_extra_remotes; i++) { addrs[n_addrs++] = ofproto->extra_in_band_remotes[i]; } - /* Create or update or destroy in-band. - * - * Ordinarily we only enable in-band if there's at least one remote - * address, but discovery needs the in-band rules for DHCP to be installed - * even before we know any remote addresses. */ - if (n_addrs || discovery) { + /* Create or update or destroy in-band. */ + if (n_addrs) { if (!ofproto->in_band) { - in_band_create(ofproto, ofproto->dpif, ofproto->switch_status, - &ofproto->in_band); + in_band_create(ofproto, ofproto->dpif, &ofproto->in_band); } if (ofproto->in_band) { in_band_set_remotes(ofproto->in_band, addrs, n_addrs); @@ -711,7 +664,7 @@ update_fail_open(struct ofproto *p) size_t n; if (!p->fail_open) { - p->fail_open = fail_open_create(p, p->switch_status); + p->fail_open = fail_open_create(p); } n = 0; @@ -736,7 +689,6 @@ ofproto_set_controllers(struct ofproto *p, struct shash new_controllers; struct ofconn *ofconn, *next_ofconn; struct ofservice *ofservice, *next_ofservice; - bool ss_exists; size_t i; /* Create newly configured controllers and services. @@ -745,7 +697,7 @@ ofproto_set_controllers(struct ofproto *p, for (i = 0; i < n_controllers; i++) { const struct ofproto_controller *c = &controllers[i]; - if (!vconn_verify_name(c->target) || !strcmp(c->target, "discover")) { + if (!vconn_verify_name(c->target)) { if (!find_controller_by_target(p, c->target)) { add_controller(p, c); } @@ -764,7 +716,6 @@ ofproto_set_controllers(struct ofproto *p, /* Delete controllers that are no longer configured. * Update configuration of all now-existing controllers. */ - ss_exists = false; HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &p->controllers) { struct ofproto_controller *c; @@ -773,9 +724,6 @@ ofproto_set_controllers(struct ofproto *p, ofconn_destroy(ofconn); } else { update_controller(ofconn, c); - if (ofconn->ss) { - ss_exists = true; - } } } @@ -797,13 +745,6 @@ ofproto_set_controllers(struct ofproto *p, update_in_band_remotes(p); update_fail_open(p); - - if (!hmap_is_empty(&p->controllers) && !ss_exists) { - ofconn = CONTAINER_OF(hmap_first(&p->controllers), - struct ofconn, hmap_node); - ofconn->ss = switch_status_register(p->switch_status, "remote", - rconn_status_cb, ofconn->rconn); - } } void @@ -1010,7 +951,71 @@ ofproto_set_sflow(struct ofproto *ofproto, ofproto->sflow = NULL; } } + +/* Connectivity Fault Management configuration. */ +/* Clears the CFM configuration from 'port_no' on 'ofproto'. */ +void +ofproto_iface_clear_cfm(struct ofproto *ofproto, uint32_t port_no) +{ + struct ofport *ofport = get_port(ofproto, port_no); + if (ofport && ofport->cfm){ + cfm_destroy(ofport->cfm); + ofport->cfm = NULL; + } +} + +/* Configures connectivity fault management on 'port_no' in 'ofproto'. Takes + * basic configuration from the configuration members in 'cfm', and the set of + * remote maintenance points from the 'n_remote_mps' elements in 'remote_mps'. + * Ignores the statistics members of 'cfm'. + * + * This function has no effect if 'ofproto' does not have a port 'port_no'. */ +void +ofproto_iface_set_cfm(struct ofproto *ofproto, uint32_t port_no, + const struct cfm *cfm, + const uint16_t *remote_mps, size_t n_remote_mps) +{ + struct ofport *ofport; + + ofport = get_port(ofproto, port_no); + if (!ofport) { + VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu32, + dpif_name(ofproto->dpif), port_no); + return; + } + + if (!ofport->cfm) { + ofport->cfm = cfm_create(); + } + + ofport->cfm->mpid = cfm->mpid; + ofport->cfm->interval = cfm->interval; + memcpy(ofport->cfm->eth_src, cfm->eth_src, ETH_ADDR_LEN); + memcpy(ofport->cfm->maid, cfm->maid, CCM_MAID_LEN); + + cfm_update_remote_mps(ofport->cfm, remote_mps, n_remote_mps); + + if (!cfm_configure(ofport->cfm)) { + VLOG_WARN("%s: CFM configuration on port %"PRIu32" (%s) failed", + dpif_name(ofproto->dpif), port_no, + netdev_get_name(ofport->netdev)); + cfm_destroy(ofport->cfm); + ofport->cfm = NULL; + } +} + +/* Returns the connectivity fault management object associated with 'port_no' + * within 'ofproto', or a null pointer if 'ofproto' does not have a port + * 'port_no' or if that port does not have CFM configured. The caller must not + * modify or destroy the returned object. */ +const struct cfm * +ofproto_iface_get_cfm(struct ofproto *ofproto, uint32_t port_no) +{ + struct ofport *ofport = get_port(ofproto, port_no); + return ofport ? ofport->cfm : NULL; +} + uint64_t ofproto_get_datapath_id(const struct ofproto *ofproto) { @@ -1078,7 +1083,6 @@ ofproto_destroy(struct ofproto *p) } shash_destroy(&p->port_by_name); - switch_status_destroy(p->switch_status); netflow_destroy(p->netflow); ofproto_sflow_destroy(p->sflow); @@ -1174,6 +1178,7 @@ ofproto_run1(struct ofproto *p) { struct ofconn *ofconn, *next_ofconn; struct ofservice *ofservice; + struct ofport *ofport; char *devname; int error; int i; @@ -1210,6 +1215,10 @@ ofproto_run1(struct ofproto *p) process_port_change(p, error, devname); } + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { + ofport_run(p, ofport); + } + if (p->in_band) { if (time_msec() >= p->next_in_band_update) { update_in_band_remotes(p); @@ -1310,11 +1319,15 @@ ofproto_wait(struct ofproto *p) { struct ofservice *ofservice; struct ofconn *ofconn; + struct ofport *ofport; size_t i; dpif_recv_wait(p->dpif); dpif_port_poll_wait(p->dpif); netdev_monitor_poll_wait(p->netdev_monitor); + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { + ofport_wait(ofport); + } LIST_FOR_EACH (ofconn, node, &p->all_conns) { ofconn_wait(ofconn); } @@ -1468,26 +1481,34 @@ ofproto_port_is_floodable(struct ofproto *ofproto, uint16_t odp_port) return ofport && !(ofport->opp.config & OFPPC_NO_FLOOD); } +/* Sends 'packet' out of port 'port_no' within 'p'. If 'vlan_tci' is zero the + * packet will not have any 802.1Q hader; if it is nonzero, then the packet + * will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'. + * + * Returns 0 if successful, otherwise a positive errno value. */ int -ofproto_send_packet(struct ofproto *p, const struct flow *flow, - const union ofp_action *actions, size_t n_actions, +ofproto_send_packet(struct ofproto *ofproto, + uint32_t port_no, uint16_t vlan_tci, const struct ofpbuf *packet) { - struct action_xlate_ctx ctx; - struct ofpbuf *odp_actions; - - action_xlate_ctx_init(&ctx, p, flow, packet); - /* Always xlate packets originated in this function. */ - ctx.check_special = false; - odp_actions = xlate_actions(&ctx, actions, n_actions); - - /* XXX Should we translate the dpif_execute() errno value into an OpenFlow - * error code? */ - dpif_execute(p->dpif, odp_actions->data, odp_actions->size, packet); + struct ofpbuf odp_actions; + int error; - ofpbuf_delete(odp_actions); + ofpbuf_init(&odp_actions, 32); + if (vlan_tci != 0) { + nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_SET_DL_TCI, + ntohs(vlan_tci & ~VLAN_CFI)); + } + nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, port_no); + error = dpif_execute(ofproto->dpif, odp_actions.data, odp_actions.size, + packet); + ofpbuf_uninit(&odp_actions); - return 0; + if (error) { + VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)", + dpif_name(ofproto->dpif), port_no, strerror(error)); + } + return error; } /* Adds a flow to the OpenFlow flow table in 'p' that matches 'cls_rule' and @@ -1704,10 +1725,31 @@ ofport_remove(struct ofproto *p, struct ofport *ofport) } } +static void +ofport_run(struct ofproto *ofproto, struct ofport *ofport) +{ + if (ofport->cfm) { + struct ofpbuf *packet = cfm_run(ofport->cfm); + if (packet) { + ofproto_send_packet(ofproto, ofport->odp_port, 0, packet); + ofpbuf_delete(packet); + } + } +} + +static void +ofport_wait(struct ofport *ofport) +{ + if (ofport->cfm) { + cfm_wait(ofport->cfm); + } +} + static void ofport_free(struct ofport *ofport) { if (ofport) { + cfm_destroy(ofport->cfm); netdev_close(ofport->netdev); free(ofport); } @@ -1840,10 +1882,8 @@ ofconn_destroy(struct ofconn *ofconn) if (ofconn->type == OFCONN_PRIMARY) { hmap_remove(&ofconn->ofproto->controllers, &ofconn->hmap_node); } - discovery_destroy(ofconn->discovery); list_remove(&ofconn->node); - switch_status_unregister(ofconn->ss); rconn_destroy(ofconn->rconn); rconn_packet_counter_destroy(ofconn->packet_in_counter); rconn_packet_counter_destroy(ofconn->reply_counter); @@ -1858,23 +1898,6 @@ ofconn_run(struct ofconn *ofconn) int iteration; size_t i; - if (ofconn->discovery) { - char *controller_name; - if (rconn_is_connectivity_questionable(ofconn->rconn)) { - discovery_question_connectivity(ofconn->discovery); - } - if (discovery_run(ofconn->discovery, &controller_name)) { - if (controller_name) { - char *ofconn_name = ofconn_make_name(p, controller_name); - rconn_connect(ofconn->rconn, controller_name, ofconn_name); - free(ofconn_name); - free(controller_name); - } else { - rconn_disconnect(ofconn->rconn); - } - } - } - for (i = 0; i < N_SCHEDULERS; i++) { pinsched_run(ofconn->schedulers[i], do_send_packet_in, ofconn); } @@ -1897,7 +1920,7 @@ ofconn_run(struct ofconn *ofconn) } } - if (!ofconn->discovery && !rconn_is_alive(ofconn->rconn)) { + if (!rconn_is_alive(ofconn->rconn)) { ofconn_destroy(ofconn); } } @@ -1907,9 +1930,6 @@ ofconn_wait(struct ofconn *ofconn) { int i; - if (ofconn->discovery) { - discovery_wait(ofconn->discovery); - } for (i = 0; i < N_SCHEDULERS; i++) { pinsched_wait(ofconn->schedulers[i]); } @@ -1958,8 +1978,7 @@ ofconn_set_rate_limit(struct ofconn *ofconn, int rate, int burst) if (rate > 0) { if (!*s) { - *s = pinsched_create(rate, burst, - ofconn->ofproto->switch_status); + *s = pinsched_create(rate, burst); } else { pinsched_set_limits(*s, rate, burst); } @@ -3160,6 +3179,18 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx, ctx->check_special = true; } +static void +ofproto_process_cfm(struct ofproto *ofproto, const struct flow *flow, + const struct ofpbuf *packet) +{ + struct ofport *ofport; + + ofport = get_port(ofproto, flow->in_port); + if (ofport && ofport->cfm) { + cfm_process_heartbeat(ofport->cfm, packet); + } +} + static struct ofpbuf * xlate_actions(struct action_xlate_ctx *ctx, const union ofp_action *in, size_t n_in) @@ -3173,13 +3204,18 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->recurse = 0; ctx->last_pop_priority = -1; - if (!ctx->check_special - || !ctx->ofproto->ofhooks->special_cb - || ctx->ofproto->ofhooks->special_cb(&ctx->flow, ctx->packet, - ctx->ofproto->aux)) { - do_xlate_actions(in, n_in, ctx); - } else { + if (ctx->check_special && cfm_should_process_flow(&ctx->flow)) { + if (ctx->packet) { + ofproto_process_cfm(ctx->ofproto, &ctx->flow, ctx->packet); + } + ctx->may_set_up_flow = false; + } else if (ctx->check_special + && ctx->ofproto->ofhooks->special_cb + && !ctx->ofproto->ofhooks->special_cb(&ctx->flow, ctx->packet, + ctx->ofproto->aux)) { ctx->may_set_up_flow = false; + } else { + do_xlate_actions(in, n_in, ctx); } remove_pop_action(ctx); @@ -3688,7 +3724,7 @@ flow_stats_ds(struct rule *rule, struct ds *results) } /* Adds a pretty-printed description of all flows to 'results', including - * those marked hidden by secchan (e.g., by in-band control). */ + * hidden flows (e.g., set up by in-band control). */ void ofproto_get_all_flows(struct ofproto *p, struct ds *results) { @@ -4359,10 +4395,6 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) return 0; /* Nicira extension requests. */ - case OFPUTIL_NXT_STATUS_REQUEST: - return switch_status_handle_request( - ofconn->ofproto->switch_status, ofconn->rconn, oh); - case OFPUTIL_NXT_TUN_ID_FROM_COOKIE: return handle_tun_id_from_cookie(ofconn, oh); @@ -4418,7 +4450,6 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPUTIL_OFPST_PORT_REPLY: case OFPUTIL_OFPST_TABLE_REPLY: case OFPUTIL_OFPST_AGGREGATE_REPLY: - case OFPUTIL_NXT_STATUS_REPLY: case OFPUTIL_NXT_ROLE_REPLY: case OFPUTIL_NXT_FLOW_REMOVED: case OFPUTIL_NXST_FLOW_REPLY: @@ -4459,8 +4490,12 @@ handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall) /* Set header pointers in 'flow'. */ flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow); - if (p->ofhooks->special_cb - && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) { + if (cfm_should_process_flow(&flow)) { + ofproto_process_cfm(p, &flow, upcall->packet); + ofpbuf_delete(upcall->packet); + return; + } else if (p->ofhooks->special_cb + && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) { ofpbuf_delete(upcall->packet); return; } @@ -4468,13 +4503,7 @@ handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall) /* Check with in-band control to see if this packet should be sent * to the local port regardless of the flow table. */ if (in_band_msg_in_hook(p->in_band, &flow, upcall->packet)) { - struct ofpbuf odp_actions; - - ofpbuf_init(&odp_actions, 32); - nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, ODPP_LOCAL); - dpif_execute(p->dpif, odp_actions.data, odp_actions.size, - upcall->packet); - ofpbuf_uninit(&odp_actions); + ofproto_send_packet(p, ODPP_LOCAL, 0, upcall->packet); } facet = facet_lookup_valid(p, &flow); @@ -5007,7 +5036,8 @@ schedule_packet_in(struct ofconn *ofconn, struct dpif_upcall *upcall, * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to * their individual configurations. * - * Takes ownership of 'packet'. */ + * If 'clone' is true, the caller retains ownership of 'upcall->packet'. + * Otherwise, ownership is transferred to this function. */ static void send_packet_in(struct ofproto *ofproto, struct dpif_upcall *upcall, const struct flow *flow, bool clone) @@ -5161,7 +5191,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, goto exit; } - tun_id = htonll(strtoull(tun_id_s, NULL, 10)); + tun_id = htonll(strtoull(tun_id_s, NULL, 0)); in_port = ofp_port_to_odp_port(atoi(in_port_s)); packet_s = ofpbuf_put_hex(&packet, packet_s, NULL); @@ -5232,7 +5262,7 @@ default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet, uint16_t *nf_output_iface, void *ofproto_) { struct ofproto *ofproto = ofproto_; - int out_port; + struct mac_entry *dst_mac; /* Drop frames for reserved multicast addresses. */ if (eth_addr_is_reserved(flow->dl_dst)) { @@ -5240,31 +5270,37 @@ default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet, } /* Learn source MAC (but don't try to learn from revalidation). */ - if (packet != NULL) { - tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src, - 0, flow->in_port, - GRAT_ARP_LOCK_NONE); - if (rev_tag) { + if (packet != NULL + && mac_learning_may_learn(ofproto->ml, flow->dl_src, 0)) { + struct mac_entry *src_mac; + + src_mac = mac_learning_insert(ofproto->ml, flow->dl_src, 0); + if (mac_entry_is_new(src_mac) || src_mac->port.i != flow->in_port) { /* The log messages here could actually be useful in debugging, * so keep the rate limit relatively high. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16, ETH_ADDR_ARGS(flow->dl_src), flow->in_port); - ofproto_revalidate(ofproto, rev_tag); + + ofproto_revalidate(ofproto, + mac_learning_changed(ofproto->ml, src_mac)); + src_mac->port.i = flow->in_port; } } /* Determine output port. */ - out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags, - NULL); - if (out_port < 0) { + dst_mac = mac_learning_lookup(ofproto->ml, flow->dl_dst, 0, tags); + if (!dst_mac) { flood_packets(ofproto, flow->in_port, OFPPC_NO_FLOOD, nf_output_iface, odp_actions); - } else if (out_port != flow->in_port) { - nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, out_port); - *nf_output_iface = out_port; } else { - /* Drop. */ + int out_port = dst_mac->port.i; + if (out_port != flow->in_port) { + nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, out_port); + *nf_output_iface = out_port; + } else { + /* Drop. */ + } } return true;