dupl_addr.c revision 69bb4bb45c98da60d21839c4dc3c01ea1be60585
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Perform IPv6 duplicate address detection for a given interface
* and IPv6 address.
*
* Only the modifications necessary to integrate into the message
* scheme of in.ndpd have been made. This is intended to be a
* temporary fix to allow Duplicate Address Detection to be performed
* by in.ndpd for temporary (rfc 3041) addresses; the long-term
* solution will be to use libinetcfg.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
int DupAddrDetectTransmits = 1;
#define IPV6_MAX_HOPS 255
extern struct in6_addr all_nodes_mcast;
static int send_dad_probe(int s, char *phyname,
struct sockaddr_in6 *testaddr,
struct sockaddr_in6 *solicited_mc);
int ifindex);
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 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.
*/
logperror("do_dad: socket");
return (-1);
}
/*
* Determine interface index (for IPV6_BOUND_PIF) and
* save the flag values so they can be restored on return.
*/
logperror("do_dad: SIOCGLIFINDEX");
goto done;
}
logperror("do_dad: SIOCGLIFFLAGS");
goto done;
}
if (!(saved_flags & IFF_MULTICAST)) {
/* Not possible to do DAD. Pretend it is ok */
ret = 0;
goto done;
}
logperror("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.
*/
logperror("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) {
logperror("do_dad: IPV6_BOUND_PIF");
goto restore;
}
{
int hops = IPV6_MAX_HOPS;
int on = 1;
int off = 0;
logperror("do_dad: IPV6_MULTICAST_HOPS");
goto restore;
}
logperror("do_dad: IPV6_UNSPEC_SRC");
goto restore;
}
logperror("do_dad: IPV6_MULTICAST_LOOP");
goto restore;
}
/* Enable receipt of ancillary data */
logperror("do_dad: IPV6_RECVHOPLIMIT");
goto restore;
}
logperror("do_dad: IPV6_RECVPKTINFO");
goto restore;
}
logperror("do_dad: 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. */
{
logperror("do_dad: IPV6_JOIN_GROUP");
goto restore;
}
logperror("do_dad: IPV6_JOIN_GROUP");
goto restore;
}
}
/* Restore flags */
logperror("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;
/*
* 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 (time_left <= 0) {
break;
}
case -1:
logperror("run_dad: 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) {
logperror("DAD sendto");
return (-1);
}
sizeof (abuf));
}
return (-1);
}
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;
logperror("DAD recvmsg");
return (-1);
}
if (len == 0)
return (0);
}
/* Ignore packets > 64k or control buffers that don't fit */
}
return (0);
}
if (len < ICMP6_MINLEN) {
}
return (0);
}
/* Unknown hoplimit - must drop */
}
return (0);
}
/* Unknown destination address - must drop */
abuf);
}
return (0);
}
/* Can't allow routing headers in ND messages */
"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) {
}
return (0);
}
if (icmp->icmp6_code != 0) {
}
return (0);
}
if (len < sizeof (struct nd_neighbor_solicit)) {
}
return (0);
}
char abuf2[INET6_ADDRSTRLEN];
(void *)&ns->nd_ns_target,
}
return (0);
}
if (len > sizeof (struct nd_neighbor_solicit)) {
/*
* For DAD neighbor solicitation type message,
* we need to further verify if SLLA option is present
* in received options,
* so we pass TRUE to reject_dad_slla argument.
*/
len - sizeof (struct nd_neighbor_solicit),
return (0);
}
/* Sender is doing address resolution */
return (0);
}
if (rcv_ifindex != ifindex) {
"solicitation on ifindex %d, "
}
return (0);
}
&ns->nd_ns_target)) {
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) {
}
return (0);
}
if (icmp->icmp6_code != 0) {
}
return (0);
}
if (len < sizeof (struct nd_neighbor_advert)) {
}
return (0);
}
char abuf2[INET6_ADDRSTRLEN];
(void *)&na->nd_na_target,
}
return (0);
}
if (IN6_IS_ADDR_MULTICAST(&dst) &&
char abuf2[INET6_ADDRSTRLEN];
(void *)&na->nd_na_target,
}
return (0);
}
if (len > sizeof (struct nd_neighbor_advert)) {
/*
* Since this is a Neighbor advertisement
* we unset the reject_dad_slla flag, thus
* there is no need to verify the SLLA options.
*/
len - sizeof (struct nd_neighbor_advert),
return (0);
}
&na->nd_na_target)) {
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).
* If reject_dad_slla is set, we also verify that no SLLA option is present
* as mandated by section 7.1.1 of RFC 2461.
*/
static boolean_t
{
while (optlen > 0) {
if (opt->nd_opt_len == 0) {
char abuf[INET6_ADDRSTRLEN];
}
return (_B_FALSE);
}
if (optlen < 0) {
char abuf[INET6_ADDRSTRLEN];
"0x%x len %u from %s\n",
}
return (_B_FALSE);
}
if (reject_dad_slla &&
return (_B_FALSE);
}
}
return (_B_TRUE);
}
static void
{
char abuf[INET6_ADDRSTRLEN];
}
/* Printing functions */
static void
{
struct nd_opt_hdr *opt;
char abuf[INET6_ADDRSTRLEN];
}
static void
{
struct nd_opt_hdr *opt;
char abuf[INET6_ADDRSTRLEN];
"Set" : "Not set");
"Set" : "Not set");
"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) {
break;
}
switch (opt->nd_opt_type) {
break;
break;
case ND_OPT_MTU:
break;
break;
case ND_OPT_SOURCE_LINKADDR:
if (optlen < 8 ||
break;
break;
case ND_OPT_TARGET_LINKADDR:
if (optlen < 8||
break;
break;
case ND_OPT_REDIRECTED_HEADER:
break;
default:
break;
}
}
}