---
title: 用户自定义parser
description: "在Hydra中，parser是处理网络数据包的必要步骤，parser用于将原始packet的比特流，根据自定义的解析规则转换为结构化的协议头部，便于后续进行表的匹配以及action的处理。"
url: https://www.hikunpeng.com/document/detail/zh/flexda/devguide/dpak_dev0044.html
sourcePath: /source/zh/flexda/devguide/dpak_dev0044.html
indexId: b00bf097a9c6ea505c5c34af60b9a098df0447ba1065244075d8a4452631c03e44
---
# 用户自定义parser

在Hydra中，parser是处理网络数据包的必要步骤，parser用于将原始packet的比特流，根据自定义的解析规则转换为结构化的协议头部，便于后续进行表的匹配以及action的处理。

Hydra parser通过一组有状态的状态机来定义，起始状态为start，根据packet中的内容逐步跳转到不同的状态，提取特定的协议头部。解析器的核心是状态的定义以及状态之间的转换逻辑。

#### 解析器定义

```
parser Parser<H>(packet_in buffer, out H parsed_hdr);
```


解析器的定义包含两个入参：packet_in（输入的数据包，用来从数据包中逐步提取字段）和parsed_hdr（存储输出的头部信息，保存解析后的协议头部）。其中hdr的out方向参数表示其在parser内部赋值后将值传递给调用方。

#### 状态

Hydra解析器描述了包含一个起始状态和两个终止状态的状态机。

- start：起始状态。固定为进入parser功能块后的第一个状态。
- accept：终止状态。表示解析成功，网络数据包会进入后续control流程。
- reject：终止状态。表示解析失败，网络数据包会被上报。

除了固定的起始状态和终止状态，用户可自定义一系列中间状态，每个状态包含name和body。

- 用户自定义状态的name不可与固定状态重名，即不可为start、accept、reject。
- 一个状态负责提取一个协议头部，并根据条件（如协议的类型字段）来决定如何跳转到下一个状态。


#### 解析方式

parser从第一个字节开始解析报文。它将维护报文的当前偏移量，这个偏移量是一个指向报文中的特定字节的指针。在初步extract的过程中，这个指针会持续移动并且将报文中的内容提取到hdr之中，并且将这些字段标记为有效。

以FullNAT中解析器的代码为例，示例代码如下所示。可查看《Data Acceleration Kit 26.1.RC1 FlexDA 编程框架 API参考》，了解具体函数作用。

```
struct headers {
ethernet_t   ethernet;
l3_union_t     l3;
l4_union_t     l4;
}
parser
MyParser
(packet_in packet,
out headers hdr) {
state start {
transition parse_ethernet;
}
state parse_ethernet {
packet.
extract
(hdr.ethernet, TYPE_ETH, layers_e.l2);
transition
select
(hdr.ethernet.eth_type) {
TYPE_IPV4 : parse_ipv4;
TYPE_IPV6 : parse_ipv6;
default : reject;
}
}
state parse_ipv6 {
packet.
extract
(hdr.l3.ipv6, TYPE_IPV6, layers_e.l3);
transition
select
(hdr.l3.ipv6.next_header) {
TYPE_TCP : parse_tcp;
TYPE_UDP : parse_udp;
default : reject;
}
}
state parse_ipv4 {
packet.
extract
(hdr.l3.ipv4, TYPE_IPV4, layers_e.l3);
transition
select
(hdr.l3.ipv4.protocol) {
TYPE_TCP : parse_tcp;
TYPE_UDP : parse_udp;
default : reject;
}
}
state parse_tcp {
packet.
extract
(hdr.l4.tcp, TYPE_TCP, layers_e.l4);
transition accept;
}
state parse_udp {
packet.
extract
(hdr.l4.udp, TYPE_UDP, layers_e.l4);
transition accept;
}
}
```

上述代码中，解析器的入口如下所示。

```
state start {
transition parse_ethernet;
}
```

整个解析过程从start状态开始，在parser中必须有一个start状态，如果没有start状态，在编译时会报错。在此处出于代码入口整洁的考虑将parse_ethernet作为一个新的状态，实际上start状态中也可以写解析逻辑。

其中，transition是parser状态机进行跳转的关键字，parse_ethernet是跳转到的状态。

在parse_ethernet状态里，可以看到以下内容。

```
state parse_ethernet {
packet.
extract
(hdr.ethernet, TYPE_ETH, layers_e.l2);
transition
select
(hdr.ethernet.eth_type) {
TYPE_IPV4 : parse_ipv4;
TYPE_IPV6 : parse_ipv6;
default : reject;
}
}
```

示例如下。

```
packet.extract(hdr.ethernet, TYPE_ETH, layers_e.l2);
```

调用了extract方法，从数据包中提取以太网的头部，并且将其存储到hdr.ethernet之中。这里的hdr.ethernet是header类型的变量，包含了以太网头部的各个字段。TYPE_ETH表示Ethernet的协议值，layers_e.l2表示Ethernet协议属于L2层。

示例如下。
```
transition select(hdr.ethernet.eth_type) {
TYPE_IPV4 : parse_ipv4;
TYPE_IPV6 : parse_ipv6;
default : reject;
}
```


transition select(hdr.ethernet.eth_type)会对hdr中的ethernet header中的eth_type进行检查。

- 如果eth_type==TYPE_IPV4 ，表示这是一个IPv4包，转移到parse_ipv4状态。
- 如果eth_type==TYPE_IPV6 ，表示这是一个IPv6包，转移到parse_ipv6状态。
- 否则会进入默认状态reject，上报该数据包。

用户自定义的报文解析header结构体内最多可设置14个报文头结构。
