前言
這篇文章寫了獲取本機(jī)的ip地址和子網(wǎng)掩碼:Linux c語言獲取本機(jī) ip、子網(wǎng)掩碼
一、獲取本機(jī)網(wǎng)關(guān) ip 地址
1.1 Netlink套接字簡介
關(guān)于Netlink套接字請(qǐng)參考:Linux 網(wǎng)絡(luò)之netlink 簡介
Linux Netlink套接字是一種用于在Linux內(nèi)核和用戶空間之間進(jìn)行通信的機(jī)制。它是Linux內(nèi)核中的一種通信協(xié)議,用于讓用戶空間程序與內(nèi)核進(jìn)行交互。使用Netlink套接字,用戶空間程序可以向內(nèi)核發(fā)送請(qǐng)求,以獲取系統(tǒng)信息、控制系統(tǒng)行為或獲取有關(guān)網(wǎng)絡(luò)接口、路由表、套接字等的信息。
Netlink套接字提供了一種可擴(kuò)展的、可靠的、異步的機(jī)制,用于在內(nèi)核和用戶空間之間傳輸大量的網(wǎng)絡(luò)相關(guān)信息。它還支持多播,可以同時(shí)向多個(gè)進(jìn)程發(fā)送消息。
Netlink套接字有多個(gè)協(xié)議族,每個(gè)協(xié)議族都有不同的目的和用途。其中最常見的是NETLINK_ROUTE協(xié)議族,它用于與網(wǎng)絡(luò)配置和路由相關(guān)的操作。
1.2 代碼示例
下面是使用Netlink套接字從Linux內(nèi)核的路由表中檢索默認(rèn)網(wǎng)關(guān)IP地址的代碼例程
代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <unistd.h>
#define BUFSIZE 8192
struct nlreq {
struct nlmsghdr hdr;
struct rtmsg msg;
};
int main(void)
{
int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sockfd == -1) {
perror("socket error");
exit(1);
}
struct sockaddr_nl sa;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
perror("bind error");
exit(1);
}
struct nlreq req;
memset(&req, 0, sizeof(req));
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.hdr.nlmsg_type = RTM_GETROUTE;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.msg.rtm_family = AF_INET;
req.msg.rtm_table = RT_TABLE_MAIN;
req.msg.rtm_protocol = RTPROT_UNSPEC;
req.msg.rtm_scope = RT_SCOPE_UNIVERSE;
req.msg.rtm_type = RTN_UNICAST;
struct iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = &req;
iov.iov_len = req.hdr.nlmsg_len;
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char buf[BUFSIZE];
memset(buf, 0, sizeof(buf));
struct nlmsghdr *hdr;
int len;
if (sendmsg(sockfd, &msg, 0) == -1) {
perror("sendmsg error");
exit(1);
}
while ((len = recv(sockfd, buf, sizeof(buf), 0)) > 0) {
for (hdr = (struct nlmsghdr *) buf; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
if (hdr->nlmsg_type == NLMSG_DONE) {
goto finish;
}
if (hdr->nlmsg_type == NLMSG_ERROR) {
perror("NLMSG_ERROR");
exit(1);
}
struct rtmsg *rt = (struct rtmsg *) NLMSG_DATA(hdr);
if (rt->rtm_family != AF_INET || rt->rtm_table != RT_TABLE_MAIN || rt->rtm_type != RTN_UNICAST) {
continue;
}
struct rtattr *attr;
int attrlen;
for (attr = (struct rtattr *) RTM_RTA(rt), attrlen = RTM_PAYLOAD(hdr); RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
if (attr->rta_type == RTA_GATEWAY) {
char gw_addr[INET_ADDRSTRLEN];
struct in_addr addr;
memcpy(&addr, RTA_DATA(attr), sizeof(addr));
if (inet_ntop(AF_INET, &addr, gw_addr, sizeof(gw_addr)) == NULL) {
perror("inet_ntop error");
continue;
}
printf("Gateway address: %s\n", gw_addr);
goto finish;
}
}
}
}
finish:
close(sockfd);
return 0;
}
首先,它使用socket()函數(shù)創(chuàng)建一個(gè)Netlink套接字,并使用bind()將其綁定到本地地址。然后,它使用nlreq結(jié)構(gòu)體變量初始化Netlink消息的參數(shù)。消息請(qǐng)求使用RTM_GETROUTE類型檢索路由表,并指定要檢索的路由的地址族、表、協(xié)議、范圍和類型。消息還指定了NLM_F_DUMP標(biāo)志以檢索所有可用路由。
消息使用sendmsg()發(fā)送到內(nèi)核,并使用recv()接收。接收到的數(shù)據(jù)通過一個(gè)循環(huán)解析緩沖區(qū)中的Netlink消息。該循環(huán)跳過與IPv4單播路由和主表無關(guān)的消息。對(duì)于每個(gè)相關(guān)消息,它使用另一個(gè)循環(huán)迭代路由屬性,并檢索網(wǎng)關(guān)地址(如果存在)。網(wǎng)關(guān)地址使用printf()打印到控制臺(tái),并使用goto語句退出循環(huán)。
1.3 代碼詳解介紹
(1)使用socket函數(shù)創(chuàng)建一個(gè)Netlink套接字
int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sockfd == -1) {
perror("socket error");
exit(1);
}
/* Protocol families. */
#define PF_NETLINK 16
/* Address families. */
#define AF_NETLINK PF_NETLINK
/* Types of sockets. */
enum __socket_type
{
SOCK_RAW = 3, /* Raw protocol interface. */
#define SOCK_RAW SOCK_RAW
};
#define NETLINK_ROUTE 0 /* Routing/device hook */
NETLINK_ROUTE:用于與網(wǎng)絡(luò)配置和路由相關(guān)的操作,如獲取和修改網(wǎng)絡(luò)接口、路由表和ARP緩存等。
(2)綁定套接字到本地地址
struct sockaddr_nl sa;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
perror("bind error");
exit(1);
}
綁定套接字到本地地址。這是為了確保接收到內(nèi)核發(fā)送的Netlink消息。
(3)創(chuàng)建一個(gè)Netlink消息
struct nlreq req;
memset(&req, 0, sizeof(req));
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.hdr.nlmsg_type = RTM_GETROUTE;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.msg.rtm_family = AF_INET;
req.msg.rtm_table = RT_TABLE_MAIN;
req.msg.rtm_protocol = RTPROT_UNSPEC;
req.msg.rtm_scope = RT_SCOPE_UNIVERSE;
req.msg.rtm_type = RTN_UNICAST;
創(chuàng)建一個(gè)Netlink消息,用于向內(nèi)核請(qǐng)求路由表。我們?cè)O(shè)置消息頭部的長度、類型和標(biāo)志,以及rtmsg結(jié)構(gòu)體的成員。這里我們只請(qǐng)求主路由表中的單播路由表項(xiàng),以獲取默認(rèn)網(wǎng)關(guān)的地址。
(4)將req結(jié)構(gòu)體打包成一個(gè)iovec結(jié)構(gòu)體
struct iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = &req;
iov.iov_len = req.hdr.nlmsg_len;
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
將req結(jié)構(gòu)體打包成一個(gè)iovec結(jié)構(gòu)體,用于在消息中發(fā)送。我們還創(chuàng)建了一個(gè)msghdr結(jié)構(gòu)體,指定發(fā)送和接收消息的參數(shù)。
(5)使用sendmsg函數(shù)將消息發(fā)送到內(nèi)核
if (sendmsg(sockfd, &msg, 0) == -1) {
perror("sendmsg error");
exit(1);
}
(6)使用recv函數(shù)從內(nèi)核接收消息
char buf[BUFSIZE];
memset(buf, 0, sizeof(buf));
struct nlmsghdr *hdr;
int len;
if (sendmsg(sockfd, &msg, 0) == -1) {
perror("sendmsg error");
exit(1);
}
while ((len = recv(sockfd, buf, sizeof(buf), 0)) > 0) {
for (hdr = (struct nlmsghdr *) buf; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
if (hdr->nlmsg_type == NLMSG_DONE) {
goto finish;
}
if (hdr->nlmsg_type == NLMSG_ERROR) {
perror("NLMSG_ERROR");
exit(1);
}
// 解析路由表項(xiàng)
}
}
使用recv函數(shù)從內(nèi)核接收消息,并使用NLMSG_OK、NLMSG_NEXT和NLMSG_DATA宏來循環(huán)遍歷消息中的所有路由表項(xiàng)。
(7)獲取默認(rèn)網(wǎng)關(guān)的地址
struct rtmsg *rt = (struct rtmsg *) NLMSG_DATA(hdr);
if (rt->rtm_family != AF_INET || rt->rtm_table != RT_TABLE_MAIN || rt->rtm_type != RTN_UNICAST) {
continue;
}
struct rtattr *attr;
int attrlen;
for (attr = (struct rtattr *) RTM_RTA(rt), attrlen = RTM_PAYLOAD(hdr); RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
if (attr->rta_type == RTA_GATEWAY) {
char gw_addr[INET_ADDRSTRLEN];
struct in_addr addr;
memcpy(&addr, RTA_DATA(attr), sizeof(addr));
if (inet_ntop(AF_INET, &addr, gw_addr, sizeof(gw_addr)) == NULL) {
perror("inet_ntop error");
continue;
}
printf("Gateway address: %s\n", gw_addr);
goto finish;
}
}
finish:
close(sockfd);
return 0;
}
/* Routing message attributes */
enum rtattr_type_t {
RTA_GATEWAY,
};
檢查每個(gè)路由表項(xiàng)的類型和成員,以確定是否找到了默認(rèn)網(wǎng)關(guān)的地址。如果找到了,使用inet_ntop函數(shù)將地址轉(zhuǎn)換為可讀形式,并打印它。
二、使用Netlink套接字實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)事件
2.1 簡介
可以使用Netlink套接字實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)事件。Netlink是一種基于套接字的接口,用于與Linux內(nèi)核通信。內(nèi)核中的各個(gè)子系統(tǒng)都使用Netlink與用戶空間應(yīng)用程序通信,包括網(wǎng)絡(luò)子系統(tǒng)。
網(wǎng)絡(luò)子系統(tǒng)使用Netlink在某些網(wǎng)絡(luò)事件發(fā)生時(shí)向用戶空間應(yīng)用程序發(fā)送消息,例如添加或刪除網(wǎng)絡(luò)接口、更改網(wǎng)絡(luò)路由和更改網(wǎng)絡(luò)地址。這些消息可以被用戶空間應(yīng)用程序用于實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)事件。
要使用Netlink實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)事件,您需要?jiǎng)?chuàng)建一個(gè)Netlink套接字,將其綁定到特定的Netlink協(xié)議,然后使用recv函數(shù)從內(nèi)核接收Netlink消息。然后,您可以解析消息以提取有關(guān)已發(fā)生的網(wǎng)絡(luò)事件的信息。
2.2 示例代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#define BUFFER_SIZE 4096
int main(int argc, char *argv[]) {
int netlinkSocket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (netlinkSocket < 0) {
perror("創(chuàng)建Netlink套接字失敗");
exit(EXIT_FAILURE);
}
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
if (bind(netlinkSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("將Netlink套接字綁定到地址失敗");
close(netlinkSocket);
exit(EXIT_FAILURE);
}
char buffer[BUFFER_SIZE];
struct iovec iov = { buffer, sizeof(buffer) };
struct msghdr msg = { (void *)&addr, sizeof(addr), &iov, 1, NULL, 0, 0 };
while (1) {
ssize_t len = recvmsg(netlinkSocket, &msg, 0);
if (len < 0) {
perror("接收Netlink消息失敗");
close(netlinkSocket);
exit(EXIT_FAILURE);
}
struct nlmsghdr *nlh;
for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
switch(nlh->nlmsg_type) {
case RTM_NEWLINK:
printf("檢測(cè)到新鏈接\n");
break;
case RTM_DELLINK:
printf("鏈接已刪除\n");
break;
case RTM_NEWADDR:
printf("檢測(cè)到新地址\n");
break;
case RTM_DELADDR:
printf("地址已刪除\n");
break;
default:
printf("未知的消息類型 (%d)\n", nlh->nlmsg_type);
break;
}
}
}
close(netlinkSocket);
return 0;
}
在一個(gè)終端上編譯程序并運(yùn)行。
在另一個(gè)終端上輸入以下命令來更改網(wǎng)絡(luò)接口的狀態(tài):
sudo ifconfig eth0 down
sudo ifconfig eth0 up
這只是一個(gè)簡單的示例,可以使用Netlink監(jiān)控許多其他類型的網(wǎng)絡(luò)事件。文章來源:http://www.zghlxwxcb.cn/news/detail-733408.html
rtnetlink.h頭文件提供了所有可能的Netlink消息類型的列表,可以用于監(jiān)控其他類型的網(wǎng)絡(luò)事件。
如下所示:文章來源地址http://www.zghlxwxcb.cn/news/detail-733408.html
/****
* Routing/neighbour discovery messages.
****/
/* Types of messages */
enum {
RTM_BASE = 16,
#define RTM_BASE RTM_BASE
RTM_NEWLINK = 16,
#define RTM_NEWLINK RTM_NEWLINK
RTM_DELLINK,
#define RTM_DELLINK RTM_DELLINK
RTM_GETLINK,
#define RTM_GETLINK RTM_GETLINK
RTM_SETLINK,
#define RTM_SETLINK RTM_SETLINK
RTM_NEWADDR = 20,
#define RTM_NEWADDR RTM_NEWADDR
RTM_DELADDR,
#define RTM_DELADDR RTM_DELADDR
RTM_GETADDR,
#define RTM_GETADDR RTM_GETADDR
RTM_NEWROUTE = 24,
#define RTM_NEWROUTE RTM_NEWROUTE
RTM_DELROUTE,
#define RTM_DELROUTE RTM_DELROUTE
RTM_GETROUTE,
#define RTM_GETROUTE RTM_GETROUTE
RTM_NEWNEIGH = 28,
#define RTM_NEWNEIGH RTM_NEWNEIGH
RTM_DELNEIGH,
#define RTM_DELNEIGH RTM_DELNEIGH
RTM_GETNEIGH,
#define RTM_GETNEIGH RTM_GETNEIGH
RTM_NEWRULE = 32,
#define RTM_NEWRULE RTM_NEWRULE
RTM_DELRULE,
#define RTM_DELRULE RTM_DELRULE
RTM_GETRULE,
#define RTM_GETRULE RTM_GETRULE
RTM_NEWQDISC = 36,
#define RTM_NEWQDISC RTM_NEWQDISC
RTM_DELQDISC,
#define RTM_DELQDISC RTM_DELQDISC
RTM_GETQDISC,
#define RTM_GETQDISC RTM_GETQDISC
RTM_NEWTCLASS = 40,
#define RTM_NEWTCLASS RTM_NEWTCLASS
RTM_DELTCLASS,
#define RTM_DELTCLASS RTM_DELTCLASS
RTM_GETTCLASS,
#define RTM_GETTCLASS RTM_GETTCLASS
RTM_NEWTFILTER = 44,
#define RTM_NEWTFILTER RTM_NEWTFILTER
RTM_DELTFILTER,
#define RTM_DELTFILTER RTM_DELTFILTER
RTM_GETTFILTER,
#define RTM_GETTFILTER RTM_GETTFILTER
RTM_NEWACTION = 48,
#define RTM_NEWACTION RTM_NEWACTION
RTM_DELACTION,
#define RTM_DELACTION RTM_DELACTION
RTM_GETACTION,
#define RTM_GETACTION RTM_GETACTION
RTM_NEWPREFIX = 52,
#define RTM_NEWPREFIX RTM_NEWPREFIX
RTM_GETMULTICAST = 58,
#define RTM_GETMULTICAST RTM_GETMULTICAST
RTM_GETANYCAST = 62,
#define RTM_GETANYCAST RTM_GETANYCAST
RTM_NEWNEIGHTBL = 64,
#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL
RTM_GETNEIGHTBL = 66,
#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL
RTM_SETNEIGHTBL,
#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL
RTM_NEWNDUSEROPT = 68,
#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
RTM_NEWADDRLABEL = 72,
#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
RTM_DELADDRLABEL,
#define RTM_DELADDRLABEL RTM_DELADDRLABEL
RTM_GETADDRLABEL,
#define RTM_GETADDRLABEL RTM_GETADDRLABEL
RTM_GETDCB = 78,
#define RTM_GETDCB RTM_GETDCB
RTM_SETDCB,
#define RTM_SETDCB RTM_SETDCB
RTM_NEWNETCONF = 80,
#define RTM_NEWNETCONF RTM_NEWNETCONF
RTM_GETNETCONF = 82,
#define RTM_GETNETCONF RTM_GETNETCONF
RTM_NEWMDB = 84,
#define RTM_NEWMDB RTM_NEWMDB
RTM_DELMDB = 85,
#define RTM_DELMDB RTM_DELMDB
RTM_GETMDB = 86,
#define RTM_GETMDB RTM_GETMDB
RTM_NEWNSID = 88,
#define RTM_NEWNSID RTM_NEWNSID
RTM_DELNSID = 89,
#define RTM_DELNSID RTM_DELNSID
RTM_GETNSID = 90,
#define RTM_GETNSID RTM_GETNSID
RTM_NEWSTATS = 92,
#define RTM_NEWSTATS RTM_NEWSTATS
RTM_GETSTATS = 94,
#define RTM_GETSTATS RTM_GETSTATS
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
到了這里,關(guān)于Linux c語言獲取本機(jī)網(wǎng)關(guān) ip 地址的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!