如何通过libnl或原生Netlink API获取VLAN接口的父接口ID?
获取VLAN接口的父接口(real_dev)ID via libnl或原生Netlink API
我需要获取指定VLAN接口的real_dev(例如ID)。我使用libnl编写了如下测试代码:
int main(void) { struct nl_sock *sock; struct nl_cache *cache; char iface[] = "eno1.10"; //char iface[] = "eno1"; if (!(sock = nl_socket_alloc())) { perror("nl_socket_alloc"); return -1; } if (nl_connect(sock, NETLINK_ROUTE) < 0) { perror("nl_connect"); nl_socket_free( sock ); return -1; } if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache) < 0) { perror("rtnl_link_alloc_cache"); nl_socket_free( sock ); nl_close( sock ); return -1; } { int ifindex; struct rtnl_link *link = NULL; if (!(ifindex = rtnl_link_name2i(cache, iface))) { perror("rtnl_link_name2i"); return -1; } printf("ind: %d\n", ifindex); if (!(link = rtnl_link_get(cache, ifindex))) { perror("rtnl_link_get"); return -1; } if (rtnl_link_is_vlan(link)) { puts("It's VLAN link"); /* alas it's not about the 'real' device */ printf("master: %d\n", rtnl_link_get_master(link)); } else puts("It's 'real' link"); } return 0; }目前我能获取接口ID并判断是否为VLAN接口,但不知道如何获取该VLAN所依附的父接口。似乎libnl的API不提供此功能,请问是否可通过libnl或原生Netlink API获取VLAN的父接口ID?
解决方案
其实你完全可以通过libnl或者原生Netlink API获取VLAN的父接口ID,只是用错了API而已——rtnl_link_get_master是针对bond、网桥这类主从接口模型的,VLAN的父接口需要用专门的VLAN属性接口来获取。
方法一:使用libnl的VLAN专用API
libnl提供了rtnl_link_vlan_get_parent()函数,专门用来获取VLAN接口的父接口ifindex。你只需要在判断接口是VLAN之后调用这个函数即可,甚至还可以通过父接口的ifindex反向获取接口名称。
修改你的代码如下:
int main(void) { struct nl_sock *sock; struct nl_cache *cache; char iface[] = "eno1.10"; //char iface[] = "eno1"; if (!(sock = nl_socket_alloc())) { perror("nl_socket_alloc"); return -1; } if (nl_connect(sock, NETLINK_ROUTE) < 0) { perror("nl_connect"); nl_socket_free( sock ); return -1; } if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache) < 0) { perror("rtnl_link_alloc_cache"); nl_socket_free( sock ); nl_close( sock ); return -1; } { int ifindex; struct rtnl_link *link = NULL; if (!(ifindex = rtnl_link_name2i(cache, iface))) { perror("rtnl_link_name2i"); return -1; } printf("ind: %d\n", ifindex); if (!(link = rtnl_link_get(cache, ifindex))) { perror("rtnl_link_get"); return -1; } if (rtnl_link_is_vlan(link)) { puts("It's VLAN link"); // 获取VLAN父接口的ifindex int parent_ifindex = rtnl_link_vlan_get_parent(link); printf("Parent interface index: %d\n", parent_ifindex); // 可选:通过ifindex获取父接口名称 char parent_name[IFNAMSIZ]; if (rtnl_link_i2name(cache, parent_ifindex, parent_name, sizeof(parent_name))) { printf("Parent interface name: %s\n", parent_name); } } else { puts("It's 'real' link"); } // 别忘了释放link对象 rtnl_link_put(link); } // 释放缓存和socket nl_cache_free(cache); nl_socket_free(sock); return 0; }
注意事项:
- 编译时需要链接libnl-route库,比如用
gcc your_code.c -o vlan_parent -lnl-3 -lnl-route-3 - 用完
rtnl_link_get获取的link对象后,记得调用rtnl_link_put释放,避免内存泄漏 - 你的原代码没有释放缓存和socket,建议加上,养成良好的内存管理习惯
方法二:使用原生Netlink API解析属性
如果不想依赖libnl,直接用原生Netlink的话,你需要在接收RTM_NEWLINK消息时,解析嵌套的IFLA_VLAN属性,其中的IFLA_VLAN_PARENT字段就是父接口的ifindex。
核心逻辑大概是:
- 发送
RTM_GETLINK请求,指定目标VLAN接口的ifindex - 接收Netlink响应,遍历消息中的属性
- 找到
IFLA_VLAN类型的属性(这是一个嵌套属性) - 在
IFLA_VLAN的子属性中找到IFLA_VLAN_PARENT,其值就是父接口的ifindex
示例伪代码片段:
// 假设已经获取到了Netlink消息的nlmsghdr指针nlh struct ifinfomsg *ifm = NLMSG_DATA(nlh); struct nlattr *tb[IFLA_MAX+1]; // 解析顶层属性 nla_parse(tb, IFLA_MAX, IFLA_RTA(ifm), NLMSG_PAYLOAD(nlh, sizeof(*ifm)), NULL); if (tb[IFLA_VLAN]) { struct nlattr *vlan_tb[IFLA_VLAN_MAX+1]; // 解析VLAN嵌套属性 nla_parse(vlan_tb, IFLA_VLAN_MAX, nla_data(tb[IFLA_VLAN]), nla_len(tb[IFLA_VLAN]), NULL); if (vlan_tb[IFLA_VLAN_PARENT]) { int parent_ifindex = nla_get_u32(vlan_tb[IFLA_VLAN_PARENT]); printf("Parent ifindex: %d\n", parent_ifindex); } }
这种方法更底层,但不需要依赖libnl库,适合对依赖有严格要求的场景。
内容的提问来源于stack exchange,提问作者red0ct




