routerboard as TFTP server

Greetings,

I’m trying to use routerboard as a TFTP server for ip phone configurations.

It seems I always need to specify the requested and real filenames.

Since the ip phones request specific filenames, can we pass this to the real-filename?

It would be nice to avoid creating dozens of tftp entries (one for each phone and one for each phone model).

I am using sd card on RB750Gr3 to store the config files.

I can get it to work when specifying, but I’d prefer if I didn’t have to.

[admin@Agnes] > /ip tftp print
Flags: X - disabled 
 #   IP-ADDRESSES         REQ-FILENAME                  REAL-FILENAME                 ALLOW READ-ONLY ALLOW-ROLLOVER       HITS
 0 X 192.168.7.0/24                                     files/.*                     yes   yes       no                     22
 1   192.168.7.0/24       00156588f666.cfg              disk1/files/00156588f666.cfg  yes   yes       no                      2
 2 X 192.168.7.0/24       y000000000052.cfg             disk1/files/y000000000052.cfg yes   yes       no                      3

Entries 1 & 2 work, but of course entry 0 doesn’t.

At first, manual sounds very promising:

req-filename - requested filename as regular expression (regex) if field is left empty it defaults to .*

There’s regexp, so usual thing you can do with those is to match some part and use it. But then there’s this:

real-filename - if req-filename and real-filename values are set and valid, the requested filename will be replaced with matched file. This field has to be set. If multiple regex are specified in req-filename, with this field you can set which ones should match, so this rule is validated. real-filename format for using multiple regex is filename\0\5\6

It’s weird and I don’t understand it. Ok, let’s try the example, maybe it will be better:

example 3 - if user requests aaa.bin or bbb.bin then give them ccc.bin:

/ip tftp add req-filename=“(aaa.bin)|(bbb.bin)” real-filename=“/sata1/ccc.bin\0” allow=yes read-only=yes

>

Erm, what the <beep>?! And when I try it and request aaa.bin, it doesn't work, file not found. Too bad logging doesn't include name of file it tries to read from disk.

I'd expect something like:

