aboutsummaryrefslogtreecommitdiffstats
path: root/test/test_geneve.py
blob: 04271e31b6940b36c7dc37a463f1bc1cec83190c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!/usr/bin/env python

import socket
from util import ip4n_range, ip4_range
import unittest
from framework import VppTestCase, VppTestRunner
from template_bd import BridgeDomain

from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
from scapy.layers.geneve import GENEVE
from scapy.utils import atol
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_ip import INVALID_INDEX


class TestGeneve(BridgeDomain, VppTestCase):
    """ GENEVE Test Case """

    def __init__(self, *args):
        BridgeDomain.__init__(self)
        VppTestCase.__init__(self, *args)

    def encapsulate(self, pkt, vni):

        """
        Encapsulate the original payload frame by adding GENEVE header with its
        UDP, IP and Ethernet fields
        """
        return (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
                UDP(sport=self.dport, dport=self.dport, chksum=0) /
                GENEVE(vni=vni) /
                pkt)

    def ip_range(self, start, end):
        """ range of remote ip's """
        return ip4_range(self.pg0.remote_ip4, start, end)

    def encap_mcast(self, pkt, src_ip, src_mac, vni):
        """
        Encapsulate the original payload frame by adding GENEVE header with its
        UDP, IP and Ethernet fields
        """
        return (Ether(src=src_mac, dst=self.mcast_mac) /
                IP(src=src_ip, dst=self.mcast_ip4) /
                UDP(sport=self.dport, dport=self.dport, chksum=0) /
                GENEVE(vni=vni) /
                pkt)

    def decapsulate(self, pkt):
        """
        Decapsulate the original payload frame by removing GENEVE header
        """
        # check if is set I flag
        # self.assertEqual(pkt[GENEVE].flags, int('0x8', 16))
        return pkt[GENEVE].payload

    # Method for checking GENEVE encapsulation.
    #
    def check_encapsulation(self, pkt, vni, local_only=False, mcast_pkt=False):
        # TODO: add error messages
        # Verify source MAC is VPP_MAC and destination MAC is MY_MAC resolved
        #  by VPP using ARP.
        self.assertEqual(pkt[Ether].src, self.pg0.local_mac)
        if not local_only:
            if not mcast_pkt:
                self.assertEqual(pkt[Ether].dst, self.pg0.remote_mac)
            else:
                self.assertEqual(pkt[Ether].dst, type(self).mcast_mac)
        # Verify GENEVE tunnel source IP is VPP_IP and destination IP is MY_IP.
        self.assertEqual(pkt[IP].src, self.pg0.local_ip4)
        if not local_only:
            if not mcast_pkt:
                self.assertEqual(pkt[IP].dst, self.pg0.remote_ip4)
            else:
                self.assertEqual(pkt[IP].dst, type(self).mcast_ip4)
        # Verify UDP destination port is GENEVE 4789, source UDP port could be
        #  arbitrary.
        self.assertEqual(pkt[UDP].dport, type(self).dport)
        # TODO: checksum check
        # Verify VNI
        self.assertEqual(pkt[GENEVE].vni, vni)

    @classmethod
    def create_geneve_flood_test_bd(cls, vni, n_ucast_tunnels):
        # Create 10 ucast geneve tunnels under bd
        ip_range_start = 10
        ip_range_end = ip_range_start + n_ucast_tunnels
        next_hop_address = cls.pg0.remote_ip4
        for dest_ip4 in ip4_range(next_hop_address, ip_range_start,
                                  ip_range_end):
            # add host route so dest_ip4n will not be resolved
            rip = VppIpRoute(cls, dest_ip4, 32,
                             [VppRoutePath(next_hop_address,
                                           INVALID_INDEX)],
                             register=False)
            rip.add_vpp_config()
            dest_ip4n = socket.inet_pton(socket.AF_INET, dest_ip4)
            r = cls.vapi.geneve_add_del_tunnel(
                local_address=cls.pg0.local_ip4n, remote_address=dest_ip4n,
                vni=vni)
            cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
                                                bd_id=vni)

    @classmethod
    def add_del_shared_mcast_dst_load(cls, is_add):
        """
        add or del tunnels sharing the same mcast dst
        to test geneve ref_count mechanism
        """
        n_shared_dst_tunnels = 10
        vni_start = 10000
        vni_end = vni_start + n_shared_dst_tunnels
        for vni in range(vni_start, vni_end):
            r = cls.vapi.geneve_add_del_tunnel(
                local_address=cls.pg0.local_ip4n,
                remote_address=cls.mcast_ip4n, mcast_sw_if_index=1,
                is_add=is_add, vni=vni)
            if r.sw_if_index == 0xffffffff:
                raise ValueError("bad sw_if_index: ~0")

    @classmethod
    def add_shared_mcast_dst_load(cls):
        cls.add_del_shared_mcast_dst_load(is_add=1)

    @classmethod
    def del_shared_mcast_dst_load(cls):
        cls.add_del_shared_mcast_dst_load(is_add=0)

    @classmethod
    def add_del_mcast_tunnels_load(cls, is_add):
        """
        add or del tunnels to test geneve stability
        """
        n_distinct_dst_tunnels = 10
        ip_range_start = 10
        ip_range_end = ip_range_start + n_distinct_dst_tunnels
        for dest_ip4n in ip4n_range(cls.mcast_ip4n, ip_range_start,
                                    ip_range_end):
            vni = bytearray(dest_ip4n)[3]
            cls.vapi.geneve_add_del_tunnel(local_address=cls.pg0.local_ip4n,
                                           remote_address=dest_ip4n,
                                           mcast_sw_if_index=1, is_add=is_add,
                                           vni=vni)

    @classmethod
    def add_mcast_tunnels_load(cls):
        cls.add_del_mcast_tunnels_load(is_add=1)

    @classmethod
    def del_mcast_tunnels_load(cls):
        cls.add_del_mcast_tunnels_load(is_add=0)

    # Class method to start the GENEVE test case.
    #  Overrides setUpClass method in VppTestCase class.
    #  Python try..except statement is used to ensure that the tear down of
    #  the class will be executed even if exception is raised.
    #  @param cls The class pointer.
    @classmethod
    def setUpClass(cls):
        super(TestGeneve, cls).setUpClass()

        try:
            cls.dport = 6081

            # Create 2 pg interfaces.
            cls.create_pg_interfaces(range(4))
            for pg in cls.pg_interfaces:
                pg.admin_up()

            # Configure IPv4 addresses on VPP pg0.
            cls.pg0.config_ip4()

            # Resolve MAC address for VPP's IP address on pg0.
            cls.pg0.resolve_arp()

            # Our Multicast address
            cls.mcast_ip4 = '239.1.1.1'
            cls.mcast_ip4n = socket.inet_pton(socket.AF_INET, cls.mcast_ip4)
            iplong = atol(cls.mcast_ip4)
            cls.mcast_mac = "01:00:5e:%02x:%02x:%02x" % (
                (iplong >> 16) & 0x7F, (iplong >> 8) & 0xFF, iplong & 0xFF)

            # Create GENEVE VTEP on VPP pg0, and put geneve_tunnel0 and pg1
            #  into BD.
            cls.single_tunnel_bd = 1
            r = cls.vapi.geneve_add_del_tunnel(
                local_address=cls.pg0.local_ip4n,
                remote_address=cls.pg0.remote_ip4n, vni=cls.single_tunnel_bd)
            cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
                                                bd_id=cls.single_tunnel_bd)
            cls.vapi.sw_interface_set_l2_bridge(
                rx_sw_if_index=cls.pg1.sw_if_index, bd_id=cls.single_tunnel_bd)

            # Setup vni 2 to test multicast flooding
            cls.n_ucast_tunnels = 10
            cls.mcast_flood_bd = 2
            cls.create_geneve_flood_test_bd(cls.mcast_flood_bd,
                                            cls.n_ucast_tunnels)
            r = cls.vapi.geneve_add_del_tunnel(
                local_address=cls.pg0.local_ip4n,
                remote_address=cls.mcast_ip4n, mcast_sw_if_index=1,
                vni=cls.mcast_flood_bd)
            cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
                                                bd_id=cls.mcast_flood_bd)
            cls.vapi.sw_interface_set_l2_bridge(
                rx_sw_if_index=cls.pg2.sw_if_index, bd_id=cls.mcast_flood_bd)

            # Add and delete mcast tunnels to check stability
            cls.add_shared_mcast_dst_load()
            cls.add_mcast_tunnels_load()
            cls.del_shared_mcast_dst_load()
            cls.del_mcast_tunnels_load()

            # Setup vni 3 to test unicast flooding
            cls.ucast_flood_bd = 3
            cls.create_geneve_flood_test_bd(cls.ucast_flood_bd,
                                            cls.n_ucast_tunnels)
            cls.vapi.sw_interface_set_l2_bridge(
                rx_sw_if_index=cls.pg3.sw_if_index, bd_id=cls.ucast_flood_bd)
        except Exception:
            super(TestGeneve, cls).tearDownClass()
            raise

    # Method to define VPP actions before tear down of the test case.
    #  Overrides tearDown method in VppTestCase class.
    #  @param self The object pointer.
    def tearDown(self):
        super(TestGeneve, self).tearDown()

    def show_commands_at_teardown(self):
        self.logger.info(self.vapi.cli("show bridge-domain 1 detail"))
        self.logger.info(self.vapi.cli("show bridge-domain 2 detail"))
        self.logger.info(self.vapi.cli("show bridge-domain 3 detail"))
        self.logger.info(self.vapi.cli("show geneve tunnel"))


