- 解析
/proc/net/route文件 (最常用、最稳定) - 解析
/proc/net/ip_route文件 (IPv6 友好) - 使用
libnl库 (功能最强大、最现代,但最复杂)
解析 /proc/net/route 文件 (推荐用于 IPv4)
这是最经典、最简单的方法,它不依赖任何外部库,仅通过读取内核导出的虚拟文件来获取路由信息。

(图片来源网络,侵删)
原理
/proc/net/route 文件包含了内核路由表的核心信息,每一行代表一个路由条目,我们可以通过解析它来找到默认路由(Destination 为 00000000),其对应的 Gateway 列就是网关的 IP 地址(以十六进制表示)。
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h> // for if_nametoindex
// 解析 /proc/net/route 文件获取默认网关
void get_default_gateway_v4(const char* interface, char* gateway_ip, size_t max_len) {
FILE *fp;
char line[256];
unsigned int dest, gw, mask, flags;
char iface[16];
fp = fopen("/proc/net/route", "r");
if (fp == NULL) {
perror("Failed to open /proc/net/route");
return;
}
// 跳过标题行
fgets(line, sizeof(line), fp);
while (fgets(line, sizeof(line), fp)) {
// 使用 sscanf 解析每一行
// 格式: Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
if (sscanf(line, "%15s %x %x %x %*u %*u %*u %*x %*u %*u %*u",
iface, &dest, &gw, &flags) != 4) {
continue; // 解析失败,跳过
}
// 检查是否是默认路由 (Destination 为 0) 并且是主路由表 (RTF_GATEWAY 标志)
if (dest == 0 && (flags & 0x2)) { // 0x2 是 RTF_GATEWAY 标志
// 如果指定了网络接口,则检查是否匹配
if (interface == NULL || strcmp(iface, interface) == 0) {
// 将网关的十六进制地址转换为点分十进制字符串
struct in_addr addr;
addr.s_addr = gw;
strncpy(gateway_ip, inet_ntoa(addr), max_len - 1);
gateway_ip[max_len - 1] = '\0';
fclose(fp);
return; // 找到后立即返回
}
}
}
fclose(fp);
// 如果没找到
if (gateway_ip) {
gateway_ip[0] = '\0';
}
}
int main() {
char gateway_ip[INET_ADDRSTRLEN] = {0};
// 获取所有接口的默认网关
get_default_gateway_v4(NULL, gateway_ip, sizeof(gateway_ip));
if (gateway_ip[0] != '\0') {
printf("Default Gateway (all interfaces): %s\n", gateway_ip);
} else {
printf("Default Gateway not found.\n");
}
// 获取特定接口 (eth0) 的默认网关
get_default_gateway_v4("eth0", gateway_ip, sizeof(gateway_ip));
if (gateway_ip[0] != '\0') {
printf("Default Gateway (eth0): %s\n", gateway_ip);
} else {
printf("Default Gateway for eth0 not found.\n");
}
return 0;
}
编译与运行:
gcc -o get_gateway get_gateway.c ./get_gateway
解析 /proc/net/ip_route 文件 (IPv6 和 IPv4)
这个文件是 ip route 命令使用的原始数据源,信息更丰富,并且同时支持 IPv4 和 IPv6。
原理
/proc/net/ip_route 的格式比 /proc/net/route 更复杂,但更易读,我们寻找 default 或 default via ... 这样的行,这个文件同样会指定输出接口。

(图片来源网络,侵删)
示例代码 (IPv4)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
void get_default_gateway_v4_from_iproute(const char* interface, char* gateway_ip, size_t max_len) {
FILE *fp;
char line[512];
char iface[16];
char gw_str[40]; // 足够存放 IPv4 或 IPv6 地址
fp = fopen("/proc/net/ip_route", "r");
if (fp == NULL) {
perror("Failed to open /proc/net/ip_route");
return;
}
while (fgets(line, sizeof(line), fp)) {
// 查找默认路由行,"default scope link ..."
if (strstr(line, "default") == NULL) {
continue;
}
// 尝试提取网关和接口信息
// 格式大致为: default ... <dev iface> ... <via gw>
char *dev_ptr = strstr(line, "dev ");
char *via_ptr = strstr(line, "via ");
if (dev_ptr && via_ptr) {
dev_ptr += 4; // 跳过 "dev "
via_ptr += 4; // 跳过 "via "
// 提取接口名
char *dev_end = strchr(dev_ptr, ' ');
if (dev_end) {
strncpy(iface, dev_ptr, dev_end - dev_ptr);
iface[dev_end - dev_ptr] = '\0';
} else {
continue; // 格式错误
}
// 提取网关地址
char *via_end = strchr(via_ptr, ' ');
if (via_end) {
strncpy(gw_str, via_ptr, via_end - via_ptr);
gw_str[via_end - via_ptr] = '\0';
} else {
strncpy(gw_str, via_ptr, sizeof(gw_str) - 1);
gw_str[sizeof(gw_str) - 1] = '\0';
}
// 检查接口是否匹配
if (interface == NULL || strcmp(iface, interface) == 0) {
// 验证提取的网关是否是有效的 IPv4 地址
struct in_addr addr;
if (inet_pton(AF_INET, gw_str, &addr) == 1) {
strncpy(gateway_ip, gw_str, max_len - 1);
gateway_ip[max_len - 1] = '\0';
fclose(fp);
return;
}
}
}
}
fclose(fp);
if (gateway_ip) {
gateway_ip[0] = '\0';
}
}
int main() {
char gateway_ip[INET_ADDRSTRLEN] = {0};
get_default_gateway_v4_from_iproute("eth0", gateway_ip, sizeof(gateway_ip));
printf("Default Gateway (from ip_route, eth0): %s\n", gateway_ip[0] ? gateway_ip : "Not found");
return 0;
}
注意: 这个方法解析字符串更灵活,但也更容易因为内核输出格式的微小变化而失败。/proc/net/route 的结构化格式通常更稳定。
使用 libnl 库 (功能最强大)
libnl (Netlink library) 是与 Linux 内核进行通信的现代标准方式。ip 命令本身也是基于它工作的,使用 libnl 可以获取非常详尽和准确的路由信息,并且是编程方式的首选。
原理
程序通过 Netlink 套接字向内核发送 RTM_GETROUTE 消息,请求路由表信息,内核会返回一个包含所有路由条目的消息,程序再解析这个消息来找到默认网关。
准备工作
首先需要安装 libnl 的开发包。

