0

GOAL: crafting a packet from zero and send it.

PROBLEM: after I enabled "validate checksum" on wireshark I noticed that the expected checksum value should be 0xa85d but the checksum function I'm using computes a different value each time. Why the checksum is changing if the packet is always the same? What is the cause of the checksum mismatch between the one I compute and the one expected by wireshark? Thanks

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h> /* the L2 protocols */
#include <netinet/ip.h> 
#include <netinet/udp.h> 
#include <stdio.h>
#include <arpa/inet.h> /* htons */
#include <sys/ioctl.h> 
#include <net/if.h> /* ifreq */
#include <string.h> 
#include <stdlib.h>

#include <linux/seccomp.h> /* seccomp_data */

#define BUF_SIZE 1024 



// https://gist.github.com/leonid-ed/909a883c114eb58ed49f
unsigned short csum(unsigned short *buf, int nwords)
{
  unsigned long sum;
  for(sum=0; nwords>0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum &0xffff);
  sum += (sum >> 16);
  return (unsigned short)(~sum);
}

int main(){

    const char IF[] = "lo"; // modify to change interface
    int sockfd, ifindex, tx_len=sizeof(struct ether_header)+sizeof(struct iphdr)+sizeof(struct udphdr);
    struct ifreq ifr;
    size_t if_name_len;
    char buf[BUF_SIZE];
    struct ether_header *eh = (struct ether_header *) buf;
    struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header));
    struct udphdr *udph = (struct udphdr *) (buf + sizeof(struct ether_header) + sizeof(struct iphdr));
    unsigned char *data = buf + sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct udphdr);
    u_int16_t src_port, dst_port;
    struct sockaddr_ll dst_addr;
    struct seccomp_data sec_payload;
    const char dmac[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
    const char smac[] = {0x00, 0xd0, 0x56, 0xf2, 0xb5, 0x12};

    // create raw socket to send/receive ethernet frames that can transport all protocols
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
        perror("socket");
    }

    // get interface name length
    if_name_len = strlen(IF);
    if(if_name_len < IF_NAMESIZE) {
        strncpy(ifr.ifr_name, IF, strlen(IF));
        ifr.ifr_name[if_name_len]=0;
    }

    // get the interface index number
    if(ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1){
        perror("ioctl");
    }
        ifindex = ifr.ifr_ifindex;

    // build ethernet header    
        memcpy(eh->ether_dhost, dmac, ETHER_ADDR_LEN);  
        memcpy(eh->ether_shost, smac, ETHER_ADDR_LEN);  
    eh->ether_type = htons(ETH_P_IP);

    // add a struct seccomp_data as data 
        memset(&sec_payload,5,sizeof(struct seccomp_data)); 
    data = (char*)malloc(sizeof(struct seccomp_data));
    memcpy(data, (const unsigned char *)&sec_payload, sizeof(struct seccomp_data));

    int i;
    for(i=0;i<sizeof(sec_payload);i++){
        buf[tx_len++] = data[i];
        printf("%02X ",data[i]);
    }

    // build ip header
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(sec_payload));
    iph->id = htons(54321);
    iph->frag_off = 0;
    iph->ttl = 64;
    iph->protocol = IPPROTO_UDP;
    iph->check = 0;
    iph->saddr = inet_addr("127.0.0.1");
    iph->daddr = inet_addr("127.0.0.1");

    // build udp header 
    udph->source = htons(8090);
    udph->dest = htons(8091);
    udph->len = htons(sizeof(struct udphdr) + sizeof(sec_payload));

    iph->check = csum((unsigned short *)buf,  sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(sec_payload));

    memset(&dst_addr,0,sizeof(struct sockaddr_ll)); 
    dst_addr.sll_ifindex = ifr.ifr_ifindex; 
    dst_addr.sll_halen = ETH_ALEN;
    memcpy(dst_addr.sll_addr, dmac, ETH_ALEN);

        if (sendto(sockfd, buf, tx_len, 0, (struct sockaddr*)&dst_addr, sizeof(struct sockaddr_ll)) < 0)
        printf("Send failed\n");
    return 0;
}
Maicake
  • 1,046
  • 10
  • 34
  • 1
    _[RFC 1071, Computing the Internet Checksum](https://tools.ietf.org/html/rfc1071)_ explains how to calculate the checksum, including sample C code in Section 4.1 (the IPv4 checksum is over the IPv4 header, as explained in _[RFC 791, Internet Protocol](https://tools.ietf.org/html/rfc791)_). – Ron Maupin Nov 15 '19 at 15:27
  • Should I use 10 as the number of 16bit (2bytes) words given the fact that the ip header is 20bytes long? My ideas is that the number of words is always 10 or this assumption is wrong? – Maicake Nov 15 '19 at 16:01
  • 1
    You should really calculate the length of the IPv4 header because it is a variable length. An IPv4 header will be at least five 32-bit words long, and as much as 15 32-bit words long. The header length, in 32-bit words, is contained in the IHL field of the IPv4 header, and you can take that value and double it for the length of the header in 16-bit words. – Ron Maupin Nov 15 '19 at 16:07
  • The header size is different from 5 words only if options field is set. In the example I didn't set the field option. Can this alter the header length and consequently the checksum? – Maicake Nov 16 '19 at 08:52
  • 1
    You can use the magic number `10` if you want, but I am saying that is a poor programming practice. Proper code would derive the number of 16-bit words in the header from the header field that tells you how long the header is. That is the entire purpose of the field. Also, remember that you must also calculate the checksum on incoming packets, where you have no idea of the header length, so one method for deriving the checksum that can be called by both sending and receiving routines. Remember that magic numbers should be avoided. – Ron Maupin Nov 16 '19 at 15:57

0 Answers0