Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 17 Jan 2010 21:00:29 +0000 (UTC)
From:      Michael Tuexen <tuexen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r202526 - head/sys/netinet
Message-ID:  <201001172100.o0HL0TFM044459@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tuexen
Date: Sun Jan 17 21:00:28 2010
New Revision: 202526
URL: http://svn.freebsd.org/changeset/base/202526

Log:
  Get rid of a lot of duplicated code for NR-SACK handle.
  Generalize the SACK to code handle also NR-SACKs.

Modified:
  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_output.c
  head/sys/netinet/sctp_structs.h

Modified: head/sys/netinet/sctp_header.h
==============================================================================
--- head/sys/netinet/sctp_header.h	Sun Jan 17 20:49:34 2010	(r202525)
+++ head/sys/netinet/sctp_header.h	Sun Jan 17 21:00:28 2010	(r202526)
@@ -284,13 +284,6 @@ struct sctp_sack_chunk {
 	struct sctp_sack sack;
 }               SCTP_PACKED;
 
-
-/* EY Following 3 structs define NR Selective Ack (NR_SACK) chunk */
-struct sctp_nr_gap_ack_block {
-	uint16_t start;		/* NR Gap Ack block start */
-	uint16_t end;		/* NR Gap Ack block end */
-}                     SCTP_PACKED;
-
 struct sctp_nr_sack {
 	uint32_t cum_tsn_ack;	/* cumulative TSN Ack */
 	uint32_t a_rwnd;	/* updated a_rwnd of sender */
@@ -299,7 +292,6 @@ struct sctp_nr_sack {
 	uint16_t num_dup_tsns;	/* number of duplicate TSNs */
 	uint16_t reserved;	/* not currently used */
 	/* struct sctp_gap_ack_block's follow */
-	/* struct sctp_nr_gap_ack_block's follow */
 	/* uint32_t duplicate_tsn's follow */
 }            SCTP_PACKED;
 

Modified: head/sys/netinet/sctp_indata.c
==============================================================================
--- head/sys/netinet/sctp_indata.c	Sun Jan 17 20:49:34 2010	(r202525)
+++ head/sys/netinet/sctp_indata.c	Sun Jan 17 21:00:28 2010	(r202526)
@@ -439,12 +439,11 @@ abandon:
 		 * EY!-TODO- this tsn should be tagged nr only if it is
 		 * out-of-order, the if statement should be modified
 		 */
-		if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
-
+		if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+		    asoc->peer_supports_nr_sack) {
 			nr_tsn = chk->rec.data.TSN_seq;
 			SCTP_CALC_TSN_TO_GAP(nr_gap, nr_tsn, asoc->nr_mapping_array_base_tsn);
-			if ((nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3)) ||
-			    (nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
+			if ((nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
 				/*
 				 * EY The 1st should never happen, as in
 				 * process_a_data_chunk method this check
@@ -525,11 +524,11 @@ abandon:
 						 * nr_gap and tag this tsn
 						 * NR
 						 */
-						if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+						if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+						    asoc->peer_supports_nr_sack) {
 							SCTP_CALC_TSN_TO_GAP(nr_gap, nr_tsn, asoc->nr_mapping_array_base_tsn);
 							if ((nr_gap >= (SCTP_NR_MAPPING_ARRAY << 3)) ||
 							    (nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
-								printf("Impossible NR gap calculation?\n");
 								/*
 								 * EY The
 								 * 1st
@@ -694,7 +693,8 @@ protocol_error:
 		 * calculate the gap and such then tag this TSN nr
 		 * chk->rec.data.TSN_seq
 		 */
-		if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+		if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+		    asoc->peer_supports_nr_sack) {
 			SCTP_CALC_TSN_TO_GAP(nr_gap, nr_tsn, asoc->nr_mapping_array_base_tsn);
 			if ((nr_gap >= (SCTP_NR_MAPPING_ARRAY << 3)) ||
 			    (nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
@@ -750,11 +750,11 @@ protocol_error:
 				 * such then tag this TSN nr
 				 * chk->rec.data.TSN_seq
 				 */
-				if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+				if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+				    asoc->peer_supports_nr_sack) {
 					SCTP_CALC_TSN_TO_GAP(nr_gap, nr_tsn, asoc->nr_mapping_array_base_tsn);
 					if ((nr_gap >= (SCTP_NR_MAPPING_ARRAY << 3)) ||
 					    (nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
-						printf("Impossible nr TSN set 3?\n");
 						/*
 						 * EY The 1st should never
 						 * happen, as in
@@ -773,7 +773,8 @@ protocol_error:
 						SCTP_TCB_LOCK_ASSERT(stcb);
 						SCTP_REVERSE_OUT_TSN_PRES(nr_gap, nr_tsn, asoc);
 						SCTP_SET_TSN_PRESENT(asoc->nr_mapping_array, nr_gap);
-						if (compare_with_wrap(nr_tsn, asoc->highest_tsn_inside_nr_map,
+						if (compare_with_wrap(nr_tsn,
+						    asoc->highest_tsn_inside_nr_map,
 						    MAX_TSN))
 							asoc->highest_tsn_inside_nr_map = nr_tsn;
 					}
@@ -1759,7 +1760,8 @@ sctp_process_a_data_chunk(struct sctp_tc
 		SCTP_TCB_LOCK_ASSERT(stcb);
 		SCTP_SET_TSN_PRESENT(asoc->mapping_array, gap);
 		/* EY set this tsn present in  nr_sack's nr_mapping_array */
-		if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+		if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+		    asoc->peer_supports_nr_sack) {
 			SCTP_TCB_LOCK_ASSERT(stcb);
 			SCTP_SET_TSN_PRESENT(asoc->nr_mapping_array, gap);
 			SCTP_REVERSE_OUT_TSN_PRES(gap, tsn, asoc);
@@ -2015,7 +2017,8 @@ failed_express_del:
 			 * block here I should check if this delivered tsn
 			 * is out_of_order, if yes then update the nr_map
 			 */
-			if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+			if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+			    asoc->peer_supports_nr_sack) {
 				/* EY debugging block */
 				{
 					/*
@@ -2260,13 +2263,13 @@ failed_pdapi_express_del:
 			sctp_add_to_readq(stcb->sctp_ep, stcb,
 			    control,
 			    &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED);
-
 			/*
 			 * EY It is added to the read queue in prev if block
 			 * here I should check if this delivered tsn is
 			 * out_of_order, if yes then update the nr_map
 			 */
-			if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+			if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+			    asoc->peer_supports_nr_sack) {
 				/*
 				 * EY check if the mapping_array and
 				 * nr_mapping array are consistent
@@ -2412,6 +2415,10 @@ finish_express_del:
 	SCTP_TCB_LOCK_ASSERT(stcb);
 	SCTP_SET_TSN_PRESENT(asoc->mapping_array, gap);
 
+	/*
+	 * EY - set tsn present in nr-map if  doing nr-sacks and the tsn is
+	 * non-renegable
+	 */
 	if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
 	    asoc->peer_supports_nr_sack &&
 	    (SCTP_BASE_SYSCTL(sctp_do_drain) == 0)) {
@@ -3416,49 +3423,37 @@ sctp_process_segment_range(struct sctp_t
 }
 
 
-static void
+static int
 sctp_handle_segments(struct mbuf *m, int *offset, struct sctp_tcb *stcb, struct sctp_association *asoc,
-    struct sctp_sack_chunk *ch, uint32_t last_tsn, uint32_t * biggest_tsn_acked,
+    uint32_t last_tsn, uint32_t * biggest_tsn_acked,
     uint32_t * biggest_newly_acked_tsn, uint32_t * this_sack_lowest_newack,
-    int num_seg, int *ecn_seg_sums)
+    int num_seg, int num_nr_seg, int *ecn_seg_sums)
 {
-	/************************************************/
-	/* process fragments and update sendqueue        */
-	/************************************************/
-	struct sctp_sack *sack;
 	struct sctp_gap_ack_block *frag, block;
 	struct sctp_tmit_chunk *tp1;
 	int i;
 	int num_frs = 0;
+	int chunk_freed;
+	int non_revocable;
+	uint16_t frag_strt, frag_end;
+	uint32_t last_frag_high;
 
-	uint16_t frag_strt, frag_end, primary_flag_set;
-	u_long last_frag_high;
-
-	/*
-	 * @@@ JRI : TODO: This flag is not used anywhere .. remove?
-	 */
-	if (asoc->primary_destination->dest_state & SCTP_ADDR_SWITCH_PRIMARY) {
-		primary_flag_set = 1;
-	} else {
-		primary_flag_set = 0;
-	}
-	sack = &ch->sack;
-
-	frag = (struct sctp_gap_ack_block *)sctp_m_getptr(m, *offset,
-	    sizeof(struct sctp_gap_ack_block), (uint8_t *) & block);
-	*offset += sizeof(block);
-	if (frag == NULL) {
-		return;
-	}
 	tp1 = NULL;
 	last_frag_high = 0;
-	for (i = 0; i < num_seg; i++) {
+	chunk_freed = 0;
+
+	for (i = 0; i < (num_seg + num_nr_seg); i++) {
+		frag = (struct sctp_gap_ack_block *)sctp_m_getptr(m, *offset,
+		    sizeof(struct sctp_gap_ack_block), (uint8_t *) & block);
+		*offset += sizeof(block);
+		if (frag == NULL) {
+			return (chunk_freed);
+		}
 		frag_strt = ntohs(frag->start);
 		frag_end = ntohs(frag->end);
 		/* some sanity checks on the fragment offsets */
 		if (frag_strt > frag_end) {
 			/* this one is malformed, skip */
-			frag++;
 			continue;
 		}
 		if (compare_with_wrap((frag_end + last_tsn), *biggest_tsn_acked,
@@ -3468,7 +3463,6 @@ sctp_handle_segments(struct mbuf *m, int
 		/* mark acked dgs and find out the highestTSN being acked */
 		if (tp1 == NULL) {
 			tp1 = TAILQ_FIRST(&asoc->sent_queue);
-
 			/* save the locations of the last frags */
 			last_frag_high = frag_end + last_tsn;
 		} else {
@@ -3496,14 +3490,15 @@ sctp_handle_segments(struct mbuf *m, int
 			}
 			last_frag_high = frag_end + last_tsn;
 		}
-		sctp_process_segment_range(stcb, &tp1, last_tsn, frag_strt, frag_end,
-		    0, &num_frs, biggest_newly_acked_tsn,
-		    this_sack_lowest_newack, ecn_seg_sums);
-		frag = (struct sctp_gap_ack_block *)sctp_m_getptr(m, *offset,
-		    sizeof(struct sctp_gap_ack_block), (uint8_t *) & block);
-		*offset += sizeof(block);
-		if (frag == NULL) {
-			break;
+		if (i < num_seg) {
+			non_revocable = 0;
+		} else {
+			non_revocable = 1;
+		}
+		if (sctp_process_segment_range(stcb, &tp1, last_tsn, frag_strt, frag_end,
+		    non_revocable, &num_frs, biggest_newly_acked_tsn,
+		    this_sack_lowest_newack, ecn_seg_sums)) {
+			chunk_freed = 1;
 		}
 	}
 	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
@@ -3512,6 +3507,7 @@ sctp_handle_segments(struct mbuf *m, int
 			    *biggest_newly_acked_tsn,
 			    last_tsn, SCTP_FR_LOG_BIGGEST_TSNS);
 	}
+	return (chunk_freed);
 }
 
 static void
@@ -4450,7 +4446,6 @@ sctp_express_handle_sack(struct sctp_tcb
 		struct socket *so;
 
 #endif
-
 		SOCKBUF_LOCK(&stcb->sctp_socket->so_snd);
 		if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_WAKE_LOGGING_ENABLE) {
 			/* sa_ignore NO_NULL_CHK */
@@ -4525,10 +4520,10 @@ sctp_express_handle_sack(struct sctp_tcb
 			if (compare_with_wrap(asoc->last_acked_seq, asoc->nonce_resync_tsn, MAX_TSN)) {
 				asoc->nonce_sum_check = 1;
 				/*
-				 * now we must calculate what the base is.
+				 * Now we must calculate what the base is.
 				 * We do this based on two things, we know
 				 * the total's for all the segments
-				 * gap-acked in the SACK (none), We also
+				 * gap-acked in the SACK (none). We also
 				 * know the SACK's nonce sum, its in
 				 * nonce_sum_flag. So we can build a truth
 				 * table to back-calculate the new value of
@@ -4567,6 +4562,7 @@ again:
 			/* sa_ignore FREED_MEMORY */
 			TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
 				if (tp1->window_probe) {
+					/* move back to data send queue */
 					sctp_window_probe_recovery(stcb, asoc, net, tp1);
 					break;
 				}
@@ -4620,9 +4616,7 @@ again:
 		 */
 		if (sctp_fs_audit(asoc)) {
 			TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-				if (net->flight_size) {
-					net->flight_size = 0;
-				}
+				net->flight_size = 0;
 			}
 			asoc->total_flight = 0;
 			asoc->total_flight_count = 0;
@@ -4782,20 +4776,62 @@ again:
 	}
 }
 
+/* EY- nr_sack */
+/* Identifies the non-renegable tsns that are revoked*/
+static void
+sctp_check_for_nr_revoked(struct sctp_tcb *stcb,
+    struct sctp_association *asoc, uint32_t cumack,
+    u_long biggest_tsn_acked)
+{
+	struct sctp_tmit_chunk *tp1;
+
+	for (tp1 = TAILQ_FIRST(&asoc->sent_queue); tp1; tp1 = TAILQ_NEXT(tp1, sctp_next)) {
+		if (compare_with_wrap(tp1->rec.data.TSN_seq, cumack,
+		    MAX_TSN)) {
+			/*
+			 * ok this guy is either ACK or MARKED. If it is
+			 * ACKED it has been previously acked but not this
+			 * time i.e. revoked.  If it is MARKED it was ACK'ed
+			 * again.
+			 */
+			if (compare_with_wrap(tp1->rec.data.TSN_seq, biggest_tsn_acked,
+			    MAX_TSN))
+				break;
+
+
+			if (tp1->sent == SCTP_DATAGRAM_NR_ACKED) {
+				/*
+				 * EY! a non-renegable TSN is revoked, need
+				 * to abort the association
+				 */
+				/*
+				 * EY TODO: put in the code to abort the
+				 * assoc.
+				 */
+				return;
+			} else if (tp1->sent == SCTP_DATAGRAM_NR_MARKED) {
+				/* it has been re-acked in this SACK */
+				tp1->sent = SCTP_DATAGRAM_NR_ACKED;
+			}
+		}
+		if (tp1->sent == SCTP_DATAGRAM_UNSENT)
+			break;
+	}
+	return;
+}
+
 void
-sctp_handle_sack(struct mbuf *m, int offset,
-    struct sctp_sack_chunk *ch, struct sctp_tcb *stcb,
-    struct sctp_nets *net_from, int *abort_now, int sack_len, uint32_t rwnd)
+sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup,
+    struct sctp_tcb *stcb, struct sctp_nets *net_from,
+    uint16_t num_seg, uint16_t num_nr_seg, uint16_t num_dup,
+    int *abort_now, uint8_t flags,
+    uint32_t cum_ack, uint32_t rwnd)
 {
 	struct sctp_association *asoc;
-	struct sctp_sack *sack;
 	struct sctp_tmit_chunk *tp1, *tp2;
-	uint32_t cum_ack, last_tsn, biggest_tsn_acked, biggest_tsn_newly_acked,
-	         this_sack_lowest_newack;
+	uint32_t last_tsn, biggest_tsn_acked, biggest_tsn_newly_acked, this_sack_lowest_newack;
 	uint32_t sav_cum_ack;
-	uint16_t num_seg, num_dup;
 	uint16_t wake_him = 0;
-	unsigned int sack_length;
 	uint32_t send_s = 0;
 	long j;
 	int accum_moved = 0;
@@ -4831,15 +4867,13 @@ sctp_handle_sack(struct mbuf *m, int off
 	 * if in shutdown_recv state.
 	 */
 	SCTP_TCB_LOCK_ASSERT(stcb);
-	sack = &ch->sack;
 	/* CMT DAC algo */
 	this_sack_lowest_newack = 0;
 	j = 0;
-	sack_length = (unsigned int)sack_len;
-	/* ECN Nonce */
 	SCTP_STAT_INCR(sctps_slowpath_sack);
-	nonce_sum_flag = ch->ch.chunk_flags & SCTP_SACK_NONCE_SUM;
-	cum_ack = last_tsn = ntohl(sack->cum_tsn_ack);
+	last_tsn = cum_ack;
+	nonce_sum_flag = flags & SCTP_SACK_NONCE_SUM;
+	cmt_dac_flag = flags & SCTP_SACK_CMT_DAC;
 #ifdef SCTP_ASOCLOG_OF_TSNS
 	stcb->asoc.cumack_log[stcb->asoc.cumack_log_at] = cum_ack;
 	stcb->asoc.cumack_log_at++;
@@ -4847,13 +4881,12 @@ sctp_handle_sack(struct mbuf *m, int off
 		stcb->asoc.cumack_log_at = 0;
 	}
 #endif
-	num_seg = ntohs(sack->num_gap_ack_blks);
 	a_rwnd = rwnd;
 
-	/* CMT DAC algo */
-	cmt_dac_flag = ch->ch.chunk_flags & SCTP_SACK_CMT_DAC;
-	num_dup = ntohs(sack->num_dup_tsns);
-
+	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_SACK_ARRIVALS_ENABLE) {
+		sctp_misc_ints(SCTP_SACK_LOG_NORMAL, cum_ack,
+		    rwnd, stcb->asoc.last_acked_seq, stcb->asoc.peers_rwnd);
+	}
 	old_rwnd = stcb->asoc.peers_rwnd;
 	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) {
 		sctp_misc_ints(SCTP_THRESHOLD_CLEAR,
@@ -4873,27 +4906,16 @@ sctp_handle_sack(struct mbuf *m, int off
 		    SCTP_LOG_NEW_SACK);
 	}
 	if ((num_dup) && (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_FR_LOGGING_ENABLE | SCTP_EARLYFR_LOGGING_ENABLE))) {
-		int off_to_dup, iii;
+		uint16_t i;
 		uint32_t *dupdata, dblock;
 
-		off_to_dup = (num_seg * sizeof(struct sctp_gap_ack_block)) + sizeof(struct sctp_sack_chunk);
-		if ((off_to_dup + (num_dup * sizeof(uint32_t))) <= sack_length) {
-			dupdata = (uint32_t *) sctp_m_getptr(m, off_to_dup,
+		for (i = 0; i < num_dup; i++) {
+			dupdata = (uint32_t *) sctp_m_getptr(m, offset_dup + i * sizeof(uint32_t),
 			    sizeof(uint32_t), (uint8_t *) & dblock);
-			off_to_dup += sizeof(uint32_t);
-			if (dupdata) {
-				for (iii = 0; iii < num_dup; iii++) {
-					sctp_log_fr(*dupdata, 0, 0, SCTP_FR_DUPED);
-					dupdata = (uint32_t *) sctp_m_getptr(m, off_to_dup,
-					    sizeof(uint32_t), (uint8_t *) & dblock);
-					if (dupdata == NULL)
-						break;
-					off_to_dup += sizeof(uint32_t);
-				}
+			if (dupdata == NULL) {
+				break;
 			}
-		} else {
-			SCTP_PRINTF("Size invalid offset to dups:%d number dups:%d sack_len:%d num gaps:%d\n",
-			    off_to_dup, num_dup, sack_length, num_seg);
+			sctp_log_fr(*dupdata, 0, 0, SCTP_FR_DUPED);
 		}
 	}
 	if (SCTP_BASE_SYSCTL(sctp_strict_sacks)) {
@@ -4915,8 +4937,6 @@ sctp_handle_sack(struct mbuf *m, int off
 	hopeless_peer:
 			panic("Impossible sack 1");
 #else
-
-
 			/*
 			 * no way, we have not even sent this TSN out yet.
 			 * Peer is hopelessly messed up with us.
@@ -4956,8 +4976,7 @@ sctp_handle_sack(struct mbuf *m, int off
 	/* update the Rwnd of the peer */
 	if (TAILQ_EMPTY(&asoc->sent_queue) &&
 	    TAILQ_EMPTY(&asoc->send_queue) &&
-	    (asoc->stream_queue_cnt == 0)
-	    ) {
+	    (asoc->stream_queue_cnt == 0)) {
 		/* nothing left on send/sent and strmq */
 		if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_RWND_ENABLE) {
 			sctp_log_rwnd_set(SCTP_SET_PEER_RWND_VIA_SACK,
@@ -5128,14 +5147,7 @@ sctp_handle_sack(struct mbuf *m, int off
 	/* always set this up to cum-ack */
 	asoc->this_sack_highest_gap = last_tsn;
 
-	/* Move offset up to point to gaps/dups */
-	offset += sizeof(struct sctp_sack_chunk);
-	if (((num_seg * (sizeof(struct sctp_gap_ack_block))) + sizeof(struct sctp_sack_chunk)) > sack_length) {
-
-		/* skip corrupt segments */
-		goto skip_segments;
-	}
-	if (num_seg > 0) {
+	if ((num_seg > 0) || (num_nr_seg > 0)) {
 
 		/*
 		 * CMT: SFR algo (and HTNA) - this_sack_highest_newack has
@@ -5153,10 +5165,11 @@ sctp_handle_sack(struct mbuf *m, int off
 		 * handling NEWLY ACKED chunks. this_sack_lowest_newack is
 		 * used for CMT DAC algo. saw_newack will also change.
 		 */
-		sctp_handle_segments(m, &offset, stcb, asoc, ch, last_tsn,
-		    &biggest_tsn_acked, &biggest_tsn_newly_acked, &this_sack_lowest_newack,
-		    num_seg, &ecn_seg_sums);
-
+		if (sctp_handle_segments(m, &offset_seg, stcb, asoc, last_tsn, &biggest_tsn_acked,
+		    &biggest_tsn_newly_acked, &this_sack_lowest_newack,
+		    num_seg, num_nr_seg, &ecn_seg_sums)) {
+			wake_him++;
+		}
 		if (SCTP_BASE_SYSCTL(sctp_strict_sacks)) {
 			/*
 			 * validate the biggest_tsn_acked in the gap acks if
@@ -5172,7 +5185,6 @@ sctp_handle_sack(struct mbuf *m, int off
 			}
 		}
 	}
-skip_segments:
 	/*******************************************/
 	/* cancel ALL T3-send timer if accum moved */
 	/*******************************************/
@@ -5310,6 +5322,19 @@ done_with_it:
 		if (tp1 != NULL) {
 			/* Peer revoked all dg's marked or acked */
 			TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
+				/*
+				 * EY- maybe check only if it is nr_acked
+				 * nr_marked may not be possible
+				 */
+				if ((tp1->sent == SCTP_DATAGRAM_NR_ACKED) ||
+				    (tp1->sent == SCTP_DATAGRAM_NR_MARKED)) {
+					/*
+					 * EY! - TODO: Something previously
+					 * nr_gapped is reneged, abort the
+					 * association
+					 */
+					return;
+				}
 				if ((tp1->sent > SCTP_DATAGRAM_RESEND) &&
 				    (tp1->sent < SCTP_FORWARD_TSN_SKIP)) {
 					tp1->sent = SCTP_DATAGRAM_SENT;
@@ -5345,6 +5370,10 @@ done_with_it:
 	else
 		asoc->saw_sack_with_frags = 0;
 
+	/* EY! - not sure about if there should be an IF */
+	if (num_nr_seg > 0)
+		sctp_check_for_nr_revoked(stcb, asoc, cum_ack, biggest_tsn_acked);
+
 	/* JRS - Use the congestion control given in the CC module */
 	asoc->cc_functions.sctp_cwnd_update_after_sack(stcb, asoc, accum_moved, reneged_all, will_exit_fast_recovery);
 
@@ -5480,7 +5509,7 @@ done_with_it:
 	if (SCTP_BASE_SYSCTL(sctp_cmt_on_off) && SCTP_BASE_SYSCTL(sctp_cmt_use_dac) && (cmt_dac_flag == 0)) {
 		this_sack_lowest_newack = cum_ack;
 	}
-	if (num_seg > 0) {
+	if ((num_seg > 0) || (num_nr_seg > 0)) {
 		sctp_strike_gap_ack_chunks(stcb, asoc, biggest_tsn_acked,
 		    biggest_tsn_newly_acked, this_sack_lowest_newack, accum_moved);
 	}
@@ -5603,9 +5632,12 @@ again:
 		}
 		if (net->flight_size) {
 			j++;
-			sctp_timer_start(SCTP_TIMER_TYPE_SEND,
-			    stcb->sctp_ep, stcb, net);
+			if (!SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND,
+				    stcb->sctp_ep, stcb, net);
+			}
 			if (net->window_probe) {
+				net->window_probe = 0;
 			}
 		} else {
 			if (net->window_probe) {
@@ -5613,7 +5645,6 @@ again:
 				 * In window probes we must assure a timer
 				 * is still running there
 				 */
-
 				if (!SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
 					sctp_timer_start(SCTP_TIMER_TYPE_SEND,
 					    stcb->sctp_ep, stcb, net);
@@ -5661,7 +5692,11 @@ again:
 		done_once = 1;
 		goto again;
 	}
-	/* Fix up the a-p-a-p for future PR-SCTP sends */
+	/*********************************************/
+	/* Here we perform PR-SCTP procedures        */
+	/* (section 4.2)                             */
+	/*********************************************/
+	/* C1. update advancedPeerAckPoint */
 	if (compare_with_wrap(cum_ack, asoc->advanced_peer_ack_point, MAX_TSN)) {
 		asoc->advanced_peer_ack_point = cum_ack;
 	}
@@ -5773,8 +5808,8 @@ sctp_kick_prsctp_reorder_queue(struct sc
 				 * such then tag this TSN nr
 				 * chk->rec.data.TSN_seq
 				 */
-				if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
-
+				if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+				    asoc->peer_supports_nr_sack) {
 					SCTP_CALC_TSN_TO_GAP(nr_gap, nr_tsn, asoc->nr_mapping_array_base_tsn);
 					if ((nr_gap >= (SCTP_NR_MAPPING_ARRAY << 3)) ||
 					    (nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
@@ -5874,7 +5909,8 @@ sctp_kick_prsctp_reorder_queue(struct sc
 				 * such then tag this TSN nr
 				 * chk->rec.data.TSN_seq
 				 */
-				if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && asoc->peer_supports_nr_sack) {
+				if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) &&
+				    asoc->peer_supports_nr_sack) {
 					SCTP_CALC_TSN_TO_GAP(nr_gap, nr_tsn, asoc->nr_mapping_array_base_tsn);
 					if ((nr_gap >= (SCTP_NR_MAPPING_ARRAY << 3)) ||
 					    (nr_gap >= (uint32_t) (asoc->nr_mapping_array_size << 3))) {
@@ -6359,1743 +6395,3 @@ sctp_handle_forward_tsn(struct sctp_tcb 
 		sctp_deliver_reasm_check(stcb, &stcb->asoc);
 	}
 }
-
-/* EY fully identical to sctp_express_handle_sack, duplicated for only naming convention */
-void
-sctp_express_handle_nr_sack(struct sctp_tcb *stcb, uint32_t cumack,
-    uint32_t rwnd, int nonce_sum_flag, int *abort_now)
-{
-	struct sctp_nets *net;
-	struct sctp_association *asoc;
-	struct sctp_tmit_chunk *tp1, *tp2;
-	uint32_t old_rwnd;
-	int win_probe_recovery = 0;
-	int win_probe_recovered = 0;
-	int j, done_once = 0;
-
-	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_SACK_ARRIVALS_ENABLE) {
-		sctp_misc_ints(SCTP_SACK_LOG_EXPRESS, cumack,
-		    rwnd, stcb->asoc.last_acked_seq, stcb->asoc.peers_rwnd);
-	}
-	SCTP_TCB_LOCK_ASSERT(stcb);
-#ifdef SCTP_ASOCLOG_OF_TSNS
-	stcb->asoc.cumack_log[stcb->asoc.cumack_log_at] = cumack;
-	stcb->asoc.cumack_log_at++;
-	if (stcb->asoc.cumack_log_at > SCTP_TSN_LOG_SIZE) {
-		stcb->asoc.cumack_log_at = 0;
-	}
-#endif
-	asoc = &stcb->asoc;
-	old_rwnd = asoc->peers_rwnd;
-	if (compare_with_wrap(asoc->last_acked_seq, cumack, MAX_TSN)) {
-		/* old ack */
-		return;
-	} else if (asoc->last_acked_seq == cumack) {
-		/* Window update sack */
-		asoc->peers_rwnd = sctp_sbspace_sub(rwnd,
-		    (uint32_t) (asoc->total_flight + (asoc->sent_queue_cnt * SCTP_BASE_SYSCTL(sctp_peer_chunk_oh))));
-		if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
-			/* SWS sender side engages */
-			asoc->peers_rwnd = 0;
-		}
-		if (asoc->peers_rwnd > old_rwnd) {
-			goto again;
-		}
-		return;
-	}
-	/* First setup for CC stuff */
-	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-		net->prev_cwnd = net->cwnd;
-		net->net_ack = 0;
-		net->net_ack2 = 0;
-
-		/*
-		 * CMT: Reset CUC and Fast recovery algo variables before
-		 * SACK processing
-		 */
-		net->new_pseudo_cumack = 0;
-		net->will_exit_fast_recovery = 0;
-	}
-	if (SCTP_BASE_SYSCTL(sctp_strict_sacks)) {
-		uint32_t send_s;
-
-		if (!TAILQ_EMPTY(&asoc->sent_queue)) {
-			tp1 = TAILQ_LAST(&asoc->sent_queue,
-			    sctpchunk_listhead);
-			send_s = tp1->rec.data.TSN_seq + 1;
-		} else {
-			send_s = asoc->sending_seq;
-		}
-		if ((cumack == send_s) ||
-		    compare_with_wrap(cumack, send_s, MAX_TSN)) {
-#ifndef INVARIANTS
-			struct mbuf *oper;
-
-#endif
-#ifdef INVARIANTS
-			panic("Impossible sack 1");
-#else
-			*abort_now = 1;
-			/* XXX */
-			oper = sctp_get_mbuf_for_msg((sizeof(struct sctp_paramhdr) + sizeof(uint32_t)),
-			    0, M_DONTWAIT, 1, MT_DATA);
-			if (oper) {
-				struct sctp_paramhdr *ph;
-				uint32_t *ippp;
-
-				SCTP_BUF_LEN(oper) = sizeof(struct sctp_paramhdr) +
-				    sizeof(uint32_t);
-				ph = mtod(oper, struct sctp_paramhdr *);
-				ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
-				ph->param_length = htons(SCTP_BUF_LEN(oper));
-				ippp = (uint32_t *) (ph + 1);
-				*ippp = htonl(SCTP_FROM_SCTP_INDATA + SCTP_LOC_25);
-			}
-			stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_25;
-			sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_PEER_FAULTY, oper, SCTP_SO_NOT_LOCKED);
-			return;
-#endif
-		}
-	}
-	asoc->this_sack_highest_gap = cumack;
-	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) {
-		sctp_misc_ints(SCTP_THRESHOLD_CLEAR,
-		    stcb->asoc.overall_error_count,
-		    0,
-		    SCTP_FROM_SCTP_INDATA,
-		    __LINE__);
-	}
-	stcb->asoc.overall_error_count = 0;
-	if (compare_with_wrap(cumack, asoc->last_acked_seq, MAX_TSN)) {
-		/* process the new consecutive TSN first */
-		tp1 = TAILQ_FIRST(&asoc->sent_queue);
-		while (tp1) {
-			tp2 = TAILQ_NEXT(tp1, sctp_next);
-			if (compare_with_wrap(cumack, tp1->rec.data.TSN_seq,
-			    MAX_TSN) ||
-			    cumack == tp1->rec.data.TSN_seq) {
-				if (tp1->sent == SCTP_DATAGRAM_UNSENT) {
-					printf("Warning, an unsent is now acked?\n");
-				}
-				/*
-				 * ECN Nonce: Add the nonce to the sender's
-				 * nonce sum
-				 */
-				asoc->nonce_sum_expect_base += tp1->rec.data.ect_nonce;
-				if (tp1->sent < SCTP_DATAGRAM_ACKED) {
-					/*
-					 * If it is less than ACKED, it is
-					 * now no-longer in flight. Higher
-					 * values may occur during marking
-					 */
-					if (tp1->sent < SCTP_DATAGRAM_RESEND) {
-						if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) {
-							sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_CA,
-							    tp1->whoTo->flight_size,
-							    tp1->book_size,
-							    (uintptr_t) tp1->whoTo,
-							    tp1->rec.data.TSN_seq);
-						}
-						sctp_flight_size_decrease(tp1);
-						/* sa_ignore NO_NULL_CHK */
-						sctp_total_flight_decrease(stcb, tp1);
-					}
-					tp1->whoTo->net_ack += tp1->send_size;
-					if (tp1->snd_count < 2) {
-						/*
-						 * True non-retransmited
-						 * chunk
-						 */
-						tp1->whoTo->net_ack2 +=
-						    tp1->send_size;
-
-						/* update RTO too? */
-						if (tp1->do_rtt) {
-							tp1->whoTo->RTO =
-							/*
-							 * sa_ignore
-							 * NO_NULL_CHK
-							 */
-							    sctp_calculate_rto(stcb,
-							    asoc, tp1->whoTo,
-							    &tp1->sent_rcv_time,
-							    sctp_align_safe_nocopy);
-							tp1->do_rtt = 0;
-						}
-					}
-					/*
-					 * CMT: CUCv2 algorithm. From the
-					 * cumack'd TSNs, for each TSN being
-					 * acked for the first time, set the
-					 * following variables for the
-					 * corresp destination.
-					 * new_pseudo_cumack will trigger a
-					 * cwnd update.
-					 * find_(rtx_)pseudo_cumack will
-					 * trigger search for the next
-					 * expected (rtx-)pseudo-cumack.
-					 */
-					tp1->whoTo->new_pseudo_cumack = 1;
-					tp1->whoTo->find_pseudo_cumack = 1;
-					tp1->whoTo->find_rtx_pseudo_cumack = 1;
-
-					if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) {
-						/* sa_ignore NO_NULL_CHK */
-						sctp_log_cwnd(stcb, tp1->whoTo, tp1->rec.data.TSN_seq, SCTP_CWND_LOG_FROM_SACK);
-					}
-				}
-				if (tp1->sent == SCTP_DATAGRAM_RESEND) {
-					sctp_ucount_decr(asoc->sent_queue_retran_cnt);
-				}
-				if (tp1->rec.data.chunk_was_revoked) {
-					/* deflate the cwnd */
-					tp1->whoTo->cwnd -= tp1->book_size;
-					tp1->rec.data.chunk_was_revoked = 0;
-				}
-				tp1->sent = SCTP_DATAGRAM_ACKED;
-				TAILQ_REMOVE(&asoc->sent_queue, tp1, sctp_next);
-				if (tp1->data) {
-					/* sa_ignore NO_NULL_CHK */
-					sctp_free_bufspace(stcb, asoc, tp1, 1);
-					sctp_m_freem(tp1->data);
-				}
-				if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SACK_LOGGING_ENABLE) {
-					sctp_log_sack(asoc->last_acked_seq,
-					    cumack,
-					    tp1->rec.data.TSN_seq,
-					    0,
-					    0,
-					    SCTP_LOG_FREE_SENT);
-				}
-				tp1->data = NULL;
-				asoc->sent_queue_cnt--;
-				sctp_free_a_chunk(stcb, tp1);
-				tp1 = tp2;
-			} else {
-				break;
-			}
-		}
-
-	}
-	/* sa_ignore NO_NULL_CHK */
-	if (stcb->sctp_socket) {
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-		struct socket *so;
-
-#endif
-
-		SOCKBUF_LOCK(&stcb->sctp_socket->so_snd);
-		if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_WAKE_LOGGING_ENABLE) {
-			/* sa_ignore NO_NULL_CHK */
-			sctp_wakeup_log(stcb, cumack, 1, SCTP_WAKESND_FROM_SACK);
-		}
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-		so = SCTP_INP_SO(stcb->sctp_ep);
-		atomic_add_int(&stcb->asoc.refcnt, 1);
-		SCTP_TCB_UNLOCK(stcb);
-		SCTP_SOCKET_LOCK(so, 1);
-		SCTP_TCB_LOCK(stcb);
-		atomic_subtract_int(&stcb->asoc.refcnt, 1);
-		if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) {
-			/* assoc was freed while we were unlocked */
-			SCTP_SOCKET_UNLOCK(so, 1);
-			return;
-		}
-#endif
-		sctp_sowwakeup_locked(stcb->sctp_ep, stcb->sctp_socket);
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-		SCTP_SOCKET_UNLOCK(so, 1);
-#endif
-	} else {
-		if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_WAKE_LOGGING_ENABLE) {
-			sctp_wakeup_log(stcb, cumack, 1, SCTP_NOWAKE_FROM_SACK);
-		}
-	}
-
-	/* JRS - Use the congestion control given in the CC module */
-	if (asoc->last_acked_seq != cumack)
-		asoc->cc_functions.sctp_cwnd_update_after_sack(stcb, asoc, 1, 0, 0);
-
-	asoc->last_acked_seq = cumack;
-
-	if (TAILQ_EMPTY(&asoc->sent_queue)) {
-		/* nothing left in-flight */
-		TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-			net->flight_size = 0;
-			net->partial_bytes_acked = 0;
-		}
-		asoc->total_flight = 0;
-		asoc->total_flight_count = 0;
-	}
-	/* Fix up the a-p-a-p for future PR-SCTP sends */
-	if (compare_with_wrap(cumack, asoc->advanced_peer_ack_point, MAX_TSN)) {
-		asoc->advanced_peer_ack_point = cumack;
-	}
-	/* ECN Nonce updates */
-	if (asoc->ecn_nonce_allowed) {
-		if (asoc->nonce_sum_check) {
-			if (nonce_sum_flag != ((asoc->nonce_sum_expect_base) & SCTP_SACK_NONCE_SUM)) {
-				if (asoc->nonce_wait_for_ecne == 0) {
-					struct sctp_tmit_chunk *lchk;
-
-					lchk = TAILQ_FIRST(&asoc->send_queue);
-					asoc->nonce_wait_for_ecne = 1;
-					if (lchk) {
-						asoc->nonce_wait_tsn = lchk->rec.data.TSN_seq;
-					} else {
-						asoc->nonce_wait_tsn = asoc->sending_seq;
-					}
-				} else {
-					if (compare_with_wrap(asoc->last_acked_seq, asoc->nonce_wait_tsn, MAX_TSN) ||
-					    (asoc->last_acked_seq == asoc->nonce_wait_tsn)) {
-						/*
-						 * Misbehaving peer. We need
-						 * to react to this guy
-						 */
-						asoc->ecn_allowed = 0;
-						asoc->ecn_nonce_allowed = 0;
-					}
-				}
-			}
-		} else {
-			/* See if Resynchronization Possible */
-			if (compare_with_wrap(asoc->last_acked_seq, asoc->nonce_resync_tsn, MAX_TSN)) {
-				asoc->nonce_sum_check = 1;
-				/*
-				 * now we must calculate what the base is.
-				 * We do this based on two things, we know
-				 * the total's for all the segments
-				 * gap-acked in the SACK (none), We also
-				 * know the SACK's nonce sum, its in
-				 * nonce_sum_flag. So we can build a truth
-				 * table to back-calculate the new value of
-				 * asoc->nonce_sum_expect_base:
-				 * 
-				 * SACK-flag-Value         Seg-Sums Base 0 0 0
-				 * 1                    0 1 0 1 1 1 1 0
-				 */
-				asoc->nonce_sum_expect_base = (0 ^ nonce_sum_flag) & SCTP_SACK_NONCE_SUM;
-			}
-		}
-	}
-	/* RWND update */
-	asoc->peers_rwnd = sctp_sbspace_sub(rwnd,
-	    (uint32_t) (asoc->total_flight + (asoc->sent_queue_cnt * SCTP_BASE_SYSCTL(sctp_peer_chunk_oh))));
-	if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
-		/* SWS sender side engages */
-		asoc->peers_rwnd = 0;
-	}
-	if (asoc->peers_rwnd > old_rwnd) {
-		win_probe_recovery = 1;
-	}
-	/* Now assure a timer where data is queued at */
-again:
-	j = 0;
-	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-		int to_ticks;
-
-		if (win_probe_recovery && (net->window_probe)) {
-			win_probe_recovered = 1;
-			/*
-			 * Find first chunk that was used with window probe
-			 * and clear the sent
-			 */
-			/* sa_ignore FREED_MEMORY */
-			TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
-				if (tp1->window_probe) {
-					/* move back to data send queue */
-					sctp_window_probe_recovery(stcb, asoc, net, tp1);
-					break;
-				}
-			}
-		}
-		if (net->RTO == 0) {
-			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
-		} else {
-			to_ticks = MSEC_TO_TICKS(net->RTO);
-		}
-		if (net->flight_size) {
-
-			j++;
-			(void)SCTP_OS_TIMER_START(&net->rxt_timer.timer, to_ticks,
-			    sctp_timeout_handler, &net->rxt_timer);
-			if (net->window_probe) {
-				net->window_probe = 0;
-			}
-		} else {
-			if (net->window_probe) {
-				/*
-				 * In window probes we must assure a timer
-				 * is still running there
-				 */
-				net->window_probe = 0;
-				(void)SCTP_OS_TIMER_START(&net->rxt_timer.timer, to_ticks,
-				    sctp_timeout_handler, &net->rxt_timer);
-			} else if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
-				sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
-				    stcb, net,
-				    SCTP_FROM_SCTP_INDATA + SCTP_LOC_22);
-			}
-			if (SCTP_BASE_SYSCTL(sctp_early_fr)) {
-				if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) {
-					SCTP_STAT_INCR(sctps_earlyfrstpidsck4);
-					sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net,
-					    SCTP_FROM_SCTP_INDATA + SCTP_LOC_23);
-				}
-			}
-		}
-	}
-	if ((j == 0) &&
-	    (!TAILQ_EMPTY(&asoc->sent_queue)) &&
-	    (asoc->sent_queue_retran_cnt == 0) &&
-	    (win_probe_recovered == 0) &&
-	    (done_once == 0)) {
-		/*
-		 * huh, this should not happen unless all packets are
-		 * PR-SCTP and marked to skip of course.
-		 */
-		if (sctp_fs_audit(asoc)) {
-			TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-				net->flight_size = 0;
-			}
-			asoc->total_flight = 0;
-			asoc->total_flight_count = 0;
-			asoc->sent_queue_retran_cnt = 0;
-			TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
-				if (tp1->sent < SCTP_DATAGRAM_RESEND) {
-					sctp_flight_size_increase(tp1);
-					sctp_total_flight_increase(stcb, tp1);
-				} else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
-					asoc->sent_queue_retran_cnt++;
-				}
-			}
-		}
-		done_once = 1;
-		goto again;
-	}
-	/**********************************/

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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