Forum Discussion
Matthew_Gamble_
Nimbostratus
Mar 12, 2009TFTP Server?
Is it possible to write a simple TFTP server as an iRule to return static content? I see some examples for load balancing TFTP requests, but not for actually acting as a TFTP server. I have a very small and static TFTP file that I need to distribute, and rather than running a dedicated TFTP server it would be far better if it could be deployed as an iRule.
Is this possible? Any resources I should be looking at?
25 Replies
- JRahm
Admin
Mine is working from the pumpkin tftp client. Still needs to be cleaned up a bit, though:when RULE_INIT { set blockdata(1) [binary format H* 0003000123212F7573722F62696E2F7065726C202D77DA757365207374726963743BDA757365204E65743A3A534E4D50207177283A736E6D70293BDADA6D7920247573616765203D20226C746D5F696E74537461742E706C203C686F73743E203C736E6D7020636F6D6D756E6974793E203C696E746572666163653E203C696E74657276616C3E223BDADA646965202255736167653A202475736167655C6E222069662024234152475620213D20333BDADA6D792024686F7374203D2024415247565B305D3BDA6D792024736E6D705F636F6D6D203D2024415247565B315D3BDA6D792024696E74203D2024415247565B325D3BDA6D792024696E74657276616C203D2024415247565B335D3BDADA63686F6D70202824686F7374202C2024736E6D705F636F6D6D202C2024696E74202C2024696E74657276616C293BDADA6D7920246C746D5F496E42797465735F496E646578203D2022312E332E362E312E342E312E333337352E322E312E322E342E342E332E312E33223BDA6D7920246C746D5F4F757442797465735F496E646578203D2022312E332E362E312E342E312E333337352E322E312E322E342E342E332E312E35223BDADADA6D792025696E745F6D6170203D202822312E31223D3E222E332E34392E34362E3439222CDA20202020202020202020202020202022312E32223D3E222E332E34392E34362E3530222CDA2] set blockdata(2) [binary format H* 000300020202020202020202020202020202022312E33223D3E222E332E34392E34362E3531222CDA20202020202020202020202020202022312E34223D3E222E332E34392E34362E3532222CDA20202020202020202020202020202022312E35223D3E222E332E34392E34362E3533222CDA20202020202020202020202020202022312E36223D3E222E332E34392E34362E3534222CDA20202020202020202020202020202022312E37223D3E222E332E34392E34362E3535222CDA20202020202020202020202020202022312E38223D3E222E332E34392E34362E3536222CDA20202020202020202020202020202022312E39223D3E222E332E34392E34362E3537222CDA20202020202020202020202020202022312E3130223D3E222E342E34392E34362E34392E3438222CDA20202020202020202020202020202022312E3131223D3E222E342E34392E34362E34392E3439222CDA20202020202020202020202020202022312E3132223D3E222E342E34392E34362E34392E3530222CDA20202020202020202020202020202022312E3133223D3E222E342E34392E34362E34392E3531222CDA20202020202020202020202020202022312E3134223D3E222E342E34392E34362E34392E3532222CDA20202020202020202020202020202022312E3135223D3E222E342E34392E34362E34392E3533222CDA2020202020202020] set blockdata(3) [binary format H* 000300032020202020202022312E3136223D3E222E342E34392E34362E34392E3534222CDA20202020202020202020202020202022322E31223D3E222E332E35302E34362E3439222CDA20202020202020202020202020202022322E32223D3E222E332E35302E34362E3530222CDA20202020202020202020202020202022322E33223D3E222E332E35302E34362E3531222CDA20202020202020202020202020202022322E34223D3E222E332E35302E34362E3532222CDA202020202020202020202020202020226D676D74223D3E222E342E3130392E3130332E3130392E31313622293BDADA6D7920246C746D5F696E744279746573496E203D20246C746D5F496E42797465735F496E646578202E2024696E745F6D61707B24696E747D3BDA6D7920246C746D5F696E7442797465734F7574203D20246C746D5F4F757442797465735F496E646578202E2024696E745F6D61707B24696E747D3BDADA6D7920282473657373696F6E2C20246572726F7229203D204E65743A3A534E4D502D3E73657373696F6E28DA20202020202020202D686F73746E616D65202020202020203D3E2024686F73742CDA20202020202020202D636F6D6D756E6974792020202020203D3E2024736E6D705F636F6D6D2CDA20202020202020202D706F727420202020202020202020203D3E203136312CDA20202020202020202D76657273696F6E2] set blockdata(4) [binary format H* 000300040202020202020203D3E2027736E6D70763263272CDA20202020202020202D6E6F6E626C6F636B696E67202020203D3E2030DA2020202020202020293BDADA6966202821646566696E6564202473657373696F6E29DA20202020202020207BDA20202020202020207072696E7420225265636569766564206E6F20534E4D5020726573706F6E73652066726F6D2024686F73745C6E223BDA20202020202020207072696E742053544445525220224572726F723A20246572726F725C6E223BDA202020202020202065786974202D313BDA20202020202020207DDADA2347657420666972737420696E7374616E6365DA6D7920246F6964735F31203D202473657373696F6E2D3E6765745F7265717565737428DA2020202020202020202020202020202020202D76617262696E646C697374203D3EDA2020202020202020202020202020202020205B246C746D5F696E744279746573496E2C20246C746D5F696E7442797465734F75745D20293BDADA736C6565702024696E74657276616C3BDADA23476574207365636F6E6420696E7374616E6365DA6D7920246F6964735F32203D202473657373696F6E2D3E6765745F7265717565737428DA2020202020202020202020202020202020202D76617262696E646C697374203D3EDA2020202020202020202020202020202020205B246C746D5F696E744279746573496E2C20] set blockdata(5) [binary format H* 00030005246C746D5F696E7442797465734F75745D20293BDADA2343616C63756C617465205261746573DA6D792024726174655F696E203D2028246F6964735F322D3E7B246C746D5F696E744279746573496E7D202D20246F6964735F312D3E7B246C746D5F696E744279746573496E7D292A38202F2024696E74657276616C3BDA6D792024726174655F6F7574203D2028246F6964735F322D3E7B246C746D5F696E7442797465734F75747D202D20246F6964735F312D3E7B246C746D5F696E7442797465734F75747D292A38202F2024696E74657276616C3BDADA23526F756E6420746F20696E7465676572DA24726174655F696E203D20696E742824726174655F696E202B202E35293BDA24726174655F6F7574203D20696E74202824726174655F6F7574202B202E35293BDA6D792024726174655F746F74616C203D2024726174655F696E202B2024726174655F6F75743BDADA235072696E7420526573756C7473DA7072696E7420225C6E5C6E5C7424726174655F696E20626974732F7365636F6E642028494E295C6E223BDA7072696E7420225C7424726174655F6F757420626974732F7365636F6E6420284F5554295C6E223BDA7072696E7420225C7424726174655F746F74616C20626974732F7365636F6E642028544F54414C295C6E223B0D0A] set ::debug 1 } when CLIENT_ACCEPTED { binary scan [UDP::payload] xc opcode if { $::debug } { log local0. "Opcode is $opcode" } switch $opcode { 1 { binary scan [UDP::payload] xxa* string if { $::debug } { log local0. "String is $string" } set file [lindex [split $string \000] 0] set mode [lindex [split $string \000] 1] if { $::debug} { log local0. "File is $file, Mode is $mode" } if { $mode == "octet" || $mode == "netascii" } { if { $file eq "test.txt" } { if { $::debug } { log local0. "Request is valid" } UDP::respond $::blockdata(1) UDP::respond $::blockdata(2) UDP::respond $::blockdata(3) UDP::respond $::blockdata(4) UDP::respond $::blockdata(5) } else { if { $::debug } { log local0. "Request is invalid" } reject } } else { if { $::debug } { log local0. "Invalid mode selected" } reject } } 2 { if { $::debug } { log local0. "Write request not supported here" } } 3 { if { $::debug } { log local0. "Data receipt not supported here" } } 4 { if { $::debug } { log local0. "Ack from client received" } } 5 { if { $::debug } { log local0. "Error: $string" } } default { if { $::debug } { log local0. "Opcode $opcode is invalid" } reject } } }
contents received are in the tftp file are:
!/usr/bin/perl -wÚuse strict;Úuse Net::SNMP qw(:snmp);ÚÚmy $usage = "ltm_intStat.pl ";ÚÚdie "Usage: $usage\n" if $ARGV != 3;ÚÚmy $host = $ARGV[0];Úmy $snmp_comm = $ARGV[1];Úmy $int = $ARGV[2];Úmy $interval = $ARGV[3];ÚÚchomp ($host , $snmp_comm , $int , $interval);ÚÚmy $ltm_InBytes_Index = "1.3.6.1.4.1.3375.2.1.2.4.4.3.1.3";Úmy $ltm_OutBytes_Index = "1.3.6.1.4.1.3375.2.1.2.4.4.3.1.5";ÚÚÚmy %int_map = ("1.1"=>".3.49.46.49",Ú "1.2"=>".3.49.46.50",Úã2Óâ"ã2ãC’ãCbãS"Í¢ãBÓâ"ã2ãC’ãCbãS""Í¢ãRÓâ"ã2ãC’ãCbãS2"Í¢ãbÓâ"ã2ãC’ãCbãSB"Í¢ãrÓâ"ã2ãC’ãCbãSR"Í¢ã‚Óâ"ã2ãC’ãCbãSb"Í¢ã’Óâ"ã2ãC’ãCbãSr"Í¢ãÓâ"ãBãC’ãCbãC’ãC‚"Í¢ãÓâ"ãBãC’ãCbãC’ãC’"Í¢ã"Óâ"ãBãC’ãCbãC’ãS"Í¢ã2Óâ"ãBãC’ãCbãC’ãS"Í¢ãBÓâ"ãBãC’ãCbãC’ãS""Í¢ãRÓâ"ãBãC’ãCbãC’ãS2"Í¢ "1.16"=>".4.49.46.49.54",Ú "2.1"=>".3.50.46.49",Ú "2.2"=>".3.50.46.50",Ú "2.3"=>".3.50.46.51",Ú "2.4"=>".3.50.46.52",Ú "mgmt"=>".4.109.103.109.116");ÚÚmy $ltm_intBytesIn = $ltm_InBytes_Index . $int_map{$int};Úmy $ltm_intBytesOut = $ltm_OutBytes_Index . $int_map{$int};ÚÚmy ($session, $error) = Net::SNMP->session(Ú -hostname => $host,Ú -community => $snmp_comm,Ú -port => 161,Ú -versionÓâw6æ×c&2rÍ¢Öæöæ&Æö6¶–ærÓâ
¢“½¦–b‚FVf–æVBG6W76–öâ�¢½¢&–çB%&V6V—fVBæò4äÕ&W7öç6Rg&öÒF†÷7EÆâ½¢&–çB5DDU%"$W'&÷¢FW'&÷%Æâ½¢W†—BÓ½¢Ý¢4vWBf—'7B–ç7Fæ6]¦×’Fö–G5óÒG6W76–öâÓævWE÷&WVW7B�¢×f&&–æFÆ—7BÓí¢²FÇFÕö–çD'—FW4–âÂFÇFÕö–çD'—FW4÷WEÒ“½§6ÆVWF–çFW'fý¢4vWB6V6öæB–ç7Fæ6]¦×’Fö–G5ó"ÒG6W76–öâÓævWE÷&WVW7B�¢×f&&–æFÆ—7BÓí¢²FÇFÕö–çD'—FW4–âÂ$ltm_intBytesOut] );ÚÚCalculate RatesÚmy $rate_in = ($oids_2->{$ltm_intBytesIn} - $oids_1->{$ltm_intBytesIn})*8 / $interval;Úmy $rate_out = ($oids_2->{$ltm_intBytesOut} - $oids_1->{$ltm_intBytesOut})*8 / $interval;ÚÚRound to integerÚ$rate_in = int($rate_in + .5);Ú$rate_out = int ($rate_out + .5);Úmy $rate_total = $rate_in + $rate_out;ÚÚPrint ResultsÚprint "\n\n\t$rate_in bits/second (IN)\n";Úprint "\t$rate_out bits/second (OUT)\n";Úprint "\t$rate_total bits/second (TOTAL)\n";
Haven't quite figured out the correct ascii to hex conversion. I am cutting into blocks first, then prepending the blocks with 0003xxxx, where xxxx is the blockID. - Nat_Thirasuttakorn
Employee
first of all, sorry for long line.
regarding the block problem
I did not check RFC but from packet trace it shows that both opcode and block-id use 2 bytes (short integer)
I think you may change it to something like this
set opcode 3
set blockid 1
set block [binary format SSH* $opcode $blockid [string range $::tftp_file 0 511] ]
UDP::respond $block
sorry again, not tested
Nat - JRahm
Admin
The important stuff doesn't require a scroll... :-) - James_Quinby_46Historic F5 AccountOk, got the enconding figured out (I ripped it straight from the Tcl wiki). Here's the latest version.
when CLIENT_ACCEPTED { log local0. "Client connected from [IP::client_addr]" binary scan [UDP::payload] xc opcode log local0. "Opcode is $opcode" if { $opcode == 1 } { binary scan [UDP::payload] xxa* string log local0. "String is $string" set stringlist [split $string \000] set file [lindex $stringlist 0] set mode [lindex $stringlist 1] log local0. "File is $file, Mode is $mode" if { $mode != "octet" && $mode != "netascii" } { reject } else { log local0. "Send the file!" set data [binary format xcSa* 3 1 [string range $::tftp_file 0 511]] log local0. "data = $data" UDP::respond $data } } else { reject } }
So - incrementing the block ID (the "1" in the format command above) along with moving to the next 512 bytes is all that's left, modulo the changes citizen_elah has added that improve the protocol handling. This is pretty close. This will hopefully let us avoid having to embed the file into the actual rule. - James_Quinby_46Historic F5 AccountOK, stuck again. I can't test it because I can't get it to parse right in the IRE. I'm missing something dumb here, but am going cross-eyed trying to find it. I'll post what I have and come back to it later after I clear my head.
(updated with some comments and a little visual cleanup)when CLIENT_ACCEPTED { log local0. "Client connected from [IP::client_addr]" binary scan [UDP::payload] xc opcode log local0. "Opcode is $opcode" if { $opcode == 1 } { binary scan [UDP::payload] xxa* string log local0. "String is $string" set stringlist [split $string \000] set file [lindex $stringlist 0] set mode [lindex $stringlist 1] log local0. "File is $file, Mode is $mode" if { $mode != "octet" && $mode != "netascii" } { reject } else { log local0. "Send the file!" set block_id 1 set block_start 0 set block_len 512 set last 0 while { $last < 1 } { log local0. "Block number $block_id" set data [binary format xcSa* 3 $block_id [string range $::tftp_file $block_start $block_end]] test for the last block - is it less than 512 bytes? If so, set a flag if { [string length [string range $::tftp_file $block_start $block_end]] < 512 } then { set last 1 } log local0. "data = $data" send the data UDP::respond $data increment block_id and shift the pointers to the next block of data to send incr block_id block_start = block_len set block_len = block_len + 512 } We should be breaking out of the while loop now The last block should have been sent. } } } else { reject } } - James_Quinby_46Historic F5 AccountAlso - the file you drop into file.class has to follow the same format as in the iRule for returning HTML and graphics from an iRule:
http://devcentral.f5.com/wiki/default.aspx/iRules/LTMMaintenancePage.html
Pay particular attention to the treatment of text within the class file. No newlines are allowed, the block must be quoted, and there must be a trailing comma.
The warning about memory is also useful - the file will be read into (and served from) memory, so be wary of using this method to transfer giant files. Not that anyone would do that with TFTP, but you never know.
If you're using this iRule to serve binary data, there may need to be additional tweaks. You might have uuencode it first, then add the b64decode statement (see the above page and the lines for sending the PNG file). - JRahm
Admin
Hey all, I wrote up a tech tip on the excellent work done here in the forum...great collaboration guys!
http://devcentral.f5.com/Default.aspx?tabid=63&articleType=ArticleView&articleId=343 Click here - Nat_Thirasuttakorn
Employee
your irule looks perfect.
just a comment that you may forward request onto server side which you will get 2 things in return
- server will send file from ephemeral port (not port 69)
- retry in case packet loss. you can have routine in SERVER_DATA that replies content based on ack it receives. if let say block 10 is loss, client will resend ack-for block 9. irule can detect block id from ack message and response with block "ack + 1"
however, I didnt read RFC, feel free to correct me if I am wrong. - James_Quinby_46Historic F5 Accountnatty76 -
The behavior you describe is correct. The way I read it, the server should wait for ACK(block) before sending block(ACK+1). In any case, the final rule looks great. I was a little surprised to not find one out there already. - hoolio
Cirrostratus
Very nice collaboration, indeed.
Now if you guys can just figure out a way to serve the LTM ISO from a datagroup, you could build an "Enterprise Manager in an iRule" rule! :D
Aaron
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects
