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高速缓存*/