1 puan yazan GN⁺ 2025-03-05 | 1 yorum | WhatsApp'ta paylaş
  • 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/write edilebilir
  • Ethernet başlığı dmac, smac, ethertype, payload olarak ele alınır; ethertype değ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
  • arping testinde özel yığının ARP yanıtını Linux çekirdeği tanır ve tap0 arayüzünün ARP önbelleğine 10.0.0.4 girdisi 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/tap açılıp ioctl(fd, TUNSETIFF, ...) ile oluşturulur
    • IFF_TAP TAP aygıtını seçer
    • IFF_NO_PI, Ethernet çerçevesinin önüne gereksiz paket bilgisinin eklenmesini engeller
  • Oluşturma sonrasında dönen dosya tanımlayıcı fd ile 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.h dosyasını dahil eder
  • Ethernet başlığı C yapısında şu alanlarla temsil edilir
    • dmac: hedef MAC adresi
    • smac: kaynak MAC adresi
    • ethertype: payload uzunluğu veya tipi
    • payload: ARP veya IPv4 paketini taşıyan payload işaretçisi
  • ethertype 2 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;
  • 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ır
    • handle_frame(&netdev, hdr) ethertype değ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ğeri 0x0001
    • protype: protokol tipini gösteren 2 oktetlik alan; IPv4 için değeri 0x0800
    • hwsize: donanım adresi boyutunu gösteren 1 oktetlik alan; MAC adresi 6 bayttır
    • prosize: protokol adresi boyutunu gösteren 1 oktetlik alan; IPv4 adresi 4 bayttır
    • opcode: ARP mesaj tipini gösteren 2 oktetlik alan
  • opcode değeri dört türe ayrılır
    • ARP request: 1
    • ARP reply: 2
    • RARP request: 3
    • RARP reply: 4
  • IPv4 için ARP verisi arp_ipv4 yapısıyla ele alınır
    • smac: gönderen MAC adresi
    • sip: gönderen IP adresi
    • dmac: alıcı MAC adresi
    • dip: 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.c dosyası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ığında 10.0.0.4 adresinden 00:0C:29:6D:50:25 MAC adresiyle unicast yanıt döner
  • Ardından arp çıktısında Linux çekirdeğinin ARP önbelleğinde 10.0.0.4 ether 00:0c:29:6d:50:25 tap0 girdisi 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

 
GN⁺ 2025-03-05
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

    • Çok uzun zaman önce bir pcap/tcpdump ayrıştırıcısını saf bash ile yazmıştım; o zamanlar “program” yazmayı bildiğim tek araç oydu.
      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.
    • Pek çok gömülü cihaz TCP/IP uygulaması olarak lwip kullanıyor.
      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.

    • Hiçbir şey bilmeyen biri olarak bu oldukça şaşırtıcı, ama bu TCP/IP kısmının çekirdeğin tüm kaynak kodunun yarısı olduğu anlamına gelmiyor herhâlde, merak ediyorum.
    • IP yığınının neden bu kadar büyük olduğunu merak ediyorum. 400KB’lık bir ikili dosya epey fazla kod demek; büyük sunucu kullanımına göre yüksek ölçüde optimize edildiği için mi böyle?
  • 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ı.

    • Cisco IOS SLB de benzer şekilde çalışabilir. Sunucu çiftliğindeki her sunucunun loopback’ine sanal IP’yi takma ad olarak ekleme yöntemidir.
      Daha yaygın kullanılan L3 yük dengelemeye göre avantajı, IP paket başlığını yeniden yazmaya gerek olmamasıdır.
    • Bu, DSR (Direct Server Return) olarak da bilinir: https://www.haproxy.com/blog/layer-4-load-balancing-direct-s...
    • ARP’yi devre dışı bırakıp aynı ağdaki birden fazla sunucuya aynı IP’yi atarsanız, switch/bridge MAC adresini öğrenemediği için paketleri o segmentteki tüm portlara sürekli flooding/broadcast etmeye devam eder.
      Bu yüzden bu yöntemi kullanacaksanız özel bir VLAN oluşturmak iyi olur.
    • F5’te böyle yapmaya gerek bırakmayan ARP proxy ayarı var. Dezavantajı, çoğu zaman DHCP’yi bozması.
    • Bu tür düşük seviyeli kurcalamalar için DPDK’yı da deneyebilirsiniz. ARP varsayılan olarak devre dışıdır.
  • 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.

    • Cümlenin tamamı aslında “dmac ve smac oldukça bariz alanlardır. İletişim taraflarının MAC adreslerini içerirler (sırasıyla hedef ve kaynak)” şeklinde, yani açıklıyor.
      Üstelik ağ yığını yazma üzerine bir yazıysa, okurun ağ konusunda belli ölçüde bilgi sahibi olduğunu varsaymak güvenli.
    • Az önce güncellenmediyse, hemen sonraki cümlede “İletişim taraflarının MAC adreslerini içerirler (sırasıyla hedef ve kaynak)” diye açıklıyor.
  • İ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ı?

    • Yazıda geçmiyor ama arayüz başlatılırken yazarın hardcode ettiği bir değer: https://github.com/saminiir/level-ip/blob/e9ceb08f01a5499b85...
      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