linux udp raw socket

tcp/udp网络通信与socket实际上是两个概念,不过因为我们平常使用tcp/udp,不可避免的使用socket,所以认为两者是同一个事物。

我们现在所说的或者最常用到的都是BSD版本的socket。socket是对tcp/udp等网络协议的封装,提供上层接口,供我们使用,可以编写程序在网络间传递数据。

tcp/ip是一种协议标准,规定了数据如何传输,socket相当于对这个标准的实现。

https://www.rfc-editor.org/rfc/rfc147.html

https://man7.org/linux/man-pages/man2/socket.2.html

raw socket 原生套接字

使用linux开发udp socket程序时,我们只需要接收发送数据,并不需要关心每个数据包的头如何组装、MTU大小是多少、IP如何分片,这都是底层socket实现的。如果需要对收发数据包的头进行操作,linux提供了原生套接字,可以获取到完整的数据包——完整的数据包包括以太网头->IP头->TCP/UDP头->负载数据。

使用原生套接字需要root权限

TCP/IP层原生套接字发送数据

linux提供的原生套接字功能,分两部分,一部分是获取到TCP/IP层,会屏蔽上层的以太网层的头部信息,也会屏蔽当前IP层分片等信息,也就是获取到的数据是处理过的,并不是真正原始的数据包,不过当前数据包包含IP层的头和TCP/UDP层的头;但是发送数据又不会帮我们自动分片。另一部分是获取原始数据包,也就是网络上数据包是什么样,就会获取到什么样的数据,包括以太网层的头,也就是真正原始的数据包。

#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[])
{
    u_int16_t src_port, dst_port;
    u_int32_t src_addr, dst_addr;
    src_addr = inet_addr("192.168.10.111");
    dst_addr = inet_addr("192.168.10.112");
    src_port = atoi("1234");
    dst_port = atoi("1234");

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(dst_port);
    sin.sin_addr.s_addr = dst_addr;

    int sd;
    // 接收数据的缓冲区
    char buffer[PCKT_LEN];
    memset(buffer, 0, PCKT_LEN);
    // 数据包含ip头和udp头,所以使用linux提供的ip头和udp头的结构体映射数据
    struct iphdr *ip = (struct iphdr *) buffer;
    struct udphdr *udp = (struct udphdr *) (buffer + sizeof(struct iphdr));

    // 创建socket
    // AF_INET表示ipv4协议
    // SOCK_RAW表示原生socket
    // IPPROTO_UDP表示接收udp协议
    sd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (sd < 0)
    {
        perror("socket() error");
        exit(-1);
    }

    int optval = 1;
    // 设置socket,由我们管理ip头和udp头
    // sd表示要设置的socket id
    // IPPROTO_IP表示设置的是IP层
    // IP_HDRINCL表示由用户层管理tcp/ip层的头
    // &optval 设置IP_HDRINCL的数值,这里1表示打开,传递的是设置数值的指针
    // sizeof(optval) 设置参数数值的大小,因为上面传递的是指针,这里就要给定大小,不然不知道设置的内容有多长
    if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) < 0)
    {
        perror("setsockopt error");
        exit(-1);
    }

    // 设置ip的头
    ip->ihl = 5;
    ip->version = 4;
    ip->tos = 16;
    ip->id = htons(54321);
    ip->ttl = 64;
    ip->protocol = 17;
    // 设置ip头中源ip的地址,这里就可以随意修改,很多伪造数据包攻击(比如反射攻击)就是用到这种方法
    ip->saddr = src_addr;
    ip->daddr = dst_addr;

    // 设置udp头
    udp->source = htons(src_port);
    udp->dest = htons(dst_port);
    // 设置ip的check位,有ip协议得知,这里只需要校验头,不包括payload
    ip->check = csum((unsigned short *) buffer, sizeof(struct iphdr) + sizeof(struct udphdr));
    int sendbufflen = 0;
    // 从udp头之后,填充用户数据,这里才是我们使用普通 socket发送数据填充的地方
    unsigned short *mbuffer = buffer + sizeof(struct iphdr) + sizeof(struct udphdr);
    int datalen = 1000;
    for (int i = 0; i < datalen; i++)
    {
        *(mbuffer + i) = (unsigned short) i;
    }
    sendbufflen = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen;
    // 设置ip和udp中记录数据包长度的字段。注意需要使用htons,把本地字节序转为网络字节序
    ip->tot_len = htons(sendbufflen);
    udp->len = htons(sizeof(struct udphdr) + datalen);
    // 发送数据
    if (sendto(sd, buffer, sendbufflen, 0, (struct sockaddr *) &sin, sizeof(sin)) < 0)
    {
        perror("sendto error");
        exit(-1);
    }

    close(sd);
    return 0;
}

