1 /* $USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ */ 2 /* 3 * Copyright (C) 2002 USAGI/WIDE Project. 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 * 3. Neither the name of the project nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* reformatted by indent -kr -i8 -l 1000 */ 32 /* USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp */ 33 34 /************************************************************************** 35 * ifaddrs.c 36 * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. Neither the name of the author nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63 #include "config.h" 64 65 #include <string.h> 66 #include <time.h> 67 #include <malloc.h> 68 #include <errno.h> 69 #include <unistd.h> 70 71 #include <sys/socket.h> 72 #include <asm/types.h> 73 #include <linux/netlink.h> 74 #include <linux/rtnetlink.h> 75 #include <sys/types.h> 76 #include <sys/socket.h> 77 #include <netpacket/packet.h> 78 #include <net/ethernet.h> /* the L2 protocols */ 79 #include <sys/uio.h> 80 #include <net/if.h> 81 #include <net/if_arp.h> 82 #include "ni_ifaddrs.h" 83 #include <netinet/in.h> 84 85 #ifdef _USAGI_LIBINET6 86 #include "libc-compat.h" 87 #endif 88 89 //#define IFA_LOCAL IFA_LOCAL 90 91 static const char *RCSID __attribute__ ((unused)) = "$USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ based on USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp"; 92 93 /* ====================================================================== */ 94 struct nlmsg_list { 95 struct nlmsg_list *nlm_next; 96 struct nlmsghdr *nlh; 97 int size; 98 time_t seq; 99 }; 100 101 #ifndef IFA_LOCAL 102 struct rtmaddr_ifamap { 103 void *address; 104 void *local; 105 void *broadcast; 106 int address_len; 107 int local_len; 108 int broadcast_len; 109 }; 110 #endif 111 112 /* ====================================================================== */ 113 static int nl_sendreq(int sd, int request, int flags, int *seq) 114 { 115 char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))]; 116 struct sockaddr_nl nladdr; 117 struct nlmsghdr *req_hdr; 118 struct rtgenmsg *req_msg; 119 time_t t = time(NULL); 120 121 if (seq) 122 *seq = t; 123 memset(&reqbuf, 0, sizeof(reqbuf)); 124 req_hdr = (struct nlmsghdr *) reqbuf; 125 req_msg = (struct rtgenmsg *) NLMSG_DATA(req_hdr); 126 req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg)); 127 req_hdr->nlmsg_type = request; 128 req_hdr->nlmsg_flags = flags | NLM_F_REQUEST; 129 req_hdr->nlmsg_pid = 0; 130 req_hdr->nlmsg_seq = t; 131 req_msg->rtgen_family = AF_UNSPEC; 132 memset(&nladdr, 0, sizeof(nladdr)); 133 nladdr.nl_family = AF_NETLINK; 134 return (sendto(sd, (void *) req_hdr, req_hdr->nlmsg_len, 0, (struct sockaddr *) &nladdr, sizeof(nladdr))); 135 } 136 137 static int nl_recvmsg(int sd, int request, int seq, void *buf, size_t buflen, int *flags) 138 { 139 struct msghdr msg; 140 struct iovec iov = { buf, buflen }; 141 struct sockaddr_nl nladdr; 142 int read_len; 143 144 for (;;) { 145 msg.msg_name = (void *) &nladdr; 146 msg.msg_namelen = sizeof(nladdr); 147 msg.msg_iov = &iov; 148 msg.msg_iovlen = 1; 149 msg.msg_control = NULL; 150 msg.msg_controllen = 0; 151 msg.msg_flags = 0; 152 read_len = recvmsg(sd, &msg, 0); 153 if ((read_len < 0 && errno == EINTR) 154 || (msg.msg_flags & MSG_TRUNC)) 155 continue; 156 if (flags) 157 *flags = msg.msg_flags; 158 break; 159 } 160 return read_len; 161 } 162 163 static int nl_getmsg(int sd, int request, int seq, struct nlmsghdr **nlhp, int *done) 164 { 165 struct nlmsghdr *nh; 166 size_t bufsize = 65536, lastbufsize = 0; 167 void *buff = NULL; 168 int result = 0, read_size; 169 int msg_flags; 170 pid_t pid = getpid(); 171 for (;;) { 172 void *newbuff = realloc(buff, bufsize); 173 if (newbuff == NULL || bufsize < lastbufsize) { 174 free(newbuff); 175 result = -1; 176 break; 177 } 178 buff = newbuff; 179 result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags); 180 if (read_size < 0 || (msg_flags & MSG_TRUNC)) { 181 lastbufsize = bufsize; 182 bufsize *= 2; 183 continue; 184 } 185 if (read_size == 0) 186 break; 187 nh = (struct nlmsghdr *) buff; 188 for (nh = (struct nlmsghdr *) buff; NLMSG_OK(nh, read_size); nh = (struct nlmsghdr *) NLMSG_NEXT(nh, read_size)) { 189 if (nh->nlmsg_pid != pid || nh->nlmsg_seq != seq) 190 continue; 191 if (nh->nlmsg_type == NLMSG_DONE) { 192 (*done)++; 193 break; /* ok */ 194 } 195 if (nh->nlmsg_type == NLMSG_ERROR) { 196 struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA(nh); 197 result = -1; 198 if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) 199 errno = EIO; 200 else 201 errno = -nlerr->error; 202 break; 203 } 204 } 205 break; 206 } 207 if (result < 0) 208 if (buff) { 209 int saved_errno = errno; 210 free(buff); 211 buff = NULL; 212 errno = saved_errno; 213 } 214 *nlhp = (struct nlmsghdr *) buff; 215 return result; 216 } 217 218 static int nl_getlist(int sd, int seq, int request, struct nlmsg_list **nlm_list, struct nlmsg_list **nlm_end) 219 { 220 struct nlmsghdr *nlh = NULL; 221 int status; 222 int done = 0; 223 224 status = nl_sendreq(sd, request, NLM_F_ROOT | NLM_F_MATCH, &seq); 225 if (status < 0) 226 return status; 227 if (seq == 0) 228 seq = (int) time(NULL); 229 while (!done) { 230 status = nl_getmsg(sd, request, seq, &nlh, &done); 231 if (status < 0) 232 return status; 233 if (nlh) { 234 struct nlmsg_list *nlm_next = (struct nlmsg_list *) malloc(sizeof(struct nlmsg_list)); 235 if (nlm_next == NULL) { 236 int saved_errno = errno; 237 free(nlh); 238 errno = saved_errno; 239 status = -1; 240 } else { 241 nlm_next->nlm_next = NULL; 242 nlm_next->nlh = (struct nlmsghdr *) nlh; 243 nlm_next->size = status; 244 nlm_next->seq = seq; 245 if (*nlm_list == NULL) { 246 *nlm_list = nlm_next; 247 *nlm_end = nlm_next; 248 } else { 249 (*nlm_end)->nlm_next = nlm_next; 250 *nlm_end = nlm_next; 251 } 252 } 253 } 254 } 255 return status >= 0 ? seq : status; 256 } 257 258 /* ---------------------------------------------------------------------- */ 259 static void free_nlmsglist(struct nlmsg_list *nlm0) 260 { 261 struct nlmsg_list *nlm, *nlm_next; 262 int saved_errno; 263 if (!nlm0) 264 return; 265 saved_errno = errno; 266 nlm = nlm0; 267 while(nlm) { 268 if(nlm->nlh) 269 free(nlm->nlh); 270 nlm_next = nlm->nlm_next; 271 free(nlm); 272 nlm = nlm_next; 273 } 274 errno = saved_errno; 275 } 276 277 static void free_data(void *data) 278 { 279 int saved_errno = errno; 280 if (data != NULL) 281 free(data); 282 errno = saved_errno; 283 } 284 285 /* ---------------------------------------------------------------------- */ 286 static void nl_close(int sd) 287 { 288 int saved_errno = errno; 289 if (sd >= 0) 290 close(sd); 291 errno = saved_errno; 292 } 293 294 /* ---------------------------------------------------------------------- */ 295 static int nl_open(void) 296 { 297 struct sockaddr_nl nladdr; 298 int sd; 299 300 sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 301 if (sd < 0) 302 return -1; 303 memset(&nladdr, 0, sizeof(nladdr)); 304 nladdr.nl_family = AF_NETLINK; 305 if (bind(sd, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { 306 nl_close(sd); 307 return -1; 308 } 309 return sd; 310 } 311 312 /* ====================================================================== */ 313 int ni_ifaddrs(struct ni_ifaddrs **ifap, sa_family_t family) 314 { 315 int sd; 316 struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; 317 /* - - - - - - - - - - - - - - - */ 318 int icnt; 319 size_t dlen, xlen; 320 uint32_t max_ifindex = 0; 321 322 pid_t pid = getpid(); 323 int seq = 0; 324 int result; 325 int build; /* 0 or 1 */ 326 327 /* ---------------------------------- */ 328 /* initialize */ 329 icnt = dlen = xlen = 0; 330 nlmsg_list = nlmsg_end = NULL; 331 332 if (ifap) 333 *ifap = NULL; 334 335 /* ---------------------------------- */ 336 /* open socket and bind */ 337 sd = nl_open(); 338 if (sd < 0) 339 return -1; 340 341 /* ---------------------------------- */ 342 /* gather info */ 343 if ((seq = nl_getlist(sd, seq + 1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0) { 344 free_nlmsglist(nlmsg_list); 345 nl_close(sd); 346 return -1; 347 } 348 349 /* ---------------------------------- */ 350 /* Estimate size of result buffer and fill it */ 351 for (build = 0; build <= 1; build++) { 352 struct ni_ifaddrs *ifl = NULL, *ifa = NULL; 353 struct nlmsghdr *nlh, *nlh0; 354 void *data = NULL, *xdata = NULL; 355 uint16_t *ifflist = NULL; 356 #ifndef IFA_LOCAL 357 struct rtmaddr_ifamap ifamap; 358 #endif 359 360 if (build) { 361 ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ni_ifaddrs[icnt])) 362 + dlen + xlen); 363 if (ifap != NULL) 364 *ifap = ifa; 365 else { 366 free_data(data); 367 result = 0; 368 break; 369 } 370 if (data == NULL) { 371 free_data(data); 372 result = -1; 373 break; 374 } 375 ifl = NULL; 376 data += NLMSG_ALIGN(sizeof(struct ni_ifaddrs)) * icnt; 377 xdata = data + dlen; 378 ifflist = xdata + xlen; 379 } 380 381 for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) { 382 int nlmlen = nlm->size; 383 if (!(nlh0 = nlm->nlh)) 384 continue; 385 for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh = NLMSG_NEXT(nlh, nlmlen)) { 386 struct ifaddrmsg *ifam = NULL; 387 struct rtattr *rta; 388 389 size_t nlm_struct_size = 0; 390 sa_family_t nlm_family = 0; 391 uint32_t nlm_scope = 0, nlm_index = 0; 392 unsigned int nlm_flags; 393 size_t rtasize; 394 395 #ifndef IFA_LOCAL 396 memset(&ifamap, 0, sizeof(ifamap)); 397 #endif 398 399 /* check if the message is what we want */ 400 if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq) 401 continue; 402 if (nlh->nlmsg_type == NLMSG_DONE) { 403 break; /* ok */ 404 } 405 switch (nlh->nlmsg_type) { 406 case RTM_NEWADDR: 407 ifam = (struct ifaddrmsg *) NLMSG_DATA(nlh); 408 nlm_struct_size = sizeof(*ifam); 409 nlm_family = ifam->ifa_family; 410 nlm_scope = ifam->ifa_scope; 411 nlm_index = ifam->ifa_index; 412 nlm_flags = ifam->ifa_flags; 413 if (family && nlm_family != family) 414 continue; 415 if (build) { 416 ifa->ifa_ifindex = nlm_index; 417 ifa->ifa_flags = nlm_flags; 418 } 419 break; 420 default: 421 continue; 422 } 423 424 if (!build) { 425 if (max_ifindex < nlm_index) 426 max_ifindex = nlm_index; 427 } else { 428 if (ifl != NULL) 429 ifl->ifa_next = ifa; 430 } 431 432 rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size); 433 for (rta = (struct rtattr *) (((char *) NLMSG_DATA(nlh)) + 434 NLMSG_ALIGN(nlm_struct_size)); 435 RTA_OK(rta, rtasize); 436 rta = RTA_NEXT(rta, rtasize)) { 437 void *rtadata = RTA_DATA(rta); 438 size_t rtapayload = RTA_PAYLOAD(rta); 439 440 switch (nlh->nlmsg_type) { 441 case RTM_NEWADDR: 442 if (nlm_family == AF_PACKET) 443 break; 444 switch (rta->rta_type) { 445 #ifndef IFA_LOCAL 446 case IFA_ADDRESS: 447 ifamap.address = rtadata; 448 ifamap.address_len = rtapayload; 449 break; 450 case IFA_LOCAL: 451 ifamap.local = rtadata; 452 ifamap.local_len = rtapayload; 453 break; 454 case IFA_BROADCAST: 455 ifamap.broadcast = rtadata; 456 ifamap.broadcast_len = rtapayload; 457 break; 458 case IFA_LABEL: 459 break; 460 case IFA_UNSPEC: 461 break; 462 #else 463 case IFA_LOCAL: 464 if (!build) 465 dlen += NLMSG_ALIGN(rtapayload); 466 else { 467 memcpy(data, rtadata, rtapayload); 468 ifa->ifa_addr = data; 469 data += NLMSG_ALIGN(rtapayload); 470 } 471 break; 472 #endif 473 case IFA_CACHEINFO: 474 if (!build) 475 xlen += NLMSG_ALIGN(rtapayload); 476 else { 477 memcpy(xdata, rtadata, rtapayload); 478 ifa->ifa_cacheinfo = xdata; 479 xdata += NLMSG_ALIGN(rtapayload); 480 } 481 break; 482 } 483 } 484 } 485 #ifndef IFA_LOCAL 486 if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) { 487 if (!ifamap.local) { 488 ifamap.local = ifamap.address; 489 ifamap.local_len = ifamap.address_len; 490 } 491 if (!ifamap.address) { 492 ifamap.address = ifamap.local; 493 ifamap.address_len = ifamap.local_len; 494 } 495 if (ifamap.address_len != ifamap.local_len || 496 (ifamap.address != NULL && 497 memcmp(ifamap.address, ifamap.local, ifamap.address_len))) { 498 /* p2p; address is peer and local is ours */ 499 ifamap.broadcast = ifamap.address; 500 ifamap.broadcast_len = ifamap.address_len; 501 ifamap.address = ifamap.local; 502 ifamap.address_len = ifamap.local_len; 503 } 504 if (ifamap.address) { 505 if (!build) 506 dlen += NLMSG_ALIGN(ifamap.address_len); 507 else { 508 ifa->ifa_addr = (struct sockaddr *) data; 509 memcpy(ifa->ifa_addr, ifamap.address, ifamap.address_len); 510 data += NLMSG_ALIGN(ifamap.address_len); 511 } 512 } 513 } 514 #endif 515 if (!build) { 516 icnt++; 517 } else { 518 ifl = ifa++; 519 } 520 } 521 } 522 if (!build) { 523 if (icnt == 0 && (dlen + xlen == 0)) { 524 if (ifap != NULL) 525 *ifap = NULL; 526 break; /* cannot found any addresses */ 527 } 528 } 529 } 530 531 /* ---------------------------------- */ 532 /* Finalize */ 533 free_nlmsglist(nlmsg_list); 534 nl_close(sd); 535 return 0; 536 } 537 538 /* ---------------------------------------------------------------------- */ 539 void ni_freeifaddrs(struct ni_ifaddrs *ifa) 540 { 541 free(ifa); 542 } 543 544