Linux network programming - struct ifreq & ioctl() system call

struct ifreq

struct ifreqIs a data structure used for various interface-related input/output control (ioctl) calls. Its main use is to get and set properties of network interfaces in network programming. This structure <net/if.h>is defined in the header file.

Here are struct ifreqsome of the main fields of and their uses:

  1. ifr_name : A character array representing the name of the interface, such as "eth0", "wlan0", etc.

  2. ifr_addr : A struct sockaddrstructure of type representing the address of the interface.

  3. ifr_netmask : It is also a struct sockaddrtype structure that represents the network mask of the interface.

  4. ifr_broadaddr : Represents the broadcast address of the interface.

  5. ifr_flags : Flags representing the interface, such as IFF_UP(the interface is running), IFF_BROADCAST(the interface supports broadcast), IFF_PROMISC(the interface is in promiscuous mode), etc.

  6. ifr_hwaddr : Represents the hardware (usually MAC) address of the interface.

  7. ifr_mtu : Indicates the maximum transmission unit (MTU) of the interface.

There are a few other fields, but the ones listed above are the most commonly used.

When we want to query or set a specific property of the interface, struct ifreqthe appropriate fields of are populated and ioctl()a system call is used. For example, to set the interface to promiscuous mode, we would populate ifr_namethe fields and set IFF_PROMISCthe flag, then call ioctl().

For example:

struct ifreq ifr;
int sockfd;

// 创建一个套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    
    
    perror("socket");
    exit(EXIT_FAILURE);
}

// 设置ifr_name为我们想查询或修改的接口名
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);

// 获取接口标志
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
    
    
    perror("ioctl");
    close(sockfd);
    exit(EXIT_FAILURE);
}

// 打印是否处于混杂模式
if (ifr.ifr_flags & IFF_PROMISC) {
    
    
    printf("eth0 is in promiscuous mode\n");
} else {
    
    
    printf("eth0 is not in promiscuous mode\n");
}

close(sockfd);

Here is a simple example that demonstrates how to check if a network interface ("eth0" in this case) is in promiscuous mode.


ioctl() system call

ioctl()System calls provide a general device driver or interface operation mechanism that can be used for various device-specific operations and control. It is generally used for device-specific operations that are not suitable for expression with regular system calls (such as read(), write(), etc.).open()

Function prototype:

int ioctl(int fd, unsigned long request, ...);

parameter:

  1. fd : is the file descriptor of an already open file or socket.

  2. request : is a device-specific request code. This request code defines the specific action or command we want to perform.

  3. : This is a variable parameter, depending on the specific request. It may be a pointer to data, or it may be a direct value.

return value:

  • ioctl()The return value depends on the specific operation. Typically, a non-negative value is returned on success, -1 on failure, and set errno.

use:

  1. Network programming : ioctl()often used to query and set parameters of network interfaces. For example, get or set the IP address of the interface, query or set the flag of the interface, etc.

  2. Device control : For various devices (such as terminals, sound cards, graphics cards), ioctl()it can be used to perform specific operations or query information.

  3. Filesystem : In some cases, ioctl()can also be used for filesystem operations.

Some examples:

  1. Get the flag of the network interface :

    struct ifreq ifr;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
    ioctl(fd, SIOCGIFFLAGS, &ifr);
    
  2. Set up a non-blocking socket :

    int flags = fcntl(fd, F_GETFL, 0);
    ioctl(fd, FIONBIO, &flags);
    
  3. Query audio device properties :

    int volume;
    ioctl(fd, SOUND_MIXER_READ_VOLUME, &volume);
    

Precautions:

  1. Not all devices support all ioctl operations . To know what operations a device supports, you need to consult the documentation or header files for the specific device.

  2. Error Handling : As with other system calls, ioctl()appropriate error handling should be implemented when using . It may fail and set errno, so its return value should be checked and errors handled accordingly.

  3. Portability : ioctl()is a very general and powerful function, but it is often device or platform specific. This means that different ioctl request codes or parameters may be required on different operating systems or hardware platforms.

In summary, ioctl()a method of interacting with the device is provided, allowing developers to perform different device-specific operations. However, using it correctly requires a deep understanding of how a particular device works.


Example

Let's use a simple example to show how to use ioctl()to get and set eth0the IP address of a network interface (for example).

