加入星計(jì)劃,您可以享受以下權(quán)益:

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

從0學(xué)ARM-uboot中的網(wǎng)絡(luò)協(xié)議棧

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

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

想學(xué)習(xí)ARM知識(shí),建議收藏下面的合集的鏈接。

《從0學(xué)ARM》

uboot中網(wǎng)絡(luò)協(xié)議棧

網(wǎng)卡的驅(qū)動(dòng),對于上層協(xié)議來說,已經(jīng)封裝好了發(fā)送和接收數(shù)據(jù)的接口,那么上層協(xié)議棧只需要按照順序調(diào)用對應(yīng)的網(wǎng)卡驅(qū)動(dòng)函數(shù)就可以進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)。

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

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

1. 網(wǎng)絡(luò)協(xié)議棧架構(gòu)

下面是uboot網(wǎng)絡(luò)協(xié)議棧的函數(shù)調(diào)用流程:

2. 通過DNS命令來解析一個(gè)數(shù)據(jù)包的收發(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 );

當(dāng)我們在uboot的命令終端輸入命令dns后,命令解析函數(shù)就會(huì)調(diào)用dns執(zhí)行函數(shù)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行 判斷參數(shù)字符串長度,大于255非法 411行 參數(shù)1必須是要解析的主機(jī),存儲(chǔ)在NetDNSResolve 中 413~416行 保存dns命令的環(huán)境參數(shù),該參數(shù)可以沒有 418行 進(jìn)入網(wǎng)絡(luò)協(xié)議處理函數(shù)入口NetLoop(),并將對應(yīng)的協(xié)議DNS傳遞給該函數(shù)

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 }

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

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行 對傳入的參數(shù)做switch操作,不同的協(xié)議進(jìn)入到不同的處理流程 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行 函數(shù)net_set_udp_handler()主要將dns協(xié)議的回調(diào)函數(shù)DnsHandler()注冊到udp協(xié)議的回調(diào)指針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é)議回調(diào)函數(shù)指針
 else
  udp_packet_handler = f;
}

DnsStart()最終會(huì)調(diào)用函數(shù)DnsSend()發(fā)送dns協(xié)議數(shù)據(jù)包,該函數(shù)是根據(jù)dns協(xié)議填充udp數(shù)據(jù)包

 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 != '