if-linux.c revision a3a2260384a906e1674c7498c2f479e9f37bc503
17dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch/* 27dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * dhcpcd - DHCP client daemon 37dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * Copyright (c) 2006-2011 Roy Marples <roy@marples.name> 47dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * All rights reserved 57dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 67dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * Redistribution and use in source and binary forms, with or without 77dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * modification, are permitted provided that the following conditions 87dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * are met: 97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * 1. Redistributions of source code must retain the above copyright 109ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch * notice, this list of conditions and the following disclaimer. 119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch * 2. Redistributions in binary form must reproduce the above copyright 127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * notice, this list of conditions and the following disclaimer in the 137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * documentation and/or other materials provided with the distribution. 147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * 15558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * SUCH DAMAGE. 267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch */ 277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <asm/types.h> /* Needed for 2.4 kernels */ 297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <sys/types.h> 317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <sys/socket.h> 327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <sys/ioctl.h> 337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <sys/param.h> 347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <linux/netlink.h> 367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <linux/rtnetlink.h> 377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch/* Support older kernels */ 397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#ifndef IFLA_WIRELESS 407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# define IFLA_WIRELESS (IFLA_MASTER + 1) 417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#endif 427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch/* For some reason, glibc doesn't include newer flags from linux/if.h 447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * However, we cannot include linux/if.h directly as it conflicts 457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * with the glibc version. D'oh! */ 46558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#ifndef IFF_LOWER_UP 47558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ 48558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#endif 49558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 50558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#include <errno.h> 517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <ctype.h> 527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <stddef.h> 537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <stdio.h> 547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <stdlib.h> 557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <string.h> 567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include <unistd.h> 577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "config.h" 59558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#include "common.h" 60558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch#include "configure.h" 617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "dhcp.h" 627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "net.h" 637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochstatic int sock_fd; 657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochstatic struct sockaddr_nl sock_nl; 667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochint 687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochif_init(struct interface *iface) 697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch{ 709ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch char path[PATH_MAX]; 719ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch FILE *fp; 729ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch int n; 739ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch 747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch /* We enable promote_secondaries so that we can do this 757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * add 192.168.1.2/24 76558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch * add 192.168.1.3/24 77558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch * del 192.168.1.2/24 787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * and the subnet mask moves onto 192.168.1.3/24 797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * This matches the behaviour of BSD which makes coding dhcpcd 807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * a little easier as there's just one behaviour. */ 817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch snprintf(path, sizeof(path), 82 "/proc/sys/net/ipv4/conf/%s/promote_secondaries", 83 iface->name); 84 85 fp = fopen(path, "w"); 86 if (fp == NULL) 87 return errno == ENOENT ? 0 : -1; 88 n = fprintf(fp, "1"); 89 fclose(fp); 90 return n == -1 ? -1 : 0; 91} 92 93int 94if_conf(struct interface *iface) 95{ 96 char path[PATH_MAX], buf[1]; 97 FILE *fp; 98 99 /* Some qeth setups require the use of the broadcast flag. */ 100 snprintf(path, sizeof(path), 101 "/sys/class/net/%s/device/layer2", 102 iface->name); 103 104 fp = fopen(path, "r"); 105 if (fp == NULL) 106 return errno == ENOENT ? 0 : -1; 107 if (fgets(buf, sizeof(buf), fp) != NULL && buf[0] == '0') 108 iface->state->options->options |= DHCPCD_BROADCAST; 109 fclose(fp); 110 return 0; 111} 112 113static int 114_open_link_socket(struct sockaddr_nl *nl) 115{ 116 int fd; 117 118 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) 119 return -1; 120 nl->nl_family = AF_NETLINK; 121 if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1) 122 return -1; 123 set_cloexec(fd); 124 return fd; 125} 126 127int 128init_sockets(void) 129{ 130 if ((socket_afnet = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 131 return -1; 132 set_cloexec(socket_afnet); 133 sock_fd = _open_link_socket(&sock_nl); 134 set_cloexec(sock_fd); 135 return sock_fd; 136} 137 138int 139open_link_socket(void) 140{ 141 struct sockaddr_nl snl; 142 143 memset(&snl, 0, sizeof(snl)); 144 snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR; 145 return _open_link_socket(&snl); 146} 147 148static int 149get_netlink(int fd, int flags, 150 int (*callback)(struct nlmsghdr *)) 151{ 152 char *buf = NULL, *nbuf; 153 ssize_t buflen = 0, bytes; 154 struct nlmsghdr *nlm; 155 int r = -1; 156 157 for (;;) { 158 bytes = recv(fd, NULL, 0, 159 flags | MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC); 160 if (bytes == -1) { 161 if (errno == EAGAIN) { 162 r = 0; 163 goto eexit; 164 } 165 if (errno == EINTR) 166 continue; 167 goto eexit; 168 } else if (bytes == buflen) { 169 /* Support kernels older than 2.6.22 */ 170 if (bytes == 0) 171 bytes = 512; 172 else 173 bytes *= 2; 174 } 175 if (buflen < bytes) { 176 /* Alloc 1 more so we work with older kernels */ 177 buflen = bytes + 1; 178 nbuf = realloc(buf, buflen); 179 if (nbuf == NULL) 180 goto eexit; 181 buf = nbuf; 182 } 183 bytes = recv(fd, buf, buflen, flags); 184 if (bytes == -1) { 185 if (errno == EAGAIN) { 186 r = 0; 187 goto eexit; 188 } 189 if (errno == EINTR) 190 continue; 191 goto eexit; 192 } 193 for (nlm = (struct nlmsghdr *)buf; 194 NLMSG_OK(nlm, (size_t)bytes); 195 nlm = NLMSG_NEXT(nlm, bytes)) 196 { 197 r = callback(nlm); 198 if (r != 0) 199 goto eexit; 200 } 201 } 202 203eexit: 204 free(buf); 205 return r; 206} 207 208static int 209err_netlink(struct nlmsghdr *nlm) 210{ 211 struct nlmsgerr *err; 212 int l; 213 214 if (nlm->nlmsg_type != NLMSG_ERROR) 215 return 0; 216 l = nlm->nlmsg_len - sizeof(*nlm); 217 if ((size_t)l < sizeof(*err)) { 218 errno = EBADMSG; 219 return -1; 220 } 221 err = (struct nlmsgerr *)NLMSG_DATA(nlm); 222 if (err->error == 0) 223 return l; 224 errno = -err->error; 225 return -1; 226} 227 228static int 229link_route(struct nlmsghdr *nlm) 230{ 231 int len, idx, metric; 232 struct rtattr *rta; 233 struct rtmsg *rtm; 234 struct rt rt; 235 char ifn[IF_NAMESIZE + 1]; 236 237 if (nlm->nlmsg_type != RTM_DELROUTE) 238 return 0; 239 240 len = nlm->nlmsg_len - sizeof(*nlm); 241 if ((size_t)len < sizeof(*rtm)) { 242 errno = EBADMSG; 243 return -1; 244 } 245 rtm = NLMSG_DATA(nlm); 246 if (rtm->rtm_type != RTN_UNICAST || 247 rtm->rtm_table != RT_TABLE_MAIN || 248 rtm->rtm_family != AF_INET || 249 nlm->nlmsg_pid == (uint32_t)getpid()) 250 return 1; 251 rta = (struct rtattr *) ((char *)rtm + NLMSG_ALIGN(sizeof(*rtm))); 252 len = NLMSG_PAYLOAD(nlm, sizeof(*rtm)); 253 rt.iface = NULL; 254 rt.dest.s_addr = INADDR_ANY; 255 rt.net.s_addr = INADDR_ANY; 256 rt.gate.s_addr = INADDR_ANY; 257 rt.next = NULL; 258 metric = 0; 259 while (RTA_OK(rta, len)) { 260 switch (rta->rta_type) { 261 case RTA_DST: 262 memcpy(&rt.dest.s_addr, RTA_DATA(rta), 263 sizeof(rt.dest.s_addr)); 264 break; 265 case RTA_GATEWAY: 266 memcpy(&rt.gate.s_addr, RTA_DATA(rta), 267 sizeof(rt.gate.s_addr)); 268 break; 269 case RTA_OIF: 270 idx = *(int *)RTA_DATA(rta); 271 if (if_indextoname(idx, ifn)) 272 rt.iface = find_interface(ifn); 273 break; 274 case RTA_PRIORITY: 275 metric = *(int *)RTA_DATA(rta); 276 break; 277 } 278 rta = RTA_NEXT(rta, len); 279 } 280 if (rt.iface != NULL) { 281 if (metric == rt.iface->metric) { 282 inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net); 283 route_deleted(&rt); 284 } 285 } 286 return 1; 287} 288 289static int 290link_addr(struct nlmsghdr *nlm) 291{ 292 int len; 293 struct rtattr *rta; 294 struct ifaddrmsg *ifa; 295 struct in_addr addr, net, dest; 296 char ifn[IF_NAMESIZE + 1]; 297 struct interface *iface; 298 299 if (nlm->nlmsg_type != RTM_DELADDR && nlm->nlmsg_type != RTM_NEWADDR) 300 return 0; 301 302 len = nlm->nlmsg_len - sizeof(*nlm); 303 if ((size_t)len < sizeof(*ifa)) { 304 errno = EBADMSG; 305 return -1; 306 } 307 if (nlm->nlmsg_pid == (uint32_t)getpid()) 308 return 1; 309 ifa = NLMSG_DATA(nlm); 310 if (if_indextoname(ifa->ifa_index, ifn) == NULL) 311 return -1; 312 iface = find_interface(ifn); 313 if (iface == NULL) 314 return 1; 315 rta = (struct rtattr *) IFA_RTA(ifa); 316 len = NLMSG_PAYLOAD(nlm, sizeof(*ifa)); 317 addr.s_addr = dest.s_addr = INADDR_ANY; 318 dest.s_addr = INADDR_ANY; 319 inet_cidrtoaddr(ifa->ifa_prefixlen, &net); 320 while (RTA_OK(rta, len)) { 321 switch (rta->rta_type) { 322 case IFA_ADDRESS: 323 if (iface->flags & IFF_POINTOPOINT) { 324 memcpy(&dest.s_addr, RTA_DATA(rta), 325 sizeof(addr.s_addr)); 326 } 327 break; 328 case IFA_LOCAL: 329 memcpy(&addr.s_addr, RTA_DATA(rta), 330 sizeof(addr.s_addr)); 331 break; 332 } 333 rta = RTA_NEXT(rta, len); 334 } 335 handle_ifa(nlm->nlmsg_type, ifn, &addr, &net, &dest); 336 return 1; 337} 338 339static int 340link_netlink(struct nlmsghdr *nlm) 341{ 342 int len; 343 struct rtattr *rta; 344 struct ifinfomsg *ifi; 345 char ifn[IF_NAMESIZE + 1]; 346 347 len = link_route(nlm); 348 if (len != 0) 349 return len; 350 len = link_addr(nlm); 351 if (len != 0) 352 return len; 353 354 if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK) 355 return 0; 356 len = nlm->nlmsg_len - sizeof(*nlm); 357 if ((size_t)len < sizeof(*ifi)) { 358 errno = EBADMSG; 359 return -1; 360 } 361 ifi = NLMSG_DATA(nlm); 362 if (ifi->ifi_flags & IFF_LOOPBACK) 363 return 1; 364 rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi))); 365 len = NLMSG_PAYLOAD(nlm, sizeof(*ifi)); 366 *ifn = '\0'; 367 while (RTA_OK(rta, len)) { 368 switch (rta->rta_type) { 369 case IFLA_WIRELESS: 370 /* Ignore wireless messages */ 371 if (nlm->nlmsg_type == RTM_NEWLINK && 372 ifi->ifi_change == 0) 373 return 1; 374 break; 375 case IFLA_IFNAME: 376 strlcpy(ifn, RTA_DATA(rta), sizeof(ifn)); 377 break; 378 } 379 rta = RTA_NEXT(rta, len); 380 } 381 382 if (nlm->nlmsg_type == RTM_DELLINK) { 383 handle_interface(-1, ifn); 384 return 1; 385 } 386 387 /* Bridge interfaces set IFF_LOWER_UP when they have a valid 388 * hardware address. To trigger a valid hardware address pickup 389 * we need to pretend that that don't exist until they have 390 * IFF_LOWER_UP set. */ 391 if (ifi->ifi_flags & IFF_MASTER && !(ifi->ifi_flags & IFF_LOWER_UP)) { 392 handle_interface(-1, ifn); 393 return 1; 394 } 395 396 handle_carrier(ifi->ifi_flags & IFF_RUNNING ? 1 : -1, 397 ifi->ifi_flags, ifn); 398 return 1; 399} 400 401int 402manage_link(int fd) 403{ 404 return get_netlink(fd, MSG_DONTWAIT, &link_netlink); 405} 406 407static int 408send_netlink(struct nlmsghdr *hdr) 409{ 410 int r; 411 struct iovec iov; 412 struct msghdr msg; 413 static unsigned int seq; 414 415 memset(&iov, 0, sizeof(iov)); 416 iov.iov_base = hdr; 417 iov.iov_len = hdr->nlmsg_len; 418 memset(&msg, 0, sizeof(msg)); 419 msg.msg_name = &sock_nl; 420 msg.msg_namelen = sizeof(sock_nl); 421 msg.msg_iov = &iov; 422 msg.msg_iovlen = 1; 423 /* Request a reply */ 424 hdr->nlmsg_flags |= NLM_F_ACK; 425 hdr->nlmsg_seq = ++seq; 426 427 if (sendmsg(sock_fd, &msg, 0) != -1) 428 r = get_netlink(sock_fd, 0, &err_netlink); 429 else 430 r = -1; 431 return r; 432} 433 434#define NLMSG_TAIL(nmsg) \ 435 ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len))) 436 437static int 438add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type, 439 const void *data, int alen) 440{ 441 int len = RTA_LENGTH(alen); 442 struct rtattr *rta; 443 444 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { 445 errno = ENOBUFS; 446 return -1; 447 } 448 449 rta = NLMSG_TAIL(n); 450 rta->rta_type = type; 451 rta->rta_len = len; 452 memcpy(RTA_DATA(rta), data, alen); 453 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); 454 455 return 0; 456} 457 458static int 459add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data) 460{ 461 int len = RTA_LENGTH(sizeof(data)); 462 struct rtattr *rta; 463 464 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { 465 errno = ENOBUFS; 466 return -1; 467 } 468 469 rta = NLMSG_TAIL(n); 470 rta->rta_type = type; 471 rta->rta_len = len; 472 memcpy(RTA_DATA(rta), &data, sizeof(data)); 473 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 474 475 return 0; 476} 477 478struct nlma 479{ 480 struct nlmsghdr hdr; 481 struct ifaddrmsg ifa; 482 char buffer[64]; 483}; 484 485struct nlmr 486{ 487 struct nlmsghdr hdr; 488 struct rtmsg rt; 489 char buffer[256]; 490}; 491 492int 493if_address(const struct interface *iface, 494 const struct in_addr *address, const struct in_addr *netmask, 495 const struct in_addr *broadcast, int action) 496{ 497 struct nlma *nlm; 498 int retval = 0; 499 500 nlm = xzalloc(sizeof(*nlm)); 501 nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); 502 nlm->hdr.nlmsg_flags = NLM_F_REQUEST; 503 if (action >= 0) { 504 nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 505 nlm->hdr.nlmsg_type = RTM_NEWADDR; 506 } else 507 nlm->hdr.nlmsg_type = RTM_DELADDR; 508 if (!(nlm->ifa.ifa_index = if_nametoindex(iface->name))) { 509 free(nlm); 510 errno = ENODEV; 511 return -1; 512 } 513 nlm->ifa.ifa_family = AF_INET; 514 nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask); 515 /* This creates the aliased interface */ 516 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL, 517 iface->name, strlen(iface->name) + 1); 518 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL, 519 &address->s_addr, sizeof(address->s_addr)); 520 if (action >= 0 && broadcast) 521 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST, 522 &broadcast->s_addr, sizeof(broadcast->s_addr)); 523 524 if (send_netlink(&nlm->hdr) == -1) 525 retval = -1; 526 free(nlm); 527 return retval; 528} 529 530int 531if_route(const struct rt *rt, int action) 532{ 533 struct nlmr *nlm; 534 unsigned int ifindex; 535 int retval = 0; 536 537 if (!(ifindex = if_nametoindex(rt->iface->name))) { 538 errno = ENODEV; 539 return -1; 540 } 541 542 nlm = xzalloc(sizeof(*nlm)); 543 nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); 544 nlm->hdr.nlmsg_type = RTM_NEWROUTE; 545 if (action == 0) 546 nlm->hdr.nlmsg_flags = NLM_F_REPLACE; 547 else if (action == 1) 548 nlm->hdr.nlmsg_flags = NLM_F_CREATE /*| NLM_F_EXCL*/; 549 else 550 nlm->hdr.nlmsg_type = RTM_DELROUTE; 551 nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; 552 nlm->rt.rtm_family = AF_INET; 553 nlm->rt.rtm_table = RT_TABLE_MAIN; 554 555 if (action == -1 || action == -2) 556 nlm->rt.rtm_scope = RT_SCOPE_NOWHERE; 557 else { 558 nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/; 559 /* We only change route metrics for kernel routes */ 560 if (rt->dest.s_addr == 561 (rt->iface->addr.s_addr & rt->iface->net.s_addr) && 562 rt->net.s_addr == rt->iface->net.s_addr) 563 nlm->rt.rtm_protocol = RTPROT_KERNEL; 564 else 565 nlm->rt.rtm_protocol = RTPROT_BOOT; 566 if (rt->gate.s_addr == INADDR_ANY || 567 (rt->gate.s_addr == rt->dest.s_addr && 568 rt->net.s_addr == INADDR_BROADCAST)) 569 nlm->rt.rtm_scope = RT_SCOPE_LINK; 570 else 571 nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE; 572 nlm->rt.rtm_type = RTN_UNICAST; 573 } 574 575 nlm->rt.rtm_dst_len = inet_ntocidr(rt->net); 576 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST, 577 &rt->dest.s_addr, sizeof(rt->dest.s_addr)); 578 if (nlm->rt.rtm_protocol == RTPROT_KERNEL) { 579 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC, 580 &rt->iface->addr.s_addr, sizeof(rt->iface->addr.s_addr)); 581 } 582 /* If destination == gateway then don't add the gateway */ 583 if (rt->dest.s_addr != rt->gate.s_addr || 584 rt->net.s_addr != INADDR_BROADCAST) 585 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY, 586 &rt->gate.s_addr, sizeof(rt->gate.s_addr)); 587 588 add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex); 589 add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, rt->metric); 590 591 if (send_netlink(&nlm->hdr) == -1) 592 retval = -1; 593 free(nlm); 594 return retval; 595} 596