简体   繁体   English

创建SOCK_RAW套接字只是为了在没有任何recvform的情况下发送数据()

[英]create SOCK_RAW socket just for sending data without any recvform()

If I create a socket whose type is SOCK_RAW only to send some data without receiving any data, is there any problem when kernel continue to receive network packets and copy its datagram to somebuffer (of application?). 如果我创建一个类型为SOCK_RAW的套接字只发送一些数据而不接收任何数据,那么当内核继续接收网络数据包并将其数据报复制到somebuffer(应用程序?)时是否有任何问题。 In other words, after the somebuffer is filled what will happened? 换句话说,在填充一些缓冲区后会发生什么? error or ignore? 错误或忽略?

I don't know how to prevent kernel from delivering the copy of datagram to my application. 我不知道如何防止内核将数据报副本传递给我的应用程序。

Reference http://sock-raw.org/papers/sock_raw 0x4 raw_input 参考http://sock-raw.org/papers/sock_raw 0x4 raw_input

After the IP layer processes a new incoming IP datagram, it calls ip_local_deliver_finish() kernel function which is responsibe for calling a registered transport protocol handler by inspecting the protocol field of the IP header (remember from above). 在IP层处理新的传入IP数据报之后,它调用ip_local_deliver_finish()内核函数,该函数负责通过检查IP头的协议字段来调用已注册的传输协议处理程序(请记住上面)。 However before it delivers the datagram to the handler, it checks every time if an application has created a raw socket with the same protocol number. 但是,在将数据报传递给处理程序之前,它每次都会检查应用程序是否创建了具有相同协议编号的原始套接字。 If there is one or more such applications, it makes a copy of the datagram and delivers it to them as well. 如果有一个或多个此类应用程序,它会复制数据报并将其传递给它们。

You can use shutdown(2) in order to shutdown reception part of the socket. 您可以使用shutdown(2)来关闭套接字的接收部分。 See shutdown man page 请参阅关闭手册页

EDIT : I found that shutdown only works on connected (ie TCP) sockets. 编辑:我发现shutdown只适用于连接(即TCP)套接字。 With Raw socket, there are 2 possibilities : 使用Raw套接字,有两种可能性:

  • Receive data into a temporary buffer (with recv ) and discard them (perhaps in an other thread) 将数据接收到临时缓冲区(使用recv )并丢弃它们(可能在其他线程中)
  • If I remember well, when the socket buffer is full, incoming data are automatically discarded (and data in the buffer aren't modified), so you can set the socket reception buffer size to 0 (and increase it later if needed). 如果我记得很清楚,当套接字缓冲区已满时,会自动丢弃传入的数据(并且不会修改缓冲区中的数据),因此您可以将套接字接收缓冲区大小设置为0(如果需要,可以稍后再增加)。

Here's how to set reception buffer size to 0 : 以下是如何将接收缓冲区大小设置为0:

int opt = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));

TEST 测试

/**
 * @file raw_print_pkt.c
 * @brief 
 * @author Airead Fan <fgh1987168@gmail.com>
 * @date 2012/08/22 12:35:22
 */

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

int main(int argc, char *argv[])
{
    int s;
    ssize_t rn;                 /* receive number */
    struct sockaddr_in saddr;
    char packet[4096];
    int count;

    if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
        perror("error:");
        exit(EXIT_FAILURE);
    }

    memset(packet, 0, sizeof(packet));
    socklen_t *len = (socklen_t *)sizeof(saddr);
    int fromlen = sizeof(saddr);
    int opt = 0;

    count = 0;
    while(1) {
        if ((rn = recvfrom(s, (char *)&packet, sizeof(packet), 0,
                           (struct sockaddr *)&saddr, &fromlen)) < 0)
            perror("packet receive error:");
        if (rn == 0) {
            printf("the peer has performed an orderly shutdown\n");
            break;
        }

        printf("[%d] rn = %lu \n", count++, rn);

        if (count == 16) {
            if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
                perror("setsocketopt failed");
            } else {
                fprintf(stdout, "setsocketopt successful\n");
            }
            // int shutdown(int sockfd, int how);
            /* if (shutdown(s, SHUT_RD) < 0) {
             *     perror("shutdown failed");
             * } */
        }
    }

    return 0;
}

TEST 2 (same includes): 测试2(相同包括):

int main(int argc, char *argv[])
{
int s;
ssize_t rn;                 /* receive number */
char packet[4096];
int count;

if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
    perror("error:");
    exit(EXIT_FAILURE);
}

