}
}
+struct output {
+ uint16_t vlan;
+ uint16_t dp_ifidx;
+};
+
+static void
+set_output(struct output *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
+ : ntohs(flow->dl_vlan));
+ p->dp_ifidx = out_port->ifaces[0]->dp_ifidx;
+}
+
+static void *
+add_action_header(struct ofpbuf *buf, uint16_t type)
+{
+ struct ofp_action_header *oah = ofpbuf_put_zeros(buf, sizeof *oah);
+ oah->type = htons(type);
+ oah->len = htons(sizeof *oah);
+ return oah;
+}
+
+static void
+add_output_action(struct ofpbuf *buf, uint16_t dp_ifidx)
+{
+ struct ofp_action_output *oao = add_action_header(buf, OFPAT_OUTPUT);
+ oao->port = htons(dp_ifidx);
+ oao->max_len = htons(0);
+}
+
+static void
+add_vlan_action(struct ofpbuf *buf, uint16_t old_vlan, uint16_t new_vlan)
+{
+ assert(old_vlan != new_vlan);
+ if (new_vlan == htons(OFP_VLAN_NONE)) {
+ add_action_header(buf, OFPAT_STRIP_VLAN);
+ } else {
+ struct ofp_action_vlan_vid *oavv
+ = add_action_header(buf, OFPAT_SET_VLAN_VID);
+ oavv->vlan_vid = htons(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)
+{
+ 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) {
+ /* Unicast. */
+ set_output(&outs[n_outs++], flow, in_port, out_port);
+ } else {
+ /* Flood. */
+ size_t i;
+
+ 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);
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ *actions = (char *) buf->data + actions_ofs;
+ *actions_len = buf->size - actions_ofs;
+}
+
+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,
+ const struct port *in_port, const struct port *out_port,
+ bool setup_flow)
+{
+ struct ofpbuf *fbuf = NULL;
+ struct ofpbuf *pbuf = NULL;
+ void *actions = NULL;
+ size_t actions_len = 0;
+
+ if (setup_flow) {
+ fbuf = make_add_flow(flow, buffer_id, br->flow_idle_time,
+ sizeof(struct ofp_action_header) * 4);
+ put_actions(br, flow, vlan, in_port, out_port, fbuf,
+ &actions, &actions_len);
+ update_openflow_length(fbuf);
+
+ /* 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 (!setup_flow || buffer_id == UINT32_MAX) {
+ struct ofp_packet_out *opo;
+
+ pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_len);
+ opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, pbuf);
+ opo->buffer_id = htonl(buffer_id);
+ opo->in_port = htons(in_ifidx);
+ opo->actions_len = htons(actions_len);
+ put_actions(br, flow, vlan, in_port, out_port, pbuf,
+ &actions, &actions_len);
+ if (buffer_id == UINT32_MAX) {
+ ofpbuf_put(pbuf, pkt_data, pkt_len);
+ }
+ update_openflow_length(pbuf);
+ queue_tx(br, pbuf);
+ }
+
+ if (fbuf) {
+ queue_tx(br, fbuf);
+ }
+ if (pbuf) {
+ queue_tx(br, pbuf);
+ }
+}
+
static void
process_packet_in(struct bridge *br, void *opi_)
{
struct ofp_packet_in *opi = opi_;
- uint16_t in_port = ntohs(opi->in_port);
- uint16_t out_port = OFPP_FLOOD;
+ uint16_t in_ifidx = ntohs(opi->in_port);
+ uint16_t out_ifidx;
- size_t pkt_len;
struct ofpbuf pkt;
struct flow flow;
+ struct iface *ifa;
+ struct port *in_port, *out_port;
+ int vlan;
if (check_ofp_message_array(&opi->header, OFPT_PACKET_IN,
offsetof(struct ofp_packet_in, data),
- 1, &pkt_len)) {
+ 1, &pkt.size)) {
return;
}
+ /* 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);
+ goto drop;
+ }
+ ifa = br->ifaces[in_ifidx];
+ in_port = ifa->port;
+
/* Extract flow data from 'opi' into 'flow'. */
pkt.data = opi->data;
- pkt.size = pkt_len;
- flow_extract(&pkt, in_port, &flow);
+ flow_extract(&pkt, in_ifidx, &flow);
+
+ /* Figure out what VLAN this packet belongs to.
+ *
+ * Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
+ * belongs to VLAN 0, so we should treat both cases identically. (In the
+ * 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);
+ 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);
+ goto drop;
+ }
+ vlan = in_port->vlan;
+ }
+ /* MAC learning. */
+ out_port = NULL;
if (br->ml) {
- if (mac_learning_learn(br->ml, flow.dl_src, in_port)) {
+ uint16_t out_port_idx;
+ /* XXX flush learning table entries when port indexes change due to
+ * reconfiguration */
+ if (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 %"PRIu16,
- br->name, ETH_ADDR_ARGS(flow.dl_src), in_port);
+ "port %s in VLAN %d",
+ br->name, ETH_ADDR_ARGS(flow.dl_src),
+ in_port->name, vlan);
+ }
+ out_port_idx = mac_learning_lookup(br->ml, flow.dl_dst, vlan);
+ if (out_port_idx < br->n_ports) {
+ out_port = br->ports[out_port_idx];
}
}
- if (br->ml) {
- out_port = mac_learning_lookup(br->ml, flow.dl_dst);
- }
-
- if (in_port == out_port) {
- /* Don't send out packets on their input ports. */
- goto drop_it;
- } else if (br->flow_idle_time >= 0
- && (!br->ml || out_port != OFPP_FLOOD)) {
- /* The output port is known, or we always flood everything, so add a
- * new flow. */
- queue_tx(br, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
- out_port, br->flow_idle_time));
-
- /* If the switch didn't buffer the packet, we need to send a copy. */
- if (ntohl(opi->buffer_id) == UINT32_MAX) {
- queue_tx(br,
- make_unbuffered_packet_out(&pkt, in_port, out_port));
- }
+ /* Send it out. */
+ out_ifidx = out_port ? out_port->ifaces[0]->dp_ifidx : OFPP_FLOOD;
+ if (in_port != out_port) {
+ /* Add a new flow. */
+ send_packets(br, &flow, ntohl(opi->buffer_id), vlan,
+ in_ifidx, pkt.data, pkt.size, in_port, out_port,
+ br->flow_idle_time >= 0);
} else {
- /* We don't know that MAC, or we don't set up flows. Send along the
- * packet without setting up a flow. */
- struct ofpbuf *b;
- if (ntohl(opi->buffer_id) == UINT32_MAX) {
- b = make_unbuffered_packet_out(&pkt, in_port, out_port);
- } else {
- b = make_buffered_packet_out(ntohl(opi->buffer_id),
- in_port, out_port);
- }
- queue_tx(br, b);
+ /* Don't send out packets on their input ports. */
+ goto drop;
}
return;
-drop_it:
- /* Set up a flow to drop packets, or just drop the packet if we don't set
- * up flows at all. */
+drop:
if (br->flow_idle_time >= 0) {
+ /* Set up a flow to drop packets. */
queue_tx(br, make_add_flow(&flow, ntohl(opi->buffer_id),
br->flow_idle_time, 0));
+ } else {
+ /* Just drop the packet, since we don't set up flows at all.
+ * XXX we should send a packet_out with no actions if buffer_id !=
+ * UINT32_MAX, to avoid clogging the kernel buffers. */
}
- return;
}
static void
static void
port_reconfigure(struct port *port)
{
+ bool bonded = cfg_has_section("bonding.%s", port->name);
struct svec old_ifaces, new_ifaces;
size_t i;
for (i = 0; i < port->n_ifaces; i++) {
svec_add(&old_ifaces, port->ifaces[i]->name);
}
- if (cfg_has_section("bonding.%s", port->name)) {
+ if (bonded) {
cfg_get_all_keys(&new_ifaces, "bonding.%s.slave", port->name);
} else {
svec_init(&new_ifaces);
iface_create(port, name);
}
}
+
+ /* Get VLAN tag. */
+ port->vlan = 0;
+ if (cfg_has("vlan.%s.tag", port->name)) {
+ if (!bonded) {
+ int vlan = cfg_get_int(0, "vlan.%s.tag", port->name);
+ if (port->vlan >= 0 && port->vlan <= 4095) {
+ VLOG_DBG("port %s: assigning VLAN tag %d",
+ port->name, port->vlan);
+ port->vlan = vlan;
+ } else {
+ VLOG_WARN("port %s: ignoring VLAN tag %d because it is not in "
+ "the valid range from 0 to 4095",
+ port->name, port->vlan);
+ }
+ } else {
+ /* It's possible that bonded, VLAN-tagged ports make sense but they
+ * are not worth implementing yet. */
+ VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
+ port->name);
+ }
+ }
}
static void