Bilgisayarınızı C# ile uzaktan başlatın, Wake-On-Lan

Wake-On-Lan

WoL keyboard   Geçtiğimiz günlerde kullandığım netbook’un daha fazla yapmak istediğim işleri desteklemeyeceğine karar vererek güçlü bir masaüstü bilgisayar almaya karar verdim. Güzel bir yapılandırma ile Windows Server 2008 R2 Core ve üzerine de Hyper-V kurduktan sonra sanal makinelerimi ayarlayarak uzak masaüstü bağlantısı ile çalışmaya başladım. Sonrasında bir adım daha ileri giderek modem üzerinden gerekli yönlendirmeleri de yaparak ev dışında bulunduğum zamanlarda dahi belgelerime/çalışmalarıma ulaşabilmeye başladım.
   Buraya kadar her şey normal olmasına karşın yurdum elektrik altyapısını göz ardı ettiğimi acı bir deneyimle hatırladım (ya da hatırlattılar mı demeliyim :)). Haliyle evdeki elektrik kesintisi sonrasında gün içerisinde bilgisayarıma ulaşamadım. Tembel bir yazılımcının her zaman için iyi bir yazılımcı olacağını hatırlayalım, bir daha böyle bir şey yaşamamak, evdeyken bile bilgisayarın başına gitmeden başlatabilmek ne güzel olur diye düşünmeye başladım. Şanslıyım ki bilgisayarımın WOL (Wake-On-Lan) desteği var, yazdığım küçük bir uygulama ile bilgisayarımın başında olmaksızın gerek internet gerekse de intranetten bilgisayarımı başlatabiliyorum. Aşağıda, yazdığım bu kısa programım ve Wake-On-Lan hakkında bilgilere ulaşabilirsiniz.
   Uygulamaya geçmeden önce isterseniz Wake-On-Lan (diğer isimleri ile Wake On WAN, Remote Wake-up, Power On By LAN, Power Up By LAN, Resume by LAN, Resume on LAN, Wake Up On LAN) nedir birlikte görelim.
   Wake-On-Lan, en kaba anlatımıyla, network üzerinden gönderilen bir paket ile bir bilgisayarın çalışması için geliştirilmiş bir Ethernet bilgisayar ağ standardıdır. İşletim sisteminden bağımsız olarak, donanım seviyesinde desteklenen Wake-On-Lan standardında, anakartın ve ağ ara yüzünün (network kartı) bu özelliği destekliyor ve aktive edilmiş olması gerekmektedir. Çalışma prensibi olarak, biraz önce de belirttiğim gibi, ağ üzerinden gönderilen bir paket sonucunda sistem çalışır duruma geçmektedir. Tamamen kapalı durumdaki bir sistemin ağ üzerinden gelen paketleri işlemesinin mümkün olmadığı düşünecek olursak bilgisayarınızı öncelikle elektriğe bağlı ve düşük güç tüketiminde, bir nevi uyku modunda,  çalışıyor olması gerekmekte. Sistemin gelen her ağ paketi ile çalışır duruma gelmesini önlemek adına standartta özel/sihirli paket (magic packet) tanımlanmıştır. Ağ  ara yüzü sistem kapalı durumda iken teslim aldığı her bir paketi inceleyerek bu sihirli paketin ulaşıp ulaşmadığını kontrol etmekte ve uygun paket ulaştığında sistemi  çalıştırmaktadır. OSI modelinde 2. katmana yerleştirebileceğimiz Wake-On-Lan standardı bu özelliği nedeniyle OSI modelinde 3. katmanda yer alan IP adresi üzerinden çalışmamaktadır. İşletim sisteminin dahi devrede olmadığı ve sistemin neredeyse kapalı olduğu uyku halinde zaten bir IP adresinin kullanılabilir olması da pek mümkün değildir.
   Peki sisteme sihirli paketi IP adresini kullanarak ulaştıramıyorsam nasıl ulaştırabilirim? Yapılması gereken, sihirli paketin ağ üzerinde yer alan tüm bilgisayarlara iletilerek doğru bilgisayarın bu paketi kabul ederek çalışır duruma geçmesidir. Ağ üzerinde yer alan tüm bilgisayarlara bir paket göndermek, bir paketin yayınını yapmak dediğimde zaten pek çoğunuz kullanılması gereken yöntemin Broadcast olduğunu fark edecektir. Sistem hemen çalışır duruma gelerek pakete yanıt veremiyor olması nedeniyle TCP üzerinden göndermek yerine yanıt beklemediğimiz UDP tercih edilmesi oldukça normaldir. Tüm resmi bir araya getirdiğimizde sihirli paketimizi hedef bilgisayara iletmek için yapılması gereken ağ üzerinden bir UDP broadcast’i gönderilmesidir. Wake-On-Lan konusunda sıklıkla hataya düşülen nokta, sistemin tamamen broadcast üzerinden çalışabiliyor olduğunun düşünülmesidir. Yapılan broadcast sadece sihirli paketi hedef makineye ulaştırmak için bir araçtır. Örneğin; internete bağlı bir modem üzerinden yapılacak bir yönlendirme sonrasında dış ip adresinize yapacağını bir sihirli paket gönderimi de işe yarayacaktır. Ağ ara yüzü herhangi bir port üzerinden gelecek olan sihirli paket ile sistemi çalışır duruma getireceğinden broadcast için kullanacağınız portun bir önemi bulunmamaktadır, herhangi bir port tercih edilebilir.
   Teorik olarak sistemin nasıl işlediğini gördükten sonra bunu C# ile hayata geçirmek için yayını yapılan sihirli paketin içeriğini bilmemiz gerekli. Sihirli paketin ilk 6  byte’ı 255 (yani hexedecimal FF) olmalıdır. 6 byte’lık 255’in devamında hedef bilgisayarın 48-bitlik MAC adresi (olasılığı düşürmek adına) 16 kez tekrar ediliyor olmalıdır. Ağ ara yüzü, gönderim protokolünden bağımsız olarak, teslim aldığı paketleri kontrol ederek paket içerisindeki herhangi bir noktada bu kombinasyonu bulması halinde sistemi başlatacaktır.

