// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2023 FUJITSU LIMITED. All rights reserved. * Copyright (c) International Business Machines Corp., 2001 * Author: David L Stevens */ /*\ * [Description] * * Basic test for ICMP6_FILTER. * * For ICMP6_FILTER usage, refer to: https://man.openbsd.org/icmp6. * * Because of the extra functionality of ICMPv6 in comparison to ICMPv4, a * larger number of messages may be potentially received on an ICMPv6 socket. * Input filters may therefore be used to restrict input to a subset of the * incoming ICMPv6 messages so only interesting messages are returned by the * recv(2) family of calls to an application. * The icmp6_filter structure may be used to refine the input message set * according to the ICMPv6 type. By default, all messages types are allowed * on newly created raw ICMPv6 sockets. The following macros may be used to * refine the input set, thus being tested: * * void ICMP6_FILTER_SETPASSALL(struct icmp6_filter *filterp) * – Allow all incoming messages. filterp is modified to allow all message types. * * void ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter *filterp) * – Ignore all incoming messages. filterp is modified to ignore all message types. * * void ICMP6_FILTER_SETPASS(int, struct icmp6_filter *filterp) * – Allow ICMPv6 messages with the given type. filterp is modified to allow such * messages. * * void ICMP6_FILTER_SETBLOCK(int, struct icmp6_filter *filterp) * – Ignore ICMPv6 messages with the given type. filterp is modified to ignore * such messages. * * int ICMP6_FILTER_WILLPASS(int, const struct icmp6_filter *filterp) * – Determine if the given filter will allow an ICMPv6 message of the given type. * * int ICMP6_FILTER_WILLBLOCK(int, const struct icmp6_filter *) * – Determine if the given filter will ignore an ICMPv6 message of the given type. * * The getsockopt(2) and setsockopt(2) calls may be used to obtain and install * the filter on ICMPv6 sockets at option level IPPROTO_ICMPV6 and name ICMP6_FILTER * with a pointer to the icmp6_filter structure as the option value. */ #include #include "tst_test.h" static int sall = -1, sf = -1; enum filter_macro { FILTER_SETPASS, FILTER_SETBLOCK, FILTER_PASSALL, FILTER_BLOCKALL, FILTER_WILLBLOCK, FILTER_WILLPASS }; #define DESC(x, y, z) .tname = "ICMP6_" #x ", send type: " #y ", filter type: " \ #z, .send_type = y, .filter_type = z, .test_macro = x static struct tcase { char *tname; unsigned char send_type; unsigned char filter_type; enum filter_macro test_macro; int pass_packet; } tcases[] = { {DESC(FILTER_SETPASS, 20, 20), .pass_packet = 1}, {DESC(FILTER_SETPASS, 20, 21)}, {DESC(FILTER_SETBLOCK, 20, 20)}, {DESC(FILTER_SETBLOCK, 20, 21), .pass_packet = 1}, {DESC(FILTER_PASSALL, 20, 20), .pass_packet = 1}, {DESC(FILTER_PASSALL, 21, 0), .pass_packet = 1}, {DESC(FILTER_BLOCKALL, 20, 0)}, {DESC(FILTER_BLOCKALL, 21, 0)}, {DESC(FILTER_WILLBLOCK, 20, 21)}, {DESC(FILTER_WILLBLOCK, 20, 20), .pass_packet = 1}, {DESC(FILTER_WILLPASS, 20, 21)}, {DESC(FILTER_WILLPASS, 22, 22), .pass_packet = 1}, }; static void ic6_send(unsigned char type) { struct sockaddr_in6 sin6; struct icmp6_hdr ic6; int s; s = SAFE_SOCKET(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); memset(&ic6, 0, sizeof(ic6)); ic6.icmp6_type = type; ic6.icmp6_data32[0] = htonl(getpid()); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6addr_loopback; SAFE_SENDTO(0, s, &ic6, sizeof(ic6), 0, (struct sockaddr *)&sin6, sizeof(sin6)); } static int ic6_recv(void) { fd_set readfds, readfds_saved; struct timeval tv; int maxfd, nfds, gotall, gotone; unsigned char rbuf[2048]; tv.tv_sec = 0; tv.tv_usec = 250000; FD_ZERO(&readfds_saved); FD_SET(sall, &readfds_saved); FD_SET(sf, &readfds_saved); maxfd = MAX(sall, sf); memcpy(&readfds, &readfds_saved, sizeof(readfds)); gotall = gotone = 0; while (!gotall || !gotone) { struct icmp6_hdr *pic6 = (struct icmp6_hdr *)rbuf; nfds = select(maxfd + 1, &readfds, 0, 0, &tv); if (nfds == 0) break; if (nfds < 0) { if (errno == EINTR) continue; tst_brk(TBROK | TERRNO, "select failed"); } if (FD_ISSET(sall, &readfds)) { SAFE_RECV(0, sall, rbuf, sizeof(rbuf), 0); if (htonl(pic6->icmp6_data32[0]) == (uint32_t)getpid()) gotall = 1; } if (FD_ISSET(sf, &readfds)) { SAFE_RECV(0, sf, rbuf, sizeof(rbuf), 0); if (htonl(pic6->icmp6_data32[0]) == (uint32_t)getpid()) gotone = 1; } memcpy(&readfds, &readfds_saved, sizeof(readfds)); } if (!gotall) { tst_res(TFAIL, "recv all time out"); return -1; } if (gotone) return 1; return 0; } static void verify_icmp6_filter(unsigned int n) { struct tcase *tc = &tcases[n]; struct icmp6_filter i6f; int rc; tst_res(TINFO, "Testing %s", tc->tname); switch (tc->test_macro) { case FILTER_SETPASS: ICMP6_FILTER_SETBLOCKALL(&i6f); ICMP6_FILTER_SETPASS(tc->filter_type, &i6f); break; case FILTER_PASSALL: ICMP6_FILTER_SETPASSALL(&i6f); break; case FILTER_SETBLOCK: ICMP6_FILTER_SETPASSALL(&i6f); ICMP6_FILTER_SETBLOCK(tc->filter_type, &i6f); break; case FILTER_BLOCKALL: ICMP6_FILTER_SETBLOCKALL(&i6f); break; case FILTER_WILLBLOCK: ICMP6_FILTER_SETPASSALL(&i6f); ICMP6_FILTER_SETBLOCK(tc->filter_type, &i6f); rc = ICMP6_FILTER_WILLBLOCK(tc->send_type, &i6f); TST_EXP_EXPR(rc == tc->pass_packet, "%d (%d)", tc->pass_packet, rc); return; case FILTER_WILLPASS: ICMP6_FILTER_SETBLOCKALL(&i6f); ICMP6_FILTER_SETPASS(tc->filter_type, &i6f); rc = ICMP6_FILTER_WILLPASS(tc->send_type, &i6f); TST_EXP_EXPR(rc == tc->pass_packet, "%d (%d)", tc->pass_packet, rc); return; default: tst_brk(TBROK, "unknown test type %d", tc->filter_type); break; } SAFE_SETSOCKOPT(sf, IPPROTO_ICMPV6, ICMP6_FILTER, &i6f, sizeof(i6f)); ic6_send(tc->send_type); rc = ic6_recv(); if (rc < 0) return; TST_EXP_EXPR(rc == tc->pass_packet, "%s packet type %d", rc ? "pass" : "block", tc->send_type); } static void setup(void) { struct icmp6_filter i6f; sall = SAFE_SOCKET(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); ICMP6_FILTER_SETPASSALL(&i6f); SAFE_SETSOCKOPT(sall, IPPROTO_ICMPV6, ICMP6_FILTER, &i6f, sizeof(i6f)); sf = SAFE_SOCKET(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); } static void cleanup(void) { if (sall > -1) SAFE_CLOSE(sall); if (sf > -1) SAFE_CLOSE(sf); } static struct tst_test test = { .needs_root = 1, .setup = setup, .cleanup = cleanup, .test = verify_icmp6_filter, .tcnt = ARRAY_SIZE(tcases) };