这样我们就可以伪造一份网络数据包,并且发送到指定的服务器和端口。通过wireshark等抓包可以看到源ip地址并不是我们机器的地址,而是指定的地址。
https://man7.org/linux/man-pages/man3/setsockopt.3p.html

AF_INET与PF_INET

https://man7.org/linux/man-pages/man2/socket.2.html

有时候我们发现代码中有用AF_INET,也有用PF_INET,这两者有什么区别呢?实际上官方文档已经给了说明:

HISTORY         top

       POSIX.1-2001, 4.4BSD.

       socket() appeared in 4.2BSD.  It is generally portable to/from
       non-BSD systems supporting clones of the BSD socket layer
       (including System V variants).

       The manifest constants used under 4.x BSD for protocol families
       are PF_UNIX, PF_INET, and so on, while AF_UNIX, AF_INET, and so
       on are used for address families.  However, already the BSD man
       page promises: "The protocol family generally is the same as the
       address family", and subsequent standards use AF_* everywhere.

现在用的socket基本上都是有BSD版本迁移而来,当时设定根据协议簇和地址簇进行区分,但是最后没有实现,PF_*AF_*现在是相等的,所以用AF_INETPF_INET是一样的,不过建议使用AF_*

raw - Linux IPv4 raw sockets

IP_HDRINCL

设置了IP_HDRINCL后,有些ip头的数据系统是可以帮我们填充的:

┌───────────────────────────────────────────────────┐
│IP Header fields modified on sending by IP_HDRINCL │
├──────────────────────┬────────────────────────────┤
│IP Checksum           │ Always filled in           │
├──────────────────────┼────────────────────────────┤
│Source Address        │ Filled in when zero        │
├──────────────────────┼────────────────────────────┤
│Packet ID             │ Filled in when zero        │
├──────────────────────┼────────────────────────────┤
│Total Length          │ Always filled in           │
└──────────────────────┴────────────────────────────┘

其他设置ip头的方式

按照官方文档所说,从linux 2.2起,ip头所有的字段都可以通过ip socket设置进行修改,也就是原生socket只需用在新的协议或者用户层无法控制的协议,比如ICMP。

Starting with Linux 2.2, all IP header fields and options can be
set using IP socket options. This means raw sockets are usually
needed only for new protocols or protocols with no user interface
(like ICMP).

https://man7.org/linux/man-pages/man7/raw.7.html

sendto error: Message too long

如果使用了IP_HDRINCL,自己进行填充ip头,就会遇到这个问题,因为系统发送数据会被MTU限制(大部分是1500个字节),超过了需要分片,但是设置了IP_HDRINCL,系统就不会自动帮你分片了,所以需要手动分片。

有人说获取udp数据最大是65535,这句话本身是正确的,udp协议规则一个udp包最大是65535个字节,但是网络数据包受到MTU的限制,导致ip数据包并不会这么大,udp被ip数据包封装,所以也同样受到ip数据包大小的限制,这才有了IP分片(IP Fragement),这也是为什么有IP分片,而没有tcp或者udp分片。使用普通的socket通信,底层系统会自动分片,也会自动组装分片,所以接收和发送的最大长度可以是udp协议设定的65535个字节。

虽然官方文档说设置IP_MTU_DISCOVER可以控制对大于MTU的包分片,但是测试下来,针对UDP,并没有效果。

https://man7.org/linux/man-pages/man7/ip.7.html
https://man7.org/linux/man-pages/man7/raw.7.html

BUGS         top

       Transparent proxy extensions are not described.

       When the IP_HDRINCL option is set, datagrams will not be
       fragmented and are limited to the interface MTU.

       Setting the IP protocol for sending in sin_port got lost in Linux
       2.2.  The protocol that the socket was bound to or that was
       specified in the initial socket(2) call is always used.

TCP/IP层原生套接字接收数据

接收一个原生socket数据,并且修改部分信息,再发送给另一个ip

