İlgili sayfalar: UPnP Port Yönlendirme Yöneticisi
LANPhone uygulaması ile farklı yerel ağlarda bulunan bilgisayarlar haberleşemiyor, bu konuda eleştiriler alıyorum. LANPhone amacından sapmasın diye programı yerel ağ dışında çalışacak şekilde güncellemeyi düşünmüyorum ama şunu da merak etmiştim doğrusu; torrent programları veya Skype gibi fazla miktarda kesintisiz veri transferi gerektiren uygulamalar nasıl oluyor da farklı yerel ağlardaki bilgisayarı haberleştirebiliyor?... Bayram tatilimin önemli bir kısmını bunu araştırmakla geçirdim ve tesadüftür; LANPhone'un ilk versiyonunu geçen sene yine aynı bayramın tatilinde, aynı günlerde yazmıştım...
Birbirine bağlı ağlar arasında dolaşan her bir IP paketinde göndericinin adres bilgisinin, yeni ağa geçiş esnasında geçişi sağlayan cihaza atanmış olan IP adresi ile değiştirilmesi gerekir. Yapılan bu IP değiştirme işlemine NAT (ağ adresi dönüşümü, network address translation) adı verilir. NAT sayesinde internete bağlı milyonlarca(tahminlere göre yaklaşık 100 milyon sunucu ve 350 milyon kullanıcı) bilgisayarın her birine farklı birer ip adresi verilmesi gerekmez.
Ek bir müdahalede bulunulmadıkça NAT işlemi yapan cihazlara bağlı bilgisayaları internet üzerinden direkt olarak haberleştirmek mümkün değildir. Bunun sağlanması için NAT işlemini yapan cihazın port yönlendirmesi yapması sağlanmalıdır. Normal bir bilgisayar kullanıcısından böyle birşey istemek mantıklı değil. Mesela MSN bizden modem ayarlarına girip port yönlendirmesi yapmamızı isteseydi ne kadarımız MSN kullanırdık?... NAT engelinin kullanıcıyı yormadan programsal olarak aşılması gerekir.
NAT'ı aşmanın birkaç yöntemi var. Bunlara, bir modemin ayarlarına girip elle port yönlendirme işlemini dahil etmeyeceğim. Çünkü amacımız, kullanıcılara talimat verip birşeyleri onlardan beklemek değil, kullanıcıları teknik ayrıntılara bulaştırmadan yapılması gerekenleri programlara yaptırabilmek...
Bu yöntem özellikle VoIP uygulamalarında NATı aşmak için en sık kullanılan yöntem olmuştur. Yöntem şöyle uygulanır; B1 ile B2 farklı yerel ağlarda bulunan iki bilgisayar ve S de bir uzak sunucu olsun. B1 ve B2 bağlı oldukları NATları aşıp birbirleri ile direkt haberleşmek için öncelikle S sunucusuna UDP paketleri yollar. Bu esnada A'nın ve B'nin bağlı olduğu NATlar S ile iletişim kurabilmek için geçici portlar açar. S sunucusu paketleri alır ve her bir paketin başlık bilgisinde yer alan göndericiye ait IP adresini ve port numarasını kaydeder. Ardından A'nın bilgilerini B'ye, B'ninkileri de A'ya gönderir. Birbirlerinin IP ve açık port bilgilerini elde eden A ve B, S ile iletişimi keser ve bağlı oldukları NATların geçici olarak açtığı portları kullanarak birbirleri ile haberleşmeyi sürdürür.
Bütün NAT tiplerinde çözüm olamayan bu mantık TCP paketlerini de NATtan geçirmek için kullanılabilir. Ancak yöntem TCP için çok kullanılmamıştır. Ayrıca UPnP özellikli cihazlar yaygınlaşmakta olduğundan delik açma yöntemi eskimektedir.
Gelelim yazımızın asıl konusuna. Öncelikle şunu belirtmekte fayda var; UPnP özelliği ile PnP dediğimiz "tak çalıştır" özelliği aynı şeyler değil. UPnP, ağ cihazlarının birbirlerini tanıyabilmelerini, birbirleri arasında iletişim kurabilmelerini sağlayan standartların genel adıdır. UPnP standartları, UPnP Forumu tarafından belirlenmektedir.
Burada bizi ilgilendiren şey, UPnP standartlarına uygun cihazların programsal olarak denetim altına alınabiliyor olması. Örneğin program yazarak UPnP destekli bir modemin bilgilerini elde edebilir, modemde port yönlendirmeleri oluşturup silebilirsiniz.
Port yönlendirme, dışarıdan belirli bir porttan gelen IP paketlerinin istenen adres ve porta yönlendirilmesi işlemidir. Bunun için öncelikle gelen IP paketinin gönderici IP ve port bilgilerinin, kayıtlı yönlendirme bilgilerinden biri ile eşleşip eşleşmediği kontrol edilir. Eşleşme bulunması halinde, IP paketi eşleşen kayıtta yer alan iç IP ve porta yönlendirilir. Böylece NAT engeli aşılmış olur.
Ağa yeni bağlanan UPnP destekli cihazlar, bir çoklu yayın adresi olan 239.255.255.250 adresine 1900 portu üzerinden yayın grubuna katılım mesajı gönderir. Bu sayede 239.255.255.250 adresine 1900 üzerinden gönderilen tüm istekler, çoklu yayın grubuna katılmış tüm cihazlara iletilir. Cihazlar listelenmek istendiğinde çoklu yayın adresine aşağıdaki mesaj gönderilir:
M-SEARCH * HTTP/1.1 HOST:239.255.255.250:1900 MAN:"ssdp:discover" MX:2 ST:upnp:rootdevice
Bu mesaj bir SSDP (Basit Servis Algılama Protokolü, Simple Service Discovery Protocol) mesajıdır:
Metot: M-SEARCH
Adres: *
HTTP versiyonu: HTTP/1.1
Sunucu (HOST): 239.255.255.250:1900
Zorunlu Uzantı (MAN, Mandatory Extension): "ssdp:discover"
Maximum Yanıt Geciktirme Süresi (MX, Maximum): 2
Aranacak Hedef (ST, Search Target): upnp:rootdevice
Cihaz, hazırladığı yanıtı 0 ile MX değeri arasında rastgele bir değer kadar geciktirerek verir. Amaç, çok fazla cihaz olması durumunda tümünün aynı anda yanıt vermesini engellemektir.
"upnp:rootdevice" ifadesi ağ geçidi aygıtları gibi herhangi bir üst aygıta bağlı olmayan kök aygıtların aranacağını belirtir.
Yukarıdaki mesaj UDP ile paketlenerek 1900 portu üzerinden 239.255.255.250 adresine gönderilir. UDP güvenilir olmadığından, UPnP forumu mesajın 1-2 kez tekrarlanarak gönderilmesini önermektedir. Çoklu yayın grubuna katıldıkları için mesajı alan UPnP destekli cihazlar, paketi yollayan ip adresine kendilerini tanıtan XML dosyasının konumunu belirten bir yanıt gönderir:
HTTP/1.1 200 OK LOCATION: http://192.168.1.1:37215/tr064dev.xml SERVER: Linux UPnP/1.0 Huawei-ATP-IGD CACHE-CONTROL: max-age=86500 EXT: ST: upnp:rootdevice USN: uuid:00e0fc37-2626-2828-2600-00e04cbb5cf2::upnp:rootdevice
Cihaz ile ilgili ayrıntılı bilgiler içeren ve "location:" ifadesinden sonra gelen XML dosyası, standart bir http isteği gönderilerek alınır. Daha sonra alınan bilgilere bakılarak cihazın bir ağ geçidi cihazı olduğu doğrulanır. Doğrulama için, deviceType etiketindeki değerin "InternetGatewayDevice" ifadesini içerip içermediğinin kontrol edilmesi yeterlidir:
... <device> <deviceType>urn:dslforum-org:device:InternetGatewayDevice:1</deviceType> ...
Cihazın bir ağ geçidi cihazı olduğu doğrulandıktan sonra port yönlendirmesi yapılabilmesi için önce bağlantıyı sağayan servis ve ardından bu servisin kontrolünün ele alınmasını sağlayacak adres elde edilmelidir. Bağlantıyı sağlayan servisin teşhisi için serviceType etiketi içerisinde "WANPPPConnection" veya "WANIPConnection" değerlerinin bulunması yeterlidir. Kontrol adresinin elde edilmesi için ise controlURL etiketi içerisine bakılır:
... <serviceList> <service> <serviceType>urn:dslforum-org:service:WANPPPConnection:1</serviceType> <serviceId>urn:dslforum-org:serviceId:WANPPPConnection1</serviceId> <SCPDURL>/desc/WanPppConn.xml</SCPDURL> <controlURL>/ctrlt/WANPPPConnection_1</controlURL> <eventSubURL>/evt/WANPPPConnection_1</eventSubURL> </service> ...
Kontrol adresi elde edildikten sonra, servis kontrolü ile ilgili tüm istekler SOAP (Simple Object Access Protocol, Basit Nesne Erişim Protokolü) ile HTTP üzerinden kontrol adresine gönderilir. Konunun dışına çıkmadan bir SOAP mesajının genel formunu görelim:
<?xml version="1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <m:METOTADI xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1"> <PARAMETREADI1 xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="VERITIPI">PARAMETRE1</PARAMETREADI1> </m:METOTADI> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
Yanıtlar SOAP'a uygun olarak XML formatında döndürülür.
En çok işimize yarayacak metotları, aldıkları ve döndürdükleri parametreleri görelim. Diğer mototların listesine ve metotlarla ilgili daha ayrıntılı bilgiye http://www.upnp.org/specs/gw/UPnP-gw-WANIPConnection-v1-Service.pdf adresinden ulaşabilirsiniz...
Metot: GetExternalIPAddress
Döndürdüğü Parametreler:
NewGetExternalIPAddress
Metot: AddPortMapping
Aldığı parametreler:
NewRemoteHost: Uzak bilgisayarın ip adresi (Boş bırakılabilir, bu durumda tümü yönlendirilir.)
NewExternalPort: Dış port
NewProtocol: Protokol (TCP veya UDP)
NewInternalPort: İç port
NewInternalClient: Yerel ip adresi
NewEnabled: 1/0:Aktif/Pasif
NewPortMappingDescription: Açıklama
NewLeaseDuration: Geçerlilik süresi (Saniye cinsinden. 0: sınırsız)
Metot: DeletePortMapping
Aldığı parametreler:
NewRemoteHost: Uzak bilgisayarın ip adresi (Boş bırakılabilir)
NewExternalPort: Dış port
NewProtocol: Protokol (TCP veya UDP)
Metot: GetGenericPortMappingEntry
Aldığı parametreler:
NewPortMappingIndex: Yönlendirmenin indisi/sırası
Döndürdüğü parametreler:
NewRemoteHost: Uzak bilgisayarın ip adresi
NewExternalPort: Dış port
NewProtocol: Protokol (TCP veya UDP)
NewInternalPort: İç port
NewInternalClient: Yerel ip adresi
NewEnabled: 1/0:Aktif/Pasif
NewPortMappingDescription: Açıklama
NewLeaseDuration: Geçerlilik süresi (Saniye cinsinden. 0: sınırsız)
Metot: GetSpecificPortMappingEntry
Aldığı parametreler:
NewRemoteHost: Uzak bilgisayarın ip adresi (Boş bırakılabilir)
NewExternalPort: Dış port
NewProtocol: Protokol (TCP veya UDP)
Döndürdüğü Parametreler:
NewInternalPort: İç port
NewInternalClient: Yerel ip adresi
NewEnabled: 1/0:Aktif/Pasif
NewPortMappingDescription: Açıklama
NewLeaseDuration: Geçerlilik süresi (Saniye cinsinden. 0: sınırsız)
Merhaba ben bilgisayarım ile mobil telefonum arasında bir haberleşme sağlamak istiyorum. Server ve Client programlarım Local olarak sorunsuz çalışıyor. Bu yazınızda anlattığınız UPnp protokolünün C# ile kullabilmem için bir kaynak önerebilir misiniz? Teşekkür ederim.
UPnP destekleyen cihazlarla iletişim kurmak için 239.255.255.250 ip adresine 1900 portu üzerinden UDP paketleri yollayıp yanıt beklemeniz yeterli. İnternette mutlaka örnek bir kod bulursunuz ama kabaca yapmanız gereken şey şu (sadece fikir vermesi için kodu hızlıca ve denemeden yazıyorum):
Socket soket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint hedef = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900); string msj = "gönderilecek mesaj"; byte[] arabellek = Encoding.ASCII.GetBytes(msj); soket.SendTo(arabellek, hedef); Thread.Sleep(250); soket.Close();Yanıt almak için:
UdpClient alici = new UdpClient(1900); IPEndPoint herhangiBirGonderici = new IPEndPoint(IPAddress.Any, 0); byte[] bGelen = alici.Receive(ref herhangiBirGonderici); string gelen = Encoding.ASCII.GetString(bGelen);