Wake-On-Lan Sihirli Paket yapısı
   Sistemin işleyişini ve sihirli paketin yapısını öğrendikten sonra bu işi yapacak olan C# kodunu yazmak aslında oldukça kolay. Öncelikle byte dizisi olarak MAC adresini bir değişkende saklamalıyız. Örnek olması adına sanal bir MAC adresi olarak 1A-4F-F5-4F-98-AA değerini kullanıyorum. Gerçek sistemlerde kullanmanız gereken MAC adresini hedef bilgisayarda komut satırından çalıştıracağınız ipconfig /all komutu ile öğrenebilirsiniz.

var bitMacAdresi = new byte[] { 0x1A, 0x4F, 0xF5, 0x4F, 0x98, 0xAA };

   Ardından yayınlanacak olan paketin içeriğini tutacak ikinci bir byte dizi oluşturarak ilk 6 byte’ını 0xFF ile doldurmalıyız;

var sihirliPaket = new List<byte>(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });

   paketimizin kalan kısmına ise 16’lık bir döngü ile MAC adresini eklemeliyiz;

for (int i = 0; i < 16; i++) {
sihirliPaket.AddRange(bitMacAdresi);
}

   Son adımda ise, oluşturduğumuz sihirli paketi ağ üzerinden yayınlamalıyız. Yayınlama için System.Net.Sockets isim uzayında yer alan UDPClient sınıfı kullanılabilir;

