]> err.no Git - linux-2.6/blobdiff - net/sctp/input.c
Merge upstream 2.6.13-rc3 into ieee80211 branch of netdev-2.6.
[linux-2.6] / net / sctp / input.c
index b719a77d66b47e3a553672c1d712e8e53b93e99b..5e085e041a6ef1f5528c9eb5fbd85a198f9712b2 100644 (file)
@@ -115,6 +115,17 @@ static void sctp_rcv_set_owner_r(struct sk_buff *skb, struct sock *sk)
        atomic_add(sizeof(struct sctp_chunk),&sk->sk_rmem_alloc);
 }
 
+struct sctp_input_cb {
+       union {
+               struct inet_skb_parm    h4;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+               struct inet6_skb_parm   h6;
+#endif
+       } header;
+       struct sctp_chunk *chunk;
+};
+#define SCTP_INPUT_CB(__skb)   ((struct sctp_input_cb *)&((__skb)->cb[0]))
+
 /*
  * This is the routine which IP calls when receiving an SCTP packet.
  */
@@ -178,6 +189,37 @@ int sctp_rcv(struct sk_buff *skb)
 
        asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
 
+       if (!asoc)
+               ep = __sctp_rcv_lookup_endpoint(&dest);
+
+       /* Retrieve the common input handling substructure. */
+       rcvr = asoc ? &asoc->base : &ep->base;
+       sk = rcvr->sk;
+
+       /*
+        * If a frame arrives on an interface and the receiving socket is
+        * bound to another interface, via SO_BINDTODEVICE, treat it as OOTB
+        */
+       if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb)))
+       {
+               sock_put(sk);
+               if (asoc) {
+                       sctp_association_put(asoc);
+                       asoc = NULL;
+               } else {
+                       sctp_endpoint_put(ep);
+                       ep = NULL;
+               }
+               sk = sctp_get_ctl_sock();
+               ep = sctp_sk(sk)->ep;
+               sctp_endpoint_hold(ep);
+               sock_hold(sk);
+               rcvr = &ep->base;
+       }
+
+       if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
+               goto discard_release;
+
        /*
         * RFC 2960, 8.4 - Handle "Out of the blue" Packets.
         * An SCTP packet is called an "out of the blue" (OOTB)
@@ -187,22 +229,12 @@ int sctp_rcv(struct sk_buff *skb)
         * packet belongs.
         */
        if (!asoc) {
-               ep = __sctp_rcv_lookup_endpoint(&dest);
                if (sctp_rcv_ootb(skb)) {
                        SCTP_INC_STATS_BH(SCTP_MIB_OUTOFBLUES);
                        goto discard_release;
                }
        }
 
-       /* Retrieve the common input handling substructure. */
-       rcvr = asoc ? &asoc->base : &ep->base;
-       sk = rcvr->sk;
-
-       if ((sk) && (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)) {
-               goto discard_release;
-       }
-
-
        /* SCTP seems to always need a timestamp right now (FIXME) */
        if (skb->stamp.tv_sec == 0) {
                do_gettimeofday(&skb->stamp);
@@ -222,6 +254,7 @@ int sctp_rcv(struct sk_buff *skb)
                ret = -ENOMEM;
                goto discard_release;
        }
+       SCTP_INPUT_CB(skb)->chunk = chunk;
 
        sctp_rcv_set_owner_r(skb,sk);
 
@@ -244,9 +277,9 @@ int sctp_rcv(struct sk_buff *skb)
        sctp_bh_lock_sock(sk);
 
        if (sock_owned_by_user(sk))
-               sk_add_backlog(sk, (struct sk_buff *) chunk);
+               sk_add_backlog(sk, skb);
        else
-               sctp_backlog_rcv(sk, (struct sk_buff *) chunk);
+               sctp_backlog_rcv(sk, skb);
 
        /* Release the sock and any reference counts we took in the
         * lookup calls.
@@ -265,13 +298,11 @@ discard_it:
 
 discard_release:
        /* Release any structures we may be holding. */
-       if (asoc) {
-               sock_put(asoc->base.sk);
+       sock_put(sk);
+       if (asoc)
                sctp_association_put(asoc);
-       } else {
-               sock_put(ep->base.sk);
+       else
                sctp_endpoint_put(ep);
-       }
 
        goto discard_it;
 }
@@ -283,14 +314,8 @@ discard_release:
  */
 int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 {
-       struct sctp_chunk *chunk;
-       struct sctp_inq *inqueue;
-
-       /* One day chunk will live inside the skb, but for
-        * now this works.
-        */
-       chunk = (struct sctp_chunk *) skb;
-       inqueue = &chunk->rcvr->inqueue;
+       struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
+       struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
 
        sctp_inq_push(inqueue, chunk);
         return 0;
@@ -334,7 +359,7 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
 
        sctp_do_sm(SCTP_EVENT_T_OTHER,
                   SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
-                  asoc->state, asoc->ep, asoc, NULL,
+                  asoc->state, asoc->ep, asoc, t,
                   GFP_ATOMIC);
 
 }