* 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:
parent
9dd957535b
commit
91e46c89d1
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user