Author: tuexen
Date: Wed Jun 23 15:19:07 2010
New Revision: 209470
URL: http://svn.freebsd.org/changeset/base/209470

Log:
  * 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

Modified:
  head/sys/netinet/sctp_pcb.c

Modified: head/sys/netinet/sctp_pcb.c
==============================================================================
--- head/sys/netinet/sctp_pcb.c Wed Jun 23 14:28:08 2010        (r209469)
+++ head/sys/netinet/sctp_pcb.c Wed Jun 23 15:19:07 2010        (r209470)
@@ -1010,6 +1010,149 @@ sctp_tcb_special_locate(struct sctp_inpc
        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
                                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
                                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
        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,
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to