Linux下通过C语言管理网络--基于ioctl

186 篇文章 24 订阅
144 篇文章 13 订阅

Linux下通过ioctl接口获取和设置IP地址,获取和设置mac地址,获取和设置网卡当前状态,获取和配置网关

/*
 * Copyright (C) 2021, 2021  huohongpeng
 * Author: huohongpeng <1045338804@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Change logs:
 * Date        Author       Notes
 * 2021-10-20  huohongpeng   首次添加
 * 
 * https://man7.org/linux/man-pages/man7/netdevice.7.html
 */
#include <sys/socket.h>
#include <linux/route.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
 
/*
 * 启动/停用 网卡状态 (类似 ifconfig ethx up或ifconfig ethx down)
 * @eth_name: 网卡名, 如:eth0, wlan0..
 * @state: "up": 启动网卡, "down": 停用网卡
 * @ret: 0: 成功, -1: 失败
 */
int net_eth_state(char *eth_name, char *state)
{
	int ret = 0;
	int fd;
	struct ifreq ifreq;
 
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
	memset(&ifreq, 0x00, sizeof(struct ifreq));
	strcpy(ifreq.ifr_name, eth_name);
 
	ret = ioctl(fd, SIOCGIFFLAGS, &ifreq);
	if (ret < 0) {
		ret = -1;
		perror("SIOCGIFFLAGS: ");
		goto ERROR;
	}
 
	if (strcmp("up", state) == 0) {
		ifreq.ifr_flags |= IFF_UP;
	} else {
		ifreq.ifr_flags &= ~IFF_UP;
	}
 
	ret = ioctl(fd, SIOCSIFFLAGS, &ifreq);
	if (ret < 0) {
		ret = -1;
		perror("SIOCSIFFLAGS: ");
		goto ERROR;
	}
ERROR:
	close(fd);
 
	return ret;
}
 
/*
 * 查看网卡是否已经启动
 * @ret: 0: down, 1: up, -1: 错误
 */
int net_eth_state_is_up(char *eth_name)
{
	int ret;
	int fd;
	struct ifreq ifreq;
 
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
	memset(&ifreq, 0x00, sizeof(struct ifreq));
	strcpy(ifreq.ifr_name, eth_name);
 
	ret = ioctl(fd, SIOCGIFFLAGS, &ifreq);
	if (ret < 0) {
		ret = -1;
		perror("SIOCGIFFLAGS: ");
		goto ERROR;
	}
 
	if (ifreq.ifr_flags & IFF_UP) {
		ret = 1;
	} else {
		ret = 0;
	}
 
ERROR:
	close(fd);
 
	return ret;
}
 
/*
 * 获取网卡的MAC地址
 * @eth_name: 网卡名, 如:eth0, wlan0..
 * @mac: 返回6字节mac地址, 如unsigned char mac[6] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB};
 * @ret: 0: 成功, -1: 失败
 */
int net_eth_get_mac(char *eth_name, unsigned char *mac)
{
	int ret;
	int fd;
	struct ifreq ifreq;
 
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
	memset(&ifreq, 0x00, sizeof(struct ifreq));
 
	strcpy(ifreq.ifr_name, eth_name);
	ret = ioctl(fd, SIOCGIFHWADDR, &ifreq);
 
	if (ret < 0) {
		perror("SIOCGIFHWADDR: ");
	}
 
	if (ret == 0 && ifreq.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
		memcpy(mac, ifreq.ifr_hwaddr.sa_data, 6);
	} else {
		ret = -1;
	}
	
	close(fd);
 
	return ret;
}
 
/*
 * 修改网卡的MAC地址
 * @eth_name: 网卡名, 如:eth0, wlan0..
 * @mac: 6字节mac地址, 如unsigned char mac[6] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB};
 *       类似于 ifconfig ethx hw ether 12:34:56:78:90:AB
 * @ret: 0: 成功, -1: 失败
 */
