X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=400b35326219ff7af715f6dab4856a9ab8146adc;hb=cb77e39b04904c8e8ae34d5bd6dd99c852018d52;hp=3cceb6be8947e7163fd4479ca9dc1273a2cfe7f6;hpb=abff858b5ad310a529d5a5ac2a230ee4ac9736db;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 3cceb6be8..400b35326 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -164,6 +164,8 @@ static void bundle_wait(struct ofbundle *); static void stp_run(struct ofproto_dpif *ofproto); static void stp_wait(struct ofproto_dpif *ofproto); +static bool ofbundle_includes_vlan(const struct ofbundle *, uint16_t vlan); + struct action_xlate_ctx { /* action_xlate_ctx_init() initializes these members. */ @@ -949,13 +951,8 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " "with unknown MAC", ofproto->up.name, port_num); } else { - int error = netdev_send(ofport->up.netdev, pkt); - if (error) { - VLOG_WARN_RL(&rl, "%s: sending BPDU on port %s failed (%s)", - ofproto->up.name, - netdev_get_name(ofport->up.netdev), - strerror(error)); - } + send_packet(ofproto_dpif_cast(ofport->up.ofproto), + ofport->odp_port, pkt); } } ofpbuf_delete(pkt); @@ -1040,7 +1037,7 @@ update_stp_port_state(struct ofport_dpif *ofport) ofport->stp_state = state; ofport->stp_state_entered = time_msec(); - if (fwd_change) { + if (fwd_change && ofport->bundle) { bundle_update(ofport->bundle); } @@ -1070,6 +1067,7 @@ set_stp_port(struct ofport *ofport_, if (sp) { ofport->stp_port = NULL; stp_port_disable(sp); + update_stp_port_state(ofport); } return 0; } else if (sp && stp_port_no(sp) != s->port_num @@ -1109,6 +1107,7 @@ get_stp_port_status(struct ofport *ofport_, s->state = stp_port_get_state(sp); s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000; s->role = stp_port_get_role(sp); + stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count); return 0; } @@ -1549,12 +1548,8 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size) pdu_size); memcpy(packet_pdu, pdu, pdu_size); - error = netdev_send(port->up.netdev, &packet); - if (error) { - VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed " - "(%s)", port->bundle->name, - netdev_get_name(port->up.netdev), strerror(error)); - } + send_packet(ofproto_dpif_cast(port->up.ofproto), port->odp_port, + &packet); ofpbuf_uninit(&packet); } else { VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface " @@ -1573,7 +1568,16 @@ bundle_send_learning_packets(struct ofbundle *bundle) error = n_packets = n_errors = 0; LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) { if (e->port.p != bundle) { - int ret = bond_send_learning_packet(bundle->bond, e->mac, e->vlan); + struct ofpbuf *learning_packet; + struct ofport_dpif *port; + int ret; + + learning_packet = bond_compose_learning_packet(bundle->bond, e->mac, + e->vlan, + (void **)&port); + ret = send_packet(ofproto_dpif_cast(port->up.ofproto), + port->odp_port, learning_packet); + ofpbuf_delete(learning_packet); if (ret) { error = ret; n_errors++; @@ -2884,10 +2888,10 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) * hash bucket.) */ vlan_tci = facet->flow.vlan_tci; NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) { + const struct ovs_action_push_vlan *vlan; struct ofport_dpif *port; switch (nl_attr_type(a)) { - const struct nlattr *nested; case OVS_ACTION_ATTR_OUTPUT: port = get_odp_port(ofproto, nl_attr_get_u32(a)); if (port && port->bundle && port->bundle->bond) { @@ -2896,20 +2900,13 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) } break; - case OVS_ACTION_ATTR_POP: - if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) { - vlan_tci = htons(0); - } + case OVS_ACTION_ATTR_POP_VLAN: + vlan_tci = htons(0); break; - case OVS_ACTION_ATTR_PUSH: - nested = nl_attr_get(a); - if (nl_attr_type(nested) == OVS_KEY_ATTR_8021Q) { - const struct ovs_key_8021q *q_key; - - q_key = nl_attr_get_unspec(nested, sizeof(*q_key)); - vlan_tci = q_key->q_tci; - } + case OVS_ACTION_ATTR_PUSH_VLAN: + vlan = nl_attr_get(a); + vlan_tci = vlan->vlan_tci; break; } } @@ -3247,7 +3244,7 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow, } cls = &ofproto->up.tables[table_id]; - if (flow->tos_frag & FLOW_FRAG_ANY + if (flow->nw_frag & FLOW_NW_FRAG_ANY && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) { /* For OFPC_NORMAL frag_handling, we must pretend that transport ports * are unavailable. */ @@ -3585,13 +3582,10 @@ fix_sflow_action(struct action_xlate_ctx *ctx) } static void -commit_action__(struct ofpbuf *odp_actions, - enum ovs_action_attr act_type, - enum ovs_key_attr key_type, - const void *key, size_t key_size) +commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, + const void *key, size_t key_size) { - size_t offset = nl_msg_start_nested(odp_actions, act_type); - + size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); nl_msg_put_unspec(odp_actions, key_type, key, key_size); nl_msg_end_nested(odp_actions, offset); } @@ -3605,8 +3599,8 @@ commit_set_tun_id_action(const struct flow *flow, struct flow *base, } base->tun_id = flow->tun_id; - commit_action__(odp_actions, OVS_ACTION_ATTR_SET, - OVS_KEY_ATTR_TUN_ID, &base->tun_id, sizeof(base->tun_id)); + commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID, + &base->tun_id, sizeof(base->tun_id)); } static void @@ -3626,8 +3620,8 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base, memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN); memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN); - commit_action__(odp_actions, OVS_ACTION_ATTR_SET, - OVS_KEY_ATTR_ETHERNET, ð_key, sizeof(eth_key)); + commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET, + ð_key, sizeof(eth_key)); } static void @@ -3640,18 +3634,16 @@ commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci) } if (base->vlan_tci & htons(VLAN_CFI)) { - nl_msg_put_u16(ctx->odp_actions, OVS_ACTION_ATTR_POP, - OVS_KEY_ATTR_8021Q); + nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN); } if (new_tci & htons(VLAN_CFI)) { - struct ovs_key_8021q q_key; - - q_key.q_tpid = htons(ETH_TYPE_VLAN); - q_key.q_tci = new_tci & ~htons(VLAN_CFI); + struct ovs_action_push_vlan vlan; - commit_action__(ctx->odp_actions, OVS_ACTION_ATTR_PUSH, - OVS_KEY_ATTR_8021Q, &q_key, sizeof(q_key)); + vlan.vlan_tpid = htons(ETH_TYPE_VLAN); + vlan.vlan_tci = new_tci; + nl_msg_put_unspec(ctx->odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, + &vlan, sizeof vlan); } base->vlan_tci = new_tci; } @@ -3660,7 +3652,6 @@ static void commit_set_nw_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions) { - int frag = base->tos_frag & FLOW_FRAG_MASK; struct ovs_key_ipv4 ipv4_key; if (base->dl_type != htons(ETH_TYPE_IP) || @@ -3670,22 +3661,23 @@ commit_set_nw_action(const struct flow *flow, struct flow *base, if (base->nw_src == flow->nw_src && base->nw_dst == flow->nw_dst && - base->tos_frag == flow->tos_frag) { + base->nw_tos == flow->nw_tos && + base->nw_ttl == flow->nw_ttl && + base->nw_frag == flow->nw_frag) { return; } - - memset(&ipv4_key, 0, sizeof(ipv4_key)); ipv4_key.ipv4_src = base->nw_src = flow->nw_src; ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst; ipv4_key.ipv4_proto = base->nw_proto; - ipv4_key.ipv4_tos = flow->tos_frag & IP_DSCP_MASK; - ipv4_key.ipv4_frag = (frag == 0 ? OVS_FRAG_TYPE_NONE - : frag == FLOW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST - : OVS_FRAG_TYPE_LATER); + ipv4_key.ipv4_tos = flow->nw_tos; + ipv4_key.ipv4_ttl = flow->nw_ttl; + ipv4_key.ipv4_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE + : base->nw_frag == FLOW_NW_FRAG_ANY + ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); - commit_action__(odp_actions, OVS_ACTION_ATTR_SET, - OVS_KEY_ATTR_IPV4, &ipv4_key, sizeof(ipv4_key)); + commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4, + &ipv4_key, sizeof(ipv4_key)); } static void @@ -3707,8 +3699,8 @@ commit_set_port_action(const struct flow *flow, struct flow *base, port_key.tcp_src = base->tp_src = flow->tp_src; port_key.tcp_dst = base->tp_dst = flow->tp_dst; - commit_action__(odp_actions, OVS_ACTION_ATTR_SET, - OVS_KEY_ATTR_TCP, &port_key, sizeof(port_key)); + commit_set_action(odp_actions, OVS_KEY_ATTR_TCP, + &port_key, sizeof(port_key)); } else if (flow->nw_proto == IPPROTO_UDP) { struct ovs_key_udp port_key; @@ -3716,8 +3708,8 @@ commit_set_port_action(const struct flow *flow, struct flow *base, port_key.udp_src = base->tp_src = flow->tp_src; port_key.udp_dst = base->tp_dst = flow->tp_dst; - commit_action__(odp_actions, OVS_ACTION_ATTR_SET, - OVS_KEY_ATTR_UDP, &port_key, sizeof(port_key)); + commit_set_action(odp_actions, OVS_KEY_ATTR_UDP, + &port_key, sizeof(port_key)); } } @@ -3730,9 +3722,8 @@ commit_set_priority_action(const struct flow *flow, struct flow *base, } base->priority = flow->priority; - commit_action__(odp_actions, OVS_ACTION_ATTR_SET, - OVS_KEY_ATTR_PRIORITY, &base->priority, - sizeof(base->priority)); + commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY, + &base->priority, sizeof(base->priority)); } static void @@ -4166,8 +4157,8 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPUTIL_OFPAT_SET_NW_TOS: - ctx->flow.tos_frag &= ~IP_DSCP_MASK; - ctx->flow.tos_frag |= ia->nw_tos.nw_tos & IP_DSCP_MASK; + ctx->flow.nw_tos &= ~IP_DSCP_MASK; + ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK; break; case OFPUTIL_OFPAT_SET_TP_SRC: @@ -4308,7 +4299,7 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->table_id = 0; ctx->exit = false; - if (ctx->flow.tos_frag & FLOW_FRAG_ANY) { + if (ctx->flow.nw_frag & FLOW_NW_FRAG_ANY) { switch (ctx->ofproto->up.frag_handling) { case OFPC_FRAG_NORMAL: /* We must pretend that transport ports are unavailable. */ @@ -4396,6 +4387,58 @@ input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid) } } +/* Checks whether a packet with the given 'vid' may ingress on 'in_bundle'. + * If so, returns true. Otherwise, returns false and, if 'warn' is true, logs + * a warning. + * + * 'vid' should be the VID obtained from the 802.1Q header that was received as + * part of a packet (specify 0 if there was no 802.1Q header), in the range + * 0...4095. */ +static bool +input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn) +{ + switch (in_bundle->vlan_mode) { + case PORT_VLAN_ACCESS: + if (vid) { + if (warn) { + 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 as VLAN " + "%"PRIu16" access port", + in_bundle->ofproto->up.name, vid, + in_bundle->name, in_bundle->vlan); + } + return false; + } + return true; + + case PORT_VLAN_NATIVE_UNTAGGED: + case PORT_VLAN_NATIVE_TAGGED: + if (!vid) { + /* Port must always carry its native VLAN. */ + return true; + } + /* Fall through. */ + case PORT_VLAN_TRUNK: + if (!ofbundle_includes_vlan(in_bundle, vid)) { + if (warn) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" packet " + "received on port %s not configured for trunking " + "VLAN %"PRIu16, + in_bundle->ofproto->up.name, vid, + in_bundle->name, vid); + } + return false; + } + return true; + + default: + NOT_REACHED(); + } + +} + /* Given 'vlan', the VLAN that a packet belongs to, and * 'out_bundle', a bundle on which the packet is to be output, returns the VID * that should be included in the 802.1Q header. (If the return value is 0, @@ -4607,7 +4650,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, } flow_vid = vlan_tci_to_vid(ctx->flow.vlan_tci); - while (mirrors) { + for (; mirrors; mirrors &= mirrors - 1) { struct ofmirror *m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1]; if (vlan_is_mirrored(m, vlan)) { struct dst dst; @@ -4622,6 +4665,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { if (ofbundle_includes_vlan(bundle, m->out_vlan) + && !bundle->mirror_out && set_dst(ctx, &dst, in_bundle, bundle)) { /* set_dst() got dst->vid from the input packet's VLAN, @@ -4641,7 +4685,6 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, } } } - mirrors &= mirrors - 1; } } @@ -4696,49 +4739,6 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, dst_set_free(&set); } -/* Returns the effective vlan of a packet, taking into account both the - * 802.1Q header and implicitly tagged ports. A value of 0 indicates that - * the packet is untagged and -1 indicates it has an invalid header and - * should be dropped. */ -static int -flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, - struct ofbundle *in_bundle, bool have_packet) -{ - int vlan = vlan_tci_to_vid(flow->vlan_tci); - if (vlan) { - if (in_bundle->vlan_mode == PORT_VLAN_ACCESS) { - /* Drop tagged packet on access port */ - if (have_packet) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " - "packet received on port %s configured with " - "implicit VLAN %"PRIu16, - ofproto->up.name, vlan, - in_bundle->name, in_bundle->vlan); - } - return -1; - } else if (ofbundle_includes_vlan(in_bundle, vlan)) { - return vlan; - } else { - /* Drop packets from a VLAN not member of the trunk */ - if (have_packet) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " - "packet received on port %s not configured for " - "trunking VLAN %d", - ofproto->up.name, vlan, in_bundle->name, vlan); - } - return -1; - } - } else { - if (in_bundle->vlan_mode != PORT_VLAN_TRUNK) { - return in_bundle->vlan; - } else { - return ofbundle_includes_vlan(in_bundle, 0) ? 0 : -1; - } - } -} - /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to * indicate this; newer upstream kernels use gratuitous ARP requests. */ @@ -4789,7 +4789,7 @@ update_learning_table(struct ofproto_dpif *ofproto, } } -/* Determines whether packets in 'flow' within 'br' should be forwarded or +/* Determines whether packets in 'flow' within 'ofproto' should be forwarded or * dropped. Returns true if they may be forwarded, false if they should be * dropped. * @@ -4799,12 +4799,12 @@ update_learning_table(struct ofproto_dpif *ofproto, * way, 'have_packet' only affects logging (there is no point in logging errors * during revalidation). * - * Sets '*in_portp' to the input port. This will be a null pointer if + * Sets '*in_bundlep' to the input bundle. This will be a null pointer if * flow->in_port does not designate a known input port (in which case * is_admissible() returns false). * * When returning true, sets '*vlanp' to the effective VLAN of the input - * packet, as returned by flow_get_vlan(). + * packet, as returned by input_vid_to_vlan(). * * May also add tags to '*tags', although the current implementation only does * so in one special case. @@ -4816,8 +4816,11 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, { struct ofport_dpif *in_port; struct ofbundle *in_bundle; + uint16_t vid; int vlan; + *vlanp = -1; + /* Find the port and bundle for the received packet. */ in_port = get_ofp_port(ofproto, flow->in_port); *in_bundlep = in_bundle = in_port ? in_port->bundle : NULL; @@ -4841,13 +4844,23 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, "port %"PRIu16, ofproto->up.name, flow->in_port); } - *vlanp = -1; return false; } - *vlanp = vlan = flow_get_vlan(ofproto, flow, in_bundle, have_packet); - if (vlan < 0) { + + if (flow->dl_type == htons(ETH_TYPE_VLAN) && + !(flow->vlan_tci & htons(VLAN_CFI))) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial " + "VLAN tag received on port %s", + ofproto->up.name, in_bundle->name); + return -1; + } + + vid = vlan_tci_to_vid(flow->vlan_tci); + if (!input_vid_is_valid(vid, in_bundle, have_packet)) { return false; } + *vlanp = vlan = input_vid_to_vlan(in_bundle, vid); /* Drop frames for reserved multicast addresses only if forward_bpdu * option is absent. */ @@ -5077,6 +5090,10 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); int error; + if (flow->in_port >= ofproto->max_ports && flow->in_port < OFPP_MAX) { + return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT); + } + error = validate_actions(ofp_actions, n_ofp_actions, flow, ofproto->max_ports); if (!error) { @@ -5115,6 +5132,22 @@ ofproto_dpif_lookup(const char *name) : NULL); } +static void +ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, + const char *args, void *aux OVS_UNUSED) +{ + const struct ofproto_dpif *ofproto; + + ofproto = ofproto_dpif_lookup(args); + if (!ofproto) { + unixctl_command_reply(conn, 501, "no such bridge"); + return; + } + mac_learning_flush(ofproto->ml); + + unixctl_command_reply(conn, 200, "table successfully flushed"); +} + static void ofproto_unixctl_fdb_show(struct unixctl_conn *conn, const char *args, void *aux OVS_UNUSED) @@ -5367,6 +5400,8 @@ ofproto_dpif_unixctl_init(void) unixctl_command_register("ofproto/trace", "bridge {tun_id in_port packet | odp_flow [-generate]}", ofproto_unixctl_trace, NULL); + unixctl_command_register("fdb/flush", "bridge", ofproto_unixctl_fdb_flush, + NULL); unixctl_command_register("fdb/show", "bridge", ofproto_unixctl_fdb_show, NULL); unixctl_command_register("ofproto/clog", "", ofproto_dpif_clog, NULL);