免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 28879 | 回复: 15
打印 上一主题 下一主题

[C] netlink socket 编程之app:在用户空间枚举系统网络设备列表 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-23 14:49 |只看该作者 |倒序浏览
本帖最后由 duanjigang 于 2017-03-11 22:27 编辑

看了点net_device 部分的东西,还有netlink编程的文档和代码,搞了一个枚举并查看linux系统网络设备的程序。
内核模块与用户程序通过netlink socket进行通讯。
目前的功能:
(1):查看net_device列表
插入内核模块后,调用用户态程序发送命令进行查看,比如
  1. ./sender all
复制代码

能返回所有net_device 的名称列表
(2):查看任意一个net_device的状态信息。
比如
  1. ./sender all
复制代码

返回

  1. eth0 eth1
复制代码

然后就可以使用

  1. ./sender eth1
复制代码

查看eth1的状态
说明:
内核部分代码并非原创,参考修改网上的代码实现的。
未来的设想:可以类似于iptables一样,给这个程序加入set命令,目前相当于只有get功能,获取信息,加入set后就能修改网络设备的信息了。
目前的ifconfig命令是通过ioctl来实现的,感兴趣的朋友可以尝试用netlink 进行通讯,实现ifconfig.===


==============内核模块的代码=========================

//net_link.c by duanjigang

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/if.h>
#include <linux/timer.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>

#define NETLINK_GENERIC 18
#define BUF_SIZE 1024
static struct sock *netlink_exam_sock;
static unsigned char szBuff[BUF_SIZE];
static int exit_flag = 0;
static DECLARE_COMPLETION(exit_completion);
static void get_dev_info(int type, const char* name);
static void recv_handler(struct sock * sk, int length)
{
    wake_up(sk->sk_sleep);
}

static int process_message_thread(void * pData)
{
    struct sk_buff * skb = NULL;
    struct nlmsghdr * nlhdr = NULL;
    int len;
    char data[1024] = {0};
    DEFINE_WAIT(wait);
   
    daemonize("cme_netlink");
   
    while (exit_flag == 0)
    {
        prepare_to_wait(netlink_exam_sock->sk_sleep, &wait, TASK_INTERRUPTIBLE);
        schedule();
        finish_wait(netlink_exam_sock->sk_sleep, &wait);
        
        while ((skb = skb_dequeue(&netlink_exam_sock->sk_receive_queue)) != NULL)
        {
            u32 pid;
            nlhdr = (struct nlmsghdr *)skb->data;
            if (nlhdr->nlmsg_len < sizeof(struct nlmsghdr))
            {
                printk("Corrupt netlink message.\n");
                continue;
            }
            
            len = nlhdr->nlmsg_len - NLMSG_LENGTH(0);
            
            if (len > 1024)
            {
                printk("netlink buffer is overflow.\n");
                memcpy(data, NLMSG_DATA(nlhdr), 1024);
            }
            else
            {
                memcpy(data, NLMSG_DATA(nlhdr), len);
            }
            printk(data);   
            if(strncmp(data, "all", 3) == 0)
            {
                get_dev_info(0, NULL);
            }
            else
            {
                get_dev_info(1, data);
            }
            pid = nlhdr->nlmsg_pid;
            nlhdr->nlmsg_pid = 0;
            nlhdr->nlmsg_flags = 0;
            NETLINK_CB(skb).pid = 0;
            NETLINK_CB(skb).dst_pid = pid;
            NETLINK_CB(skb).dst_groups = 0;
            memset(nlhdr, 0, NLMSG_SPACE(nlhdr->nlmsg_len));
            strcpy(NLMSG_DATA(nlhdr), szBuff);
            netlink_unicast(netlink_exam_sock, skb, pid, MSG_DONTWAIT);
        }
    }
    complete(&exit_completion);
    return 0;
}

