/*
 * DF-1880 source-confirmation harness (oce_hw_update_multicast mac[32] OOB).
 *
 * sys/dev/netif/oce/oce_hw.c:547-585 oce_hw_update_multicast:
 *   TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
 *     if (req->params.req.num_mac == OCE_MAX_MC_FILTER_SIZE) break;   // L568
 *     bcopy(LLADDR(...), &req->params.req.mac[num_mac], ETH_ADDR_LEN); // L576
 *     req->params.req.num_mac++;
 *   }
 *
 * OCE_MAX_MC_FILTER_SIZE = 64 (oce_hw.h:180) but the mac[] array is
 * declared mac[32] inside mbx_set_common_iface_multicast (oce_hw.h:1119).
 * Joining 33+ multicast groups writes 6 bytes past mac[32] starting at
 * byte offset 192 in the DMA alloc; joining 64 groups writes 192 bytes
 * off the end.
 *
 * Live trigger needs an Emulex OneConnect NIC (device oce is in GENERIC
 * but no HW on this guest).  The harness reproduces the index math with
 * a flat byte buffer.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define OCE_MAX_MC_FILTER_SIZE 64           /* oce_hw.h:180 (loop bound — WRONG) */
#define MAC_ARRAY_SIZE         32           /* oce_hw.h:1119 mac[32] (actual)  */
#define ETH_ADDR_LEN           6
#define MAC_BYTES              (MAC_ARRAY_SIZE * ETH_ADDR_LEN)  /* 192 */

int main(void) {
    /* Model the DMA alloc as a flat byte buffer with a sentinel tail. */
    size_t blob_sz = 8192;
    unsigned char *buf = calloc(1, blob_sz);
    unsigned char *mac = buf;                       /* mac[32][6] is first 192 bytes */
    unsigned char *sentinel = buf + MAC_BYTES;      /* "past the mac array" */
    for (size_t i = MAC_BYTES; i < blob_sz; i++) buf[i] = 0xEE;

    unsigned int num_mac = 0;
    int groups = 64;
    for (int g = 0; g < groups; g++) {
        if (num_mac == OCE_MAX_MC_FILTER_SIZE) break;   /* oce_hw.c:568 — bound is 64 */
        unsigned char lladdr[ETH_ADDR_LEN] = { 0x01,0x00,0x5e,
            (unsigned char)((g >> 16) & 0x7f),
            (unsigned char)((g >> 8) & 0xff),
            (unsigned char)(g & 0xff) };
        memcpy(mac + num_mac * ETH_ADDR_LEN, lladdr, ETH_ADDR_LEN);  /* L576 OOB when num_mac>=32 */
        num_mac++;
    }

    int corrupted = 0;
    for (size_t i = MAC_BYTES; i < blob_sz; i++) if (buf[i] != 0xEE) corrupted++;

    int overflow_slots = (num_mac > MAC_ARRAY_SIZE) ? (int)num_mac - MAC_ARRAY_SIZE : 0;
    printf("DF-1880: oce_hw_update_multicast (oce_hw.c:547-585)\n");
    printf("  joined %u multicast groups (loop bound OCE_MAX_MC_FILTER_SIZE=%d, "
           "mac[] array size=%d slots)\n", num_mac, OCE_MAX_MC_FILTER_SIZE, MAC_ARRAY_SIZE);
    printf("  OOB writes = %d MAC slots x %d bytes = %d bytes past mac[32] "
           "into the DMA alloc\n", overflow_slots, ETH_ADDR_LEN,
           overflow_slots * ETH_ADDR_LEN);
    printf("  Harness: simulated bcopy touched %d sentinel bytes past mac[32]\n",
           corrupted);
    printf("  Fix: loop bound should be nitems(req->params.req.mac)=32, not "
           "OCE_MAX_MC_FILTER_SIZE=64.\n");
    free(buf);
    return 0;
}
