文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

从0学ARM-uboot中的网络协议栈

2024-12-03 03:59

关注

uboot中的协议栈相对来说比较简单,有以下几个特点:

传输层只支持UDP协议;

1. 网络协议栈架构

下面是uboot网络协议栈的函数调用流程:

2. 通过DNS命令来解析一个数据包的收发流程

uboot中,所有的命令都用宏U_BOOT_CMD来定义, dns命令的定义如下:

  1. 426 U_BOOT_CMD(  
  2. 427     dns,    3,  1,  do_dns, 
  3. 428     "lookup the IP of a hostname"
  4. 429     "hostname [envvar]" 
  5. 430 ); 

当我们在uboot的命令终端输入命令dns后,命令解析函数就会调用dns执行函数do_dns()

  1. 389 int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 
  2. 390 { 
  3.  …… 
  4. 406     if (strlen(argv[1]) >= 255) { 
  5. 407         printf("dns error: hostname too long\n"); 
  6. 408         return 1; 
  7. 409     } 
  8. 410  
  9. 411     NetDNSResolve = argv[1]; 
  10. 412  
  11. 413     if (argc == 3) 
  12. 414         NetDNSenvvar = argv[2]; 
  13. 415     else 
  14. 416         NetDNSenvvar = NULL
  15. 417  
  16. 418     if (NetLoop(DNS) < 0) { 
  17. 419         printf("dns lookup of %s failed, check setup\n", argv[1]); 
  18. 420         return 1; 
  19. 421     } 
  20. 422  
  21. 423     return 0; 
  22. 424 } 

406行 判断参数字符串长度,大于255非法 411行 参数1必须是要解析的主机,存储在NetDNSResolve 中 413~416行 保存dns命令的环境参数,该参数可以没有 418行 进入网络协议处理函数入口NetLoop(),并将对应的协议DNS传递给该函数

NetLoop()代码比较长,我们只分析其中比较重要的几段代码

  1. 316  
  2. 317  
  3. 320  
  4. 321 int NetLoop(enum proto_t protocol) 
  5. 322 { 
  6. 323     bd_t *bd = gd->bd; 
  7. 324     int ret = -1; 
  8. ………… 
  9. 352     NetInitLoop(); 
  10.  ………… 
  11. 367         switch (protocol) { 
  12. 368         case TFTPGET: 
  13. 369 #ifdef CONFIG_CMD_TFTPPUT 
  14. 370         case TFTPPUT: 
  15. 371 #endif 
  16. 372              
  17. 373             TftpStart(protocol); 
  18. 374             break; 
  19.  ………… 
  20. 426 #if defined(CONFIG_CMD_DNS) 
  21. 427         case DNS: 
  22. 428             DnsStart(); 
  23. 429             break; 
  24. 430 #endif 
  25. 438     } 
  26. ………… 
  27. 461     for (;;) { 
  28. 462         WATCHDOG_RESET(); 
  29. 463 #ifdef CONFIG_SHOW_ACTIVITY 
  30. 464         show_activity(1); 
  31. 465 #endif 
  32. 466          
  33. 470         eth_rx(); 
  34. 471  
  35. 472          
  36. 475         if (ctrlc()) { 
  37. 476              
  38. 477             NetArpWaitPacketIP = 0; 
  39. 478  
  40. 479             net_cleanup_loop(); 
  41. 480             eth_halt(); 
  42. 481              
  43. 482             eth_set_last_protocol(BOOTP); 
  44. 483  
  45. 484             puts("\nAbort\n"); 
  46. 485              
  47. 487             debug_cond(DEBUG_INT_STATE, "--- NetLoop Abort!\n"); 
  48. 488             goto done; 
  49. 489         } 
  50.  ………… 
  51. 522         switch (net_state) { 
  52. 523  
  53. 524         case NETLOOP_RESTART: 
  54. 525             NetRestarted = 1; 
  55. 526             goto restart; 
  56. 527  
  57. 528         case NETLOOP_SUCCESS: 
  58. 529             net_cleanup_loop(); 
  59. 530             if (NetBootFileXferSize > 0) { 
  60. 531                 char buf[20]; 
  61. 532                 printf("Bytes transferred = %ld (%lx hex)\n"
  62. 533                     NetBootFileXferSize, 
  63. 534                     NetBootFileXferSize); 
  64. 535                 sprintf(buf, "%lX", NetBootFileXferSize); 
  65. 536                 setenv("filesize", buf); 
  66. 537  
  67. 538                 sprintf(buf, "%lX", (unsigned long)load_addr); 
  68. 539                 setenv("fileaddr", buf); 
  69. 540             } 
  70. 541             if (protocol != NETCONS) 
  71. 542                 eth_halt(); 
  72. 543             else 
  73. 544                 eth_halt_state_only(); 
  74. 545  
  75. 546             eth_set_last_protocol(protocol); 
  76. 547  
  77. 548             ret = NetBootFileXferSize; 
  78. 549             debug_cond(DEBUG_INT_STATE, "--- NetLoop Success!\n"); 
  79. 550             goto done; 
  80. 551  
  81. 552         case NETLOOP_FAIL: 
  82. 553             net_cleanup_loop(); 
  83. 554              
  84. 555             eth_set_last_protocol(BOOTP); 
  85. 556             debug_cond(DEBUG_INT_STATE, "--- NetLoop Fail!\n"); 
  86. 557             goto done; 
  87. 558  
  88. 559         case NETLOOP_CONTINUE: 
  89. 560             continue
  90. 561         } 
  91. 562     } 
  92. 563  
  93. 564 done: 
  94. 565 #ifdef CONFIG_CMD_TFTPPUT 
  95. 566      
  96. 567     net_set_udp_handler(NULL); 
  97. 568     net_set_icmp_handler(NULL); 
  98. 569 #endif 
  99. 570     return ret; 
  100. 571 } 

