summaryrefslogtreecommitdiffstats
path: root/test/bfd.py
blob: e096284fbd73f25f1ff4975541f412c3d85f3af2 (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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
""" BFD protocol implementation """

from random import randint
from socket import AF_INET, AF_INET6, inet_pton
from scapy.all import bind_layers
from scapy.layers.inet import UDP
from scapy.packet import Packet
from scapy.fields import BitField, BitEnumField, XByteField, FlagsField,\
    ConditionalField, StrField
from vpp_object import VppObject
from util import NumericConstant
from vpp_ip import VppIpAddress
from vpp_papi import VppEnum


class BFDDiagCode(NumericConstant):
    """ BFD Diagnostic Code """
    no_diagnostic = 0
    control_detection_time_expired = 1
    echo_function_failed = 2
    neighbor_signaled_session_down = 3
    forwarding_plane_reset = 4
    path_down = 5
    concatenated_path_down = 6
    administratively_down = 7
    reverse_concatenated_path_down = 8

    desc_dict = {
        no_diagnostic: "No diagnostic",
        control_detection_time_expired: "Control Detection Time Expired",
        echo_function_failed: "Echo Function Failed",
        neighbor_signaled_session_down: "Neighbor Signaled Session Down",
        forwarding_plane_reset: "Forwarding Plane Reset",
        path_down: "Path Down",
        concatenated_path_down: "Concatenated Path Down",
        administratively_down: "Administratively Down",
        reverse_concatenated_path_down: "Reverse Concatenated Path Down",
    }


class BFDState(NumericConstant):
    """ BFD State """
    admin_down = 0
    down = 1
    init = 2
    up = 3

    desc_dict = {
        admin_down: "AdminDown",
        down: "Down",
        init: "Init",
        up: "Up",
    }


class BFDAuthType(NumericConstant):
    """ BFD Authentication Type """
    no_auth = 0
    simple_pwd = 1
    keyed_md5 = 2
    meticulous_keyed_md5 = 3
    keyed_sha1 = 4
    meticulous_keyed_sha1 = 5

    desc_dict = {
        no_auth: "No authentication",
        simple_pwd: "Simple Password",
        keyed_md5: "Keyed MD5",
        meticulous_keyed_md5: "Meticulous Keyed MD5",
        keyed_sha1: "Keyed SHA1",
        meticulous_keyed_sha1: "Meticulous Keyed SHA1",
    }


def bfd_is_auth_used(pkt):
    """ is packet authenticated? """
    return "A" in pkt.sprintf("%BFD.flags%")


def bfd_is_simple_pwd_used(pkt):
    """ is simple password authentication used? """
    return bfd_is_auth_used(pkt) and pkt.auth_type == BFDAuthType.simple_pwd


def bfd_is_sha1_used(pkt):
    """ is sha1 authentication used? """
    return bfd_is_auth_used(pkt) and pkt.auth_type in \
        (BFDAuthType.keyed_sha1, BFDAuthType.meticulous_keyed_sha1)


def bfd_is_md5_used(pkt):
    """ is md5 authentication used? """
    return bfd_is_auth_used(pkt) and pkt.auth_type in \
        (BFDAuthType.keyed_md5, BFDAuthType.meticulous_keyed_md5)


def bfd_is_md5_or_sha1_used(pkt):
    """ is md5 or sha1 used? """
    return bfd_is_md5_used(pkt) or bfd_is_sha1_used(pkt)


class BFD(Packet):
    """ BFD protocol layer for scapy """

    udp_dport = 3784  #: BFD destination port per RFC 5881
    udp_dport_echo = 3785  # : BFD destination port for ECHO per RFC 5881
    udp_sport_min = 49152  #: BFD source port min value per RFC 5881
    udp_sport_max = 65535  #: BFD source port max value per RFC 5881
    bfd_pkt_len = 24  # : length of BFD pkt without authentication section
    sha1_auth_len = 28  # : length of authentication section if SHA1 used

    name = "BFD"

    fields_desc = [
        BitField("version", 1, 3),
        BitEnumField("diag", 0, 5, BFDDiagCode.desc_dict),
        BitEnumField("state", 0, 2, BFDState.desc_dict),
        FlagsField("flags", 0, 6, ['M', 'D', 'A', 'C', 'F', 'P']),
        XByteField("detect_mult", 0),
        BitField("length", bfd_pkt_len, 8),
        BitField("my_discriminator", 0, 32),
        BitField("your_discriminator", 0, 32),
        BitField("desired_min_tx_interval", 0, 32),
        BitField("required_min_rx_interval", 0, 32),
        BitField("required_min_echo_rx_interval", 0, 32),
        ConditionalField(
            BitEnumField("auth_type", 0, 8, BFDAuthType.desc_dict),
            bfd_is_auth_used),
        ConditionalField(BitField("auth_len", 0, 8), bfd_is_auth_used),
        ConditionalField(BitField("auth_key_id", 0, 8), bfd_is_auth_used),
        ConditionalField(BitField("auth_reserved", 0, 8),
                         bfd_is_md5_or_sha1_used),
        ConditionalField(
            BitField("auth_seq_num", 0, 32), bfd_is_md5_or_sha1_used),
        ConditionalField(StrField("auth_key_hash", "0" * 16), bfd_is_md5_used),
        ConditionalField(
            StrField("auth_key_hash", "0" * 20), bfd_is_sha1_used),
    ]

    def mysummary(self):
        return self.sprintf("BFD(my_disc=%BFD.my_discriminator%,"
                            "your_disc=%BFD.your_discriminator%)")


# glue the BFD packet class to scapy parser
bind_layers(UDP, BFD, dport=BFD.udp_dport)


class BFD_vpp_echo(Packet):
    """ BFD echo packet as used by VPP (non-rfc, as rfc doesn't define one) """

    udp_dport = 3785  #: BFD echo destination port per RFC 5881
    name = "BFD_VPP_ECHO"

    fields_desc = [
        BitField("discriminator", 0, 32),
        BitField("expire_time_clocks", 0, 64),
        BitField("checksum", 0, 64)
    ]

    def mysummary(self):
        return self.sprintf(
            "BFD_VPP_ECHO(disc=%BFD_VPP_ECHO.discriminator%,"
            "expire_time_clocks=%BFD_VPP_ECHO.expire_time_clocks%)")


# glue the BFD echo packet class to scapy parser
bind_layers(UDP, BFD_vpp_echo, dport=BFD_vpp_echo.udp_dport)


class VppBFDAuthKey(VppObject):
    """ Represents BFD authentication key in VPP """

    def __init__(self, test, conf_key_id, auth_type, key):
        self._test = test
        self._key = key
        self._auth_type = auth_type
        test.assertIn(auth_type, BFDAuthType.desc_dict)
        self._conf_key_id = conf_key_id

    @property
    def test(self):
        """ Test which created this key """
        return self._test

    @property
    def auth_type(self):
        """ Authentication type for this key """
        return self._auth_type

    @property
    def key(self):
        """ key data """
        return self._key

    @key.setter
    def key(self, value):
        self._key = value

    @property
    def conf_key_id(self):
        """ configuration key ID """
        return self._conf_key_id

    def add_vpp_config(self):
        self.test.vapi.bfd_auth_set_key(
            conf_key_id=self._conf_key_id, auth_type=self._auth_type,
            key=self._key, key_len=len(self._key))
        self._test.registry.register(self, self.test.logger)

    def get_bfd_auth_keys_dump_entry(self):
        """ get the entry in the auth keys dump corresponding to this key """
        result = self.test.vapi.bfd_auth_keys_dump()
        for k in result:
            if k.conf_key_id == self._conf_key_id:
                return k
        return None

    def query_vpp_config(self):
        return self.get_bfd_auth_keys_dump_entry() is not None

    def remove_vpp_config(self):
        self.test.vapi.bfd_auth_del_key(conf_key_id=self._conf_key_id)

    def object_id(self):
        return "bfd-auth-key-%s" % self._conf_key_id


class VppBFDUDPSession(VppObject):
    """ Represents BFD UDP session in VPP """

    def __init__(self, test, interface, peer_addr, local_addr=None, af=AF_INET,
                 desired_min_tx=300000, required_min_rx=300000, detect_mult=3,
                 sha1_key=None, bfd_key_id=None, is_tunnel=False):
        self._test = test
        self._interface = interface
        self._af = af
        if local_addr:
            self._local_addr = VppIpAddress(local_addr)
        else:
            self._local_addr = None
        self._peer_addr = VppIpAddress(peer_addr)
        self._desired_min_tx = desired_min_tx
        self._required_min_rx = required_min_rx
        self._detect_mult = detect_mult
        self._sha1_key = sha1_key
        if bfd_key_id is not None:
            self._bfd_key_id = bfd_key_id
        else:
            self._bfd_key_id = randint(0, 255)
        self._is_tunnel = is_tunnel

    @property
    def test(self):
        """ Test which created this session """
        return self._test

    @property
    def interface(self):
        """ Interface on which this session lives """
        return self._interface

    @property
    def af(self):
        """ Address family - AF_INET or AF_INET6 """
        return self._af

    @property
    def local_addr(self):
        """ BFD session local address (VPP address) """
        if self._local_addr is None:
            if self.af == AF_INET:
                return self._interface.local_ip4
            elif self.af == AF_INET6:
                return self._interface.local_ip6
            else:
                raise Exception("Unexpected af '%s'" % self.af)
        return self._local_addr.address

    @property
    def peer_addr(self):
        """ BFD session peer address """
        return self._peer_addr.address

    def get_bfd_udp_session_dump_entry(self):
        """ get the namedtuple entry from bfd udp session dump """
        result = self.test.vapi.bfd_udp_session_dump()
        for s in result:
            self.test.logger.debug("session entry: %s" % str(s))
            if s.sw_if_index == self.interface.sw_if_index:
                if self.af == AF_INET \
                        and self.interface.local_ip4 == str(s.local_addr) \
                        and self.interface.remote_ip4 == str(s.peer_addr):
                    return s
                if self.af == AF_INET6 \
                        and self.interface.local_ip6 == str(s.local_addr) \
                        and self.interface.remote_ip6 == str(s.peer_addr):
                    return s
        return None

    @property
    def state(self):
        """ BFD session state """
        session = self.get_bfd_udp_session_dump_entry()
        if session is None:
            raise Exception("Could not find BFD session in VPP response")
        return session.state

    @property
    def desired_min_tx(self):
        """ desired minimum tx interval """
        return self._desired_min_tx

    @property
    def required_min_rx(self):
        """ required minimum rx interval """
        return self._required_min_rx

    @property
    def detect_mult(self):
        """ detect multiplier """
        return self._detect_mult

    @property
    def sha1_key(self):
        """ sha1 key """
        return self._sha1_key

    @property
    def bfd_key_id(self):
        """ bfd key id in use """
        return self._bfd_key_id

    @property
    def is_tunnel(self):
        return self._is_tunnel

    def activate_auth(self, key, bfd_key_id=None, delayed=False):
        """ activate authentication for this session """
        self._bfd_key_id = bfd_key_id if bfd_key_id else randint(0, 255)
        self._sha1_key = key
        conf_key_id = self._sha1_key.conf_key_id
        is_delayed = 1 if delayed else 0
        self.test.vapi.bfd_udp_auth_activate(
            sw_if_index=self._interface.sw_if_index,
            local_addr=self.local_addr.encode(),
            peer_addr=self.peer_addr.encode(),
            bfd_key_id=self._bfd_key_id,
            conf_key_id=conf_key_id,
            is_delayed=is_delayed)

    def deactivate_auth(self, delayed=False):
        """ deactivate authentication """
        self._bfd_key_id = None
        self._sha1_key = None
        is_delayed = 1 if delayed else 0
        self.test.vapi.bfd_udp_auth_deactivate(
            sw_if_index=self._interface.sw_if_index,
            local_addr=self.local_addr.encode(),
            peer_addr=self.peer_addr.encode(),
            is_delayed=is_delayed)

    def modify_parameters(self,
                          detect_mult=None,
                          desired_min_tx=None,
                          required_min_rx=None):
        """ modify session parameters """
        if detect_mult:
            self._detect_mult = detect_mult
        if desired_min_tx:
            self._desired_min_tx = desired_min_tx
        if required_min_rx:
            self._required_min_rx = required_min_rx
        self.test.vapi.bfd_udp_mod(sw_if_index=self._interface.sw_if_index,
                                   desired_min_tx=self.desired_min_tx,
                                   required_min_rx=self.required_min_rx,
                                   detect_mult=self.detect_mult,
                                   local_addr=self.local_addr.encode(),
                                   peer_addr=self.peer_addr.encode())

    def add_vpp_config(self):
        bfd_key_id = self._bfd_key_id if self._sha1_key else None
        conf_key_id = self._sha1_key.conf_key_id if self._sha1_key else None
        is_authenticated = True if self._sha1_key else False
        self.test.vapi.bfd_udp_add(sw_if_index=self._interface.sw_if_index,
                                   desired_min_tx=self.desired_min_tx,
                                   required_min_rx=self.required_min_rx,
                                   detect_mult=self.detect_mult,
                                   local_addr=self.local_addr.encode(),
                                   peer_addr=self.peer_addr.encode(),
                                   bfd_key_id=bfd_key_id,
                                   conf_key_id=conf_key_id,
                                   is_authenticated=is_authenticated)
        self._test.registry.register(self, self.test.logger)

    def query_vpp_config(self):
        session = self.get_bfd_udp_session_dump_entry()
        return session is not None

    def remove_vpp_config(self):
        self.test.vapi.bfd_udp_del(self._interface.sw_if_index,
                                   local_addr=self.local_addr.encode(),
                                   peer_addr=self.peer_addr.encode())

    def object_id(self):
        return "bfd-udp-%s-%s-%s-%s" % (self._interface.sw_if_index,
                                        self.local_addr,
                                        self.peer_addr,
                                        self.af)

    def admin_up(self):
        """ set bfd session admin-up """
        self.test.vapi.bfd_udp_session_set_flags(
            flags=VppEnum.vl_api_if_status_flags_t.IF_STATUS_API_FLAG_ADMIN_UP,
            sw_if_index=self._interface.sw_if_index,
            local_addr=self.local_addr.encode(),
            peer_addr=self.peer_addr.encode())

    def admin_down(self):
        """ set bfd session admin-down """
        self.test.vapi.bfd_udp_session_set_flags(
            flags=0, sw_if_index=self._interface.sw_if_index,
            local_addr=self.local_addr.encode(),
            peer_addr=self.peer_addr.encode())
lass="kt">int with_stateful_datapath, int node_trace_on, int reclassify_sessions) { u32 n_left; u32 pkts_exist_session = 0; u32 pkts_new_session = 0; u32 pkts_acl_permit = 0; u32 trace_bitmap = 0; acl_main_t *am = &acl_main; vlib_node_runtime_t *error_node; vlib_error_t no_error_existing_session; u64 now = clib_cpu_time_now (); uword thread_index = os_get_thread_index (); acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index]; u16 *next; vlib_buffer_t **b; u32 *sw_if_index; fa_5tuple_t *fa_5tuple; u64 *hash; /* for the delayed counters */ u32 saved_matched_acl_index = 0; u32 saved_matched_ace_index = 0; u32 saved_packet_count = 0; u32 saved_byte_count = 0; error_node = vlib_node_get_runtime (vm, node->node_index); no_error_existing_session = error_node->errors[ACL_FA_ERROR_ACL_EXIST_SESSION]; b = pw->bufs; next = pw->nexts; sw_if_index = pw->sw_if_indices; fa_5tuple = pw->fa_5tuples; hash = pw->hashes; /* * Now the "hard" work of session lookups and ACL lookups for new sessions. * Due to the complexity, do it for the time being in single loop with * the pipeline of three prefetches: * 1) bucket for the session bihash * 2) data for the session bihash * 3) worker session record */ fa_full_session_id_t f_sess_id_next = {.as_u64 = ~0ULL }; /* find the "next" session so we can kickstart the pipeline */ if (with_stateful_datapath) acl_fa_find_session_with_hash (am, is_ip6, sw_if_index[0], hash[0], &fa_5tuple[0], &f_sess_id_next.as_u64); n_left = frame->n_vectors; while (n_left > 0) { u8 action = 0; u32 lc_index0 = ~0; int acl_check_needed = 1; u32 match_acl_in_index = ~0; u32 match_acl_pos = ~0; u32 match_rule_index = ~0; next[0] = 0; /* drop by default */ /* Try to match an existing session first */ if (with_stateful_datapath) { fa_full_session_id_t f_sess_id = f_sess_id_next; switch (n_left) { default: acl_fa_prefetch_session_bucket_for_hash (am, is_ip6, hash[5]); /* fallthrough */ case 5: case 4: acl_fa_prefetch_session_data_for_hash (am, is_ip6, hash[3]); /* fallthrough */ case 3: case 2: acl_fa_find_session_with_hash (am, is_ip6, sw_if_index[1], hash[1], &fa_5tuple[1], &f_sess_id_next.as_u64); if (f_sess_id_next.as_u64 != ~0ULL) { prefetch_session_entry (am, f_sess_id_next); } /* fallthrough */ case 1: if (f_sess_id.as_u64 != ~0ULL) { if (node_trace_on) { trace_bitmap |= 0x80000000; } ASSERT (f_sess_id.thread_index < vlib_get_n_threads ()); b[0]->error = no_error_existing_session; acl_check_needed = 0; pkts_exist_session += 1; action = process_established_session (vm, am, node->node_index, is_input, now, f_sess_id, &sw_if_index[0], &fa_5tuple[0], b[0]->current_length, node_trace_on, &trace_bitmap); /* expose the session id to the tracer */ if (node_trace_on) { match_rule_index = f_sess_id.session_index; } if (reclassify_sessions) { if (PREDICT_FALSE (stale_session_deleted (am, is_input, pw, now, sw_if_index[0], f_sess_id))) { acl_check_needed = 1; if (node_trace_on) { trace_bitmap |= 0x40000000; } /* * If we have just deleted the session, and the next * buffer is the same 5-tuple, that session prediction * is wrong, correct it. */ if ((f_sess_id_next.as_u64 != ~0ULL) && 0 == memcmp (&fa_5tuple[1], &fa_5tuple[0], sizeof (fa_5tuple[1]))) f_sess_id_next.as_u64 = ~0ULL; } } } } if (acl_check_needed) { if (is_input) lc_index0 = am->input_lc_index_by_sw_if_index[sw_if_index[0]]; else lc_index0 = am->output_lc_index_by_sw_if_index[sw_if_index[0]]; action = 0; /* deny by default */ int is_match = acl_plugin_match_5tuple_inline (am, lc_index0, (fa_5tuple_opaque_t *) & fa_5tuple[0], is_ip6, &action, &match_acl_pos, &match_acl_in_index, &match_rule_index, &trace_bitmap); if (PREDICT_FALSE (is_match && am->interface_acl_counters_enabled)) { u32 buf_len = vlib_buffer_length_in_chain (vm, b[0]); vlib_increment_combined_counter (am->combined_acl_counters + saved_matched_acl_index, thread_index, saved_matched_ace_index, saved_packet_count, saved_byte_count); saved_matched_acl_index = match_acl_in_index; saved_matched_ace_index = match_rule_index; saved_packet_count = 1; saved_byte_count = buf_len; /* prefetch the counter that we are going to increment */ vlib_prefetch_combined_counter (am->combined_acl_counters + saved_matched_acl_index, thread_index, saved_matched_ace_index); } b[0]->error = error_node->errors[action]; if (1 == action) pkts_acl_permit++; if (2 == action) { if (!acl_fa_can_add_session (am, is_input, sw_if_index[0])) acl_fa_try_recycle_session (am, is_input, thread_index, sw_if_index[0], now); if (acl_fa_can_add_session (am, is_input, sw_if_index[0])) { u16 current_policy_epoch = get_current_policy_epoch (am, is_input, sw_if_index[0]); fa_full_session_id_t f_sess_id = acl_fa_add_session (am, is_input, is_ip6, sw_if_index[0], now, &fa_5tuple[0], current_policy_epoch); /* perform the accounting for the newly added session */ process_established_session (vm, am, node->node_index, is_input, now, f_sess_id, &sw_if_index[0], &fa_5tuple[0], b[0]->current_length, node_trace_on, &trace_bitmap); pkts_new_session++; /* * If the next 5tuple is the same and we just added the session, * the f_sess_id_next can not be ~0. Correct it. */ if ((f_sess_id_next.as_u64 == ~0ULL) && 0 == memcmp (&fa_5tuple[1], &fa_5tuple[0], sizeof (fa_5tuple[1]))) f_sess_id_next = f_sess_id; } else { action = 0; b[0]->error = error_node->errors [ACL_FA_ERROR_ACL_TOO_MANY_SESSIONS]; } } } { /* speculatively get the next0 */ vnet_feature_next_u16 (&next[0], b[0]); /* if the action is not deny - then use that next */ next[0] = action ? next[0] : 0; } if (node_trace_on) // PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) { maybe_trace_buffer (vm, node, b[0], sw_if_index[0], lc_index0, next[0], match_acl_in_index, match_rule_index, &fa_5tuple[0], action, trace_bitmap); } next++; b++; fa_5tuple++; sw_if_index++; hash++; n_left -= 1; } } /* * if we were had an acl match then we have a counter to increment. * else it is all zeroes, so this will be harmless. */ vlib_increment_combined_counter (am->combined_acl_counters + saved_matched_acl_index, thread_index, saved_matched_ace_index, saved_packet_count, saved_byte_count); vlib_node_increment_counter (vm, node->node_index, ACL_FA_ERROR_ACL_CHECK, frame->n_vectors); vlib_node_increment_counter (vm, node->node_index, ACL_FA_ERROR_ACL_EXIST_SESSION, pkts_exist_session); vlib_node_increment_counter (vm, node->node_index, ACL_FA_ERROR_ACL_NEW_SESSION, pkts_new_session); vlib_node_increment_counter (vm, node->node_index, ACL_FA_ERROR_ACL_PERMIT, pkts_acl_permit); return frame->n_vectors; } always_inline uword acl_fa_outer_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6, int is_input, int is_l2_path, int do_stateful_datapath) { acl_main_t *am = &acl_main; acl_fa_node_common_prepare_fn (vm, node, frame, is_ip6, is_input, is_l2_path, do_stateful_datapath); if (am->reclassify_sessions) { if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input, is_l2_path, do_stateful_datapath, 1 /* trace */ , 1 /* reclassify */ ); else return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input, is_l2_path, do_stateful_datapath, 0, 1 /* reclassify */ ); } else { if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input, is_l2_path, do_stateful_datapath, 1 /* trace */ , 0); else return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input, is_l2_path, do_stateful_datapath, 0, 0); } } always_inline uword acl_fa_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6, int is_input, int is_l2_path) { /* select the reclassify/no-reclassify version of the datapath */ acl_main_t *am = &acl_main; acl_fa_per_worker_data_t *pw = &am->per_worker_data[vm->thread_index]; uword rv; if (am->fa_sessions_hash_is_initialized) rv = acl_fa_outer_node_fn (vm, node, frame, is_ip6, is_input, is_l2_path, 1); else rv = acl_fa_outer_node_fn (vm, node, frame, is_ip6, is_input, is_l2_path, 0); vlib_buffer_enqueue_to_next (vm, node, vlib_frame_vector_args (frame), pw->nexts, frame->n_vectors); return rv; } static u8 * format_fa_5tuple (u8 * s, va_list * args) { fa_5tuple_t *p5t = va_arg (*args, fa_5tuple_t *); void *paddr0; void *paddr1; void *format_address_func; void *ip_af; void *ip_frag_txt = p5t->pkt.is_nonfirst_fragment ? " non-initial fragment" : ""; if (p5t->pkt.is_ip6) { ip_af = "ip6"; format_address_func = format_ip6_address; paddr0 = &p5t->ip6_addr[0]; paddr1 = &p5t->ip6_addr[1]; } else { ip_af = "ip4"; format_address_func = format_ip4_address; paddr0 = &p5t->ip4_addr[0]; paddr1 = &p5t->ip4_addr[1]; } s = format (s, "lc_index %d l3 %s%s ", p5t->pkt.lc_index, ip_af, ip_frag_txt); s = format (s, "%U -> %U ", format_address_func, paddr0, format_address_func, paddr1); s = format (s, "%U ", format_fa_session_l4_key, &p5t->l4); s = format (s, "tcp flags (%s) %02x rsvd %x", p5t->pkt.tcp_flags_valid ? "valid" : "invalid", p5t->pkt.tcp_flags, p5t->pkt.flags_reserved); return s; } #ifndef CLIB_MARCH_VARIANT u8 * format_acl_plugin_5tuple (u8 * s, va_list * args) { return format_fa_5tuple (s, args); } #endif /* packet trace format function */ static u8 * format_acl_plugin_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); acl_fa_trace_t *t = va_arg (*args, acl_fa_trace_t *); s = format (s, "acl-plugin: lc_index: %d, sw_if_index %d, next index %d, action: %d, match: acl %d rule %d trace_bits %08x\n" " pkt info %016llx %016llx %016llx %016llx %016llx %016llx", t->lc_index, t->sw_if_index, t->next_index, t->action, t->match_acl_in_index, t->match_rule_index, t->trace_bitmap, t->packet_info[0], t->packet_info[1], t->packet_info[2], t->packet_info[3], t->packet_info[4], t->packet_info[5]); /* Now also print out the packet_info in a form usable by humans */ s = format (s, "\n %U", format_fa_5tuple, t->packet_info); return s; } /* *INDENT-OFF* */ static char *acl_fa_error_strings[] = { #define _(sym,string) string, foreach_acl_fa_error #undef _ }; VLIB_NODE_FN (acl_in_l2_ip6_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 1, 1, 1); } VLIB_NODE_FN (acl_in_l2_ip4_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 0, 1, 1); } VLIB_NODE_FN (acl_out_l2_ip6_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 1, 0, 1); } VLIB_NODE_FN (acl_out_l2_ip4_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 0, 0, 1); } /**** L3 processing path nodes ****/ VLIB_NODE_FN (acl_in_fa_ip6_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 1, 1, 0); } VLIB_NODE_FN (acl_in_fa_ip4_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 0, 1, 0); } VLIB_NODE_FN (acl_out_fa_ip6_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 1, 0, 0); } VLIB_NODE_FN (acl_out_fa_ip4_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return acl_fa_node_fn (vm, node, frame, 0, 0, 0); } VLIB_REGISTER_NODE (acl_in_l2_ip6_node) = { .name = "acl-plugin-in-ip6-l2", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_in_l2_ip6_fa_feature, static) = { .arc_name = "l2-input-ip6", .node_name = "acl-plugin-in-ip6-l2", .runs_before = VNET_FEATURES ("l2-input-feat-arc-end"), }; VLIB_REGISTER_NODE (acl_in_l2_ip4_node) = { .name = "acl-plugin-in-ip4-l2", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_in_l2_ip4_fa_feature, static) = { .arc_name = "l2-input-ip4", .node_name = "acl-plugin-in-ip4-l2", .runs_before = VNET_FEATURES ("l2-input-feat-arc-end"), }; VLIB_REGISTER_NODE (acl_out_l2_ip6_node) = { .name = "acl-plugin-out-ip6-l2", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_out_l2_ip6_fa_feature, static) = { .arc_name = "l2-output-ip6", .node_name = "acl-plugin-out-ip6-l2", .runs_before = VNET_FEATURES ("l2-output-feat-arc-end"), }; VLIB_REGISTER_NODE (acl_out_l2_ip4_node) = { .name = "acl-plugin-out-ip4-l2", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_out_l2_ip4_fa_feature, static) = { .arc_name = "l2-output-ip4", .node_name = "acl-plugin-out-ip4-l2", .runs_before = VNET_FEATURES ("l2-output-feat-arc-end"), }; VLIB_REGISTER_NODE (acl_in_fa_ip6_node) = { .name = "acl-plugin-in-ip6-fa", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_in_ip6_fa_feature, static) = { .arc_name = "ip6-unicast", .node_name = "acl-plugin-in-ip6-fa", .runs_before = VNET_FEATURES ("ip6-flow-classify"), }; VLIB_REGISTER_NODE (acl_in_fa_ip4_node) = { .name = "acl-plugin-in-ip4-fa", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_in_ip4_fa_feature, static) = { .arc_name = "ip4-unicast", .node_name = "acl-plugin-in-ip4-fa", .runs_before = VNET_FEATURES ("ip4-flow-classify"), }; VLIB_REGISTER_NODE (acl_out_fa_ip6_node) = { .name = "acl-plugin-out-ip6-fa", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_out_ip6_fa_feature, static) = { .arc_name = "ip6-output", .node_name = "acl-plugin-out-ip6-fa", .runs_before = VNET_FEATURES ("ip6-dvr-reinject", "interface-output"), }; VLIB_REGISTER_NODE (acl_out_fa_ip4_node) = { .name = "acl-plugin-out-ip4-fa", .vector_size = sizeof (u32), .format_trace = format_acl_plugin_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (acl_fa_error_strings), .error_strings = acl_fa_error_strings, .n_next_nodes = ACL_FA_N_NEXT, /* edit / add dispositions here */ .next_nodes = { [ACL_FA_ERROR_DROP] = "error-drop", } }; VNET_FEATURE_INIT (acl_out_ip4_fa_feature, static) = { .arc_name = "ip4-output", .node_name = "acl-plugin-out-ip4-fa", .runs_before = VNET_FEATURES ("ip4-dvr-reinject", "interface-output"), }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */