В подразделении, где я работаю, есть традиция - новичку при онбординге вручается ссылка на Wiki с легендарными багами, приведшими к заметным последствиям. Недавно мне пришла в голову идея сделать такую же страницу, но уже со ссылками на Хабр, потому что на русском о багах пишут с бОльшим огоньком. Но, увы, оказалось, что каскадному падению серверов AT&T 15 января 1990 года внимание как-то не уделено. А ведь история получилась, прямо-таки эпическая.

Итак, 15 января 1990 года из-за одной строчки кода телефонная сеть AT&T получила 9 часов даунтайма, 70 миллионов несовершенных звонков, а общий убыток насчитали в $60 миллионов еще не инфляционных долларов. И нет, там не было неудачного релиза, развернутого сразу и везде. Все было гораздо интереснее.

Диспозиция

В конце 1980-х AT&T была доминирующим игроком дальней связи в США. Практически каждый междугородний звонок в стране проходил через ее сеть из 114 коммутаторов 4ESS (Electronic Switching System 4-го поколения), маршрутизирующих вызовы между городами, штатами и посылающих их во весь остальной мир.

AT&T была фанатично помешана на надёжности: каждый коммутатор 4ESS имел резервные источники питания, резервные тракты передачи и вообще резервировал все что можно. Сеть и вовсе строилась по принципу «параноидальной демократии» - поскольку централизованная оркестрация еще не использовалась, коммутаторы сами постоянно мониторили друг друга, отслеживая, кто здоров, а кто выбыл. "Общение" велось по протоколу CCS7 (Common Channel Signaling System 7, он же SS7), где служебные сообщения передавались отдельно от голосового трафика. Именно по CCS7 коммутаторы сообщали друг другу: «я принимаю вызовы», «я перегружен», «маршрутизируй через меня».

Два маршрутизатора 4ESS изучают сервисные сообщения. На примере фильма "назад в будущее 3", вышедшего как раз в 1990 г.
Два маршрутизатора 4ESS изучают сервисные сообщения. На примере фильма "назад в будущее 3", вышедшего как раз в 1990 г.

Сеть работала отлично - если один узел падал, остальные перенаправляли трафик мимо него и так до тех пор, пока узел снова не возвращался в строй. И ни один отдельный коммутатор не мог уронить всю сеть. Ну, по крайней мере так думалось до 15 января 1990 года.

А давайте чего-нибудь оптимизируем

Как стандартно работал коммутатор 4ESS: ОС маршрутизатора постоянно проверяла собственную работоспособность и целостность и, в случае нахождения каких-то сбоев, отправляла себя в перезагрузку. Перед перезагрузкой система слала через CCS7 всем окружающим маршрутизаторам служебное сообщение - мол, я подремлю. Окружение помечало выбывшего в таблицах как временно недоступного, плюс записывало некоторое количество логов.

Кадр из фильма "Вспомнить все", вышедшего тоже в 1990 г.
Кадр из фильма "Вспомнить все", вышедшего тоже в 1990 г.

Затем сбойный маршрутизатор перезагружался за 4-6 секунд, после чего слал сообщение "я вернулся". Окружение снова обновляло свои таблицы, плюс, мы же помним по "параноидальную демократию" - опять же записывало некоторую служебную информацию по времени перезагрузки, отмечало время сброса аптайма и т.п. Я специально это описываю, чтобы было понятно, что в таблицах маршрутизации окружения менялась не одна строка.

После того, как сбойный маршрутизатор известил всех о своем "камбеке", он начинал слать так называемый IAM (Initial Address Message), т.е. сообщения о маршрутизации и вызовах. Т.е. начинал работать в штатном режиме. Между служебным "я вернулся" и IAM была буквально секунда-две, но инженеры AT&T решили ее оптимизировать.

Логика была простая - зачем дожидаться служебного сообщения "я вернулся", если первый IAM и есть сообщение, что маршрутизатор уже в сети? Для обработки этого вызова написали кусок кода, который запускал переинициацию таблиц маршрутизации при первом IAM после перезагрузки, и условие, для обработки, если следом прилетал второй IAM.

Я не смог найти оригинальный листинг, но вот тут приведен псевдокод, описывающий логику работы:

1 while (ring receive buffer not empty
and side buffer not empty):
2 Initialize pointer to first message in side buffer
or ring receive buffer
3 get copy of buffer
4 switch (message):
5 case (incoming_message):
6 if (sending switch is out of service):
7 if (ring write buffer is empty):
8 send "in service" to status map
9 else:
10 break
END IF
11 process incoming message, set up pointers to
optional parameters
12 break
END SWITCH
13 do optional parameter work

Проблема заключалась в том, что в С, на котором был написан код, break на 10-й строке прерывал не IF, а более высокоуровневого Switch. В результате, второй входящий IAM приводил не к обработке сообщения согласно строке 11, а улетал в строку в 13, начиная перезаписывать доп. параметры. Тут подсистема для коррекции ошибок обнаруживала несколько перезаписей и отсутствие целостности таблиц, после чего перезагружала коммутатор, пока он ещё мог перезагрузиться.

Предположу, что программист, писавший код, привык работать с Ada, где поведение break именно такое, как предполагает листинг. Но в данном случае язык был именно такой, и ошибка осталась.

Позвони мне, позвони

