/*
* 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
*/
/*
*/
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
#include "sctp_asconf.h"
/* Set the source address. Refer to comments in sctp_get_dest(). */
void
{
/*
* If there is no source address avaialble, mark this peer address
* as unreachable for now. When the heartbeat timer fires, it will
* call sctp_get_dest() to re-check if there is any source address
* available.
*/
if (!addr_set)
}
/*
* 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
{
int hdrlen;
int error;
/*
* Tell sctp_make_mp it needs to call us again should we not
* complete and set the saddr.
*/
/*
* 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.
*/
}
/*
* Socket is connected - enable PMTU discovery.
*/
if (!sctps->sctps_ignore_path_mtu)
&nexthop);
if (error != 0) {
/*
* 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.
*/
return;
goto check_current;
}
if (!sctp->sctp_loopback)
/* Make sure the laddr is part of this association */
!sp->saddr_ipif_dontsrc) {
sp->saddr_ipif_unconfirmed = 0;
/* We did IPsec policy lookup for laddr already */
} else {
/*
* 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.
*/
return;
}
}
/*
* Pull out RTO information for this faddr and use it if we don't
* have any yet.
*/
/* The cached value is in ms. */
/* Bound the RTO by configured min and max values */
}
}
}
/*
* Record the MTU for this faddr. If the MTU for this faddr has
* changed, check if the assc MTU will also change.
*/
} else {
}
/* Make sure that sf_pmss is a multiple of SCTP_ALIGN. */
}
}
}
void
{
/*
* Only record the PMTU for this faddr if we actually have
* done discovery. This prevents initialized default from
* clobbering any real info that IP may have.
*/
if (fp->sf_pmtu_discovered) {
} else {
}
}
if (sctps->sctps_rtt_updates != 0 &&
/*
* dce_update_uinfo() merges these values with the
* old values.
*/
fp->sf_rtt_updates = 0;
}
ifindex = 0;
/*
* If we are going to create a DCE we'd better have
* an ifindex
*/
} else {
continue;
}
}
}
}
/*
* The sender must later set the total length in the IP header.
*/
mblk_t *
{
int isv4;
/* Need to pick a source */
/*
* Although we still may not get an IRE, the source address
* may be changed in sctp_get_ire(). Set src_changed to
* true so that the source address is copied again.
*/
}
/* There is no suitable source address to use, return. */
return (NULL);
if (isv4) {
} else {
}
ip1dbg(("sctp_make_mp: error making mp..\n"));
return (NULL);
}
if (isv4) {
/* Fix the source and destination addresses. */
}
/* set or clear the don't fragment bit */
} else {
}
} else {
/* Fix the source and destination addresses. */
}
}
return (mp);
}
/*
* Notify upper layers about preferred write offset, write size.
*/
void
{
int hdrlen;
} else {
}
sizeof (sctp_data_hdr_t);
}
/*
* Set the lengths in the packet and the transmit attributes.
*/
void
{
if (isv4) {
} else {
}
}
int
{
int na1 = 0;
int overlap = 0;
int onematch;
onematch = 0;
overlap++;
onematch = 1;
break;
}
if (!onematch) {
equal = 0;
}
}
na1++;
}
if (equal) {
return (SCTP_ADDR_EQUAL);
}
return (SCTP_ADDR_SUBSET);
}
if (overlap) {
return (SCTP_ADDR_OVERLAP);
}
return (SCTP_ADDR_DISJOINT);
}
/*
* Returns 0 on success, ENOMEM on memory allocation failure, EHOSTUNREACH
* if the connection credentials fail remote host accreditation or
* if the new destination does not support the previously established
* connection security label. If sleep is true, this function should
* never fail for a memory allocation failure. The boolean parameter
* "first" decides whether the newly created faddr structure should be
* added at the beginning of the list or at the end.
*
* Note: caller must hold conn fanout lock.
*/
int
{
int err;
if (is_system_labeled()) {
/*
* Verify the destination is allowed to receive packets
* at the security label of the connection we are initiating.
*
* tsol_check_dest() will create a new effective label for
* this connection with a modified label or label flags only
* if there are changes from the original label.
*
* Accept whatever label we get if this is the first
* destination address for this connection. The security
* label and label flags must match any previuous settings
* for all subsequent destination addresses.
*/
if (IN6_IS_ADDR_V4MAPPED(addr)) {
} else {
}
if (err != 0)
return (err);
} else if (effective_tsl != NULL) {
return (EHOSTUNREACH);
}
}
return (ENOMEM);
return (ENOMEM);
}
/* Start with any options set on the conn */
return (ENOMEM);
}
/* only element on list; first and last are same */
} else if (first) {
} else {
}
sctp->sctp_nfaddrs++;
return (0);
}
{
break;
}
return (fp);
}
{
break;
}
}
return (fp);
}
/*
* To change the currently used peer address to the specified one.
*/
void
{
/* Now setup the composite header. */
/* update don't fragment bit */
} else {
}
} else {
}
/* Update the uppper layer for the change. */
if (!SCTP_IS_DETACHED(sctp))
}
void
{
}
}
void
{
/*
* If we are under memory pressure, we abort association waiting
* in zero window probing state for too long. We do this by not
* resetting sctp_strikes. So if sctp_zero_win_probe continues
* while under memory pressure, this association will eventually
* time out.
*/
sctp->sctp_strikes = 0;
}
fp->sf_strikes = 0;
/* Should have a full IRE now */
/*
* 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.
*/
return;
}
}
}
/*
* Return B_TRUE if there is still an active peer address with zero strikes;
* otherwise rturn B_FALSE.
*/
{
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* 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
{
}
/* Current faddr down; need to switch it */
}
/* Find next alive faddr */
break;
}
}
/* Continue from beginning of list */
break;
}
}
}
/*
* Find a new fp, so if the current faddr is dead, use the new fp
* as the current one.
*/
/*
* Note that we don't need to reset the source addr
* of the new fp.
*/
}
return (0);
}
/* All faddrs are down; kill the association */
return (-1);
}
{
int min_strikes;
}
/* Nothing to do */
return (ofp);
/*
* Find the next live peer address with zero strikes. In case
* there is none, find the one with the lowest number of strikes.
*/
/* If reached end of list, continue scan from the head */
continue;
}
if (nfp->sf_strikes == 0)
break;
}
}
}
/* If reached the old address, there is no zero strike path */
/*
* If there is a peer address with zero strikes we use that, if not
* return a peer address with fewer strikes than the one last used,
* if neither exist we may as well stay with the old one.
*/
return (nfp);
return (saved_fp);
return (ofp);
}
void
{
if (!sctp->sctp_faddrs) {
return;
}
fp->sf_timer_running = 0;
}
fp->sf_rc_timer_running = 0;
}
}
goto gotit;
}
;
} else {
}
sctp->sctp_nfaddrs--;
}
void
{
return;
}
/* in conn fanout; need to hold lock */
}
}
sctp->sctp_nfaddrs--;
}
}
}
void
{
sctp_zap_faddrs(sctp, 0);
}
/*
* Build two SCTP header templates; one for IPv4 and one for IPv6.
* Store them in sctp_iphc and sctp_iphc6 respectively (and related fields).
* There are no IP addresses in the templates, but the port numbers and
* verifier are field in from the conn_t and sctp_t.
*
* Returns failure if can't allocate memory, or if there is a problem
*
* We allocate space for the minimum sctp header (sctp_hdr_t).
*
* for a routing header for sctp.
*
* Caller needs to update conn_wroff if desired.
*
* TSol notes: This assumes that a SCTP association has a single peer label
* since we only track a single pair of ipp_label_v4/v6 and not a separate one
* for each faddr.
*/
int
{
/* First do IPv4 header */
/* In case of TX label and IP options it can be too much */
if (ip_hdr_length > IP_MAX_HDR_LENGTH) {
/* Preserves existing TX errno for this */
return (EHOSTUNREACH);
}
/* Allocate new before we free any old */
return (ENOMEM);
} else {
}
/* Now IPv6 */
/* Allocate new before we free any old */
return (ENOMEM);
} else {
}
}
/*
* Verify that the first hop isn't a mapped address.
* Routers along the path need to do this verification
* for subsequent hops.
*/
return (EADDRNOTAVAIL);
}
return (0);
}
static int
{
&connp->conn_xmit_ipp));
}
static int
{
&connp->conn_xmit_ipp));
}
/*
* XXX implement more sophisticated logic
*
* Tsol note: We have already verified the addresses using tsol_check_dest
* in sctp_add_faddr, thus no need to redo that here.
* We do setup ipp_label_v4 and ipp_label_v6 based on which addresses
* we have.
*/
int
{
int gotv4 = 0;
int gotv6 = 0;
/* Set up using the primary first */
/* saddr may be unspec; make_mp() will handle this */
if (!is_system_labeled() ||
gotv4 = 1;
goto done;
}
}
} else {
if (!is_system_labeled() ||
gotv6 = 1;
}
}
if (!is_system_labeled() ||
gotv4 = 1;
break;
}
}
if (!is_system_labeled() ||
gotv6 = 1;
if (gotv4)
break;
}
}
}
done:
return (EACCES);
return (0);
}
/*
* got_errchunk is set B_TRUE only if called from validate_init_params(), when
* an ERROR chunk is already prepended the size of which needs updating for
* additional unrecognized parameters. Other callers either prepend the ERROR
* chunk with the correct size after calling this function, or they are calling
* to add an invalid parameter to an INIT_ACK chunk, in that case no ERROR chunk
* exists, the CAUSE blocks go into the INIT_ACK directly.
*
* *errmp will be non-NULL both when adding an additional CAUSE block to an
* existing prepended COOKIE ERROR chunk (processing params of an INIT_ACK),
* and when adding unrecognized parameters after the first, to an INIT_ACK
* (processing params of an INIT chunk).
*/
void
{
int pad;
}
return;
}
/* copy in the unrecognized parameter */
if (pad != 0)
/*
* Update total length if an ERROR chunk, then link
* this CAUSE block to the possible chain of CAUSE
* blocks attached to the ERROR chunk or INIT_ACK
* being created.
*/
if (got_errchunk) {
/* ERROR chunk already prepended */
}
} else {
}
}
/*
* o Bounds checking
* o Updates remaining
* o Checks alignment
*/
{
int pad;
return (NULL);
}
}
/*LINTED pointer cast may result in improper alignment*/
return (current);
}
/*
* Sets the address parameters given in the INIT chunk into sctp's
* faddrs; if psctp is non-NULL, copies psctp's saddrs. If there are
* no address parameters in the INIT chunk, a single faddr is created
* from the ip hdr at the beginning of pkt.
* If there already are existing addresses hanging from sctp, merge
* them in, if the old info contains addresses which are not present
* in this new info, get rid of them, and clean the pointers if there's
* messages which have this as their target address.
*
* We also re-adjust the source address list here since the list may
* contain more than what is actually part of the association. If
* we get here from sctp_send_cookie_echo(), we are on the active
* side and psctp will be NULL and ich will be the INIT-ACK chunk.
* If we get here from sctp_accept_comm(), ich will be the INIT chunk
* and psctp will the listening endpoint.
*
* INIT processing: When processing the INIT we inherit the src address
* list from the listener. For a loopback or linklocal association, we
* delete the list and just take the address from the IP header (since
* that's how we created the INIT-ACK). Additionally, for loopback we
* ignore the address params in the INIT. For determining which address
* types were sent in the INIT-ACK we follow the same logic as in
* creating the INIT-ACK. We delete addresses of the type that are not
* supported by the peer.
*
* INIT-ACK processing: When processing the INIT-ACK since we had not
* included addr params for loopback or linklocal addresses when creating
* the INIT, we just use the address from the IP header. Further, for
* loopback we ignore the addr param list. We mark addresses of the
* type not supported by the peer as unconfirmed.
*
* In case of INIT processing we look for supported address types in the
* supported address param, if present. In both cases the address type in
* the IP header is supported as well as types for addresses in the param
* list, if any.
*
* Once we have the supported address types sctp_check_saddr() runs through
* the source address list and deletes or marks as unconfirmed address of
* types not supported by the peer.
*
* Returns 0 on success, sys errno on failure
*/
int
{
int isv4;
int err;
int supp_af = 0;
if (sctp_options != NULL)
*sctp_options = 0;
/* extract the address from the IP header */
if (isv4) {
supp_af |= PARM_SUPP_V4;
} else {
supp_af |= PARM_SUPP_V6;
}
/*
* Unfortunately, we can't delay this because adding an faddr
* looks for the presence of the source address (from the ire
* for the faddr) in the source address list. We could have
* Now, we just end up nuking this list and taking the addr from
*/
if (err != 0)
return (err);
}
/*
* We will add the faddr before parsing the address list as this
* might be a loopback connection and we would not have to
* go through the list.
*
* Make sure the header's addr is in the list
*/
/* not included; add it now */
if (err != 0)
return (err);
/* sctp_faddrs will be the hdr addr */
}
/* make the header addr the primary */
/* For loopback connections & linklocal get address from the header */
if (sctp->sctp_nsaddrs != 0)
return (err);
/* For loopback ignore address list */
if (sctp->sctp_loopback)
return (0);
}
/* Walk the params in the INIT [ACK], pulling out addr params */
sizeof (sctp_init_chunk_t);
if (check_saddr) {
}
return (0);
}
/* params will have already been byteordered when validating */
int plen;
uint16_t *p;
while (plen > 0) {
switch (addrtype) {
case PARM_ADDR6:
supp_af |= PARM_SUPP_V6;
break;
case PARM_ADDR4:
supp_af |= PARM_SUPP_V4;
break;
default:
break;
}
p++;
plen -= sizeof (*p);
}
if (remaining >= PARM_ADDR4_LEN) {
supp_af |= PARM_SUPP_V4;
/*
* Screen out broad/multicasts & loopback.
* If the endpoint only accepts v6 address,
* go to the next one.
*
* Subnet broadcast check is done in
* sctp_add_faddr(). If the address is
* a broadcast address, it won't be added.
*/
if (ta == 0 ||
ta == INADDR_BROADCAST ||
goto next;
}
IN6_INADDR_TO_V4MAPPED((struct in_addr *)
/* Check for duplicate. */
goto next;
/* OK, add it to the faddr set */
B_FALSE);
/* Something is wrong... Try the next one. */
if (err != 0)
goto next;
}
/* An v4 socket should not take v6 addresses. */
if (remaining >= PARM_ADDR6_LEN) {
supp_af |= PARM_SUPP_V6;
/*
* Screen out link locals, mcast, loopback
* and bogus v6 address.
*/
if (IN6_IS_ADDR_LINKLOCAL(addr6) ||
goto next;
}
/* Check for duplicate. */
goto next;
B_FALSE);
/* Something is wrong... Try the next one. */
if (err != 0)
goto next;
}
if (sctp_options != NULL)
} /* else; skip */
next:
}
if (check_saddr) {
}
/*
* We have the right address list now, update clustering's
* knowledge because when we sent the INIT we had just added
* the address the INIT was sent to.
*/
return (ENOMEM);
}
/*
* Just include the address the INIT was sent to in the
* delete list and send the entire faddr list. We could
* do it differently (i.e include all the addresses in the
* add list even if it contains the original address OR
* remove the original address from the add list etc.), but
* this seems reasonable enough.
*/
dsize = sizeof (in6_addr_t);
return (ENOMEM);
}
/* alist and dlist will be freed by the clustering module */
}
return (0);
}
/*
* Returns 0 if the check failed and the restart should be refused,
* 1 if the check succeeded.
*/
int
{
int isv4;
int retval = 0;
int compres;
int nadded = 0;
/* extract the address from the IP header */
if (isv4) {
} else {
}
/* Walk the params in the INIT [ACK], pulling out addr params */
sizeof (sctp_init_chunk_t);
/* no parameters; restart OK */
return (1);
}
/* params will have already been byteordered when validating */
if (remaining >= PARM_ADDR4_LEN) {
IN6_INADDR_TO_V4MAPPED((struct in_addr *)
sleep);
goto done;
}
}
if (remaining >= PARM_ADDR6_LEN) {
sleep);
goto done;
}
}
}
/* link in the new addr, if it was an addr param */
} else {
}
}
}
/* no addr parameters; restart OK */
return (1);
}
/*
* got at least one; make sure the header's addr is
* in the list
*/
/* not included; add it now */
goto done;
}
}
/*
* Now, we can finally do the check: For each sctp instance
* on the hash line for ports, compare its faddr set against
* the new one. If the new one is a strict subset of any
* existing sctp's faddrs, the restart is OK. However, if there
* is an overlap, this could be an attack, so return failure.
* If all sctp's faddrs are disjoint, this is a legitimate new
* association.
*/
continue;
}
if (compres <= SCTP_ADDR_SUBSET) {
retval = 1;
goto done;
}
if (compres == SCTP_ADDR_OVERLAP) {
dprint(1,
("new assoc from %x:%x:%x:%x overlaps with %p\n",
/*
* While we still hold the lock, we need to
* figure out which addresses have been
* added so we can include them in the abort
* we will send back. Since these faddrs will
* never be used, we overload the rto field
* here, setting it to 0 if the address was
* not added, 1 if it was added.
*/
} else {
nadded++;
}
}
goto done;
}
}
/* All faddrs are disjoint; legit new association */
retval = 1;
done:
/* If are attempted adds, send back an abort listing the addrs */
if (nadded > 0) {
void *dtail;
goto cleanup;
}
dlen = 0;
continue;
}
ph++;
ph = (sctp_parm_hdr_t *)
dlen += PARM_ADDR4_LEN;
} else {
ph++;
ph = (sctp_parm_hdr_t *)
dlen += PARM_ADDR6_LEN;
}
}
/* Send off the abort */
ira);
}
/* Clean up */
if (fphead) {
}
}
}
return (retval);
}
/*
* Reset any state related to transmitted chunks.
*/
void
{
}
/*
* Clean up the transmit list as well since we have reset accounting
* on all the fps. Send event upstream, if required.
*/
}
sctp->sctp_unacked = 0;
/*
* Any control message as well. We will clean-up this list as well.
* If we do get an ACK we will just drop it. However, given that
* we are restarting chances are we aren't going to get any.
*/
sctp->sctp_cchunk_pend = 0;
sctp->sctp_rxt_nxttsn = 0;
sctp->sctp_rxt_maxtsn = 0;
}
static void
{
if (IN6_IS_ADDR_V4MAPPED(addr)) {
/* Make sure that sf_pmss is a multiple of SCTP_ALIGN. */
~(SCTP_ALIGN - 1);
} else {
~(SCTP_ALIGN - 1);
}
fp->sf_rtt_updates = 0;
fp->sf_strikes = 0;
/* Mark it as not confirmed. */
fp->sf_pmtu_discovered = 0;
fp->sf_T3expire = 0;
sizeof (fp->sf_hb_secret));
fp->sf_rxt_unacked = 0;
}
/*ARGSUSED*/
static int
{
fp->sf_timer_running = 0;
fp->sf_rc_timer_running = 0;
return (0);
}
/*ARGSUSED*/
static void
{
}
void
sctp_faddr_init(void)
{
}
void
sctp_faddr_fini(void)
{
}