inetcfg_dad.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This module uses the ancillary data feature that is made available
* though the UNIX 98 standards version of the Socket interface. This
* interface is normally accessed via libxnet. However, to use libxnet,
* this library would have to be compiled with _XOPEN_SOURCE=500 and
* __EXTENSIONS__. Unfortunately, this makes linting both the library
* and its consumers impractical. Therefore, this module is itself compiled
* for use with the UNIX 98 version of the Socket interface and the
* xnet versions of the Socket interfaces are called directly.
* Hopefully, our Socket implementation will one day support the ancillary
* data feature directly and this hack will no longer be needed. In the
* meantime, changes to this file should be made with the knowledge that the
* data types used by this module may differ in defintion fron the same data
* types in the other modules.
*/
#define _XOPEN_SOURCE 500
#define __EXTENSIONS__ 1
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stropts.h>
#include <inetcfg.h>
#define IPV6_MAX_HOPS 255
static int dup_addr_detect_transmits = 1;
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1 } };
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1,
0xff, 0x0, 0x0, 0x0 } };
/*
* Verifies that all options have a non-zero length and that
* the options fit within the total length of the packet (optlen).
*
* Returns: _B_TRUE if valid, _B_FALSE otherwise.
*/
static boolean_t
{
while (optlen > 0) {
if ((opt->nd_opt_len == 0)) {
return (_B_FALSE);
}
if (optlen < 0) {
return (_B_FALSE);
}
}
return (_B_TRUE);
}
/*
* Returns a pointer to the specified option buffer.
*
* Returns: A pointer to the option buffer or NULL if not found.
*/
static void *
{
}
}
return (NULL);
}
/*
* Receives an ICMP packet and tests it to see if it indicates that
* testaddr is a duplicate address. This routine returns ICFG_SUCCESS
* if no duplicate address is detected. If an unexpected error is
* encountered receiving the packet, then ICFG_FAILURE is returned.
* And of course ICFG_DAD_FOUND is returned if a duplicate address
* is detected.
*
* Returns: ICFG_SUCCESS, ICFG_FAILURE or ICFG_DAD_FOUND.
*/
static int
{
struct sockaddr_in6 from;
struct nd_neighbor_solicit *ns;
struct nd_neighbor_advert *na;
void *opt;
int rcv_ifindex;
/* Error was encountered - return failure */
return (ICFG_FAILURE);
}
if (len == 0) {
/* Ignore zero length messages */
return (ICFG_SUCCESS);
}
/* Ignore packets > 64k or control buffers that don't fit */
return (ICFG_SUCCESS);
}
if (len < ICMP6_MINLEN) {
/* Ignore packet if it is too small to be icmp */
return (ICFG_SUCCESS);
}
/* Unknown hoplimit - must drop */
return (ICFG_SUCCESS);
}
/* Unknown destination address - must drop */
return (ICFG_SUCCESS);
}
/* Can't allow routing headers in ND messages */
return (ICFG_SUCCESS);
}
/*
* We're only interested in neighbor solicitations (someone
* else soliciting for the same address) and advertisements.
* We must verify each. In either case, we assume that the
* kernel verified the AH (if present) and the ICMP checksum.
*/
switch (icmp->icmp6_type) {
case ND_NEIGHBOR_SOLICIT:
if (hoplimit != IPV6_MAX_HOPS) {
/* Packet came from different subnet */
return (ICFG_SUCCESS);
}
if (icmp->icmp6_code != 0) {
/* There are no codes for neighbor solicitations */
return (ICFG_SUCCESS);
}
if (len < sizeof (struct nd_neighbor_solicit)) {
/* Packet is too small */
return (ICFG_SUCCESS);
}
/* NS target was multicast */
return (ICFG_SUCCESS);
}
if (len > sizeof (struct nd_neighbor_solicit)) {
/*
* A neighbor solicitation packet has the form
* of a header directly followed by options.
*/
len - sizeof (struct nd_neighbor_solicit))) {
/* Invalid options */
return (ICFG_SUCCESS);
}
}
/* Sender is doing address resolution */
return (ICFG_SUCCESS);
}
if (rcv_ifindex != ifindex) {
/* Packet not received on test interface */
return (ICFG_SUCCESS);
}
&ns->nd_ns_target)) {
/* NS wasn't for test address */
return (ICFG_SUCCESS);
}
return (ICFG_DAD_FOUND);
case ND_NEIGHBOR_ADVERT:
if (hoplimit != IPV6_MAX_HOPS) {
/* Packet came from different subnet */
return (ICFG_SUCCESS);
}
if (icmp->icmp6_code != 0) {
/* There are no codes for neighbor advertisements */
return (ICFG_SUCCESS);
}
if (len < sizeof (struct nd_neighbor_advert)) {
/* Packet is too small */
return (ICFG_SUCCESS);
}
/* NA target was multicast */
return (ICFG_SUCCESS);
}
if (IN6_IS_ADDR_MULTICAST(&dst) &&
/* Dest was multicast and solicited flag not zero */
return (ICFG_SUCCESS);
}
if (len > sizeof (struct nd_neighbor_advert)) {
/*
* A neighbor advertisement packet has the form
* of a header directly followed by options.
*/
len - sizeof (struct nd_neighbor_advert))) {
return (ICFG_SUCCESS);
}
}
&na->nd_na_target)) {
/* NA wasn't for test address */
return (ICFG_SUCCESS);
}
return (ICFG_DAD_FOUND);
default:
return (ICFG_SUCCESS);
}
}
/*
* Sends a DAD neighbor solicitation packet. Assumes the socket has been
* configured correctly (i.e., an IPV6_UNSPEC_SRC and an IPV6_BOUND_IF have
* been done by the caller, etc.).
*
* Returns: ICFG_SUCCESS or ICFG_FAILURE.
*/
static int
struct sockaddr_in6 *solicited_mc)
{
int packetlen = 0;
int cc;
ns->nd_ns_code = 0;
ns->nd_ns_cksum = 0;
ns->nd_ns_reserved = 0;
packetlen = sizeof (struct nd_neighbor_solicit);
return (ICFG_FAILURE);
}
return (ICFG_SUCCESS);
}
/*
* Build a solicited node multicast address for a given address.
*/
static void
{
int i;
for (i = 13; i < 16; i++) {
}
}
/*
* Loops sending DAD probes and polling for responses.
*
* Returns: ICFG_SUCCESS, ICFG_FAILURE, ICFG_DAD_FOUND or ICFG_DAD_FAILED.
*/
static int
{
int time_left; /* In milliseconds */
int i;
int ret;
/*
* Perform duplicate address detection sequence
* 1. Send a neighbor solicitation with an unspecified source
* address to the solicited node MC address with the testaddr
* being the target.
* 2. Wait for up to retrans_timer milliseconds for either a
* neighbor advertisement (sent to all-nodes) or a DAD neighbor
* solicitation for the testaddr.
* 3. Perform step 1 and 2 dup_addr_detect_transmits times.
*/
for (i = 0; i < dup_addr_detect_transmits; i++) {
if (ret != ICFG_SUCCESS) {
return (ret);
}
/*
* Track time to make sure total wait is retrans_timer
* even though random packet will awake poll.
*/
/* CONSTCOND */
while (1) {
if (time_left <= 0) {
break;
}
case -1:
return (ICFG_FAILURE);
case 0:
/* Need loop will break */
break;
case 1:
ifindex);
if (ret != ICFG_SUCCESS) {
return (ret);
}
}
break;
default:
return (ICFG_DAD_FAILED);
}
}
}
return (ICFG_SUCCESS);
}
/*
* Configures a socket for DAD.
*
* Returns: ICFG_SUCCESS or ICFG_FAILURE.
*/
static int
{
int hops = IPV6_MAX_HOPS;
int on = 1;
int off = 0;
/*
* IPV6_BOUND_PIF prevents load spreading from happening. If we
* just do IPV6_BOUND_IF, the packet can go out on a different
* interface other than "ifindex", if interface is part of
* a group. In that case, we will get back the copy of NS that
* we sent and think it is a duplicate(Switch loops back the
* copy on all interfaces other than the one we sent the packet on).
*/
sizeof (ifindex)) < 0) {
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
/*
* Enable receipt of ancillary data
*/
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
/*
* Join the solicited node multicast address and all-nodes.
*/
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
return (ICFG_SUCCESS);
}
/*
* Performs duplicate address detection.
*
* Returns: ICFG_SUCCESS, ICFG_FAILURE, ICFG_DAD_FOUND or ICFG_DAD_FAILED.
*
* Note: the state of the interface name is unchanged.
*/
int
{
struct sockaddr_in6 solicited_mc;
int retrans_timer = ND_RETRANS_TIMER;
int ifindex;
int sock;
int syserr = 0;
int restore_ret;
int ret;
/*
* Check the address assigned to the interface.
* Skip the check if IFF_NOLOCAL, IFF_NONUD, IFF_ANYCAST, or
* IFF_LOOPBACK. Note that IFF_NONUD turns of both NUD and DAD.
* DAD is not possible if not IFF_MULTICAST.
*/
!(flags & IFF_MULTICAST)) {
return (ICFG_SUCCESS);
}
/*
* If the address is all zeroes, then just return success.
*/
return (ICFG_SUCCESS);
}
/*
* Determine interface index (for IPV6_BOUND_PIF) and
* save the flag values so they can be restored on return.
*/
return (ret);
}
return (ret);
}
if (linkinfo.lir_reachretrans != 0) {
}
/*
* Set NOLOCAL and UP flags.
* This prevents the use of the interface except when the user binds
* to unspecified IPv6 address, and sends to a link local multicast
* address.
*/
if (ret != ICFG_SUCCESS) {
return (ret);
}
/*
* Extract the address and determine the solicited node multicast
* address to use.
*/
/*
* Get a socket to use to send and receive neighbor solicitations
* for DAD. Also used for ioctls below.
*/
ret = ICFG_FAILURE;
goto restore;
}
if (ret != ICFG_SUCCESS) {
goto restore;
}
if (ret == ICFG_FAILURE) {
}
/* Restore flags */
if (ret == ICFG_SUCCESS) {
ret = restore_ret;
}
}
if (ret == ICFG_FAILURE) {
}
return (ret);
}