diff mbox

[1/3] linux-gen: packet: enable parsing only selected packet header layers

Message ID 1473777012-21850-1-git-send-email-matias.elo@nokia.com
State Accepted
Commit 144a1d8c4d0265ff3ff6ae740b4e99045645e59d
Headers show

Commit Message

Elo, Matias (Nokia - FI/Espoo) Sept. 13, 2016, 2:30 p.m. UTC
Enable parsing packet headers up to a given protocol layer.

Signed-off-by: Matias Elo <matias.elo@nokia.com>

---
 .../linux-generic/include/odp_packet_internal.h    |  24 +-
 platform/linux-generic/odp_classification.c        |   2 +-
 platform/linux-generic/odp_packet.c                | 293 ++++++++++++---------
 3 files changed, 193 insertions(+), 126 deletions(-)

-- 
2.7.4

Comments

Bill Fischofer Sept. 21, 2016, 7:36 p.m. UTC | #1
For this series:

Reviewed-and-tested-by: Bill Fischofer <bill.fischofer@linaro.org>

On Tue, Sep 13, 2016 at 9:30 AM, Matias Elo <matias.elo@nokia.com> wrote:

> Enable parsing packet headers up to a given protocol layer.

>

> Signed-off-by: Matias Elo <matias.elo@nokia.com>

> ---

>  .../linux-generic/include/odp_packet_internal.h    |  24 +-

>  platform/linux-generic/odp_classification.c        |   2 +-

>  platform/linux-generic/odp_packet.c                | 293

> ++++++++++++---------

>  3 files changed, 193 insertions(+), 126 deletions(-)

>

> diff --git a/platform/linux-generic/include/odp_packet_internal.h

> b/platform/linux-generic/include/odp_packet_internal.h

> index 392d670..9b4f59e 100644

> --- a/platform/linux-generic/include/odp_packet_internal.h

> +++ b/platform/linux-generic/include/odp_packet_internal.h

> @@ -41,7 +41,6 @@ typedef union {

>

>         struct {

>                 uint64_t parsed_l2:1; /**< L2 parsed */

> -               uint64_t parsed_all:1;/**< Parsing complete */

>                 uint64_t dst_queue:1; /**< Dst queue present */

>

>                 uint64_t flow_hash:1; /**< Flow hash present */

> @@ -131,6 +130,18 @@ ODP_STATIC_ASSERT(sizeof(output_flags_t) ==

> sizeof(uint32_t),

>                   "OUTPUT_FLAGS_SIZE_ERROR");

>

>  /**

> + * Protocol stack layers

> + */

> +typedef enum {

> +       LAYER_NONE = 0,

> +       LAYER_L1,

> +       LAYER_L2,

> +       LAYER_L3,

> +       LAYER_L4,

> +       LAYER_ALL

> +} layer_t;

> +

> +/**

>   * Packet parser metadata

>   */

>  typedef struct {

> @@ -145,6 +156,10 @@ typedef struct {

>         uint32_t l3_len;    /**< Layer 3 length */

>         uint32_t l4_len;    /**< Layer 4 length */

>

> +       layer_t parsed_layers;  /**< Highest parsed protocol stack layer */

> +       uint16_t ethtype;       /**< EtherType */

> +       uint8_t ip_proto;       /**< IP protocol */

> +

>  } packet_parser_t;

>

>  /**

> @@ -300,7 +315,7 @@ static inline int packet_parse_l2_not_done(packet_parser_t

> *prs)

>

>  static inline int packet_parse_not_complete(odp_packet_hdr_t *pkt_hdr)

>  {

> -       return !pkt_hdr->p.input_flags.parsed_all;

> +       return pkt_hdr->p.parsed_layers != LAYER_ALL;

>  }

>

>  /* Forward declarations */

> @@ -316,6 +331,9 @@ void packet_parse_l2(packet_parser_t *prs, uint32_t

> frame_len);

>  /* Perform full packet parse */

>  int packet_parse_full(odp_packet_hdr_t *pkt_hdr);

>

> +/* Perform packet parse up to a given protocol layer */

> +int packet_parse_layer(odp_packet_hdr_t *pkt_hdr, layer_t layer);

> +

>  /* Reset parser metadata for a new parse */

>  void packet_parse_reset(odp_packet_hdr_t *pkt_hdr);

>

> @@ -349,7 +367,7 @@ static inline void packet_set_ts(odp_packet_hdr_t

> *pkt_hdr, odp_time_t *ts)

>  }

>

>  int packet_parse_common(packet_parser_t *pkt_hdr, const uint8_t *ptr,

> -                       uint32_t pkt_len, uint32_t seg_len);

> +                       uint32_t pkt_len, uint32_t seg_len, layer_t layer);

>

>  int _odp_cls_parse(odp_packet_hdr_t *pkt_hdr, const uint8_t *parseptr);

>

> diff --git a/platform/linux-generic/odp_classification.c

> b/platform/linux-generic/odp_classification.c

> index ea223bf..868058d 100644

> --- a/platform/linux-generic/odp_classification.c

> +++ b/platform/linux-generic/odp_classification.c

> @@ -821,7 +821,7 @@ int cls_classify_packet(pktio_entry_t *entry, const

> uint8_t *base,

>         packet_parse_reset(pkt_hdr);

>         packet_set_len(pkt_hdr, pkt_len);

>

> -       packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len);

> +       packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len,

> LAYER_ALL);

>         cos = cls_select_cos(entry, base, pkt_hdr);

>

>         if (cos == NULL)

> diff --git a/platform/linux-generic/odp_packet.c

> b/platform/linux-generic/odp_packet.c

> index c4cf324..5f84869 100644

> --- a/platform/linux-generic/odp_packet.c

> +++ b/platform/linux-generic/odp_packet.c

> @@ -30,12 +30,13 @@

>  static inline void packet_parse_disable(odp_packet_hdr_t *pkt_hdr)

>  {

>         pkt_hdr->p.input_flags.parsed_l2  = 1;

> -       pkt_hdr->p.input_flags.parsed_all = 1;

> +       pkt_hdr->p.parsed_layers = LAYER_ALL;

>  }

>

>  void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)

>  {

>         /* Reset parser metadata before new parse */

> +       pkt_hdr->p.parsed_layers    = LAYER_NONE;

>         pkt_hdr->p.error_flags.all  = 0;

>         pkt_hdr->p.input_flags.all  = 0;

>         pkt_hdr->p.output_flags.all = 0;

> @@ -50,6 +51,8 @@ void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)

>  static void packet_init(pool_entry_t *pool, odp_packet_hdr_t *pkt_hdr,

>                         size_t size, int parse)

>  {

> +       pkt_hdr->p.parsed_layers    = LAYER_NONE;

> +

>         pkt_hdr->p.input_flags.all  = 0;

>         pkt_hdr->p.output_flags.all = 0;

>         pkt_hdr->p.error_flags.all  = 0;

> @@ -1166,151 +1169,185 @@ void packet_parse_l2(packet_parser_t *prs,

> uint32_t frame_len)

>  }

>

