if-linux.c revision 938bc384f44031877543765a9ae18c764f5da9c8
1/* 2 * dhcpcd - DHCP client daemon 3 * Copyright 2006-2008 Roy Marples <roy@marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <asm/types.h> /* Needed for 2.4 kernels */ 29 30#include <sys/types.h> 31#include <sys/socket.h> 32#include <sys/stat.h> 33#include <sys/ioctl.h> 34#include <sys/param.h> 35 36#include <arpa/inet.h> 37#include <linux/netlink.h> 38#include <linux/rtnetlink.h> 39#include <netinet/ether.h> 40#include <netpacket/packet.h> 41 42#include <errno.h> 43#include <stddef.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <unistd.h> 48 49/* Support older kernels */ 50#ifndef IFLA_WIRELESS 51# define IFLA_WIRELSSS (IFLFA_MASTER + 1) 52#endif 53 54#include "config.h" 55#include "common.h" 56#include "dhcp.h" 57#include "net.h" 58 59#define BUFFERLEN 256 60 61int 62open_link_socket(struct interface *iface) 63{ 64 int fd; 65 struct sockaddr_nl nl; 66 67 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) 68 return -1; 69 memset(&nl, 0, sizeof(nl)); 70 nl.nl_family = AF_NETLINK; 71 nl.nl_groups = RTMGRP_LINK; 72 if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) 73 return -1; 74 set_cloexec(fd); 75 if (iface->link_fd != -1) 76 close(iface->link_fd); 77 iface->link_fd = fd; 78 return 0; 79} 80 81static int 82get_netlink(int fd, int flags, 83 int (*callback)(struct nlmsghdr *, const char *), 84 const char *ifname) 85{ 86 char *buffer = NULL; 87 ssize_t bytes; 88 struct nlmsghdr *nlm; 89 int r = -1; 90 91 buffer = xzalloc(sizeof(char) * BUFFERLEN); 92 for (;;) { 93 bytes = recv(fd, buffer, BUFFERLEN, flags); 94 if (bytes == -1) { 95 if (errno == EAGAIN) { 96 r = 0; 97 goto eexit; 98 } 99 if (errno == EINTR) 100 continue; 101 goto eexit; 102 } 103 for (nlm = (struct nlmsghdr *)buffer; 104 NLMSG_OK(nlm, (size_t)bytes); 105 nlm = NLMSG_NEXT(nlm, bytes)) 106 { 107 r = callback(nlm, ifname); 108 if (r != 0) 109 goto eexit; 110 } 111 } 112 113eexit: 114 free(buffer); 115 return r; 116} 117 118static int 119err_netlink(struct nlmsghdr *nlm, _unused const char *ifname) 120{ 121 struct nlmsgerr *err; 122 int l; 123 124 if (nlm->nlmsg_type != NLMSG_ERROR) 125 return 0; 126 l = nlm->nlmsg_len - sizeof(*nlm); 127 if ((size_t)l < sizeof(*err)) { 128 errno = EBADMSG; 129 return -1; 130 } 131 err = (struct nlmsgerr *)NLMSG_DATA(nlm); 132 if (err->error == 0) 133 return l; 134 errno = -err->error; 135 return -1; 136} 137 138static int 139link_netlink(struct nlmsghdr *nlm, const char *ifname) 140{ 141 int len; 142 struct rtattr *rta; 143 struct ifinfomsg *ifi; 144 char ifn[IF_NAMESIZE + 1]; 145 146 if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK) 147 return 0; 148 len = nlm->nlmsg_len - sizeof(*nlm); 149 if ((size_t)len < sizeof(*ifi)) { 150 errno = EBADMSG; 151 return -1; 152 } 153 ifi = NLMSG_DATA(nlm); 154 if (ifi->ifi_flags & IFF_LOOPBACK) 155 return 0; 156 rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi))); 157 len = NLMSG_PAYLOAD(nlm, sizeof(*ifi)); 158 *ifn = '\0'; 159 while (RTA_OK(rta, len)) { 160 switch (rta->rta_type) { 161 case IFLA_WIRELESS: 162 /* Ignore wireless messages */ 163 if (nlm->nlmsg_type == RTM_NEWLINK && 164 ifi->ifi_change == 0) 165 return 0; 166 break; 167 case IFLA_IFNAME: 168 strlcpy(ifn, RTA_DATA(rta), sizeof(ifn)); 169 break; 170 } 171 rta = RTA_NEXT(rta, len); 172 } 173 174 if (strncmp(ifname, ifn, sizeof(ifn)) == 0) 175 return 1; 176 return 0; 177} 178 179int 180link_changed(struct interface *iface) 181{ 182 return get_netlink(iface->link_fd, MSG_DONTWAIT, 183 &link_netlink, iface->name); 184} 185 186static int 187send_netlink(struct nlmsghdr *hdr) 188{ 189 int fd, r; 190 struct sockaddr_nl nl; 191 struct iovec iov; 192 struct msghdr msg; 193 static unsigned int seq; 194 195 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) 196 return -1; 197 memset(&nl, 0, sizeof(nl)); 198 nl.nl_family = AF_NETLINK; 199 if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) { 200 close(fd); 201 return -1; 202 } 203 memset(&iov, 0, sizeof(iov)); 204 iov.iov_base = hdr; 205 iov.iov_len = hdr->nlmsg_len; 206 memset(&msg, 0, sizeof(msg)); 207 msg.msg_name = &nl; 208 msg.msg_namelen = sizeof(nl); 209 msg.msg_iov = &iov; 210 msg.msg_iovlen = 1; 211 /* Request a reply */ 212 hdr->nlmsg_flags |= NLM_F_ACK; 213 hdr->nlmsg_seq = ++seq; 214 215 if (sendmsg(fd, &msg, 0) != -1) 216 r = get_netlink(fd, 0, &err_netlink, NULL); 217 else 218 r = -1; 219 close(fd); 220 return r; 221} 222 223#define NLMSG_TAIL(nmsg) \ 224 ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len))) 225 226static int 227add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type, 228 const void *data, int alen) 229{ 230 int len = RTA_LENGTH(alen); 231 struct rtattr *rta; 232 233 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { 234 errno = ENOBUFS; 235 return -1; 236 } 237 238 rta = NLMSG_TAIL(n); 239 rta->rta_type = type; 240 rta->rta_len = len; 241 memcpy(RTA_DATA(rta), data, alen); 242 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); 243 244 return 0; 245} 246 247static int 248add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data) 249{ 250 int len = RTA_LENGTH(sizeof(data)); 251 struct rtattr *rta; 252 253 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { 254 errno = ENOBUFS; 255 return -1; 256 } 257 258 rta = NLMSG_TAIL(n); 259 rta->rta_type = type; 260 rta->rta_len = len; 261 memcpy(RTA_DATA(rta), &data, sizeof(data)); 262 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 263 264 return 0; 265} 266 267struct nlma 268{ 269 struct nlmsghdr hdr; 270 struct ifaddrmsg ifa; 271 char buffer[64]; 272}; 273 274struct nlmr 275{ 276 struct nlmsghdr hdr; 277 struct rtmsg rt; 278 char buffer[256]; 279}; 280 281int 282if_address(const char *ifname, 283 const struct in_addr *address, const struct in_addr *netmask, 284 const struct in_addr *broadcast, int action) 285{ 286 struct nlma *nlm; 287 int retval = 0; 288 289 nlm = xzalloc(sizeof(*nlm)); 290 nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); 291 nlm->hdr.nlmsg_flags = NLM_F_REQUEST; 292 if (action >= 0) { 293 nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 294 nlm->hdr.nlmsg_type = RTM_NEWADDR; 295 } else 296 nlm->hdr.nlmsg_type = RTM_DELADDR; 297 if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) { 298 free(nlm); 299 errno = ENODEV; 300 return -1; 301 } 302 nlm->ifa.ifa_family = AF_INET; 303 nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask); 304 /* This creates the aliased interface */ 305 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL, 306 ifname, strlen(ifname) + 1); 307 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL, 308 &address->s_addr, sizeof(address->s_addr)); 309 if (action >= 0) 310 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST, 311 &broadcast->s_addr, sizeof(broadcast->s_addr)); 312 313 if (send_netlink(&nlm->hdr) == -1) 314 retval = -1; 315 free(nlm); 316 return retval; 317} 318 319int 320if_route(const struct interface *iface, 321 const struct in_addr *destination, const struct in_addr *netmask, 322 const struct in_addr *gateway, int metric, int action) 323{ 324 struct nlmr *nlm; 325 unsigned int ifindex; 326 int retval = 0; 327 328 if (!(ifindex = if_nametoindex(iface->name))) { 329 errno = ENODEV; 330 return -1; 331 } 332 333 nlm = xzalloc(sizeof(*nlm)); 334 nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); 335 nlm->hdr.nlmsg_type = RTM_NEWROUTE; 336 if (action == 0) 337 nlm->hdr.nlmsg_flags = NLM_F_REPLACE; 338 else if (action == 1) 339 /* 340 * ers@google: 341 * commented out NLM_F_EXCL here and below. We 342 * sometimes keep one interface up while we are 343 * configuring the other one, and this flag 344 * causes route addition to fail. 345 */ 346 nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL */; 347 else 348 nlm->hdr.nlmsg_type = RTM_DELROUTE; 349 nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; 350 nlm->rt.rtm_family = AF_INET; 351 nlm->rt.rtm_table = RT_TABLE_MAIN; 352 353 if (action == -1 || action == -2) 354 nlm->rt.rtm_scope = RT_SCOPE_NOWHERE; 355 else { 356 nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/; 357 /* We only change route metrics for kernel routes */ 358 if (destination->s_addr == 359 (iface->addr.s_addr & iface->net.s_addr) && 360 netmask->s_addr == iface->net.s_addr) 361 nlm->rt.rtm_protocol = RTPROT_KERNEL; 362 else 363 nlm->rt.rtm_protocol = RTPROT_BOOT; 364 if (gateway->s_addr == INADDR_ANY || 365 (gateway->s_addr == destination->s_addr && 366 netmask->s_addr == INADDR_BROADCAST)) 367 nlm->rt.rtm_scope = RT_SCOPE_LINK; 368 else 369 nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE; 370 nlm->rt.rtm_type = RTN_UNICAST; 371 } 372 373 nlm->rt.rtm_dst_len = inet_ntocidr(*netmask); 374 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST, 375 &destination->s_addr, sizeof(destination->s_addr)); 376 if (nlm->rt.rtm_protocol == RTPROT_KERNEL) { 377 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC, 378 &iface->addr.s_addr, sizeof(iface->addr.s_addr)); 379 } 380 /* If destination == gateway then don't add the gateway */ 381 if (destination->s_addr != gateway->s_addr || 382 netmask->s_addr != INADDR_BROADCAST) 383 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY, 384 &gateway->s_addr, sizeof(gateway->s_addr)); 385 386 add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex); 387 add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric); 388 389 if (send_netlink(&nlm->hdr) == -1) 390 retval = -1; 391 free(nlm); 392 return retval; 393} 394