ARP 协议 代码 解释

Posted by 老沙
6月 10 2010

1. ARP高速缓存的数据结构

/* arp.h – SHA, SPA, THA, TPA */

/* 互联网地址解析协议  (see RFCs 826, 920)    */

#define    AR_HARDWARE    1    /* 以太网硬件类型代码    */

/* Definitions of codes used in operation field of ARP packet */
    
#define    AR_REQUEST    1    /* ARP请求解决地址 ARP request to resolve address    */
#define    AR_REPLY    2    /* 回答解决的请求 reply to a resolve request    */

#define    RA_REQUEST    3    /* 反向ARP请求 reverse ARP request (RARP packets)*/
#define    RA_REPLY    4    /* 答复反向请求 reply to a reverse request (RARP ")*/

struct    arp    {
    u_short    ar_hwtype;    /* 硬件类型    */
    u_short    ar_prtype;    /* 协议类型    */
    u_char    ar_hwlen;    /* 硬件地址的长度    */
    u_char    ar_prlen;    /* 协议地址的长度    */
    u_short    ar_op;        /* ARP协议运作(见上述列表)    */
    u_char    ar_addrs[1];    /* 发送者和目标硬件及原地址    */
/*    char    ar_sha[???];     – 发件人的物理硬件地址    */
/*    char    ar_spa[???];     – 发送者的协议地址(IP地址)    */
/*    char    ar_tha[???];     – 目标物理硬件地址    */
/*    char    ar_tpa[???];     – 目标的协议地址(IP)    */
};

#define    SHA(p)    (&p->ar_addrs[0])                /*计算源物理地址*/
#define    SPA(p)    (&p->ar_addrs[p->ar_hwlen])            /*计算源协议地址*/
#define    THA(p)    (&p->ar_addrs[p->ar_hwlen + p->ar_prlen])    /*计算目标物理地址*/
#define    TPA(p)    (&p->ar_addrs[(p->ar_hwlen*2) + p->ar_prlen])    /*计算目标协议地址*/

#define    MAXHWALEN    EP_ALEN    /* 以太网地址最大长度            */
#define    MAXPRALEN    IP_ALEN    /* IP 地址最大长度            */

#define ARP_HLEN    8    /* ARP协议头长度            */

#define    ARP_TSIZE    50    /*  ARP缓存大小            */
#define    ARP_QSIZE    10    /*  ARP协议端口队列的大小        */

/* 缓存超时 cache timeouts */

#define ARP_TIMEOUT    600        /* 10 minutes                */
#define    ARP_INF        0x7fffffff    /* “无限”超时值 "infinite" timeout value    */
#define    ARP_RESEND    1    /* 如果没有在1秒内回复,重新发送            */
#define    ARP_MAXRETRY    4    /* 多少秒后放弃?? give up after ~30 seconds    */

struct    arpentry {        /* ARP缓存中的条目格式    */
    short    ae_state;    /* 本条目状态(见下文)    */
    short    ae_hwtype;    /* 硬件类型            */
    short    ae_prtype;    /* 协议类型            */
    char    ae_hwlen;    /* 硬件地址的长度        */
    char    ae_prlen;    /* 协议地址的长度        */
    struct netif *ae_pni;    /* 指向接口结构体的指针    */
    int    ae_queue;    /* 队列的数据包地址    */
    int    ae_attempts;    /* 重试,到目前为止 number of retries so far    */
    int    ae_ttl;        /* 生存时间                */
    u_char    ae_hwa[MAXHWALEN];    /* 硬件地址         */
    u_char    ae_pra[MAXPRALEN];    /* 协议地址         */
};

#define    AS_FREE        0    /* 表项空闲    */
#define    AS_PENDING    1    /* 表项使用中,但未绑定        */
#define    AS_RESOLVED    2    /* 表项正确绑定         */

/* RARP变量 */

extern int    rarppid;    /* 进程ID为RARP等待答复    */
extern int    rarpsem;    /* RARP服务获取信号量    */

/*  ARP协议的变量 */

extern struct    arpentry    arptable[ARP_TSIZE];

/*  ARP协议函数声明 */