>  /**

> - * Parse common packet headers

> + * Parse common packet headers up to given layer

>   *

>   * The function expects at least PACKET_PARSE_SEG_LEN bytes of data to be

>   * available from the ptr.

>   */

>  int packet_parse_common(packet_parser_t *prs, const uint8_t *ptr,

> -                       uint32_t frame_len, uint32_t seg_len)

> +                       uint32_t frame_len, uint32_t seg_len, layer_t

> layer)

>  {

> -       const _odp_ethhdr_t *eth;

> -       const _odp_vlanhdr_t *vlan;

> -       uint16_t ethtype;

>         uint32_t offset;

> -       uint8_t ip_proto = 0;

>         const uint8_t *parseptr;

> -       uint16_t macaddr0, macaddr2, macaddr4;

> -

> -       offset = sizeof(_odp_ethhdr_t);

> -       if (packet_parse_l2_not_done(prs))

> -               packet_parse_l2(prs, frame_len);

> -

> -       eth = (const _odp_ethhdr_t *)ptr;

> -

> -       /* Handle Ethernet broadcast/multicast addresses */

> -       macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)(const void

> *)eth));

> -       prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;

> -

> -       if (macaddr0 == 0xffff) {

> -               macaddr2 =

> -                       odp_be_to_cpu_16(*((const uint16_t *)

> -                                          (const void *)eth + 1));

> -               macaddr4 =

> -                       odp_be_to_cpu_16(*((const uint16_t *)

> -                                          (const void *)eth + 2));

> -               prs->input_flags.eth_bcast =

> -                       (macaddr2 == 0xffff) && (macaddr4 == 0xffff);

> -       } else {

> -               prs->input_flags.eth_bcast = 0;

> -       }

> -

> -       /* Get Ethertype */

> -       ethtype = odp_be_to_cpu_16(eth->type);

> -       parseptr = (const uint8_t *)(eth + 1);

>

> -       /* Check for SNAP vs. DIX */

> -       if (ethtype < _ODP_ETH_LEN_MAX) {

> -               prs->input_flags.snap = 1;

> -               if (ethtype > frame_len - offset) {

> -                       prs->error_flags.snap_len = 1;

> -                       goto parse_exit;

> +       switch (prs->parsed_layers) {

> +       case LAYER_NONE:

> +       case LAYER_L2:

> +       {

> +               const _odp_ethhdr_t *eth;

> +               uint16_t macaddr0, macaddr2, macaddr4;

> +               const _odp_vlanhdr_t *vlan;

> +

> +               offset = sizeof(_odp_ethhdr_t);

> +               if (packet_parse_l2_not_done(prs))

> +                       packet_parse_l2(prs, frame_len);

> +

> +               eth = (const _odp_ethhdr_t *)ptr;

> +

> +               /* Handle Ethernet broadcast/multicast addresses */

> +               macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)

> +                                           (const void *)eth));

> +               prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;

> +

> +               if (macaddr0 == 0xffff) {

> +                       macaddr2 =

> +                               odp_be_to_cpu_16(*((const uint16_t *)

> +                                                  (const void *)eth + 1));

> +                       macaddr4 =

> +                               odp_be_to_cpu_16(*((const uint16_t *)

> +                                                  (const void *)eth + 2));

> +                       prs->input_flags.eth_bcast =

> +                               (macaddr2 == 0xffff) && (macaddr4 ==

> 0xffff);

> +               } else {

> +                       prs->input_flags.eth_bcast = 0;

>                 }

> -               ethtype = odp_be_to_cpu_16(*((const uint16_t *)

> -                                            (uintptr_t)(parseptr + 6)));

> -               offset   += 8;

> -               parseptr += 8;

> -       }

> -

> -       /* Parse the VLAN header(s), if present */

> -       if (ethtype == _ODP_ETHTYPE_VLAN_OUTER) {

> -               prs->input_flags.vlan_qinq = 1;

> -               prs->input_flags.vlan = 1;

> -

> -               vlan = (const _odp_vlanhdr_t *)parseptr;

> -               ethtype = odp_be_to_cpu_16(vlan->type);

> -               offset += sizeof(_odp_vlanhdr_t);

> -               parseptr += sizeof(_odp_vlanhdr_t);

> -       }

> -

> -       if (ethtype == _ODP_ETHTYPE_VLAN) {

> -               prs->input_flags.vlan = 1;

> -               vlan = (const _odp_vlanhdr_t *)parseptr;

> -               ethtype = odp_be_to_cpu_16(vlan->type);

> -               offset += sizeof(_odp_vlanhdr_t);

> -               parseptr += sizeof(_odp_vlanhdr_t);

> -       }

>

> -       /* Set l3_offset+flag only for known ethtypes */

> -       prs->input_flags.l3 = 1;

> -       prs->l3_offset = offset;

> +               /* Get Ethertype */

> +               prs->ethtype = odp_be_to_cpu_16(eth->type);

> +               parseptr = (const uint8_t *)(eth + 1);

> +

> +               /* Check for SNAP vs. DIX */

> +               if (prs->ethtype < _ODP_ETH_LEN_MAX) {

> +                       prs->input_flags.snap = 1;

> +                       if (prs->ethtype > frame_len - offset) {

> +                               prs->error_flags.snap_len = 1;

> +                               goto parse_exit;

> +                       }

> +                       prs->ethtype = odp_be_to_cpu_16(*((const uint16_t

> *)

> +                                                       (uintptr_t)

> +                                                       (parseptr + 6)));

> +                       offset   += 8;

> +                       parseptr += 8;

> +               }

>

> -       /* Parse Layer 3 headers */

> -       switch (ethtype) {

> -       case _ODP_ETHTYPE_IPV4:

> -               prs->input_flags.ipv4 = 1;

> -               ip_proto = parse_ipv4(prs, &parseptr, &offset, frame_len);

> -               break;

> +               /* Parse the VLAN header(s), if present */

> +               if (prs->ethtype == _ODP_ETHTYPE_VLAN_OUTER) {

> +                       prs->input_flags.vlan_qinq = 1;

> +                       prs->input_flags.vlan = 1;

>

> -       case _ODP_ETHTYPE_IPV6:

> -               prs->input_flags.ipv6 = 1;

> -               ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,

> -                                     seg_len);

> -               break;

> +                       vlan = (const _odp_vlanhdr_t *)parseptr;

> +                       prs->ethtype = odp_be_to_cpu_16(vlan->type);

> +                       offset += sizeof(_odp_vlanhdr_t);

> +                       parseptr += sizeof(_odp_vlanhdr_t);

> +               }

>

> -       case _ODP_ETHTYPE_ARP:

> -               prs->input_flags.arp = 1;

> -               ip_proto = 255;  /* Reserved invalid by IANA */

> -               break;

> +               if (prs->ethtype == _ODP_ETHTYPE_VLAN) {

> +                       prs->input_flags.vlan = 1;

> +                       vlan = (const _odp_vlanhdr_t *)parseptr;

> +                       prs->ethtype = odp_be_to_cpu_16(vlan->type);

> +                       offset += sizeof(_odp_vlanhdr_t);

> +                       parseptr += sizeof(_odp_vlanhdr_t);

> +               }

