PF PF divert-to Loop Problem

Hi,

I used ipfw before
My ipfw rule was like this :
ipfw -q add 11 divert 4444 udp from any to any dst-port 53 in via igb1


I move on pf now
My pf rule was like this :
Code:
pass in quick on igb1 proto udp from any to port { 53 } divert-to 127.0.0.1 port 4444


My Code :
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <unistd.h>


int main(int argc, char** argv) {
    printf("Start DIVERT \n");

    int ret, data_size;
    struct sockaddr_in bindPort, sin;
    socklen_t sinlen;

    unsigned char *packet = (unsigned char *)malloc(65536);

    if(argc!=2) {
        fprintf(stderr, "Usage: %s <port number>\n", argv[0]);
        exit(1);
    }

    int bind_socket;
    bind_socket = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT);

    if (bind_socket == -1) {
        perror("socket_bind_error");
        exit(1);
    }

    bindPort.sin_family=PF_INET;
    bindPort.sin_port=htons(atol(argv[1]));
    bindPort.sin_addr.s_addr=0;

    ret = bind(bind_socket, (struct sockaddr *)&bindPort, sizeof(struct sockaddr_in));
    if(ret!=0) {
        close(bind_socket);
        fprintf(stderr, "%s: Error bind(): %s",argv[0],strerror(ret));
        exit(2);
    }


    sinlen = sizeof(struct sockaddr_in);
    struct ip_header *hdr;

    while(1){
        data_size=recvfrom(bind_socket, packet, 65535, 0, (struct sockaddr *) &sin, &sinlen);
        if(data_size < 0){
            printf("Recvfrom Error, failed get packets \n");
            return 1;
        }

        printf("data_size : %d \n",data_size);

        data_size = sendto(bind_socket, packet, data_size ,0, (struct sockaddr *) &sin, sinlen);
    }
}


My code was working fine in ipfw. I was sending 1 dns request.
ipfw output :
Code:
Start DIVERT 
data_size : 52



pf output :
Code:
Start DIVERT 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
data_size : 52 
...



The dns request I send with pf loops and does not end.
Where is my problem.
Can you help

FreeBSD Version : 12.2
 
When reinject the packet try to use "(struct sockaddr *) &bindPort" as you already reset the destination address for it with "bindPort.sin_addr.s_addr=0"

You can check the example of OpenBSD divert here:
 
I also only got loops with pf's divert-to on both FreeBSD 13 and 14-CURRENT, no matter what I did in the divert socket code.

The problem appears to be in the kernel module, some rulenum check appears to be done unnecessarily and is preventing the packet from being marked as looped. (I also think pf skips processing packets written to the divert socket instead of resuming with the next rule like ipfw does, but that's a different issue, and might be by design.)

Bug report with a proposed patch: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=260867
 
Back
Top