From: Ben Pfaff Date: Mon, 9 Mar 2009 22:49:35 +0000 (-0700) Subject: Stop using vswitch OpenFlow connection to ofproto, by adding ofproto features. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6c974da5f393c9afe367bb2cd0ad1b8335ec8745;p=openvswitch Stop using vswitch OpenFlow connection to ofproto, by adding ofproto features. --- diff --git a/secchan/fail-open.c b/secchan/fail-open.c index 90915a33..9f9718cb 100644 --- a/secchan/fail-open.c +++ b/secchan/fail-open.c @@ -116,11 +116,11 @@ fail_open_handle_flow_miss(struct fail_open *fo, struct ofproto *ofproto, action.output.len = htons(sizeof action); if (in_port == out_port || out_port == DROP) { /* Set up a flow to drop packets. */ - ofproto_setup_exact_flow(ofproto, flow, NULL, 0, NULL); + ofproto_add_flow(ofproto, flow, NULL, 0, NULL, 0); } else if (out_port != FLOOD) { /* The output port is known, so add a new flow. */ action.output.port = htons(out_port); - ofproto_setup_exact_flow(ofproto, flow, &action, 1, payload); + ofproto_add_flow(ofproto, flow, &action, 1, payload, 0); } else { /* We don't know that MAC. Send along the packet without setting up a * flow. */ diff --git a/secchan/in-band.c b/secchan/in-band.c index c5793f08..3f5db494 100644 --- a/secchan/in-band.c +++ b/secchan/in-band.c @@ -189,11 +189,11 @@ in_band_handle_flow_miss(struct in_band *in_band, struct ofproto *ofproto, action.output.len = htons(sizeof action); if (in_port == out_port || out_port == DROP) { /* Set up a flow to drop packets. */ - ofproto_setup_exact_flow(ofproto, flow, NULL, 0, NULL); + ofproto_add_flow(ofproto, flow, NULL, 0, NULL, 0); } else if (out_port != FLOOD) { /* The output port is known, so add a new flow. */ action.output.port = htons(out_port); - ofproto_setup_exact_flow(ofproto, flow, &action, 1, payload); + ofproto_add_flow(ofproto, flow, &action, 1, payload, 0); } else { /* We don't know that MAC. Send along the packet without setting up a * flow. */ diff --git a/secchan/main.c b/secchan/main.c index f42fab2a..79c62b80 100644 --- a/secchan/main.c +++ b/secchan/main.c @@ -156,7 +156,7 @@ main(int argc, char *argv[]) VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION); /* Start OpenFlow processing. */ - error = ofproto_create(s.dp_name, &ofproto); + error = ofproto_create(s.dp_name, NULL, NULL, &ofproto); if (error) { ofp_fatal(error, "could not initialize openflow switch"); } diff --git a/secchan/ofproto.c b/secchan/ofproto.c index df5458c9..e6af97a4 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -168,10 +168,16 @@ struct ofproto { struct ofconn *controller; struct pvconn **listeners; size_t n_listeners; + + /* Hooks for vswitchd. */ + const struct ofhooks *ofhooks; + void *aux; }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); +static const struct ofhooks null_ofhooks; + static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid); static uint64_t pick_fallback_dpid(void); static void send_packet_in_miss(struct ofpbuf *, void *ofproto); @@ -191,11 +197,9 @@ static void update_port(struct ofproto *, const char *devname); static int init_ports(struct ofproto *); static void reinit_ports(struct ofproto *); -static uint16_t odp_port_to_ofp_port(uint16_t odp_port); -static uint16_t ofp_port_to_odp_port(uint16_t ofp_port); - int -ofproto_create(const char *datapath, struct ofproto **ofprotop) +ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux, + struct ofproto **ofprotop) { struct dpifmon *dpifmon; struct ofproto *p; @@ -266,6 +270,10 @@ ofproto_create(const char *datapath, struct ofproto **ofprotop) p->listeners = NULL; p->n_listeners = 0; + /* Initialize hooks. */ + p->ofhooks = ofhooks ? ofhooks : &null_ofhooks; + p->aux = aux; + /* Register switch status category. */ p->ss_cat = switch_status_register(p->switch_status, "remote", rconn_status_cb, p->controller->rconn); @@ -760,9 +768,9 @@ ofproto_send_packet(struct ofproto *p, const flow_t *flow, } void -ofproto_setup_exact_flow(struct ofproto *p, const flow_t *flow, - const union ofp_action *actions, size_t n_actions, - const struct ofpbuf *packet) +ofproto_add_flow(struct ofproto *p, const flow_t *flow, + const union ofp_action *actions, size_t n_actions, + const struct ofpbuf *packet, int idle_timeout) { struct rule *rule, *displaced_rule; struct odp_actions odp_actions; @@ -770,7 +778,7 @@ ofproto_setup_exact_flow(struct ofproto *p, const flow_t *flow, rule = xmalloc(sizeof *rule); cls_rule_from_flow(&rule->cr, flow, 0, UINT16_MAX); - rule->idle_timeout = 5; /* XXX */ + rule->idle_timeout = idle_timeout ? idle_timeout : 5; /* XXX */ rule->hard_timeout = 0; /* XXX */ rule->used = rule->created = time_msec(); rule->packet_count = 0; @@ -803,6 +811,43 @@ ofproto_setup_exact_flow(struct ofproto *p, const flow_t *flow, dpif_flow_add(&p->dpif, &odp_flow); odp_actions_free(&odp_actions); } + +void +ofproto_set_actions(struct ofproto *ofproto, const flow_t *flow, + const union ofp_action *actions, size_t n_actions) +{ + struct odp_actions odp_actions; + struct rule *rule; + + rule = rule_from_cls_rule(classifier_lookup_exact(&ofproto->cls, flow)); + if (!rule) { + return; + } + + free(rule->actions); + rule->n_actions = n_actions; + rule->actions = xmemdup(actions, n_actions * sizeof *rule->actions); + + rule_make_actions(ofproto, rule, &odp_actions); + dpif_flow_set_actions(&ofproto->dpif, flow, odp_actions.actions, + odp_actions.n_actions); + odp_actions_free(&odp_actions); +} + +void +ofproto_delete_flow(struct ofproto *ofproto, const flow_t *flow) +{ + struct rule *rule; + + rule = rule_from_cls_rule(classifier_lookup_exact(&ofproto->cls, flow)); + if (rule) { + classifier_remove(&ofproto->cls, &rule->cr); + rule_destroy(rule); + /* We don't call back to the flow_expired_cb because our caller would + * explode, but perhaps we should fix that. At any rate should we sent + * out a netflow message? */ + } +} static void reinit_ports(struct ofproto *p) @@ -943,6 +988,9 @@ send_port_status(struct ofproto *p, const struct ofport *ofport, hton_ofp_phy_port(&ops->desc); queue_tx(b, ofconn); } + if (p->ofhooks->port_changed_cb) { + p->ofhooks->port_changed_cb(reason, &ofport->opp, p->aux); + } } static void @@ -2416,6 +2464,12 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet) return; } + if (p->ofhooks->packet_in_cb + && p->ofhooks->packet_in_cb(&flow, &payload, p->aux)) { + ofpbuf_delete(packet); + return; + } + /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */ port = port_array_get(&p->ports, msg->port); if (port) { @@ -2579,7 +2633,8 @@ send_flow_exp(struct ofproto *p, struct rule *rule, queue_tx(buf, prev); } - if (!rule->cr.wc.wildcards && p->netflow) { + if (!rule->cr.wc.wildcards + && (p->netflow || p->ofhooks->flow_expired_cb)) { struct ofexpired expired; expired.flow = rule->cr.flow; expired.packet_count = rule->packet_count; @@ -2588,7 +2643,14 @@ send_flow_exp(struct ofproto *p, struct rule *rule, expired.created = rule->created; expired.tcp_flags = rule->tcp_flags; expired.ip_tos = rule->ip_tos; - netflow_expire(p->netflow, &expired); + + if (p->netflow) { + netflow_expire(p->netflow, &expired); + } + + if (p->ofhooks->flow_expired_cb) { + p->ofhooks->flow_expired_cb(&expired, p->aux); + } } } diff --git a/secchan/ofproto.h b/secchan/ofproto.h index 8a498e2d..1940f389 100644 --- a/secchan/ofproto.h +++ b/secchan/ofproto.h @@ -40,6 +40,7 @@ #include "flow.h" struct odp_actions; +struct ofhooks; struct ofproto; struct svec; @@ -53,7 +54,8 @@ struct ofexpired { uint8_t ip_tos; /* Last-seen IP type-of-service. */ }; -int ofproto_create(const char *datapath, struct ofproto **ofprotop); +int ofproto_create(const char *datapath, const struct ofhooks *, void *aux, + struct ofproto **ofprotop); void ofproto_destroy(struct ofproto *); int ofproto_run(struct ofproto *); void ofproto_wait(struct ofproto *); @@ -92,8 +94,20 @@ void ofproto_get_listeners(const struct ofproto *, struct svec *); int ofproto_send_packet(struct ofproto *, const flow_t *, const union ofp_action *, size_t n_actions, const struct ofpbuf *); -void ofproto_setup_exact_flow(struct ofproto *, const flow_t *, - const union ofp_action *, size_t n_actions, - const struct ofpbuf *); +void ofproto_add_flow(struct ofproto *, const flow_t *, + const union ofp_action *, size_t n_actions, + const struct ofpbuf *, int idle_timeout); +void ofproto_set_actions(struct ofproto *, const flow_t *, + const union ofp_action *, size_t n_actions); +void ofproto_delete_flow(struct ofproto *, const flow_t *); + +/* Hooks for vswitchd. */ +struct ofhooks { + void (*port_changed_cb)(enum ofp_port_reason, const struct ofp_phy_port *, + void *aux); + bool (*packet_in_cb)(const flow_t *, const struct ofpbuf *payload, + void *aux); + void (*flow_expired_cb)(const struct ofexpired *, void *aux); +}; #endif /* ofproto.h */ diff --git a/vswitchd/automake.mk b/vswitchd/automake.mk index 6a263ca8..8885f384 100644 --- a/vswitchd/automake.mk +++ b/vswitchd/automake.mk @@ -10,8 +10,6 @@ vswitchd_vswitchd_SOURCES = \ vswitchd/bridge.h \ vswitchd/flowtrack.c \ vswitchd/flowtrack.h \ - vswitchd/stats.c \ - vswitchd/stats.h \ vswitchd/vswitchd.c vswitchd_vswitchd_LDADD = \ secchan/libsecchan.a \ diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index d40d6594..38e8550f 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -51,6 +51,7 @@ #include "list.h" #include "mac-learning.h" #include "netdev.h" +#include "odp-util.h" #include "ofp-print.h" #include "ofpbuf.h" #include "poll-loop.h" @@ -59,7 +60,6 @@ #include "rconn.h" #include "secchan/ofproto.h" #include "socket-util.h" -#include "stats.h" #include "stp.h" #include "svec.h" #include "timeval.h" @@ -152,7 +152,6 @@ struct bridge { struct mac_learning *ml; /* MAC learning table, or null not to learn. */ int flow_idle_time; /* Idle time for flows we set up. */ bool sent_config_request; /* Successfully sent config request? */ - bool sent_features_request; /* Successfully sent features request? */ uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */ /* Support for remote controllers. */ @@ -178,8 +177,6 @@ struct bridge { bool flush; /* Flow statistics gathering. */ - struct stats_mgr *stats_mgr; - struct stats_request *flow_rq; /* Current flow statistics request. */ time_t next_stats_request; /* Port mirroring. */ @@ -243,6 +240,9 @@ static void iface_destroy(struct iface *); static struct iface *iface_lookup(const struct bridge *, const char *name); static struct iface *iface_from_dp_ifidx(const struct bridge *, uint16_t dp_ifidx); + +/* Hooks into ofproto processing. */ +static struct ofhooks bridge_ofhooks; /* Public functions. */ @@ -570,7 +570,6 @@ bridge_wait(void) if (br->ml) { mac_learning_wait(br->ml); } - stats_mgr_wait(br->stats_mgr); flowstats_wait(br); bond_wait(br); brstp_wait(br); @@ -618,7 +617,7 @@ bridge_create(const char *name) return NULL; } - error = ofproto_create(name, &br->ofproto); + error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto); if (error) { VLOG_ERR("failed to create OpenFlow switch %s: %s", name, strerror(error)); @@ -633,7 +632,6 @@ bridge_create(const char *name) br->ml = mac_learning_create(); br->flow_idle_time = 5; br->sent_config_request = false; - br->sent_features_request = false; eth_addr_random(br->default_ea); br->rconn = rconn_create(30, 1); @@ -644,8 +642,6 @@ bridge_create(const char *name) tag_set_init(&br->revalidate_set); br->flush = false; - br->stats_mgr = stats_mgr_create(br->rconn); - list_push_back(&all_bridges, &br->node); VLOG_INFO("created bridge %s on dp%u", br->name, dpif_id(&br->dpif)); @@ -707,8 +703,6 @@ bridge_destroy(struct bridge *br) rconn_packet_counter_destroy(br->txqlen); free(br->controller); ft_destroy(br->ft); - stats_request_destroy(br->flow_rq); - stats_mgr_destroy(br->stats_mgr); mac_learning_destroy(br->ml); port_array_destroy(&br->ifaces); free(br->ports); @@ -764,18 +758,6 @@ send_set_config_request(struct bridge *br) } } -static void -send_features_request(struct bridge *br) -{ - struct ofp_header *oh; - struct ofpbuf *msg; - - oh = make_openflow(sizeof *oh, OFPT_FEATURES_REQUEST, &msg); - if (!rconn_send_with_limit(br->rconn, msg, br->txqlen, INT_MAX)) { - br->sent_features_request = true; - } -} - static int bridge_run_one(struct bridge *br) { @@ -792,9 +774,6 @@ bridge_run_one(struct bridge *br) if (!br->sent_config_request) { send_set_config_request(br); } - if (!br->sent_features_request) { - send_features_request(br); - } } /* Now do the things that may want to revalidate flows. */ @@ -1126,12 +1105,6 @@ bridge_idle_time(const struct bridge *br) typedef void packet_handler_func(struct bridge *, void *); static packet_handler_func process_echo_request; -static packet_handler_func process_packet_in; -static packet_handler_func process_flow_expired; -static packet_handler_func process_stats_reply; -static packet_handler_func process_error_msg; -static packet_handler_func process_features_reply; -static packet_handler_func process_port_status; static void bridge_process_msg(struct bridge *br, struct ofpbuf *msg) @@ -1141,24 +1114,6 @@ bridge_process_msg(struct bridge *br, struct ofpbuf *msg) case OFPT_ECHO_REQUEST: process_echo_request(br, msg->data); break; - case OFPT_PACKET_IN: - process_packet_in(br, msg->data); - break; - case OFPT_FLOW_EXPIRED: - process_flow_expired(br, msg->data); - break; - case OFPT_STATS_REPLY: - process_stats_reply(br, msg->data); - break; - case OFPT_ERROR: - process_error_msg(br, msg->data); - break; - case OFPT_FEATURES_REPLY: - process_features_reply(br, msg->data); - break; - case OFPT_PORT_STATUS: - process_port_status(br, msg->data); - break; default: if (VLOG_IS_DBG_ENABLED()) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); @@ -1564,27 +1519,28 @@ put_actions(const struct ft_dst dsts[], size_t n_dsts, uint16_t flow_vlan, } } -struct received_packet { - struct ofpbuf *buf; /* NULL if no packet data. */ - uint32_t buffer_id; /* Host byte order; UINT32_MAX if not buffered. */ -}; - static void send_packets(struct bridge *br, const flow_t *flow, - const struct received_packet *pkt, uint16_t vlan, + const struct ofpbuf *pkt, uint16_t vlan, const struct port *in_port, const struct port *out_port, tag_type tags, bool setup_flow) { struct ft_dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)]; size_t actions_len; /* Estimated length of actions, in bytes. */ size_t n_dsts; + flow_t odp_flow; n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, &tags); actions_len = (sizeof(struct ofp_action_header) + 2) * n_dsts; + /* XXX for now everything in bridge.c is still in terms of OFP ports. In + * the long term we should change that (and avoid this awkward copy). */ + odp_flow = *flow; + odp_flow.in_port = ofp_port_to_odp_port(odp_flow.in_port); + if (setup_flow) { + enum { NO_OP, ADD_FLOW, SET_ACTIONS } command; struct ft_flow *f; - int command; f = ft_lookup(br->ft, flow, flow_hash(flow, 0)); if (f) { @@ -1593,7 +1549,7 @@ send_packets(struct bridge *br, const flow_t *flow, * OFPFC_ADD so that we don't reset the idle-timer * countdown for this flow. */ ftf_set_dsts(f, dsts, n_dsts); - command = OFPFC_MODIFY_STRICT; + command = SET_ACTIONS; } else { /* Correct flow is already in the flow table, nothing to do. * This should only happen on revalidate, since @@ -1601,29 +1557,36 @@ send_packets(struct bridge *br, const flow_t *flow, * no-match packet-in message. (If it could happen in other * circumstances then we'd want to arrange to send a packet-out * below, but there's no need.) */ - command = -1; + command = NO_OP; } f->tags = tags; } else { f = ftf_create(flow, dsts, n_dsts, tags); ft_insert(br->ft, f); - command = OFPFC_ADD; + command = ADD_FLOW; } f->need_drop = false; - if (command >= 0) { - struct ofp_flow_mod *ofm; - struct ofpbuf *fbuf; + if (command != NO_OP) { + struct ofpbuf abuf; + size_t n_actions; + + ofpbuf_init(&abuf, actions_len); + put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), &abuf); + n_actions = abuf.size / sizeof(union ofp_action); - fbuf = make_add_flow(flow, pkt->buffer_id, bridge_idle_time(br), - actions_len); - ofm = fbuf->data; - ofm->command = htons(command); - put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), fbuf); - queue_tx(br, fbuf); + if (command == ADD_FLOW) { + ofproto_add_flow(br->ofproto, &odp_flow, abuf.data, n_actions, + pkt, bridge_idle_time(br)); + pkt = NULL; /* Already sent. */ - /* The add_flow message will reset the byte counters. */ - f->last_byte_count = f->byte_count = 0; + /* ofproto_add_flow() will reset the byte counters. */ + f->last_byte_count = f->byte_count = 0; + } else { + ofproto_set_actions(br->ofproto, &odp_flow, abuf.data, + n_actions); + } + ofpbuf_uninit(&abuf); } } else { struct ft_flow *f = ft_lookup(br->ft, flow, flow_hash(flow, 0)); @@ -1632,22 +1595,17 @@ send_packets(struct bridge *br, const flow_t *flow, } } - if (pkt->buffer_id == UINT32_MAX ? pkt->buf != NULL : !setup_flow) { - size_t pkt_size = pkt->buf ? pkt->buf->size : 0; - struct ofp_packet_out *opo; - struct ofpbuf *pbuf; + /* XXX we should send the packet *before* we set up the flow, so as to not + * reorder packets in the flow. */ + if (pkt) { + struct ofpbuf abuf; - pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_size); - opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, pbuf); - opo->buffer_id = htonl(pkt->buffer_id); - opo->in_port = flow->in_port; - put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), pbuf); - opo = pbuf->data; - opo->actions_len = htons(pbuf->size - sizeof *opo); - if (pkt->buffer_id == UINT32_MAX) { - ofpbuf_put(pbuf, pkt->buf->data, pkt->buf->size); - } - queue_tx(br, pbuf); + ofpbuf_init(&abuf, actions_len); + put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), &abuf); + ofproto_send_packet(br->ofproto, &odp_flow, + abuf.data, abuf.size / sizeof(union ofp_action), + pkt); + ofpbuf_uninit(&abuf); } } @@ -1661,8 +1619,7 @@ is_bcast_arp_reply(const flow_t *flow, const struct ofpbuf *pkt) } static void -process_flow(struct bridge *br, const flow_t *flow, - struct received_packet *pkt) +process_flow(struct bridge *br, const flow_t *flow, const struct ofpbuf *pkt) { struct iface *in_iface; struct port *in_port; @@ -1675,7 +1632,7 @@ process_flow(struct bridge *br, const flow_t *flow, if (!in_iface) { /* No interface? Something fishy... */ struct ft_flow *f = ft_lookup(br->ft, flow, flow_hash(flow, 0)); - if (pkt->buf || (f && f->need_drop)) { + if (pkt || (f && f->need_drop)) { /* Odd. A few possible reasons here: * * - We deleted an interface but there are still a few packets @@ -1688,11 +1645,18 @@ process_flow(struct bridge *br, const flow_t *flow, * one of our bridge ports. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + flow_t odp_flow; + VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown " "interface %"PRIu16, br->name, flow->in_port); - queue_tx(br, make_add_flow(flow, pkt->buffer_id, - bridge_idle_time(br), 0)); + /* XXX for now everything in bridge.c is still in terms of OFP + * ports. In the long term we should change that (and avoid this + * awkward copy). */ + odp_flow = *flow; + odp_flow.in_port = ofp_port_to_odp_port(odp_flow.in_port); + ofproto_add_flow(br->ofproto, &odp_flow, NULL, 0, NULL, + bridge_idle_time(br)); if (f) { ftf_set_dsts(f, NULL, 0); f->tags = tags; @@ -1709,8 +1673,19 @@ process_flow(struct bridge *br, const flow_t *flow, /* We're revalidating, not receiving a fresh packet. Most likely, * this interface existed and has now been deleted from the * datapath. Drop the flow. */ + flow_t odp_flow; + assert(f); - queue_tx(br, make_del_flow(flow)); + + /* XXX for now everything in bridge.c is still in terms of OFP + * ports. In the long term we should change that (and avoid this + * awkward copy). */ + odp_flow = *flow; + odp_flow.in_port = ofp_port_to_odp_port(odp_flow.in_port); + ofproto_delete_flow(br->ofproto, &odp_flow); + + /* ofproto_delete_flow() better not call back to + * bridge_flow_expired_ofhook_cb(). */ ft_remove(br->ft, f); ftf_destroy(f); /* 'flow' pointed to &f->flow, so don't access it anymore. */ @@ -1733,7 +1708,7 @@ process_flow(struct bridge *br, const flow_t *flow, if (in_port->vlan >= 0) { if (vlan) { /* XXX support double tagging? */ - if (pkt->buf) { + if (pkt) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged " "packet received on port %s configured with " @@ -1791,7 +1766,7 @@ process_flow(struct bridge *br, const flow_t *flow, int out_port_idx; bool may_learn; - if (!pkt->buf) { + if (!pkt) { /* Don't try to learn from revalidation. */ may_learn = false; } else if (in_port->n_ifaces > 1) { @@ -1805,7 +1780,7 @@ process_flow(struct bridge *br, const flow_t *flow, /* Broadcast ARP replies are an exception to this rule: the host * has moved to another switch. */ - if (!may_learn && is_bcast_arp_reply(flow, pkt->buf)) { + if (!may_learn && is_bcast_arp_reply(flow, pkt)) { may_learn = true; } } else { @@ -1866,47 +1841,7 @@ done: static void revalidate_flow(struct bridge *br, struct ft_flow *f) { - struct received_packet pkt; - pkt.buf = NULL; - pkt.buffer_id = UINT32_MAX; - process_flow(br, &f->flow, &pkt); -} - -static void -process_packet_in(struct bridge *br, void *opi_) -{ - struct ofp_packet_in *opi = opi_; - struct received_packet pkt; - struct ofpbuf buf; - flow_t flow; - - if (check_ofp_message_array(&opi->header, OFPT_PACKET_IN, - offsetof(struct ofp_packet_in, data), - 1, &buf.size)) { - return; - } - buf.data = opi->data; - pkt.buf = &buf; - pkt.buffer_id = ntohl(opi->buffer_id); - flow_extract(&buf, ntohs(opi->in_port), &flow); - - if (opi->reason == OFPR_NO_MATCH) { - /* Delete any existing flow from the flow table. It must not really be - * there (otherwise we wouldn't be getting a packet-in). */ - struct ft_flow *f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0)); - if (f) { - ft_remove(br->ft, f); - ftf_destroy(f); - } - } - - if (flow.dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE) - && eth_addr_equals(flow.dl_dst, stp_eth_addr)) { - brstp_receive(br, &flow, &buf); - return; - } - - process_flow(br, &flow, &pkt); + process_flow(br, &f->flow, NULL); } static void @@ -1916,58 +1851,18 @@ process_echo_request(struct bridge *br, void *rq_) queue_tx(br, make_echo_reply(rq)); } +/* Careful: 'opp' is in host byte order and opp->port_no is an OFP port + * number. */ static void -process_flow_expired(struct bridge *br, void *ofe_) -{ - struct ofp_flow_expired *ofe = ofe_; - struct ft_flow *f; - flow_t flow; - - if (check_ofp_message(&ofe->header, OFPT_FLOW_EXPIRED, sizeof *ofe)) { - return; - } - - if (ofe->match.wildcards != htonl(0)) { - /* We don't use flows with wildcards, so there's nothing to do. */ - return; - } - flow_from_match(&flow, NULL, &ofe->match); - - f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0)); - if (f) { - /* Update statistics. */ - f->last_byte_count = f->byte_count; - f->byte_count = ntohll(ofe->byte_count); - bond_account_flow(br, f); - - ft_remove(br->ft, f); - ftf_destroy(f); - } else if (VLOG_IS_DBG_ENABLED()) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_DBG_RL(&rl, "received flow expiration for flow not in table"); - } -} - -static void -process_stats_reply(struct bridge *br, void *osr) -{ - stats_mgr_receive_stats_reply(br->stats_mgr, osr); -} - -static void -process_error_msg(struct bridge *br, void *oem) -{ - stats_mgr_receive_error_msg(br->stats_mgr, oem); -} - -static void -phy_port_changed(struct bridge *br, enum ofp_port_reason reason, - const struct ofp_phy_port *opp) +bridge_port_changed_ofhook_cb(enum ofp_port_reason reason, + const struct ofp_phy_port *opp, + void *br_) { + struct bridge *br = br_; struct iface *iface; struct port *port; - iface = iface_from_dp_ifidx(br, ntohs(opp->port_no)); + iface = iface_from_dp_ifidx(br, opp->port_no); if (!iface) { return; } @@ -1987,39 +1882,77 @@ phy_port_changed(struct bridge *br, enum ofp_port_reason reason, bridge_flush(br); } else { if (port->n_ifaces > 1) { - bool up = !(opp->state & htonl(OFPPS_LINK_DOWN)); + bool up = !(opp->state & OFPPS_LINK_DOWN); bond_link_status_update(iface, up); } memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN); } } -static void -process_features_reply(struct bridge *br, void *osf_) +static bool +bridge_packet_in_ofhook_cb(const flow_t *flow_, const struct ofpbuf *payload, + void *br_) { - struct ofp_switch_features *osf = osf_; - struct ofp_phy_port *pp; - size_t n_pp; + struct bridge *br = br_; + struct ft_flow *f; + flow_t flow; - if (check_ofp_message_array(&osf->header, OFPT_FEATURES_REPLY, - sizeof *osf, sizeof *osf->ports, &n_pp)) { - return; + /* XXX for now everything in bridge.c is still in terms of OFP ports. In + * the long term we should change that (and avoid this awkward copy). */ + flow = *flow_; + flow.in_port = odp_port_to_ofp_port(flow.in_port); + + /* Delete any existing flow from the flow tracker. The flow cannot really + * be in the flow table (otherwise we wouldn't be getting called). */ + f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0)); + if (f) { + ft_remove(br->ft, f); + ftf_destroy(f); } - for (pp = &osf->ports[0]; pp < &osf->ports[n_pp]; pp++) { - phy_port_changed(br, OFPPR_MODIFY, pp); + + if (flow.dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE) + && eth_addr_equals(flow.dl_dst, stp_eth_addr)) { + brstp_receive(br, &flow, payload); + return true; } + + process_flow(br, &flow, payload); + + return true; } static void -process_port_status(struct bridge *br, void *ops_) +bridge_flow_expired_ofhook_cb(const struct ofexpired *expired, void *br_) { - struct ofp_port_status *ops = ops_; + struct bridge *br = br_; + struct ft_flow *f; + flow_t flow; - if (check_ofp_message(&ops->header, OFPT_PORT_STATUS, sizeof *ops)) { - return; + /* XXX for now everything in bridge.c is still in terms of OFP ports. In + * the long term we should change that (and avoid this awkward copy). */ + flow = expired->flow; + flow.in_port = odp_port_to_ofp_port(flow.in_port); + + f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0)); + if (f) { + /* Update statistics. */ + f->last_byte_count = f->byte_count; + f->byte_count = expired->byte_count; + bond_account_flow(br, f); + + ft_remove(br->ft, f); + ftf_destroy(f); + } else if (VLOG_IS_DBG_ENABLED()) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_DBG_RL(&rl, "received flow expiration for flow not in table"); } - phy_port_changed(br, ops->reason, &ops->desc); } + +static struct ofhooks bridge_ofhooks = { + bridge_port_changed_ofhook_cb, + bridge_packet_in_ofhook_cb, + bridge_flow_expired_ofhook_cb, +}; /* Flow statistics collection. */ @@ -2213,100 +2146,64 @@ bond_rebalance(struct bridge *br) static void request_flow_stats(struct bridge *br) -{ - struct ofp_flow_stats_request *ofsr; - struct ofp_stats_request *osr; - struct ofpbuf *msg; - - osr = make_openflow(sizeof *osr + sizeof *ofsr, OFPT_STATS_REQUEST, &msg); - osr->type = htons(OFPST_FLOW); - osr->flags = htons(0); - ofsr = (struct ofp_flow_stats_request *) osr->body; - ofsr->match.wildcards = htonl(OFPFW_ALL); - ofsr->table_id = 0xff; - ofsr->out_port = htons(OFPP_NONE); - - stats_request_destroy(br->flow_rq); - br->flow_rq = stats_request_create(br->stats_mgr, msg); -} - -static void -flowstats_process(struct bridge *br) { /* This ft_dst will never appear as a valid flow's action. Thus, we can * set it as a flow's action to force revalidation to update that flow. */ static const struct ft_dst invalid_dst = {0xffff, 0xffff}; + struct odp_flow *flows, *odp_flow; + size_t n_flows; + int error; + tag_type flowstats_tag; - struct ofpbuf *msg; struct ft *new_ft; size_t n_tagged; + error = dpif_flow_list_all(&br->dpif, &flows, &n_flows); + if (error) { + return; + } + new_ft = ft_create(); flowstats_tag = tag_create_random(); n_tagged = 0; - while (stats_request_get_reply(br->flow_rq, &msg)) { - struct ofp_stats_reply *osr = msg->data; - const struct ofp_flow_stats *fs; - struct flow_stats_iterator i; - - for (fs = flow_stats_first(&i, osr); fs; fs = flow_stats_next(&i)) { - size_t n_actions = ((ntohs(fs->length) - - offsetof(struct ofp_flow_stats, actions)) - / sizeof(struct ofp_action_header)); - struct ft_flow *f; - flow_t flow; - size_t hash; - - if (fs->match.wildcards != htonl(0)) { - /* XXX delete flow */ - continue; - } - flow_from_match(&flow, NULL, &fs->match); - hash = flow_hash(&flow, 0); + for (odp_flow = flows; odp_flow < &flows[n_flows]; odp_flow++) { + const flow_t *flow = &odp_flow->key; + size_t hash = flow_hash(flow, 0); + struct ft_flow *f = ft_lookup(br->ft, flow, hash); + if (f) { + /* Move from br->ft to new_ft. */ + ft_remove(br->ft, f); + hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash); - f = ft_lookup(br->ft, &flow, hash); - if (f) { - /* Move from br->ft to new_ft. */ - ft_remove(br->ft, f); - hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash); + /* Update statistics. + * XXX is this still accurate with the new datapath? */ + f->last_byte_count = f->byte_count; + f->byte_count = odp_flow->stats.n_bytes; - /* Update statistics. */ - f->last_byte_count = f->byte_count; - f->byte_count = ntohll(fs->byte_count); - - /* Force the flow to be revalidated and replaced if its actions - * aren't what we expect. */ - if (!ftd_equal_actions(f->dsts, f->n_dsts, - fs->actions, n_actions, - ntohs(flow.dl_vlan))) { - ftf_set_dsts(f, &invalid_dst, 1); - f->tags |= flowstats_tag; - n_tagged++; - } else { - bond_account_flow(br, f); + bond_account_flow(br, f); + + /* XXX Should we verify that the flow's actions are what we + * expect? (This used to be easy; now it's harder.) */ + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + f = ft_lookup(new_ft, flow, hash); + if (!f) { + if (!VLOG_DROP_WARN(&rl)) { + char *flow_string = flow_to_string(flow); + VLOG_WARN("unexpected flow in flow table: %s", + flow_string); + free(flow_string); } + f = ftf_create(flow, &invalid_dst, 1, flowstats_tag); + hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash); + f->tags |= flowstats_tag; + n_tagged++; + f->last_byte_count = f->byte_count = odp_flow->stats.n_bytes; } else { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - f = ft_lookup(new_ft, &flow, hash); - if (!f) { - if (!VLOG_DROP_WARN(&rl)) { - char *flow_string = flow_to_string(&flow); - VLOG_WARN("unexpected flow in flow table: %s", - flow_string); - free(flow_string); - } - f = ftf_create(&flow, &invalid_dst, 1, flowstats_tag); - hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash); - f->tags |= flowstats_tag; - n_tagged++; - f->last_byte_count = f->byte_count = fs->byte_count; - } else { - VLOG_WARN_RL(&rl, "duplicate flow in flow stats reply"); - } + VLOG_WARN_RL(&rl, "duplicate flow in flow stats reply"); } } - ofpbuf_delete(msg); } /* Delete all the flows that remain in br->ft, replacing them by the ones @@ -2328,39 +2225,17 @@ flowstats_process(struct bridge *br) static void flowstats_run(struct bridge *br) { - stats_mgr_run(br->stats_mgr); - if (br->flow_rq) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - - switch (stats_request_get_status(br->flow_rq)) { - case EAGAIN: - return; - - case 0: - flowstats_process(br); - break; - - default: - VLOG_WARN_RL(&rl, "flow stats request returned error: %s", - strerror(stats_request_get_status(br->flow_rq))); - br->next_stats_request = time_now() + 1; - break; - } - stats_request_destroy(br->flow_rq); - br->flow_rq = NULL; - } else { - if (rconn_is_connected(br->rconn) - && time_now() >= br->next_stats_request) { - request_flow_stats(br); - br->next_stats_request = time_now() + 10; - } + if (rconn_is_connected(br->rconn) + && time_now() >= br->next_stats_request) { + request_flow_stats(br); + br->next_stats_request = time_now() + 10; } } static void flowstats_wait(struct bridge *br) { - if (!br->flow_rq && rconn_is_connected(br->rconn)) { + if (rconn_is_connected(br->rconn)) { poll_timer_wait((br->next_stats_request - time_now()) * 1000); } } @@ -2591,7 +2466,10 @@ port_update_bonding(struct port *port) static void iface_create(struct port *port, const char *name) { - struct iface *iface = xcalloc(1, sizeof *iface); + enum netdev_flags flags; + struct iface *iface; + + iface = xcalloc(1, sizeof *iface); iface->port = port; iface->port_ifidx = port->n_ifaces; iface->name = xstrdup(name); @@ -2600,6 +2478,10 @@ iface_create(struct port *port, const char *name) iface->enabled = true; iface->delay_expires = LLONG_MAX; + if (!netdev_nodev_get_flags(name, &flags)) { + iface->enabled = (flags & NETDEV_UP) != 0; + } + if (port->n_ifaces >= port->allocated_ifaces) { port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces, sizeof *port->ifaces); @@ -2974,9 +2856,19 @@ brstp_send_bpdu(struct ofpbuf *pkt, int port_no, void *br_) VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d with unknown MAC", br->name, port_no); } else { + union ofp_action action; struct eth_header *eth = pkt->l2; + flow_t flow; + memcpy(eth->eth_src, iface->mac, ETH_ADDR_LEN); - queue_tx(br, make_unbuffered_packet_out(pkt, OFPP_NONE, port_no)); + + memset(&action, 0, sizeof action); + action.type = htons(OFPAT_OUTPUT); + action.output.len = htons(sizeof action); + action.output.port = htons(port_no); + + flow_extract(pkt, htons(OFPP_NONE), &flow); + ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt); } ofpbuf_delete(pkt); } diff --git a/vswitchd/stats.c b/vswitchd/stats.c deleted file mode 100644 index 26eea8eb..00000000 --- a/vswitchd/stats.c +++ /dev/null @@ -1,307 +0,0 @@ -/* Copyright (c) 2008, 2009 Nicira Networks - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, Nicira Networks gives permission - * to link the code of its release of vswitchd with the OpenSSL project's - * "OpenSSL" library (or with modified versions of it that use the same - * license as the "OpenSSL" library), and distribute the linked - * executables. You must obey the GNU General Public License in all - * respects for all of the code used other than "OpenSSL". If you modify - * this file, you may extend this exception to your version of the file, - * but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. - */ - -#include -#include "stats.h" -#include -#include -#include -#include -#include -#include "list.h" -#include "ofpbuf.h" -#include "openflow/openflow.h" -#include "poll-loop.h" -#include "rconn.h" -#include "timeval.h" -#include "util.h" -#include "vconn.h" - -#define THIS_MODULE VLM_stats -#include "vlog.h" - -/* XXX when an ongoing stats requests is canceled, might want to wait for the - * completed replies before sending the next request */ - -struct stats_mgr { - struct stats_request *rq; - struct list requests; - struct rconn *rconn; - unsigned int rconn_seqno; - struct rconn_packet_counter *txqlen; - time_t timeout; -}; - -/* Number of seconds we'll wait for a stats reply before giving up. */ -#define STATS_TIMEOUT 5 - -struct stats_request { - struct stats_mgr *mgr; - struct list node; - struct ofpbuf *osr; - uint32_t xid; - uint16_t type; - enum { - SR_INIT, - SR_PARTIAL, - SR_DONE - } state; - int error; - struct ofp_queue replies; -}; - -static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - -static void complete_request(struct stats_request *, int error); -static void send_stats_request(struct stats_mgr *); -static void cancel_all_requests(struct stats_mgr *, int error); - -/* Creates and returns a new stats_mgr that sends stats requests on 'rconn'. */ -struct stats_mgr * -stats_mgr_create(struct rconn *rconn) -{ - struct stats_mgr *mgr = xcalloc(1, sizeof *mgr); - mgr->rq = NULL; - list_init(&mgr->requests); - mgr->rconn = rconn; - mgr->rconn_seqno = rconn_get_connection_seqno(rconn); - mgr->txqlen = rconn_packet_counter_create(); - return mgr; -} - -/* Destroys 'mgr'. Careful: the rconn underlying 'mgr' must be destroyed - * *before* calling this function (because destroying the rconn will update - * 'mgr->txqlen'). - * - * stats_requests on 'mgr' are canceled, but not destroyed; the client must - * destroy them individually with stats_request_destroy(). */ -void -stats_mgr_destroy(struct stats_mgr *mgr) -{ - if (mgr) { - rconn_packet_counter_destroy(mgr->txqlen); - cancel_all_requests(mgr, ECONNABORTED); - free(mgr); - } -} - -/* Allows 'mgr' to process 'osr' that was received on 'mgr''s rconn. */ -void -stats_mgr_receive_stats_reply(struct stats_mgr *mgr, - const struct ofp_stats_reply *osr) -{ - struct stats_request *rq = mgr->rq; - - if (!rq || check_ofp_message_array(&osr->header, OFPT_STATS_REPLY, - sizeof *osr, 1, NULL)) { - return; - } - if (osr->header.xid != rq->xid) { - VLOG_DBG_RL(&rl, "stats reply has xid %08"PRIx32" != " - "expected %08"PRIx32, osr->header.xid, rq->xid); - return; - } - if (osr->type != rq->type) { - VLOG_WARN_RL(&rl, "stats reply has type %04"PRIx16" != " - "expected %04"PRIx16, osr->type, rq->type); - return; - } - - queue_push_tail(&rq->replies, - ofpbuf_clone_data(osr, ntohs(osr->header.length))); - if (osr->flags & htons(OFPSF_REPLY_MORE)) { - rq->state = SR_PARTIAL; - mgr->timeout = time_now() + STATS_TIMEOUT; - } else { - complete_request(rq, 0); - send_stats_request(mgr); - } -} - -/* Allows 'mgr' to process 'oem' that was received on 'mgr''s rconn. */ -void -stats_mgr_receive_error_msg(struct stats_mgr *mgr, - const struct ofp_error_msg *oem) -{ - struct stats_request *rq = mgr->rq; - - if (!rq - || check_ofp_message_array(&oem->header, OFPT_ERROR, - sizeof *oem, 1, NULL) - || oem->header.xid != rq->xid) { - return; - } - - VLOG_WARN_RL(&rl, "error reply to stats request (%"PRIu16", %"PRIu16")", - ntohs(oem->type), ntohs(oem->code)); - complete_request(rq, EPROTO); - send_stats_request(mgr); -} - -/* Performs internal processing for 'mgr'. */ -void -stats_mgr_run(struct stats_mgr *mgr) -{ - if (mgr->rq && time_now() > mgr->timeout) { - VLOG_WARN_RL(&rl, "stats request timed out"); - complete_request(mgr->rq, ETIMEDOUT); - send_stats_request(mgr); - } -} - -/* Causes the next call to poll_block() to wake up when stats_mgr_run() - * should be called on 'mgr'. */ -void -stats_mgr_wait(struct stats_mgr *mgr) -{ - if (mgr->rq) { - poll_timer_wait((mgr->timeout - time_now()) * 1000); - } -} - -/* Creates a new stats_request, adds it to 'mgr', and returns it. 'msg' will - * be sent as the statistics request. */ -struct stats_request * -stats_request_create(struct stats_mgr *mgr, struct ofpbuf *msg) -{ - struct ofp_stats_request *osr = msg->data; - struct stats_request *rq; - - /* Flush out any dead requests if the rconn connected or disconnected, and - * update mgr->rconn_seqno so that the request we are about to queue can be - * sent. */ - send_stats_request(mgr); - - /* Queue the request. */ - rq = xcalloc(1, sizeof *rq); - rq->mgr = mgr; - list_push_back(&mgr->requests, &rq->node); - rq->xid = osr->header.xid; - rq->type = osr->type; - rq->osr = msg; - rq->state = SR_INIT; - queue_init(&rq->replies); - - /* Send the request, if possible. */ - send_stats_request(mgr); - - return rq; -} - -/* Destroys 'rq'. */ -void -stats_request_destroy(struct stats_request *rq) -{ - if (rq) { - complete_request(rq, ECANCELED); - queue_destroy(&rq->replies); - free(rq); - } -} - -/* Reports the status of 'rq': 0 if the request completed successfully, EAGAIN - * if the request is in progress, otherwise a positive errno value if the - * request completed with an error. */ -int -stats_request_get_status(const struct stats_request *rq) -{ - return rq->state == SR_DONE ? rq->error : EAGAIN; -} - -/* - * If statistics reply messages remain in the queue in 'rq', stores the first - * one in '*msg' and returns true. Otherwise, if no statistics reply messages - * remain in 'rq''s queue, stores a null pointer in '*msg' and returns false. - * - * This function may only be called for 'rq' if stats_request_get_status(rq) - * returns 0. */ -bool -stats_request_get_reply(struct stats_request *rq, struct ofpbuf **msg) -{ - assert(rq->state == SR_DONE && !rq->error); - if (rq->replies.n) { - *msg = queue_pop_head(&rq->replies); - return true; - } else { - *msg = NULL; - return false; - } -} - -static void -complete_request(struct stats_request *rq, int error) -{ - struct stats_mgr *mgr = rq->mgr; - if (mgr) { - if (mgr->rq == rq) { - mgr->rq = NULL; - } else { - list_remove(&rq->node); - } - rq->mgr = NULL; - } - if (rq->osr) { - ofpbuf_delete(rq->osr); - rq->osr = NULL; - } - rq->state = SR_DONE; - rq->error = error; -} - -static void -cancel_all_requests(struct stats_mgr *mgr, int error) -{ - struct stats_request *rq, *next; - LIST_FOR_EACH_SAFE (rq, next, struct stats_request, node, &mgr->requests) { - complete_request(rq, error); - } -} - -static void -send_stats_request(struct stats_mgr *mgr) -{ - if (mgr->rconn_seqno != rconn_get_connection_seqno(mgr->rconn) - || !rconn_is_connected(mgr->rconn)) { - mgr->rconn_seqno = rconn_get_connection_seqno(mgr->rconn); - cancel_all_requests(mgr, ENOTCONN); - } else if (!mgr->txqlen->n && !mgr->rq && !list_is_empty(&mgr->requests)) { - struct stats_request *rq; - int retval; - - rq = mgr->rq = CONTAINER_OF(list_pop_front(&mgr->requests), - struct stats_request, node); - retval = rconn_send(mgr->rconn, rq->osr, mgr->txqlen); - if (!retval) { - /* rconn_send() consumed the message. */ - rq->osr = NULL; - mgr->timeout = time_now() + STATS_TIMEOUT; - } else { - /* Sending failed, although it shouldn't have since we already - * checked that we were connected. Odd. */ - cancel_all_requests(mgr, ENOTCONN); - } - } -} diff --git a/vswitchd/stats.h b/vswitchd/stats.h deleted file mode 100644 index ccc006b0..00000000 --- a/vswitchd/stats.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright (c) 2008, 2009 Nicira Networks - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, Nicira Networks gives permission - * to link the code of its release of vswitchd with the OpenSSL project's - * "OpenSSL" library (or with modified versions of it that use the same - * license as the "OpenSSL" library), and distribute the linked - * executables. You must obey the GNU General Public License in all - * respects for all of the code used other than "OpenSSL". If you modify - * this file, you may extend this exception to your version of the file, - * but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. - */ - -#ifndef VSWITCHD_STATS_H -#define VSWITCHD_STATS_H 1 - -#include - -struct ofp_error_msg; -struct ofp_stats_reply; -struct ofpbuf; -struct rconn; - -/* A stats_mgr managing sending OpenFlow statistics requests over an rconn - * and receiving the replies. */ -struct stats_mgr *stats_mgr_create(struct rconn *); -void stats_mgr_destroy(struct stats_mgr *); -void stats_mgr_receive_stats_reply(struct stats_mgr *, - const struct ofp_stats_reply *); -void stats_mgr_receive_error_msg(struct stats_mgr *, - const struct ofp_error_msg *); -void stats_mgr_run(struct stats_mgr *); -void stats_mgr_wait(struct stats_mgr *); - -/* A stats_request represents a single OpenFlow statistics request within a - * stats_mgr. */ -struct stats_request *stats_request_create(struct stats_mgr *, - struct ofpbuf *); -void stats_request_destroy(struct stats_request *); -int stats_request_get_status(const struct stats_request *); -bool stats_request_get_reply(struct stats_request *, struct ofpbuf **); - -#endif /* vswitchd/stats.h */