[ovs-dev] [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.

Eelco Chaudron echaudro at redhat.com
Wed Mar 31 13:59:40 UTC 2021



On 26 Mar 2021, at 7:21, Martin Varghese wrote:

> From: Martin Varghese <martin.varghese at nokia.com>
>
> 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


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

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 <martin.varghese at nokia.com>
> ---
>  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", &ethertype);
> +            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




More information about the dev mailing list