#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[])
{
    u_int16_t src_port, dst_port;
    u_int32_t src_addr, dst_addr;
    src_addr = inet_addr("192.168.10.111");
    dst_addr = inet_addr("192.168.10.112");
    src_port = atoi("1234");
    dst_port = atoi("1234");

    // 创建原生接收socket
    int recvsd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (recvsd < 0)
    {
        perror("recvsd");
        return -1;
    }

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(dst_port);
    sin.sin_addr.s_addr = dst_addr;

    int sd;
    // 接收数据的缓冲区
    char buffer[PCKT_LEN];
    memset(buffer, 0, PCKT_LEN);

    // 创建socket
    // AF_INET表示ipv4协议
    // SOCK_RAW表示原生socket
    // IPPROTO_UDP表示接收udp协议
    sd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (sd < 0)
    {
        perror("socket() error");
        exit(-1);
    }

    int optval = 1;
    // 设置socket,由我们管理ip头和udp头
    // sd表示要设置的socket id
    // IPPROTO_IP表示设置的是IP层
    // IP_HDRINCL表示由用户层管理tcp/ip层的头
    // &optval 设置IP_HDRINCL的数值,这里1表示打开,传递的是设置数值的指针
    // sizeof(optval) 设置参数数值的大小,因为上面传递的是指针,这里就要给定大小,不然不知道设置的内容有多长
    if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) < 0)
    {
        perror("setsockopt error");
        exit(-1);
    }

    int recvlen = 0;
    // 接收数据
    // 这里把接收保存的地址设置为NULL,所以地址长度也指定为0,是因为这里使用不到,我们在下面sendto的时候指定了一个其他地址
    recvlen = recvfrom(recvsd, buffer, PCKT_LEN, 0, NULL, 0);
    if (recvlen <= 0)
    {
        perror("recv error");
        exit(-1);
    }

    // 数据包含ip头和udp头,所以使用linux提供的ip头和udp头的结构体映射数据
    struct iphdr *ip = (struct iphdr *) buffer;
    struct udphdr *udp = (struct udphdr *) (buffer + sizeof(struct iphdr));
    // 这里只做了部分修改,如果想修改其他的,可以任意修改对应的变量
    ip->saddr = src_addr;
    ip->daddr = dst_addr;

    // 设置ip的check位,有ip协议得知,这里只需要校验头,不包括payload
    ip->check = csum((unsigned short *) buffer, sizeof(struct iphdr) + sizeof(struct udphdr));

    // 发送数据
    if (sendto(sd, buffer, recvlen, 0, (struct sockaddr *) &sin, sizeof(sin)) < 0)
    {
        perror("sendto error");
        exit(-1);
    }

    close(sd);
    close(recvsd);
    return 0;
}

上面的代码我们会发现一个不一样的地方,就是没有bind,而可以直接接收数据,因为当前设定的原生socket是ip层,指定端口是tcp/udp层,所以原生socket不支持bind端口,即使绑定了,也不会生效。

但是我们可以通过bind绑定地址,也可以通过SO_BINDTODEVICE设定网卡名称。

https://man7.org/linux/man-pages/man7/raw.7.html

以太网层接收原数据

当到了以太网层后,也就是数据的最原始的一层(再上一层是链路层),就没有tcp/udp层协议之说了,只能指定ip层的协议。

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/if_ether.h>
#include<netinet/ip.h>
#include<netinet/udp.h>
#include<netinet/tcp.h>
#include<arpa/inet.h>
#include <unistd.h>

FILE *log_txt;
int iphdrlen;

struct sockaddr saddr;
struct sockaddr_in source, dest;

