Raw Socket Linux send/receive a packet
Asked Answered
C

2

11

Have some problems in receiving packets. I can receive and read incoming packets, but I think i do not get a handshake with any host. I only want to send a packet to a remote computer with an open port on receiving an answer to see the TTL(time to live) and the window size. Does anyone have an idea where the errors are? (I don't have very deep knowledge in C programming)

CODE:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

struct pseudohdr {
    u_int32_t src_addr;
    u_int32_t dst_addr;
    u_int8_t padding;
    u_int8_t proto;
    u_int16_t length;
};

struct data_4_checksum {
    struct pseudohdr pshd;
    struct tcphdr tcphdr;
    char payload[1024];
};
unsigned short comp_chksum(unsigned short *addr, int len) {
    long sum = 0;

    while (len > 1) {
        sum += *(addr++);
        len -= 2;
    }

    if (len > 0)
        sum += *addr;

    while (sum >> 16)
        sum = ((sum & 0xffff) + (sum >> 16));

    sum = ~sum;

    return ((u_short) sum);

}

int main(int argc, char *argv[]) {

    int sock, bytes, on = 1;
    char buffer[1024];
    struct iphdr *ip;
    struct tcphdr *tcp;
    struct sockaddr_in to;
    struct pseudohdr pseudoheader;
    struct data_4_checksum tcp_chk_construct;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s ", argv[0]);
        fprintf(stderr, "<dest-addr>\n");
        return 1;
    }

    sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sock == -1) {
        perror("socket() failed");
        return 1;
    }else{
        printf("socket() ok\n");
    }

    if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) {
        perror("setsockopt() failed");
        return 2;
    }else{
        printf("setsockopt() ok\n");
    }

    ip = (struct iphdr*) buffer;
    tcp = (struct tcphdr*) (buffer + sizeof(struct tcphdr));

    int iphdrlen = sizeof(struct iphdr);
    int tcphdrlen = sizeof(struct tcphdr);
    int datalen = 0;
    printf("Typecasting ok\n");

    ip->frag_off = 0;
    ip->version = 4;
    ip->ihl = 5;
    ip->tot_len = htons(iphdrlen + tcphdrlen);
    ip->id = 0;
    ip->ttl = 40;
    ip->protocol = IPPROTO_TCP;
    ip->saddr = inet_addr("192.168.165.135");
    ip->daddr = inet_addr(argv[1]);
    ip->check = 0;

    tcp->source     = htons(12345);
    tcp->dest       = htons(80);
    tcp->seq        = random();
    tcp->doff       = 5;
    tcp->ack        = 0;
    tcp->psh        = 0;
    tcp->rst        = 0;
    tcp->urg        = 0;
    tcp->syn        = 1;
    tcp->fin        = 0;
    tcp->window     = htons(65535);

    pseudoheader.src_addr = ip->saddr;
    pseudoheader.dst_addr = ip->daddr;
    pseudoheader.padding = 0;
    pseudoheader.proto = ip->protocol;
    pseudoheader.length = htons(tcphdrlen + datalen);

    tcp_chk_construct.pshd = pseudoheader;
    tcp_chk_construct.tcphdr = *tcp;

    int checksum = comp_chksum((unsigned short*) &tcp_chk_construct,
            sizeof(struct pseudohdr) + tcphdrlen + datalen);

    tcp->check = checksum;

    printf("TCP Checksum: %i\n", checksum);
    printf("Destination : %i\n", ntohs(tcp->dest));
    printf("Source: %i\n", ntohs(tcp->source));

    to.sin_addr.s_addr = ip->daddr;
    to.sin_family = AF_INET;
    to.sin_port = tcp->dest;

    bytes = sendto(sock, buffer, ntohs(ip->tot_len), 0, (struct sockaddr*) &to,
            sizeof(to));

    if (bytes == -1) {
        perror("sendto() failed");
        return 1;
    }

    recv(sock, buffer, sizeof(buffer), 0);
    printf("TTL= %d\n", ip->ttl);
    printf("Window= %d\n", tcp->window);
    printf("ACK= %d\n", tcp->ack);
    printf("%s:%d\t --> \t%s:%d \tSeq: %d \tAck: %d\n",
                    inet_ntoa(*(struct in_addr*) &ip->saddr), ntohs(tcp->source),
                    inet_ntoa(*(struct in_addr *) &ip->daddr), ntohs(tcp->dest),
                    ntohl(tcp->seq), ntohl(tcp->ack_seq));

    return 0;
}
Casey answered 30/12, 2012 at 4:16 Comment(12)
with incoming packets i mean, there are other incoming from everywhere but not from i needCasey
why to parse the packet? i was trying to get some response on a clean unix machine under vmware. when i sended a packet manually, i got what i need. but if i simply wait nothing happens. under real system there are packets comming in. but these are from somewhere else.Casey
I just re-posted my comments as an actual answer. I hope you don't mind.Latin
Layer 2 => Data Link? I thought for layer 2 there are packet sockets. In my code I use typecast to access the buffer in the right way. And you don't get my problem. My problem is, that I'am receiving packets. Program works but I'am not receiving an answer to my packet I have send.Casey
root@***:/********# ./OS_Sniffer 141.28.78.142Casey
socket() ok setsockopt() ok Typecasting ok TCP Checksum: 350 Destination : 80 Source: 12345 TTL= 52 Window= 31232 ACK= 1 67.228.168.221:80 --> 67.228.168.221:60806 Seq: -1525233884 Ack: -46020552Casey
sorry, typo. It's Layer 3.Latin
no prob. please look at the output. it works, but wrong :)Casey
I don't see what's wrong with your output. The negative values? Print them using the unsigned format specifier, %u. Other than that, could you highlight?Latin
67.228.168.221:80 --> 67.228.168.221:60806 But I need something from 141.28.78.142Casey
I'am working with C only for few weeks. Thats why its little bit difficult for meCasey
Understandable, but you're doing great. Please, take a look at point 4 in my answer.Latin
L
13
  1. You're receiving and storing packets in buffer, but you're printing data from ip and tcp without parsing that buffer. You should parse the packet from buffer after receiving it, and before printing.
  2. Your code assumes all packets are TCP, which is not the case. RAW sockets only support Layer 3 protocols (IP, ICMP, etc). In other words, using IPPROTO_TCP is misleading when creating a RAW socket. Stick to IPPROTO_IP, and add the necessary conditions to your code for each protocol you care about (TCP, UDP, etc). This happens to be working because the Linux Kernel validates the protocol number, and fallbacks to IPPROTO_IP. However, this might not work in other systems.
  3. Review if your network communication is using the correct byte-order. The network-byte-order is Big-Endian, while the host-byte-order depends on your architecture, so you may need to convert multi-byte fields back and forth.
  4. Your tcp->seq might have an invalid value, because TCP only accepts values up to 65535, while random() returns values from 0 to RAND_MAX (0x7fffffff). Try tcp->seq = htonl(random() % 65535);
  5. Your offset calculation for the TCP header is incorrect. It should be sizeof(struct iphdr) rather than sizeof(struct tcphdr).
Latin answered 30/12, 2012 at 4:48 Comment(6)
Is it even possible to receive TCP or UDP packets with a raw socket? I thought these incoming packets were always passed to the TCP and UDP drivers.Mayest
@Mayest it's possible. The kernel delivers a copy of each datagram to every process that has created a RAW socket that matches the datagram protocol.Latin
Found the error! Its the buffer, because i was sending some rubbish to the remote host. Therefore i could not receive an answer. Correction: char buffer[1024]={0};Casey
Regarding your point 2, trying to use IPPROTO_IP results in an error Protocol not supported for me, when calling socket() on linux. Using either IPPROTO_TCP or IPPROTO_RAW works, although I didn't check whether it filters TCP packets or not.Clingy
@jweyrich, ICMP is layer 3?Saberhagen
@Saberhagen yep. It's encapsulated in IPv4/6 packets. It's part of the IP though.Latin
M
0
ip = (struct iphdr*) buffer;
tcp = (struct tcphdr*) (buffer + sizeof(struct tcphdr)); //This is wrong

Here to get array index of tcp header in buffer, you need to add sizeof(struct iphdr) to buffer like mentioned below.

ip = (struct iphdr*) buffer;
tcp = (struct tcphdr*) (buffer + sizeof(struct iphdr)); //This is correct
Moriahmoriarty answered 29/12, 2014 at 6:31 Comment(1)
Is not correct as it should use the ip->ihl * 4 to know the header size.Suffrage

© 2022 - 2024 — McMap. All rights reserved.