>

> -       default:

> -               prs->input_flags.l3 = 0;

> -               prs->l3_offset = ODP_PACKET_OFFSET_INVALID;

> -               ip_proto = 255;  /* Reserved invalid by IANA */

> +               prs->l3_offset = offset;

> +               prs->parsed_layers = LAYER_L2;

> +               if (layer == LAYER_L2)

> +                       return prs->error_flags.all != 0;

>         }

> +       case LAYER_L3:

> +       {

> +               offset = prs->l3_offset;

> +               parseptr = (const uint8_t *)(ptr + offset);

> +               /* Set l3_offset+flag only for known ethtypes */

> +               prs->input_flags.l3 = 1;

> +

> +               /* Parse Layer 3 headers */

> +               switch (prs->ethtype) {

> +               case _ODP_ETHTYPE_IPV4:

> +                       prs->input_flags.ipv4 = 1;

> +                       prs->ip_proto = parse_ipv4(prs, &parseptr, &offset,

> +                                                  frame_len);

> +                       break;

> +

> +               case _ODP_ETHTYPE_IPV6:

> +                       prs->input_flags.ipv6 = 1;

> +                       prs->ip_proto = parse_ipv6(prs, &parseptr, &offset,

> +                                                  frame_len, seg_len);

> +                       break;

> +

> +               case _ODP_ETHTYPE_ARP:

> +                       prs->input_flags.arp = 1;

> +                       prs->ip_proto = 255;  /* Reserved invalid by IANA

> */

> +                       break;

> +

> +               default:

> +                       prs->input_flags.l3 = 0;

> +                       prs->l3_offset = ODP_PACKET_OFFSET_INVALID;

> +                       prs->ip_proto = 255;  /* Reserved invalid by IANA

> */

> +               }

>

> -       /* Set l4_offset+flag only for known ip_proto */

> -       prs->input_flags.l4 = 1;

> -       prs->l4_offset = offset;

> -

> -       /* Parse Layer 4 headers */

> -       switch (ip_proto) {

> -       case _ODP_IPPROTO_ICMP:

> -               prs->input_flags.icmp = 1;

> -               break;

> -

> -       case _ODP_IPPROTO_TCP:

> -               if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_len))

> -                       return -1;

> -               prs->input_flags.tcp = 1;

> -               parse_tcp(prs, &parseptr, NULL);

> -               break;

> -

> -       case _ODP_IPPROTO_UDP:

> -               if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))

> -                       return -1;

> -               prs->input_flags.udp = 1;

> -               parse_udp(prs, &parseptr, NULL);

> -               break;

> +               /* Set l4_offset+flag only for known ip_proto */

> +               prs->l4_offset = offset;

> +               prs->parsed_layers = LAYER_L3;

> +               if (layer == LAYER_L3)

> +                       return prs->error_flags.all != 0;

> +       }

> +       case LAYER_L4:

> +       {

> +               offset = prs->l4_offset;

> +               parseptr = (const uint8_t *)(ptr + offset);

> +               prs->input_flags.l4 = 1;

> +

> +               /* Parse Layer 4 headers */

> +               switch (prs->ip_proto) {

> +               case _ODP_IPPROTO_ICMP:

> +                       prs->input_flags.icmp = 1;

> +                       break;

> +

> +               case _ODP_IPPROTO_TCP:

> +                       if (odp_unlikely(offset + _ODP_TCPHDR_LEN >

> seg_len))

> +                               return -1;

> +                       prs->input_flags.tcp = 1;

> +                       parse_tcp(prs, &parseptr, NULL);

> +                       break;

> +

> +               case _ODP_IPPROTO_UDP:

> +                       if (odp_unlikely(offset + _ODP_UDPHDR_LEN >

> seg_len))

> +                               return -1;

> +                       prs->input_flags.udp = 1;

> +                       parse_udp(prs, &parseptr, NULL);

> +                       break;

> +

> +               case _ODP_IPPROTO_AH:

> +                       prs->input_flags.ipsec = 1;

> +                       prs->input_flags.ipsec_ah = 1;

> +                       break;

> +

> +               case _ODP_IPPROTO_ESP:

> +                       prs->input_flags.ipsec = 1;

> +                       prs->input_flags.ipsec_esp = 1;

> +                       break;

> +

> +               default:

> +                       prs->input_flags.l4 = 0;

> +                       prs->l4_offset = ODP_PACKET_OFFSET_INVALID;

> +                       break;

> +               }

>

> -       case _ODP_IPPROTO_AH:

> -               prs->input_flags.ipsec = 1;

> -               prs->input_flags.ipsec_ah = 1;

> +               prs->parsed_layers = LAYER_L4;

>                 break;

> -

> -       case _ODP_IPPROTO_ESP:

> -               prs->input_flags.ipsec = 1;

> -               prs->input_flags.ipsec_esp = 1;

> +       }

> +       case LAYER_ALL:

>                 break;

>

>         default:

> -               prs->input_flags.l4 = 0;

> -               prs->l4_offset = ODP_PACKET_OFFSET_INVALID;

> -               break;

> +               ODP_ERR("Invalid parse layer: %d\n", (int)layer);

> +               return -1;

>         }

>

> +       prs->parsed_layers = LAYER_ALL;

> +

>  parse_exit:

> -       prs->input_flags.parsed_all = 1;

>         return prs->error_flags.all != 0;

>  }

>

> @@ -1323,5 +1360,17 @@ int packet_parse_full(odp_packet_hdr_t *pkt_hdr)

>         void *base = packet_map(pkt_hdr, 0, &seg_len);

>

>         return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,

> -                                  seg_len);

> +                                  seg_len, LAYER_ALL);

> +}

> +

> +/**

> + * Simple packet parser

> + */

> +int packet_parse_layer(odp_packet_hdr_t *pkt_hdr, layer_t layer)

> +{

> +       uint32_t seg_len;

> +       void *base = packet_map(pkt_hdr, 0, &seg_len);

> +

> +       return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,

> +                                  seg_len, layer);

>  }

> --

> 2.7.4

>

>
Maxim Uvarov Oct. 24, 2016, 1:51 p.m. UTC | #2
Merged,
Maxim.

On 10/24/16 14:48, Elo, Matias (Nokia - FI/Espoo) wrote:
> Ping. This patch set has been reviewed and tested.

>

> -Matias

>

>

> On 21 Sep 2016, at 22.36, Bill Fischofer <bill.fischofer@linaro.org<mailto:bill.fischofer@linaro.org>> wrote:

>

> For this series:

>

> Reviewed-and-tested-by: Bill Fischofer <bill.fischofer@linaro.org<mailto:bill.fischofer@linaro.org>>

>

> On Tue, Sep 13, 2016 at 9:30 AM, Matias Elo <matias.elo@nokia.com<mailto:matias.elo@nokia.com>> wrote:

> Enable parsing packet headers up to a given protocol layer.

>

> Signed-off-by: Matias Elo <matias.elo@nokia.com<mailto:matias.elo@nokia.com>>

> ---

>   .../linux-generic/include/odp_packet_internal.h    |  24 +-

