一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

電腦之家 - 專業計算機基礎知識與電腦技術學習網站
分類導航

路由器|交換機|網絡協議|網絡知識|

服務器之家 - 電腦之家 - 網絡技術 - 網絡協議 - 從0學ARM-uboot中的網絡協議棧

從0學ARM-uboot中的網絡協議棧

2021-05-26 01:04一口Linux土豆居士 網絡協議

網卡的驅動,對于上層協議來說,已經封裝好了發送和接收數據的接口,那么上層協議棧只需要按照順序調用對應的網卡驅動函數就可以進行網絡數據的收發。

從0學ARM-uboot中的網絡協議棧

uboot中網絡協議棧

 

網卡的驅動,對于上層協議來說,已經封裝好了發送和接收數據的接口,那么上層協議棧只需要按照順序調用對應的網卡驅動函數就可以進行網絡數據的收發。

uboot中的協議棧相對來說比較簡單,有以下幾個特點:

傳輸層只支持UDP協議;

  • 目前只支持ICMP、TFTP、NFS、DNS、DHCP、CDP、SNTP等幾種協議;
  • 網卡采用poll接收數據包,而不是中斷方式;
  • 數據包的發送和接收操作是串行操作,不支持并發。

1. 網絡協議棧架構

下面是uboot網絡協議棧的函數調用流程:

從0學ARM-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. 318  *  Main network processing loop. 
  4. 319  */ 
  5. 320  
  6. 321 int NetLoop(enum proto_t protocol) 
  7. 322 { 
  8. 323     bd_t *bd = gd->bd; 
  9. 324     int ret = -1; 
  10. ………… 
  11. 352     NetInitLoop(); 
  12.  ………… 
  13. 367         switch (protocol) { 
  14. 368         case TFTPGET: 
  15. 369 #ifdef CONFIG_CMD_TFTPPUT 
  16. 370         case TFTPPUT: 
  17. 371 #endif 
  18. 372             /* always use ARP to get server ethernet address */ 
  19. 373             TftpStart(protocol); 
  20. 374             break; 
  21.  ………… 
  22. 426 #if defined(CONFIG_CMD_DNS) 
  23. 427         case DNS: 
  24. 428             DnsStart(); 
  25. 429             break; 
  26. 430 #endif 
  27. 438     } 
  28. ………… 
  29. 461     for (;;) { 
  30. 462         WATCHDOG_RESET(); 
  31. 463 #ifdef CONFIG_SHOW_ACTIVITY 
  32. 464         show_activity(1); 
  33. 465 #endif 
  34. 466         /* 
  35. 467          *  Check the ethernet for a new packet.  The ethernet 
  36. 468          *  receive routine will process it. 
  37. 469          */ 
  38. 470         eth_rx(); 
  39. 471  
  40. 472         /* 
  41. 473          *  Abort if ctrl-c was pressed. 
  42. 474          */ 
  43. 475         if (ctrlc()) { 
  44. 476             /* cancel any ARP that may not have completed */ 
  45. 477             NetArpWaitPacketIP = 0; 
  46. 478  
  47. 479             net_cleanup_loop(); 
  48. 480             eth_halt(); 
  49. 481             /* Invalidate the last protocol */ 
  50. 482             eth_set_last_protocol(BOOTP); 
  51. 483  
  52. 484             puts("\nAbort\n"); 
  53. 485             /* include a debug print as well incase the debug 
  54. 486                messages are directed to stderr */ 
  55. 487             debug_cond(DEBUG_INT_STATE, "--- NetLoop Abort!\n"); 
  56. 488             goto done; 
  57. 489         } 
  58.  ………… 
  59. 522         switch (net_state) { 
  60. 523  
  61. 524         case NETLOOP_RESTART: 
  62. 525             NetRestarted = 1; 
  63. 526             goto restart; 
  64. 527  
  65. 528         case NETLOOP_SUCCESS: 
  66. 529             net_cleanup_loop(); 
  67. 530             if (NetBootFileXferSize > 0) { 
  68. 531                 char buf[20]; 
  69. 532                 printf("Bytes transferred = %ld (%lx hex)\n"
  70. 533                     NetBootFileXferSize, 
  71. 534                     NetBootFileXferSize); 
  72. 535                 sprintf(buf, "%lX", NetBootFileXferSize); 
  73. 536                 setenv("filesize", buf); 
  74. 537  
  75. 538                 sprintf(buf, "%lX", (unsigned long)load_addr); 
  76. 539                 setenv("fileaddr", buf); 
  77. 540             } 
  78. 541             if (protocol != NETCONS) 
  79. 542                 eth_halt(); 
  80. 543             else 
  81. 544                 eth_halt_state_only(); 
  82. 545  
  83. 546             eth_set_last_protocol(protocol); 
  84. 547  
  85. 548             ret = NetBootFileXferSize; 
  86. 549             debug_cond(DEBUG_INT_STATE, "--- NetLoop Success!\n"); 
  87. 550             goto done; 
  88. 551  
  89. 552         case NETLOOP_FAIL: 
  90. 553             net_cleanup_loop(); 
  91. 554             /* Invalidate the last protocol */ 
  92. 555             eth_set_last_protocol(BOOTP); 
  93. 556             debug_cond(DEBUG_INT_STATE, "--- NetLoop Fail!\n"); 
  94. 557             goto done; 
  95. 558  
  96. 559         case NETLOOP_CONTINUE: 
  97. 560             continue
  98. 561         } 
  99. 562     } 
  100. 563  
  101. 564 done: 
  102. 565 #ifdef CONFIG_CMD_TFTPPUT 
  103. 566     /* Clear out the handlers */ 
  104. 567     net_set_udp_handler(NULL); 
  105. 568     net_set_icmp_handler(NULL); 
  106. 569 #endif 
  107. 570     return ret; 
  108. 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.  /* update only when the environment has changed */ 
  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     /* Prepare DNS packet header */ 
  15. 51     header           = (struct header *) pkt; 
  16. 52     header->tid      = 1; 
  17. 53     header->flags    = htons(0x100);    /* standard query */ 
  18. 54     header->nqueries = htons(1);        /* Just one query */ 
  19. 55     header->nanswers = 0; 
  20. 56     header->nauth    = 0; 
  21. 57     header->nother   = 0; 
  22. 58  
  23. 59     /* Encode DNS name */ 
  24. 60     name_len = strlen(name); 
  25. 61     p = (uchar *) &header->data;    /* For encoding host name into packet */ 
  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;           /* Chunk length */ 
  33. 69         *p++ = n;           /* Copy length  */ 
  34. 70         memcpy(p, name, n);     /* Copy chunk   */ 
  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;           /* Mark end of host name */ 
  45. 81     *p++ = 0;           /* Some servers require double null */ 
  46. 82     *p++ = (unsigned char) qtype;   /* Query Type */ 
  47. 83  
  48. 84     *p++ = 0; 
  49. 85     *p++ = 1;               /* Class: inet, 0x0001 */ 
  50. 86  
  51. 87     n = p - pkt;                /* Total packet length */ 
  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     /* make sure the NetTxPacket is initialized (NetInit() was called) */ 
  9. 696     assert(NetTxPacket != NULL); 
  10. 697     if (NetTxPacket == NULL
  11. 698         return -1; 
  12. 699  
  13. 700     /* convert to new style broadcast */ 
  14. 701     if (dest == 0) 
  15. 702         dest = 0xFFFFFFFF; 
  16. 703  
  17. 704     /* if broadcast, make the ether address a broadcast and don't do ARP */ 
  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     /* if MAC address was not discovered yet, do an ARP request */ 
  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         /* save the ip and eth addr for the packet to send after arp */ 
  33. 720         NetArpWaitPacketIP = dest; 
  34. 721         NetArpWaitPacketMAC = ether; 
  35. 722  
  36. 723         /* size of the waiting packet */ 
  37. 724         NetArpWaitTxPacketSize = pkt_hdr_size + payload_len; 
  38. 725  
  39. 726         /* and do the ARP request */ 
  40. 727         NetArpWaitTry = 1; 
  41. 728         NetArpWaitTimerStart = get_timer(0); 
  42. 729         ArpRequest(); 
  43. 730         return 1;   /* waiting */ 
  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;   /* transmitted */ 
  49. 736     } 
  50. 737 } 

