* Implement sctp_does_stcb_own_this_addr() correclty. It was taking the

wrong side into account.
* sctp_findassociation_ep_addr() must check the local address if available.
This fixes a bug where ABORT chunks were accepted even in the case where
the local was not owned by the endpoint.
Thanks to brucec for pointing out a bug in my first version of the fix.
MFC after: 3 days
This commit is contained in:
tuexen 2010-06-23 15:19:07 +00:00
parent 9dd957535b
commit 91e46c89d1

View File

@ -1010,6 +1010,149 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
return (NULL);
}
static int
sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
{
int loopback_scope, ipv4_local_scope, local_scope, site_scope;
int ipv4_addr_legal, ipv6_addr_legal;
struct sctp_vrf *vrf;
struct sctp_ifn *sctp_ifn;
struct sctp_ifa *sctp_ifa;
loopback_scope = stcb->asoc.loopback_scope;
ipv4_local_scope = stcb->asoc.ipv4_local_scope;
local_scope = stcb->asoc.local_scope;
site_scope = stcb->asoc.site_scope;
ipv4_addr_legal = ipv6_addr_legal = 0;
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
ipv6_addr_legal = 1;
if (SCTP_IPV6_V6ONLY(stcb->sctp_ep) == 0) {
ipv4_addr_legal = 1;
}
} else {
ipv4_addr_legal = 1;
}
SCTP_IPI_ADDR_RLOCK();
vrf = sctp_find_vrf(stcb->asoc.vrf_id);
if (vrf == NULL) {
/* no vrf, no addresses */
SCTP_IPI_ADDR_RUNLOCK();
return (0);
}
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
if ((loopback_scope == 0) &&
SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
continue;
}
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
if (sctp_is_addr_restricted(stcb, sctp_ifa))
continue;
switch (sctp_ifa->address.sa.sa_family) {
#ifdef INET
case AF_INET:
if (ipv4_addr_legal) {
struct sockaddr_in *sin,
*rsin;
sin = &sctp_ifa->address.sin;
rsin = (struct sockaddr_in *)to;
if ((ipv4_local_scope == 0) &&
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
continue;
}
if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
SCTP_IPI_ADDR_RUNLOCK();
return (1);
}
}
break;
#endif
#ifdef INET6
case AF_INET6:
if (ipv6_addr_legal) {
struct sockaddr_in6 *sin6,
*rsin6;
sin6 = &sctp_ifa->address.sin6;
rsin6 = (struct sockaddr_in6 *)to;
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
if (local_scope == 0)
continue;
if (sin6->sin6_scope_id == 0) {
if (sa6_recoverscope(sin6) != 0)
continue;
}
}
if ((site_scope == 0) &&
(IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
continue;
}
if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) {
SCTP_IPI_ADDR_RUNLOCK();
return (1);
}
}
break;
#endif
default:
/* TSNH */
break;
}
}
}
} else {
struct sctp_laddr *laddr;
LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) {
if (sctp_is_addr_restricted(stcb, laddr->ifa)) {
continue;
}
if (laddr->ifa->address.sa.sa_family != to->sa_family) {
continue;
}
switch (to->sa_family) {
#ifdef INET
case AF_INET:
{
struct sockaddr_in *sin, *rsin;
sin = (struct sockaddr_in *)&laddr->ifa->address.sin;
rsin = (struct sockaddr_in *)to;
if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
SCTP_IPI_ADDR_RUNLOCK();
return (1);
}
break;
}
#endif
#ifdef INET6
case AF_INET6:
{
struct sockaddr_in6 *sin6, *rsin6;
sin6 = (struct sockaddr_in6 *)&laddr->ifa->address.sin6;
rsin6 = (struct sockaddr_in6 *)to;
if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) {
SCTP_IPI_ADDR_RUNLOCK();
return (1);
}
break;
}
#endif
default:
/* TSNH */
break;
}
}
}
SCTP_IPI_ADDR_RUNLOCK();
return (0);
}
/*
* rules for use
*
@ -1090,6 +1233,10 @@ sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote,
SCTP_TCB_UNLOCK(stcb);
goto null_return;
}
if (!(local && sctp_does_stcb_own_this_addr(stcb, local))) {
SCTP_TCB_UNLOCK(stcb);
goto null_return;
}
/* now look at the list of remote addresses */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
#ifdef INVARIANTS
@ -1187,6 +1334,10 @@ sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote,
SCTP_TCB_UNLOCK(stcb);
continue;
}
if (!(local && sctp_does_stcb_own_this_addr(stcb, local))) {
SCTP_TCB_UNLOCK(stcb);
continue;
}
/* now look at the list of remote addresses */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
#ifdef INVARIANTS
@ -1800,63 +1951,6 @@ sctp_findassociation_special_addr(struct mbuf *m, int iphlen, int offset,
return (NULL);
}
static int
sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
{
struct sctp_nets *net;
/*
* Simple question, the ports match, does the tcb own the to
* address?
*/
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)) {
/* of course */
return (1);
}
/* have to look at all bound addresses */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
if (net->ro._l_addr.sa.sa_family != to->sa_family) {
/* not the same family, can't be a match */
continue;
}
switch (to->sa_family) {
case AF_INET:
{
struct sockaddr_in *sin, *rsin;
sin = (struct sockaddr_in *)&net->ro._l_addr;
rsin = (struct sockaddr_in *)to;
if (sin->sin_addr.s_addr ==
rsin->sin_addr.s_addr) {
/* found it */
return (1);
}
break;
}
#ifdef INET6
case AF_INET6:
{
struct sockaddr_in6 *sin6, *rsin6;
sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
rsin6 = (struct sockaddr_in6 *)to;
if (SCTP6_ARE_ADDR_EQUAL(sin6,
rsin6)) {
/* Update the endpoint pointer */
return (1);
}
break;
}
#endif
default:
/* TSNH */
break;
}
}
/* Nope, do not have the address ;-( */
return (0);
}
static struct sctp_tcb *
sctp_findassoc_by_vtag(struct sockaddr *from, struct sockaddr *to, uint32_t vtag,
struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint16_t rport,