Считается, что в AT&T очень серьезно относятся к тестированию. Однако баг проглядели и раскатали его в продакшн на все 114 маршрутизаторов. Причем, сделали это еще в 1989 году и он благополучно пережил рождественскую нагрузку. Но там звонки относительно равномерно грузили сеть и баг не проявляется.

Но 15 января из Нью-Йорка стали как-то особенно настойчиво звонить. Источники, описывающие баг, не поднимают вопрос, откуда взялась эта активность. Однако, мне стало любопытно, я погуглил мировые события и нашел, что именно 15 января 1990 г. бывшие жители ГДР взяли штурмом штаб-квартиру Штази. Чтобы понять, почему это важно, нужна небольшая историческая справка.

После падения берлинской стены одним из главных требований активистов стала ликвидация Штази (местного аналога КГБ). Однако штаб-квартира в Восточном Берлине продолжила работать под новой вывеской "Управление национальной безопасности", попутно отдав приказ об уничтожении документов. Под нож шли списки осведомителей, папки, фотографии и документы. И именно 15-го числа множество активистов взяло штурмом штаб-квартиру вместе с архивом. Как пишут в интернетах "общая длина стеллажей, на которых хранятся неуничтоженные в 1989-90 гг. документы Штази, достигают 112 км". А зная любовь США ко всяким архивам, не удивлюсь, что кто-то проявил настойчивость. Но, повторюсь, это мои домыслы.

В любом случае, из Нью-Йорка, несмотря на праздничный день имени Лютера Кинга, кто-то очень настойчиво звонил. Маршрутизатор AT&T в 14:25 накопил критическое количество в части транковского оборудования и принял решение о штатной 4-х секундной перезагрузке. Поскольку активность Нью-Йорка не уменьшалась, звонки копились в очереди, а сервисные сообщения IAM копились в буфере (и, судя по всему, активность была такая, какой не случалось даже в рождество). После перезагрузки, в соответствии с новой логикой, маршрутизатор Нью-Йорка не стал слать сообщение "я вернулся", а сразу пульнул первый IAM, после чего, в долю секунды послал второй и последующие IAM. Важно: т.е. до этого момента на центральных высоконагруженных узлах не возникала ситуация, когда два сервисных сообщения шли друг за другом в течении нескольких миллисекунд. Даже в праздники. А тут пошли.

Окружающие маршрутизаторы начали обновлять таблицы, одновременно получили второй IAM, вывалились из switch, начали писать данные непонятно куда, после чего словили ошибку "audit subsystems" и ушли в перезагрузку. А из Штатов продолжали звонить.

Упавший маршрутизатор At&T (один, чтобы не дублировать заглавную картинку). На примере фильма "Один дома", аналогично вышедшего в 1990 г.
Упавший маршрутизатор At&T (один, чтобы не дублировать заглавную картинку). На примере фильма "Один дома", аналогично вышедшего в 1990 г.

Поскольку сразу несколько маршрутизаторов ушли в ребут, их нагрузка распределилась на соседние. Перезагрузившиеся маршрутизаторы начинали разбор буфера и клали следующие маршрутизаторы по цепочке, включая те, что перезагрузились чуть раньше. И чем сильнее сеть пыталась перераспределить нагрузку, тем больше сообщений IAM копилось и тем быстрее шел каскад перезагрузок. Как только количество перезагружающихся коммутаторов стало преобладать над работающими, сеть перешла в режим коллапса.

Где все?

Суммарно даунтайм продлился около 9 часов. Попытки инженеров решить проблему через перезагрузки ни к чему не приводили - им оставалось наблюдать, как маршрутизаторы волнами красятся в красный по всей стране, уходя в перезагрузку. 

Сервисный инженер пытается оживить маршрутизатор. На примере фильма "Призрак", опять же вышедшего в 1990 г.
Сервисный инженер пытается оживить маршрутизатор. На примере фильма "Призрак", опять же вышедшего в 1990 г.

Как потом посчитали, всего в этот день не случилось 50-70 миллионов звонков, а 60.000 абонентов и вовсе полностью остались без связи. Пострадали системы бронирования авиакомпаний, гостиниц, прокатных компаний (тогда многое было завязано именно на телефонном подтверждении). Более того, почему-то задержали 500 авиарейсов. Всего убытков насчитали на 60 миллионов долларов (с учетом покупательной способности, сейчас это ближе к 120-150 миллионам).

Проблему удалось частично решить замедлением сети CCS7, в результате чего маршрутизаторы стали успевать обновить свои таблицы и не падать. Ну а к ночи активность звонков спала сама собой и системщикам за ночь удалось откатить маршрутизаторы на старую версию ПО.

Выводы и рекомендации

Если бы эта статья была написана нейросетью, то тут был бы обязательный раздел с выученными уроками и рекомендациями. Но мы не будем. За 36 лет, прошедших с момента инцидента, индустрия выпустила достаточно методик и рекомендаций по предотвращению такого рода инцидентов. 

Программист, вооруженный накопленным опытом, работает с диким кодом. На примере фильма "Эдвард Руки-Ножницы", догадайтесь из какого года
Программист, вооруженный накопленным опытом, работает с диким кодом. На примере фильма "Эдвард Руки-Ножницы", догадайтесь из какого года

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


Размещайте облачную инфраструктуру и масштабируйте сервисы с надежным облачным провайдером Beget.
Эксклюзивно для читателей Хабра мы даем бонус 10% при первом пополнении.

Воспользоваться