diff --git a/libknet/etherfilter.c b/libknet/etherfilter.c index 036913c9..a6199dcf 100644 --- a/libknet/etherfilter.c +++ b/libknet/etherfilter.c @@ -1,53 +1,51 @@ #include "config.h" #include "libknet.h" #include #include /* * stole from linux kernel/include/linux/etherdevice.h */ static inline int is_zero_ether_addr(const uint8_t *addr) { return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); } static inline int is_multicast_ether_addr(const uint8_t *addr) { return 0x01 & addr[0]; } static inline int is_broadcast_ether_addr(const uint8_t *addr) { return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff; } int ether_host_filter_fn (const unsigned char *outdata, ssize_t outdata_len, uint16_t src_node_id, uint16_t *dst_host_ids, size_t *dst_host_ids_entries) { struct ether_header *eth_h = (struct ether_header *)outdata; uint8_t *dst_mac = (uint8_t *)eth_h->ether_dhost; - uint16_t *dst_host_id_map; uint16_t dst_host_id; if (is_zero_ether_addr(dst_mac)) return -1; if (is_multicast_ether_addr(dst_mac) || is_broadcast_ether_addr(dst_mac)) { return 1; } - dst_host_id_map = (uint16_t *)&dst_mac[4]; - dst_host_id = ntohs(dst_host_id_map[0]); + memcpy(&dst_host_id, &dst_mac[4], 2); - dst_host_ids[0] = dst_host_id; + dst_host_ids[0] = ntohs(dst_host_id); *dst_host_ids_entries = 1; return 0; } diff --git a/libknet/handle.c b/libknet/handle.c index 13b222ec..158196a0 100644 --- a/libknet/handle.c +++ b/libknet/handle.c @@ -1,550 +1,553 @@ #include "config.h" #include #include #include #include #include #include #include "libknet-private.h" #include "nsscrypto.h" #define KNET_MAX_EVENTS 8 #define KNET_PING_TIMERES 200000 static void *_handle_tap_to_links_thread(void *data); static void *_handle_recv_from_links_thread(void *data); static void *_handle_heartbt_thread(void *data); knet_handle_t knet_handle_new(const struct knet_handle_cfg *knet_handle_cfg) { knet_handle_t knet_h; struct epoll_event ev; /* * validate incoming config request */ if (knet_handle_cfg == NULL) { errno = EINVAL; return NULL; } if (knet_handle_cfg->fd <= 0) { errno = EINVAL; return NULL; } if ((knet_h = malloc(sizeof(struct knet_handle))) == NULL) return NULL; memset(knet_h, 0, sizeof(struct knet_handle)); knet_h->dst_host_filter = knet_handle_cfg->dst_host_filter; knet_h->dst_host_filter_fn = knet_handle_cfg->dst_host_filter_fn; if ((knet_h->dst_host_filter) && (!knet_h->dst_host_filter_fn)) goto exit_fail1; if (crypto_init(knet_h, knet_handle_cfg) < 0) goto exit_fail1; if ((knet_h->tap_to_links_buf = malloc(KNET_DATABUFSIZE))== NULL) goto exit_fail2; memset(knet_h->tap_to_links_buf, 0, KNET_DATABUFSIZE); if ((knet_h->recv_from_links_buf = malloc(KNET_DATABUFSIZE))== NULL) goto exit_fail3; memset(knet_h->recv_from_links_buf, 0, KNET_DATABUFSIZE); if ((knet_h->pingbuf = malloc(KNET_PINGBUFSIZE))== NULL) goto exit_fail4; memset(knet_h->pingbuf, 0, KNET_PINGBUFSIZE); if (pthread_rwlock_init(&knet_h->list_rwlock, NULL) != 0) goto exit_fail5; knet_h->sockfd = knet_handle_cfg->fd; knet_h->tap_to_links_epollfd = epoll_create(KNET_MAX_EVENTS); knet_h->recv_from_links_epollfd = epoll_create(KNET_MAX_EVENTS); knet_h->node_id = knet_handle_cfg->node_id; if ((knet_h->tap_to_links_epollfd < 0) || (knet_h->recv_from_links_epollfd < 0)) goto exit_fail6; if ((_fdset_cloexec(knet_h->tap_to_links_epollfd) != 0) || (_fdset_cloexec(knet_h->recv_from_links_epollfd != 0))) goto exit_fail6; memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = knet_h->sockfd; if (epoll_ctl(knet_h->tap_to_links_epollfd, EPOLL_CTL_ADD, knet_h->sockfd, &ev) != 0) goto exit_fail6; if (pthread_create(&knet_h->tap_to_links_thread, 0, _handle_tap_to_links_thread, (void *) knet_h) != 0) goto exit_fail6; if (pthread_create(&knet_h->recv_from_links_thread, 0, _handle_recv_from_links_thread, (void *) knet_h) != 0) goto exit_fail7; if (pthread_create(&knet_h->heartbt_thread, 0, _handle_heartbt_thread, (void *) knet_h) != 0) goto exit_fail8; return knet_h; exit_fail8: pthread_cancel(knet_h->recv_from_links_thread); exit_fail7: pthread_cancel(knet_h->tap_to_links_thread); exit_fail6: if (knet_h->tap_to_links_epollfd >= 0) close(knet_h->tap_to_links_epollfd); if (knet_h->recv_from_links_epollfd >= 0) close(knet_h->recv_from_links_epollfd); pthread_rwlock_destroy(&knet_h->list_rwlock); exit_fail5: free(knet_h->pingbuf); exit_fail4: free(knet_h->recv_from_links_buf); exit_fail3: free(knet_h->tap_to_links_buf); exit_fail2: crypto_fini(knet_h); exit_fail1: free(knet_h); return NULL; } int knet_handle_free(knet_handle_t knet_h) { void *retval; if ((knet_h->host_head != NULL) || (knet_h->listener_head != NULL)) goto exit_busy; pthread_cancel(knet_h->heartbt_thread); pthread_join(knet_h->heartbt_thread, &retval); if (retval != PTHREAD_CANCELED) goto exit_busy; pthread_cancel(knet_h->tap_to_links_thread); pthread_join(knet_h->tap_to_links_thread, &retval); if (retval != PTHREAD_CANCELED) goto exit_busy; pthread_cancel(knet_h->recv_from_links_thread); pthread_join(knet_h->recv_from_links_thread, &retval); if (retval != PTHREAD_CANCELED) goto exit_busy; close(knet_h->tap_to_links_epollfd); close(knet_h->recv_from_links_epollfd); pthread_rwlock_destroy(&knet_h->list_rwlock); free(knet_h->tap_to_links_buf); free(knet_h->recv_from_links_buf); free(knet_h->pingbuf); crypto_fini(knet_h); free(knet_h); return 0; exit_busy: errno = EBUSY; return -EBUSY; } void knet_handle_setfwd(knet_handle_t knet_h, int enabled) { knet_h->enabled = (enabled == 1) ? 1 : 0; } void knet_link_timeout(struct knet_link *lnk, time_t interval, time_t timeout, int precision) { lnk->ping_interval = interval * 1000; /* microseconds */ lnk->pong_timeout = timeout * 1000; /* microseconds */ lnk->latency_fix = precision; lnk->latency_exp = precision - \ ((lnk->ping_interval * precision) / 8000000); } static void _handle_tap_to_links(knet_handle_t knet_h) { ssize_t inlen, len, snt, outlen; struct knet_host *dst_host; int link_idx; uint16_t dst_host_ids[KNET_MAX_HOST]; size_t dst_host_ids_entries = 0; int bcast = 1; inlen = read(knet_h->sockfd, knet_h->tap_to_links_buf->kf_data, KNET_DATABUFSIZE - (KNET_FRAME_SIZE + sizeof(seq_num_t))); if (inlen == 0) { /* TODO: disconnection, should never happen! */ return; } len = inlen + KNET_FRAME_SIZE + sizeof(seq_num_t); if (knet_h->enabled != 1) /* data forward is disabled */ return; if (knet_h->dst_host_filter) { bcast = knet_h->dst_host_filter_fn( (const unsigned char *)knet_h->tap_to_links_buf->kf_data, inlen, knet_h->tap_to_links_buf->kf_node, dst_host_ids, &dst_host_ids_entries); if (bcast < 0) return; if ((!bcast) && (!dst_host_ids_entries)) return; } if (pthread_rwlock_rdlock(&knet_h->list_rwlock) != 0) return; if (!bcast) { int host_idx; for (host_idx = 0; host_idx < dst_host_ids_entries; host_idx++) { dst_host = knet_h->host_index[dst_host_ids[host_idx]]; if (!dst_host) continue; - knet_h->tap_to_links_buf->kf_seq_num = ++dst_host->ucast_seq_num_tx; + knet_h->tap_to_links_buf->kf_seq_num = htons(++dst_host->ucast_seq_num_tx); if (crypto_encrypt_and_sign(knet_h->crypto_instance, (const unsigned char *)knet_h->tap_to_links_buf, len, knet_h->tap_to_links_buf_crypt, &outlen) < 0) { pthread_rwlock_unlock(&knet_h->list_rwlock); return; } for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (dst_host->link[link_idx].configured != 1) /* link is not configured */ continue; if (dst_host->link[link_idx].connected != 1) /* link is not enabled */ continue; snt = sendto(dst_host->link[link_idx].sock, knet_h->tap_to_links_buf_crypt, outlen, MSG_DONTWAIT, (struct sockaddr *) &dst_host->link[link_idx].address, sizeof(struct sockaddr_storage)); if ((dst_host->active == 0) && (snt == outlen)) break; } } } else { - knet_h->tap_to_links_buf->kf_seq_num = ++knet_h->bcast_seq_num; + knet_h->tap_to_links_buf->kf_seq_num = htons(++knet_h->bcast_seq_num); if (crypto_encrypt_and_sign(knet_h->crypto_instance, (const unsigned char *)knet_h->tap_to_links_buf, len, knet_h->tap_to_links_buf_crypt, &outlen) < 0) { pthread_rwlock_unlock(&knet_h->list_rwlock); return; } for (dst_host = knet_h->host_head; dst_host != NULL; dst_host = dst_host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (dst_host->link[link_idx].configured != 1) /* link is not configured */ continue; if (dst_host->link[link_idx].connected != 1) /* link is not enabled */ continue; snt = sendto(dst_host->link[link_idx].sock, knet_h->tap_to_links_buf_crypt, outlen, MSG_DONTWAIT, (struct sockaddr *) &dst_host->link[link_idx].address, sizeof(struct sockaddr_storage)); if ((dst_host->active == 0) && (snt == outlen)) break; } } } pthread_rwlock_unlock(&knet_h->list_rwlock); } static void _handle_recv_from_links(knet_handle_t knet_h, int sockfd) { - ssize_t len, outlen; + ssize_t len, outlen, wlen; struct sockaddr_storage address; socklen_t addrlen; struct knet_host *src_host; struct knet_link *src_link; unsigned long long latency_last; uint16_t dst_host_ids[KNET_MAX_HOST]; size_t dst_host_ids_entries = 0; int bcast = 1; if (pthread_rwlock_rdlock(&knet_h->list_rwlock) != 0) return; addrlen = sizeof(struct sockaddr_storage); len = recvfrom(sockfd, knet_h->recv_from_links_buf, KNET_DATABUFSIZE, MSG_DONTWAIT, (struct sockaddr *) &address, &addrlen); if (crypto_authenticate_and_decrypt(knet_h->crypto_instance, (unsigned char *)knet_h->recv_from_links_buf, &len) < 0) goto exit_unlock; if (len < (KNET_FRAME_SIZE + 1)) goto exit_unlock; if (ntohl(knet_h->recv_from_links_buf->kf_magic) != KNET_FRAME_MAGIC) goto exit_unlock; if (knet_h->recv_from_links_buf->kf_version != KNET_FRAME_VERSION) goto exit_unlock; knet_h->recv_from_links_buf->kf_node = ntohs(knet_h->recv_from_links_buf->kf_node); src_host = knet_h->host_index[knet_h->recv_from_links_buf->kf_node]; if (src_host == NULL) { /* host not found */ goto exit_unlock; } src_link = NULL; if ((knet_h->recv_from_links_buf->kf_type & KNET_FRAME_PMSK) != 0) { src_link = src_host->link + (knet_h->recv_from_links_buf->kf_link % KNET_MAX_LINK); } switch (knet_h->recv_from_links_buf->kf_type) { case KNET_FRAME_DATA: if (knet_h->enabled != 1) /* data forward is disabled */ break; + knet_h->recv_from_links_buf->kf_seq_num = ntohs(knet_h->recv_from_links_buf->kf_seq_num); + if (knet_h->dst_host_filter) { int host_idx; int found = 0; bcast = knet_h->dst_host_filter_fn( (const unsigned char *)knet_h->recv_from_links_buf->kf_data, len, knet_h->recv_from_links_buf->kf_node, dst_host_ids, &dst_host_ids_entries); if (bcast < 0) goto exit_unlock; if ((!bcast) && (!dst_host_ids_entries)) goto exit_unlock; /* check if we are dst for this packet */ if (!bcast) { for (host_idx = 0; host_idx < dst_host_ids_entries; host_idx++) { if (dst_host_ids[host_idx] == knet_h->node_id) { found = 1; break; } } if (!found) goto exit_unlock; } } if (!knet_should_deliver(src_host, bcast, knet_h->recv_from_links_buf->kf_seq_num)) goto exit_unlock; - write(knet_h->sockfd, + wlen = write(knet_h->sockfd, knet_h->recv_from_links_buf->kf_data, len - (KNET_FRAME_SIZE + sizeof(seq_num_t))); - knet_has_been_delivered(src_host, bcast, knet_h->recv_from_links_buf->kf_seq_num); + if (wlen == len - (KNET_FRAME_SIZE + sizeof(seq_num_t))) + knet_has_been_delivered(src_host, bcast, knet_h->recv_from_links_buf->kf_seq_num); break; case KNET_FRAME_PING: knet_h->recv_from_links_buf->kf_type = KNET_FRAME_PONG; knet_h->recv_from_links_buf->kf_node = htons(knet_h->node_id); if (crypto_encrypt_and_sign(knet_h->crypto_instance, (const unsigned char *)knet_h->recv_from_links_buf, len, knet_h->recv_from_links_buf_crypt, &outlen) < 0) break; sendto(src_link->sock, knet_h->recv_from_links_buf_crypt, outlen, MSG_DONTWAIT, (struct sockaddr *) &src_link->address, sizeof(struct sockaddr_storage)); break; case KNET_FRAME_PONG: clock_gettime(CLOCK_MONOTONIC, &src_link->pong_last); timespec_diff(knet_h->recv_from_links_buf->kf_time, src_link->pong_last, &latency_last); src_link->latency = ((src_link->latency * src_link->latency_exp) + ((latency_last / 1000llu) * (src_link->latency_fix - src_link->latency_exp))) / src_link->latency_fix; if (src_link->latency < src_link->pong_timeout) { if (!src_link->connected) { src_link->connected = 1; /* TODO: notify packet inspector */ } } break; default: goto exit_unlock; } exit_unlock: pthread_rwlock_unlock(&knet_h->list_rwlock); } static void _handle_check_each(knet_handle_t knet_h, struct knet_link *dst_link) { int len; ssize_t outlen; struct timespec clock_now, pong_last; unsigned long long diff_ping; /* caching last pong to avoid race conditions */ pong_last = dst_link->pong_last; if (clock_gettime(CLOCK_MONOTONIC, &clock_now) != 0) return; timespec_diff(dst_link->ping_last, clock_now, &diff_ping); if (diff_ping >= (dst_link->ping_interval * 1000llu)) { knet_h->pingbuf->kf_time = clock_now; knet_h->pingbuf->kf_link = dst_link->link_id; if (crypto_encrypt_and_sign(knet_h->crypto_instance, (const unsigned char *)knet_h->pingbuf, KNET_PINGBUFSIZE, knet_h->pingbuf_crypt, &outlen) < 0) return; len = sendto(dst_link->sock, knet_h->pingbuf_crypt, outlen, MSG_DONTWAIT, (struct sockaddr *) &dst_link->address, sizeof(struct sockaddr_storage)); if (len == outlen) dst_link->ping_last = clock_now; } if (dst_link->connected == 1) { timespec_diff(pong_last, clock_now, &diff_ping); if (diff_ping >= (dst_link->pong_timeout * 1000llu)) { dst_link->connected = 0; /* TODO: might need write lock */ /* TODO: notify packet inspector */ } } } static void *_handle_heartbt_thread(void *data) { knet_handle_t knet_h; struct knet_host *dst_host; int link_idx; knet_h = (knet_handle_t) data; /* preparing ping buffer */ knet_h->pingbuf->kf_magic = htonl(KNET_FRAME_MAGIC); knet_h->pingbuf->kf_version = KNET_FRAME_VERSION; knet_h->pingbuf->kf_type = KNET_FRAME_PING; knet_h->pingbuf->kf_node = htons(knet_h->node_id); while (1) { usleep(KNET_PING_TIMERES); if (pthread_rwlock_rdlock(&knet_h->list_rwlock) != 0) continue; for (dst_host = knet_h->host_head; dst_host != NULL; dst_host = dst_host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (dst_host->link[link_idx].configured != 1) continue; _handle_check_each(knet_h, &dst_host->link[link_idx]); } } pthread_rwlock_unlock(&knet_h->list_rwlock); } return NULL; } static void *_handle_tap_to_links_thread(void *data) { knet_handle_t knet_h; struct epoll_event events[KNET_MAX_EVENTS]; knet_h = (knet_handle_t) data; /* preparing data buffer */ knet_h->tap_to_links_buf->kf_magic = htonl(KNET_FRAME_MAGIC); knet_h->tap_to_links_buf->kf_version = KNET_FRAME_VERSION; knet_h->tap_to_links_buf->kf_type = KNET_FRAME_DATA; knet_h->tap_to_links_buf->kf_node = htons(knet_h->node_id); while (1) { if (epoll_wait(knet_h->tap_to_links_epollfd, events, KNET_MAX_EVENTS, -1) >= 1) _handle_tap_to_links(knet_h); } return NULL; } static void *_handle_recv_from_links_thread(void *data) { int i, nev; knet_handle_t knet_h = (knet_handle_t) data; struct epoll_event events[KNET_MAX_EVENTS]; while (1) { nev = epoll_wait(knet_h->recv_from_links_epollfd, events, KNET_MAX_EVENTS, -1); for (i = 0; i < nev; i++) { _handle_recv_from_links(knet_h, events[i].data.fd); } } return NULL; } diff --git a/tests/ping_test.c b/tests/ping_test.c index d86447ce..a35cf30b 100644 --- a/tests/ping_test.c +++ b/tests/ping_test.c @@ -1,254 +1,257 @@ #include "config.h" #include #include #include #include #include #include #include #include "libknet.h" static unsigned char crypto_key[KNET_MAX_KEY_LEN]; static int knet_sock[2]; static knet_handle_t knet_h; static struct knet_handle_cfg knet_handle_cfg; static in_port_t tok_inport(char *str) { int value = atoi(str); if ((value < 0) || (value > UINT16_MAX)) return 0; return (in_port_t) value; } static int tok_inaddrport(char *str, struct sockaddr_in *addr) { char *strhost, *strport, *tmp = NULL; strhost = strtok_r(str, ":", &tmp); strport = strtok_r(NULL, ":", &tmp); if (strport == NULL) addr->sin_port = htons(KNET_RING_DEFPORT); else addr->sin_port = htons(tok_inport(strport)); return inet_aton(strhost, &addr->sin_addr); } static void print_usage(char *name) { printf("usage: %s [:] [:port] [...]\n", name); printf("example: %s 0.0.0.0 192.168.0.2\n", name); printf("example: %s 127.0.0.1:50000 127.0.0.1:50000 crypto:aes256,sha1\n", name); } static void set_crypto(int argc, char *argv[]) { int i, found = 0; for (i = 0; i < argc; i++) { if (!strncmp(argv[i], "crypto", 6)) { printf("found crypto at: %i %s\n", i, argv[i]); found = 1; break; } } if (!found) { knet_handle_cfg.crypto_cipher_type = (char *)"none"; knet_handle_cfg.crypto_hash_type = (char *)"none"; } else { char *tmp = NULL; strtok_r(argv[i], ":", &tmp); knet_handle_cfg.crypto_cipher_type = strtok_r(NULL, ",", &tmp); knet_handle_cfg.crypto_hash_type = strtok_r(NULL, ",", &tmp); } printf("Setting up encryption: %s hmac: %s\n", knet_handle_cfg.crypto_cipher_type, knet_handle_cfg.crypto_hash_type); } static void argv_to_hosts(int argc, char *argv[]) { int err, i; struct sockaddr_in *address; struct knet_host *host; struct knet_listener *listener; listener = malloc(sizeof(struct knet_listener)); if (listener == NULL) { printf("Unable to create listener\n"); exit(EXIT_FAILURE); } memset(listener, 0, sizeof(struct knet_listener)); address = (struct sockaddr_in *) &listener->address; address->sin_family = AF_INET; err = tok_inaddrport(argv[1], address); if (err < 0) { printf("Unable to convert ip address: %s\n", argv[1]); exit(EXIT_FAILURE); } err = knet_listener_add(knet_h, listener); if (err != 0) { printf("Unable to start knet listener\n"); exit(EXIT_FAILURE); } for (i = 2; i < argc; i++) { if (!strncmp(argv[i], "crypto", 6)) continue; if (knet_host_add(knet_h, i - 1) != 0) { printf("Unable to add new knet_host\n"); exit(EXIT_FAILURE); } knet_host_get(knet_h, i - 1, &host); host->link[0].sock = listener->sock; host->link[0].address.ss_family = AF_INET; knet_link_timeout(&host->link[0], 1000, 5000, 2048); host->link[0].configured = 1; err = tok_inaddrport(argv[i], (struct sockaddr_in *) &host->link[0].address); if (err < 0) { printf("Unable to convert ip address: %s", argv[i]); exit(EXIT_FAILURE); } knet_host_release(knet_h, &host); } } /* Testing the latency/timeout: * # tc qdisc add dev lo root handle 1:0 netem delay 1s limit 1000 * # tc -d qdisc show dev lo * # tc qdisc del dev lo root */ static int print_link(knet_handle_t khandle, struct knet_host *host, struct knet_host_search *data) { int i; for (i = 0; i < KNET_MAX_LINK; i++) { if (host->link[i].configured != 1) continue; printf("host %p, link %p latency is %llu microsecs, status: %s\n", host, &host->link[i], host->link[i].latency, (host->link[i].connected == 0) ? "disconnected" : "connected"); } return KNET_HOST_FOREACH_NEXT; } static void sigint_handler(int signum) { int err; printf("Cleaning up...\n"); if (knet_h != NULL) { err = knet_handle_free(knet_h); if (err != 0) { printf("Unable to cleanup before exit\n"); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) { char buff[1024]; size_t len; fd_set rfds; struct timeval tv; struct knet_host_search print_search; if (argc < 3) { print_usage(argv[0]); exit(EXIT_FAILURE); } if (socketpair(AF_UNIX, SOCK_STREAM, IPPROTO_IP, knet_sock) != 0) { printf("Unable to create socket\n"); exit(EXIT_FAILURE); } knet_h = NULL; if (signal(SIGINT, sigint_handler) == SIG_ERR) { printf("Unable to configure SIGINT handler\n"); exit(EXIT_FAILURE); } memset(&knet_handle_cfg, 0, sizeof(struct knet_handle_cfg)); knet_handle_cfg.fd = knet_sock[0]; knet_handle_cfg.node_id = 1; set_crypto(argc, argv); knet_handle_cfg.private_key = crypto_key; knet_handle_cfg.private_key_len = sizeof(crypto_key); /* * fake a 4KB key of all 0's */ memset(knet_handle_cfg.private_key, 0, sizeof(crypto_key)); if ((knet_h = knet_handle_new(&knet_handle_cfg)) == NULL) { printf("Unable to create new knet_handle_t\n"); exit(EXIT_FAILURE); } argv_to_hosts(argc, argv); knet_handle_setfwd(knet_h, 1); while (1) { + ssize_t wlen; knet_host_foreach(knet_h, print_link, &print_search); printf("Sending 'Hello World!' frame\n"); - write(knet_sock[1], "Hello World!", 13); + wlen = write(knet_sock[1], "Hello World!", 13); + if (wlen != 13) + printf("Unable to send Hello World! to socket!\n"); tv.tv_sec = 5; tv.tv_usec = 0; select_loop: FD_ZERO(&rfds); FD_SET(knet_sock[1], &rfds); len = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); /* uncomment this to replicate the one-message problem */ /* usleep(500000); */ if (len < 0) { printf("Unable select over knet_handle_t\n"); exit(EXIT_FAILURE); } else if (FD_ISSET(knet_sock[1], &rfds)) { len = read(knet_sock[1], buff, sizeof(buff)); printf("Received data (%zu bytes): '%s'\n", len, buff); } if ((tv.tv_sec > 0) || (tv.tv_usec > 0)) goto select_loop; } /* FIXME: allocated hosts should be free'd */ return 0; }