int         arp_in(struct netif *, struct ep *);
struct arpentry *arpfind(u_char *, u_short, struct netif *);
struct arpentry *arpadd(struct netif *, struct arp *);


2. 搜索ARP高速缓存

/* arpfind.c – arpfind */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 * arpfind – find an ARP entry given a protocol address and interface
 *————————————————————————
 */
struct arpentry *
arpfind(u_char *pra, u_short prtype, struct netif *pni)    /*参数:协议地址,协议类型,接口结构指针*/
{
    struct arpentry    *pae;                /*定义ARP缓存结构体指针*/
    int        i;

    for (i=0; i<ARP_TSIZE; ++i) {            /*遍历ARP的缓存*/
        pae = &arptable[i];            /*缓存数组*/
        if (pae->ae_state == AS_FREE)        /*缓存为空闲接找下一个*/
            continue;
        if (pae->ae_prtype == prtype &&    
            pae->ae_pni == pni &&
            BLKEQU(pae->ae_pra, pra, pae->ae_prlen))
            return pae;            /*如果缓存中的协议类型与参数相同,并且接口与协议地址全相同,则返回这个ARP条目的指针。BLKEQU的定义 #define    BLKEQU(b1, b2, len)    (!memcmp((b1), (b2), len))*/
    }
    return 0;
}
 


3. ARP请求广播分组

/* arpsend.c – arpsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 * arpsend – broadcast an ARP request
 *    N.B. Assumes interrupts disabled
 *————————————————————————
 */
int
arpsend(struct arpentry *pae)                    /*指向ARP表项的指针*/
{
    struct    netif    *pni = pae->ae_pni;            /*指向接口结构指针*/
    struct    ep    *pep;                    /*贞结构指针*/
    struct    arp    *parp;                    /*ARP分组指针*/
    int        arplen;

    pep = (struct ep *) getbuf(Net.netpool);        /*为分组分配一个缓存区*/
    if ((int)pep == SYSERR)
        return SYSERR;
    memcpy(pep->ep_dst, pni->ni_hwb.ha_addr, pae->ae_hwlen);/*目的地播为物理广播地址*/
    pep->ep_type = EPT_ARP;                    /*帧类型*/
    pep->ep_order = EPO_NET;                /*网络工作层*/
    
    /*填写ARP包*/
    parp = (struct arp *) pep->ep_data;            /*以太网帧中ARP的数据*/
    parp->ar_hwtype = hs2net(pae->ae_hwtype);        /*硬件类型*/        
    parp->ar_prtype = hs2net(pae->ae_prtype);        /*协议类型*/
    parp->ar_hwlen = pae->ae_hwlen;                /*硬件地址长度*/
    parp->ar_prlen = pae->ae_prlen;                /*协议地址长度*/
    parp->ar_op = hs2net(AR_REQUEST);            /*操作字段,请求*/
    memcpy(SHA(parp), pni->ni_hwa.ha_addr, pae->ae_hwlen);    /*发送方硬件地址*/
    memcpy(SPA(parp), &pni->ni_ip, pae->ae_prlen);        /*发送方协议地址*/
    memset(THA(parp), 0, pae->ae_hwlen);            /*目标硬件地址清空*/
    memcpy(TPA(parp), pae->ae_pra, pae->ae_prlen);        /*目标协议地址*/
    arplen = ARP_HLEN + 2*(parp->ar_hwlen + parp->ar_prlen);/*ARP的总长度*/
    write(pni->ni_dev, pep, EP_HLEN+arplen);        /*发送分组*/
    return OK;
}


4. ARP输出过程

/* netwrite.c – netwrite */

#include <conf.h>
#include <kernel.h>
#include <network.h>

#include <ospf.h>

struct    arpentry    *arpalloc();
struct    arpentry     *arpfind(u_char *, u_short, struct netif *);
int    arpsend(struct arpentry *);
int    local_out(struct ep *);        //用于将目的是本地机的数据包通过loopback 发送到指定本机端口

/*#define    DEBUG */

/*————————————————————————
 * netwrite – write a packet on an interface, using ARP if needed
 *————————————————————————
 */
