summaryrefslogtreecommitdiffstats
path: root/test/vpp_sub_interface.py
blob: 989769f09fb58541e6546692ad4db53b32c82b53 (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
from scapy.layers.l2 import Dot1Q
import abc
from vpp_pg_interface import VppPGInterface
from vpp_interface import VppInterface
from vpp_papi import VppEnum


class L2_VTR_OP:
    L2_DISABLED = 0
    L2_PUSH_1 = 1
    L2_PUSH_2 = 2
    L2_POP_1 = 3
    L2_POP_2 = 4
    L2_TRANSLATE_1_1 = 5
    L2_TRANSLATE_1_2 = 6
    L2_TRANSLATE_2_1 = 7
    L2_TRANSLATE_2_2 = 8


class VppSubInterface(VppPGInterface, metaclass=abc.ABCMeta):

    @property
    def parent(self):
        """Parent interface for this sub-interface"""
        return self._parent

    @property
    def sub_id(self):
        """Sub-interface ID"""
        return self._sub_id

    @property
    def tag1(self):
        return self._tag1

    @property
    def tag2(self):
        return self._tag2

    @property
    def vtr(self):
        return self._vtr

    def __init__(self, test, parent, sub_id):
        VppInterface.__init__(self, test)
        self._parent = parent
        self._parent.add_sub_if(self)
        self._sub_id = sub_id
        self.DOT1AD_TYPE = 0x88A8
        self.DOT1Q_TYPE = 0x8100

    def set_sw_if_index(self, sw_if_index):
        super(VppSubInterface, self).set_sw_if_index(sw_if_index)
        self.set_vtr(L2_VTR_OP.L2_DISABLED)

    @abc.abstractmethod
    def create_arp_req(self):
        pass

    @abc.abstractmethod
    def create_ndp_req(self):
        pass

    def resolve_arp(self):
        super(VppSubInterface, self).resolve_arp(self.parent)

    def resolve_ndp(self):
        super(VppSubInterface, self).resolve_ndp(self.parent)

    @abc.abstractmethod
    def add_dot1_layer(self, pkt):
        pass

    def remove_vpp_config(self):
        self.test.vapi.delete_subif(self.sw_if_index)

    def _add_tag(self, packet, vlan, tag_type):
        payload = packet.payload
        inner_type = packet.type
        packet.remove_payload()
        packet.add_payload(Dot1Q(vlan=vlan) / payload)
        packet.payload.type = inner_type
        packet.payload.vlan = vlan
        packet.type = tag_type
        return packet

    def _remove_tag(self, packet, vlan=None, tag_type=None):
        if tag_type:
            self.test.instance().assertEqual(packet.type, tag_type)

        payload = packet.payload
        if vlan:
            self.test.instance().assertEqual(payload.vlan, vlan)
        inner_type = payload.type
        payload = payload.payload
        packet.remove_payload()
        packet.add_payload(payload)
        packet.type = inner_type
        return packet

    def add_dot1q_layer(self, packet, vlan):
        return self._add_tag(packet, vlan, self.DOT1Q_TYPE)

    def add_dot1ad_layer(self, packet, outer, inner):
        p = self._add_tag(packet, inner, self.DOT1Q_TYPE)
        return self._add_tag(p, outer, self.DOT1AD_TYPE)

    def remove_dot1q_layer(self, packet, vlan=None):
        return self._remove_tag(packet, vlan, self.DOT1Q_TYPE)

    def remove_dot1ad_layer(self, packet, outer=None, inner=None):
        p = self._remove_tag(packet, outer, self.DOT1AD_TYPE)
        return self._remove_tag(p, inner, self.DOT1Q_TYPE)

    def set_vtr(self, vtr, push1q=0, tag=None, inner=None, outer=None):
        self._tag1 = 0
        self._tag2 = 0
        self._push1q = 0

        if (vtr == L2_VTR_OP.L2_PUSH_1 or
            vtr == L2_VTR_OP.L2_TRANSLATE_1_1 or
                vtr == L2_VTR_OP.L2_TRANSLATE_2_1):
            self._tag1 = tag
            self._push1q = push1q
        if (vtr == L2_VTR_OP.L2_PUSH_2 or
            vtr == L2_VTR_OP.L2_TRANSLATE_1_2 or
                vtr == L2_VTR_OP.L2_TRANSLATE_2_2):
            self._tag1 = outer
            self._tag2 = inner
            self._push1q = push1q

        self.test.vapi.l2_interface_vlan_tag_rewrite(
            sw_if_index=self.sw_if_index, vtr_op=vtr, push_dot1q=self._push1q,
            tag1=self._tag1, tag2=self._tag2)
        self._vtr = vtr


class VppDot1QSubint(VppSubInterface):

    @property
    def vlan(self):
        """VLAN tag"""
        return self._vlan

    def __init__(self, test, parent, sub_id, vlan=None):
        super(VppDot1QSubint, self).__init__(test, parent, sub_id)
        if vlan is None:
            vlan = sub_id
        self._vlan = vlan
        r = test.vapi.create_vlan_subif(parent.sw_if_index, vlan)
        self.set_sw_if_index(r.sw_if_index)

    def create_arp_req(self):
        packet = VppPGInterface.create_arp_req(self)
        return self.add_dot1_layer(packet)

    def create_ndp_req(self):
        packet = VppPGInterface.create_ndp_req(self)
        return self.add_dot1_layer(packet)

    # called before sending packet
    def add_dot1_layer(self, packet):
        return self.add_dot1q_layer(packet, self.vlan)

    # called on received packet to "reverse" the add call
    def remove_dot1_layer(self, packet):
        return self.remove_dot1q_layer(packet, self.vlan)


class VppDot1ADSubint(VppSubInterface):

    @property
    def outer_vlan(self):
        """Outer VLAN tag"""
        return self._outer_vlan

    @property
    def inner_vlan(self):
        """Inner VLAN tag"""
        return self._inner_vlan

    def __init__(self, test, parent, sub_id, outer_vlan, inner_vlan):
        super(VppDot1ADSubint, self).__init__(test, parent, sub_id)
        flags = (VppEnum.vl_api_sub_if_flags_t.SUB_IF_API_FLAG_DOT1AD |
                 VppEnum.vl_api_sub_if_flags_t.SUB_IF_API_FLAG_TWO_TAGS |
                 VppEnum.vl_api_sub_if_flags_t.SUB_IF_API_FLAG_EXACT_MATCH)
        r = test.vapi.create_subif(sw_if_index=parent.sw_if_index,
                                   sub_id=sub_id, outer_vlan_id=outer_vlan,
                                   inner_vlan_id=inner_vlan,
                                   sub_if_flags=flags)
        self.set_sw_if_index(r.sw_if_index)
        self._outer_vlan = outer_vlan
        self._inner_vlan = inner_vlan

    def create_arp_req(self):
        packet = VppPGInterface.create_arp_req(self)
        return self.add_dot1_layer(packet)

    def create_ndp_req(self):
        packet = VppPGInterface.create_ndp_req(self)
        return self.add_dot1_layer(packet)

    def add_dot1_layer(self, packet):
        return self.add_dot1ad_layer(packet, self.outer_vlan, self.inner_vlan)

    def remove_dot1_layer(self, packet):
        return self.remove_dot1ad_layer(packet, self.outer_vlan,
                                        self.inner_vlan)


class VppP2PSubint(VppSubInterface):

    def __init__(self, test, parent, sub_id, remote_mac):
        super(VppP2PSubint, self).__init__(test, parent, sub_id)
        r = test.vapi.p2p_ethernet_add(parent.sw_if_index,
                                       remote_mac, sub_id)
        self.set_sw_if_index(r.sw_if_index)
        self.parent_sw_if_index = parent.sw_if_index
        self.p2p_remote_mac = remote_mac

    def add_dot1_layer(self, packet):
        return packet

    def remove_dot1_layer(self, packet):
        return packet

    def create_arp_req(self):
        packet = VppPGInterface.create_arp_req(self)
        return packet

    def create_ndp_req(self):
        packet = VppPGInterface.create_ndp_req(self)
        return packet
pan> { vl_msg_api_msg_config_t cfg; vl_msg_api_msg_config_t *c = &cfg; clib_memset (c, 0, sizeof (*c)); #define _(N,n) do { \ c->id = VL_API_##N; \ c->name = #n; \ c->handler = vl_api_##n##_t_handler; \ c->cleanup = vl_noop_handler; \ c->endian = vl_api_##n##_t_endian; \ c->print = vl_api_##n##_t_print; \ c->size = sizeof(vl_api_##n##_t); \ c->traced = 1; /* trace, so these msgs print */ \ c->replay = 0; /* don't replay client create/delete msgs */ \ c->message_bounce = 0; /* don't bounce this message */ \ vl_msg_api_config(c);} while (0); foreach_vlib_api_msg; #undef _ return 0; } u64 vector_rate_histogram[SLEEP_N_BUCKETS]; /* * Callback to send ourselves a plugin numbering-space trace msg */ static void send_one_plugin_msg_ids_msg (u8 * name, u16 first_msg_id, u16 last_msg_id) { vl_api_trace_plugin_msg_ids_t *mp; api_main_t *am = vlibapi_get_main (); vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr; svm_queue_t *q; mp = vl_msg_api_alloc_as_if_client (sizeof (*mp)); clib_memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_TRACE_PLUGIN_MSG_IDS); strncpy ((char *) mp->plugin_name, (char *) name, sizeof (mp->plugin_name) - 1); mp->first_msg_id = clib_host_to_net_u16 (first_msg_id); mp->last_msg_id = clib_host_to_net_u16 (last_msg_id); q = shmem_hdr->vl_input_queue; vl_msg_api_send_shmem (q, (u8 *) & mp); } void vl_api_save_msg_table (void) { u8 *serialized_message_table; api_main_t *am = vlibapi_get_main (); u8 *chroot_file; int fd, rv; /* * Snapshoot the api message table. */ if (strstr ((char *) am->save_msg_table_filename, "..") || index ((char *) am->save_msg_table_filename, '/')) { clib_warning ("illegal save-message-table filename '%s'", am->save_msg_table_filename); return; } chroot_file = format (0, "/tmp/%s%c", am->save_msg_table_filename, 0); fd = creat ((char *) chroot_file, 0644); if (fd < 0) { clib_unix_warning ("creat"); return; } serialized_message_table = vl_api_serialize_message_table (am, 0); rv = write (fd, serialized_message_table, vec_len (serialized_message_table)); if (rv != vec_len (serialized_message_table)) clib_unix_warning ("write"); rv = close (fd); if (rv < 0) clib_unix_warning ("close"); vec_free (chroot_file); vec_free (serialized_message_table); } clib_error_t *vat_builtin_main_init (vlib_main_t * vm) __attribute__ ((weak)); clib_error_t * vat_builtin_main_init (vlib_main_t * vm) { return 0; } static uword vl_api_clnt_process (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * f) { int private_segment_rotor = 0, i, rv; vl_socket_args_for_process_t *a; vl_shmem_hdr_t *shm; svm_queue_t *q; clib_error_t *e; api_main_t *am = vlibapi_get_main (); f64 dead_client_scan_time; f64 sleep_time, start_time; f64 vector_rate; clib_error_t *error; uword event_type; uword *event_data = 0; f64 now; if ((error = vl_sock_api_init (vm))) { clib_error_report (error); clib_warning ("socksvr_api_init failed, quitting..."); return 0; } if ((rv = vlib_api_init ()) < 0) { clib_warning ("vlib_api_init returned %d, quitting...", rv); return 0; } shm = am->shmem_hdr; q = shm->vl_input_queue; e = vlib_call_init_exit_functions (vm, &vm->api_init_function_registrations, 1 /* call_once */ ); if (e) clib_error_report (e); e = vat_builtin_main_init (vm); if (e) clib_error_report (e); sleep_time = 10.0; dead_client_scan_time = vlib_time_now (vm) + 10.0; /* * Send plugin message range messages for each plugin we loaded */ for (i = 0; i < vec_len (am->msg_ranges); i++) { vl_api_msg_range_t *rp = am->msg_ranges + i; send_one_plugin_msg_ids_msg (rp->name, rp->first_msg_id, rp->last_msg_id); } /* * Save the api message table snapshot, if configured */ if (am->save_msg_table_filename) vl_api_save_msg_table (); /* $$$ pay attention to frame size, control CPU usage */ while (1) { /* * There's a reason for checking the queue before * sleeping. If the vlib application crashes, it's entirely * possible for a client to enqueue a connect request * during the process restart interval. * * Unless some force of physics causes the new incarnation * of the application to process the request, the client will * sit and wait for Godot... */ vector_rate = (f64) vlib_last_vectors_per_main_loop (vm); start_time = vlib_time_now (vm); while (1) { if (vl_mem_api_handle_rpc (vm, node) || vl_mem_api_handle_msg_main (vm, node)) { vm->api_queue_nonempty = 0; VL_MEM_API_LOG_Q_LEN ("q-underflow: len %d", 0); sleep_time = 20.0; break; } /* Allow no more than 10us without a pause */ if (vlib_time_now (vm) > start_time + 10e-6) { int index = SLEEP_400_US; if (vector_rate > 40.0) sleep_time = 400e-6; else if (vector_rate > 20.0) { index = SLEEP_200_US; sleep_time = 200e-6; } else if (vector_rate >= 1.0) { index = SLEEP_100_US; sleep_time = 100e-6; } else { index = SLEEP_10_US; sleep_time = 10e-6; } vector_rate_histogram[index] += 1; break; } } /* * see if we have any private api shared-memory segments * If so, push required context variables, and process * a message. */ if (PREDICT_FALSE (vec_len (am->vlib_private_rps))) { if (private_segment_rotor >= vec_len (am->vlib_private_rps)) private_segment_rotor = 0; vl_mem_api_handle_msg_private (vm, node, private_segment_rotor++); } vlib_process_wait_for_event_or_clock (vm, sleep_time); vec_reset_length (event_data); event_type = vlib_process_get_events (vm, &event_data); now = vlib_time_now (vm); switch (event_type) { case QUEUE_SIGNAL_EVENT: vm->queue_signal_pending = 0; VL_MEM_API_LOG_Q_LEN ("q-awake: len %d", q->cursize); break; case SOCKET_READ_EVENT: for (i = 0; i < vec_len (event_data); i++) { vl_api_registration_t *regp; a = pool_elt_at_index (socket_main.process_args, event_data[i]); regp = vl_socket_get_registration (a->reg_index); if (regp) { vl_socket_process_api_msg (regp, (i8 *) a->data); a = pool_elt_at_index (socket_main.process_args, event_data[i]); } vec_free (a->data); pool_put (socket_main.process_args, a); } break; /* Timeout... */ case -1: break; default: clib_warning ("unknown event type %d", event_type); break; } if (now > dead_client_scan_time) { vl_mem_api_dead_client_scan (am, shm, now); dead_client_scan_time = vlib_time_now (vm) + 10.0; } } return 0; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (vl_api_clnt_node) = { .function = vl_api_clnt_process, .type = VLIB_NODE_TYPE_PROCESS, .name = "api-rx-from-ring", .state = VLIB_NODE_STATE_DISABLED, .process_log2_n_stack_bytes = 18, }; /* *INDENT-ON* */ void vl_mem_api_enable_disable (vlib_main_t * vm, int enable) { vlib_node_set_state (vm, vl_api_clnt_node.index, (enable ? VLIB_NODE_STATE_POLLING : VLIB_NODE_STATE_DISABLED)); } static uword api_rx_from_node (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { uword n_packets = frame->n_vectors; uword n_left_from; u32 *from; static u8 *long_msg; vec_validate (long_msg, 4095); n_left_from = frame->n_vectors; from = vlib_frame_vector_args (frame); while (n_left_from > 0) { u32 bi0; vlib_buffer_t *b0; void *msg; uword msg_len; bi0 = from[0]; b0 = vlib_get_buffer (vm, bi0); from += 1; n_left_from -= 1; msg = b0->data + b0->current_data; msg_len = b0->current_length; if (b0->flags & VLIB_BUFFER_NEXT_PRESENT) { ASSERT (long_msg != 0); _vec_len (long_msg) = 0; vec_add (long_msg, msg, msg_len); while (b0->flags & VLIB_BUFFER_NEXT_PRESENT) { b0 = vlib_get_buffer (vm, b0->next_buffer); msg = b0->data + b0->current_data; msg_len = b0->current_length; vec_add (long_msg, msg, msg_len); } msg = long_msg; } vl_msg_api_handler_no_trace_no_free (msg); } /* Free what we've been given. */ vlib_buffer_free (vm, vlib_frame_vector_args (frame), n_packets); return n_packets; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (api_rx_from_node_node,static) = { .function = api_rx_from_node, .type = VLIB_NODE_TYPE_INTERNAL, .vector_size = 4, .name = "api-rx-from-node", }; /* *INDENT-ON* */ static void vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp) { vl_api_rpc_call_reply_t *rmp; int (*fp) (void *); i32 rv = 0; vlib_main_t *vm = vlib_get_main (); if (mp->function == 0) { rv = -1; clib_warning ("rpc NULL function pointer"); } else { if (mp->need_barrier_sync) vlib_worker_thread_barrier_sync (vm); fp = uword_to_pointer (mp->function, int (*)(void *)); rv = fp (mp->data); if (mp->need_barrier_sync) vlib_worker_thread_barrier_release (vm); } if (mp->send_reply) { svm_queue_t *q = vl_api_client_index_to_input_queue (mp->client_index); if (q) { rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp)); rmp->_vl_msg_id = ntohs (VL_API_RPC_CALL_REPLY); rmp->context = mp->context; rmp->retval = rv; vl_msg_api_send_shmem (q, (u8 *) & rmp); } } if (mp->multicast) { clib_warning ("multicast not yet implemented..."); } } static void vl_api_rpc_call_reply_t_handler (vl_api_rpc_call_reply_t * mp) { clib_warning ("unimplemented"); } void vl_api_send_pending_rpc_requests (vlib_main_t * vm) { vlib_main_t *vm_global = &vlib_global_main; ASSERT (vm != vm_global); clib_spinlock_lock_if_init (&vm_global->pending_rpc_lock); vec_append (vm_global->pending_rpc_requests, vm->pending_rpc_requests); vec_reset_length (vm->pending_rpc_requests); clib_spinlock_unlock_if_init (&vm_global->pending_rpc_lock); } always_inline void vl_api_rpc_call_main_thread_inline (void *fp, u8 * data, u32 data_length, u8 force_rpc) { vl_api_rpc_call_t *mp; vlib_main_t *vm_global = &vlib_global_main; vlib_main_t *vm = vlib_get_main (); /* Main thread and not a forced RPC: call the function directly */ if ((force_rpc == 0) && (vlib_get_thread_index () == 0)) { void (*call_fp) (void *); vlib_worker_thread_barrier_sync (vm); call_fp = fp; call_fp (data); vlib_worker_thread_barrier_release (vm); return; } /* Otherwise, actually do an RPC */ mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) + data_length); clib_memset (mp, 0, sizeof (*mp)); clib_memcpy_fast (mp->data, data, data_length); mp->_vl_msg_id = ntohs (VL_API_RPC_CALL); mp->function = pointer_to_uword (fp); mp->need_barrier_sync = 1; /* Add to the pending vector. Thread 0 requires locking. */ if (vm == vm_global) clib_spinlock_lock_if_init (&vm_global->pending_rpc_lock); vec_add1 (vm->pending_rpc_requests, (uword) mp); if (vm == vm_global) clib_spinlock_unlock_if_init (&vm_global->pending_rpc_lock); } /* * Check if called from worker threads. * If so, make rpc call of fp through shmem. * Otherwise, call fp directly */ void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length) { vl_api_rpc_call_main_thread_inline (fp, data, data_length, /*force_rpc */ 0); } /* * Always make rpc call of fp through shmem, useful for calling from threads * not setup as worker threads, such as DPDK callback thread */ void vl_api_force_rpc_call_main_thread (void *fp, u8 * data, u32 data_length) { vl_api_rpc_call_main_thread_inline (fp, data, data_length, /*force_rpc */ 1); } static void vl_api_trace_plugin_msg_ids_t_handler (vl_api_trace_plugin_msg_ids_t * mp) { api_main_t *am = vlibapi_get_main (); vl_api_msg_range_t *rp; uword *p; /* Noop (except for tracing) during normal operation */ if (am->replay_in_progress == 0) return; p = hash_get_mem (am->msg_range_by_name, mp->plugin_name); if (p == 0) { clib_warning ("WARNING: traced plugin '%s' not in current image", mp->plugin_name); return; } rp = vec_elt_at_index (am->msg_ranges, p[0]); if (rp->first_msg_id != clib_net_to_host_u16 (mp->first_msg_id)) { clib_warning ("WARNING: traced plugin '%s' first message id %d not %d", mp->plugin_name, clib_net_to_host_u16 (mp->first_msg_id), rp->first_msg_id); } if (rp->last_msg_id != clib_net_to_host_u16 (mp->last_msg_id)) { clib_warning ("WARNING: traced plugin '%s' last message id %d not %d", mp->plugin_name, clib_net_to_host_u16 (mp->last_msg_id), rp->last_msg_id); } } #define foreach_rpc_api_msg \ _(RPC_CALL,rpc_call) \ _(RPC_CALL_REPLY,rpc_call_reply) #define foreach_plugin_trace_msg \ _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids) /* * Set the rpc callback at our earliest possible convenience. * This avoids ordering issues between thread_init() -> start_workers and * an init function which we could define here. If we ever intend to use * vlib all by itself, we can't create a link-time dependency on * an init function here and a typical "call foo_init first" * guitar lick. */ extern void *rpc_call_main_thread_cb_fn; static clib_error_t * rpc_api_hookup (vlib_main_t * vm) { api_main_t *am = vlibapi_get_main (); #define _(N,n) \ vl_msg_api_set_handlers(VL_API_##N, #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_noop_handler, \ vl_api_##n##_t_print, \ sizeof(vl_api_##n##_t), 0 /* do not trace */); foreach_rpc_api_msg; #undef _ #define _(N,n) \ vl_msg_api_set_handlers(VL_API_##N, #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_noop_handler, \ vl_api_##n##_t_print, \ sizeof(vl_api_##n##_t), 1 /* do trace */); foreach_plugin_trace_msg; #undef _ /* No reason to halt the parade to create a trace record... */ am->is_mp_safe[VL_API_TRACE_PLUGIN_MSG_IDS] = 1; rpc_call_main_thread_cb_fn = vl_api_rpc_call_main_thread; return 0; } VLIB_API_INIT_FUNCTION (rpc_api_hookup); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */