免费注册 查看新帖 |

Chinaunix

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

vxworks(2.0.2)版本中对于arp的处理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-10-29 19:48 |只看该作者 |倒序浏览
相关概念虽然是vxworks2.0.2版本中的,但是与老土的BSD代码基本一样,事实上,最新的ip协议栈的代码上虽然加上不少新鲜的功能,但是其主体也依旧一样.ifnet也就是协议栈中的接口的概念,跟arp相关处理的最重要的三个成员是:if_ioctl 用于接口上的ioctl命令;if_resolve 用于进行地址解析的函数;if_output 用于在接口上发送数据包;在ipAttach时,这三个值都进行了初始化:    pIfp->if_ioctl  = (FUNCPTR) ipIoctl;    pIfp->if_output = ipOutput;    pIfp->if_resolve = muxAddrResFuncGet(mib2Tbl.ifType, 0x800);其中if_resolve的值,实际上就是arpresolve函数.
in_ifaddrin_ifaddr是ifaddr的一种特殊形式,即ipv4版本的的ifaddr.当我们给接口配置ip地址时,实际上要生成一个in_ifaddr结构体,并与ifnet相关联.那么它与arp最相关的内容实际上是在ifaddr结构体中,它们是:ifa_rtrequest 这是一个处理arp相关的函数,在后面我们就会解释到它的用处.ia_ifp 与地址相关联的接口.sockaddr_dl数据链路层地址,它的作用就是保存MAC地址,其中与ARP处理相关的内容包括:sdl_len 长度,如果为0,表示mac信息无效,否则就是有效.这点很重要sdl_data 如果有效,保存有mac信息.llinfo_arp它就是arp控制结构,整个系统中的llinfo_arp通过一个双向链表连接起来,链表头就是全局变量llinfo_arp.(C语言中,总是喜欢将全局变量定义成结构体的名字).其中现在我们关心的内容包括la_rt 指向相关的rtentry,关于rtentry,后面马上就要讲到了.la_hold 持有的数据,在arp处理中会使用到,现在只知道它是要通过接口发送的数据包;la_asked 计数,用于统计在接收到arp回应前,发出了多少arp请求.rtentry路由表项,每一条路由都由一个rtentry表示,与arp相关的内容包括rt_ifp 与路由相关联的接口;rt_ifa 与路由相关的接口地址;rt_genmask 用于clone路由时使用;rt_llinfo 指向arp控制结构rt_gateway 表示下一跳信息,可能保存mac地址.rt_expire arp超时处理使用,如果为0,表示永久有效(用于静态配置的mac).routeroute数据结构主要用于路由处理,它包括两个成员:ro_rt 路由引用的rtentryro_dst 目的地址数据的发送过程ip_outputip协议栈发送数据总是以intip_output(m0, opt, ro, flags, imo)struct mbuf *m0;struct mbuf *opt;struct route *ro;int flags;struct ip_moptions *imo;函数调用开发的,对于其中一些特殊情况的处理我们就不会加以描述,我们只对普通情况说明.m0表示要发送的数据,而ro就是发送的路由.ip_output在进行了一大堆的事情之后,就会调用(*ifp->if_output)(ifp, m,   (struct sockaddr *)dst, ro->ro_rt);发送数据,其中ifp就是根据路由或者什么的,找到的要outgoing接口.前面我们说过,ipAttach时,就已经指定了if_output为ipOutput函数:int ipOutput    (    register struct ifnet *ifp,    struct mbuf *m0,    struct sockaddr *dst,    struct rtentry *rt0    )上面的几个参数比较明显ifp为发送数据要使用的接口m0是要发送的数据;dst是目的地址;rt0是使用的路由;在ipOutput中与arp相关的最重要的一环,就是下面的switch-case语句:switch (dst->sa_family)        {        case AF_INET:            if (ifp->if_resolve != NULL)                if (!ifp->if_resolve(ac, rt, m, dst, edst))                    return (0);        /* if not yet resolved */            /* If broadcasting on a simplex interface, loopback a copy */            if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))                mcopy = m_copy(m, 0, (int)M_COPYALL);            off = m->m_pkthdr.len - m->m_len;            etype = ETHERTYPE_IP;            break;        case AF_UNSPEC:当dst->sa_family为AF_UNSPEC时,说明dst中保存着对方的MAC地址信息,会导致构造二层帧头,然后放到接口的发送队列中.而接口最终会把它发送出去.但是如果dst是一个ip地址(由AF_INET表示),则情况就不一样了.它首先调用ifp->if_resolve函数.if_resolve函数的作用就是进行ip地址到MAC地址的映射,如果if_resolve能够直接返回对应的mac地址,则调用返回之后,edst就保存着目的mac,随后就可以构造二层报头,入发送队列(跟AF_UNSPEC时的情况一样),否则就直接返回.
由于ipAttach时将if_resolve初始成arpresolve,所以我们还是看看arpresolve:intarpresolve(ac, rt, m, dst, desten)register struct arpcom *ac;register struct rtentry *rt;struct mbuf *m;register struct sockaddr *dst;register u_char *desten;前面说过,arpresolve的功能就是进行目的ip地址到mac地址的转换映射;目的地址由参数dst指定,如果arpresolve能够完成映射,则目的mac填写在desten中.这是由下面的代码段完成的sdl = SDL(rt->rt_gateway);if ((rt->rt_expire == 0 || rt->rt_expire > tickGet()) &&   sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {bcopy(LLADDR(sdl), (char *)desten, sdl->sdl_alen);return 1;}上面的意思是说,如果当前的arp信息有效,则直接返回mac信息.这个函数中,还处理一些NOARP的处理(对于NOARP的,自己搞定一个mac出来).if (la->la_hold)
    m_freem(la->la_hold);
la->la_hold = m;上面的这段代码说,将要发送的数据保存在la_hold中,从上面可以看出,arp只保存最后一次请求时的数据.同时也说明了,如果arp无效,则要发送的数据是保存在arp控制信息中的,在后面的分析中,我们可以看到,在处理arp回应时,这个被保存的数据,会被协议栈发送.再接着看代码:
if (rt->rt_expire) {
    rt->rt_flags &= ~RTF_REJECT;
    if (la->la_asked == 0 ||
       (tickGet () - rt->rt_expire >= arpRxmitTicks)) {
                rt->rt_expire = tickGet();
                if (la->la_asked++ sin_addr));
                else {
                    rt->rt_flags |= RTF_REJECT;
                    rt->rt_expire += (sysClkRateGet() * arpt_down);
                    la->la_asked = 0;
                }
        }
}上面代码有三个主要功能:清除RTF_REJECT标志,RTF_REJECT标志用于控制发送arp信息的频度,后面会有专门的讲解.如果当前发送的arp请求次数小于arp_maxtries(5次),则调用arpshowhas发送arp请求;否则,设置RTF_REJECT,以抑制ARP请求的发送,并将抑制时间定为20秒;
arpwhohas实际调用arprequest,以请求ARP信息:static voidarprequest(ac, sip, tip, enaddr)register struct arpcom *ac;register u_long *sip, *tip;register u_char *enaddr;arpwhohas首先构造一个报文,然后调用接口的发送函数:bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,   sizeof(eh->ether_dhost));sa.sa_family = AF_UNSPEC;sa.sa_len = sizeof(sa);(*ac->ac_if.if_output)(&ac->ac_if, m, &sa, (struct rtentry *)0);我们可以看到目的mac在这写成了广播地址(全1),而sa.sa_family设置为AF_UNSPEC,这与我们前面描述的ipOutput是一致的(由前面的描述我们知道,这里调用的函数if_output实际上就是ipOutput).我们也知道,这次调用由于给的目的地址参数sa_family为AF_UNSPEC,所以会导致直接把要发送的数据放到接口发送队列,而不会再调用if_resolve造成死循环.
arp的输入处理现在,该看看另外一个分支了,arp输入处理,arp的输入由arpintr驱动,它调用in_arpinput以处理跟ip相碰的arp报文:
la = arplookup(isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, 0);if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) {if (sdl->sdl_alen &&   bcmp((caddr_t)ea->arp_sha, LLADDR(sdl), sdl->sdl_alen))logMsg("arp info overwritten for %08x by %s\n",   (int) ntohl(isaddr.s_addr),    (int) ether_sprintf(ea->arp_sha),0,0,0,0);bcopy((caddr_t)ea->arp_sha, LLADDR(sdl),   sdl->sdl_alen = sizeof(ea->arp_sha));if (rt->rt_expire)rt->rt_expire = tickGet() + (sysClkRateGet() *     arpt_keep);rt->rt_flags &= ~RTF_REJECT;la->la_asked = 0;if (la->la_hold) {(*ac->ac_if.if_output)(&ac->ac_if, la->la_hold,rt_key(rt), rt);la->la_hold = 0;}}首先,in_arpinput会调用arplookup,以检查发送者是不是在我们当前的arp缓存中(如果我们在前面发送了arp请求,那么就应该存在一个无效的arp项,如果是对方主请求,那么说明对方想与我们通讯,也需要检查对方在不在的).arplookup传入的第二个参数,表示在不存在arp项的时候,是不是要建立.那么什么时候建立呢?由于arp请求是广播发送的,所以我们可能接收到请求的目的ip不是我们自己的ip地址,在这种情况下,只需要更新arp信息(时标,以防止老化),只有请求是针对我自己的情况下(因为对方可能要与我通讯了,我马上就要使用对方的mac地址了),才会创建新的arp项.
如果arplookup查找出来一个有效的arp项,说明arp要被覆盖了,这也是我们看到"arp info overwritten for 0xXXXXXXXX by xxxx \n"
这条信息的原因.
arp下面的处理,就是复制目的mac,然后设置rt_expire,将老化时间设置为20分钟.设置la_asked为0;调用if_output发送la_hold.这与我们前面说过的,arp输入处理时,发送保存的数据是一致的.这一次由于系统中arp项的存在,会导致ipPutput调用arpresolve时返回1(有效mac),从而能够正常发送数据.arp的老化arp的老化由arptimer完成,arptimer每分钟运行一次,它处理全局链表llinfo_arp,将老化的arp设置为无效.
if (rt->rt_expire && rt->rt_expire    {   arptfree(la->la_prev); /* timer has expired; clear */   }我们看到老化操作是在arptfree完成的.if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&   sdl->sdl_family == AF_LINK) {sdl->sdl_alen = 0;la->la_asked = 0;rt->rt_flags &= ~RTF_REJECT;return;}rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),   0, (struct rtentry **)0);从上面的代码中可以看出,在rt_refcnt大于0的情况下,仅仅将mac信息标志为无效(sdl_len=0),否则调用rtrequest删除相应的arp表项.arp表项的建立从前面我们大概知道了整个arp表项,但是我们还没有知道arp在内存中到底是什么.其实我们所谓的arp,就是主机直接路由,它通过clone接口子网路由产生.所以这一次,我们从arp的前生今世开始讲.接口网络路由的建立当我们给接口增加/删除地址的时候,都会影响路由表,通过设置地址/掩码,也就确定一个可以直达的网络,如ifAddrAdd("mottsec2","10.0.0.8","10.0.255.255",0xffff0000).这个函数调用经过一系列的传递之后,会调用到in_control,然后调用到in_ifinit,它里面有这样一段代码:
if (ifp->if_ioctl &&   (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {splx(s);ia->ia_addr = oldaddr;return (error);}因为if_ioctl就是ipIoctl,所以来看看,它作了些什么事情:    switch (cmd){case SIOCSIFADDR:            for (pIa = in_ifaddr; pIa; pIa = pIa->ia_next)                if ((pIa->ia_ifp == (struct ifnet *)ifp) &&                    (pIa->ia_addr.sin_addr.s_addr == dt_saddr))                    break;            pIa->ia_ifa.ifa_rtrequest = arp_rtrequest;            pIa->ia_ifa.ifa_flags |= RTF_CLONING;   ifp->ac_ipaddr = IA_SIN (data)->sin_addr;            arpwhohas (ifp, &IA_SIN (data)->sin_addr);   break;其它的不管,其中对我们arp影响最大的两项,就是将ifa_rtrequest设置为arp_rtrequest,并置上了RTF_CLONING标志.incontrol最后会调用rtinit,并由rtinit调用rtrequest将接口网络路由加入到系统路由表中.clone路由的产生当调用rtalloc查找主机路由时,它实际调用的是rtalloc1,我们来看一下代码:if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) &&   ((rn->rn_flags & RNF_ROOT) == 0)) {        newrt = rt = (struct rtentry *)rn;        if (report && (rt->rt_flags & RTF_CLONING)) {            err = rtrequest(RTM_RESOLVE, dst, SA(0),                 SA(0), 0, &newrt);它首先在路由表中查找路由,如果找到了,并且具有clone标志RTF_CLONING,则需要调用rtrequest进行RTM_RESOLVE,这个过程就是clone过程,也就是建立主机直接路由的过程,也就是建立arp的过程.我们经常说过的arp实际上就是保存在路由表中,只不过是主机直接路由罢了.rtrequest在生成主机路由之后,执行下面的代码:
if (ifa->ifa_rtrequest)
    ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0));我们知道ifa_rtrequest函数就是arp_rtrequest函数,当以RTM_RESOLVE为参数调用时,它负责生成arp控制信息,并把它加入到全局的arp链表中,只是现在它的mac地址还是为空.arp的处理模块会使得它填写有效值.被动创建的arp项由前面我们知道,当对方请求本地接口地址的mac信息时,我们会创建相应的arp项,这其实是通过arplookup实现的,其实arplookup也是通过调用rtalloc1的.
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/104365/showart_2082392.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP