Linux通过c语言函数管理网络(1)-----获取/修改IP和MAC地址

在Linux下编写程序经常遇到需要对网卡进行管理,但是在glic库中或者其他函数库中却发现没有境界能用的API。有的人在程序中通过system()执行ifconfig命令进行配置,但是获取参数却很麻烦,也有的人通过int getifaddrs(struct ifaddrs **ifap);去获取ip地址等信息,但是只能获取,却不能配置。

还有是通过ioctl()接口进行控制,ioctl接口算是比较常用的,具体如何使用可以从参考:

Linux下通过C语言管理网络--基于ioctl_霍宏鹏的专栏-CSDN博客Linux下通过ioctl接口获取和设置IP地址,获取和设置mac地址,获取和设置网卡当前状态​编辑https://huohongpeng.blog.csdn.net/article/details/120866088

netdevice(7) - Linux manual page​编辑https://man7.org/linux/man-pages/man7/netdevice.7.html还有是通过netlink协议,在NETLINK_ROUTE协议下进行通信,可以参考:

https://man7.org/linux/man-pages/man7/rtnetlink.7.html
https://man7.org/linux/man-pages/man3/rtnetlink.3.html

但是处理原始的netlink消息非常麻烦,且容易出错。但是netlink的功能相比于上面的方法要强大,比如现在ioctl()接口是不能处理ipv6的。netlink还可以轻松的获取路由信息。

我这里不直接使用netlink进行配置,其实早就已经有大牛帮我们写好了netlink相关的库即libnl-3.0,关于libnl的相关介绍可以看:

https://www.infradead.org/~tgr/libnl/doc/route.html
http://www.infradead.org/~tgr/libnl/
http://www.infradead.org/~tgr/libnl/doc/api/group__core.html

