Implementing a dummy verification protocol on top of ETH protocol stack

I recently completed a course "Inter Networking Technologies" as a disciplinary elective. As a part of the course me and my group were asked to complete various assignments, so few of my next posts will be about that. And to those who are considering about the course, I urge you to take this course if you have any interest in Networking. You will learn a lot in this about the architecture of different networks. 

The dummy verification protocol could be developed on top of either UDP or IP or ETH protocol. We chose ETH protocol as it was the easy way ahead. The verification protocol required to demonstrate a client sending a 32-bit nonce in network byte order as payload of an UDP message to the server and the server increments nonce by one and returns the reply to the client. 

Here are the server and client codes that implement the protocol using ethernetframe.

Server Code :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>


// Ethernet frame data structure
union ethernetframe{  
  struct{
    struct ethhdr header;
    unsigned char data[ETH_DATA_LEN];
  } field;
  unsigned char buffer[ETH_FRAME_LEN];
};


int main(int argc, char* argv[]){  
    char *iface = "eth0";
    unsigned char dest[ETH_ALEN]
           = { 0x, 0x, 0x, 0x, 0x, 0x }; // Client mac address
    unsigned short int protocol_type = 0x88b5;
    short int sock_desc=0, count=0;

    if ((sock_desc = socket(AF_PACKET, SOCK_RAW, htons(protocol_type))) < 0)
        printf("\nError creating socket\n");
    else{
        printf("\nListening...\n");
        struct sockaddr_ll sock_addrll;
        unsigned char frame_buffer[ETH_FRAME_LEN];
        int data_rcvd=0;
        socklen_t sock_addrll_len = (socklen_t)sizeof(sock_addrll);
        data_rcvd = recvfrom(
                    sock_desc,
                    frame_buffer,
                    ETH_FRAME_LEN,
                    0x00,
                    (struct sockaddr*) &sock_addrll,
                    &sock_addrll_len
                );
        // Printing received data
        printf("\nData received: %s\n", frame_buffer+sizeof(struct ethhdr));

        // Converting string to integer and incrementing nonce
        int new_data = atoi(frame_buffer+sizeof(struct ethhdr)) + 1;\
        bzero(&frame_buffer, sizeof(frame_buffer));

        // Converting changed nonce back to string
        snprintf(frame_buffer, sizeof(frame_buffer), "%d", new_data);
        unsigned short data_len = strlen(frame_buffer);

        // Creating time delay so that client initiates listening mode
        long long int z; for(z=0; z<99999999; z++);

        // Creating packet socket
        int s;
        if ((s = socket(AF_PACKET, SOCK_RAW, htons(protocol_type))) < 0) {
            printf("Error: could not open socket\n");
            return -1;
        }

        // Looking up the interface index
        struct ifreq buffer;
        int ifindex;
        memset(&buffer, 0x00, sizeof(buffer));
        strncpy(buffer.ifr_name, iface, IFNAMSIZ);
        if (ioctl(s, SIOCGIFINDEX, &buffer) < 0) {
            printf("Error: could not get interface index\n");
            close(s);
            return -1;
        }
        ifindex = buffer.ifr_ifindex;

        // Looking up source MAC address
        unsigned char source[ETH_ALEN];
        if (ioctl(s, SIOCGIFHWADDR, &buffer) < 0) {
            printf("Error: could not get interface address\n");
            close(s);
            return -1;
        }
        memcpy((void*)source, (void*)(buffer.ifr_hwaddr.sa_data),
             ETH_ALEN);

        // Filling in the packet fields
        union ethernetframe frame;
        memcpy(frame.field.header.h_dest, dest, ETH_ALEN);
        memcpy(frame.field.header.h_source, source, ETH_ALEN);
        frame.field.header.h_proto = htons(protocol_type);
        memcpy(frame.field.data, frame_buffer, data_len);

        unsigned int frame_len = data_len + ETH_HLEN;

        // Filling in the sockaddr_ll struct
        struct sockaddr_ll saddrll;
        memset((void*)&saddrll, 0, sizeof(saddrll));
        saddrll.sll_family = PF_PACKET;
        saddrll.sll_ifindex = ifindex;
        saddrll.sll_halen = ETH_ALEN;
        memcpy((void*)(saddrll.sll_addr), (void*)dest, ETH_ALEN);

        // Sending the packet
        if (sendto(s, frame.buffer, frame_len, 0,
                 (struct sockaddr*)&saddrll, sizeof(saddrll)) > 0){
            printf("Data sent successfully!\n");
        }else{
            printf("Error, could not send data.\n");
        }

        // Closing socket
        close(s);
    }
    return 0;
}