(图片来源网络,侵删)
# Debian / Ubuntu sudo apt-get install libnl-3-dev libnl-route-3-dev # CentOS / RHEL / Fedora sudo yum install libnl3-devel libnl-route3-devel
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netlink/netlink.h>
#include <netlink/route/route.h>
#include <netlink/route/addr.h>
#include <arpa/inet.h>
void get_default_gateway_libnl(const char* interface, char* gateway_ip, size_t max_len) {
struct nl_sock *sock;
struct nl_cache *cache;
struct nl_msg *msg;
struct rtnl_route *route;
struct nl_addr *gw_addr;
int err;
// 1. 创建 Netlink 套接字
sock = nl_socket_alloc();
if (!sock) {
fprintf(stderr, "Failed to allocate netlink socket\n");
return;
}
// 2. 连接到内核路由协议
if (nl_connect(sock, NETLINK_ROUTE) < 0) {
fprintf(stderr, "Failed to connect to netlink\n");
nl_socket_free(sock);
return;
}
// 3. 请求路由表缓存
err = rtnl_route_alloc_cache(sock, AF_UNSPEC, &cache);
if (err < 0) {
fprintf(stderr, "Failed to allocate route cache: %s\n", nl_geterror(err));
nl_socket_free(sock);
return;
}
// 4. 遍历路由缓存
nl_cache_foreach(cache, (nl_object_cb)rtnl_route_foreach, NULL) {
route = (struct rtnl_route *)nl_object_get_data(cache->c_cur);
// 检查是否是默认路由
if (rtnl_route_get_dst(route) == NULL &&
rtnl_route_get_prefixlen(route) == 0) {
// 获取网关地址
gw_addr = rtnl_route_get_gateway(route);
if (gw_addr) {
// 检查是否是 IPv4 地址
if (nl_addr_get_family(gw_addr) == AF_INET) {
// 检查接口是否匹配
if (interface == NULL ||
strcmp(rtnl_route_get_ifname(route), interface) == 0) {
nl_addr2str(gw_addr, gateway_ip, max_len);
nl_cache_free(cache);
nl_socket_free(sock);
return; // 找到后立即返回
}
}
}
}
}
// 5. 清理资源
nl_cache_free(cache);
nl_socket_free(sock);
if (gateway_ip) {
gateway_ip[0] = '\0';
}
}
int main() {
char gateway_ip[INET_ADDRSTRLEN] = {0};
get_default_gateway_libnl("eth0", gateway_ip, sizeof(gateway_ip));
printf("Default Gateway (using libnl, eth0): %s\n", gateway_ip[0] ? gateway_ip : "Not found");
return 0;
}
编译与运行:
需要链接 libnl-3 和 libnl-route-3 库。
gcc -o get_gateway_libnl get_gateway_libnl.c -lnl-3 -lnl-route-3 ./get_gateway_libnl
总结与对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
解析 /proc/net/route |
简单,无外部依赖。 性能高,直接读取文件。 非常稳定,是内核的标准输出。 |
仅支持 IPv4。 解析逻辑固定,不够灵活。 |
快速获取 IPv4 网关,嵌入式系统,或需要最小依赖的简单脚本。 |
解析 /proc/net/ip_route |
同时支持 IPv4 和 IPv6。 信息更丰富,接近 ip route 命令输出。 |
解析字符串复杂,易受内核格式微小变化影响。 依赖特定内核版本格式。 |
需要同时处理 IPv4/IPv6,且对依赖关系不敏感的场景。 |
使用 libnl 库 |
功能最强大,可获取所有路由属性。 最可靠,是官方 API。 现代、灵活,支持所有 Netlink 功能。 同时支持 IPv4 和 IPv6。 |
依赖外部库,增加部署复杂度。 学习曲线陡峭,API 相对复杂。 代码量最多。 |
需要编写复杂网络工具、需要高度可靠性和完整路由信息的专业应用。 |
推荐建议:
- 如果只是简单地在 C 程序中获取 IPv4 默认网关,方法一 (
/proc/net/route) 是最佳选择,它简单、高效且足够稳定。 - 如果你的程序需要同时处理 IPv4 和 IPv6,并且不介意稍微复杂的解析逻辑,可以考虑方法二 (
/proc/net/ip_route)。 - 如果你正在开发一个功能全面的网络管理工具,或者需要获取路由的元数据(如协议、优先级等),那么方法三 (
libnl) 是不二之选,尽管它更复杂。
