This is my almost full code which I think sending valid IP header for a TCP packet. But my code fails the Wireshark test that it's not recognized as TCP packet; instead Wireshark just label it as hideous raw packet with out any source and destination IPs. This is really a simple IPs packet that I was sure that does the job of having passed Wireshark test no it doesn't.
What am I doing wrong? currently no clue to look further on this how to debug it, what else I can set in my tcp or ip header. before it was giving me message in Wireshark Fragmented TCP packet what that even means, then started saying its simply malformed packet now its giving me messages that these response packets responding is simply raw packets sending from my this code with out source or destination ips, considering for TCP is not even showing that pane
void * receiver(void *data)
{
int recvlen = -1;
int writelen = -1;
while (!_do_exit) {
char buf[VPN_MAX_MTU] = {0};
char buf_1[VPN_MAX_MTU] = {0};
memset(buf,0,VPN_MAX_MTU);
memset(buf_1,0,VPN_MAX_MTU);
char *str_source=malloc(18);
char *str_dest=malloc(18);
memset(str_source,0,18);
memset(str_dest,0,18);
recvlen=read(_tun_fd,buf,VPN_MAX_MTU);
debug("SR:d\n", recvlen);
struct iphdr *iph=(struct iphdr *)buf;
struct iphdr *ip=(struct iphdr *)buf_1;
char str_src[18]={0};
char str_dest_t[18]={0};
memcpy(&ip->saddr,&iph->daddr,sizeof(uint32_t));
memcpy(&ip->daddr,&iph->saddr,sizeof(uint32_t));
printf("IN %s %s\n",get_ip_str_1(iph->saddr,str_src),get_ip_str_1(iph->daddr,str_dest_t));
//clear the str_str and str_dest_t with memset(,0)
printf("OUT %s %s\n",get_ip_str_1(ip->saddr,str_src),get_ip_str_1(ip->daddr,str_dest_t));
ip->ihl = 5;
ip->version = 4;
ip->tot_len = sizeof(struct iphdr) sizeof(struct tcphdr);
ip->protocol = IPPROTO_TCP;
ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr));
uint16_t k=csum(ip,sizeof(*ip));
ip->check=k;
printf("checksum %d | %d\n",iph->check,ip->check);
int i=iph->ihl*4;
struct tcphdr *tcph=(struct tcphdr *)(buf i);
int j=(struct iphdr *)(ip->ihl*4);
struct tcphdr *tcp=(struct tcphdr *)(buf_1 j);
populate_tcp_some(tcph,tcp);
printf("received syn = %d\n",tcph->syn);
if(tcph->syn==1)
{
populate_tcp_some(tcph,tcp);
tcp->syn=1;
tcp->ack=1;
tcp->dest=tcph->source;
tcp->source=htons(80);
printf("received tcp syn = %d\n",tcph->syn);
}
else{
populate_tcp_some(tcph,tcp);
tcp->syn=0;
tcp->ack=1;
tcp->dest=tcph->source;
tcp->source=htons(80);
printf("sending tcp syn = %d ack = %d\n",tcp->syn,tcp->ack);
}
tcp->check=0;
tcp->check=tcp_chksum(ip,tcp);
printf("checksums %d | %d\n",tcph->check,tcp->check);
if (recvlen > 0) {
writelen = write(_tun_fd, buf_1, sizeof(*ip));
debug("TW:d\n", writelen);
if (writelen < 0) {
debug("%s: rwrite() %s [%d]\n", _progname, strerror(errno), errno);
}
} else if (recvlen < 0) {
debug("%s: rrecvfrom() %s\n", _progname, strerror(errno));
} else if (recvlen == 0) {
}
}
debug("** Receiver ending.\n");
pthread_exit(NULL);
}
CodePudding user response:
Partial Answer
summary of the chat discussion so far.
The issue turned out to be in the opening of TUN interface, in a code, which is not shown.
Opening TUN interface is shown in this link. In section "basic usage" there is a following code snippet
int fd = open("/dev/net/tun", O_RDWR);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, "tun0", IFNAMSIZ);
ioctl(fd, TUNSETIFF, &ifr);
The usage of IFF_NO_PI
is explained in section "TUN interfaces" below. If this flag is not set, the packet (i.e., what is written in buffer in recvlen=read(_tun_fd,buf,VPN_MAX_MTU);
) is prefixed by a struct tun_pi
(link), which contains some flags and protocol. Protocol is the value that is usually written in ethertype in Ethernet header.
Since OP did not set the IFF_NO_PI
. The IP header in variable buf
contained 4 bytes of struct tun_pi
, which, as can be seen from the code is ignored. Since OP is trying to parse IP header where there is no IP header, the code does not work.
Writing to the descriptor also ignored the struct tun_pi
, which I think resulted in the kernel not interpreting the packet correctly.
debugging process
- there is a screenshot from wireshark. one can see that instead of an IP packet it contains some strange structure starting with several zeros and random numbers. IP header should start with bytes
45
. So the next step is to figure out what are the values inbuf
andbuf_1
. - after getting the content of the buffers printed, it turned out that
buf
started with00 80
and then45 ...
(... - the rest of IP packet). Obviously, the packet inbuf
is not parsed correctly, andbuf_1
is filled with some random values from buf instead of intended values. Looking for format returned bytun
descriptor, I have found the link above. - currently the packet is parsed as IP header in wireshark, but
protocol
field of IP header is parsed as ICMP in wireshark.
issues in code
(1), is that the code that parses the packet is not checking whether the code is correct.
First, after the line recvlen=read(_tun_fd,buf,VPN_MAX_MTU);
the packet is parsed without reading the value of recvlen. somewhere later there is a if (recvlen > 0) {
line, which actually should follow the read
line.
Also, before casting buf
to iphdr
one needs to check that the buf is at least 20bypes (or sizeof(struct iphdr)
long. Before casting next header to TCP one needs to check that the protocol is indeed TCP (by value of iphdr->protocol
) and the buf is long enough to contain TCP header
(2): ip->tot_len = sizeof(struct iphdr) sizeof(struct tcphdr);
unless there is no content in tcp header (not visible from the code), the length should be total length of the packet, not the length of two headers
(3): the line writelen = write(_tun_fd, buf_1, sizeof(*ip));
the last parameter should be the lenght of the packet, which is the value that should be written in ip->tot_len
(4): some values probably need to be translated from host to network byte order.