summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authortuexen <tuexen@FreeBSD.org>2010-06-23 15:19:07 +0000
committertuexen <tuexen@FreeBSD.org>2010-06-23 15:19:07 +0000
commit91e46c89d10a80ec1efcf2457a87088b2a3d8da3 (patch)
tree6c12d24718e25745dbeaafc25ba767ff104bdc08 /sys
parent9dd957535b6e318b7f8a9d0b9f226a7eac01da6c (diff)
downloadFreeBSD-src-91e46c89d10a80ec1efcf2457a87088b2a3d8da3.zip
FreeBSD-src-91e46c89d10a80ec1efcf2457a87088b2a3d8da3.tar.gz
* 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
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/sctp_pcb.c208
1 files changed, 151 insertions, 57 deletions
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,
OpenPOWER on IntegriCloud