dupl_addr.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 (c) 1995-1999 by Sun Microsystems, Inc.
* All rights reserved.
*/
/*
* Perform IPv6 duplicate address detection for a given interface
* and IPv6 address.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
#include "ifconfig.h"
/* XXX extract DupAddrDetectTransmits from LNKINFO? */
#define IPV6_MAX_HOPS 255
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1 } };
static int send_dad_probe(int s, char *phyname,
struct sockaddr_in6 *testaddr,
struct sockaddr_in6 *solicited_mc);
int ifindex);
struct sockaddr_in6 *from);
int code);
struct sockaddr_in6 *addr);
struct sockaddr_in6 *addr);
/*
* Performing duplicate address detection.
*
* Returns 0 if the address is ok, 1 if there is a duplicate,
* and -1 (with errno set) if there is some internal error.
* As a side effect this does a syslog and a stderr printf
* identifying any duplicate.
* Note that the state of the interface name is unchanged.
*/
int
{
int s;
char *cp;
int ifindex;
struct sockaddr_in6 solicited_mc;
/*
* Truncate name at ':'. Needed for SIOCGLIFLNKINFO
* Keep untruncated ifname for other use.
*/
*cp = '\0';
/*
* Get a socket to use to send and receive neighbor solicitations
* for DAD. Also used for ioctls below.
*/
Perror0("socket");
return (-1);
}
/*
* Determine interface index (for IPV6_BOUND_PIF) and
* save the flag values so they can be restored on return.
*/
Perror0("do_dad: SIOCGLIFINDEX");
goto done;
}
Perror0("do_dad: SIOCGLIFFLAGS");
goto done;
}
if (!(saved_flags & IFF_MULTICAST)) {
/* Not possible to do DAD. Pretend it is ok */
ret = 0;
goto done;
}
Perror0("do_dad: SIOCGLIFLNKINFO");
goto done;
}
}
/*
* 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.
*/
Perror0("do_dad: SIOCSLIFFLAGS");
goto restore;
}
/*
* IPV6_BOUND_PIF prevents load spreading to happen. 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) {
Perror0("IPV6_BOUND_PIF");
goto restore;
}
{
int hops = IPV6_MAX_HOPS;
int on = 1;
int off = 0;
if (debug > 1)
Perror0("IPV6_MULTICAST_HOPS");
goto restore;
}
Perror0("IPV6_UNSPEC_SRC");
goto restore;
}
Perror0("IPV6_MULTICAST_LOOP");
goto restore;
}
/* Enable receipt of ancillary data */
Perror0("IPV6_RECVHOPLIMIT");
goto restore;
}
Perror0("IPV6_RECVPKTINFO");
goto restore;
}
Perror0("IPV6_RECVRTHDR");
goto restore;
}
}
/*
* Extract the address and determine the solicited node multicast
* address to use.
*/
/* Join the solicited node multicast address and all-nodes. */
{
Perror0("IPV6_JOIN_GROUP");
goto restore;
}
Perror0("IPV6_JOIN_GROUP");
goto restore;
}
}
/* Restore flags */
Perror0("do_dad: SIOCSLIFFLAGS");
ret = -1;
goto done;
}
done:
(void) close(s);
return (ret);
}
/*
* Determine the solicited node multicast address for a given address.
*/
static void
{
struct in6_addr solicited_prefix = {
{ 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xFF, 0x0, 0x0, 0x0 } };
int i;
for (i = 13; i < 16; i++)
}
static int
{
int time_left; /* In milliseconds */
int i;
int ret;
if (debug)
/*
* 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 RetransTimer milliseconds for either a
* neighbor advertisement (sent to all-nodes) or a DAD neighbor
* solicitation for the testaddr.
* 3. Perform step 1 and 2 DupAddrDetectTransmits times.
*/
/* XXX perform a random delay: 0 - MAX_RTR_SOLICITATION_DELAY */
/* XXX use poll+recv logic for the random delay */
for (i = 0; i < DupAddrDetectTransmits; i++) {
return (-1);
/*
* Track time to make sure total wait is RetransTimer
* even though random packet will awake poll.
*/
/* CONSTCOND */
while (1) {
if (debug) {
(void) printf("run_dad: time_left %d ms\n",
}
if (time_left <= 0) {
if (debug)
(void) printf("run_dad: timeout\n");
break;
}
case -1:
Perror0("poll");
return (-1);
case 0:
/* Need loop will break */
break;
default:
/* Huh? */
return (-1);
case 1:
ifindex);
if (ret < 0)
return (-1);
if (ret > 0) {
ret);
return (1);
}
}
break;
}
}
}
return (0);
}
/*
* Send a DAD NS packet. Assumes an IPV6_UNSPEC_SRC and an IPV6_BOUND_IF
* have been done by the caller.
*/
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);
char abuf[INET6_ADDRSTRLEN];
if (cc < 0) {
Perror0("DAD, sendto");
return (-1);
}
return (-1);
}
if (debug)
return (0);
}
/*
* Return a pointer to the specified option buffer.
* If not found return NULL.
*/
static void *
{
}
}
return (NULL);
}
/*
* Receive an ICMP packet. If the packet signals a duplicate address for
* testaddr then return a positive non-zero number. Otherwise return zero.
* Internal errors cause a return of -1.
*/
static int
{
struct sockaddr_in6 from;
struct nd_neighbor_solicit *ns;
struct nd_neighbor_advert *na;
int len;
char abuf[INET6_ADDRSTRLEN];
int rcv_ifindex;
Perror0("DAD recvmsg");
return (-1);
}
if (len == 0)
return (0);
if (debug) {
}
/* Ignore packets > 64k or control buffers that don't fit */
if (debug) {
"Truncated message: msg_flags 0x%x from %s\n",
}
return (0);
}
if (len < ICMP6_MINLEN) {
if (debug) {
"Too short ICMP packet: %d bytes from %s\n",
}
return (0);
}
/* Unknown hoplimit - must drop */
if (debug) {
"Unknown hop limit from %s\n", abuf);
}
return (0);
}
/* Unknown destination address - must drop */
if (debug) {
"Unknown destination from %s\n", abuf);
}
return (0);
}
/* Can't allow routing headers in ND messages */
if (debug) {
"ND message with routing header from %s\n", abuf);
}
return (0);
}
switch (icmp->icmp6_type) {
case ND_NEIGHBOR_SOLICIT:
/*
* Assumes that the kernel has verified the AH (if present)
* and the ICMP checksum.
*/
if (hoplimit != IPV6_MAX_HOPS) {
if (debug) {
"NS hop limit: %d from %s\n",
}
return (0);
}
if (icmp->icmp6_code != 0) {
if (debug) {
}
return (0);
}
if (len < sizeof (struct nd_neighbor_solicit)) {
if (debug) {
"NS too short: %d bytes from %s\n",
}
return (0);
}
if (debug) {
char abuf2[INET6_ADDRSTRLEN];
(void *)&ns->nd_ns_target,
"NS with multicast target: %s from %s\n",
}
return (0);
}
if (len > sizeof (struct nd_neighbor_solicit)) {
return (0);
}
if (debug)
/* Sender is doing address resolution */
return (0);
}
if (rcv_ifindex != ifindex) {
if (debug) {
"Received Neighbor solicitation on"
" ifindex %d, expecting on %d\n",
}
return (0);
}
&ns->nd_ns_target)) {
if (debug) {
"NS - duplicate from %s\n",
abuf);
}
return (1);
}
return (0);
case ND_NEIGHBOR_ADVERT:
/*
* Assumes that the kernel has verified the AH (if present)
* and the ICMP checksum.
*/
if (hoplimit != IPV6_MAX_HOPS) {
if (debug) {
"NA hop limit: %d from %s\n",
}
return (0);
}
if (icmp->icmp6_code != 0) {
if (debug) {
}
return (0);
}
if (len < sizeof (struct nd_neighbor_advert)) {
if (debug) {
"NA too short: %d bytes from %s\n",
}
return (0);
}
if (debug) {
char abuf2[INET6_ADDRSTRLEN];
(void *)&na->nd_na_target,
"NA with multicast target: %s from %s\n",
}
return (0);
}
if (IN6_IS_ADDR_MULTICAST(&dst) &&
if (debug) {
char abuf2[INET6_ADDRSTRLEN];
(void *)&na->nd_na_target,
"NA solicited w/ mc target: %s from %s\n",
}
return (0);
}
if (len > sizeof (struct nd_neighbor_advert)) {
return (0);
}
if (debug)
&na->nd_na_target)) {
if (debug) {
"NA - duplicate from %s\n",
abuf);
}
return (1);
}
return (0);
default:
return (0);
}
}
/*
* Verify that all options have a non-zero length and that
* the options fit within the total length of the packet (optlen).
*/
static boolean_t
{
while (optlen > 0) {
if (opt->nd_opt_len == 0) {
if (debug) {
char abuf[INET6_ADDRSTRLEN];
"Zero length option type 0x%x from %s\n",
}
return (_B_FALSE);
}
if (optlen < 0) {
if (debug) {
char abuf[INET6_ADDRSTRLEN];
"Too large option: type 0x%x len %u "
"from %s\n",
abuf);
}
return (_B_FALSE);
}
}
return (_B_TRUE);
}
static void
{
char abuf[INET6_ADDRSTRLEN];
"ifconfig: "
"Duplicate address detected on link %s for address %s. Code %d\n",
"Duplicate address detected on link %s for address %s. Code %d\n",
closelog();
}
/* Printing functions */
static void
{
struct nd_opt_hdr *opt;
char abuf[INET6_ADDRSTRLEN];
(void) printf("\ttarget %s\n",
}
static void
{
struct nd_opt_hdr *opt;
char abuf[INET6_ADDRSTRLEN];
(void) printf("\ttarget %s\n",
(void) printf("\tRouter: %s\n",
"Set" : "Not set");
(void) printf("\tSolicited: %s\n",
"Set" : "Not set");
(void) printf("\tOverride: %s\n",
"Set" : "Not set");
}
static void
{
struct nd_opt_prefix_info *po;
struct nd_opt_mtu *mo;
struct nd_opt_lla *lo;
int optlen;
char abuf[INET6_ADDRSTRLEN];
while (len >= sizeof (struct nd_opt_hdr)) {
if (optlen == 0) {
if (debug)
(void) printf("Zero length option!\n");
break;
}
switch (opt->nd_opt_type) {
break;
(void) printf("\tOn link flag:%s\n",
"Set" : "Not set");
(void) printf("\tAuto addrconf flag:%s\n",
"Set" : "Not set");
(void) printf("\tValid time: %u\n",
(void) printf("\tPreferred time: %u\n",
(void) printf("\tPrefix: %s/%u\n",
break;
case ND_OPT_MTU:
break;
(void) printf("\tMTU: %d\n",
break;
case ND_OPT_SOURCE_LINKADDR:
if (optlen < 8 ||
break;
(void) printf("\tSource LLA: len %d <%s>\n",
break;
case ND_OPT_TARGET_LINKADDR:
if (optlen < 8||
break;
(void) printf("\tTarget LLA: len %d <%s>\n",
break;
case ND_OPT_REDIRECTED_HEADER:
(void) printf("\tRedirected header option!\n");
break;
default:
(void) printf("Unkown option %d (0x%x)\n",
break;
}
}
}
static char *
{
int i;
for (i = 0; i < llalen; i++) {
if (i == llalen - 1)
else
}
return (llabuf);
}