void ethernet_header(unsigned char *buffer)
{
    struct ethhdr *eth = (struct ethhdr *) (buffer);
    fprintf(log_txt, "\nEthernet Header\n");
    fprintf(log_txt, "\t|-Source Address	: %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n", eth->h_source[0], eth->h_source[1],
            eth->h_source[2], eth->h_source[3], eth->h_source[4], eth->h_source[5]);
    fprintf(log_txt, "\t|-Destination Address	: %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n", eth->h_dest[0], eth->h_dest[1],
            eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
    fprintf(log_txt, "\t|-Protocol		: %d\n", eth->h_proto);
}

void ip_header(unsigned char *buffer)
{
    struct iphdr *ip = (struct iphdr *) (buffer + sizeof(struct ethhdr));
    iphdrlen = ip->ihl * 4;
    memset(&source, 0, sizeof(source));
    source.sin_addr.s_addr = ip->saddr;
    memset(&dest, 0, sizeof(dest));
    dest.sin_addr.s_addr = ip->daddr;
    fprintf(log_txt, "\nIP Header\n");
    fprintf(log_txt, "\t|-Version              : %d\n", (unsigned int) ip->version);
    fprintf(log_txt, "\t|-Internet Header Length  : %d DWORDS or %d Bytes\n", (unsigned int) ip->ihl,
            ((unsigned int) (ip->ihl)) * 4);
    fprintf(log_txt, "\t|-Type Of Service   : %d\n", (unsigned int) ip->tos);
    fprintf(log_txt, "\t|-Total Length      : %d  Bytes\n", ntohs(ip->tot_len));
    fprintf(log_txt, "\t|-Identification    : %d\n", ntohs(ip->id));
    fprintf(log_txt, "\t|-Time To Live	    : %d\n", (unsigned int) ip->ttl);
    fprintf(log_txt, "\t|-Protocol 	    : %d\n", (unsigned int) ip->protocol);
    fprintf(log_txt, "\t|-Header Checksum   : %d\n", ntohs(ip->check));
    fprintf(log_txt, "\t|-Source IP         : %s\n", inet_ntoa(source.sin_addr));
    fprintf(log_txt, "\t|-Destination IP    : %s\n", inet_ntoa(dest.sin_addr));
}

void payload(unsigned char *buffer, size_t buflen)
{
    unsigned char *data = (buffer + iphdrlen + sizeof(struct ethhdr) + sizeof(struct udphdr));
    fprintf(log_txt, "\nData\n");
    int remaining_data = buflen - (iphdrlen + sizeof(struct ethhdr) + sizeof(struct udphdr));
    for (int i = 0; i < remaining_data; i++)
    {
        if (i != 0 && i % 16 == 0)
            fprintf(log_txt, "\n");
        fprintf(log_txt, " %.2X ", data[i]);
    }
    fprintf(log_txt, "\n");
}

void tcp_header(unsigned char *buffer, size_t buflen)
{
    fprintf(log_txt, "\n*************************TCP Packet******************************");
    ethernet_header(buffer);
    ip_header(buffer);

    struct tcphdr *tcp = (struct tcphdr *) (buffer + iphdrlen + sizeof(struct ethhdr));
    fprintf(log_txt, "\nTCP Header\n");
    fprintf(log_txt, "\t|-Source Port          : %u\n", ntohs(tcp->source));
    fprintf(log_txt, "\t|-Destination Port     : %u\n", ntohs(tcp->dest));
    fprintf(log_txt, "\t|-Sequence Number      : %u\n", ntohl(tcp->seq));
    fprintf(log_txt, "\t|-Acknowledge Number   : %u\n", ntohl(tcp->ack_seq));
    fprintf(log_txt, "\t|-Header Length        : %d DWORDS or %d BYTES\n", (unsigned int) tcp->doff, (unsigned int) tcp->doff * 4);
    fprintf(log_txt, "\t|----------Flags-----------\n");
    fprintf(log_txt, "\t\t|-Urgent Flag          : %d\n", (unsigned int) tcp->urg);
    fprintf(log_txt, "\t\t|-Acknowledgement Flag : %d\n", (unsigned int) tcp->ack);
    fprintf(log_txt, "\t\t|-Push Flag            : %d\n", (unsigned int) tcp->psh);
    fprintf(log_txt, "\t\t|-Reset Flag           : %d\n", (unsigned int) tcp->rst);
    fprintf(log_txt, "\t\t|-Synchronise Flag     : %d\n", (unsigned int) tcp->syn);
    fprintf(log_txt, "\t\t|-Finish Flag          : %d\n", (unsigned int) tcp->fin);
    fprintf(log_txt, "\t|-Window size          : %d\n", ntohs(tcp->window));
    fprintf(log_txt, "\t|-Checksum             : %d\n", ntohs(tcp->check));
    fprintf(log_txt, "\t|-Urgent Pointer       : %d\n", tcp->urg_ptr);

    payload(buffer, buflen);

    fprintf(log_txt, "*****************************************************************\n\n\n");
}

void udp_header(unsigned char *buffer, size_t buflen)
{
    fprintf(log_txt, "\n*************************UDP Packet******************************");
    ethernet_header(buffer);
    ip_header(buffer);
    fprintf(log_txt, "\nUDP Header\n");

    struct udphdr *udp = (struct udphdr *) (buffer + iphdrlen + sizeof(struct ethhdr));
    fprintf(log_txt, "\t|-Source Port    	: %d\n", ntohs(udp->source));
    fprintf(log_txt, "\t|-Destination Port	: %d\n", ntohs(udp->dest));
    fprintf(log_txt, "\t|-UDP Length      	: %d\n", ntohs(udp->len));
    fprintf(log_txt, "\t|-UDP Checksum   	: %d\n", ntohs(udp->check));

    payload(buffer, buflen);

    fprintf(log_txt, "*****************************************************************\n\n\n");
}

void data_process(unsigned char *buffer, size_t buflen)
{
    struct iphdr *ip = (struct iphdr *) (buffer + sizeof(struct ethhdr));
    switch (ip->protocol)
    {
    case 6:
        tcp_header(buffer, buflen);
        break;

    case 17:
        udp_header(buffer, buflen);
        break;
    }
}

int main()
{
    int sock_r, saddr_len;
    size_t buflen = 0;
    unsigned char *buffer = (unsigned char *) malloc(65536);
    memset(buffer, 0, 65536);
    log_txt = fopen("log.txt", "w");
    if (!log_txt)
    {
        printf("unable to open log.txt\n");
        return -1;
    }
    sock_r = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
    if (sock_r < 0)
    {
        printf("error in socket\n");
        return -1;
    }
    while (1)
    {
        saddr_len = sizeof(saddr);
        buflen = recvfrom(sock_r, buffer, 65536, 0, &saddr, (socklen_t *) &saddr_len);
        if (buflen < 0)
        {
            printf("error in reading recvfrom function\n");
            return -1;
        }
        fflush(log_txt);
        data_process(buffer, buflen);
    }
    close(sock_r);
}

https://man7.org/linux/man-pages/man7/packet.7.html

https://www.opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/

以太网层发送原数据

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<sys/socket.h>
#include<sys/ioctl.h>

#include<net/if.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/if_ether.h>
#include<netinet/udp.h>

#include<linux/if_packet.h>

#include<arpa/inet.h>

struct ifreq ifreq_c, ifreq_i, ifreq_ip;
int sock_raw;
unsigned char *sendbuff;

#define DESTMAC0    0xd0
#define DESTMAC1    0x67
#define DESTMAC2    0xe5
#define DESTMAC3    0x12
#define DESTMAC4    0x6f
#define DESTMAC5    0x8f

int total_len = 0, send_len;

void get_eth_index()
{
    memset(&ifreq_i, 0, sizeof(ifreq_i));
    strncpy(ifreq_i.ifr_name, "你的网卡名称", IFNAMSIZ - 1);
    // 获取网卡信息
    if ((ioctl(sock_raw, SIOCGIFINDEX, &ifreq_i)) < 0)
    {
        printf("error in index ioctl reading");
    }
}

void get_mac()
{
    memset(&ifreq_c, 0, sizeof(ifreq_c));
    strncpy(ifreq_c.ifr_name, "wlan0", IFNAMSIZ - 1);
    // 获取网卡mac地址
    if ((ioctl(sock_raw, SIOCGIFHWADDR, &ifreq_c)) < 0)
    {
        printf("error in SIOCGIFHWADDR ioctl reading");
    }
    // 设置网卡mac地址
    struct ethhdr *eth = (struct ethhdr *) (sendbuff);
    eth->h_source[0] = (unsigned char) (ifreq_c.ifr_hwaddr.sa_data[0]);
    eth->h_source[1] = (unsigned char) (ifreq_c.ifr_hwaddr.sa_data[1]);
    eth->h_source[2] = (unsigned char) (ifreq_c.ifr_hwaddr.sa_data[2]);
    eth->h_source[3] = (unsigned char) (ifreq_c.ifr_hwaddr.sa_data[3]);
    eth->h_source[4] = (unsigned char) (ifreq_c.ifr_hwaddr.sa_data[4]);
    eth->h_source[5] = (unsigned char) (ifreq_c.ifr_hwaddr.sa_data[5]);
    // 设置目的网卡地址,这里随便设置的,如果使用需要确定设置正确,不然数据发送出去目的服务器有可能会丢掉该数据包
    eth->h_dest[0] = DESTMAC0;
    eth->h_dest[1] = DESTMAC1;
    eth->h_dest[2] = DESTMAC2;
    eth->h_dest[3] = DESTMAC3;
    eth->h_dest[4] = DESTMAC4;
    eth->h_dest[5] = DESTMAC5;

    eth->h_proto = htons(ETH_P_IP);

    total_len += sizeof(struct ethhdr);
}

void get_data()
{
    // 设置发送的数据
    sendbuff[total_len++] = 0xAA;
    sendbuff[total_len++] = 0xBB;
    sendbuff[total_len++] = 0xCC;
    sendbuff[total_len++] = 0xDD;
    sendbuff[total_len++] = 0xEE;
}

// 设置udp的头
void get_udp()
{
    struct udphdr *uh = (struct udphdr *) (sendbuff + sizeof(struct iphdr) + sizeof(struct ethhdr));

    uh->source = htons(23451);
    uh->dest = htons(23452);
    uh->check = 0;

    total_len += sizeof(struct udphdr);
    get_data();
    uh->len = htons((total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)));
}

