UIP协议栈二_dipaddr-程序员宅基地

技术标签: UIP  

1. 网卡如何与uIP协议交互(包括arp, icmp等)
   在我看来,CP2200提供了读取网络数据的能力,而UIP提供的是一种如何封装网路数据的策略。对用户数
据不断封装,最后交给CP2200发送,在UIP协议中有一个uip_buf缓冲用来接收和发送数据。

(转自:维库电子开发网>子通列表 > 协议栈)

   ARP请求和应答
在UIP协议中定义了一个ARP的struct。维护了一张
缓存表。
  1. struct arp_entry {
  2. u16_t ipaddr[2];  // 保存的是IP地址
  3. struct uip_eth_addr ethaddr; // 保存的是mac地址
  4. u8_t time;       // 缓存更新时间
  5. };
     ARP请求发送函数:void uip_arp_out(void)
* 为传出的IP包添加以太网头并看是否需要发送ARP请求. 
* 此函数应该在发送IP包时调用,它会检查IP包的目的IP地址,看看以太网应该使用什么目的MAC地址.
* 如果目的IP地址是在局域网中(由IP地址与子网掩码的与逻辑决定),函数就会从ARP缓存表中查找有
* 无对应项.若有,就取对应的MAC地址,加上以太网头,并返回,否则uip_buf[]中的数据包会被替换成一个
* 目的IP在址的ARP请求.原来的IP包会被简单的仍掉,此函数假设高层协议(如TCP)会最终重传扔掉的包.
* 如果目标IP地址并非一个局域网IP,则会使用默认路由的IP地址.
* uip_len.函数返回时,uip_buf[]中已经有了一个包,其长度由uip_len指定.
  1. void uip_arp_out(void)
  2. {
  3.   struct arp_entry *tabptr=0;
  4.   // 在ARP表中找到目的IP地址,构成以太网头.如果目的IP地址不在局域网中,则使用默认路由的IP.
  5.   // 如果ARP表中找不到,则将原来的IP包替换成一个ARP请求.  
  6.   // 首先检查目标是不是广播
  7.   if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr))
  8.   {
  9.     memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);
  10.   } 
  11.   else 
  12.   {
  13.         /* 检查目标地址是否在局域网内 */
  14.         if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask))
  15.         {
  16.               /* 目的地址不在局域网内,所以保用默认路由器的地址来确在MAC地址 */
  17.               uip_ipaddr_copy(ipaddr, uip_draddr);
  18.         } 
  19.         else 
  20.         {
  21.             /* 否则,使用目标IP地址 */
  22.           uip_ipaddr_copy(ipaddr, IPBUF->destipaddr);
  23.         }
  24.         //这里遍历表,对比目的IP与ARP缓存表中的IP.
  25.         for(= 0; i < UIP_ARPTAB_SIZE; ++i)
  26.         {
  27.              tabptr = &arp_table[i];
  28.              if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr))
  29.              {
  30.                  break;
  31.              }
  32.          }
  33.          
  34.          if(== UIP_ARPTAB_SIZE)
  35.          {
  36.             /* 如果遍历到头没找到,将原IP包替换为ARP请求并返回 */
  37.               memset(BUF->ethhdr.dest.addr, 0xff, 6);  // 以太网目的地址
  38.               memset(BUF->dhwaddr.addr, 0x00, 6);      // 目的以太网地址
  39.               memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);  //
  40.               memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);  // 源以太网地址
  41.     
  42.               uip_ipaddr_copy(BUF->dipaddr, ipaddr);  // 目的IP地址
  43.               uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr);  // 源IP地址
  44.               BUF->opcode = HTONS(ARP_REQUEST);  // ARP 请求
  45.               BUF->hwtype = HTONS(ARP_HWTYPE_ETH);  // 硬件类型 值为1
  46.               BUF->protocol = HTONS(UIP_ETHTYPE_IP); // 协议类型 值为0x8000表示IP地址
  47.               BUF->hwlen = 6;  
  48.               BUF->protolen = 4
  49.               BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);  

  50.               uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];
  51.               uip_len = sizeof(struct arp_hdr);
  52.               return;
  53.           }

  54.         // 如果是在局域网中,且在ARP缓存中找到了(如果没找到进行不到这一步,在上面就返回了),则构建以太网头
  55.          memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);
  56.   }
  57.   memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
  58.   IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP);
  59.   uip_len += sizeof(struct uip_eth_hdr);
  60. }
  61. 接下来看看UIP如何处理ARP应答的情况,在主循环中一段代码:
    1. else if(BUF->type == htons(UIP_ETHTYPE_ARP)) 
    2. {    
    3.     uip_arp_arpin();  // 处理ARP应答
    4.     /* If the above function invocation resulted in data that
    5.        should be sent out on the network, the global variable
    6.        uip_len is set to a value > 0. */
    7.    // 如果上面的函数返回的结果需要发送到网络上,那么uip_len就必须设置 > 0
    8.     if(uip_len > 0) 
    9.     {
    10.         network_device_send(); // 回应ARP包
    11.     }
    12. }

    在uip_arp_arpin()函数中主要是处理ARP应答。
        这个函数是在设备接收到ARP包时,由驱动程序调用的.如果收到是ARP包是一个对本地主机上次发送的ARP请求的应答,那么就从包中取得自己想要的主机的MAC地址,加入自己的ARP缓存表中.如果收到是一个ARP请求,那就把自己的MAC地址打包成一个ARP应答,发送给请求的主机.看代码uip_arp.c的254行:
    1. void uip_arp_arpin(void)
    2.  {
    3.   if(uip_len < sizeof(struct arp_hdr))
    4.   {
    5.     uip_len = 0;
    6.     return;
    7.   }
    8.   uip_len = 0;
    9.   
    10.   switch(BUF->opcode)  // 操作码
    11.   {
    12.       case HTONS(ARP_REQUEST):
    13.       // 如果是一个ARP请求,则发送应答
    14.       if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr))
    15.       {
    16.           // 首先,我们将发送请求的主机注册到ARP缓存表中,因为我们很
    17.           // 可能要跟它要有更多的交流
    18.           uip_arp_update(BUF->sipaddr, &BUF->shwaddr);  
    19.       
    20.           // 回应的操作码是 2.
    21.           BUF->opcode = HTONS(2);
    22.           // 将收到的ARP包的发送端以太网地址,变为目的以太网地址
    23.           memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6);
    24.           // 将自己的以太网地址,赋值给ARP包的发送端以太网地址
    25.           memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);
    26.           // 对应以太网源地址
    27.           memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
    28.           // 对应以太网目的地址
    29.           memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6);
    30.       
    31.           BUF->dipaddr[0] = BUF->sipaddr[0];
    32.           BUF->dipaddr[1] = BUF->sipaddr[1];
    33.           BUF->sipaddr[0] = uip_hostaddr[0];
    34.           BUF->sipaddr[1] = uip_hostaddr[1];

    35.           BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
    36.           uip_len = sizeof(struct arp_hdr);
    37.      }
    38.      break;
    39.     // 如果收到的是一个ARP应答,而且也是我们所要的应答的话,就插件
    40.     // 并更新ARP缓存表
    41.     case HTONS(ARP_REPLY):  
    42.     if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr))
    43.     {
    44.       uip_arp_update(BUF->sipaddr, &BUF->shwaddr);
    45.     }
    46.     break;
    47.   }

    48.   return;
    49. }
    还有一个是ARP周期处理函数,在主循环中代码如下:
    // 每10秒运行一次
    if(timer_expired(&arp_timer)) 
    {
        timer_reset(&arp_timer); 

        uip_arp_timer();
    }
    具体的代码:
    1. void uip_arp_timer(void)
    2. {
    3.   struct arp_entry *tabptr;
    4.   ++arptime;    // 这个是个全局变量,结合uip_arp_update来更新缓存表
    5.   for(= 0; i < UIP_ARPTAB_SIZE; ++i)
    6.   {
    7.     tabptr = &arp_table[i];
    8.     // 把超过20分钟都没有更新的项扔掉
    9.     if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 &&
    10.        arptime - tabptr->time >= UIP_ARP_MAXAGE)
    11.     {
    12.       memset(tabptr->ipaddr, 0, 4);
    13.     }
    14.   }
    15. }

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014186096/article/details/47760819

