#include <stdio.h>
#include <stdlib.h>
#include <string.h>             /* mdepasca for memset */
#include <unistd.h>            /* mdepasca for close (int fd) */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>   /* The L2 protocols */
#include <linux/if_arp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>              /* mdepasca to get error number after socket call */
//#endif

#define uint8  unsigned char
#define uint16 unsigned short int

#define IP_ALEN 4

/* Microgate IP ASSignment */
#define ETH_P_IPASS     0xaaaa

/* IPASS protocol opcodes. */
#define IPASS_OP_SET       1          /* IPASS setting */

/*
 *      This structure defines an ethernet arp header.
 */
#define IPASS_HLEN          46

struct ipasshdr
{
    uint16 ipass_op; /* (02) ARP opcode (command)         */
    uint8 ipass_sha[ETH_ALEN]; /* (06) sender hardware address      */
    uint8 ipass_sip[IP_ALEN]; /* (04) sender IP address            */
    uint8 ipass_tha[ETH_ALEN]; /* (06) target hardware address      */
    uint8 ipass_tip[IP_ALEN]; /* (04) target IP address            */
};

#define ETH_HEADER_LEN 14

int main(int argc, char **argv)
{
    int sockfd, ifindex;
    char str[5];
    char *strEthName = "em"; /*"enp0s31f"; //"eth";*/
    int i, s, res;
    void *buffer;
    unsigned char *etherhead;

    unsigned int dest[ETH_ALEN];
    unsigned char src_mac[ETH_ALEN];
    unsigned char dest_mac[ETH_ALEN];
    unsigned char src_ip[IP_ALEN];
    unsigned char dest_ip[IP_ALEN];

    struct sockaddr_in *sin_ptr;
    struct ifreq ifr;
    struct sockaddr_ll socket_address;
    struct ipasshdr *data;
    struct ethhdr *eh;

    sockfd = -1;

    if (argc != 4)
    {
        printf(
            "\
Usage: <index of network device -> 0,1,2,...>\n\
       <bcu MAC address -> %%x:%%x:%%x:%%x:%%x:%%x> (SHARK-NIR BCU has 00:20:af:f4:cc:69) \n\
       <new bcu IP addr -> %%d.%%d.%%d.%%d>\n\n");
        res = -1;
        goto end;
    }

    ifindex = atoi(argv[1]);

    sscanf(argv[2], "%x:%x:%x:%x:%x:%x", dest, dest + 1, dest + 2, dest + 3, dest + 4, dest + 5);

    /* dest[0] = 0x00; */
    /* dest[1] = 0x20; */
    /* dest[2] = 0xaf; */
    /* dest[3] = 0xf4; */
    /* dest[4] = 0xcc; */
    /* dest[5] = 0x69; */

    for (i = 0; i < ETH_ALEN; i++)
    {
        dest_mac[i] = (unsigned char) dest[i];
        printf("%x ", dest_mac[i]);
    }
    printf("\n");

    sscanf(argv[3], "%d.%d.%d.%d.", dest, dest + 1, dest + 2, dest + 3);

    for (i = 0; i < IP_ALEN; i++)
        dest_ip[i] = (unsigned char) dest[i];

    // lettura del mac e ip locali
    printf("lettura del mac e ip locali di ");
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1)
    {
        perror("socket() #1");
        res = -1; /*  */
        goto end;
    }

    memset(ifr.ifr_name, 0, sizeof(ifr.ifr_name));
    sprintf(ifr.ifr_name, "%s%d", strEthName, ifindex);
    printf("%s%d\n\n", strEthName, ifindex);

    res = ioctl(sockfd, SIOCGIFADDR, &ifr);
    if (res == -1)
    {
        perror("ioctl() #1");
        goto end;
    }

    for (i = 0; i < IP_ALEN; i++)
        src_ip[i] = ifr.ifr_dstaddr.sa_data[i + 2];

    res = ioctl(sockfd, SIOCGIFHWADDR, &ifr);
    if (res == -1)
    {
        perror("ioctl() #2");
        goto end;
    }

    for (i = 0; i < ETH_ALEN; i++)
        src_mac[i] = ifr.ifr_dstaddr.sa_data[i];

    if (sockfd != -1)
        close(sockfd);

    printf("IP address:  %d.%d.%d.%d\n", src_ip[0], src_ip[1], src_ip[2], src_ip[3]);
    printf("Eth address: %02x:%02x:%02x:%02x:%02x:%02x\n", src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4],
        src_mac[5]);

    printf("create an endpoint for communication\n");
    sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sockfd == -1)
    {
        printf("htons -> %d\n", htons(ETH_P_ALL));
        printf("errno = %s\n", strerror(errno));
        perror("socket() #2");
        res = -1;
        goto end;
    }

    // Various buffer pointers
    buffer = malloc(ETH_HEADER_LEN + IPASS_HLEN);
    etherhead = buffer;
    data = buffer + ETH_HEADER_LEN;
    eh = (struct ethhdr*) etherhead;

    socket_address.sll_family = PF_PACKET; /* Always AF_PACKET */
    socket_address.sll_protocol = htons(ETH_P_IP); /* Physical layer protocol in network order */
    socket_address.sll_ifindex = 2; //ifindex + 2;       /* index of the network device */
    //socket_address.sll_ifindex = 2;       /* index of the network device */
    socket_address.sll_hatype = ARPHRD_ETHER; /* Header type */
    socket_address.sll_pkttype = PACKET_OTHERHOST; /* Packet type */
    socket_address.sll_halen = ETH_ALEN; /* Length of address */
    memcpy(socket_address.sll_addr, dest_mac, 6); /* Physical layer address */
    socket_address.sll_addr[6] = 0x00;
    socket_address.sll_addr[7] = 0x00;

    /* Set the frame header */
    memcpy(buffer, dest_mac, ETH_ALEN);
    memcpy(buffer + ETH_ALEN, src_mac, ETH_ALEN);
    eh->h_proto = ETH_P_IPASS;

    /* Set User data */
    memset(data, 0, IPASS_HLEN);
    data->ipass_op = IPASS_OP_SET;
    memcpy(data->ipass_sha, src_mac, ETH_ALEN);
    memcpy(data->ipass_sip, src_ip, IP_ALEN);
    memcpy(data->ipass_tha, dest_mac, ETH_ALEN);
    memcpy(data->ipass_tip, dest_ip, IP_ALEN);

    printf("send a message on a socket\n");
    res = sendto(sockfd, buffer, ETH_HEADER_LEN + IPASS_HLEN, 0, (struct sockaddr*) &socket_address, sizeof(socket_address));
    if (res == -1)
    {
        perror("sendto()");
        goto end;
    }

    end:

    if (sockfd != -1)
        close(sockfd);

    if (res == -1)
        printf("Unable to send BCU IP command\n");
    else
        printf("New BCU IP command successfully sent\n");



    return 0;
}
