加入星計劃,您可以享受以下權益:

  • 創(chuàng)作內容快速變現
  • 行業(yè)影響力擴散
  • 作品版權保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 1. 網絡協(xié)議棧架構
    • 2. 通過DNS命令來解析一個數據包的收發(fā)流程
  • 相關推薦
  • 電子產業(yè)圖譜
申請入駐 產業(yè)圖譜

從0學ARM-uboot中的網絡協(xié)議棧

2021/05/26
463
閱讀需 41 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

本篇是從0學ARM系列文章的最后一篇,后面暫無更新計劃。

想學習ARM知識,建議收藏下面的合集的鏈接。

《從0學ARM》

uboot中網絡協(xié)議棧

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

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

  1. 傳輸層只支持UDP協(xié)議;目前只支持ICMPTFTP、NFS、DNS、DHCP、CDP、SNTP等幾種協(xié)議;網卡采用poll接收數據包,而不是中斷方式;數據包的發(fā)送和接收操作是串行操作,不支持并發(fā)。

1. 網絡協(xié)議棧架構

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

2. 通過DNS命令來解析一個數據包的收發(fā)流程

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

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

當我們在uboot的命令終端輸入命令dns后,命令解析函數就會調用dns執(zhí)行函數do_dns()

389 int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
390 {
 ……
406     if (strlen(argv[1]) >= 255) {
407         printf("dns error: hostname too longn");
408         return 1;
409     }
410 
411     NetDNSResolve = argv[1];
412 
413     if (argc == 3)
414         NetDNSenvvar = argv[2];
415     else
416         NetDNSenvvar = NULL;
417 
418     if (NetLoop(DNS) < 0) {
419         printf("dns lookup of %s failed, check setupn", argv[1]);
420         return 1;
421     }
422 
423     return 0;
424 }

406行 判斷參數字符串長度,大于255非法 411行 參數1必須是要解析的主機,存儲在NetDNSResolve 中 413~416行 保存dns命令的環(huán)境參數,該參數可以沒有 418行 進入網絡協(xié)議處理函數入口NetLoop(),并將對應的協(xié)議DNS傳遞給該函數

NetLoop()代碼比較長,我們只分析其中比較重要的幾段代碼

 316 /**********************************************************************/
 317 /*
 318  *  Main network processing loop.
 319  */
 320 
 321 int NetLoop(enum proto_t protocol)
 322 {
 323     bd_t *bd = gd->bd;
 324     int ret = -1;
 …………
 352     NetInitLoop();
  …………
 367         switch (protocol) {
 368         case TFTPGET:
 369 #ifdef CONFIG_CMD_TFTPPUT
 370         case TFTPPUT:
 371 #endif
 372             /* always use ARP to get server ethernet address */
 373             TftpStart(protocol);
 374             break;
  …………
 426 #if defined(CONFIG_CMD_DNS)
 427         case DNS:
 428             DnsStart();
 429             break;
 430 #endif
 438     }
 …………
 461     for (;;) {
 462         WATCHDOG_RESET();
 463 #ifdef CONFIG_SHOW_ACTIVITY
 464         show_activity(1);
 465 #endif
 466         /*
 467          *  Check the ethernet for a new packet.  The ethernet
 468          *  receive routine will process it.
 469          */
 470         eth_rx();
 471 
 472         /*
 473          *  Abort if ctrl-c was pressed.
 474          */
 475         if (ctrlc()) {
 476             /* cancel any ARP that may not have completed */
 477             NetArpWaitPacketIP = 0;
 478 
 479             net_cleanup_loop();
 480             eth_halt();
 481             /* Invalidate the last protocol */
 482             eth_set_last_protocol(BOOTP);
 483 
 484             puts("nAbortn");
 485             /* include a debug print as well incase the debug
 486                messages are directed to stderr */
 487             debug_cond(DEBUG_INT_STATE, "--- NetLoop Abort!n");
 488             goto done;
 489         }
  …………
 522         switch (net_state) {
 523 
 524         case NETLOOP_RESTART:
 525             NetRestarted = 1;
 526             goto restart;
 527 
 528         case NETLOOP_SUCCESS:
 529             net_cleanup_loop();
 530             if (NetBootFileXferSize > 0) {
 531                 char buf[20];
 532                 printf("Bytes transferred = %ld (%lx hex)n",
 533                     NetBootFileXferSize,
 534                     NetBootFileXferSize);
 535                 sprintf(buf, "%lX", NetBootFileXferSize);
 536                 setenv("filesize", buf);
 537 
 538                 sprintf(buf, "%lX", (unsigned long)load_addr);
 539                 setenv("fileaddr", buf);
 540             }
 541             if (protocol != NETCONS)
 542                 eth_halt();
 543             else
 544                 eth_halt_state_only();
 545 
 546             eth_set_last_protocol(protocol);
 547 
 548             ret = NetBootFileXferSize;
 549             debug_cond(DEBUG_INT_STATE, "--- NetLoop Success!n");
 550             goto done;
 551 
 552         case NETLOOP_FAIL:
 553             net_cleanup_loop();
 554             /* Invalidate the last protocol */
 555             eth_set_last_protocol(BOOTP);
 556             debug_cond(DEBUG_INT_STATE, "--- NetLoop Fail!n");
 557             goto done;
 558 
 559         case NETLOOP_CONTINUE:
 560             continue;
 561         }
 562     }
 563 
 564 done:
 565 #ifdef CONFIG_CMD_TFTPPUT
 566     /* Clear out the handlers */
 567     net_set_udp_handler(NULL);
 568     net_set_icmp_handler(NULL);
 569 #endif
 570     return ret;
 571 }

