1/*
2  Ido Barnea
3  Cisco Systems, Inc.
4*/
5
6/*
7  Copyright (c) 2016-2017 Cisco Systems, Inc.
8
9  Licensed under the Apache License, Version 2.0 (the "License");
10  you may not use this file except in compliance with the License.
11  You may obtain a copy of the License at
12
13  http://www.apache.org/licenses/LICENSE-2.0
14
15  Unless required by applicable law or agreed to in writing, software
16  distributed under the License is distributed on an "AS IS" BASIS,
17  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  See the License for the specific language governing permissions and
19  limitations under the License.
20*/
21
22#include <netinet/in.h>
23#include "common/basic_utils.h"
24#include "common/Network/Packet/EthernetHeader.h"
25#include "common/Network/Packet/IPHeader.h"
26#include "common/Network/Packet/IPv6Header.h"
27#include "common/Network/Packet/TcpHeader.h"
28#include "common/Network/Packet/VLANHeader.h"
29#include "pkt_gen.h"
30#include "flow_stat_parser.h"
31#include "bp_sim.h"
32
33CFlowStatParser::CFlowStatParser(CFlowStatParser_mode mode) {
34    reset();
35    switch(mode) {
36    case FLOW_STAT_PARSER_MODE_HW:
37        m_flags = FSTAT_PARSER_VLAN_SUPP;
38        break;
39    case FLOW_STAT_PARSER_MODE_SW:
40        m_flags = FSTAT_PARSER_VLAN_SUPP | FSTAT_PARSER_QINQ_SUPP;
41        break;
42    // In 82599 we configure the card to either always use VLAN, or never use
43    case FLOW_STAT_PARSER_MODE_82599:
44        m_flags = 0;
45        break;
46    case FLOW_STAT_PARSER_MODE_82599_vlan:
47        m_flags = FSTAT_PARSER_VLAN_SUPP | FSTAT_PARSER_VLAN_NEEDED;
48        break;
49    }
50}
51
52void CFlowStatParser::reset() {
53    m_start = 0;
54    m_len = 0;
55    m_ipv4 = 0;
56    m_ipv6 = 0;
57    m_l4_proto = 0;
58    m_l4 = 0;
59}
60
61std::string CFlowStatParser::get_error_str(CFlowStatParser_err_t err) {
62    std::string base = "Failed parsing given packet for flow stat. ";
63
64    switch(err) {
65    case FSTAT_PARSER_E_OK:
66        return "";
67    case FSTAT_PARSER_E_TOO_SHORT:
68        return base + " Packet too short";
69    case FSTAT_PARSER_E_SHORT_IP_HDR:
70        return base + " Packet IP header too short";
71    case FSTAT_PARSER_E_VLAN_NOT_SUP:
72        return base + " NIC does not support vlan (for 82599, you can try starting TRex with --vlan)";
73    case FSTAT_PARSER_E_QINQ_NOT_SUP:
74        return base + " NIC does not support Q in Q";
75    case FSTAT_PARSER_E_MPLS_NOT_SUP:
76        return base + " NIC does not support MPLS";
77    case FSTAT_PARSER_E_UNKNOWN_HDR:
78        return base + " NIC does not support given L2 header type";
79    case FSTAT_PARSER_E_VLAN_NEEDED:
80        return base + " NIC does not support packets with no vlan (If you used --vlan command line arg, try to remove it)";
81        return "";
82    }
83
84    return "";
85}
86
87CFlowStatParser_err_t CFlowStatParser::parse(uint8_t *p, uint16_t len) {
88    EthernetHeader *ether = (EthernetHeader *)p;
89    VLANHeader *vlan;
90    int min_len = ETH_HDR_LEN;
91    uint16_t next_hdr = ether->getNextProtocol();
92    bool finished = false;
93    bool has_vlan = false;
94    reset();
95
96    m_start = p;
97    m_len = len;
98    if (len < min_len)
99        return FSTAT_PARSER_E_TOO_SHORT;
100
101    p += ETH_HDR_LEN;
102    while (! finished) {
103        switch( next_hdr ) {
104        case EthernetHeader::Protocol::IP :
105            min_len += IPV4_HDR_LEN;
106            if (len < min_len)
107                return FSTAT_PARSER_E_SHORT_IP_HDR;
108            m_ipv4 = (IPHeader *) p;
109            m_l4 = ((uint8_t *)m_ipv4) + m_ipv4->getHeaderLength();
110            m_l4_proto = m_ipv4->getProtocol();
111            finished = true;
112            break;
113        case EthernetHeader::Protocol::IPv6 :
114            min_len += IPV6_HDR_LEN;
115            if (len < min_len)
116                return FSTAT_PARSER_E_SHORT_IP_HDR;
117            m_ipv6 = (IPv6Header *) p;
118            finished = true;
119            break;
120        case EthernetHeader::Protocol::QINQ :
121            if (! (m_flags & FSTAT_PARSER_QINQ_SUPP))
122                return FSTAT_PARSER_E_QINQ_NOT_SUP;
123        case EthernetHeader::Protocol::VLAN :
124            if (! (m_flags & FSTAT_PARSER_VLAN_SUPP))
125                return FSTAT_PARSER_E_VLAN_NOT_SUP;
126            // In QINQ, we also allow multiple 0x8100 headers
127            if (has_vlan && (! (m_flags & FSTAT_PARSER_QINQ_SUPP)))
128                return FSTAT_PARSER_E_QINQ_NOT_SUP;
129            has_vlan = true;
130            min_len += sizeof(VLANHeader);
131            if (len < min_len)
132                return FSTAT_PARSER_E_TOO_SHORT;
133            vlan = (VLANHeader *)p;
134            p += sizeof(VLANHeader);
135            next_hdr = vlan->getNextProtocolHostOrder();
136            break;
137        case EthernetHeader::Protocol::MPLS_Unicast :
138        case EthernetHeader::Protocol::MPLS_Multicast :
139            if (! (m_flags & FSTAT_PARSER_MPLS_SUPP))
140                return FSTAT_PARSER_E_MPLS_NOT_SUP;
141            break;
142        default:
143            return FSTAT_PARSER_E_UNKNOWN_HDR;
144        }
145    }
146
147    if (unlikely(m_flags & FSTAT_PARSER_VLAN_NEEDED) && ! has_vlan) {
148        return FSTAT_PARSER_E_VLAN_NEEDED;
149    }
150    return FSTAT_PARSER_E_OK;
151}
152
153// arg is uint32_t in below two functions because we want same function to work for IPv4 and IPv6
154int CFlowStatParser::get_ip_id(uint32_t &ip_id) {
155    if (m_ipv4) {
156        ip_id = m_ipv4->getId();
157        return 0;
158    }
159
160    if (m_ipv6) {
161        ip_id = m_ipv6->getFlowLabel();
162        return 0;
163    }
164
165    return -1;
166}
167
168void CFlowStatParser::set_ip_id(uint32_t new_id) {
169    if (m_ipv4) {
170        uint16_t ipv4_ip_id = (uint16_t) new_id;
171        // Updating checksum, not recalculating, so if someone put bad checksum on purpose, it will stay bad
172        m_ipv4->updateCheckSum(PKT_NTOHS(m_ipv4->getId()), PKT_NTOHS(ipv4_ip_id));
173        m_ipv4->setId(ipv4_ip_id);
174    }
175
176    if (m_ipv6) {
177        m_ipv6->setFlowLabel(new_id);
178    }
179}
180
181// In Mellanox and VIC cards, we use TOS to mark packets to go to CPU
182void CFlowStatParser::set_tos_to_cpu() {
183    if (m_ipv4) {
184        // Updating checksum, not recalculating, so if someone put bad checksum on purpose, it will stay bad
185        m_ipv4->updateCheckSum(PKT_NTOHS(m_ipv4->getFirstWord()), PKT_NTOHS(m_ipv4->getFirstWord()
186                                                                            | TOS_GO_TO_CPU));
187        m_ipv4->setTOS(m_ipv4->getTOS() | TOS_GO_TO_CPU);
188    }
189
190    if (m_ipv6) {
191        m_ipv6->setTrafficClass(m_ipv6->getTrafficClass() | TOS_GO_TO_CPU);
192    }
193}
194
195
196int CFlowStatParser::get_l3_proto(uint16_t &proto) {
197    if (m_ipv4) {
198        proto = EthernetHeader::Protocol::IP;
199        return 0;
200    }
201
202    if (m_ipv6) {
203        proto = EthernetHeader::Protocol::IPv6;
204        return 0;
205    }
206
207    return -1;
208}
209
210int CFlowStatParser::get_l4_proto(uint8_t &proto) {
211    if (m_ipv4) {
212        proto = m_ipv4->getProtocol();
213        return 0;
214    }
215
216    if (m_ipv6) {
217        if (!m_l4) {
218            uint16_t payload_len;
219            // in IPv6 we calculate l4 proto only when running get_payload_len
220            get_payload_len(m_start, m_len, payload_len);
221        }
222        proto = m_l4_proto;
223        return 0;
224    }
225
226    return -1;
227}
228
229uint16_t CFlowStatParser::get_pkt_size() {
230    uint16_t ip_len=0;
231
232    if (m_ipv4) {
233        ip_len = m_ipv4->getTotalLength();
234    } else if (m_ipv6) {
235        ip_len = m_ipv6->getHeaderLength() + m_ipv6->getPayloadLen();
236    }
237    return ( ip_len + m_vlan_offset + ETH_HDR_LEN);
238}
239
240uint8_t CFlowStatParser::get_ttl(){
241    if (m_ipv4) {
242        return ( m_ipv4->getTimeToLive() );
243    }
244    if (m_ipv6) {
245        return ( m_ipv6->getHopLimit() );
246    }
247    return (0);
248}
249
250// calculate the payload len. Do not want to do this in parse(), since this is required only in
251// specific cases, while parse is used in many places (including on packet RX path, where we want to be as fast as possible)
252int CFlowStatParser::get_payload_len(uint8_t *p, uint16_t len, uint16_t &payload_len) {
253    uint16_t l2_header_len;
254    uint16_t l4_header_len;
255    uint8_t *p_l3 = NULL;
256    uint8_t *p_l4 = NULL;
257    TCPHeader *p_tcp = NULL;
258    if (!m_ipv4 && !m_ipv6) {
259        payload_len = 0;
260        return -1;
261    }
262
263    if (m_ipv4) {
264        l2_header_len = ((uint8_t *)m_ipv4) - p;
265        m_l4_proto = m_ipv4->getProtocol();
266        p_l3 = (uint8_t *)m_ipv4;
267        p_l4 = p_l3 + m_ipv4->getHeaderLength();
268    } else if (m_ipv6) {
269        l2_header_len = ((uint8_t *)m_ipv6) - p;
270        m_l4_proto = m_ipv6->getl4Proto((uint8_t *)m_ipv6, len - l2_header_len, p_l4);
271    }
272
273    switch (m_l4_proto) {
274    case IPPROTO_UDP:
275        l4_header_len = 8;
276        break;
277    case IPPROTO_TCP:
278        if ((p_l4 + TCP_HEADER_LEN) > (p + len)) {
279            //Not enough space for TCP header
280            payload_len = 0;
281            return -2;
282        }
283        p_tcp = (TCPHeader *)p_l4;
284        l4_header_len = p_tcp->getHeaderLength();
285        break;
286    case IPPROTO_ICMP:
287        l4_header_len = 8;
288        break;
289    default:
290        l4_header_len = 0;
291        break;
292    }
293
294    payload_len = len - (p_l4 - p) - l4_header_len;
295
296    if (payload_len <= 0) {
297        payload_len = 0;
298        return -3;
299    }
300
301    return 0;
302}
303
304static const uint16_t TEST_IP_ID = 0xabcd;
305static const uint16_t TEST_IP_ID2 = 0xabcd;
306static const uint8_t TEST_L4_PROTO = IPPROTO_UDP;
307
308
309int CFlowStatParserTest::verify_pkt_one_parser(uint8_t * p, uint16_t pkt_size, uint16_t payload_len, uint32_t ip_id
310                                               , uint8_t l4_proto, CFlowStatParser &parser, CFlowStatParser_err_t exp_ret) {
311    CFlowStatParser_err_t ret;
312    uint32_t pkt_ip_id = 0;
313    uint8_t pkt_l4_proto;
314    uint16_t pkt_payload_len;
315
316    ret = parser.parse(p, pkt_size);
317    assert(ret == exp_ret);
318
319    if (ret == FSTAT_PARSER_E_OK) {
320        parser.get_ip_id(pkt_ip_id);
321        assert(pkt_ip_id == ip_id);
322        parser.set_ip_id(TEST_IP_ID2);
323        // utl_DumpBuffer(stdout, test_pkt, sizeof(test_pkt), 0);
324        parser.get_ip_id(ip_id);
325        assert(ip_id == TEST_IP_ID2);
326        if (parser.m_ipv4)
327            assert(parser.m_ipv4->isChecksumOK() == true);
328        assert(parser.get_l4_proto(pkt_l4_proto) == 0);
329        assert(pkt_l4_proto == l4_proto);
330        uint32_t ret2 = parser.get_payload_len(p, pkt_size, pkt_payload_len);
331        assert(ret2 == 0);
332        assert(pkt_payload_len == payload_len);
333    }
334
335    return 0;
336}
337
338int CFlowStatParserTest::verify_pkt(uint8_t *p, uint16_t pkt_size, uint16_t payload_len, uint32_t ip_id, uint8_t l4_proto
339                                    , CFlowStatParserTest_exp_err_t exp_err) {
340    int ret;
341    int ret_val = 0;
342    CFlowStatParser parser_hw(CFlowStatParser::FLOW_STAT_PARSER_MODE_HW);
343    CFlowStatParser parser_sw(CFlowStatParser::FLOW_STAT_PARSER_MODE_SW);
344    CFlowStatParser parser82599(CFlowStatParser::FLOW_STAT_PARSER_MODE_82599);
345    CFlowStatParser parser82599_vlan(CFlowStatParser::FLOW_STAT_PARSER_MODE_82599_vlan);
346
347    printf ("  ");
348    printf("Hardware mode parser");
349    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser_hw, exp_err.m_hw);
350    ret_val = ret;
351    if (ret == 0)
352        printf("-OK");
353    else {
354        printf("-BAD");
355        ret_val = -1;
356    }
357
358    printf(", software mode parser");
359    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser_sw, exp_err.m_sw);
360    ret_val = ret;
361    if (ret == 0)
362        printf("-OK");
363    else {
364        printf("-BAD");
365        ret_val = -1;
366    }
367
368    printf(", 82599 parser");
369    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser82599, exp_err.m_82599);
370    ret_val |= ret;
371    if (ret == 0)
372        printf("-OK");
373    else {
374        printf("-BAD");
375        ret_val = -1;
376    }
377
378    printf(", 82599 vlan parser");
379    ret = verify_pkt_one_parser(p, pkt_size, payload_len, ip_id, l4_proto, parser82599_vlan, exp_err.m_82599_vlan);
380    ret_val |= ret;
381    if (ret == 0)
382        printf("-OK");
383    else {
384        printf("-BAD");
385        ret_val = -1;
386    }
387
388    printf("\n");
389
390    return 0;
391}
392
393int CFlowStatParserTest::test_one_pkt(const char *name, uint16_t ether_type, uint8_t l4_proto, int vlan_num
394                                      , CFlowStatParserTest_exp_err_t exp_err) {
395    CTestPktGen gen;
396    uint8_t *p;
397    int pkt_size;
398    uint16_t payload_len = 16;
399    uint16_t pkt_flags;
400    int ret = 0;
401
402    printf("%s - ", name);
403
404    // in case of IPv6, we add rx_check header, just to make sure we know how to parse with multiple headers
405    switch (vlan_num) {
406    case 1:
407        pkt_flags = DPF_VLAN | DPF_RXCHECK;
408        break;
409    case 0:
410        pkt_flags = DPF_RXCHECK;
411        break;
412    case 2:
413        pkt_flags = DPF_QINQ | DPF_RXCHECK;
414        break;
415    default:
416        printf("Internal error: vlan_num = %d\n", vlan_num);
417        exit(-1);
418    }
419
420    p = (uint8_t *)gen.create_test_pkt(ether_type, l4_proto, 255, TEST_IP_ID, pkt_flags, payload_len, pkt_size);
421    ret = verify_pkt(p, pkt_size, payload_len, TEST_IP_ID, l4_proto, exp_err);
422    free(p);
423
424    return ret;
425}
426
427int CFlowStatParserTest::test() {
428    uint8_t tcp = IPPROTO_TCP, udp = IPPROTO_UDP, icmp = IPPROTO_ICMP;
429    uint16_t ipv4 = EthernetHeader::Protocol::IP, ipv6 = EthernetHeader::Protocol::IPv6;
430    CFlowStatParserTest_exp_err_t exp_ret;
431
432    // no vlan tests
433    exp_ret.m_hw = FSTAT_PARSER_E_OK;
434    exp_ret.m_sw = FSTAT_PARSER_E_OK;
435    exp_ret.m_82599 = FSTAT_PARSER_E_OK;
436    exp_ret.m_82599_vlan = FSTAT_PARSER_E_VLAN_NEEDED;
437    test_one_pkt("IPv4 TCP", ipv4, tcp, 0, exp_ret);
438    test_one_pkt("IPv4 UDP", ipv4, udp, 0, exp_ret);
439    test_one_pkt("IPv4 ICMP", ipv4, icmp, 0, exp_ret);
440    test_one_pkt("IPv6 TCP", ipv6, tcp, 0, exp_ret);
441    test_one_pkt("IPv6 UDP", ipv6, udp, 0, exp_ret);
442    test_one_pkt("IPv4 IGMP", ipv4, IPPROTO_IGMP, 0, exp_ret);
443
444    // vlan tests
445    exp_ret.m_hw = FSTAT_PARSER_E_OK;
446    exp_ret.m_sw = FSTAT_PARSER_E_OK;
447    exp_ret.m_82599 = FSTAT_PARSER_E_VLAN_NOT_SUP;
448    exp_ret.m_82599_vlan = FSTAT_PARSER_E_OK;;
449    test_one_pkt("IPv4 TCP VLAN", ipv4, tcp, 1, exp_ret);
450    test_one_pkt("IPv4 UDP VLAN", ipv4, udp, 1, exp_ret);
451    test_one_pkt("IPv4 ICMP VLAN", ipv4, icmp, 1, exp_ret);
452    test_one_pkt("IPv6 TCP VLAN", ipv6, tcp, 1, exp_ret);
453    test_one_pkt("IPv6 UDP VLAN", ipv6, udp, 1, exp_ret);
454
455    // qinq tests
456    exp_ret.m_hw = FSTAT_PARSER_E_QINQ_NOT_SUP;
457    exp_ret.m_sw = FSTAT_PARSER_E_OK;
458    exp_ret.m_82599 = FSTAT_PARSER_E_QINQ_NOT_SUP;
459    exp_ret.m_82599_vlan = FSTAT_PARSER_E_QINQ_NOT_SUP;
460    test_one_pkt("IPv4 TCP QINQ", ipv4, tcp, 2, exp_ret);
461    test_one_pkt("IPv4 UDP QINQ", ipv4, udp, 2, exp_ret);
462    test_one_pkt("IPv4 ICMP QINQ", ipv4, icmp, 2, exp_ret);
463    test_one_pkt("IPv6 TCP QINQ", ipv6, tcp, 2, exp_ret);
464    test_one_pkt("IPv6 UDP QINQ", ipv6, udp, 2, exp_ret);
465
466    // bad packets tests
467    exp_ret.m_hw = FSTAT_PARSER_E_UNKNOWN_HDR;
468    exp_ret.m_sw = FSTAT_PARSER_E_UNKNOWN_HDR;
469    exp_ret.m_82599 = FSTAT_PARSER_E_UNKNOWN_HDR;
470    exp_ret.m_82599_vlan = FSTAT_PARSER_E_UNKNOWN_HDR;
471    test_one_pkt("BAD l3 type", 0xaa, icmp, 0, exp_ret);
472
473    exp_ret.m_82599 = FSTAT_PARSER_E_VLAN_NOT_SUP;
474    test_one_pkt("VLAN + BAD l3 type", 0xaa, icmp, 1, exp_ret);
475
476    return 0;
477}
478
479CFlowStatParser_err_t CPassAllParser::parse(uint8_t *pkt, uint16_t len) {
480    reset();
481
482    if (len < ETH_HDR_LEN)
483        return FSTAT_PARSER_E_TOO_SHORT;
484
485    m_len = len;
486
487    return FSTAT_PARSER_E_OK;
488}
489
490bool CSimplePacketParser::Parse(){
491
492    rte_mbuf_t * m=m_m;
493    uint8_t *p=rte_pktmbuf_mtod(m, uint8_t*);
494    EthernetHeader *m_ether = (EthernetHeader *)p;
495    IPHeader * ipv4=0;
496    IPv6Header * ipv6=0;
497    m_vlan_offset=0;
498    uint8_t protocol = 0;
499
500    // Retrieve the protocol type from the packet
501    switch( m_ether->getNextProtocol() ) {
502    case EthernetHeader::Protocol::IP :
503        // IPv4 packet
504        ipv4=(IPHeader *)(p+14);
505        m_l4 = (uint8_t *)ipv4 + ipv4->getHeaderLength();
506        protocol = ipv4->getProtocol();
507        break;
508    case EthernetHeader::Protocol::IPv6 :
509        // IPv6 packet
510        ipv6=(IPv6Header *)(p+14);
511        m_l4 = (uint8_t *)ipv6 + ipv6->getHeaderLength();
512        protocol = ipv6->getNextHdr();
513        break;
514    case EthernetHeader::Protocol::VLAN :
515        m_vlan_offset = 4;
516        switch ( m_ether->getVlanProtocol() ){
517        case EthernetHeader::Protocol::IP:
518            // IPv4 packet
519            ipv4=(IPHeader *)(p+18);
520            m_l4 = (uint8_t *)ipv4 + ipv4->getHeaderLength();
521            protocol = ipv4->getProtocol();
522            break;
523        case EthernetHeader::Protocol::IPv6 :
524            // IPv6 packet
525            ipv6=(IPv6Header *)(p+18);
526            m_l4 = (uint8_t *)ipv6 + ipv6->getHeaderLength();
527            protocol = ipv6->getNextHdr();
528            break;
529        default:
530        break;
531        }
532        default:
533        break;
534    }
535    m_protocol =protocol;
536    m_ipv4=ipv4;
537    m_ipv6=ipv6;
538
539    if ( protocol == 0 ){
540        return (false);
541    }
542    return (true);
543}
544