Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 6 Dec 2008 13:19:54 +0000 (UTC)
From:      Randall Stewart <rrs@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r185694 - in head/sys: netinet netinet6
Message-ID:  <200812061319.mB6DJs0g094953@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200812061319.mB6DJs0g094953>