>   platform/linux-generic/odp_classification.c        |   2 +-

>   platform/linux-generic/odp_packet.c                | 293 ++++++++++++---------

>   3 files changed, 193 insertions(+), 126 deletions(-)

>

> diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h

> index 392d670..9b4f59e 100644

> --- a/platform/linux-generic/include/odp_packet_internal.h

> +++ b/platform/linux-generic/include/odp_packet_internal.h

> @@ -41,7 +41,6 @@ typedef union {

>

>          struct {

>                  uint64_t parsed_l2:1; /**< L2 parsed */

> -               uint64_t parsed_all:1;/**< Parsing complete */

>                  uint64_t dst_queue:1; /**< Dst queue present */

>

>                  uint64_t flow_hash:1; /**< Flow hash present */

> @@ -131,6 +130,18 @@ ODP_STATIC_ASSERT(sizeof(output_flags_t) == sizeof(uint32_t),

>                    "OUTPUT_FLAGS_SIZE_ERROR");

>

>   /**

> + * Protocol stack layers

> + */

> +typedef enum {

> +       LAYER_NONE = 0,

> +       LAYER_L1,

> +       LAYER_L2,

> +       LAYER_L3,

> +       LAYER_L4,

> +       LAYER_ALL

> +} layer_t;

> +

> +/**

>    * Packet parser metadata

>    */

>   typedef struct {

> @@ -145,6 +156,10 @@ typedef struct {

>          uint32_t l3_len;    /**< Layer 3 length */

>          uint32_t l4_len;    /**< Layer 4 length */

>

> +       layer_t parsed_layers;  /**< Highest parsed protocol stack layer */

> +       uint16_t ethtype;       /**< EtherType */

> +       uint8_t ip_proto;       /**< IP protocol */

> +

>   } packet_parser_t;

>

>   /**

> @@ -300,7 +315,7 @@ static inline int packet_parse_l2_not_done(packet_parser_t *prs)

>

>   static inline int packet_parse_not_complete(odp_packet_hdr_t *pkt_hdr)

>   {

> -       return !pkt_hdr->p.input_flags.parsed_all;

> +       return pkt_hdr->p.parsed_layers != LAYER_ALL;

>   }

>

>   /* Forward declarations */

> @@ -316,6 +331,9 @@ void packet_parse_l2(packet_parser_t *prs, uint32_t frame_len);

>   /* Perform full packet parse */

>   int packet_parse_full(odp_packet_hdr_t *pkt_hdr);

>

> +/* Perform packet parse up to a given protocol layer */

> +int packet_parse_layer(odp_packet_hdr_t *pkt_hdr, layer_t layer);

> +

>   /* Reset parser metadata for a new parse */

>   void packet_parse_reset(odp_packet_hdr_t *pkt_hdr);

>

> @@ -349,7 +367,7 @@ static inline void packet_set_ts(odp_packet_hdr_t *pkt_hdr, odp_time_t *ts)

>   }

>

>   int packet_parse_common(packet_parser_t *pkt_hdr, const uint8_t *ptr,

> -                       uint32_t pkt_len, uint32_t seg_len);

> +                       uint32_t pkt_len, uint32_t seg_len, layer_t layer);

>

>   int _odp_cls_parse(odp_packet_hdr_t *pkt_hdr, const uint8_t *parseptr);

>

> diff --git a/platform/linux-generic/odp_classification.c b/platform/linux-generic/odp_classification.c

> index ea223bf..868058d 100644

> --- a/platform/linux-generic/odp_classification.c

> +++ b/platform/linux-generic/odp_classification.c

> @@ -821,7 +821,7 @@ int cls_classify_packet(pktio_entry_t *entry, const uint8_t *base,

>          packet_parse_reset(pkt_hdr);

>          packet_set_len(pkt_hdr, pkt_len);

>

> -       packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len);

> +       packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len, LAYER_ALL);

>          cos = cls_select_cos(entry, base, pkt_hdr);

>

>          if (cos == NULL)

> diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c

> index c4cf324..5f84869 100644

> --- a/platform/linux-generic/odp_packet.c

> +++ b/platform/linux-generic/odp_packet.c

> @@ -30,12 +30,13 @@

>   static inline void packet_parse_disable(odp_packet_hdr_t *pkt_hdr)

>   {

>          pkt_hdr->p.input_flags.parsed_l2  = 1;

> -       pkt_hdr->p.input_flags.parsed_all = 1;

> +       pkt_hdr->p.parsed_layers = LAYER_ALL;

>   }

>

>   void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)

>   {

>          /* Reset parser metadata before new parse */

> +       pkt_hdr->p.parsed_layers    = LAYER_NONE;

>          pkt_hdr->p.error_flags.all  = 0;

>          pkt_hdr->p.input_flags.all  = 0;

>          pkt_hdr->p.output_flags.all = 0;

> @@ -50,6 +51,8 @@ void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)

>   static void packet_init(pool_entry_t *pool, odp_packet_hdr_t *pkt_hdr,

>                          size_t size, int parse)

>   {

> +       pkt_hdr->p.parsed_layers    = LAYER_NONE;

> +

>          pkt_hdr->p.input_flags.all  = 0;

>          pkt_hdr->p.output_flags.all = 0;

>          pkt_hdr->p.error_flags.all  = 0;

> @@ -1166,151 +1169,185 @@ void packet_parse_l2(packet_parser_t *prs, uint32_t frame_len)

>   }

>

>   /**

> - * Parse common packet headers

> + * Parse common packet headers up to given layer

>    *

>    * The function expects at least PACKET_PARSE_SEG_LEN bytes of data to be

>    * available from the ptr.

>    */

>   int packet_parse_common(packet_parser_t *prs, const uint8_t *ptr,

> -                       uint32_t frame_len, uint32_t seg_len)

> +                       uint32_t frame_len, uint32_t seg_len, layer_t layer)

>   {

> -       const _odp_ethhdr_t *eth;

> -       const _odp_vlanhdr_t *vlan;

> -       uint16_t ethtype;

>          uint32_t offset;

> -       uint8_t ip_proto = 0;

>          const uint8_t *parseptr;

> -       uint16_t macaddr0, macaddr2, macaddr4;

> -

> -       offset = sizeof(_odp_ethhdr_t);

> -       if (packet_parse_l2_not_done(prs))

> -               packet_parse_l2(prs, frame_len);

> -

> -       eth = (const _odp_ethhdr_t *)ptr;

> -

> -       /* Handle Ethernet broadcast/multicast addresses */

> -       macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)(const void *)eth));

> -       prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;

> -

> -       if (macaddr0 == 0xffff) {

> -               macaddr2 =

> -                       odp_be_to_cpu_16(*((const uint16_t *)

> -                                          (const void *)eth + 1));

> -               macaddr4 =

> -                       odp_be_to_cpu_16(*((const uint16_t *)

> -                                          (const void *)eth + 2));

> -               prs->input_flags.eth_bcast =

> -                       (macaddr2 == 0xffff) && (macaddr4 == 0xffff);

> -       } else {

> -               prs->input_flags.eth_bcast = 0;

> -       }

