python

Show version and haiku, но не только: ищем все скрытые команды Junos

  • четверг, 26 февраля 2015 г. в 02:11:29
http://habrahabr.ru/post/251245/

Всем привет!

Эта статья — о скрытых командах операционной системы Junos. Тем, кто работал с оборудованием Juniper под управлением ОС Junos (сюда относятся серии MX, SRX, EX, QFX, T, J, и многие другие) наверняка известно, что кроме «официальных» (документированных) команд в системе имеются и недокументированные. Их особенность в том, что они не видны в интерфейсе командной строки по контекстной подсказке (это когда вводишь вопросительный знак) и для них не работает автозаполнение, то есть команду нужно знать и ввести полностью (все буквы). Видимо, самая известная (и самая бесполезная) из таких команд — show version and haiku, выдающая «японское» трехстишие о жизни сетевых инженеров.

Скрытых команд, на самом деле, много. Вендор нигде не приводит их полного списка, но, например, на официальном форуме Juniper имеется прилепленный топик с довольно большим набором. Так что производитель не возражает против использования нами таких команд, просто на них не дается никакой гарантии — может работать, а может и положить ваше оборудование.

В этой статье я расскажу о том, как можно получить список всех скрытых команд Junos операционного режима, в пределах какой-то начальной ветки. Метод основан на довольно простом наблюдении, но гуглением мне не удалось найти свидетельств того, что вопрос раньше ставился в такой форме. Пример скрипта на Python прилагается.



Идея


Основная идея подхода очень проста, но чтобы ее уяснить, все-таки хорошо иметь доступ к CLI какого-либо Junos-устройства.

Рассмотрим, например, команду «show version». Если мы вводим «show version a» (в конце — всегда жмем Enter), то вывод команды — такой:
lab@jsrxA-1> show version a
                           ^
syntax error.

А если «show version с», то
lab@jsrxA-1> show version c   
                          ^
syntax error, expecting <command>.

В первом случае имеется скрытое продолжение (and haiku), во втором — нет. Как видно, реакция CLI при наличии скрытого продолжения отличается двумя аспектами:
  • «syntax error.» вместо «syntax error, expecting ‹command›.»;
  • шляпка (a.k.a. циркумфлекс) стоит под следующей позицией строки.

Соответственно, перебирая буквы одну за другой (хоть и вручную, но лучше автоматизировать), мы можем найти спрятанные команды — Junos сам их подсказывает, хоть и не так явно, как с обычными командами!

Предварительные замечания


Прежде чем писать скрипт, я должен предупредить читателей, что скрытые команды были скрыты разработчиком не просто так. Некоторые из них могут нарушить работу устройства, повредить файловую систему, и т.п. Поэтому их, даже по одной, следует использовать с большой осторожностью. В нашем случае, когда делается поиск перебором, это предостережение возводится в такую степень, что ни в коем случае не следует запускать подобный скрипт на обрабатывающем пользовательский трафик оборудовании. Ведь мы перебираем все команды, среди которых могут содержаться и file delete, и request system zeroize, и restart routing, и еще много чего. Так что играйтесь только с неподключенными к сети железками, которые не жалко убить, а лучше с виртуальным SRX (a.k.a. Firefly Perimeter).

Также следует пояснить, что хотя у Junos имеется очень удобный и продвинутый, основанный на XML, API, его использование для данной задачи не представляется возможным, так как наш подход поиска команд основан на особенностях работы CLI. Поэтому будем открывать обычную telnet-сессию, давать команды и парсить текстовый вывод.

В данной статье я ограничусь поиском команд операционного режима. Есть еще конфигурация, и в ней тоже много всего интересного припрятано (тот же commit full). Поиск скрытых команд там можно проводить аналогично.

Алгоритм


