Message ID | 284076d79cd331cf1a4923e0cea32fefc4c14fba.1716566960.git.jerome.forissier@linaro.org |
---|---|
State | New |
Headers | show |
Series | Introduce the lwIP network stack | expand |
> +#include <net-lwip.h> > +#include <time.h> > + > +#define PING_DELAY_MS 1000 > +#define PING_TIMEOUT_MS 10000 > +/* Additional data size to include in the packet */ > +#define PING_DATA_SIZE 32 > +/* Ping identifier - must fit on a u16_t */ > +#define PING_ID 0xAFAF > + > +static const ip_addr_t *ping_target; > +static struct raw_pcb *ping_pcb; > +static u16_t ping_seq_num; As a general note, u8_t u16_t etc are lwip constructs. Can we not use them and instead use the original definition? uint8_t etc. I am not sure introducing another define is what we want. Tom? > +static bool ping_target_alive; > + > +static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, > + const ip_addr_t *addr) > +{ > + struct icmp_echo_hdr *iecho; > + > + if (addr->addr != ping_target->addr) > + return 0; > + > + if ((p->tot_len >= (IP_HLEN + sizeof(struct icmp_echo_hdr))) && > + pbuf_remove_header(p, IP_HLEN) == 0) { > + iecho = (struct icmp_echo_hdr *)p->payload; > + > + if ((iecho->id == PING_ID) && > + (iecho->seqno == lwip_htons(ping_seq_num))) { > + ping_target_alive = true; > + printf("host %s is alive\n", ipaddr_ntoa(addr)); > + pbuf_free(p); > + return 1; /* eat the packet */ > + } > + /* not eaten, restore original packet */ > + pbuf_add_header(p, IP_HLEN); > + } > + > + return 0; /* don't eat the packet */ > +} > + > +static int ping_raw_init(void) > +{ > + ping_pcb = raw_new(IP_PROTO_ICMP); > + if (!ping_pcb) > + return -ENOMEM; > + > + raw_recv(ping_pcb, ping_recv, NULL); Instead of defining a global variable ping_target_alive can we instead pass the ptr of void *recv_arg? We can then fill in that private ptr with the status > + raw_bind(ping_pcb, IP_ADDR_ANY); > + > + return 0; > +} > + > +static void ping_raw_stop(void) > +{ > + if (ping_pcb != NULL) { > + raw_remove(ping_pcb); > + ping_pcb = NULL; > + } > +} > + > +static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len) > +{ > + size_t i; > + size_t data_len = len - sizeof(struct icmp_echo_hdr); > + > + ICMPH_TYPE_SET(iecho, ICMP_ECHO); > + ICMPH_CODE_SET(iecho, 0); > + iecho->chksum = 0; > + iecho->id = PING_ID; > + iecho->seqno = lwip_htons(++ping_seq_num); > + > + /* Fill the additional data buffer with some data */ > + for(i = 0; i < data_len; i++) { > + ((char *)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; > + } is this additional data used? And if they are shouldn't we care about endianess? > + > + iecho->chksum = inet_chksum(iecho, len); > +} > + > +static void ping_send_icmp(struct raw_pcb *raw, const ip_addr_t *addr) > +{ > + struct pbuf *p; > + struct icmp_echo_hdr *iecho; > + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; > + > + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); > + if (!p) > + return; > + > + if ((p->len == p->tot_len) && (p->next == NULL)) { > + iecho = (struct icmp_echo_hdr *)p->payload; > + ping_prepare_echo(iecho, (u16_t)ping_size); > + raw_sendto(raw, p, addr); > + } > + > + pbuf_free(p); > +} > + > +static void ping_send(void *arg) > +{ > + struct raw_pcb *pcb = (struct raw_pcb *)arg; > + > + ping_send_icmp(pcb, ping_target); > + sys_timeout(PING_DELAY_MS, ping_send, ping_pcb); > +} > + > +static int ping_loop(const ip_addr_t* addr) > +{ > + ulong start; > + int ret; > + > + printf("Using %s device\n", eth_get_name()); > + > + ret = ping_raw_init(); > + if (ret < 0) > + return ret; > + ping_target = addr; > + > + start = get_timer(0); > + ping_send(ping_pcb); > + > + do { > + eth_rx(); eth_rx() has a ret value. Don't we have to check it here? > + if (ping_target_alive) > + break; > + sys_check_timeouts(); > + if (ctrlc()) { > + printf("\nAbort\n"); > + break; > + } > + } while (get_timer(start) < PING_TIMEOUT_MS); > + > + sys_untimeout(ping_send, ping_pcb); > + ping_raw_stop(); > + ping_target = NULL; > + > + if (ping_target_alive) { > + ping_target_alive = false; > + return 0; > + } > + printf("ping failed; host %s is not alive\n", ipaddr_ntoa(addr)); > + return -1; > +} > + > +int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) > +{ > + ip_addr_t addr; > + > + if (argc < 2) > + return CMD_RET_USAGE; > + > + if (!ipaddr_aton(argv[1], &addr)) > + return CMD_RET_USAGE; > + > + if (ping_loop(&addr) < 0) > + return CMD_RET_FAILURE; > + > + return CMD_RET_SUCCESS; > +} > -- > 2.40.1 > Regards /Ilias
On 6/6/24 11:10, Ilias Apalodimas wrote: >> +#include <net-lwip.h> >> +#include <time.h> >> + >> +#define PING_DELAY_MS 1000 >> +#define PING_TIMEOUT_MS 10000 >> +/* Additional data size to include in the packet */ >> +#define PING_DATA_SIZE 32 >> +/* Ping identifier - must fit on a u16_t */ >> +#define PING_ID 0xAFAF >> + >> +static const ip_addr_t *ping_target; >> +static struct raw_pcb *ping_pcb; >> +static u16_t ping_seq_num; > > As a general note, u8_t u16_t etc are lwip constructs. > Can we not use them and instead use the original definition? > uint8_t etc. I am not sure introducing another define is what we want. Tom? I think it makes sense to use the more standard type (uint16_t etc.) when defining variables like here. However I'd rather keep the lwIP types in the function definitions to reflect the function prototype exactly. >> +static bool ping_target_alive; >> + >> +static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, >> + const ip_addr_t *addr) >> +{ >> + struct icmp_echo_hdr *iecho; >> + >> + if (addr->addr != ping_target->addr) >> + return 0; >> + >> + if ((p->tot_len >= (IP_HLEN + sizeof(struct icmp_echo_hdr))) && >> + pbuf_remove_header(p, IP_HLEN) == 0) { >> + iecho = (struct icmp_echo_hdr *)p->payload; >> + >> + if ((iecho->id == PING_ID) && >> + (iecho->seqno == lwip_htons(ping_seq_num))) { >> + ping_target_alive = true; >> + printf("host %s is alive\n", ipaddr_ntoa(addr)); >> + pbuf_free(p); >> + return 1; /* eat the packet */ >> + } >> + /* not eaten, restore original packet */ >> + pbuf_add_header(p, IP_HLEN); >> + } >> + >> + return 0; /* don't eat the packet */ >> +} >> + >> +static int ping_raw_init(void) >> +{ >> + ping_pcb = raw_new(IP_PROTO_ICMP); >> + if (!ping_pcb) >> + return -ENOMEM; >> + >> + raw_recv(ping_pcb, ping_recv, NULL); > > Instead of defining a global variable ping_target_alive can we instead pass > the ptr of void *recv_arg? We can then fill in that private ptr with the > status Sure. Fixed in v3. >> + raw_bind(ping_pcb, IP_ADDR_ANY); >> + >> + return 0; >> +} >> + >> +static void ping_raw_stop(void) >> +{ >> + if (ping_pcb != NULL) { >> + raw_remove(ping_pcb); >> + ping_pcb = NULL; >> + } >> +} >> + >> +static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len) >> +{ >> + size_t i; >> + size_t data_len = len - sizeof(struct icmp_echo_hdr); >> + >> + ICMPH_TYPE_SET(iecho, ICMP_ECHO); >> + ICMPH_CODE_SET(iecho, 0); >> + iecho->chksum = 0; >> + iecho->id = PING_ID; >> + iecho->seqno = lwip_htons(++ping_seq_num); >> + >> + /* Fill the additional data buffer with some data */ >> + for(i = 0; i < data_len; i++) { >> + ((char *)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; >> + } > > is this additional data used? And if they are shouldn't we care about > endianess? It's a random payload for the ICMP packet. TBH it's totally useless here, I will just remove the additional data in v3. > >> + >> + iecho->chksum = inet_chksum(iecho, len); >> +} >> + >> +static void ping_send_icmp(struct raw_pcb *raw, const ip_addr_t *addr) >> +{ >> + struct pbuf *p; >> + struct icmp_echo_hdr *iecho; >> + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; >> + >> + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); >> + if (!p) >> + return; >> + >> + if ((p->len == p->tot_len) && (p->next == NULL)) { >> + iecho = (struct icmp_echo_hdr *)p->payload; >> + ping_prepare_echo(iecho, (u16_t)ping_size); >> + raw_sendto(raw, p, addr); >> + } >> + >> + pbuf_free(p); >> +} >> + >> +static void ping_send(void *arg) >> +{ >> + struct raw_pcb *pcb = (struct raw_pcb *)arg; >> + >> + ping_send_icmp(pcb, ping_target); >> + sys_timeout(PING_DELAY_MS, ping_send, ping_pcb); >> +} >> + >> +static int ping_loop(const ip_addr_t* addr) >> +{ >> + ulong start; >> + int ret; >> + >> + printf("Using %s device\n", eth_get_name()); >> + >> + ret = ping_raw_init(); >> + if (ret < 0) >> + return ret; >> + ping_target = addr; >> + >> + start = get_timer(0); >> + ping_send(ping_pcb); >> + >> + do { >> + eth_rx(); > > eth_rx() has a ret value. Don't we have to check it here? We would not do anything different on error because an error might be transient, we don't want too much complexity. Letting the loop run until success (which is determined by the proper ICMP echo reply being received) or timeout or Ctrl-C is good enough. BTW net_loop() in net/net.c doesn't check the return status, either. > >> + if (ping_target_alive) >> + break; >> + sys_check_timeouts(); >> + if (ctrlc()) { >> + printf("\nAbort\n"); >> + break; >> + } >> + } while (get_timer(start) < PING_TIMEOUT_MS); >> + >> + sys_untimeout(ping_send, ping_pcb); >> + ping_raw_stop(); >> + ping_target = NULL; >> + >> + if (ping_target_alive) { >> + ping_target_alive = false; >> + return 0; >> + } >> + printf("ping failed; host %s is not alive\n", ipaddr_ntoa(addr)); >> + return -1; >> +} >> + >> +int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) >> +{ >> + ip_addr_t addr; >> + >> + if (argc < 2) >> + return CMD_RET_USAGE; >> + >> + if (!ipaddr_aton(argv[1], &addr)) >> + return CMD_RET_USAGE; >> + >> + if (ping_loop(&addr) < 0) >> + return CMD_RET_FAILURE; >> + >> + return CMD_RET_SUCCESS; >> +} >> -- >> 2.40.1 >> > > Regards > /Ilias
diff --git a/boot/Kconfig b/boot/Kconfig index 004e69dd92a..983a284ec52 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -379,7 +379,7 @@ config BOOT_DEFAULTS_CMDS select CMD_FS_GENERIC select CMD_PART if PARTITIONS select CMD_DHCP if CMD_NET || CMD_NET_LWIP - select CMD_PING if CMD_NET + select CMD_PING if CMD_NET || CMD_NET_LWIP select CMD_PXE if CMD_NET select CMD_BOOTI if ARM64 select CMD_BOOTZ if ARM && !ARM64 diff --git a/cmd/Kconfig b/cmd/Kconfig index 94a8de266f6..07cfe824e3f 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2098,6 +2098,12 @@ config CMD_DHCP help Boot image via network using DHCP/TFTP protocol +config CMD_PING + bool "ping" + select PROT_RAW_LWIP + help + Send ICMP ECHO_REQUEST to network host + config CMD_TFTPBOOT bool "tftp" select PROT_UDP_LWIP diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c index 490a2e4ac5c..13856703fcf 100644 --- a/cmd/net-lwip.c +++ b/cmd/net-lwip.c @@ -12,6 +12,14 @@ U_BOOT_CMD( ); #endif +#if defined(CONFIG_CMD_PING) +U_BOOT_CMD( + ping, 2, 1, do_ping, + "send ICMP ECHO_REQUEST to network host", + "pingAddress" +); +#endif + #if defined(CONFIG_CMD_TFTPBOOT) U_BOOT_CMD( tftpboot, 3, 0, do_tftpb, diff --git a/include/net-lwip.h b/include/net-lwip.h index 2308703e46b..2abaaa3b4e3 100644 --- a/include/net-lwip.h +++ b/include/net-lwip.h @@ -5,6 +5,7 @@ #include <asm/cache.h> #include <linux/types.h> +#include <lwip/ip_addr.h> #include <lwip/netif.h> #include <time.h> @@ -50,6 +51,7 @@ int eth_env_get_enetaddr_by_index(const char *base_name, int index, int eth_init(void); /* Initialize the device */ int eth_send(void *packet, int length); /* Send a packet */ int eth_rx(void); +const char *eth_get_name(void); int eth_get_dev_index(void); int eth_init_state_only(void); /* Set active state */ void eth_set_current(void); /* set nterface to ethcur var */ @@ -80,6 +82,7 @@ int net_lwip_init(void); struct netif *net_lwip_get_netif(void); int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); #endif /* __NET_LWIP_H__ */ diff --git a/net-lwip/Makefile b/net-lwip/Makefile index a56c32bfa74..e68d4e24197 100644 --- a/net-lwip/Makefile +++ b/net-lwip/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth_common.o obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth-uclass.o obj-$(CONFIG_$(SPL_)DM_ETH) += net-lwip.o obj-$(CONFIG_CMD_DHCP) += dhcp.o +obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o # Disable this warning as it is triggered by: diff --git a/net-lwip/ping.c b/net-lwip/ping.c new file mode 100644 index 00000000000..763a9bb1e38 --- /dev/null +++ b/net-lwip/ping.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2024 Linaro Ltd. */ + +#include <command.h> +#include <console.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <lwip/icmp.h> +#include <lwip/inet_chksum.h> +#include <lwip/raw.h> +#include <lwip/timeouts.h> +#include <net-lwip.h> +#include <time.h> + +#define PING_DELAY_MS 1000 +#define PING_TIMEOUT_MS 10000 +/* Additional data size to include in the packet */ +#define PING_DATA_SIZE 32 +/* Ping identifier - must fit on a u16_t */ +#define PING_ID 0xAFAF + +static const ip_addr_t *ping_target; +static struct raw_pcb *ping_pcb; +static u16_t ping_seq_num; +static bool ping_target_alive; + +static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr) +{ + struct icmp_echo_hdr *iecho; + + if (addr->addr != ping_target->addr) + return 0; + + if ((p->tot_len >= (IP_HLEN + sizeof(struct icmp_echo_hdr))) && + pbuf_remove_header(p, IP_HLEN) == 0) { + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == PING_ID) && + (iecho->seqno == lwip_htons(ping_seq_num))) { + ping_target_alive = true; + printf("host %s is alive\n", ipaddr_ntoa(addr)); + pbuf_free(p); + return 1; /* eat the packet */ + } + /* not eaten, restore original packet */ + pbuf_add_header(p, IP_HLEN); + } + + return 0; /* don't eat the packet */ +} + +static int ping_raw_init(void) +{ + ping_pcb = raw_new(IP_PROTO_ICMP); + if (!ping_pcb) + return -ENOMEM; + + raw_recv(ping_pcb, ping_recv, NULL); + raw_bind(ping_pcb, IP_ADDR_ANY); + + return 0; +} + +static void ping_raw_stop(void) +{ + if (ping_pcb != NULL) { + raw_remove(ping_pcb); + ping_pcb = NULL; + } +} + +static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len) +{ + size_t i; + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = PING_ID; + iecho->seqno = lwip_htons(++ping_seq_num); + + /* Fill the additional data buffer with some data */ + for(i = 0; i < data_len; i++) { + ((char *)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); +} + +static void ping_send_icmp(struct raw_pcb *raw, const ip_addr_t *addr) +{ + struct pbuf *p; + struct icmp_echo_hdr *iecho; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); + if (!p) + return; + + if ((p->len == p->tot_len) && (p->next == NULL)) { + iecho = (struct icmp_echo_hdr *)p->payload; + ping_prepare_echo(iecho, (u16_t)ping_size); + raw_sendto(raw, p, addr); + } + + pbuf_free(p); +} + +static void ping_send(void *arg) +{ + struct raw_pcb *pcb = (struct raw_pcb *)arg; + + ping_send_icmp(pcb, ping_target); + sys_timeout(PING_DELAY_MS, ping_send, ping_pcb); +} + +static int ping_loop(const ip_addr_t* addr) +{ + ulong start; + int ret; + + printf("Using %s device\n", eth_get_name()); + + ret = ping_raw_init(); + if (ret < 0) + return ret; + ping_target = addr; + + start = get_timer(0); + ping_send(ping_pcb); + + do { + eth_rx(); + if (ping_target_alive) + break; + sys_check_timeouts(); + if (ctrlc()) { + printf("\nAbort\n"); + break; + } + } while (get_timer(start) < PING_TIMEOUT_MS); + + sys_untimeout(ping_send, ping_pcb); + ping_raw_stop(); + ping_target = NULL; + + if (ping_target_alive) { + ping_target_alive = false; + return 0; + } + printf("ping failed; host %s is not alive\n", ipaddr_ntoa(addr)); + return -1; +} + +int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ip_addr_t addr; + + if (argc < 2) + return CMD_RET_USAGE; + + if (!ipaddr_aton(argv[1], &addr)) + return CMD_RET_USAGE; + + if (ping_loop(&addr) < 0) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +}
Add support for the the ping command with NET_LWIP. The implementation is derived from lwIP's contrib/apps/ping/ping.c. Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> --- boot/Kconfig | 2 +- cmd/Kconfig | 6 ++ cmd/net-lwip.c | 8 +++ include/net-lwip.h | 3 + net-lwip/Makefile | 1 + net-lwip/ping.c | 171 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 net-lwip/ping.c