aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Tuexen <tuexen@FreeBSD.org>2012-05-13 22:27:54 +0000
committerMichael Tuexen <tuexen@FreeBSD.org>2012-05-13 22:27:54 +0000
commit389b1b118cc93d1fefa92c5340145737dabc6c53 (patch)
tree8eeaadc880398332a0539160f5de5970baf94f1d
parent1edc9dbae5dff7375b0f4a8735c671983f4478c4 (diff)
downloadsrc-389b1b118cc93d1fefa92c5340145737dabc6c53.tar.gz
src-389b1b118cc93d1fefa92c5340145737dabc6c53.zip
Support SCTP_REMOTE_ERROR notification.
MFC after: 3 days
Notes
Notes: svn path=/head/; revision=235418
-rw-r--r--sys/netinet/sctp_constants.h1
-rw-r--r--sys/netinet/sctp_input.c8
-rw-r--r--sys/netinet/sctputil.c60
3 files changed, 68 insertions, 1 deletions
diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h
index 5ad0bd96a835..c183f1c1984c 100644
--- a/sys/netinet/sctp_constants.h
+++ b/sys/netinet/sctp_constants.h
@@ -765,6 +765,7 @@ __FBSDID("$FreeBSD$");
#define SCTP_NOTIFY_AUTH_FREE_KEY 24
#define SCTP_NOTIFY_NO_PEER_AUTH 25
#define SCTP_NOTIFY_SENDER_DRY 26
+#define SCTP_NOTIFY_REMOTE_ERROR 27
/* This is the value for messages that are NOT completely
* copied down where we will start to split the message.
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 49a0b2773495..755fb44eb1ae 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -1111,7 +1111,7 @@ sctp_handle_error(struct sctp_chunkhdr *ch,
{
int chklen;
struct sctp_paramhdr *phdr;
- uint16_t error_type;
+ uint16_t error, error_type;
uint16_t error_len;
struct sctp_association *asoc;
int adjust;
@@ -1126,6 +1126,7 @@ sctp_handle_error(struct sctp_chunkhdr *ch,
phdr = (struct sctp_paramhdr *)((caddr_t)ch +
sizeof(struct sctp_chunkhdr));
chklen = ntohs(ch->chunk_length) - sizeof(struct sctp_chunkhdr);
+ error = 0;
while ((size_t)chklen >= sizeof(struct sctp_paramhdr)) {
/* Process an Error Cause */
error_type = ntohs(phdr->param_type);
@@ -1136,6 +1137,10 @@ sctp_handle_error(struct sctp_chunkhdr *ch,
chklen, error_len);
return (0);
}
+ if (error == 0) {
+ /* report the first error cause */
+ error = error_type;
+ }
switch (error_type) {
case SCTP_CAUSE_INVALID_STREAM:
case SCTP_CAUSE_MISSING_PARAM:
@@ -1252,6 +1257,7 @@ sctp_handle_error(struct sctp_chunkhdr *ch,
chklen -= adjust;
phdr = (struct sctp_paramhdr *)((caddr_t)phdr + adjust);
}
+ sctp_ulp_notify(SCTP_NOTIFY_REMOTE_ERROR, stcb, error, ch, SCTP_SO_NOT_LOCKED);
return (0);
}
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index 3e05e304b75f..9eed40975ea8 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -3464,6 +3464,63 @@ sctp_notify_stream_reset(struct sctp_tcb *stcb,
}
+static void
+sctp_notify_remote_error(struct sctp_tcb *stcb, uint16_t error, struct sctp_error_chunk *chunk)
+{
+ struct mbuf *m_notify;
+ struct sctp_remote_error *sre;
+ struct sctp_queued_to_read *control;
+ size_t notif_len, chunk_len;
+
+ if ((stcb == NULL) ||
+ sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVPEERERR)) {
+ return;
+ }
+ if (chunk != NULL) {
+ chunk_len = htons(chunk->ch.chunk_length);
+ } else {
+ chunk_len = 0;
+ }
+ notif_len = sizeof(struct sctp_remote_error) + chunk_len;
+ m_notify = sctp_get_mbuf_for_msg(notif_len, 0, M_DONTWAIT, 1, MT_DATA);
+ if (m_notify == NULL) {
+ /* Retry with smaller value. */
+ notif_len = sizeof(struct sctp_remote_error);
+ m_notify = sctp_get_mbuf_for_msg(notif_len, 0, M_DONTWAIT, 1, MT_DATA);
+ if (m_notify == NULL) {
+ return;
+ }
+ }
+ SCTP_BUF_NEXT(m_notify) = NULL;
+ sre = mtod(m_notify, struct sctp_remote_error *);
+ sre->sre_type = SCTP_REMOTE_ERROR;
+ sre->sre_flags = 0;
+ sre->sre_length = sizeof(struct sctp_remote_error);
+ sre->sre_error = error;
+ sre->sre_assoc_id = sctp_get_associd(stcb);
+ if (notif_len > sizeof(struct sctp_remote_error)) {
+ memcpy(sre->sre_data, chunk, chunk_len);
+ sre->sre_length += chunk_len;
+ }
+ SCTP_BUF_LEN(m_notify) = sre->sre_length;
+ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
+ 0, 0, stcb->asoc.context, 0, 0, 0,
+ m_notify);
+ if (control != NULL) {
+ control->length = SCTP_BUF_LEN(m_notify);
+ /* not that we need this */
+ control->tail_mbuf = m_notify;
+ control->spec_flags = M_NOTIFICATION;
+ sctp_add_to_readq(stcb->sctp_ep, stcb,
+ control,
+ &stcb->sctp_socket->so_rcv, 1,
+ SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED);
+ } else {
+ sctp_m_freem(m_notify);
+ }
+}
+
+
void
sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
uint32_t error, void *data, int so_locked
@@ -3634,6 +3691,9 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
case SCTP_NOTIFY_SENDER_DRY:
sctp_notify_sender_dry_event(stcb, so_locked);
break;
+ case SCTP_NOTIFY_REMOTE_ERROR:
+ sctp_notify_remote_error(stcb, error, data);
+ break;
default:
SCTPDBG(SCTP_DEBUG_UTIL1, "%s: unknown notification %xh (%u)\n",
__FUNCTION__, notification, notification);