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