
0237ba54bef7 ci: switch to c-util automation 0a8c47d55a65 ci: switch to github-actions b10e6918ab42 build: update submodules 596b7e70acbe n-acd: fix valgrind warnings 70e2822584e0 build: update submodules 1446edb496d9 ci: drop broken armv7hl 6093f34b019b n-acd: fix leaking socket handle in n_acd_socket_new() when setsockopt() fails git-subtree-dir: shared/n-acd git-subtree-split: 0237ba54bef7d91232a9285c2ec93f3e5691a1b2
227 lines
8.1 KiB
C
227 lines
8.1 KiB
C
/*
|
|
* eBPF socket filter tests
|
|
*/
|
|
|
|
#undef NDEBUG
|
|
#include <assert.h>
|
|
#include <c-stdaux.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/in.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#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;
|
|
}
|