home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.lbl.gov / 2014.05.ftp.ee.lbl.gov.tar / ftp.ee.lbl.gov / acld-1.11.tar.gz / acld-1.11.tar / acld-1.11 / force10.expect < prev    next >
Text File  |  2012-02-17  |  30KB  |  1,355 lines

  1. # @(#) $Id: force10.expect 811 2012-02-18 04:50:40Z leres $ (LBL)
  2. #
  3. #  Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012
  4. #    The Regents of the University of California.  All rights reserved.
  5. #
  6. #  Redistribution and use in source and binary forms, with or without
  7. #  modification, are permitted provided that: (1) source code distributions
  8. #  retain the above copyright notice and this paragraph in its entirety, (2)
  9. #  distributions including binary code include the above copyright notice and
  10. #  this paragraph in its entirety in the documentation or other materials
  11. #  provided with the distribution, and (3) all advertising materials mentioning
  12. #  features or use of this software display the following acknowledgement:
  13. #  ``This product includes software developed by the University of California,
  14. #  Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
  15. #  the University nor the names of its contributors may be used to endorse
  16. #  or promote products derived from this software without specific prior
  17. #  written permission.
  18. #  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  19. #  WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  20. #  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  21. #
  22. # force10.expect - acld script for controlling a force 10 router (using ssh)
  23. #
  24.  
  25. #
  26. # Always include an explicit "return" in each procedure:
  27. #
  28. #    This will cause an expect error if the procedure is supposed
  29. #    to kreturn a value
  30. #
  31. # Always exclude \r\n in negated regex character classes:
  32. #
  33. #    Otherwise the regex can match more than one line
  34. #
  35.  
  36. set timeout 10
  37. match_max 2048
  38. set cprompt "\[^#\r\n]*#"
  39. set currentmode "logout"
  40. set currentacl ""
  41. set f "expect.log"
  42. set logname "/var/log/acld/expect.log"
  43. if [catch {log_file -a $logname} err] {
  44.     #send_error "$prog: warning: $err\n"
  45.     set logname $f
  46.     if [catch {log_file -a $logname} err] {
  47.         #send_error "$prog: warning: $err\n"
  48.     }
  49. }
  50.  
  51. set lastseq 65535
  52.  
  53. # Cleanup if our child dies
  54. trap { wait } SIGCHLD
  55.  
  56. # Higher level is easier if the prompt doesn't change
  57. proc prompt1 {} {
  58.     return "expect>"
  59. }
  60.  
  61. # Required procedures
  62.  
  63. proc attr {acl attrlist} {
  64.     global isipv6
  65.  
  66.     foreach attr $attrlist {
  67.         if { $attr == "ipv6" } {
  68.             set isipv6($acl) 1
  69.             continue
  70.         }
  71.         puts "attr-failed -"
  72.         puts "unknown attr \"$attr\""
  73.         puts "."
  74.         return
  75.     }
  76.     puts "attr"
  77.     return
  78. }
  79.  
  80. proc drop {addr acl seq} {
  81.     global logname
  82.  
  83.     set ip [acl2ip $acl]
  84.     if { [string first "/" $addr] > 0 } {
  85.         # Network with mask
  86.         aclcmd "drop" $acl $seq "" "deny $ip $addr any"
  87.     } else {
  88.         # Host address
  89.         aclcmd "drop" $acl $seq "" "deny $ip host $addr any"
  90.     }
  91.  
  92.     # Reopen the logfile from time to time
  93.     log_file
  94.     log_file -a $logname
  95.     return
  96. }
  97.  
  98. proc blockhosthost {addr1 addr2 acl seq} {
  99.  
  100.     set ip [acl2ip $acl]
  101.  
  102.     aclcmd "blockhosthost" $acl $seq "" "deny $ip host $addr1 host $addr2"
  103.     return
  104. }
  105.  
  106. proc dropudpport {port acl seq} {
  107.  
  108.     aclcmd "dropudpport" $acl $seq "" "deny udp any any eq $port"
  109.     return
  110. }
  111.  
  112. proc droptcpport {port acl seq} {
  113.  
  114.     aclcmd "droptcpport" $acl $seq "" "deny tcp any any eq $port"
  115.     return
  116. }
  117.  
  118. proc droptcpdsthostport {addr port acl seq} {
  119.  
  120.     aclcmd "droptcpdsthostport" $acl $seq "" \
  121.         "deny tcp any host $addr eq $port"
  122.     return
  123. }
  124.  
  125. proc ayt {} {
  126.     global cprompt
  127.  
  128.     sendcmd ""
  129.     set timeout 10
  130.     expect {
  131.         "Connection closed by foreign host" {
  132.             puts "ayt-failed -"
  133.             puts "ssh session ended"
  134.             puts "."
  135.  
  136.             # exit so acld will start a fresh expect session
  137.             exit
  138.         }
  139.         -re $cprompt {
  140.             # done
  141.         }
  142.         timeout {
  143.             puts "ayt-failed -"
  144.             puts "timeout ($timeout seconds)"
  145.             puts "."
  146.  
  147.             # exit so acld will start a fresh expect session
  148.             exit
  149.         }
  150.     }
  151.     puts "ayt"
  152.     return
  153. }
  154.  
  155. # Try the "show ip accounting" version first (to get counts)
  156. proc listacl {acl interface} {
  157.     global cprompt
  158.     global currentmode
  159.  
  160.     set err [entermode "enable" ""]
  161.     if { $err != "" } {
  162.         puts "listacl-failed -"
  163.         puts "$err"
  164.         puts "."
  165.         return
  166.     }
  167.  
  168.     set ip [acl2ip $acl]
  169.  
  170.     sendcmd "show $ip accounting access-list $acl interface $interface"
  171.  
  172.     set timeout 10
  173.     expect {
  174.         -re "Extended \[^\r\n]+ IP access list $acl on \[^\r\n]+\[\r\n]*\r\n" {
  175.             puts "listacl -"
  176.             expect {
  177.         -re "(Access List is not in sync with running config)\[\r\n]*\r\n" {
  178.                     set s "# "
  179.                     append s $expect_out(1,string)
  180.                     puts $s
  181.                     exp_continue
  182.                 }
  183.         -re "(List is not in sync with running config)\[\r\n]*\r\n" {
  184.                     set s "# "
  185.                     append s $expect_out(1,string)
  186.                     puts $s
  187.                     exp_continue
  188.                 }
  189.                 -re "(Total cam count \[^\r\n]+)\[\r\n]*\r\n" {
  190.                     set s "# "
  191.                     append s $expect_out(1,string)
  192.                     puts $s
  193.                     exp_continue
  194.                 }
  195.                 -re "^ *(\[^\r\n]*)\[\r\n]*\r\n" {
  196.                     printacl $expect_out(1,string)
  197.                     exp_continue
  198.                 }
  199.                 -re $cprompt {
  200.                     # This action must be last
  201.                     # done
  202.                 }
  203.             }
  204.             puts "."
  205.             return
  206.         }
  207.         -re "% Error: Access-list $acl does not exist\.\[\r\n]*\r\n" {
  208.             # Send back an empty list
  209.             expect -re $cprompt
  210.             puts "listacl"
  211.             return
  212.         }
  213.         -re "% Error: Access-list was not applied on this interface\.\[\r\n]*\r\n" {
  214.             # Fall back to config'd ACL (without counts)
  215.             expect -re $cprompt
  216.             listaclsimple $acl
  217.             return
  218.         }
  219.         "Connection closed by foreign host" {
  220.             puts "listacl-failed -"
  221.             puts "ssh session ended"
  222.             puts "."
  223.  
  224.             # exit so acld will start a fresh expect session
  225.             exit
  226.         }
  227.         -re $cprompt {
  228.             # Fall back to config'd ACL (without counts)
  229.             # This action must be last
  230.             listaclsimple $acl
  231.             return
  232.         }
  233.         timeout {
  234.             puts "listacl-failed -"
  235.             puts "timeout ($timeout seconds)"
  236.             puts "."
  237.  
  238.             # exit so acld will start a fresh expect session
  239.             exit
  240.         }
  241.     }
  242.     return
  243. }
  244.  
  245. # List both IPv4 and IPv6 routes
  246. # XXX there's no easy way to return an error if IPv4 are listed ok
  247. # but IPv6 routes fail
  248. proc listroute {} {
  249.     global currentmode
  250.  
  251.     set err [entermode "enable" ""]
  252.     if { $err != "" } {
  253.         puts "listroute-failed -"
  254.         puts "$err"
  255.         puts "."
  256.         return
  257.     }
  258.  
  259.     puts "listroute -"
  260.     _listroute ip
  261.     _listroute ipv6
  262.     puts "."
  263.     return
  264. }
  265.  
  266. proc login {addr cuser cpass1 cpass2 euser epass1 epass2} {
  267.     global spawn_id
  268.     global cprompt
  269.     global currentmode
  270.  
  271.     # Fire up subprocess
  272.     # We only use the enable username with the force 10
  273.     # Make swapping routers less painful: don't check the host key
  274.     set pid [spawn ssh "-2ax" "-o" "stricthostkeychecking=no" $euser@$addr]
  275.  
  276.     # Wait for connect
  277.     # If the router is busy it can take a long time for the initial connect
  278.     set timeout 60
  279.     expect {
  280.         eof {
  281.             # Don't hammer on router if we're misconfigured
  282.             catch {close} err
  283.             sleep 10
  284.             puts "login-failed -"
  285.             puts "child died (1)"
  286.             puts "."
  287.  
  288.             # exit so acld will start a fresh expect session
  289.             exit
  290.         }
  291.         timeout {
  292.             catch {close} err
  293.             puts "login-failed -"
  294.             puts "connect timeout ($timeout seconds)"
  295.             puts "."
  296.  
  297.             # exit so acld will start a fresh expect session
  298.             exit
  299.         }
  300.         -re "The authenticity of host\[^\r\n]*\[\r\n]*\r\n" {
  301.             exp_continue
  302.         }
  303.         "Are you sure you want to continue connecting \(yes/no\)\? " {
  304.             sendcmd "yes"
  305.             exp_continue
  306.         }
  307.         "assword: " {
  308.             # done
  309.         }
  310.         default {
  311.             catch {close} err
  312.             puts "login-failed -"
  313.             puts "unknown response: $expect_out(buffer)"
  314.             puts "."
  315.  
  316.             # exit so acld will start a fresh expect session
  317.             exit
  318.         }
  319.     }
  320.  
  321.     # We only use the enable passwords with the force 10
  322.     if { ![sendpass "enable" $epass1 $epass2] } {
  323.         catch {close} err
  324.         return
  325.     }
  326.  
  327.     sendcmd "terminal length 0"
  328.     expect -re $cprompt
  329.  
  330.     set currentmode "enable"
  331.     puts "login -"
  332.     puts "child ssh pid is $pid"
  333.     puts "."
  334.     return
  335. }
  336.  
  337. proc logout {} {
  338.     global currentmode
  339.  
  340.     set err [entermode "enable" ""]
  341.     if { $err != "" } {
  342.         puts "logout-failed -"
  343.         puts "$err"
  344.         puts "."
  345.  
  346.         # exit so acld will start a fresh expect session
  347.         exit
  348.     }
  349.  
  350.     sendcmd "exit"
  351.     set currentmode "logout"
  352.     set timeout 10
  353.     expect {
  354.         -re "Connection.*closed by.*host" {
  355.             catch {close} err
  356.             puts "logout"
  357.             return
  358.         }
  359.         timeout {
  360.             puts "logout-failed -"
  361.             puts "timeout ($timeout seconds)"
  362.             puts "."
  363.  
  364.             # exit so acld will start a fresh expect session
  365.             exit
  366.         }
  367.         default {
  368.             puts "logout-failed -"
  369.             puts "logout already logged out"
  370.             puts "."
  371.  
  372.             # exit so acld will start a fresh expect session
  373.             exit
  374.         }
  375.     }
  376.     return
  377. }
  378.  
  379. proc nonullzero {addr} {
  380.  
  381.     nullzerocmd "nonullzero" $addr "no"
  382.     return
  383. }
  384.  
  385. proc nullzero {addr} {
  386.  
  387.     nullzerocmd "nullzero" $addr {}
  388.     return
  389. }
  390.  
  391. proc permitudpdsthostport {addr port acl seq} {
  392.  
  393.     aclcmd "permitudpdsthostport" $acl $seq "" \
  394.         "permit udp any host $addr eq $port"
  395.     return
  396. }
  397.  
  398. proc permittcpdsthostport {addr port acl seq} {
  399.  
  400.     aclcmd "permittcpdsthostport" $acl $seq "" \
  401.         "permit tcp any host $addr eq $port"
  402.     return
  403. }
  404.  
  405. proc restore {addr acl seq} {
  406.  
  407.     aclcmd "restore" $acl $seq "no" ""
  408.     return
  409. }
  410.  
  411. proc restorehosthost {addr1 addr2 acl seq} {
  412.  
  413.     set ip [acl2ip $acl]
  414.  
  415.     aclcmd "restorehosthost" $acl $seq "no" \
  416.         "deny $ip host $addr1 host $addr2"
  417.     return
  418. }
  419.  
  420. proc restoreudpport {port acl seq} {
  421.  
  422.     aclcmd "restoreudpport" $acl $seq "no" "deny udp any any eq $port"
  423.     return
  424. }
  425.  
  426. proc restoretcpport {port acl seq} {
  427.  
  428.     aclcmd "restoretcpport" $acl $seq "no" "deny tcp any any eq $port"
  429.     return
  430. }
  431.  
  432. proc restoretcpdsthostport {addr port acl seq} {
  433.  
  434.     aclcmd "restoretcpdsthostport" $acl $seq "no" ""
  435.     return
  436. }
  437.  
  438. proc sync {} {
  439.     global cprompt
  440.     global currentmode
  441.     global currentacl
  442.  
  443.     # Save these so we can change back when we're done
  444.     set oldmode $currentmode
  445.     set oldacl $currentacl
  446.  
  447.     set err [entermode "enable" ""]
  448.     if { $err != "" } {
  449.         puts "sync-failed -"
  450.         puts "$err"
  451.         puts "."
  452.         return
  453.     }
  454.     sendcmd "copy running-config startup-config"
  455.     set timeout 10
  456.     expect {
  457.         # Try to be compatible with old/new versions of this dialogue...
  458.         #
  459.         #    "Proceed with copy"
  460.         #    "Proceed to copy the file"
  461.         #
  462.         -re "Proceed \[^\r\n]* copy\[^:\r\n]*:" {
  463.             # done
  464.         }
  465.         "Connection closed by foreign host" {
  466.             puts "sync-failed -"
  467.             puts "ssh session ended"
  468.             puts "."
  469.  
  470.             # exit so acld will start a fresh expect session
  471.             exit
  472.         }
  473.         -re $cprompt {
  474.             # This action must be last
  475.             puts "sync"
  476.             return
  477.         }
  478.         timeout {
  479.             puts "sync-failed -"
  480.             puts "confirm timeout ($timeout seconds)"
  481.             puts "exiting"
  482.             puts "."
  483.  
  484.             # exit so acld will start a fresh expect session
  485.             exit
  486.         }
  487.     }
  488.     sendcmd "yes"
  489.     set timeout 90
  490.     expect {
  491.         -re "(% Error: \[^\r\n]*)\[\r\n]*\r\n" {
  492.             puts "sync-failed -"
  493.             puts "$expect_out(1,string)"
  494.             puts "."
  495.  
  496.             expect -re $cprompt
  497.             return
  498.         }
  499.         -re "bytes successfully copied\[\r\n]*\r\n" {
  500.             # done
  501.             expect -re $cprompt
  502.         }
  503.         "Connection closed by foreign host" {
  504.             puts "sync-failed -"
  505.             puts "ssh session ended"
  506.             puts "."
  507.  
  508.             # exit so acld will start a fresh expect session
  509.             exit
  510.         }
  511.         timeout {
  512.             puts "sync-failed -"
  513.             puts "copy timeout ($timeout seconds)"
  514.             puts "exiting"
  515.             puts "."
  516.  
  517.             # exit so acld will start a fresh expect session
  518.             exit
  519.         }
  520.     }
  521.  
  522.     set err [entermode $oldmode $oldacl]
  523.     if { $err != "" } {
  524.         puts "sync-failed -"
  525.         puts "$err"
  526.         puts "."
  527.         return
  528.     }
  529.  
  530.     puts "sync"
  531.     return
  532. }
  533.  
  534. proc unpermitudpdsthostport {addr port acl seq} {
  535.  
  536.     aclcmd "unpermitudpdsthostport" $acl $seq "no" ""
  537.     return
  538. }
  539.  
  540. proc unpermittcpdsthostport {addr port acl seq} {
  541.  
  542.     aclcmd "unpermittcpdsthostport" $acl $seq "no" ""
  543.     return
  544. }
  545.  
  546. # Local procedures
  547.  
  548. # Returns "ip" or "ipv6" based on the acl name
  549. proc acl2ip {acl} {
  550.     global isipv6
  551.  
  552.     if [catch { set ipv6 [set isipv6($acl)] } err] {
  553.         set ipv6 0
  554.     }
  555.  
  556.     if { $ipv6 } {
  557.         set ip "ipv6"
  558.     } else {
  559.         set ip "ip"
  560.     }
  561.  
  562.     return $ip
  563. }
  564.  
  565. # aclcmd "restore" $acl $seq "no" "deny host $addr"
  566. proc aclcmd {what acl seq pre cmd} {
  567.     global cprompt
  568.     global lastseq
  569.  
  570.     if { $seq != 0 } {
  571.         if { $seq > $lastseq } {
  572.             puts "$what-failed -"
  573.             puts "sequence too large ($seq > $lastseq)"
  574.             puts "."
  575.             return
  576.         }
  577.         set cmd "seq $seq $cmd"
  578.     }
  579.     if { [string length $pre] > 0 } {
  580.         set cmd "$pre $cmd"
  581.     }
  582.  
  583.     set err [entermode "acl" $acl]
  584.     if { $err != "" } {
  585.         puts "$what-failed -"
  586.         puts "$err"
  587.         puts "."
  588.         return
  589.     }
  590.     sendcmd "$cmd"
  591.     set timeout 10
  592.     expect {
  593.         -re "(% Error: IPv6 Access-list not supported on this CAM profile\.)\[\r\n]*\r\n" {
  594.             puts "$what-failed -"
  595.             puts "$expect_out(1,string)"
  596.             puts "."
  597.             return
  598.         }
  599.         -re "(% Error: \[^\r\n]*)\[\r\n]*\r\n" {
  600.             puts "$what-failed -"
  601.             puts "$expect_out(1,string)"
  602.             puts "exiting"
  603.             puts "."
  604.  
  605.             # exit so acld will start a fresh expect session
  606.             exit
  607.         }
  608.         -re "^\[^\r\n]*\[\r\n]*\r\n" {
  609.             exp_continue
  610.         }
  611.         -re $cprompt {
  612.             # success
  613.             puts "$what"
  614.             return
  615.         }
  616.         timeout {
  617.             puts "$what-failed -"
  618.             puts "aclcmd: timeout ($timeout seconds)"
  619.             puts "."
  620.  
  621.             # exit so acld will start a fresh expect session
  622.             exit
  623.         }
  624.         "Connection closed by foreign host" {
  625.             puts "$what-failed -"
  626.             puts "ssh session ended"
  627.             puts "."
  628.  
  629.             # exit so acld will start a fresh expect session
  630.             exit
  631.         }
  632.         default {
  633.             puts "$what-failed -"
  634.             puts "aclcmd: unknown problem"
  635.             puts "."
  636.             return
  637.         }
  638.     }
  639.     return
  640. }
  641.  
  642. # Fall back to this version when the ACL is not active
  643. proc listaclsimple {acl} {
  644.     global cprompt
  645.  
  646.     set err [entermode "acl" $acl]
  647.     if { $err != "" } {
  648.         puts "listacl-failed -"
  649.         puts "$err"
  650.         puts "."
  651.         return
  652.     }
  653.  
  654.     set ip [acl2ip $acl]
  655.     if { $ip == "ip" } {
  656.         set expr "ip access-list extended $acl\[\r\n]*\r\n"
  657.     } else {
  658.         set expr "ipv6 access-list $acl\[\r\n]*\r\n"
  659.     }
  660.  
  661.     sendcmd "show config"
  662.     set timeout 10
  663.     expect {
  664.         -re $expr {
  665.             # done
  666.         }
  667.         -re $cprompt {
  668.             # This action must be last
  669.             puts "listacl"
  670.             return
  671.         }
  672.         timeout {
  673.             puts "listacl-failed -"
  674.             puts "listaclsimple: timeout 1 ($timeout seconds)"
  675.             puts "."
  676.  
  677.             # exit so acld will start a fresh expect session
  678.             exit
  679.         }
  680.     }
  681.  
  682.     # List 'em out
  683.     puts "listacl -"
  684.     expect {
  685.         -re "^ *(\[^\r\n]*)\[\r\n]*\r\n" {
  686.             printacl $expect_out(1,string)
  687.             exp_continue
  688.         }
  689.         -re $cprompt {
  690.             # This action must be last
  691.             # done
  692.         }
  693.         timeout {
  694.             # We've already declared success
  695.             puts "listaclsimple: timeout 2 ($timeout seconds)"
  696.             puts "."
  697.  
  698.             # exit so acld will start a fresh expect session
  699.             exit
  700.         }
  701.     }
  702.     puts "."
  703.     return
  704. }
  705.  
  706. proc _listroute {ip} {
  707.     global cprompt
  708.  
  709.     sendcmd "show $ip route"
  710.     set timeout 10
  711.     expect {
  712.         -re " ----+\[\r\n]+" {
  713.             # ok (IPv4 or IPv6)
  714.         }
  715.         -re $cprompt {
  716.             # This action must be last
  717.             puts "# couldn't find routes"
  718.             return
  719.         }
  720.         timeout {
  721.             puts "# timeout ($timeout seconds)"
  722.             puts "."
  723.  
  724.             # exit so acld will start a fresh expect session
  725.             exit
  726.         }
  727.     }
  728.     set what ""
  729.     set src ""
  730.     set dst ""
  731.     expect {
  732.         -re "^ .(\[BCORS]) .. (\[^ \r\n]+) *via (\[^, \r\n]+).*\[\r\n]*\r\n" {
  733.             # IPv4
  734.             set what $expect_out(1,string)
  735.             set src $expect_out(2,string)
  736.             set dst $expect_out(3,string)
  737.             printroute $what $src $dst
  738.             exp_continue
  739.         }
  740.         -re "^ .(\[BCORS]) .. (\[^ \r\n]+) *Direct, (\[^ \r\n]+ \[^ \r\n]+) .*\[\r\n]*\r\n" {
  741.             # IPv4
  742.             set what $expect_out(1,string)
  743.             set src $expect_out(2,string)
  744.             set dst $expect_out(3,string)
  745.             printroute $what $src $dst
  746.             exp_continue
  747.         }
  748.         -re "^ .(\[BCORSL]) .. (\[^ \r\n]+) *\[[]\[0-9]+/\[0-9]+]\[\r\n]*\r\n" {
  749.             # IPv6: first line
  750.             set what $expect_out(1,string)
  751.             set src $expect_out(2,string)
  752.             exp_continue
  753.         }
  754.         -re "^ +via (\[^,\r\n]+), .*\[\r\n]*\r\n" {
  755.             set dst $expect_out(1,string)
  756.             printroute $what $src $dst
  757.             exp_continue
  758.         }
  759.         -re "^ +Direct, (\[^,\r\n]+), .*\[\r\n]*\r\n" {
  760.             # IPv6: second line
  761.             set dst $expect_out(1,string)
  762.             printroute $what $src $dst
  763.             exp_continue
  764.         }
  765.         -re "^\[\r\n]*\r\n" {
  766.             # eat blank lines
  767.             exp_continue
  768.         }
  769.         -re "^(.*)\[\r\n]*\r\n" {
  770.             # Echo stuff we don't understand
  771.             puts "# unknown $expect_out(1,string)"
  772.             exp_continue
  773.         }
  774.         -re $cprompt {
  775.             # This action must be last
  776.             # done
  777.         }
  778.         timeout {
  779.             puts "# timeout ($timeout seconds)"
  780.             puts "."
  781.  
  782.             # exit so acld will start a fresh expect session
  783.             exit
  784.         }
  785.     }
  786.     return
  787. }
  788.  
  789. proc printacl {str} {
  790.  
  791.     # Break into a list
  792.     set av [split $str " "]
  793.     set an [llength $av]
  794.  
  795.     # XXX gag me: remove empty elements (caused by multiple blanks)
  796.     set n 0
  797.     while { $n < $an } {
  798.         if { [lindex $av $n] == "" } {
  799.             set av [concat [lrange $av 0 [expr $n - 1]] \
  800.                 [lrange $av [expr $n + 1] [expr $an - 1]]]
  801.             set an [expr $an - 1]
  802.         } else {
  803.             incr n
  804.         }
  805.     }
  806.  
  807.     if { $an < 3 } {
  808.         error "# printacl: not enough args \"$str\""
  809.     }
  810.  
  811.     if { [lindex $av 0] == "seq" } {
  812.         set seq [lindex $av 1]
  813.     } else {
  814.         set seq 0
  815.     }
  816.  
  817.     if { [lindex $av [expr $an - 3]] == "count" } {
  818.         set count [lindex $av [expr $an - 2]]
  819.         set count [string trimleft $count "("]
  820.  
  821.         # Remove from argument vector
  822.         set an [expr $an - 3]
  823.         set av [lrange $av 0 $an]
  824.     } else {
  825.         set count 0
  826.     }
  827.  
  828.     set which [lindex $av 2]
  829.     if { $which == "deny" } {
  830.         set which "block"
  831.     } elseif { $which != "permit" } {
  832.         error "# printacl: can't find permit/deny \"$av\""
  833.     }
  834.  
  835.     set proto [lindex $av 3]
  836.     set what "unknown # $str"
  837.     set append ""
  838.     if { $proto == "ip" || $proto == "icmp" } {
  839.         if { $proto == "icmp" } {
  840.             set protostr $proto
  841.         } else {
  842.             set protostr ""
  843.         }
  844.         if { $an == 7 &&
  845.             [lindex $av 4] == "host" &&
  846.             [lindex $av 6] == "any" } {
  847.             # "permithost/blockhost"
  848.             # seq 123 deny ip host 10.0.0.1 any
  849.             # 0   1   2    3  4    5        6
  850.             set what "[set which][set protostr]host"
  851.             append append [lindex $av 5]
  852.         } elseif { $an == 8 &&
  853.             [lindex $av 4] == "host" &&
  854.             [lindex $av 6] == "host" } {
  855.             # "permithosthost/blockhosthost"
  856.             # seq 666 deny ip host 1.2.3.4 host 4.5.6.7
  857.             # 0   1   2    3  4    5       6    7
  858.             set what "[set which][set protostr]hosthost"
  859.             append append "[lindex $av 5] [lindex $av 7]"
  860.         } elseif { $an == 6 &&
  861.             [string first "/" [lindex $av 4]] > 1 &&
  862.             [lindex $av 5] == "any" } {
  863.             # "permitnet/blocknet"
  864.             # "show ip accounting" style
  865.             #    (net and mask width one word)
  866.             # seq 123 permit ip 10.0.0.0/24 any
  867.             # 0   1   2      3  4           5
  868.             set what "[set which][set protostr]net"
  869.             append append [lindex $av 4]
  870.         } elseif { $an == 7 &&
  871.             [string first "/" [lindex $av 5]] == 0 &&
  872.             [lindex $av 6] == "any" } {
  873.             # "permitnet/blocknet"
  874.             # "show config" style
  875.             #    (blank between net and mask width)
  876.             # seq 123 permit ip 10.0.0.0 /24 any
  877.             # 0   1   2      3  4        5   6
  878.             set what "[set which][set protostr]net"
  879.             append append "[lindex $av 4][lindex $av 5]"
  880.         } elseif { $an == 6 &&
  881.             [lindex $av 4] == "any" &&
  882.             [string first "/" [lindex $av 5]] > 1 } {
  883.             # "permitdstnet/blockdstnet"
  884.             # "show ip accounting" style
  885.             #    (net and mask width one word)
  886.             # seq 123 permit ip any 10.0.0.0/24
  887.             # 0   1   2      3  4   5
  888.             set what "[set which][set protostr]dstnet"
  889.             append append [lindex $av 5]
  890.         } elseif { $an == 7 &&
  891.             [lindex $av 4] == "any" &&
  892.             [string first "/" [lindex $av 6]] == 0 } {
  893.             # "permitdstnet/blockdstnet"
  894.             # "show config" style
  895.             #    (blank between net and mask width)
  896.             # seq 123 permit ip any 10.0.0.0 /24
  897.             # 0   1   2      3  4   5        6
  898.             set what "[set which][set protostr]dstnet"
  899.             append append "[lindex $av 5][lindex $av 6]"
  900.         } elseif { $an == 6 && [lrange $av 4 5] == "any any" } {
  901.             # "permitany/blockany"
  902.             # seq 123 permit ip any any
  903.             # 0   1   2      3  4   5
  904.             set what "[set which][set protostr]any"
  905.         } elseif { $an == 7 &&
  906.             [string first "/" [lindex $av 4]] > 0 &&
  907.             [lindex $av 5] == "host" } {
  908.             # "permitnethost/blocknethost"
  909.             # "permiticmpnethost/blockicmpnethost"
  910.             # "show ip accounting" style
  911.             #    (net and mask width one word)
  912.             # XXX do we still need the split version?
  913.             # seq 6 permit ip 10.0.0.0/22 host 10.1.2.3
  914.             # 0   1 2      3  4           5    6
  915.  
  916.             set what "$which[set protostr]nethost"
  917.             append append "[lindex $av 4] [lindex $av 6]"
  918.         } elseif { $an == 7 &&
  919.             [lrange $av 4 5] == "any host" } {
  920.             # "permiticmpdsthost/blockicmpdsthost"
  921.             # seq 123 permit icmp any host 10.0.0.1
  922.             # 0   1   2      3    4   5    6
  923.             set what "$which[set protostr]dsthost"
  924.             append append "[lindex $av 6]"
  925.         }
  926.     } elseif { $proto == "udp" || $proto == "tcp" } {
  927.         if { $an == 8 &&
  928.             [lrange $av 4 6] == "any any eq" } {
  929.             # "permitudpport/blockudpport"
  930.             # "permittcpport/blocktcpport"
  931.             # seq 123 deny udp any any eq 666
  932.             # 0   1   2    3   4   5   6  7
  933.             set what "$which[set proto]port"
  934.             append append [lindex $av 7]
  935.         } elseif { $an == 9 &&
  936.             [lindex $av 4] == "host" &&
  937.             [lrange $av 6 7] == "any eq" } {
  938.             # "permitudphostport/blockudphostport"
  939.             # "permittcphostport/blocktcphostport"
  940.             # seq 123 permit udp host 10.0.0.1 any eq 666
  941.             # 0   1   2      3   4    5        6   7  8
  942.             set what "$which[set proto]hostport"
  943.             append append "[lindex $av 5] [lindex $av 8]"
  944.         } elseif { $an == 9 &&
  945.             [lrange $av 4 5] == "any host" &&
  946.             [lindex $av 7] == "eq" } {
  947.             # "permitudpdsthostport/blockudpdsthostport"
  948.             # "permittcpdsthostport/blocktcpdsthostport"
  949.             # seq 123 permit udp any host 10.0.0.1 eq 666
  950.             # 0   1   2      3   4   5    6        7  8
  951.  
  952.             set what "$which[set proto]dsthostport"
  953.             append append "[lindex $av 6] [lindex $av 8]"
  954.         } elseif { $an == 7 &&
  955.             [string first "/" [lindex $av 5]] > 0 &&
  956.             [lindex $av 4] == "any" &&
  957.             [lindex $av 6] == "eq" } {
  958.             # "permitudpdsthostport/blockudpdsthostport"
  959.             # "permittcpdsthostport/blocktcpdsthostport"
  960.             # seq 500 permit tcp any 131.243.1.6/31 eq 22
  961.             # 0   1   2      3   4   5              6  7
  962.  
  963.             set what "$which[set proto]dstnetport"
  964.             append append "[lindex $av 5] [lindex $av 7]"
  965.         } elseif { $an == 8 &&
  966.             [string first "/" [lindex $av 4]] > 0 &&
  967.             [lrange $av 5 6] == "any eq" } {
  968.             # "permitudpnetport/blockudpnetport"
  969.             # "permittcpnetport/blocktcpnetport"
  970.             # "show ip accounting" style
  971.             #     (net and mask width one word)
  972.             # seq 123 permit udp 10.0.0.0/8 any eq 666
  973.             # 0   1   2      3   4          5   6  7
  974.             set what "$which[set proto]netport"
  975.             append append "[lindex $av 4] [lindex $av 7]"
  976.         } elseif { $an == 9 &&
  977.             [string first "/" [lindex $av 5]] == 0 &&
  978.             [lrange $av 6 7] == "any eq" } {
  979.             # "permitudpnetport/blockudpnetport"
  980.             # "permittcpnetport/blocktcpnetport"
  981.             # "show config" style
  982.             #     (blank between net and mask width)
  983.             # seq 123 permit udp 10.0.0.0 /8 any eq 666
  984.             # 0   1   2      3   4        5  6   7  8
  985.             set what "$which[set proto]netport"
  986.             append append "[lindex $av 4][lindex $av 5]"
  987.             append append " [lindex $av 8]"
  988.         } elseif { $an == 9 &&
  989.             [lindex $av 4] == "host" &&
  990.             [lindex $av 6] == "eq" &&
  991.             [lindex $av 8] == "any" } {
  992.             # "permitudphostsrcport/blockudphostsrcport"
  993.             # "permittcphostsrcport/blocktcphostsrcport"
  994.             # seq 123 permit udp host 10.0.0.1 eq 666 any
  995.             # 0   1   2      3   4    5        6  7   8
  996.  
  997.             set what "$which[set proto]hostsrcport"
  998.             append append "[lindex $av 5] [lindex $av 7]"
  999.         } elseif { $an == 10 &&
  1000.             [lindex $av 4] == "host" &&
  1001.             [lindex $av 6] == "host" &&
  1002.             [lindex $av 8] == "eq" } {
  1003.             # "permitudphostpairdstport/blockudphostpairdstport"
  1004.             # "permittcphostpairdstport/blocktcphostpairdstport"
  1005.             # seq 123 permit udp host 10.0.0.1 host 10.0.0.2 eq 666
  1006.             # 0   1   2      3   4    5        6    7        8  9
  1007.  
  1008.             set what "$which[set proto]hostpairdstport"
  1009.             append append "[lindex $av 5] [lindex $av 7]"
  1010.             append append " [lindex $av 9]"
  1011.         } elseif { $an == 9 &&
  1012.             [string first "/" [lindex $av 4]] > 0 &&
  1013.             [lindex $av 5] == "host" &&
  1014.             [lindex $av 7] == "eq" } {
  1015.             # "permitudpnethostport/blockudpnethostport"
  1016.             # "permittcpnethostport/blocktcpnethostport"
  1017.             # "show ip accounting" style
  1018.             #    (net and mask width one word)
  1019.             # XXX do we still need the split version?
  1020.             # seq 7 permit tcp 10.0.0.0/22 host 10.3.4.5 eq 5
  1021.             # 0   1 2      3   4           5    6        7  8
  1022.  
  1023.             set what "$which[set proto]nethostport"
  1024.             append append "[lindex $av 4] [lindex $av 6]"
  1025.             append append " [lindex $av 8]"
  1026.         } elseif { $an == 6 &&
  1027.             [string first "/" [lindex $av 5]] > 0 &&
  1028.             [lindex $av 4] == "any" } {
  1029.             # "permitudpdstnet/blockudpdstnet"
  1030.             # "permittcpdstnet/blocktcpdstnet"
  1031.             # "show ip accounting" style
  1032.             #    (net and mask width one word)
  1033.             # XXX2 do we still need the split version?
  1034.             # seq 4 deny tcp any 10.1.0.0/30
  1035.             # 0   1 2    3   4   5
  1036.  
  1037.             set what "$which[set proto]dstnet"
  1038.             append append "[lindex $av 5]"
  1039.         } elseif { $an == 7 &&
  1040.             [lrange $av 4 5] == "any host" } {
  1041.             # "permitudpdsthost/blockudpdsthost"
  1042.             # seq 123 permit udp any host 10.0.0.1
  1043.             # 0   1   2      3   4   5    6
  1044.  
  1045.             set what "$which[set proto]dsthost"
  1046.             append append "[lindex $av 6]"
  1047.         }
  1048.     }
  1049.  
  1050.     set msg "$seq $count $what"
  1051.     if { $append != "" } {
  1052.         append msg " $append"
  1053.     }
  1054.     puts $msg
  1055.     return
  1056. }
  1057.  
  1058. proc printroute {what src dst} {
  1059.  
  1060.     # Strip trailing /32 or /128 from host routes
  1061.     if { [string first "." $src] > 0 } {
  1062.         set w "32"
  1063.     } else {
  1064.         set w "128"
  1065.     }
  1066.     set i [string first "/$w" $src]
  1067.     if { $i > 1 } {
  1068.         set src [string range $src 0 [expr $i - 1]]
  1069.     }
  1070.  
  1071.     if { $what == "C" } {
  1072.         # interface
  1073.         puts "I $src"
  1074.     } elseif { $what == "O" || $what == "B" || $what == "R" } {
  1075.         # dynamic
  1076.         puts "D $src $dst"
  1077.     } elseif { $what == "S" || $what == "L" } {
  1078.         if { $dst == "Nu 0" } {
  1079.             # null zero
  1080.             puts "N $src"
  1081.         } else {
  1082.             # static
  1083.             puts "S $src $dst"
  1084.         }
  1085.     } else {
  1086.         puts "# unknown $what $src $dst"
  1087.     }
  1088.     return
  1089. }
  1090.  
  1091. proc nullzerocmd {what addr pre} {
  1092.     global cprompt
  1093.  
  1094.     set err [entermode "config" {}]
  1095.     if { $err != "" } {
  1096.         puts "$what-failed -"
  1097.         puts "$err"
  1098.         puts "."
  1099.         return
  1100.     }
  1101.  
  1102.     if { [string first "." $addr] > 0 } {
  1103.         set ip "ip"
  1104.         set w "32"
  1105.     } else {
  1106.         set ip "ipv6"
  1107.         set w "128"
  1108.     }
  1109.  
  1110.     if { [string first "/" $addr] > 0 } {
  1111.         # Network with mask
  1112.         set cmd "$ip route $addr null 0"
  1113.     } else {
  1114.         # Host address
  1115.         set cmd "$ip route $addr/$w null 0"
  1116.     }
  1117.     if { $pre != "" } {
  1118.         set cmd "$pre $cmd"
  1119.     }
  1120.     sendcmd $cmd
  1121.     set timeout 10
  1122.     expect {
  1123.         -re "(% Error: \[^\r\n]*)\[\r\n]*\r\n" {
  1124.             puts "$what-failed -"
  1125.             puts "$expect_out(1,string)"
  1126.             puts "exiting"
  1127.             puts "."
  1128.  
  1129.             # exit so acld will start a fresh expect session
  1130.             exit
  1131.         }
  1132.         -re "^\[^\r\n]*\[\r\n]*\r\n" {
  1133.             exp_continue
  1134.         }
  1135.         "Connection closed by foreign host" {
  1136.             puts "$what-failed -"
  1137.             puts "ssh session ended"
  1138.             puts "."
  1139.  
  1140.             # exit so acld will start a fresh expect session
  1141.             exit
  1142.         }
  1143.         -re $cprompt {
  1144.             # This action must be last
  1145.             # success
  1146.             puts "$what"
  1147.             return
  1148.         }
  1149.         timeout {
  1150.             puts "$what-failed -"
  1151.             puts "nullzerocmd: timeout ($timeout seconds)"
  1152.             puts "."
  1153.  
  1154.             # exit so acld will start a fresh expect session
  1155.             exit
  1156.         }
  1157.         default {
  1158.             puts "$what-failed -"
  1159.             puts "nullzerocmd: unknown problem"
  1160.             puts "."
  1161.             return
  1162.         }
  1163.     }
  1164.     return
  1165. }
  1166.  
  1167. proc sendcmd {cmd} {
  1168.     global spawn_id
  1169.  
  1170.     if [catch {send -- "$cmd\r"} err] {
  1171.         puts "sendcmd: $err"
  1172.         # exit so acld will start a fresh expect session
  1173.         exit
  1174.     }
  1175.     return
  1176. }
  1177.  
  1178. proc sendpass {what pass1 pass2} {
  1179.     global spawn_id
  1180.     global cprompt
  1181.  
  1182.     # Send first password
  1183.     sendcmd "$pass1"
  1184.  
  1185.     set timeout 10
  1186.     set which "first"
  1187.     expect {
  1188.         "Received disconnect" {
  1189.             puts "login-failed -"
  1190.             puts "all passwords failed"
  1191.             puts "."
  1192.             return 0
  1193.         }
  1194.         "password: " {
  1195.             if { $which == "second" } {
  1196.                 puts "login-failed -"
  1197.                 puts "first and second $what passwords failed"
  1198.                 puts "."
  1199.                 return 0
  1200.             }
  1201.  
  1202.             # Maybe we were only given one password
  1203.             if { $pass2 == "" } {
  1204.                 puts "login-failed -"
  1205.                 puts "$what password failed"
  1206.                 puts "."
  1207.                 return 0
  1208.             }
  1209.  
  1210.             # Send second password
  1211.             sendcmd "$pass2"
  1212.             set which "second"
  1213.             exp_continue
  1214.         }
  1215.         -re "^Permission denied.*\[\r\n]*\r\n" {
  1216.             exp_continue
  1217.         }
  1218.  
  1219.         -re $cprompt {
  1220.             # This action must be last
  1221.             # done
  1222.         }
  1223.         timeout {
  1224.             puts "login-failed -"
  1225.             puts "sendpass: timeout ($timeout seconds)"
  1226.             puts "."
  1227.             return 0
  1228.         }
  1229.     }
  1230.     return 1
  1231. }
  1232.  
  1233. #
  1234. # Modes we currently know about:
  1235. #
  1236. #    enable
  1237. #    config
  1238. #    acl
  1239. #
  1240. # This table tells entermode how to get from one mode to another
  1241. #
  1242. set modemoves(enable,enable)    { }
  1243. set modemoves(enable,config)    { "config" }
  1244. set modemoves(enable,acl)    { "config" "acl" }
  1245.  
  1246. set modemoves(config,enable)    { "exit" }
  1247. set modemoves(config,config)    { }
  1248. set modemoves(config,acl)    { "acl" }
  1249.  
  1250. set modemoves(acl,enable)    { "exit" "exit" }
  1251. set modemoves(acl,config)    { "exit" }
  1252. set modemoves(acl,acl)        { "acl" }
  1253.  
  1254. proc entermode {mode acl} {
  1255.     global cprompt
  1256.     global currentmode
  1257.     global currentacl
  1258.     global modemoves
  1259.  
  1260.     set timeout 10
  1261.     if [catch {set moves $modemoves($currentmode,$mode)} err] {
  1262.         puts "entermode: $err"
  1263.  
  1264.         # exit so acld will start a fresh expect session
  1265.         exit
  1266.     }
  1267.     set n [llength $moves]
  1268.     if { $n == 0 } {
  1269.         return ""
  1270.     }
  1271.  
  1272.     set ip [acl2ip $acl]
  1273.  
  1274.     set errstr ""
  1275.     for { set i 0 } { $i < $n } { incr i } {
  1276.         set move [lindex $moves $i]
  1277.         if { $move != "acl" } {
  1278.             set cmd "$move"
  1279.         } elseif { $currentmode != "acl" || $currentacl != $acl } {
  1280.             if { $ip == "ip" } {
  1281.                 set cmd "ip access-list extended $acl"
  1282.             } else {
  1283.                 set cmd "ipv6 access-list $acl"
  1284.             }
  1285.         } else {
  1286.             # already there
  1287.             continue
  1288.         }
  1289.  
  1290.         sendcmd "$cmd"
  1291.         expect {
  1292.             -re "(% Warning: The following users are currently configuring the system:)\[\r\n]*\r\n" {
  1293.                 # Ignore
  1294.                 exp_continue
  1295.             }
  1296.             -re "(User \"\[^\"\r\n]*\" on \[^\r\n]*)\[\r\n]*\r\n" {
  1297.                 # Ignore
  1298.                 exp_continue
  1299.             }
  1300.             -re "(% Error: Access-list was not applied on this interface\.)\[\r\n]*\r\n" {
  1301.                 set errstr "$expect_out(1,string)"
  1302.                 if { $currentmode != "enable" } {
  1303.                     set currentmode "config"
  1304.                 }
  1305.                 exp_continue
  1306.             }
  1307.             -re "(% Error: IPv6 Access-list not supported on this CAM profile\.)\[\r\n]*\r\n" {
  1308.                 set errstr "$expect_out(1,string)"
  1309.                 set currentmode "config"
  1310.                 exp_continue
  1311.             }
  1312.             -re "(% Error: \[^\r\n]*)\[\r\n]*\r\n" {
  1313.                 set errstr "$expect_out(1,string)"
  1314.                 exp_continue
  1315.             }
  1316.             "Connection closed by foreign host" {
  1317.                 puts "entermode: ssh session ended"
  1318.  
  1319.                 # exit so acld will start a fresh expect session
  1320.                 exit
  1321.             }
  1322.             -re $cprompt {
  1323.                 # This action must be last
  1324.                 # done
  1325.             }
  1326.             timeout {
  1327.                 puts "entermode: timeout ($timeout seconds)"
  1328.  
  1329.                 # exit so acld will start a fresh expect session
  1330.                 exit
  1331.             }
  1332.         }
  1333.  
  1334.         # Bail at the first sign of trouble
  1335.         if { $errstr != "" } {
  1336.             break
  1337.         }
  1338.  
  1339.         # Keep track of current mode in case we're making more than one
  1340.         if { $move == "config" } {
  1341.             set currentmode $move
  1342.         } else {
  1343.             set currentmode $mode
  1344.         }
  1345.         if { $move == "acl" } {
  1346.             set currentacl $acl
  1347.         }
  1348.     }
  1349.     return $errstr
  1350. }
  1351.  
  1352. # These are required to be at the end
  1353. log_user 0
  1354. interpreter
  1355.