var istemci = new UdpClient();
istemci.Connect(IPAddress.Broadcast, 80);
istemci.Send(sihirliPaket.ToArray(), sihirliPaket.Count);

   Her ne kadar örneğimde UDP yayınını 80. port üzerinden yapıyor olsam da, siz herhangi başka bir portu kullanmakta serbestsiniz.

   Yukarıdaki örnek hedef bilgisayarın uygulamanın çalışacağı makine ile aynı alt ağ’da (subnet) olduğunu varsayarak, bu alt ağa yayın yapmaktadır. Bazı senaryolarda hedef bilgisayarın uygulamanın çalışacağı alt ağdan farklı bir alt ağ’da bulunuyor olması söz konusudur. Bu gibi durumlarda ilgili alt ağ’a ait yayın adresi hesaplanarak sihirli paket bu adrese gönderilmelidir. Aşağıda sizlerle paylaştığım static YayinAdresiniHesapla fonksiyonu hedef makinenin ip adresi ve alt ağ maskesi verilerek yayının yapılacağı ip adresini hesaplamaktadır;

public static IPAddress YayinAdresiniHesapla(IPAddress hedefMakineIP, IPAddress altAgMaskesi) {
    var ipAdresBitleri = hedefMakineIP.GetAddressBytes();
    var altAgMaskesiBitleri = altAgMaskesi.GetAddressBytes();

    var yayinAdresi = new byte[ipAdresBitleri.Length];
    for (int i = 0; i < yayinAdresi.Length; i++) {
        yayinAdresi[i] = (byte)(ipAdresBitleri[i] | (altAgMaskesiBitleri[i] ^ 255));
    }

    return new IPAddress(yayinAdresi);
}

   Bu fonksiyon kullanılırken hedef makineni ip adresi bilinmiyorsa, ip adresi olarak hedef makine ile aynı ağ ayda bulunan bir başka makine ip’si verilerek de yayının yapılacağı adres bulunabilir. Aşağıda, şimdiye kadar sizlerle paylaştığım kodları toplu halde bulabilirsiniz;

class Program {
    static void Main(string[] args) {
        var hedefMakineIP = "192.168.2.12";
        var altAgMaskesi = "255.255.255.0";
        var macAdresi = "1A-4F-F5-4F-98-AA";

        var yayinAdresi = YayinAdresiniHesapla(IPAddress.Parse(hedefMakineIP), IPAddress.Parse(altAgMaskesi));

#region Mac adresini parse et
        var hexMacAdresi = long.Parse(macAdresi.Replace("-", string.Empty), NumberStyles.HexNumber, CultureInfo.CurrentCulture.NumberFormat);
        var bitMacAdresi = BitConverter.GetBytes(hexMacAdresi);
        Array.Reverse(bitMacAdresi);
#endregion

        #region Sihirli paketi oluştur
        var sihirliPaket = new List<byte>(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });

        for (int i = 0; i < 16; i++) {
            sihirliPaket.AddRange(bitMacAdresi);
        } 
        #endregion

        #region Sihirli paketi yayınla
        var istemci = new UdpClient();
        istemci.Connect(yayinAdresi, 80);
        istemci.Send(sihirliPaket.ToArray(), sihirliPaket.Count);  
        #endregion
    }

    public static IPAddress YayinAdresiniHesapla(IPAddress hedefMakineIP, IPAddress altAgMaskesi) {
        var ipAdresBitleri = hedefMakineIP.GetAddressBytes();
        var altAgMaskesiBitleri = altAgMaskesi.GetAddressBytes();

        var yayinAdresi = new byte[ipAdresBitleri.Length];
        for (int i = 0; i < yayinAdresi.Length; i++) {
            yayinAdresi[i] = (byte)(ipAdresBitleri[i] | (altAgMaskesiBitleri[i] ^ 255));
        }

        return new IPAddress(yayinAdresi);
    }
}

    Örneğimiz ip adresi ve alt ağ maskesi ikilisi verilerek intranet üzerinden çalıştırılabilir. Internet üzerinden bir bilgisayarı başlatmak istiyorsak ise öncelikli bu bilgisayarın bağlı olduğu modem üzerinden kullanılacak olarak port için NAT yönlendirmesi ve firewall ayarlarının yapılmış olması gereklidir. Bu ayarların yapılmasından sonra doğrudan ip adresine gönderim yapmak için ip adresi ile birlikte 255.255.255.255 alt ağ maskesi kullanılmalıdır.

    Konuyla ilgili olarak düşülmesi gereken son not ise; donanımsal olarak anakart ve ağ arayüzünüzün Wake-On-Lan özelliğinin aktif edilmiş olması gerektiğidir.

