summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/inet/ip/ip_if.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/inet/ip/ip_if.c')
-rw-r--r--usr/src/uts/common/inet/ip/ip_if.c495
1 files changed, 398 insertions, 97 deletions
diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c
index 179c1dd7ce..8edf1bb113 100644
--- a/usr/src/uts/common/inet/ip/ip_if.c
+++ b/usr/src/uts/common/inet/ip/ip_if.c
@@ -157,10 +157,8 @@ static void ipif_check_bcast_ires(ipif_t *test_ipif);
static void ipif_down_delete_ire(ire_t *ire, char *ipif);
static void ipif_delete_cache_ire(ire_t *, char *);
static int ipif_logical_down(ipif_t *ipif, queue_t *q, mblk_t *mp);
-static void ipif_down_tail(ipif_t *ipif);
static void ipif_free(ipif_t *ipif);
static void ipif_free_tail(ipif_t *ipif);
-static void ipif_mask_reply(ipif_t *);
static void ipif_mtu_change(ire_t *ire, char *ipif_arg);
static void ipif_multicast_down(ipif_t *ipif);
static void ipif_recreate_interface_routes(ipif_t *old_ipif, ipif_t *ipif);
@@ -180,6 +178,7 @@ static int ill_arp_off(ill_t *ill);
static int ill_arp_on(ill_t *ill);
static void ill_delete_interface_type(ill_if_t *);
static int ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q);
+static void ill_dl_down(ill_t *ill);
static void ill_down(ill_t *ill);
static void ill_downi(ire_t *ire, char *ill_arg);
static void ill_downi_mrtun_srcif(ire_t *ire, char *ill_arg);
@@ -671,6 +670,20 @@ ill_arp_alloc(ill_t *ill, uchar_t *template, caddr_t addr)
return (mp);
}
+mblk_t *
+ipif_area_alloc(ipif_t *ipif)
+{
+ return (ill_arp_alloc(ipif->ipif_ill, (uchar_t *)&ip_area_template,
+ (char *)&ipif->ipif_lcl_addr));
+}
+
+mblk_t *
+ipif_ared_alloc(ipif_t *ipif)
+{
+ return (ill_arp_alloc(ipif->ipif_ill, (uchar_t *)&ip_ared_template,
+ (char *)&ipif->ipif_lcl_addr));
+}
+
/*
* Completely vaporize a lower level tap and all associated interfaces.
* ill_delete is called only out of ip_close when the device control
@@ -751,6 +764,19 @@ ill_delete(ill_t *ill)
rw_exit(&ill_g_usesrc_lock);
}
+static void
+ipif_non_duplicate(ipif_t *ipif)
+{
+ ill_t *ill = ipif->ipif_ill;
+ mutex_enter(&ill->ill_lock);
+ if (ipif->ipif_flags & IPIF_DUPLICATE) {
+ ipif->ipif_flags &= ~IPIF_DUPLICATE;
+ ASSERT(ill->ill_ipif_dup_count > 0);
+ ill->ill_ipif_dup_count--;
+ }
+ mutex_exit(&ill->ill_lock);
+}
+
/*
* ill_delete_tail is called from ip_modclose after all references
* to the closing ill are gone. The wait is done in ip_modclose
@@ -761,8 +787,14 @@ ill_delete_tail(ill_t *ill)
mblk_t **mpp;
ipif_t *ipif;
- for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next)
+ for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
+ ipif_non_duplicate(ipif);
ipif_down_tail(ipif);
+ }
+
+ ASSERT(ill->ill_ipif_dup_count == 0 &&
+ ill->ill_arp_down_mp == NULL &&
+ ill->ill_arp_del_mapping_mp == NULL);
/*
* If polling capability is enabled (which signifies direct
@@ -1489,8 +1521,10 @@ ipif_all_down_tail(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
ipif_t *ipif;
ASSERT(IAM_WRITER_IPSQ(ipsq));
- for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next)
+ for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
+ ipif_non_duplicate(ipif);
ipif_down_tail(ipif);
+ }
ill_down_tail(ill);
freemsg(mp);
ipsq->ipsq_current_ipif = NULL;
@@ -5645,8 +5679,10 @@ ipif_is_quiescent(ipif_t *ipif)
}
ill = ipif->ipif_ill;
- if (ill->ill_ipif_up_count != 0 || ill->ill_logical_down)
+ if (ill->ill_ipif_up_count != 0 || ill->ill_ipif_dup_count != 0 ||
+ ill->ill_logical_down) {
return (B_TRUE);
+ }
/* This is the last ipif going down or being deleted on this ill */
if (ill->ill_ire_cnt != 0 || ill->ill_refcnt != 0) {
@@ -9144,6 +9180,8 @@ ip_sioctl_arp_common(ill_t *ill, queue_t *q, mblk_t *mp, sin_t *sin,
area->area_flags |= ACE_F_PERMANENT;
if (flags & ATF_PUBL)
area->area_flags |= ACE_F_PUBLISH;
+ if (flags & ATF_AUTHORITY)
+ area->area_flags |= ACE_F_AUTHORITY;
/*
* Up to ARP it goes. The response will come
@@ -10118,6 +10156,8 @@ errack:
*flagsp |= ATF_PERM;
if (area->area_flags & ACE_F_PUBLISH)
*flagsp |= ATF_PUBL;
+ if (area->area_flags & ACE_F_AUTHORITY)
+ *flagsp |= ATF_AUTHORITY;
if (area->area_hw_addr_length != 0) {
*flagsp |= ATF_COM;
/*
@@ -10524,10 +10564,11 @@ ip_sioctl_removeif(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
if (ipif->ipif_refcnt == 0 && ipif->ipif_ire_cnt == 0) {
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
+ ipif_non_duplicate(ipif);
ipif_down_tail(ipif);
ipif_free_tail(ipif);
return (0);
- }
+ }
success = ipsq_pending_mp_add(connp, ipif, CONNP_TO_WQ(connp), mp,
IPIF_FREE);
mutex_exit(&ill->ill_lock);
@@ -10565,6 +10606,7 @@ ip_sioctl_removeif_restart(ipif_t *ipif, sin_t *dummy_sin, queue_t *q,
ASSERT(IAM_WRITER_IPIF(ipif));
ASSERT(ipif->ipif_state_flags & IPIF_CONDEMNED);
+ ipif_non_duplicate(ipif);
ipif_down_tail(ipif);
ipif_free_tail(ipif);
@@ -10682,10 +10724,19 @@ ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ipaddr_t addr;
sin6_t *sin6;
int err = 0;
+ ill_t *ill = ipif->ipif_ill;
+ boolean_t need_dl_down;
+ boolean_t need_arp_down;
ip1dbg(("ip_sioctl_addr_tail(%s:%u %p)\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
+ ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
+
+ /* Must cancel any pending timer before taking the ill_lock */
+ if (ipif->ipif_recovery_id != 0)
+ (void) untimeout(ipif->ipif_recovery_id);
+ ipif->ipif_recovery_id = 0;
+
if (ipif->ipif_isv6) {
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
@@ -10693,17 +10744,37 @@ ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
addr = sin->sin_addr.s_addr;
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
}
- mutex_enter(&ipif->ipif_ill->ill_lock);
+ mutex_enter(&ill->ill_lock);
ipif->ipif_v6lcl_addr = v6addr;
if (ipif->ipif_flags & (IPIF_ANYCAST | IPIF_NOLOCAL)) {
ipif->ipif_v6src_addr = ipv6_all_zeros;
} else {
ipif->ipif_v6src_addr = v6addr;
}
+ ipif->ipif_addr_ready = 0;
+
+ /*
+ * If the interface was previously marked as a duplicate, then since
+ * we've now got a "new" address, it should no longer be considered a
+ * duplicate -- even if the "new" address is the same as the old one.
+ * Note that if all ipifs are down, we may have a pending ARP down
+ * event to handle. This is because we want to recover from duplicates
+ * and thus delay tearing down ARP until the duplicates have been
+ * removed or disabled.
+ */
+ need_dl_down = need_arp_down = B_FALSE;
+ if (ipif->ipif_flags & IPIF_DUPLICATE) {
+ need_arp_down = !need_up;
+ ipif->ipif_flags &= ~IPIF_DUPLICATE;
+ if (--ill->ill_ipif_dup_count == 0 && !need_up &&
+ ill->ill_ipif_up_count == 0 && ill->ill_dl_up) {
+ need_dl_down = B_TRUE;
+ }
+ }
- if ((ipif->ipif_isv6) && IN6_IS_ADDR_6TO4(&v6addr) &&
- (!ipif->ipif_ill->ill_is_6to4tun)) {
- queue_t *wqp = ipif->ipif_ill->ill_wq;
+ if (ipif->ipif_isv6 && IN6_IS_ADDR_6TO4(&v6addr) &&
+ !ill->ill_is_6to4tun) {
+ queue_t *wqp = ill->ill_wq;
/*
* The local address of this interface is a 6to4 address,
@@ -10719,7 +10790,7 @@ ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
if (wqp->q_next->q_qinfo->qi_minfo->mi_idnum
== TUN6TO4_MODID) {
/* set for use in IP */
- ipif->ipif_ill->ill_is_6to4tun = 1;
+ ill->ill_is_6to4tun = 1;
break;
}
wqp = wqp->q_next;
@@ -10728,7 +10799,7 @@ ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
}
ipif_set_default(ipif);
- mutex_exit(&ipif->ipif_ill->ill_lock);
+ mutex_exit(&ill->ill_lock);
if (need_up) {
/*
@@ -10748,6 +10819,11 @@ ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
sctp_update_ipif(ipif, SCTP_IPIF_UPDATE);
}
+ if (need_dl_down)
+ ill_dl_down(ill);
+ if (need_arp_down)
+ ipif_arp_down(ipif);
+
return (err);
}
@@ -10872,9 +10948,17 @@ ip_sioctl_dstaddr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
in6_addr_t v6addr;
ill_t *ill = ipif->ipif_ill;
int err = 0;
+ boolean_t need_dl_down;
+ boolean_t need_arp_down;
+
+ ip1dbg(("ip_sioctl_dstaddr_tail(%s:%u %p)\n", ill->ill_name,
+ ipif->ipif_id, (void *)ipif));
+
+ /* Must cancel any pending timer before taking the ill_lock */
+ if (ipif->ipif_recovery_id != 0)
+ (void) untimeout(ipif->ipif_recovery_id);
+ ipif->ipif_recovery_id = 0;
- ip1dbg(("ip_sioctl_dstaddr_tail(%s:%u %p)\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_isv6) {
sin6_t *sin6;
@@ -10898,7 +10982,24 @@ ip_sioctl_dstaddr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ipif->ipif_flags |= IPIF_POINTOPOINT;
ipif->ipif_flags &= ~IPIF_BROADCAST;
if (ipif->ipif_isv6)
- ipif->ipif_ill->ill_flags |= ILLF_NONUD;
+ ill->ill_flags |= ILLF_NONUD;
+ }
+
+ /*
+ * If the interface was previously marked as a duplicate, then since
+ * we've now got a "new" address, it should no longer be considered a
+ * duplicate -- even if the "new" address is the same as the old one.
+ * Note that if all ipifs are down, we may have a pending ARP down
+ * event to handle.
+ */
+ need_dl_down = need_arp_down = B_FALSE;
+ if (ipif->ipif_flags & IPIF_DUPLICATE) {
+ need_arp_down = !need_up;
+ ipif->ipif_flags &= ~IPIF_DUPLICATE;
+ if (--ill->ill_ipif_dup_count == 0 && !need_up &&
+ ill->ill_ipif_up_count == 0 && ill->ill_dl_up) {
+ need_dl_down = B_TRUE;
+ }
}
/* Set the new address. */
@@ -10918,6 +11019,12 @@ ip_sioctl_dstaddr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
*/
err = ipif_up(ipif, q, mp);
}
+
+ if (need_dl_down)
+ ill_dl_down(ill);
+
+ if (need_arp_down)
+ ipif_arp_down(ipif);
return (err);
}
@@ -12917,47 +13024,45 @@ void
ipif_arp_down(ipif_t *ipif)
{
mblk_t *mp;
+ ill_t *ill = ipif->ipif_ill;
- ip1dbg(("ipif_arp_down(%s:%u)\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id));
+ ip1dbg(("ipif_arp_down(%s:%u)\n", ill->ill_name, ipif->ipif_id));
ASSERT(IAM_WRITER_IPIF(ipif));
/* Delete the mapping for the local address */
mp = ipif->ipif_arp_del_mp;
if (mp != NULL) {
- ip1dbg(("ipif_arp_down: %s (%u) for %s:%u\n",
- dlpi_prim_str(*(int *)mp->b_rptr), *(int *)mp->b_rptr,
- ipif->ipif_ill->ill_name, ipif->ipif_id));
- putnext(ipif->ipif_ill->ill_rq, mp);
+ ip1dbg(("ipif_arp_down: arp cmd %x for %s:%u\n",
+ *(unsigned *)mp->b_rptr, ill->ill_name, ipif->ipif_id));
+ putnext(ill->ill_rq, mp);
ipif->ipif_arp_del_mp = NULL;
}
/*
- * If this is the last ipif that is going down, we need
- * to clean up ARP completely.
+ * If this is the last ipif that is going down and there are no
+ * duplicate addresses we may yet attempt to re-probe, then we need to
+ * clean up ARP completely.
*/
- if (ipif->ipif_ill->ill_ipif_up_count == 0) {
+ if (ill->ill_ipif_up_count == 0 && ill->ill_ipif_dup_count == 0) {
/* Send up AR_INTERFACE_DOWN message */
- mp = ipif->ipif_ill->ill_arp_down_mp;
+ mp = ill->ill_arp_down_mp;
if (mp != NULL) {
- ip1dbg(("ipif_arp_down: %s (%u) for %s:%u\n",
- dlpi_prim_str(*(int *)mp->b_rptr),
- *(int *)mp->b_rptr, ipif->ipif_ill->ill_name,
+ ip1dbg(("ipif_arp_down: arp cmd %x for %s:%u\n",
+ *(unsigned *)mp->b_rptr, ill->ill_name,
ipif->ipif_id));
- putnext(ipif->ipif_ill->ill_rq, mp);
- ipif->ipif_ill->ill_arp_down_mp = NULL;
+ putnext(ill->ill_rq, mp);
+ ill->ill_arp_down_mp = NULL;
}
/* Tell ARP to delete the multicast mappings */
- mp = ipif->ipif_ill->ill_arp_del_mapping_mp;
+ mp = ill->ill_arp_del_mapping_mp;
if (mp != NULL) {
- ip1dbg(("ipif_arp_down: %s (%u) for %s:%u\n",
- dlpi_prim_str(*(int *)mp->b_rptr),
- *(int *)mp->b_rptr, ipif->ipif_ill->ill_name,
+ ip1dbg(("ipif_arp_down: arp cmd %x for %s:%u\n",
+ *(unsigned *)mp->b_rptr, ill->ill_name,
ipif->ipif_id));
- putnext(ipif->ipif_ill->ill_rq, mp);
- ipif->ipif_ill->ill_arp_del_mapping_mp = NULL;
+ putnext(ill->ill_rq, mp);
+ ill->ill_arp_del_mapping_mp = NULL;
}
}
}
@@ -13000,9 +13105,8 @@ ipif_arp_setup_multicast(ipif_t *ipif, mblk_t **arp_add_mapping_mp)
*/
mp = ill->ill_arp_del_mapping_mp;
if (mp != NULL) {
- ip1dbg(("ipif_arp_down: %s (%u) for %s:%u\n",
- dlpi_prim_str(*(int *)mp->b_rptr),
- *(int *)mp->b_rptr, ill->ill_name, ipif->ipif_id));
+ ip1dbg(("ipif_arp_down: arp cmd %x for %s:%u\n",
+ *(unsigned *)mp->b_rptr, ill->ill_name, ipif->ipif_id));
putnext(ill->ill_rq, mp);
ill->ill_arp_del_mapping_mp = NULL;
}
@@ -13077,6 +13181,7 @@ ipif_arp_setup_multicast(ipif_t *ipif, mblk_t **arp_add_mapping_mp)
return (0);
}
ASSERT(add_mp != NULL && del_mp != NULL);
+ ASSERT(ill->ill_arp_del_mapping_mp == NULL);
ill->ill_arp_del_mapping_mp = del_mp;
if (arp_add_mapping_mp != NULL) {
/* The caller just wants the mblks allocated */
@@ -13095,15 +13200,18 @@ ipif_arp_setup_multicast(ipif_t *ipif, mblk_t **arp_add_mapping_mp)
* though it only sets up the resolver for v6
* if it's an xresolv interface (one using an external resolver).
* Honors ILLF_NOARP.
- * The boolean value arp_just_publish, if B_TRUE, indicates that
- * it only needs to send an AR_ENTRY_ADD message up to ARP for
- * IPv4 interfaces. Currently, B_TRUE is only set when this
- * function is called by ip_rput_dlpi_writer() to handle
- * asynchronous hardware address change notification.
+ * The enumerated value res_act is used to tune the behavior.
+ * If set to Res_act_initial, then we set up all the resolver
+ * structures for a new interface. If set to Res_act_move, then
+ * we just send an AR_ENTRY_ADD message up to ARP for IPv4
+ * interfaces; this is called by ip_rput_dlpi_writer() to handle
+ * asynchronous hardware address change notification. If set to
+ * Res_act_defend, then we tell ARP that it needs to send a single
+ * gratuitous message in defense of the address.
* Returns error on failure.
*/
int
-ipif_resolver_up(ipif_t *ipif, boolean_t arp_just_publish)
+ipif_resolver_up(ipif_t *ipif, enum ip_resolver_action res_act)
{
caddr_t addr;
mblk_t *arp_up_mp = NULL;
@@ -13116,22 +13224,43 @@ ipif_resolver_up(ipif_t *ipif, boolean_t arp_just_publish)
uchar_t *area_p = NULL;
uchar_t *ared_p = NULL;
int err = ENOMEM;
+ boolean_t was_dup;
ip1dbg(("ipif_resolver_up(%s:%u) flags 0x%x\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id,
- (uint_t)ipif->ipif_flags));
+ ill->ill_name, ipif->ipif_id, (uint_t)ipif->ipif_flags));
ASSERT(IAM_WRITER_IPIF(ipif));
- if ((ill->ill_net_type != IRE_IF_RESOLVER) ||
- (ill->ill_isv6 && !(ill->ill_flags & ILLF_XRESOLV))) {
+ was_dup = B_FALSE;
+ if (res_act == Res_act_initial) {
+ ipif->ipif_addr_ready = 0;
+ /*
+ * We're bringing an interface up here. There's no way that we
+ * should need to shut down ARP now.
+ */
+ mutex_enter(&ill->ill_lock);
+ if (ipif->ipif_flags & IPIF_DUPLICATE) {
+ ipif->ipif_flags &= ~IPIF_DUPLICATE;
+ ill->ill_ipif_dup_count--;
+ was_dup = B_TRUE;
+ }
+ mutex_exit(&ill->ill_lock);
+ }
+ if (ipif->ipif_recovery_id != 0)
+ (void) untimeout(ipif->ipif_recovery_id);
+ ipif->ipif_recovery_id = 0;
+ if (ill->ill_net_type != IRE_IF_RESOLVER) {
+ ipif->ipif_addr_ready = 1;
return (0);
}
+ /* NDP will set the ipif_addr_ready flag when it's ready */
+ if (ill->ill_isv6 && !(ill->ill_flags & ILLF_XRESOLV))
+ return (0);
if (ill->ill_isv6) {
/*
* External resolver for IPv6
*/
- ASSERT(!arp_just_publish);
+ ASSERT(res_act == Res_act_initial);
if (!IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr)) {
addr = (caddr_t)&ipif->ipif_v6lcl_addr;
area_p = (uchar_t *)&ip6_area_template;
@@ -13149,7 +13278,8 @@ ipif_resolver_up(ipif_t *ipif, boolean_t arp_just_publish)
err = EINVAL;
goto failed;
} else {
- if (ill->ill_ipif_up_count == 0)
+ if (ill->ill_ipif_up_count == 0 &&
+ ill->ill_ipif_dup_count == 0 && !was_dup)
ill->ill_arp_bringup_pending = 1;
mutex_exit(&ill->ill_lock);
}
@@ -13164,17 +13294,19 @@ ipif_resolver_up(ipif_t *ipif, boolean_t arp_just_publish)
* Add an entry for the local address in ARP only if it
* is not UNNUMBERED and the address is not INADDR_ANY.
*/
- if (((ipif->ipif_flags & IPIF_UNNUMBERED) == 0) && area_p != NULL) {
+ if (!(ipif->ipif_flags & IPIF_UNNUMBERED) && area_p != NULL) {
+ area_t *area;
+
/* Now ask ARP to publish our address. */
arp_add_mp = ill_arp_alloc(ill, area_p, addr);
if (arp_add_mp == NULL)
goto failed;
- if (arp_just_publish) {
+ area = (area_t *)arp_add_mp->b_rptr;
+ if (res_act != Res_act_initial) {
/*
* Copy the new hardware address and length into
* arp_add_mp to be sent to ARP.
*/
- area_t *area = (area_t *)arp_add_mp->b_rptr;
area->area_hw_addr_length =
ill->ill_phys_addr_length;
bcopy((char *)ill->ill_phys_addr,
@@ -13182,10 +13314,20 @@ ipif_resolver_up(ipif_t *ipif, boolean_t arp_just_publish)
area->area_hw_addr_length);
}
- ((area_t *)arp_add_mp->b_rptr)->area_flags =
- ACE_F_PERMANENT | ACE_F_PUBLISH | ACE_F_MYADDR;
+ area->area_flags = ACE_F_PERMANENT | ACE_F_PUBLISH |
+ ACE_F_MYADDR;
+
+ if (res_act == Res_act_defend) {
+ area->area_flags |= ACE_F_DEFEND;
+ /*
+ * If we're just defending our address now, then
+ * there's no need to set up ARP multicast mappings.
+ * The publish command is enough.
+ */
+ goto done;
+ }
- if (arp_just_publish)
+ if (res_act != Res_act_initial)
goto arp_setup_multicast;
/*
@@ -13197,15 +13339,17 @@ ipif_resolver_up(ipif_t *ipif, boolean_t arp_just_publish)
goto failed;
} else {
- if (arp_just_publish)
+ if (res_act != Res_act_initial)
goto done;
}
/*
* Need to bring up ARP or setup multicast mapping only
* when the first interface is coming UP.
*/
- if (ill->ill_ipif_up_count != 0)
+ if (ill->ill_ipif_up_count != 0 || ill->ill_ipif_dup_count != 0 ||
+ was_dup) {
goto done;
+ }
/*
* Allocate an ARP down message (to be saved) and an ARP up
@@ -13236,7 +13380,7 @@ arp_setup_multicast:
ASSERT(arp_add_mapping_mp != NULL);
}
-done:;
+done:
if (arp_del_mp != NULL) {
ASSERT(ipif->ipif_arp_del_mp == NULL);
ipif->ipif_arp_del_mp = arp_del_mp;
@@ -13251,41 +13395,48 @@ done:;
}
if (arp_up_mp != NULL) {
ip1dbg(("ipif_resolver_up: ARP_UP for %s:%u\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id));
+ ill->ill_name, ipif->ipif_id));
putnext(ill->ill_rq, arp_up_mp);
}
if (arp_add_mp != NULL) {
ip1dbg(("ipif_resolver_up: ARP_ADD for %s:%u\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id));
+ ill->ill_name, ipif->ipif_id));
+ /*
+ * If it's an extended ARP implementation, then we'll wait to
+ * hear that DAD has finished before using the interface.
+ */
+ if (!ill->ill_arp_extend)
+ ipif->ipif_addr_ready = 1;
putnext(ill->ill_rq, arp_add_mp);
+ } else {
+ ipif->ipif_addr_ready = 1;
}
if (arp_add_mapping_mp != NULL) {
ip1dbg(("ipif_resolver_up: MAPPING_ADD for %s:%u\n",
- ipif->ipif_ill->ill_name, ipif->ipif_id));
+ ill->ill_name, ipif->ipif_id));
putnext(ill->ill_rq, arp_add_mapping_mp);
}
- if (arp_just_publish)
+ if (res_act != Res_act_initial)
return (0);
if (ill->ill_flags & ILLF_NOARP)
err = ill_arp_off(ill);
else
err = ill_arp_on(ill);
- if (err) {
+ if (err != 0) {
ip0dbg(("ipif_resolver_up: arp_on/off failed %d\n", err));
freemsg(ipif->ipif_arp_del_mp);
- if (arp_down_mp != NULL)
- freemsg(ill->ill_arp_down_mp);
- if (ill->ill_arp_del_mapping_mp != NULL)
- freemsg(ill->ill_arp_del_mapping_mp);
+ freemsg(ill->ill_arp_down_mp);
+ freemsg(ill->ill_arp_del_mapping_mp);
ipif->ipif_arp_del_mp = NULL;
ill->ill_arp_down_mp = NULL;
ill->ill_arp_del_mapping_mp = NULL;
return (err);
}
- return (ill->ill_ipif_up_count != 0 ? 0 : EINPROGRESS);
+ return ((ill->ill_ipif_up_count != 0 || was_dup ||
+ ill->ill_ipif_dup_count != 0) ? 0 : EINPROGRESS);
-failed:;
+failed:
ip1dbg(("ipif_resolver_up: FAILED\n"));
freemsg(arp_add_mp);
freemsg(arp_del_mp);
@@ -13297,6 +13448,143 @@ failed:;
}
/*
+ * This routine restarts IPv4 duplicate address detection (DAD) when a link has
+ * just gone back up.
+ */
+static void
+ipif_arp_start_dad(ipif_t *ipif)
+{
+ ill_t *ill = ipif->ipif_ill;
+ mblk_t *arp_add_mp;
+ area_t *area;
+
+ if (ill->ill_net_type != IRE_IF_RESOLVER || ill->ill_arp_closing ||
+ (ipif->ipif_flags & IPIF_UNNUMBERED) ||
+ ipif->ipif_lcl_addr == INADDR_ANY ||
+ (arp_add_mp = ill_arp_alloc(ill, (uchar_t *)&ip_area_template,
+ (char *)&ipif->ipif_lcl_addr)) == NULL) {
+ /*
+ * If we can't contact ARP for some reason, that's not really a
+ * problem. Just send out the routing socket notification that
+ * DAD completion would have done, and continue.
+ */
+ ipif_mask_reply(ipif);
+ ip_rts_ifmsg(ipif);
+ ip_rts_newaddrmsg(RTM_ADD, 0, ipif);
+ sctp_update_ipif(ipif, SCTP_IPIF_UP);
+ ipif->ipif_addr_ready = 1;
+ return;
+ }
+
+ /* Setting the 'unverified' flag restarts DAD */
+ area = (area_t *)arp_add_mp->b_rptr;
+ area->area_flags = ACE_F_PERMANENT | ACE_F_PUBLISH | ACE_F_MYADDR |
+ ACE_F_UNVERIFIED;
+ putnext(ill->ill_rq, arp_add_mp);
+}
+
+static void
+ipif_ndp_start_dad(ipif_t *ipif)
+{
+ nce_t *nce;
+
+ nce = ndp_lookup_v6(ipif->ipif_ill, &ipif->ipif_v6lcl_addr, B_FALSE);
+ if (nce == NULL)
+ return;
+
+ if (!ndp_restart_dad(nce)) {
+ /*
+ * If we can't restart DAD for some reason, that's not really a
+ * problem. Just send out the routing socket notification that
+ * DAD completion would have done, and continue.
+ */
+ ip_rts_ifmsg(ipif);
+ ip_rts_newaddrmsg(RTM_ADD, 0, ipif);
+ sctp_update_ipif(ipif, SCTP_IPIF_UP);
+ ipif->ipif_addr_ready = 1;
+ }
+ NCE_REFRELE(nce);
+}
+
+/*
+ * Restart duplicate address detection on all interfaces on the given ill.
+ *
+ * This is called when an interface transitions from down to up
+ * (DL_NOTE_LINK_UP) or up to down (DL_NOTE_LINK_DOWN).
+ *
+ * Note that since the underlying physical link has transitioned, we must cause
+ * at least one routing socket message to be sent here, either via DAD
+ * completion or just by default on the first ipif. (If we don't do this, then
+ * in.mpathd will see long delays when doing link-based failure recovery.)
+ */
+void
+ill_restart_dad(ill_t *ill, boolean_t went_up)
+{
+ ipif_t *ipif;
+
+ if (ill == NULL)
+ return;
+
+ /*
+ * If layer two doesn't support duplicate address detection, then just
+ * send the routing socket message now and be done with it.
+ */
+ if ((ill->ill_isv6 && (ill->ill_flags & ILLF_XRESOLV)) ||
+ (!ill->ill_isv6 && !ill->ill_arp_extend)) {
+ ip_rts_ifmsg(ill->ill_ipif);
+ return;
+ }
+
+ for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
+ if (went_up) {
+ if (ipif->ipif_flags & IPIF_UP) {
+ if (ill->ill_isv6)
+ ipif_ndp_start_dad(ipif);
+ else
+ ipif_arp_start_dad(ipif);
+ } else if (ill->ill_isv6 &&
+ (ipif->ipif_flags & IPIF_DUPLICATE)) {
+ /*
+ * For IPv4, the ARP module itself will
+ * automatically start the DAD process when it
+ * sees DL_NOTE_LINK_UP. We respond to the
+ * AR_CN_READY at the completion of that task.
+ * For IPv6, we must kick off the bring-up
+ * process now.
+ */
+ ndp_do_recovery(ipif);
+ } else {
+ /*
+ * Unfortunately, the first ipif is "special"
+ * and represents the underlying ill in the
+ * routing socket messages. Thus, when this
+ * one ipif is down, we must still notify so
+ * that the user knows the IFF_RUNNING status
+ * change. (If the first ipif is up, then
+ * we'll handle eventual routing socket
+ * notification via DAD completion.)
+ */
+ if (ipif == ill->ill_ipif)
+ ip_rts_ifmsg(ill->ill_ipif);
+ }
+ } else {
+ /*
+ * After link down, we'll need to send a new routing
+ * message when the link comes back, so clear
+ * ipif_addr_ready.
+ */
+ ipif->ipif_addr_ready = 0;
+ }
+ }
+
+ /*
+ * If we've torn down links, then notify the user right away.
+ */
+ if (!went_up)
+ ip_rts_ifmsg(ill->ill_ipif);
+}
+
+/*
* Wakeup all threads waiting to enter the ipsq, and sleeping
* on any of the ills in this ipsq. The ill_lock of the ill
* must be held so that waiters don't miss wakeups
@@ -13716,6 +14004,7 @@ ill_down_ipifs(ill_t *ill, mblk_t *mp, int index, boolean_t chk_nofailover)
if (!ipif->ipif_isv6)
ipif_check_bcast_ires(ipif);
(void) ipif_logical_down(ipif, NULL, NULL);
+ ipif_non_duplicate(ipif);
ipif_down_tail(ipif);
/*
* We don't do ipif_multicast_down for IPv4 in
@@ -16658,7 +16947,7 @@ ipif_move(ipif_t *ipif, ill_t *to_ill, queue_t *q, mblk_t *mp,
* move.
*/
rep_ipif->ipif_flags = ipif->ipif_flags | IPIF_NOFAILOVER;
- rep_ipif->ipif_flags &= ~IPIF_UP;
+ rep_ipif->ipif_flags &= ~IPIF_UP & ~IPIF_DUPLICATE;
rep_ipif->ipif_replace_zero = B_TRUE;
mutex_init(&rep_ipif->ipif_saved_ire_lock, NULL,
MUTEX_DEFAULT, NULL);
@@ -17796,7 +18085,7 @@ ipif_down(ipif_t *ipif, queue_t *q, mblk_t *mp)
return (EINPROGRESS);
}
-static void
+void
ipif_down_tail(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
@@ -17809,11 +18098,10 @@ ipif_down_tail(ipif_t *ipif)
* there are other logical units that are up.
* This occurs e.g. when we change a "significant" IFF_ flag.
*/
- if (ipif->ipif_ill->ill_wq != NULL) {
- if (!ill->ill_logical_down && (ill->ill_ipif_up_count == 0) &&
- ill->ill_dl_up) {
- ill_dl_down(ill);
- }
+ if (ill->ill_wq != NULL && !ill->ill_logical_down &&
+ ill->ill_ipif_up_count == 0 && ill->ill_ipif_dup_count == 0 &&
+ ill->ill_dl_up) {
+ ill_dl_down(ill);
}
ill->ill_logical_down = 0;
@@ -17821,7 +18109,7 @@ ipif_down_tail(ipif_t *ipif)
* Have to be after removing the routes in ipif_down_delete_ire.
*/
if (ipif->ipif_isv6) {
- if (ipif->ipif_ill->ill_flags & ILLF_XRESOLV)
+ if (ill->ill_flags & ILLF_XRESOLV)
ipif_arp_down(ipif);
} else {
ipif_arp_down(ipif);
@@ -18048,6 +18336,10 @@ ipif_free(ipif_t *ipif)
{
ASSERT(IAM_WRITER_IPIF(ipif));
+ if (ipif->ipif_recovery_id != 0)
+ (void) untimeout(ipif->ipif_recovery_id);
+ ipif->ipif_recovery_id = 0;
+
/* Remove conn references */
reset_conn_ipif(ipif);
@@ -18127,6 +18419,9 @@ ipif_free_tail(ipif_t *ipif)
rw_exit(&ill_g_lock);
mutex_destroy(&ipif->ipif_saved_ire_lock);
+
+ ASSERT(!(ipif->ipif_flags & (IPIF_UP | IPIF_DUPLICATE)));
+
/* Free the memory. */
mi_free((char *)ipif);
}
@@ -18344,7 +18639,7 @@ ipif_lookup_on_name(char *name, size_t namelen, boolean_t do_alloc,
* but might not make the system manager very popular. (May be called
* as writer.)
*/
-static void
+void
ipif_mask_reply(ipif_t *ipif)
{
icmph_t *icmph;
@@ -18900,13 +19195,14 @@ ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp)
err = ipif_ndp_up(ipif, &ipif->ipif_v6lcl_addr,
B_FALSE);
if (err != 0) {
- mp = ipsq_pending_mp_get(ipsq, &connp);
+ if (err != EINPROGRESS)
+ mp = ipsq_pending_mp_get(ipsq, &connp);
return (err);
}
}
/* Now, ARP */
- if ((err = ipif_resolver_up(ipif, B_FALSE)) ==
- EINPROGRESS) {
+ err = ipif_resolver_up(ipif, Res_act_initial);
+ if (err == EINPROGRESS) {
/* We will complete it in ip_arp_done */
return (err);
}
@@ -19455,7 +19751,6 @@ ipif_up_done(ipif_t *ipif)
}
-
/* This is the first interface on this ill */
if (ipif->ipif_ipif_up_count == 1 && !loopback) {
/*
@@ -19496,14 +19791,7 @@ ipif_up_done(ipif_t *ipif)
}
}
- /*
- * This had to be deferred until we had bound.
- * tell routing sockets that this interface is up
- */
- ip_rts_ifmsg(ipif);
- ip_rts_newaddrmsg(RTM_ADD, 0, ipif);
-
- if (!loopback) {
+ if (!loopback && ipif->ipif_addr_ready) {
/* Broadcast an address mask reply. */
ipif_mask_reply(ipif);
}
@@ -19513,8 +19801,19 @@ ipif_up_done(ipif_t *ipif)
}
if (src_ipif_held)
ipif_refrele(src_ipif);
- /* Let SCTP update the status for this ipif */
- sctp_update_ipif(ipif, SCTP_IPIF_UP);
+
+ /*
+ * This had to be deferred until we had bound. Tell routing sockets and
+ * others that this interface is up if it looks like the address has
+ * been validated. Otherwise, if it isn't ready yet, wait for
+ * duplicate address detection to do its thing.
+ */
+ if (ipif->ipif_addr_ready) {
+ ip_rts_ifmsg(ipif);
+ ip_rts_newaddrmsg(RTM_ADD, 0, ipif);
+ /* Let SCTP update the status for this ipif */
+ sctp_update_ipif(ipif, SCTP_IPIF_UP);
+ }
return (0);
bad:
@@ -19919,7 +20218,8 @@ retry:
/* Always skip NOLOCAL and ANYCAST interfaces */
if (ipif->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST))
continue;
- if (!(ipif->ipif_flags & IPIF_UP))
+ if (!(ipif->ipif_flags & IPIF_UP) ||
+ !ipif->ipif_addr_ready)
continue;
if (ipif->ipif_zoneid != zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
@@ -20700,7 +21000,8 @@ ip_sioctl_slifname(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
* This ill has not been inserted into the global list.
* So we are still single threaded and don't need any lock
*/
- ipif->ipif_flags = lifr->lifr_flags & IFF_LOGINT_FLAGS;
+ ipif->ipif_flags = lifr->lifr_flags & IFF_LOGINT_FLAGS &
+ ~IFF_DUPLICATE;
ill->ill_flags = lifr->lifr_flags & IFF_PHYINTINST_FLAGS;
ill->ill_phyint->phyint_flags = lifr->lifr_flags & IFF_PHYINT_FLAGS;