当前位置: 首页 > news >正文

uboot添加ping命令的响应处理

一、前言

🔍在uboot里使用tftp和nfs来启动linux系统是常用的方式,在搭建好网络环境后,我们经常会使用ping命令测试开发板和虚拟机之间的网络,但是能够发现,虚拟机或其它对uboot是ping不通的,原因是uboot里对ping请求包没有进行响应处理。那么,勤奋好学的我们便自然而然会想这是为什么呢,该怎么在uboot里添加对ping请求包的响应,让外界也能ping通uboot呢?
🔍ping请求包的说法实际上并不合适,因为用ping命令来测试网络通断,实际上使用的是ICMP协议。

二、ICMP协议

🔍ICMP协议主要用于在网络中传递控制消息、错误报告和诊断信息,在OSI网络模型里属于网络层协议,是IP协议的辅助协议,不传输用户数据。它的报文分为两种:①差错报告、②查询报文;使用ping命令时,用wireshark抓包可以看到,常见的报文是Echo Reply、Echo Request和Destination Unreachable。
在这里插入图片描述
🔍Echo Request和Echo Reply:目标请求和响应报文,属于查询报文。在windows ping其它局域网下的用网设备,收到响应报文Echo Reply,一般我们才视为ping通。

C:\Users\Administrator.DESKTOP-O6LBFD9>ping 192.168.6.98
正在 Ping 192.168.6.98 具有 32 字节的数据:
来自 192.168.6.98 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.6.98 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.6.98 的回复: 字节=32 时间<1ms TTL=64

🔍在uboot中,一般都没有响应报文,也就是我们在虚拟机ping uboot的时候,看到ping不通,实际上网络是通的,通过抓包可以看到,Request报文是成功发送到uboot的,只是没有收到回复,超时之后就会收到Destination Unreachable。
在这里插入图片描述

三、uboot里的ping命令

🔍了解完ICMP协议,就来简单看看uboot里是怎么实现ping命令的,以下源码阅读顺序自上而下,从最外层调用往里看的。

3.1 uboot的ping命令定义

⚡定义位置:cmd/net.c
⚡以下代码将ping命令注册uboot的命令行对应模块,主要通过U_BOOT_CMD( )这个宏来实现,其参数含义如下:

📌 命令名:ping
📌 参数个数:2个(包括命令本身,类似argc)
📌 是否可重复执行:1表示true
📌 调用函数:do_ping( )
📌 帮助信息

⚡调用的do_ping( )内容比较简单,核心就是net_loop( )这个函数,实现发送ICMP包核心都在这里。


#if defined(CONFIG_CMD_PING)
static int do_ping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{if (argc < 2)return CMD_RET_USAGE;net_ping_ip = string_to_ip(argv[1]);if (net_ping_ip.s_addr == 0)return CMD_RET_USAGE;if (net_loop(PING) < 0) {printf("ping failed; host %s is not alive\n", argv[1]);return CMD_RET_FAILURE;}printf("host %s is alive\n", argv[1]);return CMD_RET_SUCCESS;
}U_BOOT_CMD(ping,	2,	1,	do_ping,"send ICMP ECHO_REQUEST to network host","ipaddress"
);

3.2 uboot的net_loop( )部分内容的简单分析

⚡ 代码位置:net/net.c
⚡ 协议栈层面的初始化:net_init( )函数

