Files
NetworkManager/src/test-bpf.c
Thomas Haller 2a30e4b75b Squashed 'shared/n-acd/' changes from 54708168399f..0237ba54bef7
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
2020-06-03 22:11:43 +02:00

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;
}