函数参数为DNS 352行 初始化网络信息,读取ipaddr、gatewayip、netmask、serverip、dnsip等环境变量的值并复制到对应的全局变量中

  1. static void NetInitLoop(void) 
  2.  static int env_changed_id; 
  3.  int env_id = get_env_id(); 
  4.  
  5.   
  6.  if (env_changed_id != env_id) { 
  7.   NetOurIP = getenv_IPaddr("ipaddr"); 
  8.   NetOurGatewayIP = getenv_IPaddr("gatewayip"); 
  9.   NetOurSubnetMask = getenv_IPaddr("netmask"); 
  10.   NetServerIP = getenv_IPaddr("serverip"); 
  11.   NetOurNativeVLAN = getenv_VLAN("nvlan"); 
  12.   NetOurVLAN = getenv_VLAN("vlan"); 
  13. #if defined(CONFIG_CMD_DNS) 
  14.   NetOurDNSIP = getenv_IPaddr("dnsip"); 
  15. #endif 
  16.   env_changed_id = env_id; 
  17.  } 
  18.  memcpy(NetOurEther, eth_get_dev()->enetaddr, 6); 
  19.  
  20.  return

367行 对传入的参数做switch操作,不同的协议进入到不同的处理流程 428行 执行DnsStart(),

  1. 197 void 
  2. 198 DnsStart(void) 
  3. 199 { 
  4. 200     debug("%s\n", __func__); 
  5. 201  
  6. 202     NetSetTimeout(DNS_TIMEOUT, DnsTimeout); 
  7. 203     net_set_udp_handler(DnsHandler); 
  8. 204  
  9. 205     DnsSend(); 
  10. 206 }  

