Linux下C语言操作网卡的几个代码实例?特别实用

发布时间 2023-09-30 11:39:21作者: 一口Linux

前面写了一篇关于网络相关的文章:如何获取当前可用网口。

《简简单单教你如何用C语言列举当前所有网口!》

那么如何使用C语言直接操作网口?

比如读写IP地址、读写MAC地址等。

一、原理

主要通过系统用socket()、ioctl()、实现

int socket(int domain, int type, int protocol);
功能:
    创建套接字
参数:
	domain: 
		Name                Purpose                          Man page
		AF_UNIX, AF_LOCAL   Local communication              unix(7)
		AF_INET             IPv4 Internet protocols          ip(7)

	type:
		SOCK_STREAM     Provides sequenced, reliable, two-way, connection-based
	                     byte  streams.  An out-of-band data transmission mecha‐
	                     nism may be supported.
	     SOCK_DGRAM      Supports datagrams (connectionless, unreliable messages
	                     of a fixed maximum length).
	protocol:
		通常为0
返回值:
	成功:新的套接字的文件描述符
	失败:错误码,负值
int ioctl(int fd, unsigned long request, ...);
参数:
   fd :文件描述符
   request:命令
   ... :参数

其中网络用到的request定义头文件位于:

/usr/include/linux/sockios.h
/* Linux-specific socket ioctls */
#define SIOCINQ		FIONREAD
#define SIOCOUTQ	TIOCOUTQ        /* output queue size (not sent + not acked) */

/* Routing table calls. */
#define SIOCADDRT	0x890B		/* add routing table entry	*/
#define SIOCDELRT	0x890C		/* delete routing table entry	*/
#define SIOCRTMSG	0x890D		/* call to routing system	*/

/* Socket configuration controls. */
#define SIOCGIFNAME	0x8910		/* get iface name		*/
#define SIOCSIFLINK	0x8911		/* set iface channel		*/
#define SIOCGIFCONF	0x8912		/* get iface list		*/
#define SIOCGIFFLAGS	0x8913		/* get flags			*/
#define SIOCSIFFLAGS	0x8914		/* set flags			*/
#define SIOCGIFADDR	0x8915		/* get PA address		*/
#define SIOCSIFADDR	0x8916		/* set PA address		*/
#define SIOCGIFDSTADDR	0x8917		/* get remote PA address	*/
#define SIOCSIFDSTADDR	0x8918		/* set remote PA address	*/
#define SIOCGIFBRDADDR	0x8919		/* get broadcast PA address	*/
#define SIOCSIFBRDADDR	0x891a		/* set broadcast PA address	*/
#define SIOCGIFNETMASK	0x891b		/* get network PA mask		*/
#define SIOCSIFNETMASK	0x891c		/* set network PA mask		*/
#define SIOCGIFMETRIC	0x891d		/* get metric			*/
#define SIOCSIFMETRIC	0x891e		/* set metric			*/
#define SIOCGIFMEM	0x891f		/* get memory address (BSD)	*/
#define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
#define SIOCGIFMTU	0x8921		/* get MTU size			*/
#define SIOCSIFMTU	0x8922		/* set MTU size			*/
#define SIOCSIFNAME	0x8923		/* set interface name */
#define	SIOCSIFHWADDR	0x8924		/* set hardware address 	*/
#define SIOCGIFENCAP	0x8925		/* get/set encapsulations       */
#define SIOCSIFENCAP	0x8926		
#define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
#define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
#define SIOCSIFSLAVE	0x8930
#define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
#define SIOCDELMULTI	0x8932
#define SIOCGIFINDEX	0x8933		/* name -> if_index mapping	*/
#define SIOGIFINDEX	SIOCGIFINDEX	/* misprint compatibility :-)	*/
#define SIOCSIFPFLAGS	0x8934		/* set/get extended flags set	*/
#define SIOCGIFPFLAGS	0x8935
#define SIOCDIFADDR	0x8936		/* delete PA address		*/
#define	SIOCSIFHWBROADCAST	0x8937	/* set hardware broadcast addr	*/
#define SIOCGIFCOUNT	0x8938		/* get number of devices */
……

其中ioctl的参数需要借助结构体struct ifreq
定义头文件:

/usr/include/linux/if.h
#if __UAPI_DEF_IF_IFREQ
struct ifreq {
#define IFHWADDRLEN	6
	union
	{
		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
	} ifr_ifrn;
	
	union {
		struct	sockaddr ifru_addr;
		struct	sockaddr ifru_dstaddr;
		struct	sockaddr ifru_broadaddr;
		struct	sockaddr ifru_netmask;
		struct  sockaddr ifru_hwaddr;
		short	ifru_flags;
		int	ifru_ivalue;
		int	ifru_mtu;
		struct  ifmap ifru_map;
		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
		char	ifru_newname[IFNAMSIZ];
		void *	ifru_data;
		struct	if_settings ifru_settings;
	} ifr_ifru;
};
#endif /* __UAPI_DEF_IF_IFREQ */

#define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
#define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
#define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
#define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
#define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
#define	ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
#define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
#define	ifr_metric	ifr_ifru.ifru_ivalue	/* metric		*/
#define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
#define ifr_map		ifr_ifru.ifru_map	/* device map		*/
#define ifr_slave	ifr_ifru.ifru_slave	/* slave device		*/
#define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
#define ifr_ifindex	ifr_ifru.ifru_ivalue	/* interface index	*/
#define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
#define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
#define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
#define ifr_settings	ifr_ifru.ifru_settings	/* Device/proto settings*/

二、函数实现

下面将实现不同功能的函数一一列举。

0. 列出所有可用网口

int list_all_port()
{
        struct ifconf ifconf;
        struct ifreq *ifr;
        int m, n, s, fd;

        if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            return -11;
        }

        s = sizeof(struct ifreq)*5;
        for (;;) {
            ifr = malloc(s);

            ifconf.ifc_len = s;
            ifconf.ifc_req = ifr;
            
            if (ioctl(fd, SIOCGIFCONF, &ifconf) < 0) {
				perror("SIOCGIFCONF:");
                free(ifr);
                close(fd);
                return 1;
            }
                
            
            if (ifconf.ifc_len != s)
                break;
                
            free(ifr);
            s *= 2;
        }

        close(fd);
        
        m = ifconf.ifc_len/sizeof(struct ifreq);
        for (n = 0; n < m; n++)
		{
			printf("port:\t%s\n",ifconf.ifc_req[n].ifr_name);
		}
        free(ifr);
}

1. 获取指定网卡IP

int getLocalIp(const char *eth, char *ip) {
    struct ifreq ifr;
    struct sockaddr_in sin;
    int fd;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
    if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
        close(fd);
        return -1;
    }
    memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
    snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));
    close(fd);
    return 0;
}

2. 设置本网卡IP地址

int setIpAddrManual(const char *eth, char *ipstr) {
    int fd;
    struct sockaddr_in sin;
    struct ifreq ifr;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);

	sin.sin_addr.s_addr = inet_addr(ipstr);
	
	sin.sin_family = AF_INET;
    memcpy(&ifr.ifr_addr, &sin, sizeof(sin));
	
    if (ioctl(fd, SIOCSIFADDR, &ifr) < 0)
    {
    	perror("");
        close(fd);
        return -1;
    }
    close(fd);
    return 0;
} 

3. 获取本机网卡Mac地址

int getLocalMac(const char *eth, char *mac) {


    int fd;
    struct ifreq ifr;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
    {
        close(fd);
        return -1;
    }
   snprintf(mac,18, "%02x:%02x:%02x:%02x:%02x:%02x",
         (unsigned char) ifr.ifr_hwaddr.sa_data[0],
         (unsigned char) ifr.ifr_hwaddr.sa_data[1],
         (unsigned char) ifr.ifr_hwaddr.sa_data[2],
         (unsigned char) ifr.ifr_hwaddr.sa_data[3],
         (unsigned char) ifr.ifr_hwaddr.sa_data[4],
         (unsigned char) ifr.ifr_hwaddr.sa_data[5]);
    close(fd);
    return 0;

} 

