aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Graf <tgraf@infradead.org>2011-07-08 00:37:46 -0400
committerDavid S. Miller <davem@davemloft.net>2011-07-08 12:53:08 -0400
commitcd4fcc704f30f2064ab30b5300d44d431e46db50 (patch)
treecc08106433d2353adb5f7b52b7bfe6e4f9ebceb8
parent3f97fae9482dac1dbdd870a25c89033d3a0b35dc (diff)
sctp: ABORT if receive, reassmbly, or reodering queue is not empty while closing socket
Trigger user ABORT if application closes a socket which has data queued on the socket receive queue or chunks waiting on the reassembly or ordering queue as this would imply data being lost which defeats the point of a graceful shutdown. This behavior is already practiced in TCP. We do not check the input queue because that would mean to parse all chunks on it to look for unacknowledged data which seems too much of an effort. Control chunks or duplicated chunks may also be in the input queue and should not be stopping a graceful shutdown. Signed-off-by: Thomas Graf <tgraf@infradead.org> Acked-by: Vlad Yasevich <vladislav.yasevich@hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sctp/ulpevent.h2
-rw-r--r--net/sctp/socket.c13
-rw-r--r--net/sctp/ulpevent.c16
3 files changed, 22 insertions, 9 deletions
diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h
index 99b027b2adce..ca4693b4e09e 100644
--- a/include/net/sctp/ulpevent.h
+++ b/include/net/sctp/ulpevent.h
@@ -80,7 +80,7 @@ static inline struct sctp_ulpevent *sctp_skb2event(struct sk_buff *skb)
80 80
81void sctp_ulpevent_free(struct sctp_ulpevent *); 81void sctp_ulpevent_free(struct sctp_ulpevent *);
82int sctp_ulpevent_is_notification(const struct sctp_ulpevent *); 82int sctp_ulpevent_is_notification(const struct sctp_ulpevent *);
83void sctp_queue_purge_ulpevents(struct sk_buff_head *list); 83unsigned int sctp_queue_purge_ulpevents(struct sk_buff_head *list);
84 84
85struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( 85struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
86 const struct sctp_association *asoc, 86 const struct sctp_association *asoc,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 08c6238802de..d3ccf7973c59 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1384,6 +1384,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
1384 struct sctp_endpoint *ep; 1384 struct sctp_endpoint *ep;
1385 struct sctp_association *asoc; 1385 struct sctp_association *asoc;
1386 struct list_head *pos, *temp; 1386 struct list_head *pos, *temp;
1387 unsigned int data_was_unread;
1387 1388
1388 SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout); 1389 SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout);
1389 1390
@@ -1393,6 +1394,10 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
1393 1394
1394 ep = sctp_sk(sk)->ep; 1395 ep = sctp_sk(sk)->ep;
1395 1396
1397 /* Clean up any skbs sitting on the receive queue. */
1398 data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
1399 data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
1400
1396 /* Walk all associations on an endpoint. */ 1401 /* Walk all associations on an endpoint. */
1397 list_for_each_safe(pos, temp, &ep->asocs) { 1402 list_for_each_safe(pos, temp, &ep->asocs) {
1398 asoc = list_entry(pos, struct sctp_association, asocs); 1403 asoc = list_entry(pos, struct sctp_association, asocs);
@@ -1410,7 +1415,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
1410 } 1415 }
1411 } 1416 }
1412 1417
1413 if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 1418 if (data_was_unread || !skb_queue_empty(&asoc->ulpq.lobby) ||
1419 !skb_queue_empty(&asoc->ulpq.reasm) ||
1420 (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) {
1414 struct sctp_chunk *chunk; 1421 struct sctp_chunk *chunk;
1415 1422
1416 chunk = sctp_make_abort_user(asoc, NULL, 0); 1423 chunk = sctp_make_abort_user(asoc, NULL, 0);
@@ -1420,10 +1427,6 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
1420 sctp_primitive_SHUTDOWN(asoc, NULL); 1427 sctp_primitive_SHUTDOWN(asoc, NULL);
1421 } 1428 }
1422 1429
1423 /* Clean up any skbs sitting on the receive queue. */
1424 sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
1425 sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
1426
1427 /* On a TCP-style socket, block for at most linger_time if set. */ 1430 /* On a TCP-style socket, block for at most linger_time if set. */
1428 if (sctp_style(sk, TCP) && timeout) 1431 if (sctp_style(sk, TCP) && timeout)
1429 sctp_wait_for_close(sk, timeout); 1432 sctp_wait_for_close(sk, timeout);
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index e70e5fc87890..8a84017834c2 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -1081,9 +1081,19 @@ void sctp_ulpevent_free(struct sctp_ulpevent *event)
1081} 1081}
1082 1082
1083/* Purge the skb lists holding ulpevents. */ 1083/* Purge the skb lists holding ulpevents. */
1084void sctp_queue_purge_ulpevents(struct sk_buff_head *list) 1084unsigned int sctp_queue_purge_ulpevents(struct sk_buff_head *list)
1085{ 1085{
1086 struct sk_buff *skb; 1086 struct sk_buff *skb;
1087 while ((skb = skb_dequeue(list)) != NULL) 1087 unsigned int data_unread = 0;
1088 sctp_ulpevent_free(sctp_skb2event(skb)); 1088
1089 while ((skb = skb_dequeue(list)) != NULL) {
1090 struct sctp_ulpevent *event = sctp_skb2event(skb);
1091
1092 if (!sctp_ulpevent_is_notification(event))
1093 data_unread += skb->len;
1094
1095 sctp_ulpevent_free(event);
1096 }
1097
1098 return data_unread;
1089} 1099}