203行 函数net_set_udp_handler()主要将dns协议的回调函数DnsHandler()注册到udp协议的回调指针udp_packet_handler,

  1. void net_set_udp_handler(rxhand_f *f) 
  2.  debug_cond(DEBUG_INT_STATE, "--- NetLoop UDP handler set (%p)\n", f); 
  3.  if (f == NULL
  4.   udp_packet_handler = dummy_handler;//注册到udp协议回调函数指针 
  5.  else 
  6.   udp_packet_handler = f; 

DnsStart()最终会调用函数DnsSend()发送dns协议数据包,该函数是根据dns协议填充udp数据包

  1. 37 static void 
  2. 38 DnsSend(void) 
  3. 39 { 
  4. 40     struct header *header; 
  5. 41     int n, name_len; 
  6. 42     uchar *p, *pkt; 
  7. 43     const char *s; 
  8. 44     const char *name
  9. 45     enum dns_query_type qtype = DNS_A_RECORD; 
  10. 46  
  11. 47     name = NetDNSResolve; 
  12. 48     pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE); 
  13. 49  
  14. 50      
  15. 51     header           = (struct header *) pkt; 
  16. 52     header->tid      = 1; 
  17. 53     header->flags    = htons(0x100);     
  18. 54     header->nqueries = htons(1);         
  19. 55     header->nanswers = 0; 
  20. 56     header->nauth    = 0; 
  21. 57     header->nother   = 0; 
  22. 58  
  23. 59      
  24. 60     name_len = strlen(name); 
  25. 61     p = (uchar *) &header->data;     
  26. 62  
  27. 63     do { 
  28. 64         s = strchr(name'.'); 
  29. 65         if (!s) 
  30. 66             s = name + name_len; 
  31. 67  
  32. 68         n = s - name;            
  33. 69         *p++ = n;            
  34. 70         memcpy(p, name, n);      
  35. 71         p += n; 
  36. 72  
  37. 73         if (*s == '.'
  38. 74             n++; 
  39. 75  
  40. 76         name += n; 
  41. 77         name_len -= n; 
  42. 78     } while (*s != '\0'); 
  43. 79  
  44. 80     *p++ = 0;            
  45. 81     *p++ = 0;            
  46. 82     *p++ = (unsigned char) qtype;    
  47. 83  
  48. 84     *p++ = 0; 
  49. 85     *p++ = 1;                
  50. 86  
  51. 87     n = p - pkt;                 
  52. 88     debug("Packet size %d\n", n); 
  53. 89  
  54. 90     DnsOurPort = random_port(); 
  55. 91  
  56. 92     NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT, 
  57. 93         DnsOurPort, n); 
  58. 94     debug("DNS packet sent\n"); 
  59. 95 } 

51~57行 根据dns协议填充dns协议头,数据帧首地址为NetTxPacket,此处通过指针pkt和p来填充dns数据帧 60~85行 根据协议格式要求填充要解析的host名字到数据包 87行 计算数据包长度 90行 产生一个随机的端口号 92~93行 调用udp协议的发送函数NetSendUDPPacket(),参数依次是:以太头信息,DNS服务器 ip地址,DNS服务器端口号,我们的dns服务端口号,数据包长度

  1. 688 int NetSendUDPPacket(uchar *ether, IPaddr_t dest, int dport, int sport, 
  2. 689         int payload_len) 
  3. 690 { 
  4. 691     uchar *pkt; 
  5. 692     int eth_hdr_size; 
  6. 693     int pkt_hdr_size; 
  7. 694  
  8. 695      
  9. 696     assert(NetTxPacket != NULL); 
  10. 697     if (NetTxPacket == NULL
  11. 698         return -1; 
  12. 699  
  13. 700      
  14. 701     if (dest == 0) 
  15. 702         dest = 0xFFFFFFFF; 
  16. 703  
  17. 704      
  18. 705     if (dest == 0xFFFFFFFF) 
  19. 706         ether = NetBcastAddr; 
  20. 707  
  21. 708     pkt = (uchar *)NetTxPacket; 
  22. 709  
  23. 710     eth_hdr_size = NetSetEther(pkt, ether, PROT_IP); 
  24. 711     pkt += eth_hdr_size; 
  25. 712     net_set_udp_header(pkt, dest, dport, sport, payload_len); 
  26. 713     pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE; 
  27. 714  
  28. 715      
  29. 716     if (memcmp(ether, NetEtherNullAddr, 6) == 0) { 
  30. 717         debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &dest); 
  31. 718  
  32. 719          
  33. 720         NetArpWaitPacketIP = dest; 
  34. 721         NetArpWaitPacketMAC = ether; 
  35. 722  
  36. 723          
  37. 724         NetArpWaitTxPacketSize = pkt_hdr_size + payload_len; 
  38. 725  
  39. 726          
  40. 727         NetArpWaitTry = 1; 
  41. 728         NetArpWaitTimerStart = get_timer(0); 
  42. 729         ArpRequest(); 
  43. 730         return 1;    
  44. 731     } else { 
  45. 732         debug_cond(DEBUG_DEV_PKT, "sending UDP to %pI4/%pM\n",                                                                                
  46. 733             &dest, ether); 
  47. 734         NetSendPacket(NetTxPacket, pkt_hdr_size + payload_len); 
  48. 735         return 0;    
  49. 736     } 
  50. 737 } 

