00 Netlink nedir
Netlink, Linux'ta kernel ve userspace arasında ağ konfigürasyonu, route yönetimi, netfilter ve sistem gözlemleme gibi iletişimleri sağlayan özel bir IPC mekanizmasıdır.
/proc vs ioctl vs netlink
| Mekanizma | Yön | Avantaj | Dezavantaj |
|---|---|---|---|
| /proc / /sys | Çift yönlü (dosya R/W) | Basit, script dostu | String parsing, yüksek overhead, kernel event yok |
| ioctl | Çift yönlü (senkron) | Hızlı, eski API uyumlu | Genişletilemez, ABI kırılgan, async yok |
| Netlink | Çift yönlü + asenkron | Binary verimli, multicast, async bildirim, genişletilebilir | Karmaşık mesaj yapısı, doğrulama gerekli |
Netlink, ip, iw, ss, tc gibi standart Linux ağ araçlarının altında yatan protokoldür. strace ip link show çalıştırıldığında AF_NETLINK socket'inin açıldığı ve çeşitli sendmsg/recvmsg çağrılarının yapıldığı görülür.
AF_NETLINK socket protokol türleri
| Protokol | Değer | Kullanım |
|---|---|---|
| NETLINK_ROUTE | 0 | Ağ arayüzleri, route tablosu, ARP, neigh, trafik şekillendirme |
| NETLINK_NETFILTER | 12 | iptables/nftables kural yönetimi, conntrack |
| NETLINK_KOBJECT_UEVENT | 15 | Donanım olayları — hotplug, udev |
| NETLINK_AUDIT | 9 | Linux Audit subsystem |
| NETLINK_GENERIC | 16 | Generic Netlink — özel kernel modülleri için |
| NETLINK_CRYPTO | 21 | Kernel crypto API konfigürasyonu |
| NETLINK_XFRM | 6 | IPsec SA/SP yönetimi |
# ip komutunun altında netlink görmek için
strace -e trace=network ip link show 2>&1 | grep -E "socket|sendmsg|recvmsg"
# socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) = 3
# bind(3, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=0}, 12) = 0
# sendmsg(3, {msg_name={...}, msg_iov=[{iov_base="\x28\x00..."}]}, 0) = 40
# recvmsg(3, {msg_iov=[{iov_base="...", iov_len=4096}]}, 0) = 1592
# Sistem geneli netlink socket'lerini listele
ss -f netlink
cat /proc/net/netlink
Bu bölümde
- Netlink: /proc ve ioctl'nin modern alternatifi — binary, async, multicast
- ip/iw/ss/tc araçlarının tamamı altta Netlink kullanır
- NETLINK_ROUTE: ağ konfigürasyonu; NETLINK_GENERIC: özel modüller
- strace ile netlink trafiğini gözlemleyebilirsin
01 Netlink mesaj yapısı
Her Netlink mesajı sabit uzunluklu bir başlık (nlmsghdr) ile başlar, ardından isteğe bağlı payload ve nlattr nitelik dizisi gelir. Tüm alanlar 4 byte hizalamasına tabidir.
nlmsghdr — mesaj başlığı
#include <linux/netlink.h>
struct nlmsghdr {
__u32 nlmsg_len; /* Başlık dahil toplam mesaj uzunluğu (byte) */
__u16 nlmsg_type; /* Mesaj tipi: RTM_GETLINK, RTM_NEWROUTE, NLMSG_DONE vb. */
__u16 nlmsg_flags; /* NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK */
__u32 nlmsg_seq; /* Sequence numarası — istek/yanıt eşleştirme */
__u32 nlmsg_pid; /* Gönderenin PID'i (kernel için 0) */
};
nlmsg_flags değerleri
| Flag | Değer | Anlam |
|---|---|---|
| NLM_F_REQUEST | 0x01 | Bu mesaj bir istektir (request) |
| NLM_F_MULTI | 0x02 | Çok parçalı yanıt — NLMSG_DONE ile biter |
| NLM_F_ACK | 0x04 | Yanıt olarak ACK iste |
| NLM_F_ECHO | 0x08 | Mesajı echo et |
| NLM_F_DUMP | NLM_F_ROOT|NLM_F_MATCH | Tüm nesneleri listele (dump) |
| NLM_F_CREATE | 0x400 | Nesne oluştur (yoksa) |
| NLM_F_REPLACE | 0x100 | Varsa değiştir |
nlattr — Netlink özelliği
#include <linux/netlink.h>
/* Her özellik bu başlıkla başlar */
struct nlattr {
__u16 nla_len; /* Başlık + değer uzunluğu (byte) */
__u16 nla_type; /* Özellik tipi (IFLA_MTU, RTA_DST vb.) */
/* Ardından nla_len - sizeof(struct nlattr) byte değer gelir */
};
/* Makrolar: hizalama ve gezinme */
#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
#define NLMSG_HDRLEN ((int)NLMSG_ALIGN(sizeof(struct nlmsghdr)))
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_HDRLEN))
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len <= (len))
/* nlattr hizalama */
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
Mesaj layout diyagramı
[ nlmsghdr (16 byte) ][ payload (struct ifinfomsg/rtmsg vb.) ][ nlattr ][ nlattr ][ nlattr ]... ←───── nlmsg_len ─────────────────────────────────────────────────────────────────→
Bu bölümde
- nlmsghdr: len/type/flags/seq/pid — 16 byte sabit başlık
- nlmsg_flags: NLM_F_REQUEST (istek) | NLM_F_DUMP (tümünü al)
- nlattr: type-length-value; 4 byte hizalamalı; iç içe (nested) olabilir
- NLMSG_OK + NLMSG_NEXT: çok parçalı yanıtları döngüyle işlemek için
02 NETLINK_ROUTE — rtnetlink
rtnetlink, NETLINK_ROUTE ailesi üzerinde çalışan ağ konfigürasyon protokolüdür. ip komutunun kullandığı aynı API'dir: arayüz, adres, route, neigh yönetimi.
Önemli RTM_ mesaj tipleri
| Tip | Değer | Açıklama |
|---|---|---|
| RTM_NEWLINK / RTM_DELLINK | 16/17 | Ağ arayüzü ekle/sil (ip link add/del) |
| RTM_GETLINK | 18 | Arayüz bilgilerini al (ip link show) |
| RTM_NEWADDR / RTM_DELADDR | 20/21 | IP adresi ekle/sil (ip addr add/del) |
| RTM_GETADDR | 22 | IP adres listesi (ip addr show) |
| RTM_NEWROUTE / RTM_DELROUTE | 24/25 | Route ekle/sil (ip route add/del) |
| RTM_GETROUTE | 26 | Route tablosunu al (ip route show) |
| RTM_NEWNEIGH / RTM_GETNEIGH | 28/30 | ARP/neighbor tablosu (ip neigh) |
RTM_GETLINK — ifinfomsg yapısı
#include <linux/rtnetlink.h>
#include <linux/if_link.h>
/* RTM_GETLINK / RTM_NEWLINK için payload */
struct ifinfomsg {
unsigned char ifi_family; /* AF_UNSPEC */
unsigned char __ifi_pad;
unsigned short ifi_type; /* ARPHRD_ETHER vb. */
int ifi_index; /* Arayüz indeksi */
unsigned int ifi_flags; /* IFF_UP, IFF_RUNNING vb. */
unsigned int ifi_change; /* Değişen bayraklar için maske */
};
/* IFLA_ özellik tipleri (nlattr nla_type) */
/* IFLA_IFNAME: char[] — arayüz adı (eth0, lo, vb.) */
/* IFLA_MTU: uint32 — MTU değeri */
/* IFLA_LINK: int32 — üst arayüz indeksi */
/* IFLA_STATS: struct rtnl_link_stats */
/* RTM_GETADDR için payload */
struct ifaddrmsg {
__u8 ifa_family; /* AF_INET veya AF_INET6 */
__u8 ifa_prefixlen; /* Önek uzunluğu (prefix length) */
__u8 ifa_flags; /* IFA_F_PERMANENT, IFA_F_TEMPORARY */
__u8 ifa_scope; /* RT_SCOPE_HOST, RT_SCOPE_LINK, vb. */
__u32 ifa_index; /* Arayüz indeksi */
};
Bu bölümde
- RTM_GETLINK: arayüz dump — ip link show'un altı
- RTM_GETADDR: adres dump — ip addr show'un altı
- RTM_GETROUTE: route dump — ip route show'un altı
- ifinfomsg/ifaddrmsg/rtmsg: payload yapıları; nlattr ile özellikler
03 C ile netlink client
Ham POSIX socket API'siyle netlink client: socket() → bind() → sendmsg() → recvmsg() → mesaj parse. Arayüz listesi alma örneği.
Tam C örneği — arayüz listesi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#define BUFSIZE 8192
/* Netlink isteği gönder */
static int send_nl_req(int sock_fd, uint16_t msg_type, uint16_t flags)
{
struct {
struct nlmsghdr nlh;
struct ifinfomsg ifm;
} req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nlh.nlmsg_type = msg_type;
req.nlh.nlmsg_flags = flags | NLM_F_REQUEST;
req.nlh.nlmsg_seq = 1;
req.nlh.nlmsg_pid = getpid();
req.ifm.ifi_family = AF_UNSPEC;
struct sockaddr_nl sa = { .nl_family = AF_NETLINK };
struct iovec iov = { &req, req.nlh.nlmsg_len };
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
return sendmsg(sock_fd, &msg, 0);
}
/* nlattr'lardan arayüz adını al */
static void parse_link(struct nlmsghdr *nlh)
{
struct ifinfomsg *ifm = NLMSG_DATA(nlh);
struct rtattr *rta;
int rta_len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifm));
printf("Index: %d Flags: %s\n",
ifm->ifi_index,
(ifm->ifi_flags & IFF_UP) ? "UP" : "DOWN");
for (rta = IFLA_RTA(ifm); RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
switch (rta->rta_type) {
case IFLA_IFNAME:
printf(" Name: %s\n", (char *)RTA_DATA(rta));
break;
case IFLA_MTU:
printf(" MTU: %u\n", *(uint32_t *)RTA_DATA(rta));
break;
default:
break;
}
}
}
int main(void)
{
/* 1. Socket oluştur */
int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock_fd < 0) { perror("socket"); return 1; }
/* 2. Bind (pid=0: kernel otomatik atar) */
struct sockaddr_nl sa = { .nl_family = AF_NETLINK, .nl_pid = 0 };
if (bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind"); return 1;
}
/* 3. RTM_GETLINK isteği gönder (dump modu) */
if (send_nl_req(sock_fd, RTM_GETLINK, NLM_F_DUMP) < 0) {
perror("sendmsg"); return 1;
}
/* 4. Yanıtları oku ve parse et */
char buf[BUFSIZE];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl src;
struct msghdr msg = {
.msg_name = &src,
.msg_namelen = sizeof(src),
.msg_iov = &iov,
.msg_iovlen = 1,
};
while (1) {
ssize_t len = recvmsg(sock_fd, &msg, 0);
if (len < 0) { perror("recvmsg"); break; }
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
for (; NLMSG_OK(nlh, (unsigned)len); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == NLMSG_DONE)
goto done;
if (nlh->nlmsg_type == NLMSG_ERROR) {
fprintf(stderr, "Netlink hatası\n");
goto done;
}
if (nlh->nlmsg_type == RTM_NEWLINK)
parse_link(nlh);
}
}
done:
close(sock_fd);
return 0;
}
gcc -Wall -o netlink_iflist netlink_iflist.c
./netlink_iflist
# Beklenen çıktı:
# Index: 1 Flags: UP
# Name: lo
# MTU: 65536
# Index: 2 Flags: UP
# Name: eth0
# MTU: 1500
Bu bölümde
- socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE): netlink soketi
- bind() + sendmsg(): istek gönder; recvmsg() + NLMSG_OK/NEXT: yanıt parse
- NLM_F_DUMP: tüm nesneleri listele; NLMSG_DONE: dump sonu sinyali
- RTA_OK + RTA_NEXT: rtattr (nlattr benzeri) döngüsü
04 libnl kütüphanesi
libnl, ham netlink API'sini saran yüksek seviyeli C kütüphanesidir. Mesaj oluşturma, hata yönetimi ve callback mekanizmaları ile geliştirme sürecini kolaylaştırır.
libnl kurulumu
sudo apt install libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
# Derleme bayrağı (pkg-config ile)
pkg-config --cflags --libs libnl-3.0 libnl-route-3.0
# -I/usr/include/libnl3 -lnl-3 -lnl-route-3
libnl temel API
#include <netlink/netlink.h>
#include <netlink/route/link.h>
int main(void)
{
/* Socket oluştur ve kernel'e bağlan */
struct nl_sock *sock = nl_socket_alloc();
if (!sock) { fprintf(stderr, "nl_socket_alloc hatası\n"); return 1; }
int err = nl_connect(sock, NETLINK_ROUTE);
if (err < 0) {
fprintf(stderr, "nl_connect: %s\n", nl_geterror(err));
return 1;
}
/* Link cache doldur (RTM_GETLINK dump) */
struct nl_cache *cache;
err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache);
if (err < 0) {
fprintf(stderr, "rtnl_link_alloc_cache: %s\n", nl_geterror(err));
return 1;
}
/* Cache'deki her arayüzü gezin */
struct rtnl_link *link = (struct rtnl_link *)nl_cache_get_first(cache);
while (link) {
printf("Index: %-3d Name: %-12s MTU: %u Flags: 0x%08X\n",
rtnl_link_get_ifindex(link),
rtnl_link_get_name(link),
rtnl_link_get_mtu(link),
rtnl_link_get_flags(link));
link = (struct rtnl_link *)nl_cache_get_next((struct nl_object *)link);
}
nl_cache_free(cache);
nl_socket_free(sock);
return 0;
}
gcc -Wall -o libnl_iflist libnl_iflist.c \
$(pkg-config --cflags --libs libnl-3.0 libnl-route-3.0)
libnl ile IP adresi okuma
#include <netlink/route/addr.h>
#include <arpa/inet.h>
void list_addresses(struct nl_sock *sock)
{
struct nl_cache *addr_cache;
rtnl_addr_alloc_cache(sock, &addr_cache);
struct rtnl_addr *addr = (struct rtnl_addr *)nl_cache_get_first(addr_cache);
char buf[INET6_ADDRSTRLEN];
while (addr) {
struct nl_addr *local = rtnl_addr_get_local(addr);
if (local) {
nl_addr2str(local, buf, sizeof(buf));
printf(" IF idx: %d Addr: %s/%d\n",
rtnl_addr_get_ifindex(addr),
buf,
rtnl_addr_get_prefixlen(addr));
}
addr = (struct rtnl_addr *)nl_cache_get_next((struct nl_object *)addr);
}
nl_cache_free(addr_cache);
}
Bu bölümde
- nl_socket_alloc + nl_connect: bağlantı yönetimi
- rtnl_link_alloc_cache: tek çağrıda tüm link dump
- nl_cache_get_first + nl_cache_get_next: cache iterasyonu
- libnl-route-3: rtnl_link_*, rtnl_addr_*, rtnl_route_* API'leri
05 Generic Netlink (genl)
Generic Netlink, kernel modüllerinin kendi netlink aile (family) tanımlamalarına olanak tanıyan genişletilebilir bir çerçevedir. Özel kernel-userspace iletişim protokolleri için idealdir.
Generic Netlink mimarisi
Userspace (genl socket) → NETLINK_GENERIC → genetlink.ko → Aile araması (by name) → Kernel modülü handler
Kernel modülü tarafı
#include <linux/module.h>
#include <net/genetlink.h>
/* Komut ve özellik tanımları */
enum mymod_cmd {
MYMOD_CMD_UNSPEC = 0,
MYMOD_CMD_HELLO,
MYMOD_CMD_GET_DATA,
__MYMOD_CMD_MAX,
};
enum mymod_attr {
MYMOD_ATTR_UNSPEC = 0,
MYMOD_ATTR_MSG, /* string */
MYMOD_ATTR_VALUE, /* u32 */
__MYMOD_ATTR_MAX,
};
/* Özellik politikası (tip ve boyut doğrulaması) */
static const struct nla_policy mymod_policy[__MYMOD_ATTR_MAX] = {
[MYMOD_ATTR_MSG] = { .type = NLA_STRING, .len = 256 },
[MYMOD_ATTR_VALUE] = { .type = NLA_U32 },
};
/* Handler: HELLO komutu */
static int mymod_hello(struct sk_buff *skb, struct genl_info *info)
{
char *msg = "Merhaba Userspace!";
if (info->attrs[MYMOD_ATTR_MSG])
msg = nla_data(info->attrs[MYMOD_ATTR_MSG]);
pr_info("mymod: HELLO alındı: %s\n", msg);
/* Yanıt gönder */
struct sk_buff *reply = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
void *reply_hdr = genlmsg_put_reply(reply, info, &mymod_family, 0, MYMOD_CMD_HELLO);
nla_put_string(reply, MYMOD_ATTR_MSG, "Merhaba!");
genlmsg_end(reply, reply_hdr);
return genlmsg_reply(reply, info);
}
/* Operasyon tablosu */
static const struct genl_ops mymod_ops[] = {
{
.cmd = MYMOD_CMD_HELLO,
.flags = 0,
.policy = mymod_policy,
.doit = mymod_hello,
},
};
/* Aile tanımı */
static struct genl_family mymod_family = {
.name = "MYMODULE",
.version = 1,
.maxattr = __MYMOD_ATTR_MAX - 1,
.ops = mymod_ops,
.n_ops = ARRAY_SIZE(mymod_ops),
};
static int __init mymod_init(void) { return genl_register_family(&mymod_family); }
static void __exit mymod_exit(void) { genl_unregister_family(&mymod_family); }
module_init(mymod_init);
module_exit(mymod_exit);
MODULE_LICENSE("GPL");
Userspace genl client (libnl-genl)
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
int main(void)
{
struct nl_sock *sock = nl_socket_alloc();
genl_connect(sock);
/* Aile ID'sini çözümle */
int family_id = genl_ctrl_resolve(sock, "MYMODULE");
if (family_id < 0) {
fprintf(stderr, "Aile bulunamadı: %s\n", nl_geterror(family_id));
return 1;
}
printf("Family ID: %d\n", family_id);
/* HELLO mesajı oluştur ve gönder */
struct nl_msg *msg = nlmsg_alloc();
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
family_id, 0, 0, MYMOD_CMD_HELLO, 1);
nla_put_string(msg, MYMOD_ATTR_MSG, "Merhaba Kernel!");
nl_send_auto(sock, msg);
nlmsg_free(msg);
/* Yanıtı bekle */
nl_recvmsgs_default(sock);
nl_socket_free(sock);
return 0;
}
Bu bölümde
- genl_register_family: kernel modülü kendi netlink ailesi kaydeder
- genl_ctrl_resolve("MYMODULE"): dinamik family ID çözümleme
- policy: nla_policy ile attr tipi ve uzunluk doğrulaması — güvenli parsing
- genlmsg_put + nla_put_*: mesaj inşa API'si; nl_send_auto: gönder
06 nl80211 — Linux WiFi kontrol API
nl80211, Linux'ta WiFi konfigürasyonunun tüm arayüzüdür. Tarama, bağlanma, station dump, AP modu — hepsi nl80211 üzerinden. iw aracı nl80211'i kullanır.
nl80211 temelleri
# iw'nin altındaki nl80211 komutları görmek için
iw dev wlan0 scan dump
# nl80211 komut ID'lerini listele (kernel kaynağından)
grep NL80211_CMD /usr/include/linux/nl80211.h | head -30
# Arayüz bilgisi al
iw dev wlan0 info
# Station listesi (AP modundaysa bağlı cihazlar)
iw dev wlan0 station dump
# Tarama tetikle
iw dev wlan0 scan trigger
# Tarama sonuçlarını oku
iw dev wlan0 scan dump
nl80211 ile C — arayüz bilgisi
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <linux/nl80211.h>
#include <net/if.h>
struct wifi_info {
char ifname[IFNAMSIZ];
uint32_t ifindex;
uint32_t wiphy;
uint32_t iftype; /* NL80211_IFTYPE_STATION, AP, MONITOR vb. */
};
/* Callback: yanıt mesajını parse et */
static int nl80211_cb(struct nl_msg *msg, void *arg)
{
struct wifi_info *info = (struct wifi_info *)arg;
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
nla_parse(attrs, NL80211_ATTR_MAX,
genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (attrs[NL80211_ATTR_IFNAME])
strncpy(info->ifname, nla_get_string(attrs[NL80211_ATTR_IFNAME]),
sizeof(info->ifname) - 1);
if (attrs[NL80211_ATTR_IFINDEX])
info->ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
if (attrs[NL80211_ATTR_WIPHY])
info->wiphy = nla_get_u32(attrs[NL80211_ATTR_WIPHY]);
if (attrs[NL80211_ATTR_IFTYPE])
info->iftype = nla_get_u32(attrs[NL80211_ATTR_IFTYPE]);
return NL_OK;
}
int main(void)
{
struct nl_sock *sock = nl_socket_alloc();
genl_connect(sock);
int nl80211_id = genl_ctrl_resolve(sock, "nl80211");
if (nl80211_id < 0) {
fprintf(stderr, "nl80211 bulunamadı — cfg80211 yüklü mü?\n");
return 1;
}
struct wifi_info info = {0};
nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, nl80211_cb, &info);
struct nl_msg *msg = nlmsg_alloc();
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
nl80211_id, 0, NLM_F_DUMP,
NL80211_CMD_GET_INTERFACE, 0);
nl_send_auto(sock, msg);
nlmsg_free(msg);
nl_recvmsgs_default(sock);
printf("Arayüz: %s (idx=%u, wiphy=%u, type=%u)\n",
info.ifname, info.ifindex, info.wiphy, info.iftype);
nl_socket_free(sock);
return 0;
}
Tarama tetikleme
/* Tarama tetikle (özet) */
void trigger_scan(struct nl_sock *sock, int nl80211_id, uint32_t ifindex)
{
struct nl_msg *msg = nlmsg_alloc();
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
nl80211_id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex);
/* İsteğe bağlı: belirli SSID'leri tara */
struct nlattr *ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
nla_put(msg, 1, 0, ""); /* Boş SSID = broadcast scan */
nla_nest_end(msg, ssids);
nl_send_auto(sock, msg);
nlmsg_free(msg);
/* Tarama tamamlanana kadar bekle (NL80211_CMD_NEW_SCAN_RESULTS event) */
/* Gerçek uygulamada multicast group'a abone olunur: */
/* nl_socket_add_membership(sock, scan_mcgrp_id); */
}
Bu bölümde
- nl80211: tüm Linux WiFi konfigürasyonunun API'si — iw aracının altı
- genl_ctrl_resolve("nl80211"): family ID; NL80211_CMD_* ile komutlar
- nla_parse: attrs dizisine nlattr ayrıştırma; nla_get_u32/string ile değer al
- NL80211_CMD_TRIGGER_SCAN: tarama başlat; multicast ile sonuç bildirimi
07 Kernel tarafı — netlink_kernel_create
Kernel modülü, netlink_kernel_create ile kendi netlink socket'ini açabilir. Userspace'ten gelen mesajları input callback'te işler ve yanıt gönderir.
Minimal kernel netlink modülü
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/net_namespace.h>
#define NETLINK_MYMOD 31 /* Özel protocol numarası (17-31 kullanılabilir) */
static struct sock *nl_sk = NULL;
/* Kernel tarafı input callback: userspace'ten mesaj geldiğinde çağrılır */
static void nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
char *msg = "Kernel'dan merhaba!";
int msg_size = strlen(msg) + 1;
nlh = (struct nlmsghdr *)skb->data;
pid = nlh->nlmsg_pid; /* Gönderenin PID'i */
pr_info("nl_module: PID %d'den mesaj: %s\n",
pid, (char *)nlmsg_data(nlh));
/* Yanıt gönder */
skb_out = nlmsg_new(msg_size, GFP_KERNEL);
if (!skb_out) {
pr_err("nl_module: nlmsg_new başarısız\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; /* unicast */
strncpy(nlmsg_data(nlh), msg, msg_size);
int ret = nlmsg_unicast(nl_sk, skb_out, pid);
if (ret < 0)
pr_err("nl_module: nlmsg_unicast hatası: %d\n", ret);
}
static int __init nl_module_init(void)
{
struct netlink_kernel_cfg cfg = {
.input = nl_recv_msg,
};
nl_sk = netlink_kernel_create(&init_net, NETLINK_MYMOD, &cfg);
if (!nl_sk) {
pr_err("nl_module: netlink_kernel_create başarısız\n");
return -ENOMEM;
}
pr_info("nl_module: NETLINK_MYMOD=%d kaydedildi\n", NETLINK_MYMOD);
return 0;
}
static void __exit nl_module_exit(void)
{
netlink_kernel_release(nl_sk);
pr_info("nl_module: kaldırıldı\n");
}
module_init(nl_module_init);
module_exit(nl_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Minimal Netlink Kernel Module");
Userspace client (kernel modülü için)
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define NETLINK_MYMOD 31
#define BUFSIZE 256
int main(void)
{
int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_MYMOD);
struct sockaddr_nl src_addr = {
.nl_family = AF_NETLINK,
.nl_pid = getpid(),
};
bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
/* Mesaj oluştur */
char buf[NLMSG_SPACE(BUFSIZE)];
memset(buf, 0, sizeof(buf));
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
nlh->nlmsg_len = NLMSG_SPACE(BUFSIZE);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strncpy(NLMSG_DATA(nlh), "Merhaba Kernel!", BUFSIZE - 1);
/* Kernel'e gönder (pid=0: kernel hedef) */
struct sockaddr_nl dest_addr = {
.nl_family = AF_NETLINK,
.nl_pid = 0, /* Kernel */
};
struct iovec iov = { buf, nlh->nlmsg_len };
struct msghdr msg = {
.msg_name = &dest_addr,
.msg_namelen = sizeof(dest_addr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
sendmsg(sock_fd, &msg, 0);
/* Yanıtı bekle */
memset(buf, 0, sizeof(buf));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
recvmsg(sock_fd, &msg, 0);
printf("Kernel yanıtı: %s\n", (char *)NLMSG_DATA(nlh));
close(sock_fd);
return 0;
}
Bu bölümde
- netlink_kernel_create: kernel modülü tarafında socket aç; cfg.input = callback
- nlmsg_unicast: tek bir process'e yanıt; nlmsg_multicast: gruba yayın
- NETLINK_MYMOD 31: özel protocol numarası (17–31 kullanıcı tanımlı)
- Userspace: socket(AF_NETLINK, SOCK_RAW, NETLINK_MYMOD) + pid=0 hedef
08 Pratik uygulamalar
Üç senaryo: C ile route tablosunu listele, Generic Netlink aile + userspace client, nl80211 ile scan sonuçlarını oku.
C ile route tablosunu listele
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
static void print_route(struct nlmsghdr *nlh)
{
struct rtmsg *rtm = NLMSG_DATA(nlh);
struct rtattr *rta;
int rta_len = RTM_PAYLOAD(nlh);
char dst_buf[INET_ADDRSTRLEN] = "0.0.0.0";
char gw_buf[INET_ADDRSTRLEN] = "-";
uint32_t oif = 0;
/* Yalnızca main route tablosu */
if (rtm->rtm_table != RT_TABLE_MAIN)
return;
for (rta = RTM_RTA(rtm); RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
switch (rta->rta_type) {
case RTA_DST:
inet_ntop(rtm->rtm_family, RTA_DATA(rta), dst_buf, sizeof(dst_buf));
break;
case RTA_GATEWAY:
inet_ntop(rtm->rtm_family, RTA_DATA(rta), gw_buf, sizeof(gw_buf));
break;
case RTA_OIF:
oif = *(uint32_t *)RTA_DATA(rta);
break;
default:
break;
}
}
char ifname[IF_NAMESIZE];
if_indextoname(oif, ifname);
printf("%-20s/%-3u GW: %-16s IF: %s\n",
dst_buf, rtm->rtm_dst_len, gw_buf, ifname);
}
int main(void)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
struct sockaddr_nl sa = { .nl_family = AF_NETLINK };
bind(fd, (struct sockaddr *)&sa, sizeof(sa));
/* RTM_GETROUTE isteği */
struct { struct nlmsghdr nlh; struct rtmsg rtm; } req = {
.nlh = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
.nlmsg_type = RTM_GETROUTE,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
.nlmsg_seq = 1,
},
.rtm = { .rtm_family = AF_INET },
};
send(fd, &req, req.nlh.nlmsg_len, 0);
char buf[32768];
printf("%-20s %-19s %s\n", "Hedef/Prefix", "Gateway", "Arayüz");
printf("%-60s\n", "------------------------------------------------------------");
while (1) {
ssize_t len = recv(fd, buf, sizeof(buf), 0);
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
for (; NLMSG_OK(nlh, (unsigned)len); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == NLMSG_DONE) goto done;
if (nlh->nlmsg_type == RTM_NEWROUTE) print_route(nlh);
}
}
done:
close(fd);
return 0;
}
gcc -Wall -o route_list route_list.c
./route_list
# Beklenen çıktı:
# Hedef/Prefix Gateway Arayüz
# ----------------------------------------------------------------
# 0.0.0.0 /0 GW: 192.168.1.1 IF: eth0
# 192.168.1.0 /24 GW: - IF: eth0
# 127.0.0.0 /8 GW: - IF: lo
nl80211 ile scan sonuçlarını oku
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <linux/nl80211.h>
static int scan_cb(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
nla_parse(attrs, NL80211_ATTR_MAX,
genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (attrs[NL80211_ATTR_BSS]) {
struct nlattr *bss[NL80211_BSS_MAX + 1];
nla_parse_nested(bss, NL80211_BSS_MAX,
attrs[NL80211_ATTR_BSS], NULL);
if (bss[NL80211_BSS_BSSID]) {
uint8_t *bssid = nla_data(bss[NL80211_BSS_BSSID]);
printf("BSSID: %02X:%02X:%02X:%02X:%02X:%02X",
bssid[0], bssid[1], bssid[2],
bssid[3], bssid[4], bssid[5]);
}
if (bss[NL80211_BSS_SIGNAL_MBM]) {
int signal = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
printf(" Sinyal: %d dBm", signal / 100);
}
printf("\n");
}
return NL_SKIP;
}
void read_scan_results(uint32_t ifindex)
{
struct nl_sock *sock = nl_socket_alloc();
genl_connect(sock);
int nl80211_id = genl_ctrl_resolve(sock, "nl80211");
nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, scan_cb, NULL);
struct nl_msg *msg = nlmsg_alloc();
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
nl80211_id, 0, NLM_F_DUMP,
NL80211_CMD_GET_SCAN, 0);
nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex);
nl_send_auto(sock, msg);
nlmsg_free(msg);
nl_recvmsgs_default(sock);
nl_socket_free(sock);
}
gcc -Wall -o wifi_scan wifi_scan.c \
$(pkg-config --cflags --libs libnl-3.0 libnl-genl-3.0)
# Taramayı önce tetikle
sudo iw dev wlan0 scan trigger
# Sonuçları oku (root veya CAP_NET_ADMIN gerekli)
sudo ./wifi_scan
# Beklenen çıktı:
# BSSID: AA:BB:CC:DD:EE:FF Sinyal: -45 dBm
# BSSID: 11:22:33:44:55:66 Sinyal: -72 dBm
Bu bölümde
- RTM_GETROUTE dump: route tablosunu oku; RTA_DST/GATEWAY/OIF ile parse
- nl80211 scan dump: NL80211_CMD_GET_SCAN + NLM_F_DUMP; BSS attr ile AP bilgisi
- nla_parse_nested: iç içe nlattr (BSS içindeki BSSID, signal vb.)
- CAP_NET_ADMIN: çoğu netlink işlemi bu capability'yi gerektirir