if-linux.c revision e95877ecfa1170d77b1ec1f66752725cdda01b64
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#include "config.h" 50#include "common.h" 51#include "dhcp.h" 52#include "net.h" 53 54/* This netlink stuff is overly compex IMO. 55 * The BSD implementation is much cleaner and a lot less code. 56 * send_netlink handles the actual transmission so we can work out 57 * if there was an error or not. */ 58#define BUFFERLEN 256 59static int 60send_netlink(struct nlmsghdr *hdr) 61{ 62 int s; 63 pid_t mypid = getpid (); 64 struct sockaddr_nl nl; 65 struct iovec iov; 66 struct msghdr msg; 67 static unsigned int seq; 68 char *buffer = NULL; 69 ssize_t bytes; 70 union 71 { 72 char *buffer; 73 struct nlmsghdr *nlm; 74 } h; 75 int len, l; 76 struct nlmsgerr *err; 77 78 if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) 79 return -1; 80 81 memset(&nl, 0, sizeof(nl)); 82 nl.nl_family = AF_NETLINK; 83 if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1) 84 goto eexit; 85 86 memset(&iov, 0, sizeof(iov)); 87 iov.iov_base = hdr; 88 iov.iov_len = hdr->nlmsg_len; 89 90 memset(&msg, 0, sizeof(msg)); 91 msg.msg_name = &nl; 92 msg.msg_namelen = sizeof(nl); 93 msg.msg_iov = &iov; 94 msg.msg_iovlen = 1; 95 96 /* Request a reply */ 97 hdr->nlmsg_flags |= NLM_F_ACK; 98 hdr->nlmsg_seq = ++seq; 99 100 if (sendmsg(s, &msg, 0) == -1) 101 goto eexit; 102 103 buffer = xzalloc(sizeof(char) * BUFFERLEN); 104 iov.iov_base = buffer; 105 106 for (;;) { 107 iov.iov_len = BUFFERLEN; 108 bytes = recvmsg(s, &msg, 0); 109 110 if (bytes == -1) { 111 if (errno == EINTR) 112 continue; 113 goto eexit; 114 } 115 116 if (bytes == 0) { 117 errno = ENODATA; 118 goto eexit; 119 } 120 121 if (msg.msg_namelen != sizeof(nl)) { 122 errno = EBADMSG; 123 goto eexit; 124 } 125 126 for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) { 127 len = h.nlm->nlmsg_len; 128 l = len - sizeof(*h.nlm); 129 err = (struct nlmsgerr *)NLMSG_DATA(h.nlm); 130 131 if (l < 0 || len > bytes) { 132 errno = EBADMSG; 133 goto eexit; 134 } 135 136 /* Ensure it's our message */ 137 if (nl.nl_pid != 0 || 138 (pid_t)h.nlm->nlmsg_pid != mypid || 139 h.nlm->nlmsg_seq != seq) 140 { 141 /* Next Message */ 142 bytes -= NLMSG_ALIGN(len); 143 h.buffer += NLMSG_ALIGN(len); 144 continue; 145 } 146 147 /* We get an NLMSG_ERROR back with a code of zero for success */ 148 if (h.nlm->nlmsg_type != NLMSG_ERROR) 149 continue; 150 151 if ((unsigned)l < sizeof(*err)) { 152 errno = EBADMSG; 153 goto eexit; 154 } 155 156 if (err->error == 0) { 157 close(s); 158 free(buffer); 159 return l; 160 } 161 162 errno = -err->error; 163 goto eexit; 164 } 165 } 166 167eexit: 168 close(s); 169 free(buffer); 170 return -1; 171} 172 173#define NLMSG_TAIL(nmsg) \ 174 ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len))) 175 176static int 177add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type, 178 const void *data, int alen) 179{ 180 int len = RTA_LENGTH(alen); 181 struct rtattr *rta; 182 183 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { 184 errno = ENOBUFS; 185 return -1; 186 } 187 188 rta = NLMSG_TAIL(n); 189 rta->rta_type = type; 190 rta->rta_len = len; 191 memcpy(RTA_DATA(rta), data, alen); 192 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); 193 194 return 0; 195} 196 197static int 198add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data) 199{ 200 int len = RTA_LENGTH(sizeof(data)); 201 struct rtattr *rta; 202 203 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { 204 errno = ENOBUFS; 205 return -1; 206 } 207 208 rta = NLMSG_TAIL(n); 209 rta->rta_type = type; 210 rta->rta_len = len; 211 memcpy(RTA_DATA(rta), &data, sizeof(data)); 212 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 213 214 return 0; 215} 216 217struct nlma 218{ 219 struct nlmsghdr hdr; 220 struct ifaddrmsg ifa; 221 char buffer[64]; 222}; 223 224struct nlmr 225{ 226 struct nlmsghdr hdr; 227 struct rtmsg rt; 228 char buffer[256]; 229}; 230 231int 232if_address(const char *ifname, 233 const struct in_addr *address, const struct in_addr *netmask, 234 const struct in_addr *broadcast, int action) 235{ 236 struct nlma *nlm; 237 int retval = 0; 238 239 nlm = xzalloc(sizeof(*nlm)); 240 nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); 241 nlm->hdr.nlmsg_flags = NLM_F_REQUEST; 242 if (action >= 0) { 243 nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 244 nlm->hdr.nlmsg_type = RTM_NEWADDR; 245 } else 246 nlm->hdr.nlmsg_type = RTM_DELADDR; 247 if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) { 248 free(nlm); 249 errno = ENODEV; 250 return -1; 251 } 252 nlm->ifa.ifa_family = AF_INET; 253 nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask); 254 /* This creates the aliased interface */ 255 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL, 256 ifname, strlen(ifname) + 1); 257 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL, 258 &address->s_addr, sizeof(address->s_addr)); 259 if (action >= 0) 260 add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST, 261 &broadcast->s_addr, sizeof(broadcast->s_addr)); 262 263 if (send_netlink(&nlm->hdr) == -1) 264 retval = -1; 265 free(nlm); 266 return retval; 267} 268 269int 270if_route(const char *ifname, 271 const struct in_addr *destination, const struct in_addr *netmask, 272 const struct in_addr *gateway, int metric, int action) 273{ 274 struct nlmr *nlm; 275 unsigned int ifindex; 276 int retval = 0; 277 278 279 if (!(ifindex = if_nametoindex(ifname))) { 280 errno = ENODEV; 281 return -1; 282 } 283 284 nlm = xzalloc(sizeof(*nlm)); 285 nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); 286 nlm->hdr.nlmsg_type = RTM_NEWROUTE; 287 if (action == 0) 288 nlm->hdr.nlmsg_flags = NLM_F_REPLACE; 289 else if (action > 0) 290 /* 291 * commented out NLM_F_EXCL here and 292 * below. We sometimes keep one interface up while 293 * we are configuring the other one, and this flag 294 * causes route addition to fail. 295 */ 296 nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL*/; 297 else 298 nlm->hdr.nlmsg_type = RTM_DELROUTE; 299 nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; 300 nlm->rt.rtm_family = AF_INET; 301 nlm->rt.rtm_table = RT_TABLE_MAIN; 302 303 if (action < 0) 304 nlm->rt.rtm_scope = RT_SCOPE_NOWHERE; 305 else { 306 nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/; 307 nlm->rt.rtm_protocol = RTPROT_BOOT; 308 if (gateway->s_addr == INADDR_ANY) 309 nlm->rt.rtm_scope = RT_SCOPE_LINK; 310 else 311 nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE; 312 nlm->rt.rtm_type = RTN_UNICAST; 313 } 314 315 nlm->rt.rtm_dst_len = inet_ntocidr(*netmask); 316 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST, 317 &destination->s_addr, sizeof(destination->s_addr)); 318 add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY, 319 &gateway->s_addr, sizeof(gateway->s_addr)); 320 321 add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex); 322 add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric); 323 324 if (send_netlink(&nlm->hdr) == -1) 325 retval = -1; 326 free(nlm); 327 return retval; 328} 329