智能推荐

pandas dataframe 提取行和列_pd.dataframe 输出第几列-程序员宅基地

文章浏览阅读10w+次,点赞57次,收藏229次。import pandas as pddata = pd.DataFrame({'a':[1,2,3],'b':[4,5,6],'c':[7,8,9]})提取列单列data['a']多列data[['a', 'b']]使用 .loc或者 .iloc 提取第一个参数是行,第二个参数为列.loc为按标签提取, .iloc为按位置索引提取data..._pd.dataframe 输出第几列

Vue在启动项目时报错 ValidationError: webpack Dev Server Invalid Options-程序员宅基地

文章浏览阅读2.2k次。#Vue项目开发报错欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新的写作体验;在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 _webpack dev server invalid options

android驱动开发书籍推荐,2024年Android春招面试经历-程序员宅基地

文章浏览阅读287次,点赞3次,收藏3次。都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

浅谈分布式光伏电站的运维管理-程序员宅基地

文章浏览阅读610次,点赞18次,收藏18次。十四五”期间,随着“双碳”目标提出及逐步落实,本就呈现出较好发展势头的分布式光伏发展有望大幅提速。就“十四五”光伏发展规划,国家发改委能源研究所可再生能源发展中心副主任陶冶表示,“双碳”目标意味着国家产业结构的调整,未来10年,新能源装机将保持在110GW以上的年增速,这里面包含集中式光伏电站和分布式光伏电站。相较于集中式电站来说,分布式对土地等自然资源没有依赖,各个地方的屋顶就是分布式电站的形成基础,在碳中和方案的可选项中,分布式光伏由于其灵活性必将被大力发展,目前已有河北、甘肃、安徽、浙江、陕西等

如何搭建反欺诈策略与模型-程序员宅基地