static void get_dev_info(int ntype, const char* name)
{
    int n = 1;
    struct net_device * dev = dev_base;
    struct net_device_stats * stats = NULL;
    char type[32];
    struct in_device *ind = NULL;
    struct in_ifaddr *ina = NULL;
    memset(szBuff, 0, 1024);
    read_lock(&dev_base_lock);
    while (dev)
    {
        char szdata[1024] ={0};
        if (dev->type == ARPHRD_ETHER)
        {
            sprintf(type, "Ethernet");
        }
        else if (dev->type == ARPHRD_LOOPBACK)
        {
            sprintf(type, "Local Loopback");
        }
        else
        {
            sprintf(type, "%d", dev->type);
        }
        stats = dev->get_stats(dev);
        sprintf(szdata, "%s\t", dev->name);
        //printk("%s\n", dev->name);

        if(ntype != 0)
        {
            if(strncmp(dev->name, name, strlen(name)) != 0)
            {
                dev = dev->next;
                continue;
            }
        }else
        {
            memcpy(szBuff + strlen(szBuff), szdata, strlen(szdata));
            dev = dev->next;
            continue;   
        }
        sprintf(szdata + strlen(szdata), "Link encap:%s  ", type);
        sprintf(szdata + strlen(szdata), "HWaddr %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
            dev->dev_addr[0], dev->dev_addr[1],
            dev->dev_addr[2], dev->dev_addr[3],
            dev->dev_addr[4], dev->dev_addr[5]);
        ind = in_dev_get(dev);
        if (ind)
        {
            ina = (struct in_ifaddr *)ind->ifa_list;
            if (ina)
            {
                sprintf(szdata + strlen(szdata),
                "\tinet addr:%u.%u.%u.%u Bcast:%u.%u.%u.%u Mask:%u.%u.%u.%u\n",
                    NIPQUAD(ina->ifa_address),
                    NIPQUAD(ina->ifa_broadcast),
                    NIPQUAD(ina->ifa_mask));
            }
        }
        sprintf(szdata + strlen(szdata), "\t");
        if (dev->flags & IFF_UP) sprintf(szdata + strlen(szdata), "UP ");
        if (dev->flags & IFF_BROADCAST) sprintf(szdata + strlen(szdata), "BROADCAST ");
        if (dev->flags & IFF_RUNNING) sprintf(szdata + strlen(szdata), "RUNNING ");
        if (dev->flags & IFF_LOOPBACK) sprintf(szdata + strlen(szdata), "LOOPBACK ");
        if (dev->flags & IFF_MULTICAST) sprintf(szdata + strlen(szdata), "MULTICAST ");
        sprintf(szdata + strlen(szdata), "MTU:%d\n", dev->mtu);
        sprintf(szdata + strlen(szdata), "\t");
        sprintf(szdata + strlen(szdata),
        "RX packets:%lu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
            stats->rx_packets, stats->rx_errors, stats->rx_dropped,
            stats->rx_over_errors, stats->rx_frame_errors);
        sprintf(szdata + strlen(szdata), "\t");
        sprintf(szdata + strlen(szdata),
        "TX packets:%lu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
            stats->tx_packets, stats->tx_errors, stats->tx_dropped,
            stats->tx_aborted_errors, stats->tx_carrier_errors);
        sprintf(szdata + strlen(szdata), "\t");
        sprintf(szdata + strlen(szdata), "RX Bytes:%lu  TX Bytes:%lu\n", stats->rx_bytes, stats->tx_bytes);
        sprintf(szdata + strlen(szdata), "\t");
        sprintf(szdata + strlen(szdata), "Interrupt:%d Base address:0x%lx\n", dev->irq, dev->base_addr);
        sprintf(szdata + strlen(szdata), "\n");
        n++;
        dev = dev->next;
        //printk("%d: %s\n", n, szdata);

        memcpy(szBuff + strlen(szBuff), szdata, strlen(szdata));
    }
    read_unlock(&dev_base_lock);
    //printk("\n==\n%d:%s==\n",n, szBuff);

}

int init_module()
{
    netlink_exam_sock = netlink_kernel_create(NETLINK_GENERIC, recv_handler);
    if (!netlink_exam_sock)
    {
        printk("Fail to create netlink socket.\n");
        return 1;
    }
    kernel_thread(process_message_thread, NULL, CLONE_KERNEL);
    return 0;
}
void cleanup_module( )
{
    exit_flag = 1;
    wake_up(netlink_exam_sock->sk_sleep);
    wait_for_completion(&exit_completion);
    sock_release(netlink_exam_sock->sk_socket);
}
MODULE_LICENSE("GPL");   
MODULE_AUTHOR("duanjigang");  



==========用户态程序===================

