/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* FIXME opcode name remove to save table space; enum x87 3dnow cbw naming */ #include #include #include #define foreach_x86_gp_register \ _ (AX) _ (CX) _ (DX) _ (BX) \ _ (SP) _ (BP) _ (SI) _ (DI) typedef enum { #define _(r) X86_INSN_GP_REG_##r, foreach_x86_gp_register #undef _ } x86_insn_gp_register_t; typedef union { struct { u8 rm : 3; u8 reg : 3; u8 mode : 2; }; u8 byte; } x86_insn_modrm_byte_t; typedef union { struct { u8 base : 3; u8 index : 3; u8 log2_scale : 2; }; u8 byte; } x86_insn_sib_byte_t; always_inline uword x86_insn_has_modrm_byte (x86_insn_t * insn) { int i; for (i = 0; i < ARRAY_LEN (insn->operands); i++) switch (insn->operands[i].code) { case 'G': case 'E': case 'M': case 'R': return 1; } return 0; } always_inline uword x86_insn_immediate_type (x86_insn_t * insn) { int i; for (i = 0; i < ARRAY_LEN (insn->operands); i++) switch (insn->operands[i].code) { case 'J': case 'I': case 'O': return insn->operands[i].type; } return 0; } /* Opcode extension in modrm byte reg field. */ #define foreach_x86_insn_modrm_reg_group \ _ (1) _ (1a) _ (2) _ (3) _ (4) _ (5) _ (6) _ (7) \ _ (8) _ (9) _ (10) _ (11) _ (12) _ (13) _ (14) \ _ (15) _ (16) _ (p) #define foreach_x86_insn_sse_group \ _ (10) _ (28) _ (50) _ (58) _ (60) _ (68) _ (70) _ (78) \ _ (c0) _ (d0) _ (d8) _ (e0) _ (e8) _ (f0) _ (f8) enum { #define _(x) X86_INSN_MODRM_REG_GROUP_##x, foreach_x86_insn_modrm_reg_group #undef _ #define _(x) X86_INSN_SSE_GROUP_##x, foreach_x86_insn_sse_group #undef _ }; enum { #define _(x) \ X86_INSN_FLAG_MODRM_REG_GROUP_##x \ = X86_INSN_FLAG_SET_MODRM_REG_GROUP (1 + X86_INSN_MODRM_REG_GROUP_##x), foreach_x86_insn_modrm_reg_group #undef _ #define _(x) \ X86_INSN_FLAG_SSE_GROUP_##x \ = X86_INSN_FLAG_SET_SSE_GROUP (1 + X86_INSN_SSE_GROUP_##x), foreach_x86_insn_sse_group #undef _ }; #define foreach_x86_gp_reg \ _ (AX) _ (CX) _ (DX) _ (BX) \ _ (SP) _ (BP) _ (SI) _ (DI) #define foreach_x86_condition \ _ (o) _ (no) _ (b) _ (nb) \ _ (z) _ (nz) _ (be) _ (nbe) \ _ (s) _ (ns) _ (p) _ (np) \ _ (l) _ (nl) _ (le) _ (nle) #define _3f(x,f,o0,o1,o2) \ { \ .name = #x, \ .flags = (f), \ .operands[0] = { .data = #o0 }, \ .operands[1] = { .data = #o1 }, \ .operands[2] = { .data = #o2 }, \ } #define _2f(x,f,o0,o1) _3f(x,f,o0,o1,__) #define _1f(x,f,o0) _2f(x,f,o0,__) #define _0f(x,f) _1f(x,f,__) #define _3(x,o0,o1,o2) _3f(x,0,o0,o1,o2) #define _2(x,o0,o1) _2f(x,0,o0,o1) #define _1(x,o0) _1f(x,0,o0) #define _0(x) _0f(x,0) static x86_insn_t x86_insns_one_byte[256] = { #define _(x) \ _2 (x, Eb, Gb), \ _2 (x, Ev, Gv), \ _2 (x, Gb, Eb), \ _2 (x, Gv, Ev), \ _2 (x, AL, Ib), \ _2 (x, AX, Iz) /* 0x00 */ _ (add), _0 (push_es), _0 (pop_es), _ (or), _0 (push_cs), _0 (escape_two_byte), /* 0x10 */ _ (adc), _0 (push_ss), _0 (pop_ss), _ (sbb), _0 (push_ds), _0 (pop_ds), /* 0x20 */ _ (and), _0 (segment_es), _0 (daa), _ (sub), _0 (segment_cs), _0 (das), /* 0x30 */ _ (xor), _0 (segment_ss), _0 (aaa), _ (cmp), _0 (segment_ds), _0 (aas), #undef _ /* 0x40 */ #define _(r) _1 (inc, r), foreach_x86_gp_reg #undef _ #define _(r) _1 (dec, r), foreach_x86_gp_reg #undef _ /* 0x50 */ #define _(r) _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, r), foreach_x86_gp_reg #undef _ #define _(r) _1f (pop, X86_INSN_FLAG_DEFAULT_64_BIT, r), foreach_x86_gp_reg #undef _ /* 0x60 */ _0 (pusha), _0 (popa), _2 (bound, Gv, Ma), _2 (movsxd, Gv, Ed), _0 (segment_fs), _0 (segment_gs), _0 (operand_type), _0 (address_size), _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Iz), _3 (imul, Gv, Ev, Iz), _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ib), _3 (imul, Gv, Ev, Ib), _1 (insb, DX), _1 (insw, DX), _1 (outsb, DX), _1 (outsw, DX), /* 0x70 */ #define _(x) _1 (j##x, Jb), foreach_x86_condition #undef _ /* 0x80 */ _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib), _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Iz), _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib), _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Ib), _2 (test, Eb, Gb), _2 (test, Ev, Gv), _2 (xchg, Eb, Gb), _2 (xchg, Ev, Gv), _2 (mov, Eb, Gb), _2 (mov, Ev, Gv), _2 (mov, Gb, Eb), _2 (mov, Gv, Ev), _2 (mov, Ev, Sw), _2 (lea, Gv, Ev), _2 (mov, Sw, Ew), _1f (modrm_group_1a, X86_INSN_FLAG_MODRM_REG_GROUP_1a, Ev), /* 0x90 */ _0 (nop), _1 (xchg, CX), _1 (xchg, DX), _1 (xchg, BX), _1 (xchg, SP), _1 (xchg, BP), _1 (xchg, SI), _1 (xchg, DI), _0 (cbw), _0 (cwd), _1 (call, Ap), _0 (wait), _0 (pushf), _0 (popf), _0 (sahf), _0 (lahf), /* 0xa0 */ _2 (mov, AL, Ob), _2 (mov, AX, Ov), _2 (mov, Ob, AL), _2 (mov, Ov, AX), _0 (movsb), _0 (movsw), _0 (cmpsb), _0 (cmpsw), _2 (test, AL, Ib), _2 (test, AX, Iz), _1 (stosb, AL), _1 (stosw, AX), _1 (lodsb, AL), _1 (lodsw, AX), _1 (scasb, AL), _1 (scasw, AX), /* 0xb0 */ _2 (mov, AL, Ib), _2 (mov, CL, Ib), _2 (mov, DL, Ib), _2 (mov, BL, Ib), _2 (mov, AH, Ib), _2 (mov, CH, Ib), _2 (mov, DH, Ib), _2 (mov, BH, Ib), #define _(r) _2 (mov, r, Iv), foreach_x86_gp_reg #undef _ /* 0xc0 */ _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, Ib), _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, Ib), _1 (ret, Iw), _0 (ret), _2 (les, Gz, Mp), _2 (lds, Gz, Mp), _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Eb, Ib), _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Ev, Iz), _2 (enter, Iw, Ib), _0 (leave), _1 (ret, Iw), _0 (ret), _0 (int3), _1 (int, Ib), _0 (into), _0 (iret), /* 0xd0 */ _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, 1b), _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, 1b), _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, CL), _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, CL), _0 (aam), _0 (aad), _0 (salc), _0 (xlat), /* FIXME x87 */ _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), /* 0xe0 */ _1 (loopnz, Jb), _1 (loopz, Jb), _1 (loop, Jb), _1 (jcxz, Jb), _2 (in, AL, Ib), _2 (in, AX, Ib), _2 (out, Ib, AL), _2 (out, Ib, AX), _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Jz), _1f ( jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Jz), _1 (jmp, Ap), _1 (jmp, Jb), _2 (in, AL, DX), _2 (in, AX, DX), _2 (out, DX, AL), _2 (out, DX, AX), /* 0xf0 */ _0 (lock), _0 (int1), _0 (repne), _0 (rep), _0 (hlt), _0 (cmc), _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3), _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3), _0 (clc), _0 (stc), _0 (cli), _0 (sti), _0 (cld), _0 (std), _1f (modrm_group_4, X86_INSN_FLAG_MODRM_REG_GROUP_4, Eb), _0f (modrm_group_5, X86_INSN_FLAG_MODRM_REG_GROUP_5), }; static x86_insn_t x86_insns_two_byte[256] = { /* 0x00 */ _0f (modrm_group_6, X86_INSN_FLAG_MODRM_REG_GROUP_6), _0f (modrm_group_7, X86_INSN_FLAG_MODRM_REG_GROUP_7), _2 (lar, Gv, Ew), _2 (lsl, Gv, Ew), _0 (bad), _0 (syscall), _0 (clts), _0 (sysret), _0 (invd), _0 (wbinvd), _0 (bad), _0 (ud2), _0 (bad), _0f (modrm_group_p, X86_INSN_FLAG_MODRM_REG_GROUP_p), _0 (femms), _0 (escape_3dnow), /* 0x10 */ _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), _2f (unpcklps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), _2f (unpckhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), _0f (modrm_group_16, X86_INSN_FLAG_MODRM_REG_GROUP_16), _0 (nop), _0 (nop), _0 (nop), _0 (nop), _0 (nop), _0 (nop), _0 (nop), /* 0x20 */ _2 (mov, Rv, Cv), _2 (mov, Rv, Dv), _2 (mov, Cv, Rv), _2 (mov, Dv, Rv), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Ex, Gx), _2f (cvtpi2ps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), _2f (movntps, X86_INSN_FLAG_SSE_GROUP_28, Mx, Gx), _2f (cvttps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), _2f (cvtps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), _2f (ucomiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), _2f (comiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), /* 0x30 */ _0 (wrmsr), _0 (rdtsc), _0 (rdmsr), _0 (rdpmc), _0 (sysenter), _0 (sysexit), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), _0 (bad), /* 0x40 */ #define _(x) _2 (cmov##x, Gv, Ev), foreach_x86_condition #undef _ /* 0x50 */ _2f (movmskps, X86_INSN_FLAG_SSE_GROUP_50, Gd, Rx), _2f (sqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (rsqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (rcpps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (andps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (andnps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (orps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (xorps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), _2f (addps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), _2f (mulps, X8
#!/usr/bin/env python

