sctp_common.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
static struct kmem_cache *sctp_kmem_faddr_cache;
/* Set the source address. Refer to comments in sctp_ire2faddr(). */
static void
{
if (sctp->sctp_bound_to_all) {
} else {
/* Disable heartbeat. */
}
}
}
/*
* 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.
*/
}
/*
* 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.
*/
goto set_current;
}
/* make sure the laddr is part of this association */
} else {
ip2dbg(("ire2faddr: src addr is not part of assc\n"));
}
} else {
goto set_current;
}
/* make sure the laddr is part of this association */
} else {
"of assc\n"));
}
}
/* Cache the IRE */
/*
* Pull out RTO information for this faddr and use it if we don't
* have any yet.
*/
/* 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. */
}
}
if (!SCTP_IS_DETACHED(sctp)) {
}
}
}
/*ARGSUSED*/
void
{
return;
}
/* If the cached IRE is going sway, there is no point to update it. */
return;
}
/*
* 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 there is no old cached values, initialize them
* conservatively. Set them to be (1.5 * new value).
* This code copied from ip_ire_advise().
*/
} 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. */
/* There is no suitable source address to use, return. */
return (NULL);
if (isv4) {
} else {
}
ip1dbg(("sctp_make_mp: error makign mp..\n"));
return (NULL);
}
if (isv4) {
/* fiddle with the dst addr */
/* fix up src addr */
} else if (sctp->sctp_bound_to_all) {
}
}
/* set or clear the don't fragment bit */
} else {
}
} else {
/* fiddle with the dst addr */
/* fix up src addr */
} else if (sctp->sctp_bound_to_all) {
sizeof (in6_addr_t));
}
}
}
/*
* 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;
} else {
}
}
void
{
if (isv4) {
} else {
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, should never fail.
* Caller must hold conn fanout lock.
*/
int
{
sleep));
return (-1);
}
/* tack it on to the end */
} else {
/* list is empty */
}
return (0);
}
/*
* Caller must hold conn fanout lock.
*/
int
{
sleep));
return (-1);
}
/* Put it at the beginning of the list */
} else {
}
return (0);
}
{
break;
}
return (fp);
}
{
break;
}
}
return (fp);
}
void
{
/* Must not allow unspec src addr if not bound to all */
!sctp->sctp_bound_to_all) {
/*
* set the src to the first v4 saddr and hope
* for the best
*/
}
/* update don't fragment bit */
} else {
}
} else {
/* Must not allow unspec src addr if not bound to all */
!sctp->sctp_bound_to_all) {
/*
* set the src to the first v6 saddr and hope
* for the best
*/
}
}
}
void
{
}
}
void
{
sctp->sctp_strikes = 0;
/* If this is the primary, switch back to it now */
/* Reset the addrs in the composite header */
if (!SCTP_IS_DETACHED(sctp)) {
}
}
}
/* Should have a full IRE now */
}
}
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;
}
}
}
/* Reset the addrs in the composite header */
if (!SCTP_IS_DETACHED(sctp)) {
}
}
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? */
}
void
{
return;
}
/* in conn fanout; need to hold lock */
}
}
}
}
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 IPV6_HOPLIMIT was set in ipp, use that value.
* For sticky options, if it does not exist use
* All this as per RFC 2922.
*/
/*
* Set the IPv6 header payload length.
* If there's an ip6i_t included, don't count it in the length.
*/
/*
* 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);
}
/*
* XXX implement more sophisticated logic
*/
void
{
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;
break;
}
} else if (!gotv6) {
/* copy in the faddr_t's saddr */
gotv6 = 1;
if (gotv4) {
break;
}
}
}
/* copy in the ports for good measure */
}
void
{
int pad;
}
return;
}
/* copy in the unrecognized parameter */
} 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.
*
* Returns 0 on success, sys errno on failure
*/
int
{
int isv4;
int err;
if (sctp_options != NULL)
*sctp_options = 0;
/* inherit laddrs, if given */
if (err != 0)
return (err);
}
/* extract the address from the IP header */
if (isv4) {
} else {
}
/* For loopback connections ignore address list */
if (sctp->sctp_loopback)
goto get_from_iphdr;
/* Walk the params in the INIT [ACK], pulling out addr params */
sizeof (sctp_init_chunk_t);
/* no parameters */
goto get_from_iphdr;
}
/* params will have already been byteordered when validating */
if (remaining >= PARM_ADDR4_LEN) {
/*
* Screen out broad/multicasts & loopback.
* If the endpoint only accepts v6 address,
* go to the next one.
*/
if (ta == 0 ||
ta == INADDR_BROADCAST ||
IN_MULTICAST(ta) ||
goto next;
}
/*
* XXX also need to check for subnet
* broadcasts. This should probably
* wait until we have full access
* to the ILL tables.
*/
IN6_INADDR_TO_V4MAPPED((struct in_addr *)
/* Check for duplicate. */
goto next;
/* OK, add it to the faddr set */
KM_NOSLEEP) != 0) {
return (ENOMEM);
}
}
/* An v4 socket should not take v6 addresses. */
if (remaining >= PARM_ADDR6_LEN) {
/*
* Screen out link locals, mcast, loopback
* and bogus v6 address.
*/
if (IN6_IS_ADDR_LINKLOCAL(addr6) ||
goto next;
}
/* Check for duplicate. */
goto next;
if (sctp_add_faddr(sctp,
return (ENOMEM);
}
}
if (sctp_options != NULL)
} /* else; skip */
next:
}
/* Make sure the header's addr is in the list */
/* not included; add it now */
return (ENOMEM);
/* sctp_faddrs will be the hdr addr */
}
/* make the header addr the primary */
return (0);
}
/*
* Returns 0 if the check failed and the restart should be refused,
* 1 if the check succeeded.
*/
int
int sleep)
{
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);
if (!fpa) {
goto done;
}
}
if (remaining >= PARM_ADDR6_LEN) {
sleep);
if (!fpa) {
goto done;
}
}
} else {
/* else not addr param; skip */
}
/* link in the new addr, if it was an addr param */
if (fpa) {
if (!fphead) {
} else {
}
}
}
/* no addr parameters; restart OK */
return (1);
}
/*
* got at least one; make sure the header's addr is
* in the list
*/
if (!fp) {
/* not included; add it now */
if (!fp) {
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);
}
void
{
}
}
/*
* Return zero if the buffers are identical in length and content.
* This is used for comparing extension header buffers.
* Note that an extension header would be declared different
* even if all that changed was the next header value in that header i.e.
* what really changed is the next extension header.
*/
{
if (!b_valid)
blen = 0;
return (B_TRUE);
if (alen == 0)
return (B_FALSE); /* Both zero length */
}
/*
* Preallocate memory for sctp_savebuf(). Returns B_TRUE if ok.
* Return B_FALSE if memory allocation fails - don't change any state!
*/
{
void *dst;
if (!src_valid)
srclen = 0;
return (B_FALSE);
} else {
}
*dstlenp = 0;
}
else
*dstlenp = 0;
return (B_TRUE);
}
/*
* Replace what is in *dst, *dstlen with the source.
* Assumes sctp_allocbuf has already been called.
*/
void
{
if (!src_valid)
srclen = 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->timer_running = 0;
fp->pmtu_discovered = 0;
fp->rc_timer_running = 0;
}
/*ARGSUSED*/
static void
{
}
void
{
}
void
{
}