编译下面函数需要:

 
  1. -lnl-route-3

  2. -lnl-3

  3. -Ilibnl3

 
  1. /*

  2. * Copyright (C) 2021, 2021 huohongpeng

  3. * Author: huohongpeng <1045338804@qq.com>

  4. *

  5. * This program is free software; you can redistribute it and/or modify

  6. * it under the terms of the GNU General Public License as published by

  7. * the Free Software Foundation; either version 2 of the License, or

  8. * (at your option) any later version.

  9. *

  10. * Change logs:

  11. * Date Author Notes

  12. * 2021-10-19 huohongpeng 首次添加

  13. */

  14. #include <stdio.h>

  15. #include <string.h>

  16. #include <stdlib.h>

  17. #include <sys/types.h>

  18. #include <sys/socket.h>

  19. #include <arpa/inet.h>

  20. #include <linux/netlink.h>

  21. #include <linux/rtnetlink.h>

  22. #include <linux/route.h>

  23. #include <errno.h>

  24. #include <netlink/route/link.h>

  25. #include <netlink/route/route.h>

  26. #include <netlink/route/addr.h>

  27. #include <netlink/netlink.h>

  28. #include <linux/if_arp.h>

  29. #include <ifaddrs.h>

  30. #include <net/ethernet.h>

  31. #include <sys/socket.h>

  32. #include <netdb.h>

  33. /*

  34. * 启动/停用 网卡状态 (类似 ifconfig ethx up或ifconfig ethx down)

  35. * @eth_name: 网卡名, 如:eth0, wlan0..

  36. * @state: "up": 启动网卡, "down": 停用网卡

  37. * @ret: 0: 成功, -1: 失败

  38. */

  39. int rtnetlink_eth_state(char *eth_name, char *state)

  40. {

  41. int ret;

  42. struct rtnl_link *link, *link_conf;

  43. struct nl_sock *sk = nl_socket_alloc();

  44. nl_connect(sk, NETLINK_ROUTE);

  45. ret = rtnl_link_get_kernel(sk, 0, eth_name, &link);

  46. if (ret < 0) {

  47. printf("Err: %s not found!!!\n", eth_name);

  48. ret = -1;

  49. goto ERROR1;

  50. }

  51. link_conf = rtnl_link_alloc();

  52. if (strcmp("up", state) == 0) {

  53. rtnl_link_set_flags(link_conf, IFF_UP);

  54. } else if (strcmp("down", state) == 0) {

  55. rtnl_link_unset_flags(link_conf, IFF_UP);

  56. } else {

  57. ret = -1;

  58. printf("Err: %s para error!!!\n", state);

  59. goto ERROR2;

  60. }

  61. ret = rtnl_link_change(sk, link, link_conf, 0);

  62. if (ret < 0) {

  63. printf("Err: rtnetlink_eth_state: %d\n", ret);

  64. }

  65. ERROR2:

  66. rtnl_link_put(link);

  67. rtnl_link_put(link_conf);

  68. ERROR1:

  69. nl_close(sk);

  70. nl_socket_free(sk);

  71. return ret;

  72. }

  73. /*

  74. * 修改网卡的MAC地址

  75. * @eth_name: 网卡名, 如:eth0, wlan0..

  76. * @mac: 6字节mac地址, 如unsigned char mac[6] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB};

  77. * 类似于 ifconfig ethx hw ether 12:34:56:78:90:AB

  78. * @ret: 0: 成功, -1: 失败

  79. */

  80. int rtnetlink_eth_set_mac(char *eth_name, unsigned char *mac)

  81. {

  82. int ret;

  83. struct rtnl_link *link, *link_conf;

  84. struct nl_addr *hw_addr;

  85. struct nl_sock *sk = nl_socket_alloc();

  86. nl_connect(sk, NETLINK_ROUTE);

  87. ret = rtnl_link_get_kernel(sk, 0, eth_name, &link);

  88. if (ret < 0) {

  89. printf("Err: %s not found!!!\n", eth_name);

  90. ret = -1;

  91. goto ERROR1;

  92. }

  93. unsigned int if_flag = rtnl_link_get_flags(link);

  94. /*

  95. * 只有在网卡不启用的时候才能设置MAC地址,所以如果网卡已经启用,

  96. * 首先停用网卡,然后设置MAC地址,设置完成后,再次启动网卡

  97. */

  98. if (if_flag & IFF_UP) {

  99. rtnetlink_eth_state(eth_name, "down");

  100. }

  101. link_conf = rtnl_link_alloc();

  102. /*

  103. * AF_LLC: 地址簇是MAC地址

  104. */

  105. hw_addr = nl_addr_build(AF_LLC, mac, 6);

  106. rtnl_link_set_addr(link_conf, hw_addr);

  107. ret = rtnl_link_change(sk, link, link_conf, 0);

  108. if (ret < 0) {

  109. printf("Err: rtnetlink_eth_change_mac: %d\n", ret);

  110. }

  111. if (if_flag & IFF_UP) {

  112. rtnetlink_eth_state(eth_name, "up");

  113. }

  114. nl_addr_put(hw_addr);

  115. rtnl_link_put(link);

  116. rtnl_link_put(link_conf);

  117. ERROR1:

  118. nl_close(sk);

  119. nl_socket_free(sk);

  120. return ret;

  121. }

  122. /*

  123. * 获取网卡的MAC地址

  124. * @eth_name: 网卡名, 如:eth0, wlan0..

  125. * @mac: 返回6字节mac地址, 如unsigned char mac[6] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB};

  126. * @ret: 0: 成功, -1: 失败

  127. */

  128. int rtnetlink_eth_get_mac(char *eth_name, unsigned char *mac)

  129. {

  130. int ret;

  131. struct rtnl_link *link;

  132. struct nl_addr *hw_addr;

  133. struct nl_sock *sk = nl_socket_alloc();

  134. nl_connect(sk, NETLINK_ROUTE);

  135. ret = rtnl_link_get_kernel(sk, 0, eth_name, &link);

  136. if (ret < 0) {

  137. printf("Err: %s not found!!!", eth_name);

  138. ret = -1;

  139. goto ERROR1;

  140. }

  141. hw_addr = rtnl_link_get_addr(link);

  142. if (nl_addr_get_len(hw_addr) != 6 || nl_addr_get_family(hw_addr) != AF_LLC) {

  143. ret = -1;

  144. printf("Err: hw_addr len or family error!!!\n");

  145. goto ERROR2;

  146. }

  147. unsigned char *pmac = (unsigned char *)nl_addr_get_binary_addr(hw_addr);

  148. memcpy(mac, pmac, 6);

  149. ERROR2:

  150. rtnl_link_put(link);

  151. ERROR1:

  152. nl_close(sk);

  153. nl_socket_free(sk);

  154. return ret;

  155. }

  156. static int ipv4_netmask2prefixlen(unsigned char *netmask)

  157. {

  158. int count = 0;

  159. for (int i = 0; i < 4; i++) {

  160. for (int j = 0; j < 8; j++) {

  161. if ((netmask[i] & (0x01<<j)) != 0) {

  162. count++;

  163. }

  164. }

  165. }

  166. return count;

  167. }

  168. static void ipv4_prefixlen2netmask(int prefixlen, unsigned char *netmask)

  169. {

  170. int i;

  171. memset(netmask, 0x00, 4);

  172. int byte = prefixlen / 8;

  173. int bit = prefixlen % 8;

  174. for (i = 0; i < byte; i++) {

  175. netmask[i] = 0xff;

  176. }

  177. netmask[byte] = 0;

  178. for (i = 0; i < bit; i++) {

  179. netmask[byte] |= (0x01<<(7 - i));

  180. }

  181. }

  182. struct get_ipv4_addr_ctx_t {

  183. struct nl_sock *sk;

  184. int flag; /* 是否找到ip */

  185. int index; /* 网卡索引 */

  186. unsigned char *ip;

  187. unsigned char *netmask;

  188. };

  189. static void get_ipv4_addr_cache_cb(struct nl_object *obj, void *arg)

  190. {

  191. struct get_ipv4_addr_ctx_t *ctx = (struct get_ipv4_addr_ctx_t *)arg;

  192. struct rtnl_addr *rtnl_addr = (struct rtnl_addr *)obj;

  193. if (rtnl_addr_get_ifindex(rtnl_addr) == ctx->index) {

  194. struct nl_addr *nl_addr = rtnl_addr_get_local(rtnl_addr);

  195. if (nl_addr_get_family(nl_addr) == AF_INET && nl_addr_get_len(nl_addr) == 4) {

  196. ctx->flag = 0x01;

  197. int netmask_prefixlen = rtnl_addr_get_prefixlen(rtnl_addr);

  198. char *ip = (char *)nl_addr_get_binary_addr(nl_addr);

  199. memcpy(ctx->ip, ip, 4);

  200. if (netmask_prefixlen <= 32) {

  201. ipv4_prefixlen2netmask(netmask_prefixlen, ctx->netmask);

  202. }

  203. }

  204. }

  205. }

  206. /*

  207. * 获取网卡ip地址

  208. * @eth_name: 网卡名, 如:eth0, wlan0..

  209. * @ip: 返回4字节ip地址, 如char ip[4] = {10, 10, 10, 3};即: 10.10.10.3

  210. * @netmask: 返回4字节netmask地址, 如char netmask[4] = {0xff, 0xff, 0xff, 0};即: 255.255.255.0

  211. * @ret: 0: 成功, -1: 不能获取ip地址

  212. */

  213. int rtnetlink_eth_get_ipv4_addr(char *eth_name, unsigned char *ip, unsigned char *netmask)

  214. {

  215. int ret = 0;

  216. struct nl_cache *addr_cache;

  217. struct rtnl_link *link;

  218. struct nl_sock *sk = nl_socket_alloc();

  219. nl_connect(sk, NETLINK_ROUTE);

  220. ret = rtnl_link_get_kernel(sk, 0, eth_name, &link);

  221. if (ret < 0) {

  222. printf("Err: %s not found!!!\n", eth_name);

  223. ret = -1;

  224. goto ERROR1;

  225. }

  226. struct get_ipv4_addr_ctx_t ctx;

  227. memset(&ctx, 0x00, sizeof(struct get_ipv4_addr_ctx_t));

  228. ctx.index = rtnl_link_get_ifindex(link);

  229. ctx.ip = ip;

  230. ctx.netmask = netmask;

  231. rtnl_addr_alloc_cache(sk, &addr_cache);

  232. nl_cache_foreach(addr_cache, get_ipv4_addr_cache_cb, &ctx);

  233. if (ctx.flag == 0) {

  234. ret = -1;

  235. printf("Err: %s not find ip!!!\n", eth_name);

  236. }

  237. nl_cache_put(addr_cache);

  238. rtnl_link_put(link);

  239. ERROR1:

  240. nl_close(sk);

  241. nl_socket_free(sk);

  242. return ret;

  243. }

  244. static void del_ipv4_addr_cache_cb(struct nl_object *obj, void *arg)

  245. {

  246. struct get_ipv4_addr_ctx_t *ctx = (struct get_ipv4_addr_ctx_t *)arg;

  247. struct rtnl_addr *rtnl_addr = (struct rtnl_addr *)obj;

  248. if (rtnl_addr_get_ifindex(rtnl_addr) == ctx->index) {

  249. struct nl_addr *nl_addr = rtnl_addr_get_local(rtnl_addr);

  250. if (nl_addr_get_family(nl_addr) == AF_INET && nl_addr_get_len(nl_addr) == 4) {

  251. int ret = rtnl_addr_delete(ctx->sk, rtnl_addr, 0);

  252. if (ret < 0) {

  253. printf("Err: rtnl_addr_delete:%d!!!\n", ret);

  254. }

  255. }

  256. }

  257. }

  258. /*

  259. * 删除网卡ip地址

  260. * @eth_name: 网卡名, 如:eth0, wlan0..

  261. * @ip: 返回4字节ip地址, 如char ip[4] = {10, 10, 10, 3};即: 10.10.10.3

  262. * @netmask: 返回4字节netmask地址, 如char netmask[4] = {0xff, 0xff, 0xff, 0};即: 255.255.255.0

  263. * @ret: 0: 成功, -1: 不能获取ip地址

  264. */

  265. int rtnetlink_eth_del_ipv4_addr(char *eth_name)

  266. {

  267. int ret = 0;

  268. struct nl_cache *addr_cache;

  269. struct rtnl_link *link;

  270. struct nl_sock *sk = nl_socket_alloc();

  271. nl_connect(sk, NETLINK_ROUTE);

  272. ret = rtnl_link_get_kernel(sk, 0, eth_name, &link);

  273. if (ret < 0) {

  274. printf("Err: %s not found!!!\n", eth_name);

  275. ret = -1;

  276. goto ERROR1;

  277. }

  278. struct get_ipv4_addr_ctx_t ctx;

  279. memset(&ctx, 0x00, sizeof(struct get_ipv4_addr_ctx_t));

  280. ctx.index = rtnl_link_get_ifindex(link);

  281. ctx.sk = sk;

  282. rtnl_addr_alloc_cache(sk, &addr_cache);

  283. nl_cache_foreach(addr_cache, del_ipv4_addr_cache_cb, &ctx);

  284. nl_cache_put(addr_cache);

  285. rtnl_link_put(link);

  286. ERROR1:

  287. nl_close(sk);

  288. nl_socket_free(sk);

  289. return ret;

  290. }

  291. /*

  292. * 设置网卡ip地址

  293. * @eth_name: 网卡名, 如:eth0, wlan0..

  294. * @ip: 返回4字节ip地址, 如char ip[4] = {10, 10, 10, 3};即: 10.10.10.3

  295. * @netmask: 返回4字节netmask地址, 如char netmask[4] = {0xff, 0xff, 0xff, 0};即: 255.255.255.0

  296. * @ret: 0: 成功, -1: 不能获取ip地址

  297. * 注意: 一般情况下网卡可以设置多个IP地址,这个函数只设置1个IP地址,设置过程是首先删除原来的IP地址,然后在添加新的IP地址

  298. */

  299. int rtnetlink_eth_set_ipv4_addr(char *eth_name, unsigned char *ip, unsigned char *netmask)

  300. {

  301. int ret = 0;

  302. struct rtnl_link *link;

  303. struct nl_sock *sk = nl_socket_alloc();

  304. nl_connect(sk, NETLINK_ROUTE);

  305. ret = rtnl_link_get_kernel(sk, 0, eth_name, &link);

  306. if (ret < 0) {

  307. printf("Err: %s not found!!!\n", eth_name);

  308. ret = -1;

  309. goto ERROR1;

  310. }

  311. struct rtnl_addr *new_rtnl_addr = rtnl_addr_alloc();

  312. int if_index = rtnl_link_get_ifindex(link);

  313. /*

  314. * 在rtnl_addr中,netmask用ip地址有效长度表示,

  315. * 如: 255.225.0.0有效长度为16

  316. */

  317. int netmask_prefixlen = ipv4_netmask2prefixlen(netmask);

  318. rtnl_addr_set_ifindex(new_rtnl_addr, if_index);

  319. rtnl_addr_set_family(new_rtnl_addr, AF_INET);

  320. struct nl_addr *ip_addr = nl_addr_build(AF_INET, ip, 4);

  321. rtnl_addr_set_local(new_rtnl_addr, ip_addr);

  322. nl_addr_put(ip_addr);

  323. rtnl_addr_set_prefixlen(new_rtnl_addr, netmask_prefixlen);

  324. unsigned char broadcast[4];

  325. for (int i = 0; i< 4; i++) {

  326. broadcast[i] = (netmask[i] & ip[i]);

  327. broadcast[i] |= ~netmask[i];

  328. }

  329. struct nl_addr *broadcast_addr = nl_addr_build(AF_INET, broadcast, 4);

  330. rtnl_addr_set_broadcast(new_rtnl_addr, broadcast_addr);

  331. nl_addr_put(broadcast_addr);

  332. /*

  333. * 添加地址之前首先删除原来的地址

  334. */

  335. rtnetlink_eth_del_ipv4_addr(eth_name);

  336. ret = rtnl_addr_add(sk, new_rtnl_addr, 0);

  337. if (ret < 0) {

  338. printf("Err: rtnl_addr_add: %d\n", ret);

  339. }

  340. rtnl_addr_put(new_rtnl_addr);

  341. rtnl_link_put(link);

  342. ERROR1:

  343. nl_close(sk);

  344. nl_socket_free(sk);

  345. return ret;

  346. }

  347. void test_rtnetlink_manage(void)

  348. {

  349. unsigned char ip[4] = {192, 168, 60, 85};

  350. unsigned char netmask[] = {255, 255, 255, 0};

  351. //rtnetlink_eth_del_ipv4_addr("wlan0");

  352. rtnetlink_eth_set_ipv4_addr("eth0", ip, netmask);

  353. return;

  354. //getifaddrs()

  355. }

