+vnet_api_error_t
+bfd_udp_set_echo_source (u32 sw_if_index)
+{
+ vnet_sw_interface_t *sw_if =
+ vnet_get_sw_interface_or_null (bfd_udp_main.vnet_main, sw_if_index);
+ if (sw_if)
+ {
+ bfd_udp_main.echo_source_sw_if_index = sw_if_index;
+ bfd_udp_main.echo_source_is_set = 1;
+ return 0;
+ }
+ return VNET_API_ERROR_BFD_ENOENT;
+}
+
+vnet_api_error_t
+bfd_udp_del_echo_source (u32 sw_if_index)
+{
+ bfd_udp_main.echo_source_sw_if_index = ~0;
+ bfd_udp_main.echo_source_is_set = 0;
+ return 0;
+}
+
+int
+bfd_udp_is_echo_available (bfd_transport_e transport)
+{
+ if (!bfd_udp_main.echo_source_is_set)
+ {
+ BFD_DBG ("UDP echo source not set - echo not available");
+ return 0;
+ }
+ /*
+ * for the echo to work, we need a loopback interface with at least one
+ * address with netmask length at most 31 (ip4) or 127 (ip6) so that we can
+ * pick an unused address from that subnet
+ */
+ vnet_sw_interface_t *sw_if =
+ vnet_get_sw_interface_or_null (bfd_udp_main.vnet_main,
+ bfd_udp_main.echo_source_sw_if_index);
+ if (sw_if && sw_if->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+ {
+ if (BFD_TRANSPORT_UDP4 == transport)
+ {
+ ip4_main_t *im = &ip4_main;
+ ip_interface_address_t *ia = NULL;
+ /* *INDENT-OFF* */
+ foreach_ip_interface_address (&im->lookup_main, ia,
+ bfd_udp_main.echo_source_sw_if_index,
+ 0 /* honor unnumbered */, ({
+ if (ia->address_length <= 31)
+ {
+ return 1;
+ }
+ }));
+ /* *INDENT-ON* */
+ }
+ else if (BFD_TRANSPORT_UDP6 == transport)
+ {
+ ip6_main_t *im = &ip6_main;
+ ip_interface_address_t *ia = NULL;
+ /* *INDENT-OFF* */
+ foreach_ip_interface_address (&im->lookup_main, ia,
+ bfd_udp_main.echo_source_sw_if_index,
+ 0 /* honor unnumbered */, ({
+ if (ia->address_length <= 127)
+ {
+ return 1;
+ }
+ }));
+ /* *INDENT-ON* */
+ }
+ }
+ BFD_DBG ("No usable IP address for UDP echo - echo not available");
+ return 0;
+}
+
+static u16
+bfd_udp_bs_idx_to_sport (u32 bs_idx)
+{
+ /* The source port MUST be in the range 49152 through 65535. The same UDP
+ * source port number MUST be used for all BFD Control packets associated
+ * with a particular session. The source port number SHOULD be unique among
+ * all BFD sessions on the system. If more than 16384 BFD sessions are
+ * simultaneously active, UDP source port numbers MAY be reused on
+ * multiple sessions, but the number of distinct uses of the same UDP
+ * source port number SHOULD be minimized.
+ */
+ return 49152 + bs_idx % (65535 - 49152 + 1);
+}
+
+int
+bfd_udp_get_echo_src_ip4 (ip4_address_t * addr)
+{
+ if (!bfd_udp_main.echo_source_is_set)
+ {
+ BFD_ERR ("cannot find ip4 address, echo source not set");
+ return 0;
+ }
+ ip_interface_address_t *ia = NULL;
+ ip4_main_t *im = &ip4_main;
+
+ /* *INDENT-OFF* */
+ foreach_ip_interface_address (
+ &im->lookup_main, ia, bfd_udp_main.echo_source_sw_if_index,
+ 0 /* honor unnumbered */, ({
+ ip4_address_t *x =
+ ip_interface_address_get_address (&im->lookup_main, ia);
+ if (ia->address_length <= 31)
+ {
+ addr->as_u32 = clib_host_to_net_u32 (x->as_u32);
+ /*
+ * flip the last bit to get a different address, might be network,
+ * we don't care ...
+ */
+ addr->as_u32 ^= 1;
+ addr->as_u32 = clib_net_to_host_u32 (addr->as_u32);
+ return 1;
+ }
+ }));
+ /* *INDENT-ON* */
+ BFD_ERR ("cannot find ip4 address, no usable address found");
+ return 0;
+}
+
+int
+bfd_udp_get_echo_src_ip6 (ip6_address_t * addr)
+{
+ if (!bfd_udp_main.echo_source_is_set)
+ {
+ BFD_ERR ("cannot find ip6 address, echo source not set");
+ return 0;
+ }
+ ip_interface_address_t *ia = NULL;
+ ip6_main_t *im = &ip6_main;
+
+ /* *INDENT-OFF* */
+ foreach_ip_interface_address (
+ &im->lookup_main, ia, bfd_udp_main.echo_source_sw_if_index,
+ 0 /* honor unnumbered */, ({
+ ip6_address_t *x =
+ ip_interface_address_get_address (&im->lookup_main, ia);
+ if (ia->address_length <= 127)
+ {
+ *addr = *x;
+ addr->as_u8[15] ^= 1; /* flip the last bit of the address */
+ return 1;
+ }
+ }));
+ /* *INDENT-ON* */
+ BFD_ERR ("cannot find ip6 address, no usable address found");
+ return 0;
+}
+