diff --git a/libknet/tests/test-common.c b/libknet/tests/test-common.c index e7968ce6..073f8e57 100644 --- a/libknet/tests/test-common.c +++ b/libknet/tests/test-common.c @@ -1,435 +1,435 @@ /* * Copyright (C) 2016 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "libknet.h" #include "test-common.h" static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; static int log_init = 0; static pthread_mutex_t log_thread_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t log_thread; static int log_thread_init = 0; static int log_fds[2]; struct log_thread_data { int logfd; FILE *std; }; static struct log_thread_data data; static pthread_mutex_t shutdown_mutex = PTHREAD_MUTEX_INITIALIZER; static int shutdown_in_progress = 0; static int _read_pipe(int fd, char **file, size_t *length) { char buf[4096]; int n; int done = 0; *file = NULL; *length = 0; memset(buf, 0, sizeof(buf)); while (!done) { n = read(fd, buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; if (*file) free(*file); return n; } if (n == 0 && (!*length)) return 0; if (n == 0) done = 1; if (*file) *file = realloc(*file, (*length) + n + done); else *file = malloc(n + done); if (!*file) return -1; - memcpy((*file) + (*length), buf, n); + memmove((*file) + (*length), buf, n); *length += (done + n); } /* Null terminator */ (*file)[(*length) - 1] = 0; return 0; } int execute_shell(const char *command, char **error_string) { pid_t pid; int status, err = 0; int fd[2]; size_t size = 0; if ((command == NULL) || (!error_string)) { errno = EINVAL; return FAIL; } *error_string = NULL; err = pipe(fd); if (err) goto out_clean; pid = fork(); if (pid < 0) { err = pid; goto out_clean; } if (pid) { /* parent */ close(fd[1]); err = _read_pipe(fd[0], error_string, &size); if (err) goto out_clean0; waitpid(pid, &status, 0); if (!WIFEXITED(status)) { err = -1; goto out_clean0; } if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { err = WEXITSTATUS(status); goto out_clean0; } goto out_clean0; } else { /* child */ close(0); close(1); close(2); close(fd[0]); dup2(fd[1], 1); dup2(fd[1], 2); close(fd[1]); execlp("/bin/sh", "/bin/sh", "-c", command, NULL); exit(FAIL); } out_clean: close(fd[1]); out_clean0: close(fd[0]); return err; } int is_memcheck(void) { char *val; val = getenv("KNETMEMCHECK"); if (val) { if (!strncmp(val, "yes", 3)) { return 1; } } return 0; } int is_helgrind(void) { char *val; val = getenv("KNETHELGRIND"); if (val) { if (!strncmp(val, "yes", 3)) { return 1; } } return 0; } int need_root(void) { if (geteuid() != 0) { printf("This test requires root privileges\n"); exit(SKIP); } return PASS; } void set_scheduler(int policy) { struct sched_param sched_param; int err; err = sched_get_priority_max(policy); if (err < 0) { printf("Could not get maximum scheduler priority\n"); exit(FAIL); } sched_param.sched_priority = err; err = sched_setscheduler(0, policy, &sched_param); if (err < 0) { printf("Could not set priority\n"); exit(FAIL); } return; } int setup_logpipes(int *logfds) { if (pipe2(logfds, O_CLOEXEC | O_NONBLOCK) < 0) { printf("Unable to setup logging pipe\n"); exit(FAIL); } return PASS; } void close_logpipes(int *logfds) { close(logfds[0]); logfds[0] = 0; close(logfds[1]); logfds[1] = 0; } void flush_logs(int logfd, FILE *std) { struct knet_log_msg msg; size_t bytes_read; int len; next: len = 0; bytes_read = 0; memset(&msg, 0, sizeof(struct knet_log_msg)); while (bytes_read < sizeof(struct knet_log_msg)) { len = read(logfd, &msg + bytes_read, sizeof(struct knet_log_msg) - bytes_read); if (len <= 0) { return; } bytes_read += len; } if (len > 0) { fprintf(std, "knet logs: [%s] %s: %s\n", knet_log_get_loglevel_name(msg.msglevel), knet_log_get_subsystem_name(msg.subsystem), msg.msg); goto next; } } static void *_logthread(void *args) { fd_set rfds; ssize_t len; struct timeval tv; select_loop: tv.tv_sec = 60; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(data.logfd, &rfds); len = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if (len < 0) { fprintf(data.std, "Unable select over logfd!\nHALTING LOGTHREAD!\n"); return NULL; } if (!len) { fprintf(data.std, "knet logs: No logs in the last 60 seconds\n"); } if (FD_ISSET(data.logfd, &rfds)) { flush_logs(data.logfd, data.std); } goto select_loop; return NULL; } int start_logthread(int logfd, FILE *std) { int savederrno = 0; savederrno = pthread_mutex_lock(&log_thread_mutex); if (savederrno) { printf("Unable to get log_thread mutex lock\n"); return -1; } if (!log_thread_init) { data.logfd = logfd; data.std = std; savederrno = pthread_create(&log_thread, 0, _logthread, NULL); if (savederrno) { printf("Unable to start logging thread: %s\n", strerror(savederrno)); pthread_mutex_unlock(&log_thread_mutex); return -1; } log_thread_init = 1; } pthread_mutex_unlock(&log_thread_mutex); return 0; } int stop_logthread(void) { int savederrno = 0; void *retval; savederrno = pthread_mutex_lock(&log_thread_mutex); if (savederrno) { printf("Unable to get log_thread mutex lock\n"); return -1; } if (log_thread_init) { pthread_cancel(log_thread); pthread_join(log_thread, &retval); log_thread_init = 0; } pthread_mutex_unlock(&log_thread_mutex); return 0; } static void stop_logging(void) { stop_logthread(); flush_logs(log_fds[0], stdout); close_logpipes(log_fds); } int start_logging(FILE *std) { int savederrno = 0; savederrno = pthread_mutex_lock(&log_mutex); if (savederrno) { printf("Unable to get log_mutex lock\n"); return -1; } if (!log_init) { setup_logpipes(log_fds); if (atexit(&stop_logging) != 0) { printf("Unable to register atexit handler to stop logging: %s\n", strerror(errno)); exit(FAIL); } if (start_logthread(log_fds[0], std) < 0) { exit(FAIL); } log_init = 1; } pthread_mutex_unlock(&log_mutex); return log_fds[1]; } int knet_handle_stop(knet_handle_t knet_h) { int savederrno; size_t i, j; knet_node_id_t host_ids[KNET_MAX_HOST]; uint8_t link_ids[KNET_MAX_LINK]; size_t host_ids_entries = 0, link_ids_entries = 0; struct knet_link_status status; savederrno = pthread_mutex_lock(&shutdown_mutex); if (savederrno) { printf("Unable to get shutdown mutex lock\n"); return -1; } if (shutdown_in_progress) { pthread_mutex_unlock(&shutdown_mutex); errno = EINVAL; return -1; } shutdown_in_progress = 1; pthread_mutex_unlock(&shutdown_mutex); if (!knet_h) { errno = EINVAL; return -1; } if (knet_host_get_host_list(knet_h, host_ids, &host_ids_entries) < 0) { printf("knet_host_get_host_list failed: %s\n", strerror(errno)); return -1; } for (i = 0; i < host_ids_entries; i++) { if (knet_link_get_link_list(knet_h, host_ids[i], link_ids, &link_ids_entries)) { printf("knet_link_get_link_list failed: %s\n", strerror(errno)); return -1; } for (j = 0; j < link_ids_entries; j++) { if (knet_link_get_status(knet_h, host_ids[i], link_ids[j], &status, sizeof(struct knet_link_status))) { printf("knet_link_get_status failed: %s\n", strerror(errno)); return -1; } if (status.enabled) { if (knet_link_set_enable(knet_h, host_ids[i], j, 0)) { printf("knet_link_set_enable failed: %s\n", strerror(errno)); return -1; } } knet_link_clear_config(knet_h, host_ids[i], j); } if (knet_host_remove(knet_h, host_ids[i]) < 0) { printf("knet_host_remove failed: %s\n", strerror(errno)); return -1; } } if (knet_handle_free(knet_h)) { printf("knet_handle_free failed: %s\n", strerror(errno)); return -1; } return 0; } diff --git a/libknet/transport_sctp.c b/libknet/transport_sctp.c index df8ed168..150e642a 100644 --- a/libknet/transport_sctp.c +++ b/libknet/transport_sctp.c @@ -1,1440 +1,1440 @@ #include "config.h" #include #include #include #include #include #include #include #include "compat.h" #include "host.h" #include "links.h" #include "logging.h" #include "common.h" #include "transports.h" #include "threads_common.h" #ifdef HAVE_NETINET_SCTP_H #include /* * https://en.wikipedia.org/wiki/SCTP_packet_structure */ #define KNET_PMTUD_SCTP_OVERHEAD_COMMON 12 #define KNET_PMTUD_SCTP_OVERHEAD_DATA_CHUNK 16 #define KNET_PMTUD_SCTP_OVERHEAD KNET_PMTUD_SCTP_OVERHEAD_COMMON + KNET_PMTUD_SCTP_OVERHEAD_DATA_CHUNK typedef struct sctp_handle_info { struct knet_list_head listen_links_list; struct knet_list_head connect_links_list; int connect_epollfd; int connectsockfd[2]; int listen_epollfd; int listensockfd[2]; pthread_t connect_thread; pthread_t listen_thread; } sctp_handle_info_t; /* * use by fd_tracker data type */ #define SCTP_NO_LINK_INFO 0 #define SCTP_LISTENER_LINK_INFO 1 #define SCTP_ACCEPTED_LINK_INFO 2 #define SCTP_CONNECT_LINK_INFO 3 /* * this value is per listener */ #define MAX_ACCEPTED_SOCKS 256 typedef struct sctp_listen_link_info { struct knet_list_head list; int listen_sock; int accepted_socks[MAX_ACCEPTED_SOCKS]; struct sockaddr_storage src_address; int on_listener_epoll; int on_rx_epoll; } sctp_listen_link_info_t; typedef struct sctp_accepted_link_info { char mread_buf[KNET_DATABUFSIZE]; ssize_t mread_len; sctp_listen_link_info_t *link_info; } sctp_accepted_link_info_t ; typedef struct sctp_connect_link_info { struct knet_list_head list; sctp_listen_link_info_t *listener; struct knet_link *link; struct sockaddr_storage dst_address; int connect_sock; int on_connected_epoll; int on_rx_epoll; int close_sock; } sctp_connect_link_info_t; /* * socket handling functions * * those functions do NOT perform locking. locking * should be handled in the right context from callers */ /* * sockets are removed from rx_epoll from callers * see also error handling functions */ static int _close_connect_socket(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; sctp_connect_link_info_t *info = kn_link->transport_link; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; struct epoll_event ev; if (info->on_connected_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLOUT; ev.data.fd = info->connect_sock; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, info->connect_sock, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from the epoll pool: %s", strerror(errno)); goto exit_error; } info->on_connected_epoll = 0; } exit_error: if (info->connect_sock != -1) { if (_set_fd_tracker(knet_h, info->connect_sock, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } close(info->connect_sock); info->connect_sock = -1; } errno = savederrno; return err; } static int _enable_sctp_notifications(knet_handle_t knet_h, int sock, const char *type) { int err = 0, savederrno = 0; struct sctp_event_subscribe events; memset(&events, 0, sizeof (events)); events.sctp_data_io_event = 1; events.sctp_association_event = 1; events.sctp_send_failure_event = 1; events.sctp_address_event = 1; events.sctp_peer_error_event = 1; events.sctp_shutdown_event = 1; if (setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof (events)) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to enable %s events: %s", type, strerror(savederrno)); } errno = savederrno; return err; } static int _configure_sctp_socket(knet_handle_t knet_h, int sock, struct sockaddr_storage *address, uint64_t flags, const char *type) { int err = 0, savederrno = 0; int value; int level; #ifdef SOL_SCTP level = SOL_SCTP; #else level = IPPROTO_SCTP; #endif if (_configure_transport_socket(knet_h, sock, address, flags, type) < 0) { savederrno = errno; err = -1; goto exit_error; } value = 1; if (setsockopt(sock, level, SCTP_NODELAY, &value, sizeof(value)) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set sctp nodelay: %s", strerror(savederrno)); goto exit_error; } if (_enable_sctp_notifications(knet_h, sock, type) < 0) { savederrno = errno; err = -1; } exit_error: errno = savederrno; return err; } static int _reconnect_socket(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; sctp_connect_link_info_t *info = kn_link->transport_link; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; struct epoll_event ev; if (connect(info->connect_sock, (struct sockaddr *)&kn_link->dst_addr, sockaddr_len(&kn_link->dst_addr)) < 0) { if ((errno != EALREADY) && (errno != EINPROGRESS) && (errno != EISCONN)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to connect SCTP socket %d: %s", info->connect_sock, strerror(savederrno)); goto exit_error; } } if (!info->on_connected_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLOUT; ev.data.fd = info->connect_sock; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, info->connect_sock, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add send/recv to epoll pool: %s", strerror(savederrno)); goto exit_error; } info->on_connected_epoll = 1; } exit_error: errno = savederrno; return err; } static int _create_connect_socket(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; sctp_connect_link_info_t *info = kn_link->transport_link; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; struct epoll_event ev; int connect_sock; connect_sock = socket(kn_link->dst_addr.ss_family, SOCK_STREAM, IPPROTO_SCTP); if (connect_sock < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create send/recv socket: %s", strerror(savederrno)); goto exit_error; } if (_configure_sctp_socket(knet_h, connect_sock, &kn_link->dst_addr, kn_link->flags, "SCTP connect") < 0) { savederrno = errno; err = -1; goto exit_error; } if (_set_fd_tracker(knet_h, connect_sock, KNET_TRANSPORT_SCTP, SCTP_CONNECT_LINK_INFO, info) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } info->connect_sock = connect_sock; info->close_sock = 0; if (_reconnect_socket(knet_h, kn_link) < 0) { savederrno = errno; err = -1; goto exit_error; } exit_error: if (err) { if (info->on_connected_epoll) { epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, connect_sock, &ev); } if (connect_sock >= 0) { close(connect_sock); } } errno = savederrno; return err; } static int sctp_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno) { sctp_connect_link_info_t *connect_info = knet_h->knet_transport_fd_tracker[sockfd].data; sctp_accepted_link_info_t *accepted_info = knet_h->knet_transport_fd_tracker[sockfd].data; sctp_listen_link_info_t *listen_info; if (recv_err < 0) { switch (knet_h->knet_transport_fd_tracker[sockfd].data_type) { case SCTP_CONNECT_LINK_INFO: if (connect_info->link->transport_connected == 0) { return -1; } break; case SCTP_ACCEPTED_LINK_INFO: listen_info = accepted_info->link_info; if (listen_info->listen_sock != sockfd) { if (listen_info->on_rx_epoll == 0) { return -1; } } break; } if (recv_errno == EAGAIN) { #ifdef DEBUG log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Sock: %d is overloaded. Slowing TX down", sockfd); #endif /* Don't hold onto the lock while sleeping */ pthread_rwlock_unlock(&knet_h->global_rwlock); usleep(KNET_THREADS_TIMERES / 16); pthread_rwlock_rdlock(&knet_h->global_rwlock); return 1; } return -1; } return 0; } /* * socket error management functions * * both called with global read lock. * * NOTE: we need to remove the fd from the epoll as soon as possible * even before we notify the respective thread to take care of it * because scheduling can make it so that this thread will overload * and the threads supposed to take care of the error will never * be able to take action. * we CANNOT handle FDs here diretly (close/reconnect/etc) due * to locking context. We need to delegate that to their respective * management threads within global write lock. * * this function is called from: * - RX thread with recv_err <= 0 directly on recvmmsg error * - transport_rx_is_data when msg_len == 0 (recv_err = 1) * - transport_rx_is_data on notification (recv_err = 2) * * basically this small abouse of recv_err is to detect notifications * generated by sockets created by listen(). */ static int sctp_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno) { struct epoll_event ev; sctp_connect_link_info_t *connect_info = knet_h->knet_transport_fd_tracker[sockfd].data; sctp_accepted_link_info_t *accepted_info = knet_h->knet_transport_fd_tracker[sockfd].data; sctp_listen_link_info_t *listen_info; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; switch (knet_h->knet_transport_fd_tracker[sockfd].data_type) { case SCTP_CONNECT_LINK_INFO: /* * all connect link have notifications enabled * and we accept only data from notification and * generic recvmmsg errors. * * Errors generated by msg_len 0 can be ignored because * they follow a notification (double notification) */ if (recv_err != 1) { connect_info->link->transport_connected = 0; if (connect_info->on_rx_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = sockfd; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sockfd, &ev)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s", strerror(errno)); return -1; } connect_info->on_rx_epoll = 0; } log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Notifying connect thread that sockfd %d received an error", sockfd); if (sendto(handle_info->connectsockfd[1], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(int)) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to notify connect thread: %s", strerror(errno)); } } break; case SCTP_ACCEPTED_LINK_INFO: listen_info = accepted_info->link_info; if (listen_info->listen_sock != sockfd) { if (recv_err != 1) { if (listen_info->on_rx_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = sockfd; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sockfd, &ev)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s", strerror(errno)); return -1; } listen_info->on_rx_epoll = 0; } log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Notifying listen thread that sockfd %d received an error", sockfd); if (sendto(handle_info->listensockfd[1], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(int)) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to notify listen thread: %s", strerror(errno)); } } } else { /* * this means the listen() socket has generated * a notification. now what? :-) */ log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for listen() socket %d", sockfd); } break; default: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received unknown notification? %d", sockfd); break; } /* * Under RX pressure we need to give time to IPC to pick up the message */ /* Don't hold onto the lock while sleeping */ pthread_rwlock_unlock(&knet_h->global_rwlock); usleep(KNET_THREADS_TIMERES / 2); pthread_rwlock_rdlock(&knet_h->global_rwlock); return 0; } /* * NOTE: sctp_transport_rx_is_data is called with global rdlock * delegate any FD error management to sctp_transport_rx_sock_error * and keep this code to parsing incoming data only */ static int sctp_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg) { size_t i; struct iovec *iov = msg->msg_hdr.msg_iov; size_t iovlen = msg->msg_hdr.msg_iovlen; struct sctp_assoc_change *sac; union sctp_notification *snp; sctp_accepted_link_info_t *info = knet_h->knet_transport_fd_tracker[sockfd].data; if (!(msg->msg_hdr.msg_flags & MSG_NOTIFICATION)) { if (msg->msg_len == 0) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "received 0 bytes len packet: %d", sockfd); /* * NOTE: with event notification enabled, we receive error twice: * 1) from the event notification * 2) followed by a 0 byte msg_len * * This is generally not a problem if not for causing extra * handling for the same issue. Should we drop notifications * and keep the code generic (handle all errors via msg_len = 0) * or keep the duplication as safety measure, or drop msg_len = 0 * handling (what about sockets without events enabled?) */ sctp_transport_rx_sock_error(knet_h, sockfd, 1, 0); return 1; } /* * missing MSG_EOR has to be treated as a short read * from the socket and we need to fill in the mread buf * while we wait for MSG_EOR */ if (!(msg->msg_hdr.msg_flags & MSG_EOR)) { /* * copy the incoming data into mread_buf + mread_len (incremental) * and increase mread_len */ memmove(info->mread_buf + info->mread_len, iov->iov_base, msg->msg_len); info->mread_len = info->mread_len + msg->msg_len; return 0; } /* * got EOR. * if mread_len is > 0 we are completing a packet from short reads * complete reassembling the packet in mread_buf, copy it back in the iov * and set the iov/msg len numbers (size) correctly */ if (info->mread_len) { /* * add last fragment to mread_buf */ memmove(info->mread_buf + info->mread_len, iov->iov_base, msg->msg_len); info->mread_len = info->mread_len + msg->msg_len; /* * move all back into the iovec */ memmove(iov->iov_base, info->mread_buf, info->mread_len); msg->msg_len = info->mread_len; info->mread_len = 0; } return 2; } if (!(msg->msg_hdr.msg_flags & MSG_EOR)) { return 1; } for (i=0; i< iovlen; i++) { snp = iov[i].iov_base; switch (snp->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp assoc change"); sac = &snp->sn_assoc_change; if (sac->sac_state == SCTP_COMM_LOST) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp assoc change: comm_lost"); sctp_transport_rx_sock_error(knet_h, sockfd, 2, 0); } break; case SCTP_SHUTDOWN_EVENT: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp shutdown event"); sctp_transport_rx_sock_error(knet_h, sockfd, 2, 0); break; case SCTP_SEND_FAILED: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp send failed"); break; case SCTP_PEER_ADDR_CHANGE: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp peer addr change"); break; case SCTP_REMOTE_ERROR: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp remote error"); break; default: log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] unknown sctp event type: %hu\n", snp->sn_header.sn_type); break; } } return 0; } /* * connect / outgoing socket management thread */ /* * _handle_connected_sctp* are called with a global write lock * from the connect_thread */ static void _handle_connected_sctp(knet_handle_t knet_h, int connect_sock) { int err; struct epoll_event ev; unsigned int status, len = sizeof(status); sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; sctp_connect_link_info_t *info = knet_h->knet_transport_fd_tracker[connect_sock].data; struct knet_link *kn_link = info->link; err = getsockopt(connect_sock, SOL_SOCKET, SO_ERROR, &status, &len); if (err) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP getsockopt() on connecting socket %d failed: %s", connect_sock, strerror(errno)); return; } if (info->close_sock) { if (_close_connect_socket(knet_h, kn_link) < 0) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to close sock %d from _handle_connected_sctp: %s", connect_sock, strerror(errno)); return; } info->close_sock = 0; if (_create_connect_socket(knet_h, kn_link) < 0) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to recreate connecting sock! %s", strerror(errno)); return; } } if (status) { log_info(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect on %d to %s port %s failed: %s", connect_sock, kn_link->status.dst_ipaddr, kn_link->status.dst_port, strerror(status)); /* * No need to create a new socket if connect failed, * just retry connect */ _reconnect_socket(knet_h, info->link); return; } /* * Connected - Remove us from the connect epoll */ memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLOUT; ev.data.fd = connect_sock; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, connect_sock, &ev)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket %d from epoll pool: %s", connect_sock, strerror(errno)); } info->on_connected_epoll = 0; kn_link->transport_connected = 1; kn_link->outsock = info->connect_sock; memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = connect_sock; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, connect_sock, &ev)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connected socket to epoll pool: %s", strerror(errno)); } info->on_rx_epoll = 1; log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP handler fd %d now connected to %s port %s", connect_sock, kn_link->status.dst_ipaddr, kn_link->status.dst_port); } static void _handle_connected_sctp_errors(knet_handle_t knet_h) { int sockfd = -1; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; sctp_connect_link_info_t *info; if (recv(handle_info->connectsockfd[0], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(int)) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Short read on connectsockfd"); return; } if (_is_valid_fd(knet_h, sockfd) < 1) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for connected socket fd error"); return; } log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Processing connected error on socket: %d", sockfd); info = knet_h->knet_transport_fd_tracker[sockfd].data; info->close_sock = 1; info->link->transport_connected = 0; _reconnect_socket(knet_h, info->link); } static void *_sctp_connect_thread(void *data) { int savederrno; int i, nev; knet_handle_t knet_h = (knet_handle_t) data; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; while (!shutdown_in_progress(knet_h)) { nev = epoll_wait(handle_info->connect_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1); if (nev < 0) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect handler EPOLL ERROR: %s", strerror(errno)); continue; } /* * Sort out which FD has a connection */ savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to get write lock: %s", strerror(savederrno)); continue; } /* * minor optimization: deduplicate events * * in some cases we can receive multiple notifcations * of the same FD having issues or need handling. * It's enough to process it once even tho it's safe * to handle them multiple times. */ for (i = 0; i < nev; i++) { if (events[i].data.fd == handle_info->connectsockfd[0]) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received notification from rx_error for connected socket"); _handle_connected_sctp_errors(knet_h); } else { if (_is_valid_fd(knet_h, events[i].data.fd) == 1) { _handle_connected_sctp(knet_h, events[i].data.fd); } else { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for dead fd %d\n", events[i].data.fd); } } } pthread_rwlock_unlock(&knet_h->global_rwlock); /* * this thread can generate events for itself. * we need to sleep in between loops to allow other threads * to be scheduled */ usleep(knet_h->reconnect_int * 1000); } return NULL; } /* * listen/incoming connections management thread */ /* * Listener received a new connection * called with a write lock from main thread */ static void _handle_incoming_sctp(knet_handle_t knet_h, int listen_sock) { int err = 0, savederrno = 0; int new_fd; int i = -1; sctp_listen_link_info_t *info = knet_h->knet_transport_fd_tracker[listen_sock].data; struct epoll_event ev; struct sockaddr_storage ss; socklen_t sock_len = sizeof(ss); char addr_str[KNET_MAX_HOST_LEN]; char port_str[KNET_MAX_PORT_LEN]; sctp_accepted_link_info_t *accept_info = NULL; new_fd = accept(listen_sock, (struct sockaddr *)&ss, &sock_len); if (new_fd < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accept error: %s", strerror(errno)); goto exit_error; } if (knet_addrtostr(&ss, sizeof(ss), addr_str, KNET_MAX_HOST_LEN, port_str, KNET_MAX_PORT_LEN) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to gather socket info"); goto exit_error; } log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: received connection from: %s port: %s", addr_str, port_str); /* * Keep a track of all accepted FDs */ for (i=0; iaccepted_socks[i] == -1) { info->accepted_socks[i] = new_fd; break; } } if (i == MAX_ACCEPTED_SOCKS) { errno = EBUSY; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: too many connections!"); goto exit_error; } if (_configure_common_socket(knet_h, new_fd, 0, "SCTP incoming") < 0) { /* Inherit flags from listener? */ savederrno = errno; err = -1; goto exit_error; } if (_enable_sctp_notifications(knet_h, new_fd, "Incoming connection") < 0) { savederrno = errno; err = -1; goto exit_error; } accept_info = malloc(sizeof(sctp_accepted_link_info_t)); if (!accept_info) { savederrno = errno; err = -1; goto exit_error; } memset(accept_info, 0, sizeof(sctp_accepted_link_info_t)); accept_info->link_info = info; if (_set_fd_tracker(knet_h, new_fd, KNET_TRANSPORT_SCTP, SCTP_ACCEPTED_LINK_INFO, accept_info) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", strerror(errno)); goto exit_error; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = new_fd; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, new_fd, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to add accepted socket %d to epoll pool: %s", new_fd, strerror(errno)); goto exit_error; } info->on_rx_epoll = 1; log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accepted new fd %d for %s/%s (listen fd: %d). index: %d", new_fd, addr_str, port_str, info->listen_sock, i); exit_error: if (err) { if ((i >= 0) || (i < MAX_ACCEPTED_SOCKS)) { info->accepted_socks[i] = -1; } _set_fd_tracker(knet_h, new_fd, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL); free(accept_info); close(new_fd); } errno = savederrno; return; } /* * Listen thread received a notification of a bad socket that needs closing * called with a write lock from main thread */ static void _handle_listen_sctp_errors(knet_handle_t knet_h) { int sockfd = -1; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; sctp_accepted_link_info_t *accept_info; sctp_listen_link_info_t *info; struct knet_host *host; int link_idx; int i; if (recv(handle_info->listensockfd[0], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(int)) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Short read on listensockfd"); return; } if (_is_valid_fd(knet_h, sockfd) < 1) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for listen socket fd error"); return; } log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Processing listen error on socket: %d", sockfd); accept_info = knet_h->knet_transport_fd_tracker[sockfd].data; info = accept_info->link_info; /* * clear all links using this accepted socket as * outbound dynamically connected socket */ for (host = knet_h->host_head; host != NULL; host = host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if ((host->link[link_idx].dynamic == KNET_LINK_DYNIP) && (host->link[link_idx].outsock == sockfd)) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Found dynamic connection on host %d link %d (%d)", host->host_id, link_idx, sockfd); host->link[link_idx].status.dynconnected = 0; host->link[link_idx].transport_connected = 0; host->link[link_idx].outsock = 0; memset(&host->link[link_idx].dst_addr, 0, sizeof(struct sockaddr_storage)); } } } for (i=0; iaccepted_socks[i]) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Closing accepted socket %d", sockfd); _set_fd_tracker(knet_h, sockfd, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL); info->accepted_socks[i] = -1; free(accept_info); close(sockfd); } } } static void *_sctp_listen_thread(void *data) { int savederrno; int i, nev; knet_handle_t knet_h = (knet_handle_t) data; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; while (!shutdown_in_progress(knet_h)) { nev = epoll_wait(handle_info->listen_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1); if (nev < 0) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listen handler EPOLL ERROR: %s", strerror(errno)); continue; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to get write lock: %s", strerror(savederrno)); continue; } /* * Sort out which FD has an incoming connection */ for (i = 0; i < nev; i++) { if (events[i].data.fd == handle_info->listensockfd[0]) { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received notification from rx_error for listener/accepted socket"); _handle_listen_sctp_errors(knet_h); } else { if (_is_valid_fd(knet_h, events[i].data.fd) == 1) { _handle_incoming_sctp(knet_h, events[i].data.fd); } else { log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received listen notification from invalid socket"); } } } pthread_rwlock_unlock(&knet_h->global_rwlock); } return NULL; } /* * sctp_link_listener_start/stop are called in global write lock * context from set_config and clear_config. */ static sctp_listen_link_info_t *sctp_link_listener_start(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; int listen_sock = -1; struct epoll_event ev; sctp_listen_link_info_t *info = NULL; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; /* * Only allocate a new listener if src address is different */ knet_list_for_each_entry(info, &handle_info->listen_links_list, list) { if (memcmp(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)) == 0) { return info; } } info = malloc(sizeof(sctp_listen_link_info_t)); if (!info) { err = -1; goto exit_error; } memset(info, 0, sizeof(sctp_listen_link_info_t)); memset(info->accepted_socks, -1, sizeof(info->accepted_socks)); - memcpy(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)); + memmove(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)); listen_sock = socket(kn_link->src_addr.ss_family, SOCK_STREAM, IPPROTO_SCTP); if (listen_sock < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create listener socket: %s", strerror(savederrno)); goto exit_error; } if (_configure_sctp_socket(knet_h, listen_sock, &kn_link->src_addr, kn_link->flags, "SCTP listener") < 0) { savederrno = errno; err = -1; goto exit_error; } if (bind(listen_sock, (struct sockaddr *)&kn_link->src_addr, sockaddr_len(&kn_link->src_addr)) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to bind listener socket: %s", strerror(savederrno)); goto exit_error; } if (listen(listen_sock, 5) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to listen on listener socket: %s", strerror(savederrno)); goto exit_error; } if (_set_fd_tracker(knet_h, listen_sock, KNET_TRANSPORT_SCTP, SCTP_LISTENER_LINK_INFO, info) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = listen_sock; if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, listen_sock, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listener to epoll pool: %s", strerror(savederrno)); goto exit_error; } info->on_listener_epoll = 1; info->listen_sock = listen_sock; knet_list_add(&info->list, &handle_info->listen_links_list); log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Listening on fd %d for %s:%s", listen_sock, kn_link->status.src_ipaddr, kn_link->status.src_port); exit_error: if (err) { if (info->on_listener_epoll) { epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, listen_sock, &ev); } if (listen_sock >= 0) { close(listen_sock); } if (info) { free(info); info = NULL; } } errno = savederrno; return info; } static int sctp_link_listener_stop(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; int found = 0, i; struct knet_host *host; int link_idx; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; sctp_connect_link_info_t *this_link_info = kn_link->transport_link; sctp_listen_link_info_t *info = this_link_info->listener; sctp_connect_link_info_t *link_info; struct epoll_event ev; for (host = knet_h->host_head; host != NULL; host = host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (&host->link[link_idx] == kn_link) continue; link_info = host->link[link_idx].transport_link; if ((link_info) && (link_info->listener == info) && (host->link[link_idx].status.enabled == 1)) { found = 1; break; } } } if (found) { this_link_info->listener = NULL; log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listener socket %d still in use", info->listen_sock); savederrno = EBUSY; err = -1; goto exit_error; } if (info->on_listener_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = info->listen_sock; if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, info->listen_sock, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove listener to epoll pool: %s", strerror(savederrno)); goto exit_error; } info->on_listener_epoll = 0; } if (_set_fd_tracker(knet_h, info->listen_sock, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } close(info->listen_sock); for (i=0; i< MAX_ACCEPTED_SOCKS; i++) { if (info->accepted_socks[i] > -1) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = info->accepted_socks[i]; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->accepted_socks[i], &ev)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s", strerror(errno)); } info->on_rx_epoll = 0; free(knet_h->knet_transport_fd_tracker[info->accepted_socks[i]].data); close(info->accepted_socks[i]); if (_set_fd_tracker(knet_h, info->accepted_socks[i], KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } info->accepted_socks[i] = -1; } } knet_list_del(&info->list); free(info); this_link_info->listener = NULL; exit_error: errno = savederrno; return err; } /* * Links config/clear. Both called with global wrlock from link_set_config/clear_config */ static int sctp_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link) { int savederrno = 0, err = 0; sctp_connect_link_info_t *info; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; info = malloc(sizeof(sctp_connect_link_info_t)); if (!info) { goto exit_error; } memset(info, 0, sizeof(sctp_connect_link_info_t)); kn_link->transport_link = info; info->link = kn_link; - memcpy(&info->dst_address, &kn_link->dst_addr, sizeof(struct sockaddr_storage)); + memmove(&info->dst_address, &kn_link->dst_addr, sizeof(struct sockaddr_storage)); info->on_connected_epoll = 0; info->connect_sock = -1; info->listener = sctp_link_listener_start(knet_h, kn_link); if (!info->listener) { savederrno = errno; err = -1; goto exit_error; } if (kn_link->dynamic == KNET_LINK_STATIC) { if (_create_connect_socket(knet_h, kn_link) < 0) { savederrno = errno; err = -1; goto exit_error; } kn_link->outsock = info->connect_sock; } knet_list_add(&info->list, &handle_info->connect_links_list); exit_error: if (err) { if (info) { if (info->connect_sock) { close(info->connect_sock); } if (info->listener) { sctp_link_listener_stop(knet_h, kn_link); } kn_link->transport_link = NULL; free(info); } } errno = savederrno; return err; } /* * called with global wrlock */ static int sctp_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; sctp_connect_link_info_t *info; struct epoll_event ev; if (!kn_link) { errno = EINVAL; return -1; } info = kn_link->transport_link; if (!info) { errno = EINVAL; return -1; } if ((sctp_link_listener_stop(knet_h, kn_link) <0) && (errno != EBUSY)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove listener trasport: %s", strerror(savederrno)); goto exit_error; } if (info->on_rx_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = info->connect_sock; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->connect_sock, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from epoll pool: %s", strerror(savederrno)); goto exit_error; } info->on_rx_epoll = 0; } if (_close_connect_socket(knet_h, kn_link) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to close connected socket: %s", strerror(savederrno)); goto exit_error; } knet_list_del(&info->list); free(info); kn_link->transport_link = NULL; exit_error: errno = savederrno; return err; } /* * transport_free and transport_init are * called only from knet_handle_new and knet_handle_free. * all resources (hosts/links) should have been already freed at this point * and they are called in a write locked context, hence they * don't need their own locking. */ static int sctp_transport_free(knet_handle_t knet_h) { sctp_handle_info_t *handle_info; void *thread_status; struct epoll_event ev; if (!knet_h->transports[KNET_TRANSPORT_SCTP]) { errno = EINVAL; return -1; } handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; /* * keep it here while we debug list usage and such */ if (!knet_list_empty(&handle_info->listen_links_list)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Internal error. listen links list is not empty"); } if (!knet_list_empty(&handle_info->connect_links_list)) { log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Internal error. connect links list is not empty"); } if (handle_info->listen_thread) { pthread_cancel(handle_info->listen_thread); pthread_join(handle_info->listen_thread, &thread_status); } if (handle_info->connect_thread) { pthread_cancel(handle_info->connect_thread); pthread_join(handle_info->connect_thread, &thread_status); } if (handle_info->listensockfd[0] >= 0) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = handle_info->listensockfd[0]; epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, handle_info->listensockfd[0], &ev); } if (handle_info->connectsockfd[0] >= 0) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = handle_info->connectsockfd[0]; epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, handle_info->connectsockfd[0], &ev); } _close_socketpair(knet_h, handle_info->connectsockfd); _close_socketpair(knet_h, handle_info->listensockfd); if (handle_info->listen_epollfd >= 0) { close(handle_info->listen_epollfd); } if (handle_info->connect_epollfd >= 0) { close(handle_info->connect_epollfd); } free(handle_info); knet_h->transports[KNET_TRANSPORT_SCTP] = NULL; return 0; } static int sctp_transport_init(knet_handle_t knet_h) { int err = 0, savederrno = 0; sctp_handle_info_t *handle_info; struct epoll_event ev; if (knet_h->transports[KNET_TRANSPORT_SCTP]) { errno = EEXIST; return -1; } handle_info = malloc(sizeof(sctp_handle_info_t)); if (!handle_info) { return -1; } memset(handle_info, 0,sizeof(sctp_handle_info_t)); knet_h->transports[KNET_TRANSPORT_SCTP] = handle_info; knet_list_init(&handle_info->listen_links_list); knet_list_init(&handle_info->connect_links_list); handle_info->listen_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); if (handle_info->listen_epollfd < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll listen fd: %s", strerror(savederrno)); goto exit_fail; } if (_fdset_cloexec(handle_info->listen_epollfd)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set CLOEXEC on listen_epollfd: %s", strerror(savederrno)); goto exit_fail; } handle_info->connect_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); if (handle_info->connect_epollfd < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll connect fd: %s", strerror(savederrno)); goto exit_fail; } if (_fdset_cloexec(handle_info->connect_epollfd)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set CLOEXEC on connect_epollfd: %s", strerror(savederrno)); goto exit_fail; } if (_init_socketpair(knet_h, handle_info->connectsockfd) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to init connect socketpair: %s", strerror(savederrno)); goto exit_fail; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = handle_info->connectsockfd[0]; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, handle_info->connectsockfd[0], &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connectsockfd[0] to connect epoll pool: %s", strerror(savederrno)); goto exit_fail; } if (_init_socketpair(knet_h, handle_info->listensockfd) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to init listen socketpair: %s", strerror(savederrno)); goto exit_fail; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = handle_info->listensockfd[0]; if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, handle_info->listensockfd[0], &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listensockfd[0] to listen epoll pool: %s", strerror(savederrno)); goto exit_fail; } /* * Start connect & listener threads */ savederrno = pthread_create(&handle_info->listen_thread, 0, _sctp_listen_thread, (void *) knet_h); if (savederrno) { err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp listen thread: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_create(&handle_info->connect_thread, 0, _sctp_connect_thread, (void *) knet_h); if (savederrno) { err = -1; log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp connect thread: %s", strerror(savederrno)); goto exit_fail; } exit_fail: if (err < 0) { sctp_transport_free(knet_h); } errno = savederrno; return err; } static int sctp_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link) { kn_link->outsock = sockfd; kn_link->status.dynconnected = 1; kn_link->transport_connected = 1; return 0; } static knet_transport_ops_t sctp_transport_ops = { .transport_name = "SCTP", .transport_id = KNET_TRANSPORT_SCTP, .transport_mtu_overhead = KNET_PMTUD_SCTP_OVERHEAD, .transport_init = sctp_transport_init, .transport_free = sctp_transport_free, .transport_link_set_config = sctp_transport_link_set_config, .transport_link_clear_config = sctp_transport_link_clear_config, .transport_link_dyn_connect = sctp_transport_link_dyn_connect, .transport_rx_sock_error = sctp_transport_rx_sock_error, .transport_tx_sock_error = sctp_transport_tx_sock_error, .transport_rx_is_data = sctp_transport_rx_is_data, }; knet_transport_ops_t *get_sctp_transport() { return &sctp_transport_ops; } #else // HAVE_NETINET_SCTP_H knet_transport_ops_t *get_sctp_transport() { return NULL; } #endif diff --git a/libknet/transport_udp.c b/libknet/transport_udp.c index 5a3be305..622a6532 100644 --- a/libknet/transport_udp.c +++ b/libknet/transport_udp.c @@ -1,408 +1,408 @@ #include "config.h" #include #include #include #include #include #include #include #include #include #if defined (IP_RECVERR) || defined (IPV6_RECVERR) #include #endif #include "libknet.h" #include "compat.h" #include "host.h" #include "link.h" #include "logging.h" #include "common.h" #include "transports.h" #include "threads_common.h" #define KNET_PMTUD_UDP_OVERHEAD 8 typedef struct udp_handle_info { struct knet_list_head links_list; } udp_handle_info_t; typedef struct udp_link_info { struct knet_list_head list; struct sockaddr_storage local_address; int socket_fd; int on_epoll; } udp_link_info_t; static int udp_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; int sock = -1; struct epoll_event ev; udp_link_info_t *info; udp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_UDP]; #if defined (IP_RECVERR) || defined (IPV6_RECVERR) int value; #endif /* * Only allocate a new link if the local address is different */ knet_list_for_each_entry(info, &handle_info->links_list, list) { if (memcmp(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)) == 0) { log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Re-using existing UDP socket for new link"); kn_link->outsock = info->socket_fd; kn_link->transport_link = info; kn_link->transport_connected = 1; return 0; } } info = malloc(sizeof(udp_link_info_t)); if (!info) { err = -1; goto exit_error; } sock = socket(kn_link->src_addr.ss_family, SOCK_DGRAM, 0); if (sock < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_LISTENER, "Unable to create listener socket: %s", strerror(savederrno)); goto exit_error; } if (_configure_transport_socket(knet_h, sock, &kn_link->src_addr, kn_link->flags, "UDP") < 0) { savederrno = errno; err = -1; goto exit_error; } #ifdef IP_RECVERR if (kn_link->src_addr.ss_family == AF_INET) { value = 1; if (setsockopt(sock, SOL_IP, IP_RECVERR, &value, sizeof(value)) <0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set RECVERR on socket: %s", strerror(savederrno)); goto exit_error; } } #endif #ifdef IPV6_RECVERR if (kn_link->src_addr.ss_family == AF_INET6) { value = 1; if (setsockopt(sock, SOL_IPV6, IPV6_RECVERR, &value, sizeof(value)) <0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set RECVERR on socket: %s", strerror(savederrno)); goto exit_error; } } #endif if (bind(sock, (struct sockaddr *)&kn_link->src_addr, sockaddr_len(&kn_link->src_addr))) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to bind listener socket: %s", strerror(savederrno)); goto exit_error; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = sock; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, sock, &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to add listener to epoll pool: %s", strerror(savederrno)); goto exit_error; } info->on_epoll = 1; if (_set_fd_tracker(knet_h, sock, KNET_TRANSPORT_UDP, 0, info) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } - memcpy(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)); + memmove(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)); info->socket_fd = sock; knet_list_add(&info->list, &handle_info->links_list); kn_link->outsock = sock; kn_link->transport_link = info; kn_link->transport_connected = 1; exit_error: if (err) { if (info) { if (info->on_epoll) { epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sock, &ev); } free(info); } if (sock >= 0) { close(sock); } } errno = savederrno; return err; } static int udp_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link) { int err = 0, savederrno = 0; int found = 0; struct knet_host *host; int link_idx; udp_link_info_t *info = kn_link->transport_link; struct epoll_event ev; for (host = knet_h->host_head; host != NULL; host = host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (&host->link[link_idx] == kn_link) continue; if ((host->link[link_idx].transport_link == info) && (host->link[link_idx].status.enabled == 1)) { found = 1; break; } } } if (found) { log_debug(knet_h, KNET_SUB_TRANSP_UDP, "UDP socket %d still in use", info->socket_fd); savederrno = EBUSY; err = -1; goto exit_error; } if (info->on_epoll) { memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = info->socket_fd; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->socket_fd, &ev) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to remove UDP socket from epoll poll: %s", strerror(errno)); goto exit_error; } info->on_epoll = 0; } if (_set_fd_tracker(knet_h, info->socket_fd, KNET_MAX_TRANSPORTS, 0, NULL) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set fd tracker: %s", strerror(savederrno)); goto exit_error; } close(info->socket_fd); knet_list_del(&info->list); free(kn_link->transport_link); exit_error: errno = savederrno; return err; } static int udp_transport_free(knet_handle_t knet_h) { udp_handle_info_t *handle_info; if (!knet_h->transports[KNET_TRANSPORT_UDP]) { errno = EINVAL; return -1; } handle_info = knet_h->transports[KNET_TRANSPORT_UDP]; /* * keep it here while we debug list usage and such */ if (!knet_list_empty(&handle_info->links_list)) { log_err(knet_h, KNET_SUB_TRANSP_UDP, "Internal error. handle list is not empty"); return -1; } free(handle_info); knet_h->transports[KNET_TRANSPORT_UDP] = NULL; return 0; } static int udp_transport_init(knet_handle_t knet_h) { udp_handle_info_t *handle_info; if (knet_h->transports[KNET_TRANSPORT_UDP]) { errno = EEXIST; return -1; } handle_info = malloc(sizeof(udp_handle_info_t)); if (!handle_info) { return -1; } memset(handle_info, 0, sizeof(udp_handle_info_t)); knet_h->transports[KNET_TRANSPORT_UDP] = handle_info; knet_list_init(&handle_info->links_list); return 0; } #if defined (IP_RECVERR) || defined (IPV6_RECVERR) static int read_errs_from_sock(knet_handle_t knet_h, int sockfd) { int err = 0, savederrno = 0; int got_err = 0; char buffer[1024]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; struct sock_extended_err *sock_err; struct icmphdr icmph; struct sockaddr_storage remote; struct sockaddr_storage *origin; char addr_str[KNET_MAX_HOST_LEN]; char port_str[KNET_MAX_PORT_LEN]; iov.iov_base = &icmph; iov.iov_len = sizeof(icmph); msg.msg_name = (void*)&remote; msg.msg_namelen = sizeof(remote); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = buffer; msg.msg_controllen = sizeof(buffer); for (;;) { err = recvmsg(sockfd, &msg, MSG_ERRQUEUE); savederrno = errno; if (err < 0) { if (!got_err) { errno = savederrno; return -1; } else { return 0; } } got_err = 1; for (cmsg = CMSG_FIRSTHDR(&msg);cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_RECVERR)) || ((cmsg->cmsg_level == SOL_IPV6 && (cmsg->cmsg_type == IPV6_RECVERR)))) { sock_err = (struct sock_extended_err*)(void *)CMSG_DATA(cmsg); if (sock_err) { switch (sock_err->ee_origin) { case 0: /* no origin */ case 1: /* local source (EMSGSIZE) */ /* * those errors are way too noisy */ break; case 2: /* ICMP */ case 3: /* ICMP6 */ origin = (struct sockaddr_storage *)(void *)SO_EE_OFFENDER(sock_err); if (knet_addrtostr(origin, sizeof(origin), addr_str, KNET_MAX_HOST_LEN, port_str, KNET_MAX_PORT_LEN) < 0) { log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from unknown source: %s", strerror(sock_err->ee_errno)); } else { log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from %s: %s", addr_str, strerror(sock_err->ee_errno)); } break; } } else { log_debug(knet_h, KNET_SUB_TRANSP_UDP, "No data in MSG_ERRQUEUE"); } } } } } #else static int read_errs_from_sock(knet_handle_t knet_h, int sockfd) { return 0; } #endif static int udp_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno) { if (recv_errno == EAGAIN) { read_errs_from_sock(knet_h, sockfd); } return 0; } static int udp_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno) { if (recv_err < 0) { if ((recv_errno == ENOBUFS) || (recv_errno == EAGAIN)) { #ifdef DEBUG log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Sock: %d is overloaded. Slowing TX down", sockfd); #endif usleep(KNET_THREADS_TIMERES / 16); return 1; } read_errs_from_sock(knet_h, sockfd); if (recv_errno == EMSGSIZE) { return 0; } return -1; } return 0; } static int udp_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg) { if (msg->msg_len == 0) return 0; return 2; } static int udp_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link) { kn_link->status.dynconnected = 1; return 0; } static knet_transport_ops_t udp_transport_ops = { .transport_name = "UDP", .transport_id = KNET_TRANSPORT_UDP, .transport_mtu_overhead = KNET_PMTUD_UDP_OVERHEAD, .transport_init = udp_transport_init, .transport_free = udp_transport_free, .transport_link_set_config = udp_transport_link_set_config, .transport_link_clear_config = udp_transport_link_clear_config, .transport_link_dyn_connect = udp_transport_link_dyn_connect, .transport_rx_sock_error = udp_transport_rx_sock_error, .transport_tx_sock_error = udp_transport_tx_sock_error, .transport_rx_is_data = udp_transport_rx_is_data, }; knet_transport_ops_t *get_udp_transport() { return &udp_transport_ops; } diff --git a/libtap/libtap.c b/libtap/libtap.c index c7f2373b..734b3b9a 100644 --- a/libtap/libtap.c +++ b/libtap/libtap.c @@ -1,2125 +1,2125 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libtap.h" #define MAX_IP_CHAR 128 #define MAX_PREFIX_CHAR 4 #define MAX_MAC_CHAR 18 struct _ip { char ip_addr[MAX_IP_CHAR]; char prefix[MAX_PREFIX_CHAR]; int domain; struct _ip *next; }; struct _iface { struct ifreq ifr; int fd; char tapname[IFNAMSIZ]; char default_mac[MAX_MAC_CHAR]; int default_mtu; int current_mtu; char updownpath[PATH_MAX]; int hasupdown; int up; struct _ip *ip; struct _iface *next; }; #define ifname ifr.ifr_name struct _config { struct _iface *head; int sockfd; }; static int lib_init = 0; static struct _config lib_cfg; static pthread_mutex_t lib_mutex = PTHREAD_MUTEX_INITIALIZER; /* forward declarations */ static int _execute_shell(const char *command, char **error_string); static int _exec_updown(const tap_t tap, const char *action, char **error_string); static int _read_pipe(int fd, char **file, size_t *length); static int _check(const tap_t tap); static void _close(tap_t tap); static void _close_cfg(void); static int _get_mtu(const tap_t tap); static int _get_mac(const tap_t tap, char **ether_addr); static int _set_down(tap_t tap, char **error_down, char **error_postdown); static char *_get_v4_broadcast(const char *ip_addr, const char *prefix); static int _set_ip(tap_t tap, const char *command, const char *ip_addr, const char *prefix, char **error_string); static int _find_ip(tap_t tap, const char *ip_addr, const char *prefix, struct _ip **ip, struct _ip **ip_prev); static int _read_pipe(int fd, char **file, size_t *length) { char buf[4096]; int n; int done = 0; *file = NULL; *length = 0; memset(buf, 0, sizeof(buf)); while (!done) { n = read(fd, buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; if (*file) free(*file); return n; } if (n == 0 && (!*length)) return 0; if (n == 0) done = 1; if (*file) *file = realloc(*file, (*length) + n + done); else *file = malloc(n + done); if (!*file) return -1; - memcpy((*file) + (*length), buf, n); + memmove((*file) + (*length), buf, n); *length += (done + n); } /* Null terminator */ (*file)[(*length) - 1] = 0; return 0; } static int _execute_shell(const char *command, char **error_string) { pid_t pid; int status, err = 0; int fd[2]; size_t size = 0; if ((command == NULL) || (!error_string)) { errno = EINVAL; return -1; } *error_string = NULL; err = pipe(fd); if (err) goto out_clean; pid = fork(); if (pid < 0) { err = pid; goto out_clean; } if (pid) { /* parent */ close(fd[1]); err = _read_pipe(fd[0], error_string, &size); if (err) goto out_clean0; waitpid(pid, &status, 0); if (!WIFEXITED(status)) { err = -1; goto out_clean0; } if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { err = WEXITSTATUS(status); goto out_clean0; } goto out_clean0; } else { /* child */ close(0); close(1); close(2); close(fd[0]); dup2(fd[1], 1); dup2(fd[1], 2); close(fd[1]); execlp("/bin/sh", "/bin/sh", "-c", command, NULL); exit(EXIT_FAILURE); } out_clean: close(fd[1]); out_clean0: close(fd[0]); return err; } static int _exec_updown(const tap_t tap, const char *action, char **error_string) { char command[PATH_MAX]; struct stat sb; int err = 0; if (!tap->hasupdown) return 0; memset(command, 0, PATH_MAX); snprintf(command, PATH_MAX, "%s%s/%s", tap->updownpath, action, tap->tapname); err = stat(command, &sb); if ((err < 0) && (errno == ENOENT)) return 0; err = _execute_shell(command, error_string); if ((!err) && (*error_string)) { free(*error_string); *error_string = NULL; } return err; } static int _check(const tap_t tap) { tap_t temp = lib_cfg.head; if (!tap) { return 0; } while (temp != NULL) { if (tap == temp) return 1; temp = temp->next; } return 0; } static void _close(tap_t tap) { if (!tap) return; if (tap->fd) close(tap->fd); free(tap); return; } static void _close_cfg(void) { if (lib_cfg.head == NULL) { close(lib_cfg.sockfd); lib_init = 0; } } static int _get_mtu(const tap_t tap) { int err; memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, tap->tapname, IFNAMSIZ); err = ioctl(lib_cfg.sockfd, SIOCGIFMTU, &tap->ifr); if (err) goto out_clean; err = tap->ifr.ifr_mtu; out_clean: return err; } static int _get_mac(const tap_t tap, char **ether_addr) { int err; char mac[MAX_MAC_CHAR]; memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, tap->tapname, IFNAMSIZ); err = ioctl(lib_cfg.sockfd, SIOCGIFHWADDR, &tap->ifr); if (err) goto out_clean; ether_ntoa_r((struct ether_addr *)tap->ifr.ifr_hwaddr.sa_data, mac); *ether_addr = strdup(mac); if (!*ether_addr) err = -1; out_clean: return err; } tap_t tap_find(char *dev, size_t dev_size) { tap_t tap; if (dev == NULL) { errno = EINVAL; return NULL; } if (dev_size < IFNAMSIZ) { errno = EINVAL; return NULL; } if (strlen(dev) > IFNAMSIZ) { errno = E2BIG; return NULL; } pthread_mutex_lock(&lib_mutex); tap = lib_cfg.head; while (tap != NULL) { if (!strcmp(dev, tap->tapname)) break; tap = tap->next; } pthread_mutex_unlock(&lib_mutex); return tap; } tap_t tap_open(char *dev, size_t dev_size, const char *updownpath) { tap_t tap; char *temp_mac = NULL; if (dev == NULL) { errno = EINVAL; return NULL; } if (dev_size < IFNAMSIZ) { errno = EINVAL; return NULL; } if (strlen(dev) > IFNAMSIZ) { errno = E2BIG; return NULL; } if (updownpath) { /* only absolute paths */ if (updownpath[0] != '/') { errno = EINVAL; return NULL; } /* 14: 2 for /, 1 for \0 + 11 (post-down.d) */ if (strlen(updownpath) >= (PATH_MAX - (strlen(dev) + 14))) { errno = E2BIG; return NULL; } } pthread_mutex_lock(&lib_mutex); tap = malloc(sizeof(struct _iface)); if (!tap) return NULL; memset(tap, 0, sizeof(struct _iface)); if ((tap->fd = open("/dev/net/tun", O_RDWR)) < 0) goto out_error; strncpy(tap->tapname, dev, IFNAMSIZ); memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, dev, IFNAMSIZ); tap->ifr.ifr_flags = IFF_TAP | IFF_NO_PI; if (ioctl(tap->fd, TUNSETIFF, &tap->ifr) < 0) goto out_error; if ((strlen(dev) > 0) && (strcmp(dev, tap->ifname) != 0)) { errno = EBUSY; goto out_error; } strcpy(dev, tap->ifname); strcpy(tap->tapname, tap->ifname); if (!lib_init) { lib_cfg.head = NULL; lib_cfg.sockfd = socket(AF_INET, SOCK_STREAM, 0); if (lib_cfg.sockfd < 0) goto out_error; lib_init = 1; } memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, tap->tapname, IFNAMSIZ); if (ioctl(lib_cfg.sockfd, SIOGIFINDEX, &tap->ifr) < 0) goto out_error; tap->default_mtu = _get_mtu(tap); if (tap->default_mtu < 0) goto out_error; if (_get_mac(tap, &temp_mac) < 0) goto out_error; strncpy(tap->default_mac, temp_mac, 18); free(temp_mac); if (updownpath) { int len = strlen(updownpath); strcpy(tap->updownpath, updownpath); if (tap->updownpath[len-1] != '/') { tap->updownpath[len] = '/'; } tap->hasupdown = 1; } tap->next = lib_cfg.head; lib_cfg.head = tap; pthread_mutex_unlock(&lib_mutex); return tap; out_error: _close(tap); _close_cfg(); pthread_mutex_unlock(&lib_mutex); return NULL; } // TODO: consider better error report from here int tap_close(tap_t tap) { int err = 0; tap_t temp = lib_cfg.head; tap_t prev = lib_cfg.head; struct _ip *ip, *ip_next; char *error_string = NULL; char *error_down = NULL, *error_postdown = NULL; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; err = -1; goto out_clean; } while ((temp) && (temp != tap)) { prev = temp; temp = temp->next; } if (tap == prev) { lib_cfg.head = tap->next; } else { prev->next = tap->next; } _set_down(tap, &error_down, &error_postdown); if (error_down) free(error_down); if (error_postdown) free(error_postdown); ip = tap->ip; while (ip) { ip_next = ip->next; _set_ip(tap, "del", ip->ip_addr, ip->prefix, &error_string); if (error_string) { free(error_string); error_string = NULL; } free(ip); ip = ip_next; } _close(tap); _close_cfg(); out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_get_mtu(const tap_t tap) { int err; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; err = -1; goto out_clean; } err = _get_mtu(tap); out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_set_mtu(tap_t tap, const int mtu) { struct _ip *tmp_ip; char *error_string = NULL; int err; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; err = -1; goto out_clean; } err = tap->current_mtu = _get_mtu(tap); if (err < 0) goto out_clean; tap->ifr.ifr_mtu = mtu; err = ioctl(lib_cfg.sockfd, SIOCSIFMTU, &tap->ifr); if ((!err) && (tap->current_mtu < 1280) && (mtu >= 1280)) { tmp_ip = tap->ip; while(tmp_ip) { if (tmp_ip->domain == AF_INET6) { err = _set_ip(tap, "add", tmp_ip->ip_addr, tmp_ip->prefix, &error_string); if (error_string) { free(error_string); error_string = NULL; } } tmp_ip = tmp_ip->next; } } out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_reset_mtu(tap_t tap) { return tap_set_mtu(tap, tap->default_mtu); } int tap_get_mac(const tap_t tap, char **ether_addr) { int err; pthread_mutex_lock(&lib_mutex); if ((!_check(tap)) || (!ether_addr)) { errno = EINVAL; err = -1; goto out_clean; } err = _get_mac(tap, ether_addr); out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_set_mac(tap_t tap, const char *ether_addr) { int err; pthread_mutex_lock(&lib_mutex); if ((!_check(tap)) || (!ether_addr)) { errno = EINVAL; err = -1; goto out_clean; } memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, tap->tapname, IFNAMSIZ); err = ioctl(lib_cfg.sockfd, SIOCGIFHWADDR, &tap->ifr); if (err) goto out_clean; - memcpy(tap->ifr.ifr_hwaddr.sa_data, ether_aton(ether_addr), ETH_ALEN); + memmove(tap->ifr.ifr_hwaddr.sa_data, ether_aton(ether_addr), ETH_ALEN); err = ioctl(lib_cfg.sockfd, SIOCSIFHWADDR, &tap->ifr); out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_reset_mac(tap_t tap) { return tap_set_mac(tap, tap->default_mac); } int tap_set_up(tap_t tap, char **error_preup, char **error_up) { int err = 0; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; err = -1; goto out_clean; } if ((tap->hasupdown) && ((!error_preup) || (!error_up))) { errno = EINVAL; err = -1; goto out_clean; } if (tap->up) goto out_clean; memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, tap->tapname, IFNAMSIZ); err=ioctl(lib_cfg.sockfd, SIOCGIFFLAGS, &tap->ifr); if (err) goto out_clean; _exec_updown(tap, "pre-up.d", error_preup); tap->ifr.ifr_flags |= IFF_UP | IFF_RUNNING; err=ioctl(lib_cfg.sockfd, SIOCSIFFLAGS, &tap->ifr); if (err) goto out_clean; _exec_updown(tap, "up.d", error_up); tap->up = 1; out_clean: pthread_mutex_unlock(&lib_mutex); return err; } static int _set_down(tap_t tap, char **error_down, char **error_postdown) { int err = 0; if (!tap->up) goto out_clean; memset(&tap->ifr, 0, sizeof(struct ifreq)); strncpy(tap->ifname, tap->tapname, IFNAMSIZ); err=ioctl(lib_cfg.sockfd, SIOCGIFFLAGS, &tap->ifr); if (err) goto out_clean; _exec_updown(tap, "down.d", error_down); tap->ifr.ifr_flags &= ~IFF_UP; err=ioctl(lib_cfg.sockfd, SIOCSIFFLAGS, &tap->ifr); if (err) goto out_clean; _exec_updown(tap, "post-down.d", error_postdown); tap->up = 0; out_clean: return err; } int tap_set_down(tap_t tap, char **error_down, char **error_postdown) { int err = 0; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; err = -1; goto out_clean; } if ((tap->hasupdown) && ((!error_down) || (!error_postdown))) { errno = EINVAL; err = -1; goto out_clean; } err = _set_down(tap, error_down, error_postdown); out_clean: pthread_mutex_unlock(&lib_mutex); return err; } static char *_get_v4_broadcast(const char *ip_addr, const char *prefix) { int prefix_len; struct in_addr mask; struct in_addr broadcast; struct in_addr address; prefix_len = atoi(prefix); if ((prefix_len > 32) || (prefix_len < 0)) return NULL; if (inet_pton(AF_INET, ip_addr, &address) <= 0) return NULL; mask.s_addr = htonl(~((1 << (32 - prefix_len)) - 1)); memset(&broadcast, 0, sizeof(broadcast)); broadcast.s_addr = (address.s_addr & mask.s_addr) | ~mask.s_addr; return strdup(inet_ntoa(broadcast)); } static int _set_ip(tap_t tap, const char *command, const char *ip_addr, const char *prefix, char **error_string) { char *broadcast = NULL; char cmdline[4096]; if (!strchr(ip_addr, ':')) { broadcast = _get_v4_broadcast(ip_addr, prefix); if (!broadcast) { errno = EINVAL; return -1; } } memset(cmdline, 0, sizeof(cmdline)); if (broadcast) { snprintf(cmdline, sizeof(cmdline)-1, "ip addr %s %s/%s dev %s broadcast %s", command, ip_addr, prefix, tap->tapname, broadcast); free(broadcast); } else { snprintf(cmdline, sizeof(cmdline)-1, "ip addr %s %s/%s dev %s", command, ip_addr, prefix, tap->tapname); } return _execute_shell(cmdline, error_string); } static int _find_ip(tap_t tap, const char *ip_addr, const char *prefix, struct _ip **ip, struct _ip **ip_prev) { struct _ip *local_ip, *local_ip_prev; int found = 0; local_ip = local_ip_prev = tap->ip; while(local_ip) { if ((!strcmp(local_ip->ip_addr, ip_addr)) && (!strcmp(local_ip->prefix, prefix))) { found = 1; break; } local_ip_prev = local_ip; local_ip = local_ip->next; } if (found) { *ip = local_ip; *ip_prev = local_ip_prev; } return found; } int tap_add_ip(tap_t tap, const char *ip_addr, const char *prefix, char **error_string) { int err = 0, found; struct _ip *ip = NULL, *ip_prev = NULL, *ip_last = NULL; pthread_mutex_lock(&lib_mutex); if ((!_check(tap)) || (!ip_addr) || (!prefix) || (!error_string)) { errno = EINVAL; err = -1; goto out_clean; } found = _find_ip(tap, ip_addr, prefix, &ip, &ip_prev); if (found) goto out_clean; ip = malloc(sizeof(struct _ip)); if (!ip) { err = -1 ; goto out_clean; } memset(ip, 0, sizeof(struct _ip)); strncpy(ip->ip_addr, ip_addr, MAX_IP_CHAR); strncpy(ip->prefix, prefix, MAX_PREFIX_CHAR); if (!strchr(ip->ip_addr, ':')) { ip->domain = AF_INET; } else { ip->domain = AF_INET6; } /* * if user asks for an IPv6 address, but MTU < 1280 * store the IP and bring it up later if and when MTU > 1280 */ if ((ip->domain == AF_INET6) && (_get_mtu(tap) < 1280)) { err = 0; } else { err = _set_ip(tap, "add", ip_addr, prefix, error_string); } if (err) { free(ip); goto out_clean; } if (tap->ip) { ip_last = tap->ip; while (ip_last->next != NULL) { ip_last = ip_last->next; } ip_last->next = ip; } else { tap->ip = ip; } out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_del_ip(tap_t tap, const char *ip_addr, const char *prefix, char **error_string) { int err = 0, found; struct _ip *ip = NULL, *ip_prev = NULL; pthread_mutex_lock(&lib_mutex); if ((!_check(tap)) || (!ip_addr) || (!prefix) || (!error_string)) { errno = EINVAL; err = -1; goto out_clean; } found = _find_ip(tap, ip_addr, prefix, &ip, &ip_prev); if (!found) goto out_clean; err = _set_ip(tap, "del", ip_addr, prefix, error_string); if (!err) { if (ip == ip_prev) { tap->ip = ip->next; } else { ip_prev->next = ip->next; } free(ip); } out_clean: pthread_mutex_unlock(&lib_mutex); return err; } int tap_get_fd(const tap_t tap) { int fd; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; fd = -1; goto out_clean; } fd = tap->fd; out_clean: pthread_mutex_unlock(&lib_mutex); return fd; } const char *tap_get_name(const tap_t tap) { char *name = NULL; pthread_mutex_lock(&lib_mutex); if (!_check(tap)) { errno = EINVAL; goto out_clean; } name = tap->tapname; out_clean: pthread_mutex_unlock(&lib_mutex); return name; } int tap_get_ips(const tap_t tap, char **ip_addr_list, int *entries) { int err = 0; int found = 0; char *ip_list = NULL; int size = 0, offset = 0, len; struct _ip *ip = tap->ip; pthread_mutex_lock(&lib_mutex); while (ip) { found++; ip = ip->next; } size = found * (MAX_IP_CHAR + MAX_PREFIX_CHAR + 2); ip_list = malloc(size); if (!ip_list) { err = -1; goto out_clean; } memset(ip_list, 0, size); ip = tap->ip; while (ip) { len = strlen(ip->ip_addr); - memcpy(ip_list + offset, ip->ip_addr, len); + memmove(ip_list + offset, ip->ip_addr, len); offset = offset + len + 1; len = strlen(ip->prefix); - memcpy(ip_list + offset, ip->prefix, len); + memmove(ip_list + offset, ip->prefix, len); offset = offset + len + 1; ip = ip->next; } *ip_addr_list = ip_list; *entries = found; out_clean: pthread_mutex_unlock(&lib_mutex); return err; } #ifdef TEST static int is_if_in_system(char *name) { struct ifaddrs *ifap = NULL; struct ifaddrs *ifa; int found = 0; if (getifaddrs(&ifap) < 0) { printf("Unable to get interface list.\n"); return -1; } ifa = ifap; while (ifa) { if (!strncmp(name, ifa->ifa_name, IFNAMSIZ)) { found = 1; break; } ifa=ifa->ifa_next; } freeifaddrs(ifap); return found; } static int test_iface(char *name, size_t size, const char *updownpath) { tap_t tap; tap=tap_open(name, size, updownpath); if (!tap) { if (lib_cfg.sockfd < 0) printf("Unable to open knet_socket\n"); printf("Unable to open knet.\n"); return -1; } printf("Created interface: %s\n", name); if (is_if_in_system(name) > 0) { printf("Found interface %s on the system\n", name); } else { printf("Unable to find interface %s on the system\n", name); } if (!tap_find(name, size)) { printf("Unable to find interface %s in tap db\n", name); } else { printf("Found interface %s in tap db\n", name); } tap_close(tap); if (is_if_in_system(name) == 0) printf("Successfully removed interface %s from the system\n", name); return 0; } static int check_tap_open_close(void) { char device_name[2*IFNAMSIZ]; char fakepath[PATH_MAX]; size_t size = IFNAMSIZ; memset(device_name, 0, sizeof(device_name)); printf("Creating random tap interface:\n"); if (test_iface(device_name, size, NULL) < 0) { printf("Unable to create random interface\n"); return -1; } printf("Creating kronostest tap interface:\n"); strncpy(device_name, "kronostest", IFNAMSIZ); if (test_iface(device_name, size, NULL) < 0) { printf("Unable to create kronosnet interface\n"); return -1; } printf("Testing ERROR conditions\n"); printf("Testing dev == NULL\n"); errno=0; if ((test_iface(NULL, size, NULL) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_open sanity checks\n"); return -1; } printf("Testing size < IFNAMSIZ\n"); errno=0; if ((test_iface(device_name, 1, NULL) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_open sanity checks\n"); return -1; } printf("Testing device_name size > IFNAMSIZ\n"); errno=0; strcpy(device_name, "abcdefghilmnopqrstuvwz"); if ((test_iface(device_name, IFNAMSIZ, NULL) >= 0) || (errno != E2BIG)) { printf("Something is wrong in tap_open sanity checks\n"); return -1; } printf("Testing updown path != abs\n"); errno=0; strcpy(device_name, "kronostest"); if ((test_iface(device_name, IFNAMSIZ, "foo") >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_open sanity checks\n"); return -1; } memset(fakepath, 0, PATH_MAX); memset(fakepath, '/', PATH_MAX - 2); printf("Testing updown path > PATH_MAX\n"); errno=0; strcpy(device_name, "kronostest"); if ((test_iface(device_name, IFNAMSIZ, fakepath) >= 0) || (errno != E2BIG)) { printf("Something is wrong in tap_open sanity checks\n"); return -1; } return 0; } static int check_knet_multi_eth(void) { char device_name1[IFNAMSIZ]; char device_name2[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap1 = NULL; tap_t tap2 = NULL; printf("Testing multiple knet interface instances\n"); memset(device_name1, 0, size); memset(device_name2, 0, size); strncpy(device_name1, "kronostest1", size); strncpy(device_name2, "kronostest2", size); tap1 = tap_open(device_name1, size, NULL); if (!tap1) { printf("Unable to init %s\n", device_name1); err = -1; goto out_clean; } if (is_if_in_system(device_name1) > 0) { printf("Found interface %s on the system\n", device_name1); } else { printf("Unable to find interface %s on the system\n", device_name1); } tap2 = tap_open(device_name2, size, NULL); if (!tap2) { printf("Unable to init %s\n", device_name2); err = -1; goto out_clean; } if (is_if_in_system(device_name2) > 0) { printf("Found interface %s on the system\n", device_name2); } else { printf("Unable to find interface %s on the system\n", device_name2); } if (tap1) tap_close(tap1); if (tap2) tap_close(tap2); printf("Testing error conditions\n"); printf("Open same device twice\n"); tap1 = tap_open(device_name1, size, NULL); if (!tap1) { printf("Unable to init %s\n", device_name1); err = -1; goto out_clean; } if (is_if_in_system(device_name1) > 0) { printf("Found interface %s on the system\n", device_name1); } else { printf("Unable to find interface %s on the system\n", device_name1); } tap2 = tap_open(device_name1, size, NULL); if (tap2) { printf("We were able to init 2 interfaces with the same name!\n"); err = -1; goto out_clean; } out_clean: if (tap1) tap_close(tap1); if (tap2) tap_close(tap2); return err; } static int check_knet_mtu(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap; int current_mtu = 0; int expected_mtu = 1500; printf("Testing get/set MTU\n"); memset(device_name, 0, size); strncpy(device_name, "kronostest", size); tap = tap_open(device_name, size, NULL); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Comparing default MTU\n"); current_mtu = tap_get_mtu(tap); if (current_mtu < 0) { printf("Unable to get MTU\n"); err = -1; goto out_clean; } if (current_mtu != expected_mtu) { printf("current mtu [%d] does not match expected default [%d]\n", current_mtu, expected_mtu); err = -1; goto out_clean; } printf("Setting MTU to 9000\n"); expected_mtu = 9000; if (tap_set_mtu(tap, expected_mtu) < 0) { printf("Unable to set MTU to %d\n", expected_mtu); err = -1; goto out_clean; } current_mtu = tap_get_mtu(tap); if (current_mtu < 0) { printf("Unable to get MTU\n"); err = -1; goto out_clean; } if (current_mtu != expected_mtu) { printf("current mtu [%d] does not match expected value [%d]\n", current_mtu, expected_mtu); err = -1; goto out_clean; } printf("Testing ERROR conditions\n"); printf("Passing empty struct to get_mtu\n"); if (tap_get_mtu(NULL) > 0) { printf("Something is wrong in tap_get_mtu sanity checks\n"); err = -1; goto out_clean; } printf("Passing empty struct to set_mtu\n"); if (tap_set_mtu(NULL, 1500) == 0) { printf("Something is wrong in tap_set_mtu sanity checks\n"); err = -1; goto out_clean; } out_clean: tap_close(tap); return err; } static int check_knet_mtu_ipv6(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap; char *error_string = NULL; printf("Testing get/set MTU with IPv6 address\n"); memset(device_name, 0, size); strncpy(device_name, "kronostest", size); tap = tap_open(device_name, size, NULL); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Adding ip: 3ffe::1/64\n"); err = tap_add_ip(tap, "3ffe::1", "64", &error_string); if (error_string) { printf("add ipv6 output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::1/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Setting MTU to 1200\n"); if (tap_set_mtu(tap, 1200) < 0) { printf("Unable to set MTU to 1200\n"); err = -1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::1/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Adding ip: 3ffe::2/64\n"); err = tap_add_ip(tap, "3ffe::2", "64", &error_string); if (error_string) { printf("add ipv6 output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::2/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Restoring MTU to default\n"); if (tap_reset_mtu(tap) < 0) { printf("Unable to reset mtu\n"); err = -1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::1/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::2/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } out_clean: tap_close(tap); return err; } static int check_knet_mac(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap; char *current_mac = NULL, *temp_mac = NULL, *err_mac = NULL; struct ether_addr *cur_mac, *tmp_mac; printf("Testing get/set MAC\n"); memset(device_name, 0, size); strncpy(device_name, "kronostest", size); tap = tap_open(device_name, size, NULL); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Get current MAC\n"); if (tap_get_mac(tap, ¤t_mac) < 0) { printf("Unable to get current MAC address.\n"); err = -1; goto out_clean; } printf("Current MAC: %s\n", current_mac); printf("Setting MAC: 00:01:01:01:01:01\n"); if (tap_set_mac(tap, "00:01:01:01:01:01") < 0) { printf("Unable to set current MAC address.\n"); err = -1; goto out_clean; } if (tap_get_mac(tap, &temp_mac) < 0) { printf("Unable to get current MAC address.\n"); err = -1; goto out_clean; } printf("Current MAC: %s\n", temp_mac); cur_mac = ether_aton(current_mac); tmp_mac = ether_aton(temp_mac); printf("Comparing MAC addresses\n"); if (memcmp(cur_mac, tmp_mac, sizeof(struct ether_addr))) { printf("Mac addresses are not the same?!\n"); err = -1; goto out_clean; } printf("Testing ERROR conditions\n"); printf("Pass NULL to get_mac (pass1)\n"); errno = 0; if ((tap_get_mac(NULL, &err_mac) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_get_mac sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to get_mac (pass2)\n"); errno = 0; if ((tap_get_mac(tap, NULL) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_get_mac sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to set_mac (pass1)\n"); errno = 0; if ((tap_set_mac(tap, NULL) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_mac sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to set_mac (pass2)\n"); errno = 0; if ((tap_set_mac(NULL, err_mac) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_mac sanity checks\n"); err = -1; goto out_clean; } out_clean: if (err_mac) { printf("Something managed to set err_mac!\n"); err = -1; free(err_mac); } if (current_mac) free(current_mac); if (temp_mac) free(temp_mac); tap_close(tap); return err; } static int check_tap_execute_shell(void) { int err = 0; char command[4096]; char *error_string = NULL; memset(command, 0, sizeof(command)); printf("Testing _execute_shell\n"); printf("command /bin/true\n"); err = _execute_shell("/bin/true", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to execute /bin/true ?!?!\n"); goto out_clean; } printf("Testing ERROR conditions\n"); printf("command /bin/false\n"); err = _execute_shell("/bin/false", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute /bin/false successfully?!?!\n"); err = -1; goto out_clean; } printf("command that outputs to stdout (enforcing redirect)\n"); err = _execute_shell("/bin/grep -h 2>&1", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute /bin/grep -h successfully?!?\n"); err = -1; goto out_clean; } printf("command that outputs to stderr\n"); err = _execute_shell("/bin/grep -h", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute /bin/grep -h successfully?!?\n"); err = -1; goto out_clean; } printf("empty command\n"); err = _execute_shell(NULL, &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute (nil) successfully?!?!\n"); err = -1; goto out_clean; } printf("empty error\n"); err = _execute_shell("/bin/true", NULL); if (!err) { printf("Check EINVAL filter for no error_string!\n"); err = -1; goto out_clean; } err = 0; out_clean: return err; } static int check_knet_up_down(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap; char *error_string = NULL; char *error_preup = NULL, *error_up = NULL; char *error_down = NULL, *error_postdown = NULL; printf("Testing interface up/down\n"); memset(device_name, 0, size); strncpy(device_name, "kronostest", size); tap = tap_open(device_name, size, NULL); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Put the interface up\n"); err = tap_set_up(tap, &error_preup, &error_up); if (error_preup) { printf("preup output: %s\n", error_preup); free(error_preup); error_preup = NULL; } if (error_up) { printf("up output: %s\n", error_up); free(error_up); error_up = NULL; } if (err < 0) { printf("Unable to set interface up\n"); err = -1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q UP", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to verify inteface UP\n"); err = -1; goto out_clean; } printf("Put the interface down\n"); err = tap_set_down(tap, &error_down, &error_postdown); if (error_down) { printf("down output: %s\n", error_down); free(error_down); error_down = NULL; } if (error_postdown) { printf("postdown output: %s\n", error_down); free(error_down); error_down = NULL; } if (err < 0) { printf("Unable to put the interface down\n"); err = -1; goto out_clean; } err = _execute_shell("ifconfig kronostest | grep -q UP", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify inteface DOWN\n"); err = -1; goto out_clean; } tap_close(tap); printf("Testing interface pre-up/up/down/post-down (exec errors)\n"); tap = tap_open(device_name, size, ABSBUILDDIR "/tap_updown_bad"); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Put the interface up\n"); err = tap_set_up(tap, &error_preup, &error_up); if (error_preup) { printf("preup output: %s\n", error_preup); free(error_preup); error_preup = NULL; } if (error_up) { printf("up output: %s\n", error_up); free(error_up); error_up = NULL; } if (err < 0) { printf("Unable to set interface up\n"); err = -1; goto out_clean; } printf("Put the interface down\n"); err = tap_set_down(tap, &error_down, &error_postdown); if (error_down) { printf("down output: %s\n", error_down); free(error_down); error_down = NULL; } if (error_postdown) { printf("postdown output: %s\n", error_down); free(error_down); error_down = NULL; } if (err < 0) { printf("Unable to put the interface down\n"); err = -1; goto out_clean; } tap_close(tap); printf("Testing interface pre-up/up/down/post-down\n"); tap = tap_open(device_name, size, ABSBUILDDIR "/tap_updown_good"); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Put the interface up\n"); err = tap_set_up(tap, &error_preup, &error_up); if (error_preup) { printf("preup output: %s\n", error_preup); free(error_preup); error_preup = NULL; } if (error_up) { printf("up output: %s\n", error_up); free(error_up); error_up = NULL; } if (err < 0) { printf("Unable to set interface up\n"); err = -1; goto out_clean; } printf("Put the interface down\n"); err = tap_set_down(tap, &error_down, &error_postdown); if (error_down) { printf("down output: %s\n", error_down); free(error_down); error_down = NULL; } if (error_postdown) { printf("postdown output: %s\n", error_down); free(error_down); error_down = NULL; } if (err < 0) { printf("Unable to put the interface down\n"); err = -1; goto out_clean; } tap_close(tap); printf("Test ERROR conditions\n"); printf("Pass NULL to tap set_up\n"); errno = 0; if ((tap_set_up(NULL, &error_preup, &error_up) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_up sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to error_preup set_up\n"); errno = 0; if ((tap_set_up(tap, NULL, &error_up) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_up sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to error_up set_up\n"); errno = 0; if ((tap_set_up(tap, &error_preup, NULL) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_up sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to tap set_down\n"); errno = 0; if ((tap_set_down(NULL, &error_down, &error_postdown) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_down sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to error_down set_down\n"); errno = 0; if ((tap_set_down(tap, NULL, &error_postdown) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_down sanity checks\n"); err = -1; goto out_clean; } printf("Pass NULL to error_postdown set_down\n"); errno = 0; if ((tap_set_down(tap, &error_down, NULL) >= 0) || (errno != EINVAL)) { printf("Something is wrong in tap_set_down sanity checks\n"); err = -1; goto out_clean; } out_clean: tap_close(tap); return err; } static int check_knet_close_leak(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap; char *error_string = NULL; printf("Testing close leak (needs valgrind)\n"); memset(device_name, 0, size); strncpy(device_name, "kronostest", size); tap = tap_open(device_name, size, NULL); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Adding ip: 192.168.168.168/24\n"); err = tap_add_ip(tap, "192.168.168.168", "24", &error_string); if (error_string) { printf("add ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } printf("Adding ip: 192.168.169.169/24\n"); err = tap_add_ip(tap, "192.168.169.169", "24", &error_string); if (error_string) { printf("add ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } out_clean: tap_close(tap); return err; } static int check_knet_set_del_ip(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; int err=0; tap_t tap; char *ip_list = NULL; int ip_list_entries = 0, i, offset = 0; char *error_string = NULL; printf("Testing interface add/remove ip\n"); memset(device_name, 0, size); strncpy(device_name, "kronostest", size); tap = tap_open(device_name, size, NULL); if (!tap) { printf("Unable to init %s\n", device_name); return -1; } printf("Adding ip: 192.168.168.168/24\n"); err = tap_add_ip(tap, "192.168.168.168", "24", &error_string); if (error_string) { printf("add ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } printf("Adding ip: 192.168.169.169/24\n"); err = tap_add_ip(tap, "192.168.169.169", "24", &error_string); if (error_string) { printf("add ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } printf("Adding duplicate ip: 192.168.168.168/24\n"); err = tap_add_ip(tap, "192.168.168.168", "24", &error_string); if (error_string) { printf("add ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to find IP address in libtap db\n"); err=-1; goto out_clean; } printf("Checking ip: 192.168.168.168/24\n"); err = _execute_shell("ip addr show dev kronostest | grep -q 192.168.168.168/24", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Get ip list from libtap:\n"); if (tap_get_ips(tap, &ip_list, &ip_list_entries) < 0) { printf("Not enough mem?\n"); err=-1; goto out_clean; } if (ip_list_entries != 2) { printf("Didn't get enough ip back from libtap?\n"); err=-1; goto out_clean; } for (i = 1; i <= ip_list_entries; i++) { printf("Found IP %s %s in libtap db\n", ip_list + offset, ip_list + offset + strlen(ip_list + offset) + 1); offset = offset + strlen(ip_list) + 1; offset = offset + strlen(ip_list + offset) + 1; } free(ip_list); printf("Deleting ip: 192.168.168.168/24\n"); err = tap_del_ip(tap, "192.168.168.168", "24", &error_string); if (error_string) { printf("del ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } printf("Deleting ip: 192.168.169.169/24\n"); err = tap_del_ip(tap, "192.168.169.169", "24", &error_string); if (error_string) { printf("del ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } printf("Deleting again ip: 192.168.168.168/24\n"); err = tap_del_ip(tap, "192.168.168.168", "24", &error_string); if (error_string) { printf("del ip output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 192.168.168.168/24", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Adding ip: 3ffe::1/64\n"); err = tap_add_ip(tap, "3ffe::1", "64", &error_string); if (error_string) { printf("add ipv6 output: %s\n", error_string); free(error_string); error_string = NULL; } if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::1/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Deleting ip: 3ffe::1/64\n"); err = tap_del_ip(tap, "3ffe::1", "64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } err = _execute_shell("ip addr show dev kronostest | grep -q 3ffe::1/64", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } out_clean: tap_close(tap); return err; } int main(void) { if (geteuid() != 0) { printf("This test requires root privileges\n"); exit(77); } if (check_tap_open_close() < 0) return -1; if (check_knet_multi_eth() < 0) return -1; if (check_knet_mtu() < 0) return -1; if (check_knet_mtu_ipv6() < 0) return -1; if (check_knet_mac() < 0) return -1; if (check_tap_execute_shell() < 0) return -1; if (check_knet_up_down() < 0) return -1; if (check_knet_set_del_ip() < 0) return -1; if (check_knet_close_leak() < 0) return -1; return 0; } #endif diff --git a/poc-code/access-list/ipcheck.c b/poc-code/access-list/ipcheck.c index b71ac5c1..c151e440 100644 --- a/poc-code/access-list/ipcheck.c +++ b/poc-code/access-list/ipcheck.c @@ -1,210 +1,210 @@ #include #include #include #include #include #include "ipcheck.h" struct ip_match_entry { ipcheck_type_t type; ipcheck_acceptreject_t acceptreject; struct sockaddr_storage addr1; /* Actual IP address, mask top or low IP */ struct sockaddr_storage addr2; /* high IP address or address bitmask */ struct ip_match_entry *next; }; /* Lists of things to match against. These are dummy structs to provide a quick list head */ static struct ip_match_entry match_entry_head_v4; static struct ip_match_entry match_entry_head_v6; /* * IPv4 See if the address we have matches the current match entry * */ static int ip_matches_v4(struct sockaddr_storage *checkip, struct ip_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 IPCHECK_TYPE_ADDRESS: if (ip_to_check->sin_addr.s_addr == match1->sin_addr.s_addr) return 1; break; case IPCHECK_TYPE_MASK: if ((ip_to_check->sin_addr.s_addr & match2->sin_addr.s_addr) == match1->sin_addr.s_addr) return 1; break; case IPCHECK_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 ip_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 IPCHECK_TYPE_ADDRESS: if (!memcmp(ip_to_check->sin6_addr.s6_addr32, match1->sin6_addr.s6_addr32, sizeof(struct in6_addr))) return 1; break; case IPCHECK_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 IPCHECK_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; } /* * YOU ARE HERE */ int ipcheck_validate(struct sockaddr_storage *checkip) { struct ip_match_entry *match_entry; int (*match_fn)(struct sockaddr_storage *checkip, struct ip_match_entry *match_entry); if (checkip->ss_family == AF_INET){ match_entry = match_entry_head_v4.next; match_fn = ip_matches_v4; } else { match_entry = match_entry_head_v6.next; match_fn = ip_matches_v6; } while (match_entry) { if (match_fn(checkip, match_entry)) { if (match_entry->acceptreject == IPCHECK_ACCEPT) return 1; else return 0; } match_entry = match_entry->next; } return 0; /* Default reject */ } /* * Routines to manuipulate the lists */ void ipcheck_clear(void) { struct ip_match_entry *match_entry; struct ip_match_entry *next_match_entry; match_entry = match_entry_head_v4.next; while (match_entry) { next_match_entry = match_entry->next; free(match_entry); match_entry = next_match_entry; } match_entry = match_entry_head_v6.next; while (match_entry) { next_match_entry = match_entry->next; free(match_entry); match_entry = next_match_entry; } } int ipcheck_addip(struct sockaddr_storage *ip1, struct sockaddr_storage *ip2, ipcheck_type_t type, ipcheck_acceptreject_t acceptreject) { struct ip_match_entry *match_entry; struct ip_match_entry *new_match_entry; if (type == IPCHECK_TYPE_RANGE && (ip1->ss_family != ip2->ss_family)) return -1; if (ip1->ss_family == AF_INET){ match_entry = &match_entry_head_v4; } else { match_entry = &match_entry_head_v6; } new_match_entry = malloc(sizeof(struct ip_match_entry)); if (!new_match_entry) return -1; - memcpy(&new_match_entry->addr1, ip1, sizeof(struct sockaddr_storage)); - memcpy(&new_match_entry->addr2, ip2, sizeof(struct sockaddr_storage)); + 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; /* 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; return 0; } diff --git a/poc-code/access-list/test_ipcheck.c b/poc-code/access-list/test_ipcheck.c index 851efebd..520e2aac 100644 --- a/poc-code/access-list/test_ipcheck.c +++ b/poc-code/access-list/test_ipcheck.c @@ -1,185 +1,185 @@ #include #include #include #include #include #include #include #include #include "ipcheck.h" /* 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) { - memcpy(addr, info->ai_addr, info->ai_addrlen); + memmove(addr, info->ai_addr, info->ai_addrlen); free(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; ipcheck_type_t type; ipcheck_acceptreject_t acceptreject; struct sockaddr_storage addr1; struct sockaddr_storage addr2; ipcheck_clear(); filterfile = fopen("test_ipcheck.txt", "r"); if (!filterfile) { fprintf(stderr, "Cannot open test_ipcheck.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 = IPCHECK_ACCEPT; break; case 'R': acceptreject = IPCHECK_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 = IPCHECK_TYPE_ADDRESS; ret = read_address(filebuf+2, &addr1); break; case 'M': type = IPCHECK_TYPE_MASK; ret = read_mask(filebuf+2, &addr1, &addr2); break; case 'R': type = IPCHECK_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 { ipcheck_addip(&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; int ret; int i; if (load_file()) return 1; for (i=1; i