import unittest

from framework import VppTestCase, VppTestRunner

from scapy.packet import Raw
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP


class TestFlowperpkt(VppTestCase):
    """ Flow-per-packet plugin: test both L2 and IP4 reporting """

    def setUp(self):
        """
        Set up

        **Config:**
            - create three PG interfaces
        """
        super(TestFlowperpkt, self).setUp()

        self.create_pg_interfaces(range(3))

        self.pg_if_packet_sizes = [150]

        self.interfaces = list(self.pg_interfaces)

        for intf in self.interfaces:
            intf.admin_up()
            intf.config_ip4()
            intf.resolve_arp()

    def create_stream(self, src_if, dst_if, packet_sizes):
        """Create a packet stream to tickle the plugin

        :param VppInterface src_if: Source interface for packet stream
        :param VppInterface src_if: Dst interface for packet stream
        :param list packet_sizes: Sizes to test
        """
        pkts = []
        for size in packet_sizes:
            info = self.create_packet_info(src_if, dst_if)
            payload = self.info_to_payload(info)
            p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) /
                 IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
                 UDP(sport=1234, dport=4321) /
                 Raw(payload))
            info.data = p.copy()
            self.extend_packet(p, size)
            pkts.append(p)
        return pkts

    @staticmethod
    def compare_with_mask(payload, masked_expected_data):
        if len(payload) * 2 != len(masked_expected_data):
            return False

        # iterate over pairs: raw byte from payload and ASCII code for that
        # byte from masked payload (or XX if masked)
        for i in range(len(payload)):
            p = payload[i]
            m = masked_expected_data[2 * i:2 * i + 2]
            if m != "XX":
                if "%02x" % ord(p) != m:
                    return False
        return True

    def verify_ipfix(self, collector_if):
        """Check the ipfix capture"""
        found_data_packet = False
        found_template_packet = False
        found_l2_data_packet = False
        found_l2_template_packet = False

        # Scapy, of course, understands ipfix not at all...
        # These data vetted by manual inspection in wireshark
        # X'ed out fields are timestamps, which will absolutely
        # fail to compare.

        data_udp_string = "1283128300370000000a002fXXXXXXXX000000000000000101"\
            "00001f0000000100000002ac100102ac10020200XXXXXXXXXXXXXXXX0092"

        template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000000"\
            "010002002401000007000a0004000e000400080004000c000400050001009c00"\
            "0801380002"

        l2_data_udp_string = "12831283003c0000000a0034XXXXXXXX000000010000000"\
            "1010100240000000100000002%s02020000ff020008XXXXXXXXXXX"\
            "XXXXX0092" % self.pg1.local_mac.translate(None, ":")

        l2_template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000"\
            "000010002002401010007000a0004000e0004003800060050000601000002009"\
            "c000801380002"

        self.logger.info("Look for ipfix packets on %s sw_if_index %d "
                         % (collector_if.name, collector_if.sw_if_index))
        # expecting 4 packets on collector interface based on traffic on other
        # interfaces
        capture = collector_if.get_capture(4)

        for p in capture:
            ip = p[IP]
            udp = p[UDP]
            self.logger.info("src %s dst %s" % (ip.src, ip.dst))
            self.logger.info(" udp src_port %s dst_port %s"
                             % (udp.sport, udp.dport))

            payload = str(udp)

            if self.compare_with_mask(payload, data_udp_string):
                self.logger.info("found ip4 data packet")
                found_data_packet = True
            elif self.compare_with_mask(payload, template_udp_string):
                self.logger.info("found ip4 template packet")
                found_template_packet = True
            elif self.compare_with_mask(payload, l2_data_udp_string):
                self.logger.info("found l2 data packet")
                found_l2_data_packet = True
            elif self.compare_with_mask(payload, l2_template_udp_string):
                self.logger.info("found l2 template packet")
                found_l2_template_packet = True
            else:
                unmasked_payload = "".join(["%02x" % ord(c) for c in payload])
                self.logger.error("unknown pkt '%s'" % unmasked_payload)

        self.assertTrue(found_data_packet, "Data packet not found")
        self.assertTrue(found_template_packet, "Template packet not found")
        self.assertTrue(found_l2_data_packet, "L2 data packet not found")
        self.assertTrue(found_l2_template_packet,
                        "L2 template packet not found")

    def test_L3_fpp(self):
        """ Flow per packet L3 test """

        # Configure an ipfix report on the [nonexistent] collector
        # 172.16.3.2, as if it was connected to the pg2 interface
        # Install a FIB entry, so the exporter's work won't turn into
        # an ARP request

        self.pg_enable_capture(self.pg_interfaces)
        self.pg2.configure_ipv4_neighbors()
        self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n,
                                     src_address=self.pg2.local_ip4n,
                                     path_mtu=1450,
                                     template_interval=1)

        # Export flow records for all pkts transmitted on pg1
        self.vapi.cli("flowperpkt feature add-del pg1")
        self.vapi.cli("flowperpkt feature add-del pg1 l2")

        # Arrange to minimally trace generated ipfix packets
        self.vapi.cli("trace add flowperpkt-ipv4 10")
        self.vapi.cli("trace add flowperpkt-l2 10")

        # Create a stream from pg0 -> pg1, which causes
        # an ipfix packet to be transmitted on pg2

        pkts = self.create_stream(self.pg0, self.pg1,
                                  self.pg_if_packet_sizes)
        self.pg0.add_stream(pkts)
        self.pg_start()

        # Flush the ipfix collector, so we don't need any
        # asinine time.sleep(5) action
        self.vapi.cli("ipfix flush")

        # Make sure the 4 pkts we expect actually showed up
        self.verify_ipfix(self.pg2)

if __name__ == '__main__':
    unittest.main(testRunner=VppTestRunner)