unsigned short checksum(unsigned short *buff, int _16bitword)
{
    unsigned long sum;
    for (sum = 0; _16bitword > 0; _16bitword--)
    {
        sum += htons(*(buff)++);
    }
    do
    {
        sum = ((sum >> 16) + (sum & 0xFFFF));
    } while (sum & 0xFFFF0000);

    return (~sum);
}

void get_ip()
{
    memset(&ifreq_ip, 0, sizeof(ifreq_ip));
    strncpy(ifreq_ip.ifr_name, "你的网卡名称", IFNAMSIZ - 1);
    // 获取当前网卡ip地址
    if (ioctl(sock_raw, SIOCGIFADDR, &ifreq_ip) < 0)
    {
        printf("error in SIOCGIFADDR \n");
    }

    struct iphdr *iph = (struct iphdr *) (sendbuff + sizeof(struct ethhdr));
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 16;
    iph->id = htons(10201);
    iph->ttl = 64;
    iph->protocol = 17;
    iph->saddr = inet_addr(inet_ntoa((((struct sockaddr_in *) &(ifreq_ip.ifr_addr))->sin_addr)));
    iph->daddr = inet_addr("目的ip地址");
    total_len += sizeof(struct iphdr);
    get_udp();

    iph->tot_len = htons(total_len - sizeof(struct ethhdr));
    iph->check = htons(checksum((unsigned short *) (sendbuff + sizeof(struct ethhdr)), (sizeof(struct iphdr) / 2)));
}

