Author: rrs
Date: Thu Feb 12 18:50:27 2009
New Revision: 188532
URL: http://svn.freebsd.org/changeset/base/188532

Log:
  MFC of 185694 - The IETF hack session:
  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 :-)

Modified:
  stable/7/sys/   (props changed)
  stable/7/sys/netinet/sctp.h
  stable/7/sys/netinet/sctp_asconf.c
  stable/7/sys/netinet/sctp_asconf.h
  stable/7/sys/netinet/sctp_auth.c
  stable/7/sys/netinet/sctp_auth.h
  stable/7/sys/netinet/sctp_constants.h
  stable/7/sys/netinet/sctp_header.h
  stable/7/sys/netinet/sctp_indata.c
  stable/7/sys/netinet/sctp_indata.h
  stable/7/sys/netinet/sctp_input.c
  stable/7/sys/netinet/sctp_os_bsd.h
  stable/7/sys/netinet/sctp_output.c
  stable/7/sys/netinet/sctp_output.h
  stable/7/sys/netinet/sctp_pcb.c
  stable/7/sys/netinet/sctp_pcb.h
  stable/7/sys/netinet/sctp_structs.h
  stable/7/sys/netinet/sctp_sysctl.c
  stable/7/sys/netinet/sctp_sysctl.h
  stable/7/sys/netinet/sctp_timer.c
  stable/7/sys/netinet/sctp_uio.h
  stable/7/sys/netinet/sctp_usrreq.c
  stable/7/sys/netinet/sctp_var.h
  stable/7/sys/netinet/sctputil.c
  stable/7/sys/netinet/sctputil.h
  stable/7/sys/netinet6/sctp6_usrreq.c

Modified: stable/7/sys/netinet/sctp.h
==============================================================================
--- stable/7/sys/netinet/sctp.h Thu Feb 12 18:33:56 2009        (r188531)
+++ stable/7/sys/netinet/sctp.h Thu Feb 12 18:50:27 2009        (r188532)
@@ -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: stable/7/sys/netinet/sctp_asconf.c
==============================================================================
--- stable/7/sys/netinet/sctp_asconf.c  Thu Feb 12 18:33:56 2009        
(r188531)
+++ stable/7/sys/netinet/sctp_asconf.c  Thu Feb 12 18:50:27 2009        
(r188532)
@@ -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: stable/7/sys/netinet/sctp_asconf.h
==============================================================================
--- stable/7/sys/netinet/sctp_asconf.h  Thu Feb 12 18:33:56 2009        
(r188531)
+++ stable/7/sys/netinet/sctp_asconf.h  Thu Feb 12 18:50:27 2009        
(r188532)
@@ -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: stable/7/sys/netinet/sctp_auth.c
==============================================================================
--- stable/7/sys/netinet/sctp_auth.c    Thu Feb 12 18:33:56 2009        
(r188531)
+++ stable/7/sys/netinet/sctp_auth.c    Thu Feb 12 18:50:27 2009        
(r188532)
@@ -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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to