696~706行 參數檢查 710行 設置以太頭 713行 設置udp協議頭 716~730行 如果沒有目的MAC地址,就要先發送ARP請求 734行 調用函數NetSendPacket(),參數分別是:要發送數據幀的首地址,數據包長度

  1. 529 /* Transmit a packet */   
  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     /* too small packet? */ 
  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) {    /* normal packet */ 
  28.  999         ip = (struct ip_udp_hdr *)(inpkt + ETHER_HDR_SIZE); 
  29. 1000         len -= ETHER_HDR_SIZE; 
  30. 1001  
  31. 1002     } else {            /* VLAN packet */ 
  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         /* Before we start poking the header, make sure it is there */ 
  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         /* Check the packet length */ 
  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         /* Can't deal with anything except IPv4 */ 
  56. 1074         if ((ip->ip_hl_v & 0xf0) != 0x40) 
  57. 1075             return
  58. 1076         /* Can't deal with IP options (headers != 20 bytes) */ 
  59. 1077         if ((ip->ip_hl_v & 0x0f) > 0x05) 
  60. 1078             return
  61. 1079         /* Check the Checksum of the header */ 
  62. 1080         if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE / 2)) { 
  63. 1081             debug("checksum bad\n"); 
  64. 1082             return
  65. 1083         } 
  66. 1084         /* If it is not for us, ignore it */ 
  67. 1085         dst_ip = NetReadIP(&ip->ip_dst); 
  68.  
  69. 1092         /* Read source IP address for later use */ 
  70. 1093         src_ip = NetReadIP(&ip->ip_src); 
  71.  
  72.  
  73. 1184         /* 
  74. 1185          *  IP header OK.  Pass the packet to the current handler. 
  75. 1186          */ 
  76. 1187         (*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE, 
  77. 1188                 ntohs(ip->udp_dst), 
  78. 1189                 src_ip, 
  79. 1190                 ntohs(ip->udp_src), 
  80. 1191                 ntohs(ip->udp_len) - UDP_HDR_SIZE); 
  81. 1192         break; 
  82. 1193     } 
  83. 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); /* Wait at least 20 us */ 
  4. 280     } while (DM9000_ior(DM9000_NCR) & 1); 
  5. 281     udelay(150); 
  6. 282     /* Check whether the ethernet controller is present */ 
  7. 283     if ((DM9000_ior(DM9000_PIDL) != 0x0) || 
  8. 284         (DM9000_ior(DM9000_PIDH) != 0x90)) 
  9. 285         printf("ERROR: resetting DM9000 -> not responding\n"); 

原文鏈接:https://mp.weixin.qq.com/s/ZdwtDspSoByuEshue6dOLg

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美亚洲国产另类 | 精品在线播放 | 欧美色图亚洲天堂 | 国产剧情一区 | 无限在线观看免费入口 | 调教女秘书 | 亚洲天堂岛国片 | 久久精品亚洲牛牛影视 | 2021麻豆剧果冻传媒入口永久 | 精品久久久久久久久久香蕉 | 精品无人区一区二区三区 | www.av免费 | 久久久高清国产999尤物 | 精东影业传媒全部作品 | 久久久久久久久人体 | 久久这里只精品热在线18 | anal22日本人视频 | 双性np肉文| 热久久99精品这里有精品 | 国产免费福利片 | 精品一区二区三区免费站 | 欧美一区二区三区免费看 | 99综合在线| 奇米影视在线视频 | 忘忧草高清 | 久久精品国产免费播高清无卡 | 九九热视频免费 | 毛片网站大全 | 男人天堂999 | 亚洲成在人线视频 | 久久午夜夜伦痒痒想咳嗽P 久久无码AV亚洲精品色午夜麻豆 | 亚洲欧美日韩综合在线 | 色网在线观看 | 日本三级欧美三级人妇英文 | 俄罗斯15一16处交 | 四虎永久免费地址在线观看 | 国产精品免费观在线 | 我的男友是消防员在线观看 | 国产1区二区| 欧美又大又粗又长又硬 | 国产美女在线一区二区三区 |