//sender.c by cme

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#define NETLINK_GENERIC 18
#define MAX_PAYLOAD 1024
struct sockaddr_nl src_addr, dst_addr;
struct nlmsghdr *nlh = NULL;
struct msghdr msg;
struct iovec iov;
int sock_fd;
int main(int argc, char **argv)
{
    int i = 0;
    if(argc < 2)
       {
        printf("usage: %s <all>/<nic name>\n", argv[0]);
        exit(0);
    }
   
        sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
        memset(&dst_addr, 0, sizeof(dst_addr));
        dst_addr.nl_family = AF_NETLINK;
        dst_addr.nl_pid = 0;
        dst_addr.nl_groups = 0; // no multicast

        nlh = (struct nlhmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));

        for(i = 1; i < argc; i++)
    {
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
            nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
            nlh->nlmsg_pid = getpid();
            nlh->nlmsg_flags = 0;
            strcpy(NLMSG_DATA(nlh), argv[i);
            iov.iov_base = (void *)nlh;
            iov.iov_len = nlh->nlmsg_len;
            msg.msg_name = (void *)&dst_addr;
            msg.msg_namelen = sizeof(dst_addr);
            msg.msg_iov = &iov;
            msg.msg_iovlen = 1;
            sendmsg(sock_fd, &msg, 0);
            memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
            recvmsg(sock_fd, &msg, 0);
            printf("Received message:\n%s\n", NLMSG_DATA(nlh));
        usleep(1000);
        }
    close(sock_fd);
        return (1);
}



===================makefile如下===================:

MODULE_NAME :=net_link
obj-m   :=$(MODULE_NAME).o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)
all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD)
    gcc -o sender sender.c
clean:
    rm -fr *.ko *.o *.cmd sender $(MODULE_NAME).mod.c





[ 本帖最后由 duanjigang 于 2008-9-23 14:53 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2008-09-23 14:55 |只看该作者
<<netlink socket编程之why & how >>在此:
http://linux.chinaunix.net/bbs/thread-1031932-1-2.html
可以参考下

论坛徽章:
0
3 [报告]
发表于 2008-09-23 14:57 |只看该作者
lz写的东西还真多啊,又收藏一个

论坛徽章:
0
4 [报告]
发表于 2008-09-23 15:01 |只看该作者
原帖由 5毛党党员 于 2008-9-23 14:57 发表
lz写的东西还真多啊,又收藏一个


最近好好表现争取转正,陈年老醋都搬出来

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
5 [报告]
发表于 2008-10-05 17:29 |只看该作者
好东西,支持版主

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
6 [报告]
发表于 2008-12-02 21:33 |只看该作者
这个东西写得好,学习了

论坛徽章:
0
7 [报告]
发表于 2008-12-03 01:15 |只看该作者
请教一下,为什么不直接把发送给userspace的动作放在recv_handler()中,而要单独起一个内核线程process_message_thread来做net_device信息的获取和发送?
这是基于什么考虑呢?谢谢!

  1. int init_module()
  2. {
  3.     netlink_exam_sock = netlink_kernel_create(NETLINK_GENERIC, recv_handler);
  4.     if (!netlink_exam_sock)
  5.     {
  6.         printk("Fail to create netlink socket.\n");
  7.         return 1;
  8.     }
  9.     kernel_thread(process_message_thread, NULL, CLONE_KERNEL);
  10.     return 0;
  11. }
复制代码

论坛徽章:
0
8 [报告]
发表于 2008-12-03 09:51 |只看该作者
原帖由 new_learner 于 2008-12-3 01:15 发表
请教一下,为什么不直接把发送给userspace的动作放在recv_handler()中,而要单独起一个内核线程process_message_thread来做net_device信息的获取和发送?
这是基于什么考虑呢?谢谢!

int init_module()
{ ...

呵呵,第一篇文章中做了说明:
http://linux.chinaunix.net/bbs/thread-1031932-1-2.html
.............
回调函数input()是在发送进程的系统调用sendmsg()的上下文被调用的。如果input函数中处理消息很快的话,一切都没有问题。但是如果处理netlink消息花费很长时间的话,我们则希望把消息的处理部分放在input()函数的外面,因为长时间的消息处理过程可能会阻止其它系统调用进入内核。取而代之,我们可以牺牲一个内核线程来完成后续的无限的的处理动作。...............

论坛徽章:
0
9 [报告]
发表于 2008-12-03 09:58 |只看该作者
原帖由 duanjigang 于 2008-12-3 09:51 发表

呵呵,第一篇文章中做了说明:
http://linux.chinaunix.net/bbs/thread-1031932-1-2.html

谢谢回复。
我对回调函数的理解一直不是很清楚,因为我在内核板块里看到了独孤九贱的帖子http://linux.chinaunix.net/bbs/thread-822500-1-1.html
那篇帖子开头出提到:
netlink 套接字的最大特点是对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函数。

这里却说用户指定的接收回调函数是在软中断中调用的。
你怎么看呢?

这个问题我已经发到了内核板块,http://linux.chinaunix.net/bbs/thread-1049425-1-1.html
谢谢!

论坛徽章:
0
10 [报告]
发表于 2008-12-03 11:03 |只看该作者
学习了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP