void
sctp_faddr_alive(sctp_t *sctp, sctp_faddr_t *fp)
{
	int64_t now = ddi_get_lbolt64();

	fp->strikes = 0;
	sctp->sctp_strikes = 0;
	fp->lastactive = now;
	fp->hb_expiry = now + SET_HB_INTVL(fp);
	fp->hb_pending = B_FALSE;
	if (fp->state != SCTP_FADDRS_ALIVE) {
		fp->state = SCTP_FADDRS_ALIVE;
		sctp_intf_event(sctp, fp->faddr, SCTP_ADDR_AVAILABLE, 0);
		/* Should have a full IRE now */
		sctp_get_dest(sctp, fp);

		/*
		 * If this is the primary, switch back to it now.  And
		 * we probably want to reset the source addr used to reach
		 * it.
		 * Note that if we didn't find a source in sctp_get_dest
		 * then we'd be unreachable at this point in time.
		 */
		if (fp == sctp->sctp_primary &&
		    fp->state != SCTP_FADDRS_UNREACH) {
			sctp_set_faddr_current(sctp, fp);
			return;
		}
	}
}
/*ARGSUSED*/
static mblk_t *
sctp_setprim_req(sctp_t *sctp, sctp_parm_hdr_t *ph, uint32_t cid,
    sctp_faddr_t *fp, int *cont, int act, in6_addr_t *raddr)
{
	mblk_t *mp;
	sctp_parm_hdr_t *oph;
	sctp_faddr_t *nfp;
	in6_addr_t addr;

	*cont = 1;

	/* Check input */
	if (ntohs(ph->sph_len) < (sizeof (*ph) * 2)) {
		mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, ph, cid);
		if (mp == NULL) {
			*cont = -1;
		}
		return (mp);
	}

	oph = ph;
	ph = (sctp_parm_hdr_t *)((char *)ph + sizeof (sctp_parm_hdr_t) +
	    sizeof (cid));
	mp = sctp_check_addip_addr(ph, oph, cont, cid, &addr);
	if (mp != NULL) {
		return (mp);
	}

	nfp = sctp_lookup_faddr(sctp, &addr);
	if (nfp == NULL) {
		/*
		 * Peer is trying to set an address that is not
		 * part of the association.
		 */
		dprint(1, ("setprim: addr not here: %x:%x:%x:%x\n",
		    SCTP_PRINTADDR(addr)));
		mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid);
		if (mp == NULL) {
			*cont = -1;
		}
		return (mp);
	}

	sctp_intf_event(sctp, addr, SCTP_ADDR_MADE_PRIM, 0);
	sctp->sctp_primary = nfp;
	if (nfp->state != SCTP_FADDRS_ALIVE || nfp == sctp->sctp_current) {
		return (NULL);
	}
	sctp_set_faddr_current(sctp, nfp);
	return (NULL);
}
/*
 * Call this function to get information about a peer addr fp.
 *
 * Uses ip_attr_connect to avoid explicit use of ire and source address
 * selection.
 */
