FD.io VPP  v19.04.1-1-ge4a0f9f
Vector Packet Processing
sctp.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 SUSE LLC.
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 #include <vnet/sctp/sctp.h>
16 #include <vnet/sctp/sctp_debug.h>
17 
19 
20 static u32
22 {
23  sctp_main_t *tm = &sctp_main;
25  void *iface_ip;
26  u32 mtu = 1460;
27 
28  pool_get (tm->listener_pool, listener);
29  clib_memset (listener, 0, sizeof (*listener));
30 
31  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].subconn_idx =
33  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index =
34  listener - tm->listener_pool;
35  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.lcl_port = tep->port;
36 
37  /* If we are provided a sw_if_index, bind using one of its IPs */
38  if (ip_is_zero (&tep->ip, 1) && tep->sw_if_index != ENDPOINT_INVALID_INDEX)
39  {
40  if ((iface_ip = ip_interface_get_first_ip (tep->sw_if_index,
41  tep->is_ip4)))
42  ip_set (&tep->ip, iface_ip, tep->is_ip4);
43  }
44  ip_copy (&listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.lcl_ip,
45  &tep->ip, tep->is_ip4);
46 
47  if (tep->sw_if_index != ENDPOINT_INVALID_INDEX)
48  mtu = tep->is_ip4 ? vnet_sw_interface_get_mtu (vnet_get_main (),
49  tep->sw_if_index,
50  VNET_MTU_IP4) :
51  vnet_sw_interface_get_mtu (vnet_get_main (), tep->sw_if_index,
52  VNET_MTU_IP6);
53 
54  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].PMTU = mtu;
55  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.is_ip4 = tep->is_ip4;
56  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.proto =
58  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_s_index = session_index;
59  listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.fib_index =
60  tep->fib_index;
61  listener->state = SCTP_STATE_CLOSED;
62 
63  sctp_connection_timers_init (listener);
64 
65  return listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index;
66 }
67 
68 u32
70 {
71  return sctp_connection_bind (session_index, tep);
72 }
73 
74 static void
75 sctp_connection_unbind (u32 listener_index)
76 {
78  sctp_connection_t *sctp_conn;
79 
80  sctp_conn = pool_elt_at_index (tm->listener_pool, listener_index);
81 
82  /* Poison the entry */
83  if (CLIB_DEBUG > 0)
84  clib_memset (sctp_conn, 0xFA, sizeof (*sctp_conn));
85 
86  pool_put_index (tm->listener_pool, listener_index);
87 }
88 
89 u32
90 sctp_session_unbind (u32 listener_index)
91 {
92  sctp_connection_unbind (listener_index);
93  return 0;
94 }
95 
96 void
98 {
99  sctp_main_t *tm = &sctp_main;
100  if (is_ip4)
101  tm->punt_unknown4 = is_add;
102  else
103  tm->punt_unknown6 = is_add;
104 }
105 
106 static int
107 sctp_alloc_custom_local_endpoint (sctp_main_t * tm, ip46_address_t * lcl_addr,
108  u16 * lcl_port, u8 is_ip4)
109 {
110  int index, port;
111  if (is_ip4)
112  {
113  index = tm->last_v4_address_rotor++;
114  if (tm->last_v4_address_rotor >= vec_len (tm->ip4_src_addresses))
115  tm->last_v4_address_rotor = 0;
116  lcl_addr->ip4.as_u32 = tm->ip4_src_addresses[index].as_u32;
117  }
118  else
119  {
120  index = tm->last_v6_address_rotor++;
121  if (tm->last_v6_address_rotor >= vec_len (tm->ip6_src_addresses))
122  tm->last_v6_address_rotor = 0;
123  clib_memcpy (&lcl_addr->ip6, &tm->ip6_src_addresses[index],
124  sizeof (ip6_address_t));
125  }
127  if (port < 1)
128  {
129  clib_warning ("Failed to allocate src port");
130  return -1;
131  }
132  *lcl_port = port;
133  return 0;
134 }
135 
136 /**
137  * Initialize all connection timers as invalid
138  */
139 void
141 {
142  int i, j;
143 
144  /* Set all to invalid */
145  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
146  {
147  sctp_conn->sub_conn[i].RTO = SCTP_RTO_INIT;
148 
149  for (j = 0; j < SCTP_N_TIMERS; j++)
150  {
151  sctp_conn->sub_conn[i].timers[j] = SCTP_TIMER_HANDLE_INVALID;
152  }
153  }
154 }
155 
156 /**
157  * Stop all connection timers
158  */
159 void
161 {
162  int i, j;
163  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
164  {
165  for (j = 0; j < SCTP_N_TIMERS; j++)
166  sctp_timer_reset (sctp_conn, i, j);
167  }
168 }
169 
170 const char *sctp_fsm_states[] = {
171 #define _(sym, str) str,
173 #undef _
174 };
175 
176 u8 *
177 format_sctp_state (u8 * s, va_list * args)
178 {
179  u32 state = va_arg (*args, u32);
180 
181  if (state < SCTP_N_STATES)
182  s = format (s, "%s", sctp_fsm_states[state]);
183  else
184  s = format (s, "UNKNOWN (%d (0x%x))", state, state);
185  return s;
186 }
187 
188 u8 *
189 format_sctp_connection_id (u8 * s, va_list * args)
190 {
191  sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *);
192  if (!sctp_conn)
193  return s;
194 
195  u8 i;
196  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
197  {
198  if (i > 0 && sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN)
199  continue;
200  if (sctp_conn->sub_conn[i].connection.is_ip4)
201  {
202  s = format (s, "[#%d][%s] %U:%d->%U:%d",
203  sctp_conn->sub_conn[i].connection.thread_index,
204  "S",
206  &sctp_conn->sub_conn[i].connection.lcl_ip.ip4,
207  clib_net_to_host_u16 (sctp_conn->sub_conn[i].
208  connection.lcl_port),
210  &sctp_conn->sub_conn[i].connection.rmt_ip.ip4,
211  clib_net_to_host_u16 (sctp_conn->sub_conn[i].
212  connection.rmt_port));
213  }
214  else
215  {
216  s = format (s, "[#%d][%s] %U:%d->%U:%d",
217  sctp_conn->sub_conn[i].connection.thread_index,
218  "S",
220  &sctp_conn->sub_conn[i].connection.lcl_ip.ip6,
221  clib_net_to_host_u16 (sctp_conn->sub_conn[i].
222  connection.lcl_port),
224  &sctp_conn->sub_conn[i].connection.rmt_ip.ip6,
225  clib_net_to_host_u16 (sctp_conn->sub_conn[i].
226  connection.rmt_port));
227  }
228  }
229  return s;
230 }
231 
232 u8 *
233 format_sctp_connection (u8 * s, va_list * args)
234 {
235  sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *);
236  u32 verbose = va_arg (*args, u32);
237 
238  if (!sctp_conn)
239  return s;
240  s = format (s, "%-50U", format_sctp_connection_id, sctp_conn);
241  if (verbose)
242  {
243  s = format (s, "%-15U", format_sctp_state, sctp_conn->state);
244  if (verbose > 1)
245  s = format (s, "\n");
246  }
247 
248  return s;
249 }
250 
251 /**
252  * Initialize connection send variables.
253  */
254 void
256 {
257  u32 time_now;
258  /*
259  * We use the time to randomize iss and for setting up the initial
260  * timestamp. Make sure it's updated otherwise syn and ack in the
261  * handshake may make it look as if time has flown in the opposite
262  * direction for us.
263  */
264 
266  time_now = sctp_time_now ();
267 
268  sctp_conn->local_initial_tsn = random_u32 (&time_now);
269  sctp_conn->last_unacked_tsn = sctp_conn->local_initial_tsn;
270  sctp_conn->next_tsn = sctp_conn->local_initial_tsn + 1;
271 
272  sctp_conn->remote_initial_tsn = 0x0;
273  sctp_conn->last_rcvd_tsn = sctp_conn->remote_initial_tsn;
274 }
275 
278 {
280  sctp_connection_t *sctp_conn = tm->connections[thread_index];
281 
282  u8 subconn_idx = sctp_next_avail_subconn (sctp_conn);
283 
284  ASSERT (subconn_idx < MAX_SCTP_CONNECTIONS);
285 
286  sctp_conn->sub_conn[subconn_idx].connection.c_index =
287  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.c_index;
288  sctp_conn->sub_conn[subconn_idx].connection.thread_index = thread_index;
289  sctp_conn->sub_conn[subconn_idx].subconn_idx = subconn_idx;
290 
291  return sctp_conn;
292 }
293 
294 u8
296  ip4_address_t * lcl_addr,
297  ip4_address_t * rmt_addr)
298 {
300 
301  u8 subconn_idx = sctp_next_avail_subconn (sctp_conn);
302 
303  if (subconn_idx == MAX_SCTP_CONNECTIONS)
304  return SCTP_ERROR_MAX_CONNECTIONS;
305 
306  clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.lcl_ip,
307  &lcl_addr, sizeof (lcl_addr));
308 
309  clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.rmt_ip,
310  &rmt_addr, sizeof (rmt_addr));
311 
312  sctp_conn->forming_association_changed = 1;
313 
314  return SCTP_ERROR_NONE;
315 }
316 
317 u8
319  ip4_address_t * rmt_addr)
320 {
322 
323  u32 thread_idx = vlib_get_thread_index ();
324  u8 i;
325 
326  ASSERT (thread_idx == 0);
327 
328  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
329  {
330  sctp_connection_t *sctp_conn = sctp_main->connections[thread_idx];
331  sctp_sub_connection_t *sub_conn =
332  &sctp_main->connections[thread_idx]->sub_conn[i];
333  ip46_address_t *lcl_ip =
334  &sctp_main->connections[thread_idx]->sub_conn[i].connection.lcl_ip;
335  ip46_address_t *rmt_ip =
336  &sctp_main->connections[thread_idx]->sub_conn[i].connection.rmt_ip;
337 
338  if (!sub_conn->connection.is_ip4)
339  continue;
340  if (lcl_ip->ip4.as_u32 == lcl_addr->as_u32 &&
341  rmt_ip->ip4.as_u32 == rmt_addr->as_u32)
342  {
343  sub_conn->state = SCTP_SUBCONN_STATE_DOWN;
344  sctp_conn->forming_association_changed = 1;
345  break;
346  }
347  }
348  return SCTP_ERROR_NONE;
349 }
350 
351 u8
353  ip6_address_t * lcl_addr,
354  ip6_address_t * rmt_addr)
355 {
357 
358  u8 subconn_idx = sctp_next_avail_subconn (sctp_conn);
359 
360  if (subconn_idx == MAX_SCTP_CONNECTIONS)
361  return SCTP_ERROR_MAX_CONNECTIONS;
362 
363  clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.lcl_ip,
364  &lcl_addr, sizeof (lcl_addr));
365 
366  clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.rmt_ip,
367  &rmt_addr, sizeof (rmt_addr));
368 
369  sctp_conn->forming_association_changed = 1;
370 
371  return SCTP_ERROR_NONE;
372 }
373 
374 u8
376  ip6_address_t * rmt_addr)
377 {
379 
380  u32 thread_idx = vlib_get_thread_index ();
381  u8 i;
382 
383  ASSERT (thread_idx == 0);
384 
385  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
386  {
387  sctp_connection_t *sctp_conn = sctp_main->connections[thread_idx];
388  sctp_sub_connection_t *sub_conn =
389  &sctp_main->connections[thread_idx]->sub_conn[i];
390  ip46_address_t *lcl_ip =
391  &sctp_main->connections[thread_idx]->sub_conn[i].connection.lcl_ip;
392  ip46_address_t *rmt_ip =
393  &sctp_main->connections[thread_idx]->sub_conn[i].connection.rmt_ip;
394 
395  if (!sub_conn->connection.is_ip4)
396  continue;
397  if ((lcl_ip->ip6.as_u64[0] == lcl_addr->as_u64[0]
398  && lcl_ip->ip6.as_u64[1] == lcl_addr->as_u64[1])
399  && (rmt_ip->ip6.as_u64[0] == rmt_addr->as_u64[0]
400  && rmt_ip->ip6.as_u64[1] == rmt_addr->as_u64[1]))
401  {
402  sub_conn->state = SCTP_SUBCONN_STATE_DOWN;
403  sctp_conn->forming_association_changed = 1;
404  break;
405  }
406  }
407  return SCTP_ERROR_NONE;
408 }
409 
410 u8
412 {
414 
415  u32 thread_idx = vlib_get_thread_index ();
416 
417  sctp_main->connections[thread_idx]->conn_config.never_delay_sack =
418  config.never_delay_sack;
419  sctp_main->connections[thread_idx]->conn_config.never_bundle =
420  config.never_bundle;
421 
422  return 0;
423 }
424 
426 sctp_connection_new (u8 thread_index)
427 {
429  sctp_connection_t *sctp_conn;
430 
431  pool_get (sctp_main->connections[thread_index], sctp_conn);
432  clib_memset (sctp_conn, 0, sizeof (*sctp_conn));
433  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].subconn_idx =
435  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index =
436  sctp_conn - sctp_main->connections[thread_index];
437  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].c_thread_index = thread_index;
438  sctp_conn->local_tag = 0;
439 
440  return sctp_conn;
441 }
442 
445 {
447  sctp_connection_t *sctp_conn = 0;
448  ASSERT (vlib_get_thread_index () == 0);
449  pool_get (tm->half_open_connections, sctp_conn);
450  clib_memset (sctp_conn, 0, sizeof (*sctp_conn));
451  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index =
452  sctp_conn - tm->half_open_connections;
453  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].subconn_idx =
455  return sctp_conn;
456 }
457 
458 static inline int
460 {
462  sctp_connection_t *sctp_conn;
463  ip46_address_t lcl_addr;
464  u16 lcl_port;
465  uword thread_id;
466  u32 mtu = 1460;
467  int rv;
468 
470 
471  /*
472  * Allocate local endpoint
473  */
474  if ((rmt->is_ip4 && vec_len (tm->ip4_src_addresses))
475  || (!rmt->is_ip4 && vec_len (tm->ip6_src_addresses)))
476  rv = sctp_alloc_custom_local_endpoint (tm, &lcl_addr, &lcl_port,
477  rmt->is_ip4);
478  else
480  rmt, &lcl_addr, &lcl_port);
481 
482  if (rv)
483  return -1;
484 
485  /*
486  * Create connection and send INIT CHUNK
487  */
488  thread_id = vlib_get_thread_index ();
489  ASSERT (thread_id == 0);
490 
491  clib_spinlock_lock_if_init (&tm->half_open_lock);
492  sctp_conn = sctp_half_open_connection_new (thread_id);
493  if (rmt->peer.sw_if_index != ENDPOINT_INVALID_INDEX)
494  mtu = rmt->is_ip4 ? vnet_sw_interface_get_mtu (vnet_get_main (),
495  rmt->peer.sw_if_index,
496  VNET_MTU_IP4) :
497  vnet_sw_interface_get_mtu (vnet_get_main (), rmt->peer.sw_if_index,
498  VNET_MTU_IP6);
499  sctp_conn->sub_conn[idx].PMTU = mtu;
500 
501  transport_connection_t *trans_conn = &sctp_conn->sub_conn[idx].connection;
502  ip_copy (&trans_conn->rmt_ip, &rmt->ip, rmt->is_ip4);
503  ip_copy (&trans_conn->lcl_ip, &lcl_addr, rmt->is_ip4);
504  sctp_conn->sub_conn[idx].subconn_idx = idx;
505  trans_conn->rmt_port = rmt->port;
506  trans_conn->lcl_port = clib_host_to_net_u16 (lcl_port);
507  trans_conn->is_ip4 = rmt->is_ip4;
508  trans_conn->proto = TRANSPORT_PROTO_SCTP;
509  trans_conn->fib_index = rmt->fib_index;
510 
511  sctp_connection_timers_init (sctp_conn);
512  /* The other connection vars will be initialized after INIT_ACK chunk received */
513  sctp_init_snd_vars (sctp_conn);
514 
515  sctp_send_init (sctp_conn);
516 
517  clib_spinlock_unlock_if_init (&tm->half_open_lock);
518 
519  return sctp_conn->sub_conn[idx].connection.c_index;
520 }
521 
522 /**
523  * Cleans up connection state.
524  *
525  * No notifications.
526  */
527 void
529 {
530  sctp_main_t *tm = &sctp_main;
531  u8 i;
532 
533  /* Cleanup local endpoint if this was an active connect */
534  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
536  &sctp_conn->sub_conn[i].connection.lcl_ip,
537  sctp_conn->sub_conn[i].connection.lcl_port);
538 
539  int thread_index =
540  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.thread_index;
541 
542  /* Make sure all timers are cleared */
543  sctp_connection_timers_reset (sctp_conn);
544 
545  /* Poison the entry */
546  if (CLIB_DEBUG > 0)
547  clib_memset (sctp_conn, 0xFA, sizeof (*sctp_conn));
548  pool_put (tm->connections[thread_index], sctp_conn);
549 }
550 
551 int
553 {
554  return sctp_connection_open (tep);
555 }
556 
557 u16
559 {
560  u8 i;
561  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
562  {
563  if (sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN)
564  continue;
565 
566  if (sctp_conn->sub_conn[i].is_retransmitting == 1 ||
567  sctp_conn->sub_conn[i].enqueue_state != SCTP_ERROR_ENQUEUED)
568  {
570  ("Connection %u has still DATA to be enqueued inboud / outboud",
571  sctp_conn->sub_conn[i].connection.c_index);
572  return 1;
573  }
574 
575  }
576  return 0; /* Indicates no more data to be read/sent */
577 }
578 
579 void
581 {
582  SCTP_DBG ("Closing connection %u...",
583  sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.c_index);
584 
585  sctp_conn->state = SCTP_STATE_SHUTDOWN_PENDING;
586 
587  sctp_send_shutdown (sctp_conn);
588 }
589 
590 void
591 sctp_session_close (u32 conn_index, u32 thread_index)
592 {
593  ASSERT (thread_index == 0);
594 
595  sctp_connection_t *sctp_conn =
596  sctp_connection_get (conn_index, thread_index);
597  if (sctp_conn != NULL)
598  sctp_connection_close (sctp_conn);
599 }
600 
601 void
602 sctp_session_cleanup (u32 conn_index, u32 thread_index)
603 {
604  sctp_connection_t *sctp_conn =
605  sctp_connection_get (conn_index, thread_index);
606 
607  if (sctp_conn != NULL)
608  {
609  sctp_connection_timers_reset (sctp_conn);
610  /* Wait for the session tx events to clear */
611  sctp_conn->state = SCTP_STATE_CLOSED;
612  }
613 }
614 
615 /**
616  * Compute maximum segment size for session layer.
617  */
618 u16
620 {
621  sctp_connection_t *sctp_conn =
623 
624  if (sctp_conn == NULL)
625  {
626  SCTP_DBG ("sctp_conn == NULL");
627  return 0;
628  }
629 
630  update_cwnd (sctp_conn);
631  update_smallest_pmtu_idx (sctp_conn);
632 
633  u8 idx = sctp_data_subconn_select (sctp_conn);
634  return sctp_conn->sub_conn[idx].cwnd;
635 }
636 
637 u16
639 {
640  /* RFC 4096 Section 6.1; point (A) */
641  if (sctp_conn->peer_rwnd == 0)
642  return 0;
643 
644  u8 idx = sctp_data_subconn_select (sctp_conn);
645 
646  u32 available_wnd =
647  clib_min (sctp_conn->peer_rwnd, sctp_conn->sub_conn[idx].cwnd);
648  int flight_size = (int) (sctp_conn->next_tsn - sctp_conn->last_unacked_tsn);
649 
650  if (available_wnd <= flight_size)
651  return 0;
652 
653  /* Finally, let's subtract the DATA chunk headers overhead */
654  return available_wnd -
655  flight_size -
656  sizeof (sctp_payload_data_chunk_t) - sizeof (sctp_full_hdr_t);
657 }
658 
659 /**
660  * Compute TX window session is allowed to fill.
661  */
662 u32
664 {
665  sctp_connection_t *sctp_conn =
667 
668  return sctp_snd_space (sctp_conn);
669 }
670 
672 sctp_session_get_transport (u32 conn_index, u32 thread_index)
673 {
674  sctp_connection_t *sctp_conn =
675  sctp_connection_get (conn_index, thread_index);
676 
677  if (PREDICT_TRUE (sctp_conn != NULL))
678  return &sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection;
679 
680  return NULL;
681 }
682 
685 {
687  sctp_connection_t *sctp_conn;
688  sctp_conn = pool_elt_at_index (tm->listener_pool, listener_index);
689  return &sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection;
690 }
691 
692 u8 *
693 format_sctp_session (u8 * s, va_list * args)
694 {
695  u32 tci = va_arg (*args, u32);
696  u32 thread_index = va_arg (*args, u32);
697  u32 verbose = va_arg (*args, u32);
698  sctp_connection_t *tc;
699 
700  tc = sctp_connection_get (tci, thread_index);
701  if (tc)
702  s = format (s, "%U", format_sctp_connection, tc, verbose);
703  else
704  s = format (s, "empty\n");
705  return s;
706 }
707 
708 u8 *
709 format_sctp_listener_session (u8 * s, va_list * args)
710 {
711  u32 tci = va_arg (*args, u32);
713  return format (s, "%U", format_sctp_connection_id, tc);
714 }
715 
716 void
717 sctp_expired_timers_cb (u32 conn_index, u32 timer_id)
718 {
719  sctp_connection_t *sctp_conn;
720 
721  SCTP_DBG ("%s expired", sctp_timer_to_string (timer_id));
722 
723  sctp_conn = sctp_connection_get (conn_index, vlib_get_thread_index ());
724  /* note: the connection may have already disappeared */
725  if (PREDICT_FALSE (sctp_conn == 0))
726  return;
727 
728  if (sctp_conn->sub_conn[conn_index].unacknowledged_hb >
730  {
731  // The remote-peer is considered to be unreachable hence shutting down
732  u8 i, total_subs_down = 1;
733  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
734  {
735  if (sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN)
736  continue;
737 
738  u32 now = sctp_time_now ();
739  if (now > (sctp_conn->sub_conn[i].last_seen + SCTP_HB_INTERVAL))
740  {
741  total_subs_down += 1;
742  sctp_conn->sub_conn[i].state = SCTP_SUBCONN_STATE_DOWN;
743  }
744  }
745 
746  if (total_subs_down == MAX_SCTP_CONNECTIONS)
747  {
748  /* Start cleanup. App wasn't notified yet so use delete notify as
749  * opposed to delete to cleanup session layer state. */
750  session_transport_delete_notify (&sctp_conn->sub_conn
751  [SCTP_PRIMARY_PATH_IDX].connection);
752 
753  sctp_connection_timers_reset (sctp_conn);
754 
755  sctp_connection_cleanup (sctp_conn);
756  }
757  return;
758  }
759 
760  switch (timer_id)
761  {
762  case SCTP_TIMER_T1_INIT:
763  sctp_send_init (sctp_conn);
764  break;
765  case SCTP_TIMER_T1_COOKIE:
766  sctp_send_cookie_echo (sctp_conn);
767  break;
768  case SCTP_TIMER_T2_SHUTDOWN:
769  sctp_send_shutdown (sctp_conn);
770  break;
771  case SCTP_TIMER_T3_RXTX:
772  sctp_timer_reset (sctp_conn, conn_index, timer_id);
773  sctp_conn->flags |= SCTP_CONN_RECOVERY;
774  sctp_data_retransmit (sctp_conn);
775  break;
776  case SCTP_TIMER_T4_HEARTBEAT:
777  sctp_timer_reset (sctp_conn, conn_index, timer_id);
778  goto heartbeat;
779  }
780  return;
781 
782 heartbeat:
783  sctp_send_heartbeat (sctp_conn);
784 }
785 
786 static void
788 {
789  int i;
790  u32 connection_index, timer_id;
791 
792  for (i = 0; i < vec_len (expired_timers); i++)
793  {
794  /* Get session index and timer id */
795  connection_index = expired_timers[i] & 0x0FFFFFFF;
796  timer_id = expired_timers[i] >> 28;
797 
798  SCTP_DBG ("Expired timer ID: %u", timer_id);
799 
800  /* Handle expiration */
801  sctp_expired_timers_cb (connection_index, timer_id);
802  }
803 }
804 
805 void
807 {
808  tw_timer_wheel_16t_2w_512sl_t *tw;
809  /* *INDENT-OFF* */
811  tw = &tm->timer_wheels[ii];
812  tw_timer_wheel_init_16t_2w_512sl (tw, sctp_expired_timers_dispatch,
813  100e-3 /* timer period 100ms */ , ~0);
814  tw->last_run_time = vlib_time_now (this_vlib_main);
815  }));
816  /* *INDENT-ON* */
817 }
818 
819 clib_error_t *
821 {
824  clib_error_t *error = 0;
825  u32 num_threads;
826  int thread;
827  sctp_connection_t *sctp_conn __attribute__ ((unused));
828  u32 preallocated_connections_per_thread;
829 
830  if ((error = vlib_call_init_function (vm, ip_main_init)))
831  return error;
832  if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
833  return error;
834  if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
835  return error;
836 
837  /*
838  * Registrations
839  */
840 
843 
844  /*
845  * Initialize data structures
846  */
847 
848  num_threads = 1 /* main thread */ + vtm->n_threads;
849  vec_validate (tm->connections, num_threads - 1);
850 
851  /*
852  * Preallocate connections. Assume that thread 0 won't
853  * use preallocated threads when running multi-core
854  */
855  if (num_threads == 1)
856  {
857  thread = 0;
858  preallocated_connections_per_thread = tm->preallocated_connections;
859  }
860  else
861  {
862  thread = 1;
863  preallocated_connections_per_thread =
864  tm->preallocated_connections / (num_threads - 1);
865  }
866  for (; thread < num_threads; thread++)
867  {
868  if (preallocated_connections_per_thread)
869  pool_init_fixed (tm->connections[thread],
870  preallocated_connections_per_thread);
871  }
872 
873  /* Initialize per worker thread tx buffers (used for control messages) */
874  vec_validate (tm->tx_buffers, num_threads - 1);
875 
876  /* Initialize timer wheels */
877  vec_validate (tm->timer_wheels, num_threads - 1);
879 
880  /* Initialize clocks per tick for SCTP timestamp. Used to compute
881  * monotonically increasing timestamps. */
882  tm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
884 
885  if (num_threads > 1)
886  {
887  clib_spinlock_init (&tm->half_open_lock);
888  }
889 
890  vec_validate (tm->tx_frames[0], num_threads - 1);
891  vec_validate (tm->tx_frames[1], num_threads - 1);
892  vec_validate (tm->ip_lookup_tx_frames[0], num_threads - 1);
893  vec_validate (tm->ip_lookup_tx_frames[1], num_threads - 1);
894 
895  tm->bytes_per_buffer = vlib_buffer_get_default_data_size (vm);
896 
897  vec_validate (tm->time_now, num_threads - 1);
898  return error;
899 }
900 
901 clib_error_t *
903 {
904  if (is_en)
905  {
906  if (sctp_main.is_enabled)
907  return 0;
908 
909  return sctp_main_enable (vm);
910  }
911  else
912  {
913  sctp_main.is_enabled = 0;
914  }
915 
916  return 0;
917 }
918 
921 {
922  sctp_connection_t *sctp_conn = sctp_half_open_connection_get (conn_index);
923  return &sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection;
924 }
925 
926 u8 *
927 format_sctp_half_open (u8 * s, va_list * args)
928 {
929  u32 tci = va_arg (*args, u32);
931  return format (s, "%U", format_sctp_connection_id, sctp_conn);
932 }
933 
934 void
935 sctp_update_time (f64 now, u8 thread_index)
936 {
937  sctp_set_time_now (thread_index);
938  tw_timer_expire_timers_16t_2w_512sl (&sctp_main.timer_wheels[thread_index],
939  now);
940  sctp_flush_frames_to_output (thread_index);
941 }
942 
943 /* *INDENT OFF* */
945  .enable = sctp_enable_disable,
946  .start_listen = sctp_session_bind,
947  .stop_listen = sctp_session_unbind,
948  .connect = sctp_session_open,
949  .close = sctp_session_close,
950  .cleanup = sctp_session_cleanup,
951  .push_header = sctp_push_header,
952  .send_mss = sctp_session_send_mss,
953  .send_space = sctp_session_send_space,
954  .update_time = sctp_update_time,
955  .get_connection = sctp_session_get_transport,
956  .get_listener = sctp_session_get_listener,
957  .get_half_open = sctp_half_open_session_get_transport,
958  .format_connection = format_sctp_session,
959  .format_listener = format_sctp_listener_session,
960  .format_half_open = format_sctp_half_open,
961  .tx_type = TRANSPORT_TX_DEQUEUE,
962  .service_type = TRANSPORT_SERVICE_VC,
963 };
964 
965 /* *INDENT ON* */
966 
967 clib_error_t *
969 {
971  ip_main_t *im = &ip_main;
972  ip_protocol_info_t *pi;
973  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "sctp4-established");
974  tm->sctp4_established_phase_node_index = node->index;
975 
976  node = vlib_get_node_by_name (vm, (u8 *) "sctp6-established");
977  tm->sctp6_established_phase_node_index = node->index;
978 
979  /* Session layer, and by implication SCTP, are disabled by default */
980  tm->is_enabled = 0;
981 
982  /* Register with IP for header parsing */
984  if (pi == 0)
985  return clib_error_return (0, "SCTP protocol info AWOL");
988 
989  /* Register as transport with session layer */
994 
996 
997  return 0;
998 }
999 
1001 
1002 static clib_error_t *
1004  vlib_cli_command_t * cmd_arg)
1005 {
1006  sctp_main_t *tm = &sctp_main;
1008  return clib_error_return (0, "unknown input `%U'", format_unformat_error,
1009  input);
1010  vlib_cli_output (vm, "IPv4 UDP punt: %s",
1011  tm->punt_unknown4 ? "enabled" : "disabled");
1012  vlib_cli_output (vm, "IPv6 UDP punt: %s",
1013  tm->punt_unknown6 ? "enabled" : "disabled");
1014  return 0;
1015 }
1016 /* *INDENT-OFF* */
1017 VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
1018 {
1019  .path = "show sctp punt",
1020  .short_help = "show sctp punt",
1021  .function = show_sctp_punt_fn,
1022 };
1023 /* *INDENT-ON* */
1024 
1025 /*
1026  * fd.io coding-style-patch-verification: ON
1027  *
1028  * Local Variables:
1029  * eval: (c-set-style "gnu")
1030  * End:
1031  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
void sctp_connection_timers_reset(sctp_connection_t *sctp_conn)
Stop all connection timers.
Definition: sctp.c:160
u16 sctp_check_outstanding_data_chunks(sctp_connection_t *sctp_conn)
Definition: sctp.c:558
struct _sctp_main sctp_main_t
void sctp_connection_timers_init(sctp_connection_t *sctp_conn)
Initialize all connection timers as invalid.
Definition: sctp.c:140
#define ENDPOINT_INVALID_INDEX
static void update_cwnd(sctp_connection_t *sctp_conn)
Definition: sctp.h:937
clib_error_t * sctp_main_enable(vlib_main_t *vm)
Definition: sctp.c:820
void sctp_flush_frames_to_output(u8 thread_index)
Flush v4 and v6 sctp and ip-lookup tx frames for thread index.
Definition: sctp_output.c:57
#define clib_min(x, y)
Definition: clib.h:295
#define SCTP_DBG(_fmt, _args...)
Definition: sctp_debug.h:38
static int sctp_alloc_custom_local_endpoint(sctp_main_t *tm, ip46_address_t *lcl_addr, u16 *lcl_port, u8 is_ip4)
Definition: sctp.c:107
void sctp_init_snd_vars(sctp_connection_t *sctp_conn)
Initialize connection send variables.
Definition: sctp.c:255
void ip_copy(ip46_address_t *dst, ip46_address_t *src, u8 is_ip4)
Definition: ip.c:81
void ip6_register_protocol(u32 protocol, u32 node_index)
Definition: ip6_forward.c:1466
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
static clib_error_t * ip4_lookup_init(vlib_main_t *vm)
Definition: ip4_forward.c:909
transport_connection_t * sctp_half_open_session_get_transport(u32 conn_index)
Definition: sctp.c:920
#define PREDICT_TRUE(x)
Definition: clib.h:112
u64 as_u64[2]
Definition: ip6_packet.h:51
void sctp_send_init(sctp_connection_t *sctp_conn)
Definition: sctp_output.c:1285
struct _sctp_sub_connection sctp_sub_connection_t
void ip_set(ip46_address_t *dst, void *src, u8 is_ip4)
Definition: ip.c:90
#define NULL
Definition: clib.h:58
u32 index
Definition: node.h:279
void session_transport_delete_notify(transport_connection_t *tc)
Notification from transport that connection is being deleted.
Definition: session.c:746
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:255
static u64 sctp_time_now(void)
Definition: sctp.h:671
static_always_inline void clib_spinlock_unlock_if_init(clib_spinlock_t *p)
Definition: lock.h:98
u32 thread_index
Definition: main.h:197
#define foreach_sctp_fsm_state
SSCTP FSM state definitions as per RFC4960.
Definition: sctp.h:346
vlib_node_registration_t sctp6_output_node
(constructor) VLIB_REGISTER_NODE (sctp6_output_node)
clib_error_t * sctp_enable_disable(vlib_main_t *vm, u8 is_en)
Definition: sctp.c:902
u8 * format_sctp_half_open(u8 *s, va_list *args)
Definition: sctp.c:927
int i
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
u16 sctp_session_send_mss(transport_connection_t *trans_conn)
Compute maximum segment size for session layer.
Definition: sctp.c:619
void * ip_interface_get_first_ip(u32 sw_if_index, u8 is_ip4)
Definition: ip.c:134
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
static void update_smallest_pmtu_idx(sctp_connection_t *sctp_conn)
Definition: sctp.h:883
static u32 vnet_sw_interface_get_mtu(vnet_main_t *vnm, u32 sw_if_index, vnet_mtu_t af)
clib_time_t clib_time
Definition: main.h:72
Definition: ip.h:109
static u32 sctp_connection_bind(u32 session_index, transport_endpoint_t *tep)
Definition: sctp.c:21
void ip4_register_protocol(u32 protocol, u32 node_index)
Definition: ip4_forward.c:1699
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:236
struct _sctp_user_configuration sctp_user_configuration_t
u8 sctp_configure(sctp_user_configuration_t config)
Definition: sctp.c:411
unsigned char u8
Definition: types.h:56
static sctp_main_t * vnet_get_sctp_main()
Definition: sctp.h:544
double f64
Definition: types.h:142
int sctp_session_open(transport_endpoint_cfg_t *tep)
Definition: sctp.c:552
vlib_node_registration_t sctp6_input_node
(constructor) VLIB_REGISTER_NODE (sctp6_input_node)
Definition: sctp_input.c:2297
#define clib_memcpy(d, s, n)
Definition: string.h:180
#define SCTP_PATH_MAX_RETRANS
Definition: sctp.h:464
sctp_main_t sctp_main
Definition: sctp.c:18
format_function_t format_ip4_address
Definition: format.h:75
unformat_function_t * unformat_pg_edit
Definition: ip.h:90
u8 * format_sctp_listener_session(u8 *s, va_list *args)
Definition: sctp.c:709
u32 sctp_session_unbind(u32 listener_index)
Definition: sctp.c:90
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:163
#define always_inline
Definition: clib.h:98
transport_connection_t * sctp_session_get_transport(u32 conn_index, u32 thread_index)
Definition: sctp.c:672
#define clib_error_return(e, args...)
Definition: error.h:99
vhost_vring_state_t state
Definition: vhost_user.h:120
u8 * format_sctp_session(u8 *s, va_list *args)
Definition: sctp.c:693
static void sctp_connection_unbind(u32 listener_index)
Definition: sctp.c:75
pthread_t thread[MAX_CONNS]
Definition: main.c:142
int transport_alloc_local_endpoint(u8 proto, transport_endpoint_cfg_t *rmt_cfg, ip46_address_t *lcl_addr, u16 *lcl_port)
Definition: transport.c:472
unsigned int u32
Definition: types.h:88
sctp_connection_t * sctp_connection_new(u8 thread_index)
Definition: sctp.c:426
void sctp_session_cleanup(u32 conn_index, u32 thread_index)
Definition: sctp.c:602
#define vlib_call_init_function(vm, x)
Definition: init.h:260
void sctp_data_retransmit(sctp_connection_t *sctp_conn)
Definition: sctp_output.c:1492
static int sctp_connection_open(transport_endpoint_cfg_t *rmt)
Definition: sctp.c:459
struct _transport_proto_vft transport_proto_vft_t
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:57
void sctp_punt_unknown(vlib_main_t *vm, u8 is_ip4, u8 is_add)
Definition: sctp.c:97
#define SCTP_DBG_OUTPUT(_fmt, _args...)
Definition: sctp_debug.h:52
static void sctp_timer_reset(sctp_connection_t *tc, u8 conn_idx, u8 timer_id)
Definition: sctp.h:611
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
static ip_protocol_info_t * ip_get_protocol_info(ip_main_t *im, u32 protocol)
Definition: ip.h:136
static sctp_connection_t * sctp_get_connection_from_transport(transport_connection_t *tconn)
Definition: sctp.h:652
format_function_t * format_header
Definition: ip.h:81
void sctp_initialize_timer_wheels(sctp_main_t *tm)
Definition: sctp.c:806
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
u8 sctp_sub_connection_add_ip4(vlib_main_t *vm, ip4_address_t *lcl_addr, ip4_address_t *rmt_addr)
Definition: sctp.c:295
u32 sctp_session_bind(u32 session_index, transport_endpoint_t *tep)
Definition: sctp.c:69
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:286
#define PREDICT_FALSE(x)
Definition: clib.h:111
struct _sctp_connection sctp_connection_t
static u64 sctp_set_time_now(u32 thread_index)
Definition: sctp.h:588
f64 seconds_per_clock
Definition: time.h:57
#define SCTP_RTO_INIT
Definition: sctp.h:456
static char * sctp_timer_to_string(u8 timer_id)
Definition: sctp.h:45
u8 * format_sctp_connection_id(u8 *s, va_list *args)
Definition: sctp.c:189
#define foreach_vlib_main(body)
Definition: threads.h:235
#define MAX_SCTP_CONNECTIONS
Definition: sctp.h:80
static clib_error_t * show_sctp_punt_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd_arg)
Definition: sctp.c:1003
u8 * format_sctp_state(u8 *s, va_list *args)
Definition: sctp.c:177
#define SCTP_TSTAMP_RESOLUTION
Time stamp resolution.
Definition: sctp.h:453
vlib_node_registration_t sctp4_output_node
(constructor) VLIB_REGISTER_NODE (sctp4_output_node)
static u8 sctp_next_avail_subconn(sctp_connection_t *sctp_conn)
Definition: sctp.h:870
static_always_inline u32 vlib_buffer_get_default_data_size(vlib_main_t *vm)
Definition: buffer_funcs.h:96
unformat_function_t unformat_pg_sctp_header
Definition: format.h:103
u8 sctp_sub_connection_del_ip4(ip4_address_t *lcl_addr, ip4_address_t *rmt_addr)
Definition: sctp.c:318
clib_error_t * ip_main_init(vlib_main_t *vm)
Definition: ip_init.c:45
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:212
format_function_t format_ip6_address
Definition: format.h:93
vlib_main_t * vm
Definition: buffer.c:312
ip_main_t ip_main
Definition: ip_init.c:42
u8 * format_sctp_connection(u8 *s, va_list *args)
Definition: sctp.c:233
const char * sctp_fsm_states[]
Definition: sctp.c:170
transport_connection_t * sctp_session_get_listener(u32 listener_index)
Definition: sctp.c:684
virtual circuit service
void sctp_api_reference(void)
Definition: sctp_api.c:137
#define IP_PROTOCOL_SCTP
Definition: sctp.h:343
#define clib_warning(format, args...)
Definition: error.h:59
u32 sctp_push_header(transport_connection_t *tconn, vlib_buffer_t *b)
Definition: sctp_output.c:1418
struct _transport_connection transport_connection_t
vlib_node_t * vlib_get_node_by_name(vlib_main_t *vm, u8 *name)
Definition: node.c:45
#define pool_init_fixed(pool, max_elts)
initialize a fixed-size, preallocated pool
Definition: pool.h:86
void transport_endpoint_cleanup(u8 proto, ip46_address_t *lcl_ip, u16 port)
Definition: transport.c:341
void transport_register_protocol(transport_proto_t transport_proto, const transport_proto_vft_t *vft, fib_protocol_t fib_proto, u32 output_node)
Register transport virtual function table.
Definition: transport.c:248
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:155
static sctp_connection_t * sctp_half_open_connection_get(u32 conn_index)
Definition: sctp.h:560
#define pool_put_index(p, i)
Free pool element with given index.
Definition: pool.h:311
#define ASSERT(truth)
u16 sctp_snd_space(sctp_connection_t *sctp_conn)
Definition: sctp.c:638
#define SCTP_PRIMARY_PATH_IDX
Definition: sctp.h:81
u8 is_add
Definition: ipsec_gre.api:36
format_function_t format_sctp_header
Definition: format.h:101
void sctp_connection_cleanup(sctp_connection_t *sctp_conn)
Cleans up connection state.
Definition: sctp.c:528
u8 ip_is_zero(ip46_address_t *ip46_address, u8 is_ip4)
Definition: ip.c:20
void sctp_update_time(f64 now, u8 thread_index)
Definition: sctp.c:935
static sctp_connection_t * sctp_connection_get(u32 conn_index, u32 thread_index)
Definition: sctp.h:757
void sctp_send_cookie_echo(sctp_connection_t *sctp_conn)
Definition: sctp_output.c:569
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
u64 uword
Definition: types.h:112
unreliable transport protos
clib_error_t * sctp_init(vlib_main_t *vm)
Definition: sctp.c:968
u8 sctp_sub_connection_del_ip6(ip6_address_t *lcl_addr, ip6_address_t *rmt_addr)
Definition: sctp.c:375
void sctp_send_heartbeat(sctp_connection_t *sctp_conn)
Definition: sctp_output.c:1203
static u32 random_u32(u32 *seed)
32-bit random number generator
Definition: random.h:69
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
void sctp_expired_timers_cb(u32 conn_index, u32 timer_id)
Definition: sctp.c:717
static vlib_thread_main_t * vlib_get_thread_main()
Definition: global_funcs.h:32
#define SCTP_HB_INTERVAL
Definition: sctp.h:466
void sctp_connection_close(sctp_connection_t *sctp_conn)
Definition: sctp.c:580
static sctp_connection_t * sctp_sub_connection_add(u8 thread_index)
Definition: sctp.c:277
static sctp_connection_t * sctp_listener_get(u32 tli)
Definition: sctp.h:749
#define SCTP_TIMER_HANDLE_INVALID
Definition: sctp.h:42
int transport_alloc_local_port(u8 proto, ip46_address_t *ip)
Allocate local port and add if successful add entry to local endpoint table to mark the pair as used...
Definition: transport.c:375
static clib_error_t * ip6_lookup_init(vlib_main_t *vm)
Definition: ip6_forward.c:2684
vlib_node_registration_t sctp4_input_node
(constructor) VLIB_REGISTER_NODE (sctp4_input_node)
Definition: sctp_input.c:2277
static u8 sctp_data_subconn_select(sctp_connection_t *sctp_conn)
Definition: sctp.h:768
void sctp_send_shutdown(sctp_connection_t *sctp_conn)
Definition: sctp_output.c:1019
sctp_connection_t * sctp_half_open_connection_new(u8 thread_index)
Definition: sctp.c:444
u8 sctp_sub_connection_add_ip6(vlib_main_t *vm, ip6_address_t *lcl_addr, ip6_address_t *rmt_addr)
Definition: sctp.c:352
#define SCTP_CONN_RECOVERY
Definition: sctp.h:471
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:725
static_always_inline void clib_spinlock_lock_if_init(clib_spinlock_t *p)
Definition: lock.h:82
void sctp_session_close(u32 conn_index, u32 thread_index)
Definition: sctp.c:591
u32 sctp_session_send_space(transport_connection_t *trans_conn)
Compute TX window session is allowed to fill.
Definition: sctp.c:663
static void sctp_expired_timers_dispatch(u32 *expired_timers)
Definition: sctp.c:787
static const transport_proto_vft_t sctp_proto
Definition: sctp.c:944
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170