memset(packet, 0, sizeof(packet));
int opt = 0;
count = 0;

//Set recv buffer size
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
    perror("setsocketopt failed");
} else {
    fprintf(stdout, "setsocketopt successful\n");
}

//10 seconds countdown
int i = 10;
while(i > 0)
{
    printf("\r%d              ", i);
    fflush(stdout);
    i--;
    sleep(1);
}
printf("\n");
while(1) {
    if ((rn = recv(s, (char *)&packet, sizeof(packet), 0)) <= 0)
        perror("packet receive error:");

    printf("[%d] rn = %lu \n", count++, rn);
    }
return 0;
}

Here's how to proceed with test 2 : 以下是如何进行测试2:

First of all, set the buffer size to 4096 (or bigger if you have a lot of traffic on your network). 首先,将缓冲区大小设置为4096(如果网络上有大量流量,则将其设置为更大)。 Compile and launch. 编译并启动。 During the 10 seconds before starting receiving data, send a lot of data to the socket. 在开始接收数据之前的10秒内,将大量数据发送到套接字。 After the 10 seconds, the program will receive everything you sent during the countdown. 10秒后,程序将收到您在倒计时期间发送的所有内容。

After that, set the buffer size to 0. Proceed as previously. 之后,将缓冲区大小设置为0.继续执行之前的操作。 After the 10 seconds, the program won't receive the data you sent during the countdown. 10秒后,程序将不会收到您在倒计时期间发送的数据。 But if you send data while it's in recvfrom , it will read them normally. 但是如果你在recvfrom中发送数据,它将正常读取它们。

I don't really understand what you want! 我真的不明白你想要什么! if you want just to inject some packets, it's simple: 如果你只想注入一些数据包,那很简单:

#include<netinet/tcp.h> /* TCP header */
#include<netinet/ip.h>  /* IP header */

/* Checksum compute function */
/* source : http://www.winpcap.org/pipermail/winpcap-users/2007-July/001984.html */
unsigned short checksum(unsigned short *buffer, int size)
{
    unsigned long cksum=0;
    while(size >1)
    {
        cksum+=*buffer++;
        size -=sizeof(unsigned short);
    }
    if(size)
        cksum += *(UCHAR*)buffer;

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);
    return (unsigned short)(~cksum);
}

int main (int argc, char **argv)
{
    char packet_buffer[BUFFER_SIZE];
    struct sockaddr_in sin;     
    struct iphdr *ip_header;    /* IP header */
    struct tcphdr *tcp_header;  /* TCP header */
    int flag = 1;

    /* Creating RAW socket */
    int raw_socket = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);

    ip_header = (struct iphdr *) packet_buffer;

    tcp_header = (struct tcphdr *) (packet_buffer + sizeof (struct ip));

    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT_NUMBER);
    sin.sin_addr.s_addr = inet_addr (IP_ADDRESS);

    /* Zeroing the bbuffer */ 
    memset (packet_buffer, 0, BUFFER_SIZE);

    /* Construct your IP Header */
    ip_header->ihl = 5;
    ip_header->version = 4;
    ip_header->tos = 0;
    ip_header->tot_len = sizeof (struct ip) + sizeof (struct tcphdr);
    ip_header->id = htonl(CHOOSE_PACKET_ID);
    ip_header->frag_off = 0;
    ip_header->ttl = 255;
    ip_header->protocol = 6;    /* TCP. Change to 17 if you want UDP */
    ip_header->check = 0;
    ip_header->saddr = inet_addr (SOURCE_IP_ADDRESS_TO_SPOOF);
    ip_header->daddr = sin.sin_addr.s_addr;

    /* Construct your TCP Header */
    tcp_header->source = htons (SOURCE);
    tcp_header->dest = htons(DEST);
    tcp_header->seq = random();
    tcp_header->ack_seq = 0;
    tcp_header->doff = 0;
    tcp_header->syn = 1; 
    tcp_header->window = htonl(65535);
    tcp_header->check = 0;
    tcp_header->urg_ptr = 0;

    /* IP Checksum */
    ip_header->check = checksum((unsigned short *) packet_buffer, ip_header->tot_len >> 1);

    if (setsockopt(raw_socket, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(flag)) < 0)
    {
        /* ERROR handling */
    }

    while (1)
    {
        /* Send the packet */
    if (sendto(raw_socket, packet_buffer, ip_header->tot_len, 0,  (struct sockaddr *) &sin, sizeof (sin)) < 0)
    {
        /* ERROR handling */
    }
    /* The rest of your need */
 }

 return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM