[PATCH 10/17] net: tftp: Add IPv6 support for tftpboot

Ramon Fried rfried.dev at gmail.com
Mon Sep 12 09:20:54 CEST 2022


On Tue, Sep 6, 2022 at 6:10 PM Viacheslav Mitrofanov
<v.v.mitrofanov at yadro.com> wrote:
>
> The command tftpboot uses IPv4 by default. Add the possibility to use IPv6
> instead. If an address in the command is an IPv6 address it will use IPv6
> to boot or if there is a suffix -ipv6 in the end of the command it also
> force using IPv6. All other tftpboot features and parameters are left
> the same.
>
> Signed-off-by: Viacheslav Mitrofanov <v.v.mitrofanov at yadro.com>
> ---
>  cmd/net.c  | 35 ++++++++++++++++++++++++++++++
>  net/net.c  | 17 +++++++++++++--
>  net/tftp.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++-------
>  3 files changed, 105 insertions(+), 10 deletions(-)
>
> diff --git a/cmd/net.c b/cmd/net.c
> index 3619c843d8..0f4f9a5625 100644
> --- a/cmd/net.c
> +++ b/cmd/net.c
> @@ -14,6 +14,7 @@
>  #include <env.h>
>  #include <image.h>
>  #include <net.h>
> +#include <net6.h>
>  #include <net/udp.h>
>  #include <net/sntp.h>
>
> @@ -44,12 +45,22 @@ int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>         return ret;
>  }
>
> +#if IS_ENABLED(CONFIG_IPV6)
> +U_BOOT_CMD(
> +       tftpboot,       4,      1,      do_tftpb,
> +       "boot image via network using TFTP protocol\n"
> +       "To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed "
> +       "with [] brackets",
> +       "[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]"
> +);
> +#else
>  U_BOOT_CMD(
>         tftpboot,       3,      1,      do_tftpb,
>         "boot image via network using TFTP protocol",
>         "[loadAddress] [[hostIPaddr:]bootfilename]"
>  );
>  #endif
> +#endif
>
>  #ifdef CONFIG_CMD_TFTPPUT
>  static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc,
> @@ -205,6 +216,17 @@ static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc,
>         if (s != NULL)
>                 image_load_addr = hextoul(s, NULL);
>
> +       if (IS_ENABLED(CONFIG_IPV6)) {
> +               use_ip6 = false;
> +
> +               /* IPv6 parameter has to be always *last* */
> +               if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) {
> +                       use_ip6 = true;
> +                       /* It is a hack not to break switch/case code */
> +                       --argc;
> +               }
> +       }
> +
>         switch (argc) {
>         case 1:
>                 /* refresh bootfile name from env */
> @@ -257,6 +279,19 @@ static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc,
>         }
>         bootstage_mark(BOOTSTAGE_ID_NET_START);
>
> +       if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) {
> +               char *s, *e;
> +               size_t len;
> +
> +               s = strchr(net_boot_file_name, '[');
> +               e = strchr(net_boot_file_name, ']');
> +               if (s && e) {
> +                       len = e - s;
> +                       if (!string_to_ip6(s + 1, len - 1, &net_server_ip6))
> +                               use_ip6 = true;
> +               }
> +       }
> +
>         size = net_loop(proto);
>         if (size < 0) {
>                 bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK);
> diff --git a/net/net.c b/net/net.c
> index f818170930..eaf78c4980 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -1381,7 +1381,14 @@ static int net_check_prereq(enum proto_t protocol)
>                 /* Fall through */
>         case TFTPGET:
>         case TFTPPUT:
> -               if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
> +               if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
> +                       if (!memcmp(&net_server_ip6, &net_null_addr_ip6,
> +                                   sizeof(struct in6_addr)) &&
> +                                   !strchr(net_boot_file_name, '[')) {
> +                               puts("*** ERROR: `serverip6' not set\n");
> +                               return 1;
> +                       }
> +               } else if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
>                         puts("*** ERROR: `serverip' not set\n");
>                         return 1;
>                 }
> @@ -1394,7 +1401,13 @@ common:
>         case NETCONS:
>         case FASTBOOT:
>         case TFTPSRV:
> -               if (net_ip.s_addr == 0) {
> +               if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
> +                       if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6,
> +                                   sizeof(struct in6_addr))) {
> +                               puts("*** ERROR: `ip6addr` not set\n");
> +                               return 1;
> +                       }
> +               } else if (net_ip.s_addr == 0) {
>                         puts("*** ERROR: `ipaddr' not set\n");
>                         return 1;
>                 }
> diff --git a/net/tftp.c b/net/tftp.c
> index dea9c25ffd..a6b0ec57a4 100644
> --- a/net/tftp.c
> +++ b/net/tftp.c
> @@ -15,6 +15,7 @@
>  #include <log.h>
>  #include <mapmem.h>
>  #include <net.h>
> +#include <net6.h>
>  #include <asm/global_data.h>
>  #include <net/tftp.h>
>  #include "bootp.h"
> @@ -41,6 +42,7 @@ DECLARE_GLOBAL_DATA_PTR;
>  static ulong timeout_ms = TIMEOUT;
>  static int timeout_count_max = (CONFIG_NET_RETRY_COUNT * 2);
>  static ulong time_start;   /* Record time we started tftp */
> +static struct in6_addr tftp_remote_ip6;
>
>  /*
>   * These globals govern the timeout behavior when attempting a connection to a
> @@ -116,6 +118,7 @@ static int  tftp_put_final_block_sent;
>
>  /* default TFTP block size */
>  #define TFTP_BLOCK_SIZE                512
> +#define TFTP_MTU_BLOCKSIZE6 (CONFIG_TFTP_BLOCKSIZE - 20)
>  /* sequence number is 16 bit */
>  #define TFTP_SEQUENCE_SIZE     ((ulong)(1<<16))
>
> @@ -320,7 +323,11 @@ static void tftp_send(void)
>          *      We will always be sending some sort of packet, so
>          *      cobble together the packet headers now.
>          */
> -       pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
> +       if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
> +               pkt = net_tx_packet + net_eth_hdr_size() +
> +                     IP6_HDR_SIZE + UDP_HDR_SIZE;
> +       else
> +               pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
>
>         switch (tftp_state) {
>         case STATE_SEND_RRQ:
> @@ -422,8 +429,14 @@ static void tftp_send(void)
>                 break;
>         }
>
> -       net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
> -                           tftp_remote_port, tftp_our_port, len);
> +       if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
> +               net_send_udp_packet6(net_server_ethaddr,
> +                                    &tftp_remote_ip6,
> +                                    tftp_remote_port,
> +                                    tftp_our_port, len);
> +       else
> +               net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
> +                                   tftp_remote_port, tftp_our_port, len);
>
>         if (err_pkt)
>                 net_set_state(NETLOOP_FAIL);
> @@ -750,6 +763,9 @@ void tftp_start(enum proto_t protocol)
>         debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
>               tftp_block_size_option, tftp_window_size_option, timeout_ms);
>
> +       if (IS_ENABLED(CONFIG_IPV6))
> +               tftp_remote_ip6 = net_server_ip6;
> +
>         tftp_remote_ip = net_server_ip;
>         if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
>                 sprintf(default_filename, "%02X%02X%02X%02X.img",
> @@ -764,18 +780,49 @@ void tftp_start(enum proto_t protocol)
>                 printf("*** Warning: no boot file name; using '%s'\n",
>                        tftp_filename);
>         }
> +       if (IS_ENABLED(CONFIG_IPV6)) {
> +               if (use_ip6) {
> +                       char *s, *e;
> +                       size_t len;
> +
> +                       s = strchr(net_boot_file_name, '[');
> +                       e = strchr(net_boot_file_name, ']');
> +                       len = e - s;
> +                       if (s && e) {
> +                               string_to_ip6(s + 1, len, &tftp_remote_ip6);
> +                               strlcpy(tftp_filename, e + 2, MAX_LEN);
> +                       } else {
> +                               strlcpy(tftp_filename, net_boot_file_name, MAX_LEN);
> +                               tftp_filename[MAX_LEN - 1] = 0;
> +                       }
> +               }
> +       }
>
>         printf("Using %s device\n", eth_get_name());
> -       printf("TFTP %s server %pI4; our IP address is %pI4",
> +
> +       if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
> +               printf("TFTP from server %pI6c; our IP address is %pI6c",
> +                      &tftp_remote_ip6, &net_ip6);
> +
> +               if (tftp_block_size_option > TFTP_MTU_BLOCKSIZE6)
> +                       tftp_block_size_option = TFTP_MTU_BLOCKSIZE6;
> +       } else {
> +               printf("TFTP %s server %pI4; our IP address is %pI4",
>  #ifdef CONFIG_CMD_TFTPPUT
> -              protocol == TFTPPUT ? "to" : "from",
> +                      protocol == TFTPPUT ? "to" : "from",
>  #else
> -              "from",
> +                      "from",
>  #endif
> -              &tftp_remote_ip, &net_ip);
> +                      &tftp_remote_ip, &net_ip);
> +       }
>
>         /* Check if we need to send across this subnet */
> -       if (net_gateway.s_addr && net_netmask.s_addr) {
> +       if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
> +               if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6,
> +                                       net_prefix_length))
> +                       printf("; sending through gateway %pI6c",
> +                              &net_gateway6);
> +       } else if (net_gateway.s_addr && net_netmask.s_addr) {
>                 struct in_addr our_net;
>                 struct in_addr remote_net;
>
> --
> 2.25.1
>
Reviewed-by: Ramon Fried <rfried.dev at gmail.com>


More information about the U-Boot mailing list