当前位置: 首页 > news >正文

地址转换协议ARP

  在以太网协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的MAC地址。而在TCP/IP协议中,网络层和传输层只关心目标主机的IP地址。这就导致在以太网中使用IP协议时,数据链路层的以太网协议接到上层IP协议提供的数据中,只包含目的主机的IP地址。于是需要一种方法,根据目的主机的IP地址,获得其MAC地址。这就是ARP协议要做的事情。

  所谓地址解析(address resolution)就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。

理论结构

  ARP软件可分为三部分:

  • 输出模块
    • 将高层协议地址与相应的物理地址进行绑定,返回给网络接口程序
  • 输入模块
    • 处理来自网络的ARP分组,并通过增加新的绑定来修改ARP高速缓存的内容
  • 高速缓存管理程序
    • 实现高速缓存替换策略;检测高速缓存中的所有表项,删除已达到规定时限的表项

输出模块

  该模块主要是要接收IP数组请求,然后查找物理地址,返回。

  主要步骤为:

  1. 睡眠,直到IP软件收到IP分组。

  2. 检查高速缓存表,寻找对应于这个IP分组的项目。

  3. if ( 找到 ){

      if ( 状态为 RESOLVED ){

        提取硬件物理地址;

        将分组连同硬件物理地址一起发送到数据链路层;

                   return;

      }

      else if ( 状态为 PENDING ){

       将分组放入相应的队列;

                  return;

     }

    }

    else{

      创建一个队列;

      将分组加入到队列中;

      创建一个高速缓存项目,状态设置为 PENDING ,ATTEMPTS 为 1;

      发送ARP请求;

    }

ARP高速缓存队列

  它是用数组来存储的。

extern struct arpentry arptable[ARP_TSIZE]

搜索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))   /* BLKEQU的定义 #define    BLKEQU(b1, b2, len)    (!memcmp((b1), (b2), len))*/
            return pae;   
    }   
    return 0;   
} 
复制代码

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(pae)
struct    arpentry    *pae; //指向高速缓存的表项
{
    struct    netif    *pni = pae->ae_pni;
    struct    ep    *pep;
    struct    arp    *parp;
    int        arplen;

    pep = (struct ep *) getbuf(Net.netpool); //生成ARP请求分组
    if ((int)pep == SYSERR)
        return SYSERR;
    blkcopy(pep->ep_dst, pni->ni_hwb.ha_addr, pae->ae_hwlen);
    pep->ep_type = EPT_ARP;
    pep->ep_order = EPO_NET;
    parp = (struct arp *) pep->ep_data;
    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);
    blkcopy(SHA(parp), pni->ni_hwa.ha_addr, pae->ae_hwlen);
    blkcopy(SPA(parp), &pni->ni_ip, pae->ae_prlen);
    bzero(THA(parp), pae->ae_hwlen);
    blkcopy(TPA(parp), pae->ae_pra, pae->ae_prlen);
    arplen = ARP_HLEN + 2*(parp->ar_hwlen + parp->ar_prlen);
    write(pni->ni_dev, pep, EP_HLEN+arplen); //发送请求分组
    return OK;
}
复制代码

输入模块

    从一个队列中拿走一个分组,并连同解析出的物理地址一起发送给数据报链路层传输。

  主要步骤为

  1. 睡眠,直到ARP分组到达(请求或回答)。

  2. 检查高速缓存表,寻找对应这个ARP分组的项目。

  3. if ( 找到 ){

     if ( 状态是 RESOLVED ){

      更新项目;

      return;

     }

     else if ( 状态是 PENDING ){

      更新项目;

                 如果队列非空的话,将一个分组从队列中取出,将它与硬件地址一起发送给数据链路层;

      return;

     {

    }

    else{

    创建一个项目;

    将此项目添加到表中;

    return;

    }

  4.  如果分组是一个请求, 发送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;

    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;
}
复制代码

发送等待发送的分组

复制代码
/* 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;
        /* 遍历等待发送的分组队列,调用netwrite逐个放入网络输出队列中 */
    while (pep = (struct ep *)deq(pae->ae_queue)) 
        netwrite(pni, pep, pep->ep_len);
    freeq(pae->ae_queue); //队列为空后,释放自身
    pae->ae_queue = EMPTY;
}
复制代码

高速缓存管理

   高速缓存是用来存储IP地址与物理地址的。如果一个IP进行需要发送一个数据报,但其目的地址不在ARP高速缓存中,就会创建一个新的表项,然后广播相应的请求分组,并等待分组置入队列中。

  主要步骤:

1. 睡眠,周期性的唤醒。

2. 遍历高速缓存的每一个项目:

  if ( 状态为FREE )

           continue;

      if ( 状态为PENDING ){

    尝试次数+1;

    if ( 尝试次数达到最大次数 ){

      该项目状态->FREE;

          撤销相应的队列;

    }

    else {

      发送ARP请求;

    }

    continue;

  }

   else if( 状态为RESOLVED ){

    将超时字段的值减去已经过去的时间;

    若结果小于0,状态->FREE,撤销相应队列。

  }

 替换策略

复制代码
/* 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;   
} 
复制代码

 

 

知识共享许可协议
本文 由 cococo点点 创作,采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。欢迎转载,请注明出处:
转载自:cococo点点 http://www.cnblogs.com/coder2012


相关文章:

  • netstat -aon|findstr 8888 终止进程
  • 判断jQuery元素是否隐藏
  • 第二阶段—个人工作总结01
  • IO流的操作规律
  • C#创建https请求并使用pfx证书
  • Xcode 7 缺少 *.dylib库的解决方法
  • [C#基础]说说lock到底锁谁?
  • Shell学习笔记---重定向输入、输出(原创)
  • 堆的一些简单应用
  • dtrace4linux_Example
  • jQuery.extend 函数详解
  • Shiro安全框架入门篇(登录验证实例详解与源码)
  • goldengate一些参数整理
  • HDU1161 Eddy's mistakes
  • Unity3d标签管理类-利用脚本控制标签,提升工作效率
  • 自己简单写的 事件订阅机制
  • #Java异常处理
  • 230. Kth Smallest Element in a BST
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • Java教程_软件开发基础
  • linux安装openssl、swoole等扩展的具体步骤
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • spring cloud gateway 源码解析(4)跨域问题处理
  • TypeScript迭代器
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 机器学习中为什么要做归一化normalization
  • 简单基于spring的redis配置(单机和集群模式)
  • 聊聊hikari连接池的leakDetectionThreshold
  • 面试总结JavaScript篇
  • 你不可错过的前端面试题(一)
  • 手写一个CommonJS打包工具(一)
  • 数据结构java版之冒泡排序及优化
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 延迟脚本的方式
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • 阿里云ACE认证之理解CDN技术
  • ​你们这样子,耽误我的工作进度怎么办?
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #每天一道面试题# 什么是MySQL的回表查询
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • $.ajax()
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (第27天)Oracle 数据泵转换分区表
  • (二)JAVA使用POI操作excel
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (七)理解angular中的module和injector,即依赖注入
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (十一)图像的罗伯特梯度锐化
  • (转载)Linux网络编程入门
  • ***通过什么方式***网吧
  • .Net 8.0 新的变化
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)