void
sctp_get_dest(sctp_t *sctp, sctp_faddr_t *fp)
{
	in6_addr_t	laddr;
	in6_addr_t	nexthop;
	sctp_saddr_ipif_t *sp;
	int		hdrlen;
	sctp_stack_t	*sctps = sctp->sctp_sctps;
	conn_t		*connp = sctp->sctp_connp;
	iulp_t		uinfo;
	uint_t		pmtu;
	int		error;
	uint32_t	flags = IPDF_VERIFY_DST | IPDF_IPSEC |
	    IPDF_SELECT_SRC | IPDF_UNIQUE_DCE;

	/*
	 * Tell sctp_make_mp it needs to call us again should we not
	 * complete and set the saddr.
	 */
	fp->saddr = ipv6_all_zeros;

	/*
	 * If this addr is not reachable, mark it as unconfirmed for now, the
	 * state will be changed back to unreachable later in this function
	 * if it is still the case.
	 */
	if (fp->state == SCTP_FADDRS_UNREACH) {
		fp->state = SCTP_FADDRS_UNCONFIRMED;
	}

	/*
	 * Socket is connected - enable PMTU discovery.
	 */
	if (!sctps->sctps_ignore_path_mtu)
		fp->ixa->ixa_flags |= IXAF_PMTU_DISCOVERY;

	ip_attr_nexthop(&connp->conn_xmit_ipp, fp->ixa, &fp->faddr,
	    &nexthop);

	laddr = fp->saddr;
	error = ip_attr_connect(connp, fp->ixa, &laddr, &fp->faddr, &nexthop,
	    connp->conn_fport, &laddr, &uinfo, flags);

	if (error != 0) {
		dprint(3, ("sctp_get_dest: no ire for %x:%x:%x:%x\n",
		    SCTP_PRINTADDR(fp->faddr)));
		/*
		 * It is tempting to just leave the src addr
		 * unspecified and let IP figure it out, but we
		 * *cannot* do this, since IP may choose a src addr
		 * that is not part of this association... unless
		 * this sctp has bound to all addrs.  So if the dest
		 * lookup fails, try to find one in our src addr
		 * list, unless the sctp has bound to all addrs, in
		 * which case we change the src addr to unspec.
		 *
		 * Note that if this is a v6 endpoint but it does
		 * not have any v4 address at this point (e.g. may
		 * have been  deleted), sctp_get_valid_addr() will
		 * return mapped INADDR_ANY.  In this case, this
		 * address should be marked not reachable so that
		 * it won't be used to send data.
		 */
		sctp_set_saddr(sctp, fp);
		if (fp->state == SCTP_FADDRS_UNREACH)
			return;
		goto check_current;
	}
	ASSERT(fp->ixa->ixa_ire != NULL);
	ASSERT(!(fp->ixa->ixa_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)));

	if (!sctp->sctp_loopback)
		sctp->sctp_loopback = uinfo.iulp_loopback;

	/* Make sure the laddr is part of this association */
	if ((sp = sctp_saddr_lookup(sctp, &laddr, 0)) != NULL &&
	    !sp->saddr_ipif_dontsrc) {
		if (sp->saddr_ipif_unconfirmed == 1)
			sp->saddr_ipif_unconfirmed = 0;
		/* We did IPsec policy lookup for laddr already */
		fp->saddr = laddr;
	} else {
		dprint(2, ("sctp_get_dest: src addr is not part of assoc "
		    "%x:%x:%x:%x\n", SCTP_PRINTADDR(laddr)));

		/*
		 * Set the src to the first saddr and hope for the best.
		 * Note that this case should very seldomly
		 * happen.  One scenario this can happen is an app
		 * explicitly bind() to an address.  But that address is
		 * not the preferred source address to send to the peer.
		 */
		sctp_set_saddr(sctp, fp);
		if (fp->state == SCTP_FADDRS_UNREACH) {
			return;
		}
	}

	/*
	 * Pull out RTO information for this faddr and use it if we don't
	 * have any yet.
	 */
	if (fp->srtt == -1 && uinfo.iulp_rtt != 0) {
		/* The cached value is in ms. */
		fp->srtt = MSEC_TO_TICK(uinfo.iulp_rtt);
		fp->rttvar = MSEC_TO_TICK(uinfo.iulp_rtt_sd);
		fp->rto = 3 * fp->srtt;

		/* Bound the RTO by configured min and max values */
		if (fp->rto < sctp->sctp_rto_min) {
			fp->rto = sctp->sctp_rto_min;
		}
		if (fp->rto > sctp->sctp_rto_max) {
			fp->rto = sctp->sctp_rto_max;
		}
		SCTP_MAX_RTO(sctp, fp);
	}
	pmtu = uinfo.iulp_mtu;

	/*
	 * Record the MTU for this faddr. If the MTU for this faddr has
	 * changed, check if the assc MTU will also change.
	 */
	if (fp->isv4) {
		hdrlen = sctp->sctp_hdr_len;
	} else {
		hdrlen = sctp->sctp_hdr6_len;
	}
	if ((fp->sfa_pmss + hdrlen) != pmtu) {
		/* Make sure that sfa_pmss is a multiple of SCTP_ALIGN. */
		fp->sfa_pmss = (pmtu - hdrlen) & ~(SCTP_ALIGN - 1);
		if (fp->cwnd < (fp->sfa_pmss * 2)) {
			SET_CWND(fp, fp->sfa_pmss,
			    sctps->sctps_slow_start_initial);
		}
	}

check_current:
	if (fp == sctp->sctp_current)
		sctp_set_faddr_current(sctp, fp);
}
/*
 * Returns 0 if there is at leave one other active faddr, -1 if there
 * are none. If there are none left, faddr_dead() will start killing the
 * association.
 * If the downed faddr was the current faddr, a new current faddr
 * will be chosen.
 */
