#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#define VLAN_ID 10
#define DEI 1   // DEI bit set to 1
#define PRIORITY 0

// Compute checksum (IP/ICMP)
uint16_t checksum(void *buf, int len) {
    uint16_t *data = buf;
    uint32_t sum = 0;
    for (; len > 1; len -= 2)
        sum += *data++;
    if (len == 1)
        sum += *(unsigned char*)data;
    while (sum >> 16)
        sum = (sum & 0xFFFF) + (sum >> 16);
    return ~sum;
}

int main() {
    int sockfd;
    struct ifreq if_idx;
    struct sockaddr_ll socket_address;
    unsigned char sendbuf[1500];
    int tx_len = 0;

    // Ethernet header (14 bytes)
    struct ether_header *eh = (struct ether_header *) sendbuf;

    // VLAN tag (4 bytes)
    uint16_t vlan_tci = (PRIORITY << 13) | (DEI << 12) | VLAN_ID;

    unsigned char *vlan_tag = sendbuf + sizeof(struct ether_header);

    // IPv4 header starts after VLAN tag
    struct iphdr *ip = (struct iphdr *)(sendbuf + sizeof(struct ether_header) + 4);

    // ICMP header starts after IPv4 header
    struct icmphdr *icmp = (struct icmphdr *)(sendbuf + sizeof(struct ether_header) + 4 + sizeof(struct iphdr));

    // Interface name - change as needed
    char ifName[] = "eth0";

    // Create RAW socket
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

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

    // Build Ethernet header
    memset(eh->ether_dhost, 0xff, ETH_ALEN);  // Broadcast dest MAC
    unsigned char src_mac[6] = {0x00, 0x0c, 0x29, 0x3e, 0x5c, 0x7d};
    memcpy(eh->ether_shost, src_mac, ETH_ALEN);
    eh->ether_type = htons(0x8100);  // VLAN Ethertype

    // VLAN tag: TCI
    vlan_tag[0] = vlan_tci >> 8;
    vlan_tag[1] = vlan_tci & 0xff;
    vlan_tag[2] = 0x08;  // Next ethertype = IPv4 (0x0800)
    vlan_tag[3] = 0x00;

    // Build IPv4 header
    ip->version = 4;
    ip->ihl = 5;
    ip->tos = 0;
    ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr));
    ip->id = htons(0x1234);
    ip->frag_off = 0;
    ip->ttl = 64;
    ip->protocol = IPPROTO_ICMP;
    ip->check = 0;
    inet_pton(AF_INET, "192.168.10.2", &ip->saddr);
    inet_pton(AF_INET, "192.168.10.3", &ip->daddr);
    ip->check = checksum(ip, sizeof(struct iphdr));

    // Build ICMP Echo Request
    icmp->type = ICMP_ECHO;
    icmp->code = 0;
    icmp->un.echo.id = htons(0x1234);
    icmp->un.echo.sequence = htons(1);
    icmp->checksum = 0;
    icmp->checksum = checksum(icmp, sizeof(struct icmphdr));

    // Total length: Ethernet header + VLAN tag + IP + ICMP
    tx_len = sizeof(struct ether_header) + 4 + sizeof(struct iphdr) + sizeof(struct icmphdr);

    // Prepare sockaddr_ll
    memset(&socket_address, 0, sizeof(struct sockaddr_ll));
    socket_address.sll_ifindex = if_idx.ifr_ifindex;
    socket_address.sll_halen = ETH_ALEN;
    memcpy(socket_address.sll_addr, eh->ether_dhost, ETH_ALEN);

    // Send the frame
    if (sendto(sockfd, sendbuf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0) {
        perror("sendto");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Sent VLAN tagged Ethernet frame with DEI=1 and valid IPv4/ICMP payload on %s\n", ifName);

    close(sockfd);
    return 0;
}

