X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=ofproto%2Fofproto.c;h=aad6333173a0201695f00fbd995667c181b38801;hb=eec336f2c312b10557b59bb3bd9faf01dedb8dc8;hp=87569b98b1c81cfdf51acf8b574e8f06b2c38b8f;hpb=7024dffed8cf0b19228704d99acb222b400a3354;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 87569b98b..aad633317 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks. * Copyright (c) 2010 Jean Tourrilhes - HP-Labs. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ #include #include #include +#include "bitmap.h" #include "byte-order.h" #include "classifier.h" #include "connmgr.h" @@ -136,22 +137,18 @@ static void ofproto_rule_send_removed(struct rule *, uint8_t reason); static void ofopgroup_destroy(struct ofopgroup *); static int add_flow(struct ofproto *, struct ofconn *, - struct ofputil_flow_mod *, const struct ofp_header *); - -/* This return value tells handle_openflow() that processing of the current - * OpenFlow message must be postponed until some ongoing operations have - * completed. - * - * This particular value is a good choice because it is negative (so it won't - * collide with any errno value or any value returned by ofp_mkerr()) and large - * (so it won't accidentally collide with EOF or a negative errno value). */ -enum { OFPROTO_POSTPONE = -100000 }; + const struct ofputil_flow_mod *, + const struct ofp_header *); static bool handle_openflow(struct ofconn *, struct ofpbuf *); +static int handle_flow_mod__(struct ofproto *, struct ofconn *, + const struct ofputil_flow_mod *, + const struct ofp_header *); static void update_port(struct ofproto *, const char *devname); static int init_ports(struct ofproto *); static void reinit_ports(struct ofproto *); +static void set_internal_devs_mtu(struct ofproto *); static void ofproto_unixctl_init(void); @@ -331,6 +328,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ofproto->sw_desc = xstrdup(DEFAULT_SW_DESC); ofproto->serial_desc = xstrdup(DEFAULT_SERIAL_DESC); ofproto->dp_desc = xstrdup(DEFAULT_DP_DESC); + ofproto->frag_handling = OFPC_FRAG_NORMAL; hmap_init(&ofproto->ports); shash_init(&ofproto->port_by_name); ofproto->tables = NULL; @@ -340,6 +338,8 @@ ofproto_create(const char *datapath_name, const char *datapath_type, list_init(&ofproto->pending); ofproto->n_pending = 0; hmap_init(&ofproto->deletions); + ofproto->vlan_bitmap = NULL; + ofproto->vlans_changed = false; error = ofproto->ofproto_class->construct(ofproto, &n_tables); if (error) { @@ -446,6 +446,16 @@ ofproto_set_forward_bpdu(struct ofproto *ofproto, bool forward_bpdu) } } +/* Sets the MAC aging timeout for the OFPP_NORMAL action on 'ofproto' to + * 'idle_time', in seconds. */ +void +ofproto_set_mac_idle_time(struct ofproto *ofproto, unsigned idle_time) +{ + if (ofproto->ofproto_class->set_mac_idle_time) { + ofproto->ofproto_class->set_mac_idle_time(ofproto, idle_time); + } +} + void ofproto_set_desc(struct ofproto *p, const char *mfr_desc, const char *hw_desc, @@ -533,6 +543,106 @@ ofproto_set_sflow(struct ofproto *ofproto, } } +/* Spanning Tree Protocol (STP) configuration. */ + +/* Configures STP on 'ofproto' using the settings defined in 's'. If + * 's' is NULL, disables STP. + * + * Returns 0 if successful, otherwise a positive errno value. */ +int +ofproto_set_stp(struct ofproto *ofproto, + const struct ofproto_stp_settings *s) +{ + return (ofproto->ofproto_class->set_stp + ? ofproto->ofproto_class->set_stp(ofproto, s) + : EOPNOTSUPP); +} + +/* Retrieves STP status of 'ofproto' and stores it in 's'. If the + * 'enabled' member of 's' is false, then the other members are not + * meaningful. + * + * Returns 0 if successful, otherwise a positive errno value. */ +int +ofproto_get_stp_status(struct ofproto *ofproto, + struct ofproto_stp_status *s) +{ + return (ofproto->ofproto_class->get_stp_status + ? ofproto->ofproto_class->get_stp_status(ofproto, s) + : EOPNOTSUPP); +} + +/* Configures STP on 'ofp_port' of 'ofproto' using the settings defined + * in 's'. The caller is responsible for assigning STP port numbers + * (using the 'port_num' member in the range of 1 through 255, inclusive) + * and ensuring there are no duplicates. If the 's' is NULL, then STP + * is disabled on the port. + * + * Returns 0 if successful, otherwise a positive errno value.*/ +int +ofproto_port_set_stp(struct ofproto *ofproto, uint16_t ofp_port, + const struct ofproto_port_stp_settings *s) +{ + struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); + if (!ofport) { + VLOG_WARN("%s: cannot configure STP on nonexistent port %"PRIu16, + ofproto->name, ofp_port); + return ENODEV; + } + + return (ofproto->ofproto_class->set_stp_port + ? ofproto->ofproto_class->set_stp_port(ofport, s) + : EOPNOTSUPP); +} + +/* Retrieves STP port status of 'ofp_port' on 'ofproto' and stores it in + * 's'. If the 'enabled' member in 's' is false, then the other members + * are not meaningful. + * + * Returns 0 if successful, otherwise a positive errno value.*/ +int +ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port, + struct ofproto_port_stp_status *s) +{ + struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); + if (!ofport) { + VLOG_WARN("%s: cannot get STP status on nonexistent port %"PRIu16, + ofproto->name, ofp_port); + return ENODEV; + } + + return (ofproto->ofproto_class->get_stp_port_status + ? ofproto->ofproto_class->get_stp_port_status(ofport, s) + : EOPNOTSUPP); +} + +/* Queue DSCP configuration. */ + +/* Registers meta-data associated with the 'n_qdscp' Qualities of Service + * 'queues' attached to 'ofport'. This data is not intended to be sufficient + * to implement QoS. Instead, it is used to implement features which require + * knowledge of what queues exist on a port, and some basic information about + * them. + * + * Returns 0 if successful, otherwise a positive errno value. */ +int +ofproto_port_set_queues(struct ofproto *ofproto, uint16_t ofp_port, + const struct ofproto_port_queue *queues, + size_t n_queues) +{ + struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); + + if (!ofport) { + VLOG_WARN("%s: cannot set queues on nonexistent port %"PRIu16, + ofproto->name, ofp_port); + return ENODEV; + } + + return (ofproto->ofproto_class->set_queues + ? ofproto->ofproto_class->set_queues(ofport, queues, n_queues) + : EOPNOTSUPP); +} + /* Connectivity Fault Management configuration. */ /* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */ @@ -624,10 +734,7 @@ ofproto_bundle_unregister(struct ofproto *ofproto, void *aux) /* Registers a mirror associated with client data pointer 'aux' in 'ofproto'. * If 'aux' is already registered then this function updates its configuration - * to 's'. Otherwise, this function registers a new mirror. - * - * Mirrors affect only the treatment of packets output to the OFPP_NORMAL - * port. */ + * to 's'. Otherwise, this function registers a new mirror. */ int ofproto_mirror_register(struct ofproto *ofproto, void *aux, const struct ofproto_mirror_settings *s) @@ -645,6 +752,23 @@ ofproto_mirror_unregister(struct ofproto *ofproto, void *aux) return ofproto_mirror_register(ofproto, aux, NULL); } +/* Retrieves statistics from mirror associated with client data pointer + * 'aux' in 'ofproto'. Stores packet and byte counts in 'packets' and + * 'bytes', respectively. If a particular counters is not supported, + * the appropriate argument is set to UINT64_MAX. */ +int +ofproto_mirror_get_stats(struct ofproto *ofproto, void *aux, + uint64_t *packets, uint64_t *bytes) +{ + if (!ofproto->ofproto_class->mirror_get_stats) { + *packets = *bytes = UINT64_MAX; + return EOPNOTSUPP; + } + + return ofproto->ofproto_class->mirror_get_stats(ofproto, aux, + packets, bytes); +} + /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs on * which all packets are flooded, instead of using MAC learning. If * 'flood_vlans' is NULL, then MAC learning applies to all VLANs. @@ -662,7 +786,7 @@ ofproto_set_flood_vlans(struct ofproto *ofproto, unsigned long *flood_vlans) /* Returns true if 'aux' is a registered bundle that is currently in use as the * output for a mirror. */ bool -ofproto_is_mirror_output_bundle(struct ofproto *ofproto, void *aux) +ofproto_is_mirror_output_bundle(const struct ofproto *ofproto, void *aux) { return (ofproto->ofproto_class->is_mirror_output_bundle ? ofproto->ofproto_class->is_mirror_output_bundle(ofproto, aux) @@ -737,6 +861,8 @@ ofproto_destroy__(struct ofproto *ofproto) hmap_destroy(&ofproto->deletions); + free(ofproto->vlan_bitmap); + ofproto->ofproto_class->dealloc(ofproto); } @@ -792,14 +918,8 @@ ofproto_run(struct ofproto *p) int error; error = p->ofproto_class->run(p); - if (error == ENODEV) { - /* Someone destroyed the datapath behind our back. The caller - * better destroy us and give up, because we're just going to - * spin from here on out. */ - static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally", - p->name); - return ENODEV; + if (error && error != EAGAIN) { + VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, strerror(error)); } if (p->ofproto_class->port_poll) { @@ -835,7 +955,26 @@ ofproto_run(struct ofproto *p) NOT_REACHED(); } - return 0; + return error; +} + +/* Performs periodic activity required by 'ofproto' that needs to be done + * with the least possible latency. + * + * It makes sense to call this function a couple of times per poll loop, to + * provide a significant performance boost on some benchmarks with the + * ofproto-dpif implementation. */ +int +ofproto_run_fast(struct ofproto *p) +{ + int error; + + error = p->ofproto_class->run_fast ? p->ofproto_class->run_fast(p) : 0; + if (error && error != EAGAIN) { + VLOG_ERR_RL(&rl, "%s: fastpath run failed (%s)", + p->name, strerror(error)); + } + return error; } void @@ -1061,6 +1200,18 @@ ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule, } } +/* Executes the flow modification specified in 'fm'. Returns 0 on success, an + * OpenFlow error code as encoded by ofp_mkerr() on failure, or + * OFPROTO_POSTPONE if the operation cannot be initiated now but may be retried + * later. + * + * This is a helper function for in-band control and fail-open. */ +int +ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm) +{ + return handle_flow_mod__(ofproto, NULL, fm, NULL); +} + /* Searches for a rule with matching criteria exactly equal to 'target' in * ofproto's table 0 and, if it finds one, deletes it. * @@ -1186,6 +1337,7 @@ ofport_install(struct ofproto *p, { const char *netdev_name = netdev_get_name(netdev); struct ofport *ofport; + int dev_mtu; int error; /* Create ofport. */ @@ -1204,6 +1356,13 @@ ofport_install(struct ofproto *p, hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0)); shash_add(&p->port_by_name, netdev_name, ofport); + if (!netdev_get_mtu(netdev, &dev_mtu)) { + set_internal_devs_mtu(p); + ofport->mtu = dev_mtu; + } else { + ofport->mtu = 0; + } + /* Let the ofproto_class initialize its private data. */ error = p->ofproto_class->port_construct(ofport); if (error) { @@ -1242,7 +1401,7 @@ ofport_remove_with_name(struct ofproto *ofproto, const char *name) } } -/* Updates 'port' within 'ofproto' with the new 'netdev' and 'opp'. +/* Updates 'port' with new 'opp' description. * * Does not handle a name or port number change. The caller must implement * such a change as a delete followed by an add. */ @@ -1261,11 +1420,28 @@ ofport_modified(struct ofport *port, struct ofp_phy_port *opp) connmgr_send_port_status(port->ofproto->connmgr, &port->opp, OFPPR_MODIFY); } +/* Update OpenFlow 'state' in 'port' and notify controller. */ +void +ofproto_port_set_state(struct ofport *port, ovs_be32 state) +{ + if (port->opp.state != state) { + port->opp.state = state; + connmgr_send_port_status(port->ofproto->connmgr, &port->opp, + OFPPR_MODIFY); + } +} + void ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port) { struct ofport *port = ofproto_get_port(ofproto, ofp_port); if (port) { + if (port->ofproto->ofproto_class->set_realdev) { + port->ofproto->ofproto_class->set_realdev(port, 0, 0); + } + if (port->ofproto->ofproto_class->set_stp_port) { + port->ofproto->ofproto_class->set_stp_port(port, NULL); + } if (port->ofproto->ofproto_class->set_cfm) { port->ofproto->ofproto_class->set_cfm(port, NULL); } @@ -1312,6 +1488,21 @@ ofproto_get_port(const struct ofproto *ofproto, uint16_t ofp_port) return NULL; } +int +ofproto_port_get_stats(const struct ofport *port, struct netdev_stats *stats) +{ + struct ofproto *ofproto = port->ofproto; + int error; + + if (ofproto->ofproto_class->port_get_stats) { + error = ofproto->ofproto_class->port_get_stats(port, stats); + } else { + error = EOPNOTSUPP; + } + + return error; +} + static void update_port(struct ofproto *ofproto, const char *name) { @@ -1330,12 +1521,22 @@ update_port(struct ofproto *ofproto, const char *name) port = ofproto_get_port(ofproto, ofproto_port.ofp_port); if (port && !strcmp(netdev_get_name(port->netdev), name)) { struct netdev *old_netdev = port->netdev; + int dev_mtu; /* 'name' hasn't changed location. Any properties changed? */ if (!ofport_equal(&port->opp, &opp)) { ofport_modified(port, &opp); } + /* If this is a non-internal port and the MTU changed, check + * if the datapath's MTU needs to be updated. */ + if (strcmp(netdev_get_type(netdev), "internal") + && !netdev_get_mtu(netdev, &dev_mtu) + && port->mtu != dev_mtu) { + set_internal_devs_mtu(ofproto); + port->mtu = dev_mtu; + } + /* Install the newly opened netdev in case it has changed. * Don't close the old netdev yet in case port_modified has to * remove a retained reference to it.*/ @@ -1391,6 +1592,52 @@ init_ports(struct ofproto *p) return 0; } + +/* Find the minimum MTU of all non-datapath devices attached to 'p'. + * Returns ETH_PAYLOAD_MAX or the minimum of the ports. */ +static int +find_min_mtu(struct ofproto *p) +{ + struct ofport *ofport; + int mtu = 0; + + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { + struct netdev *netdev = ofport->netdev; + int dev_mtu; + + /* Skip any internal ports, since that's what we're trying to + * set. */ + if (!strcmp(netdev_get_type(netdev), "internal")) { + continue; + } + + if (netdev_get_mtu(netdev, &dev_mtu)) { + continue; + } + if (!mtu || dev_mtu < mtu) { + mtu = dev_mtu; + } + } + + return mtu ? mtu: ETH_PAYLOAD_MAX; +} + +/* Set the MTU of all datapath devices on 'p' to the minimum of the + * non-datapath ports. */ +static void +set_internal_devs_mtu(struct ofproto *p) +{ + struct ofport *ofport; + int mtu = find_min_mtu(p); + + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { + struct netdev *netdev = ofport->netdev; + + if (!strcmp(netdev_get_type(netdev), "internal")) { + netdev_set_mtu(netdev, mtu); + } + } +} static void ofproto_rule_destroy__(struct rule *rule) @@ -1450,7 +1697,7 @@ rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet) assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in)); - flow_extract(packet, 0, in_port, &flow); + flow_extract(packet, 0, 0, in_port, &flow); return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet); } @@ -1490,7 +1737,7 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh) osf->n_buffers = htonl(pktbuf_capacity()); osf->n_tables = ofproto->n_tables; osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS | - OFPC_PORT_STATS); + OFPC_PORT_STATS | OFPC_QUEUE_STATS); if (arp_match_ip) { osf->capabilities |= htonl(OFPC_ARP_MATCH_IP); } @@ -1508,18 +1755,12 @@ static int handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); - struct ofpbuf *buf; struct ofp_switch_config *osc; - uint16_t flags; - bool drop_frags; - - /* Figure out flags. */ - drop_frags = ofproto->ofproto_class->get_drop_frags(ofproto); - flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL; + struct ofpbuf *buf; /* Send reply. */ osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf); - osc->flags = htons(flags); + osc->flags = htons(ofproto->frag_handling); osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn)); ofconn_send_reply(ofconn, buf); @@ -1532,19 +1773,20 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc) struct ofproto *ofproto = ofconn_get_ofproto(ofconn); uint16_t flags = ntohs(osc->flags); - if (ofconn_get_type(ofconn) == OFCONN_PRIMARY - && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) { - switch (flags & OFPC_FRAG_MASK) { - case OFPC_FRAG_NORMAL: - ofproto->ofproto_class->set_drop_frags(ofproto, false); - break; - case OFPC_FRAG_DROP: - ofproto->ofproto_class->set_drop_frags(ofproto, true); - break; - default: - VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")", - osc->flags); - break; + if (ofconn_get_type(ofconn) != OFCONN_PRIMARY + || ofconn_get_role(ofconn) != NX_ROLE_SLAVE) { + enum ofp_config_flags cur = ofproto->frag_handling; + enum ofp_config_flags next = flags & OFPC_FRAG_MASK; + + assert((cur & OFPC_FRAG_MASK) == cur); + if (cur != next) { + if (ofproto->ofproto_class->set_frag_handling(ofproto, next)) { + ofproto->frag_handling = next; + } else { + VLOG_WARN_RL(&rl, "%s: unsupported fragment handling mode %s", + ofproto->name, + ofputil_frag_handling_to_string(next)); + } } } @@ -1555,18 +1797,12 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc) /* Checks whether 'ofconn' is a slave controller. If so, returns an OpenFlow * error message code (composed with ofp_mkerr()) for the caller to propagate - * upward. Otherwise, returns 0. - * - * The log message mentions 'msg_type'. */ + * upward. Otherwise, returns 0. */ static int -reject_slave_controller(struct ofconn *ofconn, const char *msg_type) +reject_slave_controller(const struct ofconn *ofconn) { if (ofconn_get_type(ofconn) == OFCONN_PRIMARY && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) { - static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&perm_rl, "rejecting %s message from slave controller", - msg_type); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); } else { return 0; @@ -1588,7 +1824,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) COVERAGE_INC(ofproto_packet_out); - error = reject_slave_controller(ofconn, "OFPT_PACKET_OUT"); + error = reject_slave_controller(ofconn); if (error) { return error; } @@ -1607,7 +1843,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) /* Get payload. */ if (opo->buffer_id != htonl(UINT32_MAX)) { error = ofconn_pktbuf_retrieve(ofconn, ntohl(opo->buffer_id), - &buffer, &in_port); + &buffer, NULL); if (error || !buffer) { return error; } @@ -1617,8 +1853,18 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) buffer = NULL; } + /* Get in_port and partially validate it. + * + * We don't know what range of ports the ofproto actually implements, but + * we do know that only certain reserved ports (numbered OFPP_MAX and + * above) are valid. */ + in_port = ntohs(opo->in_port); + if (in_port >= OFPP_MAX && in_port != OFPP_LOCAL && in_port != OFPP_NONE) { + return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT); + } + /* Send out packet. */ - flow_extract(&payload, 0, ntohs(opo->in_port), &flow); + flow_extract(&payload, 0, 0, in_port, &flow); error = p->ofproto_class->packet_out(p, &payload, &flow, ofp_actions, n_ofp_actions); ofpbuf_delete(buffer); @@ -1656,7 +1902,7 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh) struct ofport *port; int error; - error = reject_slave_controller(ofconn, "OFPT_PORT_MOD"); + error = reject_slave_controller(ofconn); if (error) { return error; } @@ -1729,7 +1975,7 @@ append_port_stat(struct ofport *port, struct list *replies) /* Intentionally ignore return value, since errors will set * 'stats' to all-1s, which is correct for OpenFlow, and * netdev_get_stats() will log errors. */ - netdev_get_stats(port->netdev, &stats); + ofproto_port_get_stats(port, &stats); ops = ofputil_append_stats_reply(sizeof *ops, replies); ops->port_no = port->opp.port_no; @@ -1780,6 +2026,17 @@ calc_flow_duration__(long long int start, uint32_t *sec, uint32_t *nsec) *nsec = (msecs % 1000) * (1000 * 1000); } +/* Checks whether 'table_id' is 0xff or a valid table ID in 'ofproto'. Returns + * 0 if 'table_id' is OK, otherwise an OpenFlow error code. */ +static int +check_table_id(const struct ofproto *ofproto, uint8_t table_id) +{ + return (table_id == 0xff || table_id < ofproto->n_tables + ? 0 + : ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_TABLE_ID)); + +} + static struct classifier * first_matching_table(struct ofproto *ofproto, uint8_t table_id) { @@ -1788,11 +2045,6 @@ first_matching_table(struct ofproto *ofproto, uint8_t table_id) } else if (table_id < ofproto->n_tables) { return &ofproto->tables[table_id]; } else { - /* It would probably be better to reply with an error but there doesn't - * seem to be any appropriate value, so that might just be - * confusing. */ - VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8, - table_id); return NULL; } } @@ -1815,8 +2067,9 @@ next_matching_table(struct ofproto *ofproto, * - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates * only once, for that table. * - * - Otherwise, TABLE_ID isn't valid for OFPROTO, so ofproto logs a warning - * and does not enter the loop at all. + * - Otherwise, TABLE_ID isn't valid for OFPROTO, so the loop won't be + * entered at all. (Perhaps you should have validated TABLE_ID with + * check_table_id().) * * All parameters are evaluated multiple times. */ @@ -1838,10 +2091,17 @@ next_matching_table(struct ofproto *ofproto, * Returns 0 on success, otherwise an OpenFlow error code. */ static int collect_rules_loose(struct ofproto *ofproto, uint8_t table_id, - const struct cls_rule *match, uint16_t out_port, - struct list *rules) + const struct cls_rule *match, + ovs_be64 cookie, ovs_be64 cookie_mask, + uint16_t out_port, struct list *rules) { struct classifier *cls; + int error; + + error = check_table_id(ofproto, table_id); + if (error) { + return error; + } list_init(rules); FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) { @@ -1853,7 +2113,8 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id, if (rule->pending) { return OFPROTO_POSTPONE; } - if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) { + if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port) + && !((rule->flow_cookie ^ cookie) & cookie_mask)) { list_push_back(rules, &rule->ofproto_node); } } @@ -1874,10 +2135,17 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id, * Returns 0 on success, otherwise an OpenFlow error code. */ static int collect_rules_strict(struct ofproto *ofproto, uint8_t table_id, - const struct cls_rule *match, uint16_t out_port, - struct list *rules) + const struct cls_rule *match, + ovs_be64 cookie, ovs_be64 cookie_mask, + uint16_t out_port, struct list *rules) { struct classifier *cls; + int error; + + error = check_table_id(ofproto, table_id); + if (error) { + return error; + } list_init(rules); FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) { @@ -1888,7 +2156,8 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id, if (rule->pending) { return OFPROTO_POSTPONE; } - if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) { + if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port) + && !((rule->flow_cookie ^ cookie) & cookie_mask)) { list_push_back(rules, &rule->ofproto_node); } } @@ -1913,6 +2182,7 @@ handle_flow_stats_request(struct ofconn *ofconn, } error = collect_rules_loose(ofproto, fsr.table_id, &fsr.match, + fsr.cookie, fsr.cookie_mask, fsr.out_port, &rules); if (error) { return error; @@ -2043,6 +2313,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn, } error = collect_rules_loose(ofproto, request.table_id, &request.match, + request.cookie, request.cookie_mask, request.out_port, &rules); if (error) { return error; @@ -2189,14 +2460,15 @@ is_flow_deletion_pending(const struct ofproto *ofproto, * in which no matching flow already exists in the flow table. * * Adds the flow specified by 'ofm', which is followed by 'n_actions' - * ofp_actions, to the ofproto's flow table. Returns 0 on success or an - * OpenFlow error code as encoded by ofp_mkerr() on failure. + * ofp_actions, to the ofproto's flow table. Returns 0 on success, an OpenFlow + * error code as encoded by ofp_mkerr() on failure, or OFPROTO_POSTPONE if the + * operation cannot be initiated now but may be retried later. * * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id, * if any. */ static int add_flow(struct ofproto *ofproto, struct ofconn *ofconn, - struct ofputil_flow_mod *fm, const struct ofp_header *request) + const struct ofputil_flow_mod *fm, const struct ofp_header *request) { struct classifier *table; struct ofopgroup *group; @@ -2204,15 +2476,9 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, struct rule *rule; int error; - /* Check for overlap, if requested. */ - if (fm->flags & OFPFF_CHECK_OVERLAP) { - struct classifier *cls; - - FOR_EACH_MATCHING_TABLE (cls, fm->table_id, ofproto) { - if (classifier_rule_overlaps(cls, &fm->cr)) { - return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP); - } - } + error = check_table_id(ofproto, fm->table_id); + if (error) { + return error; } /* Pick table. */ @@ -2235,6 +2501,12 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, return ofp_mkerr_nicira(OFPET_FLOW_MOD_FAILED, NXFMFC_BAD_TABLE_ID); } + /* Check for overlap, if requested. */ + if (fm->flags & OFPFF_CHECK_OVERLAP + && classifier_rule_overlaps(table, &fm->cr)) { + return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP); + } + /* Serialize against pending deletion. */ if (is_flow_deletion_pending(ofproto, &fm->cr, table - ofproto->tables)) { return OFPROTO_POSTPONE; @@ -2251,7 +2523,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, rule->cr = fm->cr; rule->pending = NULL; rule->flow_cookie = fm->cookie; - rule->created = time_msec(); + rule->created = rule->modified = time_msec(); rule->idle_timeout = fm->idle_timeout; rule->hard_timeout = fm->hard_timeout; rule->table_id = table - ofproto->tables; @@ -2314,6 +2586,8 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions); rule->n_actions = fm->n_actions; rule->ofproto->ofproto_class->rule_modify_actions(rule); + } else { + rule->modified = time_msec(); } rule->flow_cookie = fm->cookie; } @@ -2329,14 +2603,15 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, * if any. */ static int modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn, - struct ofputil_flow_mod *fm, + const struct ofputil_flow_mod *fm, const struct ofp_header *request) { struct list rules; int error; - error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, OFPP_NONE, - &rules); + error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, + fm->cookie, fm->cookie_mask, + OFPP_NONE, &rules); return (error ? error : list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request) : modify_flows__(ofproto, ofconn, fm, request, &rules)); @@ -2349,14 +2624,15 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn, * if any. */ static int modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn, - struct ofputil_flow_mod *fm, + const struct ofputil_flow_mod *fm, const struct ofp_header *request) { struct list rules; int error; - error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, OFPP_NONE, - &rules); + error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, + fm->cookie, fm->cookie_mask, + OFPP_NONE, &rules); return (error ? error : list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request) : list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn, @@ -2398,8 +2674,9 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn, struct list rules; int error; - error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, fm->out_port, - &rules); + error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, + fm->cookie, fm->cookie_mask, + fm->out_port, &rules); return (error ? error : !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request, &rules) @@ -2409,14 +2686,15 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn, /* Implements OFPFC_DELETE_STRICT. */ static int delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn, - struct ofputil_flow_mod *fm, + const struct ofputil_flow_mod *fm, const struct ofp_header *request) { struct list rules; int error; - error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, fm->out_port, - &rules); + error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, + fm->cookie, fm->cookie_mask, + fm->out_port, &rules); return (error ? error : list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn, request, &rules) @@ -2469,20 +2747,14 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason) static int handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) { - struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_flow_mod fm; int error; - error = reject_slave_controller(ofconn, "flow_mod"); + error = reject_slave_controller(ofconn); if (error) { return error; } - if (ofproto->n_pending >= 50) { - assert(!list_is_empty(&ofproto->pending)); - return OFPROTO_POSTPONE; - } - error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_flow_mod_table_id(ofconn)); if (error) { @@ -2497,24 +2769,37 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL); } - switch (fm.command) { + return handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh); +} + +static int +handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn, + const struct ofputil_flow_mod *fm, + const struct ofp_header *oh) +{ + if (ofproto->n_pending >= 50) { + assert(!list_is_empty(&ofproto->pending)); + return OFPROTO_POSTPONE; + } + + switch (fm->command) { case OFPFC_ADD: - return add_flow(ofproto, ofconn, &fm, oh); + return add_flow(ofproto, ofconn, fm, oh); case OFPFC_MODIFY: - return modify_flows_loose(ofproto, ofconn, &fm, oh); + return modify_flows_loose(ofproto, ofconn, fm, oh); case OFPFC_MODIFY_STRICT: - return modify_flow_strict(ofproto, ofconn, &fm, oh); + return modify_flow_strict(ofproto, ofconn, fm, oh); case OFPFC_DELETE: - return delete_flows_loose(ofproto, ofconn, &fm, oh); + return delete_flows_loose(ofproto, ofconn, fm, oh); case OFPFC_DELETE_STRICT: - return delete_flow_strict(ofproto, ofconn, &fm, oh); + return delete_flow_strict(ofproto, ofconn, fm, oh); default: - if (fm.command > 0xff) { + if (fm->command > 0xff) { VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but " "flow_mod_table_id extension is not enabled"); } @@ -2531,17 +2816,13 @@ handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh) uint32_t role; if (ofconn_get_type(ofconn) != OFCONN_PRIMARY) { - VLOG_WARN_RL(&rl, "ignoring role request on service connection"); return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); } role = ntohl(nrr->role); if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER && role != NX_ROLE_SLAVE) { - VLOG_WARN_RL(&rl, "received request for unknown role %"PRIu32, role); - - /* There's no good error code for this. */ - return ofp_mkerr(OFPET_BAD_REQUEST, -1); + return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_ROLE); } if (ofconn_get_role(ofconn) != role @@ -2562,8 +2843,8 @@ static int handle_nxt_flow_mod_table_id(struct ofconn *ofconn, const struct ofp_header *oh) { - const struct nxt_flow_mod_table_id *msg - = (const struct nxt_flow_mod_table_id *) oh; + const struct nx_flow_mod_table_id *msg + = (const struct nx_flow_mod_table_id *) oh; ofconn_set_flow_mod_table_id(ofconn, msg->set != 0); return 0; @@ -2572,8 +2853,8 @@ handle_nxt_flow_mod_table_id(struct ofconn *ofconn, static int handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh) { - const struct nxt_set_flow_format *msg - = (const struct nxt_set_flow_format *) oh; + const struct nx_set_flow_format *msg + = (const struct nx_set_flow_format *) oh; uint32_t format; format = ntohl(msg->format); @@ -2591,6 +2872,29 @@ handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh) return 0; } +static int +handle_nxt_set_packet_in_format(struct ofconn *ofconn, + const struct ofp_header *oh) +{ + const struct nx_set_packet_in_format *msg; + uint32_t format; + + msg = (const struct nx_set_packet_in_format *) oh; + format = ntohl(msg->format); + if (format != NXFF_OPENFLOW10 && format != NXPIF_NXM) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); + } + + if (format != ofconn_get_packet_in_format(ofconn) + && ofconn_has_pending_opgroups(ofconn)) { + /* Avoid sending async message in surprsing packet in format. */ + return OFPROTO_POSTPONE; + } + + ofconn_set_packet_in_format(ofconn, format); + return 0; +} + static int handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -2658,6 +2962,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPUTIL_NXT_SET_FLOW_FORMAT: return handle_nxt_set_flow_format(ofconn, oh); + case OFPUTIL_NXT_SET_PACKET_IN_FORMAT: + return handle_nxt_set_packet_in_format(ofconn, oh); + case OFPUTIL_NXT_FLOW_MOD: return handle_flow_mod(ofconn, oh); @@ -2701,14 +3008,10 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPUTIL_OFPST_AGGREGATE_REPLY: case OFPUTIL_NXT_ROLE_REPLY: case OFPUTIL_NXT_FLOW_REMOVED: + case OFPUTIL_NXT_PACKET_IN: case OFPUTIL_NXST_FLOW_REPLY: case OFPUTIL_NXST_AGGREGATE_REPLY: default: - if (VLOG_IS_WARN_ENABLED()) { - char *s = ofp_to_string(oh, ntohs(oh->length), 2); - VLOG_DBG_RL(&rl, "OpenFlow message ignored: %s", s); - free(s); - } if (oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY) { return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT); } else { @@ -2892,7 +3195,8 @@ ofoperation_complete(struct ofoperation *op, int error) { struct ofopgroup *group = op->group; struct rule *rule = op->rule; - struct classifier *table = &rule->ofproto->tables[rule->table_id]; + struct ofproto *ofproto = rule->ofproto; + struct classifier *table = &ofproto->tables[rule->table_id]; assert(rule->pending == op); assert(op->status < 0); @@ -2924,6 +3228,19 @@ ofoperation_complete(struct ofoperation *op, int error) if (op->victim) { ofproto_rule_destroy__(op->victim); } + if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK)) + == htons(VLAN_VID_MASK)) { + if (ofproto->vlan_bitmap) { + uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci); + + if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) { + bitmap_set1(ofproto->vlan_bitmap, vid); + ofproto->vlans_changed = true; + } + } else { + ofproto->vlans_changed = true; + } + } } else { if (op->victim) { classifier_replace(table, &op->victim->cr); @@ -2943,7 +3260,9 @@ ofoperation_complete(struct ofoperation *op, int error) break; case OFOPERATION_MODIFY: - if (error) { + if (!error) { + rule->modified = time_msec(); + } else { free(rule->actions); rule->actions = op->actions; rule->n_actions = op->n_actions; @@ -3009,8 +3328,8 @@ ofproto_lookup(const char *name) } static void -ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED, - void *aux OVS_UNUSED) +ofproto_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ofproto *ofproto; struct ds results; @@ -3032,5 +3351,90 @@ ofproto_unixctl_init(void) } registered = true; - unixctl_command_register("ofproto/list", ofproto_unixctl_list, NULL); + unixctl_command_register("ofproto/list", "", 0, 0, + ofproto_unixctl_list, 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. */ + +/* Sets a 1-bit in the 4096-bit 'vlan_bitmap' for each VLAN ID that is matched + * (exactly) by an OpenFlow rule in 'ofproto'. */ +void +ofproto_get_vlan_usage(struct ofproto *ofproto, unsigned long int *vlan_bitmap) +{ + const struct classifier *cls; + + free(ofproto->vlan_bitmap); + ofproto->vlan_bitmap = bitmap_allocate(4096); + ofproto->vlans_changed = false; + + OFPROTO_FOR_EACH_TABLE (cls, ofproto) { + const struct cls_table *table; + + HMAP_FOR_EACH (table, hmap_node, &cls->tables) { + if ((table->wc.vlan_tci_mask & htons(VLAN_VID_MASK)) + == htons(VLAN_VID_MASK)) { + const struct cls_rule *rule; + + HMAP_FOR_EACH (rule, hmap_node, &table->rules) { + uint16_t vid = vlan_tci_to_vid(rule->flow.vlan_tci); + bitmap_set1(vlan_bitmap, vid); + bitmap_set1(ofproto->vlan_bitmap, vid); + } + } + } + } +} + +/* Returns true if new VLANs have come into use by the flow table since the + * last call to ofproto_get_vlan_usage(). + * + * We don't track when old VLANs stop being used. */ +bool +ofproto_has_vlan_usage_changed(const struct ofproto *ofproto) +{ + return ofproto->vlans_changed; +} + +/* Configures a VLAN splinter binding between the ports identified by OpenFlow + * port numbers 'vlandev_ofp_port' and 'realdev_ofp_port'. If + * 'realdev_ofp_port' is nonzero, then the VLAN device is enslaved to the real + * device as a VLAN splinter for VLAN ID 'vid'. If 'realdev_ofp_port' is zero, + * then the VLAN device is un-enslaved. */ +int +ofproto_port_set_realdev(struct ofproto *ofproto, uint16_t vlandev_ofp_port, + uint16_t realdev_ofp_port, int vid) +{ + struct ofport *ofport; + int error; + + assert(vlandev_ofp_port != realdev_ofp_port); + + ofport = ofproto_get_port(ofproto, vlandev_ofp_port); + if (!ofport) { + VLOG_WARN("%s: cannot set realdev on nonexistent port %"PRIu16, + ofproto->name, vlandev_ofp_port); + return EINVAL; + } + + if (!ofproto->ofproto_class->set_realdev) { + if (!vlandev_ofp_port) { + return 0; + } + VLOG_WARN("%s: vlan splinters not supported", ofproto->name); + return EOPNOTSUPP; + } + + error = ofproto->ofproto_class->set_realdev(ofport, realdev_ofp_port, vid); + if (error) { + VLOG_WARN("%s: setting realdev on port %"PRIu16" (%s) failed (%s)", + ofproto->name, vlandev_ofp_port, + netdev_get_name(ofport->netdev), strerror(error)); + } + return error; }