From owner-svn-src-all@FreeBSD.ORG Sat Dec 6 13:19:54 2008 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B70F1106564A; Sat, 6 Dec 2008 13:19:54 +0000 (UTC) (envelope-from rrs@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id A49228FC08; Sat, 6 Dec 2008 13:19:54 +0000 (UTC) (envelope-from rrs@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id mB6DJseb094958; Sat, 6 Dec 2008 13:19:54 GMT (envelope-from rrs@svn.freebsd.org) Received: (from rrs@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id mB6DJs0g094953; Sat, 6 Dec 2008 13:19:54 GMT (envelope-from rrs@svn.freebsd.org) Message-Id: <200812061319.mB6DJs0g094953@svn.freebsd.org> From: Randall Stewart Date: Sat, 6 Dec 2008 13:19:54 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r185694 - in head/sys: netinet netinet6 X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 06 Dec 2008 13:19:54 -0000 Author: rrs Date: Sat Dec 6 13:19:54 2008 New Revision: 185694 URL: http://svn.freebsd.org/changeset/base/185694 Log: Code from the hack-session known as the IETF (and a bit of debugging afterwards): - Fix protection code for notification generation. - Decouple associd from vtag - Allow vtags to have less strigent requirements in non-uniqueness. o don't pre-hash them when you issue one in a cookie. o Allow duplicates and use addresses and ports to discriminate amongst the duplicates during lookup. - Add support for the NAT draft draft-ietf-behave-sctpnat-00, this is still experimental and needs more extensive testing with the Jason Butt ipfw changes. - Support for the SENDER_DRY event to get DTLS in OpenSSL working with a set of patches from Michael Tuexen (hopefully heading to OpenSSL soon). - Update the support of SCTP-AUTH by Peter Lei. - Use macros for refcounting. - Fix MTU for UDP encapsulation. - Fix reporting back of unsent data. - Update assoc send counter handling to be consistent with endpoint sent counter. - Fix a bug in PR-SCTP. - Fix so we only send another FWD-TSN when a SACK arrives IF and only if the adv-peer-ack point progressed. However we still make sure a timer is running if we do have an adv_peer_ack point. - Fix PR-SCTP bug where chunks were retransmitted if they are sent unreliable but not abandoned yet. With the help of: Michael Teuxen and Peter Lei :-) MFC after: 4 weeks Modified: head/sys/netinet/sctp.h head/sys/netinet/sctp_asconf.c head/sys/netinet/sctp_asconf.h head/sys/netinet/sctp_auth.c head/sys/netinet/sctp_auth.h head/sys/netinet/sctp_constants.h head/sys/netinet/sctp_header.h head/sys/netinet/sctp_indata.c head/sys/netinet/sctp_indata.h head/sys/netinet/sctp_input.c head/sys/netinet/sctp_os_bsd.h head/sys/netinet/sctp_output.c head/sys/netinet/sctp_output.h head/sys/netinet/sctp_pcb.c head/sys/netinet/sctp_pcb.h head/sys/netinet/sctp_structs.h head/sys/netinet/sctp_sysctl.c head/sys/netinet/sctp_sysctl.h head/sys/netinet/sctp_timer.c head/sys/netinet/sctp_uio.h head/sys/netinet/sctp_usrreq.c head/sys/netinet/sctp_var.h head/sys/netinet/sctputil.c head/sys/netinet/sctputil.h head/sys/netinet6/sctp6_usrreq.c Modified: head/sys/netinet/sctp.h ============================================================================== --- head/sys/netinet/sctp.h Sat Dec 6 11:33:10 2008 (r185693) +++ head/sys/netinet/sctp.h Sat Dec 6 13:19:54 2008 (r185694) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -111,6 +111,7 @@ struct sctp_paramhdr { /* explict EOR signalling */ #define SCTP_EXPLICIT_EOR 0x0000001b #define SCTP_REUSE_PORT 0x0000001c /* rw */ +#define SCTP_AUTH_DEACTIVATE_KEY 0x0000001d /* * read-only options @@ -154,6 +155,8 @@ struct sctp_paramhdr { /* CMT ON/OFF socket option */ #define SCTP_CMT_ON_OFF 0x00001200 #define SCTP_CMT_USE_DAC 0x00001201 +/* EY - NR_SACK on/off socket option */ +#define SCTP_NR_SACK_ON_OFF 0x00001300 /* JRS - Pluggable Congestion Control Socket option */ #define SCTP_PLUGGABLE_CC 0x00001202 @@ -293,11 +296,15 @@ struct sctp_paramhdr { #define SCTP_CAUSE_PROTOCOL_VIOLATION 0x000d /* Error causes from RFC5061 */ -#define SCTP_CAUSE_DELETING_LAST_ADDR 0xa0 -#define SCTP_CAUSE_RESOURCE_SHORTAGE 0xa1 -#define SCTP_CAUSE_DELETING_SRC_ADDR 0xa2 -#define SCTP_CAUSE_ILLEGAL_ASCONF_ACK 0xa3 -#define SCTP_CAUSE_REQUEST_REFUSED 0xa4 +#define SCTP_CAUSE_DELETING_LAST_ADDR 0x00a0 +#define SCTP_CAUSE_RESOURCE_SHORTAGE 0x00a1 +#define SCTP_CAUSE_DELETING_SRC_ADDR 0x00a2 +#define SCTP_CAUSE_ILLEGAL_ASCONF_ACK 0x00a3 +#define SCTP_CAUSE_REQUEST_REFUSED 0x00a4 + +/* Error causes from nat-draft */ +#define SCTP_CAUSE_NAT_COLLIDING_STATE 0x00b0 +#define SCTP_CAUSE_NAT_MISSING_STATE 0x00b1 /* Error causes from RFC4895 */ #define SCTP_CAUSE_UNSUPPORTED_HMACID 0x0105 @@ -364,6 +371,8 @@ struct sctp_error_unrecognized_chunk { #define SCTP_SHUTDOWN_COMPLETE 0x0e /* RFC4895 */ #define SCTP_AUTHENTICATION 0x0f +/* EY nr_sack chunk id*/ +#define SCTP_NR_SELECTIVE_ACK 0x10 /************0x40 series ***********/ /************0x80 series ***********/ /* RFC5061 */ @@ -406,6 +415,9 @@ struct sctp_error_unrecognized_chunk { /* ECN Nonce: SACK Chunk Specific Flags */ #define SCTP_SACK_NONCE_SUM 0x01 +/* EY nr_sack all bit - All bit is the 2nd LSB of nr_sack chunk flags*/ +/* if All bit is set in an nr-sack chunk, then all nr gap acks gap acks*/ +#define SCTP_NR_SACK_ALL_BIT 0x02 /* CMT DAC algorithm SACK flag */ #define SCTP_SACK_CMT_DAC 0x80 @@ -467,6 +479,7 @@ struct sctp_error_unrecognized_chunk { #define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4 0x00800000 #define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x01000000 #define SCTP_PCB_FLAGS_PORTREUSE 0x02000000 +#define SCTP_PCB_FLAGS_DRYEVNT 0x04000000 /*- * mobility_features parameters (by micchie).Note * these features are applied against the Modified: head/sys/netinet/sctp_asconf.c ============================================================================== --- head/sys/netinet/sctp_asconf.c Sat Dec 6 11:33:10 2008 (r185693) +++ head/sys/netinet/sctp_asconf.c Sat Dec 6 13:19:54 2008 (r185694) @@ -761,6 +761,9 @@ sctp_handle_asconf(struct mbuf *m, unsig m_result = sctp_process_asconf_set_primary(m, aph, stcb, error); break; + case SCTP_NAT_VTAGS: + SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: sees a NAT VTAG state parameter\n"); + break; case SCTP_SUCCESS_REPORT: /* not valid in an ASCONF chunk */ break; @@ -1349,6 +1352,7 @@ sctp_asconf_queue_mgmt(struct sctp_tcb * SCTPDBG(SCTP_DEBUG_ASCONF1, "asconf_queue_mgmt: failed to get memory!\n"); return (-1); } + aa->special_del = 0; /* fill in asconf address parameter fields */ /* top level elements are "networked" during send */ aa->ap.aph.ph.param_type = type; @@ -1555,6 +1559,7 @@ sctp_asconf_queue_sa_delete(struct sctp_ "sctp_asconf_queue_sa_delete: failed to get memory!\n"); return (-1); } + aa->special_del = 0; /* fill in asconf address parameter fields */ /* top level elements are "networked" during send */ aa->ap.aph.ph.param_type = SCTP_DEL_IP_ADDRESS; @@ -2691,6 +2696,7 @@ sctp_compose_asconf(struct sctp_tcb *stc * case) */ if (lookup_used == 0 && + (aa->special_del == 0) && aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS) { struct sctp_ipv6addr_param *lookup; uint16_t p_size, addr_size; @@ -3234,3 +3240,195 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb * } return (0); } + +void +sctp_asconf_send_nat_state_update(struct sctp_tcb *stcb, + struct sctp_nets *net) +{ + struct sctp_asconf_addr *aa; + struct sctp_ifa *sctp_ifap; + struct sctp_asconf_tag_param *vtag; + struct sockaddr_in *to; + +#ifdef INET6 + struct sockaddr_in6 *to6; + +#endif + if (net == NULL) { + SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_asconf_send_nat_state_update: Missing net\n"); + return; + } + if (stcb == NULL) { + SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_asconf_send_nat_state_update: Missing stcb\n"); + return; + } + /* + * Need to have in the asconf: - vtagparam(my_vtag/peer_vtag) - + * add(0.0.0.0) - del(0.0.0.0) - Any global addresses add(addr) + */ + SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), + SCTP_M_ASC_ADDR); + if (aa == NULL) { + /* didn't get memory */ + SCTPDBG(SCTP_DEBUG_ASCONF1, + "sctp_asconf_send_nat_state_update: failed to get memory!\n"); + return; + } + aa->special_del = 0; + /* fill in asconf address parameter fields */ + /* top level elements are "networked" during send */ + aa->ifa = NULL; + aa->sent = 0; /* clear sent flag */ + vtag = (struct sctp_asconf_tag_param *)&aa->ap.aph; + vtag->aph.ph.param_type = SCTP_NAT_VTAGS; + vtag->aph.ph.param_length = sizeof(struct sctp_asconf_tag_param); + vtag->local_vtag = htonl(stcb->asoc.my_vtag); + vtag->remote_vtag = htonl(stcb->asoc.peer_vtag); + TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next); + + SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), + SCTP_M_ASC_ADDR); + if (aa == NULL) { + /* didn't get memory */ + SCTPDBG(SCTP_DEBUG_ASCONF1, + "sctp_asconf_send_nat_state_update: failed to get memory!\n"); + return; + } + memset(aa, 0, sizeof(struct sctp_asconf_addr)); + /* fill in asconf address parameter fields */ + /* ADD(0.0.0.0) */ + if (net->ro._l_addr.sa.sa_family == AF_INET) { + aa->ap.aph.ph.param_type = SCTP_ADD_IP_ADDRESS; + aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addrv4_param); + aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS; + aa->ap.addrp.ph.param_length = sizeof(struct sctp_ipv4addr_param); + /* No need to add an address, we are using 0.0.0.0 */ + TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next); + } +#ifdef INET6 + else if (net->ro._l_addr.sa.sa_family == AF_INET6) { + aa->ap.aph.ph.param_type = SCTP_ADD_IP_ADDRESS; + aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addr_param); + aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS; + aa->ap.addrp.ph.param_length = sizeof(struct sctp_ipv6addr_param); + /* No need to add an address, we are using 0.0.0.0 */ + TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next); + } +#endif /* INET6 */ + SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), + SCTP_M_ASC_ADDR); + if (aa == NULL) { + /* didn't get memory */ + SCTPDBG(SCTP_DEBUG_ASCONF1, + "sctp_asconf_send_nat_state_update: failed to get memory!\n"); + return; + } + memset(aa, 0, sizeof(struct sctp_asconf_addr)); + /* fill in asconf address parameter fields */ + /* ADD(0.0.0.0) */ + if (net->ro._l_addr.sa.sa_family == AF_INET) { + aa->ap.aph.ph.param_type = SCTP_ADD_IP_ADDRESS; + aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addrv4_param); + aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS; + aa->ap.addrp.ph.param_length = sizeof(struct sctp_ipv4addr_param); + /* No need to add an address, we are using 0.0.0.0 */ + TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next); + } +#ifdef INET6 + else if (net->ro._l_addr.sa.sa_family == AF_INET6) { + aa->ap.aph.ph.param_type = SCTP_DEL_IP_ADDRESS; + aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addr_param); + aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS; + aa->ap.addrp.ph.param_length = sizeof(struct sctp_ipv6addr_param); + /* No need to add an address, we are using 0.0.0.0 */ + TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next); + } +#endif /* INET6 */ + /* Now we must hunt the addresses and add all global addresses */ + if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { + struct sctp_vrf *vrf = NULL; + struct sctp_ifn *sctp_ifnp; + uint32_t vrf_id; + + vrf_id = stcb->sctp_ep->def_vrf_id; + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) { + goto skip_rest; + } + SCTP_IPI_ADDR_RLOCK(); + LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) { + LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) { + if (sctp_ifap->address.sa.sa_family == AF_INET) { + to = &sctp_ifap->address.sin; + + if (IN4_ISPRIVATE_ADDRESS(&to->sin_addr)) { + continue; + } + if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) { + continue; + } + } +#ifdef INET6 + else if (sctp_ifap->address.sa.sa_family == AF_INET6) { + to6 = &sctp_ifap->address.sin6; + if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr)) { + continue; + } + if (IN6_IS_ADDR_LINKLOCAL(&to6->sin6_addr)) { + continue; + } + } +#endif + sctp_asconf_queue_mgmt(stcb, sctp_ifap, SCTP_ADD_IP_ADDRESS); + } + } + SCTP_IPI_ADDR_RUNLOCK(); + } else { + struct sctp_laddr *laddr; + + LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) { + if (laddr->ifa == NULL) { + continue; + } + if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) + /* + * Address being deleted by the system, dont + * list. + */ + continue; + if (laddr->action == SCTP_DEL_IP_ADDRESS) { + /* + * Address being deleted on this ep don't + * list. + */ + continue; + } + sctp_ifap = laddr->ifa; + if (sctp_ifap->address.sa.sa_family == AF_INET) { + to = &sctp_ifap->address.sin; + + if (IN4_ISPRIVATE_ADDRESS(&to->sin_addr)) { + continue; + } + if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) { + continue; + } + } +#ifdef INET6 + else if (sctp_ifap->address.sa.sa_family == AF_INET6) { + to6 = &sctp_ifap->address.sin6; + if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr)) { + continue; + } + if (IN6_IS_ADDR_LINKLOCAL(&to6->sin6_addr)) { + continue; + } + } +#endif + sctp_asconf_queue_mgmt(stcb, sctp_ifap, SCTP_ADD_IP_ADDRESS); + } + } +skip_rest: + /* Now we must send the asconf into the queue */ + sctp_send_asconf(stcb, net, 0); +} Modified: head/sys/netinet/sctp_asconf.h ============================================================================== --- head/sys/netinet/sctp_asconf.h Sat Dec 6 11:33:10 2008 (r185693) +++ head/sys/netinet/sctp_asconf.h Sat Dec 6 13:19:54 2008 (r185694) @@ -86,6 +86,10 @@ extern void extern void sctp_net_immediate_retrans(struct sctp_tcb *, struct sctp_nets *); +extern void +sctp_asconf_send_nat_state_update(struct sctp_tcb *stcb, + struct sctp_nets *net); + extern int sctp_is_addr_pending(struct sctp_tcb *, struct sctp_ifa *); Modified: head/sys/netinet/sctp_auth.c ============================================================================== --- head/sys/netinet/sctp_auth.c Sat Dec 6 11:33:10 2008 (r185693) +++ head/sys/netinet/sctp_auth.c Sat Dec 6 13:19:54 2008 (r185694) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -359,9 +359,11 @@ sctp_set_key(uint8_t * key, uint32_t key return (new_key); } -/* +/*- * given two keys of variable size, compute which key is "larger/smaller" - * returns: 1 if key1 > key2 -1 if key1 < key2 0 if key1 = key2 + * returns: 1 if key1 > key2 + * -1 if key1 < key2 + * 0 if key1 = key2 */ static int sctp_compare_key(sctp_key_t * key1, sctp_key_t * key2) @@ -531,13 +533,18 @@ sctp_alloc_sharedkey(void) } new_key->keyid = 0; new_key->key = NULL; + new_key->refcount = 1; + new_key->deactivated = 0; return (new_key); } void sctp_free_sharedkey(sctp_sharedkey_t * skey) { - if (skey != NULL) { + if (skey == NULL) + return; + + if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&skey->refcount)) { if (skey->key != NULL) sctp_free_key(skey->key); SCTP_FREE(skey, SCTP_M_AUTH_KY); @@ -556,40 +563,93 @@ sctp_find_sharedkey(struct sctp_keyhead return (NULL); } -void +int sctp_insert_sharedkey(struct sctp_keyhead *shared_keys, sctp_sharedkey_t * new_skey) { sctp_sharedkey_t *skey; if ((shared_keys == NULL) || (new_skey == NULL)) - return; + return (EINVAL); /* insert into an empty list? */ if (SCTP_LIST_EMPTY(shared_keys)) { LIST_INSERT_HEAD(shared_keys, new_skey, next); - return; + return (0); } /* insert into the existing list, ordered by key id */ LIST_FOREACH(skey, shared_keys, next) { if (new_skey->keyid < skey->keyid) { /* insert it before here */ LIST_INSERT_BEFORE(skey, new_skey, next); - return; + return (0); } else if (new_skey->keyid == skey->keyid) { /* replace the existing key */ + /* verify this key *can* be replaced */ + if ((skey->deactivated) && (skey->refcount > 1)) { + SCTPDBG(SCTP_DEBUG_AUTH1, + "can't replace shared key id %u\n", + new_skey->keyid); + return (EBUSY); + } SCTPDBG(SCTP_DEBUG_AUTH1, "replacing shared key id %u\n", new_skey->keyid); LIST_INSERT_BEFORE(skey, new_skey, next); LIST_REMOVE(skey, next); sctp_free_sharedkey(skey); - return; + return (0); } if (LIST_NEXT(skey, next) == NULL) { /* belongs at the end of the list */ LIST_INSERT_AFTER(skey, new_skey, next); - return; + return (0); + } + } + /* shouldn't reach here */ + return (0); +} + +void +sctp_auth_key_acquire(struct sctp_tcb *stcb, uint16_t key_id) +{ + sctp_sharedkey_t *skey; + + /* find the shared key */ + skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, key_id); + + /* bump the ref count */ + if (skey) { + atomic_add_int(&skey->refcount, 1); + SCTPDBG(SCTP_DEBUG_AUTH2, + "%s: stcb %p key %u refcount acquire to %d\n", + __FUNCTION__, stcb, key_id, skey->refcount); + } +} + +void +sctp_auth_key_release(struct sctp_tcb *stcb, uint16_t key_id) +{ + sctp_sharedkey_t *skey; + + /* find the shared key */ + skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, key_id); + + /* decrement the ref count */ + if (skey) { + sctp_free_sharedkey(skey); + SCTPDBG(SCTP_DEBUG_AUTH2, + "%s: stcb %p key %u refcount release to %d\n", + __FUNCTION__, stcb, key_id, skey->refcount); + + /* see if a notification should be generated */ + if ((skey->refcount <= 1) && (skey->deactivated)) { + /* notify ULP that key is no longer used */ + sctp_ulp_notify(SCTP_NOTIFY_AUTH_FREE_KEY, stcb, + key_id, 0, SCTP_SO_NOT_LOCKED); + SCTPDBG(SCTP_DEBUG_AUTH2, + "%s: stcb %p key %u no longer used, %d\n", + __FUNCTION__, stcb, key_id, skey->refcount); } } } @@ -623,7 +683,7 @@ sctp_copy_skeylist(const struct sctp_key LIST_FOREACH(skey, src, next) { new_skey = sctp_copy_sharedkey(skey); if (new_skey != NULL) { - sctp_insert_sharedkey(dest, new_skey); + (void)sctp_insert_sharedkey(dest, new_skey); count++; } } @@ -727,9 +787,9 @@ sctp_default_supported_hmaclist(void) return (new_list); } -/* - * HMAC algos are listed in priority/preference order find the best HMAC id - * to use for the peer based on local support +/*- + * HMAC algos are listed in priority/preference order + * find the best HMAC id to use for the peer based on local support */ uint16_t sctp_negotiate_hmacid(sctp_hmaclist_t * peer, sctp_hmaclist_t * local) @@ -760,9 +820,9 @@ sctp_negotiate_hmacid(sctp_hmaclist_t * return (SCTP_AUTH_HMAC_ID_RSVD); } -/* - * serialize the HMAC algo list and return space used caller must guarantee - * ptr has appropriate space +/*- + * serialize the HMAC algo list and return space used + * caller must guarantee ptr has appropriate space */ int sctp_serialize_hmaclist(sctp_hmaclist_t * list, uint8_t * ptr) @@ -994,7 +1054,7 @@ sctp_hmac_final(uint16_t hmac_algo, sctp } /* end switch */ } -/* +/*- * Keyed-Hashing for Message Authentication: FIPS 198 (RFC 2104) * * Compute the HMAC digest using the desired hash key, text, and HMAC @@ -1142,9 +1202,10 @@ sctp_hmac_m(uint16_t hmac_algo, uint8_t return (digestlen); } -/* +/*- * verify the HMAC digest using the desired hash key, text, and HMAC - * algorithm. Returns -1 on error, 0 on success. + * algorithm. + * Returns -1 on error, 0 on success. */ int sctp_verify_hmac(uint16_t hmac_algo, uint8_t * key, uint32_t keylen, @@ -1263,10 +1324,10 @@ sctp_auth_is_supported_hmac(sctp_hmaclis } -/* - * clear any cached key(s) if they match the given key id on an association - * the cached key(s) will be recomputed and re-cached at next use. ASSUMES - * TCB_LOCK is already held +/*- + * clear any cached key(s) if they match the given key id on an association. + * the cached key(s) will be recomputed and re-cached at next use. + * ASSUMES TCB_LOCK is already held */ void sctp_clear_cachedkeys(struct sctp_tcb *stcb, uint16_t keyid) @@ -1284,9 +1345,10 @@ sctp_clear_cachedkeys(struct sctp_tcb *s } } -/* +/*- * clear any cached key(s) if they match the given key id for all assocs on - * an association ASSUMES INP_WLOCK is already held + * an endpoint. + * ASSUMES INP_WLOCK is already held */ void sctp_clear_cachedkeys_ep(struct sctp_inpcb *inp, uint16_t keyid) @@ -1304,8 +1366,9 @@ sctp_clear_cachedkeys_ep(struct sctp_inp } } -/* - * delete a shared key from an association ASSUMES TCB_LOCK is already held +/*- + * delete a shared key from an association + * ASSUMES TCB_LOCK is already held */ int sctp_delete_sharedkey(struct sctp_tcb *stcb, uint16_t keyid) @@ -1316,7 +1379,7 @@ sctp_delete_sharedkey(struct sctp_tcb *s return (-1); /* is the keyid the assoc active sending key */ - if (keyid == stcb->asoc.authinfo.assoc_keyid) + if (keyid == stcb->asoc.authinfo.active_keyid) return (-1); /* does the key exist? */ @@ -1324,6 +1387,10 @@ sctp_delete_sharedkey(struct sctp_tcb *s if (skey == NULL) return (-1); + /* are there other refcount holders on the key? */ + if (skey->refcount > 1) + return (-1); + /* remove it */ LIST_REMOVE(skey, next); sctp_free_sharedkey(skey); /* frees skey->key as well */ @@ -1333,35 +1400,29 @@ sctp_delete_sharedkey(struct sctp_tcb *s return (0); } -/* - * deletes a shared key from the endpoint ASSUMES INP_WLOCK is already held +/*- + * deletes a shared key from the endpoint + * ASSUMES INP_WLOCK is already held */ int sctp_delete_sharedkey_ep(struct sctp_inpcb *inp, uint16_t keyid) { sctp_sharedkey_t *skey; - struct sctp_tcb *stcb; if (inp == NULL) return (-1); - /* is the keyid the active sending key on the endpoint or any assoc */ + /* is the keyid the active sending key on the endpoint */ if (keyid == inp->sctp_ep.default_keyid) return (-1); - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { - SCTP_TCB_LOCK(stcb); - if (keyid == stcb->asoc.authinfo.assoc_keyid) { - SCTP_TCB_UNLOCK(stcb); - return (-1); - } - SCTP_TCB_UNLOCK(stcb); - } /* does the key exist? */ skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid); if (skey == NULL) return (-1); + /* endpoint keys are not refcounted */ + /* remove it */ LIST_REMOVE(skey, next); sctp_free_sharedkey(skey); /* frees skey->key as well */ @@ -1371,60 +1432,36 @@ sctp_delete_sharedkey_ep(struct sctp_inp return (0); } -/* - * set the active key on an association ASSUME TCB_LOCK is already held +/*- + * set the active key on an association + * ASSUMES TCB_LOCK is already held */ int sctp_auth_setactivekey(struct sctp_tcb *stcb, uint16_t keyid) { sctp_sharedkey_t *skey = NULL; - sctp_key_t *key = NULL; - int using_ep_key = 0; /* find the key on the assoc */ skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid); if (skey == NULL) { - /* if not on the assoc, find the key on the endpoint */ - atomic_add_int(&stcb->asoc.refcnt, 1); - SCTP_TCB_UNLOCK(stcb); - SCTP_INP_RLOCK(stcb->sctp_ep); - SCTP_TCB_LOCK(stcb); - atomic_add_int(&stcb->asoc.refcnt, -1); - skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys, - keyid); - using_ep_key = 1; - } - if (skey == NULL) { /* that key doesn't exist */ - if (using_ep_key) { - SCTP_INP_RUNLOCK(stcb->sctp_ep); - } return (-1); } - /* get the shared key text */ - key = skey->key; - - /* free any existing cached key */ - if (stcb->asoc.authinfo.assoc_key != NULL) - sctp_free_key(stcb->asoc.authinfo.assoc_key); - /* compute a new assoc key and cache it */ - stcb->asoc.authinfo.assoc_key = - sctp_compute_hashkey(stcb->asoc.authinfo.random, - stcb->asoc.authinfo.peer_random, key); - stcb->asoc.authinfo.assoc_keyid = keyid; -#ifdef SCTP_DEBUG - if (SCTP_AUTH_DEBUG) - sctp_print_key(stcb->asoc.authinfo.assoc_key, "Assoc Key"); -#endif - - if (using_ep_key) { - SCTP_INP_RUNLOCK(stcb->sctp_ep); + if ((skey->deactivated) && (skey->refcount > 1)) { + /* can't reactivate a deactivated key with other refcounts */ + return (-1); } + /* set the (new) active key */ + stcb->asoc.authinfo.active_keyid = keyid; + /* reset the deactivated flag */ + skey->deactivated = 0; + return (0); } -/* - * set the active key on an endpoint ASSUMES INP_WLOCK is already held +/*- + * set the active key on an endpoint + * ASSUMES INP_WLOCK is already held */ int sctp_auth_setactivekey_ep(struct sctp_inpcb *inp, uint16_t keyid) @@ -1441,6 +1478,69 @@ sctp_auth_setactivekey_ep(struct sctp_in return (0); } +/*- + * deactivates a shared key from the association + * ASSUMES INP_WLOCK is already held + */ +int +sctp_deact_sharedkey(struct sctp_tcb *stcb, uint16_t keyid) +{ + sctp_sharedkey_t *skey; + + if (stcb == NULL) + return (-1); + + /* is the keyid the assoc active sending key */ + if (keyid == stcb->asoc.authinfo.active_keyid) + return (-1); + + /* does the key exist? */ + skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid); + if (skey == NULL) + return (-1); + + /* are there other refcount holders on the key? */ + if (skey->refcount == 1) { + /* no other users, send a notification for this key */ + sctp_ulp_notify(SCTP_NOTIFY_AUTH_FREE_KEY, stcb, keyid, 0, + SCTP_SO_LOCKED); + } + /* mark the key as deactivated */ + skey->deactivated = 1; + + return (0); +} + +/*- + * deactivates a shared key from the endpoint + * ASSUMES INP_WLOCK is already held + */ +int +sctp_deact_sharedkey_ep(struct sctp_inpcb *inp, uint16_t keyid) +{ + sctp_sharedkey_t *skey; + + if (inp == NULL) + return (-1); + + /* is the keyid the active sending key on the endpoint */ + if (keyid == inp->sctp_ep.default_keyid) + return (-1); + + /* does the key exist? */ + skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid); + if (skey == NULL) + return (-1); + + /* endpoint keys are not refcounted */ + + /* remove it */ + LIST_REMOVE(skey, next); + sctp_free_sharedkey(skey); /* frees skey->key as well */ + + return (0); +} + /* * get local authentication parameters from cookie (from INIT-ACK) */ @@ -1581,9 +1681,13 @@ sctp_auth_get_cookie_params(struct sctp_ /* negotiate what HMAC to use for the peer */ stcb->asoc.peer_hmac_id = sctp_negotiate_hmacid(stcb->asoc.peer_hmacs, stcb->asoc.local_hmacs); + /* copy defaults from the endpoint */ /* FIX ME: put in cookie? */ - stcb->asoc.authinfo.assoc_keyid = stcb->sctp_ep->sctp_ep.default_keyid; + stcb->asoc.authinfo.active_keyid = stcb->sctp_ep->sctp_ep.default_keyid; + /* copy out the shared key list (by reference) from the endpoint */ + (void)sctp_copy_skeylist(&stcb->sctp_ep->sctp_ep.shared_keys, + &stcb->asoc.shared_keys); } /* @@ -1591,7 +1695,7 @@ sctp_auth_get_cookie_params(struct sctp_ */ void sctp_fill_hmac_digest_m(struct mbuf *m, uint32_t auth_offset, - struct sctp_auth_chunk *auth, struct sctp_tcb *stcb) + struct sctp_auth_chunk *auth, struct sctp_tcb *stcb, uint16_t keyid) { uint32_t digestlen; sctp_sharedkey_t *skey; @@ -1603,15 +1707,15 @@ sctp_fill_hmac_digest_m(struct mbuf *m, /* zero the digest + chunk padding */ digestlen = sctp_get_hmac_digest_len(stcb->asoc.peer_hmac_id); bzero(auth->hmac, SCTP_SIZE32(digestlen)); - /* is an assoc key cached? */ - if (stcb->asoc.authinfo.assoc_key == NULL) { - skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, - stcb->asoc.authinfo.assoc_keyid); - if (skey == NULL) { - /* not in the assoc list, so check the endpoint list */ - skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys, - stcb->asoc.authinfo.assoc_keyid); + + /* is the desired key cached? */ + if ((keyid != stcb->asoc.authinfo.assoc_keyid) || + (stcb->asoc.authinfo.assoc_key == NULL)) { + if (stcb->asoc.authinfo.assoc_key != NULL) { + /* free the old cached key */ + sctp_free_key(stcb->asoc.authinfo.assoc_key); } + skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid); /* the only way skey is NULL is if null key id 0 is used */ if (skey != NULL) key = skey->key; @@ -1621,6 +1725,7 @@ sctp_fill_hmac_digest_m(struct mbuf *m, stcb->asoc.authinfo.assoc_key = sctp_compute_hashkey(stcb->asoc.authinfo.random, stcb->asoc.authinfo.peer_random, key); + stcb->asoc.authinfo.assoc_keyid = keyid; SCTPDBG(SCTP_DEBUG_AUTH1, "caching key id %u\n", stcb->asoc.authinfo.assoc_keyid); #ifdef SCTP_DEBUG @@ -1630,11 +1735,10 @@ sctp_fill_hmac_digest_m(struct mbuf *m, #endif } /* set in the active key id */ - auth->shared_key_id = htons(stcb->asoc.authinfo.assoc_keyid); + auth->shared_key_id = htons(keyid); /* compute and fill in the digest */ - (void)sctp_compute_hmac_m(stcb->asoc.peer_hmac_id, - stcb->asoc.authinfo.assoc_key, + (void)sctp_compute_hmac_m(stcb->asoc.peer_hmac_id, stcb->asoc.authinfo.assoc_key, m, auth_offset, auth->hmac); } @@ -1671,9 +1775,11 @@ sctp_bzero_m(struct mbuf *m, uint32_t m_ } } -/* - * process the incoming Authentication chunk return codes: -1 on any - * authentication error 0 on authentication verification +/*- + * process the incoming Authentication chunk + * return codes: + * -1 on any authentication error + * 0 on authentication verification */ int sctp_handle_auth(struct sctp_tcb *stcb, struct sctp_auth_chunk *auth, @@ -1736,12 +1842,8 @@ sctp_handle_auth(struct sctp_tcb *stcb, if ((stcb->asoc.authinfo.recv_key == NULL) || (stcb->asoc.authinfo.recv_keyid != shared_key_id)) { /* find the shared key on the assoc first */ - skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, shared_key_id); - if (skey == NULL) { - /* if not on the assoc, find it on the endpoint */ - skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys, - shared_key_id); - } + skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, + shared_key_id); /* if the shared key isn't found, discard the chunk */ if (skey == NULL) { SCTP_STAT_INCR(sctps_recvivalkeyid); @@ -1758,7 +1860,8 @@ sctp_handle_auth(struct sctp_tcb *stcb, * *)stcb->asoc.authinfo.recv_keyid); */ sctp_notify_authentication(stcb, SCTP_AUTH_NEWKEY, - shared_key_id, stcb->asoc.authinfo.recv_keyid); + shared_key_id, stcb->asoc.authinfo.recv_keyid, + SCTP_SO_NOT_LOCKED); /* compute a new recv assoc key and cache it */ if (stcb->asoc.authinfo.recv_key != NULL) sctp_free_key(stcb->asoc.authinfo.recv_key); @@ -1801,7 +1904,11 @@ sctp_handle_auth(struct sctp_tcb *stcb, */ void sctp_notify_authentication(struct sctp_tcb *stcb, uint32_t indication, - uint16_t keyid, uint16_t alt_keyid) + uint16_t keyid, uint16_t alt_keyid, int so_locked +#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) + SCTP_UNUSED +#endif +) { struct mbuf *m_notify; struct sctp_authkey_event *auth; @@ -1851,11 +1958,11 @@ sctp_notify_authentication(struct sctp_t /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, - &stcb->sctp_socket->so_rcv, 1, SCTP_SO_NOT_LOCKED); + &stcb->sctp_socket->so_rcv, 1, so_locked); } -/* +/*- * validates the AUTHentication related parameters in an INIT/INIT-ACK * Note: currently only used for INIT as INIT-ACK is handled inline * with sctp_load_addresses_from_init() @@ -2027,7 +2134,11 @@ sctp_initialize_auth_params(struct sctp_ } } /* copy defaults from the endpoint */ - stcb->asoc.authinfo.assoc_keyid = inp->sctp_ep.default_keyid; + stcb->asoc.authinfo.active_keyid = inp->sctp_ep.default_keyid; + + /* copy out the shared key list (by reference) from the endpoint */ + (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys, + &stcb->asoc.shared_keys); /* now set the concatenated key (random + chunks + hmacs) */ #ifdef SCTP_AUTH_DRAFT_04 @@ -2135,11 +2246,13 @@ sctp_test_hmac_sha1(void) uint32_t digestlen = 20; int failed = 0; - /* - * test_case = 1 key = - * 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b key_len = 20 - * data = "Hi There" data_len = 8 digest = - * 0xb617318655057264e28bc0b6fb378c8ef146be00 + /*- + * test_case = 1 + * key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + * key_len = 20 + * data = "Hi There" + * data_len = 8 + * digest = 0xb617318655057264e28bc0b6fb378c8ef146be00 */ keylen = 20; memset(key, 0x0b, keylen); @@ -2150,10 +2263,13 @@ sctp_test_hmac_sha1(void) text, textlen, digest, digestlen) < 0) failed++; - /* - * test_case = 2 key = "Jefe" key_len = 4 data = - * "what do ya want for nothing?" data_len = 28 digest = - * 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79 + /*- + * test_case = 2 + * key = "Jefe" + * key_len = 4 + * data = "what do ya want for nothing?" + * data_len = 28 + * digest = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79 */ keylen = 4; strcpy(key, "Jefe"); @@ -2164,11 +2280,13 @@ sctp_test_hmac_sha1(void) text, textlen, digest, digestlen) < 0) failed++; - /* - * test_case = 3 key = - * 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa key_len = 20 - * data = 0xdd repeated 50 times data_len = 50 digest - * = 0x125d7342b9ac11cd91a39af48aa17b4f63f175d3 + /*- + * test_case = 3 + * key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + * key_len = 20 + * data = 0xdd repeated 50 times + * data_len = 50 + * digest = 0x125d7342b9ac11cd91a39af48aa17b4f63f175d3 */ keylen = 20; memset(key, 0xaa, keylen); @@ -2179,11 +2297,13 @@ sctp_test_hmac_sha1(void) text, textlen, digest, digestlen) < 0) failed++; - /* - * test_case = 4 key = - * 0x0102030405060708090a0b0c0d0e0f10111213141516171819 key_len = 25 - * data = 0xcd repeated 50 times data_len = 50 digest - * = 0x4c9007f4026250c6bc8414f9bf50c86c2d7235da + /*- + * test_case = 4 + * key = 0x0102030405060708090a0b0c0d0e0f10111213141516171819 + * key_len = 25 + * data = 0xcd repeated 50 times + * data_len = 50 + * digest = 0x4c9007f4026250c6bc8414f9bf50c86c2d7235da */ keylen = 25; memcpy(key, "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", keylen); @@ -2194,12 +2314,14 @@ sctp_test_hmac_sha1(void) text, textlen, digest, digestlen) < 0) failed++; - /* - * test_case = 5 key = - * 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c key_len = 20 - * data = "Test With Truncation" data_len = 20 digest - * = 0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04 digest-96 = *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***