C 未通过linux上的原始套接字发送UDP数据包

C 未通过linux上的原始套接字发送UDP数据包,c,sockets,network-programming,udp,raw-sockets,C,Sockets,Network Programming,Udp,Raw Sockets,过去几天我一直在学习原始套接字,我无法通过原始套接字发送UDP数据包。我尝试为此编写代码,但UDP数据包没有发送。(我是通过wireshark捕获数据包的,只发送了IPv4数据包,包含IP层的头)。我在互联网上搜索了很多,也尝试了各种代码,但仍然无法发送UDP数据包(wireshark上没有看到)。是否有人可以建议对代码进行更改,或者指出与此相关的资源 #include <stdlib.h> #include <unistd.h> #include <stdio.h

过去几天我一直在学习原始套接字,我无法通过原始套接字发送UDP数据包。我尝试为此编写代码,但UDP数据包没有发送。(我是通过wireshark捕获数据包的,只发送了IPv4数据包,包含IP层的头)。我在互联网上搜索了很多,也尝试了各种代码,但仍然无法发送UDP数据包(wireshark上没有看到)。是否有人可以建议对代码进行更改,或者指出与此相关的资源

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/udp.h>

#define PCKT_LEN 8192

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(int argc, char const *argv[])
{
  if (argc != 5) {
    printf("Error: Invalid parameters!\n");
    printf("Usage: %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
    exit(1);
  }

  u_int16_t src_port, dst_port;
  u_int32_t src_addr, dst_addr;
  src_addr = inet_addr(argv[1]);
  dst_addr = inet_addr(argv[3]);
  src_port = atoi(argv[2]);
  dst_port = atoi(argv[4]);

  int sd;
  char buffer[PCKT_LEN];
  struct iphdr *ip = (struct iphdr *) buffer;
  struct udphdr *udp = (struct udphdr *) (buffer + sizeof(struct iphdr));

  struct sockaddr_in sin;
  int one = 1;
  const int *val = &one;

  memset(buffer, 0, PCKT_LEN);

  // create a raw socket with UDP protocol
  sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
  if (sd < 0) {
    perror("socket() error");
    exit(2);
  }
  printf("OK: a raw socket is created.\n");

  // inform the kernel do not fill up the packet structure, we will build our own
  if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
    perror("setsockopt() error");
    exit(2);
  }
  printf("OK: socket option IP_HDRINCL is set.\n");

  sin.sin_family = AF_INET;
  sin.sin_port = htons(dst_port);
  sin.sin_addr.s_addr = dst_addr;

  // fabricate the IP header
  ip->ihl      = 5;
  ip->version  = 4;
  ip->tos      = 16; // low delay
  ip->tot_len  = sizeof(struct iphdr) + sizeof(struct udphdr);
  ip->id       = htons(54321);
  ip->ttl      = 64; // hops
  ip->protocol = 17; // UDP
  // source IP address, can use spoofed address here
  ip->saddr = src_addr;
  ip->daddr = dst_addr;

  // fabricate the UDP header
  udp->source = htons(src_port);
  // destination port number
  udp->dest = htons(dst_port);
  udp->len = htons(sizeof(struct udphdr));

  // calculate the checksum for integrity
  ip->check = csum((unsigned short *)buffer,
                   sizeof(struct iphdr) + sizeof(struct udphdr));

  if (sendto(sd, buffer, ip->tot_len, 0,
             (struct sockaddr *)&sin, sizeof(sin)) < 0)
  {
    perror("sendto()");
    exit(3);
  }
  printf("OK: one packet is sent.\n");

  close(sd);
  return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义PCKT_LEN 8192
无符号短csum(无符号短*buf,整数nwords)
{
无符号长和;
对于(总和=0;nwords>0;nwords--)
总和+=*buf++;
总和=(总和>>16)+(总和&0xffff);
总和+=(总和>>16);
返回值(无符号短)(~sum);
}
int main(int argc,char const*argv[]
{
如果(argc!=5){
printf(“错误:无效参数!\n”);
printf(“用法:%s\n”,argv[0]);
出口(1);
}
u_int16_t src_端口、dst_端口;
u_int32_t src_addr,dst_addr;
src_addr=inet_addr(argv[1]);
dst_addr=inet_addr(argv[3]);
src_port=atoi(argv[2]);
dst_port=atoi(argv[4]);
国际标准差;
字符缓冲区[PCKT_LEN];
结构iphdr*ip=(结构iphdr*)缓冲区;
结构udphdr*udp=(结构udphdr*)(缓冲区+sizeof(结构iphdr));
sin中的结构sockaddr_;
int-one=1;
常量int*val=&one;
memset(缓冲区,0,PCKT_LEN);
//使用UDP协议创建原始套接字
sd=套接字(PF_INET、SOCK_RAW、IPPROTO_UDP);
如果(sd<0){
perror(“套接字()错误”);
出口(2);
}
printf(“确定:已创建原始套接字。\n”);
//通知内核不要填充包结构,我们将构建自己的包结构
如果(设置锁定选项(sd、IPPROTO_IP、IP_HDRINCL、val、sizeof(一个))小于0){
perror(“setsockopt()错误”);
出口(2);
}
printf(“确定:设置了套接字选项IP_HDRINCL。\n”);
sin.sin_family=AF_INET;
sin.sin_端口=htons(dst_端口);
sin.sin_addr.s_addr=dst_addr;
//制作IP报头
ip->ihl=5;
ip->version=4;
ip->tos=16;//低延迟
ip->tot_len=sizeof(struct iphdr)+sizeof(struct udphdr);
ip->id=htons(54321);
ip->ttl=64;//跳数
ip->协议=17;//UDP
//源IP地址,可在此处使用伪造地址
ip->saddr=src\u addr;
ip->daddr=dst\U地址;
//制作UDP报头
udp->source=htons(src_端口);
//目的地端口号
udp->dest=htons(dst_端口);
udp->len=htons(sizeof(struct udphdr));
//计算完整性校验和
ip->check=csum((无符号短*)缓冲区,
sizeof(struct iphdr)+sizeof(struct udphdr));
如果(发送到)(sd,缓冲区,ip->tot_len,0,
(结构sockaddr*)和sin,sizeof(sin))<0)
{
perror(“sendto()”);
出口(3);
}
printf(“确定:发送一个数据包。\n”);
关闭(sd);
返回0;
}

一个天真的问题:您是否以root用户身份运行您的程序(例如,
sudo myprogram
),因为您需要
CAP\u NET\u RAW
(根据
man 7 RAW
)?我发现:这似乎与手册页相矛盾。具体来说,它在
套接字
调用中使用
PF_数据包
而不是
PF_INET
。而且,这意味着您必须构造自己的以太网报头(位于其他报头之前)和接口的MAC地址等。当我编写一些商业S/W来嗅探数据包时,我使用的是
pfu-PACKET
是的,我在执行代码时使用了sudo。正如我所说,wireshark数据包捕获中只缺少UDP报头。我是否也必须构建以太网报头,因为我看到的大多数示例仅使用IP_HDRINCL选项创建了IP和UDP报头。使用wireshark,您应该能够将以太网报头视为数据的第一部分。那么,你看到了什么?它不太可能在中间有一个缺口。你会看到
eth | ip | udp |数据
或者仅仅是
ip | udp |数据
,但你不会看到
eth | ip |数据
[或者
ip |数据
]。所以,我在数据中加入了一个签名模式,以便能够在wireshark中看到数据偏移量。尝试发送[数据]:
0x00、0x01、0x02,…
(例如)。然后,看看最终的偏移量是多少。让发送方程序将整个缓冲区以十六进制转储到标准输出,并与wireshark.Re进行比较<代码>PF_数据包,请参见我的答案: