User-defined Parser
In Hydra, the parser is a necessary step for processing network packets. It converts the raw bitstream into structured protocol headers based on user-defined parsing rules, facilitating subsequent table lookup and action execution.
The Hydra parser is defined by a state machine containing a set of states. Beginning at the start state, it transitions through various states based on packet content to extract specific protocol headers. The core of the parser consists of state definitions and the transition logic between these states.
Parser Definition
parser Parser<H>(packet_in buffer, out H parsed_hdr);
The parser definition contains two input parameters: packet_in (input data packet used to extract fields incrementally) and parsed_hdr (used to store the output parsed protocol headers). The out directional parameter indicates that its value is passed to the caller after being assigned within the parser.
States
The Hydra parser describes a state machine that contains one start state and two end states.
- Start: Fixed as the initial state entered upon invoking the parser functional block.
- Accept: An end state that indicates the parsing is successful, allowing the network packet to proceed to the subsequent control stage.
- Reject: An end state that indicates a parsing failure, and a packet upcall is triggered.
In addition to the pre-defined start and end states, users can customize intermediate states, each consisting of a name and a body.
- The names of user-defined states must not conflict with the pre-defined states; specifically, they cannot be named start, accept, or reject.
- Each state is responsible for extracting a single protocol header and determining the transition to the subsequent state based on specific conditions, such as the protocol's type field.
Parsing Mode
The parser parses a packet from the first byte. It maintains the packet's current offset, which is a pointer to a specific byte within the packet. During the initial extraction process, this pointer advances continuously as content is extracted into the hdr structure and the corresponding fields are marked as valid.
The following uses the parser code of FullNAT as an example. For details about the functions, see the BeiMing 26.0.RC1 FlexDA Programming Framework API Reference.
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;
}
}
The parser entry in the preceding code is as follows:
state start {
transition parse_ethernet;
}
The parsing process initiates from the start state which is mandatory within the parser. Otherwise, a compilation error will occur. In this case, parse_ethernet is defined as a new state to maintain a clean entry point, although parsing logic can be implemented directly within the start state.
The transition keyword is used to trigger jumps within the parser state machine, with parse_ethernet serving as the target state.
The parse_ethernet state includes the following content:
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;
}
}
Example:
packet.extract(hdr.ethernet, TYPE_ETH, layers_e.l2);
The extract method is called to extract the Ethernet header from the packet and store it in hdr.ethernet, a variable of the header type that contains the fields of the Ethernet header. TYPE_ETH indicates the Ethernet protocol type, and layers_e.l2 indicates that the Ethernet protocol resides at L2.
transition select(hdr.ethernet.eth_type) {
TYPE_IPV4 : parse_ipv4;
TYPE_IPV6 : parse_ipv6;
default : reject;
}
transition select(hdr.ethernet.eth_type) checks eth_type of the Ethernet header in hdr.
- If eth_type==TYPE_IPV4, it is an IPv4 packet, and the state transitions to parse_ipv4.
- If eth_type==TYPE_IPV6, it is an IPv6 packet, and the state transitions to parse_ipv6.
- Otherwise, the state machine enters the default reject state and a packet upcall is triggered.
A maximum of 14 packet header structures can be set in a user-defined packet parsing header structure.