Netlink实现异步检测网线插拔并分配IP

Netlink实现异步检测网线插拔并分配IP

今天遇到开发板无法在插拔时通过dhcp来获取ip的问题,同时用户有实时性的要求因此也不能一直轮询检测插拔状态。因此就会打算用C语言和脚本来实现异步检测功能。

C代码

C代码后台运行负责检测插拔并执行脚本

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <unistd.h>

#define IFF_LOWER_UP                    1<<16/* volatile */
#define MAX_BUFFER_SIZE 4096

int conn_flag0 = 0;
int conn_flag1 = 0;

int main() {
    int nlSock;
    struct sockaddr_nl srcAddr;
    struct nlmsghdr *nlMsg;
    char buffer[MAX_BUFFER_SIZE];

    // 创建 Netlink Socket
    nlSock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (nlSock < 0) {
        perror("Failed to create Netlink socket");
        return -1;
    }

    memset(&srcAddr, 0, sizeof(srcAddr));
    srcAddr.nl_family = AF_NETLINK;
    srcAddr.nl_groups = RTMGRP_LINK | RTMGRP_NOTIFY;

    // 绑定 Socket
    if (bind(nlSock, (struct sockaddr *)&srcAddr, sizeof(srcAddr)) < 0) {
        perror("Failed to bind Netlink socket");
        close(nlSock);
        return -1;
    }

    // 设置非阻塞模式
    int flags = fcntl(nlSock, F_GETFL, 0);
    fcntl(nlSock, F_SETFL, flags | O_NONBLOCK);

    while (1) {
        fd_set readSet;
        FD_ZERO(&readSet);
        FD_SET(nlSock, &readSet);

        // 设置超时时间为 1 秒
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        // 监视 Netlink Socket 的可读状态
        int result = select(nlSock + 1, &readSet, NULL, NULL, &timeout);
        if (result == -1) {
            perror("Failed to select");
            close(nlSock);
            return -1;
        } else if (result == 0) {
            // 超时,没有新数据到达
            // printf("Timeout\n");
            continue;
        }

        if (FD_ISSET(nlSock, &readSet)) {
            // 接收消息
            int bytesRead = recv(nlSock, buffer, MAX_BUFFER_SIZE, 0);
            if (bytesRead < 0) {
                perror("Failed to receive message from Netlink socket");
                close(nlSock);
                return -1;
            }

            // 遍历接收到的消息,获取到Netlink的消息头。
            nlMsg = (struct nlmsghdr *)buffer;
            while (NLMSG_OK(nlMsg, bytesRead)) {
                if (nlMsg->nlmsg_type == NLMSG_DONE) {
                    break;
                }

                if (nlMsg->nlmsg_type == RTM_NEWLINK || nlMsg->nlmsg_type == RTM_DELLINK) {
                    //NLMSG_DATA()获取完整的ifinfomsg结构体的指针
                    //根据消息头nlMsg获取到其后面的数据部分ifinfomsg结构体的指针
                    struct ifinfomsg *ifInfo = (struct ifinfomsg *)NLMSG_DATA(nlMsg);
                    if (ifInfo->ifi_family == AF_UNSPEC) {
                        //获取路由信息的属性,以及消息负载的长度,通过这两个参数来判断本次的数据是否有效
                        struct rtattr *attribute = IFLA_RTA(ifInfo);
                        int attributeLen = IFLA_PAYLOAD(nlMsg);

                        while (RTA_OK(attribute, attributeLen)) {
                            //如果这个本次数据的属性是接口属性(IFLA_IFNAME)则开始接收
                            if (attribute->rta_type == IFLA_IFNAME) {
                                //RTA_DATA()获取数据内容的指针
                                char *ifaceName = (char *)RTA_DATA(attribute);
                                printf("Interface: %s \n", ifaceName);
                                /*如果接口处于新链接(RTM_NEWLINK)且是启动状态(IFF_LOWER_UP)则是插上动作
                                  由此可以看出插拔有网络链接和接口启动两个动作来实现
                                */
                                if (nlMsg->nlmsg_type == RTM_NEWLINK && (ifInfo->ifi_flags & IFF_LOWER_UP)) {
                                    // printf("Interface plugged-in\n");
                                    if (strcmp(ifaceName, "eth0") == 0) {1
                                        if(!conn_flag0){
                                            conn_flag0 = 1;
                                            system("/bin/sh ./autoAssignIp.sh 0");
                                            printf("eth0 Interface plugged-in\n");
                                        }
                                    }else if(strcmp(ifaceName, "eth1") == 0){
                                        if(!conn_flag1){
                                            conn_flag1 = 1;
                                            system("/bin/sh ./autoAssignIp.sh 1");
                                            printf("eth1 Interface plugged-in\n");
                                        }
                                    }else{
                                        printf("other netplan Interface plugged-in\n");
                                    }

                                } else if (nlMsg->nlmsg_type == RTM_DELLINK) {
                                    if (strcmp(ifaceName, "eth0") == 0) {
                                        if(conn_flag0){
                                            conn_flag0 = 0;
                                            system("/bin/sh ./autoAssignIp.sh 0");
                                        }
                                    }else if(strcmp(ifaceName, "eth1") == 0){
                                        if(conn_flag1){
                                            conn_flag1 = 0;
                                            system("/bin/sh ./autoAssignIp.sh 1");
                                        }
                                    }else{
                                        printf("Not plugged with Eth\n");
                                    }

                                    // printf("Interface unplugged\n");
                                }

                            }
                            attribute = RTA_NEXT(attribute, attributeLen);
                        }
                    }
                }
                nlMsg = NLMSG_NEXT(nlMsg, bytesRead);
            }
        }
    }

    close(nlSock);
    return 0;
}

