diff options
author | Anil udupa <anil.udupa@sun.com> | 2010-05-07 11:42:37 -0700 |
---|---|---|
committer | Anil udupa <anil.udupa@sun.com> | 2010-05-07 11:42:37 -0700 |
commit | a5407c02d5ed61b29481b9b71f1307d7ebec9e5c (patch) | |
tree | 18b6bc52531df5b4e7f784db63e205baa52881ae /usr/src/uts/common/inet | |
parent | 770915ebe81263e14c9bdd49d7d24aac978ef725 (diff) | |
download | illumos-gate-a5407c02d5ed61b29481b9b71f1307d7ebec9e5c.tar.gz |
6903932 Failure creating SCTP association to multi-homed remote host
Diffstat (limited to 'usr/src/uts/common/inet')
-rw-r--r-- | usr/src/uts/common/inet/ip/ip6_input.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/inet/ip/ip_input.c | 6 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_cookie.c | 26 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_hash.c | 188 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_impl.h | 8 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp_ip.h | 6 |
6 files changed, 193 insertions, 46 deletions
diff --git a/usr/src/uts/common/inet/ip/ip6_input.c b/usr/src/uts/common/inet/ip/ip6_input.c index 8f305114d1..8a718dbc11 100644 --- a/usr/src/uts/common/inet/ip/ip6_input.c +++ b/usr/src/uts/common/inet/ip/ip6_input.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved */ /* Copyright (c) 1990 Mentat Inc. */ @@ -2257,7 +2256,7 @@ repeat: return; } connp = sctp_fanout(&ip6h->ip6_src, &ip6h->ip6_dst, ports, - ira, mp, sctps); + ira, mp, sctps, sctph); if (connp == NULL) { /* Check for raw socket or OOTB handling */ ip_fanout_sctp_raw(mp, NULL, ip6h, ports, ira); diff --git a/usr/src/uts/common/inet/ip/ip_input.c b/usr/src/uts/common/inet/ip/ip_input.c index dbb4c067da..4ef8e0898c 100644 --- a/usr/src/uts/common/inet/ip/ip_input.c +++ b/usr/src/uts/common/inet/ip/ip_input.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved */ /* Copyright (c) 1990 Mentat Inc. */ @@ -2601,7 +2600,8 @@ ip_fanout_v4(mblk_t *mp, ipha_t *ipha, ip_recv_attr_t *ira) ip_fanout_sctp_raw(mp, ipha, NULL, ports, ira); return; } - connp = sctp_fanout(&map_src, &map_dst, ports, ira, mp, sctps); + connp = sctp_fanout(&map_src, &map_dst, ports, ira, mp, + sctps, sctph); if (connp == NULL) { /* Check for raw socket or OOTB handling */ ip_fanout_sctp_raw(mp, ipha, NULL, ports, ira); diff --git a/usr/src/uts/common/inet/sctp/sctp_cookie.c b/usr/src/uts/common/inet/sctp/sctp_cookie.c index cd55db5e68..9d708b99eb 100644 --- a/usr/src/uts/common/inet/sctp/sctp_cookie.c +++ b/usr/src/uts/common/inet/sctp/sctp_cookie.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved */ #include <sys/types.h> @@ -1449,7 +1448,7 @@ sctp_addrlist2sctp(mblk_t *mp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ich, ipha_t *iph; ip6_t *ip6h; in6_addr_t dst; - in6_addr_t src; + in6_addr_t src, *srcp = &src; sctp_parm_hdr_t *ph; ssize_t remaining; sctp_init_chunk_t *iack; @@ -1483,18 +1482,23 @@ sctp_addrlist2sctp(mblk_t *mp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ich, while (ph != NULL) { /* - * params have been put in host byteorder by - * sctp_check_input() + * params have been verified in sctp_check_input(), + * so no need to do it again here. * * For labeled systems, there's no need to check the * label here. It's known to be good as we checked * before allowing the connection to become bound. + * + * According to RFC4960 : + * All integer fields in an SCTP packet MUST be transmitted + * in network byte order, unless otherwise stated. + * Therefore convert the param type to network byte order. */ - if (ph->sph_type == PARM_ADDR4) { + if (ph->sph_type == htons(PARM_ADDR4)) { IN6_INADDR_TO_V4MAPPED((struct in_addr *)(ph + 1), - &src); + srcp); - sctp = sctp_conn_match(&src, &dst, ports, zoneid, + sctp = sctp_conn_match(&srcp, 1, &dst, ports, zoneid, 0, sctps); dprint(1, @@ -1505,9 +1509,9 @@ sctp_addrlist2sctp(mblk_t *mp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ich, if (sctp != NULL) { return (sctp); } - } else if (ph->sph_type == PARM_ADDR6) { - src = *(in6_addr_t *)(ph + 1); - sctp = sctp_conn_match(&src, &dst, ports, zoneid, + } else if (ph->sph_type == htons(PARM_ADDR6)) { + srcp = (in6_addr_t *)(ph + 1); + sctp = sctp_conn_match(&srcp, 1, &dst, ports, zoneid, 0, sctps); dprint(1, diff --git a/usr/src/uts/common/inet/sctp/sctp_hash.c b/usr/src/uts/common/inet/sctp/sctp_hash.c index b5c838d297..a92d43df91 100644 --- a/usr/src/uts/common/inet/sctp/sctp_hash.c +++ b/usr/src/uts/common/inet/sctp/sctp_hash.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved */ #include <sys/socket.h> @@ -231,18 +230,20 @@ cl_sctp_walk_list_stack(int (*cl_callback)(cl_sctp_info_t *, void *), } sctp_t * -sctp_conn_match(in6_addr_t *faddr, in6_addr_t *laddr, uint32_t ports, - zoneid_t zoneid, iaflags_t iraflags, sctp_stack_t *sctps) +sctp_conn_match(in6_addr_t **faddrpp, uint32_t nfaddr, in6_addr_t *laddr, + uint32_t ports, zoneid_t zoneid, iaflags_t iraflags, sctp_stack_t *sctps) { sctp_tf_t *tf; sctp_t *sctp; sctp_faddr_t *fp; conn_t *connp; + in6_addr_t **faddrs, **endaddrs = &faddrpp[nfaddr]; tf = &(sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, ports)]); mutex_enter(&tf->tf_lock); - for (sctp = tf->tf_sctp; sctp; sctp = sctp->sctp_conn_hash_next) { + for (sctp = tf->tf_sctp; sctp != NULL; sctp = + sctp->sctp_conn_hash_next) { connp = sctp->sctp_connp; if (ports != connp->conn_ports) continue; @@ -254,25 +255,23 @@ sctp_conn_match(in6_addr_t *faddr, in6_addr_t *laddr, uint32_t ports, continue; /* check for faddr match */ - for (fp = sctp->sctp_faddrs; fp; fp = fp->next) { - if (IN6_ARE_ADDR_EQUAL(faddr, &fp->faddr)) { - break; + for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->next) { + for (faddrs = faddrpp; faddrs < endaddrs; faddrs++) { + if (IN6_ARE_ADDR_EQUAL(*faddrs, &fp->faddr)) { + /* check for laddr match */ + if (sctp_saddr_lookup(sctp, laddr, 0) + != NULL) { + SCTP_REFHOLD(sctp); + mutex_exit(&tf->tf_lock); + return (sctp); + } + } } } - /* no faddr match; keep looking */ - if (fp == NULL) - continue; - - /* check for laddr match */ - if (sctp_saddr_lookup(sctp, laddr, 0) != NULL) { - SCTP_REFHOLD(sctp); - goto done; - } /* no match; continue to the next in the chain */ } -done: mutex_exit(&tf->tf_lock); return (sctp); } @@ -322,7 +321,7 @@ sctp_find_conn(in6_addr_t *src, in6_addr_t *dst, uint32_t ports, { sctp_t *sctp; - sctp = sctp_conn_match(src, dst, ports, zoneid, iraflags, sctps); + sctp = sctp_conn_match(&src, 1, dst, ports, zoneid, iraflags, sctps); if (sctp == NULL) { /* Not in conn fanout; check listen fanout */ sctp = listen_match(dst, ports, zoneid, iraflags, sctps); @@ -333,17 +332,162 @@ sctp_find_conn(in6_addr_t *src, in6_addr_t *dst, uint32_t ports, } /* + * This is called from sctp_fanout() with IP header src & dst addresses. + * First call sctp_conn_match() to get a match by passing in src & dst + * addresses from IP header. + * However sctp_conn_match() can return no match under condition such as : + * A host can send an INIT ACK from a different address than the INIT was sent + * to (in a multi-homed env). + * According to RFC4960, a host can send additional addresses in an INIT + * ACK chunk. + * Therefore extract all addresses from the INIT ACK chunk, pass to + * sctp_conn_match() to get a match. + */ +static sctp_t * +sctp_lookup_by_faddrs(mblk_t *mp, sctp_hdr_t *sctph, in6_addr_t *srcp, + in6_addr_t *dstp, uint32_t ports, zoneid_t zoneid, sctp_stack_t *sctps, + iaflags_t iraflags) +{ + sctp_t *sctp; + sctp_chunk_hdr_t *ich; + sctp_init_chunk_t *iack; + sctp_parm_hdr_t *ph; + ssize_t mlen, remaining; + uint16_t param_type, addr_len = PARM_ADDR4_LEN; + in6_addr_t src; + in6_addr_t **addrbuf = NULL, **faddrpp = NULL; + boolean_t isv4; + uint32_t totaddr, nfaddr = 0; + + /* + * If we get a match with the passed-in IP header src & dst addresses, + * quickly return the matched sctp. + */ + if ((sctp = sctp_conn_match(&srcp, 1, dstp, ports, zoneid, iraflags, + sctps)) != NULL) { + return (sctp); + } + + /* + * Currently sctph is set to NULL in icmp error fanout case + * (ip_fanout_sctp()). + * The above sctp_conn_match() should handle that, otherwise + * return no match found. + */ + if (sctph == NULL) + return (NULL); + + /* + * Do a pullup again in case the previous one was partially successful, + * so try to complete the pullup here and have a single contiguous + * chunk for processing of entire INIT ACK chunk below. + */ + if (mp->b_cont != NULL) { + if (pullupmsg(mp, -1) == 0) { + return (NULL); + } + } + + mlen = mp->b_wptr - (uchar_t *)(sctph + 1); + if ((ich = sctp_first_chunk((uchar_t *)(sctph + 1), mlen)) == NULL) { + return (NULL); + } + + if (ich->sch_id == CHUNK_INIT_ACK) { + remaining = ntohs(ich->sch_len) - sizeof (*ich) - + sizeof (*iack); + if (remaining < sizeof (*ph)) { + return (NULL); + } + + isv4 = (iraflags & IRAF_IS_IPV4) ? B_TRUE : B_FALSE; + if (!isv4) + addr_len = PARM_ADDR6_LEN; + totaddr = remaining/addr_len; + + iack = (sctp_init_chunk_t *)(ich + 1); + ph = (sctp_parm_hdr_t *)(iack + 1); + + addrbuf = (in6_addr_t **) + kmem_zalloc(totaddr * sizeof (in6_addr_t *), KM_NOSLEEP); + if (addrbuf == NULL) + return (NULL); + faddrpp = addrbuf; + + while (ph != NULL) { + /* + * According to RFC4960 : + * All integer fields in an SCTP packet MUST be + * transmitted in network byte order, + * unless otherwise stated. + * Therefore convert the param type to host byte order. + * Also do not add src address present in IP header + * as it has already been thru sctp_conn_match() above. + */ + param_type = ntohs(ph->sph_type); + switch (param_type) { + case PARM_ADDR4: + IN6_INADDR_TO_V4MAPPED((struct in_addr *) + (ph + 1), &src); + if (IN6_ARE_ADDR_EQUAL(&src, srcp)) + break; + *faddrpp = (in6_addr_t *) + kmem_zalloc(sizeof (in6_addr_t), + KM_NOSLEEP); + if (*faddrpp == NULL) + break; + IN6_INADDR_TO_V4MAPPED((struct in_addr *) + (ph + 1), *faddrpp); + nfaddr++; + faddrpp++; + break; + case PARM_ADDR6: + *faddrpp = (in6_addr_t *)(ph + 1); + if (IN6_ARE_ADDR_EQUAL(*faddrpp, srcp)) + break; + nfaddr++; + faddrpp++; + break; + default: + break; + } + ph = sctp_next_parm(ph, &remaining); + } + + ASSERT(nfaddr < totaddr); + + if (nfaddr > 0) { + sctp = sctp_conn_match(addrbuf, nfaddr, dstp, ports, + zoneid, iraflags, sctps); + + if (isv4) { + for (faddrpp = addrbuf; nfaddr > 0; + faddrpp++, nfaddr--) { + if (IN6_IS_ADDR_V4MAPPED(*faddrpp)) { + kmem_free(*faddrpp, + sizeof (in6_addr_t)); + } + } + } + } + kmem_free(addrbuf, totaddr * sizeof (in6_addr_t *)); + } + return (sctp); +} + +/* * Fanout to a sctp instance. */ conn_t * sctp_fanout(in6_addr_t *src, in6_addr_t *dst, uint32_t ports, - ip_recv_attr_t *ira, mblk_t *mp, sctp_stack_t *sctps) + ip_recv_attr_t *ira, mblk_t *mp, sctp_stack_t *sctps, sctp_hdr_t *sctph) { zoneid_t zoneid = ira->ira_zoneid; iaflags_t iraflags = ira->ira_flags; sctp_t *sctp; - sctp = sctp_conn_match(src, dst, ports, zoneid, iraflags, sctps); + sctp = sctp_lookup_by_faddrs(mp, sctph, src, dst, ports, zoneid, + sctps, iraflags); if (sctp == NULL) { /* Not in conn fanout; check listen fanout */ sctp = listen_match(dst, ports, zoneid, iraflags, sctps); @@ -417,7 +561,7 @@ ip_fanout_sctp(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h, uint32_t ports, src = &map_src; dst = &map_dst; } - connp = sctp_fanout(src, dst, ports, ira, mp, sctps); + connp = sctp_fanout(src, dst, ports, ira, mp, sctps, NULL); if (connp == NULL) { ip_fanout_sctp_raw(mp, ipha, ip6h, ports, ira); return; diff --git a/usr/src/uts/common/inet/sctp/sctp_impl.h b/usr/src/uts/common/inet/sctp/sctp_impl.h index 5823e03de2..94fb45b0a3 100644 --- a/usr/src/uts/common/inet/sctp/sctp_impl.h +++ b/usr/src/uts/common/inet/sctp/sctp_impl.h @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved */ #ifndef _INET_SCTP_SCTP_IMPL_H @@ -912,8 +911,8 @@ extern void sctp_congest_reset(sctp_t *); extern void sctp_conn_hash_insert(sctp_tf_t *, sctp_t *, int); extern void sctp_conn_hash_remove(sctp_t *); extern void sctp_conn_init(conn_t *); -extern sctp_t *sctp_conn_match(in6_addr_t *, in6_addr_t *, uint32_t, - zoneid_t, iaflags_t, sctp_stack_t *); +extern sctp_t *sctp_conn_match(in6_addr_t **, uint32_t, in6_addr_t *, + uint32_t, zoneid_t, iaflags_t, sctp_stack_t *); extern sctp_t *sctp_conn_request(sctp_t *, mblk_t *, uint_t, uint_t, sctp_init_chunk_t *, ip_recv_attr_t *); extern uint32_t sctp_cumack(sctp_t *, uint32_t, mblk_t **); @@ -1068,6 +1067,7 @@ extern int sctp_xmit_list_clean(sctp_t *, ssize_t); extern void sctp_zap_addrs(sctp_t *); extern void sctp_zap_faddrs(sctp_t *, int); +extern sctp_chunk_hdr_t *sctp_first_chunk(uchar_t *, ssize_t); /* Contract private interface between SCTP and Clustering - PSARC/2005/602 */ diff --git a/usr/src/uts/common/inet/sctp_ip.h b/usr/src/uts/common/inet/sctp_ip.h index 95c74bcbfe..2698f75e24 100644 --- a/usr/src/uts/common/inet/sctp_ip.h +++ b/usr/src/uts/common/inet/sctp_ip.h @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved */ #ifndef _INET_SCTP_IP_H @@ -30,6 +29,7 @@ extern "C" { #endif +#include <netinet/sctp.h> #include <inet/sctp/sctp_stack.h> #define SCTP_COMMON_HDR_LENGTH 12 /* SCTP common header length */ @@ -42,7 +42,7 @@ extern void sctp_ddi_g_destroy(void); extern conn_t *sctp_find_conn(in6_addr_t *, in6_addr_t *, uint32_t, zoneid_t, iaflags_t, sctp_stack_t *); extern conn_t *sctp_fanout(in6_addr_t *, in6_addr_t *, uint32_t, - ip_recv_attr_t *, mblk_t *, sctp_stack_t *); + ip_recv_attr_t *, mblk_t *, sctp_stack_t *, sctp_hdr_t *); extern void sctp_input(conn_t *, ipha_t *, ip6_t *, mblk_t *, ip_recv_attr_t *); extern void sctp_ootb_input(mblk_t *, ip_recv_attr_t *, ip_stack_t *); |