Linux 中的套接字创建错误,但 MacOS 中没有

Socket Creation Error in Linux but not MacOS

我有以下程序试图重新创建 ping。这在我的 macOS 开发环境中 运行 时非常有效,但是当在我的 prod Linux 环境中 运行 时,无法创建套接字。我不知道为什么。此外,任何解决此问题的方法都可以在 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);

    }
    
}

这是我在 Linux 中 运行 时的输出:

Please enter an IP address: 8.8.8.8
Error creating socket

您必须像下面的示例一样将 ping_group_range 设置为 sysctl,以指定可以创建 IPPROTO_ICMP 个套接字的组。

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

来自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. The default is "1 0", which means no group is allowed to create ICMP Echo sockets.

编辑

套接字类型必须是 SOCK_DGRAM 而不是 int soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 中的 SOCK_RAW 并且您还需要定义 toSendTo.sin_family = AF_INET;.