From: Jarno Rajahalme Date: Thu, 24 Oct 2013 20:19:30 +0000 (-0700) Subject: ofp-actions: Set-Field OF 1.0/1.1 compatibility. X-Git-Tag: sliver-openvswitch-2.0.90-1~6^2~35 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=a6fd70bb8944cc4144af287105b6b39a19442b04;p=sliver-openvswitch.git ofp-actions: Set-Field OF 1.0/1.1 compatibility. Output set field actions as standard OF1.0/1.1 set actions or to reg_load instructions, when a compatible set action(s) do not exist. Signed-off-by: Jarno Rajahalme Signed-off-by: Ben Pfaff --- diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 761cea5df..f166a73d9 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -857,6 +857,203 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) } } +/* Convert 'sf' to standard OpenFlow 1.1 actions, if we can, falling back + * to Nicira extensions if we must. + * + * We check only meta-flow types that can appear within set field actions and + * that have a mapping to compatible action types. These struct mf_field + * definitions have a defined OXM or NXM header value and specify the field as + * writable. */ +static void +set_field_to_openflow11(const struct ofpact_set_field *sf, + struct ofpbuf *openflow) +{ + switch ((int) sf->field->id) { + case MFF_VLAN_TCI: + /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping: + * + * If CFI=1, Add or modify VLAN VID & PCP. + * OpenFlow 1.1 set actions only apply if the packet + * already has VLAN tags. To be sure that is the case + * we have to push a VLAN header. As we do not support + * multiple layers of VLANs, this is a no-op, if a VLAN + * header already exists. This may backfire, however, + * when we start supporting multiple layers of VLANs. + * If CFI=0, strip VLAN header, if any. + */ + if (sf->value.be16 & htons(VLAN_CFI)) { + /* Push a VLAN tag, if one was not seen at action validation + * time. */ + if (!sf->flow_has_vlan) { + ofputil_put_OFPAT11_PUSH_VLAN(openflow)->ethertype + = htons(ETH_TYPE_VLAN_8021Q); + } + ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid + = sf->value.be16 & htons(VLAN_VID_MASK); + ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp + = vlan_tci_to_pcp(sf->value.be16); + } else { + /* If the flow did not match on vlan, we have no way of + * knowing if the vlan tag exists, so we must POP just to be + * sure. */ + ofputil_put_OFPAT11_POP_VLAN(openflow); + } + break; + + case MFF_VLAN_VID: + /* OXM VLAN_PCP to OpenFlow 1.1. + * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan + * tag. Clear the OFPVID_PRESENT bit. + */ + ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid + = sf->value.be16 & htons(VLAN_VID_MASK); + break; + + case MFF_VLAN_PCP: + /* OXM VLAN_PCP to OpenFlow 1.1. + * OXM_OF_VLAN_PCP only applies to existing vlan tag. */ + ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8; + break; + + case MFF_ETH_SRC: + memcpy(ofputil_put_OFPAT11_SET_DL_SRC(openflow)->dl_addr, + sf->value.mac, ETH_ADDR_LEN); + break; + + case MFF_ETH_DST: + memcpy(ofputil_put_OFPAT11_SET_DL_DST(openflow)->dl_addr, + sf->value.mac, ETH_ADDR_LEN); + break; + + case MFF_IPV4_SRC: + ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32; + break; + + case MFF_IPV4_DST: + ofputil_put_OFPAT11_SET_NW_DST(openflow)->nw_addr = sf->value.be32; + break; + + case MFF_IP_DSCP: + ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8; + break; + + case MFF_IP_DSCP_SHIFTED: + ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2; + break; + + case MFF_IP_ECN: + ofputil_put_OFPAT11_SET_NW_ECN(openflow)->nw_ecn = sf->value.u8; + break; + + case MFF_IP_TTL: + ofputil_put_OFPAT11_SET_NW_TTL(openflow)->nw_ttl = sf->value.u8; + break; + + case MFF_TCP_SRC: + case MFF_UDP_SRC: + case MFF_SCTP_SRC: + ofputil_put_OFPAT11_SET_TP_SRC(openflow)->tp_port = sf->value.be16; + break; + + case MFF_TCP_DST: + case MFF_UDP_DST: + case MFF_SCTP_DST: + ofputil_put_OFPAT11_SET_TP_DST(openflow)->tp_port = sf->value.be16; + break; + + case MFF_MPLS_TC: /* XXX */ + case MFF_MPLS_LABEL: /* XXX */ + default: + set_field_to_nxast(sf, openflow); + break; + } +} + +/* Convert 'sf' to standard OpenFlow 1.0 actions, if we can, falling back + * to Nicira extensions if we must. + * + * We check only meta-flow types that can appear within set field actions and + * that have a mapping to compatible action types. These struct mf_field + * definitions have a defined OXM or NXM header value and specify the field as + * writable. */ +static void +set_field_to_openflow10(const struct ofpact_set_field *sf, + struct ofpbuf *openflow) +{ + switch ((int) sf->field->id) { + case MFF_VLAN_TCI: + /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping: + * + * If CFI=1, Add or modify VLAN VID & PCP. + * If CFI=0, strip VLAN header, if any. + */ + if (sf->value.be16 & htons(VLAN_CFI)) { + ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid + = sf->value.be16 & htons(VLAN_VID_MASK); + ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp + = vlan_tci_to_pcp(sf->value.be16); + } else { + ofputil_put_OFPAT10_STRIP_VLAN(openflow); + } + break; + + case MFF_VLAN_VID: + /* OXM VLAN_VID to OpenFlow 1.0. + * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan + * tag. Clear the OFPVID_PRESENT bit. + */ + ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid + = sf->value.be16 & htons(VLAN_VID_MASK); + break; + + case MFF_VLAN_PCP: + /* OXM VLAN_PCP to OpenFlow 1.0. + * OXM_OF_VLAN_PCP only applies to existing vlan tag. */ + ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8; + break; + + case MFF_ETH_SRC: + memcpy(ofputil_put_OFPAT10_SET_DL_SRC(openflow)->dl_addr, + sf->value.mac, ETH_ADDR_LEN); + break; + + case MFF_ETH_DST: + memcpy(ofputil_put_OFPAT10_SET_DL_DST(openflow)->dl_addr, + sf->value.mac, ETH_ADDR_LEN); + break; + + case MFF_IPV4_SRC: + ofputil_put_OFPAT10_SET_NW_SRC(openflow)->nw_addr = sf->value.be32; + break; + + case MFF_IPV4_DST: + ofputil_put_OFPAT10_SET_NW_DST(openflow)->nw_addr = sf->value.be32; + break; + + case MFF_IP_DSCP: + ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8; + break; + + case MFF_IP_DSCP_SHIFTED: + ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2; + break; + + case MFF_TCP_SRC: + case MFF_UDP_SRC: + ofputil_put_OFPAT10_SET_TP_SRC(openflow)->tp_port = sf->value.be16; + break; + + case MFF_TCP_DST: + case MFF_UDP_DST: + ofputil_put_OFPAT10_SET_TP_DST(openflow)->tp_port = sf->value.be16; + break; + + default: + set_field_to_nxast(sf, openflow); + break; + } +} + static void set_field_to_openflow(const struct ofpact_set_field *sf, struct ofpbuf *openflow) @@ -865,8 +1062,12 @@ set_field_to_openflow(const struct ofpact_set_field *sf, if (oh->version >= OFP12_VERSION) { set_field_to_openflow12(sf, openflow); + } else if (oh->version == OFP11_VERSION) { + set_field_to_openflow11(sf, openflow); + } else if (oh->version == OFP10_VERSION) { + set_field_to_openflow10(sf, openflow); } else { - set_field_to_nxast(sf, openflow); + NOT_REACHED(); } } @@ -1789,6 +1990,15 @@ ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports, mf->name); return OFPERR_OFPBAC_MATCH_INCONSISTENT; } + /* Remember if we saw a vlan tag in the flow to aid translating to + * OpenFlow 1.1 if need be. */ + ofpact_get_SET_FIELD(a)->flow_has_vlan = + (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI); + if (mf->id == MFF_VLAN_TCI) { + /* The set field may add or remove the vlan tag, + * Mark the status temporarily. */ + flow->vlan_tci = ofpact_get_SET_FIELD(a)->value.be16; + } return 0; case OFPACT_STACK_PUSH: diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 0478a9b9f..2268a36a6 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -379,6 +379,7 @@ enum ofpact_mpls_position { struct ofpact_set_field { struct ofpact ofpact; const struct mf_field *field; + bool flow_has_vlan; /* VLAN present at action validation time. */ union mf_value value; }; diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 01ef8e77f..39f382ff3 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -154,7 +154,7 @@ OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output: OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 -OFPT_FLOW_MOD: ADD ip actions=load:0xa04034d->NXM_OF_IP_SRC[] +OFPT_FLOW_MOD: ADD ip actions=mod_nw_src:10.4.3.77 OFPT_FLOW_MOD: ADD sctp actions=drop OFPT_FLOW_MOD: ADD sctp actions=drop OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0 @@ -191,7 +191,7 @@ OFPT_FLOW_MOD (OF1.1): ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=se OFPT_FLOW_MOD (OF1.1): ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=mod_nw_ecn:2,output:1 OFPT_FLOW_MOD (OF1.1): ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 OFPT_FLOW_MOD (OF1.1): ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 -OFPT_FLOW_MOD (OF1.1): ADD table:255 ip actions=mod_nw_ttl:1,load:0xa04034d->NXM_OF_IP_SRC[] +OFPT_FLOW_MOD (OF1.1): ADD table:255 ip actions=mod_nw_ttl:1,mod_nw_src:10.4.3.77 OFPT_FLOW_MOD (OF1.1): ADD table:255 sctp actions=drop OFPT_FLOW_MOD (OF1.1): ADD table:255 sctp actions=drop OFPT_FLOW_MOD (OF1.1): ADD table:255 in_port=0 actions=resubmit:0