sctp_common.c revision c513743f1851cb6b7265d554537508ded93c4c71
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
#include "sctp_asconf.h"
static struct kmem_cache *sctp_kmem_faddr_cache;
/* Set the source address. Refer to comments in sctp_get_ire(). */
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_ire() to re-check if there is any source address
* available.
*/
if (!addr_set)
}
/*
* Call this function to update the cached IRE of a peer addr fp.
*/
void
{
int hdrlen;
/* Remove the previous cache IRE */
}
/*
* 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.
*/
}
} else {
ipst);
}
/*
* 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 ire
* 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;
}
/* Make sure the laddr is part of this association */
sp->saddr_ipif_unconfirmed = 0;
} else {
/*
* Set the src to the first saddr and hope for the best.
* Note that we will still do the ire caching below.
* Otherwise, whenever we send a packet, we need to do
* the ire lookup again and still may not get the correct
* source address. 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;
}
}
/*
* Note that ire_cache_lookup_*() returns an ire with the tracing
* bits enabled. This requires the thread holding the ire also
* do the IRE_REFRELE(). Thus we need to do IRE_REFHOLD_NOTR()
* and then IRE_REFRELE() the ire here to make the tracing bits
* work.
*/
/* Cache the IRE */
/*
* 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 sfa_pmss is a multiple of SCTP_ALIGN. */
}
}
}
void
{
continue;
/*
* If the cached IRE is going away, there is no point to
* update it.
*/
continue;
}
/*
* 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->pmtu_discovered) {
} else {
}
}
if (sctps->sctps_rtt_updates != 0 &&
/*
* If there is no old cached values, initialize them
* conservatively. Set them to be (1.5 * new value).
* This code copied from ip_ire_advise(). The cached
* value is in ms.
*/
} else {
}
} else {
}
fp->rtt_updates = 0;
}
}
}
/*
* The sender must set the total length in the IP header.
* If sendto == NULL, the current will be used.
*/
mblk_t *
{
int isv4;
} else {
}
/* Try to look for another IRE again. */
/*
* 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. */
}
}
/*
* IP will not free this IRE if it is condemned. SCTP needs to
* free it.
*/
}
/* Stash the conn and ire ptr info. for IP */
return (mp);
}
/*
* Notify upper layers about preferred write offset, write size.
*/
void
{
int hdrlen;
struct sock_proto_props sopp;
} else {
}
sizeof (sctp_data_hdr_t);
}
void
{
if (isv4) {
} else {
/*
* If an ip6i_t is present, the real IPv6 header
* immediately follows.
*/
sctp->sctp_iphc6));
}
}
int
{
int na1 = 0;
int overlap = 0;
int equal = 1;
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, -1 on memory allocation failure. If sleep
* is true, this function should never fail. 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
{
if (is_system_labeled()) {
int retv;
/* find_tpc automatically does the right thing with IPv4 */
return (EACCES);
case UNLABELED:
/*
* Can talk to unlabeled hosts if any of the
* following are true:
* 1. zone's label matches the remote host's
* default label,
* 2. mac_exempt is on and the zone dominates
* the remote host's label, or
* 3. mac_exempt is on and the socket is from
* the global zone.
*/
(sctp->sctp_mac_exempt &&
retv = 0;
break;
case SUN_CIPSO:
retv = 0;
break;
}
}
if (retv != 0)
return (retv);
}
return (ENOMEM);
return (ENOMEM);
}
/* Check for subnet broadcast. */
return (EADDRNOTAVAIL);
}
/* 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
{
sctp->sctp_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.
*/
return;
}
}
}
int
{
return (1);
}
}
return (0);
}
/*
* 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);
}
{
}
/* Find the next live one */
break;
}
}
/* Continue from beginning of list */
break;
}
}
}
/*
* nfp could only be NULL if all faddrs are down, and when
* this happens, faddr_dead() should have killed the
* association. Hence this assertion...
*/
return (nfp);
}
void
{
if (!sctp->sctp_faddrs) {
return;
}
fp->timer_running = 0;
}
fp->rc_timer_running = 0;
}
}
goto gotit;
}
;
} else {
}
/* XXX faddr2ire? */
sctp->sctp_nfaddrs--;
}
void
{
return;
}
/* in conn fanout; need to hold lock */
}
sctp->sctp_nfaddrs--;
}
}
}
void
{
sctp_zap_faddrs(sctp, 0);
}
/*
* Initialize the IPv4 header. Loses any record of any IP options.
*/
int
{
/*
* This is a simple initialization. If there's
* already a template, it should never be too small,
* so reuse it. Otherwise, allocate space for the new one.
*/
} else {
sctp->sctp_iphc_len = 0;
return (ENOMEM);
}
}
sizeof (sctp_hdr_t));
/*
* These two fields should be zero, and are already set above.
*
* sctp->sctp_ipha->ipha_ident,
* sctp->sctp_ipha->ipha_fragment_offset_and_flags.
*/
return (0);
}
/*
* Update sctp_sticky_hdrs based on sctp_sticky_ipp.
* The headers include ip6i_t (if needed), ip6_t, any sticky extension
* headers, and the maximum size sctp header (to avoid reallocation
* on the fly for additional sctp options).
* Returns failure if can't allocate memory.
*/
int
{
char *hdrs;
char buf[SCTP_MAX_HDR_LENGTH];
/*
*/
/* Need to reallocate */
return (ENOMEM);
if (sctp->sctp_iphc6_len != 0)
}
/* Set header fields not in ipp */
} else {
}
/*
* sctp->sctp_ip_hdr_len will include ip6i_t if there is one.
*/
/*
* If the hoplimit was not set by ip_build_hdrs_v6(), we need to
* set it to the default value for SCTP.
*/
/*
* If we're setting extension headers after a connection
* has been established, and if we have a routing header
* among the extension headers, call ip_massage_options_v6 to
* difference in the sctp header template.
* (This happens in sctp_connect_ipv6 if the routing header
* is set prior to the connect.)
*/
}
}
return (0);
}
/*
* Initialize the IPv6 header. Loses any record of any IPv6 extension headers.
*/
int
{
/*
* This is a simple initialization. If there's
* already a template, it should never be too small,
* so reuse it. Otherwise, allocate space for the new one.
* Ensure that there is enough space to "downgrade" the sctp_t
* to an IPv4 sctp_t. This requires having space for a full load
* of IPv4 options
*/
} else {
sctp->sctp_iphc6_len = 0;
return (ENOMEM);
}
}
/* Initialize the header template */
return (0);
}
static int
{
int added;
return (EACCES);
if (added == -1)
return (EACCES);
sctp->sctp_hdr_len);
if (added == -1)
return (EACCES);
added);
}
return (0);
}
static int
{
return (EACCES);
optbuf) != 0)
return (EACCES);
if (sctp_build_hdrs(sctp) != 0)
return (EACCES);
return (0);
}
/*
* XXX implement more sophisticated logic
*/
int
{
int gotv4 = 0;
int gotv6 = 0;
/* Set up using the primary first */
/* saddr may be unspec; make_mp() will handle this */
gotv4 = 1;
goto copyports;
}
}
} else {
/* saddr may be unspec; make_mp() will handle this */
gotv6 = 1;
}
/* copy in the faddr_t's saddr */
gotv4 = 1;
gotv6) {
break;
}
}
/* copy in the faddr_t's saddr */
gotv6 = 1;
if (gotv4)
break;
}
}
}
return (EACCES);
/* copy in the ports for good measure */
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 */
}
/* 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 sfa_pmss is a multiple of SCTP_ALIGN. */
~(SCTP_ALIGN - 1);
} else {
~(SCTP_ALIGN - 1);
}
fp->rtt_updates = 0;
/* Mark it as not confirmed. */
fp->pmtu_discovered = 0;
fp->rxt_unacked = 0;
}
/*ARGSUSED*/
static int
{
fp->timer_running = 0;
fp->rc_timer_running = 0;
return (0);
}
/*ARGSUSED*/
static void
{
}
void
sctp_faddr_init(void)
{
}
void
sctp_faddr_fini(void)
{
}