FD.io VPP  v18.07-rc0-415-g6c78436
Vector Packet Processing
ioam_cache_node.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 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  * This file implements caching of ioam header and reattaching
17  * it in response message by performing request-response matching.
18  * Works for TCP SYN/SYN-ACK.
19  * This feature is used for anycast server selection.
20  * ioam data thus cached is used to measure and get complete round trip
21  * network path to help in server selection.
22  * There are 2 graph nodes defined to :
23  * 1. process packets that contain iOAM header and cache it
24  * 2. process TCP SYN-ACKs and reattach ioam header from the
25  * cache corresponding to TCP-SYN
26  * These graph nodes are attached to the vnet graph based on
27  * ioam cache and classifier configs.
28  * e.g.
29  * If db06::06 is the anycast service IP6 address:
30  *
31  * set ioam ip6 cache
32  *
33  * Apply this classifier on interface where requests for anycast service are received:
34  * classify session acl-hit-next ip6-node ip6-lookup table-index 0 match l3 ip6 dst db06::06
35  * ioam-decap anycast <<< ioam-decap is hooked to cache when set ioam ip6 cache is enabled
36  *
37  * Apply this classifier on interface where responses from anycast service are received:
38  * classify session acl-hit-next ip6-node ip6-add-from-cache-hop-by-hop table-index 0 match l3
39  * ip6 src db06::06 ioam-encap anycast-response
40  *
41  */
42 #include <vlib/vlib.h>
43 #include <vnet/vnet.h>
44 #include <vnet/pg/pg.h>
45 #include <vppinfra/error.h>
46 #include <vnet/ip/ip.h>
47 #include <ioam/ip6/ioam_cache.h>
48 #include <vnet/ip/ip6_hop_by_hop.h>
50 
51 typedef struct
52 {
56 
57 /* packet trace format function */
58 static u8 *
59 format_cache_trace (u8 * s, va_list * args)
60 {
61  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
62  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
63  cache_trace_t *t = va_arg (*args, cache_trace_t *);
64 
65  s = format (s, "CACHE: flow_label %d, next index %d",
66  t->flow_label, t->next_index);
67  return s;
68 }
69 
70 #define foreach_cache_error \
71 _(RECORDED, "ip6 iOAM headers cached")
72 
73 typedef enum
74 {
75 #define _(sym,str) CACHE_ERROR_##sym,
77 #undef _
80 
81 static char *cache_error_strings[] = {
82 #define _(sym,string) string,
84 #undef _
85 };
86 
87 typedef enum
88 {
91 } cache_next_t;
92 
93 static uword
95  vlib_node_runtime_t * node, vlib_frame_t * frame)
96 {
97  u32 n_left_from, *from, *to_next;
98  cache_next_t next_index;
99  u32 recorded = 0;
100 
101  from = vlib_frame_vector_args (frame);
102  n_left_from = frame->n_vectors;
103  next_index = node->cached_next_index;
104 
105  while (n_left_from > 0)
106  {
107  u32 n_left_to_next;
108 
109  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
110  // TODO: Dual loop
111  while (n_left_from > 0 && n_left_to_next > 0)
112  {
113  u32 bi0;
114  vlib_buffer_t *p0;
116  ip6_header_t *ip0;
118  tcp_header_t *tcp0;
119  u32 tcp_offset0;
120 
121  /* speculatively enqueue p0 to the current next frame */
122  bi0 = from[0];
123  to_next[0] = bi0;
124  from += 1;
125  to_next += 1;
126  n_left_from -= 1;
127  n_left_to_next -= 1;
128 
129  p0 = vlib_get_buffer (vm, bi0);
130  ip0 = vlib_buffer_get_current (p0);
131  if (IP_PROTOCOL_TCP ==
132  ip6_locate_header (p0, ip0, IP_PROTOCOL_TCP, &tcp_offset0))
133  {
134  tcp0 = (tcp_header_t *) ((u8 *) ip0 + tcp_offset0);
135  if ((tcp0->flags & TCP_FLAG_SYN) == TCP_FLAG_SYN &&
136  (tcp0->flags & TCP_FLAG_ACK) == 0)
137  {
138  /* Cache the ioam hbh header */
139  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
140  if (0 == ioam_cache_add (p0,
141  ip0,
142  clib_net_to_host_u16
143  (tcp0->src_port),
144  clib_net_to_host_u16
145  (tcp0->dst_port), hbh0,
146  clib_net_to_host_u32
147  (tcp0->seq_number) + 1))
148  {
149  recorded++;
150  }
151  }
152  }
153  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
154  {
155  if (p0->flags & VLIB_BUFFER_IS_TRACED)
156  {
157  cache_trace_t *t =
158  vlib_add_trace (vm, node, p0, sizeof (*t));
159  t->flow_label =
160  clib_net_to_host_u32
162  t->next_index = next0;
163  }
164  }
165  /* verify speculative enqueue, maybe switch current next frame */
166  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
167  to_next, n_left_to_next,
168  bi0, next0);
169  }
170 
171  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
172  }
173 
175  CACHE_ERROR_RECORDED, recorded);
176  return frame->n_vectors;
177 }
178 
179 /*
180  * Node for IP6 iOAM header cache
181  */
182 /* *INDENT-OFF* */
184 {
185  .function = ip6_ioam_cache_node_fn,
186  .name = "ip6-ioam-cache",
187  .vector_size = sizeof (u32),
188  .format_trace = format_cache_trace,
189  .type = VLIB_NODE_TYPE_INTERNAL,
190  .n_errors = ARRAY_LEN (cache_error_strings),
191  .error_strings = cache_error_strings,
192  .n_next_nodes = IOAM_CACHE_N_NEXT,
193  /* edit / add dispositions here */
194  .next_nodes =
195  {
196  [IOAM_CACHE_NEXT_POP_HBYH] = "ip6-pop-hop-by-hop"
197  },
198 };
199 /* *INDENT-ON* */
200 
201 typedef struct
202 {
205 
206 /* packet trace format function */
207 static u8 *
209 {
210  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
211  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
212  ip6_add_from_cache_hbh_trace_t *t = va_arg (*args,
214  *);
215 
216  s = format (s, "IP6_ADD_FROM_CACHE_HBH: next index %d", t->next_index);
217  return s;
218 }
219 
221 
222 #define foreach_ip6_add_from_cache_hbh_error \
223 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
224 
225 typedef enum
226 {
227 #define _(sym,str) IP6_ADD_FROM_CACHE_HBH_ERROR_##sym,
229 #undef _
232 
234 #define _(sym,string) string,
236 #undef _
237 };
238 
239 #define foreach_ip6_ioam_cache_input_next \
240  _(IP6_LOOKUP, "ip6-lookup") \
241  _(DROP, "error-drop")
242 
243 typedef enum
244 {
245 #define _(s,n) IP6_IOAM_CACHE_INPUT_NEXT_##s,
247 #undef _
250 
251 
252 static uword
254  vlib_node_runtime_t * node,
255  vlib_frame_t * frame)
256 {
258  u32 n_left_from, *from, *to_next;
259  ip_lookup_next_t next_index;
260  u32 processed = 0;
261  u8 *rewrite = 0;
262  u32 rewrite_len = 0;
263  u32 sr_rewrite_len = vec_len (cm->sr_rewrite_template);
264 
265  from = vlib_frame_vector_args (frame);
266  n_left_from = frame->n_vectors;
267  next_index = node->cached_next_index;
268 
269  while (n_left_from > 0)
270  {
271  u32 n_left_to_next;
272 
273  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
274  // TODO: Dual loop
275  while (n_left_from > 0 && n_left_to_next > 0)
276  {
277  u32 bi0;
278  vlib_buffer_t *b0;
279  u32 next0;
280  ip6_header_t *ip0;
282  ip6_sr_header_t *srh0 = 0;
283  u64 *copy_src0, *copy_dst0;
284  u16 new_l0;
285  tcp_header_t *tcp0;
286  u32 tcp_offset0;
287  ioam_cache_entry_t *entry = 0;
288 
289  next0 = IP6_IOAM_CACHE_INPUT_NEXT_IP6_LOOKUP;
290  /* speculatively enqueue b0 to the current next frame */
291  bi0 = from[0];
292  to_next[0] = bi0;
293  from += 1;
294  to_next += 1;
295  n_left_from -= 1;
296  n_left_to_next -= 1;
297 
298  b0 = vlib_get_buffer (vm, bi0);
299 
300  ip0 = vlib_buffer_get_current (b0);
301  if (IP_PROTOCOL_TCP !=
302  ip6_locate_header (b0, ip0, IP_PROTOCOL_TCP, &tcp_offset0))
303  {
304  goto TRACE0;
305  }
306  tcp0 = (tcp_header_t *) ((u8 *) ip0 + tcp_offset0);
307  if (((tcp0->flags & TCP_FLAG_SYN) == TCP_FLAG_SYN &&
308  (tcp0->flags & TCP_FLAG_ACK) == TCP_FLAG_ACK) ||
309  (tcp0->flags & TCP_FLAG_RST) == TCP_FLAG_RST)
310  {
311  if (0 != (entry = ioam_cache_lookup (ip0,
312  clib_net_to_host_u16
313  (tcp0->src_port),
314  clib_net_to_host_u16
315  (tcp0->dst_port),
316  clib_net_to_host_u32
317  (tcp0->ack_number))))
318  {
319  rewrite = entry->ioam_rewrite_string;
320  rewrite_len = vec_len (rewrite);
321  }
322  else
323  {
324  next0 = IP6_IOAM_CACHE_INPUT_NEXT_DROP;
325  goto TRACE0;
326  }
327  }
328  else
329  goto TRACE0;
330 
331 
332  /* Copy the ip header left by the required amount */
333  copy_dst0 = (u64 *) (((u8 *) ip0) - (rewrite_len + sr_rewrite_len));
334  copy_src0 = (u64 *) ip0;
335 
336  copy_dst0[0] = copy_src0[0];
337  copy_dst0[1] = copy_src0[1];
338  copy_dst0[2] = copy_src0[2];
339  copy_dst0[3] = copy_src0[3];
340  copy_dst0[4] = copy_src0[4];
341  vlib_buffer_advance (b0, -(word) (rewrite_len + sr_rewrite_len));
342  ip0 = vlib_buffer_get_current (b0);
343 
344  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
345  srh0 = (ip6_sr_header_t *) ((u8 *) hbh0 + rewrite_len);
346  /* $$$ tune, rewrite_len is a multiple of 8 */
347  clib_memcpy (hbh0, rewrite, rewrite_len);
348  clib_memcpy (srh0, cm->sr_rewrite_template, sr_rewrite_len);
349  /* Copy dst address into the DA slot in the segment list */
350  clib_memcpy (srh0->segments, ip0->dst_address.as_u64,
351  sizeof (ip6_address_t));
352  /* Rewrite the ip6 dst address with the first hop */
354  sizeof (ip6_address_t));
355  clib_memcpy (&srh0->segments[1],
356  (u8 *) hbh0 + entry->my_address_offset,
357  sizeof (ip6_address_t));
358  ioam_cache_entry_free (entry);
359 
360  /* Patch the protocol chain, insert the h-b-h (type 0) header */
361  srh0->protocol = ip0->protocol;
363  ip0->protocol = 0;
364  new_l0 =
365  clib_net_to_host_u16 (ip0->payload_length) + rewrite_len +
366  sr_rewrite_len;
367  ip0->payload_length = clib_host_to_net_u16 (new_l0);
368  processed++;
369  TRACE0:
371  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
372  {
374  vlib_add_trace (vm, node, b0, sizeof (*t));
375  t->next_index = next0;
376  }
377 
378  /* verify speculative enqueue, maybe switch current next frame */
379  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
380  to_next, n_left_to_next,
381  bi0, next0);
382  }
383 
384  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
385  }
386 
388  IP6_ADD_FROM_CACHE_HBH_ERROR_PROCESSED,
389  processed);
390  return frame->n_vectors;
391 }
392 /* *INDENT-OFF* */
394 {
395  .function = ip6_add_from_cache_hbh_node_fn,
396  .name = "ip6-add-from-cache-hop-by-hop",
397  .vector_size = sizeof (u32),
398  .format_trace = format_ip6_add_from_cache_hbh_trace,
399  .type = VLIB_NODE_TYPE_INTERNAL,
401  .error_strings = ip6_add_from_cache_hbh_error_strings,
402  /* See ip/lookup.h */
403  .n_next_nodes = IP6_IOAM_CACHE_INPUT_N_NEXT,
404  .next_nodes =
405  {
406 #define _(s,n) [IP6_IOAM_CACHE_INPUT_NEXT_##s] = n,
408 #undef _
409  },
410 };
411 /* *INDENT-ON* */
412 
415 /*
416  * fd.io coding-style-patch-verification: ON
417  *
418  * Local Variables:
419  * eval: (c-set-style "gnu")
420  * End:
421  */
#define CLIB_UNUSED(x)
Definition: clib.h:79
static ioam_cache_entry_t * ioam_cache_lookup(ip6_header_t *ip0, u16 src_port, u16 dst_port, u32 seq_no)
Definition: ioam_cache.h:319
#define TCP_FLAG_SYN
Definition: fa_node.h:13
u64 as_u64[2]
Definition: ip6_packet.h:51
unsigned long u64
Definition: types.h:89
u8 * sr_rewrite_template
Definition: ioam_cache.h:176
#define foreach_cache_error
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
static uword ip6_ioam_cache_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
static u8 * format_ip6_add_from_cache_hbh_trace(u8 *s, va_list *args)
struct _tcp_header tcp_header_t
unsigned char u8
Definition: types.h:56
ip_lookup_next_t
An adjacency is a representation of an attached L3 peer.
Definition: adj.h:50
Definition: ioam_cache.h:99
i64 word
Definition: types.h:111
static int ioam_cache_add(vlib_buffer_t *b0, ip6_header_t *ip0, u16 src_port, u16 dst_port, ip6_hop_by_hop_header_t *hbh0, u32 seq_no)
Definition: ioam_cache.h:362
#define TCP_FLAG_ACK
Definition: fa_node.h:16
#define foreach_ip6_add_from_cache_hbh_error
#define IPPROTO_IPV6_ROUTE
Definition: sr_packet.h:113
unsigned int u32
Definition: types.h:88
cache_next_t
#define VLIB_NODE_FUNCTION_MULTIARCH(node, fn)
Definition: node.h:194
static char * cache_error_strings[]
unsigned short u16
Definition: types.h:57
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:202
static uword ip6_add_from_cache_hbh_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
#define PREDICT_FALSE(x)
Definition: clib.h:105
static void ioam_cache_entry_free(ioam_cache_entry_t *entry)
Definition: ioam_cache.h:296
ip6_address_t next_hop
Definition: ioam_cache.h:109
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
Finish enqueueing one buffer forward in the graph.
Definition: buffer_node.h:218
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Get pointer to next frame vector data by (vlib_node_runtime_t, next_index).
Definition: node_funcs.h:364
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1168
#define TCP_FLAG_RST
Definition: fa_node.h:14
ip6_ioam_cache_input_next_t
ip6_add_from_cache_hbh_error_t
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:153
u16 n_vectors
Definition: node.h:380
vlib_main_t * vm
Definition: buffer.c:294
#define foreach_ip6_ioam_cache_input_next
#define clib_memcpy(a, b, c)
Definition: string.h:75
#define ARRAY_LEN(x)
Definition: clib.h:59
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Release pointer to next frame vector data.
Definition: main.c:454
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:492
u16 my_address_offset
Definition: ioam_cache.h:110
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:215
static int ip6_locate_header(vlib_buffer_t *p0, ip6_header_t *ip0, int find_hdr_type, u32 *offset)
Definition: ip6.h:497
vlib_node_registration_t ip6_add_from_cache_hbh_node
(constructor) VLIB_REGISTER_NODE (ip6_add_from_cache_hbh_node)
static void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:55
struct _vlib_node_registration vlib_node_registration_t
u32 ip_version_traffic_class_and_flow_label
Definition: ip6_packet.h:334
u16 payload_length
Definition: ip6_packet.h:338
static char * ip6_add_from_cache_hbh_error_strings[]
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
ip6_address_t segments[0]
Definition: sr_packet.h:148
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:267
ioam_cache_main_t ioam_cache_main
Definition: ioam_cache.c:58
u16 flags
Copy of main node flags.
Definition: node.h:486
cache_error_t
static u8 * format_cache_trace(u8 *s, va_list *args)
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:295
vlib_node_registration_t ioam_cache_node
(constructor) VLIB_REGISTER_NODE (ioam_cache_node)
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index, VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:111
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:57
u8 * ioam_rewrite_string
Definition: ioam_cache.h:111
ip6_address_t dst_address
Definition: ip6_packet.h:347