| /* $USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $ */ |
| /* |
| * Copyright (C) 2002 USAGI/WIDE Project. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the project nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| /* |
| * Author: |
| * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> |
| */ |
| |
| #if HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #if HAVE_SYS_TYPES_H |
| # include <sys/types.h> |
| #endif |
| |
| #if STDC_HEADERS |
| # include <stdio.h> |
| # include <stdlib.h> |
| # include <stddef.h> |
| #else |
| # if HAVE_STDLIB_H |
| # include <stdlib.h> |
| # endif |
| #endif |
| #if HAVE_STRING_H |
| # if !STDC_HEADERS && HAVE_MEMORY_H |
| # include <memory.h> |
| # endif |
| # include <string.h> |
| #endif |
| #if HAVE_STRINGS_H |
| # include <strings.h> |
| #endif |
| #if HAVE_INTTYPES_H |
| # include <inttypes.h> |
| #else |
| # if HAVE_STDINT_H |
| # include <stdint.h> |
| # endif |
| #endif |
| #if HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| |
| #if TIME_WITH_SYS_TIME |
| # include <sys/time.h> |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| |
| #if HAVE_SYS_UIO_H |
| #include <sys/uio.h> |
| #endif |
| |
| #include <sys/socket.h> |
| #if HAVE_LINUX_RTNETLINK_H |
| #include <asm/types.h> |
| #include <linux/rtnetlink.h> |
| #endif |
| |
| #if HAVE_NETINET_IN_H |
| # include <netinet/in.h> |
| #endif |
| |
| #if HAVE_NETINET_IP6_H |
| # include <netinet/ip6.h> |
| #endif |
| |
| #if HAVE_NETINET_ICMP6_H |
| # include <netinet/icmp6.h> |
| #endif |
| #ifndef HAVE_STRUCT_ICMP6_NODEINFO |
| # include "icmp6_nodeinfo.h" |
| #endif |
| |
| #if HAVE_NETDB_H |
| # include <netdb.h> |
| #endif |
| #include <errno.h> |
| |
| #if HAVE_SYSLOG_H |
| # include <syslog.h> |
| #endif |
| |
| #include "ninfod.h" |
| #include "ni_ifaddrs.h" |
| |
| #ifndef offsetof |
| # define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member) |
| #endif |
| |
| /* ---------- */ |
| /* ID */ |
| static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $"; |
| |
| /* ---------- */ |
| /* ipv6 address */ |
| void init_nodeinfo_ipv6addr(INIT_ARGS) |
| { |
| DEBUG(LOG_DEBUG, "%s()\n", __func__); |
| return; |
| } |
| |
| int filter_ipv6addr(const struct in6_addr *ifaddr, unsigned int flags) |
| { |
| if (IN6_IS_ADDR_UNSPECIFIED(ifaddr) || |
| IN6_IS_ADDR_LOOPBACK(ifaddr)) { |
| return 1; |
| } else if (IN6_IS_ADDR_V4COMPAT(ifaddr) || |
| IN6_IS_ADDR_V4MAPPED(ifaddr)) { |
| return !(flags & NI_NODEADDR_FLAG_COMPAT); |
| } else if (IN6_IS_ADDR_LINKLOCAL(ifaddr)) { |
| return !(flags & NI_NODEADDR_FLAG_LINKLOCAL); |
| } else if (IN6_IS_ADDR_SITELOCAL(ifaddr)) { |
| return !(flags & NI_NODEADDR_FLAG_SITELOCAL); |
| } |
| return !(flags & NI_NODEADDR_FLAG_GLOBAL); |
| } |
| |
| int pr_nodeinfo_ipv6addr(CHECKANDFILL_ARGS) |
| { |
| struct ni_ifaddrs *ifa0; |
| unsigned int ifindex = 0; |
| |
| DEBUG(LOG_DEBUG, "%s()\n", __func__); |
| |
| if (subject && subjlen != sizeof(struct in6_addr)) { |
| DEBUG(LOG_INFO, |
| "%s(): invalid subject length %zu for IPv6 Address Subject\n", |
| __func__, subjlen); |
| return 1; |
| } |
| if (ni_ifaddrs(&ifa0, AF_INET6)) |
| return -1; /* failed to get addresses */ |
| |
| /* pass 0: consider subject and determine subjected interface */ |
| if (subject) { |
| struct ni_ifaddrs *ifa; |
| |
| for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { |
| if (!ifa->ifa_addr) |
| continue; |
| if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) |
| continue; |
| if (!ifindex && |
| IN6_ARE_ADDR_EQUAL(&p->pktinfo.ipi6_addr, |
| (struct in6_addr *)subject)) { |
| /* |
| * if subject is equal to destination |
| * address, receiving interface is |
| * the candidate subject interface. |
| */ |
| ifindex = p->pktinfo.ipi6_ifindex; |
| } |
| if (!IN6_IS_ADDR_LOOPBACK((struct in6_addr *)subject) && |
| IN6_ARE_ADDR_EQUAL((struct in6_addr *)ifa->ifa_addr, |
| (struct in6_addr *)subject)) { |
| /* |
| * address is assigned on some interface. |
| * if multiple interfaces have the same interface, |
| * 1) prefer receiving interface |
| * 2) use first found one |
| */ |
| if (!ifindex || |
| (p->pktinfo.ipi6_ifindex == ifindex)) |
| ifindex = ifa->ifa_ifindex; |
| } |
| } |
| if (!ifindex) { |
| ni_freeifaddrs(ifa0); |
| return 1; /* subject not found */ |
| } |
| if (subj_if) |
| *subj_if = ifindex; |
| } else { |
| ifindex = subj_if ? *subj_if : 0; |
| if (ifindex == 0) |
| ifindex = p->pktinfo.ipi6_ifindex; |
| if (ifindex == 0) { |
| ni_freeifaddrs(ifa0); |
| return 1; /* XXX */ |
| } |
| } |
| |
| if (reply) { |
| struct ni_ifaddrs *ifa; |
| unsigned int addrs0 = 0, paddrs0 = 0; |
| unsigned int addrs, paddrs = 0, daddrs = 0; |
| |
| flags &= ~NI_NODEADDR_FLAG_TRUNCATE; |
| |
| /* pass 1: count addresses and preferred addresses to be returned */ |
| for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { |
| if (!ifa->ifa_addr) |
| continue; |
| if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) |
| continue; |
| if (!(flags & NI_NODEADDR_FLAG_ALL) && |
| ifa->ifa_ifindex != ifindex) |
| continue; |
| if (filter_ipv6addr((struct in6_addr *)ifa->ifa_addr, flags)) |
| continue; |
| |
| if (addrs0 + 1 >= ((MAX_REPLY_SIZE - sizeof(struct icmp6_nodeinfo)) / (sizeof(uint32_t) + sizeof(struct in6_addr)))) { |
| flags |= ~NI_NODEADDR_FLAG_TRUNCATE; |
| break; |
| } |
| |
| addrs0++; |
| if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) |
| paddrs0++; |
| } |
| |
| p->reply.ni_type = ICMP6_NI_REPLY; |
| p->reply.ni_code = ICMP6_NI_SUCCESS; |
| p->reply.ni_cksum = 0; |
| p->reply.ni_qtype = htons(NI_QTYPE_NODEADDR); |
| p->reply.ni_flags = flags&(NI_NODEADDR_FLAG_COMPAT| |
| NI_NODEADDR_FLAG_LINKLOCAL| |
| NI_NODEADDR_FLAG_SITELOCAL| |
| NI_NODEADDR_FLAG_GLOBAL); |
| |
| /* pass 2: store addresses */ |
| p->replydatalen = (sizeof(uint32_t)+sizeof(struct in6_addr)) * addrs0; |
| p->replydata = p->replydatalen ? ni_malloc(p->replydatalen) : NULL; |
| |
| if (p->replydatalen && !p->replydata) { |
| p->reply.ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; |
| addrs0 = paddrs0 = 0; |
| } |
| |
| for (ifa = ifa0, addrs = 0; |
| ifa && addrs < addrs0; |
| ifa = ifa->ifa_next) { |
| char *cp; |
| uint32_t ttl; |
| |
| if (!ifa->ifa_addr) |
| continue; |
| if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) |
| continue; |
| if (!(flags & NI_NODEADDR_FLAG_ALL) && |
| ((subj_if && *subj_if) ? (ifa->ifa_ifindex != *subj_if) : |
| (ifa->ifa_ifindex != p->pktinfo.ipi6_ifindex))) |
| continue; |
| if (filter_ipv6addr((struct in6_addr *)ifa->ifa_addr, flags)) |
| continue; |
| |
| #if ENABLE_TTL |
| if (ifa->ifa_cacheinfo) { |
| ttl = ifa->ifa_cacheinfo->ifa_valid > 0x7fffffff ? |
| htonl(0x7fffffff) : htonl(ifa->ifa_cacheinfo->ifa_valid); |
| } else { |
| ttl = (ifa->ifa_flags & IFA_F_PERMANENT) ? htonl(0x7fffffff) : 0; |
| } |
| #else |
| ttl = 0; |
| #endif |
| |
| cp = p->replydata + |
| (sizeof(uint32_t)+sizeof(struct in6_addr)) * (ifa->ifa_flags & IFA_F_DEPRECATED ? paddrs0+daddrs : paddrs); |
| memcpy(cp, &ttl, sizeof(ttl)); |
| memcpy(cp + sizeof(ttl), ifa->ifa_addr, sizeof(struct in6_addr)); |
| |
| addrs++; |
| if (ifa->ifa_flags & IFA_F_DEPRECATED) |
| daddrs++; |
| else |
| paddrs++; |
| } |
| } |
| |
| ni_freeifaddrs(ifa0); |
| return 0; |
| } |
| |
| /* ipv4 address */ |
| void init_nodeinfo_ipv4addr(INIT_ARGS) |
| { |
| DEBUG(LOG_DEBUG, "%s()\n", __func__); |
| return; |
| } |
| |
| int filter_ipv4addr(const struct in_addr *ifaddr, unsigned int flags) |
| { |
| return 0; |
| } |
| |
| int pr_nodeinfo_ipv4addr(CHECKANDFILL_ARGS) |
| { |
| struct ni_ifaddrs *ifa0; |
| unsigned int ifindex = 0; |
| |
| DEBUG(LOG_DEBUG, "%s()\n", __func__); |
| |
| if (subject && subjlen != sizeof(struct in_addr)) { |
| DEBUG(LOG_INFO, |
| "%s(): invalid subject length %zu for IPv4 Address Subject\n", |
| __func__, subjlen); |
| return 1; |
| } |
| if (ni_ifaddrs(&ifa0, AF_INET)) |
| return -1; /* failed to get addresses */ |
| |
| /* pass 0: consider subject and determine subjected interface */ |
| if (subject) { |
| struct ni_ifaddrs *ifa; |
| |
| for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { |
| if (!ifa->ifa_addr) |
| continue; |
| if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY)) |
| continue; |
| if ((((struct in_addr *)subject)->s_addr != htonl(INADDR_LOOPBACK)) && |
| memcmp((struct in_addr *)ifa->ifa_addr, |
| (struct in_addr *)subject, |
| sizeof(struct in_addr)) == 0) { |
| /* |
| * address is assigned on some interface. |
| * if multiple interfaces have the same interface, |
| * 1) prefer receiving interface |
| * 2) use first found one |
| */ |
| if (!ifindex || |
| (p->pktinfo.ipi6_ifindex == ifindex)) |
| ifindex = ifa->ifa_ifindex; |
| } |
| } |
| if (!ifindex) { |
| ni_freeifaddrs(ifa0); |
| return 1; /* subject not found */ |
| } |
| if (subj_if) |
| *subj_if = ifindex; |
| } else { |
| ifindex = subj_if ? *subj_if : 0; |
| if (ifindex == 0) |
| ifindex = p->pktinfo.ipi6_ifindex; |
| if (ifindex == 0) { |
| ni_freeifaddrs(ifa0); |
| return 1; /* XXX */ |
| } |
| } |
| |
| if (reply) { |
| struct ni_ifaddrs *ifa; |
| unsigned int addrs0 = 0, paddrs0 = 0; |
| unsigned int addrs, paddrs = 0, daddrs = 0; |
| |
| flags &= ~NI_IPV4ADDR_FLAG_TRUNCATE; |
| |
| /* pass 1: count addresses and preferred addresses to be returned */ |
| for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { |
| if (!ifa->ifa_addr) |
| continue; |
| #if 1 /* not used in kernel */ |
| if (ifa->ifa_flags & (IFA_F_TENTATIVE)) |
| continue; |
| #endif |
| if (!(flags & NI_NODEADDR_FLAG_ALL) && |
| ((subj_if && *subj_if) ? (ifa->ifa_ifindex != *subj_if) : |
| (ifa->ifa_ifindex != p->pktinfo.ipi6_ifindex))) |
| continue; |
| if (filter_ipv4addr((struct in_addr *)ifa->ifa_addr, flags)) |
| continue; |
| |
| if (addrs0 + 1 >= ((MAX_REPLY_SIZE - sizeof(struct icmp6_nodeinfo)) / (sizeof(uint32_t) + sizeof(struct in_addr)))) { |
| flags |= NI_IPV4ADDR_FLAG_TRUNCATE; |
| break; |
| } |
| |
| addrs0++; |
| if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) |
| paddrs0++; |
| } |
| |
| p->reply.ni_type = ICMP6_NI_REPLY; |
| p->reply.ni_code = ICMP6_NI_SUCCESS; |
| p->reply.ni_cksum = 0; |
| p->reply.ni_qtype = htons(NI_QTYPE_IPV4ADDR); |
| p->reply.ni_flags = flags & NI_IPV4ADDR_FLAG_ALL; |
| |
| /* pass 2: store addresses */ |
| p->replydatalen = (sizeof(uint32_t)+sizeof(struct in_addr)) * addrs0; |
| p->replydata = addrs0 ? ni_malloc(p->replydatalen) : NULL; |
| |
| if (p->replydatalen && !p->replydata) { |
| p->reply.ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; |
| addrs0 = paddrs0 = 0; |
| } |
| |
| for (ifa = ifa0, addrs = 0; |
| ifa && addrs < addrs0; |
| ifa = ifa->ifa_next) { |
| char *cp; |
| uint32_t ttl; |
| |
| if (!ifa->ifa_addr) |
| continue; |
| #if 1 /* not used in kernel */ |
| if (ifa->ifa_flags & (IFA_F_TENTATIVE)) |
| continue; |
| #endif |
| if (!(flags & NI_NODEADDR_FLAG_ALL) && |
| (ifa->ifa_ifindex != ifindex)) |
| continue; |
| if (filter_ipv4addr((struct in_addr *)ifa->ifa_addr, flags)) |
| continue; |
| |
| #if ENABLE_TTL |
| if (ifa->ifa_cacheinfo) { |
| ttl = ifa->ifa_cacheinfo->ifa_valid > 0x7fffffff ? |
| htonl(0x7fffffff) : htonl(ifa->ifa_cacheinfo->ifa_valid); |
| } else { |
| ttl = 0; /*XXX*/ |
| } |
| #else |
| ttl = 0; |
| #endif |
| |
| cp = (p->replydata + |
| (sizeof(uint32_t)+sizeof(struct in_addr)) * (ifa->ifa_flags & IFA_F_DEPRECATED ? paddrs0+daddrs : paddrs)); |
| memcpy(cp, &ttl, sizeof(ttl)); |
| memcpy(cp + sizeof(ttl), ifa->ifa_addr, sizeof(struct in_addr)); |
| |
| addrs++; |
| if (ifa->ifa_flags & IFA_F_DEPRECATED) |
| daddrs++; |
| else |
| paddrs++; |
| } |
| } |
| |
| ni_freeifaddrs(ifa0); |
| return 0; |
| } |
| |