if __name__ == '__main__':
    unittest.main(testRunner=VppTestRunner)
span>}, 102, 12, IPPROTO_TCP}, 3}, }; struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS]; struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS]; static inline uint32_t ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, uint32_t init_val) { const union ipv4_5tuple_host *k; uint32_t t; const uint32_t *p; k = data; t = k->proto; p = (const uint32_t *)&k->port_src; #ifdef EM_HASH_CRC init_val = rte_hash_crc_4byte(t, init_val); init_val = rte_hash_crc_4byte(k->ip_src, init_val); init_val = rte_hash_crc_4byte(k->ip_dst, init_val); init_val = rte_hash_crc_4byte(*p, init_val); #else init_val = rte_jhash_1word(t, init_val); init_val = rte_jhash_1word(k->ip_src, init_val); init_val = rte_jhash_1word(k->ip_dst, init_val); init_val = rte_jhash_1word(*p, init_val); #endif return init_val; } static inline uint32_t ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len, uint32_t init_val) { const union ipv6_5tuple_host *k; uint32_t t; const uint32_t *p; #ifdef EM_HASH_CRC const uint32_t *ip_src0, *ip_src1, *ip_src2, *ip_src3; const uint32_t *ip_dst0, *ip_dst1, *ip_dst2, *ip_dst3; #endif k = data; t = k->proto; p = (const uint32_t *)&k->port_src; #ifdef EM_HASH_CRC ip_src0 = (const uint32_t *) k->ip_src; ip_src1 = (const uint32_t *)(k->ip_src+4); ip_src2 = (const uint32_t *)(k->ip_src+8); ip_src3 = (const uint32_t *)(k->ip_src+12); ip_dst0 = (const uint32_t *) k->ip_dst; ip_dst1 = (const uint32_t *)(k->ip_dst+4); ip_dst2 = (const uint32_t *)(k->ip_dst+8); ip_dst3 = (const uint32_t *)(k->ip_dst+12); init_val = rte_hash_crc_4byte(t, init_val); init_val = rte_hash_crc_4byte(*ip_src0, init_val); init_val = rte_hash_crc_4byte(*ip_src1, init_val); init_val = rte_hash_crc_4byte(*ip_src2, init_val); init_val = rte_hash_crc_4byte(*ip_src3, init_val); init_val = rte_hash_crc_4byte(*ip_dst0, init_val); init_val = rte_hash_crc_4byte(*ip_dst1, init_val); init_val = rte_hash_crc_4byte(*ip_dst2, init_val); init_val = rte_hash_crc_4byte(*ip_dst3, init_val); init_val = rte_hash_crc_4byte(*p, init_val); #else init_val = rte_jhash_1word(t, init_val); init_val = rte_jhash(k->ip_src, sizeof(uint8_t) * IPV6_ADDR_LEN, init_val); init_val = rte_jhash(k->ip_dst, sizeof(uint8_t) * IPV6_ADDR_LEN, init_val); init_val = rte_jhash_1word(*p, init_val); #endif return init_val; } #define IPV4_L3FWD_EM_NUM_ROUTES \ (sizeof(ipv4_l3fwd_em_route_array) / sizeof(ipv4_l3fwd_em_route_array[0])) #define IPV6_L3FWD_EM_NUM_ROUTES \ (sizeof(ipv6_l3fwd_em_route_array) / sizeof(ipv6_l3fwd_em_route_array[0])) static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned; static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned; static rte_xmm_t mask0; static rte_xmm_t mask1; static rte_xmm_t mask2; #if defined(__SSE2__) static inline xmm_t em_mask_key(void *key, xmm_t mask) { __m128i data = _mm_loadu_si128((__m128i *)(key)); return _mm_and_si128(data, mask); } #elif defined(RTE_MACHINE_CPUFLAG_NEON) static inline xmm_t em_mask_key(void *key, xmm_t mask) { int32x4_t data = vld1q_s32((int32_t *)key); return vandq_s32(data, mask); } #elif defined(RTE_MACHINE_CPUFLAG_ALTIVEC) static inline xmm_t em_mask_key(void *key, xmm_t mask) { xmm_t data = vec_ld(0, (xmm_t *)(key)); return vec_and(data, mask); } #else #error No vector engine (SSE, NEON, ALTIVEC) available, check your toolchain #endif static inline uint8_t em_get_ipv4_dst_port(void *ipv4_hdr, uint8_t portid, void *lookup_struct) { int ret = 0; union ipv4_5tuple_host key; struct rte_hash *ipv4_l3fwd_lookup_struct = (struct rte_hash *)lookup_struct; ipv4_hdr = (uint8_t *)ipv4_hdr + offsetof(struct ipv4_hdr, time_to_live); /* * Get 5 tuple: dst port, src port, dst IP address, * src IP address and protocol. */ key.xmm = em_mask_key(ipv4_hdr, mask0.x); /* Find destination port */ ret = rte_hash_lookup(ipv4_l3fwd_lookup_struct, (const void *)&key); return (uint8_t)((ret < 0) ? portid : ipv4_l3fwd_out_if[ret]); } static inline uint8_t em_get_ipv6_dst_port(void *ipv6_hdr, uint8_t portid, void *lookup_struct) { int ret = 0; union ipv6_5tuple_host key; struct rte_hash *ipv6_l3fwd_lookup_struct = (struct rte_hash *)lookup_struct; ipv6_hdr = (uint8_t *)ipv6_hdr + offsetof(struct ipv6_hdr, payload_len); void *data0 = ipv6_hdr; void *data1 = ((uint8_t *)ipv6_hdr) + sizeof(xmm_t); void *data2 = ((uint8_t *)ipv6_hdr) + sizeof(xmm_t) + sizeof(xmm_t); /* Get part of 5 tuple: src IP address lower 96 bits and protocol */ key.xmm[0] = em_mask_key(data0, mask1.x); /* * Get part of 5 tuple: dst IP address lower 96 bits * and src IP address higher 32 bits. */ key.xmm[1] = *(xmm_t *)data1; /* * Get part of 5 tuple: dst port and src port * and dst IP address higher 32 bits. */ key.xmm[2] = em_mask_key(data2, mask2.x); /* Find destination port */ ret = rte_hash_lookup(ipv6_l3fwd_lookup_struct, (const void *)&key); return (uint8_t)((ret < 0) ? portid : ipv6_l3fwd_out_if[ret]); } #if defined(__SSE4_1__) #if defined(NO_HASH_MULTI_LOOKUP) #include "l3fwd_em_sse.h" #else #include "l3fwd_em_hlm_sse.h" #endif #else #include "l3fwd_em.h" #endif static void convert_ipv4_5tuple(struct ipv4_5tuple *key1, union ipv4_5tuple_host *key2) { key2->ip_dst = rte_cpu_to_be_32(key1->ip_dst); key2->ip_src = rte_cpu_to_be_32(key1->ip_src); key2->port_dst = rte_cpu_to_be_16(key1->port_dst); key2->port_src = rte_cpu_to_be_16(key1->port_src); key2->proto = key1->proto; key2->pad0 = 0; key2->pad1 = 0; } static void convert_ipv6_5tuple(struct ipv6_5tuple *key1, union ipv6_5tuple_host *key2) { uint32_t i; for (i = 0; i < 16; i++) { key2->ip_dst[i] = key1->ip_dst[i]; key2->ip_src[i] = key1->ip_src[i]; } key2->port_dst = rte_cpu_to_be_16(key1->port_dst); key2->port_src = rte_cpu_to_be_16(key1->port_src); key2->proto = key1->proto; key2->pad0 = 0; key2->pad1 = 0; key2->reserve = 0; } #define BYTE_VALUE_MAX 256 #define ALL_32_BITS 0xffffffff #define BIT_8_TO_15 0x0000ff00 static inline void populate_ipv4_few_flow_into_table(const struct rte_hash *h) { uint32_t i; int32_t ret; mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS, ALL_32_BITS, ALL_32_BITS} }; for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) { struct ipv4_l3fwd_em_route entry; union ipv4_5tuple_host newkey; entry = ipv4_l3fwd_em_route_array[i]; convert_ipv4_5tuple(&entry.key, &newkey); ret = rte_hash_add_key(h, (void *) &newkey); if (ret < 0) { rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32 " to the l3fwd hash.\n", i); } ipv4_l3fwd_out_if[ret] = entry.if_out; } printf("Hash: Adding 0x%" PRIx64 " keys\n", (uint64_t)IPV4_L3FWD_EM_NUM_ROUTES); } #define BIT_16_TO_23 0x00ff0000 static inline void populate_ipv6_few_flow_into_table(const struct rte_hash *h) { uint32_t i; int32_t ret; mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS, ALL_32_BITS, ALL_32_BITS} }; mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} }; for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) { struct ipv6_l3fwd_em_route entry; union ipv6_5tuple_host newkey; entry = ipv6_l3fwd_em_route_array[i]; convert_ipv6_5tuple(&entry.key, &newkey); ret = rte_hash_add_key(h, (void *) &newkey); if (ret < 0) { rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32 " to the l3fwd hash.\n", i); } ipv6_l3fwd_out_if[ret] = entry.if_out; } printf("Hash: Adding 0x%" PRIx64 "keys\n", (uint64_t)IPV6_L3FWD_EM_NUM_ROUTES); } #define NUMBER_PORT_USED 4 static inline void populate_ipv4_many_flow_into_table(const struct rte_hash *h, unsigned int nr_flow) { unsigned i; mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS, ALL_32_BITS, ALL_32_BITS} }; for (i = 0; i < nr_flow; i++) { struct ipv4_l3fwd_em_route entry; union ipv4_5tuple_host newkey; uint8_t a = (uint8_t) ((i/NUMBER_PORT_USED)%BYTE_VALUE_MAX); uint8_t b = (uint8_t) (((i/NUMBER_PORT_USED)/BYTE_VALUE_MAX)%BYTE_VALUE_MAX); uint8_t c = (uint8_t) ((i/NUMBER_PORT_USED)/(BYTE_VALUE_MAX*BYTE_VALUE_MAX)); /* Create the ipv4 exact match flow */ memset(&entry, 0, sizeof(entry)); switch (i & (NUMBER_PORT_USED - 1)) { case 0: entry = ipv4_l3fwd_em_route_array[0]; entry.key.ip_dst = IPv4(101, c, b, a); break; case 1: entry = ipv4_l3fwd_em_route_array[1]; entry.key.ip_dst = IPv4(201, c, b, a); break; case 2: entry = ipv4_l3fwd_em_route_array[2]; entry.key.ip_dst = IPv4(111, c, b, a); break; case 3: entry = ipv4_l3fwd_em_route_array[3]; entry.key.ip_dst = IPv4(211, c, b, a); break; }; convert_ipv4_5tuple(&entry.key, &newkey); int32_t ret = rte_hash_add_key(h, (void *) &newkey); if (ret < 0) rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i); ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out; } printf("Hash: Adding 0x%x keys\n", nr_flow); } static inline void populate_ipv6_many_flow_into_table(const struct rte_hash *h, unsigned int nr_flow) { unsigned i; mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS, ALL_32_BITS, ALL_32_BITS} }; mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} }; for (i = 0; i < nr_flow; i++) { struct ipv6_l3fwd_em_route entry; union ipv6_5tuple_host newkey; uint8_t a = (uint8_t) ((i/NUMBER_PORT_USED)%BYTE_VALUE_MAX); uint8_t b = (uint8_t) (((i/NUMBER_PORT_USED)/BYTE_VALUE_MAX)%BYTE_VALUE_MAX); uint8_t c = (uint8_t) ((i/NUMBER_PORT_USED)/(BYTE_VALUE_MAX*BYTE_VALUE_MAX)); /* Create the ipv6 exact match flow */ memset(&entry, 0, sizeof(entry)); switch (i & (NUMBER_PORT_USED - 1)) { case 0: entry = ipv6_l3fwd_em_route_array[0]; break; case 1: entry = ipv6_l3fwd_em_route_array[1]; break; case 2: entry = ipv6_l3fwd_em_route_array[2]; break; case 3: entry = ipv6_l3fwd_em_route_array[3]; break; }; entry.key.ip_dst[13] = c; entry.key.ip_dst[14] = b; entry.key.ip_dst[15] = a; convert_ipv6_5tuple(&entry.key, &newkey); int32_t ret = rte_hash_add_key(h, (void *) &newkey); if (ret < 0) rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i); ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out; } printf("Hash: Adding 0x%x keys\n", nr_flow); } /* Requirements: * 1. IP packets without extension; * 2. L4 payload should be either TCP or UDP. */ int em_check_ptype(int portid) { int i, ret; int ptype_l3_ipv4_ext = 0; int ptype_l3_ipv6_ext = 0; int ptype_l4_tcp = 0; int ptype_l4_udp = 0; uint32_t ptype_mask = RTE_PTYPE_L3_MASK | RTE_PTYPE_L4_MASK; ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, NULL, 0); if (ret <= 0) return 0; uint32_t ptypes[ret]; ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, ptypes, ret); for (i = 0; i < ret; ++i) { switch (ptypes[i]) { case RTE_PTYPE_L3_IPV4_EXT: ptype_l3_ipv4_ext = 1; break; case RTE_PTYPE_L3_IPV6_EXT: ptype_l3_ipv6_ext = 1; break; case RTE_PTYPE_L4_TCP: ptype_l4_tcp = 1; break; case RTE_PTYPE_L4_UDP: ptype_l4_udp = 1; break; } } if (ptype_l3_ipv4_ext == 0) printf("port %d cannot parse RTE_PTYPE_L3_IPV4_EXT\n", portid); if (ptype_l3_ipv6_ext == 0) printf("port %d cannot parse RTE_PTYPE_L3_IPV6_EXT\n", portid); if (!ptype_l3_ipv4_ext || !ptype_l3_ipv6_ext) return 0; if (ptype_l4_tcp == 0) printf("port %d cannot parse RTE_PTYPE_L4_TCP\n", portid); if (ptype_l4_udp == 0) printf("port %d cannot parse RTE_PTYPE_L4_UDP\n", portid); if (ptype_l4_tcp && ptype_l4_udp) return 1; return 0; } static inline void em_parse_ptype(struct rte_mbuf *m) { struct ether_hdr *eth_hdr; uint32_t packet_type = RTE_PTYPE_UNKNOWN; uint16_t ether_type; void *l3; int hdr_len; struct ipv4_hdr *ipv4_hdr; struct ipv6_hdr *ipv6_hdr; eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *); ether_type = eth_hdr->ether_type; l3 = (uint8_t *)eth_hdr + sizeof(struct ether_hdr); if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) { ipv4_hdr = (struct ipv4_hdr *)l3; hdr_len = (ipv4_hdr->version_ihl & IPV4_HDR_IHL_MASK) * IPV4_IHL_MULTIPLIER; if (hdr_len == sizeof(struct ipv4_hdr)) { packet_type |= RTE_PTYPE_L3_IPV4; if (ipv4_hdr->next_proto_id == IPPROTO_TCP) packet_type |= RTE_PTYPE_L4_TCP; else if (ipv4_hdr->next_proto_id == IPPROTO_UDP) packet_type |= RTE_PTYPE_L4_UDP; } else packet_type |= RTE_PTYPE_L3_IPV4_EXT; } else if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv6)) { ipv6_hdr = (struct ipv6_hdr *)l3; if (ipv6_hdr->proto == IPPROTO_TCP) packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP; else if (ipv6_hdr->proto == IPPROTO_UDP) packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP; else packet_type |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN; } m->packet_type = packet_type; } uint16_t em_cb_parse_ptype(uint8_t port __rte_unused, uint16_t queue __rte_unused, struct rte_mbuf *pkts[], uint16_t nb_pkts, uint16_t max_pkts __rte_unused, void *user_param __rte_unused) { unsigned i; for (i = 0; i < nb_pkts; ++i) em_parse_ptype(pkts[i]); return nb_pkts; } /* main processing loop */ int em_main_loop(__attribute__((unused)) void *dummy) { struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; unsigned lcore_id; uint64_t prev_tsc, diff_tsc, cur_tsc; int i, nb_rx; uint8_t portid, queueid; struct lcore_conf *qconf; const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * BURST_TX_DRAIN_US; prev_tsc = 0; lcore_id = rte_lcore_id(); qconf = &lcore_conf[lcore_id]; if (qconf->n_rx_queue == 0) { RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id); return 0; } RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id); for (i = 0; i < qconf->n_rx_queue; i++) { portid = qconf->rx_queue_list[i].port_id; queueid = qconf->rx_queue_list[i].queue_id; RTE_LOG(INFO, L3FWD, " -- lcoreid=%u portid=%hhu rxqueueid=%hhu\n", lcore_id, portid, queueid); } while (!force_quit) { cur_tsc = rte_rdtsc(); /* * TX burst queue drain */ diff_tsc = cur_tsc - prev_tsc; if (unlikely(diff_tsc > drain_tsc)) { for (i = 0; i < qconf->n_tx_port; ++i) { portid = qconf->tx_port_id[i]; if (qconf->tx_mbufs[portid].len == 0) continue; send_burst(qconf, qconf->tx_mbufs[portid].len, portid); qconf->tx_mbufs[portid].len = 0; } prev_tsc = cur_tsc; } /* * Read packet from RX queues */ for (i = 0; i < qconf->n_rx_queue; ++i) { portid = qconf->rx_queue_list[i].port_id; queueid = qconf->rx_queue_list[i].queue_id; nb_rx = rte_eth_rx_burst(portid, queueid, pkts_burst, MAX_PKT_BURST); if (nb_rx == 0) continue; #if defined(__SSE4_1__) l3fwd_em_send_packets(nb_rx, pkts_burst, portid, qconf); #else l3fwd_em_no_opt_send_packets(nb_rx, pkts_burst, portid, qconf); #endif /* __SSE_4_1__ */ } } return 0; } /* * Initialize exact match (hash) parameters. */ void setup_hash(const int socketid) { struct rte_hash_parameters ipv4_l3fwd_hash_params = { .name = NULL, .entries = L3FWD_HASH_ENTRIES, .key_len = sizeof(union ipv4_5tuple_host), .hash_func = ipv4_hash_crc, .hash_func_init_val = 0, }; struct rte_hash_parameters ipv6_l3fwd_hash_params = { .name = NULL, .entries = L3FWD_HASH_ENTRIES, .key_len = sizeof(union ipv6_5tuple_host), .hash_func = ipv6_hash_crc, .hash_func_init_val = 0, }; char s[64]; /* create ipv4 hash */ snprintf(s, sizeof(s), "ipv4_l3fwd_hash_%d", socketid); ipv4_l3fwd_hash_params.name = s; ipv4_l3fwd_hash_params.socket_id = socketid; ipv4_l3fwd_em_lookup_struct[socketid] = rte_hash_create(&ipv4_l3fwd_hash_params); if (ipv4_l3fwd_em_lookup_struct[socketid] == NULL) rte_exit(EXIT_FAILURE, "Unable to create the l3fwd hash on socket %d\n", socketid); /* create ipv6 hash */ snprintf(s, sizeof(s), "ipv6_l3fwd_hash_%d", socketid); ipv6_l3fwd_hash_params.name = s; ipv6_l3fwd_hash_params.socket_id = socketid; ipv6_l3fwd_em_lookup_struct[socketid] = rte_hash_create(&ipv6_l3fwd_hash_params); if (ipv6_l3fwd_em_lookup_struct[socketid] == NULL) rte_exit(EXIT_FAILURE, "Unable to create the l3fwd hash on socket %d\n", socketid); if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) { /* For testing hash matching with a large number of flows we * generate millions of IP 5-tuples with an incremented dst * address to initialize the hash table. */ if (ipv6 == 0) { /* populate the ipv4 hash */ populate_ipv4_many_flow_into_table( ipv4_l3fwd_em_lookup_struct[socketid], hash_entry_number); } else { /* populate the ipv6 hash */ populate_ipv6_many_flow_into_table( ipv6_l3fwd_em_lookup_struct[socketid], hash_entry_number); } } else { /* * Use data in ipv4/ipv6 l3fwd lookup table * directly to initialize the hash table. */ if (ipv6 == 0) { /* populate the ipv4 hash */ populate_ipv4_few_flow_into_table( ipv4_l3fwd_em_lookup_struct[socketid]); } else { /* populate the ipv6 hash */ populate_ipv6_few_flow_into_table( ipv6_l3fwd_em_lookup_struct[socketid]); } } } /* Return ipv4/ipv6 em fwd lookup struct. */ void * em_get_ipv4_l3fwd_lookup_struct(const int socketid) { return ipv4_l3fwd_em_lookup_struct[socketid]; } void * em_get_ipv6_l3fwd_lookup_struct(const int socketid) { return ipv6_l3fwd_em_lookup_struct[socketid]; }