1f0ab9ebaSIdo Barnea/*
2f0ab9ebaSIdo Barnea  Ido Barnea
3f0ab9ebaSIdo Barnea  Cisco Systems, Inc.
4f0ab9ebaSIdo Barnea*/
5f0ab9ebaSIdo Barnea
6f0ab9ebaSIdo Barnea/*
7fa8792d5SIdo Barnea  Copyright (c) 2016-2017 Cisco Systems, Inc.
8f0ab9ebaSIdo Barnea
9f0ab9ebaSIdo Barnea  Licensed under the Apache License, Version 2.0 (the "License");
10f0ab9ebaSIdo Barnea  you may not use this file except in compliance with the License.
11f0ab9ebaSIdo Barnea  You may obtain a copy of the License at
12f0ab9ebaSIdo Barnea
13f0ab9ebaSIdo Barnea  http://www.apache.org/licenses/LICENSE-2.0
14f0ab9ebaSIdo Barnea
15f0ab9ebaSIdo Barnea  Unless required by applicable law or agreed to in writing, software
16f0ab9ebaSIdo Barnea  distributed under the License is distributed on an "AS IS" BASIS,
17f0ab9ebaSIdo Barnea  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18f0ab9ebaSIdo Barnea  See the License for the specific language governing permissions and
19f0ab9ebaSIdo Barnea  limitations under the License.
20f0ab9ebaSIdo Barnea*/
21f0ab9ebaSIdo Barnea
22aa11dd2eSIdo Barnea#include <netinet/in.h>
23810dd7d0SIdo Barnea#include "common/basic_utils.h"
24810dd7d0SIdo Barnea#include "common/Network/Packet/EthernetHeader.h"
25810dd7d0SIdo Barnea#include "common/Network/Packet/IPHeader.h"
26810dd7d0SIdo Barnea#include "common/Network/Packet/IPv6Header.h"
27810dd7d0SIdo Barnea#include "common/Network/Packet/TcpHeader.h"
28fa8792d5SIdo Barnea#include "common/Network/Packet/VLANHeader.h"
29efb88737SIdo Barnea#include "pkt_gen.h"
30810dd7d0SIdo Barnea#include "flow_stat_parser.h"
316d801943SHanoh Haim#include "bp_sim.h"
32f0ab9ebaSIdo Barnea
33fa8792d5SIdo BarneaCFlowStatParser::CFlowStatParser(CFlowStatParser_mode mode) {
34fa8792d5SIdo Barnea    reset();
35fa8792d5SIdo Barnea    switch(mode) {
36fa8792d5SIdo Barnea    case FLOW_STAT_PARSER_MODE_HW:
37fa8792d5SIdo Barnea        m_flags = FSTAT_PARSER_VLAN_SUPP;
38fa8792d5SIdo Barnea        break;
39fa8792d5SIdo Barnea    case FLOW_STAT_PARSER_MODE_SW:
40fa8792d5SIdo Barnea        m_flags = FSTAT_PARSER_VLAN_SUPP | FSTAT_PARSER_QINQ_SUPP;
41fa8792d5SIdo Barnea        break;
42fa8792d5SIdo Barnea    // In 82599 we configure the card to either always use VLAN, or never use
43fa8792d5SIdo Barnea    case FLOW_STAT_PARSER_MODE_82599:
44fa8792d5SIdo Barnea        m_flags = 0;
45fa8792d5SIdo Barnea        break;
46fa8792d5SIdo Barnea    case FLOW_STAT_PARSER_MODE_82599_vlan:
47fa8792d5SIdo Barnea        m_flags = FSTAT_PARSER_VLAN_SUPP | FSTAT_PARSER_VLAN_NEEDED;
48fa8792d5SIdo Barnea        break;
49fa8792d5SIdo Barnea    }
50fa8792d5SIdo Barnea}
51fa8792d5SIdo Barnea
524099c61aSIdo Barneavoid CFlowStatParser::reset() {
53b5c09779SIdo Barnea    m_start = 0;
54b5c09779SIdo Barnea    m_len = 0;
55f0ab9ebaSIdo Barnea    m_ipv4 = 0;
56810dd7d0SIdo Barnea    m_ipv6 = 0;
57f0ab9ebaSIdo Barnea    m_l4_proto = 0;
58810dd7d0SIdo Barnea    m_l4 = 0;
59f0ab9ebaSIdo Barnea}
60f0ab9ebaSIdo Barnea
61fa8792d5SIdo Barneastd::string CFlowStatParser::get_error_str(CFlowStatParser_err_t err) {
62fa8792d5SIdo Barnea    std::string base = "Failed parsing given packet for flow stat. ";
63fa8792d5SIdo Barnea
64fa8792d5SIdo Barnea    switch(err) {
65fa8792d5SIdo Barnea    case FSTAT_PARSER_E_OK:
66fa8792d5SIdo Barnea        return "";
67fa8792d5SIdo Barnea    case FSTAT_PARSER_E_TOO_SHORT:
68fa8792d5SIdo Barnea        return base + " Packet too short";
69fa8792d5SIdo Barnea    case FSTAT_PARSER_E_SHORT_IP_HDR:
70fa8792d5SIdo Barnea        return base + " Packet IP header too short";
71fa8792d5SIdo Barnea    case FSTAT_PARSER_E_VLAN_NOT_SUP:
72fa8792d5SIdo Barnea        return base + " NIC does not support vlan (for 82599, you can try starting TRex with --vlan)";
73fa8792d5SIdo Barnea    case FSTAT_PARSER_E_QINQ_NOT_SUP:
74fa8792d5SIdo Barnea        return base + " NIC does not support Q in Q";
75fa8792d5SIdo Barnea    case FSTAT_PARSER_E_MPLS_NOT_SUP:
76fa8792d5SIdo Barnea        return base + " NIC does not support MPLS";
77fa8792d5SIdo Barnea    case FSTAT_PARSER_E_UNKNOWN_HDR:
78fa8792d5SIdo Barnea        return base + " NIC does not support given L2 header type";
79fa8792d5SIdo Barnea    case FSTAT_PARSER_E_VLAN_NEEDED:
80fa8792d5SIdo Barnea        return base + " NIC does not support packets with no vlan (If you used --vlan command line arg, try to remove it)";
81fa8792d5SIdo Barnea        return "";
82fa8792d5SIdo Barnea    }
83fa8792d5SIdo Barnea
84fa8792d5SIdo Barnea    return "";
85fa8792d5SIdo Barnea}
86fa8792d5SIdo Barnea
87fa8792d5SIdo BarneaCFlowStatParser_err_t CFlowStatParser::parse(uint8_t *p, uint16_t len) {
88f0ab9ebaSIdo Barnea    EthernetHeader *ether = (EthernetHeader *)p;
89fa8792d5SIdo Barnea    VLANHeader *vlan;
9092880804SIdo Barnea    int min_len = ETH_HDR_LEN;
91fa8792d5SIdo Barnea    uint16_t next_hdr = ether->getNextProtocol();
92fa8792d5SIdo Barnea    bool finished = false;
93fa8792d5SIdo Barnea    bool has_vlan = false;
944099c61aSIdo Barnea    reset();
954099c61aSIdo Barnea
96b5c09779SIdo Barnea    m_start = p;
97b5c09779SIdo Barnea    m_len = len;
98fa8792d5SIdo Barnea    if (len < min_len)
99fa8792d5SIdo Barnea        return FSTAT_PARSER_E_TOO_SHORT;
100fa8792d5SIdo Barnea
101fa8792d5SIdo Barnea    p += ETH_HDR_LEN;
102fa8792d5SIdo Barnea    while (! finished) {
103fa8792d5SIdo Barnea        switch( next_hdr ) {
104fa8792d5SIdo Barnea        case EthernetHeader::Protocol::IP :
10592880804SIdo Barnea            min_len += IPV4_HDR_LEN;
10692880804SIdo Barnea            if (len < min_len)
107fa8792d5SIdo Barnea                return FSTAT_PARSER_E_SHORT_IP_HDR;
108fa8792d5SIdo Barnea            m_ipv4 = (IPHeader *) p;
109810dd7d0SIdo Barnea            m_l4 = ((uint8_t *)m_ipv4) + m_ipv4->getHeaderLength();
110810dd7d0SIdo Barnea            m_l4_proto = m_ipv4->getProtocol();
111fa8792d5SIdo Barnea            finished = true;
112810dd7d0SIdo Barnea            break;
113810dd7d0SIdo Barnea        case EthernetHeader::Protocol::IPv6 :
11492880804SIdo Barnea            min_len += IPV6_HDR_LEN;
11592880804SIdo Barnea            if (len < min_len)
116fa8792d5SIdo Barnea                return FSTAT_PARSER_E_SHORT_IP_HDR;
117fa8792d5SIdo Barnea            m_ipv6 = (IPv6Header *) p;
118fa8792d5SIdo Barnea            finished = true;
119fa8792d5SIdo Barnea            break;
120fa8792d5SIdo Barnea        case EthernetHeader::Protocol::QINQ :
121fa8792d5SIdo Barnea            if (! (m_flags & FSTAT_PARSER_QINQ_SUPP))
122fa8792d5SIdo Barnea                return FSTAT_PARSER_E_QINQ_NOT_SUP;
123fa8792d5SIdo Barnea        case EthernetHeader::Protocol::VLAN :
124fa8792d5SIdo Barnea            if (! (m_flags & FSTAT_PARSER_VLAN_SUPP))
125fa8792d5SIdo Barnea                return FSTAT_PARSER_E_VLAN_NOT_SUP;
126fa8792d5SIdo Barnea            // In QINQ, we also allow multiple 0x8100 headers
127fa8792d5SIdo Barnea            if (has_vlan && (! (m_flags & FSTAT_PARSER_QINQ_SUPP)))
128fa8792d5SIdo Barnea                return FSTAT_PARSER_E_QINQ_NOT_SUP;
129fa8792d5SIdo Barnea            has_vlan = true;
130fa8792d5SIdo Barnea            min_len += sizeof(VLANHeader);
131fa8792d5SIdo Barnea            if (len < min_len)
132fa8792d5SIdo Barnea                return FSTAT_PARSER_E_TOO_SHORT;
133fa8792d5SIdo Barnea            vlan = (VLANHeader *)p;
134fa8792d5SIdo Barnea            p += sizeof(VLANHeader);
135fa8792d5SIdo Barnea            next_hdr = vlan->getNextProtocolHostOrder();
136fa8792d5SIdo Barnea            break;
137fa8792d5SIdo Barnea        case EthernetHeader::Protocol::MPLS_Unicast :
138fa8792d5SIdo Barnea        case EthernetHeader::Protocol::MPLS_Multicast :
139fa8792d5SIdo Barnea            if (! (m_flags & FSTAT_PARSER_MPLS_SUPP))
140fa8792d5SIdo Barnea                return FSTAT_PARSER_E_MPLS_NOT_SUP;
141f0ab9ebaSIdo Barnea            break;
142f0ab9ebaSIdo Barnea        default:
143fa8792d5SIdo Barnea            return FSTAT_PARSER_E_UNKNOWN_HDR;
144f0ab9ebaSIdo Barnea        }
145f0ab9ebaSIdo Barnea    }
146f0ab9ebaSIdo Barnea
147fa8792d5SIdo Barnea    if (unlikely(m_flags & FSTAT_PARSER_VLAN_NEEDED) && ! has_vlan) {
148fa8792d5SIdo Barnea        return FSTAT_PARSER_E_VLAN_NEEDED;
149fa8792d5SIdo Barnea    }
150fa8792d5SIdo Barnea    return FSTAT_PARSER_E_OK;
151f0ab9ebaSIdo Barnea}
152f0ab9ebaSIdo Barnea
153fa606839SIdo Barnea// arg is uint32_t in below two functions because we want same function to work for IPv4 and IPv6
154810dd7d0SIdo Barneaint CFlowStatParser::get_ip_id(uint32_t &ip_id) {
155810dd7d0SIdo Barnea    if (m_ipv4) {
156810dd7d0SIdo Barnea        ip_id = m_ipv4->getId();
157810dd7d0SIdo Barnea        return 0;
158810dd7d0SIdo Barnea    }
159f0ab9ebaSIdo Barnea
160810dd7d0SIdo Barnea    if (m_ipv6) {
161810dd7d0SIdo Barnea        ip_id = m_ipv6->getFlowLabel();
162810dd7d0SIdo Barnea        return 0;
163810dd7d0SIdo Barnea    }
164f0ab9ebaSIdo Barnea
165810dd7d0SIdo Barnea    return -1;
166f0ab9ebaSIdo Barnea}
167f0ab9ebaSIdo Barnea
168fa606839SIdo Barneavoid CFlowStatParser::set_ip_id(uint32_t new_id) {
169810dd7d0SIdo Barnea    if (m_ipv4) {
170fa606839SIdo Barnea        uint16_t ipv4_ip_id = (uint16_t) new_id;
171810dd7d0SIdo Barnea        // Updating checksum, not recalculating, so if someone put bad checksum on purpose, it will stay bad
172fa606839SIdo Barnea        m_ipv4->updateCheckSum(PKT_NTOHS(m_ipv4->getId()), PKT_NTOHS(ipv4_ip_id));
173fa606839SIdo Barnea        m_ipv4->setId(ipv4_ip_id);
174810dd7d0SIdo Barnea    }
175f0ab9ebaSIdo Barnea
176810dd7d0SIdo Barnea    if (m_ipv6) {
177810dd7d0SIdo Barnea        m_ipv6->setFlowLabel(new_id);
178810dd7d0SIdo Barnea    }
179f0ab9ebaSIdo Barnea}
180f0ab9ebaSIdo Barnea
181fa606839SIdo Barnea// In Mellanox and VIC cards, we use TOS to mark packets to go to CPU
182fa606839SIdo Barneavoid CFlowStatParser::set_tos_to_cpu() {
183fa606839SIdo Barnea    if (m_ipv4) {
184fa606839SIdo Barnea        // Updating checksum, not recalculating, so if someone put bad checksum on purpose, it will stay bad
185fa606839SIdo Barnea        m_ipv4->updateCheckSum(PKT_NTOHS(m_ipv4->getFirstWord()), PKT_NTOHS(m_ipv4->getFirstWord()
186fa606839SIdo Barnea                                                                            | TOS_GO_TO_CPU));
187fa606839SIdo Barnea        m_ipv4->setTOS(m_ipv4->getTOS() | TOS_GO_TO_CPU);
188fa606839SIdo Barnea    }
189fa606839SIdo Barnea
190fa606839SIdo Barnea    if (m_ipv6) {
191fa606839SIdo Barnea        m_ipv6->setTrafficClass(m_ipv6->getTrafficClass() | TOS_GO_TO_CPU);
192fa606839SIdo Barnea    }
193fa606839SIdo Barnea}
194fa606839SIdo Barnea
195fa606839SIdo Barnea
1967cecfd8aSIdo Barneaint CFlowStatParser::get_l3_proto(uint16_t &proto) {
1977cecfd8aSIdo Barnea    if (m_ipv4) {
1987cecfd8aSIdo Barnea        proto = EthernetHeader::Protocol::IP;
1997cecfd8aSIdo Barnea        return 0;
2007cecfd8aSIdo Barnea    }
2017cecfd8aSIdo Barnea
2027cecfd8aSIdo Barnea    if (m_ipv6) {
2037cecfd8aSIdo Barnea        proto = EthernetHeader::Protocol::IPv6;
2047cecfd8aSIdo Barnea        return 0;
2057cecfd8aSIdo Barnea    }
2067cecfd8aSIdo Barnea
2077cecfd8aSIdo Barnea    return -1;
2087cecfd8aSIdo Barnea}
2097cecfd8aSIdo Barnea
2104099c61aSIdo Barneaint CFlowStatParser::get_l4_proto(uint8_t &proto) {
211810dd7d0SIdo Barnea    if (m_ipv4) {
212810dd7d0SIdo Barnea        proto = m_ipv4->getProtocol();
213810dd7d0SIdo Barnea        return 0;
214810dd7d0SIdo Barnea    }
215f0ab9ebaSIdo Barnea
216810dd7d0SIdo Barnea    if (m_ipv6) {
217b5c09779SIdo Barnea        if (!m_l4) {
218b5c09779SIdo Barnea            uint16_t payload_len;
219b5c09779SIdo Barnea            // in IPv6 we calculate l4 proto only when running get_payload_len
220b5c09779SIdo Barnea            get_payload_len(m_start, m_len, payload_len);
221b5c09779SIdo Barnea        }
222b5c09779SIdo Barnea        proto = m_l4_proto;
223810dd7d0SIdo Barnea        return 0;
224810dd7d0SIdo Barnea    }
225f0ab9ebaSIdo Barnea
226810dd7d0SIdo Barnea    return -1;
227810dd7d0SIdo Barnea}
228810dd7d0SIdo Barnea
229810dd7d0SIdo Barneauint16_t CFlowStatParser::get_pkt_size() {
230810dd7d0SIdo Barnea    uint16_t ip_len=0;
231810dd7d0SIdo Barnea
232810dd7d0SIdo Barnea    if (m_ipv4) {
233810dd7d0SIdo Barnea        ip_len = m_ipv4->getTotalLength();
234810dd7d0SIdo Barnea    } else if (m_ipv6) {
235810dd7d0SIdo Barnea        ip_len = m_ipv6->getHeaderLength() + m_ipv6->getPayloadLen();
236810dd7d0SIdo Barnea    }
237810dd7d0SIdo Barnea    return ( ip_len + m_vlan_offset + ETH_HDR_LEN);
238810dd7d0SIdo Barnea}
239810dd7d0SIdo Barnea
240810dd7d0SIdo Barneauint8_t CFlowStatParser::get_ttl(){
241810dd7d0SIdo Barnea    if (m_ipv4) {
242810dd7d0SIdo Barnea        return ( m_ipv4->getTimeToLive() );
243810dd7d0SIdo Barnea    }
244810dd7d0SIdo Barnea    if (m_ipv6) {
245810dd7d0SIdo Barnea        return ( m_ipv6->getHopLimit() );
246810dd7d0SIdo Barnea    }
247810dd7d0SIdo Barnea    return (0);
248f0ab9ebaSIdo Barnea}
249f0ab9ebaSIdo Barnea
250aa11dd2eSIdo Barnea// calculate the payload len. Do not want to do this in parse(), since this is required only in
25192880804SIdo Barnea// specific cases, while parse is used in many places (including on packet RX path, where we want to be as fast as possible)
252aa11dd2eSIdo Barneaint CFlowStatParser::get_payload_len(uint8_t *p, uint16_t len, uint16_t &payload_len) {
253aa11dd2eSIdo Barnea    uint16_t l2_header_len;
254aa11dd2eSIdo Barnea    uint16_t l4_header_len;
255810dd7d0SIdo Barnea    uint8_t *p_l3 = NULL;
256aa11dd2eSIdo Barnea    uint8_t *p_l4 = NULL;
257aa11dd2eSIdo Barnea    TCPHeader *p_tcp = NULL;
258810dd7d0SIdo Barnea    if (!m_ipv4 && !m_ipv6) {
259aa11dd2eSIdo Barnea        payload_len = 0;
260aa11dd2eSIdo Barnea        return -1;
261aa11dd2eSIdo Barnea    }
262aa11dd2eSIdo Barnea
263810dd7d0SIdo Barnea    if (m_ipv4) {
264810dd7d0SIdo Barnea        l2_header_len = ((uint8_t *)m_ipv4) - p;
265b5c09779SIdo Barnea        m_l4_proto = m_ipv4->getProtocol();
266810dd7d0SIdo Barnea        p_l3 = (uint8_t *)m_ipv4;
26715499f72SIdo Barnea        p_l4 = p_l3 + m_ipv4->getHeaderLength();
268810dd7d0SIdo Barnea    } else if (m_ipv6) {
269810dd7d0SIdo Barnea        l2_header_len = ((uint8_t *)m_ipv6) - p;
27015499f72SIdo Barnea        m_l4_proto = m_ipv6->getl4Proto((uint8_t *)m_ipv6, len - l2_header_len, p_l4);
271810dd7d0SIdo Barnea    }
272810dd7d0SIdo Barnea
273b5c09779SIdo Barnea    switch (m_l4_proto) {
274aa11dd2eSIdo Barnea    case IPPROTO_UDP:
275aa11dd2eSIdo Barnea        l4_header_len = 8;
276aa11dd2eSIdo Barnea        break;
277aa11dd2eSIdo Barnea    case IPPROTO_TCP:
278aa11dd2eSIdo Barnea        if ((p_l4 + TCP_HEADER_LEN) > (p + len)) {
279aa11dd2eSIdo Barnea            //Not enough space for TCP header
280aa11dd2eSIdo Barnea            payload_len = 0;
281b5c09779SIdo Barnea            return -2;
282aa11dd2eSIdo Barnea        }
283aa11dd2eSIdo Barnea        p_tcp = (TCPHeader *)p_l4;
284aa11dd2eSIdo Barnea        l4_header_len = p_tcp->getHeaderLength();
285aa11dd2eSIdo Barnea        break;
286aa11dd2eSIdo Barnea    case IPPROTO_ICMP:
287810dd7d0SIdo Barnea        l4_header_len = 8;
288810dd7d0SIdo Barnea        break;
289aa11dd2eSIdo Barnea    default:
290aa11dd2eSIdo Barnea        l4_header_len = 0;
29115499f72SIdo Barnea        break;
292aa11dd2eSIdo Barnea    }
293aa11dd2eSIdo Barnea
29415499f72SIdo Barnea    payload_len = len - (p_l4 - p) - l4_header_len;
29515499f72SIdo Barnea
29615499f72SIdo Barnea    if (payload_len <= 0) {
297aa11dd2eSIdo Barnea        payload_len = 0;
298b5c09779SIdo Barnea        return -3;
299aa11dd2eSIdo Barnea    }
300aa11dd2eSIdo Barnea
301aa11dd2eSIdo Barnea    return 0;
302aa11dd2eSIdo Barnea}
303aa11dd2eSIdo Barnea
304f0ab9ebaSIdo Barneastatic const uint16_t TEST_IP_ID = 0xabcd;
30592880804SIdo Barneastatic const uint16_t TEST_IP_ID2 = 0xabcd;
306aa11dd2eSIdo Barneastatic const uint8_t TEST_L4_PROTO = IPPROTO_UDP;
307f0ab9ebaSIdo Barnea
30892880804SIdo Barnea
30992880804SIdo Barneaint CFlowStatParserTest::verify_pkt_one_parser(uint8_t * p, uint16_t pkt_size, uint16_t payload_len, uint32_t ip_id
310fa8792d5SIdo Barnea                                               , uint8_t l4_proto, CFlowStatParser &parser, CFlowStatParser_err_t exp_ret) {
311fa8792d5SIdo Barnea    CFlowStatParser_err_t ret;
31292880804SIdo Barnea    uint32_t pkt_ip_id = 0;
31392880804SIdo Barnea    uint8_t pkt_l4_proto;
31492880804SIdo Barnea    uint16_t pkt_payload_len;
31592880804SIdo Barnea
31692880804SIdo Barnea    ret = parser.parse(p, pkt_size);
317fa8792d5SIdo Barnea    assert(ret == exp_ret);
318fa8792d5SIdo Barnea
319fa8792d5SIdo Barnea    if (ret == FSTAT_PARSER_E_OK) {
32092880804SIdo Barnea        parser.get_ip_id(pkt_ip_id);
32192880804SIdo Barnea        assert(pkt_ip_id == ip_id);
32292880804SIdo Barnea        parser.set_ip_id(TEST_IP_ID2);
32392880804SIdo Barnea        // utl_DumpBuffer(stdout, test_pkt, sizeof(test_pkt), 0);
32492880804SIdo Barnea        parser.get_ip_id(ip_id);
32592880804SIdo Barnea        assert(ip_id == TEST_IP_ID2);
32692880804SIdo Barnea        if (parser.m_ipv4)
32792880804SIdo Barnea            assert(parser.m_ipv4->isChecksumOK() == true);
32892880804SIdo Barnea        assert(parser.get_l4_proto(pkt_l4_proto) == 0);
32992880804SIdo Barnea        assert(pkt_l4_proto == l4_proto);
330fa8792d5SIdo Barnea        uint32_t ret2 = parser.get_payload_len(p, pkt_size, pkt_payload_len);
331fa8792d5SIdo Barnea        assert(ret2 == 0);
33292880804SIdo Barnea        assert(pkt_payload_len == payload_len);
33392880804SIdo Barnea    }
334aa11dd2eSIdo Barnea
33592880804SIdo Barnea    return 0;
33692880804SIdo Barnea}
33792880804SIdo Barnea
33892880804SIdo Barneaint CFlowStatParserTest::verify_pkt(uint8_t *p, uint16_t pkt_size, uint16_t payload_len, uint32_t ip_id, uint8_t l4_proto
339fa8792d5SIdo Barnea                                    , CFlowStatParserTest_exp_err_t exp_err) {
340fa8792d5SIdo Barnea    int ret;
341fa8792d5SIdo Barnea    int ret_val = 0;
342fa8792d5SIdo Barnea    CFlowStatParser parser_hw(CFlowStatParser::FLOW_STAT_PARSER_MODE_HW);
343fa8792d5SIdo Barnea    CFlowStatParser parser_sw(CFlowStatParser::FLOW_STAT_PARSER_MODE_SW);
344fa8792d5SIdo Barnea    CFlowStatParser parser82599(CFlowStatParser::FLOW_STAT_PARSER_MODE_82599);
345fa8792d5SIdo Barnea    CFlowStatParser parser82599_vlan(CFlowStatParser::FLOW_STAT_PARSER_MODE_82599_vlan);
34692880804SIdo Barnea
34792880804SIdo Barnea    printf ("  ");
348fa8792d5SIdo Barnea    printf("Hardware mode parser");
349fa8792d5SIdo Barnea    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser_hw, exp_err.m_hw);
350fa8792d5SIdo Barnea    ret_val = ret;
351fa8792d5SIdo Barnea    if (ret == 0)
352fa8792d5SIdo Barnea        printf("-OK");
353fa8792d5SIdo Barnea    else {
354fa8792d5SIdo Barnea        printf("-BAD");
355fa8792d5SIdo Barnea        ret_val = -1;
35692880804SIdo Barnea    }
357fa8792d5SIdo Barnea
358fa8792d5SIdo Barnea    printf(", software mode parser");
359fa8792d5SIdo Barnea    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser_sw, exp_err.m_sw);
360fa8792d5SIdo Barnea    ret_val = ret;
361fa8792d5SIdo Barnea    if (ret == 0)
362fa8792d5SIdo Barnea        printf("-OK");
363fa8792d5SIdo Barnea    else {
364fa8792d5SIdo Barnea        printf("-BAD");
365fa8792d5SIdo Barnea        ret_val = -1;
36692880804SIdo Barnea    }
367fa8792d5SIdo Barnea
368fa8792d5SIdo Barnea    printf(", 82599 parser");
369fa8792d5SIdo Barnea    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser82599, exp_err.m_82599);
370fa8792d5SIdo Barnea    ret_val |= ret;
371fa8792d5SIdo Barnea    if (ret == 0)
372fa8792d5SIdo Barnea        printf("-OK");
373fa8792d5SIdo Barnea    else {
374fa8792d5SIdo Barnea        printf("-BAD");
375fa8792d5SIdo Barnea        ret_val = -1;
376fa8792d5SIdo Barnea    }
377fa8792d5SIdo Barnea
378fa8792d5SIdo Barnea    printf(", 82599 vlan parser");
379fa8792d5SIdo Barnea    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser82599_vlan, exp_err.m_82599_vlan);
380fa8792d5SIdo Barnea    ret_val |= ret;
381fa8792d5SIdo Barnea    if (ret == 0)
382fa8792d5SIdo Barnea        printf("-OK");
383fa8792d5SIdo Barnea    else {
384fa8792d5SIdo Barnea        printf("-BAD");
385fa8792d5SIdo Barnea        ret_val = -1;
38692880804SIdo Barnea    }
387fa8792d5SIdo Barnea
38892880804SIdo Barnea    printf("\n");
38992880804SIdo Barnea
39092880804SIdo Barnea    return 0;
39192880804SIdo Barnea}
39292880804SIdo Barnea
393fa8792d5SIdo Barneaint CFlowStatParserTest::test_one_pkt(const char *name, uint16_t ether_type, uint8_t l4_proto, int vlan_num
394fa8792d5SIdo Barnea                                      , CFlowStatParserTest_exp_err_t exp_err) {
39592880804SIdo Barnea    CTestPktGen gen;
39692880804SIdo Barnea    uint8_t *p;
39792880804SIdo Barnea    int pkt_size;
39892880804SIdo Barnea    uint16_t payload_len = 16;
39992880804SIdo Barnea    uint16_t pkt_flags;
40092880804SIdo Barnea    int ret = 0;
40192880804SIdo Barnea
40292880804SIdo Barnea    printf("%s - ", name);
40392880804SIdo Barnea
404fa8792d5SIdo Barnea    // in case of IPv6, we add rx_check header, just to make sure we know how to parse with multiple headers
405fa8792d5SIdo Barnea    switch (vlan_num) {
406fa8792d5SIdo Barnea    case 1:
407b5c09779SIdo Barnea        pkt_flags = DPF_VLAN | DPF_RXCHECK;
408fa8792d5SIdo Barnea        break;
409fa8792d5SIdo Barnea    case 0:
410b5c09779SIdo Barnea        pkt_flags = DPF_RXCHECK;
411fa8792d5SIdo Barnea        break;
412fa8792d5SIdo Barnea    case 2:
413fa8792d5SIdo Barnea        pkt_flags = DPF_QINQ | DPF_RXCHECK;
414fa8792d5SIdo Barnea        break;
415fa8792d5SIdo Barnea    default:
416fa8792d5SIdo Barnea        printf("Internal error: vlan_num = %d\n", vlan_num);
417fa8792d5SIdo Barnea        exit(-1);
41892880804SIdo Barnea    }
41992880804SIdo Barnea
42092880804SIdo Barnea    p = (uint8_t *)gen.create_test_pkt(ether_type, l4_proto, 255, TEST_IP_ID, pkt_flags, payload_len, pkt_size);
421fa8792d5SIdo Barnea    ret = verify_pkt(p, pkt_size, payload_len, TEST_IP_ID, l4_proto, exp_err);
42292880804SIdo Barnea    free(p);
42392880804SIdo Barnea
42492880804SIdo Barnea    return ret;
42592880804SIdo Barnea}
426f0ab9ebaSIdo Barnea
42792880804SIdo Barneaint CFlowStatParserTest::test() {
42892880804SIdo Barnea    uint8_t tcp = IPPROTO_TCP, udp = IPPROTO_UDP, icmp = IPPROTO_ICMP;
42992880804SIdo Barnea    uint16_t ipv4 = EthernetHeader::Protocol::IP, ipv6 = EthernetHeader::Protocol::IPv6;
430fa8792d5SIdo Barnea    CFlowStatParserTest_exp_err_t exp_ret;
431fa8792d5SIdo Barnea
432fa8792d5SIdo Barnea    // no vlan tests
433fa8792d5SIdo Barnea    exp_ret.m_hw = FSTAT_PARSER_E_OK;
434fa8792d5SIdo Barnea    exp_ret.m_sw = FSTAT_PARSER_E_OK;
435fa8792d5SIdo Barnea    exp_ret.m_82599 = FSTAT_PARSER_E_OK;
436fa8792d5SIdo Barnea    exp_ret.m_82599_vlan = FSTAT_PARSER_E_VLAN_NEEDED;
437fa8792d5SIdo Barnea    test_one_pkt("IPv4 TCP", ipv4, tcp, 0, exp_ret);
438fa8792d5SIdo Barnea    test_one_pkt("IPv4 UDP", ipv4, udp, 0, exp_ret);
439fa8792d5SIdo Barnea    test_one_pkt("IPv4 ICMP", ipv4, icmp, 0, exp_ret);
440fa8792d5SIdo Barnea    test_one_pkt("IPv6 TCP", ipv6, tcp, 0, exp_ret);
441fa8792d5SIdo Barnea    test_one_pkt("IPv6 UDP", ipv6, udp, 0, exp_ret);
442fa8792d5SIdo Barnea    test_one_pkt("IPv4 IGMP", ipv4, IPPROTO_IGMP, 0, exp_ret);
443fa8792d5SIdo Barnea
444fa8792d5SIdo Barnea    // vlan tests
445fa8792d5SIdo Barnea    exp_ret.m_hw = FSTAT_PARSER_E_OK;
446fa8792d5SIdo Barnea    exp_ret.m_sw = FSTAT_PARSER_E_OK;
447fa8792d5SIdo Barnea    exp_ret.m_82599 = FSTAT_PARSER_E_VLAN_NOT_SUP;
448fa8792d5SIdo Barnea    exp_ret.m_82599_vlan = FSTAT_PARSER_E_OK;;
449fa8792d5SIdo Barnea    test_one_pkt("IPv4 TCP VLAN", ipv4, tcp, 1, exp_ret);
450fa8792d5SIdo Barnea    test_one_pkt("IPv4 UDP VLAN", ipv4, udp, 1, exp_ret);
451fa8792d5SIdo Barnea    test_one_pkt("IPv4 ICMP VLAN", ipv4, icmp, 1, exp_ret);
452fa8792d5SIdo Barnea    test_one_pkt("IPv6 TCP VLAN", ipv6, tcp, 1, exp_ret);
453fa8792d5SIdo Barnea    test_one_pkt("IPv6 UDP VLAN", ipv6, udp, 1, exp_ret);
454fa8792d5SIdo Barnea
455fa8792d5SIdo Barnea    // qinq tests
456fa8792d5SIdo Barnea    exp_ret.m_hw = FSTAT_PARSER_E_QINQ_NOT_SUP;
457fa8792d5SIdo Barnea    exp_ret.m_sw = FSTAT_PARSER_E_OK;
458fa8792d5SIdo Barnea    exp_ret.m_82599 = FSTAT_PARSER_E_QINQ_NOT_SUP;
459fa8792d5SIdo Barnea    exp_ret.m_82599_vlan = FSTAT_PARSER_E_QINQ_NOT_SUP;
460fa8792d5SIdo Barnea    test_one_pkt("IPv4 TCP QINQ", ipv4, tcp, 2, exp_ret);
461fa8792d5SIdo Barnea    test_one_pkt("IPv4 UDP QINQ", ipv4, udp, 2, exp_ret);
462fa8792d5SIdo Barnea    test_one_pkt("IPv4 ICMP QINQ", ipv4, icmp, 2, exp_ret);
463fa8792d5SIdo Barnea    test_one_pkt("IPv6 TCP QINQ", ipv6, tcp, 2, exp_ret);
464fa8792d5SIdo Barnea    test_one_pkt("IPv6 UDP QINQ", ipv6, udp, 2, exp_ret);
465fa8792d5SIdo Barnea
466fa8792d5SIdo Barnea    // bad packets tests
467fa8792d5SIdo Barnea    exp_ret.m_hw = FSTAT_PARSER_E_UNKNOWN_HDR;
468fa8792d5SIdo Barnea    exp_ret.m_sw = FSTAT_PARSER_E_UNKNOWN_HDR;
469fa8792d5SIdo Barnea    exp_ret.m_82599 = FSTAT_PARSER_E_UNKNOWN_HDR;
470fa8792d5SIdo Barnea    exp_ret.m_82599_vlan = FSTAT_PARSER_E_UNKNOWN_HDR;
471fa8792d5SIdo Barnea    test_one_pkt("BAD l3 type", 0xaa, icmp, 0, exp_ret);
472fa8792d5SIdo Barnea
473fa8792d5SIdo Barnea    exp_ret.m_82599 = FSTAT_PARSER_E_VLAN_NOT_SUP;
474fa8792d5SIdo Barnea    test_one_pkt("VLAN + BAD l3 type", 0xaa, icmp, 1, exp_ret);
475f0ab9ebaSIdo Barnea
476f0ab9ebaSIdo Barnea    return 0;
477f0ab9ebaSIdo Barnea}
478810dd7d0SIdo Barnea
479fa8792d5SIdo BarneaCFlowStatParser_err_t CPassAllParser::parse(uint8_t *pkt, uint16_t len) {
48058f1ee52SIdo Barnea    reset();
48158f1ee52SIdo Barnea
48258f1ee52SIdo Barnea    if (len < ETH_HDR_LEN)
483fa8792d5SIdo Barnea        return FSTAT_PARSER_E_TOO_SHORT;
48458f1ee52SIdo Barnea
48558f1ee52SIdo Barnea    m_len = len;
48658f1ee52SIdo Barnea
487fa8792d5SIdo Barnea    return FSTAT_PARSER_E_OK;
48858f1ee52SIdo Barnea}
48958f1ee52SIdo Barnea
490810dd7d0SIdo Barneabool CSimplePacketParser::Parse(){
491810dd7d0SIdo Barnea
492810dd7d0SIdo Barnea    rte_mbuf_t * m=m_m;
493810dd7d0SIdo Barnea    uint8_t *p=rte_pktmbuf_mtod(m, uint8_t*);
494810dd7d0SIdo Barnea    EthernetHeader *m_ether = (EthernetHeader *)p;
495810dd7d0SIdo Barnea    IPHeader * ipv4=0;
496810dd7d0SIdo Barnea    IPv6Header * ipv6=0;
497810dd7d0SIdo Barnea    m_vlan_offset=0;
498810dd7d0SIdo Barnea    uint8_t protocol = 0;
499810dd7d0SIdo Barnea
500810dd7d0SIdo Barnea    // Retrieve the protocol type from the packet
501810dd7d0SIdo Barnea    switch( m_ether->getNextProtocol() ) {
502810dd7d0SIdo Barnea    case EthernetHeader::Protocol::IP :
503810dd7d0SIdo Barnea        // IPv4 packet
504810dd7d0SIdo Barnea        ipv4=(IPHeader *)(p+14);
505810dd7d0SIdo Barnea        m_l4 = (uint8_t *)ipv4 + ipv4->getHeaderLength();
506810dd7d0SIdo Barnea        protocol = ipv4->getProtocol();
507810dd7d0SIdo Barnea        break;
508810dd7d0SIdo Barnea    case EthernetHeader::Protocol::IPv6 :
509810dd7d0SIdo Barnea        // IPv6 packet
510810dd7d0SIdo Barnea        ipv6=(IPv6Header *)(p+14);
511810dd7d0SIdo Barnea        m_l4 = (uint8_t *)ipv6 + ipv6->getHeaderLength();
512810dd7d0SIdo Barnea        protocol = ipv6->getNextHdr();
513810dd7d0SIdo Barnea        break;
514810dd7d0SIdo Barnea    case EthernetHeader::Protocol::VLAN :
515810dd7d0SIdo Barnea        m_vlan_offset = 4;
516810dd7d0SIdo Barnea        switch ( m_ether->getVlanProtocol() ){
517810dd7d0SIdo Barnea        case EthernetHeader::Protocol::IP:
518810dd7d0SIdo Barnea            // IPv4 packet
519810dd7d0SIdo Barnea            ipv4=(IPHeader *)(p+18);
520810dd7d0SIdo Barnea            m_l4 = (uint8_t *)ipv4 + ipv4->getHeaderLength();
521810dd7d0SIdo Barnea            protocol = ipv4->getProtocol();
522810dd7d0SIdo Barnea            break;
523810dd7d0SIdo Barnea        case EthernetHeader::Protocol::IPv6 :
524810dd7d0SIdo Barnea            // IPv6 packet
525810dd7d0SIdo Barnea            ipv6=(IPv6Header *)(p+18);
526810dd7d0SIdo Barnea            m_l4 = (uint8_t *)ipv6 + ipv6->getHeaderLength();
527810dd7d0SIdo Barnea            protocol = ipv6->getNextHdr();
528810dd7d0SIdo Barnea            break;
529810dd7d0SIdo Barnea        default:
530810dd7d0SIdo Barnea        break;
531810dd7d0SIdo Barnea        }
532810dd7d0SIdo Barnea        default:
533810dd7d0SIdo Barnea        break;
534810dd7d0SIdo Barnea    }
535810dd7d0SIdo Barnea    m_protocol =protocol;
536810dd7d0SIdo Barnea    m_ipv4=ipv4;
537810dd7d0SIdo Barnea    m_ipv6=ipv6;
538810dd7d0SIdo Barnea
539810dd7d0SIdo Barnea    if ( protocol == 0 ){
540810dd7d0SIdo Barnea        return (false);
541810dd7d0SIdo Barnea    }
542810dd7d0SIdo Barnea    return (true);
543810dd7d0SIdo Barnea}