SSH po adresie sprzętowym
Każda karta sieciowa w standardzie Ethernet ma unikalny[1] kod wbudowany na stałe[2] na etapie produkcji. Kod ów, zwany adresem sprzętowym lub MAC-adresem, to 48 bitów, z których pierwsze 24 oznaczają producenta, a ostatnie 24 - numer seryjny w ramach danego producenta. Zwyczajowo zapisuje się go jako sześć dubletów heksadecymalnych, na przykład: AA:BB:CC:DD:11:22. Albo aa-bb-cc-dd-11-22 (wielkość liter oraz separator między dubletami nie mają znaczenia).
Skrót MAC oznacza Media Access Control.
Unikalność tego kodu (a przynajmniej względna unikalność - szanse, że dwa urządzenia w tej samej sieci będą miały ten sam adres MAC są przyzerowe - chyba, że się ktoś specjalnie postara) jest podstawą działania na przykład serwera DHCP, który widząc urządzenie ze znanym MAC-adresem przydzieli mu ten sam adres IP co poprzednio. Dzięki temu ten sam komputer (czy drukarka, kamera czy smartfon) dostanie w tej sieci za każdym razem ten sam adres IP.
No dobra, nie do końca tak to działa. Adresy IP przyznawane przez serwer DHCP charakteryzują się czasem życia ("dzierżawy"), który administrator serwera może ustawić na pięć minut albo pięć lat; po upływie tego czasu, jeżeli urządzenie zniknie z sieci a następnie pojawi się z powrotem (na przykład w wyniku restartu, albo wyjścia z zasięgu sieci bezprzewodowej), nie ma już gwarancji, że dostanie ten sam adres IP. Ale na ogół czas dzierżawy w sieci lokalnej mierzy się w tygodniach, miesiącach, czasem nawet latach.
Mam w domowej sieci kompa z zainstalowanym nań Linuksem. Bez podłączonego monitora, klawiatury ani myszy (czyli w trybie headless). Łączę się do niego wyłącznie przez ssh z sieci lokalnej. Komp ów ma nazwę sieciową, ale zainteresowało mnie ostatnio, czy dałbym radę napisać skrypt, który otworzy mi sesję ssh do tego kompa wyłącznie na podstawie znajomości adresu MAC, bo na przykład nazwa sieciowa może się zmienić, a adres MAC - nigdy[2][3].
Wymaganie nieco z dupy wzięte, ale spróbujmy. Ponieważ maszyna źródłowa ma na pokładzie Windows, użyjemy Powershell:
$macAddress = "90-1B-0E-68-E0-B1"
$arpEntry = arp -a | Select-String -Pattern $macAddress
if ($arpEntry) {
$ipAddress = $arpEntry -replace ".*\s(\d{1,3}(\.\d{1,3}){3})\s.*", '$1'
ssh user@$ipAddress
} else {
Write-Host "MAC address $macAddress not found."
}
Jak widać zagadnienie jest całkiem proste; skrypt ma bez specjalnych wysiłków ledwie 8 linii.
Najpierw zapisujemy sobie MAC-adres docelowego kompa w zmiennej, następnie wołamy polecenie arp -a, które wypluwa kupę różnych informacji, w tym również linię z mapowaniem adresu MAC na adres IP. Poleceniem Select-String wybieramy tę jedną linię i jeżeli się udało, wyciągamy z niej (za pomocą wyrażenia regularnego) adres IP i łączymy się zeń komendą ssh, a jeżeli nie, wyrzucamy informację o niepowodzeniu.
Samo wyrażenie regularne jest obsługiwane operatorem -replace i działa tak:
-
Najpierw mamy
.*czyli cokolwiek -
Potem mamy
\sczyli spację (albo tabulację, albo inny "pusty" znak) -
Potem jest otwarty nawias zewnętrzny
(- w parze z nawiasem zamykającym niedaleko końca oznacza on tzw. "capturing group" czyli ciąg (grupę) znaków, który staramy się "złapać", znaleźć. Wewnątrz tego nawiasu będziemy próbowali "złapać" adres IP. -
Następnie jest
\dczyli dowolna cyfra 0-9 -
Potem mamy
{1,3}czyli powtórzenie poprzedniego elementu między 1 a 3 razy. Czyli\d{1,3}to nic innego jak jedna, dwie lub trzy cyfry, np.115albo192 -
Znów otwarty nawias (wewnętrzny)
(- tym razem oznacza on po prostu jakieś wyrażenie w nawiasie. -
Wewnątrz tego wewnętrznego nawiasu mamy najpierw
\.(kropka, poprzedzona ukośnikiem, bo kropka sama w sobie jest częścią składni RegEx, a my chcemy dopasować po prostu kropkę) i zaraz potem\dczyli dowolna cyfra, powtórzona nie więcej niż trzy razy (czyli{1,3}). -
Zamykamy wewnętrzny nawias
)i dodajemy{3}czyli dokładnie trzy powtórzenia tego, co jest w wewnętrznym nawiasie (czyli kropka po której następują 1, 2 lub 3 cyfry). -
Potem upewniamy się, że po ostatniej części adresu IP pojawia się spacja (albo tabulacja itd) czyli znów
\si na koniec dowolne inne śmieci czyli.*
Ciekawe, że kod, który interpretuje to wyrażenie, jest w stanie odróżnić logikę nawiasów zewnętrznych od wewnętrznych i potraktować je tak, jak trzeba. Sądzę, że po prostu capturing groups nie mogą pojawiać się wewnątrz innych nawiasów, a może chodzi o {1,3} po zamykającym nawiasie wewnętrznym? No nie wiem, w każdym razie działa.
Proste? Proste, choć sprawdzi się tylko w podstawowym scenariuszu, kiedy adres MAC pojawia się na wyjściu komendy arp tylko raz. W praktyce może być tak, że dzierżawa adresu IP jest krótka i po restarcie maszyna z Linuksem dostanie nowy adres, ale ponieważ nie restartowaliśmy komputera klienckiego (tego z Windowsem), jego pamięć arp zachowa obydwa adresy IP maszyny linuksowej. Da się to również ogarnąć, ale to już temat na kiedy indziej.
[1] Unikalny w teorii, w praktyce patrz [2].
[2] W rzeczywistości można go zmienić - nie w samej karcie, gdzie faktycznie jest zakodowany na twardo (i unikalny globalnie, przynajmniej w teorii, bo faktycznie bywa z tym różnie), ale na poziomie sterownika.
[3] Zaoszczędziłbym tu jeden odnośnik, gdyby nie [3].
Komentarze