Line data Source code
1 : /*
2 : * Copyright (c) 2019 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 :
16 : //#include <vnet/fib/fib_source.h>
17 : #include <vnet/fib/fib_table.h>
18 : #include <vnet/udp/udp_local.h>
19 : #include <vppinfra/atomics.h>
20 :
21 : #include <nat/lib/log.h>
22 :
23 : #include <nat/nat44-ei/nat44_ei.h>
24 : #include <nat/nat44-ei/nat44_ei_ha.h>
25 : #include <nat/nat44-ei/nat44_ei_inlines.h>
26 :
27 : /* number of retries */
28 : #define NAT_HA_RETRIES 3
29 :
30 : #define foreach_nat_ha_counter \
31 : _(RECV_ADD, "add-event-recv", 0) \
32 : _(RECV_DEL, "del-event-recv", 1) \
33 : _(RECV_REFRESH, "refresh-event-recv", 2) \
34 : _(SEND_ADD, "add-event-send", 3) \
35 : _(SEND_DEL, "del-event-send", 4) \
36 : _(SEND_REFRESH, "refresh-event-send", 5) \
37 : _(RECV_ACK, "ack-recv", 6) \
38 : _(SEND_ACK, "ack-send", 7) \
39 : _(RETRY_COUNT, "retry-count", 8) \
40 : _(MISSED_COUNT, "missed-count", 9)
41 :
42 : /* NAT HA protocol version */
43 : #define NAT_HA_VERSION 0x01
44 :
45 : /* NAT HA protocol flags */
46 : #define NAT_HA_FLAG_ACK 0x01
47 :
48 : /* NAT HA event types */
49 : typedef enum
50 : {
51 : NAT_HA_ADD = 1,
52 : NAT_HA_DEL,
53 : NAT_HA_REFRESH,
54 : } nat_ha_event_type_t;
55 :
56 : /* NAT HA protocol header */
57 : typedef struct
58 : {
59 : /* version */
60 : u8 version;
61 : /* flags */
62 : u8 flags;
63 : /* event count */
64 : u16 count;
65 : /* sequence number */
66 : u32 sequence_number;
67 : /* thread index where events originated */
68 : u32 thread_index;
69 : } __attribute__ ((packed)) nat_ha_message_header_t;
70 :
71 : /* NAT HA protocol event data */
72 : typedef struct
73 : {
74 : /* event type */
75 : u8 event_type;
76 : /* session data */
77 : u8 protocol;
78 : u16 flags;
79 : u32 in_addr;
80 : u32 out_addr;
81 : u16 in_port;
82 : u16 out_port;
83 : u32 eh_addr;
84 : u32 ehn_addr;
85 : u16 eh_port;
86 : u16 ehn_port;
87 : u32 fib_index;
88 : u32 total_pkts;
89 : u64 total_bytes;
90 : } __attribute__ ((packed)) nat_ha_event_t;
91 :
92 : typedef enum
93 : {
94 : #define _(N, s, v) NAT_HA_COUNTER_##N = v,
95 : foreach_nat_ha_counter
96 : #undef _
97 : NAT_HA_N_COUNTERS
98 : } nat_ha_counter_t;
99 :
100 : /* data waiting for ACK */
101 : typedef struct
102 : {
103 : /* sequence number */
104 : u32 seq;
105 : /* retry count */
106 : u32 retry_count;
107 : /* next retry time */
108 : f64 retry_timer;
109 : /* 1 if HA resync */
110 : u8 is_resync;
111 : /* packet data */
112 : u8 *data;
113 : } nat_ha_resend_entry_t;
114 :
115 : /* per thread data */
116 : typedef struct
117 : {
118 : /* buffer under construction */
119 : vlib_buffer_t *state_sync_buffer;
120 : /* frame containing NAT HA buffers */
121 : vlib_frame_t *state_sync_frame;
122 : /* number of events */
123 : u16 state_sync_count;
124 : /* next event offset */
125 : u32 state_sync_next_event_offset;
126 : /* data waiting for ACK */
127 : nat_ha_resend_entry_t *resend_queue;
128 : } nat_ha_per_thread_data_t;
129 :
130 : /* NAT HA settings */
131 : typedef struct nat_ha_main_s
132 : {
133 : u8 enabled;
134 : /* local IP address and UDP port */
135 : ip4_address_t src_ip_address;
136 : u16 src_port;
137 : /* failvoer IP address and UDP port */
138 : ip4_address_t dst_ip_address;
139 : u16 dst_port;
140 : /* path MTU between local and failover */
141 : u32 state_sync_path_mtu;
142 : /* number of seconds after which to send session counters refresh */
143 : u32 session_refresh_interval;
144 : /* counters */
145 : vlib_simple_counter_main_t counters[NAT_HA_N_COUNTERS];
146 : /* sequence number counter */
147 : u32 sequence_number;
148 : /* 1 if resync in progress */
149 : u8 in_resync;
150 : /* number of remaing ACK for resync */
151 : u32 resync_ack_count;
152 : /* number of missed ACK for resync */
153 : u32 resync_ack_missed;
154 : /* resync data */
155 : nat_ha_resync_event_cb_t event_callback;
156 : u32 client_index;
157 : u32 pid;
158 : /* per thread data */
159 : u32 num_workers;
160 : nat_ha_per_thread_data_t *per_thread_data;
161 :
162 : u32 ha_handoff_node_index;
163 : u32 ha_process_node_index;
164 : u32 ha_worker_node_index;
165 : u32 ha_node_index;
166 :
167 : /* worker handoff frame-queue index */
168 : u32 fq_index;
169 : } nat_ha_main_t;
170 :
171 : nat_ha_main_t nat_ha_main;
172 :
173 : static_always_inline void
174 2 : nat44_ei_ha_sadd (ip4_address_t *in_addr, u16 in_port, ip4_address_t *out_addr,
175 : u16 out_port, ip4_address_t *eh_addr, u16 eh_port,
176 : ip4_address_t *ehn_addr, u16 ehn_port, u8 proto,
177 : u32 fib_index, u16 flags, u32 thread_index)
178 : {
179 2 : nat44_ei_main_t *nm = &nat44_ei_main;
180 2 : nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index];
181 : nat44_ei_user_t *u;
182 : nat44_ei_session_t *s;
183 : clib_bihash_kv_8_8_t kv;
184 2 : vlib_main_t *vm = vlib_get_main ();
185 2 : f64 now = vlib_time_now (vm);
186 : nat44_ei_outside_fib_t *outside_fib;
187 2 : fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
188 2 : fib_prefix_t pfx = {
189 : .fp_proto = FIB_PROTOCOL_IP4,
190 : .fp_len = 32,
191 : .fp_addr = {
192 2 : .ip4.as_u32 = eh_addr->as_u32,
193 : },
194 : };
195 :
196 2 : if (!(flags & NAT44_EI_SESSION_FLAG_STATIC_MAPPING))
197 : {
198 2 : if (nat44_ei_set_outside_address_and_port (nm->addresses, thread_index,
199 : *out_addr, out_port, proto))
200 0 : return;
201 : }
202 :
203 2 : u = nat44_ei_user_get_or_create (nm, in_addr, fib_index, thread_index);
204 2 : if (!u)
205 0 : return;
206 :
207 2 : s = nat44_ei_session_alloc_or_recycle (nm, u, thread_index, now);
208 2 : if (!s)
209 0 : return;
210 :
211 2 : s->out2in.addr.as_u32 = out_addr->as_u32;
212 2 : s->out2in.port = out_port;
213 2 : s->nat_proto = proto;
214 2 : s->last_heard = now;
215 2 : s->flags = flags;
216 2 : s->ext_host_addr.as_u32 = eh_addr->as_u32;
217 2 : s->ext_host_port = eh_port;
218 2 : nat44_ei_user_session_increment (nm, u, nat44_ei_is_session_static (s));
219 2 : switch (vec_len (nm->outside_fibs))
220 : {
221 0 : case 0:
222 0 : s->out2in.fib_index = nm->outside_fib_index;
223 0 : break;
224 2 : case 1:
225 2 : s->out2in.fib_index = nm->outside_fibs[0].fib_index;
226 2 : break;
227 0 : default:
228 0 : vec_foreach (outside_fib, nm->outside_fibs)
229 : {
230 0 : fei = fib_table_lookup (outside_fib->fib_index, &pfx);
231 0 : if (FIB_NODE_INDEX_INVALID != fei)
232 : {
233 0 : if (fib_entry_get_resolving_interface (fei) != ~0)
234 : {
235 0 : s->out2in.fib_index = outside_fib->fib_index;
236 0 : break;
237 : }
238 : }
239 : }
240 0 : break;
241 : }
242 2 : init_nat_o2i_kv (&kv, s, thread_index, s - tnm->sessions);
243 2 : if (clib_bihash_add_del_8_8 (&nm->out2in, &kv, 1))
244 0 : nat_elog_warn (nm, "out2in key add failed");
245 :
246 2 : s->in2out.addr.as_u32 = in_addr->as_u32;
247 2 : s->in2out.port = in_port;
248 2 : s->in2out.fib_index = fib_index;
249 2 : init_nat_i2o_kv (&kv, s, thread_index, s - tnm->sessions);
250 2 : if (clib_bihash_add_del_8_8 (&nm->in2out, &kv, 1))
251 0 : nat_elog_warn (nm, "in2out key add failed");
252 : }
253 :
254 : static_always_inline void
255 1 : nat44_ei_ha_sdel (ip4_address_t *out_addr, u16 out_port,
256 : ip4_address_t *eh_addr, u16 eh_port, u8 proto, u32 fib_index,
257 : u32 thread_index)
258 : {
259 1 : nat44_ei_main_t *nm = &nat44_ei_main;
260 : clib_bihash_kv_8_8_t kv, value;
261 : nat44_ei_session_t *s;
262 : nat44_ei_main_per_thread_data_t *tnm;
263 :
264 1 : init_nat_k (&kv, *out_addr, out_port, fib_index, proto);
265 1 : if (clib_bihash_search_8_8 (&nm->out2in, &kv, &value))
266 0 : return;
267 :
268 1 : ASSERT (thread_index == nat_value_get_thread_index (&value));
269 1 : tnm = vec_elt_at_index (nm->per_thread_data, thread_index);
270 1 : s = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value));
271 1 : nat44_ei_free_session_data_v2 (nm, s, thread_index, 1);
272 1 : nat44_ei_delete_session (nm, s, thread_index);
273 : }
274 :
275 : static_always_inline void
276 1 : nat44_ei_ha_sref (ip4_address_t *out_addr, u16 out_port,
277 : ip4_address_t *eh_addr, u16 eh_port, u8 proto, u32 fib_index,
278 : u32 total_pkts, u64 total_bytes, u32 thread_index)
279 : {
280 1 : nat44_ei_main_t *nm = &nat44_ei_main;
281 : clib_bihash_kv_8_8_t kv, value;
282 : nat44_ei_session_t *s;
283 : nat44_ei_main_per_thread_data_t *tnm;
284 :
285 1 : tnm = vec_elt_at_index (nm->per_thread_data, thread_index);
286 :
287 1 : init_nat_k (&kv, *out_addr, out_port, fib_index, proto);
288 1 : if (clib_bihash_search_8_8 (&nm->out2in, &kv, &value))
289 0 : return;
290 :
291 1 : s = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value));
292 1 : s->total_pkts = total_pkts;
293 1 : s->total_bytes = total_bytes;
294 : }
295 :
296 : static void
297 0 : nat_ha_resync_fin (void)
298 : {
299 0 : nat44_ei_main_t *nm = &nat44_ei_main;
300 0 : nat_ha_main_t *ha = &nat_ha_main;
301 :
302 : /* if no more resync ACK remainig we are done */
303 0 : if (ha->resync_ack_count)
304 0 : return;
305 :
306 0 : ha->in_resync = 0;
307 0 : if (ha->resync_ack_missed)
308 : {
309 0 : nat_elog_info (nm, "resync completed with result FAILED");
310 : }
311 : else
312 : {
313 0 : nat_elog_info (nm, "resync completed with result SUCCESS");
314 : }
315 0 : if (ha->event_callback)
316 0 : ha->event_callback (ha->client_index, ha->pid, ha->resync_ack_missed);
317 : }
318 :
319 : /* cache HA NAT data waiting for ACK */
320 : static int
321 3 : nat_ha_resend_queue_add (vlib_main_t *vm, u32 seq, u8 *data, u8 data_len,
322 : u8 is_resync, u32 vlib_thread_index)
323 : {
324 3 : nat_ha_main_t *ha = &nat_ha_main;
325 3 : nat_ha_per_thread_data_t *td = &ha->per_thread_data[vlib_thread_index];
326 : nat_ha_resend_entry_t *entry;
327 3 : f64 now = vlib_time_now (vm);
328 :
329 3 : vec_add2 (td->resend_queue, entry, 1);
330 3 : clib_memset (entry, 0, sizeof (*entry));
331 3 : entry->retry_timer = now + 2.0;
332 3 : entry->seq = seq;
333 3 : entry->is_resync = is_resync;
334 3 : vec_add (entry->data, data, data_len);
335 :
336 3 : return 0;
337 : }
338 :
339 : static_always_inline void
340 2 : nat_ha_ack_recv (u32 seq, u32 thread_index)
341 : {
342 2 : nat44_ei_main_t *nm = &nat44_ei_main;
343 2 : nat_ha_main_t *ha = &nat_ha_main;
344 2 : nat_ha_per_thread_data_t *td = &ha->per_thread_data[thread_index];
345 : u32 i;
346 :
347 2 : vec_foreach_index (i, td->resend_queue)
348 : {
349 2 : if (td->resend_queue[i].seq != seq)
350 0 : continue;
351 :
352 2 : vlib_increment_simple_counter (&ha->counters[NAT_HA_COUNTER_RECV_ACK],
353 : thread_index, 0, 1);
354 : /* ACK received remove cached data */
355 2 : if (td->resend_queue[i].is_resync)
356 : {
357 0 : clib_atomic_fetch_sub (&ha->resync_ack_count, 1);
358 0 : nat_ha_resync_fin ();
359 : }
360 2 : vec_free (td->resend_queue[i].data);
361 2 : vec_del1 (td->resend_queue, i);
362 2 : nat_elog_debug_X1 (nm, "ACK for seq %d received", "i4",
363 : clib_net_to_host_u32 (seq));
364 :
365 2 : return;
366 : }
367 : }
368 :
369 : /* scan non-ACKed HA NAT for retry */
370 : static void
371 64 : nat_ha_resend_scan (vlib_main_t *vm, u32 thread_index)
372 : {
373 64 : nat44_ei_main_t *nm = &nat44_ei_main;
374 64 : nat_ha_main_t *ha = &nat_ha_main;
375 64 : nat_ha_per_thread_data_t *td = &ha->per_thread_data[thread_index];
376 64 : u32 i, *del, *to_delete = 0;
377 64 : vlib_buffer_t *b = 0;
378 : vlib_frame_t *f;
379 : u32 bi, *to_next;
380 : ip4_header_t *ip;
381 64 : f64 now = vlib_time_now (vm);
382 :
383 76 : vec_foreach_index (i, td->resend_queue)
384 : {
385 12 : if (td->resend_queue[i].retry_timer > now)
386 8 : continue;
387 :
388 : /* maximum retry reached delete cached data */
389 4 : if (td->resend_queue[i].retry_count >= NAT_HA_RETRIES)
390 : {
391 1 : nat_elog_notice_X1 (nm, "seq %d missed", "i4",
392 : clib_net_to_host_u32 (td->resend_queue[i].seq));
393 1 : if (td->resend_queue[i].is_resync)
394 : {
395 0 : clib_atomic_fetch_add (&ha->resync_ack_missed, 1);
396 0 : clib_atomic_fetch_sub (&ha->resync_ack_count, 1);
397 0 : nat_ha_resync_fin ();
398 : }
399 1 : vec_add1 (to_delete, i);
400 1 : vlib_increment_simple_counter (&ha->counters
401 : [NAT_HA_COUNTER_MISSED_COUNT],
402 : thread_index, 0, 1);
403 1 : continue;
404 : }
405 :
406 : /* retry to send non-ACKed data */
407 3 : nat_elog_debug_X1 (nm, "state sync seq %d resend", "i4",
408 : clib_net_to_host_u32 (td->resend_queue[i].seq));
409 3 : td->resend_queue[i].retry_count++;
410 3 : vlib_increment_simple_counter (&ha->counters[NAT_HA_COUNTER_RETRY_COUNT],
411 : thread_index, 0, 1);
412 3 : if (vlib_buffer_alloc (vm, &bi, 1) != 1)
413 : {
414 0 : nat_elog_warn (nm, "HA NAT state sync can't allocate buffer");
415 0 : return;
416 : }
417 3 : b = vlib_get_buffer (vm, bi);
418 3 : b->current_length = vec_len (td->resend_queue[i].data);
419 3 : b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
420 3 : b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
421 3 : vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
422 3 : vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
423 3 : ip = vlib_buffer_get_current (b);
424 3 : clib_memcpy (ip, td->resend_queue[i].data,
425 : vec_len (td->resend_queue[i].data));
426 3 : f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
427 3 : to_next = vlib_frame_vector_args (f);
428 3 : to_next[0] = bi;
429 3 : f->n_vectors = 1;
430 3 : vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
431 3 : td->resend_queue[i].retry_timer = now + 2.0;
432 : }
433 :
434 65 : vec_foreach (del, to_delete)
435 : {
436 1 : vec_free (td->resend_queue[*del].data);
437 1 : vec_del1 (td->resend_queue, *del);
438 : }
439 64 : vec_free (to_delete);
440 : }
441 :
442 : void
443 58 : nat_ha_enable ()
444 : {
445 58 : nat_ha_main_t *ha = &nat_ha_main;
446 58 : ha->enabled = 1;
447 58 : }
448 :
449 : void
450 58 : nat_ha_disable ()
451 : {
452 58 : nat_ha_main_t *ha = &nat_ha_main;
453 58 : ha->dst_port = 0;
454 58 : ha->enabled = 0;
455 58 : }
456 :
457 : void
458 559 : nat_ha_set_node_indexes (nat_ha_main_t *ha, vlib_main_t *vm)
459 : {
460 : vlib_node_t *node;
461 :
462 559 : node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha-handoff");
463 559 : ha->ha_handoff_node_index = node->index;
464 559 : node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha-process");
465 559 : ha->ha_process_node_index = node->index;
466 559 : node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha-worker");
467 559 : ha->ha_worker_node_index = node->index;
468 559 : node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha");
469 559 : ha->ha_node_index = node->index;
470 559 : }
471 :
472 : void
473 559 : nat_ha_init (vlib_main_t * vm, u32 num_workers, u32 num_threads)
474 : {
475 559 : nat_ha_main_t *ha = &nat_ha_main;
476 559 : clib_memset (ha, 0, sizeof (*ha));
477 :
478 559 : nat_ha_set_node_indexes (ha, vm);
479 :
480 559 : ha->fq_index = ~0;
481 :
482 559 : ha->num_workers = num_workers;
483 559 : vec_validate (ha->per_thread_data, num_threads);
484 :
485 : #define _(N, s, v) \
486 : ha->counters[v].name = s; \
487 : ha->counters[v].stat_segment_name = "/nat44-ei/ha/" s; \
488 : vlib_validate_simple_counter (&ha->counters[v], 0); \
489 : vlib_zero_simple_counter (&ha->counters[v], 0);
490 559 : foreach_nat_ha_counter
491 : #undef _
492 559 : }
493 :
494 : int
495 2 : nat_ha_set_listener (vlib_main_t *vm, ip4_address_t *addr, u16 port,
496 : u32 path_mtu)
497 : {
498 2 : nat44_ei_main_t *nm = &nat44_ei_main;
499 2 : nat_ha_main_t *ha = &nat_ha_main;
500 :
501 : /* unregister previously set UDP port */
502 2 : if (ha->src_port)
503 1 : udp_unregister_dst_port (vm, ha->src_port, 1);
504 :
505 2 : ha->src_ip_address.as_u32 = addr->as_u32;
506 2 : ha->src_port = port;
507 2 : ha->state_sync_path_mtu = path_mtu;
508 :
509 2 : if (port)
510 : {
511 : /* if multiple worker threads first go to handoff node */
512 2 : if (ha->num_workers > 1)
513 : {
514 0 : if (ha->fq_index == ~0)
515 0 : ha->fq_index = vlib_frame_queue_main_init (ha->ha_node_index, 0);
516 0 : udp_register_dst_port (vm, port, ha->ha_handoff_node_index, 1);
517 : }
518 : else
519 : {
520 2 : udp_register_dst_port (vm, port, ha->ha_node_index, 1);
521 : }
522 2 : nat_elog_info_X1 (nm, "HA listening on port %d for state sync", "i4",
523 : port);
524 : }
525 :
526 2 : return 0;
527 : }
528 :
529 : void
530 55 : nat_ha_get_listener (ip4_address_t * addr, u16 * port, u32 * path_mtu)
531 : {
532 55 : nat_ha_main_t *ha = &nat_ha_main;
533 :
534 55 : addr->as_u32 = ha->src_ip_address.as_u32;
535 55 : *port = ha->src_port;
536 55 : *path_mtu = ha->state_sync_path_mtu;
537 55 : }
538 :
539 : int
540 1 : nat_ha_set_failover (vlib_main_t *vm, ip4_address_t *addr, u16 port,
541 : u32 session_refresh_interval)
542 : {
543 1 : nat_ha_main_t *ha = &nat_ha_main;
544 :
545 1 : ha->dst_ip_address.as_u32 = addr->as_u32;
546 1 : ha->dst_port = port;
547 1 : ha->session_refresh_interval = session_refresh_interval;
548 :
549 1 : vlib_process_signal_event (vm, ha->ha_process_node_index, 1, 0);
550 :
551 1 : return 0;
552 : }
553 :
554 : void
555 40 : nat_ha_get_failover (ip4_address_t * addr, u16 * port,
556 : u32 * session_refresh_interval)
557 : {
558 40 : nat_ha_main_t *ha = &nat_ha_main;
559 :
560 40 : addr->as_u32 = ha->dst_ip_address.as_u32;
561 40 : *port = ha->dst_port;
562 40 : *session_refresh_interval = ha->session_refresh_interval;
563 40 : }
564 :
565 : static_always_inline void
566 2 : nat_ha_recv_add (nat_ha_event_t * event, f64 now, u32 thread_index)
567 : {
568 2 : nat_ha_main_t *ha = &nat_ha_main;
569 : ip4_address_t in_addr, out_addr, eh_addr, ehn_addr;
570 : u32 fib_index;
571 : u16 flags;
572 :
573 2 : vlib_increment_simple_counter (&ha->counters[NAT_HA_COUNTER_RECV_ADD],
574 : thread_index, 0, 1);
575 :
576 2 : in_addr.as_u32 = event->in_addr;
577 2 : out_addr.as_u32 = event->out_addr;
578 2 : eh_addr.as_u32 = event->eh_addr;
579 2 : ehn_addr.as_u32 = event->ehn_addr;
580 2 : fib_index = clib_net_to_host_u32 (event->fib_index);
581 2 : flags = clib_net_to_host_u16 (event->flags);
582 :
583 2 : nat44_ei_ha_sadd (&in_addr, event->in_port, &out_addr, event->out_port,
584 2 : &eh_addr, event->eh_port, &ehn_addr, event->ehn_port,
585 2 : event->protocol, fib_index, flags, thread_index);
586 2 : }
587 :
588 : static_always_inline void
589 1 : nat_ha_recv_del (nat_ha_event_t * event, u32 thread_index)
590 : {
591 1 : nat_ha_main_t *ha = &nat_ha_main;
592 : ip4_address_t out_addr, eh_addr;
593 : u32 fib_index;
594 :
595 1 : vlib_increment_simple_counter (&ha->counters[NAT_HA_COUNTER_RECV_DEL],
596 : thread_index, 0, 1);
597 :
598 1 : out_addr.as_u32 = event->out_addr;
599 1 : eh_addr.as_u32 = event->eh_addr;
600 1 : fib_index = clib_net_to_host_u32 (event->fib_index);
601 :
602 1 : nat44_ei_ha_sdel (&out_addr, event->out_port, &eh_addr, event->eh_port,
603 1 : event->protocol, fib_index, thread_index);
604 1 : }
605 :
606 : static_always_inline void
607 1 : nat_ha_recv_refresh (nat_ha_event_t * event, f64 now, u32 thread_index)
608 : {
609 1 : nat_ha_main_t *ha = &nat_ha_main;
610 : ip4_address_t out_addr, eh_addr;
611 : u32 fib_index, total_pkts;
612 : u64 total_bytes;
613 :
614 1 : vlib_increment_simple_counter (&ha->counters[NAT_HA_COUNTER_RECV_REFRESH],
615 : thread_index, 0, 1);
616 :
617 1 : out_addr.as_u32 = event->out_addr;
618 1 : eh_addr.as_u32 = event->eh_addr;
619 1 : fib_index = clib_net_to_host_u32 (event->fib_index);
620 1 : total_pkts = clib_net_to_host_u32 (event->total_pkts);
621 1 : total_bytes = clib_net_to_host_u64 (event->total_bytes);
622 :
623 1 : nat44_ei_ha_sref (&out_addr, event->out_port, &eh_addr, event->eh_port,
624 1 : event->protocol, fib_index, total_pkts, total_bytes,
625 : thread_index);
626 1 : }
627 :
628 : /* process received NAT HA event */
629 : static_always_inline void
630 4 : nat_ha_event_process (nat_ha_event_t * event, f64 now, u32 thread_index)
631 : {
632 4 : nat44_ei_main_t *nm = &nat44_ei_main;
633 4 : switch (event->event_type)
634 : {
635 2 : case NAT_HA_ADD:
636 2 : nat_ha_recv_add (event, now, thread_index);
637 2 : break;
638 1 : case NAT_HA_DEL:
639 1 : nat_ha_recv_del (event, thread_index);
640 1 : break;
641 1 : case NAT_HA_REFRESH:
642 1 : nat_ha_recv_refresh (event, now, thread_index);
643 1 : break;
644 0 : default:
645 0 : nat_elog_notice_X1 (nm, "Unsupported HA event type %d", "i4",
646 : event->event_type);
647 0 : break;
648 : }
649 4 : }
650 :
651 : static inline void
652 3 : nat_ha_header_create (vlib_buffer_t * b, u32 * offset, u32 thread_index)
653 : {
654 3 : nat_ha_main_t *ha = &nat_ha_main;
655 : nat_ha_message_header_t *h;
656 : ip4_header_t *ip;
657 : udp_header_t *udp;
658 : u32 sequence_number;
659 :
660 3 : b->current_data = 0;
661 3 : b->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h);
662 3 : b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
663 3 : b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
664 3 : vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
665 3 : vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
666 3 : ip = vlib_buffer_get_current (b);
667 3 : udp = (udp_header_t *) (ip + 1);
668 3 : h = (nat_ha_message_header_t *) (udp + 1);
669 :
670 : /* IP header */
671 3 : ip->ip_version_and_header_length = 0x45;
672 3 : ip->ttl = 254;
673 3 : ip->protocol = IP_PROTOCOL_UDP;
674 3 : ip->flags_and_fragment_offset =
675 3 : clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
676 3 : ip->src_address.as_u32 = ha->src_ip_address.as_u32;
677 3 : ip->dst_address.as_u32 = ha->dst_ip_address.as_u32;
678 : /* UDP header */
679 3 : udp->src_port = clib_host_to_net_u16 (ha->src_port);
680 3 : udp->dst_port = clib_host_to_net_u16 (ha->dst_port);
681 3 : udp->checksum = 0;
682 :
683 : /* NAT HA protocol header */
684 3 : h->version = NAT_HA_VERSION;
685 3 : h->flags = 0;
686 3 : h->count = 0;
687 3 : h->thread_index = clib_host_to_net_u32 (thread_index);
688 3 : sequence_number = clib_atomic_fetch_add (&ha->sequence_number, 1);
689 3 : h->sequence_number = clib_host_to_net_u32 (sequence_number);
690 :
691 3 : *offset =
692 : sizeof (ip4_header_t) + sizeof (udp_header_t) +
693 : sizeof (nat_ha_message_header_t);
694 3 : }
695 :
696 : static inline void
697 3 : nat_ha_send (vlib_frame_t *f, vlib_buffer_t *b, u8 is_resync,
698 : u32 vlib_thread_index)
699 : {
700 3 : nat_ha_main_t *ha = &nat_ha_main;
701 3 : nat_ha_per_thread_data_t *td = &ha->per_thread_data[vlib_thread_index];
702 : nat_ha_message_header_t *h;
703 : ip4_header_t *ip;
704 : udp_header_t *udp;
705 3 : vlib_main_t *vm = vlib_get_main_by_index (vlib_thread_index);
706 :
707 3 : ip = vlib_buffer_get_current (b);
708 3 : udp = ip4_next_header (ip);
709 3 : h = (nat_ha_message_header_t *) (udp + 1);
710 :
711 3 : h->count = clib_host_to_net_u16 (td->state_sync_count);
712 :
713 3 : ip->length = clib_host_to_net_u16 (b->current_length);
714 3 : ip->checksum = ip4_header_checksum (ip);
715 3 : udp->length = clib_host_to_net_u16 (b->current_length - sizeof (*ip));
716 :
717 3 : nat_ha_resend_queue_add (vm, h->sequence_number, (u8 *) ip,
718 3 : b->current_length, is_resync, vlib_thread_index);
719 :
720 3 : vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
721 3 : }
722 :
723 : /* add NAT HA protocol event */
724 : static_always_inline void
725 73 : nat_ha_event_add (nat_ha_event_t *event, u8 do_flush, u32 session_thread_index,
726 : u8 is_resync)
727 : {
728 73 : nat44_ei_main_t *nm = &nat44_ei_main;
729 73 : nat_ha_main_t *ha = &nat_ha_main;
730 73 : u32 vlib_thread_index = vlib_get_thread_index ();
731 73 : nat_ha_per_thread_data_t *td = &ha->per_thread_data[vlib_thread_index];
732 73 : vlib_main_t *vm = vlib_get_main_by_index (vlib_thread_index);
733 73 : vlib_buffer_t *b = 0;
734 : vlib_frame_t *f;
735 73 : u32 bi = ~0, offset;
736 :
737 73 : b = td->state_sync_buffer;
738 :
739 73 : if (PREDICT_FALSE (b == 0))
740 : {
741 67 : if (do_flush)
742 64 : return;
743 :
744 3 : if (vlib_buffer_alloc (vm, &bi, 1) != 1)
745 : {
746 0 : nat_elog_warn (nm, "HA NAT state sync can't allocate buffer");
747 0 : return;
748 : }
749 :
750 3 : b = td->state_sync_buffer = vlib_get_buffer (vm, bi);
751 3 : clib_memset (vnet_buffer (b), 0, sizeof (*vnet_buffer (b)));
752 3 : offset = 0;
753 : }
754 : else
755 : {
756 6 : bi = vlib_get_buffer_index (vm, b);
757 6 : offset = td->state_sync_next_event_offset;
758 : }
759 :
760 9 : f = td->state_sync_frame;
761 9 : if (PREDICT_FALSE (f == 0))
762 : {
763 : u32 *to_next;
764 3 : f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
765 3 : td->state_sync_frame = f;
766 3 : to_next = vlib_frame_vector_args (f);
767 3 : to_next[0] = bi;
768 3 : f->n_vectors = 1;
769 : }
770 :
771 9 : if (PREDICT_FALSE (td->state_sync_count == 0))
772 3 : nat_ha_header_create (b, &offset, session_thread_index);
773 :
774 9 : if (PREDICT_TRUE (do_flush == 0))
775 : {
776 6 : clib_memcpy_fast (b->data + offset, event, sizeof (*event));
777 6 : offset += sizeof (*event);
778 6 : td->state_sync_count++;
779 6 : b->current_length += sizeof (*event);
780 :
781 6 : switch (event->event_type)
782 : {
783 3 : case NAT_HA_ADD:
784 3 : vlib_increment_simple_counter (
785 : &ha->counters[NAT_HA_COUNTER_SEND_ADD], vlib_thread_index, 0, 1);
786 3 : break;
787 1 : case NAT_HA_DEL:
788 1 : vlib_increment_simple_counter (
789 : &ha->counters[NAT_HA_COUNTER_SEND_DEL], vlib_thread_index, 0, 1);
790 1 : break;
791 2 : case NAT_HA_REFRESH:
792 2 : vlib_increment_simple_counter (
793 : &ha->counters[NAT_HA_COUNTER_SEND_REFRESH], vlib_thread_index, 0,
794 : 1);
795 2 : break;
796 0 : default:
797 0 : break;
798 : }
799 3 : }
800 :
801 9 : if (PREDICT_FALSE
802 : (do_flush || offset + (sizeof (*event)) > ha->state_sync_path_mtu))
803 : {
804 3 : nat_ha_send (f, b, is_resync, vlib_thread_index);
805 3 : td->state_sync_buffer = 0;
806 3 : td->state_sync_frame = 0;
807 3 : td->state_sync_count = 0;
808 3 : offset = 0;
809 3 : if (is_resync)
810 : {
811 0 : clib_atomic_fetch_add (&ha->resync_ack_count, 1);
812 0 : nat_ha_resync_fin ();
813 : }
814 : }
815 :
816 9 : td->state_sync_next_event_offset = offset;
817 : }
818 :
819 : #define skip_if_disabled() \
820 : do { \
821 : nat_ha_main_t *ha = &nat_ha_main; \
822 : if (PREDICT_TRUE (!ha->dst_port)) \
823 : return; \
824 : } while (0)
825 :
826 : void
827 3 : nat_ha_flush (u8 is_resync)
828 : {
829 3 : skip_if_disabled ();
830 3 : nat_ha_event_add (0, 1, 0, is_resync);
831 : }
832 :
833 : void
834 10485 : nat_ha_sadd (ip4_address_t * in_addr, u16 in_port, ip4_address_t * out_addr,
835 : u16 out_port, ip4_address_t * eh_addr, u16 eh_port,
836 : ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, u32 fib_index,
837 : u16 flags, u32 thread_index, u8 is_resync)
838 : {
839 : nat_ha_event_t event;
840 :
841 10485 : skip_if_disabled ();
842 :
843 3 : clib_memset (&event, 0, sizeof (event));
844 3 : event.event_type = NAT_HA_ADD;
845 3 : event.flags = clib_host_to_net_u16 (flags);
846 3 : event.in_addr = in_addr->as_u32;
847 3 : event.in_port = in_port;
848 3 : event.out_addr = out_addr->as_u32;
849 3 : event.out_port = out_port;
850 3 : event.eh_addr = eh_addr->as_u32;
851 3 : event.eh_port = eh_port;
852 3 : event.ehn_addr = ehn_addr->as_u32;
853 3 : event.ehn_port = ehn_port;
854 3 : event.fib_index = clib_host_to_net_u32 (fib_index);
855 3 : event.protocol = proto;
856 3 : nat_ha_event_add (&event, 0, thread_index, is_resync);
857 : }
858 :
859 : void
860 10483 : nat_ha_sdel (ip4_address_t *out_addr, u16 out_port, ip4_address_t *eh_addr,
861 : u16 eh_port, u8 proto, u32 fib_index, u32 session_thread_index)
862 : {
863 : nat_ha_event_t event;
864 :
865 10483 : skip_if_disabled ();
866 :
867 1 : clib_memset (&event, 0, sizeof (event));
868 1 : event.event_type = NAT_HA_DEL;
869 1 : event.out_addr = out_addr->as_u32;
870 1 : event.out_port = out_port;
871 1 : event.eh_addr = eh_addr->as_u32;
872 1 : event.eh_port = eh_port;
873 1 : event.fib_index = clib_host_to_net_u32 (fib_index);
874 1 : event.protocol = proto;
875 1 : nat_ha_event_add (&event, 0, session_thread_index, 0);
876 : }
877 :
878 : void
879 10674 : nat_ha_sref (ip4_address_t * out_addr, u16 out_port, ip4_address_t * eh_addr,
880 : u16 eh_port, u8 proto, u32 fib_index, u32 total_pkts,
881 : u64 total_bytes, u32 thread_index, f64 * last_refreshed, f64 now)
882 : {
883 10674 : nat_ha_main_t *ha = &nat_ha_main;
884 : nat_ha_event_t event;
885 :
886 10678 : skip_if_disabled ();
887 :
888 6 : if ((*last_refreshed + ha->session_refresh_interval) > now)
889 4 : return;
890 :
891 2 : *last_refreshed = now;
892 2 : clib_memset (&event, 0, sizeof (event));
893 2 : event.event_type = NAT_HA_REFRESH;
894 2 : event.out_addr = out_addr->as_u32;
895 2 : event.out_port = out_port;
896 2 : event.eh_addr = eh_addr->as_u32;
897 2 : event.eh_port = eh_port;
898 2 : event.fib_index = clib_host_to_net_u32 (fib_index);
899 2 : event.protocol = proto;
900 2 : event.total_pkts = clib_host_to_net_u32 (total_pkts);
901 2 : event.total_bytes = clib_host_to_net_u64 (total_bytes);
902 2 : nat_ha_event_add (&event, 0, thread_index, 0);
903 : }
904 :
905 : static_always_inline u8
906 66 : plugin_enabled ()
907 : {
908 66 : nat_ha_main_t *ha = &nat_ha_main;
909 66 : return ha->enabled;
910 : }
911 :
912 : /* per thread process waiting for interrupt */
913 : static uword
914 66 : nat_ha_worker_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
915 : vlib_frame_t * f)
916 : {
917 66 : u32 thread_index = vm->thread_index;
918 :
919 66 : if (plugin_enabled () == 0)
920 2 : return 0;
921 :
922 : /* flush HA NAT data under construction */
923 64 : nat_ha_event_add (0, 1, thread_index, 0);
924 : /* scan if we need to resend some non-ACKed data */
925 64 : nat_ha_resend_scan (vm, thread_index);
926 64 : return 0;
927 : }
928 :
929 : /* *INDENT-OFF* */
930 62744 : VLIB_REGISTER_NODE (nat_ha_worker_node) = {
931 : .function = nat_ha_worker_fn,
932 : .type = VLIB_NODE_TYPE_INPUT,
933 : .state = VLIB_NODE_STATE_INTERRUPT,
934 : .name = "nat44-ei-ha-worker",
935 : };
936 : /* *INDENT-ON* */
937 :
938 : /* periodically send interrupt to each thread */
939 : static uword
940 559 : nat_ha_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
941 : {
942 559 : nat44_ei_main_t *nm = &nat44_ei_main;
943 559 : nat_ha_main_t *ha = &nat_ha_main;
944 : uword event_type;
945 559 : uword *event_data = 0;
946 : u32 ti;
947 :
948 559 : vlib_process_wait_for_event (vm);
949 1 : event_type = vlib_process_get_events (vm, &event_data);
950 1 : if (event_type)
951 1 : nat_elog_info (nm, "nat44-ei-ha-process: bogus kickoff event received");
952 1 : vec_reset_length (event_data);
953 :
954 : while (1)
955 : {
956 67 : vlib_process_wait_for_event_or_clock (vm, 1.0);
957 66 : event_type = vlib_process_get_events (vm, &event_data);
958 66 : vec_reset_length (event_data);
959 132 : for (ti = 0; ti < vlib_get_n_threads (); ti++)
960 : {
961 66 : if (ti >= vec_len (ha->per_thread_data))
962 0 : continue;
963 :
964 66 : vlib_node_set_interrupt_pending (vlib_get_main_by_index (ti),
965 : nat_ha_worker_node.index);
966 : }
967 : }
968 :
969 : return 0;
970 : }
971 :
972 : /* *INDENT-OFF* */
973 62744 : VLIB_REGISTER_NODE (nat_ha_process_node) = {
974 : .function = nat_ha_process,
975 : .type = VLIB_NODE_TYPE_PROCESS,
976 : .name = "nat44-ei-ha-process",
977 : };
978 : /* *INDENT-ON* */
979 :
980 : void
981 40 : nat_ha_get_resync_status (u8 * in_resync, u32 * resync_ack_missed)
982 : {
983 40 : nat_ha_main_t *ha = &nat_ha_main;
984 :
985 40 : *in_resync = ha->in_resync;
986 40 : *resync_ack_missed = ha->resync_ack_missed;
987 40 : }
988 :
989 : typedef struct
990 : {
991 : ip4_address_t addr;
992 : u32 event_count;
993 : } nat_ha_trace_t;
994 :
995 : static u8 *
996 1 : format_nat_ha_trace (u8 * s, va_list * args)
997 : {
998 1 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
999 1 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1000 1 : nat_ha_trace_t *t = va_arg (*args, nat_ha_trace_t *);
1001 :
1002 1 : s = format (s, "nat44-ei-ha: %u events from %U", t->event_count,
1003 : format_ip4_address, &t->addr);
1004 :
1005 1 : return s;
1006 : }
1007 :
1008 : typedef enum
1009 : {
1010 : NAT_HA_NEXT_IP4_LOOKUP,
1011 : NAT_HA_NEXT_DROP,
1012 : NAT_HA_N_NEXT,
1013 : } nat_ha_next_t;
1014 :
1015 : #define foreach_nat_ha_error \
1016 : _(PROCESSED, "pkts-processed") \
1017 : _(BAD_VERSION, "bad-version")
1018 :
1019 : typedef enum
1020 : {
1021 : #define _(sym, str) NAT_HA_ERROR_##sym,
1022 : foreach_nat_ha_error
1023 : #undef _
1024 : NAT_HA_N_ERROR,
1025 : } nat_ha_error_t;
1026 :
1027 : static char *nat_ha_error_strings[] = {
1028 : #define _(sym, str) str,
1029 : foreach_nat_ha_error
1030 : #undef _
1031 : };
1032 :
1033 : /* process received HA NAT protocol messages */
1034 : static uword
1035 5 : nat_ha_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1036 : vlib_frame_t * frame)
1037 : {
1038 : u32 n_left_from, *from, next_index, *to_next;
1039 5 : f64 now = vlib_time_now (vm);
1040 5 : u32 thread_index = vm->thread_index;
1041 5 : u32 pkts_processed = 0;
1042 5 : ip4_main_t *i4m = &ip4_main;
1043 5 : u8 host_config_ttl = i4m->host_config.ttl;
1044 5 : nat_ha_main_t *ha = &nat_ha_main;
1045 :
1046 5 : from = vlib_frame_vector_args (frame);
1047 5 : n_left_from = frame->n_vectors;
1048 5 : next_index = node->cached_next_index;
1049 :
1050 10 : while (n_left_from > 0)
1051 : {
1052 : u32 n_left_to_next;
1053 :
1054 5 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1055 :
1056 10 : while (n_left_from > 0 && n_left_to_next > 0)
1057 : {
1058 : u32 bi0, next0, src_addr0, dst_addr0;;
1059 : vlib_buffer_t *b0;
1060 : nat_ha_message_header_t *h0;
1061 : nat_ha_event_t *e0;
1062 : u16 event_count0, src_port0, dst_port0, old_len0;
1063 : ip4_header_t *ip0;
1064 : udp_header_t *udp0;
1065 : ip_csum_t sum0;
1066 :
1067 5 : bi0 = from[0];
1068 5 : to_next[0] = bi0;
1069 5 : from += 1;
1070 5 : to_next += 1;
1071 5 : n_left_from -= 1;
1072 5 : n_left_to_next -= 1;
1073 :
1074 5 : b0 = vlib_get_buffer (vm, bi0);
1075 5 : h0 = vlib_buffer_get_current (b0);
1076 5 : vlib_buffer_advance (b0, -sizeof (*udp0));
1077 5 : udp0 = vlib_buffer_get_current (b0);
1078 5 : vlib_buffer_advance (b0, -sizeof (*ip0));
1079 5 : ip0 = vlib_buffer_get_current (b0);
1080 :
1081 5 : next0 = NAT_HA_NEXT_DROP;
1082 :
1083 5 : if (h0->version != NAT_HA_VERSION)
1084 : {
1085 0 : b0->error = node->errors[NAT_HA_ERROR_BAD_VERSION];
1086 0 : goto done0;
1087 : }
1088 :
1089 5 : event_count0 = clib_net_to_host_u16 (h0->count);
1090 : /* ACK for previously send data */
1091 5 : if (!event_count0 && (h0->flags & NAT_HA_FLAG_ACK))
1092 : {
1093 2 : nat_ha_ack_recv (h0->sequence_number, thread_index);
1094 2 : b0->error = node->errors[NAT_HA_ERROR_PROCESSED];
1095 2 : goto done0;
1096 : }
1097 :
1098 3 : e0 = (nat_ha_event_t *) (h0 + 1);
1099 :
1100 : /* process each event */
1101 7 : while (event_count0)
1102 : {
1103 4 : nat_ha_event_process (e0, now, thread_index);
1104 4 : event_count0--;
1105 4 : e0 = (nat_ha_event_t *) ((u8 *) e0 + sizeof (nat_ha_event_t));
1106 : }
1107 :
1108 3 : next0 = NAT_HA_NEXT_IP4_LOOKUP;
1109 3 : pkts_processed++;
1110 :
1111 : /* reply with ACK */
1112 3 : b0->current_length = sizeof (*ip0) + sizeof (*udp0) + sizeof (*h0);
1113 :
1114 3 : src_addr0 = ip0->src_address.data_u32;
1115 3 : dst_addr0 = ip0->dst_address.data_u32;
1116 3 : ip0->src_address.data_u32 = dst_addr0;
1117 3 : ip0->dst_address.data_u32 = src_addr0;
1118 3 : old_len0 = ip0->length;
1119 3 : ip0->length = clib_host_to_net_u16 (b0->current_length);
1120 :
1121 3 : sum0 = ip0->checksum;
1122 3 : sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
1123 : ip4_header_t, ttl);
1124 3 : ip0->ttl = host_config_ttl;
1125 : sum0 =
1126 3 : ip_csum_update (sum0, old_len0, ip0->length, ip4_header_t,
1127 : length);
1128 3 : ip0->checksum = ip_csum_fold (sum0);
1129 :
1130 3 : udp0->checksum = 0;
1131 3 : src_port0 = udp0->src_port;
1132 3 : dst_port0 = udp0->dst_port;
1133 3 : udp0->src_port = dst_port0;
1134 3 : udp0->dst_port = src_port0;
1135 3 : udp0->length =
1136 3 : clib_host_to_net_u16 (b0->current_length - sizeof (*ip0));
1137 :
1138 3 : h0->flags = NAT_HA_FLAG_ACK;
1139 3 : h0->count = 0;
1140 3 : vlib_increment_simple_counter (&ha->counters
1141 : [NAT_HA_COUNTER_SEND_ACK],
1142 : thread_index, 0, 1);
1143 :
1144 5 : done0:
1145 5 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1146 : && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1147 : {
1148 5 : nat_ha_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
1149 5 : ip4_header_t *ip =
1150 5 : (void *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
1151 5 : t->event_count = clib_net_to_host_u16 (h0->count);
1152 5 : t->addr.as_u32 = ip->src_address.data_u32;
1153 : }
1154 :
1155 5 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1156 : to_next, n_left_to_next,
1157 : bi0, next0);
1158 : }
1159 :
1160 5 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1161 : }
1162 :
1163 5 : vlib_node_increment_counter (vm, ha->ha_node_index, NAT_HA_ERROR_PROCESSED,
1164 : pkts_processed);
1165 :
1166 5 : return frame->n_vectors;
1167 : }
1168 :
1169 : /* *INDENT-OFF* */
1170 62744 : VLIB_REGISTER_NODE (nat_ha_node) = {
1171 : .function = nat_ha_node_fn,
1172 : .name = "nat44-ei-ha",
1173 : .vector_size = sizeof (u32),
1174 : .format_trace = format_nat_ha_trace,
1175 : .type = VLIB_NODE_TYPE_INTERNAL,
1176 : .n_errors = ARRAY_LEN (nat_ha_error_strings),
1177 : .error_strings = nat_ha_error_strings,
1178 : .n_next_nodes = NAT_HA_N_NEXT,
1179 : .next_nodes = {
1180 : [NAT_HA_NEXT_IP4_LOOKUP] = "ip4-lookup",
1181 : [NAT_HA_NEXT_DROP] = "error-drop",
1182 : },
1183 : };
1184 : /* *INDENT-ON* */
1185 :
1186 : typedef struct
1187 : {
1188 : u32 next_worker_index;
1189 : u8 in2out;
1190 : } nat_ha_handoff_trace_t;
1191 :
1192 : #define foreach_nat_ha_handoff_error \
1193 : _(CONGESTION_DROP, "congestion drop") \
1194 : _(SAME_WORKER, "same worker") \
1195 : _(DO_HANDOFF, "do handoff")
1196 :
1197 : typedef enum
1198 : {
1199 : #define _(sym,str) NAT_HA_HANDOFF_ERROR_##sym,
1200 : foreach_nat_ha_handoff_error
1201 : #undef _
1202 : NAT_HA_HANDOFF_N_ERROR,
1203 : } nat_ha_handoff_error_t;
1204 :
1205 : static char *nat_ha_handoff_error_strings[] = {
1206 : #define _(sym,string) string,
1207 : foreach_nat_ha_handoff_error
1208 : #undef _
1209 : };
1210 :
1211 : static u8 *
1212 0 : format_nat_ha_handoff_trace (u8 * s, va_list * args)
1213 : {
1214 0 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1215 0 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1216 0 : nat_ha_handoff_trace_t *t = va_arg (*args, nat_ha_handoff_trace_t *);
1217 :
1218 : s =
1219 0 : format (s, "NAT_HA_WORKER_HANDOFF: next-worker %d", t->next_worker_index);
1220 :
1221 0 : return s;
1222 : }
1223 :
1224 : /* do worker handoff based on thread_index in NAT HA protcol header */
1225 : static uword
1226 0 : nat_ha_handoff_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1227 : vlib_frame_t * frame)
1228 : {
1229 0 : nat_ha_main_t *ha = &nat_ha_main;
1230 : vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
1231 : u32 n_enq, n_left_from, *from;
1232 : u16 thread_indices[VLIB_FRAME_SIZE], *ti;
1233 0 : u32 thread_index = vm->thread_index;
1234 0 : u32 do_handoff = 0, same_worker = 0;
1235 :
1236 0 : from = vlib_frame_vector_args (frame);
1237 0 : n_left_from = frame->n_vectors;
1238 0 : vlib_get_buffers (vm, from, bufs, n_left_from);
1239 :
1240 0 : b = bufs;
1241 0 : ti = thread_indices;
1242 :
1243 0 : while (n_left_from > 0)
1244 : {
1245 : nat_ha_message_header_t *h0;
1246 :
1247 0 : h0 = vlib_buffer_get_current (b[0]);
1248 0 : ti[0] = clib_net_to_host_u32 (h0->thread_index);
1249 :
1250 0 : if (ti[0] != thread_index)
1251 0 : do_handoff++;
1252 : else
1253 0 : same_worker++;
1254 :
1255 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1256 : && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
1257 : {
1258 : nat_ha_handoff_trace_t *t =
1259 0 : vlib_add_trace (vm, node, b[0], sizeof (*t));
1260 0 : t->next_worker_index = ti[0];
1261 : }
1262 :
1263 0 : n_left_from -= 1;
1264 0 : ti += 1;
1265 0 : b += 1;
1266 : }
1267 :
1268 0 : n_enq = vlib_buffer_enqueue_to_thread (vm, node, ha->fq_index, from,
1269 0 : thread_indices, frame->n_vectors, 1);
1270 :
1271 0 : if (n_enq < frame->n_vectors)
1272 0 : vlib_node_increment_counter (vm, node->node_index,
1273 : NAT_HA_HANDOFF_ERROR_CONGESTION_DROP,
1274 0 : frame->n_vectors - n_enq);
1275 0 : vlib_node_increment_counter (vm, node->node_index,
1276 : NAT_HA_HANDOFF_ERROR_SAME_WORKER, same_worker);
1277 0 : vlib_node_increment_counter (vm, node->node_index,
1278 : NAT_HA_HANDOFF_ERROR_DO_HANDOFF, do_handoff);
1279 0 : return frame->n_vectors;
1280 : }
1281 :
1282 : int
1283 0 : nat_ha_resync (u32 client_index, u32 pid,
1284 : nat_ha_resync_event_cb_t event_callback)
1285 : {
1286 0 : return 0;
1287 : }
1288 :
1289 : /* *INDENT-OFF* */
1290 62744 : VLIB_REGISTER_NODE (nat_ha_handoff_node) = {
1291 : .function = nat_ha_handoff_node_fn,
1292 : .name = "nat44-ei-ha-handoff",
1293 : .vector_size = sizeof (u32),
1294 : .format_trace = format_nat_ha_handoff_trace,
1295 : .type = VLIB_NODE_TYPE_INTERNAL,
1296 : .n_errors = ARRAY_LEN(nat_ha_handoff_error_strings),
1297 : .error_strings = nat_ha_handoff_error_strings,
1298 : .n_next_nodes = 1,
1299 : .next_nodes = {
1300 : [0] = "error-drop",
1301 : },
1302 : };
1303 : /* *INDENT-ON* */
1304 :
1305 : /*
1306 : * fd.io coding-style-patch-verification: ON
1307 : *
1308 : * Local Variables:
1309 : * eval: (c-set-style "gnu")
1310 : * End:
1311 : */
|