int net_eth_set_mac(char *eth_name, unsigned char *mac)
{
	int ret;
	int fd;
	short flag;
	struct ifreq ifreq;
 
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
	memset(&ifreq, 0x00, sizeof(struct ifreq));
	strcpy(ifreq.ifr_name, eth_name);
 
	ret = ioctl(fd, SIOCGIFFLAGS, &ifreq);
 
	if (ret < 0) {
		ret = -1;
		perror("SIOCGIFFLAGS: ");
		goto ERROR;
	}
 
	flag = ifreq.ifr_flags;
 
	/*
	 * 如果网卡是启动的,设置MAC地址需要先停止网卡
	 */
	if (flag & IFF_UP) {
		ifreq.ifr_flags &= ~IFF_UP;
		ioctl(fd, SIOCSIFFLAGS, &ifreq);
	}
 
	/*
	 * 配置MAC地址
	 */
	ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER;
	memcpy(ifreq.ifr_hwaddr.sa_data, mac, 6);
	ret = ioctl(fd, SIOCSIFHWADDR, &ifreq);
 
	if (ret < 0) {
		ret = -1;
		perror("SIOCSIFHWADDR: ");
		goto ERROR;
	}
 
	/*
	 * 如果恢复网卡的工作状态
	 */
	if (flag & IFF_UP) {
		ioctl(fd, SIOCGIFFLAGS, &ifreq);
		ifreq.ifr_flags |= IFF_UP;
		ioctl(fd, SIOCSIFFLAGS, &ifreq);
	}
 
ERROR:	
	close(fd);
 
	return ret;
}
 
 
/*
 * 获取网卡ip地址
 * @eth_name: 网卡名, 如:eth0, wlan0..
 * @ip: 返回4字节ip地址, 如char ip[4] = {10, 10, 10, 3};即: 10.10.10.3
 * @netmask: 返回4字节netmask地址, 如char netmask[4] = {0xff, 0xff, 0xff, 0};即: 255.255.255.0
 * @ret: 0: 成功, -1: 不能获取ip地址
 */
int net_eth_get_ipv4_addr(char *eth_name, unsigned char *ip, unsigned char *netmask)
{
	int ret;
	int fd;
	struct ifreq ifreq;
 
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
	memset(&ifreq, 0x00, sizeof(struct ifreq));
	strcpy(ifreq.ifr_name, eth_name);
 
	/*
	 * 获取IP地址
	 */
	ret = ioctl(fd, SIOCGIFADDR, &ifreq);
 
	if (ret < 0) {
		ret = -1;
		perror("SIOCGIFADDR: ");
		goto ERROR;
	}
 
	if (ifreq.ifr_addr.sa_family != AF_INET) {
		ret = -1;
		goto ERROR;
	}
 
	struct sockaddr_in *addr = (struct sockaddr_in *)&(ifreq.ifr_addr);
	memcpy(ip, &(addr->sin_addr.s_addr), 4);
 
 
	/*
	 * 获取netmask
	 */
	ret = ioctl(fd, SIOCGIFNETMASK, &ifreq);
 
	if (ret < 0) {
		ret = -1;
		perror("SIOCGIFNETMASK: ");
		goto ERROR;
	}
 
	if (ifreq.ifr_netmask.sa_family != AF_INET) {
		ret = -1;
		goto ERROR;
	}
 
	addr = (struct sockaddr_in *)&(ifreq.ifr_netmask);
	memcpy(netmask, &(addr->sin_addr.s_addr), 4);
 
ERROR:
	close(fd);
 
	return ret;
}
 
/*
 * 设置网卡ip地址
 * @eth_name: 网卡名, 如:eth0, wlan0..
 * @ip: 返回4字节ip地址, 如char ip[4] = {10, 10, 10, 3};即: 10.10.10.3
 * @netmask: 返回4字节netmask地址, 如char netmask[4] = {0xff, 0xff, 0xff, 0};即: 255.255.255.0
 * @ret: 0: 成功, -1: 不能获取ip地址
 */