> -

> -       /* Get Ethertype */

> -       ethtype = odp_be_to_cpu_16(eth->type);

> -       parseptr = (const uint8_t *)(eth + 1);

>

> -       /* Check for SNAP vs. DIX */

> -       if (ethtype < _ODP_ETH_LEN_MAX) {

> -               prs->input_flags.snap = 1;

> -               if (ethtype > frame_len - offset) {

> -                       prs->error_flags.snap_len = 1;

> -                       goto parse_exit;

> +       switch (prs->parsed_layers) {

> +       case LAYER_NONE:

> +       case LAYER_L2:

> +       {

> +               const _odp_ethhdr_t *eth;

> +               uint16_t macaddr0, macaddr2, macaddr4;

> +               const _odp_vlanhdr_t *vlan;

> +

> +               offset = sizeof(_odp_ethhdr_t);

> +               if (packet_parse_l2_not_done(prs))

> +                       packet_parse_l2(prs, frame_len);

> +

> +               eth = (const _odp_ethhdr_t *)ptr;

> +

> +               /* Handle Ethernet broadcast/multicast addresses */

> +               macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)

> +                                           (const void *)eth));

> +               prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;

> +

> +               if (macaddr0 == 0xffff) {

> +                       macaddr2 =

> +                               odp_be_to_cpu_16(*((const uint16_t *)

> +                                                  (const void *)eth + 1));

> +                       macaddr4 =

> +                               odp_be_to_cpu_16(*((const uint16_t *)

> +                                                  (const void *)eth + 2));

> +                       prs->input_flags.eth_bcast =

> +                               (macaddr2 == 0xffff) && (macaddr4 == 0xffff);

> +               } else {

> +                       prs->input_flags.eth_bcast = 0;

>                  }

> -               ethtype = odp_be_to_cpu_16(*((const uint16_t *)

> -                                            (uintptr_t)(parseptr + 6)));

> -               offset   += 8;

> -               parseptr += 8;

> -       }

> -

> -       /* Parse the VLAN header(s), if present */

> -       if (ethtype == _ODP_ETHTYPE_VLAN_OUTER) {

> -               prs->input_flags.vlan_qinq = 1;

> -               prs->input_flags.vlan = 1;

> -

> -               vlan = (const _odp_vlanhdr_t *)parseptr;

> -               ethtype = odp_be_to_cpu_16(vlan->type);

> -               offset += sizeof(_odp_vlanhdr_t);

> -               parseptr += sizeof(_odp_vlanhdr_t);

> -       }

> -

> -       if (ethtype == _ODP_ETHTYPE_VLAN) {

> -               prs->input_flags.vlan = 1;

> -               vlan = (const _odp_vlanhdr_t *)parseptr;

> -               ethtype = odp_be_to_cpu_16(vlan->type);

> -               offset += sizeof(_odp_vlanhdr_t);

> -               parseptr += sizeof(_odp_vlanhdr_t);

> -       }

>

> -       /* Set l3_offset+flag only for known ethtypes */

> -       prs->input_flags.l3 = 1;

> -       prs->l3_offset = offset;

> +               /* Get Ethertype */

> +               prs->ethtype = odp_be_to_cpu_16(eth->type);

> +               parseptr = (const uint8_t *)(eth + 1);

> +

> +               /* Check for SNAP vs. DIX */

> +               if (prs->ethtype < _ODP_ETH_LEN_MAX) {

> +                       prs->input_flags.snap = 1;

> +                       if (prs->ethtype > frame_len - offset) {

> +                               prs->error_flags.snap_len = 1;

> +                               goto parse_exit;

> +                       }

> +                       prs->ethtype = odp_be_to_cpu_16(*((const uint16_t *)

> +                                                       (uintptr_t)

> +                                                       (parseptr + 6)));

> +                       offset   += 8;

> +                       parseptr += 8;

> +               }

>

> -       /* Parse Layer 3 headers */

> -       switch (ethtype) {

> -       case _ODP_ETHTYPE_IPV4:

> -               prs->input_flags.ipv4 = 1;

> -               ip_proto = parse_ipv4(prs, &parseptr, &offset, frame_len);

> -               break;

> +               /* Parse the VLAN header(s), if present */

> +               if (prs->ethtype == _ODP_ETHTYPE_VLAN_OUTER) {

> +                       prs->input_flags.vlan_qinq = 1;

> +                       prs->input_flags.vlan = 1;

>

> -       case _ODP_ETHTYPE_IPV6:

> -               prs->input_flags.ipv6 = 1;

> -               ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,

> -                                     seg_len);

> -               break;

> +                       vlan = (const _odp_vlanhdr_t *)parseptr;

> +                       prs->ethtype = odp_be_to_cpu_16(vlan->type);

> +                       offset += sizeof(_odp_vlanhdr_t);

> +                       parseptr += sizeof(_odp_vlanhdr_t);

> +               }

>

> -       case _ODP_ETHTYPE_ARP:

> -               prs->input_flags.arp = 1;

> -               ip_proto = 255;  /* Reserved invalid by IANA */

> -               break;

> +               if (prs->ethtype == _ODP_ETHTYPE_VLAN) {

> +                       prs->input_flags.vlan = 1;

> +                       vlan = (const _odp_vlanhdr_t *)parseptr;

> +                       prs->ethtype = odp_be_to_cpu_16(vlan->type);

> +                       offset += sizeof(_odp_vlanhdr_t);

> +                       parseptr += sizeof(_odp_vlanhdr_t);

> +               }

>

> -       default:

> -               prs->input_flags.l3 = 0;

> -               prs->l3_offset = ODP_PACKET_OFFSET_INVALID;

> -               ip_proto = 255;  /* Reserved invalid by IANA */

> +               prs->l3_offset = offset;

> +               prs->parsed_layers = LAYER_L2;

> +               if (layer == LAYER_L2)

> +                       return prs->error_flags.all != 0;

>          }

> +       case LAYER_L3:

> +       {

> +               offset = prs->l3_offset;

> +               parseptr = (const uint8_t *)(ptr + offset);

> +               /* Set l3_offset+flag only for known ethtypes */

> +               prs->input_flags.l3 = 1;

> +

> +               /* Parse Layer 3 headers */

> +               switch (prs->ethtype) {

> +               case _ODP_ETHTYPE_IPV4:

> +                       prs->input_flags.ipv4 = 1;

> +                       prs->ip_proto = parse_ipv4(prs, &parseptr, &offset,

> +                                                  frame_len);

> +                       break;

> +

> +               case _ODP_ETHTYPE_IPV6:

> +                       prs->input_flags.ipv6 = 1;

> +                       prs->ip_proto = parse_ipv6(prs, &parseptr, &offset,

> +                                                  frame_len, seg_len);

> +                       break;

> +

> +               case _ODP_ETHTYPE_ARP:

> +                       prs->input_flags.arp = 1;

> +                       prs->ip_proto = 255;  /* Reserved invalid by IANA */

> +                       break;

> +

> +               default:

> +                       prs->input_flags.l3 = 0;

> +                       prs->l3_offset = ODP_PACKET_OFFSET_INVALID;

> +                       prs->ip_proto = 255;  /* Reserved invalid by IANA */

> +               }

>

> -       /* Set l4_offset+flag only for known ip_proto */

> -       prs->input_flags.l4 = 1;

> -       prs->l4_offset = offset;

> -

> -       /* Parse Layer 4 headers */

> -       switch (ip_proto) {

> -       case _ODP_IPPROTO_ICMP:

> -               prs->input_flags.icmp = 1;

> -               break;

> -

> -       case _ODP_IPPROTO_TCP:

> -               if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_len))

