📄 netlink.c
字号:
/* * BIRD -- Linux Netlink Interface * * (c) 1999--2000 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */#include <stdio.h>#include <fcntl.h>#include <net/if.h>#include <sys/socket.h>#include <sys/uio.h>#include <errno.h>#undef LOCAL_DEBUG#include "nest/bird.h"#include "nest/route.h"#include "nest/protocol.h"#include "nest/iface.h"#include "lib/timer.h"#include "lib/unix.h"#include "lib/krt.h"#include "lib/socket.h"#include "lib/string.h"#include "conf/conf.h"#include <asm/types.h>#include <linux/netlink.h>#include <linux/rtnetlink.h>#ifndef MSG_TRUNC /* Hack: Several versions of glibc miss this one :( */#define MSG_TRUNC 0x20#endif/* * Synchronous Netlink interface */static int nl_sync_fd = -1; /* Unix socket for synchronous netlink actions */static u32 nl_sync_seq; /* Sequence number of last request sent */static byte *nl_rx_buffer; /* Receive buffer */#define NL_RX_SIZE 8192static struct nlmsghdr *nl_last_hdr; /* Recently received packet */static unsigned int nl_last_size;static voidnl_open(void){ if (nl_sync_fd < 0) { nl_sync_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (nl_sync_fd < 0) die("Unable to open rtnetlink socket: %m"); nl_sync_seq = now; nl_rx_buffer = xmalloc(NL_RX_SIZE); }}static voidnl_send(struct nlmsghdr *nh){ struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; nh->nlmsg_pid = 0; nh->nlmsg_seq = ++nl_sync_seq; if (sendto(nl_sync_fd, nh, nh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) die("rtnetlink sendto: %m"); nl_last_hdr = NULL;}static voidnl_request_dump(int cmd){ struct { struct nlmsghdr nh; struct rtgenmsg g; } req; req.nh.nlmsg_type = cmd; req.nh.nlmsg_len = sizeof(req); req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; req.g.rtgen_family = BIRD_PF; nl_send(&req.nh);}static struct nlmsghdr *nl_get_reply(void){ for(;;) { if (!nl_last_hdr) { struct iovec iov = { nl_rx_buffer, NL_RX_SIZE }; struct sockaddr_nl sa; struct msghdr m = { (struct sockaddr *) &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; int x = recvmsg(nl_sync_fd, &m, 0); if (x < 0) die("nl_get_reply: %m"); if (sa.nl_pid) /* It isn't from the kernel */ { DBG("Non-kernel packet\n"); continue; } nl_last_size = x; nl_last_hdr = (void *) nl_rx_buffer; if (m.msg_flags & MSG_TRUNC) bug("nl_get_reply: got truncated reply which should be impossible"); } if (NLMSG_OK(nl_last_hdr, nl_last_size)) { struct nlmsghdr *h = nl_last_hdr; nl_last_hdr = NLMSG_NEXT(h, nl_last_size); if (h->nlmsg_seq != nl_sync_seq) { log(L_WARN "nl_get_reply: Ignoring out of sequence netlink packet (%x != %x)", h->nlmsg_seq, nl_sync_seq); continue; } return h; } if (nl_last_size) log(L_WARN "nl_get_reply: Found packet remnant of size %d", nl_last_size); nl_last_hdr = NULL; }}static struct rate_limit rl_netlink_err;static intnl_error(struct nlmsghdr *h){ struct nlmsgerr *e; int ec; if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { log(L_WARN "Netlink: Truncated error message received"); return ENOBUFS; } e = (struct nlmsgerr *) NLMSG_DATA(h); ec = -e->error; if (ec) log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec)); return ec;}static struct nlmsghdr *nl_get_scan(void){ struct nlmsghdr *h = nl_get_reply(); if (h->nlmsg_type == NLMSG_DONE) return NULL; if (h->nlmsg_type == NLMSG_ERROR) { nl_error(h); return NULL; } return h;}static intnl_exchange(struct nlmsghdr *pkt){ struct nlmsghdr *h; nl_send(pkt); for(;;) { h = nl_get_reply(); if (h->nlmsg_type == NLMSG_ERROR) break; log(L_WARN "nl_exchange: Unexpected reply received"); } return nl_error(h);}/* * Netlink attributes */static int nl_attr_len;static void *nl_checkin(struct nlmsghdr *h, int lsize){ nl_attr_len = h->nlmsg_len - NLMSG_LENGTH(lsize); if (nl_attr_len < 0) { log(L_ERR "nl_checkin: underrun by %d bytes", -nl_attr_len); return NULL; } return NLMSG_DATA(h);}static intnl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize){ int max = ksize / sizeof(struct rtattr *); bzero(k, ksize); while (RTA_OK(a, nl_attr_len)) { if (a->rta_type < max) k[a->rta_type] = a; a = RTA_NEXT(a, nl_attr_len); } if (nl_attr_len) { log(L_ERR "nl_parse_attrs: remnant of size %d", nl_attr_len); return 0; } else return 1;}static voidnl_add_attr_u32(struct nlmsghdr *h, unsigned maxsize, int code, u32 data){ unsigned len = RTA_LENGTH(4); struct rtattr *a; if (NLMSG_ALIGN(h->nlmsg_len) + len > maxsize) bug("nl_add_attr32: packet buffer overflow"); a = (struct rtattr *)((char *)h + NLMSG_ALIGN(h->nlmsg_len)); a->rta_type = code; a->rta_len = len; memcpy(RTA_DATA(a), &data, 4); h->nlmsg_len = NLMSG_ALIGN(h->nlmsg_len) + len;}static voidnl_add_attr_ipa(struct nlmsghdr *h, unsigned maxsize, int code, ip_addr ipa){ unsigned len = RTA_LENGTH(sizeof(ipa)); struct rtattr *a; if (NLMSG_ALIGN(h->nlmsg_len) + len > maxsize) bug("nl_add_attr_ipa: packet buffer overflow"); a = (struct rtattr *)((char *)h + NLMSG_ALIGN(h->nlmsg_len)); a->rta_type = code; a->rta_len = len; ipa_hton(ipa); memcpy(RTA_DATA(a), &ipa, sizeof(ipa)); h->nlmsg_len = NLMSG_ALIGN(h->nlmsg_len) + len;}/* * Scanning of interfaces */static voidnl_parse_link(struct nlmsghdr *h, int scan){ struct ifinfomsg *i; struct rtattr *a[IFLA_WIRELESS+1]; int new = h->nlmsg_type == RTM_NEWLINK; struct iface f; struct iface *ifi; char *name; u32 mtu; unsigned int fl; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), a, sizeof(a))) return; if (!a[IFLA_IFNAME] || RTA_PAYLOAD(a[IFLA_IFNAME]) < 2 || !a[IFLA_MTU] || RTA_PAYLOAD(a[IFLA_MTU]) != 4) { if (scan || !a[IFLA_WIRELESS]) log(L_ERR "nl_parse_link: Malformed message received"); return; } name = RTA_DATA(a[IFLA_IFNAME]); memcpy(&mtu, RTA_DATA(a[IFLA_MTU]), sizeof(u32)); ifi = if_find_by_index(i->ifi_index); if (!new) { DBG("KIF: IF%d(%s) goes down\n", i->ifi_index, name); if (ifi && !scan) { memcpy(&f, ifi, sizeof(struct iface)); f.flags |= IF_ADMIN_DOWN; if_update(&f); } } else { DBG("KIF: IF%d(%s) goes up (mtu=%d,flg=%x)\n", i->ifi_index, name, mtu, i->ifi_flags); if (ifi) memcpy(&f, ifi, sizeof(f)); else { bzero(&f, sizeof(f)); f.index = i->ifi_index; } strncpy(f.name, RTA_DATA(a[IFLA_IFNAME]), sizeof(f.name)-1); f.mtu = mtu; f.flags = 0; fl = i->ifi_flags; if (fl & IFF_UP) f.flags |= IF_LINK_UP; if (fl & IFF_LOOPBACK) /* Loopback */ f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE; else if (fl & IFF_POINTOPOINT) /* PtP */ f.flags |= IF_MULTICAST; else if (fl & IFF_BROADCAST) /* Broadcast */ f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST; else f.flags |= IF_MULTIACCESS; /* NBMA */ if_update(&f); }}static voidnl_parse_addr(struct nlmsghdr *h){ struct ifaddrmsg *i; struct rtattr *a[IFA_ANYCAST+1]; int new = h->nlmsg_type == RTM_NEWADDR; struct ifa ifa; struct iface *ifi; int scope; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFA_RTA(i), a, sizeof(a))) return; if (i->ifa_family != BIRD_AF) return; if (!a[IFA_ADDRESS] || RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip_addr)#ifdef IPV6 || a[IFA_LOCAL] && RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)#else || !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) || (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr))#endif ) { log(L_ERR "nl_parse_addr: Malformed message received"); return; } ifi = if_find_by_index(i->ifa_index); if (!ifi) { log(L_ERR "KIF: Received address message for unknown interface %d", i->ifa_index); return; } bzero(&ifa, sizeof(ifa)); ifa.iface = ifi; if (i->ifa_flags & IFA_F_SECONDARY) ifa.flags |= IA_SECONDARY; /* IFA_LOCAL can be unset for IPv6 interfaces */ memcpy(&ifa.ip, RTA_DATA(a[IFA_LOCAL] ? : a[IFA_ADDRESS]), sizeof(ifa.ip)); ipa_ntoh(ifa.ip); ifa.pxlen = i->ifa_prefixlen; if (i->ifa_prefixlen > BITS_PER_IP_ADDRESS || i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 1) { log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen); new = 0; } if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS) { ifa.flags |= IA_UNNUMBERED; memcpy(&ifa.opposite, RTA_DATA(a[IFA_ADDRESS]), sizeof(ifa.opposite)); ipa_ntoh(ifa.opposite); ifa.prefix = ifa.brd = ifa.opposite; } else { ip_addr netmask = ipa_mkmask(ifa.pxlen); ip_addr xbrd; ifa.prefix = ipa_and(ifa.ip, netmask); ifa.brd = ipa_or(ifa.ip, ipa_not(netmask));#ifndef IPV6 if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 2) ifa.opposite = ipa_opposite(ifa.ip, i->ifa_prefixlen); if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST]) { memcpy(&xbrd, RTA_DATA(a[IFA_BROADCAST]), sizeof(xbrd)); ipa_ntoh(xbrd); if (ipa_equal(xbrd, ifa.prefix) || ipa_equal(xbrd, ifa.brd)) ifa.brd = xbrd; else if (ifi->flags & IF_TMP_DOWN) /* Complain only during the first scan */ log(L_ERR "KIF: Invalid broadcast address %I for %s", xbrd, ifi->name); }#endif } scope = ipa_classify(ifa.ip); if (scope < 0) { log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, ifi->name); return; } ifa.scope = scope & IADDR_SCOPE_MASK; DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %I/%d, brd %I, opp %I\n", ifi->index, ifi->name, new ? "added" : "removed", ifa.ip, ifa.flags, ifa.prefix, ifa.pxlen, ifa.brd, ifa.opposite); if (new) ifa_update(&ifa); else ifa_delete(&ifa);}voidkrt_if_scan(struct kif_proto *p UNUSED){ struct nlmsghdr *h; if_start_update(); nl_request_dump(RTM_GETLINK); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK) nl_parse_link(h, 1); else log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); nl_request_dump(RTM_GETADDR); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) nl_parse_addr(h); else log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); if_end_update();}/* * Routes */static struct krt_proto *nl_table_map[NL_NUM_TABLES];intkrt_capable(rte *e){ rta *a = e->attrs; if (a->cast != RTC_UNICAST#if 0 && a->cast != RTC_ANYCAST#endif ) return 0; if (a->source == RTS_DEVICE) /* Kernel takes care of device routes itself */ return 0; switch (a->dest) { case RTD_ROUTER: case RTD_DEVICE: case RTD_BLACKHOLE: case RTD_UNREACHABLE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -