netlink.c (21398B)
1 #define _GNU_SOURCE 2 3 #include <errno.h> 4 #include <net/if.h> 5 #include <poll.h> 6 #include <signal.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/socket.h> 12 #include <time.h> 13 #include <unistd.h> 14 15 #include <linux/fib_rules.h> 16 #include <linux/if_addr.h> 17 #include <linux/if_link.h> 18 #include <linux/neighbour.h> 19 #include <linux/netlink.h> 20 #include <linux/rtnetlink.h> 21 22 #include <arpa/inet.h> 23 24 #include "rxi/log.h" 25 26 #include "dataplane.h" 27 #include "dataplane/plugin.h" 28 29 #include "filter.h" 30 #include "netlink.h" 31 #include "../ipc.h" 32 33 #define NL_BUFSZ (64 * 1024) 34 #define NL_MAX_NH 16 35 36 // VRF masters: master ifindex -> table id + name. Single digits expected. 37 struct vrf_master { 38 int ifindex; 39 uint32_t table; 40 char name[IF_NAMESIZE]; 41 }; 42 43 // Member enslavement: member ifindex -> master ifindex (0 = none). 44 struct vrf_member { 45 int ifindex; 46 int master; 47 }; 48 49 #define VRF_MAX_MASTERS 64 50 #define VRF_MAX_MEMBERS 256 51 52 static struct vrf_master vrf_masters[VRF_MAX_MASTERS]; 53 static int vrf_master_count = 0; 54 static struct vrf_member vrf_members[VRF_MAX_MEMBERS]; 55 static int vrf_member_count = 0; 56 57 static volatile sig_atomic_t nl_stop = 0; 58 static uint32_t nl_seq = 0; 59 60 void nl_request_stop(void) { 61 nl_stop = 1; 62 } 63 64 static void vrf_map_reset(void) { 65 vrf_master_count = 0; 66 vrf_member_count = 0; 67 } 68 69 static struct vrf_master *vrf_master_find(int ifindex) { 70 int i; 71 for (i = 0; i < vrf_master_count; i++) { 72 if (vrf_masters[i].ifindex == ifindex) return &vrf_masters[i]; 73 } 74 return NULL; 75 } 76 77 static void vrf_master_set(int ifindex, uint32_t table, const char *name) { 78 struct vrf_master *m = vrf_master_find(ifindex); 79 if (m) { 80 m->table = table; 81 } else { 82 if (vrf_master_count >= VRF_MAX_MASTERS) { 83 log_warn("netlink: vrf master table full, ignoring %s", name ? name : "?"); 84 return; 85 } 86 m = &vrf_masters[vrf_master_count++]; 87 m->ifindex = ifindex; 88 m->table = table; 89 } 90 if (name) { 91 strncpy(m->name, name, sizeof(m->name) - 1); 92 m->name[sizeof(m->name) - 1] = '\0'; 93 } 94 } 95 96 static void vrf_master_remove(int ifindex) { 97 int i; 98 for (i = 0; i < vrf_master_count; i++) { 99 if (vrf_masters[i].ifindex == ifindex) { 100 vrf_masters[i] = vrf_masters[--vrf_master_count]; 101 return; 102 } 103 } 104 } 105 106 static void vrf_member_set(int ifindex, int master) { 107 int i; 108 for (i = 0; i < vrf_member_count; i++) { 109 if (vrf_members[i].ifindex == ifindex) { 110 vrf_members[i].master = master; 111 return; 112 } 113 } 114 if (vrf_member_count >= VRF_MAX_MEMBERS) return; 115 vrf_members[vrf_member_count].ifindex = ifindex; 116 vrf_members[vrf_member_count].master = master; 117 vrf_member_count++; 118 } 119 120 static void vrf_member_remove(int ifindex) { 121 int i; 122 for (i = 0; i < vrf_member_count; i++) { 123 if (vrf_members[i].ifindex == ifindex) { 124 vrf_members[i] = vrf_members[--vrf_member_count]; 125 return; 126 } 127 } 128 } 129 130 // Table owning an interface: master's table, or MAIN when unenslaved/unknown. 131 static uint32_t vrf_table_of_iface(int ifindex) { 132 int i; 133 int master = 0; 134 for (i = 0; i < vrf_member_count; i++) { 135 if (vrf_members[i].ifindex == ifindex) { 136 master = vrf_members[i].master; 137 break; 138 } 139 } 140 if (!master) return RT_TABLE_MAIN; 141 for (i = 0; i < vrf_master_count; i++) { 142 if (vrf_masters[i].ifindex == master) return vrf_masters[i].table; 143 } 144 return RT_TABLE_MAIN; 145 } 146 147 // rtm_table is 8-bit; ids above 255 arrive via RTA_TABLE with rtm_table set to 148 // RT_TABLE_COMPAT. Getting this wrong silently aliases VRF tables onto MAIN. 149 static uint32_t route_table_of(const struct rtmsg *rtm, struct rtattr **tb) { 150 uint32_t table = rtm->rtm_table; 151 if (table == RT_TABLE_COMPAT && tb[RTA_TABLE]) { 152 memcpy(&table, RTA_DATA(tb[RTA_TABLE]), sizeof(table)); 153 } 154 return table; 155 } 156 157 static void parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len) { 158 memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); 159 while (RTA_OK(rta, len)) { 160 if (rta->rta_type <= max) tb[rta->rta_type] = rta; 161 rta = RTA_NEXT(rta, len); 162 } 163 } 164 165 // No NDMSG_RTA helper in the UAPI headers; attrs follow the fixed struct. 166 static struct rtattr *ndmsg_rta(const struct ndmsg *ndm) { 167 return (struct rtattr *)(((char *)ndm) + NLMSG_ALIGN(sizeof(*ndm))); 168 } 169 170 static int handle_link(struct nlmsghdr *nh) { 171 struct ifinfomsg *ifi = NLMSG_DATA(nh); 172 struct rtattr *tb[IFLA_MAX + 1]; 173 char ifname[IF_NAMESIZE] = ""; 174 uint32_t mtu = 0; 175 int master = 0; 176 int is_vrf = 0; 177 uint32_t vrf_table = 0; 178 int len; 179 180 len = (int)(nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); 181 if (len < 0) return 0; 182 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); 183 184 if (!tb[IFLA_IFNAME]) return 0; 185 strncpy(ifname, RTA_DATA(tb[IFLA_IFNAME]), sizeof(ifname) - 1); 186 if (tb[IFLA_MTU]) memcpy(&mtu, RTA_DATA(tb[IFLA_MTU]), sizeof(mtu)); 187 if (tb[IFLA_MASTER]) memcpy(&master, RTA_DATA(tb[IFLA_MASTER]), sizeof(master)); 188 189 // VRF masters carry linkinfo kind "vrf" with the table id nested inside 190 // IFLA_INFO_DATA as IFLA_VRF_TABLE. 191 if (tb[IFLA_LINKINFO]) { 192 struct rtattr *li[IFLA_INFO_MAX + 1]; 193 struct rtattr *rta = RTA_DATA(tb[IFLA_LINKINFO]); 194 int lilen = (int)RTA_PAYLOAD(tb[IFLA_LINKINFO]); 195 parse_rtattr(li, IFLA_INFO_MAX, rta, lilen); 196 if (li[IFLA_INFO_KIND] && !strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf")) { 197 is_vrf = 1; 198 if (li[IFLA_INFO_DATA]) { 199 struct rtattr *vd[IFLA_VRF_MAX + 1]; 200 struct rtattr *vrta = RTA_DATA(li[IFLA_INFO_DATA]); 201 int vlen = (int)RTA_PAYLOAD(li[IFLA_INFO_DATA]); 202 parse_rtattr(vd, IFLA_VRF_MAX, vrta, vlen); 203 if (vd[IFLA_VRF_TABLE]) { 204 memcpy(&vrf_table, RTA_DATA(vd[IFLA_VRF_TABLE]), sizeof(vrf_table)); 205 } 206 } 207 } 208 } 209 210 if (nh->nlmsg_type == RTM_DELLINK) { 211 vrf_member_remove(ifi->ifi_index); 212 if (is_vrf || vrf_master_find(ifi->ifi_index)) { 213 // Removal drops tracking; a standing exclusion survives re-creation. 214 if (vrf_table) nl_filter_untrack(vrf_table); 215 vrf_master_remove(ifi->ifi_index); 216 log_info("netlink: vrf %s removed", ifname); 217 } 218 if (dp_port_admin(ifname, false) != DP_RET_OK) { 219 log_error("netlink: port_admin %s down failed", ifname); 220 } 221 return 0; 222 } 223 224 vrf_member_set(ifi->ifi_index, master); 225 if (is_vrf && vrf_table) { 226 vrf_master_set(ifi->ifi_index, vrf_table, ifname); 227 if (nl_filter_mgmt_name(ifname)) { 228 nl_filter_exclude(vrf_table); 229 log_info("netlink: vrf %s table %u excluded (management)", ifname, vrf_table); 230 } else { 231 nl_filter_track(vrf_table); 232 log_info("netlink: vrf %s table %u tracked", ifname, vrf_table); 233 } 234 } 235 236 log_debug("netlink: link %s %s mtu %u", ifname, (ifi->ifi_flags & IFF_UP) ? "up" : "down", mtu); 237 if (dp_port_admin(ifname, (ifi->ifi_flags & IFF_UP) ? true : false) != DP_RET_OK) { 238 log_error("netlink: port_admin %s failed", ifname); 239 } 240 if (mtu && dp_port_mtu(ifname, mtu) != DP_RET_OK) { 241 log_error("netlink: port_mtu %s %u failed", ifname, mtu); 242 } 243 return 0; 244 } 245 246 static int handle_addr(struct nlmsghdr *nh) { 247 struct ifaddrmsg *ifa = NLMSG_DATA(nh); 248 struct rtattr *tb[IFA_MAX + 1]; 249 char ifname[IF_NAMESIZE] = ""; 250 struct dp_rif rif; 251 struct dp_addr addr; 252 void *bytes = NULL; 253 size_t want = 0; 254 int len; 255 int del; 256 257 len = (int)(nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); 258 if (len < 0) return 0; 259 parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len); 260 261 if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) return 0; 262 // IFA_LOCAL is the local address on point-to-point v4; IFA_ADDRESS would be 263 // the peer. Prefer LOCAL whenever present. 264 if (tb[IFA_LOCAL]) bytes = RTA_DATA(tb[IFA_LOCAL]); 265 else if (tb[IFA_ADDRESS]) bytes = RTA_DATA(tb[IFA_ADDRESS]); 266 else return 0; 267 want = (ifa->ifa_family == AF_INET) ? 4 : 16; 268 269 if (!if_indextoname(ifa->ifa_index, ifname)) return 0; 270 271 memset(&addr, 0, sizeof(addr)); 272 addr.family = ifa->ifa_family; 273 addr.prefixlen = ifa->ifa_prefixlen; 274 memcpy(addr.addr, bytes, want); 275 276 memset(&rif, 0, sizeof(rif)); 277 rif.ifname = ifname; 278 rif.addr = addr; 279 rif.table = vrf_table_of_iface(ifa->ifa_index); 280 if (!nl_table_allowed(rif.table)) { 281 log_debug("netlink: rif %s skipped (table %u not tracked)", ifname, rif.table); 282 return 0; 283 } 284 285 del = (nh->nlmsg_type == RTM_DELADDR); 286 log_debug("netlink: rif %s %s table %u", ifname, del ? "del" : "add", rif.table); 287 if (del) { 288 if (dp_rif_del(&rif) != DP_RET_OK) log_error("netlink: rif_del %s failed", ifname); 289 } else { 290 if (dp_rif_add(&rif) != DP_RET_OK) log_error("netlink: rif_add %s failed", ifname); 291 } 292 return 0; 293 } 294 295 static int handle_neigh(struct nlmsghdr *nh) { 296 struct ndmsg *ndm = NLMSG_DATA(nh); 297 struct rtattr *tb[NDA_MAX + 1]; 298 char ifname[IF_NAMESIZE] = ""; 299 struct dp_neigh neigh; 300 int len; 301 int del; 302 303 len = (int)(nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ndm))); 304 if (len < 0) return 0; 305 parse_rtattr(tb, NDA_MAX, ndmsg_rta(ndm), len); 306 307 if (ndm->ndm_family != AF_INET && ndm->ndm_family != AF_INET6) return 0; 308 // Valid-only: incomplete/failed resolutions never reach the dataplane. 309 del = (nh->nlmsg_type == RTM_DELNEIGH); 310 if (!del) { 311 if (ndm->ndm_state & (NUD_FAILED | NUD_INCOMPLETE)) del = 1; 312 else if (!(ndm->ndm_state & (NUD_PERMANENT | NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE))) { 313 return 0; 314 } 315 } 316 if (!tb[NDA_DST]) return 0; 317 if (!del && (!tb[NDA_LLADDR] || RTA_PAYLOAD(tb[NDA_LLADDR]) != 6)) { 318 log_debug("netlink: neigh skipped (no MAC)"); 319 return 0; 320 } 321 if (!if_indextoname(ndm->ndm_ifindex, ifname)) return 0; 322 323 memset(&neigh, 0, sizeof(neigh)); 324 neigh.ifname = ifname; 325 neigh.addr.family = ndm->ndm_family; 326 neigh.addr.prefixlen = (ndm->ndm_family == AF_INET) ? 32 : 128; 327 memcpy(neigh.addr.addr, RTA_DATA(tb[NDA_DST]), (ndm->ndm_family == AF_INET) ? 4 : 16); 328 if (!del) memcpy(neigh.mac, RTA_DATA(tb[NDA_LLADDR]), 6); 329 neigh.table = vrf_table_of_iface(ndm->ndm_ifindex); 330 if (!nl_table_allowed(neigh.table)) { 331 log_debug("netlink: neigh %s skipped (table %u not tracked)", ifname, neigh.table); 332 return 0; 333 } 334 335 log_debug("netlink: neigh %s %s table %u", ifname, del ? "del" : "add", neigh.table); 336 if (del) { 337 if (dp_neigh_del(&neigh) != DP_RET_OK) log_error("netlink: neigh_del failed"); 338 } else { 339 if (dp_neigh_add(&neigh) != DP_RET_OK) log_error("netlink: neigh_add failed"); 340 } 341 return 0; 342 } 343 344 static int handle_route(struct nlmsghdr *nh) { 345 struct rtmsg *rtm = NLMSG_DATA(nh); 346 struct rtattr *tb[RTA_MAX + 1]; 347 struct dp_route route; 348 struct dp_nexthop nhops[NL_MAX_NH]; 349 char nhnames[NL_MAX_NH][IF_NAMESIZE]; 350 uint32_t table; 351 uint32_t metric = 0; 352 int len; 353 int del; 354 size_t nh_count = 0; 355 356 len = (int)(nh->nlmsg_len - NLMSG_LENGTH(sizeof(*rtm))); 357 if (len < 0) return 0; 358 parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len); 359 360 table = route_table_of(rtm, tb); 361 if (!nl_route_allowed(rtm->rtm_family, rtm->rtm_type, rtm->rtm_scope, table)) { 362 return 0; 363 } 364 if (tb[RTA_PRIORITY]) memcpy(&metric, RTA_DATA(tb[RTA_PRIORITY]), sizeof(metric)); 365 366 memset(&route, 0, sizeof(route)); 367 memset(nhops, 0, sizeof(nhops)); 368 memset(nhnames, 0, sizeof(nhnames)); 369 route.dst.family = rtm->rtm_family; 370 route.dst.prefixlen = rtm->rtm_dst_len; 371 if (tb[RTA_DST]) { 372 size_t want = (rtm->rtm_family == AF_INET) ? 4 : 16; 373 memcpy(route.dst.addr, RTA_DATA(tb[RTA_DST]), want); 374 } 375 route.metric = metric; 376 route.table = table; 377 378 if (tb[RTA_MULTIPATH]) { 379 // ECMP: nexthops nest gateways inside each rtnexthop, not at top level. 380 struct rtnexthop *rtnh = RTA_DATA(tb[RTA_MULTIPATH]); 381 int mplen = (int)RTA_PAYLOAD(tb[RTA_MULTIPATH]); 382 while (mplen >= (int)sizeof(*rtnh) && RTNH_OK(rtnh, mplen) && nh_count < NL_MAX_NH) { 383 struct rtattr *hops[RTA_MAX + 1]; 384 int cur = RTNH_ALIGN(rtnh->rtnh_len); 385 int hlen = (int)rtnh->rtnh_len - (int)sizeof(*rtnh); 386 memset(hops, 0, sizeof(hops)); 387 if (hlen > 0) parse_rtattr(hops, RTA_MAX, RTNH_DATA(rtnh), hlen); 388 if (if_indextoname(rtnh->rtnh_ifindex, nhnames[nh_count])) { 389 nhops[nh_count].ifname = nhnames[nh_count]; 390 nhops[nh_count].gw.family = rtm->rtm_family; 391 if (hops[RTA_GATEWAY]) { 392 size_t want = (rtm->rtm_family == AF_INET) ? 4 : 16; 393 memcpy(nhops[nh_count].gw.addr, RTA_DATA(hops[RTA_GATEWAY]), want); 394 } else { 395 nhops[nh_count].gw.family = AF_UNSPEC; 396 } 397 nh_count++; 398 } 399 rtnh = RTNH_NEXT(rtnh); 400 mplen -= cur; 401 } 402 if (mplen > 0 && nh_count >= NL_MAX_NH) { 403 log_warn("netlink: ECMP group truncated to %d nexthops", NL_MAX_NH); 404 } 405 } else { 406 uint32_t oif = 0; 407 if (tb[RTA_OIF]) memcpy(&oif, RTA_DATA(tb[RTA_OIF]), sizeof(oif)); 408 // Directly-connected routes carry OIF without a gateway; represent with 409 // an empty (AF_UNSPEC) gw rather than inventing one. 410 if (oif || tb[RTA_GATEWAY]) { 411 if (oif && !if_indextoname(oif, nhnames[0])) return 0; 412 nhops[0].ifname = oif ? nhnames[0] : NULL; 413 nhops[0].gw.family = rtm->rtm_family; 414 if (tb[RTA_GATEWAY]) { 415 size_t want = (rtm->rtm_family == AF_INET) ? 4 : 16; 416 memcpy(nhops[0].gw.addr, RTA_DATA(tb[RTA_GATEWAY]), want); 417 } else { 418 nhops[0].gw.family = AF_UNSPEC; 419 } 420 nh_count = 1; 421 } 422 } 423 424 route.nh = nh_count ? nhops : NULL; 425 route.nh_count = nh_count; 426 427 del = (nh->nlmsg_type == RTM_DELROUTE); 428 log_debug("netlink: route %s table %u nh %zu", del ? "del" : "add", table, nh_count); 429 if (del) { 430 if (dp_route_del(&route) != DP_RET_OK) log_error("netlink: route_del table %u failed", table); 431 } else { 432 if (dp_route_add(&route) != DP_RET_OK) log_error("netlink: route_add table %u failed", table); 433 } 434 return 0; 435 } 436 437 static int handle_nlmsg(struct nlmsghdr *nh) { 438 switch (nh->nlmsg_type) { 439 case RTM_NEWLINK: 440 case RTM_DELLINK: 441 return handle_link(nh); 442 case RTM_NEWADDR: 443 case RTM_DELADDR: 444 return handle_addr(nh); 445 case RTM_NEWNEIGH: 446 case RTM_DELNEIGH: 447 return handle_neigh(nh); 448 case RTM_NEWROUTE: 449 case RTM_DELROUTE: 450 return handle_route(nh); 451 default: 452 return 0; 453 } 454 } 455 456 int nl_open(void) { 457 struct sockaddr_nl addr; 458 int fd; 459 int sndbuf = 256 * 1024; 460 int rcvbuf = 2 * 1024 * 1024; 461 462 fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_ROUTE); 463 if (fd < 0) { 464 log_error("netlink: socket failed: %s", strerror(errno)); 465 return -1; 466 } 467 // Best effort: large receive buffer so bursts become resyncs, not losses. 468 // May fail without privilege in dev environments; event flow still works. 469 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, sizeof(rcvbuf)) != 0) { 470 log_warn("netlink: SO_RCVBUFFORCE failed: %s", strerror(errno)); 471 } 472 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); 473 474 memset(&addr, 0, sizeof(addr)); 475 addr.nl_family = AF_NETLINK; 476 // RTMGRP_* are group bitmasks; RTNLGRP_* are group numbers. Do not mix up. 477 addr.nl_groups = RTMGRP_LINK | RTMGRP_NEIGH 478 | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR 479 | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE; 480 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 481 log_error("netlink: bind failed: %s", strerror(errno)); 482 close(fd); 483 return -1; 484 } 485 return fd; 486 } 487 488 // Send one GET*+DUMP and dispatch matching replies through handle_nlmsg. 489 int nl_dump(int fd, uint16_t type, int family) { 490 struct { 491 struct nlmsghdr nh; 492 struct rtgenmsg gen; 493 } req; 494 char buf[NL_BUFSZ]; 495 int done = 0; 496 int rc = 0; 497 498 memset(&req, 0, sizeof(req)); 499 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.gen)); 500 req.nh.nlmsg_type = type; 501 req.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; 502 req.nh.nlmsg_seq = ++nl_seq; 503 req.nh.nlmsg_pid = 0; 504 req.gen.rtgen_family = (uint8_t)family; 505 506 if (send(fd, &req, req.nh.nlmsg_len, 0) < 0) { 507 log_error("netlink: dump send %u failed: %s", type, strerror(errno)); 508 return -1; 509 } 510 511 while (!done) { 512 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 513 struct nlmsghdr *h; 514 int len; 515 int pr = poll(&pfd, 1, 5000); 516 if (pr <= 0) { 517 log_error("netlink: dump recv %u timed out", type); 518 return -1; 519 } 520 len = recv(fd, buf, sizeof(buf), 0); 521 if (len < 0) { 522 if (errno == EAGAIN || errno == EWOULDBLOCK) continue; 523 log_error("netlink: dump recv %u failed: %s", type, strerror(errno)); 524 return -1; 525 } 526 h = (struct nlmsghdr *)buf; 527 while (NLMSG_OK(h, len)) { 528 if (h->nlmsg_seq && h->nlmsg_seq != nl_seq) goto next; 529 if (h->nlmsg_type == NLMSG_DONE) { 530 done = 1; 531 break; 532 } 533 if (h->nlmsg_type == NLMSG_ERROR) { 534 struct nlmsgerr *e = NLMSG_DATA(h); 535 if (e->error) { 536 log_error("netlink: dump %u error: %s", type, strerror(-e->error)); 537 rc = -1; 538 done = 1; 539 break; 540 } 541 goto next; 542 } 543 handle_nlmsg(h); 544 next: 545 h = NLMSG_NEXT(h, len); 546 } 547 } 548 return rc; 549 } 550 551 // Full converge: VRF map first (routes resolve against it), then RIFs, 552 // neighbours, routes, then the backend's own resync backstop. 553 int nl_resync(int fd) { 554 int fails = 0; 555 556 nl_filter_reset(); 557 vrf_map_reset(); 558 559 if (nl_dump(fd, RTM_GETLINK, AF_UNSPEC) != 0) fails++; 560 if (nl_dump(fd, RTM_GETADDR, AF_INET) != 0) fails++; 561 if (nl_dump(fd, RTM_GETADDR, AF_INET6) != 0) fails++; 562 if (nl_dump(fd, RTM_GETNEIGH, AF_INET) != 0) fails++; 563 if (nl_dump(fd, RTM_GETNEIGH, AF_INET6) != 0) fails++; 564 if (nl_dump(fd, RTM_GETROUTE, AF_INET) != 0) fails++; 565 if (nl_dump(fd, RTM_GETROUTE, AF_INET6) != 0) fails++; 566 567 if (dp_resync() != DP_RET_OK) { 568 log_error("netlink: plugin resync failed"); 569 fails++; 570 } 571 if (fails) { 572 log_warn("netlink: resync finished with %d failures", fails); 573 return -1; 574 } 575 log_info("netlink: resync complete"); 576 return 0; 577 } 578 579 static long elapsed_ms(const struct timespec *a, const struct timespec *b) { 580 return (b->tv_sec - a->tv_sec) * 1000L + (b->tv_nsec - a->tv_nsec) / 1000000L; 581 } 582 583 int nl_run(int fd, int resync_interval_s) { 584 static char buf[NL_BUFSZ]; 585 struct timespec last_resync; 586 clock_gettime(CLOCK_MONOTONIC, &last_resync); 587 588 while (!nl_stop) { 589 // Netlink occupies slot 0; the IPC server contributes its listeners and 590 // every live connection after it. 591 struct pollfd pfds[1 + IPC_MAX_POLLFDS]; 592 int nfds = 1; 593 struct timespec now; 594 long wait_ms = 30000; 595 int pr; 596 597 pfds[0].fd = fd; 598 pfds[0].events = POLLIN; 599 pfds[0].revents = 0; 600 nfds += ipc_pollfds(&pfds[1], IPC_MAX_POLLFDS); 601 602 clock_gettime(CLOCK_MONOTONIC, &now); 603 if (resync_interval_s > 0) { 604 long remain = resync_interval_s * 1000L - elapsed_ms(&last_resync, &now); 605 if (remain <= 0) { 606 if (nl_resync(fd) != 0) { 607 // Log-and-continue with a single immediate retry; the next timer 608 // converges anything still missing. 609 log_warn("netlink: periodic resync failed, retrying once"); 610 if (nl_resync(fd) != 0) log_error("netlink: periodic resync retry failed"); 611 } 612 clock_gettime(CLOCK_MONOTONIC, &last_resync); 613 continue; 614 } 615 if (remain < wait_ms) wait_ms = remain; 616 } 617 618 pr = poll(pfds, nfds, (int)wait_ms); 619 if (pr < 0) { 620 if (errno == EINTR) continue; 621 log_error("netlink: poll failed: %s", strerror(errno)); 622 return -1; 623 } 624 if (!pr) continue; 625 626 if (nfds > 1) ipc_dispatch(&pfds[1], nfds - 1); 627 if (!(pfds[0].revents & POLLIN)) continue; 628 629 for (;;) { 630 struct nlmsghdr *h; 631 struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; 632 struct msghdr msg; 633 struct sockaddr_nl nladdr; 634 ssize_t len; 635 636 memset(&msg, 0, sizeof(msg)); 637 msg.msg_name = &nladdr; 638 msg.msg_namelen = sizeof(nladdr); 639 msg.msg_iov = &iov; 640 msg.msg_iovlen = 1; 641 642 len = recvmsg(fd, &msg, 0); 643 if (len < 0) { 644 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) break; 645 if (errno == ENOBUFS) { 646 // Expected under load, not exceptional: re-converge from dumps. 647 log_warn("netlink: ENOBUFS, resyncing"); 648 if (nl_resync(fd) != 0) { 649 log_warn("netlink: ENOBUFS resync failed, retrying once"); 650 nl_resync(fd); 651 } 652 clock_gettime(CLOCK_MONOTONIC, &last_resync); 653 break; 654 } 655 log_error("netlink: recvmsg failed: %s", strerror(errno)); 656 return -1; 657 } 658 if (len == 0) break; 659 if (msg.msg_flags & MSG_TRUNC) { 660 log_warn("netlink: message truncated, resyncing"); 661 if (nl_resync(fd) != 0) nl_resync(fd); 662 clock_gettime(CLOCK_MONOTONIC, &last_resync); 663 break; 664 } 665 666 h = (struct nlmsghdr *)buf; 667 while (NLMSG_OK(h, (int)len)) { 668 if (h->nlmsg_type == NLMSG_ERROR) { 669 struct nlmsgerr *e = NLMSG_DATA(h); 670 if (e->error == -ENOBUFS) { 671 log_warn("netlink: ENOBUFS via NLMSG_ERROR, resyncing"); 672 if (nl_resync(fd) != 0) nl_resync(fd); 673 clock_gettime(CLOCK_MONOTONIC, &last_resync); 674 } else if (e->error) { 675 log_error("netlink: NLMSG_ERROR: %s", strerror(-e->error)); 676 } 677 } else if (h->nlmsg_type != NLMSG_DONE) { 678 handle_nlmsg(h); 679 } 680 h = NLMSG_NEXT(h, len); 681 } 682 // Non-blocking drain: keep reading until EAGAIN. 683 if ((size_t)len < sizeof(buf)) break; 684 } 685 } 686 return 0; 687 }