> -                       return -1;

> -               prs->input_flags.tcp = 1;

> -               parse_tcp(prs, &parseptr, NULL);

> -               break;

> -

> -       case _ODP_IPPROTO_UDP:

> -               if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))

> -                       return -1;

> -               prs->input_flags.udp = 1;

> -               parse_udp(prs, &parseptr, NULL);

> -               break;

> +               /* Set l4_offset+flag only for known ip_proto */

> +               prs->l4_offset = offset;

> +               prs->parsed_layers = LAYER_L3;

> +               if (layer == LAYER_L3)

> +                       return prs->error_flags.all != 0;

> +       }

> +       case LAYER_L4:

> +       {

> +               offset = prs->l4_offset;

> +               parseptr = (const uint8_t *)(ptr + offset);

> +               prs->input_flags.l4 = 1;

> +

> +               /* Parse Layer 4 headers */

> +               switch (prs->ip_proto) {

> +               case _ODP_IPPROTO_ICMP:

> +                       prs->input_flags.icmp = 1;

> +                       break;

> +

> +               case _ODP_IPPROTO_TCP:

> +                       if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_len))

> +                               return -1;

> +                       prs->input_flags.tcp = 1;

> +                       parse_tcp(prs, &parseptr, NULL);

> +                       break;

> +

> +               case _ODP_IPPROTO_UDP:

> +                       if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))

> +                               return -1;

> +                       prs->input_flags.udp = 1;

> +                       parse_udp(prs, &parseptr, NULL);

> +                       break;

> +

> +               case _ODP_IPPROTO_AH:

> +                       prs->input_flags.ipsec = 1;

> +                       prs->input_flags.ipsec_ah = 1;

> +                       break;

> +

> +               case _ODP_IPPROTO_ESP:

> +                       prs->input_flags.ipsec = 1;

> +                       prs->input_flags.ipsec_esp = 1;

> +                       break;

> +

> +               default:

> +                       prs->input_flags.l4 = 0;

> +                       prs->l4_offset = ODP_PACKET_OFFSET_INVALID;

> +                       break;

> +               }

>

> -       case _ODP_IPPROTO_AH:

> -               prs->input_flags.ipsec = 1;

> -               prs->input_flags.ipsec_ah = 1;

> +               prs->parsed_layers = LAYER_L4;

>                  break;

> -

> -       case _ODP_IPPROTO_ESP:

> -               prs->input_flags.ipsec = 1;

> -               prs->input_flags.ipsec_esp = 1;

> +       }

> +       case LAYER_ALL:

>                  break;

>

>          default:

> -               prs->input_flags.l4 = 0;

> -               prs->l4_offset = ODP_PACKET_OFFSET_INVALID;

> -               break;

> +               ODP_ERR("Invalid parse layer: %d\n", (int)layer);

> +               return -1;

>          }

>

> +       prs->parsed_layers = LAYER_ALL;

> +

>   parse_exit:

> -       prs->input_flags.parsed_all = 1;

>          return prs->error_flags.all != 0;

>   }

>

> @@ -1323,5 +1360,17 @@ int packet_parse_full(odp_packet_hdr_t *pkt_hdr)

>          void *base = packet_map(pkt_hdr, 0, &seg_len);

>

>          return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,

> -                                  seg_len);

> +                                  seg_len, LAYER_ALL);

> +}

> +

> +/**

> + * Simple packet parser

> + */

> +int packet_parse_layer(odp_packet_hdr_t *pkt_hdr, layer_t layer)

> +{

> +       uint32_t seg_len;

> +       void *base = packet_map(pkt_hdr, 0, &seg_len);

> +

> +       return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,

> +                                  seg_len, layer);

>   }

> --

> 2.7.4

>

>

>
diff mbox

Patch

diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h
index 392d670..9b4f59e 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -41,7 +41,6 @@  typedef union {
 
 	struct {
 		uint64_t parsed_l2:1; /**< L2 parsed */
-		uint64_t parsed_all:1;/**< Parsing complete */
 		uint64_t dst_queue:1; /**< Dst queue present */
 
 		uint64_t flow_hash:1; /**< Flow hash present */
@@ -131,6 +130,18 @@  ODP_STATIC_ASSERT(sizeof(output_flags_t) == sizeof(uint32_t),
 		  "OUTPUT_FLAGS_SIZE_ERROR");
 
 /**
+ * Protocol stack layers
+ */
+typedef enum {
+	LAYER_NONE = 0,
+	LAYER_L1,
+	LAYER_L2,
+	LAYER_L3,
+	LAYER_L4,
+	LAYER_ALL
+} layer_t;
+
+/**
  * Packet parser metadata
  */
 typedef struct {
@@ -145,6 +156,10 @@  typedef struct {
 	uint32_t l3_len;    /**< Layer 3 length */
 	uint32_t l4_len;    /**< Layer 4 length */
 
+	layer_t parsed_layers;	/**< Highest parsed protocol stack layer */
+	uint16_t ethtype;	/**< EtherType */
+	uint8_t ip_proto;	/**< IP protocol */
+
 } packet_parser_t;
 
 /**
@@ -300,7 +315,7 @@  static inline int packet_parse_l2_not_done(packet_parser_t *prs)
 
 static inline int packet_parse_not_complete(odp_packet_hdr_t *pkt_hdr)
 {
-	return !pkt_hdr->p.input_flags.parsed_all;
+	return pkt_hdr->p.parsed_layers != LAYER_ALL;
 }
 
 /* Forward declarations */
@@ -316,6 +331,9 @@  void packet_parse_l2(packet_parser_t *prs, uint32_t frame_len);
 /* Perform full packet parse */
 int packet_parse_full(odp_packet_hdr_t *pkt_hdr);
 
+/* Perform packet parse up to a given protocol layer */
+int packet_parse_layer(odp_packet_hdr_t *pkt_hdr, layer_t layer);
+
 /* Reset parser metadata for a new parse */
 void packet_parse_reset(odp_packet_hdr_t *pkt_hdr);
 
@@ -349,7 +367,7 @@  static inline void packet_set_ts(odp_packet_hdr_t *pkt_hdr, odp_time_t *ts)
 }
 
 int packet_parse_common(packet_parser_t *pkt_hdr, const uint8_t *ptr,
-			uint32_t pkt_len, uint32_t seg_len);
+			uint32_t pkt_len, uint32_t seg_len, layer_t layer);
 
 int _odp_cls_parse(odp_packet_hdr_t *pkt_hdr, const uint8_t *parseptr);
 
diff --git a/platform/linux-generic/odp_classification.c b/platform/linux-generic/odp_classification.c
index ea223bf..868058d 100644
--- a/platform/linux-generic/odp_classification.c
+++ b/platform/linux-generic/odp_classification.c
@@ -821,7 +821,7 @@  int cls_classify_packet(pktio_entry_t *entry, const uint8_t *base,
 	packet_parse_reset(pkt_hdr);
 	packet_set_len(pkt_hdr, pkt_len);
 
-	packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len);
+	packet_parse_common(&pkt_hdr->p, base, pkt_len, seg_len, LAYER_ALL);
 	cos = cls_select_cos(entry, base, pkt_hdr);
 
 	if (cos == NULL)
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index c4cf324..5f84869 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -30,12 +30,13 @@ 
 static inline void packet_parse_disable(odp_packet_hdr_t *pkt_hdr)
 {
 	pkt_hdr->p.input_flags.parsed_l2  = 1;
-	pkt_hdr->p.input_flags.parsed_all = 1;
+	pkt_hdr->p.parsed_layers = LAYER_ALL;
 }
 
 void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)
 {
 	/* Reset parser metadata before new parse */
+	pkt_hdr->p.parsed_layers    = LAYER_NONE;
 	pkt_hdr->p.error_flags.all  = 0;
 	pkt_hdr->p.input_flags.all  = 0;
 	pkt_hdr->p.output_flags.all = 0;
@@ -50,6 +51,8 @@  void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)
 static void packet_init(pool_entry_t *pool, odp_packet_hdr_t *pkt_hdr,
 			size_t size, int parse)
 {
+	pkt_hdr->p.parsed_layers    = LAYER_NONE;
+
 	pkt_hdr->p.input_flags.all  = 0;
 	pkt_hdr->p.output_flags.all = 0;
 	pkt_hdr->p.error_flags.all  = 0;
@@ -1166,151 +1169,185 @@  void packet_parse_l2(packet_parser_t *prs, uint32_t frame_len)
 }
 
 /**
- * Parse common packet headers
+ * Parse common packet headers up to given layer
  *
  * The function expects at least PACKET_PARSE_SEG_LEN bytes of data to be
  * available from the ptr.
  */
 int packet_parse_common(packet_parser_t *prs, const uint8_t *ptr,
-			uint32_t frame_len, uint32_t seg_len)
+			uint32_t frame_len, uint32_t seg_len, layer_t layer)
 {
-	const _odp_ethhdr_t *eth;
-	const _odp_vlanhdr_t *vlan;
-	uint16_t ethtype;
 	uint32_t offset;
-	uint8_t ip_proto = 0;
 	const uint8_t *parseptr;
-	uint16_t macaddr0, macaddr2, macaddr4;
-
-	offset = sizeof(_odp_ethhdr_t);
-	if (packet_parse_l2_not_done(prs))
-		packet_parse_l2(prs, frame_len);
-
-	eth = (const _odp_ethhdr_t *)ptr;
-
-	/* Handle Ethernet broadcast/multicast addresses */
-	macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)(const void *)eth));
-	prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;
-
-	if (macaddr0 == 0xffff) {
-		macaddr2 =
-			odp_be_to_cpu_16(*((const uint16_t *)
-					   (const void *)eth + 1));
-		macaddr4 =
-			odp_be_to_cpu_16(*((const uint16_t *)
-					   (const void *)eth + 2));
-		prs->input_flags.eth_bcast =
-			(macaddr2 == 0xffff) && (macaddr4 == 0xffff);
-	} else {
-		prs->input_flags.eth_bcast = 0;
-	}
-
-	/* Get Ethertype */
-	ethtype = odp_be_to_cpu_16(eth->type);
-	parseptr = (const uint8_t *)(eth + 1);
 