```text
/ip tftp
add req-filename=".*" real-filename="/path/\\0"
add req-filename=".*" real-filename="/\\0.txt"
add req-filename="(.*)\\.cfg\$" real-filename="/\\1.txt"

Where if I’d request e.g. “test.cfg”, it would be mapped to (in same order):

/path/test.cfg
/test.cfg.txt
/test.txt

But nope.

It works, except that in the example, there is the leading slash (/) in the real-file-name value, which is wrong. And I haven’t noticed the possibility to use the \0 (a reference to the whole req-filename) so I haven’t tried that.

The name is wrong, the file name should not have “/”.

No, leading slash seems to be fine, e.g. this works:

/ip tftp
add real-filename=/x.txt req-filename=x.txt

Problem is with regexp, I haven’t found a way how to do anything with references. And honestly, I don’t understand the description of real-filename. Does it make any sense to you?

I only knew it was working and I had a look to the existing configuration which was working.

So now I’ve tested the following:

/ip tftp
add ip-addresses=192.168.88.0/24 real-filename=“disk1/\0” req-filename=“6863i\.st”
add ip-addresses=192.168.88.0/24 real-filename=“disk1/\1.\2” req-filename=“(6863i).(st)”

(always just one row was enabled at a time).

In both cases, the Tik (a hAP ac² running 6.45.9 with an USB flash recognized as disk1) serves the same file, disk1/6863i.st. The fact that the Windows client ignores the first packet of the file and keeps repeating the request is another story, but from Debian the transfer succeeds.

I agree with you that the description of real-filename in the manual is confusing, but it works as you’d expect it to work - it lets you compose the actual file name to serve from portions of text you provide manually and references to what has been captured when matching the regexp in req-filename to the requested file name received from the client. So they just mix up multiple regular expressions with multiple references in the field description - it looks the documentarist didn’t distinguish between the two.

It’s really weird. I have file test/6863i.st on router. If I add this:

/ip tftp add real-filename="test/6863i.st" req-filename="6863i\\.st"

and request “6863i.st”, I get the file. So it’s there, router can read it, and everything is ok. If instead I use this:

/ip tftp add real-filename="test/\\0" req-filename="6863i\\.st"

and request the same “6863i.st”, file not found. Tested with 6.46.7, 6.47.4, 6.48beta40 (all CHRs) and 4.46.7 on mipsbe device, in case all CHRs were bad. Same result everywhere. I compared packets with request, there’s no difference there, so it must be router.

So I downgraded one CHR to 6.45.9 and voila, it works!

Except it really doesn’t. Above examples are ok. This works too:

/ip tftp add real-filename="test/\\1.\\2" req-filename="(6863i)\\.(st)"

But this, when I request “6863i.xxx”, fails (file not found):

/ip tftp add real-filename="test/\\1.st" req-filename="(6863i)\\.xxx"

So what about something simpler:

/ip tftp add real-filename="test/\\0.st" req-filename=6863i

Request for “6863i”, file not found. Strange.

Request for “6863i.st”, surprisingly, succeeds. Sure, regexp in req-filename matches, but \0 clearly doesn’t put whole name in real-filename, because if it did, it would become test/6863i.st.st and it doesn’t exist.

Well, I had a real hard time following the responses.

My question still stands.
Is there any way to simply have Mikrotik tftp server send the filename that was requested?

It seems a very limited implementation of a tftp server (need to hard code
every filename to send).

I guess it’s not bad if I only have 5-20 phones, and it’s better than not having any option for tftp server.

I don’t need to do any filename rewriting/replacement. I simply want filename requested = real-filename.

Ideally, if “req-filename” were a variable, I would love if something like this would work:

add disabled=yes ip-addresses=192.168.7.0/24 real-filename=disk1/files/<$var1> req-filename=<var1>

It looks like I got a little carried away with testing what’s possible. But if you want to use whole file name without any advanced changes and only read it from some directory, then if you leave req-filename blank, it seems to work like this:

/ip tftp add ip-addresses=192.168.7.0/24 real-filename="disk1/files/\\0"

@Sob THANK YOU, THANK YOU, and THANK YOU!

May I ask you to explain the logic/syntax for your “real-filename”?

How can we get this most valuable information added to to the MikroTik wiki
https://wiki.mikrotik.com/wiki/Manual:IP/TFTP?

Within a “regular expression” in the computer science sense, you can use parentheses to reference parts (substrings) of the matching string. If there is a way to use these references in the application using the regular expression, its actual syntax differs depending on that application. Here, \1 to \n (n unknown to me) are the references; the number indicates the number of the opening (left) parenthesis. So if your regexp says ((some) nice (word)) and the matching string is say some nice words to me, \1 will be translated to some nice word, \2 will be translated to some, and \3 to word.

In /ip tftp handling in particular, \0 seems to be a reference to the part of the string which matches the regular expression, or to the whole string matching it, or even to something else as @Sob’s experiment has shown. If no req-filename is specified, any file name requested by the client matches to that empty regular expression as a whole, so \0 becomes simply a reference to that requested file name. And prepending it with disk1/ makes RouterOS look for that original file name in the disk1 subdirectory of its user-accessible file system.

The other bit is that as you must use quotes to specify the real-filename, some symbols have a specific meaning within the quotes, so if you want them to be used literally, you must use an “escape” symbol. Mikrotik has chosen \ as an escape symbol, so to have a literal , you must use \. Hence you actually want disk1/\0, and to set that up, it becomes disk1/\0.

@Sindy, thanks for the detailed explanation. It’d much appreciated.

Cheers!

I couldn’t find any reference to changes in “change logs” regarding tftp since 6.45
but when using 6.48 we can now leave the “real-filename” empty.
Well I must leave it empty \0 is not working in 6.48 but the following is
working when you simply want to serve the filename requested.

instead of:

/ip tftp
add ip-addresses=192.168.1.0/24 real-filename="\\\\0"

This works in 6.48:

/ip tftp
add ip-addresses=192.168.1.0/24

The pattern replacements seem to be broken in 6.49.1, but anyone looking for a working template can use this as a way to add a prefix:

add real-filename=pxe/ req-filename=.*

This does not work, despite others in the thread having success with it. I think that regular expression patterns are just broken in the latest version:

add real-filename="pxe/\\0" req-filename=.*

would anyone will be able to tell me what is the differences between SMB and FTP?
I have been using SMB for long time, that work well

SMB and TFTP are completely different protocols … as are FTP, NFS and SFTP (scp) … they all are used to transfer whole files back and forth, but details are quite different (OK, TFTP and FTP are somehow related, but are not really compatible).

TFTP has a hint in its name (Trivial File Transfer Protocol). The main goal is to be extremely simple, so that it can be implemented with smallest possible amount of code, there’s no security or any advanced features. It has limited use, nowadays probably just ethernet booting. Some older devices also used it to save and load configuration.