int
netwrite(struct netif *pni, struct ep *pep, unsigned len)
{
    struct    arpentry     *pae;            //ARP项目指针
    STATWORD        ps;            //状态字

    if (pni->ni_state != NIS_UP) {            
        freebuf(pep);
        return SYSERR;
    }                        //接口未打开则退出
    pep->ep_len = len;                //帧长度
#ifdef    DEBUG                        
if (pni != &nif[NI_LOCAL])
{
struct ip *pip = (struct ip *)pep->ep_data;
    if (pip->ip_proto == IPT_OSPF) {
        struct ospf *po = (struct ospf *)pip->ip_data;
/*        if (po->ospf_type != T_HELLO) { */
{
            kprintf("netwrite(pep %X, len %d)n", pep, len);
            pdump(pep);
        }
    }
}
#endif    /* DEBUG */
    if (pni == &nif[NI_LOCAL])        //如果是本地接口,用local_out 发送
        return local_out(pep);
    else if (isbrc(pep->ep_nexthop)) {    /*否则,首先检查下一跳是否是广播地址*/
        memcpy(pep->ep_dst, pni->ni_hwb.ha_addr, EP_ALEN);//填写目的物理地址为广播物理地址
        write(pni->ni_dev, pep, len);    //发送
        return OK;
    }
    /* 否则,该协议地址查找… */

    disable(ps);    /*如果下一跳地址不是广播地址,则先在arp 缓存表中查找是否有绑定的物理地址,有则直接发送,没有则先做arp 地址绑定。*/
    pae = arpfind((u_char *)&pep->ep_nexthop, pep->ep_type, pni);
    if (pae && pae->ae_state == AS_RESOLVED) {
        memcpy(pep->ep_dst, pae->ae_hwa, pae->ae_hwlen);
        restore(ps);
        return write(pni->ni_dev, pep, len);
    }
    if (IP_CLASSD(pep->ep_nexthop)) {    /*下一跳地址为d 类地址则直接返回*/
        restore(ps);
        return SYSERR;
    }
    if (pae == 0) {                //没有绑定,做arp 解析
        pae = arpalloc();        //分配空间
        pae->ae_hwtype = AR_HARDWARE;    //硬件类型
        pae->ae_prtype = EPT_IP;    //协议类型
        pae->ae_hwlen = EP_ALEN;    //硬件地址长度
        pae->ae_prlen = IP_ALEN;    //协议地址长度
        pae->ae_pni = pni;        //接口
        pae->ae_queue = EMPTY;        //队列地址
        memcpy(pae->ae_pra, &pep->ep_nexthop, pae->ae_prlen);    //协议地址
        pae->ae_attempts = 0;        //重试
        pae->ae_ttl = ARP_RESEND;    //生存时间
        arpsend(pae);            //发送
    }
    /*将等待ARP绑定的数据包存放在和ARP表项相关的队列中,如果队列为空,则分配一个。*/
    if (pae->ae_queue == EMPTY)    
        pae->ae_queue = newq(ARP_QSIZE, QF_NOWAIT);    //如没有队列,建立一个新队列
    if (enq(pae->ae_queue, pep, 0) < 0)    //将分组放入队列中,
        freebuf(pep);
    restore(ps);
    return OK;    //如果队列满,则失丢这个分组
}


5. ARP的输入处理

/* arpadd.c – arpadd */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct arpentry *arpalloc(void);

/*————————————————————————
 * arpadd – Add a RESOLVED entry to the ARP cache
 *     N.B. Assumes interrupts disabled
 *————————————————————————
 */
struct    arpentry *
arpadd(struct netif *pni, struct arp *parp)
{
    struct    arpentry    *pae;            //ARP缓存表项指针

    pae = arpalloc();                //为指针分配内存空间
    //从收到的ARP包中填写到缓存中
    pae->ae_hwtype = parp->ar_hwtype;        //硬件类型        
    pae->ae_prtype = parp->ar_prtype;        //协议类型
    pae->ae_hwlen = parp->ar_hwlen;            //硬件地址长度
    pae->ae_prlen = parp->ar_prlen;            //协议地址长度
    pae->ae_pni = pni;                //接口
    pae->ae_queue = EMPTY;                //队列地址
    memcpy(pae->ae_hwa, SHA(parp), parp->ar_hwlen);    //源物理地址
    memcpy(pae->ae_pra, SPA(parp), parp->ar_prlen);    //源协议地址
    pae->ae_ttl = ARP_TIMEOUT;            //生存时间
    pae->ae_state = AS_RESOLVED;            //表项的状态
    return pae;
}