Итак, начиная с определенной команды (commandStart в скрипте) мы будем обходить все возможные варианты команд, добавляя каждый раз по символу (из массива alphabet) и вводя Enter. Вывод, присылаемый Junos в ответ, может быть следующим:
  1. Ругань про «syntax error.» (и при этом шляпка указывает на наличие продолжения команды) — признак наличия hidden-команды, перебираем дальше, добавляя новые символы.
  2. Ругань про «syntax error, expecting ‹command›.» —
    здесь необходимо анализировать положение шляпки. Если она на текущей букве, как выше в примере «show version c», то далее не идем, скрытых команд нет.
    Если же она указывает на продолжение команды, как здесь:
    lab@jsrxA-1> show version and 
                                  ^
    syntax error, expecting <command>.
    

    то в этом случае команда имеет продолжение и необходимо перебирать дальше (команда тут может быть скрытой или нет, в зависимости от предыстории).
  3. Просто вывод команды, без ругани про синтаксические ошибки, но возможно с руганью про двусмысленный (ambiguous) ввод, например как здесь:
    lab@jlab-Firefly-3> show chassis cluster i
                                              ^
    'i' is ambiguous.
    Possible completions:
      interfaces           Display chassis cluster interfaces
      ip-monitoring        Display IP monitoring related information
    

    В этом случае перебор необходимо продолжать, т.к. далее может содержаться скрытая команда (в данном случае, show chassis cluster information).

Также необходимо учитывать, что выводы некоторых команд могут занимать несколько экранов, что приводит к выдаче приглашения "---(more)---". В этом случае просто шлем пробел.

Скрипт


Собственно, вот он.
Скрипт (Python 3)
import telnetlib
import re

HOST = "192.168.65.161"
user = "lab"
password = "lab123"
commandStart = "show version "     # note space at the end

alphabet = "abcdefghijklmnopqrstuvwxyz-1234567890."
PAUSE = 3 

def SearchCommands (cmd, on_hidden_now=False):
    for nChar in range(0, len(alphabet)):
        char = str(alphabet[nChar])
        tn.write(cmd.encode('ascii') + char.encode('ascii') + b"\n")
    
        totData=""
        finished = False
        while (not finished):
            inpData = tn.read_until(prompt.encode('ascii'), PAUSE)
            totData = totData + inpData.decode('ascii')
            if "---(more" in inpData.decode('ascii'): 
                tn.write(b" ")
            else:
                finished = True

        cmdNext = cmd + str(char)
        synt_error_exp_cmd = False
        synt_error_period = False
        if "syntax error, expecting <command>." in totData:
            synt_error_exp_cmd = True
        if "syntax error." in totData:
            synt_error_period = True
        
        if not (synt_error_exp_cmd or synt_error_period):  # normal output or ambiguity
            if on_hidden_now:
                print("hidden command >> " + cmdNext)
            else:
                SearchCommands(cmdNext, on_hidden_now) # i.e. False
        else:
            l = re.findall(' *\^', totData)
            lenToHat = len(l[len(l)-1])            
            if synt_error_period:                         
                if lenToHat > lenPrompt + len(cmdNext):
                    SearchCommands(cmdNext, True)        # Hidden command in progress
            if synt_error_exp_cmd:
                if (lenToHat == 2 + lenPrompt + len(cmdNext)): 
                    if on_hidden_now:
                        print("hidden command >> " + cmdNext + "  (incomplete)")
                    # else: print("Entering: " + cmdNext)
                    SearchCommands(cmdNext+" ", on_hidden_now)
                if lenToHat > 2 + lenPrompt + len(cmdNext):  
                    SearchCommands(cmdNext, on_hidden_now)                            

tn = telnetlib.Telnet(HOST)
tn.read_until(b"login: ")
tn.write(user.encode('ascii') + b"\n")
tn.read_until(b"Password:")
tn.write(password.encode('ascii') + b"\n")

loginText = tn.read_until(b"> ").decode('ascii')
prompt = re.search(".*@.*", loginText).group()
print("Working with prompt = " + prompt)
lenPrompt = len(prompt)
SearchCommands(commandStart)



Примеры работы:
Запуск для ветки show version
hidden command >> show version and (incomplete)
hidden command >> show version and blame
hidden command >> show version and haiku
hidden command >> show version extensive
hidden command >> show version forwarding-context
hidden command >> show version invoke-on (incomplete)
hidden command >> show version invoke-on a
hidden command >> show version invoke-on o
hidden command >> show version no-forwarding
hidden command >> show version scc-dont-forward
hidden command >> show version sdk

