From 80edb6758fdc7b81032f3acc7f01930cd8d1fe80 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Thu, 10 Jan 2019 17:13:32 -0200 Subject: [PATCH] bfdd: use zebra to learn about network interfaces Don't use system calls to search for and get interface information, instead use the FRR provided API to learn and cache it. Signed-off-by: Rafael Zalamena --- bfdd/bfd.c | 33 +++++++++++++++++++++++++------ bfdd/bfd.h | 3 ++- bfdd/bfd_packet.c | 49 ++++++++++++++++++++++++++++++++++------------ bfdd/ptm_adapter.c | 49 +++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 112 insertions(+), 22 deletions(-) diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 814366f320..e86659b06d 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -540,6 +540,7 @@ static void bfd_session_free(struct bfd_session *bs) struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) { struct bfd_session *bfd, *l_bfd; + struct interface *ifp = NULL; int psock; /* check to see if this needs a new session */ @@ -552,6 +553,24 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) return NULL; } + /* + * No session found, we have to allocate a new one. + * + * First a few critical checks: + * + * * Check that the specified interface exists. + * * Attempt to create the UDP socket (might fail if we exceed + * our limits). + */ + if (bpc->bpc_has_localif) { + ifp = if_lookup_by_name(bpc->bpc_localif, VRF_DEFAULT); + if (ifp == NULL) { + log_error( + "session-new: specified interface doesn't exists."); + return NULL; + } + } + /* * Get socket for transmitting control packets. Note that if we * could use the destination port (3784) for the source @@ -574,19 +593,21 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) return NULL; } - if (bpc->bpc_has_localif && !bpc->bpc_mhop) { - bfd->ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif); - ptm_bfd_fetch_local_mac(bpc->bpc_localif, bfd->local_mac); - } + if (bpc->bpc_has_localif && !bpc->bpc_mhop) + bfd->ifp = ifp; if (bpc->bpc_ipv4 == false) { BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6); /* Set the IPv6 scope id for link-local addresses. */ if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_local.sa_sin6.sin6_addr)) - bpc->bpc_local.sa_sin6.sin6_scope_id = bfd->ifindex; + bpc->bpc_local.sa_sin6.sin6_scope_id = + bfd->ifp != NULL ? bfd->ifp->ifindex + : IFINDEX_INTERNAL; if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_peer.sa_sin6.sin6_addr)) - bpc->bpc_peer.sa_sin6.sin6_scope_id = bfd->ifindex; + bpc->bpc_peer.sa_sin6.sin6_scope_id = + bfd->ifp != NULL ? bfd->ifp->ifindex + : IFINDEX_INTERNAL; } /* Initialize the session */ diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 3a58a8d53c..24d5632b36 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -31,6 +31,7 @@ #include "lib/libfrr.h" #include "lib/qobj.h" #include "lib/queue.h" +#include "lib/vrf.h" #include "bfdctl.h" @@ -237,7 +238,7 @@ struct bfd_session { struct sockaddr_any local_address; struct sockaddr_any local_ip; - int ifindex; + struct interface *ifp; uint8_t local_mac[ETHERNET_ADDRESS_LENGTH]; uint8_t peer_mac[ETHERNET_ADDRESS_LENGTH]; diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 606f739b46..9cfd7e866f 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -241,6 +241,7 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, size_t vrfnamelen, struct sockaddr_any *local, struct sockaddr_any *peer) { + struct interface *ifp; struct cmsghdr *cm; int ifindex; ssize_t mlen; @@ -307,8 +308,14 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN local->sa_sin.sin_len = sizeof(local->sa_sin); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ - fetch_portname_from_ifindex(pi->ipi_ifindex, port, - portlen); + + ifp = if_lookup_by_index(pi->ipi_ifindex, VRF_DEFAULT); + if (ifp == NULL) + break; + + if (strlcpy(port, ifp->name, portlen) >= portlen) + log_debug( + "ipv4-recv: interface name truncated"); break; } #endif /* BFD_LINUX */ @@ -345,8 +352,15 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, /* OS agnostic way of getting interface name. */ if (port[0] == 0) { ifindex = getsockopt_ifindex(AF_INET, &msghdr); - if (ifindex > 0) - fetch_portname_from_ifindex(ifindex, port, portlen); + if (ifindex <= 0) + return mlen; + + ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); + if (ifp == NULL) + return mlen; + + if (strlcpy(port, ifp->name, portlen) >= portlen) + log_debug("ipv4-recv: interface name truncated"); } return mlen; @@ -357,6 +371,7 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, size_t vrfnamelen, struct sockaddr_any *local, struct sockaddr_any *peer) { + struct interface *ifp; struct cmsghdr *cm; struct in6_pktinfo *pi6 = NULL; int ifindex = 0; @@ -413,9 +428,16 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN local->sa_sin6.sin6_len = sizeof(local->sa_sin6); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ - fetch_portname_from_ifindex(pi6->ipi6_ifindex, - port, portlen); + ifindex = pi6->ipi6_ifindex; + ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); + if (ifp == NULL) + break; + + if (strlcpy(port, ifp->name, portlen) + >= portlen) + log_debug( + "ipv6-recv: interface name truncated"); } } } @@ -610,8 +632,8 @@ int bfd_recv_cb(struct thread *t) * If no interface was detected, save the interface where the * packet came in. */ - if (bfd->ifindex == 0) - bfd->ifindex = ptm_bfd_fetch_ifindex(port); + if (bfd->ifp == NULL) + bfd->ifp = if_lookup_by_name(port, VRF_DEFAULT); /* Log remote discriminator changes. */ if ((bfd->discrs.remote_discr != 0) @@ -1046,7 +1068,8 @@ int bp_peer_socket(struct bfd_peer_cfg *bpc) int bp_peer_socketv6(struct bfd_peer_cfg *bpc) { - int sd, pcount, ifindex; + struct interface *ifp; + int sd, pcount; struct sockaddr_in6 sin6; static int srcPort = BFD_SRCPORTINIT; @@ -1076,9 +1099,11 @@ int bp_peer_socketv6(struct bfd_peer_cfg *bpc) sin6.sin6_len = sizeof(sin6); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ sin6 = bpc->bpc_local.sa_sin6; - ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif); - if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) - sin6.sin6_scope_id = ifindex; + if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) { + ifp = if_lookup_by_name(bpc->bpc_localif, VRF_DEFAULT); + sin6.sin6_scope_id = + (ifp != NULL) ? ifp->ifindex : IFINDEX_INTERNAL; + } if (bpc->bpc_has_localif) { if (bp_bind_dev(sd, bpc->bpc_localif) != 0) { diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index f9c7c16fb1..a57167376a 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -189,7 +189,10 @@ int ptm_bfd_notify(struct bfd_session *bs) stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE); /* NOTE: Interface is a shortcut to avoid comparing source address. */ - stream_putl(msg, bs->ifindex); + if (bs->ifp != NULL) + stream_putl(msg, bs->ifp->ifindex); + else + stream_putl(msg, IFINDEX_INTERNAL); /* BFD destination prefix information. */ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) @@ -287,6 +290,7 @@ stream_failure: static int _ptm_msg_read(struct stream *msg, int command, struct bfd_peer_cfg *bpc, struct ptm_client **pc) { + struct interface *ifp; uint32_t pid; uint8_t ttl __attribute__((unused)); size_t ifnamelen; @@ -393,9 +397,19 @@ static int _ptm_msg_read(struct stream *msg, int command, */ if (bpc->bpc_ipv4 == false && IN6_IS_ADDR_LINKLOCAL( - &bpc->bpc_peer.sa_sin6.sin6_addr)) + &bpc->bpc_peer.sa_sin6.sin6_addr)) { + ifp = if_lookup_by_name_all_vrf( + bpc->bpc_localif); + if (ifp == NULL) { + log_error( + "ptm-read: interface %s doesn't exists", + bpc->bpc_localif); + return -1; + } + bpc->bpc_peer.sa_sin6.sin6_scope_id = - ptm_bfd_fetch_ifindex(bpc->bpc_localif); + ifp->ifindex; + } } } @@ -576,9 +590,34 @@ static void bfdd_zebra_connected(struct zclient *zc) stream_putl(msg, ZEBRA_BFD_DEST_REPLAY); stream_putw_at(msg, 0, stream_get_endp(msg)); + /* Ask for interfaces information. */ + zclient_create_header(msg, ZEBRA_INTERFACE_ADD, VRF_DEFAULT); + + /* Send requests. */ zclient_send_message(zclient); } +static int bfdd_interface_update(int cmd, struct zclient *zc, uint16_t len, + vrf_id_t vrfid) +{ + /* + * `zebra_interface_add_read` will handle the interface creation + * on `lib/if.c`. We'll use that data structure instead of + * rolling our own. + */ + if (cmd == ZEBRA_INTERFACE_ADD) { + zebra_interface_add_read(zc->ibuf, vrfid); + return 0; + } + + /* Update interface information. */ + zebra_interface_state_read(zc->ibuf, vrfid); + + /* TODO: stop all sessions using this interface. */ + + return 0; +} + void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) { zclient = zclient_new(master, &zclient_options_default); @@ -594,6 +633,10 @@ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) /* Send replay request on zebra connect. */ zclient->zebra_connected = bfdd_zebra_connected; + + /* Learn interfaces from zebra instead of the OS. */ + zclient->interface_add = bfdd_interface_update; + zclient->interface_delete = bfdd_interface_update; } void bfdd_zclient_stop(void) -- 2.39.5