int
sctp_faddr_dead(sctp_t *sctp, sctp_faddr_t *fp, int newstate)
{
	sctp_faddr_t *ofp;
	sctp_stack_t *sctps = sctp->sctp_sctps;

	if (fp->state == SCTP_FADDRS_ALIVE) {
		sctp_intf_event(sctp, fp->faddr, SCTP_ADDR_UNREACHABLE, 0);
	}
	fp->state = newstate;

	dprint(1, ("sctp_faddr_dead: %x:%x:%x:%x down (state=%d)\n",
	    SCTP_PRINTADDR(fp->faddr), newstate));

	if (fp == sctp->sctp_current) {
		/* Current faddr down; need to switch it */
		sctp->sctp_current = NULL;
	}

	/* Find next alive faddr */
	ofp = fp;
	for (fp = fp->next; fp != NULL; fp = fp->next) {
		if (fp->state == SCTP_FADDRS_ALIVE) {
			break;
		}
	}

	if (fp == NULL) {
		/* Continue from beginning of list */
		for (fp = sctp->sctp_faddrs; fp != ofp; fp = fp->next) {
			if (fp->state == SCTP_FADDRS_ALIVE) {
				break;
			}
		}
	}

	/*
	 * Find a new fp, so if the current faddr is dead, use the new fp
	 * as the current one.
	 */
	if (fp != ofp) {
		if (sctp->sctp_current == NULL) {
			dprint(1, ("sctp_faddr_dead: failover->%x:%x:%x:%x\n",
			    SCTP_PRINTADDR(fp->faddr)));
			/*
			 * Note that we don't need to reset the source addr
			 * of the new fp.
			 */
			sctp_set_faddr_current(sctp, fp);
		}
		return (0);
	}


	/* All faddrs are down; kill the association */
	dprint(1, ("sctp_faddr_dead: all faddrs down, killing assoc\n"));
	BUMP_MIB(&sctps->sctps_mib, sctpAborted);
	sctp_assoc_event(sctp, sctp->sctp_state < SCTPS_ESTABLISHED ?
	    SCTP_CANT_STR_ASSOC : SCTP_COMM_LOST, 0, NULL);
	sctp_clean_death(sctp, sctp->sctp_client_errno ?
	    sctp->sctp_client_errno : ETIMEDOUT);

	return (-1);
}
/*
 * Handles both add and delete address requests.
 */
static mblk_t *
sctp_addip_req(sctp_t *sctp, sctp_parm_hdr_t *ph, uint32_t cid,
    sctp_faddr_t *fp, int *cont, int act, in6_addr_t *raddr)
{
	in6_addr_t	addr;
	uint16_t	type;
	mblk_t		*mp;
	sctp_faddr_t	*nfp;
	sctp_parm_hdr_t	*oph = ph;
	int		err;
	sctp_stack_t	*sctps = sctp->sctp_sctps;

	*cont = 1;

	/* Send back an authorization error if addip is disabled */
	if (!sctps->sctps_addip_enabled) {
		err = SCTP_ERR_UNAUTHORIZED;
		goto error_handler;
	}
	/* Check input */
	if (ntohs(ph->sph_len) < (sizeof (*ph) * 2)) {
		err = SCTP_ERR_BAD_MANDPARM;
		goto error_handler;
	}

	type = ntohs(ph->sph_type);
	ph = (sctp_parm_hdr_t *)((char *)ph + sizeof (sctp_parm_hdr_t) +
	    sizeof (cid));
	mp = sctp_check_addip_addr(ph, oph, cont, cid, &addr);
	if (mp != NULL)
		return (mp);
	if (raddr != NULL)
		*raddr = addr;
	if (type == PARM_ADD_IP) {
		if (sctp_lookup_faddr(sctp, &addr) != NULL) {
			/* Address is already part of association */
			dprint(1, ("addip: addr already here: %x:%x:%x:%x\n",
			    SCTP_PRINTADDR(addr)));
			err = SCTP_ERR_BAD_MANDPARM;
			goto error_handler;
		}

		if (!act) {
			return (NULL);
		}
		/* Add the new address */
		mutex_enter(&sctp->sctp_conn_tfp->tf_lock);
		err = sctp_add_faddr(sctp, &addr, KM_NOSLEEP, B_FALSE);
		mutex_exit(&sctp->sctp_conn_tfp->tf_lock);
		if (err == ENOMEM) {
			/* no memory */
			*cont = -1;
			return (NULL);
		}
		if (err != 0) {
			err = SCTP_ERR_BAD_MANDPARM;
			goto error_handler;
		}
		sctp_intf_event(sctp, addr, SCTP_ADDR_ADDED, 0);
	} else if (type == PARM_DEL_IP) {
		nfp = sctp_lookup_faddr(sctp, &addr);
		if (nfp == NULL) {
			/*
			 * Peer is trying to delete an address that is not
			 * part of the association.
			 */
			dprint(1, ("delip: addr not here: %x:%x:%x:%x\n",
			    SCTP_PRINTADDR(addr)));
			err = SCTP_ERR_BAD_MANDPARM;
			goto error_handler;
		}
		if (sctp->sctp_faddrs == nfp && nfp->next == NULL) {
			/* Peer is trying to delete last address */
			dprint(1, ("delip: del last addr: %x:%x:%x:%x\n",
			    SCTP_PRINTADDR(addr)));
			err = SCTP_ERR_DEL_LAST_ADDR;
			goto error_handler;
		}
		if (nfp == fp) {
			/* Peer is trying to delete source address */
			dprint(1, ("delip: del src addr: %x:%x:%x:%x\n",
			    SCTP_PRINTADDR(addr)));
			err = SCTP_ERR_DEL_SRC_ADDR;
			goto error_handler;
		}
		if (!act) {
			return (NULL);
		}

		sctp_unlink_faddr(sctp, nfp);
		/* Update all references to the deleted faddr */
		if (sctp->sctp_primary == nfp) {
			sctp->sctp_primary = fp;
		}
		if (sctp->sctp_current == nfp) {
			sctp_set_faddr_current(sctp, fp);
		}
		if (sctp->sctp_lastdata == nfp) {
			sctp->sctp_lastdata = fp;
		}
		if (sctp->sctp_shutdown_faddr == nfp) {
			sctp->sctp_shutdown_faddr = nfp;
		}
		if (sctp->sctp_lastfaddr == nfp) {
			for (fp = sctp->sctp_faddrs; fp->next; fp = fp->next)
				;
			sctp->sctp_lastfaddr = fp;
		}
		sctp_intf_event(sctp, addr, SCTP_ADDR_REMOVED, 0);
	} else {
		ASSERT(0);
	}

	/* Successful, don't need to return anything. */
	return (NULL);

error_handler:
	mp = sctp_asconf_adderr(err, oph, cid);
	if (mp == NULL)
		*cont = -1;
	return (mp);
}
Example #6
0
/*
 * Walk the SCTP global list and refrele the ire for this ipif
 * This is called when an address goes down, so that we release any reference
 * to the ire associated with this address. Additionally, for any SCTP if
 * this was the only/last address in its source list, we don't kill the
 * assoc., if there is no address added subsequently, or if this does not
 * come up, then the assoc. will die a natural death (i.e. timeout).
 */
