Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/libknet/links.c b/libknet/links.c
index 010aeb69..6c75c359 100644
--- a/libknet/links.c
+++ b/libknet/links.c
@@ -1,1092 +1,1188 @@
/*
* Copyright (C) 2012-2019 Red Hat, Inc. All rights reserved.
*
* Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
* Federico Simoncelli <fsimon@kronosnet.org>
*
* This software licensed under GPL-2.0+, LGPL-2.0+
*/
#include "config.h"
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <pthread.h>
#include "internals.h"
#include "logging.h"
#include "links.h"
#include "transports.h"
#include "host.h"
#include "threads_common.h"
+#include "links_acl.h"
+
+static void _link_del_all_acl(knet_handle_t knet_h, int sock)
+{
+ check_rmall(&knet_h->knet_transport_fd_tracker[sock].match_entry);
+}
+
+static int _link_add_default_acl(knet_handle_t knet_h, struct knet_link *kh_link)
+{
+ int err = -1;
+
+ switch(transport_get_proto(knet_h, kh_link->transport_type)) {
+ case LOOPBACK:
+ /*
+ * loopback does not require access lists
+ */
+ err = 0;
+ break;
+ case IP_PROTO:
+ err = ipcheck_addip(&knet_h->knet_transport_fd_tracker[kh_link->outsock].match_entry,
+ &kh_link->dst_addr, &kh_link->dst_addr, CHECK_TYPE_ADDRESS, CHECK_ACCEPT);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+static int _link_rm_default_acl(knet_handle_t knet_h, struct knet_link *kh_link)
+{
+ int err = -1;
+
+ switch(transport_get_proto(knet_h, kh_link->transport_type)) {
+ case LOOPBACK:
+ /*
+ * loopback does not require access lists
+ */
+ err = 0;
+ break;
+ case IP_PROTO:
+ err = ipcheck_rmip(&knet_h->knet_transport_fd_tracker[kh_link->outsock].match_entry,
+ &kh_link->dst_addr, &kh_link->dst_addr, CHECK_TYPE_ADDRESS, CHECK_ACCEPT);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
int _link_updown(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int enabled, unsigned int connected)
{
struct knet_link *link = &knet_h->host_index[host_id]->link[link_id];
if ((link->status.enabled == enabled) &&
(link->status.connected == connected))
return 0;
link->status.enabled = enabled;
link->status.connected = connected;
_host_dstcache_update_async(knet_h, knet_h->host_index[host_id]);
if ((link->status.dynconnected) &&
(!link->status.connected))
link->status.dynconnected = 0;
if (connected) {
time(&link->status.stats.last_up_times[link->status.stats.last_up_time_index]);
link->status.stats.up_count++;
if (++link->status.stats.last_up_time_index > MAX_LINK_EVENTS) {
link->status.stats.last_up_time_index = 0;
}
} else {
time(&link->status.stats.last_down_times[link->status.stats.last_down_time_index]);
link->status.stats.down_count++;
if (++link->status.stats.last_down_time_index > MAX_LINK_EVENTS) {
link->status.stats.last_down_time_index = 0;
}
}
return 0;
}
void _link_clear_stats(knet_handle_t knet_h)
{
struct knet_host *host;
struct knet_link *link;
uint32_t host_id;
uint8_t link_id;
for (host_id = 0; host_id < KNET_MAX_HOST; host_id++) {
host = knet_h->host_index[host_id];
if (!host) {
continue;
}
for (link_id = 0; link_id < KNET_MAX_LINK; link_id++) {
link = &host->link[link_id];
memset(&link->status.stats, 0, sizeof(struct knet_link_stats));
}
}
}
int knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t transport,
struct sockaddr_storage *src_addr,
struct sockaddr_storage *dst_addr,
uint64_t flags)
{
int savederrno = 0, err = 0, i;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!src_addr) {
errno = EINVAL;
return -1;
}
if (dst_addr && (src_addr->ss_family != dst_addr->ss_family)) {
log_err(knet_h, KNET_SUB_LINK, "Source address family does not match destination address family");
errno = EINVAL;
return -1;
}
if (transport >= KNET_MAX_TRANSPORTS) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
if (transport == KNET_TRANSPORT_LOOPBACK && knet_h->host_id != host_id) {
log_err(knet_h, KNET_SUB_LINK, "Cannot create loopback link to remote node");
err = -1;
savederrno = EINVAL;
goto exit_unlock;
}
if (knet_h->host_id == host_id && knet_h->has_loop_link) {
log_err(knet_h, KNET_SUB_LINK, "Cannot create more than 1 link when loopback is active");
err = -1;
savederrno = EINVAL;
goto exit_unlock;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
if (transport == KNET_TRANSPORT_LOOPBACK && knet_h->host_id == host_id) {
for (i=0; i<KNET_MAX_LINK; i++) {
if (host->link[i].configured) {
log_err(knet_h, KNET_SUB_LINK, "Cannot add loopback link when other links are already configured.");
err = -1;
savederrno = EINVAL;
goto exit_unlock;
}
}
}
link = &host->link[link_id];
if (link->configured != 0) {
err =-1;
savederrno = EBUSY;
log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is currently configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
if (link->status.enabled != 0) {
err =-1;
savederrno = EBUSY;
log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is currently in use: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
memmove(&link->src_addr, src_addr, sizeof(struct sockaddr_storage));
err = knet_addrtostr(src_addr, sizeof(struct sockaddr_storage),
link->status.src_ipaddr, KNET_MAX_HOST_LEN,
link->status.src_port, KNET_MAX_PORT_LEN);
if (err) {
if (err == EAI_SYSTEM) {
savederrno = errno;
log_warn(knet_h, KNET_SUB_LINK,
"Unable to resolve host: %u link: %u source addr/port: %s",
host_id, link_id, strerror(savederrno));
} else {
savederrno = EINVAL;
log_warn(knet_h, KNET_SUB_LINK,
"Unable to resolve host: %u link: %u source addr/port: %s",
host_id, link_id, gai_strerror(err));
}
err = -1;
goto exit_unlock;
}
if (!dst_addr) {
link->dynamic = KNET_LINK_DYNIP;
} else {
link->dynamic = KNET_LINK_STATIC;
memmove(&link->dst_addr, dst_addr, sizeof(struct sockaddr_storage));
err = knet_addrtostr(dst_addr, sizeof(struct sockaddr_storage),
link->status.dst_ipaddr, KNET_MAX_HOST_LEN,
link->status.dst_port, KNET_MAX_PORT_LEN);
if (err) {
if (err == EAI_SYSTEM) {
savederrno = errno;
log_warn(knet_h, KNET_SUB_LINK,
"Unable to resolve host: %u link: %u destination addr/port: %s",
host_id, link_id, strerror(savederrno));
} else {
savederrno = EINVAL;
log_warn(knet_h, KNET_SUB_LINK,
"Unable to resolve host: %u link: %u destination addr/port: %s",
host_id, link_id, gai_strerror(err));
}
err = -1;
goto exit_unlock;
}
}
link->pong_count = KNET_LINK_DEFAULT_PONG_COUNT;
link->has_valid_mtu = 0;
link->ping_interval = KNET_LINK_DEFAULT_PING_INTERVAL * 1000; /* microseconds */
link->pong_timeout = KNET_LINK_DEFAULT_PING_TIMEOUT * 1000; /* microseconds */
link->pong_timeout_backoff = KNET_LINK_PONG_TIMEOUT_BACKOFF;
link->pong_timeout_adj = link->pong_timeout * link->pong_timeout_backoff; /* microseconds */
link->latency_fix = KNET_LINK_DEFAULT_PING_PRECISION;
link->latency_exp = KNET_LINK_DEFAULT_PING_PRECISION - \
((link->ping_interval * KNET_LINK_DEFAULT_PING_PRECISION) / 8000000);
link->flags = flags;
if (transport_link_set_config(knet_h, link, transport) < 0) {
savederrno = errno;
err = -1;
goto exit_unlock;
}
+
+ /*
+ * we can only configure default access lists if we know both endpoints
+ */
+ if (link->dynamic == KNET_LINK_STATIC) {
+ log_debug(knet_h, KNET_SUB_LINK, "Configuring default access lists for host: %u link: %u",
+ host_id, link_id);
+ if (_link_add_default_acl(knet_h, link) < 0) {
+ log_warn(knet_h, KNET_SUB_LINK, "Failed to configure default access lists for host: %u link: %u", host_id, link_id);
+ savederrno = errno;
+ err = -1;
+ goto exit_unlock;
+ }
+ }
+
link->configured = 1;
log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u is configured",
host_id, link_id);
if (transport == KNET_TRANSPORT_LOOPBACK) {
knet_h->has_loop_link = 1;
knet_h->loop_link = link_id;
host->status.reachable = 1;
link->status.mtu = KNET_PMTUD_SIZE_V6;
} else {
link->status.mtu = KNET_PMTUD_MIN_MTU_V4 - KNET_HEADER_ALL_SIZE - knet_h->sec_header_size;
link->has_valid_mtu = 1;
}
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *transport,
struct sockaddr_storage *src_addr,
struct sockaddr_storage *dst_addr,
uint8_t *dynamic,
uint64_t *flags)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!src_addr) {
errno = EINVAL;
return -1;
}
if (!dynamic) {
errno = EINVAL;
return -1;
}
if (!transport) {
errno = EINVAL;
return -1;
}
if (!flags) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
if ((link->dynamic == KNET_LINK_STATIC) && (!dst_addr)) {
savederrno = EINVAL;
err = -1;
goto exit_unlock;
}
memmove(src_addr, &link->src_addr, sizeof(struct sockaddr_storage));
*transport = link->transport_type;
*flags = link->flags;
if (link->dynamic == KNET_LINK_STATIC) {
*dynamic = 0;
memmove(dst_addr, &link->dst_addr, sizeof(struct sockaddr_storage));
} else {
*dynamic = 1;
}
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_clear_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
+ int sock;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (link->configured != 1) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
if (link->status.enabled != 0) {
err = -1;
savederrno = EBUSY;
log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is currently in use: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
+ /*
+ * remove well known access lists here.
+ * After the transport has done clearing the config,
+ * then we can remove any leftover access lists if the link
+ * is no longer in use.
+ */
+ if (link->dynamic == KNET_LINK_STATIC) {
+ if (_link_rm_default_acl(knet_h, link) < 0) {
+ err = -1;
+ savederrno = EBUSY;
+ log_err(knet_h, KNET_SUB_LINK, "Host %u link %u: unable to remove default access list",
+ host_id, link_id);
+ goto exit_unlock;
+ }
+ }
+
+ /*
+ * cache it for later as we don't know if the transport
+ * will clear link info during clear_config.
+ */
+ sock = link->outsock;
+
if ((transport_link_clear_config(knet_h, link) < 0) &&
(errno != EBUSY)) {
savederrno = errno;
err = -1;
goto exit_unlock;
}
+ /*
+ * remove any other access lists when the socket is no
+ * longer in use by the transport.
+ */
+ if (knet_h->knet_transport_fd_tracker[sock].transport == KNET_MAX_TRANSPORTS) {
+ _link_del_all_acl(knet_h, sock);
+ }
+
memset(link, 0, sizeof(struct knet_link));
link->link_id = link_id;
if (knet_h->has_loop_link && host_id == knet_h->host_id && link_id == knet_h->loop_link) {
knet_h->has_loop_link = 0;
if (host->active_link_entries == 0) {
host->status.reachable = 0;
}
}
log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u config has been wiped",
host_id, link_id);
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_set_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int enabled)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (enabled > 1) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
if (link->status.enabled == enabled) {
err = 0;
goto exit_unlock;
}
err = _link_updown(knet_h, host_id, link_id, enabled, link->status.connected);
savederrno = errno;
if (enabled) {
goto exit_unlock;
}
log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u is disabled",
host_id, link_id);
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int *enabled)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!enabled) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
*enabled = link->status.enabled;
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_set_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t pong_count)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (pong_count < 1) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
link->pong_count = pong_count;
log_debug(knet_h, KNET_SUB_LINK,
"host: %u link: %u pong count update: %u",
host_id, link_id, link->pong_count);
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *pong_count)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!pong_count) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
*pong_count = link->pong_count;
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_set_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
time_t interval, time_t timeout, unsigned int precision)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!interval) {
errno = EINVAL;
return -1;
}
if (!timeout) {
errno = ENOSYS;
return -1;
}
if (!precision) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
link->ping_interval = interval * 1000; /* microseconds */
link->pong_timeout = timeout * 1000; /* microseconds */
link->latency_fix = precision;
link->latency_exp = precision - \
((link->ping_interval * precision) / 8000000);
log_debug(knet_h, KNET_SUB_LINK,
"host: %u link: %u timeout update - interval: %llu timeout: %llu precision: %u",
host_id, link_id, link->ping_interval, link->pong_timeout, precision);
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
time_t *interval, time_t *timeout, unsigned int *precision)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!interval) {
errno = EINVAL;
return -1;
}
if (!timeout) {
errno = EINVAL;
return -1;
}
if (!precision) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
*interval = link->ping_interval / 1000; /* microseconds */
*timeout = link->pong_timeout / 1000;
*precision = link->latency_fix;
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_set_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t priority)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
uint8_t old_priority;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
old_priority = link->priority;
if (link->priority == priority) {
err = 0;
goto exit_unlock;
}
link->priority = priority;
if (_host_dstcache_update_sync(knet_h, host)) {
savederrno = errno;
log_debug(knet_h, KNET_SUB_LINK,
"Unable to update link priority (host: %u link: %u priority: %u): %s",
host_id, link_id, link->priority, strerror(savederrno));
link->priority = old_priority;
err = -1;
goto exit_unlock;
}
log_debug(knet_h, KNET_SUB_LINK,
"host: %u link: %u priority set to: %u",
host_id, link_id, link->priority);
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *priority)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!priority) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
*priority = link->priority;
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_link_list(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t *link_ids, size_t *link_ids_entries)
{
int savederrno = 0, err = 0, i, count = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (!link_ids) {
errno = EINVAL;
return -1;
}
if (!link_ids_entries) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
for (i = 0; i < KNET_MAX_LINK; i++) {
link = &host->link[i];
if (!link->configured) {
continue;
}
link_ids[count] = i;
count++;
}
*link_ids_entries = count;
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
int knet_link_get_status(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct knet_link_status *status, size_t struct_size)
{
int savederrno = 0, err = 0;
struct knet_host *host;
struct knet_link *link;
if (!knet_h) {
errno = EINVAL;
return -1;
}
if (link_id >= KNET_MAX_LINK) {
errno = EINVAL;
return -1;
}
if (!status) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
host = knet_h->host_index[host_id];
if (!host) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s",
host_id, strerror(savederrno));
goto exit_unlock;
}
link = &host->link[link_id];
if (!link->configured) {
err = -1;
savederrno = EINVAL;
log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s",
host_id, link_id, strerror(savederrno));
goto exit_unlock;
}
memmove(status, &link->status, struct_size);
/* Calculate totals - no point in doing this on-the-fly */
status->stats.rx_total_packets =
status->stats.rx_data_packets +
status->stats.rx_ping_packets +
status->stats.rx_pong_packets +
status->stats.rx_pmtu_packets;
status->stats.tx_total_packets =
status->stats.tx_data_packets +
status->stats.tx_ping_packets +
status->stats.tx_pong_packets +
status->stats.tx_pmtu_packets;
status->stats.rx_total_bytes =
status->stats.rx_data_bytes +
status->stats.rx_ping_bytes +
status->stats.rx_pong_bytes +
status->stats.rx_pmtu_bytes;
status->stats.tx_total_bytes =
status->stats.tx_data_bytes +
status->stats.tx_ping_bytes +
status->stats.tx_pong_bytes +
status->stats.tx_pmtu_bytes;
status->stats.tx_total_errors =
status->stats.tx_data_errors +
status->stats.tx_ping_errors +
status->stats.tx_pong_errors +
status->stats.tx_pmtu_errors;
status->stats.tx_total_retries =
status->stats.tx_data_retries +
status->stats.tx_ping_retries +
status->stats.tx_pong_retries +
status->stats.tx_pmtu_retries;
/* Tell the caller our full size in case they have an old version */
status->size = sizeof(struct knet_link_status);
exit_unlock:
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = err ? savederrno : 0;
return err;
}
diff --git a/libknet/links_acl.c b/libknet/links_acl.c
index fe84088a..2ad3e90e 100644
--- a/libknet/links_acl.c
+++ b/libknet/links_acl.c
@@ -1,210 +1,270 @@
/*
* Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved.
*
* Author: Christine Caulfield <ccaulfie@redhat.com>
*
* This software licensed under GPL-2.0+, LGPL-2.0+
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include "internals.h"
#include "logging.h"
#include "transports.h"
#include "links_acl.h"
/*
* IPv4 See if the address we have matches the current match entry
*/
static int ip_matches_v4(struct sockaddr_storage *checkip, struct acl_match_entry *match_entry)
{
struct sockaddr_in *ip_to_check;
struct sockaddr_in *match1;
struct sockaddr_in *match2;
ip_to_check = (struct sockaddr_in *)checkip;
match1 = (struct sockaddr_in *)&match_entry->addr1;
match2 = (struct sockaddr_in *)&match_entry->addr2;
switch(match_entry->type) {
case CHECK_TYPE_ADDRESS:
if (ip_to_check->sin_addr.s_addr == match1->sin_addr.s_addr)
return 1;
break;
case CHECK_TYPE_MASK:
if ((ip_to_check->sin_addr.s_addr & match2->sin_addr.s_addr) ==
match1->sin_addr.s_addr)
return 1;
break;
case CHECK_TYPE_RANGE:
if ((ntohl(ip_to_check->sin_addr.s_addr) >= ntohl(match1->sin_addr.s_addr)) &&
(ntohl(ip_to_check->sin_addr.s_addr) <= ntohl(match2->sin_addr.s_addr)))
return 1;
break;
}
return 0;
}
/*
* Compare two IPv6 addresses
*/
static int ip6addr_cmp(struct in6_addr *a, struct in6_addr *b)
{
uint64_t a_high, a_low;
uint64_t b_high, b_low;
/* Not sure why '&' doesn't work below, so I used '+' instead which is effectively
the same thing because the bottom 32bits are always zero and the value unsigned */
a_high = ((uint64_t)htonl(a->s6_addr32[0]) << 32) + (uint64_t)htonl(a->s6_addr32[1]);
a_low = ((uint64_t)htonl(a->s6_addr32[2]) << 32) + (uint64_t)htonl(a->s6_addr32[3]);
b_high = ((uint64_t)htonl(b->s6_addr32[0]) << 32) + (uint64_t)htonl(b->s6_addr32[1]);
b_low = ((uint64_t)htonl(b->s6_addr32[2]) << 32) + (uint64_t)htonl(b->s6_addr32[3]);
if (a_high > b_high)
return 1;
if (a_high < b_high)
return -1;
if (a_low > b_low)
return 1;
if (a_low < b_low)
return -1;
return 0;
}
/*
* IPv6 See if the address we have matches the current match entry
*/
static int ip_matches_v6(struct sockaddr_storage *checkip, struct acl_match_entry *match_entry)
{
struct sockaddr_in6 *ip_to_check;
struct sockaddr_in6 *match1;
struct sockaddr_in6 *match2;
int i;
ip_to_check = (struct sockaddr_in6 *)checkip;
match1 = (struct sockaddr_in6 *)&match_entry->addr1;
match2 = (struct sockaddr_in6 *)&match_entry->addr2;
switch(match_entry->type) {
case CHECK_TYPE_ADDRESS:
if (!memcmp(ip_to_check->sin6_addr.s6_addr32, match1->sin6_addr.s6_addr32, sizeof(struct in6_addr)))
return 1;
break;
case CHECK_TYPE_MASK:
/*
* Note that this little loop will quit early if there is a non-match so the
* comparison might look backwards compared to the IPv4 one
*/
for (i=sizeof(struct in6_addr)/4-1; i>=0; i--) {
if ((ip_to_check->sin6_addr.s6_addr32[i] & match2->sin6_addr.s6_addr32[i]) !=
match1->sin6_addr.s6_addr32[i])
return 0;
}
return 1;
case CHECK_TYPE_RANGE:
if ((ip6addr_cmp(&ip_to_check->sin6_addr, &match1->sin6_addr) >= 0) &&
(ip6addr_cmp(&ip_to_check->sin6_addr, &match2->sin6_addr) <= 0))
return 1;
break;
}
return 0;
}
int ipcheck_validate(struct acl_match_entry **match_entry_head, struct sockaddr_storage *checkip)
{
struct acl_match_entry *match_entry = *match_entry_head;
int (*match_fn)(struct sockaddr_storage *checkip, struct acl_match_entry *match_entry);
if (checkip->ss_family == AF_INET){
match_fn = ip_matches_v4;
} else {
match_fn = ip_matches_v6;
}
while (match_entry) {
if (match_fn(checkip, match_entry)) {
if (match_entry->acceptreject == CHECK_ACCEPT)
return 1;
else
return 0;
}
match_entry = match_entry->next;
}
return 0; /* Default reject */
}
/*
* Routines to manuipulate access lists
*/
-void ipcheck_clear(struct acl_match_entry **match_entry_head)
+void check_rmall(struct acl_match_entry **match_entry_head)
{
struct acl_match_entry *next_match_entry;
struct acl_match_entry *match_entry = *match_entry_head;
while (match_entry) {
next_match_entry = match_entry->next;
free(match_entry);
match_entry = next_match_entry;
}
*match_entry_head = NULL;
}
+static struct acl_match_entry *ipcheck_findmatch(struct acl_match_entry **match_entry_head,
+ struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
+ check_type_t type, check_acceptreject_t acceptreject)
+{
+ struct acl_match_entry *match_entry = *match_entry_head;
+
+ while (match_entry) {
+ if ((!memcmp(&match_entry->addr1, ip1, sizeof(struct sockaddr_storage))) &&
+ (!memcmp(&match_entry->addr2, ip2, sizeof(struct sockaddr_storage))) &&
+ (match_entry->type == type) &&
+ (match_entry->acceptreject == acceptreject)) {
+ return match_entry;
+ }
+ match_entry = match_entry->next;
+ }
+
+ return NULL;
+}
+
+int ipcheck_rmip(struct acl_match_entry **match_entry_head,
+ struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
+ check_type_t type, check_acceptreject_t acceptreject)
+{
+ struct acl_match_entry *next_match_entry = NULL;
+ struct acl_match_entry *rm_match_entry;
+ struct acl_match_entry *match_entry = *match_entry_head;
+
+ rm_match_entry = ipcheck_findmatch(match_entry_head, ip1, ip2, type, acceptreject);
+ if (!rm_match_entry) {
+ return -1;
+ }
+
+ while (match_entry) {
+ next_match_entry = match_entry->next;
+ /*
+ * we are removing the list head, be careful
+ */
+ if (rm_match_entry == match_entry) {
+ *match_entry_head = next_match_entry;
+ free(match_entry);
+ break;
+ }
+ /*
+ * the next one is the one we need to remove
+ */
+ if (rm_match_entry == next_match_entry) {
+ match_entry->next = next_match_entry->next;
+ free(next_match_entry);
+ break;
+ }
+ match_entry = next_match_entry;
+ }
+
+ return 0;
+}
+
int ipcheck_addip(struct acl_match_entry **match_entry_head,
struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
check_type_t type, check_acceptreject_t acceptreject)
{
struct acl_match_entry *new_match_entry;
struct acl_match_entry *match_entry = *match_entry_head;
if (!ip1) {
return -1;
}
if ((type != CHECK_TYPE_ADDRESS) && (!ip2)) {
return -1;
}
if (type == CHECK_TYPE_RANGE &&
(ip1->ss_family != ip2->ss_family))
return -1;
+ if (ipcheck_findmatch(match_entry_head, ip1, ip2, type, acceptreject) != NULL) {
+ return -1;
+ }
+
new_match_entry = malloc(sizeof(struct acl_match_entry));
if (!new_match_entry)
return -1;
memmove(&new_match_entry->addr1, ip1, sizeof(struct sockaddr_storage));
memmove(&new_match_entry->addr2, ip2, sizeof(struct sockaddr_storage));
new_match_entry->type = type;
new_match_entry->acceptreject = acceptreject;
new_match_entry->next = NULL;
if (match_entry) {
/* Find the end of the list */
/* is this OK, or should we use a doubly-linked list or bulk-load API call? */
while (match_entry->next) {
match_entry = match_entry->next;
}
match_entry->next = new_match_entry;
} else {
/*
* first entry in the list
*/
*match_entry_head = new_match_entry;
}
return 0;
}
diff --git a/libknet/links_acl.h b/libknet/links_acl.h
index 26b0f36a..f4713d6c 100644
--- a/libknet/links_acl.h
+++ b/libknet/links_acl.h
@@ -1,22 +1,25 @@
/*
* Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved.
*
* Author: Christine Caulfield <ccaulfie@redhat.com>
*
* This software licensed under GPL-2.0+, LGPL-2.0+
*/
#ifndef __KNET_LINKS_ACL_H__
#define __KNET_LINKS_ACL_H__
#include "internals.h"
int ipcheck_validate(struct acl_match_entry **match_entry_head, struct sockaddr_storage *checkip);
-void ipcheck_clear(struct acl_match_entry **match_entry_head);
-
int ipcheck_addip(struct acl_match_entry **match_entry_head,
struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
check_type_t type, check_acceptreject_t acceptreject);
+int ipcheck_rmip(struct acl_match_entry **match_entry_head,
+ struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
+ check_type_t type, check_acceptreject_t acceptreject);
+
+void check_rmall(struct acl_match_entry **match_entry_head);
#endif
diff --git a/libknet/tests/int_links_acl.c b/libknet/tests/int_links_acl.c
index 1e7f426d..129aabe9 100644
--- a/libknet/tests/int_links_acl.c
+++ b/libknet/tests/int_links_acl.c
@@ -1,209 +1,209 @@
/*
* Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved.
*
* Author: Christine Caulfield <ccaulfie@redhat.com>
*
* This software licensed under GPL-2.0+, LGPL-2.0+
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <malloc.h>
#include "internals.h"
#include "links_acl.h"
static struct acl_match_entry *match_entry_v4;
static struct acl_match_entry *match_entry_v6;
/* This is a test program .. remember! */
#define BUFLEN 1024
static int get_ipaddress(char *buf, struct sockaddr_storage *addr)
{
struct addrinfo *info;
struct addrinfo hints;
int res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
res = getaddrinfo(buf, NULL, &hints, &info);
if (!res) {
memmove(addr, info->ai_addr, info->ai_addrlen);
freeaddrinfo(info);
}
return res;
}
static int read_address(char *buf, struct sockaddr_storage *addr)
{
return get_ipaddress(buf, addr);
}
static int read_mask(char *buf, struct sockaddr_storage *addr, struct sockaddr_storage *addr2)
{
char tmpbuf[BUFLEN];
char *slash;
int ret;
slash = strchr(buf, '/');
if (!slash)
return 1;
strncpy(tmpbuf, buf, slash-buf);
tmpbuf[slash-buf] = '\0';
ret = get_ipaddress(tmpbuf, addr);
if (ret)
return ret;
ret = get_ipaddress(slash+1, addr2);
if (ret)
return ret;
return 0;
}
static int read_range(char *buf, struct sockaddr_storage *addr1, struct sockaddr_storage *addr2)
{
char tmpbuf[BUFLEN];
char *hyphen;
int ret;
hyphen = strchr(buf, '-');
if (!hyphen)
return 1;
strncpy(tmpbuf, buf, hyphen-buf);
tmpbuf[hyphen-buf] = '\0';
ret = get_ipaddress(tmpbuf, addr1);
if (ret)
return ret;
ret = get_ipaddress(hyphen+1, addr2);
if (ret)
return ret;
return 0;
}
static int load_file(void)
{
FILE *filterfile;
char filebuf[BUFLEN];
int line = 0;
int ret;
check_type_t type;
check_acceptreject_t acceptreject;
struct sockaddr_storage addr1;
struct sockaddr_storage addr2;
- ipcheck_clear(&match_entry_v4);
- ipcheck_clear(&match_entry_v6);
+ check_rmall(&match_entry_v4);
+ check_rmall(&match_entry_v6);
filterfile = fopen("int_links_acl.txt", "r");
if (!filterfile) {
fprintf(stderr, "Cannot open int_links_acl.txt\n");
return 1;
}
while (fgets(filebuf, sizeof(filebuf), filterfile)) {
filebuf[strlen(filebuf)-1] = '\0'; /* remove trailing LF */
line++;
/*
* First char is A (accept) or R (Reject)
*/
switch(filebuf[0] & 0x5F) {
case 'A':
acceptreject = CHECK_ACCEPT;
break;
case 'R':
acceptreject = CHECK_REJECT;
break;
default:
fprintf(stderr, "Unknown record type on line %d: %s\n", line, filebuf);
goto next_record;
}
/*
* Second char is the filter type:
* A Address
* M Mask
* R Range
*/
switch(filebuf[1] & 0x5F) {
case 'A':
type = CHECK_TYPE_ADDRESS;
ret = read_address(filebuf+2, &addr1);
break;
case 'M':
type = CHECK_TYPE_MASK;
ret = read_mask(filebuf+2, &addr1, &addr2);
break;
case 'R':
type = CHECK_TYPE_RANGE;
ret = read_range(filebuf+2, &addr1, &addr2);
break;
default:
fprintf(stderr, "Unknown filter type on line %d: %s\n", line, filebuf);
goto next_record;
break;
}
if (ret) {
fprintf(stderr, "Failed to parse address on line %d: %s\n", line, filebuf);
}
else {
if (addr1.ss_family == AF_INET) {
ipcheck_addip(&match_entry_v4, &addr1, &addr2, type, acceptreject);
} else {
ipcheck_addip(&match_entry_v6, &addr1, &addr2, type, acceptreject);
}
}
next_record: {} /* empty statement to mollify the compiler */
}
fclose(filterfile);
return 0;
}
int main(int argc, char *argv[])
{
struct sockaddr_storage saddr;
struct acl_match_entry *match_entry;
int ret;
int i;
if (load_file())
return 1;
for (i=1; i<argc; i++) {
ret = get_ipaddress(argv[i], &saddr);
if (ret) {
fprintf(stderr, "Cannot parse address %s\n", argv[i]);
} else {
if (saddr.ss_family == AF_INET) {
match_entry = match_entry_v4;
} else {
match_entry = match_entry_v6;
}
if (ipcheck_validate(&match_entry, &saddr)) {
printf("%s is VALID\n", argv[i]);
} else {
printf("%s is not allowed\n", argv[i]);
}
}
}
- ipcheck_clear(&match_entry_v4);
- ipcheck_clear(&match_entry_v6);
+ check_rmall(&match_entry_v4);
+ check_rmall(&match_entry_v6);
return 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Feb 26, 12:48 AM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1465060
Default Alt Text
(41 KB)

Event Timeline