diff options
author | Tom Rini <trini@konsulko.com> | 2023-05-06 10:34:34 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-05-06 10:34:34 -0400 |
commit | ae0772f333a7e76c4fb3771a77117b2f740d7c93 (patch) | |
tree | 89fd938073db85162aab26a9096447d7a311b957 | |
parent | ab75996ba49c140c529f14b5c40d0d16430feefb (diff) | |
parent | 1a3af267c097c330fb2bf12493186a345523df38 (diff) | |
download | u-boot-ae0772f333a7e76c4fb3771a77117b2f740d7c93.zip u-boot-ae0772f333a7e76c4fb3771a77117b2f740d7c93.tar.gz u-boot-ae0772f333a7e76c4fb3771a77117b2f740d7c93.tar.bz2 |
Merge branch '2023-05-05-networking-updates'
- Cleanup NFS support, add NFSv1 support, assorted IPv6 improvements,
PHY cleanups and improvements, ksz9477, ldpaa and rtl8169
improvements, overall network performance improvements.
49 files changed, 2231 insertions, 363 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index e9ed6ac..c8f72e9 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/boot/bootmeth_distro.c b/boot/bootmeth_distro.c index 3569298..b4b73ec 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 ecf8557..5a8af2b 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 3a1e50f..d13c47d 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 e45b884..65957da 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 @@ -1916,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/cmd/fastboot.c b/cmd/fastboot.c index 97dc02c..3d5ff95 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--; @@ -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[]) @@ -186,7 +209,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 +270,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); + } + } } /** @@ -8,6 +8,8 @@ #include <command.h> #include <fs.h> #include <net.h> +#include <net6.h> +#include <malloc.h> #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); @@ -44,6 +54,22 @@ static int do_get_tftp(struct pxe_context *ctx, const char *file_path, } /* + * 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. * * Returns 1 on success or < 0 on error. @@ -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 04c0702..63a7806 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/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index f5cfdc4..f3002de 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -69,6 +69,7 @@ CONFIG_CMD_CDP=y CONFIG_CMD_SNTP=y CONFIG_CMD_DNS=y CONFIG_CMD_LINK_LOCAL=y +CONFIG_IPV6_ROUTER_DISCOVERY=y CONFIG_CMD_ETHSW=y CONFIG_CMD_BMP=y CONFIG_CMD_EFIDEBUG=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 24a4448..1ec44d5 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -91,6 +91,7 @@ CONFIG_CMD_AXI=y CONFIG_CMD_CAT=y CONFIG_CMD_SETEXPR_FMT=y CONFIG_CMD_AB_SELECT=y +CONFIG_CMD_DHCP6=y CONFIG_BOOTP_DNS2=y CONFIG_CMD_PCAP=y CONFIG_CMD_TFTPPUT=y @@ -100,6 +101,7 @@ CONFIG_CMD_CDP=y CONFIG_CMD_SNTP=y CONFIG_CMD_DNS=y CONFIG_CMD_LINK_LOCAL=y +CONFIG_IPV6_ROUTER_DISCOVERY=y CONFIG_CMD_ETHSW=y CONFIG_CMD_2048=y CONFIG_CMD_BMP=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 5b6b28a..e7657d4 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -59,6 +59,7 @@ CONFIG_CMD_CDP=y CONFIG_CMD_SNTP=y CONFIG_CMD_DNS=y CONFIG_CMD_LINK_LOCAL=y +CONFIG_IPV6_ROUTER_DISCOVERY=y CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_RTC=y CONFIG_CMD_TIME=y diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index eefa347..a3df9aa 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 57b6182..621146b 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -15,7 +15,7 @@ #include <command.h> #include <env.h> #include <fastboot.h> -#include <net/fastboot.h> +#include <net.h> /** * fastboot_buf_addr - base address of the fastboot download buffer @@ -157,6 +157,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 * * @progress: Pointer to progress callback diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index ec58697..9bbba6e 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -794,9 +794,21 @@ static int eqos_start(struct udevice *dev) */ if (!eqos->phy) { int addr = -1; - addr = eqos_get_phy_addr(eqos, dev); - eqos->phy = phy_connect(eqos->mii, addr, dev, - eqos->config->interface(dev)); + ofnode fixed_node; + + if (IS_ENABLED(CONFIG_PHY_FIXED)) { + fixed_node = ofnode_find_subnode(dev_ofnode(dev), + "fixed-link"); + if (ofnode_valid(fixed_node)) + eqos->phy = fixed_phy_create(dev_ofnode(dev)); + } + + if (!eqos->phy) { + addr = eqos_get_phy_addr(eqos, dev); + eqos->phy = phy_connect(eqos->mii, addr, dev, + eqos->config->interface(dev)); + } + if (!eqos->phy) { pr_err("phy_connect() failed"); goto err_stop_resets; diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 4f84403..78a40f2 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -29,6 +29,7 @@ #include <fsl-mc/fsl_dpsparser.h> #include <fsl-mc/fsl_qbman_portal.h> #include <fsl-mc/ldpaa_wriop.h> +#include <net/ldpaa_eth.h> #define MC_RAM_BASE_ADDR_ALIGNMENT (512UL * 1024 * 1024) #define MC_RAM_BASE_ADDR_ALIGNMENT_MASK (~(MC_RAM_BASE_ADDR_ALIGNMENT - 1)) @@ -383,37 +384,31 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id, static int mc_fixup_mac_addrs(void *blob, enum mc_fixup_type type) { - int i, err = 0, ret = 0; -#define ETH_NAME_LEN 20 struct udevice *eth_dev; - char ethname[ETH_NAME_LEN]; - - for (i = WRIOP1_DPMAC1; i < NUM_WRIOP_PORTS; i++) { - /* port not enabled */ - if (wriop_is_enabled_dpmac(i) != 1) - continue; - - snprintf(ethname, ETH_NAME_LEN, "DPMAC%d@%s", i, - phy_interface_strings[wriop_get_enet_if(i)]); - - eth_dev = eth_get_dev_by_name(ethname); - if (eth_dev == NULL) + int err = 0, ret = 0; + struct uclass *uc; + uint32_t dpmac_id; + + uclass_get(UCLASS_ETH, &uc); + uclass_foreach_dev(eth_dev, uc) { + if (!eth_dev->driver || !eth_dev->driver->name || + strcmp(eth_dev->driver->name, LDPAA_ETH_DRIVER_NAME)) continue; + dpmac_id = ldpaa_eth_get_dpmac_id(eth_dev); switch (type) { case MC_FIXUP_DPL: - err = mc_fixup_dpl_mac_addr(blob, i, eth_dev); + err = mc_fixup_dpl_mac_addr(blob, dpmac_id, eth_dev); break; case MC_FIXUP_DPC: - err = mc_fixup_dpc_mac_addr(blob, i, eth_dev); + err = mc_fixup_dpc_mac_addr(blob, dpmac_id, eth_dev); break; default: break; } if (err) - printf("fsl-mc: ERROR fixing mac address for %s\n", - ethname); + printf("fsl-mc: ERROR fixing mac address for %s\n", eth_dev->name); ret |= err; } diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c index fb5c76c..6b59b5f 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -337,11 +337,21 @@ static int ksz_port_setup(struct udevice *dev, int port, return 0; } +static int ksz_port_probe(struct udevice *dev, int port, struct phy_device *phy) +{ + int supported = PHY_GBIT_FEATURES; + + /* configure phy */ + phy->supported &= supported; + phy->advertising &= supported; + + return phy_config(phy); +} + static int ksz_port_enable(struct udevice *dev, int port, struct phy_device *phy) { struct dsa_pdata *pdata = dev_get_uclass_plat(dev); struct ksz_dsa_priv *priv = dev_get_priv(dev); - int supported = PHY_GBIT_FEATURES; u8 data8; int ret; @@ -365,23 +375,12 @@ static int ksz_port_enable(struct udevice *dev, int port, struct phy_device *phy if (port == pdata->cpu_port) return 0; - /* configure phy */ - phy->supported &= supported; - phy->advertising &= supported; - ret = phy_config(phy); - if (ret) - return ret; - - ret = phy_startup(phy); - if (ret) - return ret; - /* start switch */ ksz_read8(priv->dev, REG_SW_OPERATION, &data8); data8 |= SW_START; ksz_write8(priv->dev, REG_SW_OPERATION, data8); - return 0; + return phy_startup(phy); } static void ksz_port_disable(struct udevice *dev, int port, struct phy_device *phy) @@ -410,6 +409,7 @@ static void ksz_port_disable(struct udevice *dev, int port, struct phy_device *p } static const struct dsa_ops ksz_dsa_ops = { + .port_probe = ksz_port_probe, .port_enable = ksz_port_enable, .port_disable = ksz_port_disable, }; @@ -443,15 +443,10 @@ static int ksz_i2c_probe(struct udevice *dev) { struct dsa_pdata *pdata = dev_get_uclass_plat(dev); struct ksz_dsa_priv *priv = dev_get_priv(dev); - struct udevice *master = dsa_get_master(dev); int i, ret; u8 data8; u32 id; - if (!master) - return -ENODEV; - - dev_dbg(dev, "%s %s master:%s\n", __func__, dev->name, master->name); dev_set_parent_priv(dev, priv); ret = i2c_set_chip_offset_len(dev, 2); @@ -501,8 +496,6 @@ static int ksz_i2c_probe(struct udevice *dev) ksz_pwrite8(priv->dev, i, REG_PORT_MSTP_STATE, data8); } - dsa_set_tagging(dev, 0, 0); - return 0; }; diff --git a/drivers/net/ldpaa_eth/ldpaa_eth.c b/drivers/net/ldpaa_eth/ldpaa_eth.c index 2485077..2cb6e9b 100644 --- a/drivers/net/ldpaa_eth/ldpaa_eth.c +++ b/drivers/net/ldpaa_eth/ldpaa_eth.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2014-2016 Freescale Semiconductor, Inc. - * Copyright 2017 NXP + * Copyright 2017, 2023 NXP */ #include <common.h> @@ -21,6 +21,7 @@ #include <linux/compat.h> #include <linux/delay.h> #include <asm/global_data.h> +#include <net/ldpaa_eth.h> #include "ldpaa_eth.h" #ifdef CONFIG_PHYLIB @@ -995,7 +996,7 @@ static int ldpaa_eth_probe(struct udevice *dev) return 0; } -static uint32_t ldpaa_eth_get_dpmac_id(struct udevice *dev) +uint32_t ldpaa_eth_get_dpmac_id(struct udevice *dev) { int port_node = dev_of_offset(dev); @@ -1049,7 +1050,7 @@ static const struct udevice_id ldpaa_eth_of_ids[] = { }; U_BOOT_DRIVER(ldpaa_eth) = { - .name = "ldpaa_eth", + .name = LDPAA_ETH_DRIVER_NAME, .id = UCLASS_ETH, .of_match = ldpaa_eth_of_ids, .of_to_plat = ldpaa_eth_of_to_plat, diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index ea98cfc..36c70da 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -162,18 +162,6 @@ static int bcm5482_config(struct phy_device *phydev) return 0; } -static int bcm_cygnus_startup(struct phy_device *phydev) -{ - int ret; - - /* Read the Status (2x to make sure link is right) */ - ret = genphy_update_link(phydev); - if (ret) - return ret; - - return genphy_parse_link(phydev); -} - static void bcm_cygnus_afe(struct phy_device *phydev) { /* ensures smdspclk is enabled */ @@ -359,6 +347,6 @@ U_BOOT_PHY_DRIVER(bcm_cygnus) = { .mask = 0xfffff0, .features = PHY_GBIT_FEATURES, .config = &bcm_cygnus_config, - .startup = &bcm_cygnus_startup, + .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index b861bf7..7111e36 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -330,7 +330,7 @@ static int dp83867_config(struct phy_device *phydev) DP83867_RGMIIDCTL, delay); } - if (phy_interface_is_sgmii(phydev)) { + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { if (dp83867->sgmii_ref_clk_en) phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL, DP83867_SGMII_TYPE); diff --git a/drivers/net/phy/ethernet_id.c b/drivers/net/phy/ethernet_id.c index 8864f99..a715e83 100644 --- a/drivers/net/phy/ethernet_id.c +++ b/drivers/net/phy/ethernet_id.c @@ -39,7 +39,7 @@ struct phy_device *phy_connect_phy_id(struct mii_dev *bus, struct udevice *dev, if (!IS_ENABLED(CONFIG_DM_ETH_PHY)) { ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio, - GPIOD_ACTIVE_LOW); + GPIOD_IS_OUT | GPIOD_ACTIVE_LOW); if (!ret) { assert = ofnode_read_u32_default(node, "reset-assert-us", 0); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 247d975..396cac7 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -384,17 +384,6 @@ static int rtl8211x_startup(struct phy_device *phydev) return rtl8211x_parse_status(phydev); } -static int rtl8211e_startup(struct phy_device *phydev) -{ - int ret; - - ret = genphy_update_link(phydev); - if (ret) - return ret; - - return genphy_parse_link(phydev); -} - static int rtl8211f_startup(struct phy_device *phydev) { int ret; @@ -428,7 +417,7 @@ U_BOOT_PHY_DRIVER(rtl8211e) = { .features = PHY_GBIT_FEATURES, .probe = &rtl8211e_probe, .config = &rtl8211e_config, - .startup = &rtl8211e_startup, + .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; @@ -465,6 +454,6 @@ U_BOOT_PHY_DRIVER(rtl8201f) = { .features = PHY_BASIC_FEATURES, .probe = &rtl8210f_probe, .config = &rtl8201f_config, - .startup = &rtl8211e_startup, + .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c index 0b7436a..e2969bc 100644 --- a/drivers/net/phy/xilinx_gmii2rgmii.c +++ b/drivers/net/phy/xilinx_gmii2rgmii.c @@ -48,7 +48,14 @@ static int xilinxgmiitorgmii_config(struct phy_device *phydev) return -EINVAL; } - ext_phydev->interface = PHY_INTERFACE_MODE_RGMII; + ext_phydev->interface = ofnode_read_phy_mode(node); + if (ext_phydev->interface == PHY_INTERFACE_MODE_NA) { + ext_phydev->interface = PHY_INTERFACE_MODE_RGMII; + } else if (!phy_interface_is_rgmii(ext_phydev)) { + printf("Incorrect external interface type\n"); + return -EINVAL; + } + ext_phydev->node = phandle.node; phydev->priv = ext_phydev; diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index c9c07a5..2276a46 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -118,9 +118,9 @@ enum RTL8169_registers { FLASH = 0x30, ERSR = 0x36, ChipCmd = 0x37, - TxPoll = 0x38, - IntrMask = 0x3C, - IntrStatus = 0x3E, + TxPoll_8169 = 0x38, + IntrMask_8169 = 0x3C, + IntrStatus_8169 = 0x3E, TxConfig = 0x40, RxConfig = 0x44, RxMissed = 0x4C, @@ -148,6 +148,12 @@ enum RTL8169_registers { FuncForceEvent = 0xFC, }; +enum RTL8125_registers { + IntrMask_8125 = 0x38, + IntrStatus_8125 = 0x3C, + TxPoll_8125 = 0x90, +}; + enum RTL8169_register_content { /*InterruptStatusBits */ SYSErr = 0x8000, @@ -263,6 +269,7 @@ static struct { {"RTL-8101e", 0x34, 0xff7e1880,}, {"RTL-8100e", 0x32, 0xff7e1880,}, {"RTL-8168h/8111h", 0x54, 0xff7e1880,}, + {"RTL-8125B", 0x64, 0xff7e1880,}, }; enum _DescStatusBit { @@ -347,6 +354,7 @@ static struct pci_device_id supported[] = { { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167) }, { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168) }, { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169) }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8125) }, {} }; @@ -517,6 +525,7 @@ static int rtl_recv_common(struct udevice *dev, unsigned long dev_iobase, /* return true if there's an ethernet packet ready to read */ /* nic->packet should contain data on return */ /* nic->packetlen should contain length of data */ + struct pci_child_plat *pplat = dev_get_parent_plat(dev); int cur_rx; int length = 0; @@ -558,6 +567,10 @@ static int rtl_recv_common(struct udevice *dev, unsigned long dev_iobase, return length; } else { + u32 IntrStatus = IntrStatus_8169; + + if (pplat->device == 0x8125) + IntrStatus = IntrStatus_8125; ushort sts = RTL_R8(IntrStatus); RTL_W8(IntrStatus, sts & ~(TxErr | RxErr | SYSErr)); udelay(100); /* wait */ @@ -582,6 +595,7 @@ static int rtl_send_common(struct udevice *dev, unsigned long dev_iobase, { /* send the packet to destination */ + struct pci_child_plat *pplat = dev_get_parent_plat(dev); u32 to; u8 *ptxb; int entry = tpc->cur_tx % NUM_TX_DESC; @@ -618,7 +632,10 @@ static int rtl_send_common(struct udevice *dev, unsigned long dev_iobase, ((len > ETH_ZLEN) ? len : ETH_ZLEN)); } rtl_flush_tx_desc(&tpc->TxDescArray[entry]); - RTL_W8(TxPoll, 0x40); /* set polling bit */ + if (pplat->device == 0x8125) + RTL_W8(TxPoll_8125, 0x1); /* set polling bit */ + else + RTL_W8(TxPoll_8169, 0x40); /* set polling bit */ tpc->cur_tx++; to = currticks() + TX_TIMEOUT; @@ -824,21 +841,26 @@ static int rtl8169_eth_start(struct udevice *dev) return 0; } -static void rtl_halt_common(unsigned long dev_iobase) +static void rtl_halt_common(struct udevice *dev) { + struct rtl8169_private *priv = dev_get_priv(dev); + struct pci_child_plat *pplat = dev_get_parent_plat(dev); int i; #ifdef DEBUG_RTL8169 printf ("%s\n", __FUNCTION__); #endif - ioaddr = dev_iobase; + ioaddr = priv->iobase; /* Stop the chip's Tx and Rx DMA processes. */ RTL_W8(ChipCmd, 0x00); /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16(IntrMask, 0x0000); + if (pplat->device == 0x8125) + RTL_W16(IntrMask_8125, 0x0000); + else + RTL_W16(IntrMask_8169, 0x0000); RTL_W32(RxMissed, 0); @@ -849,9 +871,7 @@ static void rtl_halt_common(unsigned long dev_iobase) void rtl8169_eth_stop(struct udevice *dev) { - struct rtl8169_private *priv = dev_get_priv(dev); - - rtl_halt_common(priv->iobase); + rtl_halt_common(dev); } static int rtl8169_write_hwaddr(struct udevice *dev) @@ -1025,23 +1045,25 @@ static int rtl8169_eth_probe(struct udevice *dev) struct pci_child_plat *pplat = dev_get_parent_plat(dev); struct rtl8169_private *priv = dev_get_priv(dev); struct eth_pdata *plat = dev_get_plat(dev); - u32 iobase; int region; int ret; - debug("rtl8169: REALTEK RTL8169 @0x%x\n", iobase); switch (pplat->device) { case 0x8168: + case 0x8125: region = 2; break; default: region = 1; break; } - dm_pci_read_config32(dev, PCI_BASE_ADDRESS_0 + region * 4, &iobase); - iobase &= ~0xf; - priv->iobase = (int)dm_pci_mem_to_phys(dev, iobase); + priv->iobase = (ulong)dm_pci_map_bar(dev, + PCI_BASE_ADDRESS_0 + region * 4, + 0, 0, + PCI_REGION_TYPE, PCI_REGION_MEM); + + debug("rtl8169: REALTEK RTL8169 @0x%lx\n", priv->iobase); ret = rtl_init(priv->iobase, dev->name, plat->enetaddr); if (ret < 0) { printf(pr_fmt("failed to initialize card: %d\n"), ret); diff --git a/include/env_flags.h b/include/env_flags.h index 7de58cc..d785f87 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 diff --git a/include/fastboot.h b/include/fastboot.h index 07f4c8f..296451f 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -124,6 +124,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 * * @cmd_string: Pointer to command string diff --git a/include/ndisc.h b/include/ndisc.h index f6f8eb6..d0fe3ac 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 399af5e..785cb10 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,9 @@ 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_UDP, FASTBOOT_TCP, + WOL, UDP, NCSI, WGET, RS }; 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 6860209..0000000 --- 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 0000000..6cf29d5 --- /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 0000000..d4382c0 --- /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/include/net/ldpaa_eth.h b/include/net/ldpaa_eth.h new file mode 100644 index 0000000..7474bfa --- /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); diff --git a/include/net/tcp.h b/include/net/tcp.h index 3225516..c29d4ce 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/include/net6.h b/include/net6.h index 2d7c5a0..beafc05 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/include/phy.h b/include/phy.h index a837fed..247223d 100644 --- a/include/phy.h +++ b/include/phy.h @@ -361,20 +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; -} - -/** - * 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; + 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); diff --git a/include/pxe_utils.h b/include/pxe_utils.h index 1e5e842..9f19593 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 */ diff --git a/net/Makefile b/net/Makefile index bea000b..3e2d061 100644 --- a/net/Makefile +++ b/net/Makefile @@ -22,11 +22,13 @@ 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 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/dhcpv6.c b/net/dhcpv6.c new file mode 100644 index 0000000..0d1c600 --- /dev/null +++ b/net/dhcpv6.c @@ -0,0 +1,719 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Microsoft Corporation + * Author: Sean Edmond <seanedmond@microsoft.com> + * + */ + +/* Simple DHCP6 network layer implementation. */ + +#include <common.h> +#include <net6.h> +#include <malloc.h> +#include <linux/delay.h> +#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 0000000..80ca520 --- /dev/null +++ b/net/dhcpv6.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) Microsoft Corporation + * Author: Sean Edmond <seanedmond@microsoft.com> + * + */ + +#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/fastboot_tcp.c b/net/fastboot_tcp.c new file mode 100644 index 0000000..2eb52ea --- /dev/null +++ b/net/fastboot_tcp.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2023 The Android Open Source Project + */ + +#include <common.h> +#include <fastboot.h> +#include <net.h> +#include <net/fastboot_tcp.h> +#include <net/tcp.h> + +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) +{ + int fastboot_command_id; + 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_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: + 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.c b/net/fastboot_udp.c index e9569d8..d706928 100644 --- a/net/fastboot.c +++ b/net/fastboot_udp.c @@ -7,7 +7,7 @@ #include <command.h> #include <fastboot.h> #include <net.h> -#include <net/fastboot.h> +#include <net/fastboot_udp.h> enum { FASTBOOT_ERROR = 0, @@ -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,40 +207,13 @@ 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. * * @packet: Pointer to incoming UDP packet @@ -300,7 +271,7 @@ static void fastboot_handler(uchar *packet, unsigned int dport, } } -void fastboot_start_server(void) +void fastboot_udp_start_server(void) { printf("Using %s device\n", eth_get_name()); printf("Listening for fastboot command on %pI4\n", &net_ip); diff --git a/net/ndisc.c b/net/ndisc.c index 367dae7..0b27779 100644 --- a/net/ndisc.c +++ b/net/ndisc.c @@ -13,6 +13,8 @@ #include <net.h> #include <net6.h> #include <ndisc.h> +#include <stdlib.h> +#include <linux/delay.h> /* 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; @@ -24,7 +24,7 @@ * - name of bootfile * Next step: ARP * - * LINK_LOCAL: + * LINKLOCAL: * * Prerequisites: - own ethernet address * We want: - own IP address @@ -93,7 +93,8 @@ #include <net.h> #include <net6.h> #include <ndisc.h> -#include <net/fastboot.h> +#include <net/fastboot_udp.h> +#include <net/fastboot_tcp.h> #include <net/tftp.h> #include <net/ncsi.h> #if defined(CONFIG_CMD_PCAP) @@ -107,6 +108,8 @@ #include <watchdog.h> #include <linux/compiler.h> #include <test/test.h> +#include <net/tcp.h> +#include <net/wget.h> #include "arp.h" #include "bootp.h" #include "cdp.h" @@ -120,8 +123,8 @@ #if defined(CONFIG_CMD_WOL) #include "wol.h" #endif -#include <net/tcp.h> -#include <net/wget.h> +#include "dhcpv6.h" +#include "net_rand.h" /** BOOTP EXTENTIONS **/ @@ -135,6 +138,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 **/ @@ -346,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); @@ -365,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; } @@ -498,9 +511,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) @@ -510,6 +528,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(); @@ -574,6 +596,10 @@ restart: ncsi_probe_packages(); break; #endif + case RS: + if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)) + ip6_send_rs(); + break; default: break; } @@ -671,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(); @@ -1491,7 +1523,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, @@ -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: @@ -26,6 +26,10 @@ * NFSv2 is still used by default. But if server does not support NFSv2, then * NFSv3 is used, if available on NFS server. */ +/* NOTE 5: NFSv1 support added by Christian Gmeiner, Thomas Rienoessl, + * September 27, 2018. As of now, NFSv3 is the default choice. If the server + * does not support NFSv3, we fall back to versions 2 or 1. */ + #include <common.h> #include <command.h> #include <display_options.h> @@ -76,10 +80,14 @@ static char *nfs_filename; static char *nfs_path; static char nfs_path_buff[2048]; -#define NFSV2_FLAG 1 -#define NFSV3_FLAG 1 << 1 -static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG; +enum nfs_version { + NFS_UNKOWN = 0, + NFS_V1 = 1, + NFS_V2 = 2, + NFS_V3 = 3, +}; +static enum nfs_version choosen_nfs_version = NFS_V3; static inline int store_block(uchar *src, unsigned offset, unsigned len) { ulong newsize = offset + len; @@ -188,13 +196,41 @@ static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) rpc_pkt.u.call.prog = htonl(rpc_prog); switch (rpc_prog) { case PROG_NFS: - if (supported_nfs_versions & NFSV2_FLAG) - rpc_pkt.u.call.vers = htonl(2); /* NFS v2 */ - else /* NFSV3_FLAG */ - rpc_pkt.u.call.vers = htonl(3); /* NFS v3 */ + switch (choosen_nfs_version) { + case NFS_V1: + case NFS_V2: + rpc_pkt.u.call.vers = htonl(2); + break; + + case NFS_V3: + rpc_pkt.u.call.vers = htonl(3); + break; + + case NFS_UNKOWN: + /* nothing to do */ + break; + } break; - case PROG_PORTMAP: case PROG_MOUNT: + switch (choosen_nfs_version) { + case NFS_V1: + rpc_pkt.u.call.vers = htonl(1); + break; + + case NFS_V2: + rpc_pkt.u.call.vers = htonl(2); + break; + + case NFS_V3: + rpc_pkt.u.call.vers = htonl(3); + break; + + case NFS_UNKOWN: + /* nothing to do */ + break; + } + break; + case PROG_PORTMAP: default: rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */ } @@ -299,10 +335,10 @@ static void nfs_readlink_req(void) p = &(data[0]); p = rpc_add_credentials(p); - if (supported_nfs_versions & NFSV2_FLAG) { + if (choosen_nfs_version != NFS_V3) { memcpy(p, filefh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); - } else { /* NFSV3_FLAG */ + } else { /* NFS_V3 */ *p++ = htonl(filefh3_length); memcpy(p, filefh, filefh3_length); p += (filefh3_length / 4); @@ -328,7 +364,7 @@ static void nfs_lookup_req(char *fname) p = &(data[0]); p = rpc_add_credentials(p); - if (supported_nfs_versions & NFSV2_FLAG) { + if (choosen_nfs_version != NFS_V3) { memcpy(p, dirfh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); *p++ = htonl(fnamelen); @@ -340,7 +376,7 @@ static void nfs_lookup_req(char *fname) len = (uint32_t *)p - (uint32_t *)&(data[0]); rpc_req(PROG_NFS, NFS_LOOKUP, data, len); - } else { /* NFSV3_FLAG */ + } else { /* NFS_V3 */ *p++ = htonl(NFS_FHSIZE); /* Dir handle length */ memcpy(p, dirfh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); @@ -368,13 +404,13 @@ static void nfs_read_req(int offset, int readlen) p = &(data[0]); p = rpc_add_credentials(p); - if (supported_nfs_versions & NFSV2_FLAG) { + if (choosen_nfs_version != NFS_V3) { memcpy(p, filefh, NFS_FHSIZE); p += (NFS_FHSIZE / 4); *p++ = htonl(offset); *p++ = htonl(readlen); *p++ = 0; - } else { /* NFSV3_FLAG */ + } else { /* NFS_V3 */ *p++ = htonl(filefh3_length); memcpy(p, filefh, filefh3_length); p += (filefh3_length / 4); @@ -398,15 +434,15 @@ static void nfs_send(void) switch (nfs_state) { case STATE_PRCLOOKUP_PROG_MOUNT_REQ: - if (supported_nfs_versions & NFSV2_FLAG) + if (choosen_nfs_version != NFS_V3) rpc_lookup_req(PROG_MOUNT, 1); - else /* NFSV3_FLAG */ + else /* NFS_V3 */ rpc_lookup_req(PROG_MOUNT, 3); break; case STATE_PRCLOOKUP_PROG_NFS_REQ: - if (supported_nfs_versions & NFSV2_FLAG) + if (choosen_nfs_version != NFS_V3) rpc_lookup_req(PROG_NFS, 2); - else /* NFSV3_FLAG */ + else /* NFS_V3 */ rpc_lookup_req(PROG_NFS, 3); break; case STATE_MOUNT_REQ: @@ -431,6 +467,54 @@ static void nfs_send(void) Handlers for the reply from server **************************************************************************/ +static int rpc_handle_error(struct rpc_t *rpc_pkt) +{ + if (rpc_pkt->u.reply.rstatus || + rpc_pkt->u.reply.verifier || + rpc_pkt->u.reply.astatus || + rpc_pkt->u.reply.data[0]) { + switch (ntohl(rpc_pkt->u.reply.astatus)) { + case NFS_RPC_SUCCESS: /* Not an error */ + break; + case NFS_RPC_PROG_MISMATCH: { + /* Remote can't support NFS version */ + const int min = ntohl(rpc_pkt->u.reply.data[0]); + const int max = ntohl(rpc_pkt->u.reply.data[1]); + + if (max < NFS_V1 || max > NFS_V3 || min > NFS_V3) { + puts("*** ERROR: NFS version not supported"); + debug(": Requested: V%d, accepted: min V%d - max V%d\n", + choosen_nfs_version, + ntohl(rpc_pkt->u.reply.data[0]), + ntohl(rpc_pkt->u.reply.data[1])); + puts("\n"); + choosen_nfs_version = NFS_UNKOWN; + break; + } + + debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n", + choosen_nfs_version, + ntohl(rpc_pkt->u.reply.data[0]), + ntohl(rpc_pkt->u.reply.data[1])); + debug("Will retry with NFSv%d\n", min); + choosen_nfs_version = min; + return -NFS_RPC_PROG_MISMATCH; + } + case NFS_RPC_PROG_UNAVAIL: + case NFS_RPC_PROC_UNAVAIL: + case NFS_RPC_GARBAGE_ARGS: + case NFS_RPC_SYSTEM_ERR: + default: /* Unknown error on 'accept state' flag */ + debug("*** ERROR: accept state error (%d)\n", + ntohl(rpc_pkt->u.reply.astatus)); + break; + } + return -1; + } + + return 0; +} + static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len) { struct rpc_t rpc_pkt; @@ -464,6 +548,7 @@ static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len) static int nfs_mount_reply(uchar *pkt, unsigned len) { struct rpc_t rpc_pkt; + int ret; debug("%s\n", __func__); @@ -474,11 +559,9 @@ static int nfs_mount_reply(uchar *pkt, unsigned len) else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; - if (rpc_pkt.u.reply.rstatus || - rpc_pkt.u.reply.verifier || - rpc_pkt.u.reply.astatus || - rpc_pkt.u.reply.data[0]) - return -1; + ret = rpc_handle_error(&rpc_pkt); + if (ret) + return ret; fs_mounted = 1; /* NFSv2 and NFSv3 use same structure */ @@ -514,6 +597,7 @@ static int nfs_umountall_reply(uchar *pkt, unsigned len) static int nfs_lookup_reply(uchar *pkt, unsigned len) { struct rpc_t rpc_pkt; + int ret; debug("%s\n", __func__); @@ -524,55 +608,15 @@ static int nfs_lookup_reply(uchar *pkt, unsigned len) else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) return -NFS_RPC_DROP; - if (rpc_pkt.u.reply.rstatus || - rpc_pkt.u.reply.verifier || - rpc_pkt.u.reply.astatus || - rpc_pkt.u.reply.data[0]) { - switch (ntohl(rpc_pkt.u.reply.astatus)) { - case NFS_RPC_SUCCESS: /* Not an error */ - break; - case NFS_RPC_PROG_MISMATCH: - /* Remote can't support NFS version */ - switch (ntohl(rpc_pkt.u.reply.data[0])) { - /* Minimal supported NFS version */ - case 3: - debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n", - (supported_nfs_versions & NFSV2_FLAG) ? - 2 : 3, - ntohl(rpc_pkt.u.reply.data[0]), - ntohl(rpc_pkt.u.reply.data[1])); - debug("Will retry with NFSv3\n"); - /* Clear NFSV2_FLAG from supported versions */ - supported_nfs_versions &= ~NFSV2_FLAG; - return -NFS_RPC_PROG_MISMATCH; - case 4: - default: - puts("*** ERROR: NFS version not supported"); - debug(": Requested: V%d, accepted: min V%d - max V%d\n", - (supported_nfs_versions & NFSV2_FLAG) ? - 2 : 3, - ntohl(rpc_pkt.u.reply.data[0]), - ntohl(rpc_pkt.u.reply.data[1])); - puts("\n"); - } - break; - case NFS_RPC_PROG_UNAVAIL: - case NFS_RPC_PROC_UNAVAIL: - case NFS_RPC_GARBAGE_ARGS: - case NFS_RPC_SYSTEM_ERR: - default: /* Unknown error on 'accept state' flag */ - debug("*** ERROR: accept state error (%d)\n", - ntohl(rpc_pkt.u.reply.astatus)); - break; - } - return -1; - } + ret = rpc_handle_error(&rpc_pkt); + if (ret) + return ret; - if (supported_nfs_versions & NFSV2_FLAG) { + if (choosen_nfs_version != NFS_V3) { if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + NFS_FHSIZE) > len) return -NFS_RPC_DROP; memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); - } else { /* NFSV3_FLAG */ + } else { /* NFS_V3 */ filefh3_length = ntohl(rpc_pkt.u.reply.data[1]); if (filefh3_length > NFS3_FHSIZE) filefh3_length = NFS3_FHSIZE; @@ -631,7 +675,7 @@ static int nfs_readlink_reply(uchar *pkt, unsigned len) rpc_pkt.u.reply.data[0]) return -1; - if (!(supported_nfs_versions & NFSV2_FLAG)) { /* NFSV3_FLAG */ + if (choosen_nfs_version == NFS_V3) { nfsv3_data_offset = nfs3_get_attributes_offset(rpc_pkt.u.reply.data); } @@ -692,10 +736,10 @@ static int nfs_read_reply(uchar *pkt, unsigned len) if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10))) putc('#'); - if (supported_nfs_versions & NFSV2_FLAG) { + if (choosen_nfs_version != NFS_V3) { rlen = ntohl(rpc_pkt.u.reply.data[18]); data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]); - } else { /* NFSV3_FLAG */ + } else { /* NFS_V3 */ int nfsv3_data_offset = nfs3_get_attributes_offset(rpc_pkt.u.reply.data); @@ -773,6 +817,10 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip, /* just to be sure... */ nfs_state = STATE_UMOUNT_REQ; nfs_send(); + } else if (reply == -NFS_RPC_PROG_MISMATCH && + choosen_nfs_version != NFS_UNKOWN) { + nfs_state = STATE_MOUNT_REQ; + nfs_send(); } else { nfs_state = STATE_LOOKUP_REQ; nfs_send(); @@ -801,7 +849,7 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip, nfs_state = STATE_UMOUNT_REQ; nfs_send(); } else if (reply == -NFS_RPC_PROG_MISMATCH && - supported_nfs_versions != 0) { + choosen_nfs_version != NFS_UNKOWN) { /* umount */ nfs_state = STATE_UMOUNT_REQ; nfs_send(); @@ -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); } } @@ -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: diff --git a/test/dm/eth.c b/test/dm/eth.c index ebf01d8..d05d2a9 100644 --- a/test/dm/eth.c +++ b/test/dm/eth.c @@ -20,6 +20,7 @@ #include <dm/uclass-internal.h> #include <test/test.h> #include <test/ut.h> +#include <ndisc.h> #define DM_TEST_ETH_NUM 4 @@ -607,3 +608,90 @@ static int dm_test_eth_async_ping_reply(struct unit_test_state *uts) } DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT); + +#if IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY) + +static u8 ip6_ra_buf[] = {0x60, 0xf, 0xc5, 0x4a, 0x0, 0x38, 0x3a, 0xff, 0xfe, + 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x85, 0xe6, + 0x29, 0x77, 0xcb, 0xc8, 0x53, 0xff, 0x2, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x86, 0x0, 0xdc, 0x90, 0x40, 0x80, 0x15, 0x18, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x4, + 0x40, 0xc0, 0x0, 0x0, 0x37, 0xdc, 0x0, 0x0, 0x37, + 0x78, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0xca, 0xfe, 0xca, + 0xfe, 0xca, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x1, 0x0, 0x15, 0x5d, 0xe2, 0x8a, 0x2}; + +static int dm_test_validate_ra(struct unit_test_state *uts) +{ + struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf; + struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1); + __be16 temp = 0; + + ut_assert(validate_ra(ip6) == true); + + temp = ip6->payload_len; + ip6->payload_len = 15; + ut_assert(validate_ra(ip6) == false); + ip6->payload_len = temp; + + temp = ip6->saddr.s6_addr16[0]; + ip6->saddr.s6_addr16[0] = 0x2001; + ut_assert(validate_ra(ip6) == false); + ip6->saddr.s6_addr16[0] = temp; + + temp = ip6->hop_limit; + ip6->hop_limit = 15; + ut_assert(validate_ra(ip6) == false); + ip6->hop_limit = temp; + + temp = icmp->icmp6_code; + icmp->icmp6_code = 15; + ut_assert(validate_ra(ip6) == false); + icmp->icmp6_code = temp; + + return 0; +} + +DM_TEST(dm_test_validate_ra, 0); + +static int dm_test_process_ra(struct unit_test_state *uts) +{ + int len = sizeof(ip6_ra_buf); + struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf; + struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1); + struct ra_msg *msg = (struct ra_msg *)icmp; + unsigned char *option = msg->opt; + struct icmp6_ra_prefix_info *prefix = + (struct icmp6_ra_prefix_info *)option; + __be16 temp = 0; + unsigned char option_len = option[1]; + + ut_assert(process_ra(ip6, len) == 0); + + temp = icmp->icmp6_rt_lifetime; + icmp->icmp6_rt_lifetime = 0; + ut_assert(process_ra(ip6, len) != 0); + icmp->icmp6_rt_lifetime = temp; + + ut_assert(process_ra(ip6, 0) != 0); + + option[1] = 0; + ut_assert(process_ra(ip6, len) != 0); + option[1] = option_len; + + prefix->on_link = false; + ut_assert(process_ra(ip6, len) != 0); + prefix->on_link = true; + + temp = prefix->prefix.s6_addr16[0]; + prefix->prefix.s6_addr16[0] = 0x80fe; + ut_assert(process_ra(ip6, len) != 0); + prefix->prefix.s6_addr16[0] = temp; + + return 0; +} + +DM_TEST(dm_test_process_ra, 0); + +#endif diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py index 9ca6743..cd4b4dc 100644 --- a/test/py/tests/test_net.py +++ b/test/py/tests/test_net.py @@ -9,7 +9,7 @@ import u_boot_utils """ Note: This test relies on boardenv_* containing configuration values to define -which the network environment available for testing. Without this, this test +which network environment is available for testing. Without this, this test will be automatically skipped. For example: @@ -29,6 +29,11 @@ env__net_uses_pci = True # set to False. env__net_dhcp_server = True +# True if a DHCPv6 server is attached to the network, and should be tested. +# If DHCPv6 testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp6_server = True + # A list of environment variables that should be set in order to configure a # static IP. If solely relying on DHCP, this variable may be omitted or set to # an empty list. @@ -55,9 +60,15 @@ env__net_nfs_readable_file = { 'size': 5058624, 'crc32': 'c2244b26', } + +# True if a router advertisement service is connected to the network, and should +# be tested. If router advertisement testing is not possible or desired, this +variable may be omitted or set to False. +env__router_on_net = True """ net_set_up = False +net6_set_up = False def test_net_pre_commands(u_boot_console): """Execute any commands required to enable network hardware. @@ -93,6 +104,25 @@ def test_net_dhcp(u_boot_console): global net_set_up net_set_up = True +@pytest.mark.buildconfigspec('cmd_dhcp6') +def test_net_dhcp6(u_boot_console): + """Test the dhcp6 command. + + The boardenv_* file may be used to enable/disable this test; see the + comment at the beginning of this file. + """ + + test_dhcp6 = u_boot_console.config.env.get('env__net_dhcp6_server', False) + if not test_dhcp6: + pytest.skip('No DHCP6 server available') + + u_boot_console.run_command('setenv autoload no') + output = u_boot_console.run_command('dhcp6') + assert 'DHCP6 client bound to ' in output + + global net6_set_up + net6_set_up = True + @pytest.mark.buildconfigspec('net') def test_net_setup_static(u_boot_console): """Set up a static IP configuration. @@ -126,6 +156,30 @@ def test_net_ping(u_boot_console): output = u_boot_console.run_command('ping $serverip') assert 'is alive' in output +@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY') +def test_net_network_discovery(u_boot_console): + """Test the network discovery feature of IPv6. + + An IPv6 network command (ping6 in this case) is run to make U-Boot send a + router solicitation packet, receive a router advertisement message, and + parse it. + A router advertisement service needs to be running for this test to succeed. + U-Boot receives the RA, processes it, and if successful, assigns the gateway + IP and prefix length. + The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """ + + router_on_net = u_boot_console.config.env.get('env__router_on_net', False) + if not router_on_net: + pytest.skip('No router on network') + + fake_host_ip = 'fe80::215:5dff:fef6:2ec6' + output = u_boot_console.run_command('ping6 ' + fake_host_ip) + assert 'ROUTER SOLICITATION 1' in output + assert 'Set gatewayip6:' in output + assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output + @pytest.mark.buildconfigspec('cmd_net') def test_net_tftpboot(u_boot_console): """Test the tftpboot command. |