void
sctp_ire_cache_flush(ipif_t *ipif)
{
	sctp_t			*sctp;
	sctp_t			*sctp_prev = NULL;
	sctp_faddr_t		*fp;
	conn_t			*connp;
	ire_t			*ire;

	sctp = gsctp;
	mutex_enter(&sctp_g_lock);
	while (sctp != NULL) {
		mutex_enter(&sctp->sctp_reflock);
		if (sctp->sctp_condemned) {
			mutex_exit(&sctp->sctp_reflock);
			sctp = list_next(&sctp_g_list, sctp);
			continue;
		}
		sctp->sctp_refcnt++;
		mutex_exit(&sctp->sctp_reflock);
		mutex_exit(&sctp_g_lock);
		if (sctp_prev != NULL)
			SCTP_REFRELE(sctp_prev);

		RUN_SCTP(sctp);
		connp = sctp->sctp_connp;
		mutex_enter(&connp->conn_lock);
		ire = connp->conn_ire_cache;
		if (ire != NULL && ire->ire_ipif == ipif) {
			connp->conn_ire_cache = NULL;
			mutex_exit(&connp->conn_lock);
			IRE_REFRELE_NOTR(ire);
		} else {
			mutex_exit(&connp->conn_lock);
		}
		/* check for ires cached in faddr */
		for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->next) {
			/*
			 * If this ipif is being used as the source address
			 * we need to update it as well, else we will end
			 * up using the dead source address.
			 */
			ire = fp->ire;
			if (ire != NULL && ire->ire_ipif == ipif) {
				fp->ire = NULL;
				IRE_REFRELE_NOTR(ire);
			}
			/*
			 * This may result in setting the fp as unreachable,
			 * i.e. if all the source addresses are down. In
			 * that case the assoc. would timeout.
			 */
			if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr,
			    &fp->saddr)) {
				sctp_set_saddr(sctp, fp);
				if (fp == sctp->sctp_current &&
				    fp->state != SCTP_FADDRS_UNREACH) {
					sctp_set_faddr_current(sctp, fp);
				}
			}
		}
		WAKE_SCTP(sctp);
		sctp_prev = sctp;
		mutex_enter(&sctp_g_lock);
		sctp = list_next(&sctp_g_list, sctp);
	}
	mutex_exit(&sctp_g_lock);
	if (sctp_prev != NULL)
		SCTP_REFRELE(sctp_prev);
}