4. 设置网卡mac地址

/*
support format [00:11:22:33:44:55]
*/
#define MAC_ARRAY(mac_array)  (unsigned int *)&mac_array[0],(unsigned int *)&mac_array[1],(unsigned int *)&mac_array[2],(unsigned int *)&mac_array[3],(unsigned int *)&mac_array[4],(unsigned int *)&mac_array[5] 
int setLocalMac(const char *eth, char *mac) {
    int fd;
    struct ifreq ifr;
	unsigned char mac_array[6] = {0};
	
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
	ifr.ifr_hwaddr.sa_family = AF_LOCAL;
	
	sscanf(mac,"%02x:%02x:%02x:%02x:%02x:%02x",
		MAC_ARRAY(mac_array));

	memcpy(ifr.ifr_hwaddr.sa_data,mac_array,6);

    if (ioctl(fd, SIOCSIFHWADDR, &ifr) < 0)
    {
    	perror("SIOCSIFHWADDR:");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
} 

注意:

  • 网卡地址的第一字节必须是偶数
  • sa_family 值必须为:AF_LOCAL

5. 获取网卡mtu

int getMtu(const char *eth, char *mtu) {
    int fd;
    struct ifreq ifr;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
    if (ioctl(fd, SIOCGIFMTU, &ifr) < 0)
    {
        close(fd);
        return -1;
    }	
   	snprintf(mtu,64, "%d", (unsigned char) ifr.ifr_mtu);
    close(fd);
    return 0;
} 

6. 获取广播地址

int getBroadAddr(const char *eth, char *ip) {
    int fd;
    struct sockaddr_in sin;
    struct ifreq ifr;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
    if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
    {
    	perror("");
        close(fd);
        return -1;
    }
    memcpy(&sin, &ifr.ifr_broadaddr, sizeof(sin));
    snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));

    close(fd);
    return 0;
} 

7. 获取掩码

int getNetMask(const char *eth, char *mask) {
    int fd;
    struct sockaddr_in sin;
    struct ifreq ifr;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
    if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)
    {
    	perror("");
        close(fd);
        return -1;
    }
    memcpy(&sin, &ifr.ifr_netmask, sizeof(sin));
    snprintf(mask, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));

    close(fd);
    return 0;
}

8. 获取网卡flag

int getFlags(const char *eth, char *fg) {
    int fd;
    struct sockaddr_in sin;
    struct ifreq ifr;
    bzero(&ifr, sizeof(ifr));
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    strcpy(ifr.ifr_name, eth);
    if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
    {
    	perror("");
        close(fd);
        return -1;
    }
	snprintf(fg, IP_SIZE, "%x", ifr.ifr_flags);

    close(fd);
    return 0;
} 

三、测试

1. 测试程序

int main(int argc, char **argv)
{
	int fg=0;
	char mac[32]={};
	char ip[IP_SIZE]={0};
	char buf[64];


	getBroadAddr(ethname,ip);
	printf("broad ip:\t%s\n",ip);

	getNetMask(ethname,ip);
	printf("mask:\t%s\n",ip);
	
	setIpAddrManual(ethname, "1.1.1.1");
	getLocalIp(ethname,ip);
	printf("ip:\t%s\n",ip);
	
	setLocalMac(ethname,"00:11:22:33:44:55");
	getLocalMac(ethname,mac);
	printf("mac:\t%s\n",mac);

	getMtu(ethname,buf);
	printf("mtu:\t%s\n",buf);	

	return 1;
}

2. 执行结果

执行后结果:

peng@ubuntu:~/work/test/ip$ ifconfig 
eth0      Link encap:Ethernet  HWaddr 00:11:22:33:44:55  
          inet addr:1.1.1.1  Bcast:1.255.255.255  Mask:255.0.0.0
          inet6 addr: fe80::d9d4:d42b:a04a:9d40/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:188577 errors:0 dropped:0 overruns:0 frame:0
          TX packets:208116 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:53762370 (53.7 MB)  TX bytes:172094089 (172.0 MB)

完整代码获取,请点赞转发,并后台留言:eth