#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/netlink.h>
#define MAX_PAYLOAD 1024 // 最大负载长度
#define NLMSG_ERROR 0x2 // 消息类型
- 初始化Netlink Socket
初始化Netlink Socket,创建一个netlink_init
函数,用于创建Netlink Socket并绑定到NETLINK_ROUTE
。
int netlink_init(struct sockaddr_nl *nladdr) {
int sockfd;
sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sockfd < 0) {
perror("socket");
return -1;
}
memset(nladdr, 0, sizeof(struct sockaddr_nl));
nladdr->nl_family = AF_NETLINK;
nladdr->nl_groups = RTMGRP_LINK; // 绑定到link组
if (bind(sockfd, (struct sockaddr *)nladdr, sizeof(struct sockaddr_nl)) < 0) {
perror("bind");
close(sockfd);
return -1;
}
return sockfd;
}
- 创建Netlink消息
接下来创建Netlink消息,包括Netlink消息头和Netlink消息负载
void build_nlmsg(struct nlmsghdr *nlh, int ifindex, int flags) {
memset(nlh, 0, NLMSG_LENGTH(sizeof(struct ifinfomsg)));
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
nlh->nlmsg_type = RTM_NEWLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = 1;
nlh->nlmsg_pid = getpid();
struct ifinfomsg *ifm = (struct ifinfomsg *)NLMSG_DATA(nlh);
ifm->ifi_family = AF_UNSPEC;
ifm->ifi_type = 0;
ifm->ifi_index = ifindex;
ifm->ifi_flags = flags;
ifm->ifi_change = 0xffffffff;
}
int send_nlmsg(int sockfd, struct nlmsghdr *nlh, struct sockaddr_nl *nladdr) {
int ret;
struct iovec iov = { nlh, nlh->nlmsg_len };
struct msghdr msg = { nladdr, sizeof(struct sockaddr_nl), &iov, 1, NULL, 0, 0 };
ret = sendmsg(sockfd, &msg, 0);
if (ret < 0) {
perror("sendmsg");
return -1;
}
memset(nlh, 0, NLMSG_LENGTH(sizeof(struct ifinfomsg
struct ifinfomsg
是Linux内核中用于表示网络接口信息的结构体,其定义如下
struct ifinfomsg {
unsigned char ifi_family; // 接口地址族,通常为AF_UNSPEC或AF_INET6
unsigned short ifi_type; // 接口类型
int ifi_index; // 接口索引
unsigned int ifi_flags; // 接口标志,如IFF_UP等
unsigned int ifi_change; // 接口状态改变标志
};
在使用Netlink更改网络接口的过程中,可以使用struct ifinfomsg
结构体来表示要修改的网络接口的相关信息,包括接口类型、接口索引、接口标志等。在Netlink消息负载中,使用NLMSG_DATA
宏来获取struct ifinfomsg
结构体的地址,然后填充相关字段即可。
使用struct ifinfomsg
结构体的场景包括但不限于:
- 在Netlink Socket中使用
RTM_NEWLINK
消息来创建一个新的网络接口。 - 在Netlink Socket中使用
RTM_DELLINK
消息来删除一个网络接口。 - 在Netlink Socket中使用
RTM_SETLINK
消息来修改一个网络接口的属性,比如接口标志等。
在Netlink Socket中使用RTM_NEWLINK
消息来创建一个新的网络接口时,如果填充的接口名称为eth%d
,内核会根据当前系统中已经存在的网络接口的数量来确定新接口的名称。
具体来说,内核会在已有的网络接口名称中查找最小的未使用的名称,并将其分配给新的网络接口。对于以eth%d
为模板的接口名称,内核会从eth0
开始往后逐个尝试,直到找到一个未被占用的名称为止。
因此,在使用Netlink Socket来创建一个新的网络接口时,如果填充的接口名称为eth%d
,而系统中已经存在了一些网络接口,那么最终分配给新接口的名称可能会不是eth%d
,而是根据内核的算法计算出的一个实际名称。
修改网卡mtu值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#define BUFSIZE 8192
int main(int argc, char **argv) {
struct nlmsghdr *nh;
struct ifinfomsg *ifinfo;
struct rtattr *attr;
char buffer[BUFSIZE];
int sockfd, len;
// 创建netlink socket
if ((sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
perror("socket");
return -1;
}
// 准备请求消息
memset(buffer, 0, sizeof(buffer));
nh = (struct nlmsghdr *)buffer;
ifinfo = (struct ifinfomsg *)NLMSG_DATA(nh);
attr = (struct rtattr *)(((char *)ifinfo) + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nh->nlmsg_type = RTM_NEWLINK;
ifinfo->ifi_family = AF_UNSPEC;
ifinfo->ifi_type = ARPHRD_ETHER;
ifinfo->ifi_index = if_nametoindex("eth0");
ifinfo->ifi_flags = IFF_UP;
ifinfo->ifi_change = 0xFFFFFFFF;
// 添加MTU属性
attr->rta_type = IFLA_MTU;
attr->rta_len = RTA_LENGTH(sizeof(unsigned int));
*(unsigned int *)RTA_DATA(attr) = 1500;
nh->nlmsg_len += RTA_ALIGN(attr->rta_len);
nh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK;
// 发送netlink消息
len = send(sockfd, nh, nh->nlmsg_len, 0);
if (len == -1) {
perror("send");
close(sockfd);
return -1;
}
// 接收netlink回复
len = recv(sockfd, buffer, BUFSIZE, 0);
if (len == -1) {
perror("recv");
close(sockfd);
return -1;
}
// 处理netlink回复
for (nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_type == NLMSG_ERROR) {
perror("NLMSG_ERROR");
close(sockfd);
return -1;
}
}
printf("MTU has been set to 1500.\n");
close(sockfd);
return 0;
}
实现将网卡eth0改名称为eth1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#define BUFSIZE 8192
int main(int argc, char **argv) {
struct nlmsghdr *nh;
struct ifinfomsg *ifinfo;
struct rtattr *attr;
char buffer[BUFSIZE];
int sockfd, len;
// 创建netlink socket
if ((sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
perror("socket");
return -1;
}
// 准备请求消息
memset(buffer, 0, sizeof(buffer));
nh = (struct nlmsghdr *)buffer;
ifinfo = (struct ifinfomsg *)NLMSG_DATA(nh);
attr = (struct rtattr *)(((char *)ifinfo) + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nh->nlmsg_type = RTM_NEWLINK;
ifinfo->ifi_family = AF_UNSPEC;
ifinfo->ifi_type = ARPHRD_ETHER;
ifinfo->ifi_index = if_nametoindex("eth0");
ifinfo->ifi_flags = IFF_UP;
ifinfo->ifi_change = 0xFFFFFFFF;
// 添加新接口名属性
attr->rta_type = IFLA_IFNAME;
attr->rta_len = RTA_LENGTH(strlen("eth1")+1);
strcpy(RTA_DATA(attr), "eth1");
nh->nlmsg_len += RTA_ALIGN(attr->rta_len);
nh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK;
// 发送netlink消息
len = send(sockfd, nh, nh->nlmsg_len, 0);
if (len == -1) {
perror("send");
close(sockfd);
return -1;
}
// 接收netlink回复
len = recv(sockfd, buffer, BUFSIZE, 0);
if (len == -1) {
perror("recv");
close(sockfd);
return -1;
}
// 处理netlink回复
for (nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_type == NLMSG_ERROR) {
perror("NLMSG_ERROR");
close(sockfd);
return -1;
}
}
printf("Interface name has been set to eth1.\n");
close(sockfd);
return 0;
}