On Wed, Mar 31, 2021 at 03:59:40PM +0200, Eelco Chaudron wrote: > > > On 26 Mar 2021, at 7:21, Martin Varghese wrote: > > > From: Martin Varghese <[email protected]> > > > > The encap & decap actions are extended to support MPLS packet type. > > Encap & decap actions adds and removes MPLS header at start of the > > packet. > > Hi Martin, > > I’m trying to do some real-life testing, and I’m running into issues. This > might be me setting it up wrongly but just wanting to confirm… > > I’m sending an MPLS packet that contains an ARP packet into a physical port. > This is the packet: > > Frame 4: 64 bytes on wire (512 bits), 64 bytes captured (512 bits) > Encapsulation type: Ethernet (1) > [Protocols in frame: eth:ethertype:mpls:data] > Ethernet II, Src: 00:00:00_00:00:01 (00:00:00:00:00:01), Dst: > 00:00:00_00:00:02 (00:00:00:00:00:02) > Destination: 00:00:00_00:00:02 (00:00:00:00:00:02) > Address: 00:00:00_00:00:02 (00:00:00:00:00:02) > .... ..0. .... .... .... .... = LG bit: Globally unique address > (factory default) > .... ...0 .... .... .... .... = IG bit: Individual address (unicast) > Source: 00:00:00_00:00:01 (00:00:00:00:00:01) > Address: 00:00:00_00:00:01 (00:00:00:00:00:01) > .... ..0. .... .... .... .... = LG bit: Globally unique address > (factory default) > .... ...0 .... .... .... .... = IG bit: Individual address (unicast) > Type: MPLS label switched packet (0x8847) > MultiProtocol Label Switching Header, Label: 100, Exp: 0, S: 1, TTL: 64 > 0000 0000 0000 0110 0100 .... .... .... = MPLS Label: 100 > .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0 > .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1 > .... .... .... .... .... .... 0100 0000 = MPLS TTL: 64 > Data (46 bytes) > > 0000 ff ff ff ff ff ff 52 54 00 88 51 38 08 06 00 01 ......RT..Q8.... > 0010 08 00 06 04 00 01 52 54 00 88 51 38 01 01 01 65 ......RT..Q8...e > 0020 00 00 00 00 00 00 01 01 01 64 27 98 a0 47 .........d'..G > Data: > ffffffffffff525400885138080600010800060400015254008851380101016500000000? > > > I’m trying to use the following rules: > > ovs-ofctl del-flows ovs_pvp_br0 > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 > "priority=100,dl_type=0x8847,mpls_label=100 > actions=decap(),decap(packet_type(ns=0,type=0x806)),resubmit(,3)" > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 "table=3,priority=10 > actions=normal" > > With these, I expect the packet to be sent to vnet0, but it’s not. Actually, > the datapath rule looks odd, while the userspace rules seem to match: > > $ ovs-dpctl dump-flows > > recirc_id(0),in_port(1),eth(),eth_type(0x8847),mpls(label=100/0xfffff,tc=0/0,ttl=0/0x0,bos=1/1), > packets:13, bytes:1118, used:0.322s, > actions:pop_eth,pop_mpls(eth_type=0x806),recirc(0x19a) > recirc_id(0x19a),in_port(1),eth_type(0x0806), packets:13, bytes:884, > used:0.322s, actions:drop > > $ ovs-ofctl dump-flows ovs_pvp_br0 -O OpenFlow13 > cookie=0x0, duration=85.007s, table=0, n_packets=51, n_bytes=4386, > priority=100,mpls,mpls_label=100 > actions=decap(),decap(packet_type(ns=0,type=0x806)),resubmit(,3) > cookie=0x0, duration=84.990s, table=3, n_packets=51, n_bytes=3468, > priority=10 actions=NORMAL > The inner packet is ethernet. So the packet type should be (ns=0,type=0) ?
> > If I use the old way, doing pop_mpls, it works fine: > > > ovs-ofctl del-flows ovs_pvp_br0 > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 > "priority=100,dl_type=0x8847,mpls_label=100 > actions=pop_mpls:0x0806,resubmit(,3)" > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 "table=3,priority=10 > actions=normal" > > > I also noticed (despite the test example) to make encap work, I had to set > the ethernet MAC addresses, or else the packets were not getting out. > So something like: > > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 > "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),set_mpls_label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0 > The packets are not going out because you are sending the packet on a real nic and not on a virtual inerface (veth pair) ? > Maybe the test case can be made more realistic? Once I understand the > failure, I can continue with the review. > > > Cheers, > > Eelco > > > > > Signed-off-by: Martin Varghese <[email protected]> > > --- > > NEWS | 2 +- > > .../linux/compat/include/linux/openvswitch.h | 35 ++++++- > > include/openvswitch/ofp-ed-props.h | 18 ++++ > > lib/dpif-netdev.c | 1 + > > lib/dpif.c | 1 + > > lib/odp-execute.c | 12 +++ > > lib/odp-util.c | 58 +++++++++--- > > lib/ofp-actions.c | 5 + > > lib/ofp-ed-props.c | 91 +++++++++++++++++++ > > lib/ovs-actions.xml | 31 +++++-- > > lib/packets.c | 36 ++++++++ > > lib/packets.h | 2 + > > ofproto/ofproto-dpif-ipfix.c | 1 + > > ofproto/ofproto-dpif-sflow.c | 1 + > > ofproto/ofproto-dpif-xlate.c | 60 ++++++++++++ > > ofproto/ofproto-dpif.c | 39 ++++++++ > > ofproto/ofproto-dpif.h | 5 +- > > tests/system-traffic.at | 36 ++++++++ > > 18 files changed, 410 insertions(+), 24 deletions(-) > > > > diff --git a/NEWS b/NEWS > > index 95cf922aa..4bf4e9e7b 100644 > > --- a/NEWS > > +++ b/NEWS > > @@ -120,7 +120,7 @@ v2.14.0 - 17 Aug 2020 > > - GTP-U Tunnel Protocol > > * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype. > > * Only support for userspace datapath. > > - > > + - Encap & Decap action support for MPLS packet type. > > > > v2.13.0 - 14 Feb 2020 > > --------------------- > > diff --git a/datapath/linux/compat/include/linux/openvswitch.h > > b/datapath/linux/compat/include/linux/openvswitch.h > > index 875de2025..8feea7dd4 100644 > > --- a/datapath/linux/compat/include/linux/openvswitch.h > > +++ b/datapath/linux/compat/include/linux/openvswitch.h > > @@ -810,8 +810,32 @@ struct ovs_action_push_tnl { > > }; > > #endif > > > > -/** > > - * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. > > +/* struct ovs_action_add_mpls - %OVS_ACTION_ATTR_ADD_MPLS action > > + * argument. > > + * @mpls_lse: MPLS label stack entry to push. > > + * @mpls_ethertype: Ethertype to set in the encapsulating ethernet > > frame. > > + * @tun_flags: MPLS tunnel attributes. > > + * > > + * The only values @mpls_ethertype should ever be given are > > %ETH_P_MPLS_UC and > > + * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are > > rejected. > > + */ > > +struct ovs_action_add_mpls { > > + __be32 mpls_lse; > > + __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */ > > + __u16 tun_flags; > > +}; > > + > > +#define OVS_MPLS_L3_TUNNEL_FLAG_MASK (1 << 0) /* Flag to specify the > > place of > > + * insertion of MPLS header. > > + * When false, the MPLS header > > + * will be inserted at the start > > + * of the packet. > > + * When true, the MPLS header > > + * will be inserted at the start > > + * of the l3 header. > > + */ > > + > > +/* enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. > > * @OVS_CT_ATTR_COMMIT: If present, commits the connection to the > > conntrack > > * table. This allows future packets for the same connection to be > > identified > > * as 'established' or 'related'. The flow key for the current packet > > will > > @@ -1001,7 +1025,11 @@ struct check_pkt_len_arg { > > * @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and execute > > a set > > * of actions if greater than the specified packet length, else execute > > * another set of actions. > > - * @OVS_ACTION_ATTR_DROP: Explicit drop action. > > + * @OVS_ACTION_ATTR_ADD_MPLS: Push a new MPLS label stack entry at the > > + * start of the packet or at the start of the l3 header depending on > > the value > > + * of l3 tunnel flag in the tun_flags field of OVS_ACTION_ATTR_ADD_MPLS > > + * argument. > > + * @OVS_ACTION_ATTR_DROP: Explicit drop action. > > */ > > > > enum ovs_action_attr { > > @@ -1030,6 +1058,7 @@ enum ovs_action_attr { > > OVS_ACTION_ATTR_METER, /* u32 meter number. */ > > OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ > > OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ > > + OVS_ACTION_ATTR_ADD_MPLS, /* struct ovs_action_add_mpls. */ > > > > #ifndef __KERNEL__ > > OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ > > diff --git a/include/openvswitch/ofp-ed-props.h > > b/include/openvswitch/ofp-ed-props.h > > index 306c6fe73..c85f3c283 100644 > > --- a/include/openvswitch/ofp-ed-props.h > > +++ b/include/openvswitch/ofp-ed-props.h > > @@ -46,6 +46,11 @@ enum ofp_ed_nsh_prop_type { > > OFPPPT_PROP_NSH_TLV = 2, /* property TLV in NSH */ > > }; > > > > +enum ofp_ed_mpls_prop_type { > > + OFPPPT_PROP_MPLS_NONE = 0, /* unused */ > > + OFPPPT_PROP_MPLS_ETHERTYPE = 1, /* MPLS Ethertype */ > > +}; > > + > > /* > > * External representation of encap/decap properties. > > * These must be padded to a multiple of 8 bytes. > > @@ -72,6 +77,13 @@ struct ofp_ed_prop_nsh_tlv { > > uint8_t data[0]; > > }; > > > > +struct ofp_ed_prop_mpls_ethertype { > > + struct ofp_ed_prop_header header; > > + uint16_t ether_type; /* MPLS ethertype .*/ > > + uint8_t pad[2]; /* Padding to 8 bytes. */ > > +}; > > + > > + > > /* > > * Internal representation of encap/decap properties > > */ > > @@ -96,6 +108,12 @@ struct ofpact_ed_prop_nsh_tlv { > > /* tlv_len octets of metadata value, padded to a multiple of 8 > > bytes. */ > > uint8_t data[0]; > > }; > > + > > +struct ofpact_ed_prop_mpls_ethertype { > > + struct ofpact_ed_prop header; > > + uint16_t ether_type; /* MPLS ethertype .*/ > > + uint8_t pad[2]; /* Padding to 8 bytes. */ > > +}; > > enum ofperr decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop, > > struct ofpbuf *out, size_t *remaining); > > enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop, > > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c > > index 94cc9b80c..bbdea5603 100644 > > --- a/lib/dpif-netdev.c > > +++ b/lib/dpif-netdev.c > > @@ -8044,6 +8044,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > > *packets_, > > case OVS_ACTION_ATTR_CT_CLEAR: > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: > > case OVS_ACTION_ATTR_DROP: > > + case OVS_ACTION_ATTR_ADD_MPLS: > > case __OVS_ACTION_ATTR_MAX: > > OVS_NOT_REACHED(); > > } > > diff --git a/lib/dpif.c b/lib/dpif.c > > index 56d0b4a65..bbd1296e3 100644 > > --- a/lib/dpif.c > > +++ b/lib/dpif.c > > @@ -1273,6 +1273,7 @@ dpif_execute_helper_cb(void *aux_, struct > > dp_packet_batch *packets_, > > case OVS_ACTION_ATTR_UNSPEC: > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: > > case OVS_ACTION_ATTR_DROP: > > + case OVS_ACTION_ATTR_ADD_MPLS: > > case __OVS_ACTION_ATTR_MAX: > > OVS_NOT_REACHED(); > > } > > diff --git a/lib/odp-execute.c b/lib/odp-execute.c > > index 6eeda2a61..2f4cdd92c 100644 > > --- a/lib/odp-execute.c > > +++ b/lib/odp-execute.c > > @@ -819,6 +819,7 @@ requires_datapath_assistance(const struct nlattr *a) > > case OVS_ACTION_ATTR_POP_NSH: > > case OVS_ACTION_ATTR_CT_CLEAR: > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: > > + case OVS_ACTION_ATTR_ADD_MPLS: > > case OVS_ACTION_ATTR_DROP: > > return false; > > > > @@ -1061,6 +1062,17 @@ odp_execute_actions(void *dp, struct > > dp_packet_batch *batch, bool steal, > > } > > break; > > > > + case OVS_ACTION_ATTR_ADD_MPLS: { > > + const struct ovs_action_add_mpls *mpls = nl_attr_get(a); > > + bool l3_flag = mpls->tun_flags & > > OVS_MPLS_L3_TUNNEL_FLAG_MASK; > > + > > + DP_PACKET_BATCH_FOR_EACH (i, packet, batch) { > > + add_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse, > > + l3_flag); > > + } > > + break; > > + } > > + > > case OVS_ACTION_ATTR_DROP:{ > > const enum xlate_error *drop_reason = nl_attr_get(a); > > > > diff --git a/lib/odp-util.c b/lib/odp-util.c > > index a8598d52a..f24e16d08 100644 > > --- a/lib/odp-util.c > > +++ b/lib/odp-util.c > > @@ -142,6 +142,8 @@ odp_action_len(uint16_t type) > > case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE; > > case OVS_ACTION_ATTR_POP_NSH: return 0; > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: return ATTR_LEN_VARIABLE; > > + case OVS_ACTION_ATTR_ADD_MPLS: > > + return sizeof(struct ovs_action_add_mpls); > > case OVS_ACTION_ATTR_DROP: return sizeof(uint32_t); > > > > case OVS_ACTION_ATTR_UNSPEC: > > @@ -1254,6 +1256,14 @@ format_odp_action(struct ds *ds, const struct > > nlattr *a, > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: > > format_odp_check_pkt_len_action(ds, a, portno_names); > > break; > > + case OVS_ACTION_ATTR_ADD_MPLS: { > > + const struct ovs_action_push_mpls *mpls = nl_attr_get(a); > > + ds_put_cstr(ds, "add_mpls("); > > + format_mpls_lse(ds, mpls->mpls_lse); > > + ds_put_format(ds, ",eth_type=0x%"PRIx16")", > > + ntohs(mpls->mpls_ethertype)); > > + break; > > + } > > case OVS_ACTION_ATTR_DROP: > > ds_put_cstr(ds, "drop"); > > break; > > @@ -7876,7 +7886,8 @@ commit_vlan_action(const struct flow* flow, struct > > flow *base, > > /* Wildcarding already done at action translation time. */ > > static void > > commit_mpls_action(const struct flow *flow, struct flow *base, > > - struct ofpbuf *odp_actions) > > + struct ofpbuf *odp_actions, bool pending_encap, > > + bool pending_decap) > > { > > int base_n = flow_count_mpls_labels(base, NULL); > > int flow_n = flow_count_mpls_labels(flow, NULL); > > @@ -7913,7 +7924,11 @@ commit_mpls_action(const struct flow *flow, > > struct flow *base, > > if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) { > > dl_type = htons(ETH_TYPE_MPLS); > > } else { > > - dl_type = flow->dl_type; > > + if ((flow->packet_type == PT_ETH) && pending_decap) { > > + dl_type = htons(ETH_TYPE_TEB); > > + } else { > > + dl_type = flow->dl_type; > > + } > > } > > nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, > > dl_type); > > ovs_assert(flow_pop_mpls(base, base_n, flow->dl_type, > > NULL)); > > @@ -7924,18 +7939,29 @@ commit_mpls_action(const struct flow *flow, > > struct flow *base, > > /* If, after the above popping and setting, there are more LSEs in > > flow > > * than base then some LSEs need to be pushed. */ > > while (base_n < flow_n) { > > - struct ovs_action_push_mpls *mpls; > > > > - mpls = nl_msg_put_unspec_zero(odp_actions, > > - OVS_ACTION_ATTR_PUSH_MPLS, > > - sizeof *mpls); > > - mpls->mpls_ethertype = flow->dl_type; > > - mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; > > + if (pending_encap) { > > + struct ovs_action_add_mpls *mpls; > > + > > + mpls = nl_msg_put_unspec_zero(odp_actions, > > + OVS_ACTION_ATTR_ADD_MPLS, > > + sizeof *mpls); > > + mpls->mpls_ethertype = flow->dl_type; > > + mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; > > + } else { > > + struct ovs_action_push_mpls *mpls; > > + > > + mpls = nl_msg_put_unspec_zero(odp_actions, > > + OVS_ACTION_ATTR_PUSH_MPLS, > > + sizeof *mpls); > > + mpls->mpls_ethertype = flow->dl_type; > > + mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; > > + } > > /* Update base flow's MPLS stack, but do not clear L3. We need > > the L3 > > * headers if the flow is restored later due to returning from > > a patch > > * port or group bucket. */ > > - flow_push_mpls(base, base_n, mpls->mpls_ethertype, NULL, > > false); > > - flow_set_mpls_lse(base, 0, mpls->mpls_lse); > > + flow_push_mpls(base, base_n, flow->dl_type, NULL, false); > > + flow_set_mpls_lse(base, 0, flow->mpls_lse[flow_n - base_n - > > 1]); > > base_n++; > > } > > } > > @@ -8586,6 +8612,10 @@ commit_encap_decap_action(const struct flow > > *flow, > > memcpy(&base_flow->dl_dst, &flow->dl_dst, > > sizeof(*flow) - offsetof(struct flow, dl_dst)); > > break; > > + case PT_MPLS: > > + commit_mpls_action(flow, base_flow, odp_actions, > > pending_encap, > > + pending_decap); > > + break; > > default: > > /* Only the above protocols are supported for encap. > > * The check is done at action translation. */ > > @@ -8608,6 +8638,10 @@ commit_encap_decap_action(const struct flow > > *flow, > > /* pop_nsh. */ > > odp_put_pop_nsh_action(odp_actions); > > break; > > + case PT_MPLS: > > + commit_mpls_action(flow, base_flow, odp_actions, > > pending_encap, > > + pending_decap); > > + break; > > default: > > /* Checks are done during translation. */ > > OVS_NOT_REACHED(); > > @@ -8653,7 +8687,7 @@ commit_odp_actions(const struct flow *flow, struct > > flow *base, > > /* Make packet a non-MPLS packet before committing L3/4 actions, > > * which would otherwise do nothing. */ > > if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) > > { > > - commit_mpls_action(flow, base, odp_actions); > > + commit_mpls_action(flow, base, odp_actions, false, false); > > mpls_done = true; > > } > > commit_set_nsh_action(flow, base, odp_actions, wc, use_masked); > > @@ -8661,7 +8695,7 @@ commit_odp_actions(const struct flow *flow, struct > > flow *base, > > commit_set_port_action(flow, base, odp_actions, wc, use_masked); > > slow2 = commit_set_icmp_action(flow, base, odp_actions, wc); > > if (!mpls_done) { > > - commit_mpls_action(flow, base, odp_actions); > > + commit_mpls_action(flow, base, odp_actions, false, false); > > } > > commit_vlan_action(flow, base, odp_actions, wc); > > commit_set_priority_action(flow, base, odp_actions, wc, > > use_masked); > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c > > index 0342a228b..28a12a569 100644 > > --- a/lib/ofp-actions.c > > +++ b/lib/ofp-actions.c > > @@ -4441,6 +4441,7 @@ decode_NXAST_RAW_ENCAP(const struct > > nx_action_encap *nae, > > switch (ntohl(nae->new_pkt_type)) { > > case PT_ETH: > > case PT_NSH: > > + case PT_MPLS: > > /* Add supported encap header types here. */ > > break; > > default: > > @@ -4492,6 +4493,8 @@ parse_encap_header(const char *hdr, ovs_be32 > > *packet_type) > > *packet_type = htonl(PT_ETH); > > } else if (strcmp(hdr, "nsh") == 0) { > > *packet_type = htonl(PT_NSH); > > + } else if (strcmp(hdr, "mpls") == 0) { > > + *packet_type = htonl(PT_MPLS); > > } else { > > return false; > > } > > @@ -4573,6 +4576,8 @@ format_encap_pkt_type(const ovs_be32 pkt_type) > > return "ethernet"; > > case PT_NSH: > > return "nsh"; > > + case PT_MPLS: > > + return "mpls"; > > default: > > return "UNKNOWN"; > > } > > diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c > > index 02a9235d5..fc261e4c6 100644 > > --- a/lib/ofp-ed-props.c > > +++ b/lib/ofp-ed-props.c > > @@ -79,6 +79,27 @@ decode_ed_prop(const struct ofp_ed_prop_header > > **ofp_prop, > > } > > break; > > } > > + case OFPPPC_MPLS: { > > + switch (prop_type) { > > + case OFPPPT_PROP_MPLS_ETHERTYPE: { > > + struct ofp_ed_prop_mpls_ethertype *opnmt = > > + ALIGNED_CAST(struct ofp_ed_prop_mpls_ethertype *, > > *ofp_prop); > > + if (len > sizeof(*opnmt) || len > *remaining) { > > + return OFPERR_NXBAC_BAD_ED_PROP; > > + } > > + struct ofpact_ed_prop_mpls_ethertype *pnmt = > > + ofpbuf_put_uninit(out, sizeof(*pnmt)); > > + pnmt->header.prop_class = prop_class; > > + pnmt->header.type = prop_type; > > + pnmt->header.len = len; > > + pnmt->ether_type = opnmt->ether_type; > > + break; > > + } > > + default: > > + return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > + } > > + break; > > + } > > default: > > return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > } > > @@ -134,6 +155,27 @@ encode_ed_prop(const struct ofpact_ed_prop **prop, > > } > > break; > > } > > + case OFPPPC_MPLS: { > > + switch ((*prop)->type) { > > + case OFPPPT_PROP_MPLS_ETHERTYPE: { > > + struct ofpact_ed_prop_mpls_ethertype *pnmt = > > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, > > *prop); > > + struct ofp_ed_prop_mpls_ethertype *opnmt = > > + ofpbuf_put_uninit(out, sizeof(*opnmt)); > > + opnmt->header.prop_class = htons((*prop)->prop_class); > > + opnmt->header.type = (*prop)->type; > > + opnmt->header.len = > > + offsetof(struct ofpact_ed_prop_mpls_ethertype, > > pad); > > + opnmt->ether_type = pnmt->ether_type; > > + prop_len = sizeof(*pnmt); > > + break; > > + > > + } > > + default: > > + return OFPERR_OFPBAC_BAD_ARGUMENT; > > + } > > + break; > > + } > > default: > > return OFPERR_OFPBAC_BAD_ARGUMENT; > > } > > @@ -181,6 +223,13 @@ parse_ed_prop_type(uint16_t prop_class, > > } else { > > return false; > > } > > + case OFPPPC_MPLS: > > + if (!strcmp(str, "ether_type")) { > > + *type = OFPPPT_PROP_MPLS_ETHERTYPE; > > + return true; > > + } else { > > + return false; > > + } > > default: > > return false; > > } > > @@ -259,6 +308,28 @@ parse_ed_prop_value(uint16_t prop_class, uint8_t > > prop_type OVS_UNUSED, > > OVS_NOT_REACHED(); > > } > > break; > > + case OFPPPC_MPLS: > > + switch (prop_type) { > > + case OFPPPT_PROP_MPLS_ETHERTYPE: { > > + uint16_t ethertype; > > + error = str_to_u16(value, "ether_type", ðertype); > > + if (error != NULL) { > > + return error; > > + } > > + struct ofpact_ed_prop_mpls_ethertype *pnmt = > > + ofpbuf_put_uninit(out, sizeof(*pnmt)); > > + pnmt->header.prop_class = prop_class; > > + pnmt->header.type = prop_type; > > + pnmt->header.len = > > + offsetof(struct ofpact_ed_prop_mpls_ethertype, > > pad); > > + pnmt->ether_type = ethertype; > > + > > + break; > > + } > > + default: > > + break; > > + } > > + break; > > default: > > /* Unsupported property classes rejected before. */ > > OVS_NOT_REACHED(); > > @@ -300,6 +371,14 @@ format_ed_prop_type(const struct ofpact_ed_prop > > *prop) > > OVS_NOT_REACHED(); > > } > > break; > > + case OFPPPC_MPLS: > > + switch (prop->type) { > > + case OFPPPT_PROP_MPLS_ETHERTYPE: > > + return "ether_type"; > > + default: > > + OVS_NOT_REACHED(); > > + } > > + break; > > default: > > OVS_NOT_REACHED(); > > } > > @@ -332,6 +411,18 @@ format_ed_prop(struct ds *s OVS_UNUSED, > > default: > > OVS_NOT_REACHED(); > > } > > + case OFPPPC_MPLS: > > + switch (prop->type) { > > + case OFPPPT_PROP_MPLS_ETHERTYPE: { > > + struct ofpact_ed_prop_mpls_ethertype *pnmt = > > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, > > prop); > > + ds_put_format(s, "%s=%d", format_ed_prop_type(prop), > > + pnmt->ether_type); > > + return; > > + } > > + default: > > + OVS_NOT_REACHED(); > > + } > > default: > > OVS_NOT_REACHED(); > > } > > diff --git a/lib/ovs-actions.xml b/lib/ovs-actions.xml > > index a2778de4b..e97f818d9 100644 > > --- a/lib/ovs-actions.xml > > +++ b/lib/ovs-actions.xml > > @@ -265,13 +265,13 @@ > > </p> > > > > <p> > > - When a <code>decap</code> action decapsulates a packet, Open > > vSwitch > > - raises this error if it does not support the type of inner > > packet. > > - <code>decap</code> of an Ethernet header raises this error if a > > VLAN > > - header is present, <code>decap</code> of a NSH packet raises > > this error > > - if the NSH inner packet is not Ethernet, IPv4, IPv6, or NSH, > > and > > - <code>decap</code> of other types of packets is unsupported and > > also > > - raises this error. > > + The <code>decap</code> action is supported only for packet > > types > > + ethernet, NSH and MPLS. Openvswitch raises this error for other > > + packet types. When a <code>decap</code> action decapsulates a > > packet, > > + Open vSwitch raises this error if it does not support the type > > of inner > > + packet. <code>decap</code> of an Ethernet header raises this > > error if a > > + VLAN header is present, <code>decap</code> of a NSH packet > > raises this > > + error if the NSH inner packet is not Ethernet, IPv4, IPv6, or > > NSH. > > </p> > > > > <p> > > @@ -1097,6 +1097,8 @@ for <var>i</var> in [1,<var>n_members</var>]: > > <h2>The <code>encap</code> action</h2> > > > > <syntax><code>encap(nsh(</code>[<code>md_type=<var>md_type</var></code>]<code>, > > > > </code>[<code>tlv(<var>class</var>,<var>type</var>,<var>value</var>)</code>]...<code>))</code></syntax> > > <syntax><code>encap(ethernet)</code></syntax> > > + > > <syntax><code>encap(mpls(ether_type=<var>ether_type</var>))</code> > > + </syntax> > > > > <p> > > The <code>encap</code> action encapsulates a packet with a > > specified > > @@ -1135,6 +1137,12 @@ for <var>i</var> in [1,<var>n_members</var>]: > > source and destination are initially zeroed. > > </p> > > > > + <p> > > + The <code>encap(mpls(ethertype=....))</code> variant > > encapsulates an > > + ethernet or L3 packet with a MPLS header. The > > <var>ethertype</var> > > + could be MPLS unicast (0x8847) or multicast (0x8848) > > ethertypes. > > + </p> > > + > > <conformance> > > This action is an Open vSwitch extension to OpenFlow 1.3 and > > later, > > introduced in Open vSwitch 2.8. > > @@ -1144,6 +1152,9 @@ for <var>i</var> in [1,<var>n_members</var>]: > > <action name="DECAP"> > > <h2>The <code>decap</code> action</h2> > > <syntax><code>decap</code></syntax> > > + <syntax><code>decap(packet_type(ns=<var>name_space</var>, > > + type=<var>ethertype</var>))</code></syntax> for decapsulating > > MPLS > > + packets. > > > > <p> > > Removes an outermost encapsulation from the packet: > > @@ -1164,6 +1175,12 @@ for <var>i</var> in [1,<var>n_members</var>]: > > packet type errors. > > </li> > > > > + <li> > > + Otherwise, if the packet is a MPLS packet, removes the MPLS > > header > > + and classifies the inner packet as mentioned in the packet > > type > > + argument of the decap. > > + </li> > > + > > <li> > > Otherwise, raises an unsupported packet type error. > > </li> > > diff --git a/lib/packets.c b/lib/packets.c > > index 4a7643c5d..5e3c3900f 100644 > > --- a/lib/packets.c > > +++ b/lib/packets.c > > @@ -418,6 +418,38 @@ push_mpls(struct dp_packet *packet, ovs_be16 > > ethtype, ovs_be32 lse) > > pkt_metadata_init_conn(&packet->md); > > } > > > > +void > > +add_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse, bool > > l3) > > +{ > > + char * header; > > + > > + if (!eth_type_mpls(ethtype)) { > > + return; > > + } > > + > > + if (!l3) { > > + header = dp_packet_push_uninit(packet, MPLS_HLEN); > > + memcpy(header, &lse, sizeof lse); > > + packet->l2_5_ofs = 0; > > + packet->packet_type = htonl(PT_MPLS); > > + } else { > > + size_t len; > > + > > + if (!is_mpls(packet)) { > > + /* Set MPLS label stack offset. */ > > + packet->l2_5_ofs = packet->l3_ofs; > > + } > > + set_ethertype(packet, ethtype); > > + > > + /* Push new MPLS shim header onto packet. */ > > + len = packet->l2_5_ofs; > > + header = dp_packet_resize_l2_5(packet, MPLS_HLEN); > > + memmove(header, header + MPLS_HLEN, len); > > + memcpy(header + len, &lse, sizeof lse); > > + } > > + pkt_metadata_init_conn(&packet->md); > > +} > > + > > /* If 'packet' is an MPLS packet, removes its outermost MPLS label > > stack entry. > > * If the label that was removed was the only MPLS label, changes > > 'packet''s > > * Ethertype to 'ethtype' (which ordinarily should not be an MPLS > > @@ -429,6 +461,10 @@ pop_mpls(struct dp_packet *packet, ovs_be16 > > ethtype) > > struct mpls_hdr *mh = dp_packet_l2_5(packet); > > size_t len = packet->l2_5_ofs; > > > > + if (ethtype == htons(ETH_TYPE_TEB)) { > > + packet->packet_type = htonl(PT_ETH); > > + } > > + > > set_ethertype(packet, ethtype); > > if (get_16aligned_be32(&mh->mpls_lse) & htonl(MPLS_BOS_MASK)) { > > dp_packet_set_l2_5(packet, NULL); > > diff --git a/lib/packets.h b/lib/packets.h > > index 481bc22fa..3f5862e08 100644 > > --- a/lib/packets.h > > +++ b/lib/packets.h > > @@ -350,6 +350,8 @@ void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 > > label); > > void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos); > > ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, > > ovs_be32 label); > > +void add_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse, > > + bool l3_flag); > > > > /* Example: > > * > > diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c > > index 796eb6f88..9280e008e 100644 > > --- a/ofproto/ofproto-dpif-ipfix.c > > +++ b/ofproto/ofproto-dpif-ipfix.c > > @@ -3018,6 +3018,7 @@ dpif_ipfix_read_actions(const struct flow *flow, > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: > > case OVS_ACTION_ATTR_UNSPEC: > > case OVS_ACTION_ATTR_DROP: > > + case OVS_ACTION_ATTR_ADD_MPLS: > > case __OVS_ACTION_ATTR_MAX: > > default: > > break; > > diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c > > index fdcb9eabb..ca46a9bc4 100644 > > --- a/ofproto/ofproto-dpif-sflow.c > > +++ b/ofproto/ofproto-dpif-sflow.c > > @@ -1226,6 +1226,7 @@ dpif_sflow_read_actions(const struct flow *flow, > > case OVS_ACTION_ATTR_UNSPEC: > > case OVS_ACTION_ATTR_CHECK_PKT_LEN: > > case OVS_ACTION_ATTR_DROP: > > + case OVS_ACTION_ATTR_ADD_MPLS: > > case __OVS_ACTION_ATTR_MAX: > > default: > > break; > > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > > index 7108c8a30..a97534233 100644 > > --- a/ofproto/ofproto-dpif-xlate.c > > +++ b/ofproto/ofproto-dpif-xlate.c > > @@ -6381,6 +6381,45 @@ rewrite_flow_encap_ethernet(struct xlate_ctx > > *ctx, > > ctx->error = XLATE_UNSUPPORTED_PACKET_TYPE; > > } > > } > > +static void > > +rewrite_flow_encap_mpls(struct xlate_ctx *ctx, > > + const struct ofpact_encap *encap, > > + struct flow *flow, > > + struct flow_wildcards *wc) > > +{ > > + int n; > > + uint32_t i; > > + uint16_t ether_type; > > + const char *ptr = (char *) encap->props; > > + > > + for (i = 0; i < encap->n_props; i++) { > > + struct ofpact_ed_prop *prop_ptr = > > + ALIGNED_CAST(struct ofpact_ed_prop *, ptr); > > + if (prop_ptr->prop_class == OFPPPC_MPLS) { > > + switch (prop_ptr->type) { > > + case OFPPPT_PROP_MPLS_ETHERTYPE: { > > + struct ofpact_ed_prop_mpls_ethertype > > *prop_ether_type = > > + ALIGNED_CAST(struct > > ofpact_ed_prop_mpls_ethertype *, > > + prop_ptr); > > + ether_type = prop_ether_type->ether_type; > > + break; > > + } > > + } > > + } > > + } > > + > > + wc->masks.packet_type = OVS_BE32_MAX; > > + if (flow->packet_type != htonl(PT_MPLS)) { > > + memset(&ctx->wc->masks.mpls_lse, 0x0, > > + sizeof *wc->masks.mpls_lse * FLOW_MAX_MPLS_LABELS); > > + memset(&flow->mpls_lse, 0x0, sizeof *flow->mpls_lse * > > + FLOW_MAX_MPLS_LABELS); > > + } > > + flow->packet_type = htonl(PT_MPLS); > > + n = flow_count_mpls_labels(flow, ctx->wc); > > + flow_push_mpls(flow, n, htons(ether_type), ctx->wc, true); > > +} > > + > > > > /* For an MD2 NSH header returns a pointer to an ofpbuf with the > > encoded > > * MD2 TLVs provided as encap properties to the encap operation. This > > @@ -6513,6 +6552,12 @@ xlate_generic_encap_action(struct xlate_ctx *ctx, > > case PT_NSH: > > encap_data = rewrite_flow_push_nsh(ctx, encap, flow, wc); > > break; > > + case PT_MPLS: > > + rewrite_flow_encap_mpls(ctx, encap, flow, wc); > > + if (!ctx->xbridge->support.add_mpls) { > > + ctx->xout->slow |= SLOW_ACTION; > > + } > > + break; > > default: > > /* New packet type was checked during decoding. */ > > OVS_NOT_REACHED(); > > @@ -6582,6 +6627,21 @@ xlate_generic_decap_action(struct xlate_ctx *ctx, > > ctx->pending_decap = true; > > /* Trigger recirculation. */ > > return true; > > + case PT_MPLS: { > > + int n; > > + ovs_be16 ethertype; > > + > > + flow->packet_type = decap->new_pkt_type; > > + ethertype = pt_ns_type_be(flow->packet_type); > > + > > + n = flow_count_mpls_labels(flow, ctx->wc); > > + flow_pop_mpls(flow, n, ethertype, ctx->wc); > > + if (!ctx->xbridge->support.add_mpls) { > > + ctx->xout->slow |= SLOW_ACTION; > > + } > > + ctx->pending_decap = true; > > + return true; > > + } > > default: > > /* Error handling: drop packet. */ > > xlate_report_debug( > > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > > index fd0b2fdea..d9a2922e7 100644 > > --- a/ofproto/ofproto-dpif.c > > +++ b/ofproto/ofproto-dpif.c > > @@ -1520,6 +1520,44 @@ check_nd_extensions(struct dpif_backer *backer) > > > > return !error; > > } > > +/* Tests whether 'backer''s datapath supports the > > + * OVS_ACTION_ATTR_ADD_MPLS action. */ > > +static bool > > +check_add_mpls(struct dpif_backer *backer) > > +{ > > + struct odputil_keybuf keybuf; > > + struct ofpbuf actions; > > + struct ofpbuf key; > > + struct flow flow; > > + bool supported; > > + > > + struct odp_flow_key_parms odp_parms = { > > + .flow = &flow, > > + .probe = true, > > + }; > > + > > + memset(&flow, 0, sizeof flow); > > + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); > > + odp_flow_key_from_flow(&odp_parms, &key); > > + ofpbuf_init(&actions, 64); > > + > > + struct ovs_action_add_mpls *mpls; > > + > > + mpls = nl_msg_put_unspec_zero(&actions, > > + OVS_ACTION_ATTR_ADD_MPLS, > > + sizeof *mpls); > > + mpls->mpls_ethertype = htons(ETH_TYPE_MPLS); > > + > > + supported = dpif_probe_feature(backer->dpif, "add_mpls", &key, > > + &actions, NULL); > > + ofpbuf_uninit(&actions); > > + VLOG_INFO("%s: Datapath %s add_mpls action", > > + dpif_name(backer->dpif), supported ? "supports" > > + : "does not support"); > > + return supported; > > + > > +} > > + > > > > #define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE, ETHTYPE) > > \ > > static bool > > \ > > @@ -1590,6 +1628,7 @@ check_support(struct dpif_backer *backer) > > dpif_supports_explicit_drop_action(backer->dpif); > > backer->rt_support.lb_output_action= > > dpif_supports_lb_output_action(backer->dpif); > > + backer->rt_support.add_mpls = check_add_mpls(backer); > > > > /* Flow fields. */ > > backer->rt_support.odp.ct_state = check_ct_state(backer); > > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h > > index b41c3d82a..c04bfff8d 100644 > > --- a/ofproto/ofproto-dpif.h > > +++ b/ofproto/ofproto-dpif.h > > @@ -204,7 +204,10 @@ struct group_dpif *group_dpif_lookup(struct > > ofproto_dpif *, > > DPIF_SUPPORT_FIELD(bool, explicit_drop_action, "Explicit Drop > > action") \ > > > > \ > > /* True if the datapath supports balance_tcp optimization */ > > \ > > - DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP > > mode") > > + DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP > > mode")\ > > + > > \ > > + /* True if the datapath supports layer 2 MPLS tunnelling */ > > \ > > + DPIF_SUPPORT_FIELD(bool, add_mpls, "l2 MPLS tunnelling") > > > > > > /* Stores the various features which the corresponding backer supports. > > */ > > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > > index fb5b9a36d..b865b5210 100644 > > --- a/tests/system-traffic.at > > +++ b/tests/system-traffic.at > > @@ -1027,9 +1027,45 @@ NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 > > 10.1.1.2 | FORMAT_PING], [0], > > 3 packets transmitted, 3 received, 0% packet loss, time 0ms > > ]) > > > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > + > > +AT_SETUP([datapath - ptap mpls actions]) > > +OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) > > + > > +ADD_NAMESPACES(at_ns0, at_ns1) > > + > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") > > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") > > + > > +AT_CHECK([ip link add patch0 type veth peer name patch1]) > > +on_exit 'ip link del patch0' > > + > > +AT_CHECK([ip link set dev patch0 up]) > > +AT_CHECK([ip link set dev patch1 up]) > > +AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 > > ofport_request=100]) > > +AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 > > ofport_request=100]) > > + > > +AT_DATA([flows.txt], [dnl > > +table=0,priority=100,dl_type=0x0800 > > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),output:100 > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 > > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3) > > +table=0,priority=10 actions=resubmit(,3) > > +table=3,priority=10 actions=normal > > +]) > > + > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows.txt]) > > + > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | > > FORMAT_PING], [0], [dnl > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > +]) > > + > > + > > NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | > > FORMAT_PING], [0], [dnl > > 3 packets transmitted, 3 received, 0% packet loss, time 0ms > > ]) > > + > > OVS_TRAFFIC_VSWITCHD_STOP > > AT_CLEANUP > > > > -- > > 2.18.4 > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