Ağ arayüzün özellikler ekranında Wake-On-Lan seçeneğinin aktif hale getirilmesi

   Windows yüklü sistemlerde bu ayar cihaz yöneticisi üzerinden ağ arayüzünüzün özellikleri diyaloguna gelerek bu diyalogda yer alan güç yönetimi sekmesinde "bu aygıt bilgisayarı başlatsın" seçeneği seçilerek yapılabilir.

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# konusundan Microsoft tarafından dört yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz.
vCard - Twitter - Facebook - Google+

12 yorum

  1. Gökhan   •  

    Eline sağlık. Güzel bir yazı olmuş. Paylaşımın için teşekkürler.

  2. Mert   •  

    Fatih hocam öncelikle ellerinize sağlık. Tcp ile bunu nasıl gönderiyoruz. Ben yapamadım da yardımlarınızı bekliyorum.

    • Fatih Boy   •     Yazar

      Merhaba Mert,
      Wake-On-Lan özelliği temelde OSI modeli 2. katmanı olan DataLink katmanında gerçekleşmektedir ve ağ yayın adresini (broadcast address) kullanarak ağa bağlı tüm bilgisayarların NIC’lerine özel bir paket yollamaktadır. Bu özel paketin tüm ağa yayınlanmasında UDP kullanılmakta ve herhangi bir IP kullanımı söz konusu değildir. Bu nedenlerden dolayı da TCP üzerinden bu paket gönderilememektedir. Buna tek istisna alt ağ yönlendirmeli yayınlar (Subnet Directed Broadcasts (SDB)) olabilir; fakat bunun için de ağ üzerinde gelen paketleri işleyip broadcastlare dönüştürebilen özel router’ların bulunması gerekli.

  3. Mert   •  

    Teşekkür ederim Fatih Bey oldukça açıklayıcıydı.

  4. akif   •  

    hocam merhaba bu işlem local olarakta uygulanabilirmi…

    • Fatih Boy   •     Yazar

      Merhaba Akif, evet Wake-On-Lan işlemi için paket local network üzerinden gönderilebilmekte.

  5. akif   •  

    hocam bu işlemi tek bir pc için uygulamak istiyorum yardımcı olursanız sevinirim

    • Fatih Boy   •     Yazar

      Merhaba Akif,
      Yazımda paylaştığım yöntem sadece tek bir bilgisayarın ağ üzerinden uyandırılmasını sağlamaktadır. Bu sebeple, paylaştığım adımları takip ederek tek bir bilgisayarı uzaktan uyandırabilirsin.

  6. Serkan   •  

    Ben bunu console.aplication da çalıştırıyorum yanlışmı acaba?

    Kodlar aşağıda fakat classlarda problem oluyor aynı isimden olduğunu düşünüyorum ismini değiştirip heryerde uyguladıktan sonra da Main “static void Main(string[] args)” kısmında hata veriyor.

    namespace PC
    {
    class Program
    {
    static void Main(string[] args)
    {
    var bitMacAdresi = new byte[] { 0xAE, 0xE0, 0x10, 0xD0, 0x43, 0xF1 };
    var sihirliPaket = new List(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });
    for (int i = 0; i < 16; i++)
    {
    sihirliPaket.AddRange(bitMacAdresi);
    }
    var istemci = new UdpClient();
    istemci.Connect(IPAddress.Broadcast, 80);
    istemci.Send(sihirliPaket.ToArray(), sihirliPaket.Count);
    }

    public static IPAddress YayinAdresiniHesapla(IPAddress hedefMakineIP, IPAddress altAgMaskesi)
    {

    var ipAdresBitleri = hedefMakineIP.GetAddressBytes();
    var altAgMaskesiBitleri = altAgMaskesi.GetAddressBytes();
    var yayinAdresi = new byte[ipAdresBitleri.Length];
    for (int i = 0; i < yayinAdresi.Length; i++)
    {
    yayinAdresi[i] = (byte)(ipAdresBitleri[i] | (altAgMaskesiBitleri[i] ^ 255));
    }
    return new IPAddress(yayinAdresi);
    }

    class Program1
    {
    static void Main(string[] args)
    {

    var hedefMakineIP = "192.168.2.12";
    var altAgMaskesi = "255.255.255.0";
    var macAdresi = "AE-E0-10-D0-43-F1";

    var yayinAdresi = YayinAdresiniHesapla(IPAddress.Parse(hedefMakineIP), IPAddress.Parse(altAgMaskesi));

    #region Mac adresini parse et

    var hexMacAdresi = long.Parse(macAdresi.Replace("-", string.Empty), NumberStyles.HexNumber, CultureInfo.CurrentCulture.NumberFormat);

    var bitMacAdresi = BitConverter.GetBytes(hexMacAdresi);

    Array.Reverse(bitMacAdresi);
    #endregion

    #region Sihirli paketi oluştur

    var sihirliPaket = new List(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });

    for (int i = 0; i < 16; i++)
    {

    sihirliPaket.AddRange(bitMacAdresi);

    }

    #endregion

    #region Sihirli paketi yayınla

    var istemci = new UdpClient();
    istemci.Connect(yayinAdresi, 80);

    istemci.Send(sihirliPaket.ToArray(), sihirliPaket.Count);

    #endregion

    }

    public static IPAddress YayinAdresiniHesapla(IPAddress hedefMakineIP, IPAddress altAgMaskesi)
    {

    var ipAdresBitleri = hedefMakineIP.GetAddressBytes();

    var altAgMaskesiBitleri = altAgMaskesi.GetAddressBytes();

    var yayinAdresi = new byte[ipAdresBitleri.Length];

    for (int i = 0; i < yayinAdresi.Length; i++)
    {

    yayinAdresi[i] = (byte)(ipAdresBitleri[i] | (altAgMaskesiBitleri[i] ^ 255));

    }

    return new IPAddress(yayinAdresi);

    }

    }

    }

    }

    • Serkan   •  

      (MAC Adresi ve ip adresi örnektir)

  7. T@ENGINEER   •  

    fatih hocam yazdıgınız c# kodunu denedim ama calısmadı aynı alt agda oldugum için YayinAdresiniHesapla() yi iptal ettim ve istemci.Connect(altAgMaskesi,80); seklinde calıstırdım ama bu sefer de yuva gecerli degıl gibi bi hata verdi yardımcı olabılır mısınız?

    • fatih   •     Yazar

      Merhaba T@ENGINEER,
      istemci.Connect(altAgMaskesi,80); şeklindeki kod ile doğrudan bir bilgisayarın 80 portuna bağlanmaya çalışıyorsun. Bu ip’de bir bilgisayar, dolayısıyla da dinlenen bir soket olmadığı için bağlantı hatası alman beklenen bir durum. Diğer yandan, yapman gereken broadcast adresi üzerinden ağdaki tüm bilgisayarlara udp üzerinden sihirli paketi göndermek. Bu sebeple makalede istemci.Connect(IPAddress.Broadcast, 80); şeklinde broadcast adresine bağlantı açtım. Kodun çalışmaması konusunda ise, öncelikle hedef bilgisayarın wake-on-lan ayarlarını kontrol etmende fayda var. Bilgisayarın ağ üzerinden uyandırılabilir olarak yapılandırılması önemli. Sonrasında ikinci, üçüncü bir örnek kod ya da uygulama ile bilgisayarın uyandırılabildiğini teyit etmekte fayda var.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir