鲲鹏社区首页
中文
注册
开发者
我要评分
获取效率
正确性
完整性
易理解
在线提单
论坛求助

用户自定义parser

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

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

解析器定义

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

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

状态

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

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

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

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

解析方式

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

以FullNAT中解析器的代码为例,示例代码如下所示,可查看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.ether_type)会对hdr中的ethernet header中的eth_type进行检查。

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

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