Linux c语言获取本机网关 ip 地址

这篇具有很好参考价值的文章主要介绍了Linux c语言获取本机网关 ip 地址。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

这篇文章写了获取本机的ip地址和子网掩码:Linux c语言获取本机 ip、子网掩码

一、获取本机网关 ip 地址

1.1 Netlink套接字简介

关于Netlink套接字请参考:Linux 网络之netlink 简介

Linux Netlink套接字是一种用于在Linux内核和用户空间之间进行通信的机制。它是Linux内核中的一种通信协议,用于让用户空间程序与内核进行交互。使用Netlink套接字,用户空间程序可以向内核发送请求,以获取系统信息、控制系统行为或获取有关网络接口、路由表、套接字等的信息。

Netlink套接字提供了一种可扩展的、可靠的、异步的机制,用于在内核和用户空间之间传输大量的网络相关信息。它还支持多播,可以同时向多个进程发送消息。

Netlink套接字有多个协议族,每个协议族都有不同的目的和用途。其中最常见的是NETLINK_ROUTE协议族,它用于与网络配置和路由相关的操作。

1.2 代码示例

下面是使用Netlink套接字从Linux内核的路由表中检索默认网关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()函数创建一个Netlink套接字,并使用bind()将其绑定到本地地址。然后,它使用nlreq结构体变量初始化Netlink消息的参数。消息请求使用RTM_GETROUTE类型检索路由表,并指定要检索的路由的地址族、表、协议、范围和类型。消息还指定了NLM_F_DUMP标志以检索所有可用路由。

消息使用sendmsg()发送到内核,并使用recv()接收。接收到的数据通过一个循环解析缓冲区中的Netlink消息。该循环跳过与IPv4单播路由和主表无关的消息。对于每个相关消息,它使用另一个循环迭代路由属性,并检索网关地址(如果存在)。网关地址使用printf()打印到控制台,并使用goto语句退出循环。

1.3 代码详解介绍

(1)使用socket函数创建一个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:用于与网络配置和路由相关的操作,如获取和修改网络接口、路由表和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);
}

绑定套接字到本地地址。这是为了确保接收到内核发送的Netlink消息。

(3)创建一个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;

创建一个Netlink消息,用于向内核请求路由表。我们设置消息头部的长度、类型和标志,以及rtmsg结构体的成员。这里我们只请求主路由表中的单播路由表项,以获取默认网关的地址。

(4)将req结构体打包成一个iovec结构体

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结构体打包成一个iovec结构体,用于在消息中发送。我们还创建了一个msghdr结构体,指定发送和接收消息的参数。

(5)使用sendmsg函数将消息发送到内核

if (sendmsg(sockfd, &msg, 0) == -1) {
    perror("sendmsg error");
    exit(1);
}

(6)使用recv函数从内核接收消息

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);
        }
        // 解析路由表项
    }
}

使用recv函数从内核接收消息,并使用NLMSG_OK、NLMSG_NEXT和NLMSG_DATA宏来循环遍历消息中的所有路由表项。

(7)获取默认网关的地址

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,
};

检查每个路由表项的类型和成员,以确定是否找到了默认网关的地址。如果找到了,使用inet_ntop函数将地址转换为可读形式,并打印它。

二、使用Netlink套接字实时监控网络事件

2.1 简介

可以使用Netlink套接字实时监控网络事件。Netlink是一种基于套接字的接口,用于与Linux内核通信。内核中的各个子系统都使用Netlink与用户空间应用程序通信,包括网络子系统。

网络子系统使用Netlink在某些网络事件发生时向用户空间应用程序发送消息,例如添加或删除网络接口、更改网络路由和更改网络地址。这些消息可以被用户空间应用程序用于实时监控网络事件。

要使用Netlink实时监控网络事件,您需要创建一个Netlink套接字,将其绑定到特定的Netlink协议,然后使用recv函数从内核接收Netlink消息。然后,您可以解析消息以提取有关已发生的网络事件的信息。

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("创建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("检测到新链接\n");
                    break;
                case RTM_DELLINK:
                    printf("链接已删除\n");
                    break;
                case RTM_NEWADDR:
                    printf("检测到新地址\n");
                    break;
                case RTM_DELADDR:
                    printf("地址已删除\n");
                    break;
                default:
                    printf("未知的消息类型 (%d)\n", nlh->nlmsg_type);
                    break;
            }
        }
    }

    close(netlinkSocket);
    return 0;
}

在一个终端上编译程序并运行。

在另一个终端上输入以下命令来更改网络接口的状态:

sudo ifconfig eth0 down
sudo ifconfig eth0 up

