X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=switch%2Fdatapath.c;h=24744aa8bdfe382babcce781c317dada69b1f0a0;hb=553dfa572d77bff616b1ea88d90eef266d202954;hp=26fb06c60623e53dd5c005bfbfd51840ee58c205;hpb=f1c6daebf32ffabeb2d2e7c35f2cb00057dff47b;p=sliver-openvswitch.git diff --git a/switch/datapath.c b/switch/datapath.c index 26fb06c60..24744aa8b 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -46,6 +46,7 @@ #include "packets.h" #include "poll-loop.h" #include "rconn.h" +#include "stp.h" #include "vconn.h" #include "table.h" #include "timeval.h" @@ -54,18 +55,10 @@ #define THIS_MODULE VLM_datapath #include "vlog.h" -enum br_port_flags { - BRPF_NO_FLOOD = 1 << 0, -}; - -enum br_port_status { - BRPS_PORT_DOWN = 1 << 0, - BRPS_LINK_DOWN = 1 << 1, -}; - extern char mfr_desc; extern char hw_desc; extern char sw_desc; +extern char serial_num; /* Capabilities supported by this implementation. */ #define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \ @@ -83,9 +76,12 @@ extern char sw_desc; | (1 << OFPAT_SET_TP_SRC) \ | (1 << OFPAT_SET_TP_DST) ) +#define PORT_STATUS_BITS (OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN) +#define PORT_FLAG_BITS (~PORT_STATUS_BITS) + struct sw_port { - uint32_t flags; /* BRPF_* flags */ - uint32_t status; /* BRPS_* flags */ + uint32_t flags; /* Some subset of PORT_FLAG_BITS. */ + uint32_t status; /* Some subset of PORT_STATUS_BITS. */ struct datapath *dp; struct netdev *netdev; struct list node; /* Element in datapath.ports. */ @@ -121,7 +117,7 @@ struct datapath { /* Remote connections. */ struct remote *controller; /* Connection to controller. */ struct list remotes; /* All connections (including controller). */ - struct vconn *listen_vconn; + struct pvconn *listen_pvconn; time_t last_timeout; @@ -139,13 +135,15 @@ struct datapath { struct list port_list; /* List of ports, for flooding. */ }; +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); + static struct remote *remote_create(struct datapath *, struct rconn *); static void remote_run(struct datapath *, struct remote *); static void remote_wait(struct remote *); static void remote_destroy(struct remote *); void dp_output_port(struct datapath *, struct buffer *, - int in_port, int out_port); + int in_port, int out_port, bool ignore_no_fwd); void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm); void dp_output_control(struct datapath *, struct buffer *, int in_port, size_t max_len, int reason); @@ -156,7 +154,8 @@ static void send_port_status(struct sw_port *p, uint8_t status); static void del_switch_port(struct sw_port *p); static void execute_actions(struct datapath *, struct buffer *, int in_port, const struct sw_flow_key *, - const struct ofp_action *, int n_actions); + const struct ofp_action *, int n_actions, + bool ignore_no_fwd); static void modify_vlan(struct buffer *buffer, const struct sw_flow_key *key, const struct ofp_action *a); static void modify_nh(struct buffer *buffer, uint16_t eth_proto, @@ -175,8 +174,9 @@ static void modify_th(struct buffer *buffer, uint16_t eth_proto, #define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS) -int run_flow_through_tables(struct datapath *, struct buffer *, int in_port); -void fwd_port_input(struct datapath *, struct buffer *, int in_port); +int run_flow_through_tables(struct datapath *, struct buffer *, + struct sw_port *); +void fwd_port_input(struct datapath *, struct buffer *, struct sw_port *); int fwd_control_input(struct datapath *, const struct sender *, const void *, size_t); @@ -212,7 +212,7 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct rconn *rconn) dp->last_timeout = time_now(); list_init(&dp->remotes); dp->controller = remote_create(dp, rconn); - dp->listen_vconn = NULL; + dp->listen_pvconn = NULL; dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id(); dp->chain = chain_create(); if (!dp->chain) { @@ -277,10 +277,10 @@ dp_add_port(struct datapath *dp, const char *name) } void -dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn) +dp_add_listen_pvconn(struct datapath *dp, struct pvconn *listen_pvconn) { - assert(!dp->listen_vconn); - dp->listen_vconn = listen_vconn; + assert(!dp->listen_pvconn); + dp->listen_pvconn = listen_pvconn; } void @@ -328,11 +328,11 @@ dp_run(struct datapath *dp) if (!error) { p->rx_packets++; p->rx_bytes += buffer->size; - fwd_port_input(dp, buffer, port_no(dp, p)); + fwd_port_input(dp, buffer, p); buffer = NULL; } else if (error != EAGAIN) { - VLOG_ERR("error receiving data from %s: %s", - netdev_get_name(p->netdev), strerror(error)); + VLOG_ERR_RL(&rl, "error receiving data from %s: %s", + netdev_get_name(p->netdev), strerror(error)); } } buffer_delete(buffer); @@ -341,15 +341,15 @@ dp_run(struct datapath *dp) LIST_FOR_EACH_SAFE (r, rn, struct remote, node, &dp->remotes) { remote_run(dp, r); } - if (dp->listen_vconn) { + if (dp->listen_pvconn) { for (;;) { struct vconn *new_vconn; int retval; - retval = vconn_accept(dp->listen_vconn, &new_vconn); + retval = pvconn_accept(dp->listen_pvconn, &new_vconn); if (retval) { if (retval != EAGAIN) { - VLOG_WARN("accept failed (%s)", strerror(retval)); + VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval)); } break; } @@ -385,7 +385,7 @@ remote_run(struct datapath *dp, struct remote *r) sender.xid = oh->xid; fwd_control_input(dp, &sender, buffer->data, buffer->size); } else { - VLOG_WARN("received too-short OpenFlow message"); + VLOG_WARN_RL(&rl, "received too-short OpenFlow message"); } buffer_delete(buffer); } else { @@ -393,7 +393,8 @@ remote_run(struct datapath *dp, struct remote *r) int error = r->cb_dump(dp, r->cb_aux); if (error <= 0) { if (error) { - VLOG_WARN("dump callback error: %s", strerror(-error)); + VLOG_WARN_RL(&rl, "dump callback error: %s", + strerror(-error)); } r->cb_done(r->cb_aux); r->cb_dump = NULL; @@ -436,6 +437,7 @@ remote_create(struct datapath *dp, struct rconn *rconn) list_push_back(&dp->remotes, &remote->node); remote->rconn = rconn; remote->cb_dump = NULL; + remote->n_txq = 0; return remote; } @@ -477,8 +479,8 @@ dp_wait(struct datapath *dp) LIST_FOR_EACH (r, struct remote, node, &dp->remotes) { remote_wait(r); } - if (dp->listen_vconn) { - vconn_accept_wait(dp->listen_vconn); + if (dp->listen_pvconn) { + pvconn_wait(dp->listen_pvconn); } } @@ -522,16 +524,17 @@ output_all(struct datapath *dp, struct buffer *buffer, int in_port, int flood) if (port_no(dp, p) == in_port) { continue; } - if (flood && p->flags & BRPF_NO_FLOOD) { + if (flood && p->flags & OFPPFL_NO_FLOOD) { continue; } if (prev_port != -1) { - dp_output_port(dp, buffer_clone(buffer), in_port, prev_port); + dp_output_port(dp, buffer_clone(buffer), in_port, prev_port, + false); } prev_port = port_no(dp, p); } if (prev_port != -1) - dp_output_port(dp, buffer, in_port, prev_port); + dp_output_port(dp, buffer, in_port, prev_port, false); else buffer_delete(buffer); @@ -543,7 +546,7 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port) { if (out_port >= 0 && out_port < OFPP_MAX) { struct sw_port *p = &dp->ports[out_port]; - if (p->netdev != NULL && !(p->status & BRPS_PORT_DOWN)) { + if (p->netdev != NULL && !(p->status & OFPPFL_PORT_DOWN)) { if (!netdev_send(p->netdev, buffer)) { p->tx_packets++; p->tx_bytes += buffer->size; @@ -555,15 +558,14 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port) } buffer_delete(buffer); - /* FIXME: ratelimit */ - VLOG_DBG("can't forward to bad port %d\n", out_port); + VLOG_DBG_RL(&rl, "can't forward to bad port %d\n", out_port); } /* Takes ownership of 'buffer' and transmits it to 'out_port' on 'dp'. */ void dp_output_port(struct datapath *dp, struct buffer *buffer, - int in_port, int out_port) + int in_port, int out_port, bool ignore_no_fwd) { assert(buffer); @@ -576,13 +578,13 @@ dp_output_port(struct datapath *dp, struct buffer *buffer, } else if (out_port == OFPP_IN_PORT) { output_packet(dp, buffer, in_port); } else if (out_port == OFPP_TABLE) { - if (run_flow_through_tables(dp, buffer, in_port)) { + struct sw_port *p = in_port < OFPP_MAX ? &dp->ports[in_port] : 0; + if (run_flow_through_tables(dp, buffer, p)) { buffer_delete(buffer); } } else { if (in_port == out_port) { - /* FIXME: ratelimit */ - VLOG_DBG("can't directly forward to input port"); + VLOG_DBG_RL(&rl, "can't directly forward to input port"); return; } output_packet(dp, buffer, out_port); @@ -606,13 +608,10 @@ send_openflow_buffer(struct datapath *dp, struct buffer *buffer, int retval; update_openflow_length(buffer); - retval = (remote->n_txq < TXQ_LIMIT - ? rconn_send(rconn, buffer, &remote->n_txq) - : EAGAIN); + retval = rconn_send_with_limit(rconn, buffer, &remote->n_txq, TXQ_LIMIT); if (retval) { - VLOG_WARN("send to %s failed: %s", - rconn_get_name(rconn), strerror(retval)); - buffer_delete(buffer); + VLOG_WARN_RL(&rl, "send to %s failed: %s", + rconn_get_name(rconn), strerror(retval)); } return retval; } @@ -661,14 +660,7 @@ static void fill_port_desc(struct datapath *dp, struct sw_port *p, desc->flags = 0; desc->features = htonl(netdev_get_features(p->netdev)); desc->speed = htonl(netdev_get_speed(p->netdev)); - - if (p->flags & BRPF_NO_FLOOD) { - desc->flags |= htonl(OFPPFL_NO_FLOOD); - } else if (p->status & BRPS_PORT_DOWN) { - desc->flags |= htonl(OFPPFL_PORT_DOWN); - } else if (p->status & BRPS_LINK_DOWN) { - desc->flags |= htonl(OFPPFL_LINK_DOWN); - } + desc->flags = htonl(p->flags | p->status); } static void @@ -680,14 +672,11 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender) ofr = make_openflow_reply(sizeof *ofr, OFPT_FEATURES_REPLY, sender, &buffer); - ofr->datapath_id = htonll(dp->id); - ofr->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS); - ofr->n_compression = 0; /* Not supported */ - ofr->n_general = htonl(TABLE_LINEAR_MAX_FLOWS); - ofr->buffer_mb = htonl(UINT32_MAX); - ofr->n_buffers = htonl(N_PKT_BUFFERS); - ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); - ofr->actions = htonl(OFP_SUPPORTED_ACTIONS); + ofr->datapath_id = htonll(dp->id); + ofr->n_tables = dp->chain->n_tables; + ofr->n_buffers = htonl(N_PKT_BUFFERS); + ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); + ofr->actions = htonl(OFP_SUPPORTED_ACTIONS); LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { struct ofp_phy_port *opp = buffer_put_uninit(buffer, sizeof *opp); memset(opp, 0, sizeof *opp); @@ -703,6 +692,7 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm) int port_no = ntohs(opp->port_no); if (port_no < OFPP_MAX) { struct sw_port *p = &dp->ports[port_no]; + uint32_t flag_mask; /* Make sure the port id hasn't changed since this was sent */ if (!p || memcmp(opp->hw_addr, netdev_get_etheraddr(p->netdev), @@ -711,21 +701,20 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm) } - if (opm->mask & htonl(OFPPFL_NO_FLOOD)) { - if (opp->flags & htonl(OFPPFL_NO_FLOOD)) - p->flags |= BRPF_NO_FLOOD; - else - p->flags &= ~BRPF_NO_FLOOD; + flag_mask = ntohl(opm->mask) & PORT_FLAG_BITS; + if (flag_mask) { + p->flags &= ~flag_mask; + p->flags |= ntohl(opp->flags) & flag_mask; } if (opm->mask & htonl(OFPPFL_PORT_DOWN)) { if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) - && (p->status & BRPS_PORT_DOWN) == 0) { - p->status |= BRPS_PORT_DOWN; + && (p->status & OFPPFL_PORT_DOWN) == 0) { + p->status |= OFPPFL_PORT_DOWN; netdev_turn_flags_off(p->netdev, NETDEV_UP, true); } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0 - && (p->status & BRPS_PORT_DOWN)) { - p->status &= ~BRPS_PORT_DOWN; + && (p->status & OFPPFL_PORT_DOWN)) { + p->status &= ~OFPPFL_PORT_DOWN; netdev_turn_flags_on(p->netdev, NETDEV_UP, true); } } @@ -746,14 +735,14 @@ update_port_status(struct sw_port *p) uint32_t orig_status = p->status; if (netdev_get_flags(p->netdev, &flags) < 0) { - VLOG_WARN("could not get netdev flags for %s", - netdev_get_name(p->netdev)); + VLOG_WARN_RL(&rl, "could not get netdev flags for %s", + netdev_get_name(p->netdev)); return 0; } else { if (flags & NETDEV_UP) { - p->status &= ~BRPS_PORT_DOWN; + p->status &= ~OFPPFL_PORT_DOWN; } else { - p->status |= BRPS_PORT_DOWN; + p->status |= OFPPFL_PORT_DOWN; } } @@ -761,9 +750,9 @@ update_port_status(struct sw_port *p) * error. */ retval = netdev_get_link_status(p->netdev); if (retval == 1) { - p->status &= ~BRPS_LINK_DOWN; + p->status &= ~OFPPFL_LINK_DOWN; } else if (retval == 0) { - p->status |= BRPS_LINK_DOWN; + p->status |= OFPPFL_LINK_DOWN; } return (orig_status != p->status); @@ -804,7 +793,7 @@ send_flow_expired(struct datapath *dp, struct sw_flow *flow, void dp_send_error_msg(struct datapath *dp, const struct sender *sender, - uint16_t type, uint16_t code, const uint8_t *data, size_t len) + uint16_t type, uint16_t code, const void *data, size_t len) { struct buffer *buffer; struct ofp_error_msg *oem; @@ -850,52 +839,59 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow, } -/* 'buffer' was received on 'in_port', a physical switch port between 0 and - * OFPP_MAX. Process it according to 'dp''s flow table. Returns 0 if +/* 'buffer' was received on 'p', which may be a a physical switch port or a + * null pointer. Process it according to 'dp''s flow table. Returns 0 if * successful, in which case 'buffer' is destroyed, or -ESRCH if there is no * matching flow, in which case 'buffer' still belongs to the caller. */ int run_flow_through_tables(struct datapath *dp, struct buffer *buffer, - int in_port) + struct sw_port *p) { struct sw_flow_key key; struct sw_flow *flow; key.wildcards = 0; - if (flow_extract(buffer, in_port, &key.flow) + if (flow_extract(buffer, p ? port_no(dp, p) : OFPP_NONE, &key.flow) && (dp->flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) { /* Drop fragment. */ buffer_delete(buffer); return 0; } + if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP) + && p->flags & (!eth_addr_equals(key.flow.dl_dst, stp_eth_addr) + ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) { + buffer_delete(buffer); + return 0; + } flow = chain_lookup(dp->chain, &key); if (flow != NULL) { flow_used(flow, buffer); - execute_actions(dp, buffer, in_port, &key, - flow->actions, flow->n_actions); + execute_actions(dp, buffer, port_no(dp, p), + &key, flow->actions, flow->n_actions, false); return 0; } else { return -ESRCH; } } -/* 'buffer' was received on 'in_port', a physical switch port between 0 and - * OFPP_MAX. Process it according to 'dp''s flow table, sending it up to the - * controller if no flow matches. Takes ownership of 'buffer'. */ -void fwd_port_input(struct datapath *dp, struct buffer *buffer, int in_port) +/* 'buffer' was received on 'p', which may be a a physical switch port or a + * null pointer. Process it according to 'dp''s flow table, sending it up to + * the controller if no flow matches. Takes ownership of 'buffer'. */ +void fwd_port_input(struct datapath *dp, struct buffer *buffer, + struct sw_port *p) { - if (run_flow_through_tables(dp, buffer, in_port)) { - dp_output_control(dp, buffer, in_port, dp->miss_send_len, - OFPR_NO_MATCH); + if (run_flow_through_tables(dp, buffer, p)) { + dp_output_control(dp, buffer, port_no(dp, p), + dp->miss_send_len, OFPR_NO_MATCH); } } static void do_output(struct datapath *dp, struct buffer *buffer, int in_port, - size_t max_len, int out_port) + size_t max_len, int out_port, bool ignore_no_fwd) { if (out_port != OFPP_CONTROLLER) { - dp_output_port(dp, buffer, in_port, out_port); + dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd); } else { dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION); } @@ -904,7 +900,8 @@ do_output(struct datapath *dp, struct buffer *buffer, int in_port, static void execute_actions(struct datapath *dp, struct buffer *buffer, int in_port, const struct sw_flow_key *key, - const struct ofp_action *actions, int n_actions) + const struct ofp_action *actions, int n_actions, + bool ignore_no_fwd) { /* Every output action needs a separate clone of 'buffer', but the common * case is just a single output action, so that doing a clone and then @@ -923,7 +920,8 @@ execute_actions(struct datapath *dp, struct buffer *buffer, struct eth_header *eh = buffer->l2; if (prev_port != -1) { - do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port); + do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port, + ignore_no_fwd); prev_port = -1; } @@ -960,7 +958,7 @@ execute_actions(struct datapath *dp, struct buffer *buffer, } } if (prev_port != -1) - do_output(dp, buffer, in_port, max_len, prev_port); + do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd); else buffer_delete(buffer); } @@ -1110,7 +1108,7 @@ recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED, int act_len = n_actions * sizeof opo->actions[0]; if (act_len > (ntohs(opo->header.length) - sizeof *opo)) { - VLOG_DBG("message too short for number of actions"); + VLOG_DBG_RL(&rl, "message too short for number of actions"); return -EINVAL; } @@ -1128,7 +1126,7 @@ recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED, flow_extract(buffer, ntohs(opo->in_port), &key.flow); execute_actions(dp, buffer, ntohs(opo->in_port), - &key, opo->actions, n_actions); + &key, opo->actions, n_actions, true); return 0; } @@ -1199,7 +1197,8 @@ add_flow(struct datapath *dp, const struct ofp_flow_mod *ofm) uint16_t in_port = ntohs(ofm->match.in_port); flow_used(flow, buffer); flow_extract(buffer, in_port, &key.flow); - execute_actions(dp, buffer, in_port, &key, ofm->actions, n_actions); + execute_actions(dp, buffer, in_port, &key, + ofm->actions, n_actions, false); } else { error = -ESRCH; } @@ -1238,14 +1237,15 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED, } } -static int version_stats_dump(struct datapath *dp, void *state, +static int desc_stats_dump(struct datapath *dp, void *state, struct buffer *buffer) { - struct ofp_version_stats *ovs = buffer_put_uninit(buffer, sizeof *ovs); + struct ofp_desc_stats *ods = buffer_put_uninit(buffer, sizeof *ods); - strncpy(ovs->mfr_desc, &mfr_desc, sizeof ovs->mfr_desc); - strncpy(ovs->hw_desc, &hw_desc, sizeof ovs->hw_desc); - strncpy(ovs->sw_desc, &sw_desc, sizeof ovs->sw_desc); + strncpy(ods->mfr_desc, &mfr_desc, sizeof ods->mfr_desc); + strncpy(ods->hw_desc, &hw_desc, sizeof ods->hw_desc); + strncpy(ods->sw_desc, &sw_desc, sizeof ods->sw_desc); + strncpy(ods->serial_num, &serial_num, sizeof ods->serial_num); return 0; } @@ -1385,6 +1385,7 @@ static int table_stats_dump(struct datapath *dp, void *state, dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); strncpy(ots->name, stats.name, sizeof ots->name); ots->table_id = i; + ots->wildcards = htonl(stats.wildcards); memset(ots->pad, 0, sizeof ots->pad); ots->max_entries = htonl(stats.max_flows); ots->active_count = htonl(stats.n_flows); @@ -1469,11 +1470,11 @@ struct stats_type { }; static const struct stats_type stats[] = { - [OFPST_VERSION] = { + [OFPST_DESC] = { 0, 0, NULL, - version_stats_dump, + desc_stats_dump, NULL }, [OFPST_FLOW] = { @@ -1574,7 +1575,9 @@ recv_stats_request(struct datapath *dp, const struct sender *sender, type = ntohs(rq->type); if (type >= ARRAY_SIZE(stats) || !stats[type].dump) { - VLOG_WARN("received stats request of unknown type %d", type); + dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_STAT, + rq, rq_len); + VLOG_WARN_RL(&rl, "received stats request of unknown type %d", type); return -EINVAL; } @@ -1587,8 +1590,8 @@ recv_stats_request(struct datapath *dp, const struct sender *sender, body_len = rq_len - offsetof(struct ofp_stats_request, body); if (body_len < cb->s->min_body || body_len > cb->s->max_body) { - VLOG_WARN("stats request type %d with bad body length %d", - type, body_len); + VLOG_WARN_RL(&rl, "stats request type %d with bad body length %d", + type, body_len); err = -EINVAL; goto error; } @@ -1596,8 +1599,9 @@ recv_stats_request(struct datapath *dp, const struct sender *sender, if (cb->s->init) { err = cb->s->init(dp, rq->body, body_len, &cb->state); if (err) { - VLOG_WARN("failed initialization of stats request type %d: %s", - type, strerror(-err)); + VLOG_WARN_RL(&rl, + "failed initialization of stats request type %d: %s", + type, strerror(-err)); goto error; } } @@ -1675,21 +1679,24 @@ fwd_control_input(struct datapath *dp, const struct sender *sender, }, }; - const struct openflow_packet *pkt; struct ofp_header *oh; oh = (struct ofp_header *) msg; assert(oh->version == OFP_VERSION); - if (oh->type >= ARRAY_SIZE(packets) || ntohs(oh->length) > length) + if (ntohs(oh->length) > length) return -EINVAL; - pkt = &packets[oh->type]; - if (!pkt->handler) - return -ENOSYS; - if (length < pkt->min_size) - return -EFAULT; - - return pkt->handler(dp, sender, msg); + if (oh->type < ARRAY_SIZE(packets)) { + const struct openflow_packet *pkt = &packets[oh->type]; + if (pkt->handler) { + if (length < pkt->min_size) + return -EFAULT; + return pkt->handler(dp, sender, msg); + } + } + dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE, + msg, length); + return -EINVAL; } /* Packet buffering. */