简体   繁体   English

Linux 但不是 MacOS 中的套接字创建错误

[英]Socket Creation Error in Linux but not MacOS

I have the following program where I am attempting to re-create ping.我有以下程序,我正在尝试重新创建 ping。 This works great when run in my macOS dev environment, but when running in my prod Linux environment the socket fails to get created.这在我的 macOS 开发环境中运行时效果很好,但是在我的 prod Linux 环境中运行时,无法创建套接字。 I can't figure out why.我不知道为什么。 In addition, any way to resolve this so it works on Linux would be great.此外,任何解决此问题的方法都可以在 Linux 上运行。

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 

/* Calculate ICMP checksum */
uint16_t ip_checksum(void *icmp_header, int size_of_header){
    unsigned short *buffer = icmp_header;
    unsigned int sum = 0;
    uint16_t result;

    for(sum=0;size_of_header > 1; size_of_header -=2){
        sum += *buffer++;
    }

    if(size_of_header == 1){
        sum += *(unsigned char*)buffer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    result = ~sum;
    return result;
}

int main(int argc, char const *argv[]){
    char ip[20]; /* Define variable to hold user entered IP address*/
    const int icmp_size=8; /* Size of icmp header */

    struct sockaddr_in toSendTo; /* Set sockaddr_in struct */

    /* Create ICMP header */
    struct icmp_header{ 
        unsigned char icmph_type;
        unsigned char icmph_code;
        uint16_t icmph_checksum;
        unsigned short int icmph_ident;
        unsigned short int icmph_seqnum;
    } icmp_header;

    /* Get user to input IP address */
    printf("Please enter an IP address: ");
    fgets(ip, sizeof(ip), stdin);

    /* Loop to send 3 pings requests */
    for(int i = 0; i < 3; i++){
        
        icmp_header.icmph_type = 8; /* Set ICMP type to 8 for sending ICMP request */
        icmp_header.icmph_code = 0; /* Set ICMP code to 0 for sending ICMP request */
        icmp_header.icmph_checksum = 0; /* Initializee checksum to 0 */
        icmp_header.icmph_seqnum = i*20; /* Arbitrary sequence number */
        icmp_header.icmph_ident = i+50; /* Arbitrary id */
        icmp_header.icmph_checksum = ip_checksum(&icmp_header,icmp_size); /* Calculate checksum */

        /* Set transport IP in sockaddr struct  */
        if (inet_addr(ip) == -1){
            printf("Error, invalid IP address\n");
            exit(1);
        }else{
            toSendTo.sin_addr.s_addr = inet_addr(ip);
        }

        /* Create socket */
        int soc = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

        /* Check if creation of socket throws an error */
        if(soc == -1){
            printf("Error creating socket\n");
            exit(1);
        }
        
        /* Send ICMP ping request */
        int send = sendto(soc, &icmp_header, icmp_size, 0, (struct sockaddr *)&toSendTo, sizeof(toSendTo));
        
        /* Check if sending ICMP request throws an error */
        if (send < 0){
            printf("Error sending ping(%d).\n", i+1);
            exit(1);
        }else{
            printf("\nICMP Echo Request(%d) sent to %s", i+1,ip);
        }

        
        unsigned int response_address_size; /* Variable for size of response packet  */
        char response_buffer[50]; /* Buffer for content of response */
        struct sockaddr response_address; /* Struct to hold response address */

        /* Struct to hold ICMP response header information */
        typedef struct icmp_resp{
            unsigned char icmph_type;
            unsigned char icmph_code;
            uint16_t icmph_checksum;
            unsigned short int icmph_ident;
            unsigned short int icmph_seqnum;
        } icmp_r;

  
        /* Receive ICMP response */
        int resp = recvfrom(soc, response_buffer, sizeof(response_buffer), 0, &response_address, &response_address_size);

        /* Check if receiving response throws an error */
        if (resp < 0){
            printf("Error receiving response (%d)\n",i+1);
            exit(1);
        }

        icmp_r* echo_response;

        echo_response = (icmp_r *)&response_buffer[20];

        /* Determine if Destination Unreachable is response */
        if(echo_response->icmph_type == 3 && echo_response->icmph_code == 0){
            printf("Destination Unreachable...\n");
        }else{

            /* Print ICMP response information */
            printf("ICMP Response(%d) : type=%d,code=%d,checksum=%x, ident=%d, seq=%d\n\n", i+1,
                                                                            echo_response->icmph_type,
                                                                            echo_response->icmph_code,
                                                                            ntohs(echo_response->icmph_checksum),
                                                                            echo_response->icmph_ident,
                                                                            echo_response->icmph_seqnum);
        }
        
        /* Close Socket */
        close(soc);

    }
    
}

Here is my output when run in Linux:这是我在 Linux 中运行时的 output:

Please enter an IP address: 8.8.8.8
Error creating socket

You have to set ping_group_range with sysctl like in the example below for specifying the groups who can create IPPROTO_ICMP sockets.您必须使用sysctl设置ping_group_range ,如下例所示,以指定可以创建IPPROTO_ICMP sockets 的组。

sysctl -w net.ipv4.ping_group_range="gid gid"

From the documentation :文档中:

ping_group_range (two integers; default: see below; since Linux 2.6.39) Range of the group IDs (minimum and maximum group IDs, inclusive) that are allowed to create ICMP Echo sockets. ping_group_range(两个整数;默认值:见下文;自 Linux 2.6.39 起)允许创建 ICMP Echo sockets 的组 ID 范围(包括最小和最大组 ID)。 The default is "1 0", which means no group is allowed to create ICMP Echo sockets.默认为“1 0”,表示不允许任何组创建 ICMP Echo sockets。

Edit编辑

The socket type has to be SOCK_DGRAM instead of SOCK_RAW in int soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);套接字类型必须是SOCK_DGRAM而不是SOCK_RAW int soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); and you also need to define toSendTo.sin_family = AF_INET;你还需要定义toSendTo.sin_family = AF_INET; . .

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

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