Client Code :

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>

union ethernetframe{  
    struct{
      struct ethhdr    header;
      unsigned char    data[ETH_DATA_LEN];
    } field;
    unsigned char    buffer[ETH_FRAME_LEN];
};

int main(int argc, char **argv) {  
    if (argc < 2){
      printf("Please provide the data to send in argument\n");
      return -1;
    }

    char *iface = "eth0";
    unsigned char dest[ETH_ALEN]
             = { 0x, 0x, 0x, 0x, 0x, 0x }; 
    unsigned short proto = 0x88b5; // Ethernet payload type
    unsigned char *data = argv[1];  // Reading data from command line argument
    unsigned short data_len = strlen(data);

    // Creating packet socket
    int s;
    if ((s = socket(AF_PACKET, SOCK_RAW, htons(proto))) < 0) {
        printf("Error: could not open socket\n");
        return -1;
    }

    // Looking up the interface index
    struct ifreq buffer;
    int index;
    memset(&buffer, 0x00, sizeof(buffer));
    strncpy(buffer.ifr_name, iface, IFNAMSIZ);
    if (ioctl(s, SIOCGIFINDEX, &buffer) < 0) {
        printf("Error: could not get interface index\n");
        close(s);
        return -1;
    }
    index = buffer.ifr_ifindex;

    // Looking up source MAC address
    unsigned char source[ETH_ALEN];
    if (ioctl(s, SIOCGIFHWADDR, &buffer) < 0) {
        printf("Error: could not get interface address\n");
        close(s);
        return -1;
    }
    memcpy((void*)source, (void*)(buffer.ifr_hwaddr.sa_data),
           ETH_ALEN);


    // Filling in the packet fields
    union ethernetframe frame;
    memcpy(frame.field.header.h_dest, dest, ETH_ALEN);
    memcpy(frame.field.header.h_source, source, ETH_ALEN);
    frame.field.header.h_proto = htons(proto);
    memcpy(frame.field.data, data, data_len);

    unsigned int frame_len = data_len + ETH_HLEN;

    // Filling in the sockaddr_ll struct
    struct sockaddr_ll saddrll;
    memset((void*)&saddrll, 0, sizeof(saddrll));
    saddrll.sll_family = PF_PACKET;
    saddrll.sll_ifindex = index;
    saddrll.sll_halen = ETH_ALEN;
    memcpy((void*)(saddrll.sll_addr), (void*)dest, ETH_ALEN);

    // Sending the packet
    if (sendto(s, frame.buffer, frame_len, 0,
               (struct sockaddr*)&saddrll, sizeof(saddrll)) > 0){
      printf("Data sent successfully!\n");
    }else{
      printf("Error, could not send data.\n");
    }

    // Closing socket
    close(s);


    // Initiating receiver socket
    short int sock_desc=0, count=0;
    if ((sock_desc = socket(AF_PACKET, SOCK_RAW, htons(proto))) < 0)
          printf("\nError creating socket\n");
    else{
          printf("\nListening...\n");
          struct sockaddr_ll sock_addrll;
          unsigned char frame_buffer[ETH_FRAME_LEN];
          int data_rcvd=0;
          socklen_t sock_addrll_len = (socklen_t)sizeof(sock_addrll);
          data_rcvd = recvfrom(
                      sock_desc,
                      frame_buffer,
                      ETH_FRAME_LEN,
                      0x00,
                      (struct sockaddr*) &sock_addrll,
                      &sock_addrll_len
                  );
          printf("\nData received: %s\n",frame_buffer+sizeof(struct ethhdr));
    }
    return 0;
}

The data field in the ethernetframe is used to store the data to be transferred and the frame is used to fill the packet fields.
The basic MAC data frame format for Ethernet, IEEE 802.3 used within the 10 and 100 Mbps systems is given below:

The protocol type used here _0x88B5 is _ethexp, Local Experimental Ethertype 1.

Notes :

  • dest[ETHALEN]_ is used to store the client's MAC address. Make sure to enter the correct address in the server code.
  • Standard libraries_ 'linux/ifether.h' , 'linux/ifpacket.h ', 'sys/ioctl.h'_ were used to define the ETH frame.

The rest of the code is standard client server used in basic Networking in C. If you have any doubt, do drop in a comment.
Happy to help!! Happy coding!! Cheers!!