text: /* $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;
  }
  
  