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); }
/* * 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); }