netlink 与 socket


  
  

    
    
netlink对socket函数的实现(v2.6.18)
应用层调用:
skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2);
/net/socket.c:
中有一个全局变量,它存储协议族:
static struct net_proto_family *net_families[NPROTO];
 struct net_proto_family {
         int             family;
         int             (*create)(struct socket *sock, int protocol);  (注1)         
         short           authentication;
         short           encryption;
         short           encrypt_net;
         struct module   *owner;
 };




系统调用:
asmlinkage long sys_socket(int family, int type, int protocol)
继续调用:
int sock_create(int family, int type, int protocol, struct socket **res)
继续调用:
static int __sock_create(int family, int type, int protocol, struct socket **res, int kern)
其中有一句:
if ((err = net_families[family]->create(sock, protocol)) < 0) {
               sock->ops = NULL;
               goto out_module_put;
}
说明它是调用协议族规定的create回调函数(见注1)
那么,我们需要知道netlink协议的协议族( net_proto_family)是什么时候进入全局变量net_families里的,继续向下看,发现有一个函数
int sock_register(struct net_proto_family *ops)
{
int err;
if (ops->family >= NPROTO) {
printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family, NPROTO);
return -ENOBUFS;
}
net_family_write_lock();
err = -EEXIST;
if (net_families[ops->family] == NULL) {        (注2)
net_families[ops->family]=ops;
err = 0;
}
net_family_write_unlock();
printk(KERN_INFO "NET: Registered protocol family %d\n",
ops->family);
return err;
}
系统是在这个函数中给全局变量net_families赋值的(见注2)。那netlink一定有调用 sock_register函数,搜源代码。。。。
发现在/net/netlink/af_netlink.c中有一个函数static int __init netlink_proto_init(void)。
此函数中调用了注册协议族的函数:
 sock_register(&netlink_family_ops);
而netlink_family_ops是netlink的一个全局变量:
static struct net_proto_family netlink_family_ops = {
       .family = PF_NETLINK,
       .create = netlink_create,(注3)
       .owner  = THIS_MODULE,  /* for consistency 8) */
};
由此可见,socket最终是调用netlink模块的netlink_create回调来创建netlink的“特殊”socket的(见注3)


struct socket {
       socket_state            state;
       unsigned long           flags;
       const struct proto_ops  *ops;
       struct fasync_struct    *fasync_list;
       struct file             *file;
       struct sock             *sk;
       wait_queue_head_t       wait;
       short                   type;
};


struct netlink_sock {
       /* struct sock has to be the first member of netlink_sock */
   struct sock             sk;
       u32                     pid;
       u32                     dst_pid;
       u32                     dst_group;
       u32                     flags;
       u32                     subscriptions;
       u32                     ngroups;
       unsigned long           *groups;
       unsigned long           state;
       wait_queue_head_t       wait;
       struct netlink_callback *cb;
       spinlock_t              cb_lock;
       void                    (*data_ready)(struct sock *sk, int bytes);
       struct module           *module;
};




由以上两个结构可见 netlink_socket的第一个元素就是系统标准的socket,它是对系统标准socket的一个封装,这样也方便由netlink_sock强转成标准socket,保持的兼容性








再往后,netlink _create函数会调用内部函数 __netlink_create;
static int __netlink_create(struct socket *sock, int protocol)
 {
         struct sock *sk;
         struct netlink_sock *nlk;
 
         sock->ops = &netlink_ops;
 
         sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1);
         if (!sk)
                 return -ENOMEM;
 
         sock_init_data(sock, sk);
 
         nlk = nlk_sk(sk);
         spin_lock_init(&nlk->cb_lock);
         init_waitqueue_head(&nlk->wait);
 
         sk->sk_destruct = netlink_sock_destruct;
         sk->sk_protocol = protocol;
         return 0;
 }
 此函数有一句 sock->ops = &netlink_ops; 实际上netlink_ops是netlink模块的一个全局常量,它表示着socket所关联的一系列操作
 
static const struct proto_ops netlink_ops = {
        .family =       PF_NETLINK,
        .owner =        THIS_MODULE,
        .release =      netlink_release,
        .bind =         netlink_bind,
        .connect =      netlink_connect,
        .socketpair =   sock_no_socketpair,
        .accept =       sock_no_accept,
        .getname =      netlink_getname,
        .poll =         datagram_poll,
        .ioctl =        sock_no_ioctl,
        .listen =       sock_no_listen,
        .shutdown =     sock_no_shutdown,
        .setsockopt =   netlink_setsockopt,
        .getsockopt =   netlink_getsockopt,
        .sendmsg =      netlink_sendmsg,
        .recvmsg =      netlink_recvmsg,
        .mmap =         sock_no_mmap,
        .sendpage =     sock_no_sendpage,
};




二、关于bind
 
应用层调用 bind(skfd, (struct sockaddr*)&local, sizeof(local));
系统调用:/net/socket.c
asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
{
        struct socket *sock;
        char address[MAX_SOCK_ADDR];
        int err, fput_needed;




        if((sock = sockfd_lookup_light(fd, &err, &fput_needed))!=NULL)     //找到fd对应的的socket
        {
                if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) {
                        err = security_socket_bind(sock, (struct sockaddr *)address, addrlen);
                        if (!err)
                                err = sock->ops->bind(sock,(struct sockaddr *)address, addrlen);  //将address 和socket绑定
                }
                fput_light(sock->file, fput_needed);
        }                       
        return err;
}




所以  err = sock->ops->bind(sock,(struct sockaddr *)address, addrlen); 实质上是调用netlink的bind函数。此时标准的addr被转成netlink的地址格式
并通过 netlink_update_subscriptions(sk, nlk->subscriptions + hweight32(nladdr->nl_groups) - hweight32(nlk->groups[0]));来将相关信息组织起来(详情以后分析)




总结
其它函数的实现与此类似也是最终转到了netlink模块进行调用。






一、imp2源码 整个源码包含三个文件:imp2_k.c, imp2_u.c和imp2.h. 其中imp2_k.c为内核模块的源代码,imp2_u.c为应用程序,即测试代码,imp2.h为两个源文件都需要引用的头文件。其整体的功能是:注册一种新的netlink协议,并注册一个新的NF hook函数。当有ping包发往当前主机或者经过当前主机转发时,内核向用户发送ping包的源IP和目的IP。各个文件的简单分析见下文。 1. imp2.h 该文件主要是定义了一种新的Netlink协议类型NL_IMP2(31)。新的协议类型的选值不能和当前内核中已经定义的netlink协议类型重复。定义了基于该协议类型的消息类型,内核根据接收到消息的不同类型,进行不同的处理:IMP2_U_PID和IMP2_CLOSE分别为请求和关闭。IMP2_K_MSG代表内核空间发送的消息。 该头文件的源码如下: 2. imp2_k.c 该程序为内核模块程序。其完成的功能如下: (1)创建一种新的Netlink协议NL_IMP2,并注册该协议的回调函数kernel_receive。但用户空间通过建立且协议类型为NL_IMP2的socket套接字并调用sendto,sendmsg函数发送数据时,传送到内核空间的数据由kernel_receive进行处理。该函数主要是记录用户进程的ID,用于随后发送数据的时候指定目的。 (2)在Netfilter的hook点NF_IP_PRE_ROUTING注册hook函数get_icmp,对经过该hook点的ping包进行处理。get_icmp首先判断是否是ping包,如果不是,直接Accept。如果是,则记录该包的源IP和目的IP,然后调用send_to_user,将记录的信息发送给kernel_recieve函数中记录的用户进程ID。 该文件的源码如下: 3. imp2_u.c 该程序为用户空间的测试程序。该程序包括以下功能: (1)生成NL_IMP2协议的socket.然后通过调用sendto发送IMP2_U_PID类型的请求信息给内核。然后等待接受内核发回的信息。记住:仅当有ping包经过内核的NF时,内核才会向用户进程发送信息。 (2)当用户进程通过Ctrl+C来结束该程序时,调用信号处理函数sig_int,向内核发送IMP2_CLOSE的消息,结束socket。 该文件的源码如下: 二、编译和测试 1. 整个源文件编译的Makefile如下: all: gcc -O2 -DMODULE -D__KERNEL__ -W -Wstrict-prototypes -Wmissing-prototypes -isystem /lib/modules/`uname -r`/build/include -c -o imp2_k.o imp2_k.c gcc imp2_u.c -o imp2_u install: insmod imp2_k.o uninstall: rmmod imp2_k clean: rm -f imp2_k.o imp2_u 2. 加载内核模块,并执行测试程序。 #make install #./imp2_u 当没有ping包时,终端一直处于等待输出状态。通过另一台主机(192.168.1.100)向当前主机(192.168.1.101)发送ping包,则终端已经有输出: #./imp2_u [root@localhost imp2]# ./imp2_u src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值