FD.io VPP  v18.04-17-g3a0d853
Vector Packet Processing
node.c
Go to the documentation of this file.
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17 
18 #define _GNU_SOURCE
19 #include <stdint.h>
20 #include <vnet/llc/llc.h>
21 #include <vnet/snap/snap.h>
22 #include <vnet/bonding/node.h>
23 
25 
26 #define foreach_bond_input_error \
27  _(NONE, "no error") \
28  _(IF_DOWN, "interface down") \
29  _(NO_SLAVE, "no slave") \
30  _(NO_BOND, "no bond interface")\
31  _(PASS_THRU, "pass through")
32 
33 typedef enum
34 {
35 #define _(f,s) BOND_INPUT_ERROR_##f,
37 #undef _
40 
41 static char *bond_input_error_strings[] = {
42 #define _(n,s) s,
44 #undef _
45 };
46 
47 static u8 *
48 format_bond_input_trace (u8 * s, va_list * args)
49 {
50  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
51  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
52  bond_packet_trace_t *t = va_arg (*args, bond_packet_trace_t *);
53 
54  s = format (s, "src %U, dst %U, %U -> %U",
58  t->sw_if_index,
60  t->bond_sw_if_index);
61 
62  return s;
63 }
64 
67 {
68  llc_header_t *llc;
69  snap_header_t *snap;
70 
71  llc = (llc_header_t *) (eth + 1);
72  snap = (snap_header_t *) (llc + 1);
73 
74  return ((eth->type == htons (ETHERNET_TYPE_CDP)) ||
75  ((llc->src_sap == 0xAA) && (llc->control == 0x03) &&
76  (snap->protocol == htons (0x2000)) &&
77  (snap->oui[0] == 0) && (snap->oui[1] == 0) &&
78  (snap->oui[2] == 0x0C)));
79 }
80 
81 static inline void
83  slave_if_t * sif, ethernet_header_t * eth,
84  vlib_buffer_t * b0)
85 {
86  bond_if_t *bif;
88  u16 *ethertype_p, ethertype;
90 
91  if (PREDICT_TRUE (sif != 0))
92  {
94  if (PREDICT_TRUE (bif != 0))
95  {
96  if (PREDICT_TRUE (vec_len (bif->slaves) >= 1))
97  {
98  if (PREDICT_TRUE (bif->admin_up == 1))
99  {
100  ethertype = clib_mem_unaligned (&eth->type, u16);
101  if (!ethernet_frame_is_tagged (ntohs (ethertype)))
102  {
103  // Let some layer2 packets pass through.
104  if (PREDICT_TRUE ((ethertype !=
105  htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
106  && !packet_is_cdp (eth)
107  && (ethertype !=
108  htons
109  (ETHERNET_TYPE_802_1_LLDP))))
110  {
111  // Change the physical interface to
112  // bond interface
113  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
114  bif->sw_if_index;
115 
116  /* increase rx counters */
119  VNET_INTERFACE_COUNTER_RX, thread_index,
120  bif->sw_if_index, 1);
121  }
122  else
123  {
124  vlib_error_count (vm, node->node_index,
125  BOND_INPUT_ERROR_PASS_THRU, 1);
126  }
127  }
128  else
129  {
130  vlan = (void *) (eth + 1);
131  ethertype_p = &vlan->type;
132  ethertype = clib_mem_unaligned (ethertype_p, u16);
133  if (ethertype == ntohs (ETHERNET_TYPE_VLAN))
134  {
135  vlan++;
136  ethertype_p = &vlan->type;
137  }
138  ethertype = clib_mem_unaligned (ethertype_p, u16);
139  if (PREDICT_TRUE ((ethertype !=
140  htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
141  && (ethertype !=
142  htons (ETHERNET_TYPE_CDP))
143  && (ethertype !=
144  htons
145  (ETHERNET_TYPE_802_1_LLDP))))
146  {
147  // Change the physical interface to
148  // bond interface
149  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
150  bif->sw_if_index;
151 
152  /* increase rx counters */
155  VNET_INTERFACE_COUNTER_RX, thread_index,
156  bif->sw_if_index, 1);
157  }
158  else
159  {
160  vlib_error_count (vm, node->node_index,
161  BOND_INPUT_ERROR_PASS_THRU, 1);
162  }
163  }
164  }
165  else
166  {
167  vlib_error_count (vm, node->node_index,
168  BOND_INPUT_ERROR_IF_DOWN, 1);
169  }
170  }
171  else
172  {
173  vlib_error_count (vm, node->node_index,
174  BOND_INPUT_ERROR_NO_SLAVE, 1);
175  }
176  }
177  else
178  {
179  vlib_error_count (vm, node->node_index,
180  BOND_INPUT_ERROR_NO_BOND, 1);
181  }
182  }
183  else
184  {
185  vlib_error_count (vm, node->node_index, BOND_INPUT_ERROR_NO_SLAVE, 1);
186  }
187 
188 }
189 
190 static uword
192  vlib_frame_t * frame)
193 {
194  u32 bi0, bi1, bi2, bi3;
195  vlib_buffer_t *b0, *b1, *b2, *b3;
196  u32 next_index;
197  u32 *from, *to_next, n_left_from, n_left_to_next;
198  ethernet_header_t *eth, *eth1, *eth2, *eth3;
199  u32 next0, next1, next2, next3;
201  uword n_trace = vlib_get_trace_count (vm, node);
202  u32 sw_if_index, sw_if_index1, sw_if_index2, sw_if_index3;
203  slave_if_t *sif, *sif1, *sif2, *sif3;
205 
206  /* Vector of buffer / pkt indices we're supposed to process */
207  from = vlib_frame_vector_args (frame);
208 
209  /* Number of buffers / pkts */
210  n_left_from = frame->n_vectors;
211 
212  /* Speculatively send the first buffer to the last disposition we used */
213  next_index = node->cached_next_index;
214 
215  while (n_left_from > 0)
216  {
217  /* set up to enqueue to our disposition with index = next_index */
218  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
219 
220  while (n_left_from >= 12 && n_left_to_next >= 4)
221  {
222  // Prefetch next iteration
223  {
224  vlib_buffer_t *b4, *b5, *b6, *b7;
225 
226  b4 = vlib_get_buffer (vm, from[4]);
227  b5 = vlib_get_buffer (vm, from[5]);
228  b6 = vlib_get_buffer (vm, from[6]);
229  b7 = vlib_get_buffer (vm, from[7]);
230 
231  vlib_prefetch_buffer_header (b4, LOAD);
232  vlib_prefetch_buffer_header (b5, LOAD);
233  vlib_prefetch_buffer_header (b6, LOAD);
234  vlib_prefetch_buffer_header (b7, LOAD);
235 
240  }
241 
242  next0 = 0;
243  next1 = 0;
244  next2 = 0;
245  next3 = 0;
246 
247  bi0 = from[0];
248  bi1 = from[1];
249  bi2 = from[2];
250  bi3 = from[3];
251 
252  to_next[0] = bi0;
253  to_next[1] = bi1;
254  to_next[2] = bi2;
255  to_next[3] = bi3;
256 
257  from += 4;
258  to_next += 4;
259  n_left_from -= 4;
260  n_left_to_next -= 4;
261 
262  b0 = vlib_get_buffer (vm, bi0);
263  b1 = vlib_get_buffer (vm, bi1);
264  b2 = vlib_get_buffer (vm, bi2);
265  b3 = vlib_get_buffer (vm, bi3);
266 
267  vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX], &next0,
268  b0);
269  vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_RX], &next1,
270  b1);
271  vnet_feature_next (vnet_buffer (b2)->sw_if_index[VLIB_RX], &next2,
272  b2);
273  vnet_feature_next (vnet_buffer (b3)->sw_if_index[VLIB_RX], &next3,
274  b3);
275 
280 
281  sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
282  sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
283  sw_if_index2 = vnet_buffer (b2)->sw_if_index[VLIB_RX];
284  sw_if_index3 = vnet_buffer (b3)->sw_if_index[VLIB_RX];
285 
286  // sw_if_index points to the physical interface
287  sif = bond_get_slave_by_sw_if_index (sw_if_index);
288  sif1 = bond_get_slave_by_sw_if_index (sw_if_index1);
289  sif2 = bond_get_slave_by_sw_if_index (sw_if_index2);
290  sif3 = bond_get_slave_by_sw_if_index (sw_if_index3);
291 
292  bond_sw_if_index_rewrite (vm, node, sif, eth, b0);
293  bond_sw_if_index_rewrite (vm, node, sif1, eth1, b1);
294  bond_sw_if_index_rewrite (vm, node, sif2, eth2, b2);
295  bond_sw_if_index_rewrite (vm, node, sif3, eth3, b3);
296 
297  if (PREDICT_FALSE (n_trace > 0))
298  {
299  vlib_trace_buffer (vm, node, next0, b0, 0 /* follow_chain */ );
300  vlib_set_trace_count (vm, node, --n_trace);
301  t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
302  t0->ethernet = *eth;
303  t0->sw_if_index = sw_if_index;
304  t0->bond_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
305 
306  if (PREDICT_TRUE (n_trace > 0))
307  {
308  vlib_trace_buffer (vm, node, next1, b1,
309  0 /* follow_chain */ );
310  vlib_set_trace_count (vm, node, --n_trace);
311  t0 = vlib_add_trace (vm, node, b1, sizeof (*t0));
312  t0->ethernet = *eth1;
313  t0->sw_if_index = sw_if_index1;
314  t0->bond_sw_if_index =
315  vnet_buffer (b1)->sw_if_index[VLIB_RX];
316 
317  if (PREDICT_TRUE (n_trace > 0))
318  {
319  vlib_trace_buffer (vm, node, next2, b2,
320  0 /* follow_chain */ );
321  vlib_set_trace_count (vm, node, --n_trace);
322  t0 = vlib_add_trace (vm, node, b2, sizeof (*t0));
323  t0->ethernet = *eth2;
324  t0->sw_if_index = sw_if_index2;
325  t0->bond_sw_if_index =
326  vnet_buffer (b2)->sw_if_index[VLIB_RX];
327 
328  if (PREDICT_TRUE (n_trace > 0))
329  {
330  vlib_trace_buffer (vm, node, next3, b3,
331  0 /* follow_chain */ );
332  vlib_set_trace_count (vm, node, --n_trace);
333  t0 = vlib_add_trace (vm, node, b3, sizeof (*t0));
334  t0->ethernet = *eth3;
335  t0->sw_if_index = sw_if_index3;
336  t0->bond_sw_if_index =
337  vnet_buffer (b3)->sw_if_index[VLIB_RX];
338  }
339  }
340  }
341  }
342 
347 
348  /* verify speculative enqueue, maybe switch current next frame */
349  vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
350  to_next, n_left_to_next,
351  bi0, bi1, bi2, bi3, next0, next1,
352  next2, next3);
353  }
354 
355  while (n_left_from > 0 && n_left_to_next > 0)
356  {
357  // Prefetch next iteration
358  if (n_left_from > 1)
359  {
360  vlib_buffer_t *p2;
361 
362  p2 = vlib_get_buffer (vm, from[1]);
363  vlib_prefetch_buffer_header (p2, LOAD);
365  }
366 
367  next0 = 0;
368  bi0 = from[0];
369  to_next[0] = bi0;
370  from += 1;
371  to_next += 1;
372  n_left_from -= 1;
373  n_left_to_next -= 1;
374 
375  b0 = vlib_get_buffer (vm, bi0);
376  vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX], &next0,
377  b0);
378 
380 
381  sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
382  // sw_if_index points to the physical interface
383  sif = bond_get_slave_by_sw_if_index (sw_if_index);
384  bond_sw_if_index_rewrite (vm, node, sif, eth, b0);
385 
386  if (PREDICT_FALSE (n_trace > 0))
387  {
388  vlib_trace_buffer (vm, node, next0, b0, 0 /* follow_chain */ );
389  vlib_set_trace_count (vm, node, --n_trace);
390  t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
391  t0->ethernet = *eth;
392  t0->sw_if_index = sw_if_index;
393  t0->bond_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
394 
395  }
396 
398 
399  /* verify speculative enqueue, maybe switch current next frame */
400  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
401  to_next, n_left_to_next,
402  bi0, next0);
403  }
404  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
405  }
406 
408  BOND_INPUT_ERROR_NONE, frame->n_vectors);
409 
410  vnet_device_increment_rx_packets (thread_index, frame->n_vectors);
411 
412  return frame->n_vectors;
413 }
414 
415 static clib_error_t *
417 {
418  return 0;
419 }
420 
421 /* *INDENT-OFF* */
423  .function = bond_input_fn,
424  .name = "bond-input",
425  .vector_size = sizeof (u32),
426  .format_buffer = format_ethernet_header_with_length,
427  .format_trace = format_bond_input_trace,
428  .type = VLIB_NODE_TYPE_INTERNAL,
429  .n_errors = BOND_INPUT_N_ERROR,
430  .error_strings = bond_input_error_strings,
431  .n_next_nodes = 0,
432  .next_nodes =
433  {
434  [0] = "error-drop"
435  }
436 };
437 
439 
440 VNET_FEATURE_INIT (bond_input, static) =
441 {
442  .arc_name = "device-input",
443  .node_name = "bond-input",
444  .runs_before = VNET_FEATURES ("ethernet-input"),
445 };
447 /* *INDENT-ON* */
448 
449 static clib_error_t *
451 {
452  bond_main_t *bm = &bond_main;
453  slave_if_t *sif;
454  vlib_main_t *vm = bm->vlib_main;
455 
456  sif = bond_get_slave_by_sw_if_index (sw_if_index);
457  if (sif)
458  {
460  if (sif->port_enabled == 0)
461  {
462  if (sif->lacp_enabled == 0)
463  {
465  }
466  }
467  else
468  {
469  if (sif->lacp_enabled == 0)
470  {
472  }
473  }
474  }
475 
476  return 0;
477 }
478 
480 
481 static clib_error_t *
483 {
484  bond_main_t *bm = &bond_main;
485  slave_if_t *sif;
487  vlib_main_t *vm = bm->vlib_main;
488 
489  sw = vnet_get_hw_sw_interface (vnm, hw_if_index);
491  if (sif)
492  {
493  if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
494  {
495  if (sif->lacp_enabled == 0)
496  {
498  }
499  }
500  else
501  {
502  if (sif->lacp_enabled == 0)
503  {
505  }
506  }
507  }
508 
509  return 0;
510 }
511 
513 
514 /*
515  * fd.io coding-style-patch-verification: ON
516  *
517  * Local Variables:
518  * eval: (c-set-style "gnu")
519  * End:
520  */
#define foreach_bond_input_error
Definition: node.c:26
static void vnet_device_increment_rx_packets(u32 thread_index, u64 count)
Definition: devices.h:110
VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(bond_sw_interface_up_down)
#define CLIB_UNUSED(x)
Definition: clib.h:79
static u32 vlib_get_trace_count(vlib_main_t *vm, vlib_node_runtime_t *rt)
Definition: trace_funcs.h:143
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
static clib_error_t * bond_input_init(vlib_main_t *vm)
Definition: node.c:416
vnet_interface_main_t interface_main
Definition: vnet.h:56
#define PREDICT_TRUE(x)
Definition: clib.h:106
static void vlib_error_count(vlib_main_t *vm, uword node_index, uword counter, uword increment)
Definition: error_funcs.h:57
u8 src_address[6]
Definition: packet.h:56
static void bond_sw_if_index_rewrite(vlib_main_t *vm, vlib_node_runtime_t *node, slave_if_t *sif, ethernet_header_t *eth, vlib_buffer_t *b0)
Definition: node.c:82
u32 thread_index
Definition: main.h:176
u8 src_sap
Definition: llc.h:82
#define vlib_validate_buffer_enqueue_x4(vm, node, next_index, to_next, n_left_to_next, bi0, bi1, bi2, bi3, next0, next1, next2, next3)
Finish enqueueing four buffers forward in the graph.
Definition: buffer_node.h:138
bond_main_t bond_main
Definition: node.c:24
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
static void vlib_increment_simple_counter(vlib_simple_counter_main_t *cm, u32 thread_index, u32 index, u64 increment)
Increment a simple counter.
Definition: counter.h:78
#define VNET_HW_INTERFACE_FLAG_LINK_UP
Definition: interface.h:390
bond_input_error_t
Definition: node.c:33
void bond_enable_collecting_distributing(vlib_main_t *vm, slave_if_t *sif)
Definition: cli.c:47
format_function_t format_vnet_sw_if_index_name
static vnet_sw_interface_t * vnet_get_hw_sw_interface(vnet_main_t *vnm, u32 hw_if_index)
static void vlib_trace_buffer(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, vlib_buffer_t *b, int follow_chain)
Definition: trace_funcs.h:104
VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(bond_hw_interface_up_down)
vlib_node_registration_t bond_input_node
(constructor) VLIB_REGISTER_NODE (bond_input_node)
Definition: node.c:422
#define static_always_inline
Definition: clib.h:93
u8 * format_ethernet_address(u8 *s, va_list *args)
Definition: format.c:44
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
u8 dst_address[6]
Definition: packet.h:55
u32 sw_if_index
Definition: node.h:152
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:191
vlib_main_t * vlib_main
Definition: node.h:299
static clib_error_t * bond_sw_interface_up_down(vnet_main_t *vnm, u32 sw_if_index, u32 flags)
Definition: node.c:450
u8 admin_up
Definition: node.h:143
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:209
#define PREDICT_FALSE(x)
Definition: clib.h:105
vnet_main_t vnet_main
Definition: misc.c:43
vlib_simple_counter_main_t * sw_if_counters
Definition: interface.h:717
u32 node_index
Node index.
Definition: node.h:437
static_always_inline void vnet_feature_next(u32 sw_if_index, u32 *next0, vlib_buffer_t *b0)
Definition: feature.h:221
static u8 * format_bond_input_trace(u8 *s, va_list *args)
Definition: node.c:48
#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:1166
u8 * format_ethernet_header_with_length(u8 *s, va_list *args)
Definition: format.c:91
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:143
u32 * slaves
Definition: node.h:155
u16 n_vectors
Definition: node.h:344
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:221
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:74
vlib_main_t * vm
Definition: buffer.c:294
ethernet_header_t ethernet
Definition: node.h:313
void bond_disable_collecting_distributing(vlib_main_t *vm, slave_if_t *sif)
Definition: cli.c:25
VNET_FEATURE_INIT(bond_input, static)
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:456
#define VNET_SW_INTERFACE_FLAG_ADMIN_UP
Definition: interface.h:588
static uword bond_input_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: node.c:191
unsigned int u32
Definition: types.h:88
static_always_inline u8 packet_is_cdp(ethernet_header_t *eth)
Definition: node.c:66
static bond_if_t * bond_get_master_by_dev_instance(u32 dev_instance)
Definition: node.h:432
u32 bif_dev_instance
Definition: node.h:274
static_always_inline int ethernet_frame_is_tagged(u16 type)
Definition: ethernet.h:85
u8 control
Definition: llc.h:87
static char * bond_input_error_strings[]
Definition: node.c:41
#define VNET_FEATURES(...)
Definition: feature.h:375
u64 uword
Definition: types.h:112
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
u8 lacp_enabled
Definition: node.h:231
unsigned short u16
Definition: types.h:57
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
unsigned char u8
Definition: types.h:56
#define VLIB_BUFFER_TRACE_TRAJECTORY_INIT(b)
Definition: buffer.h:553
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:267
static clib_error_t * bond_hw_interface_up_down(vnet_main_t *vnm, u32 hw_if_index, u32 flags)
Definition: node.c:482
#define clib_mem_unaligned(pointer, type)
Definition: types.h:155
#define vnet_buffer(b)
Definition: buffer.h:372
u8 data[0]
Packet data.
Definition: buffer.h:179
u8 port_enabled
Definition: node.h:225
static slave_if_t * bond_get_slave_by_sw_if_index(u32 sw_if_index)
Definition: node.h:440
static void vlib_set_trace_count(vlib_main_t *vm, vlib_node_runtime_t *rt, u32 count)
Definition: trace_funcs.h:159
u32 bond_sw_if_index
Definition: node.h:315
u32 flags
Definition: vhost-user.h:77
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:59
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
VLIB_NODE_FUNCTION_MULTIARCH(ethernet_input_not_l2_node, ethernet_input_not_l2)
Definition: node.c:1207
Definition: defs.h:46