-	/* Check for SNAP vs. DIX */
-	if (ethtype < _ODP_ETH_LEN_MAX) {
-		prs->input_flags.snap = 1;
-		if (ethtype > frame_len - offset) {
-			prs->error_flags.snap_len = 1;
-			goto parse_exit;
+	switch (prs->parsed_layers) {
+	case LAYER_NONE:
+	case LAYER_L2:
+	{
+		const _odp_ethhdr_t *eth;
+		uint16_t macaddr0, macaddr2, macaddr4;
+		const _odp_vlanhdr_t *vlan;
+
+		offset = sizeof(_odp_ethhdr_t);
+		if (packet_parse_l2_not_done(prs))
+			packet_parse_l2(prs, frame_len);
+
+		eth = (const _odp_ethhdr_t *)ptr;
+
+		/* Handle Ethernet broadcast/multicast addresses */
+		macaddr0 = odp_be_to_cpu_16(*((const uint16_t *)
+					    (const void *)eth));
+		prs->input_flags.eth_mcast = (macaddr0 & 0x0100) == 0x0100;
+
+		if (macaddr0 == 0xffff) {
+			macaddr2 =
+				odp_be_to_cpu_16(*((const uint16_t *)
+						   (const void *)eth + 1));
+			macaddr4 =
+				odp_be_to_cpu_16(*((const uint16_t *)
+						   (const void *)eth + 2));
+			prs->input_flags.eth_bcast =
+				(macaddr2 == 0xffff) && (macaddr4 == 0xffff);
+		} else {
+			prs->input_flags.eth_bcast = 0;
 		}
-		ethtype = odp_be_to_cpu_16(*((const uint16_t *)
-					     (uintptr_t)(parseptr + 6)));
-		offset   += 8;
-		parseptr += 8;
-	}
-
-	/* Parse the VLAN header(s), if present */
-	if (ethtype == _ODP_ETHTYPE_VLAN_OUTER) {
-		prs->input_flags.vlan_qinq = 1;
-		prs->input_flags.vlan = 1;
-
-		vlan = (const _odp_vlanhdr_t *)parseptr;
-		ethtype = odp_be_to_cpu_16(vlan->type);
-		offset += sizeof(_odp_vlanhdr_t);
-		parseptr += sizeof(_odp_vlanhdr_t);
-	}
-
-	if (ethtype == _ODP_ETHTYPE_VLAN) {
-		prs->input_flags.vlan = 1;
-		vlan = (const _odp_vlanhdr_t *)parseptr;
-		ethtype = odp_be_to_cpu_16(vlan->type);
-		offset += sizeof(_odp_vlanhdr_t);
-		parseptr += sizeof(_odp_vlanhdr_t);
-	}
 
-	/* Set l3_offset+flag only for known ethtypes */
-	prs->input_flags.l3 = 1;
-	prs->l3_offset = offset;
+		/* Get Ethertype */
+		prs->ethtype = odp_be_to_cpu_16(eth->type);
+		parseptr = (const uint8_t *)(eth + 1);
+
+		/* Check for SNAP vs. DIX */
+		if (prs->ethtype < _ODP_ETH_LEN_MAX) {
+			prs->input_flags.snap = 1;
+			if (prs->ethtype > frame_len - offset) {
+				prs->error_flags.snap_len = 1;
+				goto parse_exit;
+			}
+			prs->ethtype = odp_be_to_cpu_16(*((const uint16_t *)
+							(uintptr_t)
+							(parseptr + 6)));
+			offset   += 8;
+			parseptr += 8;
+		}
 
-	/* Parse Layer 3 headers */
-	switch (ethtype) {
-	case _ODP_ETHTYPE_IPV4:
-		prs->input_flags.ipv4 = 1;
-		ip_proto = parse_ipv4(prs, &parseptr, &offset, frame_len);
-		break;
+		/* Parse the VLAN header(s), if present */
+		if (prs->ethtype == _ODP_ETHTYPE_VLAN_OUTER) {
+			prs->input_flags.vlan_qinq = 1;
+			prs->input_flags.vlan = 1;
 
-	case _ODP_ETHTYPE_IPV6:
-		prs->input_flags.ipv6 = 1;
-		ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,
-				      seg_len);
-		break;
+			vlan = (const _odp_vlanhdr_t *)parseptr;
+			prs->ethtype = odp_be_to_cpu_16(vlan->type);
+			offset += sizeof(_odp_vlanhdr_t);
+			parseptr += sizeof(_odp_vlanhdr_t);
+		}
 
-	case _ODP_ETHTYPE_ARP:
-		prs->input_flags.arp = 1;
-		ip_proto = 255;  /* Reserved invalid by IANA */
-		break;
+		if (prs->ethtype == _ODP_ETHTYPE_VLAN) {
+			prs->input_flags.vlan = 1;
+			vlan = (const _odp_vlanhdr_t *)parseptr;
+			prs->ethtype = odp_be_to_cpu_16(vlan->type);
+			offset += sizeof(_odp_vlanhdr_t);
+			parseptr += sizeof(_odp_vlanhdr_t);
+		}
 
-	default:
-		prs->input_flags.l3 = 0;
-		prs->l3_offset = ODP_PACKET_OFFSET_INVALID;
-		ip_proto = 255;  /* Reserved invalid by IANA */
+		prs->l3_offset = offset;
+		prs->parsed_layers = LAYER_L2;
+		if (layer == LAYER_L2)
+			return prs->error_flags.all != 0;
 	}
+	case LAYER_L3:
+	{
+		offset = prs->l3_offset;
+		parseptr = (const uint8_t *)(ptr + offset);
+		/* Set l3_offset+flag only for known ethtypes */
+		prs->input_flags.l3 = 1;
+
+		/* Parse Layer 3 headers */
+		switch (prs->ethtype) {
+		case _ODP_ETHTYPE_IPV4:
+			prs->input_flags.ipv4 = 1;
+			prs->ip_proto = parse_ipv4(prs, &parseptr, &offset,
+						   frame_len);
+			break;
+
+		case _ODP_ETHTYPE_IPV6:
+			prs->input_flags.ipv6 = 1;
+			prs->ip_proto = parse_ipv6(prs, &parseptr, &offset,
+						   frame_len, seg_len);
+			break;
+
+		case _ODP_ETHTYPE_ARP:
+			prs->input_flags.arp = 1;
+			prs->ip_proto = 255;  /* Reserved invalid by IANA */
+			break;
+
+		default:
+			prs->input_flags.l3 = 0;
+			prs->l3_offset = ODP_PACKET_OFFSET_INVALID;
+			prs->ip_proto = 255;  /* Reserved invalid by IANA */
+		}
 
-	/* Set l4_offset+flag only for known ip_proto */
-	prs->input_flags.l4 = 1;
-	prs->l4_offset = offset;
-
-	/* Parse Layer 4 headers */
-	switch (ip_proto) {
-	case _ODP_IPPROTO_ICMP:
-		prs->input_flags.icmp = 1;
-		break;
-
-	case _ODP_IPPROTO_TCP:
-		if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_len))
-			return -1;
-		prs->input_flags.tcp = 1;
-		parse_tcp(prs, &parseptr, NULL);
-		break;
-
-	case _ODP_IPPROTO_UDP:
-		if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))
-			return -1;
-		prs->input_flags.udp = 1;
-		parse_udp(prs, &parseptr, NULL);
-		break;
+		/* Set l4_offset+flag only for known ip_proto */
+		prs->l4_offset = offset;
+		prs->parsed_layers = LAYER_L3;
+		if (layer == LAYER_L3)
+			return prs->error_flags.all != 0;
+	}
+	case LAYER_L4:
+	{
+		offset = prs->l4_offset;
+		parseptr = (const uint8_t *)(ptr + offset);
+		prs->input_flags.l4 = 1;
+
+		/* Parse Layer 4 headers */
+		switch (prs->ip_proto) {
+		case _ODP_IPPROTO_ICMP:
+			prs->input_flags.icmp = 1;
+			break;
+
+		case _ODP_IPPROTO_TCP:
+			if (odp_unlikely(offset + _ODP_TCPHDR_LEN > seg_len))
+				return -1;
+			prs->input_flags.tcp = 1;
+			parse_tcp(prs, &parseptr, NULL);
+			break;
+
+		case _ODP_IPPROTO_UDP:
+			if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))
+				return -1;
+			prs->input_flags.udp = 1;
+			parse_udp(prs, &parseptr, NULL);
+			break;
+
+		case _ODP_IPPROTO_AH:
+			prs->input_flags.ipsec = 1;
+			prs->input_flags.ipsec_ah = 1;
+			break;
+
+		case _ODP_IPPROTO_ESP:
+			prs->input_flags.ipsec = 1;
+			prs->input_flags.ipsec_esp = 1;
+			break;
+
+		default:
+			prs->input_flags.l4 = 0;
+			prs->l4_offset = ODP_PACKET_OFFSET_INVALID;
+			break;
+		}
 
-	case _ODP_IPPROTO_AH:
-		prs->input_flags.ipsec = 1;
-		prs->input_flags.ipsec_ah = 1;
+		prs->parsed_layers = LAYER_L4;
 		break;
-
-	case _ODP_IPPROTO_ESP:
-		prs->input_flags.ipsec = 1;
-		prs->input_flags.ipsec_esp = 1;
+	}
+	case LAYER_ALL:
 		break;
 
 	default:
-		prs->input_flags.l4 = 0;
-		prs->l4_offset = ODP_PACKET_OFFSET_INVALID;
-		break;
+		ODP_ERR("Invalid parse layer: %d\n", (int)layer);
+		return -1;
 	}
 
+	prs->parsed_layers = LAYER_ALL;
+
 parse_exit:
-	prs->input_flags.parsed_all = 1;
 	return prs->error_flags.all != 0;
 }
 
@@ -1323,5 +1360,17 @@  int packet_parse_full(odp_packet_hdr_t *pkt_hdr)
 	void *base = packet_map(pkt_hdr, 0, &seg_len);
 
 	return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
-				   seg_len);
+				   seg_len, LAYER_ALL);
+}
+
+/**
+ * Simple packet parser
+ */
+int packet_parse_layer(odp_packet_hdr_t *pkt_hdr, layer_t layer)
+{
+	uint32_t seg_len;
+	void *base = packet_map(pkt_hdr, 0, &seg_len);
+
+	return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
+				   seg_len, layer);
 }