函數參數為DNS 352行 初始化網絡信息,讀取ipaddr、gatewayip、netmask、serverip、dnsip等環(huán)境變量的值并復制到對應的全局變量中

static void NetInitLoop(void)
{
 static int env_changed_id;
 int env_id = get_env_id();

 /* update only when the environment has changed */
 if (env_changed_id != env_id) {
  NetOurIP = getenv_IPaddr("ipaddr");
  NetOurGatewayIP = getenv_IPaddr("gatewayip");
  NetOurSubnetMask = getenv_IPaddr("netmask");
  NetServerIP = getenv_IPaddr("serverip");
  NetOurNativeVLAN = getenv_VLAN("nvlan");
  NetOurVLAN = getenv_VLAN("vlan");
#if defined(CONFIG_CMD_DNS)
  NetOurDNSIP = getenv_IPaddr("dnsip");
#endif
  env_changed_id = env_id;
 }
 memcpy(NetOurEther, eth_get_dev()->enetaddr, 6);

 return;
}

367行 對傳入的參數做switch操作,不同的協(xié)議進入到不同的處理流程 428行 執(zhí)行DnsStart(),

197 void
198 DnsStart(void)
199 {
200     debug("%sn", __func__);
201 
202     NetSetTimeout(DNS_TIMEOUT, DnsTimeout);
203     net_set_udp_handler(DnsHandler);
204 
205     DnsSend();
206 } 

203行 函數net_set_udp_handler()主要將dns協(xié)議的回調函數DnsHandler()注冊到udp協(xié)議的回調指針udp_packet_handler,

void net_set_udp_handler(rxhand_f *f)
{
 debug_cond(DEBUG_INT_STATE, "--- NetLoop UDP handler set (%p)n", f);
 if (f == NULL)
  udp_packet_handler = dummy_handler;//注冊到udp協(xié)議回調函數指針
 else
  udp_packet_handler = f;
}

DnsStart()最終會調用函數DnsSend()發(fā)送dns協(xié)議數據包,該函數是根據dns協(xié)議填充udp數據包

 37 static void
 38 DnsSend(void)
 39 {
 40     struct header *header;
 41     int n, name_len;
 42     uchar *p, *pkt;
 43     const char *s;
 44     const char *name;
 45     enum dns_query_type qtype = DNS_A_RECORD;
 46 
 47     name = NetDNSResolve;
 48     pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE);
 49 
 50     /* Prepare DNS packet header */
 51     header           = (struct header *) pkt;
 52     header->tid      = 1;
 53     header->flags    = htons(0x100);    /* standard query */
 54     header->nqueries = htons(1);        /* Just one query */
 55     header->nanswers = 0;
 56     header->nauth    = 0;
 57     header->nother   = 0;
 58 
 59     /* Encode DNS name */
 60     name_len = strlen(name);
 61     p = (uchar *) &header->data;    /* For encoding host name into packet */
 62 
 63     do {
 64         s = strchr(name, '.');
 65         if (!s)
 66             s = name + name_len;
 67 
 68         n = s - name;           /* Chunk length */
 69         *p++ = n;           /* Copy length  */
 70         memcpy(p, name, n);     /* Copy chunk   */
 71         p += n;
 72 
 73         if (*s == '.')
 74             n++;
 75 
 76         name += n;
 77         name_len -= n;
 78     } while (*s != '