netlink socket 编程之app:在用户空间枚举系统网络设备列表
本帖最后由 duanjigang 于 2017-03-11 22:27 编辑看了点net_device 部分的东西,还有netlink编程的文档和代码,搞了一个枚举并查看linux系统网络设备的程序。
内核模块与用户程序通过netlink socket进行通讯。
目前的功能:
(1):查看net_device列表
插入内核模块后,调用用户态程序发送命令进行查看,比如
./sender all
能返回所有net_device 的名称列表
(2):查看任意一个net_device的状态信息。
比如
./sender all
返回
eth0 eth1
然后就可以使用
./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:%luTX 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
:em17: :em17: :em17:
[ 本帖最后由 duanjigang 于 2008-9-23 14:53 编辑 ] <<netlink socket编程之why & how >>在此:
http://linux.chinaunix.net/bbs/thread-1031932-1-2.html
可以参考下:mrgreen: lz写的东西还真多啊,又收藏一个 原帖由 5毛党党员 于 2008-9-23 14:57 发表 http://bbs.chinaunix.net/images/common/back.gif
lz写的东西还真多啊,又收藏一个
:mrgreen: :mrgreen:
最近好好表现争取转正,陈年老醋都搬出来:P 好东西,支持版主 这个东西写得好,学习了:em53: :em53: 请教一下,为什么不直接把发送给userspace的动作放在recv_handler()中,而要单独起一个内核线程process_message_thread来做net_device信息的获取和发送?
这是基于什么考虑呢?谢谢!
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;
}
原帖由 new_learner 于 2008-12-3 01:15 发表 http://bbs.chinaunix.net/images/common/back.gif
请教一下,为什么不直接把发送给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()函数的外面,因为长时间的消息处理过程可能会阻止其它系统调用进入内核。取而代之,我们可以牺牲一个内核线程来完成后续的无限的的处理动作。...............
原帖由 duanjigang 于 2008-12-3 09:51 发表 http://bbs.chinaunix.net/images/common/back.gif
呵呵,第一篇文章中做了说明:
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
谢谢! 学习了