int net_eth_set_ipv4_addr(char *eth_name, unsigned char *ip, unsigned char *netmask)
{
	int ret = 0;
	int fd;
	struct ifreq ifreq;
 
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
	memset(&ifreq, 0x00, sizeof(struct ifreq));
	strcpy(ifreq.ifr_name, eth_name);
 
	/*
	 * 设置IP地址
	 */
	ifreq.ifr_addr.sa_family = AF_INET;
	struct sockaddr_in *addr = (struct sockaddr_in *)&(ifreq.ifr_addr);
	memcpy(&(addr->sin_addr.s_addr), ip, 4);
	ret = ioctl(fd, SIOCSIFADDR, &ifreq);
	if (ret < 0) {
		ret = -1;
		perror("SIOCSIFADDR: ");
		goto ERROR;
	}
 
	/*
	 * 获取netmask
	 */
	ifreq.ifr_netmask.sa_family = AF_INET;
	addr = (struct sockaddr_in *)&(ifreq.ifr_netmask);
	memcpy(&(addr->sin_addr.s_addr), netmask, 4);
	ret = ioctl(fd, SIOCSIFNETMASK, &ifreq);
	if (ret < 0) {
		ret = -1;
		perror("SIOCSIFNETMASK: ");
		goto ERROR;
	}
ERROR:
	close(fd);
 
	return ret;
}
 
#include <stdlib.h>
/*
 * 添加默认网关
 * @gw: 4字节地址, 如char gw[4] = {10, 10, 10, 1};即: 10.10.10.1
 * @ret: 0: 成功, -1: 失败
 * 
 * 注意: 如果想删除网卡相关的所有路由表可以直接把网卡down,即ifconfig ethx down
 */
int net_route_add_default_gateway(unsigned char *gw)
{
	char buf[128];
	sprintf(buf, "route add default gw %d.%d.%d.%d", gw[0], gw[1], gw[2], gw[3]);
	return system(buf);
}
 
/*
 * 获取默认网关
 * @eth_name: 网卡名, 如:eth0, wlan0..
 * @gw: 获取4字节地址, 如char gw[4] = {10, 10, 10, 1};即: 10.10.10.1
 * @ret: 0: 成功, -1: 失败
 * 
 * 注意: 如果想删除网卡相关的所有路由表可以直接把网卡down,即ifconfig ethx down
 */
int net_route_get_default_gateway(char *eth_name, unsigned char *gw)
{
	int ret = -1;
	FILE *fp;
	char devname[64];
	unsigned long d, g, m;
	int r;
	int flgs, ref, use, metric, mtu, win, ir;
 
	fp = fopen("/proc/net/route", "r");
 
	/* Skip the first line. */
	r = fscanf(fp, "%*[^\n]\n");
 
	if (r < 0) {
		/* Empty line, read error, or EOF. Yes, if routing table
		 * is completely empty, /proc/net/route has no header.
		 */
		ret = -1;
		goto ERROR;
	}
 
	while (1) {
		r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
				devname, &d, &g, &flgs, &ref, &use, &metric, &m,
				&mtu, &win, &ir);
 
		if (r != 11) {
			if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
				break;
			}
		}
 
		/*
		 * # cat /proc/net/route 
		 * Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT                                                       
		 * eth0    00000000        013CA8C0        0003    0       0       0       00000000        0       0       0                                                                               
		 * eth0    003CA8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0
		 * 默认网关的特性: dst为0,netmask为0, Gateway不为0
		 */
		if (strcmp(devname, eth_name) == 0 &&\
			(flgs & RTF_GATEWAY) && \
			(flgs & RTF_UP) && \
			g != 0 && d == 0 && m == 0) {
			memcpy(gw, &g, 4);
			ret = 0;
			break;
		}
	}
 
ERROR:
	fclose(fp);
	return ret;
}
 
 
void net_eth_test(void)
{
	unsigned char gw[4] = {1, 1, 1, 1};
	//net_route_add_default_gateway(gw);
 
	net_route_get_default_gateway("eth0", gw);
	printf("gw: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]);
 
	net_route_get_default_gateway("wlan0", gw);
	printf("gw1: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]);
 
	return;
 
	int ret;
	unsigned char ip[4] = {10, 10, 10, 3};
	unsigned char mask[4] = {255, 0, 0, 0};
 
	ret = net_eth_set_ipv4_addr("wlan0", ip, mask);
	printf("ret1: %d\n", ret);
 
	ret = net_eth_get_ipv4_addr("wlan0", ip, mask);
	printf("ret: %d\n", ret);
	printf("ip: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
	printf("netmask: %d.%d.%d.%d\n", mask[0], mask[1], mask[2], mask[3]);
 
 
	return;
 
	unsigned char mac[6] = {0xEE, 0x08, 0xE4, 0x41, 0xF1, 0xF4};
	net_eth_set_mac("eth0", mac);
	net_eth_get_mac("eth0", mac);
 
	printf("mac: %X:%X:%X:%X:%X:%X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
 

(836条消息) Linux下通过C语言管理网络--基于ioctl_霍宏鹏的博客-CSDN博客

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中进行RS485-RTU通讯编程,需要使用串口通信库和RS485驱动程序。以下是一个简单的例子,展示了如何使用C语言编写一个基于RS485-RTU通讯的程序。 步骤1:打开串口 首先,需要使用串口通信库打开串口,并配置相应的属性,如波特率、数据位、停止位等。 ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> int open_serial_port(const char* portname, speed_t baudrate) { int fd = open(portname, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { perror("open_serial_port: Unable to open port"); return -1; } // Configure the port struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, baudrate); cfsetospeed(&options, baudrate); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_iflag &= ~(IXON | IXOFF | IXANY); options.c_oflag &= ~OPOST; options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 100; // 10 second timeout tcsetattr(fd, TCSANOW, &options); return fd; } ``` 步骤2:配置RS485驱动程序 接下来,需要配置RS485驱动程序,以便能够进行RS485-RTU通讯。在Linux中,可以使用ioctl()函数来设置驱动程序的属性。以下是一个设置RS485驱动程序为RTS/CTS流控制的例子: ```c #include <sys/ioctl.h> int set_rs485_mode(int fd, int mode) { int status; if (ioctl(fd, TIOCMGET, &status) == -1) { perror("set_rs485_mode: Unable to get status"); return -1; } if (mode == 1) { status |= TIOCM_RTS; } else { status &= ~TIOCM_RTS; } if (ioctl(fd, TIOCMSET, &status) == -1) { perror("set_rs485_mode: Unable to set mode"); return -1; } return 0; } ``` 步骤3:发送和接收数据 最后,需要编写发送和接收数据的代码。在RS485-RTU通讯中,需要使用Modbus协议来进行通讯。以下是一个发送和接收Modbus数据的例子: ```c #include <string.h> #include <errno.h> int send_modbus_data(int fd, const char* data, int len) { if (set_rs485_mode(fd, 1) == -1) { return -1; } if (write(fd, data, len) != len) { perror("send_modbus_data: Unable to write data"); return -1; } if (set_rs485_mode(fd, 0) == -1) { return -1; } return 0; } int receive_modbus_data(int fd, char* buffer, int len) { if (set_rs485_mode(fd, 0) == -1) { return -1; } int nbytes = read(fd, buffer, len); if (nbytes == -1) { perror("receive_modbus_data: Unable to read data"); return -1; } return nbytes; } ``` 这些是基本的RS485-RTU通讯编程步骤。当然,实际的应用中,还需要根据具体的需求进行相应的修改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值