TCP/IP Yığını Kodlama 1: Ethernet ve ARP (2016)
(saminiir.com)- Linux’ta kullanıcı alanı TCP/IP yığınını doğrudan oluşturarak ağ katmanlarını öğrenen serinin ilk adımı olarak, Katman 2’de Ethernet çerçevesi işleme ve ARP yanıtı uygulamayla başlanıyor
- Çekirdeğin düşük seviyeli ağ trafiği TAP aygıtı ile alınır; dönen dosya tanımlayıcı üzerinden sanal aygıtın Ethernet tamponu
read/writeedilebilir - Ethernet başlığı
dmac,smac,ethertype,payloadolarak ele alınır;ethertypedeğeri 1536 veya üzerindeyse payload tipi, daha küçükse payload uzunluğu anlamına gelir - ARP, IPv4 adresi gibi protokol adreslerini 48 bit MAC adreslerine dinamik olarak eşler; isteklere yanıt verirken çeviri tablosunu güncelleyen bir akışla uygulanır
arpingtestinde özel yığının ARP yanıtını Linux çekirdeği tanır vetap0arayüzünün ARP önbelleğine10.0.0.4girdisi eklenir
Kullanıcı alanı TCP/IP yığınının başlangıç noktası
- Amaç, Linux’ta asgari bir kullanıcı alanı TCP/IP yığını uygularken ağ ve sistem programlamayı daha derinlemesine anlamaktır
- TCP, 30 yılı aşkın sürede biriken çok sayıda spesifikasyon nedeniyle karmaşıktır; ancak temel uygulama öğeleri TCP başlığı ayrıştırma, durum makinesi, tıkanıklık kontrolü ve yeniden iletim zaman aşımı hesaplamasıyla sınırlandırılabilir
- Ethernet ve IP, TCP’ye göre daha az karmaşık olduğundan seri uygulamaya Katman 2’den başlar
TAP aygıtıyla Ethernet trafiğini almak
- Linux çekirdeğinin düşük seviyeli ağ trafiğini yakalamak için Linux TAP aygıtı kullanılır
- TUN/TAP aygıtları, kullanıcı alanı ağ uygulamalarının sırasıyla L3/L2 trafiğini işlemesinde sık kullanılır
- Tünelleme, paketleri başka paketlerin payload’ı içine sarmalama yöntemidir
- OpenVPN gibi programlar da TUN/TAP aygıtlarını kullanır
- Ağ yığını Katman 2’den itibaren oluşturulduğu için TUN değil TAP aygıtı gerekir
- TAP aygıtı,
/dev/net/tapaçılıpioctl(fd, TUNSETIFF, ...)ile oluşturulurIFF_TAPTAP aygıtını seçerIFF_NO_PI, Ethernet çerçevesinin önüne gereksiz paket bilgisinin eklenmesini engeller
- Oluşturma sonrasında dönen dosya tanımlayıcı
fdile sanal aygıtın Ethernet tamponu okunup yazılır
Ethernet çerçeve biçimi
- Ethernet, LAN içinde bilgisayarları bağlayan temel teknolojidir; ilk Ethernet standardı 1980’de Digital Equipment Corporation, Intel ve Xerox tarafından yayımlandı
- İlk sürüm yaklaşık 10Mb/s hız ve yarı çift yönlü iletişim kullandığından veri akışını düzenleyen bir MAC protokolü gerekiyordu
- Yarı çift yönlü Ethernet arayüzlerinde MAC yöntemi olarak CSMA/CD gerekir
- 100BASE-T, twisted-pair kablolama kullanarak tam çift yönlü iletişimi ve daha yüksek aktarım kapasitesini mümkün kılar
- Ethernet switch’lerinin yaygınlaşmasıyla CSMA/CD’ye duyulan ihtiyaç büyük ölçüde azaldı
- Ethernet standardı IEEE 802.3 çalışma grubu tarafından yönetilir
Uygulamada kullanılan Ethernet başlığı
- Uygulama, ethertype ile onaltılık değer eşlemelerini kullanmak için Linux’un
if_ether.hdosyasını dahil eder - Ethernet başlığı C yapısında şu alanlarla temsil edilir
dmac: hedef MAC adresismac: kaynak MAC adresiethertype: payload uzunluğu veya tipipayload: ARP veya IPv4 paketini taşıyan payload işaretçisi
ethertype2 oktetlik bir alandır ve değere göre anlamı değişir- Değer 1536 veya üzerindeyse IPv4, ARP gibi payload tipini gösterir
- Değer bundan küçükse payload uzunluğunu gösterir
- Ethernet çerçevelerine VLAN veya QoS’u gösteren etiketler eklenebilir, ancak bu uygulamada çerçeve etiketleri hariç tutulur
- Payload uzunluğu, etiketler olmadan asgari gereksinim olan 48 bayttan küçükse sona padding baytları eklenir
- Ethernet Frame Format’ın sonunda bütünlüğü CRC ile doğrulayan Frame Check Sequence alanı bulunur, ancak bu uygulamada işlenmez
Ethernet çerçevesi ayrıştırma yöntemi
- Yapı bildiriminin
packedözniteliği, GNU C derleyicisinin veri hizalama için padding baytları ekleyerek yapı bellek düzenini optimize etmesini engeller - Uygulamadaki ayrıştırma, tamponu uygun protokol yapısına type cast etme yöntemini kullanır
- Örnek:
struct eth_hdr *hdr = (struct eth_hdr *) buf;
- Örnek:
- Daha taşınabilir yöntem, protokol verisini elle serileştirmektir
- Bu durumda derleyici, işlemciye özgü veri hizalama gereksinimlerine uymak için padding baytları ekleyebilir
- Gelen Ethernet çerçevelerinin işlenmesi basit bir akışı izler
- TAP aygıtından tampon okunur
init_eth_hdr(buf)ile Ethernet başlığı başlatılırhandle_frame(&netdev, hdr)ethertypedeğerine bakarak sonraki eyleme karar verir
ARP paket yapısı ve rolü
- ARP(Address Resolution Protocol), IPv4 adresleri gibi protokol adreslerini 48 bitlik Ethernet adresleri olan MAC adreslerine dinamik olarak eşler
- ARP yalnızca IPv4 ile sınırlı değildir; çeşitli L3 protokolleriyle birlikte kullanılabilir
- Örneğin CHAOS, 16 bitlik protokol adresi tanımlar
- Tipik LAN iletişiminde bir servisin IP adresi bilinse bile gerçek iletim için MAC adresi gerekir
- ARP, ağa bir broadcast sorgusu göndererek ilgili IP adresinin sahibinin kendi donanım adresini duyurmasını sağlar
ARP başlığı ve IPv4 için payload
- ARP başlığı şu alanlardan oluşur
hwtype: bağlantı katmanı tipini gösteren 2 oktetlik alan; Ethernet için değeri0x0001protype: protokol tipini gösteren 2 oktetlik alan; IPv4 için değeri0x0800hwsize: donanım adresi boyutunu gösteren 1 oktetlik alan; MAC adresi 6 bayttırprosize: protokol adresi boyutunu gösteren 1 oktetlik alan; IPv4 adresi 4 bayttıropcode: ARP mesaj tipini gösteren 2 oktetlik alan
opcodedeğeri dört türe ayrılır- ARP request:
1 - ARP reply:
2 - RARP request:
3 - RARP reply:
4
- ARP request:
- IPv4 için ARP verisi
arp_ipv4yapısıyla ele alınırsmac: gönderen MAC adresisip: gönderen IP adresidmac: alıcı MAC adresidip: alıcı IP adresi
Adres çözümleme algoritması ve önbellek
- RFC 826’daki adres çözümleme algoritması, donanım tipi ve protokol tipini kontrol ettikten sonra çeviri tablosunu güncelleyen ve hedef adres kendisiyse yanıt oluşturan bir akıştır
- translation table, ARP sonuçlarını saklayarak host’un zaten sahip olduğu girdileri önbellekten sorgulayabilmesini sağlar
- Bu önbellek, yinelenen ARP isteklerinin ağı gereksiz yere doldurmasını azaltır
- Uygulama kodu
arp.cdosyasındadır
ARP yanıt testi ve sonraki adım
- ARP uygulamasının nihai testi, isteklere doğru yanıt verip vermediğini kontrol etmektir
arping -I tap0 10.0.0.4çalıştırıldığında10.0.0.4adresinden00:0C:29:6D:50:25MAC adresiyle unicast yanıt döner- Ardından
arpçıktısında Linux çekirdeğinin ARP önbelleğinde10.0.0.4 ether 00:0c:29:6d:50:25 tap0girdisi oluşur - Asgari Ethernet çerçeve işleme ve ARP uygulamasıyla bile özel bir Ethernet aygıtının Linux host’un ARP önbelleğini doldurduğu doğrulanabilir
- Projenin kaynak kodu GitHub’da yer alır; sonraki adım ICMP echo/reply olan ping ve IPv4 paket ayrıştırmasının uygulanmasıdır
1 yorum
Hacker News yorumları
Birkaç yıl önce C ile kullanıcı alanı ağ yığını yazmıştım; ham paketleri bir TUN arayüzü üzerinden işleyerek bir ölçüde çalışır hâle getirdim.
Şu anda IP adresleri, rotalar vb. ayarlanabilen basit bir kabuk içeriyor; ağ paketlerini de mbuf ile sk_buf karışımı gibi duran hibrit bir yapıda tutuyor.
Ancak UDP uygulamasını bitirdikten sonra TCP’yi uygulayacak zamanı ya da motivasyonu bulamadım; kod burada: https://github.com/cakturk/unet
Doğal olarak tarihin en yavaş ve en kırılgan şeylerinden birine yakındı, ama gerçekten çalışıyordu ve epey de eğlenceliydi. Keşke o kod hâlâ bir yerlerde dursa.
lwip’in “POSIX portu” da aynı şekilde TUN/TAP aygıtından ham Ethernet baytlarını alıyor.
https://github.com/lwip-tcpip/lwip/blob/master/contrib/ports...
En küçük Linux çekirdeği TCP/IP yığını olmadan derlenince 400KB, TCP/IP yığını eklenince 800KB oluyor.
Yalnızca sıcaklık göndermesi gereken bir projede, değeri kullanıcı alanındaki küçük bir C programıyla kendi oluşturduğum UDP mesajına koyup gönderdim; böylece alandan ve karmaşıklıktan epey tasarruf edebildim.
ARP’yi devre dışı bırakırsanız aynı ağdaki birden fazla sunucuya aynı IP’yi atayabilirsiniz.
Yönlendirme ön ucu görevi gören sunucu, MAC adresine göre paketleri arka uç sunucunun ağ arayüzüne iletebiliyorsa, o arka uç kendisini hedef olarak görür ve kaynak/hedef IP’leri değiştirerek istemciye doğrudan yanıt verebilir. Bu sırada yönlendirme ön ucundan tekrar geçmez.
Ya da ARP’yi kapatmadan ortak IP adresini loopback arayüzüne takma ad olarak ekleyerek de aynı etki elde edilebilir; arka uç kendisini hedef olarak görürken ARP çakışmalarından kaçınır. Bu, 90’lar ve 2000’lerde IBM WebSphere yazılım yük dengeleyicisinin kullandığı bir numaraydı.
Daha yaygın kullanılan L3 yük dengelemeye göre avantajı, IP paket başlığını yeniden yazmaya gerek olmamasıdır.
Bu yüzden bu yöntemi kullanacaksanız özel bir VLAN oluşturmak iyi olur.
Benzer bir şeyi Python ile yapmıştım: https://github.com/georgek/notebooks/blob/master/internet.ip...
Muhtemelen kod kalitesi daha düşüktür; açıkçası adres çözümleme algoritmasını da kafadan uydurdum. ICMP ile internetteki host’lara ping göndermeye kadar başarılı oldum.
Tamamının kısa bir notebook’un içine sığması hoşuma gidiyor. Orijinal yazı, atıfta bulunduğu daha büyük kaynak koddaki pek çok ayrıntıyı metinde atlıyor.
Bu yazıyı görmeden, yalnızca Wikipedia’ya bakarak yapmıştım. Ancak TCP’den itibaren karmaşıklık ciddi biçimde arttığı için ilgim biraz azaldı. 3. bölümün o kısmı ele aldığı söyleniyor; belki bir gün okuyup tamamlarım. Ağ konularına ilginiz varsa, hangi seviyede programcı olursanız olun denemeye değer ve tatmin edici bir iş olduğunu düşünüyorum.
Birkaç yıl önce nükleer santral ölçümleme işi yapmıştım. İstemci tarafı geliştirmeyi Sun iş istasyonlarında yapıyorduk; aslında TCP/IP deneyimim sayesinde işe alınmıştım ve o deneyimi CMU’daki “İşletim Sistemleri” dersinde edinmiştim.
Buna karşılık santral bilgisayarı TCP/IP yığını olmayan bir minibilgisayardı; bu yüzden ekip kendi yığınını yazmak zorunda kalmıştı.
Yazının başlangıcından yaklaşık 1 dakika sonra “dmac ve smac oldukça bariz alanlar” deniyor; bunların ne olduğunu bilmeyen okur burada hemen kopabilir.
“Demek ki bu yazı bu alanları bariz bulanlar için. Bana göre değil, okumayı bırakmalıyım” diye düşünmesine yol açar.
Üstelik ağ yığını yazma üzerine bir yazıysa, okurun ağ konusunda belli ölçüde bilgi sahibi olduğunu varsaymak güvenli.
İlgili yazılar:
Let’s code a TCP/IP stack (2016) - https://news.ycombinator.com/item?id=27654182 - Haziran 2021, 49 yorum
Let’s code a TCP/IP stack, 1: Ethernet & ARP (2016) - https://news.ycombinator.com/item?id=17316487 - Haziran 2018, 47 yorum
Let’s Code a TCP/IP Stack: TCP Retransmission - https://news.ycombinator.com/item?id=14701199 - Temmuz 2017, 30 yorum
Let’s code a TCP/IP stack, 1: Ethernet and ARP - https://news.ycombinator.com/item?id=11234229 - Mart 2016, 49 yorum
Yazarın ARP çözümleme testinde kullandığı 10.0.0.4 IP adresini nereden aldığını bilmiyorum
Neyin adresi bu? Burada oluşturulan sahte Ethernet cihazından erişilebilen sahte bir cihaz mı, yoksa yazarın ağında gerçekten bulunan bir cihaz mı?
TAP cihazı, yazılımla emüle edilen bir Ethernet bağlantısı gibi bir şeydir. Paketleri oraya gönderdiğinizde doğrudan kullanıcı düzeyindeki programa iletilir; hangi IP adresine sahip olacağına ve ARP’ye nasıl yanıt vereceğine o program karar verir
Normalde bu işleri işletim sistemi halleder ve bir arayüze IP adresi eklemek için root yetkisi gerekir. TAP cihazını açmak için de aynı şey geçerlidir. Ağ iletişimi genel olarak işbirliğine dayalı çalışır; ağda root yetkisine sahip kötü niyetli bir aktör kötü şeyler yapabilir
Hatırladığım kadarıyla ARP yalnızca yerel segmentte çalışır. Router kendi adresini doldurup paketi iletir
Bir de RARP var; “ağa” kendi IP adresini sormanın yollarından biridir. RARP’ın bugün gerçek ortamlarda hâlâ çalışıp çalışmadığını bilmiyorum