
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