#ifndef PCP_VALUE
#error "You must define PCP_VALUE before including this file."
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <errno.h>

#define ETHER_TYPE_VLAN 0x8100
#define ETHER_TYPE_IP   0x0800
#define VLAN_ID         100
#define DEI             0

struct eth_vlan_hdr {
    unsigned char dest[6];
    unsigned char src[6];
    uint16_t tpid;
    uint16_t tci;
    uint16_t ethertype;
};

// Checksum function for IP header
unsigned short checksum(unsigned short *buf, int len) {
    unsigned long sum = 0;
    while (len > 1) {
        sum += *buf++;
        len -= 2;
    }
    if (len == 1)
        sum += *(unsigned char*)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}

int main() {
    int sock;
    char ifname[] = "enp1s0"; // Change to your network interface name
    unsigned char frame[1500];

    // Create raw socket
    if ((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
        perror("Socket");
        return 1;
    }

    // Get interface index
    struct ifreq if_idx;
    memset(&if_idx, 0, sizeof(struct ifreq));
    strncpy(if_idx.ifr_name, ifname, IFNAMSIZ - 1);
    if (ioctl(sock, SIOCGIFINDEX, &if_idx) < 0) {
        perror("SIOCGIFINDEX");
        close(sock);
        return 1;
    }

    // Get source MAC address of interface
    struct ifreq if_mac;
    memset(&if_mac, 0, sizeof(struct ifreq));
    strncpy(if_mac.ifr_name, ifname, IFNAMSIZ - 1);
    if (ioctl(sock, SIOCGIFHWADDR, &if_mac) < 0) {
        perror("SIOCGIFHWADDR");
        close(sock);
        return 1;
    }

    // Prepare sockaddr_ll
    struct sockaddr_ll socket_address;
    memset(&socket_address, 0, sizeof(socket_address));
    socket_address.sll_ifindex = if_idx.ifr_ifindex;
    socket_address.sll_halen = ETH_ALEN;
    memset(socket_address.sll_addr, 0xff, 6); // Broadcast MAC

    // Clear frame buffer
    memset(frame, 0, sizeof(frame));

    // Construct Ethernet + VLAN header
    struct eth_vlan_hdr *hdr = (struct eth_vlan_hdr *)frame;
    memset(hdr->dest, 0xff, 6); // Broadcast destination
    memcpy(hdr->src, if_mac.ifr_hwaddr.sa_data, 6); // Real source MAC
    hdr->tpid = htons(ETHER_TYPE_VLAN);
    hdr->tci = htons(((PCP_VALUE & 0x7) << 13) | ((DEI & 0x1) << 12) | (VLAN_ID & 0x0FFF));
    hdr->ethertype = htons(ETHER_TYPE_IP);

    // Construct minimal valid IPv4 header (no payload)
    struct iphdr *iph = (struct iphdr *)(frame + sizeof(struct eth_vlan_hdr));
    iph->version = 4;
    iph->ihl = 5; // 5*4 = 20 bytes
    iph->tos = 0;
    iph->tot_len = htons(sizeof(struct iphdr)); // header only
    iph->id = htons(0x1234);
    iph->frag_off = 0;
    iph->ttl = 64;
    iph->protocol = 0; // HOPOPT (valid but unused)
    iph->saddr = inet_addr("192.168.0.1");
    iph->daddr = inet_addr("192.168.0.2");
    iph->check = 0;
    iph->check = checksum((unsigned short *)iph, sizeof(struct iphdr));

    int total_len = sizeof(struct eth_vlan_hdr) + sizeof(struct iphdr);

    // Send the frame
    if (sendto(sock, frame, total_len, 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
        perror("Send failed");
        close(sock);
        return 1;
    }

    printf("✅ Sent VLAN tagged frame with PCP=%d on %s\n", PCP_VALUE, ifname);
    close(sock);
    return 0;
}

