+ vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
+ typedef struct
+ {
+ ip6_header_t ip6;
+ udp_header_t udp;
+ } ip6_udp_headers;
+ ip6_udp_headers *headers = NULL;
+ vlib_buffer_advance (b, -sizeof (*headers));
+ headers = vlib_buffer_get_current (b);
+ memset (headers, 0, sizeof (*headers));
+ headers->ip6.ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 (0x6 << 28);
+ headers->ip6.hop_limit = 255;
+ headers->ip6.protocol = IP_PROTOCOL_UDP;
+ headers->udp.src_port =
+ clib_host_to_net_u16 (bfd_udp_bs_idx_to_sport (bs->bs_idx));
+ if (is_echo)
+ {
+ int rv;
+ if (!(rv = bfd_udp_get_echo_src_ip6 (&headers->ip6.src_address)))
+ {
+ return rv;
+ }
+ clib_memcpy (&headers->ip6.dst_address, &key->local_addr.ip6,
+ sizeof (headers->ip6.dst_address));
+
+ headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd_echo6);
+ }
+ else
+ {
+ clib_memcpy (&headers->ip6.src_address, &key->local_addr.ip6,
+ sizeof (headers->ip6.src_address));
+ clib_memcpy (&headers->ip6.dst_address, &key->peer_addr.ip6,
+ sizeof (headers->ip6.dst_address));
+ headers->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_bfd6);
+ }
+
+ /* fix ip payload length and udp length */
+ const u16 udp_length =
+ vlib_buffer_length_in_chain (vm, b) - (sizeof (headers->ip6));
+ headers->udp.length = clib_host_to_net_u16 (udp_length);
+ headers->ip6.payload_length = headers->udp.length;
+
+ /* IPv6 UDP checksum is mandatory */
+ int bogus = 0;
+ headers->udp.checksum =
+ ip6_tcp_udp_icmp_compute_checksum (vm, b, &headers->ip6, &bogus);
+ ASSERT (bogus == 0);
+ if (headers->udp.checksum == 0)
+ {
+ headers->udp.checksum = 0xffff;
+ }
+ return 1;
+}
+
+static void
+bfd_create_frame_to_next_node (vlib_main_t * vm, u32 bi, u32 next_node)
+{
+ vlib_frame_t *f = vlib_get_frame_to_node (vm, next_node);
+ u32 *to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, next_node, f);
+}
+
+int
+bfd_udp_calc_next_node (const struct bfd_session_s *bs, u32 * next_node)
+{
+ const bfd_udp_session_t *bus = &bs->udp;
+ ip_adjacency_t *adj = adj_get (bus->adj_index);
+ switch (adj->lookup_next_index)
+ {
+ case IP_LOOKUP_NEXT_ARP:
+ switch (bs->transport)
+ {
+ case BFD_TRANSPORT_UDP4:
+ *next_node = bfd_udp_main.ip4_arp_idx;
+ return 1;
+ case BFD_TRANSPORT_UDP6:
+ *next_node = bfd_udp_main.ip6_ndp_idx;
+ return 1;
+ }
+ break;
+ case IP_LOOKUP_NEXT_REWRITE:
+ switch (bs->transport)
+ {
+ case BFD_TRANSPORT_UDP4:
+ *next_node = bfd_udp_main.ip4_rewrite_idx;
+ return 1;
+ case BFD_TRANSPORT_UDP6:
+ *next_node = bfd_udp_main.ip6_rewrite_idx;
+ return 1;
+ }
+ break;
+ default:
+ /* drop */
+ break;
+ }
+ return 0;
+}
+
+int
+bfd_transport_udp4 (vlib_main_t * vm, u32 bi, const struct bfd_session_s *bs)
+{
+ u32 next_node;
+ int rv = bfd_udp_calc_next_node (bs, &next_node);
+ if (rv)
+ {
+ bfd_create_frame_to_next_node (vm, bi, next_node);
+ }
+ return rv;
+}
+
+int
+bfd_transport_udp6 (vlib_main_t * vm, u32 bi, const struct bfd_session_s *bs)
+{
+ u32 next_node;
+ int rv = bfd_udp_calc_next_node (bs, &next_node);
+ if (rv)
+ {
+ bfd_create_frame_to_next_node (vm, bi, next_node);
+ }
+ return 1;