[U-Boot] [RFC] Act as a TFTP server

Luca Ceresoli
2011-04-13T09:08:58Z

Hi,

I am going to implement in U-Boot the ability to receive a file via TFTP acting
as a server, not a client. I've sketched up a way to implement it, and I would
appreciate any comments about it.

This is useful to solve the firewall issues that a Management Station can face
when sending files to a U-Boot device. These issues are only partially solved
using CONFIG_TFTP_PORT for '"punching through" the (Windows XP) firewall' (see
the README).

My implementation would be very basic (keep it simple):
- receive only (accept WRQ from remote client, not RRQ);
- the Filename in the WRQ is ignored: the destination is always a user-provided
memory location;
- binary transfers only: the Mode in the WRQ is ignored; this is allowed by
RFC1350 (section 5);
- no TFTP Option Extensions (RFC2347);
- no TFTP multicast.

After a preliminary analysis, I think the current TFTP clien implementation can
be mostly reused without code duplication, hence the amount of new code would
be pretty limited.

Even better, it would not increase binary size when the feature is disabled,
as I don't see any need to extend the existing code in a way that cannot be
easily put under appropriate #ifdefs.

From the user point of view, I would implement a new command, activated only
when CONFIG_CMD_TFTPSRV is defined:

Usage: tftpsrv [<loadaddr>]

This would be used almost like tftpboot, except no file name is specified on
the commandline.
<loadaddr> would default to the environment variable.

Here's a tentative implementation roadmap. It is presented in a roughly
top-down order, from user interface down to low level.
Many steps, except for those in net/tftp.c, are pretty trivial.

- add CONFIG_CMD_TFTPSRV config option;
- include/net.h: add TFTPSRV to proto_t, *as if it were a different protocol*;
this may look dirty but I believe it makes implementation cleaner;
- alternative: reuse TFTP protocol, but save in some flag (where?) whether
the user wants a client or server transfer;
- common/cmd_net.c: add a tftpsrv command and do_tftpsrv() function;
just like do_tftpb() simply calls netboot_common(TFTP, ...),
do_tftpsrv() would simply call netboot_common(TFTPSRV, ...);
- common/cmd_net.c: netboot_common() should not need change, except maybe
for the argv parsing: tftpsrv does not take a "hostaddr:filename" argument;
to make it simpler, it could accept (but ignore) it if passed;
- net/net.c: change net_check_prereq() as needed: it should not check for
NetServerIP when protocol==TFTPSRV;
- net/net.c: extend NetLoop() to handle the new protocol: call
TftpStartServer() (not TftpStart()) when protocol==TFTPSRV;
- net/tftp.c: add TftpStartServer() with a role similar to TftpStart();
- alternative: extend TftpStart() to handle both client and server mode;
probably not worth doing as the actions to do are mostly different;
- net/tftp.c: extend the TFTP state machine (TftpHandler() and TftpStart())
a new STATE_WAITING state (it should be enough);
- the state machine would be different in the session setup: in
STATE_WAITING handle WRQ packets and select a new UDP port;
- after the setup phase, the state machine would converge on the
currently-implemented STATE_DATA for normal DATA/ACK management.

Do you think my approach is correct?
Would this feature be considered for mainline inclusion?

Thanks in advance,
Luca