Linux通过c语言函数管理网络(1)-----获取/修改IP和MAC地址_根据网卡名获取mac地址 c语言-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言可以通过使用Windows和Linux操作系统提供的相关 API 来获取本机的 CPUID 和 MAC 地址。 首先,我们可以通过Windows API函数`GetSystemInfo()`获取CPUID。示例代码如下: ```c #include <stdio.h> #include <windows.h> void main() { SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); printf("CPU ID: %x %x %x %x\n", sys_info.dwProcessorType, sys_info.dwPageSize, sys_info.dwActiveProcessorMask, sys_info.dwNumberOfProcessors); } ``` 然后,我们可以通过使用Windows API函数`GetAdaptersInfo()`来获取本机的 MAC 地址。示例代码如下: ```c #include <stdio.h> #include <windows.h> #include <iphlpapi.h> #pragma comment(lib, "iphlpapi.lib") void main() { IP_ADAPTER_INFO adapter_info[16]; DWORD buf_len = sizeof(adapter_info); if (GetAdaptersInfo(adapter_info, &buf_len) == ERROR_SUCCESS) { PIP_ADAPTER_INFO curr_adapter = adapter_info; while (curr_adapter != NULL) { printf("MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n", curr_adapter->Address[0], curr_adapter->Address[1], curr_adapter->Address[2], curr_adapter->Address[3], curr_adapter->Address[4], curr_adapter->Address[5]); curr_adapter = curr_adapter->Next; } } } ``` 对于Linux操作系统,我们可以使用`sys/sysinfo.h`和`ifaddrs.h`头文件来获取 CPUID 和 MAC 地址。示例代码如下: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sysinfo.h> #include <ifaddrs.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> #include <string.h> void main() { struct sysinfo sys_info; if (sysinfo(&sys_info) == 0) { printf("CPU ID: %08x\n", (unsigned int)sys_info.processor_id[0]); } struct ifaddrs* ifaddr, *ifa; if (getifaddrs(&ifaddr) == 0) { for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET) continue; struct sockaddr_ll* s = (struct sockaddr_ll*)ifa->ifa_addr; unsigned char mac[6]; memcpy(mac, s->sll_addr, 6); printf("MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } freeifaddrs(ifaddr); } } ``` 需要注意的是,在使用这些API函数时,可能需要添加某些特定的库文件或在编译选项中指定相关的库文件,具体需要根据不同的操作系统和编译环境来进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值