📌 可以看到,net_init( )函数主要做的协议栈层面的初始化,前面设置了数据包缓存的大小,并对齐地址(官方注释翻译过来);net_pkt_buf是定义在net/net.h中的全局缓冲区,
📌net_pkt_buf缓存区大小设置:此处不深究具体原因了(从定义处来看,大致是由设备字节对齐要求(以满足CPU/DMA访问要求和性能,这里定义的是32字节)+以太网帧1536字节还需要X5个缓冲区确定的,最终大小是1536*5+32=7715字节`)
📌net_tx_packet是传输的数据包,其大小计算看不太懂,略过
📌接着arp_init()、net_clear_handlers()都是一些配置清零或复位
📌 最后,net_init_loop()只是获取了当前设备的MAC地址
可见,真正的实现代码还在net_loop( )里,接着往下看!

void net_init(void)
{static int first_call = 1;if (first_call) {/**	Setup packet buffers, aligned correctly.*/int i;net_tx_packet = &net_pkt_buf[0] + (PKTALIGN - 1);net_tx_packet -= (ulong)net_tx_packet % PKTALIGN;for (i = 0; i < PKTBUFSRX; i++) {net_rx_packets[i] = net_tx_packet +(i + 1) * PKTSIZE_ALIGN;}arp_init();net_clear_handlers();/* Only need to setup buffer pointers once. */first_call = 0;}net_init_loop();
}

⚡网卡的初始化:在net_init( )后,是对网络接口的初始化

📌 代码位置:net/net.c
📌 eth_is_on_demand_init() 这个条件来判断是否是按需初始化网卡,demand是n.按需、需要的意思。 true: 表示当前网卡还未被初始化,现在需要第一次使用它,因此必须初始化
📌 protocol != NETCONS:NETCONS是网络控制台的意思,也就是非网络控制台协议,就必须进行以下初始化
📌 eth_halt( )和eth_set_current( )都是重新设置网卡,类似复位的意思。eth_halt( )重新设置网卡;eth_set_current( )会重新从环境变量中获取当前使用的网卡
📌 至此,网卡的初始化也完成了,接下去的代码是一段状态机,轮询各种协议并调用相应的start函数,是一个由接收到的数据包和定时器事件驱动的状态机。
📌 这些对应的start函数实现功能就不看了,只看到ping命令相关的,也就是#if defined(CONFIG_CMD_PING)处的ping_start( )

⚡到这里已经能看到ping命令的基本实现,也就是start_ping()函数。在net_loop()下还有其它更复杂的内容,此处不再继续了,先了解start_ping( )。

/**********************************************************************/
/**	Main network processing loop.*/int net_loop(enum proto_t protocol)
{int ret = -EINVAL;net_restarted = 0;net_dev_exists = 0;net_try_count = 1;/* 调试信息 */debug_cond(DEBUG_INT_STATE, "--- net_loop Entry\n");/* uboot的启动性能分析模块,这里用于记录网卡启动的时间信息 */bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");/* 网络相关的初始化,包括硬件、协议栈等内容的初始化 */net_init();if (eth_is_on_demand_init() || protocol != NETCONS) {eth_halt();eth_set_current();ret = eth_init();if (ret < 0) {eth_halt();return ret;}} else {eth_init_state_only();}
restart:
#ifdef CONFIG_USB_KEYBOARDnet_busy_flag = 0;
#endifnet_set_state(NETLOOP_CONTINUE);/**	Start the ball rolling with the given start function.  From*	here on, this code is a state machine driven by received*	packets and timer events.*/debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n");net_init_loop();switch (net_check_prereq(protocol)) {case 1:/* network not configured */eth_halt();return -ENODEV;case 2:/* network device not configured */break;case 0:net_dev_exists = 1;net_boot_file_size = 0;switch (protocol) {case TFTPGET:
#ifdef CONFIG_CMD_TFTPPUTcase TFTPPUT:
#endif/* always use ARP to get server ethernet address */tftp_start(protocol);break;
#ifdef CONFIG_CMD_TFTPSRVcase TFTPSRV:tftp_start_server();break;
#endif
#if defined(CONFIG_CMD_DHCP)case DHCP:bootp_reset();net_ip.s_addr = 0;dhcp_request();		/* Basically same as BOOTP */break;
#endifcase BOOTP:bootp_reset();net_ip.s_addr = 0;bootp_request();break;#if defined(CONFIG_CMD_RARP)case RARP:rarp_try = 0;net_ip.s_addr = 0;rarp_request();break;
#endif
#if defined(CONFIG_CMD_PING)case PING:ping_start();break;
#endif
#if defined(CONFIG_CMD_NFS)case NFS:nfs_start();break;
#endif
#if defined(CONFIG_CMD_CDP)case CDP:cdp_start();break;
#endif
#if defined(CONFIG_NETCONSOLE) && !(CONFIG_SPL_BUILD)case NETCONS:nc_start();break;
#endif
#if defined(CONFIG_CMD_SNTP)case SNTP:sntp_start();break;
#endif
#if defined(CONFIG_CMD_DNS)case DNS:dns_start();break;
#endif
#if defined(CONFIG_CMD_LINK_LOCAL)case LINKLOCAL:link_local_start();break;
#endifdefault:break;}break;}#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
#if	defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)	&& \defined(CONFIG_STATUS_LED)			&& \defined(STATUS_LED_RED)/** Echo the inverted link state to the fault LED.*/if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR))status_led_set(STATUS_LED_RED, STATUS_LED_OFF);elsestatus_led_set(STATUS_LED_RED, STATUS_LED_ON);
#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
#endif /* CONFIG_MII, ... */
#ifdef CONFIG_USB_KEYBOARDnet_busy_flag = 1;
#endif/**	Main packet reception loop.  Loop receiving packets until*	someone sets `net_state' to a state that terminates.*/for (;;) {WATCHDOG_RESET();
#ifdef CONFIG_SHOW_ACTIVITYshow_activity(1);
#endifif (arp_timeout_check() > 0)time_start = get_timer(0);/**	Check the ethernet for a new packet.  The ethernet*	receive routine will process it.*	Most drivers return the most recent packet size, but not*	errors that may have happened.*/eth_rx();/**	Abort if ctrl-c was pressed.*/if (ctrlc()) {/* cancel any ARP that may not have completed */net_arp_wait_packet_ip.s_addr = 0;net_cleanup_loop();eth_halt();/* Invalidate the last protocol */eth_set_last_protocol(BOOTP);puts("\nAbort\n");/* include a debug print as well incase the debugmessages are directed to stderr */debug_cond(DEBUG_INT_STATE, "--- net_loop Abort!\n");ret = -EINTR;goto done;}/**	Check for a timeout, and run the timeout handler*	if we have one.*/if (time_handler &&((get_timer(0) - time_start) > time_delta)) {thand_f *x;#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
#if	defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)	&& \defined(CONFIG_STATUS_LED)			&& \defined(STATUS_LED_RED)/** Echo the inverted link state to the fault LED.*/if (miiphy_link(eth_get_dev()->name,CONFIG_SYS_FAULT_MII_ADDR))status_led_set(STATUS_LED_RED, STATUS_LED_OFF);elsestatus_led_set(STATUS_LED_RED, STATUS_LED_ON);
#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
#endif /* CONFIG_MII, ... */debug_cond(DEBUG_INT_STATE, "--- net_loop timeout\n");x = time_handler;time_handler = (thand_f *)0;(*x)();}if (net_state == NETLOOP_FAIL)ret = net_start_again();switch (net_state) {case NETLOOP_RESTART:net_restarted = 1;goto restart;case NETLOOP_SUCCESS:net_cleanup_loop();if (net_boot_file_size > 0) {printf("Bytes transferred = %d (%x hex)\n",net_boot_file_size, net_boot_file_size);setenv_hex("filesize", net_boot_file_size);setenv_hex("fileaddr", load_addr);}if (protocol != NETCONS)eth_halt();elseeth_halt_state_only();eth_set_last_protocol(protocol);ret = net_boot_file_size;debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n");goto done;case NETLOOP_FAIL:net_cleanup_loop();/* Invalidate the last protocol */eth_set_last_protocol(BOOTP);debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n");goto done;case NETLOOP_CONTINUE:continue;}}done:
#ifdef CONFIG_USB_KEYBOARDnet_busy_flag = 0;
#endif
#ifdef CONFIG_CMD_TFTPPUT/* Clear out the handlers */net_set_udp_handler(NULL);net_set_icmp_handler(NULL);
#endifreturn ret;
}

