From be2be0daa9337580866064e280a0ac564e18fc97 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 23 Dec 2008 17:06:17 -0800 Subject: [PATCH] Implement revalidation. --- vswitchd/automake.mk | 2 + vswitchd/bridge.c | 427 ++++++++++++++++++++++++++++++------------- vswitchd/flowtrack.c | 145 +++++++++++++++ vswitchd/flowtrack.h | 86 +++++++++ 4 files changed, 528 insertions(+), 132 deletions(-) create mode 100644 vswitchd/flowtrack.c create mode 100644 vswitchd/flowtrack.h diff --git a/vswitchd/automake.mk b/vswitchd/automake.mk index c53ece76..d059a07f 100644 --- a/vswitchd/automake.mk +++ b/vswitchd/automake.mk @@ -7,6 +7,8 @@ vswitchd_vswitchd_SOURCES = \ vswitchd/bridge.h \ vswitchd/cfg.c \ vswitchd/cfg.h \ + vswitchd/flowtrack.c \ + vswitchd/flowtrack.h \ vswitchd/vswitchd.c vswitchd_vswitchd_LDADD = lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS) diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 1f51a599..35bd3d7e 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -48,6 +48,7 @@ #include "dirs.h" #include "dpif.h" #include "flow.h" +#include "flowtrack.h" #include "hash.h" #include "list.h" #include "mac-learning.h" @@ -103,6 +104,7 @@ struct bridge { int txqlen; /* # of messages queued to send on 'rconn'. */ 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; /* Successfully sent config request? */ /* Kernel datapath information. */ int dp_idx; /* Kernel datapath index. */ @@ -111,6 +113,10 @@ struct bridge { /* Bridge ports. */ struct port **ports; size_t n_ports, allocated_ports; + + /* Flow tracking. */ + struct ft *ft; + struct tag_set revalidate_set; }; /* List of all bridges. */ @@ -134,6 +140,7 @@ static bool bridge_is_backlogged(const struct bridge *); static int bridge_fetch_dp_ifaces(struct bridge *, struct svec *iface_names); static void bridge_process_msg(struct bridge *, struct ofpbuf *); +static void revalidate_flow(struct bridge *br, struct ft_flow *f); static void port_create(struct bridge *, const char *name); static void port_reconfigure(struct port *); @@ -323,6 +330,8 @@ bridge_create(const char *name) br->name = xstrdup(name); br->ml = mac_learning_create(); br->flow_idle_time = 5; + br->sent_config = false; + br->ft = ft_create(); /* Create kernel datapath. */ for (;;) { @@ -423,6 +432,7 @@ bridge_destroy(struct bridge *br) for (i = 0; i < br->n_ports; i++) { port_destroy(br->ports[i]); } + ft_destroy(br->ft); mac_learning_destroy(br->ml); free(br->ports); free(br); @@ -475,6 +485,23 @@ bridge_run_one(struct bridge *br) int iteration; rconn_run(br->rconn); + if (rconn_is_connected(br->rconn) && !br->sent_config) { + struct ofp_switch_config *osc; + struct ofpbuf *msg; + int retval; + + osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &msg); + osc->flags = htons(OFPC_SEND_FLOW_EXP | OFPC_FRAG_NORMAL); + osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); + retval = rconn_send(br->rconn, msg, &br->txqlen); + if (retval) { + ofpbuf_delete(msg); + } else { + br->sent_config = true; + } + } + + tag_set_init(&br->revalidate_set); for (iteration = 0; iteration < 50 && !bridge_is_backlogged(br); iteration++) { struct ofpbuf *msg = rconn_recv(br->rconn); @@ -485,9 +512,20 @@ bridge_run_one(struct bridge *br) bridge_process_msg(br, msg); ofpbuf_delete(msg); } + hmap_shrink(&br->ft->flows); if (br->ml) { mac_learning_run(br->ml, &br->revalidate_set); } + if (!tag_set_is_empty(&br->revalidate_set)) { + struct ft_flow *f, *next; + + HMAP_FOR_EACH_SAFE (f, next, struct ft_flow, node, &br->ft->flows) { + if (tag_set_intersects(&br->revalidate_set, f->tags)) { + revalidate_flow(br, f); + } + } + } + if (process_exited(br->secchan)) { int status = process_status(br->secchan); VLOG_ERR("%s: secchan subprocess with pid %ld died unexpectedly (%s)", @@ -682,6 +720,7 @@ sanitize_opp(struct ofp_phy_port *opp) 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 void bridge_process_msg(struct bridge *br, struct ofpbuf *msg) @@ -705,7 +744,7 @@ bridge_process_msg(struct bridge *br, struct ofpbuf *msg) }, { OFPT_FLOW_EXPIRED, - NULL + process_flow_expired }, }; const size_t n_processors = ARRAY_SIZE(processors); @@ -733,7 +772,9 @@ bridge_process_msg(struct bridge *br, struct ofpbuf *msg) static void queue_tx(struct bridge *br, struct ofpbuf *msg) { - int retval = rconn_send(br->rconn, msg, &br->txqlen); + int retval; + update_openflow_length(msg); + retval = rconn_send(br->rconn, msg, &br->txqlen); if (retval) { ofpbuf_delete(msg); /* No point in logging: rconn_send() only fails due to disconnection, @@ -742,11 +783,6 @@ queue_tx(struct bridge *br, struct ofpbuf *msg) } } -struct output { - uint16_t vlan; - uint16_t dp_ifidx; -}; - static struct iface * choose_output_iface(const struct port *port, const struct flow *flow) { @@ -766,8 +802,8 @@ choose_output_iface(const struct port *port, const struct flow *flow) } static void -set_output(struct output *p, const struct flow *flow, - const struct port *in_port, const struct port *out_port) +set_dst(struct ft_dst *p, const struct flow *flow, + const struct port *in_port, const struct port *out_port) { p->vlan = (out_port->vlan ? OFP_VLAN_NONE : in_port->vlan ? in_port->vlan @@ -775,6 +811,14 @@ set_output(struct output *p, const struct flow *flow, p->dp_ifidx = choose_output_iface(out_port, flow)->dp_ifidx; } +static void +swap_dst(struct ft_dst *p, struct ft_dst *q) +{ + struct ft_dst tmp = *p; + *p = *q; + *q = tmp; +} + static void * add_action_header(struct ofpbuf *buf, uint16_t type) { @@ -805,106 +849,130 @@ add_vlan_action(struct ofpbuf *buf, uint16_t old_vlan, uint16_t new_vlan) } } -static void -put_actions(const struct bridge *br, const struct flow *flow, uint16_t vlan, - const struct port *in_port, const struct port *out_port, - struct ofpbuf *buf, void **actions, size_t *actions_len) +static size_t +compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan, + const struct port *in_port, const struct port *out_port, + struct ft_dst dsts[]) { - struct output outs[DP_MAX_PORTS]; - struct output *p; - size_t n_outs; - uint16_t orig_vlan, cur_vlan; - size_t actions_ofs; - - if (*actions) { - ofpbuf_put(buf, *actions, *actions_len); - return; - } - - n_outs = 0; if (out_port == FLOOD_PORT) { /* Flood. */ + struct ft_dst *dst; /* Next element in 'dsts'. */ + struct ft_dst *vlan_dsts; /* First 'dsts' with vlan != flow->dl_vlan */ size_t i; + dst = vlan_dsts = dsts; for (i = 0; i < br->n_ports; i++) { - struct port *op = br->ports[i]; - if (op != in_port && (!op->vlan || vlan == op->vlan)) { - set_output(&outs[n_outs++], flow, in_port, op); + struct port *port = br->ports[i]; + if (port != in_port && (!port->vlan || vlan == port->vlan)) { + /* Put destinations for original VLAN at the front, so that we + * don't have to add actions to set the VLAN tag for those. */ + set_dst(dst, flow, in_port, port); + if (dst->vlan == ntohs(flow->dl_vlan)) { + swap_dst(dst, vlan_dsts++); + } + dst++; } } + + /* XXX Sort 'vlan_dsts' through 'dst' by VLAN, to reduce number of + * VLAN-setting actions to minimum. */ + return dst - dsts; } else if (out_port) { /* Unicast. */ - set_output(&outs[n_outs++], flow, in_port, out_port); + set_dst(dsts, flow, in_port, out_port); + return 1; + } else { + /* Drop. */ + return 0; } +} - actions_ofs = buf->size; - - orig_vlan = ntohs(flow->dl_vlan); - for (p = outs; p < &outs[n_outs]; p++) { - if (orig_vlan == p->vlan) { - add_output_action(buf, p->dp_ifidx); - } - } - cur_vlan = orig_vlan; - for (p = outs; p < &outs[n_outs]; p++) { - if (p->vlan != orig_vlan) { - if (p->vlan != cur_vlan) { - add_vlan_action(buf, cur_vlan, p->vlan); - cur_vlan = p->vlan; - } - add_output_action(buf, p->dp_ifidx); +static void +put_actions(const struct ft_dst dsts[], size_t n_dsts, uint16_t flow_vlan, + struct ofpbuf *buf) +{ + const struct ft_dst *p; + uint16_t vlan = flow_vlan; + for (p = dsts; p < &dsts[n_dsts]; p++) { + if (p->vlan != vlan) { + add_vlan_action(buf, vlan, p->vlan); + vlan = p->vlan; } + add_output_action(buf, p->dp_ifidx); } - - *actions = (char *) buf->data + actions_ofs; - *actions_len = buf->size - actions_ofs; } +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 struct flow *flow, uint32_t buffer_id, - uint16_t vlan, uint16_t in_ifidx, - const void *pkt_data, size_t pkt_len, +send_packets(struct bridge *br, const struct flow *flow, + const struct received_packet *pkt, uint16_t vlan, const struct port *in_port, const struct port *out_port, - bool setup_flow) + tag_type tags, bool setup_flow) { - struct ofpbuf *fbuf = NULL; - struct ofpbuf *pbuf = NULL; + struct ft_dst dsts[DP_MAX_PORTS]; + size_t actions_len; /* Estimated length of actions, in bytes. */ + size_t n_dsts; - void *actions = NULL; - size_t actions_len = sizeof(struct ofp_action_header) * 4; /* Estimated. */ + n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts); + actions_len = (sizeof(struct ofp_action_header) + 2) * n_dsts; if (setup_flow) { - fbuf = make_add_flow(flow, buffer_id, br->flow_idle_time, actions_len); - put_actions(br, flow, vlan, in_port, out_port, fbuf, - &actions, &actions_len); - update_openflow_length(fbuf); + struct ft_flow *f; + bool queue; + + f = ft_lookup(br->ft, flow); + if (f) { + if (!ftd_equal(dsts, n_dsts, f->dsts, f->n_dsts)) { + ftf_set_dsts(f, dsts, n_dsts); + queue = true; + } else { + /* Correct flow is already in the flow table, nothing to do. + * This should only happen on revalidate, since + * process_packet_in() will delete any flow entry for a + * 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.) */ + queue = false; + } + f->tags = tags; + } else { + ft_insert(br->ft, ftf_create(flow, dsts, n_dsts, tags)); + queue = true; + } - /* Defer sending 'fbuf' to the end of the function: sending it can free - * it immediately, but we may need to reuse the cached actions for - * 'pbuf', below ('actions' points inside 'fbuf'). */ + if (queue) { + struct ofpbuf *fbuf = make_add_flow(flow, pkt->buffer_id, + br->flow_idle_time, + actions_len); + put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), fbuf); + queue_tx(br, fbuf); + } + } else { + struct ft_flow *f = ft_lookup(br->ft, flow); + if (f) { + /* XXX delete flow from ft, queue delete-flow openflow message */ + } } - if (!setup_flow || buffer_id == UINT32_MAX) { + 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; - pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_len); + pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_size); opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, pbuf); - opo->buffer_id = htonl(buffer_id); - opo->in_port = htons(in_ifidx); - put_actions(br, flow, vlan, in_port, out_port, pbuf, - &actions, &actions_len); + 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(actions_len); - if (buffer_id == UINT32_MAX) { - ofpbuf_put(pbuf, pkt_data, pkt_len); + if (pkt->buffer_id == UINT32_MAX) { + ofpbuf_put(pbuf, pkt->buf->data, pkt->buf->size); } - update_openflow_length(pbuf); - } - - if (fbuf) { - queue_tx(br, fbuf); - } - if (pbuf) { queue_tx(br, pbuf); } } @@ -919,38 +987,37 @@ is_bcast_arp_reply(const struct flow *flow, const struct ofpbuf *pkt) } static void -process_packet_in(struct bridge *br, void *opi_) +process_flow(struct bridge *br, const struct flow *flow, + struct received_packet *pkt) { - struct ofp_packet_in *opi = opi_; - uint16_t in_ifidx = ntohs(opi->in_port); - - struct ofpbuf pkt; - struct flow flow; + uint16_t in_ifidx = ntohs(flow->in_port); struct iface *in_iface; struct port *in_port; struct port *out_port = NULL; /* By default, drop the packet/flow. */ + tag_type tags = 0; int vlan; - /* Validate Openflow message. */ - if (check_ofp_message_array(&opi->header, OFPT_PACKET_IN, - offsetof(struct ofp_packet_in, data), - 1, &pkt.size)) { - return; - } + /* Find the interface and port structure for the received packet. */ + if (in_ifidx >= ARRAY_SIZE(br->ifaces) || !br->ifaces[in_ifidx]) { + struct ft_flow *f; - /* Extract flow data from 'opi' into 'flow'. */ - pkt.data = opi->data; - flow_extract(&pkt, in_ifidx, &flow); + if (pkt->buf) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown " + "interface %"PRIu16, br->name, in_ifidx); + } - /* Find the interface and port structure for the received packet. */ - if (in_ifidx < 0 - || in_ifidx >= ARRAY_SIZE(br->ifaces) - || !br->ifaces[in_ifidx]) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown " - "interface %"PRIu16, br->name, in_ifidx); - queue_tx(br, make_add_flow(&flow, ntohl(opi->buffer_id), + queue_tx(br, make_add_flow(flow, pkt->buffer_id, br->flow_idle_time, 0)); + + f = ft_lookup(br->ft, flow); + if (f) { + ftf_set_dsts(f, NULL, 0); + f->tags = tags; + } else { + ft_insert(br->ft, ftf_create(flow, NULL, 0, tags)); + } + return; } in_iface = br->ifaces[in_ifidx]; @@ -963,19 +1030,21 @@ process_packet_in(struct bridge *br, void *opi_) * former case, the packet has an 802.1Q header that specifies VLAN 0, * presumably to allow a priority to be specified. In the latter case, the * packet does not have any 802.1Q header.) */ - vlan = ntohs(flow.dl_vlan); + vlan = ntohs(flow->dl_vlan); if (vlan == OFP_VLAN_NONE) { vlan = 0; } if (in_port->vlan) { if (vlan) { /* XXX support double tagging? */ - 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 implicit " - "VLAN %"PRIu16, - br->name, ntohs(flow.dl_vlan), - in_port->name, in_port->vlan); + if (pkt->buf) { + 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 " + "implicit VLAN %"PRIu16, + br->name, ntohs(flow->dl_vlan), + in_port->name, in_port->vlan); + } goto done; } vlan = in_port->vlan; @@ -985,7 +1054,8 @@ process_packet_in(struct bridge *br, void *opi_) * avoid receiving duplicates. */ if (in_port->n_ifaces > 0 && in_port->active_iface != in_iface->port_ifidx - && eth_addr_is_multicast(flow.dl_dst)) { + && eth_addr_is_multicast(flow->dl_dst)) { + //tags |= in_iface->inactive_iface_tag; goto done; } @@ -998,17 +1068,21 @@ process_packet_in(struct bridge *br, void *opi_) /* XXX flush learning table entries when port indexes change due to * reconfiguration */ - /* If the packet arrived on a bonded port, don't learn from it unless - * we haven't learned any port at all for that address (because we - * probably sent the packet on one bonded interface and got it back on - * the other). */ - if (in_port->n_ifaces > 1) { - uint16_t src_idx = mac_learning_lookup(br->ml, flow.dl_src, vlan); + if (!pkt->buf) { + /* Don't try to learn from revalidation. */ + may_learn = false; + } else if (in_port->n_ifaces > 1) { + /* If the packet arrived on a bonded port, don't learn from it + * unless we haven't learned any port at all for that address + * (because we probably sent the packet on one bonded interface and + * got it back on the other). */ + /* XXX invalidation? */ + uint16_t src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan); may_learn = src_idx == OFPP_FLOOD || src_idx == in_port->port_idx; /* 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)) { + if (!may_learn && is_bcast_arp_reply(flow, pkt->buf)) { may_learn = true; } } else { @@ -1016,19 +1090,25 @@ process_packet_in(struct bridge *br, void *opi_) } /* Learn source MAC. */ - if (may_learn && - mac_learning_learn(br->ml, flow.dl_src, vlan, in_port->port_idx)) { - /* The log messages here could actually be useful in debugging, so - * keep the rate limit relatively high. */ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); - VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is on " - "port %s in VLAN %d", - br->name, ETH_ADDR_ARGS(flow.dl_src), - in_port->name, vlan); + if (may_learn) { + tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src, + vlan, in_port->port_idx); + if (rev_tag) { + /* The log messages here could actually be useful in debugging, + * so keep the rate limit relatively high. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, + 300); + VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is " + "on port %s in VLAN %d", + br->name, ETH_ADDR_ARGS(flow->dl_src), + in_port->name, vlan); + tag_set_add(&br->revalidate_set, rev_tag); + } } /* Determine output port. */ - out_port_idx = mac_learning_lookup(br->ml, flow.dl_dst, vlan); + out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan, + &tags); if (out_port_idx < br->n_ports) { out_port = br->ports[out_port_idx]; } @@ -1052,12 +1132,51 @@ process_packet_in(struct bridge *br, void *opi_) * them. */ done: - send_packets(br, &flow, ntohl(opi->buffer_id), vlan, - in_ifidx, pkt.data, pkt.size, in_port, out_port, + send_packets(br, flow, pkt, vlan, in_port, out_port, tags, (br->flow_idle_time >= 0 && (in_port->n_ifaces < 2 - || flow.dl_type != htons(ETH_TYPE_ARP) - || !eth_addr_is_broadcast(flow.dl_dst)))); + || flow->dl_type != htons(ETH_TYPE_ARP) + || !eth_addr_is_broadcast(flow->dl_dst)))); +} + +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; + struct flow 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); + if (f) { + ft_remove(br->ft, f); + ftf_destroy(f); + } + } + + process_flow(br, &flow, &pkt); } static void @@ -1066,6 +1185,43 @@ process_echo_request(struct bridge *br, void *rq_) struct ofp_header *rq = rq_; queue_tx(br, make_echo_reply(rq)); } + +static void +process_flow_expired(struct bridge *br, void *ofe_) +{ + struct ofp_flow_expired *ofe = ofe_; + struct ft_flow *f; + struct flow 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.nw_src = ofe->match.nw_src; + flow.nw_dst = ofe->match.nw_dst; + flow.in_port = ofe->match.in_port; + flow.dl_vlan = ofe->match.dl_vlan; + flow.dl_type = ofe->match.dl_type; + flow.tp_src = ofe->match.tp_src; + flow.tp_dst = ofe->match.tp_dst; + memcpy(flow.dl_src, ofe->match.dl_src, ETH_ADDR_LEN); + memcpy(flow.dl_dst, ofe->match.dl_dst, ETH_ADDR_LEN); + flow.nw_proto = ofe->match.nw_proto; + flow.reserved = 0; + + f = ft_lookup(br->ft, &flow); + if (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"); + } +} /* Port functions. */ @@ -1103,6 +1259,13 @@ port_reconfigure(struct port *port) } if (bonded) { cfg_get_all_keys(&new_ifaces, "bonding.%s.slave", port->name); + if (!new_ifaces.n) { + VLOG_ERR("port %s: no interfaces specified for bonded port", + port->name); + } else if (new_ifaces.n == 1) { + VLOG_WARN("port %s: only 1 interface specified for bonded port", + port->name); + } } else { svec_init(&new_ifaces); svec_add(&new_ifaces, port->name); @@ -1139,8 +1302,8 @@ port_reconfigure(struct port *port) port->name, port->vlan); } } else { - /* It's possible that bonded, VLAN-tagged ports make sense but they - * are not worth implementing yet. */ + /* It's possible that bonded, VLAN-tagged ports make sense. Maybe + * they even work as-is. But they have not been tested. */ VLOG_WARN("port %s: VLAN tags not supported on bonded ports", port->name); } diff --git a/vswitchd/flowtrack.c b/vswitchd/flowtrack.c new file mode 100644 index 00000000..d475050a --- /dev/null +++ b/vswitchd/flowtrack.c @@ -0,0 +1,145 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#include +#include "flowtrack.h" +#include +#include +#include "flow.h" +#include "openflow/openflow.h" +#include "util.h" + +void +ftd_print(const struct ft_dst *dsts, size_t n) +{ + for (; n--; dsts++) { + printf(">p%"PRIu16, dsts->dp_ifidx); + if (dsts->vlan != OFP_VLAN_NONE) { + printf("v%"PRIu16, dsts->vlan); + } + } +} + +struct ft_flow * +ftf_create(const struct flow *flow, + const struct ft_dst dsts[], size_t n_dsts, + tag_type tags) +{ + struct ft_flow *f; + + assert(!flow->reserved); + f = xmalloc(sizeof *f); + f->tags = tags; + f->flow = *flow; + f->node.hash = flow_hash(&f->flow, 0); + f->dsts = &f->one_dst; + ftf_set_dsts(f, dsts, n_dsts); + return f; +} + +void +ftf_destroy(struct ft_flow *f) +{ + if (f) { + ftf_set_dsts(f, NULL, 0); + free(f); + } +} + +void +ftf_set_dsts(struct ft_flow *f, const struct ft_dst dsts[], size_t n_dsts) +{ + if (f->dsts != &f->one_dst) { + free(f->dsts); + } + + f->n_dsts = n_dsts; + if (n_dsts > 1) { + f->dsts = xmemdup(dsts, sizeof *f->dsts * n_dsts); + } else { + f->dsts = &f->one_dst; + if (n_dsts) { + f->one_dst = *dsts; + } + } +} + +struct ft * +ft_create(void) +{ + struct ft *ft = xcalloc(1, sizeof *ft); + hmap_init(&ft->flows); + return ft; +} + +void +ft_destroy(struct ft *ft) +{ + if (ft) { + struct ft_flow *f, *next; + + HMAP_FOR_EACH_SAFE (f, next, struct ft_flow, node, &ft->flows) { + ftf_destroy(f); + } + free(ft); + } +} + +struct ft_flow * +ft_lookup(const struct ft *ft, const struct flow *target) +{ + struct ft_flow *f; + size_t hash; + + assert(!target->reserved); + hash = flow_hash(target, 0); + HMAP_FOR_EACH_WITH_HASH (f, struct ft_flow, node, hash, &ft->flows) { + if (flow_equal(&f->flow, target)) { + return f; + } + } + return NULL; +} + +void +ft_remove(struct ft *ft, struct ft_flow *f) +{ + hmap_remove(&ft->flows, &f->node); + /* It's the caller's responsible to free 'f', if desired. */ +} + +void +ft_insert(struct ft *ft, struct ft_flow *f) +{ + hmap_insert(&ft->flows, &f->node, f->node.hash); +} diff --git a/vswitchd/flowtrack.h b/vswitchd/flowtrack.h new file mode 100644 index 00000000..4c12cb17 --- /dev/null +++ b/vswitchd/flowtrack.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#ifndef VSWITCHD_FLOWTRACK_H +#define VSWITCHD_FLOWTRACK_H 1 + +#include +#include +#include +#include "flow.h" +#include "hmap.h" +#include "tag.h" + +struct ft_dst { + uint16_t vlan; + uint16_t dp_ifidx; +}; + +static inline bool ftd_equal(const struct ft_dst *, size_t, + const struct ft_dst *, size_t); +void ftd_print(const struct ft_dst *, size_t); + +static inline bool +ftd_equal(const struct ft_dst *a, size_t an, + const struct ft_dst *b, size_t bn) +{ + return an == bn && !memcmp(a, b, an * sizeof *a); +} + + +struct ft_flow { + tag_type tags; + struct hmap_node node; + struct flow flow; + struct ft_dst *dsts; + size_t n_dsts; + struct ft_dst one_dst; +}; + +struct ft_flow *ftf_create(const struct flow *, + const struct ft_dst[], size_t n_dsts, + tag_type tags); +void ftf_destroy(struct ft_flow *); +void ftf_set_dsts(struct ft_flow *, const struct ft_dst[], size_t n_dsts); + +struct ft { + struct hmap flows; +}; + +struct ft *ft_create(void); +void ft_destroy(struct ft *); +struct ft_flow *ft_lookup(const struct ft *, const struct flow *); +void ft_remove(struct ft *, struct ft_flow *); +void ft_insert(struct ft *, struct ft_flow *); + +#endif /* vswitchd/flowtrack.h */ -- 2.30.2