從0學(xué)ARM-uboot中的網(wǎng)絡(luò)協(xié)議棧
uboot中網(wǎng)絡(luò)協(xié)議棧
網(wǎng)卡的驅(qū)動(dòng),對(duì)于上層協(xié)議來(lái)說(shuō),已經(jīng)封裝好了發(fā)送和接收數(shù)據(jù)的接口,那么上層協(xié)議棧只需要按照順序調(diào)用對(duì)應(yīng)的網(wǎng)卡驅(qū)動(dòng)函數(shù)就可以進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)。
uboot中的協(xié)議棧相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,有以下幾個(gè)特點(diǎn):
傳輸層只支持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. 通過(guò)DNS命令來(lái)解析一個(gè)數(shù)據(jù)包的收發(fā)流程
uboot中,所有的命令都用宏U_BOOT_CMD來(lái)定義, dns命令的定義如下:
- 426 U_BOOT_CMD(
- 427 dns, 3, 1, do_dns,
- 428 "lookup the IP of a hostname",
- 429 "hostname [envvar]"
- 430 );
當(dāng)我們?cè)趗boot的命令終端輸入命令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 long\n");
- 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 setup\n", argv[1]);
- 420 return 1;
- 421 }
- 422
- 423 return 0;
- 424 }
406行 判斷參數(shù)字符串長(zhǎng)度,大于255非法 411行 參數(shù)1必須是要解析的主機(jī),存儲(chǔ)在NetDNSResolve 中 413~416行 保存dns命令的環(huán)境參數(shù),該參數(shù)可以沒(méi)有 418行 進(jìn)入網(wǎng)絡(luò)協(xié)議處理函數(shù)入口NetLoop(),并將對(duì)應(yīng)的協(xié)議DNS傳遞給該函數(shù)
NetLoop()代碼比較長(zhǎng),我們只分析其中比較重要的幾段代碼
- 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("\nAbort\n");
- 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ù)制到對(duì)應(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行 對(duì)傳入的參數(shù)做switch操作,不同的協(xié)議進(jìn)入到不同的處理流程 428行 執(zhí)行DnsStart(),
- 197 void
- 198 DnsStart(void)
- 199 {
- 200 debug("%s\n", __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()注冊(cè)到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;//注冊(cè)到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 != '\0');
- 79
- 80 *p++ = 0; /* Mark end of host name */
- 81 *p++ = 0; /* Some servers require double null */
- 82 *p++ = (unsigned char) qtype; /* Query Type */
- 83
- 84 *p++ = 0;
- 85 *p++ = 1; /* Class: inet, 0x0001 */
- 86
- 87 n = p - pkt; /* Total packet length */
- 88 debug("Packet size %d\n", n);
- 89
- 90 DnsOurPort = random_port();
- 91
- 92 NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT,
- 93 DnsOurPort, n);
- 94 debug("DNS packet sent\n");
- 95 }
51~57行 根據(jù)dns協(xié)議填充dns協(xié)議頭,數(shù)據(jù)幀首地址為NetTxPacket,此處通過(guò)指針pkt和p來(lái)填充dns數(shù)據(jù)幀 60~85行 根據(jù)協(xié)議格式要求填充要解析的host名字到數(shù)據(jù)包 87行 計(jì)算數(shù)據(jù)包長(zhǎng)度 90行 產(chǎn)生一個(gè)隨機(jī)的端口號(hào) 92~93行 調(diào)用udp協(xié)議的發(fā)送函數(shù)NetSendUDPPacket(),參數(shù)依次是:以太頭信息,DNS服務(wù)器 ip地址,DNS服務(wù)器端口號(hào),我們的dns服務(wù)端口號(hào),數(shù)據(jù)包長(zhǎng)度
- 688 int NetSendUDPPacket(uchar *ether, IPaddr_t dest, int dport, int sport,
- 689 int payload_len)
- 690 {
- 691 uchar *pkt;
- 692 int eth_hdr_size;
- 693 int pkt_hdr_size;
- 694
- 695 /* make sure the NetTxPacket is initialized (NetInit() was called) */
- 696 assert(NetTxPacket != NULL);
- 697 if (NetTxPacket == NULL)
- 698 return -1;
- 699
- 700 /* convert to new style broadcast */
- 701 if (dest == 0)
- 702 dest = 0xFFFFFFFF;
- 703
- 704 /* if broadcast, make the ether address a broadcast and don't do ARP */
- 705 if (dest == 0xFFFFFFFF)
- 706 ether = NetBcastAddr;
- 707
- 708 pkt = (uchar *)NetTxPacket;
- 709
- 710 eth_hdr_size = NetSetEther(pkt, ether, PROT_IP);
- 711 pkt += eth_hdr_size;
- 712 net_set_udp_header(pkt, dest, dport, sport, payload_len);
- 713 pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
- 714
- 715 /* if MAC address was not discovered yet, do an ARP request */
- 716 if (memcmp(ether, NetEtherNullAddr, 6) == 0) {
- 717 debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &dest);
- 718
- 719 /* save the ip and eth addr for the packet to send after arp */
- 720 NetArpWaitPacketIP = dest;
- 721 NetArpWaitPacketMAC = ether;
- 722
- 723 /* size of the waiting packet */
- 724 NetArpWaitTxPacketSize = pkt_hdr_size + payload_len;
- 725
- 726 /* and do the ARP request */
- 727 NetArpWaitTry = 1;
- 728 NetArpWaitTimerStart = get_timer(0);
- 729 ArpRequest();
- 730 return 1; /* waiting */
- 731 } else {
- 732 debug_cond(DEBUG_DEV_PKT, "sending UDP to %pI4/%pM\n",
- 733 &dest, ether);
- 734 NetSendPacket(NetTxPacket, pkt_hdr_size + payload_len);
- 735 return 0; /* transmitted */
- 736 }
- 737 }
696~706行 參數(shù)檢查 710行 設(shè)置以太頭 713行 設(shè)置udp協(xié)議頭 716~730行 如果沒(méi)有目的MAC地址,就要先發(fā)送ARP請(qǐng)求 734行 調(diào)用函數(shù)NetSendPacket(),參數(shù)分別是:要發(fā)送數(shù)據(jù)幀的首地址,數(shù)據(jù)包長(zhǎng)度
- 529 /* Transmit a packet */
- 530 static inline void NetSendPacket(uchar *pkt, int len)
- 531 {
- 532 (void) eth_send(pkt, len);
- 533 }
532行 調(diào)用我們注冊(cè)的函數(shù)dm9000_send() 該函數(shù)已經(jīng)分析過(guò),根據(jù)流程圖,回到函數(shù)NetLoop()
461~562行 循環(huán)接收網(wǎng)絡(luò)數(shù)據(jù)包 470行 調(diào)用網(wǎng)卡驅(qū)動(dòng)接收函數(shù)eth_rx()
- int eth_rx(void)
- {
- if (!eth_current)
- return -1;
- return eth_current->recv(eth_current);
- }
eth_current->recv(eth_current)函數(shù)就是我們注冊(cè)的網(wǎng)卡的接收函數(shù)dm9000_rx(),該函數(shù)我們上一章已經(jīng)分析過(guò),最終通過(guò)調(diào)用函數(shù)NetReceive(),將數(shù)據(jù)幀上傳到協(xié)議棧
- 943 void
- 944 NetReceive(uchar *inpkt, int len)
- 945 {
- 946 struct ethernet_hdr *et;
- 947 struct ip_udp_hdr *ip;
- 948 IPaddr_t dst_ip;
- 949 IPaddr_t src_ip;
- 950 int eth_proto;
- ……
- 957
- 958 NetRxPacket = inpkt;
- 959 NetRxPacketLen = len;
- 960 et = (struct ethernet_hdr *)inpkt;
- 961
- 962 /* too small packet? */
- 963 if (len < ETHER_HDR_SIZE)
- 964 return;
- 965
- ……
- 984
- 985 eth_proto = ntohs(et->et_protlen);
- 986
- 987 if (eth_proto < 1514) {
- 988 struct e802_hdr *et802 = (struct e802_hdr *)et;
- ……
- 997
- 998 } else if (eth_proto != PROT_VLAN) { /* normal packet */
- 999 ip = (struct ip_udp_hdr *)(inpkt + ETHER_HDR_SIZE);
- 1000 len -= ETHER_HDR_SIZE;
- 1001
- 1002 } else { /* VLAN packet */
- ……
- 1026 }
- 1027
- ……
- 1045 switch (eth_proto) {
- ……
- 1056 case PROT_IP:
- 1057 debug_cond(DEBUG_NET_PKT, "Got IP\n");
- 1058 /* Before we start poking the header, make sure it is there */
- 1059 if (len < IP_UDP_HDR_SIZE) {
- 1060 debug("len bad %d < %lu\n", len,
- 1061 (ulong)IP_UDP_HDR_SIZE);
- 1062 return;
- 1063 }
- 1064 /* Check the packet length */
- 1065 if (len < ntohs(ip->ip_len)) {
- 1066 debug("len bad %d < %d\n", len, ntohs(ip->ip_len));
- 1067 return;
- 1068 }
- 1069 len = ntohs(ip->ip_len);
- 1070 debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
- 1071 len, ip->ip_hl_v & 0xff);
- 1072
- 1073 /* Can't deal with anything except IPv4 */
- 1074 if ((ip->ip_hl_v & 0xf0) != 0x40)
- 1075 return;
- 1076 /* Can't deal with IP options (headers != 20 bytes) */
- 1077 if ((ip->ip_hl_v & 0x0f) > 0x05)
- 1078 return;
- 1079 /* Check the Checksum of the header */
- 1080 if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE / 2)) {
- 1081 debug("checksum bad\n");
- 1082 return;
- 1083 }
- 1084 /* If it is not for us, ignore it */
- 1085 dst_ip = NetReadIP(&ip->ip_dst);
- 1092 /* Read source IP address for later use */
- 1093 src_ip = NetReadIP(&ip->ip_src);
- 1184 /*
- 1185 * IP header OK. Pass the packet to the current handler.
- 1186 */
- 1187 (*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE,
- 1188 ntohs(ip->udp_dst),
- 1189 src_ip,
- 1190 ntohs(ip->udp_src),
- 1191 ntohs(ip->udp_len) - UDP_HDR_SIZE);
- 1192 break;
- 1193 }
- 1194 }
參數(shù)inpkt:指向接收到的以太數(shù)據(jù)包頭 len:接收到的數(shù)據(jù)包的長(zhǎng)度 960行 變量NetRxPacket指向接收的數(shù)據(jù)頭,以太數(shù)據(jù)包包頭比定位以太協(xié)議頭 985行 從以太協(xié)議頭提取出協(xié)議字段,該字段表示后面是否是ip協(xié)議 999行 解析出ip協(xié)議頭 1045行 根據(jù)以太頭協(xié)議進(jìn)行switch操作 1059~1083行 對(duì)協(xié)議頭進(jìn)行合法性檢查 1085行 讀取出目的ip地址 1093行 讀取出源ip地址, 1187行 ip協(xié)議頭解析成功,調(diào)用udp協(xié)議回調(diào)函數(shù)udp_packet_handler(),該函數(shù)在之前的DnStart()注冊(cè)了DnsHandler
- 104 static void
- 105 DnsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len)
- 106 {
- 193
- 194 net_set_state(NETLOOP_SUCCESS);
- 195 }
該函數(shù)用于解析DNS協(xié)議,在此不再詳解 解析成功后194行,會(huì)設(shè)置當(dāng)前執(zhí)行狀態(tài)為NETLOOP_SUCCESS, 代碼回到函數(shù)NetLoop()470行 475行 判斷是否按下ctrl+c快捷鍵,并作出操作 522~562行 對(duì)執(zhí)行結(jié)果進(jìn)行處理,計(jì)入統(tǒng)計(jì)信息 564行 如果net_state為NETLOOP_SUCCESS、NETLOOP_FAIL最終都會(huì)進(jìn)入done,從而置空udp回調(diào)函數(shù) 如果net_state為NETLOOP_CONTINUE,表明仍然有后續(xù)數(shù)據(jù)包要接收,則回到461行,繼續(xù)下一個(gè)數(shù)據(jù)包的接收
至此DNS協(xié)議的處理流程分析完畢,大家可以根據(jù)這個(gè)流程自行分析其他幾個(gè)協(xié)議的處理流程。
容易遇到的問(wèn)題
有的時(shí)候能讀取到 DM9000A 的 ID,連續(xù)操作就能讀取到 DM9000A 的 ID,但間隔一會(huì)操作就讀取不到 DM9000A 的 ID,通過(guò)調(diào)試,在 dm9000_reset 函數(shù)中加一句延時(shí)操作,就可以正常讀取 DM9000A 的 ID 了。
- 277 do {
- 278 DM9000_DBG("resetting the DM9000, 2nd reset\n");
- 279 udelay(25); /* Wait at least 20 us */
- 280 } while (DM9000_ior(DM9000_NCR) & 1);
- 281 udelay(150);
- 282 /* Check whether the ethernet controller is present */
- 283 if ((DM9000_ior(DM9000_PIDL) != 0x0) ||
- 284 (DM9000_ior(DM9000_PIDH) != 0x90))
- 285 printf("ERROR: resetting DM9000 -> not responding\n");