C语言 原始套接字 Linux 发送/接收数据包
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14088274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Raw Socket Linux send/receive a packet
提问by x4k3p
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)
接收数据包有问题。我可以接收和读取传入的数据包,但我认为我没有与任何主机握手。我只想在收到答案时将数据包发送到具有开放端口的远程计算机,以查看 TTL(生存时间)和窗口大小。有谁知道错误在哪里?(我对 C 编程没有很深的了解)
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;
}
回答by jweyrich
- You're receiving and storing packets in
buffer, but you're printing data fromipandtcpwithout parsing that buffer. You should parse the packet frombufferafter receivingit, and before printing. - 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_TCPis misleading when creating a RAW socket. Stick toIPPROTO_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 toIPPROTO_IP. However, this might not work in other systems. - 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.
- Your
tcp->seqmight have an invalid value, because TCP only accepts values up to 65535, whilerandom()returns values from 0 toRAND_MAX(0x7fffffff). Trytcp->seq = htonl(random() % 65535); - Your offset calculation for the TCP header is incorrect. It should be
sizeof(struct iphdr)rather thansizeof(struct tcphdr).
- 您在 中接收和存储数据包
buffer,但是您正在打印来自ip和tcp不解析该缓冲区的数据。您应该buffer在收到数据包之后和打印之前解析数据包。 - 您的代码假定所有数据包都是 TCP,但事实并非如此。RAW 套接字仅支持第 3 层协议(IP、ICMP 等)。换句话说,
IPPROTO_TCP在创建 RAW 套接字时使用会产生误导。坚持IPPROTO_IP,并为您关心的每个协议(TCP、UDP 等)添加必要条件到您的代码中。这恰好是有效的,因为 Linux 内核验证协议编号,并回退到IPPROTO_IP. 但是,这可能不适用于其他系统。 - 检查您的网络通信是否使用正确的字节顺序。网络字节顺序是 Big-Endian,而主机字节顺序取决于您的体系结构,因此您可能需要来回转换多字节字段。
- 您
tcp->seq可能有一个无效值,因为 TCP 只接受最多 65535 的random()值,而返回从 0 到RAND_MAX(0x7fffffff) 的值。尝试tcp->seq = htonl(random() % 65535); - 您对 TCP 标头的偏移计算不正确。应该是
sizeof(struct iphdr)而不是sizeof(struct tcphdr)。
回答by rashok
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 bufferlike mentioned below.
这里要获取 tcp header 的数组索引buffer,你需要添加sizeof(struct iphdr)到buffer下面提到的。
ip = (struct iphdr*) buffer;
tcp = (struct tcphdr*) (buffer + sizeof(struct iphdr)); //This is correct

