前言近期在排查以太网卡相关异常问题的过程中ifconfig命令被频繁使用。该命令是 Linux 系统中最经典的网络接口配置与诊断工具之一。它属于用户空间应用程序。 其核心原理是通过系统调用与内核网络栈进行通信向内核发起配置请求或者查询相关接口状态等。内核完成对应的操作后将结果返回至用户空间由ifconfig解析并格式化输出。下面将分为两个章节对ifconfig进行详细说明。简介BusyBox 中的ifconfig是一个精简但功能完备的网络接口配置工具继承了传统Unix ifconfig的核心功能并针对嵌入式环境进行了优化。其实现采用表驱动的解析方式配合ioctl系统调用与内核通信支持 IPv4、部分 IPv6 以及多种接口选项。以下从源码结构、核心数据、流程机制和特色功能等方面进行详细解析。实现解析1、整体概览ifconfig.c是该功能主要实现c文件。功能的入口函数为ifconfig_main如下所示。int ifconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ifconfig_main(int argc UNUSED_PARAM, char **argv)其核心操作是通过sockfd xsocket(AF_INET, SOCK_DGRAM, 0)创建控制套接字利用xioctl发送各类的命令配置或查询网络接口。这里使用的xsocket和xioctl和标准的socket和ioctl有什么有什么差异socket是标准的POSIX系统调用成功时返回文件描述符失败时返回-1并设置errno使用时需要检查返回值并处理错误。xsocket是BusyBox基于socket做了一个二次封装在其libb中有具体的实现相关的代码片段如下// Die with an error message if we cant open a new socket. int FAST_FUNC xsocket(int domain, int type, int protocol) { int r socket(domain, type, protocol); if (r lt; 0) { /* Hijack vaguely related config option */ #if ENABLE_VERBOSE_RESOLUTION_ERRORS const char *s INET; ifdef AF_PACKET if (domain AF_PACKET) s PACKET; endif ifdef AF_NETLINK if (domain AF_NETLINK) s NETLINK; endif IF_FEATURE_IPV6(if (domain AF_INET6) s INET6;) bb_perror_msg_and_die(socket(AF_%s,%d,%d), s, type, protocol); #else bb_simple_perror_msg_and_die(socket); #endif } return r; }如上图代码所示如果socket执行失败会进入会自动调用bb_perror_msg_and_die()或者类似的函数相关函数试下如下void FAST_FUNC bb_perror_msg_and_die(const char *s, ...) { va_list p; va_start(p, s); /* Guard against lt;error messagegt;: Success */ bb_verror_msg(s, p, errno ? strerror(errno) : NULL); va_end(p); xfunc_die(); } void FAST_FUNC bb_simple_perror_msg(const char *s) { bb_perror_msg(%s, s); } void FAST_FUNC bb_simple_perror_msg_and_die(const char *s) { bb_perror_msg_and_die(%s, s); }根据对应的实现进入bb_perror_msg_and_die()或者类似的函数打印错误信息并终止整个程序。对应ifconfig这样功能的用户空间程序这样的处理简化了主代码逻辑程序更为实用如果资源不可用故障迅速而又明确避免了二次处理。根据上面的信息对比标准的socket() BusyBox 的xsocket()具体差别如下标准socketBusyBox xsocket返回值成功返回文件描述符失败返回-1并设置errno成功返回文件描述符失败则不会返回直接终止进程错误处理需要调用者手动检查 并处理内部自动检查一旦失败立即打印错误并退出代码健壮性调用者必须记得写错误判断否则后续操作可能崩溃保证返回的socketfd 一定是合法的后续无需再判空设计思路通用库函数由上层应用决定如何处理错误适用于命令行工具“快速失败”原则一旦系统调用失败工具继续执行无意义直接退出并报错BusyBox中xopen以及xioctl等相关实现思路基本一致具体可参考BusyBox源码中xfuncs_printf.c中的相关实现注不同版本的源码实现可能会存在稍许差异整体思路基本一致2、关键数据结构BusyBox ifconfig的实现依赖于几个核心数据结构来管理网络接口的配置与状态。这些数据结构的设计体现了嵌入式环境下对内存效率和代码简洁性的追求具体如下。2.1 struct optionsstruct options { const char *name; #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS const unsigned int flags:6; const unsigned int arg_flags:10; #else const unsigned char flags; const unsigned char arg_flags; #endif const unsigned short selector; };2.2 struct arg1optstruct arg1opt { const char *name; // ioctl 命令名称 unsigned short selector; // ioctl 请求号 unsigned short ifr_offset; // struct ifreq中的偏移量 };3、ifconfig_main的执行流程进入ifconfig_main函数首先对ifconfig命令的处理# BusyBox源码networking/ifconfig.c 省略部分代码 IF_FEATURE_IFCONFIG_STATUS(char *show_all_param;) 省略部分代码 #if ENABLE_FEATURE_IFCONFIG_STATUS show_all_param NULL; if (argv[0] argv[0][0] - argv[0][1] a !argv[0][2]) { argv; show_all_param IFNAME_SHOW_DOWNED_TOO; } #endif if (!argv[0] || !argv[1]) { /* one or no args */ #if ENABLE_FEATURE_IFCONFIG_STATUS return display_interfaces(argv[0] ? argv[0] : show_all_param); #else bb_simple_error_msg_and_die(no support for status display); #endif } 省略部分代码 IF_FEATURE_IFCONFIG_STATUS巧妙的通过预编译处理与BusyBox的宏控CONFIG_FEATURE_IFCONFIG_STATUS关联起来当BusyBox的CONFIG_FEATURE_IFCONFIG_STATUSy时会将char *show_all_param;填入CONFIG_FEATURE_IFCONFIG_STATUS并判断ifconfig -a命令之后会进入display_interfaces函数中其实现如下# BusyBox源码networking/interface.c int FAST_FUNC display_interfaces(char *ifname) { struct interface *ife; int res; struct iface_list ilist; ilist.int_list NULL; ilist.int_last NULL; if_readlist(ilist, ifname ! IFNAME_SHOW_DOWNED_TOO ? ifname : NULL); if (!ifname || ifname IFNAME_SHOW_DOWNED_TOO) { for (ife ilist.int_list; ife; ife ife-next) { BUILD_BUG_ON((int)(intptr_t)IFNAME_SHOW_DOWNED_TOO ! 1); res do_if_print(ife, (int)(intptr_t)ifname); if (res 0) goto ret; } return 0; } ife add_interface(ilist, ifname); res do_if_print(ife, /*show_downed_too:*/ 1); ret: return (res 0); /* status 0 1 -- error */ }display_interface通过读取所有的接口信息并结合输出的命令是否情况进行显示。之后使用xsocket创建AF_INET套接字SOCK_DGRAM并通过strncpy_IFNAMSIZ将接口名# BusyBox源码/libbb/xfuncs.c char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src) { #ifndef IFNAMSIZ enum { IFNAMSIZ 16 }; #endif return strncpy(dst, src, IFNAMSIZ); }*argv填充到内核通信结构体ifr的ifr_name中便于后续通过系统调用与内核通信。之后进入ifconfig核心参数解析通过循环遍历OptArry匹配项匹配成功跳转FOUND_ARGfor (op OptArray; op-name; op) { /* Find table entry. */ if (strcmp(p, op-name) 0) { /* If name matches... */ mask op-flags; if (mask) /* set the mask and go. */ goto FOUND_ARG; /* If we get here, there was a valid arg with an */ /* invalid - prefix. */ bb_error_msg_and_die(bad: %s, p-1); } } /* We fell through, so treat as possible hostname. */ a1op Arg1Opt ARRAY_SIZE(Arg1Opt) - 1; mask op-arg_flags; goto HOSTNAME;匹配失败则当做主机名处理进入HOSTNAME逻辑。以上逻辑为ifconfig的主体逻辑后面将对FOUND_ARG HOSTNAME具体的逻辑进行说明