Запуск для ветки show chassis
hidden command >> show chassis accurate-statistics
hidden command >> show chassis beacon
hidden command >> show chassis broadcom
hidden command >> show chassis cfeb
hidden command >> show chassis cip
hidden command >> show chassis clocks
hidden command >> show chassis cluster ethernet-switching (incomplete)
hidden command >> show chassis cluster information
hidden command >> show chassis cluster ip-monitoring (incomplete)
hidden command >> show chassis craft-interface
hidden command >> show chassis customer-id
hidden command >> show chassis ethernet-switch
hidden command >> show chassis fabric (incomplete)
hidden command >> show chassis fchip
hidden command >> show chassis feb
hidden command >> show chassis fpc-feb-connectivity
hidden command >> show chassis hsl (incomplete)
hidden command >> show chassis hsr
hidden command >> show chassis hss (incomplete)
hidden command >> show chassis hst
hidden command >> show chassis in-service-upgrade
hidden command >> show chassis ioc-npc-connectivity
hidden command >> show chassis lccs
hidden command >> show chassis message-statistics (incomplete)
hidden command >> show chassis message-statistics i
hidden command >> show chassis network-services
hidden command >> show chassis nonstop-upgrade
hidden command >> show chassis power-budget-statistics
hidden command >> show chassis psd
hidden command >> show chassis redundancy (incomplete)
hidden command >> show chassis redundant-power-system
hidden command >> show chassis scb
hidden command >> show chassis sfm
hidden command >> show chassis sibs
hidden command >> show chassis spmb
hidden command >> show chassis ssb
hidden command >> show chassis synchronization
hidden command >> show chassis tfeb
hidden command >> show chassis timers
hidden command >> show chassis usb (incomplete)
hidden command >> show chassis zones

Запуск для ветки show security idp (на SRX240)
hidden command >> show security idp active-policy
hidden command >> show security idp application-ddos (incomplete)
hidden command >> show security idp application-identification (incomplete)
hidden command >> show security idp detector (incomplete)
hidden command >> show security idp detector a
hidden command >> show security idp detector c
hidden command >> show security idp detector p
hidden command >> show security idp ips-cache
hidden command >> show security idp logical-system (incomplete)

Как видно, скрипт помечает некоторые команды как incomplete — это те, у которых предполагается продолжение. В случае, если продолжение команды Junos уже не прячет, такая команда далее тоже находится скриптом, но выдается в сокращенном виде (show chassis message-statistics i — это show chassis message-statistics ipc).

Цели обработать скриптом все возможные ошибки и ситуации не ставилось, поэтому если у вас в дескрипшенах интерфейсов содержится строка про synax error, на которую скрипт реагирует, или включен логгинг в терминал, логика работы может нарушиться.

Еще одной проблемой являются команды, принимающие на вход любое имя, например show interfaces AnyInterfaceNameIsOKHere (в отсутствии такого интерфейса выдается ошибка, другие подобные команды могут не выдавать ничего). По понятной причине, скрипт при натравливании его на show interfaces, вылетает с ошибкой про maximum recursion depth exceeded. Зато поиск с commandStart = «show interfaces ge-0/0/0 » работает нормально:
Запуск для show interfaces ge-0/0/0
hidden command >> show interfaces ge-0/0/0 forwarding-context
hidden command >> show interfaces ge-0/0/0 ifd-index
hidden command >> show interfaces ge-0/0/0 ifl-index
hidden command >> show interfaces ge-0/0/0 instance
hidden command >> show interfaces ge-0/0/0 no-forwarding
hidden command >> show interfaces ge-0/0/0 scc-dont-forward


Заключение


Надо понимать, что значительная часть hidden-команд скрыты по причине того, что не поддерживаются (или не имеют смысла) на данном оборудовании или в данной версии софта. Многие из них бесполезны, тем не менее, среди них встречаются и «самородки» (например, show chassis cluster information). Поскольку я работаю инструктором по Juniper, то довольно часто приходится слышать от студентов вопрос — где взять список всех скрытых команд. Так что буду теперь всех отсылать к этой статье. Надеюсь, что какая-то польза от данного рецепта кому-то будет.