<span style="font-size:14px;">kaodv-mod.c,该文件是运行aodv的内核模块,开始的文件,通过从这里入手,了解aodv的整个工作方式和实现机制
kaodv-mod.c文件的两个先头函数就是module_init(kaodv_init)和module_exit(kaodv_exit),这两个函数就是执行insmod kaodv.ko和rmmod kaodv时执行的函数
函数kaodv_init:首先初始化expl(路由条目,用于存储路由),该函数kaodv_expl_init做了这些操作。主要有create_proc_read_entry(),
struct proc_dir_entry *create_proc_read_entry (const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data);</span>
说明:
name : 要创建的文件名;mode : 文件掩码,为 0 则按照系统默认的掩码创建文件。
base : 指定该文件所在的目录,如果为 NULL,则文件被创建在 /proc 根目录下。
read_proc : 实现该文件的 read_proc 函数。也就是说,当我们读取 "name" 这个文件时(如 cat /proc/myproc_name) ,读取请求会通过这个函数发送到驱动模块,然后在函数里处理的数据会写到 myproc_name 文件中。
data : 内核忽略此参数,但会把它当作参数传递给 read_proc 这个自定义函数。
init_net.proc_net应该是指目录/proc/net/
aodv中利用这个函数在/proc/net/kaodv_expl的创建了路由条目信息,只要使用cat /proc/net/kaodv_expl就是以看到相关的路由信息
expl_len记录了路由条目的数量,一个全局变量
如果路由条目是有期限的,那么这个函数也初始化路由条目定时器,init_timer(&expl_timer)
接着执行kaodv_queue_init()初始化缓存队列:
首先是对缓存队列长度初始化为0,然后在/proc/net/*下创建proc文件kaodv_queue,当我们执行cat /proc/net/kaodv_queue时,会输出队列长度和队列的最大长度信息,以上的信息都没有出错的情况下
进入netlink初始化,实现内核与用户空间通信的关键
kaodv_netlink_init():
netlink_register_notifier(&kaodv_nl_notifier);登记kaodv的netlink套接口的通知
static struct notifier_block kaodv_nl_notifier = {
.notifier_call = kaodv_netlink_rcv_nl_event,
};
以上是一个事件通知链,
通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。组成内核的核心系统代码均位于kernel目录下,通知链表位于kernel/notifier.c中,对应的头文件为include/linux/notifier.h。关于事件通知链,可以参考http://www.linuxidc.com/Linux/2013-07/86999.htm这里是netlink的通知链注册(目前还不懂怎么回事,应该是notifier.c中的事件通知链类似),netlink的通知链貌似是在notifier.c中的事件通知链的基础上进一步封装,构成自己特色的。参考http://blog.chinaunix.net/uid-23069658-id-4360706.html
对于netlink通知链,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,关于通知链的使用可以仔细阅读http://blog.chinaunix.net/uid-23069658-id-4360706.html这里的一系列文章
kaodv_netlink_rcv_nl_event处理netlink的通知链事件,如果事件是NETLINK_URELEASE,协议是NETLINK_AODV,进程号不是内核的,该函数会把路由表清除掉,缓存队列清除掉.
netlink_kernel_create(&init_net, NETLINK_AODV, AODVGRP_MAX, kaodv_netlink_rcv_skb, NULL, THIS_MODULE);创建与用户空间通信的套接字struct sock
struct sock *netlink_kernel_create(struct net *net,
int unit,unsigned int groups,
void (*input)(struct sk_buff *skb),
struct mutex *cb_mutex,
struct module *module);
其中net参数是网络设备命名空间指针,input函数是netlink socket在接受到消息时调用的回调函数指针,module默认为THIS_MODULE.
在初始化了路由表,缓存队列,同时创建了与用户空间通信的套接字后,接下来就是注册钩子函数,用于路由使用
nf_register_hook(&kaodv_ops[]),struct nf_hook_ops结构主要用于处理NF_INET_PRE_ROUTING,NF_INET_LOCAL_OUT和NF_INET_POST_ROUTING等钩子的地方,具体可参考http://www.cnblogs.com/liushaodong/archive/2013/02/26/2933593.html
5个链:
NF_IP_PRE_ROUTING:数据包进入路由表之前
NF_IP_LOCAL_IN:通过路由表后目的地为本机
NF_IP_FORWARD:通过路由表后,目的地不为本机
NF_IP_LOCAL_OUT:由本机产生,向外转发
NF_IP_POST_ROUTING:发送到网卡接口之前。
4个表:
filter,nat,mangle,raw,默认表是filter(没有指定表的时候就是filter表)。
filter:一般的过滤功能
nat: 用于nat功能(端口映射,地址映射等)
mangle: 用于对特定数据包的修改
raw:优先级最高,设置raw时一般是为了不再让iptables做数据包的链接跟踪处理,提高性能
无论那个链,都由kaodv_hook函数处理。kaodv_hook,首先判断ip header是否为空,如果是空的,则直接交给上层处理,否则获取udp header,判断是否为aodv控制信息,如果是的话,还判断信号质量问题,如果质量不好,直接扔弃,否则更新路由表信息,然后上传上层。如果不是aodv控制信息(RREQ,RREP,RERR等),则获取输入设备(输出设备)的接口地址和广播地址信息。如果是广播地址,多播地址,直接上传,return NF_ACCEPT,
接下来处理三个链的数据packet
NF_INET_PRE_ROUTING:如果是网关节点,协议是自定义的IPPROTO_MIPE,目的节点是本节点,则需要解包,如果节点不是网关节点,packet是本地产生的或者是发送给本节点的,则直接上传。如果不是到本节点的packet,则查询路由表,是否有到目的节点的路由,如果没有到目的节点的路由,则向用户空间程序发送kaodv_netlink_send_rerr_msg(PKT_INBOUND, iph->saddr, iph->daddr, in->ifindex);然后扔掉当前这个packet.如果找到了到目的节点的路由条目,首先判断flags,是否需要修复,如果需要的话,则向用户空间发送repair信息,kaodv_netlink_send_rt_msg(KAODVM_REPAIR, iph->saddr, iph->daddr);如果需要修复这个路由条目的话,当前的数据packet先进入数据缓存队列kaodv_queue_enqueue_packet(skb, okfn);(okfn是是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程,执行okfn指向的函数).这里其返回NF_STOLEN,其意思就是由这个数据packet已经被钩子函数处理过了,上面不用管,不要继续传送这个packet,意思就是说,这个packet会放在缓存队列中,由往后的一些程序处理,不再上传其他层了。由用户空间发送netlink信息,如果KAODV_QUEUE_SEND发送,通过函数kaodv_queue_set_verdict(KAODV_QUEUE_SEND, m->dst);
(这一部分应该要改,如果我们使用地理信息进行转发,必须要计算下一跳节点,那一个适合转发,所以这里应该要缓存队列)
如果该路由不需要修复,则直接交给上层(可能转发,因为在这里判断了到目的节点的路由是否存在)
NF_INET_LOCAL_OUT:本地报文流出路由前执行,如果不存在到目的节点的路由,或者路由需要修复的情况下,首先判断数据缓存队列中有没有关于该目的节点的数据packet,如果有的话,表示已经发送RREQ进行路由请求了,如果没有,则向用户空间通信,发起RREQ路由请求,同时该缓存数据packet进入缓存队列中。如果路由条目有KAODV_RT_GW_ENCAP,使能网关聚合(分片功能吗?还是说每个输出的数据packet都要修改ip header,加入min_ipedr_enc),然后执行ip_route_me_harder(skb, RTN_LOCAL),重新路由一次。
NF_INET_POST_ROUTING://路由后的报文流出前执行,这里只是更新一下路由条目的信息
三个钩子函数理解完后,如果注册钩子函数失败,进行对应的处理。
剩下的就是处理设备信息,dev_get_by_name(&inti_net, ifname[i])获取设备信息
最后,创建/proc/net/kaodv,用于读取kaodv的相关信息,包括信号质量阈值,pkts dropped ,最近的一次qual值,以及网关模式。