Implement correct handling of address parameter and

sendinfo for SCTP send calls.

MFC after: 4 weeks.
This commit is contained in:
Michael Tuexen 2010-09-05 20:13:07 +00:00
parent d57429e2c2
commit 049640c1f0
3 changed files with 100 additions and 146 deletions

View File

@ -2381,7 +2381,6 @@ sctp_generic_sendmsg (td, uap)
struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
struct socket *so;
struct file *fp = NULL;
int use_rcvinfo = 1;
int error = 0, len;
struct sockaddr *to = NULL;
#ifdef KTRACE
@ -2434,7 +2433,7 @@ sctp_generic_sendmsg (td, uap)
CURVNET_SET(so->so_vnet);
error = sctp_lower_sosend(so, to, &auio,
(struct mbuf *)NULL, (struct mbuf *)NULL,
uap->flags, use_rcvinfo, u_sinfo, td);
uap->flags, u_sinfo, td);
CURVNET_RESTORE();
if (error) {
if (auio.uio_resid != len && (error == ERESTART ||
@ -2485,7 +2484,6 @@ sctp_generic_sendmsg_iov(td, uap)
struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
struct socket *so;
struct file *fp = NULL;
int use_rcvinfo = 1;
int error=0, len, i;
struct sockaddr *to = NULL;
#ifdef KTRACE
@ -2552,7 +2550,7 @@ sctp_generic_sendmsg_iov(td, uap)
CURVNET_SET(so->so_vnet);
error = sctp_lower_sosend(so, to, &auio,
(struct mbuf *)NULL, (struct mbuf *)NULL,
uap->flags, use_rcvinfo, u_sinfo, td);
uap->flags, u_sinfo, td);
CURVNET_RESTORE();
if (error) {
if (auio.uio_resid != len && (error == ERESTART ||

View File

@ -12158,7 +12158,7 @@ sctp_sosend(struct socket *so,
error = sctp_lower_sosend(so, addr_to_use, uio, top,
control,
flags,
use_rcvinfo, &srcv
use_rcvinfo ? &srcv : NULL
,p
);
return (error);
@ -12172,7 +12172,6 @@ sctp_lower_sosend(struct socket *so,
struct mbuf *i_pak,
struct mbuf *control,
int flags,
int use_rcvinfo,
struct sctp_sndrcvinfo *srcv
,
struct thread *p
@ -12200,8 +12199,10 @@ sctp_lower_sosend(struct socket *so,
int got_all_of_the_send = 0;
int hold_tcblock = 0;
int non_blocking = 0;
int temp_flags = 0;
uint32_t local_add_more, local_soresv = 0;
uint16_t port;
uint16_t sinfo_flags;
sctp_assoc_t sinfo_assoc_id;
error = 0;
net = NULL;
@ -12236,38 +12237,6 @@ sctp_lower_sosend(struct socket *so,
SCTPDBG(SCTP_DEBUG_OUTPUT1, "Send called addr:%p send length %d\n",
addr,
sndlen);
/**
* Pre-screen address, if one is given the sin-len
* must be set correctly!
*/
if (addr) {
switch (addr->sa_family) {
#if defined(INET)
case AF_INET:
if (addr->sa_len != sizeof(struct sockaddr_in)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
break;
#endif
#if defined(INET6)
case AF_INET6:
if (addr->sa_len != sizeof(struct sockaddr_in6)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
break;
#endif
default:
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAFNOSUPPORT);
error = EAFNOSUPPORT;
goto out_unlocked;
}
}
hold_tcblock = 0;
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
(inp->sctp_socket->so_qlimit)) {
/* The listener can NOT send */
@ -12275,22 +12244,67 @@ sctp_lower_sosend(struct socket *so,
error = ENOTCONN;
goto out_unlocked;
}
if ((use_rcvinfo) && srcv) {
if (INVALID_SINFO_FLAG(srcv->sinfo_flags) ||
PR_SCTP_INVALID_POLICY(srcv->sinfo_flags)) {
/**
* Pre-screen address, if one is given the sin-len
* must be set correctly!
*/
if (addr) {
union sctp_sockstore *raddr = (union sctp_sockstore *)addr;
switch (raddr->sa.sa_family) {
#if defined(INET)
case AF_INET:
if (raddr->sin.sin_len != sizeof(struct sockaddr_in)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
port = raddr->sin.sin_port;
break;
#endif
#if defined(INET6)
case AF_INET6:
if (raddr->sin6.sin6_len != sizeof(struct sockaddr_in6)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
port = raddr->sin6.sin6_port;
break;
#endif
default:
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAFNOSUPPORT);
error = EAFNOSUPPORT;
goto out_unlocked;
}
} else
port = 0;
if (srcv) {
sinfo_flags = srcv->sinfo_flags;
sinfo_assoc_id = srcv->sinfo_assoc_id;
if (INVALID_SINFO_FLAG(sinfo_flags) ||
PR_SCTP_INVALID_POLICY(sinfo_flags)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
if (srcv->sinfo_flags)
SCTP_STAT_INCR(sctps_sends_with_flags);
if (srcv->sinfo_flags & SCTP_SENDALL) {
/* its a sendall */
error = sctp_sendall(inp, uio, top, srcv);
top = NULL;
goto out_unlocked;
}
} else {
sinfo_flags = inp->def_send.sinfo_flags;
sinfo_assoc_id = inp->def_send.sinfo_assoc_id;
}
if (sinfo_flags & SCTP_SENDALL) {
/* its a sendall */
error = sctp_sendall(inp, uio, top, srcv);
top = NULL;
goto out_unlocked;
}
if ((sinfo_flags & SCTP_ADDR_OVER) && (addr == NULL)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
/* now we must find the assoc */
if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) ||
@ -12306,80 +12320,8 @@ sctp_lower_sosend(struct socket *so,
SCTP_TCB_LOCK(stcb);
hold_tcblock = 1;
SCTP_INP_RUNLOCK(inp);
if (addr) {
/* Must locate the net structure if addr given */
net = sctp_findnet(stcb, addr);
if (net) {
/* validate port was 0 or correct */
struct sockaddr_in *sin;
sin = (struct sockaddr_in *)addr;
if ((sin->sin_port != 0) &&
(sin->sin_port != stcb->rport)) {
net = NULL;
}
}
temp_flags |= SCTP_ADDR_OVER;
} else
net = stcb->asoc.primary_destination;
if (addr && (net == NULL)) {
/* Could not find address, was it legal */
if (addr->sa_family == AF_INET) {
struct sockaddr_in *sin;
sin = (struct sockaddr_in *)addr;
if (sin->sin_addr.s_addr == 0) {
if ((sin->sin_port == 0) ||
(sin->sin_port == stcb->rport)) {
net = stcb->asoc.primary_destination;
}
}
} else {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
if ((sin6->sin6_port == 0) ||
(sin6->sin6_port == stcb->rport)) {
net = stcb->asoc.primary_destination;
}
}
}
}
if (net == NULL) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
} else if (use_rcvinfo && srcv && srcv->sinfo_assoc_id) {
stcb = sctp_findassociation_ep_asocid(inp, srcv->sinfo_assoc_id, 0);
if (stcb) {
if (addr)
/*
* Must locate the net structure if addr
* given
*/
net = sctp_findnet(stcb, addr);
else
net = stcb->asoc.primary_destination;
if ((srcv->sinfo_flags & SCTP_ADDR_OVER) &&
((net == NULL) || (addr == NULL))) {
struct sockaddr_in *sin;
if (addr == NULL) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
sin = (struct sockaddr_in *)addr;
/* Validate port is 0 or correct */
if ((sin->sin_port != 0) &&
(sin->sin_port != stcb->rport)) {
net = NULL;
}
}
}
hold_tcblock = 0;
} else if (sinfo_assoc_id) {
stcb = sctp_findassociation_ep_asocid(inp, sinfo_assoc_id, 0);
} else if (addr) {
/*-
* Since we did not use findep we must
@ -12452,10 +12394,8 @@ sctp_lower_sosend(struct socket *so,
*/
uint32_t vrf_id;
if ((use_rcvinfo) && (srcv) &&
((srcv->sinfo_flags & SCTP_ABORT) ||
((srcv->sinfo_flags & SCTP_EOF) &&
(sndlen == 0)))) {
if ((sinfo_flags & SCTP_ABORT) ||
((sinfo_flags & SCTP_EOF) && (sndlen == 0))) {
/*-
* User asks to abort a non-existant assoc,
* or EOF a non-existant assoc with no data
@ -12584,10 +12524,25 @@ sctp_lower_sosend(struct socket *so,
* structure may now have an update and thus we may need to
* change it BEFORE we append the message.
*/
net = stcb->asoc.primary_destination;
asoc = &stcb->asoc;
}
}
if (srcv == NULL)
srcv = (struct sctp_sndrcvinfo *)&stcb->asoc.def_send;
if (srcv->sinfo_flags & SCTP_ADDR_OVER) {
if (addr)
net = sctp_findnet(stcb, addr);
else
net = NULL;
if ((net == NULL) ||
((port != 0) && (port != stcb->rport))) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
} else {
net = stcb->asoc.primary_destination;
}
if ((SCTP_SO_IS_NBIO(so)
|| (flags & MSG_NBIO)
)) {
@ -12658,10 +12613,6 @@ sctp_lower_sosend(struct socket *so,
(SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED)) {
queue_only = 1;
}
if ((use_rcvinfo == 0) || (srcv == NULL)) {
/* Grab the default stuff from the asoc */
srcv = (struct sctp_sndrcvinfo *)&stcb->asoc.def_send;
}
/* we are now done with all control */
if (control) {
sctp_m_freem(control);
@ -12671,8 +12622,7 @@ sctp_lower_sosend(struct socket *so,
(SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
(SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
(asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
if ((use_rcvinfo) &&
(srcv->sinfo_flags & SCTP_ABORT)) {
if (srcv->sinfo_flags & SCTP_ABORT) {
;
} else {
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
@ -12684,16 +12634,6 @@ sctp_lower_sosend(struct socket *so,
if (p) {
p->td_ru.ru_msgsnd++;
}
if (stcb) {
if (((srcv->sinfo_flags | temp_flags) & SCTP_ADDR_OVER) == 0) {
net = stcb->asoc.primary_destination;
}
}
if (net == NULL) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
if ((net->flight_size > net->cwnd) &&
(asoc->sctp_cmt_on_off == 0)) {
/*-

View File

@ -982,6 +982,23 @@ union sctp_sockstore {
struct sockaddr sa;
};
/***********************************/
/* And something for us old timers */
/***********************************/
#ifndef ntohll
#include <sys/endian.h>
#define ntohll(x) be64toh(x)
#endif
#ifndef htonll
#include <sys/endian.h>
#define htonll(x) htobe64(x)
#endif
/***********************************/
struct xsctp_inpcb {
uint32_t last;
uint32_t flags;
@ -1079,7 +1096,6 @@ sctp_lower_sosend(struct socket *so,
struct mbuf *i_pak,
struct mbuf *control,
int flags,
int use_rcvinfo,
struct sctp_sndrcvinfo *srcv
,struct thread *p
);