freebsd-src/tests/sys/netpfil/pf/divapp.c
Igor Ostapenko fabf705f4b pf: fix pf divert-to loop
Resolved conflict between ipfw and pf if both are used and pf wants to
do divert(4) by having separate mtags for pf and ipfw.

Also fix the incorrect 'rulenum' check, which caused the reported loop.

While here add a few test cases to ensure that divert-to works as
expected, even if ipfw is loaded.

divert(4)
PR:		272770
MFC after:	3 weeks
Reviewed by:	kp
Differential Revision:	https://reviews.freebsd.org/D42142
2023-10-19 12:12:15 +02:00

150 lines
3.8 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Used by tests like divert-to.sh */
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <err.h>
#include <sysexits.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
struct context {
unsigned short divert_port;
bool divert_back;
int fd;
struct sockaddr_in sin;
socklen_t sin_len;
char pkt[IP_MAXPACKET];
ssize_t pkt_n;
};
static void
init(struct context *c)
{
c->fd = socket(PF_DIVERT, SOCK_RAW, 0);
if (c->fd == -1)
errx(EX_OSERR, "init: Cannot create divert socket.");
memset(&c->sin, 0, sizeof(c->sin));
c->sin.sin_family = AF_INET;
c->sin.sin_port = htons(c->divert_port);
c->sin.sin_addr.s_addr = INADDR_ANY;
c->sin_len = sizeof(struct sockaddr_in);
if (bind(c->fd, (struct sockaddr *) &c->sin, c->sin_len) != 0)
errx(EX_OSERR, "init: Cannot bind divert socket.");
}
static ssize_t
recv_pkt(struct context *c)
{
fd_set readfds;
struct timeval timeout;
int s;
FD_ZERO(&readfds);
FD_SET(c->fd, &readfds);
timeout.tv_sec = 3;
timeout.tv_usec = 0;
s = select(c->fd + 1, &readfds, 0, 0, &timeout);
if (s == -1)
errx(EX_IOERR, "recv_pkt: select() errors.");
if (s != 1) // timeout
return -1;
c->pkt_n = recvfrom(c->fd, c->pkt, sizeof(c->pkt), 0,
(struct sockaddr *) &c->sin, &c->sin_len);
if (c->pkt_n == -1)
errx(EX_IOERR, "recv_pkt: recvfrom() errors.");
return (c->pkt_n);
}
static void
send_pkt(struct context *c)
{
ssize_t n;
char errstr[32];
n = sendto(c->fd, c->pkt, c->pkt_n, 0,
(struct sockaddr *) &c->sin, c->sin_len);
if (n == -1) {
strerror_r(errno, errstr, sizeof(errstr));
errx(EX_IOERR, "send_pkt: sendto() errors: %d %s.", errno, errstr);
}
if (n != c->pkt_n)
errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.",
n, c->pkt_n);
}
int
main(int argc, char *argv[])
{
struct context c;
int npkt;
if (argc < 2)
errx(EX_USAGE,
"Usage: %s <divert-port> [divert-back]", argv[0]);
memset(&c, 0, sizeof(struct context));
c.divert_port = (unsigned short) strtol(argv[1], NULL, 10);
if (c.divert_port == 0)
errx(EX_USAGE, "divert port is not defined.");
if (argc >= 3 && strcmp(argv[2], "divert-back") == 0)
c.divert_back = true;
init(&c);
npkt = 0;
while (recv_pkt(&c) > 0) {
if (c.divert_back)
send_pkt(&c);
npkt++;
if (npkt >= 10)
break;
}
if (npkt != 1)
errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt);
return EXIT_SUCCESS;
}