696~706行 参数检查 710行 设置以太头 713行 设置udp协议头 716~730行 如果没有目的MAC地址,就要先发送ARP请求 734行 调用函数NetSendPacket(),参数分别是:要发送数据帧的首地址,数据包长度

  1. 529    
  2. 530 static inline void NetSendPacket(uchar *pkt, int len) 
  3. 531 { 
  4. 532     (void) eth_send(pkt, len); 
  5. 533 } 

532行 调用我们注册的函数dm9000_send() 该函数已经分析过,根据流程图,回到函数NetLoop()

461~562行 循环接收网络数据包 470行 调用网卡驱动接收函数eth_rx()

  1. int eth_rx(void) 
  2.  if (!eth_current) 
  3.   return -1; 
  4.  
  5.  return eth_current->recv(eth_current); 

eth_current->recv(eth_current)函数就是我们注册的网卡的接收函数dm9000_rx(),该函数我们上一章已经分析过,最终通过调用函数NetReceive(),将数据帧上传到协议栈

  1.  943 void 
  2.  944 NetReceive(uchar *inpkt, int len) 
  3.  945 { 
  4.  946     struct ethernet_hdr *et; 
  5.  947     struct ip_udp_hdr *ip; 
  6.  948     IPaddr_t dst_ip; 
  7.  949     IPaddr_t src_ip; 
  8.  950     int eth_proto; 
  9.  …… 
  10.  957  
  11.  958     NetRxPacket = inpkt; 
  12.  959     NetRxPacketLen = len; 
  13.  960     et = (struct ethernet_hdr *)inpkt; 
  14.  961  
  15.  962      
  16.  963     if (len < ETHER_HDR_SIZE) 
  17.  964         return
  18.  965  
  19.  …… 
  20.  984  
  21.  985     eth_proto = ntohs(et->et_protlen); 
  22.  986  
  23.  987     if (eth_proto < 1514) { 
  24.  988         struct e802_hdr *et802 = (struct e802_hdr *)et; 
  25.   …… 
  26.  997  
  27.  998     } else if (eth_proto != PROT_VLAN) {     
  28.  999         ip = (struct ip_udp_hdr *)(inpkt + ETHER_HDR_SIZE); 
  29. 1000         len -= ETHER_HDR_SIZE; 
  30. 1001  
  31. 1002     } else {             
  32.  …… 
  33. 1026     } 
  34. 1027  
  35.  ……  
  36. 1045     switch (eth_proto) { 
  37.  …… 
  38. 1056     case PROT_IP: 
  39. 1057         debug_cond(DEBUG_NET_PKT, "Got IP\n"); 
  40. 1058          
  41. 1059         if (len < IP_UDP_HDR_SIZE) { 
  42. 1060             debug("len bad %d < %lu\n", len, 
  43. 1061                 (ulong)IP_UDP_HDR_SIZE); 
  44. 1062             return
  45. 1063         } 
  46. 1064          
  47. 1065         if (len < ntohs(ip->ip_len)) { 
  48. 1066             debug("len bad %d < %d\n", len, ntohs(ip->ip_len)); 
  49. 1067             return
  50. 1068         } 
  51. 1069         len = ntohs(ip->ip_len); 
  52. 1070         debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n"
  53. 1071             len, ip->ip_hl_v & 0xff); 
  54. 1072  
  55. 1073          
  56. 1074         if ((ip->ip_hl_v & 0xf0) != 0x40) 
  57. 1075             return
  58. 1076          
  59. 1077         if ((ip->ip_hl_v & 0x0f) > 0x05) 
  60. 1078             return
  61. 1079          
  62. 1080         if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE / 2)) { 
  63. 1081             debug("checksum bad\n"); 
  64. 1082             return
  65. 1083         } 
  66. 1084          
  67. 1085         dst_ip = NetReadIP(&ip->ip_dst); 
  68.  
  69. 1092          
  70. 1093         src_ip = NetReadIP(&ip->ip_src); 
  71.  
  72.  
  73. 1184          
  74. 1187         (*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE, 
  75. 1188                 ntohs(ip->udp_dst), 
  76. 1189                 src_ip, 
  77. 1190                 ntohs(ip->udp_src), 
  78. 1191                 ntohs(ip->udp_len) - UDP_HDR_SIZE); 
  79. 1192         break; 
  80. 1193     } 
  81. 1194 } 

