diff options
| -rw-r--r-- | bgpd/bgp_rpki.c | 230 | ||||
| -rw-r--r-- | bgpd/bgp_rpki.h | 2 | 
2 files changed, 228 insertions, 4 deletions
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 458677b8ad..83ba5419d9 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 : <HOST>:<PORT> + * ssh identifier : <user>@<HOST>:<PORT> + */ +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 <SOCKET> */ +	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 de28715d1b..4f2f87d2ef 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,  | 
