diff --git a/libknet/links_acl.c b/libknet/links_acl.c index 8592f1f1..cfcc1fd2 100644 --- a/libknet/links_acl.c +++ b/libknet/links_acl.c @@ -1,93 +1,95 @@ /* * Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved. * * Author: Christine Caulfield * * This software licensed under GPL-2.0+, LGPL-2.0+ */ +#include "config.h" + #include #include #include #include "internals.h" #include "logging.h" #include "transports.h" #include "transport_common.h" #include "links_acl.h" #include "links_acl_ip.h" int check_add(knet_handle_t knet_h, int sock, uint8_t transport, struct sockaddr_storage *ip1, struct sockaddr_storage *ip2, check_type_t type, check_acceptreject_t acceptreject) { int err = -1; switch(transport_get_proto(knet_h, transport)) { case LOOPBACK: err = 0; break; case IP_PROTO: err = ipcheck_addip((struct acl_match_entry **)&knet_h->knet_transport_fd_tracker[sock].match_entry, ip1, ip2, type, acceptreject); break; default: break; } return err; } int check_rm(knet_handle_t knet_h, int sock, uint8_t transport, struct sockaddr_storage *ip1, struct sockaddr_storage *ip2, check_type_t type, check_acceptreject_t acceptreject) { int err = -1; switch(transport_get_proto(knet_h, transport)) { case LOOPBACK: err = 0; break; case IP_PROTO: err = ipcheck_rmip((struct acl_match_entry **)&knet_h->knet_transport_fd_tracker[sock].match_entry, ip1, ip2, type, acceptreject); break; default: break; } return err; } void check_rmall(knet_handle_t knet_h, int sock, uint8_t transport) { switch(transport_get_proto(knet_h, transport)) { case LOOPBACK: return; break; case IP_PROTO: ipcheck_rmall((struct acl_match_entry **)&knet_h->knet_transport_fd_tracker[sock].match_entry); break; default: break; } } /* * return 0 to reject and 1 to accept a packet */ int check_validate(knet_handle_t knet_h, int sock, uint8_t transport, struct sockaddr_storage *checkip) { switch(transport_get_proto(knet_h, transport)) { case LOOPBACK: return 1; break; case IP_PROTO: return ipcheck_validate((struct acl_match_entry **)&knet_h->knet_transport_fd_tracker[sock].match_entry, checkip); break; default: break; } /* * reject by default */ return 0; } diff --git a/libknet/links_acl_ip.c b/libknet/links_acl_ip.c index 2aef14ba..ffd18a47 100644 --- a/libknet/links_acl_ip.c +++ b/libknet/links_acl_ip.c @@ -1,278 +1,280 @@ /* * Copyright (C) 2016-2018 Red Hat, Inc. All rights reserved. * * Author: Christine Caulfield * * This software licensed under GPL-2.0+, LGPL-2.0+ */ +#include "config.h" + #include #include #include #include #include #include "internals.h" #include "logging.h" #include "transports.h" #include "links_acl.h" #include "links_acl_ip.h" /* * s6_addr32 is not defined in BSD userland, only kernel. * definition is the same as linux and it works fine for * what we need. */ #ifndef s6_addr32 #define s6_addr32 __u6_addr.__u6_addr32 #endif /* * IPv4 See if the address we have matches the current match entry */ static int ip_matches_v4(struct sockaddr_storage *checkip, struct acl_match_entry *match_entry) { struct sockaddr_in *ip_to_check; struct sockaddr_in *match1; struct sockaddr_in *match2; ip_to_check = (struct sockaddr_in *)checkip; match1 = (struct sockaddr_in *)&match_entry->addr1; match2 = (struct sockaddr_in *)&match_entry->addr2; switch(match_entry->type) { case CHECK_TYPE_ADDRESS: if (ip_to_check->sin_addr.s_addr == match1->sin_addr.s_addr) return 1; break; case CHECK_TYPE_MASK: if ((ip_to_check->sin_addr.s_addr & match2->sin_addr.s_addr) == match1->sin_addr.s_addr) return 1; break; case CHECK_TYPE_RANGE: if ((ntohl(ip_to_check->sin_addr.s_addr) >= ntohl(match1->sin_addr.s_addr)) && (ntohl(ip_to_check->sin_addr.s_addr) <= ntohl(match2->sin_addr.s_addr))) return 1; break; } return 0; } /* * Compare two IPv6 addresses */ static int ip6addr_cmp(struct in6_addr *a, struct in6_addr *b) { uint64_t a_high, a_low; uint64_t b_high, b_low; a_high = ((uint64_t)htonl(a->s6_addr32[0]) << 32) | (uint64_t)htonl(a->s6_addr32[1]); a_low = ((uint64_t)htonl(a->s6_addr32[2]) << 32) | (uint64_t)htonl(a->s6_addr32[3]); b_high = ((uint64_t)htonl(b->s6_addr32[0]) << 32) | (uint64_t)htonl(b->s6_addr32[1]); b_low = ((uint64_t)htonl(b->s6_addr32[2]) << 32) | (uint64_t)htonl(b->s6_addr32[3]); if (a_high > b_high) return 1; if (a_high < b_high) return -1; if (a_low > b_low) return 1; if (a_low < b_low) return -1; return 0; } /* * IPv6 See if the address we have matches the current match entry */ static int ip_matches_v6(struct sockaddr_storage *checkip, struct acl_match_entry *match_entry) { struct sockaddr_in6 *ip_to_check; struct sockaddr_in6 *match1; struct sockaddr_in6 *match2; int i; ip_to_check = (struct sockaddr_in6 *)checkip; match1 = (struct sockaddr_in6 *)&match_entry->addr1; match2 = (struct sockaddr_in6 *)&match_entry->addr2; switch(match_entry->type) { case CHECK_TYPE_ADDRESS: if (!memcmp(ip_to_check->sin6_addr.s6_addr32, match1->sin6_addr.s6_addr32, sizeof(struct in6_addr))) return 1; break; case CHECK_TYPE_MASK: /* * Note that this little loop will quit early if there is a non-match so the * comparison might look backwards compared to the IPv4 one */ for (i=sizeof(struct in6_addr)/4-1; i>=0; i--) { if ((ip_to_check->sin6_addr.s6_addr32[i] & match2->sin6_addr.s6_addr32[i]) != match1->sin6_addr.s6_addr32[i]) return 0; } return 1; case CHECK_TYPE_RANGE: if ((ip6addr_cmp(&ip_to_check->sin6_addr, &match1->sin6_addr) >= 0) && (ip6addr_cmp(&ip_to_check->sin6_addr, &match2->sin6_addr) <= 0)) return 1; break; } return 0; } int ipcheck_validate(struct acl_match_entry **match_entry_head, struct sockaddr_storage *checkip) { struct acl_match_entry *match_entry = *match_entry_head; int (*match_fn)(struct sockaddr_storage *checkip, struct acl_match_entry *match_entry); if (checkip->ss_family == AF_INET){ match_fn = ip_matches_v4; } else { match_fn = ip_matches_v6; } while (match_entry) { if (match_fn(checkip, match_entry)) { if (match_entry->acceptreject == CHECK_ACCEPT) return 1; else return 0; } match_entry = match_entry->next; } return 0; /* Default reject */ } /* * Routines to manuipulate access lists */ void ipcheck_rmall(struct acl_match_entry **match_entry_head) { struct acl_match_entry *next_match_entry; struct acl_match_entry *match_entry = *match_entry_head; while (match_entry) { next_match_entry = match_entry->next; free(match_entry); match_entry = next_match_entry; } *match_entry_head = NULL; } static struct acl_match_entry *ipcheck_findmatch(struct acl_match_entry **match_entry_head, struct sockaddr_storage *ip1, struct sockaddr_storage *ip2, check_type_t type, check_acceptreject_t acceptreject) { struct acl_match_entry *match_entry = *match_entry_head; while (match_entry) { if ((!memcmp(&match_entry->addr1, ip1, sizeof(struct sockaddr_storage))) && (!memcmp(&match_entry->addr2, ip2, sizeof(struct sockaddr_storage))) && (match_entry->type == type) && (match_entry->acceptreject == acceptreject)) { return match_entry; } match_entry = match_entry->next; } return NULL; } int ipcheck_rmip(struct acl_match_entry **match_entry_head, struct sockaddr_storage *ip1, struct sockaddr_storage *ip2, check_type_t type, check_acceptreject_t acceptreject) { struct acl_match_entry *next_match_entry = NULL; struct acl_match_entry *rm_match_entry; struct acl_match_entry *match_entry = *match_entry_head; rm_match_entry = ipcheck_findmatch(match_entry_head, ip1, ip2, type, acceptreject); if (!rm_match_entry) { return -1; } while (match_entry) { next_match_entry = match_entry->next; /* * we are removing the list head, be careful */ if (rm_match_entry == match_entry) { *match_entry_head = next_match_entry; free(match_entry); break; } /* * the next one is the one we need to remove */ if (rm_match_entry == next_match_entry) { match_entry->next = next_match_entry->next; free(next_match_entry); break; } match_entry = next_match_entry; } return 0; } int ipcheck_addip(struct acl_match_entry **match_entry_head, struct sockaddr_storage *ip1, struct sockaddr_storage *ip2, check_type_t type, check_acceptreject_t acceptreject) { struct acl_match_entry *new_match_entry; struct acl_match_entry *match_entry = *match_entry_head; if (!ip1) { return -1; } if ((type != CHECK_TYPE_ADDRESS) && (!ip2)) { return -1; } if (type == CHECK_TYPE_RANGE && (ip1->ss_family != ip2->ss_family)) return -1; if (ipcheck_findmatch(match_entry_head, ip1, ip2, type, acceptreject) != NULL) { return -1; } new_match_entry = malloc(sizeof(struct acl_match_entry)); if (!new_match_entry) return -1; memmove(&new_match_entry->addr1, ip1, sizeof(struct sockaddr_storage)); memmove(&new_match_entry->addr2, ip2, sizeof(struct sockaddr_storage)); new_match_entry->type = type; new_match_entry->acceptreject = acceptreject; new_match_entry->next = NULL; if (match_entry) { /* Find the end of the list */ /* is this OK, or should we use a doubly-linked list or bulk-load API call? */ while (match_entry->next) { match_entry = match_entry->next; } match_entry->next = new_match_entry; } else { /* * first entry in the list */ *match_entry_head = new_match_entry; } return 0; } diff --git a/libknet/tests/Makefile.am b/libknet/tests/Makefile.am index d46553a5..2f222939 100644 --- a/libknet/tests/Makefile.am +++ b/libknet/tests/Makefile.am @@ -1,91 +1,92 @@ # # Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved. # # Authors: Fabio M. Di Nitto # # This software licensed under GPL-2.0+, LGPL-2.0+ # MAINTAINERCLEANFILES = Makefile.in include $(top_srcdir)/build-aux/check.mk include $(top_srcdir)/libknet/tests/api-check.mk EXTRA_DIST = \ api-test-coverage \ api-check.mk \ int_links_acl.txt AM_CPPFLAGS = -I$(top_srcdir)/libknet AM_CFLAGS += $(PTHREAD_CFLAGS) LIBS = $(top_builddir)/libknet/libknet.la \ $(PTHREAD_LIBS) $(dl_LIBS) noinst_HEADERS = \ test-common.h # the order of those tests is NOT random. # some functions can only be tested properly after some dependents # API have been validated upfront. check_PROGRAMS = \ $(api_checks) \ $(int_checks) \ $(fun_checks) int_checks = \ int_timediff_test fun_checks = benchmarks = \ knet_bench_test # int_links_acl_test canĀ“t run yet standalone noinst_PROGRAMS = \ api_knet_handle_new_limit_test \ pckt_test \ int_links_acl_test \ $(benchmarks) \ $(check_PROGRAMS) noinst_SCRIPTS = \ api-test-coverage TESTS = $(check_PROGRAMS) if INSTALL_TESTS testsuitedir = $(TESTDIR) testsuite_PROGRAMS = $(noinst_PROGRAMS) endif check-local: check-api-test-coverage check-api-test-coverage: chmod u+x $(top_srcdir)/libknet/tests/api-test-coverage $(top_srcdir)/libknet/tests/api-test-coverage $(top_srcdir) $(top_builddir) pckt_test_SOURCES = pckt_test.c int_links_acl_test_SOURCES = int_links_acl.c \ ../common.c \ + ../compat.c \ ../logging.c \ ../netutils.c \ ../threads_common.c \ ../transports.c \ ../transport_common.c \ ../transport_loopback.c \ ../transport_sctp.c \ ../transport_udp.c \ ../links_acl.c \ ../links_acl_ip.c int_timediff_test_SOURCES = int_timediff.c knet_bench_test_SOURCES = knet_bench.c \ test-common.c \ ../common.c \ ../logging.c \ ../compat.c \ ../transport_common.c \ ../threads_common.c diff --git a/libknet/tests/int_links_acl.c b/libknet/tests/int_links_acl.c index 8d9f4e06..05bd829f 100644 --- a/libknet/tests/int_links_acl.c +++ b/libknet/tests/int_links_acl.c @@ -1,209 +1,211 @@ /* * Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved. * * Author: Christine Caulfield * * This software licensed under GPL-2.0+, LGPL-2.0+ */ +#include "config.h" + #include #include #include #include #include #include #include #include "internals.h" #include "links_acl.h" #include "links_acl_ip.h" static struct acl_match_entry *match_entry_v4; static struct acl_match_entry *match_entry_v6; /* This is a test program .. remember! */ #define BUFLEN 1024 static int get_ipaddress(char *buf, struct sockaddr_storage *addr) { struct addrinfo *info; struct addrinfo hints; int res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; res = getaddrinfo(buf, NULL, &hints, &info); if (!res) { memmove(addr, info->ai_addr, info->ai_addrlen); freeaddrinfo(info); } return res; } static int read_address(char *buf, struct sockaddr_storage *addr) { return get_ipaddress(buf, addr); } static int read_mask(char *buf, struct sockaddr_storage *addr, struct sockaddr_storage *addr2) { char tmpbuf[BUFLEN]; char *slash; int ret; slash = strchr(buf, '/'); if (!slash) return 1; strncpy(tmpbuf, buf, slash-buf); tmpbuf[slash-buf] = '\0'; ret = get_ipaddress(tmpbuf, addr); if (ret) return ret; ret = get_ipaddress(slash+1, addr2); if (ret) return ret; return 0; } static int read_range(char *buf, struct sockaddr_storage *addr1, struct sockaddr_storage *addr2) { char tmpbuf[BUFLEN]; char *hyphen; int ret; hyphen = strchr(buf, '-'); if (!hyphen) return 1; strncpy(tmpbuf, buf, hyphen-buf); tmpbuf[hyphen-buf] = '\0'; ret = get_ipaddress(tmpbuf, addr1); if (ret) return ret; ret = get_ipaddress(hyphen+1, addr2); if (ret) return ret; return 0; } static int load_file(void) { FILE *filterfile; char filebuf[BUFLEN]; int line = 0; int ret; check_type_t type; check_acceptreject_t acceptreject; struct sockaddr_storage addr1; struct sockaddr_storage addr2; ipcheck_rmall(&match_entry_v4); ipcheck_rmall(&match_entry_v6); filterfile = fopen("int_links_acl.txt", "r"); if (!filterfile) { fprintf(stderr, "Cannot open int_links_acl.txt\n"); return 1; } while (fgets(filebuf, sizeof(filebuf), filterfile)) { filebuf[strlen(filebuf)-1] = '\0'; /* remove trailing LF */ line++; /* * First char is A (accept) or R (Reject) */ switch(filebuf[0] & 0x5F) { case 'A': acceptreject = CHECK_ACCEPT; break; case 'R': acceptreject = CHECK_REJECT; break; default: fprintf(stderr, "Unknown record type on line %d: %s\n", line, filebuf); goto next_record; } /* * Second char is the filter type: * A Address * M Mask * R Range */ switch(filebuf[1] & 0x5F) { case 'A': type = CHECK_TYPE_ADDRESS; ret = read_address(filebuf+2, &addr1); break; case 'M': type = CHECK_TYPE_MASK; ret = read_mask(filebuf+2, &addr1, &addr2); break; case 'R': type = CHECK_TYPE_RANGE; ret = read_range(filebuf+2, &addr1, &addr2); break; default: fprintf(stderr, "Unknown filter type on line %d: %s\n", line, filebuf); goto next_record; break; } if (ret) { fprintf(stderr, "Failed to parse address on line %d: %s\n", line, filebuf); } else { if (addr1.ss_family == AF_INET) { ipcheck_addip(&match_entry_v4, &addr1, &addr2, type, acceptreject); } else { ipcheck_addip(&match_entry_v6, &addr1, &addr2, type, acceptreject); } } next_record: {} /* empty statement to mollify the compiler */ } fclose(filterfile); return 0; } int main(int argc, char *argv[]) { struct sockaddr_storage saddr; struct acl_match_entry *match_entry; int ret; int i; if (load_file()) return 1; for (i=1; i