文章浏览阅读1.7k次。转载自https://www.jianshu.com/p/fd447413e335信用风险与反欺诈哪个更加重要?为什么是先讲策略再谈模型?一个完整的反欺诈流程如何搭建?如何说服CEO接受模型测试成本?在一本财经商学院举办的第二期风控闭门课程上,天创信用首席科学家陈黎明一一做出解答。以下是她现场分享的部分干货:01基本概念今天我讲的主要课题是“反欺诈策略和模型”。为什么要把策略放前面呢?因为不管是拍脑袋决定,还是通过数据挖掘出来,反欺诈一般是先有策略,然后通过数据的积累,慢慢去构建模型。首先讲一下

hive表新增字段,指定新增字段位置,删除字段_hive 新增字段-程序员宅基地

文章浏览阅读5.6k次。经验证,hive中修改字段顺序并没有将字段对应的值移动,只是单纯的修改字段名,如果是空表(没有数据),可以使用以上两步;其中CASCADE选项为选填的字段,但是对于分区表,一定要加上,否则其历史分区的元数据信息(metadata)将无法正常更新,导致访问历史分区时会报莫名的错误。背景:项目中,客户使用hive内表,由于逻辑变更,原hive表结构需要调整,新增字段。如果已经添加了字段,可以修改字段时,在修改字段名时带上,在修改回来。实际上,使用alter语句,把保留的字段全部列出来,删除的字段不要列出来。_hive 新增字段

随便推点

解决VC++2010&&VS2010无法调试&&调试无法进入断点&&无法命中断点的问题_vc++2010调试但不执行-程序员宅基地

文章浏览阅读2.1k次。对于这个问题只要修改注册表,方法如下:开始->运行->输入regedit->HKEY_LOCALMACHINE -> SOFTWARE -> Microsoft -> Internet Explorer -> Main 增加DWORD键TabProcGrowth,值为0就行!_vc++2010调试但不执行

JDBC-ODBC数据库连接_jdbc:odbc:-程序员宅基地

文章浏览阅读2k次。我们都知道,任何一个项目都离不开数据库,常见的数据库主要有(MySQL,sql Server,Oracle,Access),我们的程序在操作数据库之前,都要进行的一个步骤,就是链接到我们的数据库,所以这次因为自己在学习这个,所以写个文章,一来希望如果我哪里写的不好的话,可以对我有指点,我会进行改正,二来是可以方便自己以后复习。不多说其他话,开始写笔记ODBC链接到数据库的操作分为4个步骤:_jdbc:odbc:

复现一个老漏洞 Discuz!7.2 faq.php 注入漏洞 分析原理_discuz 7.2 faq.php 注入漏洞全自动利用工具-程序员宅基地

文章浏览阅读4k次。源码~ http://pan.baidu.com/s/1gfkvJrXpoc/faq.php?action=grouppermission&amp;gids[99]=%27&amp;gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(version(),floor(rand(0)*2))x%20fr..._discuz 7.2 faq.php 注入漏洞全自动利用工具

ubuntu系统禁用自带Nouveau驱动_ubuntu禁用nouveau-程序员宅基地

文章浏览阅读1.9w次,点赞12次,收藏70次。Nouveau是由第三方为NVIDIA显卡开发的一个开源3D驱动,让Linux更容易的应对各种复杂的NVIDIA显卡环境,安装完Linux系统即可进入桌面并且有不错的显示效果,所以,很多Linux发行版默认集成了Nouveau驱动,在使用NVIDIA显卡时默认安装Nouveau驱动。但是用户除了想让正常显示图形界面外很多时候还需要一些其他功能,Nouveau驱动不能完成,同时还会对..._ubuntu禁用nouveau

吴恩达 机器学习 线性回归与逻辑回归中代价函数在MATLAB中具体实现总结_matlab机器学习代价函数-程序员宅基地

文章浏览阅读1.4k次。作为一个对线代已经不那么熟悉,机器学习方面也是零基础的小白,在做EX1和EX2的时候,最让我感到困难的就是代价函数cost Function、梯度在MATLAB中究竟应该是怎样的形式根据吴恩达老师给出的形式一. 线性回归1.普通线性回归预测函数H代价函数具体的MATLAB实现这里是另一种方法,二选一即可这里的采用的是向量化编程,无论是单一变量还是多变量都是适用的。梯度下降具..._matlab机器学习代价函数

ChatGPT加持,需求分析再无难题-程序员宅基地

文章浏览阅读857次,点赞21次,收藏23次。写清楚需求:在给出提示词的时候,我们通过添加场景、添加角色,让我们的需求更加明确。将复杂的任务拆分为更简单的子任务:需求分析本身就是一个复杂的过程,我们需要逐步拆解,并纠正GPT的回复,引导GPT给到更多的信息。系统的测试变化:在返回信息过程中,如果返回信息不满足需求,或者有偏差,需要测试回复信息,并予以修正。角色扮演:在给出提示词的过程中,我们告诉GPT,需要以一个测试工程师的角色给出对应的测试点。

推荐文章

热门文章

相关标签