/* * eBPF socket filter tests */ #undef NDEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include "n-acd.h" #include "n-acd-private.h" #include "test.h" #define ETHER_ARP_PACKET_INIT(_op, _mac, _sip, _tip) { \ .ea_hdr = { \ .ar_hrd = htobe16(ARPHRD_ETHER), \ .ar_pro = htobe16(ETHERTYPE_IP), \ .ar_hln = 6, \ .ar_pln = 4, \ .ar_op = htobe16(_op), \ }, \ .arp_sha[0] = (_mac)->ether_addr_octet[0], \ .arp_sha[1] = (_mac)->ether_addr_octet[1], \ .arp_sha[2] = (_mac)->ether_addr_octet[2], \ .arp_sha[3] = (_mac)->ether_addr_octet[3], \ .arp_sha[4] = (_mac)->ether_addr_octet[4], \ .arp_sha[5] = (_mac)->ether_addr_octet[5], \ .arp_spa[0] = (be32toh((_sip)->s_addr) >> 24) & 0xff, \ .arp_spa[1] = (be32toh((_sip)->s_addr) >> 16) & 0xff, \ .arp_spa[2] = (be32toh((_sip)->s_addr) >> 8) & 0xff, \ .arp_spa[3] = be32toh((_sip)->s_addr) & 0xff, \ .arp_tpa[0] = (be32toh((_tip)->s_addr) >> 24) & 0xff, \ .arp_tpa[1] = (be32toh((_tip)->s_addr) >> 16) & 0xff, \ .arp_tpa[2] = (be32toh((_tip)->s_addr) >> 8) & 0xff, \ .arp_tpa[3] = be32toh((_tip)->s_addr) & 0xff, \ } static void test_map(void) { int r, mapfd = -1; struct in_addr addr = { 1 }; r = n_acd_bpf_map_create(&mapfd, 8); c_assert(r >= 0); c_assert(mapfd >= 0); r = n_acd_bpf_map_remove(mapfd, &addr); c_assert(r == -ENOENT); r = n_acd_bpf_map_add(mapfd, &addr); c_assert(r >= 0); r = n_acd_bpf_map_add(mapfd, &addr); c_assert(r == -EEXIST); r = n_acd_bpf_map_remove(mapfd, &addr); c_assert(r >= 0); r = n_acd_bpf_map_remove(mapfd, &addr); c_assert(r == -ENOENT); close(mapfd); } static void verify_success(struct ether_arp *packet, int out_fd, int in_fd) { uint8_t buf[sizeof(struct ether_arp)]; int r; r = send(out_fd, packet, sizeof(struct ether_arp), 0); c_assert(r == sizeof(struct ether_arp)); r = recv(in_fd, buf, sizeof(buf), 0); c_assert(r == sizeof(struct ether_arp)); } static void verify_failure(struct ether_arp *packet, int out_fd, int in_fd) { uint8_t buf[sizeof(struct ether_arp)]; int r; r = send(out_fd, packet, sizeof(struct ether_arp), 0); c_assert(r == sizeof(struct ether_arp)); r = recv(in_fd, buf, sizeof(buf), 0); c_assert(r < 0); c_assert(errno == EAGAIN); } static void test_filter(void) { uint8_t buf[sizeof(struct ether_arp) + 1]; struct ether_addr mac1 = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 } }; struct ether_addr mac2 = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x07 } }; struct in_addr ip0 = { 0 }; struct in_addr ip1 = { 1 }; struct in_addr ip2 = { 2 }; struct ether_arp *packet = (struct ether_arp *)buf; int r, mapfd = -1, progfd = -1, pair[2]; r = n_acd_bpf_map_create(&mapfd, 1); c_assert(r >= 0); r = n_acd_bpf_compile(&progfd, mapfd, &mac1); c_assert(r >= 0); c_assert(progfd >= 0); r = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, pair); c_assert(r >= 0); r = setsockopt(pair[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)); c_assert(r >= 0); r = n_acd_bpf_map_add(mapfd, &ip1); c_assert(r >= 0); /* valid */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); verify_success(packet, pair[0], pair[1]); /* valid: reply instead of request */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REPLY, &mac2, &ip1, &ip2); verify_success(packet, pair[0], pair[1]); /* valid: to us instead of from us */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip0, &ip1); verify_success(packet, pair[0], pair[1]); /* invalid header type */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); packet->arp_hrd += 1; verify_failure(packet, pair[0], pair[1]); /* invalid protocol */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); packet->arp_pro += 1; verify_failure(packet, pair[0], pair[1]); /* invalid hw addr length */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); packet->arp_hln += 1; verify_failure(packet, pair[0], pair[1]); /* invalid protocol addr length */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); packet->arp_pln += 1; verify_failure(packet, pair[0], pair[1]); /* invalid operation */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_NAK, &mac2, &ip1, &ip2); packet->arp_hln += 1; verify_failure(packet, pair[0], pair[1]); /* own mac */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac1, &ip1, &ip2); verify_failure(packet, pair[0], pair[1]); /* not to, nor from us, with source */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip2, &ip2); verify_failure(packet, pair[0], pair[1]); /* not to, nor from us, without source */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip0, &ip2); verify_failure(packet, pair[0], pair[1]); /* to us instead of from us, but reply */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REPLY, &mac2, &ip0, &ip1); verify_failure(packet, pair[0], pair[1]); /* long */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); r = send(pair[0], buf, sizeof(struct ether_arp) + 1, 0); c_assert(r == sizeof(struct ether_arp) + 1); r = recv(pair[1], buf, sizeof(buf), 0); c_assert(r == sizeof(struct ether_arp)); /* short */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); r = send(pair[0], buf, sizeof(struct ether_arp) - 1, 0); c_assert(r == sizeof(struct ether_arp) - 1); r = recv(pair[1], buf, sizeof(buf), 0); c_assert(r < 0); c_assert(errno == EAGAIN); /* * Send one packet before and one packet after modifying the map, * verify that the modification applies at the time of send(), not recv(). */ *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2); r = send(pair[0], buf, sizeof(struct ether_arp), 0); c_assert(r == sizeof(struct ether_arp)); r = n_acd_bpf_map_remove(mapfd, &ip1); c_assert(r >= 0); r = send(pair[0], buf, sizeof(struct ether_arp), 0); c_assert(r == sizeof(struct ether_arp)); r = recv(pair[1], buf, sizeof(buf), 0); c_assert(r == sizeof(struct ether_arp)); r = recv(pair[1], buf, sizeof(buf), 0); c_assert(r < 0); c_assert(errno == EAGAIN); close(pair[0]); close(pair[1]); close(progfd); close(mapfd); } int main(int argc, char **argv) { test_setup(); test_map(); test_filter(); return 0; }