From 91e46c89d10a80ec1efcf2457a87088b2a3d8da3 Mon Sep 17 00:00:00 2001 From: tuexen Date: Wed, 23 Jun 2010 15:19:07 +0000 Subject: * 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 --- sys/netinet/sctp_pcb.c | 208 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 151 insertions(+), 57 deletions(-) (limited to 'sys') diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 07495f4..01c641b 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -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, -- cgit v1.1