You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
470 lines
12 KiB
470 lines
12 KiB
/* $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;
|
|
}
|
|
|