diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 458677b8ad1f..83ba5419d9f6 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -44,6 +44,7 @@ #include "bgpd/bgp_rpki_clippy.c" +DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_TEMP, "BGP RPKI Intermediate Buffer"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib"); @@ -75,6 +76,7 @@ struct cache { } tr_config; struct rtr_socket *rtr_socket; uint8_t preference; + struct rpki_vrf *rpki_vrf; }; enum return_values { SUCCESS = 0, ERROR = -1 }; @@ -287,6 +289,99 @@ static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled) return 1; } +/* tcp identifier : : + * ssh identifier : @: + */ +static struct rpki_vrf *find_rpki_vrf_from_ident(const char *ident) +{ +#if defined(FOUND_SSH) + struct tr_ssh_config *ssh_config; +#endif + struct tr_tcp_config *tcp_config; + struct listnode *rpki_vrf_nnode; + unsigned int cache_port, port; + struct listnode *cache_node; + struct rpki_vrf *rpki_vrf; + struct cache *cache; + bool is_tcp = true; + size_t host_len; + char *endptr; + char *host; + char *ptr; + char *buf; + + /* extract the */ + ptr = strrchr(ident, ':'); + if (!ptr) + return NULL; + + ptr++; + /* extract port */ + port = atoi(ptr); + if (port == 0) + /* not ours */ + return NULL; + + /* extract host */ + ptr--; + host_len = (size_t)(ptr - ident); + buf = XCALLOC(MTYPE_BGP_RPKI_TEMP, host_len + 1); + memcpy(buf, ident, host_len); + buf[host_len] = '\0'; + endptr = strrchr(buf, '@'); + + /* ssh session */ + if (endptr) { + host = XCALLOC(MTYPE_BGP_RPKI_TEMP, + (size_t)(buf + host_len - endptr) + 1); + memcpy(host, endptr + 1, (size_t)(buf + host_len - endptr) + 1); + is_tcp = false; + } else { + host = buf; + buf = NULL; + } + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) { + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, + cache)) { + if ((cache->type == TCP && !is_tcp) +#if defined(FOUND_SSH) + || (cache->type == SSH && is_tcp) +#endif + ) + continue; + + if (is_tcp) { + tcp_config = cache->tr_config.tcp_config; + cache_port = atoi(tcp_config->port); + if (cache_port != port) + continue; + if (strlen(tcp_config->host) != strlen(host)) + continue; + if (memcmp(tcp_config->host, host, host_len) == + 0) + break; + } +#if defined(FOUND_SSH) + else { + ssh_config = cache->tr_config.ssh_config; + if (port != ssh_config->port) + continue; + if (strmatch(ssh_config->host, host)) + break; + } +#endif + } + if (cache) + break; + } + if (host) + XFREE(MTYPE_BGP_RPKI_TEMP, host); + if (buf) + XFREE(MTYPE_BGP_RPKI_TEMP, buf); + return rpki_vrf; +} + static struct rpki_vrf *find_rpki_vrf(const char *vrfname) { struct listnode *rpki_vrf_nnode; @@ -608,8 +703,23 @@ static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)), { struct rpki_vrf *rpki_vrf; const char *msg; + const struct rtr_socket *rtr = rec.socket; + const char *ident; - rpki_vrf = find_rpki_vrf(NULL); + if (!rtr) { + msg = "could not find rtr_socket from cb_sync_rtr"; + goto err; + } + if (!rtr->tr_socket) { + msg = "could not find tr_socket from cb_sync_rtr"; + goto err; + } + ident = rtr->tr_socket->ident_fp(rtr->tr_socket->socket); + if (!ident) { + msg = "could not find ident from cb_sync_rtr"; + goto err; + } + rpki_vrf = find_rpki_vrf_from_ident(ident); if (!rpki_vrf) { msg = "could not find rpki_vrf"; goto err; @@ -1068,11 +1178,16 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, return RPKI_NOT_BEING_USED; } -static int add_cache(struct cache *cache, struct rpki_vrf *rpki_vrf) +static int add_cache(struct cache *cache) { uint8_t preference = cache->preference; struct rtr_mgr_group group; struct list *cache_list; + struct rpki_vrf *rpki_vrf; + + rpki_vrf = cache->rpki_vrf; + if (!rpki_vrf) + return ERROR; group.preference = preference; group.sockets_len = 1; @@ -1097,6 +1212,105 @@ static int add_cache(struct cache *cache, struct rpki_vrf *rpki_vrf) return SUCCESS; } +static int rpki_create_socket(void *_cache) +{ + struct timeval prev_snd_tmout, prev_rcv_tmout, timeout; + struct cache *cache = (struct cache *)_cache; + struct rpki_vrf *rpki_vrf = cache->rpki_vrf; + struct tr_tcp_config *tcp_config; + struct addrinfo *res = NULL; + struct addrinfo hints = {}; + socklen_t optlen; + char *host, *port; + struct vrf *vrf; + int cancel_state; + int socket; + int ret; +#if defined(FOUND_SSH) + struct tr_ssh_config *ssh_config; + char s_port[10]; +#endif + + if (!cache) + return -1; + + if (rpki_vrf->vrfname == NULL) + vrf = vrf_lookup_by_id(VRF_DEFAULT); + else + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) + return -1; + + if (!CHECK_FLAG(vrf->status, VRF_ACTIVE) || vrf->vrf_id == VRF_UNKNOWN) + return -1; + + if (cache->type == TCP) { + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + tcp_config = cache->tr_config.tcp_config; + host = tcp_config->host; + port = tcp_config->port; + } +#if defined(FOUND_SSH) + else { + ssh_config = cache->tr_config.ssh_config; + host = ssh_config->host; + snprintf(s_port, sizeof(s_port), "%hu", ssh_config->port); + port = s_port; + + hints.ai_flags |= AI_NUMERICHOST; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + } +#endif + + frr_with_privs (&bgpd_privs) { + ret = vrf_getaddrinfo(host, port, &hints, &res, vrf->vrf_id); + } + if (ret != 0) + return -1; + + frr_with_privs (&bgpd_privs) { + socket = vrf_socket(res->ai_family, res->ai_socktype, + res->ai_protocol, vrf->vrf_id, NULL); + } + if (socket < 0) + return -1; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancel_state); + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + optlen = sizeof(prev_rcv_tmout); + getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &prev_rcv_tmout, &optlen); + getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &prev_snd_tmout, &optlen); + + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + if (connect(socket, res->ai_addr, res->ai_addrlen) == -1) { + if (res) + freeaddrinfo(res); + close(socket); + pthread_setcancelstate(cancel_state, NULL); + return -1; + } + + if (res) + freeaddrinfo(res); + pthread_setcancelstate(cancel_state, NULL); + + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &prev_rcv_tmout, + sizeof(prev_rcv_tmout)); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &prev_snd_tmout, + sizeof(prev_snd_tmout)); + + return socket; +} + static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host, const char *port, const uint8_t preference, const char *bindaddr) @@ -1116,16 +1330,20 @@ static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host, else tcp_config->bindaddr = NULL; + tcp_config->data = cache; + tcp_config->new_socket = rpki_create_socket; rtr_socket = create_rtr_socket(tr_socket); + cache->rpki_vrf = rpki_vrf; cache->type = TCP; cache->tr_socket = tr_socket; cache->tr_config.tcp_config = tcp_config; cache->rtr_socket = rtr_socket; cache->preference = preference; - int ret = add_cache(cache, rpki_vrf); + int ret = add_cache(cache); if (ret != SUCCESS) { + tcp_config->data = NULL; free_cache(cache); } return ret; @@ -1152,6 +1370,8 @@ static int add_ssh_cache(struct rpki_vrf *rpki_vrf, const char *host, ssh_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr); else ssh_config->bindaddr = NULL; + ssh_config->data = cache; + ssh_config->new_socket = rpki_create_socket; ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username); ssh_config->client_privkey_path = @@ -1161,14 +1381,16 @@ static int add_ssh_cache(struct rpki_vrf *rpki_vrf, const char *host, rtr_socket = create_rtr_socket(tr_socket); + cache->rpki_vrf = rpki_vrf; cache->type = SSH; cache->tr_socket = tr_socket; cache->tr_config.ssh_config = ssh_config; cache->rtr_socket = rtr_socket; cache->preference = preference; - int ret = add_cache(cache, rpki_vrf); + int ret = add_cache(cache); if (ret != SUCCESS) { + ssh_config->data = NULL; free_cache(cache); } diff --git a/bgpd/bgp_rpki.h b/bgpd/bgp_rpki.h index de28715d1b16..4f2f87d2efb2 100644 --- a/bgpd/bgp_rpki.h +++ b/bgpd/bgp_rpki.h @@ -8,6 +8,8 @@ #ifndef __BGP_RPKI_H__ #define __BGP_RPKI_H__ +extern struct zebra_privs_t bgpd_privs; + enum rpki_states { RPKI_NOT_BEING_USED, RPKI_VALID,