From 3f1983d460d78a108d76315a4e0001add95754fa Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 9 Feb 2023 18:07:03 +0200 Subject: drivers: net: ldpaa: export driver name and API to get DPMAC id Export the ldpaa_eth_get_dpmac_id() function so that it can be used from other drivers, especially by fsl-mc which will need it the next patch. Also, create a macro for the Ethernet ldpaa driver name and export it as well. Signed-off-by: Ioana Ciornei --- include/net/ldpaa_eth.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 include/net/ldpaa_eth.h (limited to 'include') diff --git a/include/net/ldpaa_eth.h b/include/net/ldpaa_eth.h new file mode 100644 index 00000000000..7474bfaeec3 --- /dev/null +++ b/include/net/ldpaa_eth.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2023 NXP + */ + +#define LDPAA_ETH_DRIVER_NAME "ldpaa_eth" + +/** + * ldpaa_eth_get_dpmac_id() - Get the dpmac_id of a DPAA2 ethernet device + * + * @dev: DPAA2 ethernet udevice pointer + * Return: requested dpmac_id + */ + +uint32_t ldpaa_eth_get_dpmac_id(struct udevice *dev); -- cgit v1.3.1 From 5e541c48a7d34e6b6e426bf5b50d7111d7e680f6 Mon Sep 17 00:00:00 2001 From: Sean Edmond Date: Wed, 15 Feb 2023 20:38:36 -0800 Subject: net: ipv6: IPv6 environment variable cleanup Fix "setenv gatewayip6". Synchronize IPv6 local variables with environment variables in netboot_update_env() Signed-off-by: Sean Edmond --- cmd/net.c | 23 ++++++++++++++++++++++- include/env_flags.h | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/cmd/net.c b/cmd/net.c index d5e20843dda..036b7720a7c 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -186,7 +186,7 @@ U_BOOT_CMD( static void netboot_update_env(void) { - char tmp[22]; + char tmp[44]; if (net_gateway.s_addr) { ip_to_string(net_gateway, tmp); @@ -247,6 +247,27 @@ static void netboot_update_env(void) env_set("ntpserverip", tmp); } #endif + + if (IS_ENABLED(CONFIG_IPV6)) { + if (!ip6_is_unspecified_addr(&net_ip6) || + net_prefix_length != 0) { + sprintf(tmp, "%pI6c", &net_ip6); + if (net_prefix_length != 0) + sprintf(tmp, "%s/%d", tmp, net_prefix_length); + + env_set("ip6addr", tmp); + } + + if (!ip6_is_unspecified_addr(&net_server_ip6)) { + sprintf(tmp, "%pI6c", &net_server_ip6); + env_set("serverip6", tmp); + } + + if (!ip6_is_unspecified_addr(&net_gateway6)) { + sprintf(tmp, "%pI6c", &net_gateway6); + env_set("gatewayip6", tmp); + } + } } /** diff --git a/include/env_flags.h b/include/env_flags.h index 7de58cc57c3..d785f87cdcb 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -71,7 +71,7 @@ enum env_flags_varaccess { #define NET6_FLAGS \ "ip6addr:s," \ "serverip6:s," \ - "gatewayip6:s" + "gatewayip6:s," #else #define NET6_FLAGS #endif -- cgit v1.3.1 From a0245818f7f8e375abc00f36ff88326331e4e2f9 Mon Sep 17 00:00:00 2001 From: Sean Edmond Date: Tue, 11 Apr 2023 10:48:46 -0700 Subject: net: dhcp6: Add DHCPv6 (DHCP for IPv6) Adds DHCPv6 protocol to u-boot. Allows for address assignement with DHCPv6 4-message exchange (SOLICIT->ADVERTISE->REQUEST->REPLY). Includes DHCPv6 options required by RFC 8415. Also adds DHCPv6 options required for PXE boot. Possible enhancements: - Duplicate address detection on DHCPv6 assigned address - IPv6 address assignement through SLAAC - Sending/parsing other DHCPv6 options (NTP, DNS, etc...) Signed-off-by: Sean Edmond Reviewed-by: Ramon Fried --- include/net.h | 6 +- net/Makefile | 1 + net/dhcpv6.c | 719 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/dhcpv6.h | 256 +++++++++++++++++++++ net/net.c | 11 +- 5 files changed, 989 insertions(+), 4 deletions(-) create mode 100644 net/dhcpv6.c create mode 100644 net/dhcpv6.h (limited to 'include') diff --git a/include/net.h b/include/net.h index 399af5e0645..181a6e3b132 100644 --- a/include/net.h +++ b/include/net.h @@ -484,6 +484,8 @@ extern char net_hostname[32]; /* Our hostname */ #ifdef CONFIG_NET extern char net_root_path[CONFIG_BOOTP_MAX_ROOT_PATH_LEN]; /* Our root path */ #endif +/* Indicates whether the pxe path prefix / config file was specified in dhcp option */ +extern char *pxelinux_configfile; /** END OF BOOTP EXTENTIONS **/ extern u8 net_ethaddr[ARP_HLEN]; /* Our ethernet address */ extern u8 net_server_ethaddr[ARP_HLEN]; /* Boot server enet address */ @@ -504,8 +506,8 @@ extern ushort net_native_vlan; /* Our Native VLAN */ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { - BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS, - SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET + BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP, + NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET }; extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/net/Makefile b/net/Makefile index bea000b2067..5968110170b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_IPV6) += net6.o obj-$(CONFIG_CMD_NFS) += nfs.o obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_CMD_PING6) += ping6.o +obj-$(CONFIG_CMD_DHCP6) += dhcpv6.o obj-$(CONFIG_CMD_PCAP) += pcap.o obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o diff --git a/net/dhcpv6.c b/net/dhcpv6.c new file mode 100644 index 00000000000..0d1c600632f --- /dev/null +++ b/net/dhcpv6.c @@ -0,0 +1,719 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Microsoft Corporation + * Author: Sean Edmond + * + */ + +/* Simple DHCP6 network layer implementation. */ + +#include +#include +#include +#include +#include "net_rand.h" +#include "dhcpv6.h" + +#define PORT_DHCP6_S 547 /* DHCP6 server UDP port */ +#define PORT_DHCP6_C 546 /* DHCP6 client UDP port */ + +/* default timeout parameters (in ms) */ +#define SOL_MAX_DELAY_MS 1000 +#define SOL_TIMEOUT_MS 1000 +#define SOL_MAX_RT_MS 3600000 +#define REQ_TIMEOUT_MS 1000 +#define REQ_MAX_RT_MS 30000 +#define REQ_MAX_RC 10 +#define MAX_WAIT_TIME_MS 60000 + +/* global variable to track any updates from DHCP6 server */ +int updated_sol_max_rt_ms = SOL_MAX_RT_MS; +/* state machine parameters/variables */ +struct dhcp6_sm_params sm_params; + +static void dhcp6_state_machine(bool timeout, uchar *rx_pkt, unsigned int len); + +/* Handle DHCP received packets (set as UDP handler) */ +static void dhcp6_handler(uchar *pkt, unsigned int dest, struct in_addr sip, + unsigned int src, unsigned int len) +{ + /* return if ports don't match DHCPv6 ports */ + if (dest != PORT_DHCP6_C || src != PORT_DHCP6_S) + return; + + dhcp6_state_machine(false, pkt, len); +} + +/** + * dhcp6_add_option() - Adds DHCP6 option to a packet + * @option_id: The option ID to add (See DHCP6_OPTION_* definitions) + * @pkt: A pointer to the current write location of the TX packet + * + * Return: The number of bytes written into "*pkt" + */ +static int dhcp6_add_option(int option_id, uchar *pkt) +{ + struct dhcp6_option_duid_ll *duid_opt; + struct dhcp6_option_elapsed_time *elapsed_time_opt; + struct dhcp6_option_ia_ta *ia_ta_opt; + struct dhcp6_option_ia_na *ia_na_opt; + struct dhcp6_option_oro *oro_opt; + struct dhcp6_option_client_arch *client_arch_opt; + struct dhcp6_option_vendor_class *vendor_class_opt; + int opt_len; + long elapsed_time; + size_t vci_strlen; + int num_oro = 0; + int num_client_arch = 0; + int num_vc_data = 0; + struct dhcp6_option_hdr *dhcp_option = (struct dhcp6_option_hdr *)pkt; + uchar *dhcp_option_start = pkt + sizeof(struct dhcp6_option_hdr); + + dhcp_option->option_id = htons(option_id); + + switch (option_id) { + case DHCP6_OPTION_CLIENTID: + /* Only support for DUID-LL in Client ID option for now */ + duid_opt = (struct dhcp6_option_duid_ll *)dhcp_option_start; + duid_opt->duid_type = htons(DUID_TYPE_LL); + duid_opt->hw_type = htons(DUID_HW_TYPE_ENET); + memcpy(duid_opt->ll_addr, net_ethaddr, ETH_ALEN); + opt_len = sizeof(struct dhcp6_option_duid_ll) + ETH_ALEN; + + /* Save DUID for comparison later */ + memcpy(sm_params.duid, duid_opt, opt_len); + break; + case DHCP6_OPTION_ELAPSED_TIME: + /* calculate elapsed time in 1/100th of a second */ + elapsed_time = (sm_params.dhcp6_retry_ms - + sm_params.dhcp6_start_ms) / 10; + if (elapsed_time > 0xFFFF) + elapsed_time = 0xFFFF; + + elapsed_time_opt = (struct dhcp6_option_elapsed_time *)dhcp_option_start; + elapsed_time_opt->elapsed_time = htons(elapsed_time); + + opt_len = sizeof(struct dhcp6_option_elapsed_time); + break; + case DHCP6_OPTION_IA_TA: + ia_ta_opt = (struct dhcp6_option_ia_ta *)dhcp_option_start; + ia_ta_opt->iaid = htonl(sm_params.ia_id); + + opt_len = sizeof(struct dhcp6_option_ia_ta); + break; + case DHCP6_OPTION_IA_NA: + ia_na_opt = (struct dhcp6_option_ia_na *)dhcp_option_start; + ia_na_opt->iaid = htonl(sm_params.ia_id); + /* In a message sent by a client to a server, + * the T1 and T2 fields SHOULD be set to 0 + */ + ia_na_opt->t1 = 0; + ia_na_opt->t2 = 0; + + opt_len = sizeof(struct dhcp6_option_ia_na); + break; + case DHCP6_OPTION_ORO: + oro_opt = (struct dhcp6_option_oro *)dhcp_option_start; + oro_opt->req_option_code[num_oro++] = htons(DHCP6_OPTION_OPT_BOOTFILE_URL); + oro_opt->req_option_code[num_oro++] = htons(DHCP6_OPTION_SOL_MAX_RT); + if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)) { + oro_opt->req_option_code[num_oro++] = + htons(DHCP6_OPTION_OPT_BOOTFILE_PARAM); + } + + opt_len = sizeof(__be16) * num_oro; + break; + case DHCP6_OPTION_CLIENT_ARCH_TYPE: + client_arch_opt = (struct dhcp6_option_client_arch *)dhcp_option_start; + client_arch_opt->arch_type[num_client_arch++] = htons(CONFIG_DHCP6_PXE_CLIENTARCH); + + opt_len = sizeof(__be16) * num_client_arch; + break; + case DHCP6_OPTION_VENDOR_CLASS: + vendor_class_opt = (struct dhcp6_option_vendor_class *)dhcp_option_start; + vendor_class_opt->enterprise_number = htonl(CONFIG_DHCP6_ENTERPRISE_ID); + + vci_strlen = strlen(DHCP6_VCI_STRING); + vendor_class_opt->vendor_class_data[num_vc_data].vendor_class_len = + htons(vci_strlen); + memcpy(vendor_class_opt->vendor_class_data[num_vc_data].opaque_data, + DHCP6_VCI_STRING, vci_strlen); + num_vc_data++; + + opt_len = sizeof(struct dhcp6_option_vendor_class) + + sizeof(struct vendor_class_data) * num_vc_data + + vci_strlen; + break; + case DHCP6_OPTION_NII: + dhcp_option_start[0] = 1; + dhcp_option_start[1] = 0; + dhcp_option_start[2] = 0; + + opt_len = 3; + break; + default: + printf("***Warning unknown DHCP6 option %d. Not adding to message\n", option_id); + return 0; + } + dhcp_option->option_len = htons(opt_len); + + return opt_len + sizeof(struct dhcp6_option_hdr); +} + +/** + * dhcp6_send_solicit_packet() - Send a SOLICIT packet + * + * Implements RFC 8415: + * - 16.2. Solicit Message + * - 18.2.1. Creation and Transmission of Solicit Messages + * + * Adds DHCP6 header and DHCP6 options. Sends the UDP packet + * and sets the UDP handler. + */ +static void dhcp6_send_solicit_packet(void) +{ + struct in6_addr dhcp_bcast_ip6; + int len = 0; + uchar *pkt; + uchar *dhcp_pkt_start_ptr; + struct dhcp6_hdr *dhcp_hdr; + + pkt = net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE + UDP_HDR_SIZE; + dhcp_pkt_start_ptr = pkt; + + /* Add the DHCP6 header */ + dhcp_hdr = (struct dhcp6_hdr *)pkt; + dhcp_hdr->msg_type = DHCP6_MSG_SOLICIT; + dhcp_hdr->trans_id = htons(sm_params.trans_id); + pkt += sizeof(struct dhcp6_hdr); + + /* Add the options */ + pkt += dhcp6_add_option(DHCP6_OPTION_CLIENTID, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_ELAPSED_TIME, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_IA_NA, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_ORO, pkt); + if (CONFIG_DHCP6_PXE_CLIENTARCH != 0xFF) + pkt += dhcp6_add_option(DHCP6_OPTION_CLIENT_ARCH_TYPE, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_VENDOR_CLASS, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_NII, pkt); + + /* calculate packet length */ + len = pkt - dhcp_pkt_start_ptr; + + /* send UDP packet to DHCP6 multicast address */ + string_to_ip6(DHCP6_MULTICAST_ADDR, sizeof(DHCP6_MULTICAST_ADDR), &dhcp_bcast_ip6); + net_set_udp_handler(dhcp6_handler); + net_send_udp_packet6((uchar *)net_bcast_ethaddr, &dhcp_bcast_ip6, + PORT_DHCP6_S, PORT_DHCP6_C, len); +} + +/** + * dhcp6_send_request_packet() - Send a REQUEST packet + * + * * Implements RFC 8415: + * - 16.4. Request Message + * - 18.2.2. Creation and Transmission of Request Messages + * + * Adds DHCP6 header and DHCP6 options. Sends the UDP packet + * and sets the UDP handler. + */ +static void dhcp6_send_request_packet(void) +{ + struct in6_addr dhcp_bcast_ip6; + int len = 0; + uchar *pkt; + uchar *dhcp_pkt_start_ptr; + struct dhcp6_hdr *dhcp_hdr; + + pkt = net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE + UDP_HDR_SIZE; + dhcp_pkt_start_ptr = pkt; + + /* Add the DHCP6 header */ + dhcp_hdr = (struct dhcp6_hdr *)pkt; + dhcp_hdr->msg_type = DHCP6_MSG_REQUEST; + dhcp_hdr->trans_id = htons(sm_params.trans_id); + pkt += sizeof(struct dhcp6_hdr); + + /* add the options */ + pkt += dhcp6_add_option(DHCP6_OPTION_CLIENTID, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_ELAPSED_TIME, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_IA_NA, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_ORO, pkt); + /* copy received IA_TA/IA_NA into the REQUEST packet */ + if (sm_params.server_uid.uid_ptr) { + memcpy(pkt, sm_params.server_uid.uid_ptr, sm_params.server_uid.uid_size); + pkt += sm_params.server_uid.uid_size; + } + if (CONFIG_DHCP6_PXE_CLIENTARCH != 0xFF) + pkt += dhcp6_add_option(DHCP6_OPTION_CLIENT_ARCH_TYPE, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_VENDOR_CLASS, pkt); + pkt += dhcp6_add_option(DHCP6_OPTION_NII, pkt); + + /* calculate packet length */ + len = pkt - dhcp_pkt_start_ptr; + + /* send UDP packet to DHCP6 multicast address */ + string_to_ip6(DHCP6_MULTICAST_ADDR, strlen(DHCP6_MULTICAST_ADDR), &dhcp_bcast_ip6); + net_set_udp_handler(dhcp6_handler); + net_send_udp_packet6((uchar *)net_bcast_ethaddr, &dhcp_bcast_ip6, + PORT_DHCP6_S, PORT_DHCP6_C, len); +} + +static void dhcp6_parse_ia_options(struct dhcp6_option_hdr *ia_ptr, uchar *ia_option_ptr) +{ + struct dhcp6_option_hdr *ia_option_hdr; + + ia_option_hdr = (struct dhcp6_option_hdr *)ia_option_ptr; + + /* Search for options encapsulated in IA_NA/IA_TA (DHCP6_OPTION_IAADDR + * or DHCP6_OPTION_STATUS_CODE) + */ + while (ia_option_ptr < ((uchar *)ia_ptr + ntohs(ia_ptr->option_len))) { + switch (ntohs(ia_option_hdr->option_id)) { + case DHCP6_OPTION_IAADDR: + sm_params.rx_status.ia_addr_found = true; + net_copy_ip6(&sm_params.rx_status.ia_addr_ipv6, + (ia_option_ptr + sizeof(struct dhcp6_hdr))); + debug("DHCP6_OPTION_IAADDR FOUND\n"); + break; + case DHCP6_OPTION_STATUS_CODE: + sm_params.rx_status.ia_status_code = + ntohs(*((u16 *)(ia_option_ptr + sizeof(struct dhcp6_hdr)))); + printf("ERROR : IA STATUS %d\n", sm_params.rx_status.ia_status_code); + break; + default: + debug("Unknown Option in IA, skipping\n"); + break; + } + + ia_option_ptr += ntohs(((struct dhcp6_option_hdr *)ia_option_ptr)->option_len); + } +} + +/** + * dhcp6_parse_options() - Parse the DHCP6 options + * + * @rx_pkt: pointer to beginning of received DHCP6 packet + * @len: Total length of the DHCP6 packet + * + * Parses the DHCP options from a received DHCP packet. Perform error checking + * on the options received. Any relevant status is available in: + * "sm_params.rx_status" + * + */ +static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len) +{ + uchar *option_ptr; + int sol_max_rt_sec, option_len; + char *s, *e; + struct dhcp6_option_hdr *option_hdr; + + memset(&sm_params.rx_status, 0, sizeof(struct dhcp6_rx_pkt_status)); + + option_hdr = (struct dhcp6_option_hdr *)(rx_pkt + sizeof(struct dhcp6_hdr)); + /* check that required options exist */ + while (option_hdr < (struct dhcp6_option_hdr *)(rx_pkt + len)) { + option_ptr = ((uchar *)option_hdr) + sizeof(struct dhcp6_hdr); + option_len = ntohs(option_hdr->option_len); + + switch (ntohs(option_hdr->option_id)) { + case DHCP6_OPTION_CLIENTID: + if (memcmp(option_ptr, sm_params.duid, option_len) + != 0) { + debug("CLIENT ID DOESN'T MATCH\n"); + } else { + debug("CLIENT ID FOUND and MATCHES\n"); + sm_params.rx_status.client_id_match = true; + } + break; + case DHCP6_OPTION_SERVERID: + sm_params.rx_status.server_id_found = true; + sm_params.rx_status.server_uid_ptr = (uchar *)option_hdr; + sm_params.rx_status.server_uid_size = option_len + + sizeof(struct dhcp6_option_hdr); + debug("SERVER ID FOUND\n"); + break; + case DHCP6_OPTION_IA_TA: + case DHCP6_OPTION_IA_NA: + /* check the IA_ID */ + if (*((u32 *)option_ptr) != htonl(sm_params.ia_id)) { + debug("IA_ID mismatch 0x%08x 0x%08x\n", + *((u32 *)option_ptr), htonl(sm_params.ia_id)); + break; + } + + if (ntohs(option_hdr->option_id) == DHCP6_OPTION_IA_NA) { + /* skip past IA_ID/T1/T2 */ + option_ptr += 3 * sizeof(u32); + } else if (ntohs(option_hdr->option_id) == DHCP6_OPTION_IA_TA) { + /* skip past IA_ID */ + option_ptr += sizeof(u32); + } + /* parse the IA_NA/IA_TA encapsulated options */ + dhcp6_parse_ia_options(option_hdr, option_ptr); + break; + case DHCP6_OPTION_STATUS_CODE: + debug("DHCP6_OPTION_STATUS_CODE FOUND\n"); + sm_params.rx_status.status_code = ntohs(*((u16 *)option_ptr)); + debug("DHCP6 top-level status code %d\n", sm_params.rx_status.status_code); + debug("DHCP6 status message: %.*s\n", len, option_ptr + 2); + break; + case DHCP6_OPTION_SOL_MAX_RT: + debug("DHCP6_OPTION_SOL_MAX_RT FOUND\n"); + sol_max_rt_sec = ntohl(*((u32 *)option_ptr)); + + /* A DHCP client MUST ignore any SOL_MAX_RT option values that are less + * than 60 or more than 86400 + */ + if (sol_max_rt_sec >= 60 && sol_max_rt_sec <= 86400) { + updated_sol_max_rt_ms = sol_max_rt_sec * 1000; + if (sm_params.curr_state == DHCP6_SOLICIT) + sm_params.mrt_ms = updated_sol_max_rt_ms; + } + break; + case DHCP6_OPTION_OPT_BOOTFILE_URL: + debug("DHCP6_OPTION_OPT_BOOTFILE_URL FOUND\n"); + copy_filename(net_boot_file_name, option_ptr, option_len + 1); + debug("net_boot_file_name: %s\n", net_boot_file_name); + + /* copy server_ip6 (required for PXE) */ + s = strchr(net_boot_file_name, '['); + e = strchr(net_boot_file_name, ']'); + if (s && e && e > s) + string_to_ip6(s + 1, e - s - 1, &net_server_ip6); + break; + case DHCP6_OPTION_OPT_BOOTFILE_PARAM: + if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)) { + debug("DHCP6_OPTION_OPT_BOOTFILE_PARAM FOUND\n"); + + if (pxelinux_configfile) + free(pxelinux_configfile); + + pxelinux_configfile = (char *)malloc((option_len + 1) * + sizeof(char)); + if (pxelinux_configfile) + strlcpy(pxelinux_configfile, option_ptr, option_len + 1); + else + printf("Error: Failed to allocate pxelinux_configfile\n"); + + debug("PXE CONFIG FILE %s\n", pxelinux_configfile); + } + break; + case DHCP6_OPTION_PREFERENCE: + debug("DHCP6_OPTION_PREFERENCE FOUND\n"); + sm_params.rx_status.preference = *option_ptr; + break; + default: + debug("Unknown Option ID: %d, skipping parsing\n", + ntohs(option_hdr->option_id)); + break; + } + /* Increment to next option header */ + option_hdr = (struct dhcp6_option_hdr *)(((uchar *)option_hdr) + + sizeof(struct dhcp6_option_hdr) + option_len); + } +} + +/** + * dhcp6_check_advertise_packet() - Perform error checking on an expected + * ADVERTISE packet. + * + * @rx_pkt: pointer to beginning of received DHCP6 packet + * @len: Total length of the DHCP6 packet + * + * Implements RFC 8415: + * - 16.3. Advertise Message + * - 18.2.10. Receipt of Reply Messages + * + * Return : 0 : ADVERTISE packet was received with no errors. + * State machine can progress + * 1 : - packet received is not an ADVERTISE packet + * - there were errors in the packet received, + * - this is the first SOLICIT packet, but + * received preference is not 255, so we have + * to wait for more server responses. + */ +static int dhcp6_check_advertise_packet(uchar *rx_pkt, unsigned int len) +{ + u16 rx_uid_size; + struct dhcp6_hdr *dhcp6_hdr = (struct dhcp6_hdr *)rx_pkt; + + /* Ignore message if msg-type != advertise */ + if (dhcp6_hdr->msg_type != DHCP6_MSG_ADVERTISE) + return 1; + /* Ignore message if transaction ID doesn't match */ + if (dhcp6_hdr->trans_id != htons(sm_params.trans_id)) + return 1; + + dhcp6_parse_options(rx_pkt, len); + + /* Ignore advertise if any of these conditions met */ + if (!sm_params.rx_status.server_id_found || + !sm_params.rx_status.client_id_match || + sm_params.rx_status.status_code != DHCP6_SUCCESS) { + return 1; + } + + if (sm_params.rx_status.server_id_found) { + /* if no server UID has been received yet, or if the server UID + * received has a higher preference value than the currently saved + * server UID, save the new server UID and preference + */ + if (!sm_params.server_uid.uid_ptr || + (sm_params.server_uid.uid_ptr && + sm_params.server_uid.preference < sm_params.rx_status.preference)) { + rx_uid_size = sm_params.rx_status.server_uid_size; + if (sm_params.server_uid.uid_ptr) + free(sm_params.server_uid.uid_ptr); + sm_params.server_uid.uid_ptr = malloc(rx_uid_size * sizeof(uchar)); + if (sm_params.server_uid.uid_ptr) + memcpy(sm_params.server_uid.uid_ptr, + sm_params.rx_status.server_uid_ptr, rx_uid_size); + + sm_params.server_uid.uid_size = rx_uid_size; + sm_params.server_uid.preference = sm_params.rx_status.preference; + } + + /* If the first SOLICIT and preference code is 255, use right away. + * Otherwise, wait for the first SOLICIT period for more + * DHCP6 servers to respond. + */ + if (sm_params.retry_cnt == 1 && + sm_params.server_uid.preference != 255) { + debug("valid ADVERTISE, waiting for first SOLICIT period\n"); + return 1; + } + } + + return 0; +} + +/** + * dhcp6_check_reply_packet() - Perform error checking on an expected + * REPLY packet. + * + * @rx_pkt: pointer to beginning of received DHCP6 packet + * @len: Total length of the DHCP6 packet + * + * Implements RFC 8415: + * - 16.10. Reply Message + * - 18.2.10. Receipt of Reply Messages + * + * Return : 0 - REPLY packet was received with no errors + * 1 - packet received is not an REPLY packet, + * or there were errors in the packet received + */ +static int dhcp6_check_reply_packet(uchar *rx_pkt, unsigned int len) +{ + struct dhcp6_hdr *dhcp6_hdr = (struct dhcp6_hdr *)rx_pkt; + + /* Ignore message if msg-type != reply */ + if (dhcp6_hdr->msg_type != DHCP6_MSG_REPLY) + return 1; + /* check that transaction ID matches */ + if (dhcp6_hdr->trans_id != htons(sm_params.trans_id)) + return 1; + + dhcp6_parse_options(rx_pkt, len); + + /* if no addresses found, restart DHCP */ + if (!sm_params.rx_status.ia_addr_found || + sm_params.rx_status.ia_status_code == DHCP6_NO_ADDRS_AVAIL || + sm_params.rx_status.status_code == DHCP6_NOT_ON_LINK) { + /* restart DHCP */ + debug("No address found in reply. Restarting DHCP\n"); + dhcp6_start(); + } + + /* ignore reply if any of these conditions met */ + if (!sm_params.rx_status.server_id_found || + !sm_params.rx_status.client_id_match || + sm_params.rx_status.status_code == DHCP6_UNSPEC_FAIL) { + return 1; + } + + return 0; +} + +/* Timeout for DHCP6 SOLICIT/REQUEST */ +static void dhcp6_timeout_handler(void) +{ + /* call state machine with the timeout flag */ + dhcp6_state_machine(true, NULL, 0); +} + +/** + * dhcp6_state_machine() - DHCP6 state machine + * + * @timeout: TRUE : timeout waiting for response from + * DHCP6 server + * FALSE : init or received response from DHCP6 server + * @rx_pkt: Pointer to the beginning of received DHCP6 packet. + * Will be NULL if called as part of init + * or timeout==TRUE + * @len: Total length of the DHCP6 packet if rx_pkt != NULL + * + * Implements RFC 8415: + * - 5.2. Client/Server Exchanges Involving Four Messages + * - 15. Reliability of Client-Initiated Message Exchanges + * + * Handles: + * - transmission of SOLICIT and REQUEST packets + * - retransmission of SOLICIT and REQUEST packets if no + * response is received within the timeout window + * - checking received ADVERTISE and REPLY packets to + * assess if the DHCP state machine can progress + */ +static void dhcp6_state_machine(bool timeout, uchar *rx_pkt, unsigned int len) +{ + int rand_minus_plus_100; + + switch (sm_params.curr_state) { + case DHCP6_INIT: + sm_params.next_state = DHCP6_SOLICIT; + break; + case DHCP6_SOLICIT: + if (!timeout) { + /* check the rx packet and determine if we can transition to next + * state. + */ + if (dhcp6_check_advertise_packet(rx_pkt, len)) + return; + + debug("ADVERTISE good, transition to REQUEST\n"); + sm_params.next_state = DHCP6_REQUEST; + } else if (sm_params.retry_cnt == 1) { + /* If a server UID was received in the first SOLICIT period + * transition to REQUEST + */ + if (sm_params.server_uid.uid_ptr) + sm_params.next_state = DHCP6_REQUEST; + } + break; + case DHCP6_REQUEST: + if (!timeout) { + /* check the rx packet and determine if we can transition to next state */ + if (dhcp6_check_reply_packet(rx_pkt, len)) + return; + + debug("REPLY good, transition to DONE\n"); + sm_params.next_state = DHCP6_DONE; + } + break; + case DHCP6_DONE: + case DHCP6_FAIL: + /* Shouldn't get here, as state machine should exit + * immediately when DHCP6_DONE or DHCP6_FAIL is entered. + * Proceed anyway to proceed DONE/FAIL actions + */ + debug("Unexpected DHCP6 state : %d\n", sm_params.curr_state); + break; + } + /* re-seed the RNG */ + srand(get_ticks() + rand()); + + /* handle state machine entry conditions */ + if (sm_params.curr_state != sm_params.next_state) { + sm_params.retry_cnt = 0; + + if (sm_params.next_state == DHCP6_SOLICIT) { + /* delay a random ammount (special for SOLICIT) */ + udelay((rand() % SOL_MAX_DELAY_MS) * 1000); + /* init timestamp variables after SOLICIT delay */ + sm_params.dhcp6_start_ms = get_timer(0); + sm_params.dhcp6_retry_start_ms = sm_params.dhcp6_start_ms; + sm_params.dhcp6_retry_ms = sm_params.dhcp6_start_ms; + /* init transaction and ia_id */ + sm_params.trans_id = rand() & 0xFFFFFF; + sm_params.ia_id = rand(); + /* initialize retransmission parameters */ + sm_params.irt_ms = SOL_TIMEOUT_MS; + sm_params.mrt_ms = updated_sol_max_rt_ms; + /* RFCs default MRC is be 0 (try infinitely) + * give up after CONFIG_NET_RETRY_COUNT number of tries (same as DHCPv4) + */ + sm_params.mrc = CONFIG_NET_RETRY_COUNT; + sm_params.mrd_ms = 0; + + } else if (sm_params.next_state == DHCP6_REQUEST) { + /* init timestamp variables */ + sm_params.dhcp6_retry_start_ms = get_timer(0); + sm_params.dhcp6_retry_ms = sm_params.dhcp6_start_ms; + /* initialize retransmission parameters */ + sm_params.irt_ms = REQ_TIMEOUT_MS; + sm_params.mrt_ms = REQ_MAX_RT_MS; + sm_params.mrc = REQ_MAX_RC; + sm_params.mrd_ms = 0; + } + } + + if (timeout) + sm_params.dhcp6_retry_ms = get_timer(0); + + /* Check if MRC or MRD have been passed */ + if ((sm_params.mrc != 0 && + sm_params.retry_cnt >= sm_params.mrc) || + (sm_params.mrd_ms != 0 && + ((sm_params.dhcp6_retry_ms - sm_params.dhcp6_retry_start_ms) >= sm_params.mrd_ms))) { + sm_params.next_state = DHCP6_FAIL; + } + + /* calculate retransmission timeout (RT) */ + rand_minus_plus_100 = ((rand() % 200) - 100); + if (sm_params.retry_cnt == 0) { + sm_params.rt_ms = sm_params.irt_ms + + ((sm_params.irt_ms * rand_minus_plus_100) / 1000); + } else { + sm_params.rt_ms = (2 * sm_params.rt_prev_ms) + + ((sm_params.rt_prev_ms * rand_minus_plus_100) / 1000); + } + + if (sm_params.rt_ms > sm_params.mrt_ms) { + sm_params.rt_ms = sm_params.mrt_ms + + ((sm_params.mrt_ms * rand_minus_plus_100) / 1000); + } + + sm_params.rt_prev_ms = sm_params.rt_ms; + + net_set_timeout_handler(sm_params.rt_ms, dhcp6_timeout_handler); + + /* send transmit/retransmit message or fail */ + sm_params.curr_state = sm_params.next_state; + + if (sm_params.curr_state == DHCP6_SOLICIT) { + /* send solicit packet */ + dhcp6_send_solicit_packet(); + printf("DHCP6 SOLICIT %d\n", sm_params.retry_cnt); + } else if (sm_params.curr_state == DHCP6_REQUEST) { + /* send request packet */ + dhcp6_send_request_packet(); + printf("DHCP6 REQUEST %d\n", sm_params.retry_cnt); + } else if (sm_params.curr_state == DHCP6_DONE) { + net_set_timeout_handler(0, NULL); + + /* Duplicate address detection (DAD) should be + * performed here before setting net_ip6 + * (enhancement should be considered) + */ + net_copy_ip6(&net_ip6, &sm_params.rx_status.ia_addr_ipv6); + printf("DHCP6 client bound to %pI6c\n", &net_ip6); + /* will load with TFTP6 */ + net_auto_load(); + } else if (sm_params.curr_state == DHCP6_FAIL) { + printf("DHCP6 FAILED, TERMINATING\n"); + net_set_state(NETLOOP_FAIL); + } + sm_params.retry_cnt++; +} + +/* Start or restart DHCP6 */ +void dhcp6_start(void) +{ + memset(&sm_params, 0, sizeof(struct dhcp6_sm_params)); + + /* seed the RNG with MAC address */ + srand_mac(); + + sm_params.curr_state = DHCP6_INIT; + dhcp6_state_machine(false, NULL, 0); +} diff --git a/net/dhcpv6.h b/net/dhcpv6.h new file mode 100644 index 00000000000..80ca5204325 --- /dev/null +++ b/net/dhcpv6.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) Microsoft Corporation + * Author: Sean Edmond + * + */ + +#ifndef __DHCP6_H__ +#define __DHCP6_H__ + +/* Message types */ +#define DHCP6_MSG_SOLICIT 1 +#define DHCP6_MSG_ADVERTISE 2 +#define DHCP6_MSG_REQUEST 3 +#define DHCP6_MSG_REPLY 7 + +/* Option Codes */ +#define DHCP6_OPTION_CLIENTID 1 +#define DHCP6_OPTION_SERVERID 2 +#define DHCP6_OPTION_IA_NA 3 +#define DHCP6_OPTION_IA_TA 4 +#define DHCP6_OPTION_IAADDR 5 +#define DHCP6_OPTION_ORO 6 +#define DHCP6_OPTION_PREFERENCE 7 +#define DHCP6_OPTION_ELAPSED_TIME 8 +#define DHCP6_OPTION_STATUS_CODE 13 +#define DHCP6_OPTION_OPT_BOOTFILE_URL 59 +#define DHCP6_OPTION_OPT_BOOTFILE_PARAM 60 +#define DHCP6_OPTION_SOL_MAX_RT 82 +#define DHCP6_OPTION_CLIENT_ARCH_TYPE 61 +#define DHCP6_OPTION_VENDOR_CLASS 16 +#define DHCP6_OPTION_NII 62 + +/* DUID */ +#define DUID_TYPE_LL 3 +#define DUID_HW_TYPE_ENET 1 +#define DUID_LL_SIZE (sizeof(struct dhcp6_option_duid_ll) + ETH_ALEN) +#define DUID_MAX_SIZE DUID_LL_SIZE /* only supports DUID-LL currently */ + +/* vendor-class-data to send in vendor clas option */ +#define DHCP6_VCI_STRING "U-boot" + +#define DHCP6_MULTICAST_ADDR "ff02::1:2" /* DHCP multicast address */ + +/* DHCP6 States supported */ +enum dhcp6_state { + DHCP6_INIT, + DHCP6_SOLICIT, + DHCP6_REQUEST, + DHCP6_DONE, + DHCP6_FAIL, +}; + +/* DHCP6 Status codes */ +enum dhcp6_status { + DHCP6_SUCCESS = 0, + DHCP6_UNSPEC_FAIL = 1, + DHCP6_NO_ADDRS_AVAIL = 2, + DHCP6_NO_BINDING = 3, + DHCP6_NOT_ON_LINK = 4, + DHCP6_USE_MULTICAST = 5, + DHCP6_NO_PREFIX_AVAIL = 6, +}; + +/* DHCP6 message header format */ +struct dhcp6_hdr { + unsigned int msg_type : 8; /* message type */ + unsigned int trans_id : 24; /* transaction ID */ +} __packed; + +/* DHCP6 option header format */ +struct dhcp6_option_hdr { + __be16 option_id; /* option id */ + __be16 option_len; /* Option length */ + u8 option_data[0]; /* Option data */ +} __packed; + +/* DHCP6_OPTION_CLIENTID option (DUID-LL) */ +struct dhcp6_option_duid_ll { + __be16 duid_type; + __be16 hw_type; + u8 ll_addr[0]; +} __packed; + +/* DHCP6_OPTION_ELAPSED_TIME option */ +struct dhcp6_option_elapsed_time { + __be16 elapsed_time; +} __packed; + +/* DHCP6_OPTION_IA_TA option */ +struct dhcp6_option_ia_ta { + __be32 iaid; + u8 ia_ta_options[0]; +} __packed; + +/* DHCP6_OPTION_IA_NA option */ +struct dhcp6_option_ia_na { + __be32 iaid; + __be32 t1; + __be32 t2; + u8 ia_na_options[0]; +} __packed; + +/* OPTION_ORO option */ +struct dhcp6_option_oro { + __be16 req_option_code[0]; +} __packed; + +/* DHCP6_OPTION_CLIENT_ARCH_TYPE option */ +struct dhcp6_option_client_arch { + __be16 arch_type[0]; +} __packed; + +/* vendor-class-data inside OPTION_VENDOR_CLASS option */ +struct vendor_class_data { + __be16 vendor_class_len; + u8 opaque_data[0]; +} __packed; + +/* DHCP6_OPTION_VENDOR_CLASS option */ +struct dhcp6_option_vendor_class { + __be32 enterprise_number; + struct vendor_class_data vendor_class_data[0]; +} __packed; + +/** + * struct dhcp6_rx_pkt_status - Structure that holds status + * from a received message + * @client_id_match: Client ID was found and matches DUID sent + * @server_id_found: Server ID was found in the message + * @server_uid_ptr: Pointer to received server ID + * @server_uid_size: Size of received server ID + * @ia_addr_found: IA addr option was found in received message + * @ia_addr_ipv6: The IPv6 address received in IA + * @ia_status_code: Status code received in the IA + * @status_code: Top-level status code received + * @preference: Preference code received + */ +struct dhcp6_rx_pkt_status { + bool client_id_match; + bool server_id_found; + uchar *server_uid_ptr; + u16 server_uid_size; + bool ia_addr_found; + struct in6_addr ia_addr_ipv6; + enum dhcp6_status ia_status_code; + enum dhcp6_status status_code; + u8 preference; +}; + +/** + * struct dhcp6_server_uid - Structure that holds the server UID + * received from an ADVERTISE and saved + * given the server selection criteria. + * @uid_ptr: Dynamically allocated and copied server UID + * @uid_size: Size of the server UID in uid_ptr (in bytes) + * @preference: Preference code associated with this server UID + */ +struct dhcp6_server_uid { + uchar *uid_ptr; + u16 uid_size; + u8 preference; +}; + +/** + * struct dhcp6_sm_params - Structure that holds DHCP6 + * state machine parameters + * @curr_state: current DHCP6 state + * @next_state: next DHCP6 state + * @dhcp6_start_ms: timestamp DHCP6 start + * @dhcp6_retry_start_ms: timestamp of current TX message start + * @dhcp6_retry_ms: timestamp of last retransmission + * @retry_cnt: retry count + * @trans_id: transaction ID + * @ia_id: transmitted IA ID + * @irt_ms: Initial retransmission time (in ms) + * @mrt_ms: Maximum retransmission time (in ms) + * @mrc: Maximum retransmission count + * @mrd_ms: Maximum retransmission duration (in ms) + * @rt_ms: retransmission timeout (is ms) + * @rt_prev_ms: previous retransmission timeout + * @rx_status: Status from received message + * @server_uid: Saved Server UID for selected server + * @duid: pointer to transmitted Client DUID + */ +struct dhcp6_sm_params { + enum dhcp6_state curr_state; + enum dhcp6_state next_state; + ulong dhcp6_start_ms; + ulong dhcp6_retry_start_ms; + ulong dhcp6_retry_ms; + u32 retry_cnt; + u32 trans_id; + u32 ia_id; + int irt_ms; + int mrt_ms; + int mrc; + int mrd_ms; + int rt_ms; + int rt_prev_ms; + struct dhcp6_rx_pkt_status rx_status; + struct dhcp6_server_uid server_uid; + char duid[DUID_MAX_SIZE]; +}; + +/* Starts a DHCPv6 4-message exchange as a DHCPv6 client. On successful exchange, + * the DHCPv6 state machine will transition from internal states: + * DHCP6_INIT->DHCP6_SOLICIT->DHCP6_REQUEST->DHCP6_DONE + * + * Transmitted SOLICIT and REQUEST packets will set/request the minimum required + * DHCPv6 options to PXE boot. + * + * After a successful exchange, the DHCPv6 assigned address will be set in net_ip6 + * + * Additionally, the following will be set after receiving these options: + * DHCP6_OPTION_OPT_BOOTFILE_URL (option 59) -> net_server_ip6, net_boot_file_name + * DHCP6_OPTION_OPT_BOOTFILE_PARAM (option 60) - > pxelinux_configfile + * + * Illustration of a 4-message exchange with 2 servers (copied from + * https://www.rfc-editor.org/rfc/rfc8415): + * + * Server Server + * (not selected) Client (selected) + * + * v v v + * | | | + * | Begins initialization | + * | | | + * start of | _____________/|\_____________ | + * 4-message |/ Solicit | Solicit \| + * exchange | | | + * Determines | Determines + * configuration | configuration + * | | | + * |\ | ____________/| + * | \________ | /Advertise | + * | Advertise\ |/ | + * | \ | | + * | Collects Advertises | + * | \ | | + * | Selects configuration | + * | | | + * | _____________/|\_____________ | + * |/ Request | Request \| + * | | | + * | | Commits configuration + * | | | + * end of | | _____________/| + * 4-message | |/ Reply | + * exchange | | | + * | Initialization complete | + * | | | + */ +void dhcp6_start(void); + +#endif /* __DHCP6_H__ */ diff --git a/net/net.c b/net/net.c index c9a749f6cc8..8cb8b4b9f34 100644 --- a/net/net.c +++ b/net/net.c @@ -107,6 +107,8 @@ #include #include #include +#include +#include #include "arp.h" #include "bootp.h" #include "cdp.h" @@ -120,8 +122,7 @@ #if defined(CONFIG_CMD_WOL) #include "wol.h" #endif -#include -#include +#include "dhcpv6.h" /** BOOTP EXTENTIONS **/ @@ -135,6 +136,8 @@ struct in_addr net_dns_server; /* Our 2nd DNS IP address */ struct in_addr net_dns_server2; #endif +/* Indicates whether the pxe path prefix / config file was specified in dhcp option */ +char *pxelinux_configfile; /** END OF BOOTP EXTENTIONS **/ @@ -510,6 +513,10 @@ restart: dhcp_request(); /* Basically same as BOOTP */ break; #endif + case DHCP6: + if (IS_ENABLED(CONFIG_CMD_DHCP6)) + dhcp6_start(); + break; #if defined(CONFIG_CMD_BOOTP) case BOOTP: bootp_reset(); -- cgit v1.3.1 From 7d0188927bcf4f6ce74ccfca43f0f081a39ade00 Mon Sep 17 00:00:00 2001 From: Sean Edmond Date: Tue, 11 Apr 2023 10:48:47 -0700 Subject: net: dhcp6: pxe: Add DHCP/PXE commands for IPv6 Adds commands to support DHCP and PXE with IPv6. New configs added: - CMD_DHCP6 - DHCP6_PXE_CLIENTARCH - DHCP6_PXE_DHCP_OPTION - DHCP6_ENTERPRISE_ID New commands added (when IPv6 is enabled): - dhcp6 - pxe get -ipv6 - pxe boot -ipv6 Signed-off-by: Sean Edmond Reviewed-by: Ramon Fried --- boot/bootmeth_distro.c | 2 +- boot/bootmeth_pxe.c | 4 +-- boot/pxe_utils.c | 3 +- cmd/Kconfig | 26 +++++++++++++++ cmd/net.c | 23 ++++++++++++++ cmd/pxe.c | 85 ++++++++++++++++++++++++++++++++++++++++---------- cmd/sysboot.c | 2 +- include/pxe_utils.h | 10 ++++-- 8 files changed, 132 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/boot/bootmeth_distro.c b/boot/bootmeth_distro.c index 356929828b9..b4b73ecbf58 100644 --- a/boot/bootmeth_distro.c +++ b/boot/bootmeth_distro.c @@ -150,7 +150,7 @@ static int distro_boot(struct udevice *dev, struct bootflow *bflow) info.dev = dev; info.bflow = bflow; ret = pxe_setup_ctx(&ctx, &cmdtp, distro_getfile, &info, true, - bflow->subdir); + bflow->subdir, false); if (ret) return log_msg_ret("ctx", -EINVAL); diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index ecf8557af83..5a8af2bbd0b 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -70,7 +70,7 @@ static int distro_pxe_read_bootflow(struct udevice *dev, struct bootflow *bflow) addr = simple_strtoul(addr_str, NULL, 16); log_debug("calling pxe_get()\n"); - ret = pxe_get(addr, &bootdir, &size); + ret = pxe_get(addr, &bootdir, &size, false); log_debug("pxe_get() returned %d\n", ret); if (ret) return log_msg_ret("pxeb", ret); @@ -146,7 +146,7 @@ static int distro_pxe_boot(struct udevice *dev, struct bootflow *bflow) info.bflow = bflow; info.cmdtp = &cmdtp; ret = pxe_setup_ctx(ctx, &cmdtp, distro_pxe_getfile, &info, false, - bflow->subdir); + bflow->subdir, false); if (ret) return log_msg_ret("ctx", -EINVAL); diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 3a1e50f2b1b..d13c47dd942 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -1578,7 +1578,7 @@ void handle_pxe_menu(struct pxe_context *ctx, struct pxe_menu *cfg) int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp, pxe_getfile_func getfile, void *userdata, - bool allow_abs_path, const char *bootfile) + bool allow_abs_path, const char *bootfile, bool use_ipv6) { const char *last_slash; size_t path_len = 0; @@ -1588,6 +1588,7 @@ int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp, ctx->getfile = getfile; ctx->userdata = userdata; ctx->allow_abs_path = allow_abs_path; + ctx->use_ipv6 = use_ipv6; /* figure out the boot directory, if there is one */ if (bootfile && strlen(bootfile) >= MAX_TFTP_PATH_LEN) diff --git a/cmd/Kconfig b/cmd/Kconfig index e45b8847aef..460f29883ae 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1673,6 +1673,15 @@ config CMD_DHCP help Boot image via network using DHCP/TFTP protocol +config CMD_DHCP6 + bool "dhcp6" + depends on IPV6 + help + Boot image via network using DHCPv6/TFTP protocol using IPv6. + + Will perform 4-message exchange with DHCPv6 server, requesting + the minimum required options to TFTP boot. Complies with RFC 8415. + config BOOTP_MAY_FAIL bool "Allow for the BOOTP/DHCP server to not be found" depends on CMD_BOOTP @@ -1786,6 +1795,23 @@ config BOOTP_VCI_STRING default "U-Boot.arm" if ARM default "U-Boot" +if CMD_DHCP6 + +config DHCP6_PXE_CLIENTARCH + hex + default 0x16 if ARM64 + default 0x15 if ARM + default 0xFF + +config DHCP6_PXE_DHCP_OPTION + bool "Request & store 'pxe_configfile' from DHCP6 server" + +config DHCP6_ENTERPRISE_ID + int "Enterprise ID to send in DHCPv6 Vendor Class Option" + default 0 + +endif + config CMD_TFTPBOOT bool "tftpboot" default y diff --git a/cmd/net.c b/cmd/net.c index 036b7720a7c..68d406291ef 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -111,6 +111,29 @@ U_BOOT_CMD( ); #endif +#if defined(CONFIG_CMD_DHCP6) +static int do_dhcp6(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + int dhcp_argc; + char *dhcp_argv[] = {NULL, NULL, NULL, NULL}; + + /* Add -ipv6 flag for autoload */ + for (i = 0; i < argc; i++) + dhcp_argv[i] = argv[i]; + dhcp_argc = argc + 1; + dhcp_argv[dhcp_argc - 1] = USE_IP6_CMD_PARAM; + + return netboot_common(DHCP6, cmdtp, dhcp_argc, dhcp_argv); +} + +U_BOOT_CMD(dhcp6, 3, 1, do_dhcp6, + "boot image via network using DHCPv6/TFTP protocol.\n" + "Use IPv6 hostIPaddr framed with [] brackets", + "[loadAddress] [[hostIPaddr:]bootfilename]"); +#endif + #if defined(CONFIG_CMD_DHCP) static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) diff --git a/cmd/pxe.c b/cmd/pxe.c index db8e4697f24..677142520bb 100644 --- a/cmd/pxe.c +++ b/cmd/pxe.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "pxe_utils.h" @@ -29,12 +31,20 @@ static int do_get_tftp(struct pxe_context *ctx, const char *file_path, { char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; int ret; + int num_args; tftp_argv[1] = file_addr; tftp_argv[2] = (void *)file_path; + if (ctx->use_ipv6) { + tftp_argv[3] = USE_IP6_CMD_PARAM; + num_args = 4; + } else { + num_args = 3; + } - if (do_tftpb(ctx->cmdtp, 0, 3, tftp_argv)) + if (do_tftpb(ctx->cmdtp, 0, num_args, tftp_argv)) return -ENOENT; + ret = pxe_get_file_size(sizep); if (ret) return log_msg_ret("tftp", ret); @@ -43,6 +53,22 @@ static int do_get_tftp(struct pxe_context *ctx, const char *file_path, return 1; } +/* + * Looks for a pxe file with specified config file name, + * which is received from DHCPv4 option 209 or + * DHCPv6 option 60. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_dhcp_option_path(struct pxe_context *ctx, unsigned long pxefile_addr_r) +{ + int ret = get_pxe_file(ctx, pxelinux_configfile, pxefile_addr_r); + + free(pxelinux_configfile); + + return ret; +} + /* * Looks for a pxe file with a name based on the pxeuuid environment variable. * @@ -105,15 +131,24 @@ static int pxe_ipaddr_paths(struct pxe_context *ctx, unsigned long pxefile_addr_ return -ENOENT; } -int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep) +int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6) { struct cmd_tbl cmdtp[] = {}; /* dummy */ struct pxe_context ctx; int i; if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false, - env_get("bootfile"))) + env_get("bootfile"), use_ipv6)) return -ENOMEM; + + if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION) && + pxelinux_configfile && use_ipv6) { + if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0) + goto done; + + goto error_exit; + } + /* * Keep trying paths until we successfully get a file we're looking * for. @@ -131,6 +166,7 @@ int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep) i++; } +error_exit: pxe_destroy_ctx(&ctx); return -ENOENT; @@ -169,9 +205,18 @@ do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) char *fname; ulong size; int ret; + bool use_ipv6 = false; - if (argc != 1) - return CMD_RET_USAGE; + if (IS_ENABLED(CONFIG_IPV6)) { + if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) + use_ipv6 = true; + + if (!(argc == 1 || (argc == 2 && use_ipv6))) + return CMD_RET_USAGE; + } else { + if (argc != 1) + return CMD_RET_USAGE; + } pxefile_addr_str = from_env("pxefile_addr_r"); @@ -183,7 +228,7 @@ do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) if (ret < 0) return 1; - ret = pxe_get(pxefile_addr_r, &fname, &size); + ret = pxe_get(pxefile_addr_r, &fname, &size, use_ipv6); switch (ret) { case 0: printf("Config file '%s' found\n", fname); @@ -211,13 +256,19 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) char *pxefile_addr_str; struct pxe_context ctx; int ret; + bool use_ipv6 = false; + + if (IS_ENABLED(CONFIG_IPV6)) { + if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) + use_ipv6 = true; + } - if (argc == 1) { + if (argc == 1 || (argc == 2 && use_ipv6)) { pxefile_addr_str = from_env("pxefile_addr_r"); if (!pxefile_addr_str) return 1; - } else if (argc == 2) { + } else if (argc == 2 || (argc == 3 && use_ipv6)) { pxefile_addr_str = argv[1]; } else { return CMD_RET_USAGE; @@ -229,7 +280,7 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false, - env_get("bootfile"))) { + env_get("bootfile"), use_ipv6)) { printf("Out of memory\n"); return CMD_RET_FAILURE; } @@ -244,8 +295,8 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } static struct cmd_tbl cmd_pxe_sub[] = { - U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""), - U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "") + U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""), + U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "") }; static void __maybe_unused pxe_reloc(void) @@ -281,9 +332,11 @@ static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) return CMD_RET_USAGE; } -U_BOOT_CMD(pxe, 3, 1, do_pxe, - "commands to get and boot from pxe files", - "get - try to retrieve a pxe file using tftp\n" - "pxe boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n" +U_BOOT_CMD(pxe, 4, 1, do_pxe, + "commands to get and boot from pxe files\n" + "To use IPv6 add -ipv6 parameter", + "get [" USE_IP6_CMD_PARAM "] - try to retrieve a pxe file using tftp\n" + "pxe boot [pxefile_addr_r] [-ipv6] - boot from the pxe file at pxefile_addr_r\n" ); -#endif + +#endif /* CONFIG_CMD_NET */ diff --git a/cmd/sysboot.c b/cmd/sysboot.c index 04c07020269..63a7806debe 100644 --- a/cmd/sysboot.c +++ b/cmd/sysboot.c @@ -101,7 +101,7 @@ static int do_sysboot(struct cmd_tbl *cmdtp, int flag, int argc, } if (pxe_setup_ctx(&ctx, cmdtp, sysboot_read_file, &info, true, - filename)) { + filename, false)) { printf("Out of memory\n"); return CMD_RET_FAILURE; } diff --git a/include/pxe_utils.h b/include/pxe_utils.h index 1e5e8424f53..9f195930487 100644 --- a/include/pxe_utils.h +++ b/include/pxe_utils.h @@ -93,6 +93,7 @@ typedef int (*pxe_getfile_func)(struct pxe_context *ctx, const char *file_path, * @bootdir: Directory that files are loaded from ("" if no directory). This is * allocated * @pxe_file_size: Size of the PXE file + * @use_ipv6: TRUE : use IPv6 addressing, FALSE : use IPv4 addressing */ struct pxe_context { struct cmd_tbl *cmdtp; @@ -112,6 +113,7 @@ struct pxe_context { bool allow_abs_path; char *bootdir; ulong pxe_file_size; + bool use_ipv6; }; /** @@ -209,12 +211,14 @@ int format_mac_pxe(char *outbuf, size_t outbuf_len); * @allow_abs_path: true to allow absolute paths * @bootfile: Bootfile whose directory loaded files are relative to, NULL if * none + * @use_ipv6: TRUE : use IPv6 addressing + * FALSE : use IPv4 addressing * Return: 0 if OK, -ENOMEM if out of memory, -E2BIG if bootfile is larger than * MAX_TFTP_PATH_LEN bytes */ int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp, pxe_getfile_func getfile, void *userdata, - bool allow_abs_path, const char *bootfile); + bool allow_abs_path, const char *bootfile, bool use_ipv6); /** * pxe_destroy_ctx() - Destroy a PXE context @@ -251,7 +255,9 @@ int pxe_get_file_size(ulong *sizep); * "rpi/info", which indicates that all files should be fetched from the * "rpi/" subdirectory * @sizep: Size of the PXE file (not bootfile) + * @use_ipv6: TRUE : use IPv6 addressing + * FALSE : use IPv4 addressing */ -int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep); +int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6); #endif /* __PXE_UTILS_H */ -- cgit v1.3.1 From 08fb8da371331c747c265d999bcb3426e3d2d0b3 Mon Sep 17 00:00:00 2001 From: Dmitrii Merkurev Date: Wed, 12 Apr 2023 19:49:29 +0100 Subject: net: support being a TCP server to unblock TCP fastboot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make following changes to unblock TCP fastboot support: 1. Implement being a TCP server support 2. Introduce dedicated TCP traffic handler (get rid of UDP signature) 3. Ensure seq_num and ack_num are respected in net_send_tcp_packet function (make sure existing wget_cmd code is reflected with the fix) Signed-off-by: Dmitrii Merkurev Cc: Ying-Chun Liu (PaulLiu) Cc: Simon Glass Сс: Joe Hershberger Сс: Ramon Fried Reviewed-by: Ying-Chun Liu (PaulLiu) Reviewed-by: Simon Glass Reviewed-by: Ramon Fried --- include/net/tcp.h | 16 +++++--- net/tcp.c | 115 +++++++++++++++++++++++++++--------------------------- net/wget.c | 43 ++++++++++---------- 3 files changed, 90 insertions(+), 84 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 322551694f5..c29d4ce24a7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -259,6 +259,7 @@ union tcp_build_pkt { * enum tcp_state - TCP State machine states for connection * @TCP_CLOSED: Need to send SYN to connect * @TCP_SYN_SENT: Trying to connect, waiting for SYN ACK + * @TCP_SYN_RECEIVED: Initial SYN received, waiting for ACK * @TCP_ESTABLISHED: both server & client have a connection * @TCP_CLOSE_WAIT: Rec FIN, passed to app for FIN, ACK rsp * @TCP_CLOSING: Rec FIN, sent FIN, ACK waiting for ACK @@ -268,6 +269,7 @@ union tcp_build_pkt { enum tcp_state { TCP_CLOSED, TCP_SYN_SENT, + TCP_SYN_RECEIVED, TCP_ESTABLISHED, TCP_CLOSE_WAIT, TCP_CLOSING, @@ -283,14 +285,18 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, /** * rxhand_tcp() - An incoming packet handler. * @pkt: pointer to the application packet - * @dport: destination UDP port + * @dport: destination TCP port * @sip: source IP address - * @sport: source UDP port + * @sport: source TCP port + * @tcp_seq_num: TCP sequential number + * @tcp_ack_num: TCP acknowledgment number + * @action: TCP action (SYN, ACK, FIN, etc) * @len: packet length */ -typedef void rxhand_tcp(uchar *pkt, unsigned int dport, - struct in_addr sip, unsigned int sport, - unsigned int len); +typedef void rxhand_tcp(uchar *pkt, u16 dport, + struct in_addr sip, u16 sport, + u32 tcp_seq_num, u32 tcp_ack_num, + u8 action, unsigned int len); void tcp_set_tcp_handler(rxhand_tcp *f); void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int len); diff --git a/net/tcp.c b/net/tcp.c index 8d338c72e84..a713e1dd609 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -36,7 +36,6 @@ static u32 rmt_timestamp; static u32 tcp_seq_init; static u32 tcp_ack_edge; -static u32 tcp_seq_max; static int tcp_activity_count; @@ -90,9 +89,10 @@ void tcp_set_tcp_state(enum tcp_state new_state) current_tcp_state = new_state; } -static void dummy_handler(uchar *pkt, unsigned int dport, - struct in_addr sip, unsigned int sport, - unsigned int len) +static void dummy_handler(uchar *pkt, u16 dport, + struct in_addr sip, u16 sport, + u32 tcp_seq_num, u32 tcp_ack_num, + u8 action, unsigned int len) { } @@ -256,7 +256,7 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, switch (action) { case TCP_SYN: debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:SYN (%pI4, %pI4, sq=%d, ak=%d)\n", + "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); tcp_activity_count = 0; @@ -271,41 +271,46 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, current_tcp_state = TCP_SYN_SENT; } break; + case TCP_SYN | TCP_ACK: case TCP_ACK: pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b); b->ip.hdr.tcp_flags = action; debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:ACK (%pI4, %pI4, s=%d, a=%d, A=%x)\n", + "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, action); break; case TCP_FIN: debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:FIN (%pI4, %pI4, s=%d, a=%d)\n", + "TCP Hdr:FIN (%pI4, %pI4, s=%u, a=%u)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); payload_len = 0; pkt_hdr_len = IP_TCP_HDR_SIZE; current_tcp_state = TCP_FIN_WAIT_1; break; - + case TCP_RST | TCP_ACK: + case TCP_RST: + debug_cond(DEBUG_DEV_PKT, + "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n", + &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); + current_tcp_state = TCP_CLOSED; + break; /* Notify connection closing */ - case (TCP_FIN | TCP_ACK): case (TCP_FIN | TCP_ACK | TCP_PUSH): if (current_tcp_state == TCP_CLOSE_WAIT) current_tcp_state = TCP_CLOSING; - tcp_ack_edge++; debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%d, a=%d, A=%x)\n", + "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n", &net_server_ip, &net_ip, - tcp_seq_num, tcp_ack_edge, action); + tcp_seq_num, tcp_ack_num, action); fallthrough; default: pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b); b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK; debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:dft (%pI4, %pI4, s=%d, a=%d, A=%x)\n", + "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, action); } @@ -313,12 +318,12 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, pkt_len = pkt_hdr_len + payload_len; tcp_len = pkt_len - IP_HDR_SIZE; + tcp_ack_edge = tcp_ack_num; /* TCP Header */ b->ip.hdr.tcp_ack = htonl(tcp_ack_edge); b->ip.hdr.tcp_src = htons(sport); b->ip.hdr.tcp_dst = htons(dport); b->ip.hdr.tcp_seq = htonl(tcp_seq_num); - tcp_seq_num = tcp_seq_num + payload_len; /* * TCP window size - TCP header variable tcp_win. @@ -353,9 +358,8 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, * tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer) * @tcp_seq_num: TCP sequence start number * @len: the length of sequence numbers - * @tcp_seq_max: maximum of sequence numbers */ -void tcp_hole(u32 tcp_seq_num, u32 len, u32 tcp_seq_max) +void tcp_hole(u32 tcp_seq_num, u32 len) { u32 idx_sack, sack_in; u32 sack_end = TCP_SACK - 1; @@ -496,7 +500,7 @@ void tcp_parse_options(uchar *o, int o_len) } } -static u8 tcp_state_machine(u8 tcp_flags, u32 *tcp_seq_num, int payload_len) +static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) { u8 tcp_fin = tcp_flags & TCP_FIN; u8 tcp_syn = tcp_flags & TCP_SYN; @@ -527,46 +531,44 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 *tcp_seq_num, int payload_len) switch (current_tcp_state) { case TCP_CLOSED: debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags); - if (tcp_ack) - action = TCP_DATA; - else if (tcp_syn) - action = TCP_RST; - else if (tcp_fin) + if (tcp_syn) { + action = TCP_SYN | TCP_ACK; + tcp_seq_init = tcp_seq_num; + tcp_ack_edge = tcp_seq_num + 1; + current_tcp_state = TCP_SYN_RECEIVED; + } else if (tcp_ack || tcp_fin) { action = TCP_DATA; + } break; + case TCP_SYN_RECEIVED: case TCP_SYN_SENT: - debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT %x, %d\n", - tcp_flags, *tcp_seq_num); + debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT | TCP_SYN_RECEIVED %x, %u\n", + tcp_flags, tcp_seq_num); if (tcp_fin) { action = action | TCP_PUSH; current_tcp_state = TCP_CLOSE_WAIT; - } - if (tcp_syn) { - action = action | TCP_ACK | TCP_PUSH; - if (tcp_ack) { - tcp_seq_init = *tcp_seq_num; - *tcp_seq_num = *tcp_seq_num + 1; - tcp_seq_max = *tcp_seq_num; - tcp_ack_edge = *tcp_seq_num; - sack_idx = 0; - edge_a[sack_idx].se.l = *tcp_seq_num; - edge_a[sack_idx].se.r = *tcp_seq_num; - prev_len = 0; - current_tcp_state = TCP_ESTABLISHED; - for (i = 0; i < TCP_SACK; i++) - edge_a[i].st = NOPKT; - } - } else if (tcp_ack) { + } else if (tcp_ack || (tcp_syn && tcp_ack)) { + action |= TCP_ACK; + tcp_seq_init = tcp_seq_num; + tcp_ack_edge = tcp_seq_num + 1; + sack_idx = 0; + edge_a[sack_idx].se.l = tcp_ack_edge; + edge_a[sack_idx].se.r = tcp_ack_edge; + prev_len = 0; + current_tcp_state = TCP_ESTABLISHED; + for (i = 0; i < TCP_SACK; i++) + edge_a[i].st = NOPKT; + + if (tcp_syn && tcp_ack) + action |= TCP_PUSH; + } else { action = TCP_DATA; } - break; case TCP_ESTABLISHED: debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags); - if (*tcp_seq_num > tcp_seq_max) - tcp_seq_max = *tcp_seq_num; if (payload_len > 0) { - tcp_hole(*tcp_seq_num, payload_len, tcp_seq_max); + tcp_hole(tcp_seq_num, payload_len); tcp_fin = TCP_DATA; /* cause standalone FIN */ } @@ -603,15 +605,14 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 *tcp_seq_num, int payload_len) case TCP_FIN_WAIT_1: debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags); if (tcp_fin) { + tcp_ack_edge++; action = TCP_ACK | TCP_FIN; current_tcp_state = TCP_FIN_WAIT_2; } if (tcp_syn) action = TCP_RST; - if (tcp_ack) { + if (tcp_ack) current_tcp_state = TCP_CLOSED; - tcp_seq_num = tcp_seq_num + 1; - } break; case TCP_CLOSING: debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags); @@ -640,7 +641,6 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) u16 tcp_rx_xsum = b->ip.hdr.ip_sum; u8 tcp_action = TCP_DATA; u32 tcp_seq_num, tcp_ack_num; - struct in_addr action_and_state; int tcp_hdr_len, payload_len; /* Verify IP header */ @@ -685,7 +685,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) /* Packets are not ordered. Send to app as received. */ tcp_action = tcp_state_machine(b->ip.hdr.tcp_flags, - &tcp_seq_num, payload_len); + tcp_seq_num, payload_len); tcp_activity_count++; if (tcp_activity_count > TCP_ACTIVITY) { @@ -695,18 +695,17 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) if ((tcp_action & TCP_PUSH) || payload_len > 0) { debug_cond(DEBUG_DEV_PKT, - "TCP Notify (action=%x, Seq=%d,Ack=%d,Pay%d)\n", + "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n", tcp_action, tcp_seq_num, tcp_ack_num, payload_len); - action_and_state.s_addr = tcp_action; - (*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len, - tcp_seq_num, action_and_state, - tcp_ack_num, payload_len); + (*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len, b->ip.hdr.tcp_dst, + b->ip.hdr.ip_src, b->ip.hdr.tcp_src, tcp_seq_num, + tcp_ack_num, tcp_action, payload_len); } else if (tcp_action != TCP_DATA) { debug_cond(DEBUG_DEV_PKT, - "TCP Action (action=%x,Seq=%d,Ack=%d,Pay=%d)\n", - tcp_action, tcp_seq_num, tcp_ack_num, payload_len); + "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n", + tcp_action, tcp_ack_num, tcp_ack_edge, payload_len); /* * Warning: Incoming Ack & Seq sequence numbers are transposed @@ -715,6 +714,6 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src), ntohs(b->ip.hdr.tcp_dst), (tcp_action & (~TCP_PUSH)), - tcp_seq_num, tcp_ack_num); + tcp_ack_num, tcp_ack_edge); } } diff --git a/net/wget.c b/net/wget.c index eebdf80eb54..2dbfeb1a1d5 100644 --- a/net/wget.c +++ b/net/wget.c @@ -88,8 +88,8 @@ static void wget_send_stored(void) { u8 action = retry_action; int len = retry_len; - unsigned int tcp_ack_num = retry_tcp_ack_num + len; - unsigned int tcp_seq_num = retry_tcp_seq_num; + unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len); + unsigned int tcp_seq_num = retry_tcp_ack_num; uchar *ptr, *offset; switch (current_wget_state) { @@ -130,8 +130,8 @@ static void wget_send_stored(void) } } -static void wget_send(u8 action, unsigned int tcp_ack_num, - unsigned int tcp_seq_num, int len) +static void wget_send(u8 action, unsigned int tcp_seq_num, + unsigned int tcp_ack_num, int len) { retry_action = action; retry_tcp_ack_num = tcp_ack_num; @@ -178,10 +178,8 @@ static void wget_timeout_handler(void) #define PKT_QUEUE_PACKET_SIZE 0x800 static void wget_connected(uchar *pkt, unsigned int tcp_seq_num, - struct in_addr action_and_state, - unsigned int tcp_ack_num, unsigned int len) + u8 action, unsigned int tcp_ack_num, unsigned int len) { - u8 action = action_and_state.s_addr; uchar *pkt_in_q; char *pos; int hlen, i; @@ -268,22 +266,25 @@ static void wget_connected(uchar *pkt, unsigned int tcp_seq_num, } /** - * wget_handler() - handler of wget - * @pkt: the pointer to the payload - * @tcp_seq_num: tcp sequence number - * @action_and_state: TCP state - * @tcp_ack_num: tcp acknowledge number - * @len: length of the payload + * wget_handler() - TCP handler of wget + * @pkt: pointer to the application packet + * @dport: destination TCP port + * @sip: source IP address + * @sport: source TCP port + * @tcp_seq_num: TCP sequential number + * @tcp_ack_num: TCP acknowledgment number + * @action: TCP action (SYN, ACK, FIN, etc) + * @len: packet length * * In the "application push" invocation, the TCP header with all * its information is pointed to by the packet pointer. */ -static void wget_handler(uchar *pkt, unsigned int tcp_seq_num, - struct in_addr action_and_state, - unsigned int tcp_ack_num, unsigned int len) +static void wget_handler(uchar *pkt, u16 dport, + struct in_addr sip, u16 sport, + u32 tcp_seq_num, u32 tcp_ack_num, + u8 action, unsigned int len) { enum tcp_state wget_tcp_state = tcp_get_tcp_state(); - u8 action = action_and_state.s_addr; net_set_timeout_handler(wget_timeout, wget_timeout_handler); packets++; @@ -294,7 +295,7 @@ static void wget_handler(uchar *pkt, unsigned int tcp_seq_num, break; case WGET_CONNECTING: debug_cond(DEBUG_WGET, - "wget: Connecting In len=%x, Seq=%x, Ack=%x\n", + "wget: Connecting In len=%x, Seq=%u, Ack=%u\n", len, tcp_seq_num, tcp_ack_num); if (!len) { if (wget_tcp_state == TCP_ESTABLISHED) { @@ -310,14 +311,13 @@ static void wget_handler(uchar *pkt, unsigned int tcp_seq_num, } break; case WGET_CONNECTED: - debug_cond(DEBUG_WGET, "wget: Connected seq=%x, len=%x\n", + debug_cond(DEBUG_WGET, "wget: Connected seq=%u, len=%x\n", tcp_seq_num, len); if (!len) { wget_fail("Image not found, no data returned\n", tcp_seq_num, tcp_ack_num, action); } else { - wget_connected(pkt, tcp_seq_num, action_and_state, - tcp_ack_num, len); + wget_connected(pkt, tcp_seq_num, action, tcp_ack_num, len); } break; case WGET_TRANSFERRING: @@ -338,6 +338,7 @@ static void wget_handler(uchar *pkt, unsigned int tcp_seq_num, wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len); fallthrough; case TCP_SYN_SENT: + case TCP_SYN_RECEIVED: case TCP_CLOSING: case TCP_FIN_WAIT_1: case TCP_CLOSED: -- cgit v1.3.1 From 443d319180a68156ca152d164f446e6789004c1d Mon Sep 17 00:00:00 2001 From: Dmitrii Merkurev Date: Wed, 12 Apr 2023 19:49:30 +0100 Subject: net: add fastboot TCP support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Known limitations are 1. fastboot reboot doesn't work (answering OK but not rebooting) 2. flashing isn't supported (TCP transport only limitation) The command syntax is fastboot tcp Signed-off-by: Dmitrii Merkurev Cc: Ying-Chun Liu (PaulLiu) Cc: Simon Glass Сс: Joe Hershberger Сс: Ramon Fried Reviewed-by: Simon Glass --- MAINTAINERS | 6 +- cmd/fastboot.c | 25 +++- drivers/fastboot/Kconfig | 14 ++ drivers/fastboot/fb_common.c | 1 - include/net.h | 3 +- include/net/fastboot.h | 21 --- include/net/fastboot_tcp.h | 14 ++ include/net/fastboot_udp.h | 14 ++ net/Makefile | 3 +- net/fastboot.c | 317 ------------------------------------------- net/fastboot_tcp.c | 143 +++++++++++++++++++ net/fastboot_udp.c | 317 +++++++++++++++++++++++++++++++++++++++++++ net/net.c | 17 ++- 13 files changed, 545 insertions(+), 350 deletions(-) delete mode 100644 include/net/fastboot.h create mode 100644 include/net/fastboot_tcp.h create mode 100644 include/net/fastboot_udp.h delete mode 100644 net/fastboot.c create mode 100644 net/fastboot_tcp.c create mode 100644 net/fastboot_udp.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index e9ed6ac79af..c8f72e9ec6a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -988,10 +988,12 @@ F: cmd/fastboot.c F: doc/android/fastboot*.rst F: include/fastboot.h F: include/fastboot-internal.h -F: include/net/fastboot.h +F: include/net/fastboot_tcp.h +F: include/net/fastboot_udp.h F: drivers/fastboot/ F: drivers/usb/gadget/f_fastboot.c -F: net/fastboot.c +F: net/fastboot_tcp.c +F: net/fastboot_udp.c F: test/dm/fastboot.c FPGA diff --git a/cmd/fastboot.c b/cmd/fastboot.c index 97dc02ce748..3d5ff951eb6 100644 --- a/cmd/fastboot.c +++ b/cmd/fastboot.c @@ -26,7 +26,7 @@ static int do_fastboot_udp(int argc, char *const argv[], return CMD_RET_FAILURE; } - err = net_loop(FASTBOOT); + err = net_loop(FASTBOOT_UDP); if (err < 0) { printf("fastboot udp error: %d\n", err); @@ -36,6 +36,26 @@ static int do_fastboot_udp(int argc, char *const argv[], return CMD_RET_SUCCESS; } +static int do_fastboot_tcp(int argc, char *const argv[], + uintptr_t buf_addr, size_t buf_size) +{ + int err; + + if (!IS_ENABLED(CONFIG_TCP_FUNCTION_FASTBOOT)) { + pr_err("Fastboot TCP not enabled\n"); + return CMD_RET_FAILURE; + } + + err = net_loop(FASTBOOT_TCP); + + if (err < 0) { + printf("fastboot tcp error: %d\n", err); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + static int do_fastboot_usb(int argc, char *const argv[], uintptr_t buf_addr, size_t buf_size) { @@ -141,7 +161,8 @@ NXTARG: if (!strcmp(argv[1], "udp")) return do_fastboot_udp(argc, argv, buf_addr, buf_size); - + if (!strcmp(argv[1], "tcp")) + return do_fastboot_tcp(argc, argv, buf_addr, buf_size); if (!strcmp(argv[1], "usb")) { argv++; argc--; diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index eefa34779c4..a3df9aa3d0f 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -4,6 +4,13 @@ config FASTBOOT bool imply ANDROID_BOOT_IMAGE imply CMD_FASTBOOT + help + Fastboot is a protocol used in Android devices for + communicating between the device and a computer during + the bootloader stage. It allows the user to flash the + device firmware and unlock the bootloader. + More information about the protocol and usecases: + https://android.googlesource.com/platform/system/core/+/refs/heads/master/fastboot/ config USB_FUNCTION_FASTBOOT bool "Enable USB fastboot gadget" @@ -28,6 +35,13 @@ config UDP_FUNCTION_FASTBOOT_PORT help The fastboot protocol requires a UDP port number. +config TCP_FUNCTION_FASTBOOT + depends on NET + select FASTBOOT + bool "Enable fastboot protocol over TCP" + help + This enables the fastboot protocol over TCP. + if FASTBOOT config FASTBOOT_BUF_ADDR diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 57b6182c46a..dde3cda78f6 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -15,7 +15,6 @@ #include #include #include -#include /** * fastboot_buf_addr - base address of the fastboot download buffer diff --git a/include/net.h b/include/net.h index 181a6e3b132..1b09aa10779 100644 --- a/include/net.h +++ b/include/net.h @@ -507,7 +507,8 @@ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP, - NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET + NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT_UDP, FASTBOOT_TCP, + WOL, UDP, NCSI, WGET }; extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/include/net/fastboot.h b/include/net/fastboot.h deleted file mode 100644 index 68602095d2b..00000000000 --- a/include/net/fastboot.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (C) 2016 The Android Open Source Project - */ - -#ifndef __NET_FASTBOOT_H__ -#define __NET_FASTBOOT_H__ - -/**********************************************************************/ -/* - * Global functions and variables. - */ - -/** - * Wait for incoming fastboot comands. - */ -void fastboot_start_server(void); - -/**********************************************************************/ - -#endif /* __NET_FASTBOOT_H__ */ diff --git a/include/net/fastboot_tcp.h b/include/net/fastboot_tcp.h new file mode 100644 index 00000000000..6cf29d52e93 --- /dev/null +++ b/include/net/fastboot_tcp.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2023 The Android Open Source Project + */ + +#ifndef __NET_FASTBOOT_TCP_H__ +#define __NET_FASTBOOT_TCP_H__ + +/** + * Wait for incoming tcp fastboot comands. + */ +void fastboot_tcp_start_server(void); + +#endif /* __NET_FASTBOOT_TCP_H__ */ diff --git a/include/net/fastboot_udp.h b/include/net/fastboot_udp.h new file mode 100644 index 00000000000..d4382c0a0e0 --- /dev/null +++ b/include/net/fastboot_udp.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2016 The Android Open Source Project + */ + +#ifndef __NET_FASTBOOT_H__ +#define __NET_FASTBOOT_H__ + +/** + * Wait for incoming UDP fastboot comands. + */ +void fastboot_udp_start_server(void); + +#endif /* __NET_FASTBOOT_H__ */ diff --git a/net/Makefile b/net/Makefile index 5968110170b..3e2d061338d 100644 --- a/net/Makefile +++ b/net/Makefile @@ -27,7 +27,8 @@ obj-$(CONFIG_CMD_PCAP) += pcap.o obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o -obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot_udp.o +obj-$(CONFIG_TCP_FUNCTION_FASTBOOT) += fastboot_tcp.o obj-$(CONFIG_CMD_WOL) += wol.o obj-$(CONFIG_PROT_UDP) += udp.o obj-$(CONFIG_PROT_TCP) += tcp.o diff --git a/net/fastboot.c b/net/fastboot.c deleted file mode 100644 index e9569d88d2a..00000000000 --- a/net/fastboot.c +++ /dev/null @@ -1,317 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -/* - * Copyright (C) 2016 The Android Open Source Project - */ - -#include -#include -#include -#include -#include - -enum { - FASTBOOT_ERROR = 0, - FASTBOOT_QUERY = 1, - FASTBOOT_INIT = 2, - FASTBOOT_FASTBOOT = 3, -}; - -struct __packed fastboot_header { - uchar id; - uchar flags; - unsigned short seq; -}; - -#define PACKET_SIZE 1024 -#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header)) - -/* Sequence number sent for every packet */ -static unsigned short sequence_number = 1; -static const unsigned short packet_size = PACKET_SIZE; -static const unsigned short udp_version = 1; - -/* Keep track of last packet for resubmission */ -static uchar last_packet[PACKET_SIZE]; -static unsigned int last_packet_len; - -static struct in_addr fastboot_remote_ip; -/* The UDP port at their end */ -static int fastboot_remote_port; -/* The UDP port at our end */ -static int fastboot_our_port; - -static void boot_downloaded_image(void); - -/** - * fastboot_udp_send_info() - Send an INFO packet during long commands. - * - * @msg: String describing the reason for waiting - */ -static void fastboot_udp_send_info(const char *msg) -{ - uchar *packet; - uchar *packet_base; - int len = 0; - char response[FASTBOOT_RESPONSE_LEN] = {0}; - - struct fastboot_header response_header = { - .id = FASTBOOT_FASTBOOT, - .flags = 0, - .seq = htons(sequence_number) - }; - ++sequence_number; - packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; - packet_base = packet; - - /* Write headers */ - memcpy(packet, &response_header, sizeof(response_header)); - packet += sizeof(response_header); - /* Write response */ - fastboot_response("INFO", response, "%s", msg); - memcpy(packet, response, strlen(response)); - packet += strlen(response); - - len = packet - packet_base; - - /* Save packet for retransmitting */ - last_packet_len = len; - memcpy(last_packet, packet_base, last_packet_len); - - net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, - fastboot_remote_port, fastboot_our_port, len); -} - -/** - * fastboot_timed_send_info() - Send INFO packet every 30 seconds - * - * @msg: String describing the reason for waiting - * - * Send an INFO packet during long commands based on timer. An INFO packet - * is sent if the time is 30 seconds after start. Else, noop. - */ -static void fastboot_timed_send_info(const char *msg) -{ - static ulong start; - - /* Initialize timer */ - if (start == 0) - start = get_timer(0); - ulong time = get_timer(start); - /* Send INFO packet to host every 30 seconds */ - if (time >= 30000) { - start = get_timer(0); - fastboot_udp_send_info(msg); - } -} - -/** - * fastboot_send() - Sends a packet in response to received fastboot packet - * - * @header: Header for response packet - * @fastboot_data: Pointer to received fastboot data - * @fastboot_data_len: Length of received fastboot data - * @retransmit: Nonzero if sending last sent packet - */ -static void fastboot_send(struct fastboot_header header, char *fastboot_data, - unsigned int fastboot_data_len, uchar retransmit) -{ - uchar *packet; - uchar *packet_base; - int len = 0; - const char *error_msg = "An error occurred."; - short tmp; - struct fastboot_header response_header = header; - static char command[FASTBOOT_COMMAND_LEN]; - static int cmd = -1; - static bool pending_command; - char response[FASTBOOT_RESPONSE_LEN] = {0}; - - /* - * We will always be sending some sort of packet, so - * cobble together the packet headers now. - */ - packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; - packet_base = packet; - - /* Resend last packet */ - if (retransmit) { - memcpy(packet, last_packet, last_packet_len); - net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, - fastboot_remote_port, fastboot_our_port, - last_packet_len); - return; - } - - response_header.seq = htons(response_header.seq); - memcpy(packet, &response_header, sizeof(response_header)); - packet += sizeof(response_header); - - switch (header.id) { - case FASTBOOT_QUERY: - tmp = htons(sequence_number); - memcpy(packet, &tmp, sizeof(tmp)); - packet += sizeof(tmp); - break; - case FASTBOOT_INIT: - tmp = htons(udp_version); - memcpy(packet, &tmp, sizeof(tmp)); - packet += sizeof(tmp); - tmp = htons(packet_size); - memcpy(packet, &tmp, sizeof(tmp)); - packet += sizeof(tmp); - break; - case FASTBOOT_ERROR: - memcpy(packet, error_msg, strlen(error_msg)); - packet += strlen(error_msg); - break; - case FASTBOOT_FASTBOOT: - if (cmd == FASTBOOT_COMMAND_DOWNLOAD) { - if (!fastboot_data_len && !fastboot_data_remaining()) { - fastboot_data_complete(response); - } else { - fastboot_data_download(fastboot_data, - fastboot_data_len, - response); - } - } else if (!pending_command) { - strlcpy(command, fastboot_data, - min((size_t)fastboot_data_len + 1, - sizeof(command))); - pending_command = true; - } else { - cmd = fastboot_handle_command(command, response); - pending_command = false; - } - /* - * Sent some INFO packets, need to update sequence number in - * header - */ - if (header.seq != sequence_number) { - response_header.seq = htons(sequence_number); - memcpy(packet_base, &response_header, - sizeof(response_header)); - } - /* Write response to packet */ - memcpy(packet, response, strlen(response)); - packet += strlen(response); - break; - default: - pr_err("ID %d not implemented.\n", header.id); - return; - } - - len = packet - packet_base; - - /* Save packet for retransmitting */ - last_packet_len = len; - memcpy(last_packet, packet_base, last_packet_len); - - net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, - fastboot_remote_port, fastboot_our_port, len); - - /* Continue boot process after sending response */ - if (!strncmp("OKAY", response, 4)) { - switch (cmd) { - case FASTBOOT_COMMAND_BOOT: - boot_downloaded_image(); - break; - - case FASTBOOT_COMMAND_CONTINUE: - net_set_state(NETLOOP_SUCCESS); - break; - - case FASTBOOT_COMMAND_REBOOT: - case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: - case FASTBOOT_COMMAND_REBOOT_FASTBOOTD: - case FASTBOOT_COMMAND_REBOOT_RECOVERY: - do_reset(NULL, 0, 0, NULL); - break; - } - } - - if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) - cmd = -1; -} - -/** - * boot_downloaded_image() - Boots into downloaded image. - */ -static void boot_downloaded_image(void) -{ - fastboot_boot(); - net_set_state(NETLOOP_SUCCESS); -} - -/** - * fastboot_handler() - Incoming UDP packet handler. - * - * @packet: Pointer to incoming UDP packet - * @dport: Destination UDP port - * @sip: Source IP address - * @sport: Source UDP port - * @len: Packet length - */ -static void fastboot_handler(uchar *packet, unsigned int dport, - struct in_addr sip, unsigned int sport, - unsigned int len) -{ - struct fastboot_header header; - char fastboot_data[DATA_SIZE] = {0}; - unsigned int fastboot_data_len = 0; - - if (dport != fastboot_our_port) - return; - - fastboot_remote_ip = sip; - fastboot_remote_port = sport; - - if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE) - return; - memcpy(&header, packet, sizeof(header)); - header.flags = 0; - header.seq = ntohs(header.seq); - packet += sizeof(header); - len -= sizeof(header); - - switch (header.id) { - case FASTBOOT_QUERY: - fastboot_send(header, fastboot_data, 0, 0); - break; - case FASTBOOT_INIT: - case FASTBOOT_FASTBOOT: - fastboot_data_len = len; - if (len > 0) - memcpy(fastboot_data, packet, len); - if (header.seq == sequence_number) { - fastboot_send(header, fastboot_data, - fastboot_data_len, 0); - sequence_number++; - } else if (header.seq == sequence_number - 1) { - /* Retransmit last sent packet */ - fastboot_send(header, fastboot_data, - fastboot_data_len, 1); - } - break; - default: - pr_err("ID %d not implemented.\n", header.id); - header.id = FASTBOOT_ERROR; - fastboot_send(header, fastboot_data, 0, 0); - break; - } -} - -void fastboot_start_server(void) -{ - printf("Using %s device\n", eth_get_name()); - printf("Listening for fastboot command on %pI4\n", &net_ip); - - fastboot_our_port = CONFIG_UDP_FUNCTION_FASTBOOT_PORT; - - if (IS_ENABLED(CONFIG_FASTBOOT_FLASH)) - fastboot_set_progress_callback(fastboot_timed_send_info); - - net_set_udp_handler(fastboot_handler); - - /* zero out server ether in case the server ip has changed */ - memset(net_server_ethaddr, 0, 6); -} diff --git a/net/fastboot_tcp.c b/net/fastboot_tcp.c new file mode 100644 index 00000000000..b5613b6aa20 --- /dev/null +++ b/net/fastboot_tcp.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2023 The Android Open Source Project + */ + +#include +#include +#include +#include +#include + +static char command[FASTBOOT_COMMAND_LEN] = {0}; +static char response[FASTBOOT_RESPONSE_LEN] = {0}; + +static const unsigned short handshake_length = 4; +static const uchar *handshake = "FB01"; + +static u16 curr_sport; +static u16 curr_dport; +static u32 curr_tcp_seq_num; +static u32 curr_tcp_ack_num; +static unsigned int curr_request_len; +static enum fastboot_tcp_state { + FASTBOOT_CLOSED, + FASTBOOT_CONNECTED, + FASTBOOT_DISCONNECTING +} state = FASTBOOT_CLOSED; + +static void fastboot_tcp_answer(u8 action, unsigned int len) +{ + const u32 response_seq_num = curr_tcp_ack_num; + const u32 response_ack_num = curr_tcp_seq_num + + (curr_request_len > 0 ? curr_request_len : 1); + + net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport), + action, response_seq_num, response_ack_num); +} + +static void fastboot_tcp_reset(void) +{ + fastboot_tcp_answer(TCP_RST, 0); + state = FASTBOOT_CLOSED; +} + +static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len) +{ + uchar *pkt = net_get_async_tx_pkt_buf(); + + memset(pkt, '\0', PKTSIZE); + pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; + memcpy(pkt, data, len); + fastboot_tcp_answer(action, len); + memset(pkt, '\0', PKTSIZE); +} + +static void fastboot_tcp_send_message(const char *message, unsigned int len) +{ + __be64 len_be = __cpu_to_be64(len); + uchar *pkt = net_get_async_tx_pkt_buf(); + + memset(pkt, '\0', PKTSIZE); + pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; + // Put first 8 bytes as a big endian message length + memcpy(pkt, &len_be, 8); + pkt += 8; + memcpy(pkt, message, len); + fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8); + memset(pkt, '\0', PKTSIZE); +} + +static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport, + struct in_addr sip, u16 sport, + u32 tcp_seq_num, u32 tcp_ack_num, + u8 action, unsigned int len) +{ + u64 command_size; + u8 tcp_fin = action & TCP_FIN; + u8 tcp_push = action & TCP_PUSH; + + curr_sport = sport; + curr_dport = dport; + curr_tcp_seq_num = tcp_seq_num; + curr_tcp_ack_num = tcp_ack_num; + curr_request_len = len; + + switch (state) { + case FASTBOOT_CLOSED: + if (tcp_push) { + if (len != handshake_length || + strlen(pkt) != handshake_length || + memcmp(pkt, handshake, handshake_length) != 0) { + fastboot_tcp_reset(); + break; + } + fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH, + handshake, handshake_length); + state = FASTBOOT_CONNECTED; + } + break; + case FASTBOOT_CONNECTED: + if (tcp_fin) { + fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0); + state = FASTBOOT_DISCONNECTING; + break; + } + if (tcp_push) { + // First 8 bytes is big endian message length + command_size = __be64_to_cpu(*(u64 *)pkt); + len -= 8; + pkt += 8; + + // Only single packet messages are supported ATM + if (strlen(pkt) != command_size) { + fastboot_tcp_reset(); + break; + } + strlcpy(command, pkt, len + 1); + fastboot_handle_command(command, response); + fastboot_tcp_send_message(response, strlen(response)); + } + break; + case FASTBOOT_DISCONNECTING: + if (tcp_push) + state = FASTBOOT_CLOSED; + break; + } + + memset(command, 0, FASTBOOT_COMMAND_LEN); + memset(response, 0, FASTBOOT_RESPONSE_LEN); + curr_sport = 0; + curr_dport = 0; + curr_tcp_seq_num = 0; + curr_tcp_ack_num = 0; + curr_request_len = 0; +} + +void fastboot_tcp_start_server(void) +{ + printf("Using %s device\n", eth_get_name()); + printf("Listening for fastboot command on tcp %pI4\n", &net_ip); + + tcp_set_tcp_handler(fastboot_tcp_handler_ipv4); +} diff --git a/net/fastboot_udp.c b/net/fastboot_udp.c new file mode 100644 index 00000000000..27e779d8e09 --- /dev/null +++ b/net/fastboot_udp.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2016 The Android Open Source Project + */ + +#include +#include +#include +#include +#include + +enum { + FASTBOOT_ERROR = 0, + FASTBOOT_QUERY = 1, + FASTBOOT_INIT = 2, + FASTBOOT_FASTBOOT = 3, +}; + +struct __packed fastboot_header { + uchar id; + uchar flags; + unsigned short seq; +}; + +#define PACKET_SIZE 1024 +#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header)) + +/* Sequence number sent for every packet */ +static unsigned short sequence_number = 1; +static const unsigned short packet_size = PACKET_SIZE; +static const unsigned short udp_version = 1; + +/* Keep track of last packet for resubmission */ +static uchar last_packet[PACKET_SIZE]; +static unsigned int last_packet_len; + +static struct in_addr fastboot_remote_ip; +/* The UDP port at their end */ +static int fastboot_remote_port; +/* The UDP port at our end */ +static int fastboot_our_port; + +static void boot_downloaded_image(void); + +/** + * fastboot_udp_send_info() - Send an INFO packet during long commands. + * + * @msg: String describing the reason for waiting + */ +static void fastboot_udp_send_info(const char *msg) +{ + uchar *packet; + uchar *packet_base; + int len = 0; + char response[FASTBOOT_RESPONSE_LEN] = {0}; + + struct fastboot_header response_header = { + .id = FASTBOOT_FASTBOOT, + .flags = 0, + .seq = htons(sequence_number) + }; + ++sequence_number; + packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; + packet_base = packet; + + /* Write headers */ + memcpy(packet, &response_header, sizeof(response_header)); + packet += sizeof(response_header); + /* Write response */ + fastboot_response("INFO", response, "%s", msg); + memcpy(packet, response, strlen(response)); + packet += strlen(response); + + len = packet - packet_base; + + /* Save packet for retransmitting */ + last_packet_len = len; + memcpy(last_packet, packet_base, last_packet_len); + + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, len); +} + +/** + * fastboot_timed_send_info() - Send INFO packet every 30 seconds + * + * @msg: String describing the reason for waiting + * + * Send an INFO packet during long commands based on timer. An INFO packet + * is sent if the time is 30 seconds after start. Else, noop. + */ +static void fastboot_timed_send_info(const char *msg) +{ + static ulong start; + + /* Initialize timer */ + if (start == 0) + start = get_timer(0); + ulong time = get_timer(start); + /* Send INFO packet to host every 30 seconds */ + if (time >= 30000) { + start = get_timer(0); + fastboot_udp_send_info(msg); + } +} + +/** + * fastboot_send() - Sends a packet in response to received fastboot packet + * + * @header: Header for response packet + * @fastboot_data: Pointer to received fastboot data + * @fastboot_data_len: Length of received fastboot data + * @retransmit: Nonzero if sending last sent packet + */ +static void fastboot_send(struct fastboot_header header, char *fastboot_data, + unsigned int fastboot_data_len, uchar retransmit) +{ + uchar *packet; + uchar *packet_base; + int len = 0; + const char *error_msg = "An error occurred."; + short tmp; + struct fastboot_header response_header = header; + static char command[FASTBOOT_COMMAND_LEN]; + static int cmd = -1; + static bool pending_command; + char response[FASTBOOT_RESPONSE_LEN] = {0}; + + /* + * We will always be sending some sort of packet, so + * cobble together the packet headers now. + */ + packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; + packet_base = packet; + + /* Resend last packet */ + if (retransmit) { + memcpy(packet, last_packet, last_packet_len); + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, + last_packet_len); + return; + } + + response_header.seq = htons(response_header.seq); + memcpy(packet, &response_header, sizeof(response_header)); + packet += sizeof(response_header); + + switch (header.id) { + case FASTBOOT_QUERY: + tmp = htons(sequence_number); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_INIT: + tmp = htons(udp_version); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + tmp = htons(packet_size); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_ERROR: + memcpy(packet, error_msg, strlen(error_msg)); + packet += strlen(error_msg); + break; + case FASTBOOT_FASTBOOT: + if (cmd == FASTBOOT_COMMAND_DOWNLOAD) { + if (!fastboot_data_len && !fastboot_data_remaining()) { + fastboot_data_complete(response); + } else { + fastboot_data_download(fastboot_data, + fastboot_data_len, + response); + } + } else if (!pending_command) { + strlcpy(command, fastboot_data, + min((size_t)fastboot_data_len + 1, + sizeof(command))); + pending_command = true; + } else { + cmd = fastboot_handle_command(command, response); + pending_command = false; + } + /* + * Sent some INFO packets, need to update sequence number in + * header + */ + if (header.seq != sequence_number) { + response_header.seq = htons(sequence_number); + memcpy(packet_base, &response_header, + sizeof(response_header)); + } + /* Write response to packet */ + memcpy(packet, response, strlen(response)); + packet += strlen(response); + break; + default: + pr_err("ID %d not implemented.\n", header.id); + return; + } + + len = packet - packet_base; + + /* Save packet for retransmitting */ + last_packet_len = len; + memcpy(last_packet, packet_base, last_packet_len); + + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, len); + + /* Continue boot process after sending response */ + if (!strncmp("OKAY", response, 4)) { + switch (cmd) { + case FASTBOOT_COMMAND_BOOT: + boot_downloaded_image(); + break; + + case FASTBOOT_COMMAND_CONTINUE: + net_set_state(NETLOOP_SUCCESS); + break; + + case FASTBOOT_COMMAND_REBOOT: + case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: + case FASTBOOT_COMMAND_REBOOT_FASTBOOTD: + case FASTBOOT_COMMAND_REBOOT_RECOVERY: + do_reset(NULL, 0, 0, NULL); + break; + } + } + + if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) + cmd = -1; +} + +/** + * boot_downloaded_image() - Boots into downloaded image. + */ +static void boot_downloaded_image(void) +{ + fastboot_boot(); + net_set_state(NETLOOP_SUCCESS); +} + +/** + * fastboot_handler() - Incoming UDP packet handler. + * + * @packet: Pointer to incoming UDP packet + * @dport: Destination UDP port + * @sip: Source IP address + * @sport: Source UDP port + * @len: Packet length + */ +static void fastboot_handler(uchar *packet, unsigned int dport, + struct in_addr sip, unsigned int sport, + unsigned int len) +{ + struct fastboot_header header; + char fastboot_data[DATA_SIZE] = {0}; + unsigned int fastboot_data_len = 0; + + if (dport != fastboot_our_port) + return; + + fastboot_remote_ip = sip; + fastboot_remote_port = sport; + + if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE) + return; + memcpy(&header, packet, sizeof(header)); + header.flags = 0; + header.seq = ntohs(header.seq); + packet += sizeof(header); + len -= sizeof(header); + + switch (header.id) { + case FASTBOOT_QUERY: + fastboot_send(header, fastboot_data, 0, 0); + break; + case FASTBOOT_INIT: + case FASTBOOT_FASTBOOT: + fastboot_data_len = len; + if (len > 0) + memcpy(fastboot_data, packet, len); + if (header.seq == sequence_number) { + fastboot_send(header, fastboot_data, + fastboot_data_len, 0); + sequence_number++; + } else if (header.seq == sequence_number - 1) { + /* Retransmit last sent packet */ + fastboot_send(header, fastboot_data, + fastboot_data_len, 1); + } + break; + default: + pr_err("ID %d not implemented.\n", header.id); + header.id = FASTBOOT_ERROR; + fastboot_send(header, fastboot_data, 0, 0); + break; + } +} + +void fastboot_udp_start_server(void) +{ + printf("Using %s device\n", eth_get_name()); + printf("Listening for fastboot command on %pI4\n", &net_ip); + + fastboot_our_port = CONFIG_UDP_FUNCTION_FASTBOOT_PORT; + + if (IS_ENABLED(CONFIG_FASTBOOT_FLASH)) + fastboot_set_progress_callback(fastboot_timed_send_info); + + net_set_udp_handler(fastboot_handler); + + /* zero out server ether in case the server ip has changed */ + memset(net_server_ethaddr, 0, 6); +} diff --git a/net/net.c b/net/net.c index 8cb8b4b9f34..253340f3c44 100644 --- a/net/net.c +++ b/net/net.c @@ -93,7 +93,8 @@ #include #include #include -#include +#include +#include #include #include #if defined(CONFIG_CMD_PCAP) @@ -501,9 +502,14 @@ restart: tftp_start_server(); break; #endif -#ifdef CONFIG_UDP_FUNCTION_FASTBOOT - case FASTBOOT: - fastboot_start_server(); +#if defined(CONFIG_UDP_FUNCTION_FASTBOOT) + case FASTBOOT_UDP: + fastboot_udp_start_server(); + break; +#endif +#if defined(CONFIG_TCP_FUNCTION_FASTBOOT) + case FASTBOOT_TCP: + fastboot_tcp_start_server(); break; #endif #if defined(CONFIG_CMD_DHCP) @@ -1498,7 +1504,8 @@ common: /* Fall through */ case NETCONS: - case FASTBOOT: + case FASTBOOT_UDP: + case FASTBOOT_TCP: case TFTPSRV: if (IS_ENABLED(CONFIG_IPV6) && use_ip6) { if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6, -- cgit v1.3.1 From c8acbbbf0847744ff2ad98e9d8d4e11d5e37eda2 Mon Sep 17 00:00:00 2001 From: Dmitrii Merkurev Date: Wed, 12 Apr 2023 19:49:31 +0100 Subject: net: share fastboot boot handle logic between transports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce reboot, boot and continue commands support to TCP fastboot by moving existing UDP logic into the common module. Signed-off-by: Dmitrii Merkurev Cc: Ying-Chun Liu (PaulLiu) Cc: Simon Glass Сс: Joe Hershberger Сс: Ramon Fried Reviewed-by: Simon Glass Reviewed-by: Ramon Fried Reviewed-by: Simon Glass --- drivers/fastboot/fb_common.c | 32 ++++++++++++++++++++++++++++++++ include/fastboot.h | 9 +++++++++ net/fastboot_tcp.c | 5 ++++- net/fastboot_udp.c | 31 +------------------------------ 4 files changed, 46 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index dde3cda78f6..621146bc6b0 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -15,6 +15,7 @@ #include #include #include +#include /** * fastboot_buf_addr - base address of the fastboot download buffer @@ -155,6 +156,37 @@ void fastboot_boot(void) } } +/** + * fastboot_handle_boot() - Shared implementation of system reaction to + * fastboot commands + * + * Making desceisions about device boot state (stay in fastboot, reboot + * to bootloader, reboot to OS, etc). + */ +void fastboot_handle_boot(int command, bool success) +{ + if (!success) + return; + + switch (command) { + case FASTBOOT_COMMAND_BOOT: + fastboot_boot(); + net_set_state(NETLOOP_SUCCESS); + break; + + case FASTBOOT_COMMAND_CONTINUE: + net_set_state(NETLOOP_SUCCESS); + break; + + case FASTBOOT_COMMAND_REBOOT: + case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: + case FASTBOOT_COMMAND_REBOOT_FASTBOOTD: + case FASTBOOT_COMMAND_REBOOT_RECOVERY: + do_reset(NULL, 0, 0, NULL); + break; + } +} + /** * fastboot_set_progress_callback() - set progress callback * diff --git a/include/fastboot.h b/include/fastboot.h index 07f4c8fa711..296451f89d4 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -123,6 +123,15 @@ void fastboot_init(void *buf_addr, u32 buf_size); */ void fastboot_boot(void); +/** + * fastboot_handle_boot() - Shared implementation of system reaction to + * fastboot commands + * + * Making desceisions about device boot state (stay in fastboot, reboot + * to bootloader, reboot to OS, etc). + */ +void fastboot_handle_boot(int command, bool success); + /** * fastboot_handle_command() - Handle fastboot command * diff --git a/net/fastboot_tcp.c b/net/fastboot_tcp.c index b5613b6aa20..2eb52ea2567 100644 --- a/net/fastboot_tcp.c +++ b/net/fastboot_tcp.c @@ -73,6 +73,7 @@ static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport, u32 tcp_seq_num, u32 tcp_ack_num, u8 action, unsigned int len) { + int fastboot_command_id; u64 command_size; u8 tcp_fin = action & TCP_FIN; u8 tcp_push = action & TCP_PUSH; @@ -115,8 +116,10 @@ static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport, break; } strlcpy(command, pkt, len + 1); - fastboot_handle_command(command, response); + fastboot_command_id = fastboot_handle_command(command, response); fastboot_tcp_send_message(response, strlen(response)); + fastboot_handle_boot(fastboot_command_id, + strncmp("OKAY", response, 4) == 0); } break; case FASTBOOT_DISCONNECTING: diff --git a/net/fastboot_udp.c b/net/fastboot_udp.c index 27e779d8e09..d706928d168 100644 --- a/net/fastboot_udp.c +++ b/net/fastboot_udp.c @@ -40,8 +40,6 @@ static int fastboot_remote_port; /* The UDP port at our end */ static int fastboot_our_port; -static void boot_downloaded_image(void); - /** * fastboot_udp_send_info() - Send an INFO packet during long commands. * @@ -209,39 +207,12 @@ static void fastboot_send(struct fastboot_header header, char *fastboot_data, net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, fastboot_remote_port, fastboot_our_port, len); - /* Continue boot process after sending response */ - if (!strncmp("OKAY", response, 4)) { - switch (cmd) { - case FASTBOOT_COMMAND_BOOT: - boot_downloaded_image(); - break; - - case FASTBOOT_COMMAND_CONTINUE: - net_set_state(NETLOOP_SUCCESS); - break; - - case FASTBOOT_COMMAND_REBOOT: - case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: - case FASTBOOT_COMMAND_REBOOT_FASTBOOTD: - case FASTBOOT_COMMAND_REBOOT_RECOVERY: - do_reset(NULL, 0, 0, NULL); - break; - } - } + fastboot_handle_boot(cmd, strncmp("OKAY", response, 4) == 0); if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) cmd = -1; } -/** - * boot_downloaded_image() - Boots into downloaded image. - */ -static void boot_downloaded_image(void) -{ - fastboot_boot(); - net_set_state(NETLOOP_SUCCESS); -} - /** * fastboot_handler() - Incoming UDP packet handler. * -- cgit v1.3.1 From f1a63f7181784e6de92a9759e7d61e5ce9c00582 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 14 Apr 2023 17:06:44 -0500 Subject: net: phy: Drop phy_interface_is_sgmii MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent commit 75d28899e3e9 ("net: phy: Synchronize PHY interface modes with Linux") reordered the enum definitions. This caused the range of enums that this api was checking to go bad. There aren't anymore users of phy_interface_is_sgmii, so, just drop it. Also the protocols are so very different that it makes no sense to provide a helper wrapper in the hope of reuse for phy drivers. Reported-by: Tom Rini Suggested-by: Marek Vasut Suggested-by: Marek Behún Link: https://lore.kernel.org/all/20230414103852.38705065@dellmb/ Signed-off-by: Nishanth Menon Reviewed-by: Ramon Fried --- include/phy.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include') diff --git a/include/phy.h b/include/phy.h index a837fed7235..cb87d1d4fc9 100644 --- a/include/phy.h +++ b/include/phy.h @@ -365,18 +365,6 @@ static inline bool phy_interface_is_rgmii(struct phy_device *phydev) phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID; } -/** - * phy_interface_is_sgmii - Convenience function for testing if a PHY interface - * is SGMII (all variants) - * @phydev: the phy_device struct - * @return: true if MII bus is SGMII or false if it is not - */ -static inline bool phy_interface_is_sgmii(struct phy_device *phydev) -{ - return phydev->interface >= PHY_INTERFACE_MODE_SGMII && - phydev->interface <= PHY_INTERFACE_MODE_QSGMII; -} - bool phy_interface_is_ncsi(void); /* PHY UIDs for various PHYs that are referenced in external code */ -- cgit v1.3.1 From 09005c2fb288decb4010f062fdc87d0b82e57584 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 14 Apr 2023 17:06:45 -0500 Subject: net: phy: Make phy_interface_is_rgmii a switch statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent commit 75d28899e3e9 ("net: phy: Synchronize PHY interface modes with Linux") reordered the enum definitions. This exposed a problem in range checking functions to identify the interface type. Though this specific api wasn't impacted (all the RGMII definitions remained within range), this experience should be used to never to have to face this kind of challenge again. While it is possible for the phy drivers to use the enums directly, drivers such as dp83867, dp83869, marvell, micrel_ksz90x1 etc use this api. Reported-by: Tom Rini Reviewed-by: Marek Vasut Reviewed-by: Marek Behún Signed-off-by: Nishanth Menon Reviewed-by: Ramon Fried --- include/phy.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/phy.h b/include/phy.h index cb87d1d4fc9..247223d92be 100644 --- a/include/phy.h +++ b/include/phy.h @@ -361,8 +361,15 @@ int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id); */ static inline bool phy_interface_is_rgmii(struct phy_device *phydev) { - return phydev->interface >= PHY_INTERFACE_MODE_RGMII && - phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID; + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + return 1; + default: + return 0; + } } bool phy_interface_is_ncsi(void); -- cgit v1.3.1 From 6de98b60ba89fb96a3adada11a5b5406f3b3786b Mon Sep 17 00:00:00 2001 From: Ehsan Mohandesi Date: Fri, 21 Apr 2023 17:08:21 -0700 Subject: net: ipv6: Add support for default gateway discovery. In IPv6, the default gateway and prefix length are determined by receiving a router advertisement as defined in - https://www.rfc-editor.org/rfc/rfc4861. Add support for sending router solicitation (RS) and processing router advertisements (RA). If the RA has prefix info option and following conditions are met, then gatewayip6 and net_prefix_length of ip6addr env variables are initialized. These are later consumed by IPv6 code for non-local destination IP. - "Router Lifetime" != 0 - Prefix is NOT link-local prefix (0xfe80::/10) - L flag is 1 - "Valid Lifetime" != 0 Timing Parameters: - MAX_RTR_SOLICITATION_DELAY (0-1s) - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay) - MAX_RTR_SOLICITATIONS (3 RS transmissions) The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked automatically from net_init_loop(). Signed-off-by: Ehsan Mohandesi Tested-by: Viacheslav Mitrofanov Reviewed-by: Tested-by: Viacheslav Mitrofanov Reviewed-by: Viacheslav Mitrofanov Tested-by: Sergei Antonov Reviewed-by: Sergei Antonov --- cmd/Kconfig | 6 ++ include/ndisc.h | 35 ++++++++ include/net.h | 2 +- include/net6.h | 40 ++++++++++ net/ndisc.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- net/net.c | 23 +++++- net/net6.c | 1 + 7 files changed, 338 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/cmd/Kconfig b/cmd/Kconfig index 460f29883ae..65957da7f57 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1942,6 +1942,12 @@ config CMD_NCSI Normally this happens automatically before other network operations. +config IPV6_ROUTER_DISCOVERY + bool "Do IPv6 router discovery" + depends on IPV6 + help + Will automatically perform router solicitation on first IPv6 + network operation endif config CMD_ETHSW diff --git a/include/ndisc.h b/include/ndisc.h index f6f8eb6507c..d0fe3acca4a 100644 --- a/include/ndisc.h +++ b/include/ndisc.h @@ -19,6 +19,20 @@ struct nd_msg { __u8 opt[0]; }; +/* struct rs_msg - ICMPv6 Router Solicitation message format */ +struct rs_msg { + struct icmp6hdr icmph; + __u8 opt[0]; +}; + +/* struct ra_msg - ICMPv6 Router Advertisement message format */ +struct ra_msg { + struct icmp6hdr icmph; + __u32 reachable_time; + __u32 retransmission_timer; + __u8 opt[0]; +}; + /* struct echo_msg - ICMPv6 echo request/reply message format */ struct echo_msg { struct icmp6hdr icmph; @@ -57,6 +71,11 @@ extern int net_nd_try; */ void ndisc_init(void); +/* + * ip6_send_rs() - Send IPv6 Router Solicitation Message + */ +void ip6_send_rs(void); + /** * ndisc_receive() - Handle ND packet * @@ -78,6 +97,8 @@ void ndisc_request(void); * Return: 0 if no timeout, -1 otherwise */ int ndisc_timeout_check(void); +bool validate_ra(struct ip6_hdr *ip6); +int process_ra(struct ip6_hdr *ip6, int len); #else static inline void ndisc_init(void) { @@ -97,6 +118,20 @@ static inline int ndisc_timeout_check(void) { return 0; } + +static inline void ip6_send_rs(void) +{ +} + +static inline bool validate_ra(struct ip6_hdr *ip6) +{ + return true; +} + +static inline int process_ra(struct ip6_hdr *ip6, int len) +{ + return 0; +} #endif #endif /* __NDISC_H__ */ diff --git a/include/net.h b/include/net.h index 1b09aa10779..785cb1059ef 100644 --- a/include/net.h +++ b/include/net.h @@ -508,7 +508,7 @@ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP, NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT_UDP, FASTBOOT_TCP, - WOL, UDP, NCSI, WGET + WOL, UDP, NCSI, WGET, RS }; extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/include/net6.h b/include/net6.h index 2d7c5a09604..beafc053386 100644 --- a/include/net6.h +++ b/include/net6.h @@ -81,8 +81,17 @@ struct udp_hdr { 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00 } } } +/* + * All-routers multicast address is the link-local scope address to reach all + * routers. + */ +#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x02 } } } #define IPV6_LINK_LOCAL_PREFIX 0xfe80 +#define IPV6_LINK_LOCAL_MASK 0xffb0 /* The first 10-bit of address mask. */ /* hop limit for neighbour discovery packets */ #define IPV6_NDISC_HOPLIMIT 255 @@ -166,6 +175,37 @@ struct icmp6hdr { #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime } __packed; +/* + * struct icmp6_ra_prefix_info - Prefix Information option of the ICMPv6 message + * The Prefix Information option provides hosts with on-link prefixes and + * prefixes for Address Autoconfiguration. Refer to RFC 4861 for more info. + */ +struct icmp6_ra_prefix_info { + u8 type; /* Type is 3 for Prefix Information. */ + u8 len; /* Len is 4 for Prefix Information. */ + /* The number of leading bits in the Prefix that are valid. */ + u8 prefix_len; + u8 reserved1:6, /* MUST be ignored by the receiver. */ + aac:1, /* autonomous address-configuration flag */ + /* Indicates that this prefix can be used for on-link determination. */ + on_link:1; + /* + * The length of time in seconds that the prefix is valid for the + * purpose of on-link determination. + */ + __be32 valid_lifetime; + /* The length of time addresses remain preferred. */ + __be32 preferred_lifetime; + __be32 reserved2; /* MUST be ignored by the receiver. */ + /* + * Prefix is an IP address or a prefix of an IP address. The Prefix + * Length field contains the number of valid leading bits in the prefix. + * The bits in the prefix after the prefix length are reserved and MUST + * be initialized to zero by the sender and ignored by the receiver. + */ + struct in6_addr prefix; +}; + extern struct in6_addr const net_null_addr_ip6; /* NULL IPv6 address */ extern struct in6_addr net_gateway6; /* Our gateways IPv6 address */ extern struct in6_addr net_ip6; /* Our IPv6 addr (0 = unknown) */ diff --git a/net/ndisc.c b/net/ndisc.c index 367dae76766..0b27779ce5a 100644 --- a/net/ndisc.c +++ b/net/ndisc.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include /* IPv6 destination address of packet waiting for ND */ struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR; @@ -29,31 +31,37 @@ int net_nd_tx_packet_size; ulong net_nd_timer_start; /* the number of requests we have sent so far */ int net_nd_try; +struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR; + +#define MAX_RTR_SOLICITATIONS 3 +/* The maximum time to delay sending the first router solicitation message. */ +#define MAX_SOLICITATION_DELAY 1 // 1 second +/* The time to wait before sending the next router solicitation message. */ +#define RTR_SOLICITATION_INTERVAL 4000 // 4 seconds #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7) /** * ndisc_insert_option() - Insert an option into a neighbor discovery packet * - * @ndisc: pointer to ND packet + * @opt: pointer to the option element of the neighbor discovery packet * @type: option type to insert * @data: option data to insert * @len: data length * Return: the number of bytes inserted (which may be >= len) */ -static int -ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len) +static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len) { int space = IP6_NDISC_OPT_SPACE(len); - ndisc->opt[0] = type; - ndisc->opt[1] = space >> 3; - memcpy(&ndisc->opt[2], data, len); + opt[0] = type; + opt[1] = space >> 3; + memcpy(&opt[2], data, len); len += 2; /* fill the remainder with 0 */ if (space - len > 0) - memset(&ndisc->opt[len], '\0', space - len); + memset(&opt[len], '\0', space - len); return space; } @@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr *neigh_addr) /* Set the target address and llsaddr option */ net_copy_ip6(&msg->target, neigh_addr); - ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr, + ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr, INETHADDRSZ); /* checksum */ @@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr *neigh_addr) net_send_packet(net_tx_packet, (pkt - net_tx_packet)); } +/* + * ip6_send_rs() - Send IPv6 Router Solicitation Message. + * + * A router solicitation is sent to discover a router. RS message creation is + * based on RFC 4861 section 4.1. Router Solicitation Message Format. + */ +void ip6_send_rs(void) +{ + unsigned char enetaddr[6]; + struct rs_msg *msg; + __u16 icmp_len; + uchar *pkt; + unsigned short csum; + unsigned int pcsum; + static unsigned int retry_count; + + if (!ip6_is_unspecified_addr(&net_gateway6) && + net_prefix_length != 0) { + net_set_state(NETLOOP_SUCCESS); + return; + } else if (retry_count >= MAX_RTR_SOLICITATIONS) { + net_set_state(NETLOOP_FAIL); + net_set_timeout_handler(0, NULL); + retry_count = 0; + return; + } + + printf("ROUTER SOLICITATION %d\n", retry_count + 1); + + ip6_make_mult_ethdstaddr(enetaddr, &all_routers); + /* + * ICMP length is the size of ICMP header (8) + one option (8) = 16. + * The option is 2 bytes of type and length + 6 bytes for MAC. + */ + icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ); + + pkt = (uchar *)net_tx_packet; + pkt += net_set_ether(pkt, enetaddr, PROT_IP6); + pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6, + IPV6_NDISC_HOPLIMIT, icmp_len); + + /* ICMPv6 - RS */ + msg = (struct rs_msg *)pkt; + msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION; + msg->icmph.icmp6_code = 0; + memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16)); + memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32)); + + /* Set the llsaddr option */ + ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr, + INETHADDRSZ); + + /* checksum */ + pcsum = csum_partial((__u8 *)msg, icmp_len, 0); + csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers, + icmp_len, PROT_ICMPV6, pcsum); + msg->icmph.icmp6_cksum = csum; + pkt += icmp_len; + + /* Wait up to 1 second if it is the first try to get the RA */ + if (retry_count == 0) + udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY); + + /* send it! */ + net_send_packet(net_tx_packet, (pkt - net_tx_packet)); + + retry_count++; + net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs); +} + static void ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr, struct in6_addr *target) @@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr, msg->icmph.icmp6_dataun.u_nd_advt.override = 1; /* Set the target address and lltargetaddr option */ net_copy_ip6(&msg->target, target); - ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr, + ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr, INETHADDRSZ); /* checksum */ @@ -223,6 +301,10 @@ int ndisc_timeout_check(void) return 1; } +/* + * ndisc_init() - Make initial steps for ND state machine. + * Usually move variables into initial state. + */ void ndisc_init(void) { net_nd_packet_mac = NULL; @@ -234,12 +316,125 @@ void ndisc_init(void) net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN; } +/* + * validate_ra() - Validate the router advertisement message. + * + * @ip6: Pointer to the router advertisement packet + * + * Check if the router advertisement message is valid. Conditions are + * according to RFC 4861 section 6.1.2. Validation of Router Advertisement + * Messages. + * + * Return: true if the message is valid and false if it is invalid. + */ +bool validate_ra(struct ip6_hdr *ip6) +{ + struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1); + + /* ICMP length (derived from the IP length) should be 16 or more octets. */ + if (ip6->payload_len < 16) + return false; + + /* Source IP Address should be a valid link-local address. */ + if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) != + IPV6_LINK_LOCAL_PREFIX) + return false; + + /* + * The IP Hop Limit field should have a value of 255, i.e., the packet + * could not possibly have been forwarded by a router. + */ + if (ip6->hop_limit != 255) + return false; + + /* ICMP checksum has already been checked in net_ip6_handler. */ + + if (icmp->icmp6_code != 0) + return false; + + return true; +} + +/* + * process_ra() - Process the router advertisement packet. + * + * @ip6: Pointer to the router advertisement packet + * @len: Length of the router advertisement packet + * + * Process the received router advertisement message. + * Although RFC 4861 requires retaining at least two router addresses, we only + * keep one because of the U-Boot limitations and its goal of lightweight code. + * + * Return: 0 - RA is a default router and contains valid prefix information. + * Non-zero - RA options are invalid or do not indicate it is a default router + * or do not contain valid prefix information. + */ +int process_ra(struct ip6_hdr *ip6, int len) +{ + /* Pointer to the ICMP section of the packet */ + struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1); + struct ra_msg *msg = (struct ra_msg *)icmp; + int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg); + unsigned short int option_len; /* Length of each option */ + /* Pointer to the ICMPv6 message options */ + unsigned char *option = NULL; + /* 8-bit identifier of the type of ICMPv6 option */ + unsigned char type = 0; + struct icmp6_ra_prefix_info *prefix = NULL; + + /* Ignore the packet if router lifetime is 0. */ + if (!icmp->icmp6_rt_lifetime) + return -EOPNOTSUPP; + + /* Processing the options */ + option = msg->opt; + while (remaining_option_len > 0) { + /* The 2nd byte of the option is its length. */ + option_len = option[1]; + /* All included options should have a positive length. */ + if (option_len == 0) + return -EINVAL; + + type = option[0]; + /* All option types except Prefix Information are ignored. */ + switch (type) { + case ND_OPT_SOURCE_LL_ADDR: + case ND_OPT_TARGET_LL_ADDR: + case ND_OPT_REDIRECT_HDR: + case ND_OPT_MTU: + break; + case ND_OPT_PREFIX_INFO: + prefix = (struct icmp6_ra_prefix_info *)option; + /* The link-local prefix 0xfe80::/10 is ignored. */ + if ((ntohs(prefix->prefix.s6_addr16[0]) & + IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX) + break; + if (prefix->on_link && ntohl(prefix->valid_lifetime)) { + net_prefix_length = prefix->prefix_len; + net_gateway6 = ip6->saddr; + return 0; + } + break; + default: + debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n", + type); + } + + option_len <<= 3; /* Option length is a multiple of 8. */ + remaining_option_len -= option_len; + option += option_len; + } + + return -EADDRNOTAVAIL; +} + int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) { struct icmp6hdr *icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE); struct nd_msg *ndisc = (struct nd_msg *)icmp; uchar neigh_eth_addr[6]; + int err = 0; // The error code returned calling functions. switch (icmp->icmp6_type) { case IPV6_NDISC_NEIGHBOUR_SOLICITATION: @@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) net_nd_packet_mac = NULL; } break; + case IPV6_NDISC_ROUTER_SOLICITATION: + break; + case IPV6_NDISC_ROUTER_ADVERTISEMENT: + debug("Received router advertisement for %pI6c from %pI6c\n", + &ip6->daddr, &ip6->saddr); + /* + * If gateway and prefix are set, the RA packet is ignored. The + * reason is that the U-Boot code is supposed to be as compact + * as possible and does not need to take care of multiple + * routers. In addition to that, U-Boot does not want to handle + * scenarios like a router setting its lifetime to zero to + * indicate it is not routing anymore. U-Boot program has a + * short life when the system boots up and does not need such + * sophistication. + */ + if (!ip6_is_unspecified_addr(&net_gateway6) && + net_prefix_length != 0) { + break; + } + if (!validate_ra(ip6)) { + debug("Invalid router advertisement message.\n"); + break; + } + err = process_ra(ip6, len); + if (err) + debug("Ignored router advertisement. Error: %d\n", err); + else + printf("Set gatewayip6: %pI6c, prefix_length: %d\n", + &net_gateway6, net_prefix_length); + break; default: debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type); return -1; diff --git a/net/net.c b/net/net.c index 253340f3c44..43abbac7c32 100644 --- a/net/net.c +++ b/net/net.c @@ -24,7 +24,7 @@ * - name of bootfile * Next step: ARP * - * LINK_LOCAL: + * LINKLOCAL: * * Prerequisites: - own ethernet address * We want: - own IP address @@ -124,6 +124,7 @@ #include "wol.h" #endif #include "dhcpv6.h" +#include "net_rand.h" /** BOOTP EXTENTIONS **/ @@ -350,6 +351,8 @@ void net_auto_load(void) static int net_init_loop(void) { + static bool first_call = true; + if (eth_get_dev()) { memcpy(net_ethaddr, eth_get_ethaddr(), 6); @@ -369,6 +372,12 @@ static int net_init_loop(void) */ return -ENONET; + if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)) + if (first_call && use_ip6) { + first_call = false; + srand_mac(); /* This is for rand used in ip6_send_rs. */ + net_loop(RS); + } return 0; } @@ -587,6 +596,10 @@ restart: ncsi_probe_packages(); break; #endif + case RS: + if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)) + ip6_send_rs(); + break; default: break; } @@ -684,7 +697,13 @@ restart: x = time_handler; time_handler = (thand_f *)0; (*x)(); - } + } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)) + if (time_handler && protocol == RS) + if (!ip6_is_unspecified_addr(&net_gateway6) && + net_prefix_length != 0) { + net_set_state(NETLOOP_SUCCESS); + net_set_timeout_handler(0, NULL); + } if (net_state == NETLOOP_FAIL) ret = net_start_again(); diff --git a/net/net6.c b/net/net6.c index 75577bcea17..2dd64c0e161 100644 --- a/net/net6.c +++ b/net/net6.c @@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) break; case IPV6_NDISC_NEIGHBOUR_SOLICITATION: case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT: + case IPV6_NDISC_ROUTER_ADVERTISEMENT: ndisc_receive(et, ip6, len); break; default: -- cgit v1.3.1