diff --git a/.editorconfig b/.editorconfig index bb1d2806cac..4aeab816f31 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,10 @@ trim_trailing_whitespace = true indent_style = space indent_size = 2 +[integration-tests/**.c] +indent_style = space +indent_size = 2 + [*.sh] indent_style = space indent_size = 4 diff --git a/collector/lib/CollectorConfig.h b/collector/lib/CollectorConfig.h index ae19aac7c20..b87c31e0e3e 100644 --- a/collector/lib/CollectorConfig.h +++ b/collector/lib/CollectorConfig.h @@ -46,8 +46,10 @@ class CollectorConfig { "vfork", }; static constexpr const char* kSendRecvSyscalls[] = { + "sendto", "sendmsg", "sendmmsg", + "recvfrom", "recvmsg", "recvmmsg", }; diff --git a/collector/lib/NetworkSignalHandler.cpp b/collector/lib/NetworkSignalHandler.cpp index f910d0143a3..745de529c09 100644 --- a/collector/lib/NetworkSignalHandler.cpp +++ b/collector/lib/NetworkSignalHandler.cpp @@ -1,6 +1,5 @@ #include "NetworkSignalHandler.h" -#include #include #include @@ -25,8 +24,12 @@ EventMap modifiers = { {"connect<", Modifier::ADD}, {"accept<", Modifier::ADD}, {"getsockopt<", Modifier::ADD}, + {"sendto<", Modifier::ADD}, + {"sendto>", Modifier::ADD}, {"sendmsg<", Modifier::ADD}, {"sendmsg>", Modifier::ADD}, + {"recvfrom<", Modifier::ADD}, + {"recvfrom>", Modifier::ADD}, {"recvmsg<", Modifier::ADD}, {"recvmsg>", Modifier::ADD}, }, @@ -123,6 +126,9 @@ std::optional NetworkSignalHandler::GetConnection(sinsp_evt* evt) { const std::string* container_id = event_extractor_->get_container_id(evt); if (!container_id) return std::nullopt; + + CLOG(INFO) << "######### ID:" << *container_id << " - Client: " << client << " - Server: " << server; + return {Connection(*container_id, *local, *remote, l4proto, is_server)}; } @@ -141,7 +147,19 @@ SignalHandler::Result NetworkSignalHandler::HandleSignal(sinsp_evt* evt) { std::vector NetworkSignalHandler::GetRelevantEvents() { if (track_send_recv_) { - return {"close<", "shutdown<", "connect<", "accept<", "getsockopt<", "recvmsg<", "sendmsg<", "recvmsg>", "sendmsg>"}; + return { + "close<", + "shutdown<", + "connect<", + "accept<", + "getsockopt<", + "sendto<", + "sendto>", + "sendmsg<", + "sendmsg>", + "recvmsg<", + "recvmsg>", + }; } return {"close<", "shutdown<", "connect<", "accept<", "getsockopt<"}; } diff --git a/falcosecurity-libs b/falcosecurity-libs index d5ec29af8a7..d178d3f42f5 160000 --- a/falcosecurity-libs +++ b/falcosecurity-libs @@ -1 +1 @@ -Subproject commit d5ec29af8a75bdf4a3d1b04b09df32997ab1dee4 +Subproject commit d178d3f42f532cbef0f4b09d0319156242944ca9 diff --git a/integration-tests/container/udp/Containerfile b/integration-tests/container/udp/Containerfile new file mode 100644 index 00000000000..eb63b6e6d96 --- /dev/null +++ b/integration-tests/container/udp/Containerfile @@ -0,0 +1,17 @@ +FROM fedora:40 AS builder + +WORKDIR /tmp +COPY udp-server.c . +COPY udp-client.c . + +RUN dnf install -y gcc && \ + gcc udp-server.c -Wall -Wpedantic -Werror -o udp-server && \ + gcc udp-client.c -Wall -Wpedantic -Werror -o udp-client + +FROM fedora:40 + +COPY --from=builder /tmp/udp-server /usr/local/bin +COPY --from=builder /tmp/udp-client /usr/local/bin +EXPOSE 9090 + +ENTRYPOINT ["udp-server"] diff --git a/integration-tests/container/udp/Makefile b/integration-tests/container/udp/Makefile new file mode 100644 index 00000000000..69e827082a3 --- /dev/null +++ b/integration-tests/container/udp/Makefile @@ -0,0 +1,25 @@ +BASE_PATH = . +include ../Makefile-constants.mk + +.DEFAULT_GOAL = all + +COLLECTOR_QA_UDP_TAG := udp + +ifneq ($(COLLECTOR_QA_TAG),) +COLLECTOR_QA_UDP_TAG=udp-$(COLLECTOR_QA_TAG) +endif + +.PHONY: all +all: build + +.PHONY: build +build: + @docker buildx build --load --platform ${PLATFORM} \ + -t quay.io/rhacs-eng/qa-multi-arch:$(COLLECTOR_QA_UDP_TAG) \ + -f Containerfile . + +.PHONY: build-and-push +build-and-push: + @docker buildx build --push --platform ${PLATFORM} \ + -t quay.io/rhacs-eng/qa-multi-arch:$(COLLECTOR_QA_UDP_TAG) \ + -f Containerfile . diff --git a/integration-tests/container/udp/udp-client.c b/integration-tests/container/udp/udp-client.c new file mode 100644 index 00000000000..914116705a6 --- /dev/null +++ b/integration-tests/container/udp/udp-client.c @@ -0,0 +1,361 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static bool running = true; +static const char LOREM_IPSUM[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; +static const int LOREM_IPSUM_LEN = sizeof(LOREM_IPSUM) / sizeof(char); +static const size_t IOVEC_N = 32; +static const size_t MMSGHDR_N = 32; + +typedef enum send_method_e { + SENDTO = 0, + SENDMSG = 1, + SENDMMSG = 2, +} send_method_t; + +typedef struct target_array_s { + struct addrinfo** ptr; + unsigned int len; +} target_array_t; + +typedef struct config_s { + int ai_family; + int period; + send_method_t syscall; +} config_t; + +typedef struct run_data_s { + send_method_t syscall; + target_array_t targets; + int fd; + int period; +} run_data_t; + +void usage(char* prog) { + printf("%s [FLAGS] [... IP]\n", prog); + printf("\n:\n\tThe IP of the server to send messages to.\n\tCan specify multiple destinations."); + printf("\n[FLAGS]:\n"); + printf(" -6, --ipv6:\n\tUse IPv6 instead of IPv4, the provided IP must be in the correct format\n\n"); + printf(" -h, --help:\n\tShow this help message\n\n"); + printf(" -p, --period=2:\n\tPeriod used to send data\n\n"); + printf(" -s, --syscall=\"sendto\"\n\tSyscall to be used for sending messages\n\tOne of: sendto, sendmsg, sendmmsg\n\n"); +} + +void handle_stop(int sig) { + running = false; +} + +void iovec_free(struct iovec* iov) { + free(iov); +} + +struct iovec* iovec_new() { + struct iovec* iov = calloc(IOVEC_N, sizeof(struct iovec)); + if (iov == NULL) { + return NULL; + } + + for (size_t i = 0; i < IOVEC_N; i++) { + iov[i].iov_base = (void*)LOREM_IPSUM; + iov[i].iov_len = LOREM_IPSUM_LEN; + } + + return iov; +} + +void mmsghdr_free(struct mmsghdr* mmh, unsigned int len) { + if (mmh == NULL) { + return; + } + + for (int i = 0; i < len * MMSGHDR_N; i++) { + iovec_free(mmh[i].msg_hdr.msg_iov); + } + + free(mmh); +} + +struct mmsghdr* mmsghdr_new(target_array_t* targets) { + assert(targets != NULL); + + struct mmsghdr* mmh = calloc(targets->len * MMSGHDR_N, sizeof(struct mmsghdr)); + if (mmh == NULL) { + return NULL; + } + + for (int i = 0; i < targets->len; i++) { + struct addrinfo* server = targets->ptr[i]; + + for (int j = 0; j < MMSGHDR_N; j++) { + struct msghdr* mh = &mmh[j + (i * MMSGHDR_N)].msg_hdr; + mh->msg_iov = iovec_new(); + if (mh->msg_iov == NULL) { + goto fail; + } + mh->msg_iovlen = IOVEC_N; + mh->msg_name = server->ai_addr; + mh->msg_namelen = server->ai_addrlen; + } + } + + return mmh; + +fail: + mmsghdr_free(mmh, targets->len); + return NULL; +} + +ssize_t send_sendto(run_data_t* data) { + assert(data != NULL); + + for (int i = 0; i < data->targets.len; i++) { + struct addrinfo* addr = data->targets.ptr[i]; + + if (sendto(data->fd, LOREM_IPSUM, LOREM_IPSUM_LEN, 0, addr->ai_addr, addr->ai_addrlen) < 0) { + return errno; + } + } + return 0; +} + +ssize_t send_sendmsg(run_data_t* data) { + assert(data != NULL); + + int err = 0; + for (int i = 0; i < data->targets.len; i++) { + struct addrinfo* addr = data->targets.ptr[i]; + + struct iovec* iov = iovec_new(); + if (iov == NULL) { + return ENOMEM; + } + + struct msghdr mh = { + .msg_name = addr->ai_addr, + .msg_namelen = addr->ai_addrlen, + .msg_iov = iov, + .msg_iovlen = IOVEC_N, + }; + + if (sendmsg(data->fd, &mh, 0) < 0) { + err = errno; + } + + iovec_free(iov); + + if (err != 0) { + break; + } + } + return err; +} + +ssize_t send_sendmmsg(run_data_t* data) { + assert(data != NULL); + + // size that mmh will have on return from mmsghdr_new. + unsigned int vlen = data->targets.len * MMSGHDR_N; + struct mmsghdr* mmh = mmsghdr_new(&data->targets); + if (mmh == NULL) { + return ENOMEM; + } + + int err = 0; + if (sendmmsg(data->fd, mmh, vlen, 0) < 0) { + err = errno; + } + + mmsghdr_free(mmh, data->targets.len); + return err; +} + +ssize_t m_send(run_data_t* data) { + switch (data->syscall) { + case SENDTO: + return send_sendto(data); + case SENDMSG: + return send_sendmsg(data); + case SENDMMSG: + return send_sendmmsg(data); + default: + fprintf(stderr, "Invalid syscall\n"); + return -1; + } +} + +struct addrinfo** get_targets(int argc, char** argv, int n_servers, int ai_family) { + struct addrinfo** targets = (struct addrinfo**)calloc(n_servers, sizeof(struct addrinfo*)); + if (targets == NULL) { + fprintf(stderr, "Failed to reserve memory for target servers: (%d) %s", errno, strerror(errno)); + return NULL; + } + + struct addrinfo hints = { + .ai_family = ai_family, + .ai_socktype = SOCK_DGRAM, + }; + + for (int i = 0; i < n_servers; i++) { + char* ip = argv[optind + i]; + char* port = NULL; + if (ai_family == AF_INET6) { + port = strrchr(ip, ']'); + if (port == NULL) { + port = "9090"; + } else if (*ip != '[' || *(port + 1) != ':') { + fprintf(stderr, "Invalid IPv6 address\n"); + goto error; + } else { + ip++; + *port = '\0'; + port += 2; + } + } else { + port = strrchr(ip, ':'); + if (port != NULL) { + *port = '\0'; + port++; + } else { + port = "9090"; + } + } + + struct addrinfo* servinfo = NULL; + ssize_t ret = getaddrinfo(ip, port, &hints, &servinfo); + + if (ret != 0) { + fprintf(stderr, "getaddrinfo failed: %zd\n", ret); + goto error; + } + + targets[i] = servinfo; + } + + return targets; + +error: + free(targets); + return NULL; +} + +int run(run_data_t* data) { + assert(data != NULL); + + while (running) { + ssize_t ret = m_send(data); + if (ret != 0) { + fprintf(stderr, "send failed: (%zd) %s\n", ret, strerror(ret)); + return -1; + } + + sleep(data->period); + } + return 0; +} + +int main(int argc, char* argv[]) { + struct option options[] = { + {.name = "ipv6", .has_arg = no_argument, .flag = NULL, .val = '6'}, + {.name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, + {.name = "period", .has_arg = no_argument, .flag = NULL, .val = 'p'}, + {.name = "syscall", .has_arg = required_argument, .flag = NULL, .val = 's'}, + {.name = 0, .has_arg = 0, .flag = 0, .val = 0}}; + int opt = 0; + config_t config = { + .ai_family = AF_INET, + .period = 2, + .syscall = SENDTO, + }; + + while ((opt = getopt_long(argc, argv, "6hp:s:", options, NULL)) != -1) { + switch (opt) { + case '6': + config.ai_family = AF_INET6; + break; + + case 'h': + usage(argv[0]); + return 0; + + case 'p': + config.period = atoi(optarg); + break; + + case 's': + if (strcmp(optarg, "sendto") == 0) { + config.syscall = SENDTO; + } else if (strcmp(optarg, "sendmsg") == 0) { + config.syscall = SENDMSG; + } else if (strcmp(optarg, "sendmmsg") == 0) { + config.syscall = SENDMMSG; + } else { + fprintf(stderr, "Unknown send method: %s\n", optarg); + usage(argv[0]); + return -1; + } + break; + + default: + fprintf(stderr, "Unknown option %s\n", argv[optind]); + usage(argv[0]); + return -1; + } + } + + int n_servers = argc - optind; + if (n_servers <= 0) { + fprintf(stderr, "Missing required argument\n"); + usage(argv[0]); + return -1; + } + + struct addrinfo** targets = get_targets(argc, argv, n_servers, config.ai_family); + if (targets == NULL) { + return -1; + } + + int fd = socket(config.ai_family, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "Failed to create socket: (%d) %s\n", errno, strerror(errno)); + return -1; + } + + run_data_t run_data = { + .syscall = config.syscall, + .targets = {.ptr = targets, .len = n_servers}, + .fd = fd, + .period = config.period, + }; + + signal(SIGTERM, handle_stop); + signal(SIGINT, handle_stop); + + int ret = run(&run_data); + + for (int i = 0; i < run_data.targets.len; i++) { + struct addrinfo* addr = run_data.targets.ptr[i]; + freeaddrinfo(addr); + close(run_data.fd); + } + free(run_data.targets.ptr); + + return ret; +} diff --git a/integration-tests/container/udp/udp-server.c b/integration-tests/container/udp/udp-server.c new file mode 100644 index 00000000000..7e213bcb620 --- /dev/null +++ b/integration-tests/container/udp/udp-server.c @@ -0,0 +1,337 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const size_t BUF_SIZE = 4096; +static const size_t IOVEC_N = 32; +static const size_t MMSGHDR_N = 32; +static bool running = true; + +typedef enum recv_method_e { + RECVFROM = 0, + RECVMSG = 1, + RECVMMSG = 2, +} recv_method_t; + +void usage(char* prog) { + printf("%s [FLAGS]\n", prog); + printf("\nFLAGS:\n"); + printf(" -6, --ipv6:\n\tUse IPv6 instead of IPv4\n\n"); + printf(" -h, --help:\n\tShow this help message\n\n"); + printf(" -p, --port=\"9090\":\n\tUse the specified port\n\n"); + printf(" -s, --syscall=\"recvfrom\":\n\tSyscall to be used for receiving messages\n\tOne of: recvfrom, recvmsg, recvmmsg\n\n"); +} + +void handle_stop(int sig) { + running = false; +} + +void* get_in_addr(struct sockaddr* sa) { + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +void iovec_free(struct iovec* iov) { + if (iov == NULL) { + return; + } + + for (int i = 0; i < IOVEC_N; i++) { + free(iov[i].iov_base); + } + free(iov); +} + +struct iovec* iovec_new() { + struct iovec* ret = calloc(IOVEC_N, sizeof(struct iovec)); + if (ret == NULL) { + return NULL; + } + + for (int i = 0; i < IOVEC_N; i++) { + struct iovec* iov = &ret[i]; + iov->iov_base = malloc(BUF_SIZE + 1); + if (iov->iov_base == NULL) { + goto fail; + } + iov->iov_len = BUF_SIZE; + } + + return ret; + +fail: + iovec_free(ret); + return NULL; +} + +void mmsghdr_free(struct mmsghdr* mmh) { + if (mmh == NULL) { + return; + } + + for (int i = 0; i < MMSGHDR_N; i++) { + struct msghdr* mh = &mmh[i].msg_hdr; + free(mh->msg_name); + iovec_free(mh->msg_iov); + } + + free(mmh); +} + +struct mmsghdr* mmsghdr_new() { + struct mmsghdr* mmh = calloc(MMSGHDR_N, sizeof(struct mmsghdr)); + if (mmh == NULL) { + return NULL; + } + + for (int i = 0; i < MMSGHDR_N; i++) { + struct msghdr* mh = &mmh[i].msg_hdr; + mh->msg_namelen = sizeof(struct sockaddr); + mh->msg_name = malloc(mh->msg_namelen); + if (mh->msg_name == NULL) { + goto fail; + } + + mh->msg_iov = iovec_new(); + if (mh->msg_iov == NULL) { + goto fail; + } + mh->msg_iovlen = IOVEC_N; + } + + return mmh; + +fail: + mmsghdr_free(mmh); + return NULL; +} + +void print_msg(struct msghdr* mh, ssize_t remaining) { + struct sockaddr* from = mh->msg_name; + struct iovec* iov = mh->msg_iov; + char s[INET6_ADDRSTRLEN]; + + for (int i = 0; i < mh->msg_iovlen && remaining >= 0; i++) { + char* buf = iov[i].iov_base; + size_t terminator_index = remaining < iov[i].iov_len ? remaining : iov[i].iov_len; + buf[terminator_index] = '\0'; + + printf("%s: %s\n", inet_ntop(from->sa_family, get_in_addr(from), s, sizeof(s)), buf); + fflush(stdout); + + remaining -= iov[i].iov_len; + } +} + +ssize_t receive_recvfrom(int fd) { + char buf[BUF_SIZE + 1]; + char s[INET6_ADDRSTRLEN]; + struct sockaddr from; + socklen_t fromlen = sizeof(from); + ssize_t res = 0; + + res = recvfrom(fd, buf, BUF_SIZE, 0, &from, &fromlen); + if (res < 0) { + if (errno == EINTR || errno == EAGAIN) { + return 0; + } + return errno; + } + + buf[res] = '\0'; + printf("%s: %s\n", inet_ntop(from.sa_family, get_in_addr(&from), s, sizeof(s)), buf); + + return 0; +} + +ssize_t receive_recvmsg(int fd) { + ssize_t res = 0; + int err = 0; + struct sockaddr from; + socklen_t fromlen = sizeof(from); + struct iovec* iov = iovec_new(); + if (iov == NULL) { + return ENOMEM; + } + struct msghdr mh = { + .msg_name = &from, + .msg_namelen = fromlen, + .msg_iov = iov, + .msg_iovlen = IOVEC_N, + }; + + res = recvmsg(fd, &mh, 0); + if (res < 0) { + if (errno != EINTR && errno != EAGAIN) { + err = errno; + } + goto end; + } + + print_msg(&mh, res); + +end: + iovec_free(iov); + return err; +} + +ssize_t receive_recvmmsg(int fd) { + ssize_t res = 0; + int err = 0; + struct mmsghdr* mmh = mmsghdr_new(); + if (mmh == NULL) { + return ENOMEM; + } + + res = recvmmsg(fd, mmh, MMSGHDR_N, 0, NULL); + if (res < 0) { + if (errno != EINTR && errno != EAGAIN) { + err = errno; + } + goto end; + } + + for (int i = 0; i < res; i++) { + struct msghdr* mh = &mmh[i].msg_hdr; + ssize_t size = mmh[i].msg_len; + + print_msg(mh, size); + } + +end: + mmsghdr_free(mmh); + return err; +} + +ssize_t receive(int fd, recv_method_t syscall) { + switch (syscall) { + case RECVFROM: + return receive_recvfrom(fd); + case RECVMSG: + return receive_recvmsg(fd); + case RECVMMSG: + return receive_recvmmsg(fd); + default: + fprintf(stderr, "Invalid syscall\n"); + return -1; + } +} + +int run(int ai_family, const char* port, recv_method_t syscall) { + int fd = -1; + struct addrinfo hints = { + .ai_family = ai_family, + .ai_socktype = SOCK_DGRAM, + .ai_flags = AI_PASSIVE, + }; + struct addrinfo* res = NULL; + ssize_t ret = getaddrinfo(NULL, port, &hints, &res); + + if (ret != 0) { + fprintf(stderr, "getaddrinfo failed: %zd\n", ret); + return -1; + } + + struct addrinfo* p = res; + for (; p != NULL; p = p->ai_next) { + fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (fd < 0) { + fprintf(stderr, "Failed to create socket: (%d) %s\n", errno, strerror(errno)); + continue; + } + + if (bind(fd, res->ai_addr, res->ai_addrlen) != 0) { + fprintf(stderr, "bind failed: (%d) %s\n", errno, strerror(errno)); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "failed to create socket\n"); + return -1; + } + + freeaddrinfo(res); + + struct timeval tv = { + .tv_sec = 1, .tv_usec = 0}; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); + + while (running) { + ret = receive(fd, syscall); + if (ret < 0) { + fprintf(stderr, "receive failed: (%zd) %s\n", ret, strerror(ret)); + continue; + } + } + + close(fd); + return 0; +} + +int main(int argc, char* argv[]) { + struct option options[] = { + {.name = "ipv6", .has_arg = no_argument, .flag = NULL, .val = '6'}, + {.name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'}, + {.name = "port", .has_arg = required_argument, .flag = NULL, .val = 'p'}, + {.name = "syscall", .has_arg = required_argument, .flag = NULL, .val = 's'}, + {.name = 0, .has_arg = 0, .flag = 0, .val = 0}}; + int opt = 0; + int ai_family = AF_INET; + char* port = "9090"; + recv_method_t syscall = RECVFROM; + + while ((opt = getopt_long(argc, argv, "6hp:s:", options, NULL)) != -1) { + switch (opt) { + case '6': + ai_family = AF_INET6; + break; + case 'h': + usage(argv[0]); + return 0; + case 'p': + port = optarg; + printf("Using port %s\n", port); + break; + case 's': + if (strcmp(optarg, "recvfrom") == 0) { + syscall = RECVFROM; + } else if (strcmp(optarg, "recvmsg") == 0) { + syscall = RECVMSG; + } else if (strcmp(optarg, "recvmmsg") == 0) { + syscall = RECVMMSG; + } else { + fprintf(stderr, "Unknown receive method: %s\n", optarg); + usage(argv[0]); + return -1; + } + + break; + default: + fprintf(stderr, "Unknown option %s\n", argv[optind]); + usage(argv[0]); + return -1; + } + } + + signal(SIGTERM, handle_stop); + signal(SIGINT, handle_stop); + + return run(ai_family, port, syscall); +} diff --git a/integration-tests/images.yml b/integration-tests/images.yml index b89115787c1..a1e6df783dd 100644 --- a/integration-tests/images.yml +++ b/integration-tests/images.yml @@ -13,6 +13,7 @@ qa: qa-schedule-curls: quay.io/rhacs-eng/qa-multi-arch:collector-schedule-curls qa-alpine-curl: quay.io/rhacs-eng/qa-multi-arch:alpine-curl qa-perf-event-open: quay.io/rhacs-eng/qa-multi-arch:collector-perf-event-open + qa-udp: quay.io/rhacs-eng/qa-multi-arch:udp non_qa: nginx: nginx:1.14-alpine diff --git a/integration-tests/integration_test.go b/integration-tests/integration_test.go index 2ba06a8016d..e35d3f33188 100644 --- a/integration-tests/integration_test.go +++ b/integration-tests/integration_test.go @@ -483,3 +483,7 @@ func TestPerfEvent(t *testing.T) { func TestGperftools(t *testing.T) { suite.Run(t, new(suites.GperftoolsTestSuite)) } + +func TestUdpNetworkFlow(t *testing.T) { + suite.Run(t, new(suites.UdpNetworkFlow)) +} diff --git a/integration-tests/pkg/executor/executor.go b/integration-tests/pkg/executor/executor.go index a14478a0b3f..4132bc85339 100644 --- a/integration-tests/pkg/executor/executor.go +++ b/integration-tests/pkg/executor/executor.go @@ -25,6 +25,8 @@ type Executor interface { KillContainer(name string) (string, error) RemoveContainer(filter ContainerFilter) (string, error) StopContainer(name string) (string, error) + CreateNetwork(name string) (string, error) + RemoveNetwork(name string) (string, error) } type CommandBuilder interface { diff --git a/integration-tests/pkg/executor/executor_docker.go b/integration-tests/pkg/executor/executor_docker.go index e9537169b36..5e47093a68a 100644 --- a/integration-tests/pkg/executor/executor_docker.go +++ b/integration-tests/pkg/executor/executor_docker.go @@ -244,6 +244,20 @@ func (e *dockerExecutor) StopContainer(name string) (string, error) { return e.ExecWithErrorCheck(containerErrorCheckFunction(name, "stop"), RuntimeCommand, "stop", name) } +// CreateNetwork creates a docker network so containers can reference each other by name +func (e *dockerExecutor) CreateNetwork(name string) (string, error) { + cmd := []string{RuntimeCommand, "network", "create", name} + + return e.Exec(cmd...) +} + +// RemoveNetwork will attempt to delete a docker network +func (e *dockerExecutor) RemoveNetwork(name string) (string, error) { + cmd := []string{RuntimeCommand, "network", "rm", name} + + return e.Exec(cmd...) +} + func (e *localCommandBuilder) ExecCommand(execArgs ...string) *exec.Cmd { return exec.Command(execArgs[0], execArgs[1:]...) } diff --git a/integration-tests/pkg/executor/executor_k8s.go b/integration-tests/pkg/executor/executor_k8s.go index 4397287db0f..ba383fba903 100644 --- a/integration-tests/pkg/executor/executor_k8s.go +++ b/integration-tests/pkg/executor/executor_k8s.go @@ -150,6 +150,14 @@ func (e *K8sExecutor) StopContainer(name string) (string, error) { return "", fmt.Errorf("Unimplemented") } +func (e *K8sExecutor) CreateNetwork(name string) (string, error) { + return "", fmt.Errorf("Unimplemented") +} + +func (e *K8sExecutor) RemoveNetwork(name string) (string, error) { + return "", fmt.Errorf("Unimplemented") +} + func (e *K8sExecutor) CreateNamespace(ns string) (*coreV1.Namespace, error) { meta := metaV1.ObjectMeta{Name: ns} return e.clientset.CoreV1().Namespaces().Create(context.Background(), &coreV1.Namespace{ObjectMeta: meta}, metaV1.CreateOptions{}) diff --git a/integration-tests/pkg/mock_sensor/expect_conn.go b/integration-tests/pkg/mock_sensor/expect_conn.go index f6e3828b8a5..738c8d55ad9 100644 --- a/integration-tests/pkg/mock_sensor/expect_conn.go +++ b/integration-tests/pkg/mock_sensor/expect_conn.go @@ -1,6 +1,7 @@ package mock_sensor import ( + "fmt" "testing" "time" @@ -17,10 +18,13 @@ import ( // until timeout or until all the events have been received. func (s *MockSensor) ExpectConnections(t *testing.T, containerID string, timeout time.Duration, expected ...types.NetworkInfo) bool { + fmt.Printf("len(expected)=%d\n", len(expected)) to_find := funk.Filter(expected, func(x types.NetworkInfo) bool { + fmt.Printf("x=%+q\n", x) return s.HasConnection(containerID, x) }).([]types.NetworkInfo) + fmt.Printf("len(to_find)=%d\n", len(to_find)) if len(to_find) == 0 { return true } diff --git a/integration-tests/pkg/mock_sensor/server.go b/integration-tests/pkg/mock_sensor/server.go index c2cbe88eb05..3886a338a30 100644 --- a/integration-tests/pkg/mock_sensor/server.go +++ b/integration-tests/pkg/mock_sensor/server.go @@ -172,11 +172,12 @@ func (m *MockSensor) HasConnection(containerID string, conn types.NetworkInfo) b defer m.networkMutex.Unlock() if conns, ok := m.connections[containerID]; ok { + fmt.Printf("%+q\n", conn) _, exists := conns[conn] - return exists + return !exists } - return false + return true } // Liveendpoints returns a channel that can be used to read live