参数inpkt:指向接收到的以太数据包头 len:接收到的数据包的长度 960行 变量NetRxPacket指向接收的数据头,以太数据包包头比定位以太协议头 985行 从以太协议头提取出协议字段,该字段表示后面是否是ip协议 999行 解析出ip协议头 1045行 根据以太头协议进行switch操作 1059~1083行 对协议头进行合法性检查 1085行 读取出目的ip地址 1093行 读取出源ip地址, 1187行 ip协议头解析成功,调用udp协议回调函数udp_packet_handler(),该函数在之前的DnStart()注册了DnsHandler

  1. 104 static void 
  2. 105 DnsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len) 
  3. 106 { 
  4. 193  
  5. 194     net_set_state(NETLOOP_SUCCESS); 
  6. 195 } 

该函数用于解析DNS协议,在此不再详解 解析成功后194行,会设置当前执行状态为NETLOOP_SUCCESS, 代码回到函数NetLoop()470行 475行 判断是否按下ctrl+c快捷键,并作出操作 522~562行 对执行结果进行处理,计入统计信息 564行 如果net_state为NETLOOP_SUCCESS、NETLOOP_FAIL最终都会进入done,从而置空udp回调函数 如果net_state为NETLOOP_CONTINUE,表明仍然有后续数据包要接收,则回到461行,继续下一个数据包的接收

至此DNS协议的处理流程分析完毕,大家可以根据这个流程自行分析其他几个协议的处理流程。

容易遇到的问题

有的时候能读取到 DM9000A 的 ID,连续操作就能读取到 DM9000A 的 ID,但间隔一会操作就读取不到 DM9000A 的 ID,通过调试,在 dm9000_reset 函数中加一句延时操作,就可以正常读取 DM9000A 的 ID 了。

  1. 277     do { 
  2. 278         DM9000_DBG("resetting the DM9000, 2nd reset\n"); 
  3. 279         udelay(25);  
  4. 280     } while (DM9000_ior(DM9000_NCR) & 1); 
  5. 281     udelay(150); 
  6. 282      
  7. 283     if ((DM9000_ior(DM9000_PIDL) != 0x0) || 
  8. 284         (DM9000_ior(DM9000_PIDH) != 0x90)) 
  9. 285         printf("ERROR: resetting DM9000 -> not responding\n"); 

 

来源:一口Linux内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