rtnl.c (19093B)
1 #define _GNU_SOURCE 2 #include <arpa/inet.h> 3 #include <errno.h> 4 #include <linux/if_link.h> 5 #include <linux/netlink.h> 6 #include <linux/rtnetlink.h> 7 #include <net/if.h> 8 #include <poll.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/socket.h> 13 #include <unistd.h> 14 15 #include "rxi/log.h" 16 17 #include "rtnl.h" 18 19 #define NL_BUFSZ 8192 20 21 static int rtnl_open(void) { 22 int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); 23 if (fd < 0) { 24 log_error("rtnl: socket failed: %s", strerror(errno)); 25 return -1; 26 } 27 struct sockaddr_nl addr = { .nl_family = AF_NETLINK }; 28 if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) { 29 log_error("rtnl: bind failed: %s", strerror(errno)); 30 close(fd); 31 return -1; 32 } 33 return fd; 34 } 35 36 static int rtnl_talk(struct nlmsghdr *nh) { 37 int fd = rtnl_open(); 38 if (fd < 0) return -1; 39 40 struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; 41 struct iovec iov = { .iov_base = nh, .iov_len = nh->nlmsg_len }; 42 struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1 }; 43 44 if (sendmsg(fd, &msg, 0) < 0) { 45 log_error("rtnl: sendmsg failed: %s", strerror(errno)); 46 close(fd); 47 return -1; 48 } 49 50 // Wait for ACK (NLMSGERR with error 0) or NLMSG_DONE 51 char buf[NL_BUFSZ]; 52 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 53 if (poll(&pfd, 1, 5000) <= 0) { 54 log_error("rtnl: poll timeout"); 55 close(fd); 56 return -1; 57 } 58 ssize_t len = recv(fd, buf, sizeof(buf), 0); 59 if (len < 0) { 60 log_error("rtnl: recv failed: %s", strerror(errno)); 61 close(fd); 62 return -1; 63 } 64 struct nlmsghdr *h = (struct nlmsghdr*)buf; 65 while (NLMSG_OK(h, len)) { 66 if (h->nlmsg_type == NLMSG_ERROR) { 67 struct nlmsgerr *err = NLMSG_DATA(h); 68 if (err->error == 0) { 69 close(fd); 70 return 0; // ACK 71 } else { 72 // EEXIST for addr add is not fatal for ifup idempotence -- treat as success 73 if (err->error == -EEXIST) { 74 log_debug("rtnl: NLMSGERR EEXIST (already exists, treating as success)"); 75 close(fd); 76 return 0; 77 } 78 log_error("rtnl: NLMSGERR %s", strerror(-err->error)); 79 close(fd); 80 return -1; 81 } 82 } 83 h = NLMSG_NEXT(h, len); 84 } 85 close(fd); 86 return 0; 87 } 88 89 static int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { 90 int len = RTA_LENGTH(alen); 91 struct rtattr *rta; 92 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > (unsigned)maxlen) { 93 log_error("rtnl: addattr_l overflow"); 94 return -1; 95 } 96 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); 97 rta->rta_type = type; 98 rta->rta_len = len; 99 if (alen) memcpy(RTA_DATA(rta), data, alen); 100 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); 101 return 0; 102 } 103 104 // Returns the nest's byte offset for addattr_nested_end(), or -1 on overflow. 105 static int addattr_nested_start(struct nlmsghdr *n, int maxlen, int type) { 106 struct rtattr *rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); 107 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(RTA_LENGTH(0)) > (unsigned)maxlen) { 108 log_error("rtnl: addattr_nested_start overflow"); 109 return -1; 110 } 111 rta->rta_type = type; 112 rta->rta_len = RTA_LENGTH(0); 113 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(sizeof(*rta)); 114 return (char*)rta - (char*)n; 115 } 116 117 static void addattr_nested_end(struct nlmsghdr *n, int start) { 118 struct rtattr *rta = (struct rtattr*)((char*)n + start); 119 rta->rta_len = (char*)n + NLMSG_ALIGN(n->nlmsg_len) - (char*)rta; 120 } 121 122 int rtnl_link_up(const char *ifname) { 123 unsigned ifindex = if_nametoindex(ifname); 124 if (!ifindex) { 125 log_error("rtnl: link_up %s: if_nametoindex failed: %s", ifname, strerror(errno)); 126 return -1; 127 } 128 char buf[NL_BUFSZ]; 129 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 130 struct ifinfomsg *ifi; 131 memset(buf, 0, sizeof(buf)); 132 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 133 nh->nlmsg_type = RTM_NEWLINK; 134 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 135 nh->nlmsg_seq = 0; 136 ifi = NLMSG_DATA(nh); 137 ifi->ifi_family = AF_UNSPEC; 138 ifi->ifi_index = ifindex; 139 ifi->ifi_change = IFF_UP; 140 ifi->ifi_flags = IFF_UP; 141 return rtnl_talk(nh); 142 } 143 144 int rtnl_link_down(const char *ifname) { 145 unsigned ifindex = if_nametoindex(ifname); 146 if (!ifindex) { 147 log_error("rtnl: link_down %s: if_nametoindex failed", ifname); 148 return -1; 149 } 150 char buf[NL_BUFSZ]; 151 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 152 struct ifinfomsg *ifi; 153 memset(buf, 0, sizeof(buf)); 154 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 155 nh->nlmsg_type = RTM_NEWLINK; 156 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 157 ifi = NLMSG_DATA(nh); 158 ifi->ifi_family = AF_UNSPEC; 159 ifi->ifi_index = ifindex; 160 ifi->ifi_change = IFF_UP; 161 ifi->ifi_flags = 0; 162 return rtnl_talk(nh); 163 } 164 165 int rtnl_link_set_mtu(const char *ifname, int mtu) { 166 unsigned ifindex = if_nametoindex(ifname); 167 if (!ifindex) return -1; 168 char buf[NL_BUFSZ]; 169 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 170 struct ifinfomsg *ifi; 171 memset(buf, 0, sizeof(buf)); 172 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 173 nh->nlmsg_type = RTM_NEWLINK; 174 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 175 ifi = NLMSG_DATA(nh); 176 ifi->ifi_family = AF_UNSPEC; 177 ifi->ifi_index = ifindex; 178 addattr_l(nh, sizeof(buf), IFLA_MTU, &mtu, sizeof(mtu)); 179 return rtnl_talk(nh); 180 } 181 182 int rtnl_link_set_hwaddr(const char *ifname, const char *hwaddr) { 183 unsigned ifindex = if_nametoindex(ifname); 184 if (!ifindex) return -1; 185 unsigned char mac[6]; 186 if (sscanf(hwaddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) { 187 log_error("rtnl: invalid hwaddr %s", hwaddr); 188 return -1; 189 } 190 char buf[NL_BUFSZ]; 191 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 192 struct ifinfomsg *ifi; 193 memset(buf, 0, sizeof(buf)); 194 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 195 nh->nlmsg_type = RTM_NEWLINK; 196 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 197 ifi = NLMSG_DATA(nh); 198 ifi->ifi_family = AF_UNSPEC; 199 ifi->ifi_index = ifindex; 200 addattr_l(nh, sizeof(buf), IFLA_ADDRESS, mac, 6); 201 return rtnl_talk(nh); 202 } 203 204 // Parse "10.0.0.1/24" or "10.0.0.1" + netmask, returns family, addr, prefixlen 205 static int parse_addr(const char *address, const char *netmask, int *family, unsigned char *addr, int *prefixlen) { 206 char ip[128]; 207 char *slash = strchr(address, '/'); 208 if (slash) { 209 size_t iplen = slash - address; 210 if (iplen >= sizeof(ip)) return -1; 211 memcpy(ip, address, iplen); 212 ip[iplen] = '\0'; 213 *prefixlen = atoi(slash+1); 214 } else { 215 strncpy(ip, address, sizeof(ip)-1); 216 ip[sizeof(ip)-1] = '\0'; 217 if (netmask) { 218 // netmask like 255.255.255.0 -> prefixlen 219 struct in_addr nm; 220 if (inet_pton(AF_INET, netmask, &nm) != 1) return -1; 221 uint32_t m = ntohl(nm.s_addr); 222 int len = 0; 223 while (m & 0x80000000) { len++; m <<= 1; } 224 *prefixlen = len; 225 } else { 226 *prefixlen = -1; 227 } 228 } 229 // Try v4 then v6 230 struct in_addr a4; 231 struct in6_addr a6; 232 if (inet_pton(AF_INET, ip, &a4) == 1) { 233 *family = AF_INET; 234 memcpy(addr, &a4, 4); 235 if (*prefixlen < 0) *prefixlen = 32; 236 return 0; 237 } 238 if (inet_pton(AF_INET6, ip, &a6) == 1) { 239 *family = AF_INET6; 240 memcpy(addr, &a6, 16); 241 if (*prefixlen < 0) *prefixlen = 128; 242 return 0; 243 } 244 return -1; 245 } 246 247 int rtnl_addr_add(const char *ifname, const char *address, const char *netmask) { 248 unsigned ifindex = if_nametoindex(ifname); 249 if (!ifindex) { 250 log_error("rtnl: addr_add %s: ifindex failed", ifname); 251 return -1; 252 } 253 int family, prefixlen; 254 unsigned char addr[16]; 255 if (parse_addr(address, netmask, &family, addr, &prefixlen) != 0) { 256 log_error("rtnl: addr_add %s: parse %s failed", ifname, address); 257 return -1; 258 } 259 char buf[NL_BUFSZ]; 260 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 261 struct ifaddrmsg *ifa; 262 memset(buf, 0, sizeof(buf)); 263 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa)); 264 nh->nlmsg_type = RTM_NEWADDR; 265 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; 266 ifa = NLMSG_DATA(nh); 267 ifa->ifa_family = family; 268 ifa->ifa_prefixlen = prefixlen; 269 ifa->ifa_flags = 0; 270 ifa->ifa_scope = 0; 271 ifa->ifa_index = ifindex; 272 int alen = (family == AF_INET) ? 4 : 16; 273 addattr_l(nh, sizeof(buf), IFA_LOCAL, addr, alen); 274 addattr_l(nh, sizeof(buf), IFA_ADDRESS, addr, alen); 275 int rc = rtnl_talk(nh); 276 if (rc != 0) { 277 // If EEXIST, treat as success (idempotent) 278 log_debug("rtnl: addr_add %s %s/%d rc=%d", ifname, address, prefixlen, rc); 279 } 280 return rc; 281 } 282 283 int rtnl_addr_del(const char *ifname, const char *address, const char *netmask) { 284 unsigned ifindex = if_nametoindex(ifname); 285 if (!ifindex) return -1; 286 int family, prefixlen; 287 unsigned char addr[16]; 288 if (parse_addr(address, netmask, &family, addr, &prefixlen) != 0) return -1; 289 char buf[NL_BUFSZ]; 290 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 291 struct ifaddrmsg *ifa; 292 memset(buf, 0, sizeof(buf)); 293 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa)); 294 nh->nlmsg_type = RTM_DELADDR; 295 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 296 ifa = NLMSG_DATA(nh); 297 ifa->ifa_family = family; 298 ifa->ifa_prefixlen = prefixlen; 299 ifa->ifa_index = ifindex; 300 int alen = (family == AF_INET) ? 4 : 16; 301 addattr_l(nh, sizeof(buf), IFA_LOCAL, addr, alen); 302 return rtnl_talk(nh); 303 } 304 305 int rtnl_route_add_default(const char *gateway, const char *ifname) { 306 int family; 307 unsigned char gw[16]; 308 int prefixlen; 309 // gateway may be without prefix, default to /32 or /128 310 if (parse_addr(gateway, NULL, &family, gw, &prefixlen) != 0) return -1; 311 // For default route, we ignore prefixlen and use 0 312 char buf[NL_BUFSZ]; 313 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 314 struct rtmsg *rtm; 315 memset(buf, 0, sizeof(buf)); 316 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm)); 317 nh->nlmsg_type = RTM_NEWROUTE; 318 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; 319 rtm = NLMSG_DATA(nh); 320 rtm->rtm_family = family; 321 rtm->rtm_dst_len = 0; 322 rtm->rtm_src_len = 0; 323 rtm->rtm_tos = 0; 324 rtm->rtm_table = RT_TABLE_MAIN; 325 rtm->rtm_protocol = RTPROT_BOOT; 326 rtm->rtm_scope = RT_SCOPE_UNIVERSE; 327 rtm->rtm_type = RTN_UNICAST; 328 int alen = (family == AF_INET) ? 4 : 16; 329 addattr_l(nh, sizeof(buf), RTA_GATEWAY, gw, alen); 330 if (ifname) { 331 unsigned ifindex = if_nametoindex(ifname); 332 if (ifindex) addattr_l(nh, sizeof(buf), RTA_OIF, &ifindex, sizeof(ifindex)); 333 } 334 return rtnl_talk(nh); 335 } 336 337 int rtnl_route_del_default(const char *gateway, const char *ifname) { 338 int family; 339 unsigned char gw[16]; 340 int prefixlen; 341 if (parse_addr(gateway, NULL, &family, gw, &prefixlen) != 0) return -1; 342 char buf[NL_BUFSZ]; 343 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 344 struct rtmsg *rtm; 345 memset(buf, 0, sizeof(buf)); 346 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm)); 347 nh->nlmsg_type = RTM_DELROUTE; 348 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 349 rtm = NLMSG_DATA(nh); 350 rtm->rtm_family = family; 351 rtm->rtm_dst_len = 0; 352 rtm->rtm_table = RT_TABLE_MAIN; 353 int alen = (family == AF_INET) ? 4 : 16; 354 addattr_l(nh, sizeof(buf), RTA_GATEWAY, gw, alen); 355 if (ifname) { 356 unsigned ifindex = if_nametoindex(ifname); 357 if (ifindex) addattr_l(nh, sizeof(buf), RTA_OIF, &ifindex, sizeof(ifindex)); 358 } 359 return rtnl_talk(nh); 360 } 361 362 int rtnl_vlan_create(const char *name, const char *rawdev, int vlan_id) { 363 unsigned rawidx = if_nametoindex(rawdev); 364 if (!rawidx) { 365 log_error("rtnl: vlan %s: rawdev %s not found", name, rawdev); 366 return -1; 367 } 368 // Check if already exists 369 if (if_nametoindex(name) != 0) { 370 log_info("rtnl: vlan %s already exists", name); 371 return 0; 372 } 373 char buf[NL_BUFSZ]; 374 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 375 struct ifinfomsg *ifi; 376 memset(buf, 0, sizeof(buf)); 377 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 378 nh->nlmsg_type = RTM_NEWLINK; 379 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; 380 ifi = NLMSG_DATA(nh); 381 ifi->ifi_family = AF_UNSPEC; 382 addattr_l(nh, sizeof(buf), IFLA_IFNAME, name, strlen(name)+1); 383 addattr_l(nh, sizeof(buf), IFLA_LINK, &rawidx, sizeof(rawidx)); 384 int nest = addattr_nested_start(nh, sizeof(buf), IFLA_LINKINFO); 385 if (nest < 0) return -1; 386 addattr_l(nh, sizeof(buf), IFLA_INFO_KIND, "vlan", 5); 387 int nest2 = addattr_nested_start(nh, sizeof(buf), IFLA_INFO_DATA); 388 if (nest2 < 0) return -1; 389 addattr_l(nh, sizeof(buf), IFLA_VLAN_ID, &vlan_id, sizeof(vlan_id)); 390 addattr_nested_end(nh, nest2); 391 addattr_nested_end(nh, nest); 392 return rtnl_talk(nh); 393 } 394 395 int rtnl_bridge_create(const char *name) { 396 if (if_nametoindex(name) != 0) { 397 log_info("rtnl: bridge %s already exists", name); 398 return 0; 399 } 400 char buf[NL_BUFSZ]; 401 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 402 struct ifinfomsg *ifi; 403 memset(buf, 0, sizeof(buf)); 404 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 405 nh->nlmsg_type = RTM_NEWLINK; 406 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; 407 ifi = NLMSG_DATA(nh); 408 ifi->ifi_family = AF_UNSPEC; 409 addattr_l(nh, sizeof(buf), IFLA_IFNAME, name, strlen(name)+1); 410 int nest = addattr_nested_start(nh, sizeof(buf), IFLA_LINKINFO); 411 if (nest < 0) return -1; 412 addattr_l(nh, sizeof(buf), IFLA_INFO_KIND, "bridge", 7); 413 addattr_nested_end(nh, nest); 414 return rtnl_talk(nh); 415 } 416 417 // Create a VRF device bound to a routing table. 418 // 419 // A VRF is an L3 master device: enslaved interfaces have their routes looked 420 // up in IFLA_VRF_TABLE instead of main. The table id is not decoration -- it 421 // IS the separation, which is why it is mandatory here rather than defaulted. 422 // Requires CONFIG_NET_VRF (shipped as a module, see the kernel templates). 423 int rtnl_vrf_create(const char *name, uint32_t table) { 424 if (!name || !table) return -1; 425 if (if_nametoindex(name) != 0) { 426 log_info("rtnl: vrf %s already exists", name); 427 return 0; 428 } 429 char buf[NL_BUFSZ]; 430 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 431 struct ifinfomsg *ifi; 432 memset(buf, 0, sizeof(buf)); 433 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 434 nh->nlmsg_type = RTM_NEWLINK; 435 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; 436 ifi = NLMSG_DATA(nh); 437 ifi->ifi_family = AF_UNSPEC; 438 addattr_l(nh, sizeof(buf), IFLA_IFNAME, name, strlen(name)+1); 439 int nest = addattr_nested_start(nh, sizeof(buf), IFLA_LINKINFO); 440 if (nest < 0) return -1; 441 addattr_l(nh, sizeof(buf), IFLA_INFO_KIND, "vrf", 4); 442 int nest2 = addattr_nested_start(nh, sizeof(buf), IFLA_INFO_DATA); 443 if (nest2 < 0) return -1; 444 addattr_l(nh, sizeof(buf), IFLA_VRF_TABLE, &table, sizeof(table)); 445 addattr_nested_end(nh, nest2); 446 addattr_nested_end(nh, nest); 447 log_info("rtnl: vrf %s table %u", name, table); 448 return rtnl_talk(nh); 449 } 450 451 // Enslave an interface to a VRF. Same IFLA_MASTER mechanism as bridge 452 // enslavement -- the kernel distinguishes them by the master's kind. 453 int rtnl_vrf_add_port(const char *vrf, const char *port) { 454 unsigned vidx = if_nametoindex(vrf); 455 unsigned pidx = if_nametoindex(port); 456 if (!vidx || !pidx) { 457 log_error("rtnl: vrf_add_port %s -> %s: ifindex not found (%u, %u)", port, vrf, pidx, vidx); 458 return -1; 459 } 460 char buf[NL_BUFSZ]; 461 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 462 struct ifinfomsg *ifi; 463 memset(buf, 0, sizeof(buf)); 464 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 465 nh->nlmsg_type = RTM_NEWLINK; 466 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 467 ifi = NLMSG_DATA(nh); 468 ifi->ifi_family = AF_UNSPEC; 469 ifi->ifi_index = pidx; 470 addattr_l(nh, sizeof(buf), IFLA_MASTER, &vidx, sizeof(vidx)); 471 log_info("rtnl: vrf %s enslave %s", vrf, port); 472 return rtnl_talk(nh); 473 } 474 475 int rtnl_bridge_set_stp(const char *name, bool on) { 476 unsigned ifindex = if_nametoindex(name); 477 if (!ifindex) return -1; 478 char buf[NL_BUFSZ]; 479 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 480 struct ifinfomsg *ifi; 481 memset(buf, 0, sizeof(buf)); 482 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 483 nh->nlmsg_type = RTM_NEWLINK; 484 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 485 ifi = NLMSG_DATA(nh); 486 ifi->ifi_family = AF_UNSPEC; 487 ifi->ifi_index = ifindex; 488 int nest = addattr_nested_start(nh, sizeof(buf), IFLA_LINKINFO); 489 if (nest < 0) return -1; 490 addattr_l(nh, sizeof(buf), IFLA_INFO_KIND, "bridge", 7); 491 int nest2 = addattr_nested_start(nh, sizeof(buf), IFLA_INFO_DATA); 492 if (nest2 < 0) return -1; 493 uint8_t stp = on ? 1 : 0; 494 addattr_l(nh, sizeof(buf), IFLA_BR_STP_STATE, &stp, sizeof(stp)); 495 addattr_nested_end(nh, nest2); 496 addattr_nested_end(nh, nest); 497 return rtnl_talk(nh); 498 } 499 500 int rtnl_bridge_set_vlan_aware(const char *name, bool on) { 501 unsigned ifindex = if_nametoindex(name); 502 if (!ifindex) return -1; 503 char buf[NL_BUFSZ]; 504 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 505 struct ifinfomsg *ifi; 506 memset(buf, 0, sizeof(buf)); 507 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 508 nh->nlmsg_type = RTM_NEWLINK; 509 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 510 ifi = NLMSG_DATA(nh); 511 ifi->ifi_family = AF_UNSPEC; 512 ifi->ifi_index = ifindex; 513 int nest = addattr_nested_start(nh, sizeof(buf), IFLA_LINKINFO); 514 if (nest < 0) return -1; 515 addattr_l(nh, sizeof(buf), IFLA_INFO_KIND, "bridge", 7); 516 int nest2 = addattr_nested_start(nh, sizeof(buf), IFLA_INFO_DATA); 517 if (nest2 < 0) return -1; 518 uint8_t vlan = on ? 1 : 0; 519 addattr_l(nh, sizeof(buf), IFLA_BR_VLAN_FILTERING, &vlan, sizeof(vlan)); 520 addattr_nested_end(nh, nest2); 521 addattr_nested_end(nh, nest); 522 return rtnl_talk(nh); 523 } 524 525 int rtnl_bridge_add_port(const char *br, const char *port) { 526 unsigned bridx = if_nametoindex(br); 527 unsigned pidx = if_nametoindex(port); 528 if (!bridx || !pidx) { 529 log_error("rtnl: bridge_add_port %s -> %s: ifindex not found (%u, %u)", port, br, pidx, bridx); 530 return -1; 531 } 532 char buf[NL_BUFSZ]; 533 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 534 struct ifinfomsg *ifi; 535 memset(buf, 0, sizeof(buf)); 536 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 537 nh->nlmsg_type = RTM_NEWLINK; 538 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 539 ifi = NLMSG_DATA(nh); 540 ifi->ifi_family = AF_UNSPEC; 541 ifi->ifi_index = pidx; 542 addattr_l(nh, sizeof(buf), IFLA_MASTER, &bridx, sizeof(bridx)); 543 return rtnl_talk(nh); 544 } 545 546 int rtnl_bridge_del_port(const char *br, const char *port) { 547 (void)br; 548 unsigned pidx = if_nametoindex(port); 549 if (!pidx) return -1; 550 char buf[NL_BUFSZ]; 551 struct nlmsghdr *nh = (struct nlmsghdr*)buf; 552 struct ifinfomsg *ifi; 553 memset(buf, 0, sizeof(buf)); 554 nh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); 555 nh->nlmsg_type = RTM_NEWLINK; 556 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 557 ifi = NLMSG_DATA(nh); 558 ifi->ifi_family = AF_UNSPEC; 559 ifi->ifi_index = pidx; 560 // No master = 0 removes from bridge? Actually need to set master 0 and use NLM_F? 561 // For del, we set master to 0 and use RTM_NEWLINK with master 0? The kernel handles master 0 as no bridge. 562 // Alternative is to use RTM_DELLINK? No, enslavement is via master. 563 // Setting master to 0 and sending NEWLINK should release. 564 // But to be safe, we send with master 0 and hope. 565 // If that doesn't work, we can try to use `ip link set dev port nomaster` 566 uint32_t zero = 0; 567 addattr_l(nh, sizeof(buf), IFLA_MASTER, &zero, sizeof(zero)); 568 return rtnl_talk(nh); 569 }