6. 发送等待发送的分组

/* arpqsend.c – arpqsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>

int netwrite(struct netif *, struct ep *, unsigned);

/*————————————————————————
 * arpqsend – write packets queued waiting for an ARP resolution
 *————————————————————————
 */
void
arpqsend(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    netif    *pni;

    if (pae->ae_queue == EMPTY)        //待发送队列为空则返回
        return;

    pni = pae->ae_pni;            //接口
    while (pep = (struct ep *)deq(pae->ae_queue))    //遍历队列并发送,为空则释放队列
        netwrite(pni, pep, pep->ep_len);
    freeq(pae->ae_queue);
    pae->ae_queue = EMPTY;
}


7. ARP输入过程

/* arp_in.c – arp_in */

#include <conf.h>
#include <kernel.h>
#include <network.h>

void arpqsend(struct arpentry *);

/*————————————————————————
 *  arp_in  –  handle ARP packet coming in from Ethernet network
 *    N.B. – Called by ni_in– SHOULD NOT BLOCK
 *————————————————————————
 */
int
arp_in(struct netif *pni, struct ep *pep)
{
    struct    arp        *parp = (struct arp *)pep->ep_data;
    struct    arpentry    *pae;
    int            arplen;
    /*从网络字节顺序转换成主机字节顺序*/
    parp->ar_hwtype = net2hs(parp->ar_hwtype);
    parp->ar_prtype = net2hs(parp->ar_prtype);
    parp->ar_op = net2hs(parp->ar_op);
    //如果硬件类型或协议类型不匹配,则丢充此分组并返回
    if (parp->ar_hwtype != pni->ni_hwtype ||
        parp->ar_prtype != EPT_IP) {
        freebuf(pep);
        return OK;
    }
    //如果ARP表项中有此IP,就更新之
    if (pae = arpfind(SPA(parp), parp->ar_prtype, pni)) {
        memcpy(pae->ae_hwa, SHA(parp), pae->ae_hwlen);
        pae->ae_ttl = ARP_TIMEOUT;
    }
    //如果此分组不是发往此接口的,则丢弃之
    if (memcmp(TPA(parp), &pni->ni_ip, IP_ALEN)) {
        freebuf(pep);
        return OK;
    }
    //如果是发往本接口并没有此表项,则加入一个新表项
    if (pae == 0)
        pae = arpadd(pni, parp);
    //如果表项状态为正在绑定中,则发送队列中的分组
    if (pae->ae_state == AS_PENDING) {
        pae->ae_state = AS_RESOLVED;
        arpqsend(pae);
    }
    //如果收到的分组操作类型为请求ARP,则填写ARP包并发送,否则丢充分组
    if (parp->ar_op == AR_REQUEST) {
        parp->ar_op = AR_REPLY;
        memcpy(TPA(parp), SPA(parp), parp->ar_prlen);
        memcpy(THA(parp), SHA(parp), parp->ar_hwlen);
        memcpy(pep->ep_dst, THA(parp), EP_ALEN);
        memcpy(SHA(parp), pni->ni_hwa.ha_addr,
            pni->ni_hwa.ha_len);
        memcpy(SPA(parp), &pni->ni_ip, IP_ALEN);

        parp->ar_hwtype = hs2net(parp->ar_hwtype);
        parp->ar_prtype = hs2net(parp->ar_prtype);
        parp->ar_op = hs2net(parp->ar_op);

        arplen = ARP_HLEN + 2*(parp->ar_prlen + parp->ar_hwlen);

        write(pni->ni_dev, pep, arplen);
    } else
        freebuf(pep);
    return OK;
}


8. ARP高速缓存的管理

/* arpalloc.c – arpalloc */

#include <conf.h>
#include <kernel.h>
#include <proc.h>
#include <network.h>

void arpdq(struct arpentry *);

/*————————————————————————
 * arpalloc – allocate an entry in the ARP table
 *    N.B. Assumes interrupts DISABLED
 *————————————————————————
 */
struct arpentry *arpalloc()
{
    static    int    aenext = 0;    /*静态变量*/
    struct    arpentry *pae;
    int    i;
    //找空表项
    for (i=0; i<ARP_TSIZE; ++i) {
        if (arptable[aenext].ae_state == AS_FREE)
            break;
        aenext = (aenext + 1) % ARP_TSIZE;    //实现静态变量循环计数
    }
    pae = & arptable[aenext];
    aenext = (aenext + 1) % ARP_TSIZE;
    //如没有空表项,则使用这个当前的表项,判断为使用中但未绑定,并且有队列,如果有则丢弃队列中的分组
    if (pae->ae_state == AS_PENDING && pae->ae_queue >= 0)
        arpdq(pae);
    pae->ae_state = AS_PENDING;
    return pae;
}


9. 高速缓存的定期维护管理

/* arptimer.c – arptimer */

#include <conf.h>
#include <kernel.h>
#include <network.h>

void    arpdq(struct arpentry *);
int    arpsend(struct arpentry *);

/*————————————————————————
 * arptimer – Iterate through ARP cache, aging (possibly removing) entries
 *————————————————————————
 *    gran    – time since last iteration
 */
void
arptimer(int gran)
{
    struct arpentry *pae;
    STATWORD    ps;
    int        i;

    disable(ps);    /* 互斥量 */

    for (i=0; i<ARP_TSIZE; ++i) {
        if ((pae = &arptable[i])->ae_state == AS_FREE)
            continue;                /*空闲的条目*/
        if (pae->ae_ttl == ARP_INF)
            continue;                 /*不超时永久条目*/
        if ((pae->ae_ttl -= gran) <= 0)            /*寿命为0或负*/
            if (pae->ae_state == AS_RESOLVED)    /*条目绑定中但未正确*/
                pae->ae_state = AS_FREE;    /*仅改变状态为空闲*/
            else if (++pae->ae_attempts > ARP_MAXRETRY) {    /*超过最大广播次数*/
                pae->ae_state = AS_FREE;
                arpdq(pae);            /*清空队列*/
            } else {
                pae->ae_ttl = ARP_RESEND;    /*等于重发时间*/    
                arpsend(pae);            /*重新广播分组*/
            }
    }
    restore(ps);
}


10. 释放队列中的分组

/* arpdq.c – arpdq */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 * arpdq – destroy an arp queue that has expired
 *————————————————————————
 */
void
arpdq(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    ip    *pip;

    if (pae->ae_queue < 0)        /* 队列为空,直接返回 */
        return;

    while (pep = (struct ep *)deq(pae->ae_queue)) {
        if (gateway && pae->ae_prtype == EPT_IP) {/*本机是网关并且数据报为IP数据报*/
            pip = (struct ip *)pep->ep_data;    //生成ICMP"目的站不可达"信息
            icmp(ICT_DESTUR, ICC_HOSTUR, pip->ip_src, pep, 0);
        } else
            freebuf(pep);            //释放帧
    }
    freeq(pae->ae_queue);                //释放队列
    pae->ae_queue = EMPTY;
}


11. ARP初始化

/* arpinit.c – arpinit */

#include <conf.h>
#include <kernel.h>
#include <proc.h>
#include <network.h>

/*————————————————————————
 *  arpinit  –  initialize data structures for ARP processing
 *————————————————————————
 */
void arpinit()
{
    int    i;

    rarpsem = screate(1);            /*创建一个与RARP一起使用的互斥型信号量*/
    rarppid = BADPID;            /*RARP进程号初始为-1*/

    for (i=0; i<ARP_TSIZE; ++i)
        arptable[i].ae_state = AS_FREE;    /*将状态全部置为AS_FREE*/
}

int    rarpsem;
int    rarppid;

struct    arpentry    arptable[ARP_TSIZE];    /*定义ARP高速缓存*/

Trackback URL for this entry