脚本代码

脚本代码负责发送dhcp获得ip

#!/bin/bash

check_ip() {
    local interface="eth$1"

    if ! ip link show "$interface" >/dev/null 2>&1; then
        echo "Interface $interface does not exist!"
        exit 1
    fi

    if ip link show "$interface" | grep -q "state UP"; then
        echo "Interface $interface is connected"

            ip link set dev "$interface" down
            ip link set dev "$interface" up
            udhcpc -i "$interface"
            ifconfig "$interface" down
            ifconfig "$interface" up
            echo "IP has been assigned"

    else
        echo "Interface $interface does not exist"
    fi
}

    check_ip $1

总结:

通过解决本题,我了解了nlmsghdrifinfomsgrtattr 是 Linux 内核中用于处理 Netlink 消息的三个相关结构体,它们之间也存在关联关系。

  1. nlmsghdr(Netlink Message Header)是 Netlink 消息的消息头结构体,定义在 <linux/netlink.h> 头文件中。它包含了消息的通用信息,比如消息长度、类型等。在 struct nlmsghdr 中有一个成员变量 nlmsg_type,用于标识消息的类型,可以表示不同的消息类型,如路由消息、接口消息等。
  2. ifinfomsg(Interface Information Message)是用于表示网络接口信息的结构体,定义在 <linux/if.h> 头文件中。它是 Netlink 消息中一种特定类型的有效负载(payload)。ifinfomsg 结构体包含了关于网络接口的各种信息,例如接口索引、接口状态等。
  3. rtattr(Route Attribute)是用于封装可选路由信息的结构体,定义在 <linux/rtnetlink.h> 头文件中。它也是 Netlink 消息中的一种特定类型的有效负载(payload)。rtattr 结构体用于封装和传递不同类型的路由信息,以满足网络协议栈的需求。它有 rta_typerta_len 两个字段,用于表示属性的类型和长度,后面紧跟着属性的值。
  4. 在 Netlink 消息中,nlmsghdr 结构体用于表示消息头,它包含所有消息的通用信息。消息的具体有效负载(payload)部分可以是不同类型的结构体,如 ifinfomsgrtattr。这些结构体可以根据消息的类型进行解析和处理,从而获取特定类型的信息。

综上所述,nlmsghdr 是 Netlink 消息的消息头,ifinfomsgrtattr 是不同类型消息的有效负载(payload),它们之间存在关联关系。在处理 Netlink 消息时,可以使用 nlmsghdr 的字段来确定消息的类型,再根据类型选择相应的结构体类型(如 ifinfomsgrtattr),以便解析和处理消息中的具体信息。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值