int main()
{
    sock_raw = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
    if (sock_raw == -1)
    {
        printf("error in socket");
        return -1;
    }

    sendbuff = (unsigned char *) malloc(64);
    memset(sendbuff, 0, 64);
    // 构造数据包
    get_eth_index();
    get_mac();
    get_ip();
    // 构造发送地址信息,由于使用的是以太网层的socket,所以地址只能是物理地址,也就是mac地址
    struct sockaddr_ll sadr_ll;
    sadr_ll.sll_ifindex = ifreq_i.ifr_ifindex;
    sadr_ll.sll_halen = ETH_ALEN;
    sadr_ll.sll_addr[0] = DESTMAC0;
    sadr_ll.sll_addr[1] = DESTMAC1;
    sadr_ll.sll_addr[2] = DESTMAC2;
    sadr_ll.sll_addr[3] = DESTMAC3;
    sadr_ll.sll_addr[4] = DESTMAC4;
    sadr_ll.sll_addr[5] = DESTMAC5;

    while (1)
    {
        send_len = sendto(sock_raw, sendbuff, 64, 0, (const struct sockaddr *) &sadr_ll, sizeof(struct sockaddr_ll));
        if (send_len < 0)
        {
            printf("error in send");
            return -1;
        }
    }
}

https://linux.die.net/man/7/netdevice
https://www.opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/
https://man7.org/linux/man-pages/man7/packet.7.html
https://man7.org/linux/man-pages/man7/raw.7.html
https://linux.die.net/man/7/netdevice

posted @ 2023-08-30 15:45  秋来叶黄  阅读(499)  评论(0编辑  收藏  举报