3.3 uboot中ping命令的实现ping_start( )

⚡ping_start( )实际上主要内容是在ping_send( )函数中

void ping_start(void)
{printf("Using %s device\n", eth_get_name());net_set_timeout_handler(10000UL, ping_timeout_handler);ping_send();
}

⚡ICMP请求报文发送:ping_send( ):主要做的是构造好icmp的Request报文数据,再调用arp_request()发送arp请求,整个函数完。到这里,实际上我们还看不到真正的ICMP请求包的发送。还需要在看到ARP相关的代码,因为真正的ICMP Echo Request报文是在ARP的响应处理中实现的。

📌 代码位置:net/ping.c
📌 构造请求体:set_icmp_header():以太网帧头+IP头+ICMP头+数据
📌 发送arp请求:arp_request( )
📌 arp响应处理才是真正的发送icmp请求包的实现。跳转到最后可以看到,最后的发包实现函数

在这里插入图片描述
在这里插入图片描述

static int ping_send(void)
{uchar *pkt;int eth_hdr_size;/* XXX always send arp request *//* 调试信息 */debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &net_ping_ip);/* 构建以太网帧头 */net_arp_wait_packet_ip = net_ping_ip;eth_hdr_size = net_set_ether(net_tx_packet, net_null_ethaddr, PROT_IP);pkt = (uchar *)net_tx_packet + eth_hdr_size;/* 构建icmp报文头 */set_icmp_header(pkt, net_ping_ip);/* size of the waiting packet *//* 待发送的报文大小:以太网帧头+IP头+ICMP头+数据 */arp_wait_tx_packet_size = eth_hdr_size + IP_ICMP_HDR_SIZE;/* and do the ARP request *//* ARP请求:请求对端IP地址对应的MAC */arp_wait_try = 1;arp_wait_timer_start = get_timer(0);arp_request();return 1;	/* waiting */
}

