在 CentOS 中,我使用 NetworkManager 来处理我的接口和网络配置。为了进行展示,我需要快速将网络电缆从交换机的端口 1 插入到端口 2。每个交换机端口属于不同的网络。在每个网络中,都有一个单独的 DHCP 服务器为其网络分配 IP 地址。每当我将计算机插入交换机的不同端口时,我都应该收到新的 IP 地址。
然而,拔掉电缆后,大约需要 5 秒钟,IP 地址才会从我的接口中删除。因此,当我快速将计算机重新插入不同的端口时,旧的 IP 地址尚未删除,也没有分配新的 IP 地址。
我的第一次尝试是使用自定义脚本来/etc/NetworkManager/NetworkManager/dispatcher.d/
刷新接口的 IP 地址。不幸的是,我的脚本直到大约 5 秒超时后才被调用。
是否可以配置 NetworkManager 以更快地刷新 IP 地址?如果是,怎么办?如果不使用 NetworkManager,是否有其他方法可以快速删除 IP 地址?
另外,有谁知道网络堆栈的哪个组件负责此超时?有可能减少吗?
答案1
在停用设备之前,NM 会忽略载波丢失信号 5 秒。目前超时是不可配置的(在某些情况下超时甚至可以延长至 10 秒)。这样做是因为有时运营商可能会暂时消失,例如在更改 MTU 时或因为交换机决定这样做。
我不知道有什么简单的解决方案,如果您想更快地重新插入电缆,只需 10 秒。当然,您可以发出nmcli device reapply "$DEV"
或nmcli connection up "$CON"
,但这需要您进行额外的手动干预。
答案2
我现在正在使用用 C 编写的服务来自己管理界面。它基本上是一个包装器dhclient和ip。这是我用来管理界面的代码:
#include <stdio.h>
#include <string.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <stdbool.h>
#define ENTRY(x) {x, #x}
struct {
unsigned flag;
const char *name;
} ifi_flag_map[] = {
ENTRY(IFF_UP),
ENTRY(IFF_BROADCAST),
ENTRY(IFF_DEBUG),
ENTRY(IFF_LOOPBACK),
ENTRY(IFF_POINTOPOINT),
ENTRY(IFF_NOTRAILERS),
ENTRY(IFF_RUNNING),
ENTRY(IFF_NOARP),
ENTRY(IFF_PROMISC),
ENTRY(IFF_ALLMULTI),
ENTRY(IFF_MASTER),
ENTRY(IFF_SLAVE),
ENTRY(IFF_MULTICAST),
ENTRY(IFF_PORTSEL),
ENTRY(IFF_AUTOMEDIA),
ENTRY(IFF_DYNAMIC),
ENTRY(IFF_LOWER_UP),
ENTRY(IFF_DORMANT),
ENTRY(IFF_ECHO),
};
int InterfaceIndex;
char InterfaceName[32];
char FlushCommand[64];
char DHCPCommand[64];
void check_flags(unsigned flags)
{
size_t i;
bool hasFlagLowerUp = false;
for (i = 0; i < sizeof ifi_flag_map/sizeof ifi_flag_map[0]; i++) {
if (flags & ifi_flag_map[i].flag) {
if (ifi_flag_map[i].name == "IFF_LOWER_UP"){
hasFlagLowerUp = true;
}
}
}
if (!hasFlagLowerUp){
printf("Cable removed. Going to flush IPs now...\n");
system(FlushCommand);
} else {
printf("Flag IFF_LOWER_UP present. Going to call %s.\n", DHCPCommand);
system(DHCPCommand);
}
}
void read_msg(int fd)
{
int len;
char buf[4096];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl sa;
struct msghdr msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
struct nlmsghdr *nh;
len = recvmsg(fd, &msg, 0);
if(len == -1) {
perror("recvmsg");
return;
}
for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len);
nh = NLMSG_NEXT (nh, len)) {
struct ifinfomsg *ifimsg;
if (nh->nlmsg_type == NLMSG_DONE)
return;
if (nh->nlmsg_type == NLMSG_ERROR) {
continue;
}
ifimsg = NLMSG_DATA(nh);
printf("Noticed event on interface with id %u\n", ifimsg->ifi_index);
if (ifimsg->ifi_index == InterfaceIndex) {
printf("Interface is supervised interface %s. Going to check if cable was removed...\n",InterfaceName);
check_flags(ifimsg->ifi_flags);
}
}
}
int main(int argc, char *argv[])
{
if (argc < 3){
printf("Please provide interface id as first argument\n");
printf("Please provide interface name as second argument\n\n");
printf("Example: %s 2 eth0\n", argv[0]);
return 1;
}
if (sscanf (argv[1], "%i", &InterfaceIndex) != 1) {
fprintf(stderr, "Interface index must be integer\n");
return 1;
}
strcpy(InterfaceName,argv[2]);
sprintf(FlushCommand, "ip addr flush dev %s", InterfaceName);
sprintf(DHCPCommand, "dhclient -r %s && dhclient %s", InterfaceName, InterfaceName);
printf("Watching interface %s with id %u for events.\n", InterfaceName, InterfaceIndex);
printf("Will flush IPs from interface %s when cable is removed.\n", InterfaceName);
struct sockaddr_nl sa;
int fd;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK;
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(fd == -1) {
perror("socket");
return 1;
}
if(bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
perror("bind");
return 1;
}
for(;;) {
read_msg(fd);
}
return 0;
}