Bir rtnetlink RTM_GETLINK dump’ı sistemdeki her ağ arayüzünü döndürür: loopback, fiziksel NIC’ler, bridge’ler, veth çiftleri, VLAN’lar, WireGuard tünelleri, TUN cihazları vb. Yalnızca fiziksel kablolu Ethernet adaptörlerini istiyorsan üç kategoriyi filtrelemen gerekir:
- Ethernet olmayan link türleri — loopback (
ARPHRD_LOOPBACK), WireGuard/TUN (ARPHRD_NONE) vb. - Sanal Ethernet arayüzleri — bridge’ler, veth’ler, VLAN’lar, bond’lar, macvlan vb. Bunların hepsi
ifi_type == ARPHRD_ETHERbildirir ama türünü tanımlayan birIFLA_LINKINFOattribute’u taşır. - Kablosuz arayüzler — Wi-Fi adaptörleri de
ARPHRD_ETHERbildirir veIFLA_LINKINFOtaşımaz, bu yüzden yalnızca netlink ile kablolu Ethernet’ten ayırt edilemez.
Bu yazı, üç filtreyi de uygulayan ve yalnızca fiziksel kablolu Ethernet linklerini yazdıran kompakt bir C++ programı gösterir.
Üç filtre
Filtre 1: ifi_type == ARPHRD_ETHER
struct ifinfomsg içindeki ifi_type alanı ARP donanım türünü tutar. Kablolu Ethernet, Wi-Fi, bridge’ler ve veth’lerin hepsi ARPHRD_ETHER (1) bildirir. Loopback 772 (ARPHRD_LOOPBACK), tünel arayüzleri (WireGuard, TUN) 65534 (ARPHRD_NONE) bildirir. Bu filtre, L2’de Ethernet çerçeveli olmayan her şeyi kaldırır.
Filtre 2: IFLA_LINKINFO yok
Sanal Ethernet arayüzleri — bridge’ler (Kind: bridge), veth’ler (Kind: veth), VLAN’lar (Kind: vlan), bond’lar (Kind: bond), macvlan vb. — hepsi içinde IFLA_INFO_KIND bulunan iç içe IFLA_LINKINFO attribute’unu taşır. Fiziksel arayüzler (kablolu ve kablosuz) hiç IFLA_LINKINFO taşımaz.
Yani: attribute listesinde IFLA_LINKINFO varsa, arayüz sanaldır ve atlanmalıdır.
Filtre 3: /sys/class/net/<name>/wireless yok
Wi-Fi adaptörleri IFLA_LINKINFO olmayan fiziksel cihazlardır, bu yüzden filtre 1 ve 2’yi geçer. Kernel, rtnetlink link dump’ında bir “kablosuz mu” flag’i sunmaz (IFLA_WIRELESS attribute’u yalnızca wireless extension event’lerinde gönderilir, dump yanıtlarında değil).
Wi-Fi’ı tespit etmenin güvenilir yolu /sys/class/net/<name>/wireless dizininin varlığını kontrol etmektir — kernel bu dizini yalnızca kablosuz arayüzler için oluşturur. Aday arayüz başına bir stat() çağrısı gerektirir, tipik olarak yalnızca birkaç tane olduğundan ihmal edilebilir.
Program
#include <iostream>
#include <cstdint>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
int main() {
int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock_fd < 0) { perror("socket"); return 1; }
struct sockaddr_nl sa{};
sa.nl_family = AF_NETLINK;
sa.nl_pid = getpid();
bind(sock_fd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa));
// Tüm linklerin dump'ını iste (AF_UNSPEC = tüm adres aileleri).
struct {
struct nlmsghdr nlh;
struct ifinfomsg ifm;
} req{};
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.nlh.nlmsg_type = RTM_GETLINK;
req.ifm.ifi_family = AF_UNSPEC;
struct sockaddr_nl dest{};
dest.nl_family = AF_NETLINK;
struct iovec iov = { &req, req.nlh.nlmsg_len };
struct msghdr msg = { &dest, sizeof(dest), &iov, 1, nullptr, 0, 0 };
sendmsg(sock_fd, &msg, 0);
char buf[16384];
bool running = true;
while (running) {
ssize_t len = recv(sock_fd, buf, sizeof(buf), 0);
if (len < 0) break;
auto* nlh = reinterpret_cast<struct nlmsghdr*>(buf);
for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == NLMSG_DONE) { running = false; break; }
if (nlh->nlmsg_type != RTM_NEWLINK) continue;
auto* ifi = reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(nlh));
// Filtre 1: yalnızca Ethernet donanım türü (loopback,
// WireGuard, TUN ve diğer Ethernet olmayan link türlerini hariç tutar).
if (ifi->ifi_type != ARPHRD_ETHER) continue;
// Attribute'ları gez, adı çıkar ve IFLA_LINKINFO'yu tespit et.
int attr_len = IFLA_PAYLOAD(nlh);
struct rtattr* rta = IFLA_RTA(ifi);
std::string name;
bool has_linkinfo = false;
for (; RTA_OK(rta, attr_len); rta = RTA_NEXT(rta, attr_len)) {
if (rta->rta_type == IFLA_IFNAME)
name = reinterpret_cast<char*>(RTA_DATA(rta));
if (rta->rta_type == IFLA_LINKINFO)
has_linkinfo = true;
}
// Filtre 2: sanal arayüzleri atla (bridge, veth, vlan, ...).
// Hepsi içinde IFLA_INFO_KIND olan IFLA_LINKINFO taşır.
if (has_linkinfo) continue;
// Filtre 3: Wi-Fi adaptörlerini atla. Kernel
// /sys/class/net/<name>/wireless dizinini yalnızca kablosuz
// arayüzler için sunar; rtnetlink'in kendisinde dump yanıtında
// "kablosuz mu" flag'i yoktur.
std::string sysfs_wireless = "/sys/class/net/" + name + "/wireless";
struct stat st;
if (stat(sysfs_wireless.c_str(), &st) == 0) continue;
std::cout << name << " (ifindex=" << ifi->ifi_index
<< " flags=0x" << std::hex << ifi->ifi_flags
<< std::dec << ")\n";
}
}
close(sock_fd);
return 0;
}Not: <linux/if_arp.h> ve <net/if.h> ikisi de IFF_* flag’lerini tanımlar ve birlikte include edilirse çakışır. Bu program yalnızca <linux/if_arp.h>‘dan ARPHRD_ETHER‘e ihtiyaç duyar, bu yüzden <net/if.h> atlanmıştır. IFF_* flag sabitlerine ihtiyacın varsa yalnızca <net/if.h>‘ı include et ve ARPHRD_ETHER‘i 1 olarak sabit yaz.
Derleme ve çalıştırma
g++ -o list_phys_eth list_phys_eth.cpp
./list_phys_ethÖzel yetki gerekmez.
Örnek çıktı
Loopback, Wi-Fi, birkaç Docker bridge’i ve veth çifti, WireGuard tünelleri ve tek bir USB Ethernet adaptörü olan bir host’ta, program yalnızca fiziksel kablolu Ethernet linkini yazdırır:
enx8e4f6eac342f (ifindex=467 flags=0x11043)Karşılaştırma için, aynı host’un tam rtnetlink dump’ında 30+ arayüz var — hepsi yukarıdaki üç kontrolle filtrelenir.
Neden sadece arayüz adını kontrol etmiyoruz?
Arayüz adları kuralları takip eder (enX = kablolu Ethernet, wlX = Wi-Fi, brX = bridge, vethX = veth çifti), ama bunlar kuraldır, garanti değil. USB Ethernet adaptörleri varsayılan olarak enx<MAC> kullanır ama udev kuralları veya ip link set name ile yeniden adlandırılabilir. IFLA_LINKINFO + sysfs yaklaşımı, kullanıcının verdiği adı değil kernel’in arayüz hakkında bildirdiklerini denetlediği için yeniden adlandırmaya karşı dayanıklıdır.
Ayrıca bu filtrenin türetildiği tam link-dump programı için rtnetlink API ile tüm ağ linklerini listeleme, tamamlayıcı VLAN filtresi için rtnetlink ile VLAN arayüzlerini filtreleme ve attribute-walking deyimi için rtnetlink rtattr attribute’larını iterate etme yazılarına bak.