/*
 * ng_df735_inject.c - userspace injector to drive the ng_df735_poc netgraph
 * node from the command line (root only).
 *
 * 1. Opens a PF_NETGRAPH control socket (creates a new ng_socket node).
 * 2. Issues NGM_MKPEER to instantiate a df735_poc peer connected to our
 *    hook "dfh" via the peer's hook "hook1".
 * 3. Opens a PF_NETGRAPH data socket, binds it to the same ng_socket node,
 *    and writes a minimal IPv4 packet. The data flows out our "dfh" hook
 *    -> df735_poc.hook1. NG_HOOK_FORCE_QUEUE (set in df735_connect) queues
 *    the item asynchronously to the netgraph worker thread.
 * 4. The netgraph worker thread (ngthread, created at ng_base.c:2787-2789)
 *    dequeues the item and calls ng_df735_poc_rcvdata, which (mirroring
 *    ng_ipfw.c:248) calls ip_input(m) directly. ASSERT_NETISR_NCPUS at
 *    ip_input.c:460 fires -> panic.
 *
 * Usage:  ./ng_df735_inject       (as root, after kldload ng_socket ng_df735_poc)
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <netgraph7/ng_message.h>

#ifndef PF_NETGRAPH
#define PF_NETGRAPH 32
#endif

struct sockaddr_ng {
    u_char  sg_len;
    u_char  sg_family;
    char    sg_data[64];
};

/* NGM_MKPEER payload: ourhook + type + peerhook */
struct mkpeer_args {
    char    ourhook[NG_HOOKSIZ];
    char    type[NG_TYPESIZ];
    char    peerhook[NG_HOOKSIZ];
};

int main(int argc, char **argv) {
    int cs, ds;
    struct {
        struct ng_mesg ms;
        struct mkpeer_args mk;
    } msgbuf;
    struct sockaddr_ng sg;
    char pkt[] = {
        0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00,
        0x40, 0x01, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01,
        0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xab, 0xcd,
        0x00, 0x01, 0x00, 0x01
    };
    ssize_t n;

    fprintf(stderr, "[+] opening PF_NETGRAPH control socket\n");
    cs = socket(PF_NETGRAPH, SOCK_DGRAM, 0);
    if (cs < 0) { perror("socket(PF_NETGRAPH)"); return 1; }

    /* Name our control node "df735ctl" by binding. */
    memset(&sg, 0, sizeof(sg));
    sg.sg_len = 4 + strlen("df735ctl") + 1;
    sg.sg_family = PF_NETGRAPH;
    strcpy(sg.sg_data, "df735ctl");
    if (bind(cs, (struct sockaddr*)&sg, sg.sg_len) < 0) {
        perror("bind (continuing)");
    }

    /* Issue NGM_MKPEER to "." (our own node): create df735_poc peer. */
    memset(&msgbuf, 0, sizeof(msgbuf));
    msgbuf.ms.header.version = NG_VERSION;
    msgbuf.ms.header.typecookie = NGM_GENERIC_COOKIE;
    msgbuf.ms.header.cmd = NGM_MKPEER;
    msgbuf.ms.header.flags = 0;
    msgbuf.ms.header.arglen = sizeof(msgbuf.mk);
    strcpy(msgbuf.mk.ourhook, "dfh");
    strcpy(msgbuf.mk.type, "df735_poc");
    strcpy(msgbuf.mk.peerhook, "hook1");

    memset(&sg, 0, sizeof(sg));
    sg.sg_len = 4 + 2;            /* "." + NUL */
    sg.sg_family = PF_NETGRAPH;
    sg.sg_data[0] = '.';
    sg.sg_data[1] = '\0';
    n = sendto(cs, &msgbuf, sizeof(msgbuf), 0,
               (struct sockaddr*)&sg, sg.sg_len);
    if (n < 0) {
        perror("sendto NGM_MKPEER");
        fprintf(stderr, "[!] mkpeer failed -- df735_poc not loaded?\n");
    } else {
        fprintf(stderr, "[+] NGM_MKPEER sent (%zd bytes)\n", n);
    }

    /* Open a data socket bound to the same control node and push bytes. */
    fprintf(stderr, "[+] opening PF_NETGRAPH data socket\n");
    ds = socket(PF_NETGRAPH, SOCK_DGRAM, 0);
    if (ds < 0) { perror("socket data"); return 1; }

    /* For ng_socket, the data socket must be associated with the same ng
     * node as the control socket. Use connect() with the node name. */
    memset(&sg, 0, sizeof(sg));
    sg.sg_len = 4 + strlen("df735ctl") + 1;
    sg.sg_family = PF_NETGRAPH;
    strcpy(sg.sg_data, "df735ctl");
    if (connect(ds, (struct sockaddr*)&sg, sg.sg_len) < 0) {
        perror("connect data socket");
    }

    fprintf(stderr, "[+] writing %zu-byte IPv4 packet through hook dfh\n", sizeof(pkt));
    n = write(ds, pkt, sizeof(pkt));
    if (n < 0) {
        perror("write");
    } else {
        fprintf(stderr, "[+] wrote %zd bytes -- if df735_poc calls ip_input, kernel panics\n", n);
    }
    sleep(2);
    fprintf(stderr, "[!] kernel still up -- panic did not fire\n");
    return 0;
}