四、添加ping_echo命令

⚡大致阅读完上面的一点uboot源码,可以发现在net/ping.c中是有响应处理的函数的ping_receive( ),,实际上也就是将前面发送的Echo Request报文改成Echo Reply,再发送到对端。
⚡那么为什么实现了ping的响应处理,却不能ping通uboot呢?原因是uboot实际上只是一个裸机程序,并不能实现多线程多进程,所以并不能主动监听或响应来自网络的请求。所以,要实现这个uboot响应ping,我们大致有两种方式可选择,①是注册一个uboot命令,类似ping_echo,调用ping_receive( );②是修改原来的ping命令,让其在ping的同时能接受对端的请求响应,这里选择第一种方式,仿照ping命令,添加一个ping_echo命令。

4.1uboot注册ping_echo命令

⚡在cmd/net.c中添加以下代码,并在net.h中的协议类型枚举中添加PING_ECHO,即net_loop()传入的参数,这里不用#ifndef。如果和前面的命令一样添加条件编译,需要到config/Kconfig使能该命令

static int do_ping_echo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {printf("Waiting for ICMP Echo Requests...\n");if (net_loop(PING_ECHO) < 0) {return CMD_RET_FAILURE;}printf("recving icmp request\n");return CMD_RET_SUCCESS;
}U_BOOT_CMD(ping_echo, 1, 1, do_ping_echo,"Wait ICMP Echo Requests",""
);

在这里插入图片描述

# Kconfig内容,不添加条件编译可省略
config CMD_PING_ECHObool "ping_echo"helpRespond to ICMP Echo Requests from hosts

4.2 net_loop()添加对应的case分支

在这里插入图片描述
⚡ping_echo_start( )如下,net_set_timeout_handler( )绑定到ping_receive即可

void ping_echo_start(void)
{printf("%s waiting for icmp request...\n", eth_get_name());net_set_timeout_handler(0UL,ping_receive);
}

⚡调用关系:cmd/net.c中ping_echo命令调用do_ping_echo( ),do_ping_echo( )调用net_loop( ),net_loop( )调用ping_echo_start(),ping_echo_start()调用ping_receive( );

五、结果测试

在这里插入图片描述

http://www.xdnf.cn/news/1348075.html

相关文章:

  • 音视频处理工作室:实时通信的媒体层设计
  • Paddle3D-PETRv1 精度测试与推理实践指南
  • 容器安全实践(一):概念篇 - 从“想当然”到“真相”
  • 车载诊断架构 --- EOL引起关于DTC检测开始条件的思考
  • Mongodb操作指南
  • 大麦盒子DM4036-精简固件包及教程
  • 2025.8.22周五 在职老D渗透日记day24:burp+mumu抓包 安卓7.0以上证书配置
  • 电脑端完全免费的动态壁纸和屏保软件(真正免费、无广告、无会员)
  • 二叉搜索树(BST)、AVL树、红黑树
  • 爬虫基础学习-链接协议分析,熟悉相关函数
  • 基于抗辐照性能的ASP4644S电源芯片特性分析与多领域应用验证
  • 笔记本怎么才能更快散热?
  • DataStream实现WordCount
  • 信息结构统一论:物理世界与人类感知、认知及符号系统的桥梁
  • 透射TEM新手入门:衍射斑点标定 1
  • [特殊字符] TTS格局重塑!B站推出Index-TTS,速度、音质、情感表达全维度领先
  • Day25 栈 队列 二叉树
  • 特大桥施工绳断 7 人亡:索力实时监测预警机制亟待完善
  • kvcache比赛记录
  • 集群与负载均衡:HAProxy 与 Nginx 实践
  • 融云Im单独一个拍照或者拍摄插件Plugin
  • 自学嵌入式第二十五天:数据结构-队列、树
  • 配电网重构优化:以减小网损为目标的智能算法实现
  • 20250822给荣品RD-RK3588开发板刷Rockchip原厂的Android14时点亮荣品的8寸屏
  • SN编码升级:从“制造标记”到“数字孪生身份证”
  • There are test failures. clean deploy 异常
  • [RestGPT] RestGPT智能体
  • Bluedroid vs NimBLE
  • 20.9 QLoRA微调实战:1.5B参数Whisper-large-v2在24GB显存实现中文语音识别,CER骤降50%!
  • 使用tauri打包cocos小游戏,并在抖音小玩法中启动,拿到启动参数token