1. Obtain IP address

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    
    
    int sockfd;
    struct ifreq ifr;

    // 创建一个 socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
    
    
        perror("socket");
        exit(1);
    }

    // 指定要查询的接口名
    strncpy(ifr.ifr_name, "enp5s0", IFNAMSIZ);

    // 获取接口的 IP 地址
    if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {
    
    
        perror("ioctl - SIOCGIFADDR ");
        exit(1);
    }

    struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;
    printf("IP address of enp5s0: %s\n", inet_ntoa(ipaddr->sin_addr));

    close(sockfd);
    return 0;
}

The result of running the program is:

majn@tiger:~/C_Project/network_project$ ./ioctl_setip 
IP address of enp5s0: 192.168.31.6

2. Set IP address (use with caution!!!)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    
    
    int sockfd;
    struct ifreq ifr;
    struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;

    // 创建一个 socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
    
    
        perror("socket");
        exit(1);
    }

    // 指定要设置的接口名
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);

    // 设置新的 IP 地址
    sin->sin_family = AF_INET;
    inet_aton("192.168.1.100", &sin->sin_addr);

    if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
    
    
        perror("ioctl - SIOCSIFADDR ");
        exit(1);
    }

    printf("IP address of eth0 set to: %s\n", inet_ntoa(sin->sin_addr));

    close(sockfd);
    return 0;
}

Note : Setting the IP address usually requires root privileges, so you may need to use this when running the above code sudo.

In the above example, ioctl()it is used to query or set the IP address of the network interface. Specific operations are specified by SIOCGIFADDR(get address) and SIOCSIFADDR(set address).


inet_aton()

inet_aton()The function is used to convert a string in dotted decimal format (such as "192.168.1.1") into an IPv4 address in network byte order. It is <arpa/inet.h>defined in the header file.

Function prototype:

int inet_aton(const char *cp, struct in_addr *inp);

parameter:

  • cp : Pointer to an IPv4 address string in dotted decimal format.
  • inp : struct in_addrPointer to a structure used to store the converted address.

return value:

  • Returns 1 on success and 0 on failure.

Compared inet_addr()to , inet_aton()a function provides a clearer error checking mechanism because it explicitly returns a success or failure status rather than using a special address value to indicate an error.

Example:

#include <stdio.h>
#include <arpa/inet.h>

int main() {
    
    
    const char *ip_string = "192.168.1.1";
    struct in_addr ip_addr;

    if (inet_aton(ip_string, &ip_addr)) {
    
    
        printf("Conversion successful. Network byte order value: 0x%x\n", ip_addr.s_addr);
    } else {
    
    
        printf("Conversion failed.\n");
    }

    return 0;
}

The result of running the above program is:

majn@tiger:~/C_Project/network_project$ ./inet_aton_demo 
Conversion successful. Network byte order value: 0x101a8c0

In this example, we convert the dotted decimal IP address string "192.168.1.1" inet_aton()to struct in_addrthe format using , and output the conversion result.

Note: inet_aton()and inet_ntoa()are a pair, one is used to convert a string to binary format and the other is used to do the opposite. If you are looking for a thread-safe method or need to handle IPv6 addresses, it is recommended to use the inet_pton()and inet_ntop()functions instead.


inet_war()

inet_ntoa()Is a network utility function used to convert an IPv4 address in network byte order to dotted decimal string format. It is <arpa/inet.h>defined in the header file.

Function prototype:

char *inet_ntoa(struct in_addr in);

parameter:

  • in : is a struct in_addrstructure containing an IPv4 address (in network byte order).

return value:

  • Returns a pointer to a statically allocated string representing the dotted decimal representation of the IPv4 address. Because a static string is returned, subsequent inet_ntoa()calls may overwrite previous content.

Precautions:

  1. inet_ntoa()Returns a pointer to static storage, which means each call overwrites the previous result. If you need to preserve the converted address, you should copy the returned string yourself.
  2. Considering thread safety, this inet_ntoa()may cause problems in multi-threaded environment. To avoid this, consider using more modern, thread-safe functions, inet_ntop()e.g.

Example:

#include <stdio.h>
#include <arpa/inet.h>

int main() {
    
    
    struct in_addr ip_addr;
    ip_addr.s_addr = htonl(0xC0A80101);  // 0xC0A80101 = 192.168.1.1 in hexadecimal

    char *ip_str = inet_ntoa(ip_addr);
    printf("IP address in dotted-decimal format: %s\n", ip_str);

    return 0;
}

In this example, we create a struct in_addrstruct, set the IP address (in network byte order), and then inet_ntoa()convert it to a string using .

The result of running the above program is:

majn@tiger:~/C_Project/network_project$ ./inet_ntoa_demo 
IP address in dotted-decimal format: 192.168.1.1

Guess you like

Origin blog.csdn.net/weixin_43844521/article/details/133348367