这只是一个简单的示例,可以使用Netlink监控许多其他类型的网络事件。

rtnetlink.h头文件提供了所有可能的Netlink消息类型的列表,可以用于监控其他类型的网络事件。
如下所示:文章来源地址https://www.toymoban.com/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)
};

到了这里,关于Linux c语言获取本机网关 ip 地址的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue获取本机ip地址

     mounted() {       this.getUserIP((ip)={          console.log(\\\'ip=\\\')          console.log(ip)       })     },    methods: {  getUserIP (onNewIP) {         //获取不到可能是因为chrome浏览器版本过高,需要修改浏览器配置如下         //在chrome地址栏输入:chrome://flags/#enable-webrtc-hide-local-ips-

    2023年04月09日
    浏览(27)
  • JavaScript 如何获取本机IP地址

    获取本机IP地址是前端工程师经常需要处理的问题。JavaScript 有几种方法可以获取客户端的IP地址。下面是三种获取本机IP的方法。 方法1:使用第三方 API 一种获取客户端IP地址的最简单方法是使用第三方API。可以使用一些免费API,例如ipify.org等来获取IP地址。下面的代码片段展

    2024年02月04日
    浏览(41)
  • Qt 获取本机IP地址及Mac地址

    首先需要在pro文件中添加network模块 添加头文件 以下是获取IP地址代码 获取MAC地址 测试应用输出

    2024年02月09日
    浏览(34)
  • 如何设置本地连接ip 本机固定IP地址设置方法

    本地IP怎么设置?很多配置路由器的时候有时候找不到192.168.1.1就是因为本地固定ip地址有问题,这个时候就需要我们设置本地固定ip地址了,近日又有不少网友向编辑问这个问题,今天yii666编辑专门针对本地连接ip设置做详细介绍,方便大家查找。 其实需要设置本地IP一般是在

    2024年02月05日
    浏览(30)
  • Vue.js 如何获取本机 IP 地址

    在 Vue.js 中,要获取本机 IP 地址,你可以使用 JavaScript 来实现。JavaScript 提供了一种获取客户端 IP 地址的方法,可以通过浏览器的 API 来获取。下面是一个示例代码,演示了如何在 Vue.js 中获取本机 IP 地址。 首先,你需要在 Vue 组件中创建一个方法,需要在 Vue 组件中创建一个

    2024年02月04日
    浏览(41)
  • Qt 获取本机 ip地址方法 获取客户端ip和端口的方法

    上述函数返回本机所有IPv4的ip地址列表,比如192.168.1.10|192.166.1.95 其它方法=》 获取客户端IP地址:

    2024年02月14日
    浏览(30)
  • Python获取本机IP地址的三种方式

    目录 1、使用专用网址 2、使用自带socket库 3、使用第三方netifaces库 获取的是 公网IP 。 网址:http://myip.ipip.net 代码: 具体可以类似这样:  比较喜欢用这个,在命令窗口也能使用: 还可以用这个: https://api.ipify.org 获取的是 局域网IP 。 但是上面这个获取的 不是 公网IP,结果

    2024年02月04日
    浏览(33)
  • 配置Vite获取内网IP(Vue3项目​ts版本获取本机局域网IP地址)

    参考文章:vue项目获取本机局域网IP地址(vue.config.js版本) 在Vite中,没有vue.config.js文件,而是使用vite.config.js(或vite.config.ts,如果项目使用TypeScript)来配置项目; 1.获取 IP 需要借助  os  模块,需要先安装依赖: 2.其次在vite.config.ts中引用模块  3.接着,添加一个获取本机内

    2024年02月05日
    浏览(30)
  • 【Linux系统无法连接网络,修改IP地址和网关,ping解决主机不可达的情况】

    Ubuntu出现无法连接网络,ping公网IP地址显示主机不可达,打开FireFox浏览器打不开网页的情况,还有在使用sudo apt-get install gcc 下载某个工具的时候出现无法解析当前域名的情况,其实都是归终于网络无法连接,我尝试过很多办法,在教程上面看到的修改Network Manager.state文件的

    2024年02月08日
    浏览(44)
  • 网络:IP地址、子网掩码、网络地址、广播地址、网段、网关

    目录 一、IP地址 二、子网掩码 三、网络地址 四、广播地址 五、网段 六、网关 七、IP地址、子网掩码、网络地址、广指地址、网殷、网关的关系 参考链接  一、IP地址 IP地址是因特网协议(IP)中使用的一种数字标识符,用于唯一地标识网络中的设备。每台连接到因特网的设

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包