IPv6: Complete IPV6_DONTFRAG support
[pandora-kernel.git] / net / ipv6 / datagram.c
index f5076d3..5959230 100644 (file)
@@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
                kfree_skb(skb);
 }
 
+void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu)
+{
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct ipv6hdr *iph;
+       struct sk_buff *skb;
+       struct ip6_mtuinfo *mtu_info;
+
+       if (!np->rxopt.bits.rxpmtu)
+               return;
+
+       skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       skb_put(skb, sizeof(struct ipv6hdr));
+       skb_reset_network_header(skb);
+       iph = ipv6_hdr(skb);
+       ipv6_addr_copy(&iph->daddr, &fl->fl6_dst);
+
+       mtu_info = IP6CBMTU(skb);
+       if (!mtu_info) {
+               kfree_skb(skb);
+               return;
+       }
+
+       mtu_info->ip6m_mtu = mtu;
+       mtu_info->ip6m_addr.sin6_family = AF_INET6;
+       mtu_info->ip6m_addr.sin6_port = 0;
+       mtu_info->ip6m_addr.sin6_flowinfo = 0;
+       mtu_info->ip6m_addr.sin6_scope_id = fl->oif;
+       ipv6_addr_copy(&mtu_info->ip6m_addr.sin6_addr, &ipv6_hdr(skb)->daddr);
+
+       __skb_pull(skb, skb_tail_pointer(skb) - skb->data);
+       skb_reset_transport_header(skb);
+
+       skb = xchg(&np->rxpmtu, skb);
+       kfree_skb(skb);
+}
+
 /*
  *     Handle MSG_ERRQUEUE
  */
@@ -381,6 +420,54 @@ out:
        return err;
 }
 
+/*
+ *     Handle IPV6_RECVPATHMTU
+ */
+int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
+{
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct sk_buff *skb;
+       struct sockaddr_in6 *sin;
+       struct ip6_mtuinfo mtu_info;
+       int err;
+       int copied;
+
+       err = -EAGAIN;
+       skb = xchg(&np->rxpmtu, NULL);
+       if (skb == NULL)
+               goto out;
+
+       copied = skb->len;
+       if (copied > len) {
+               msg->msg_flags |= MSG_TRUNC;
+               copied = len;
+       }
+       err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+       if (err)
+               goto out_free_skb;
+
+       sock_recv_timestamp(msg, sk, skb);
+
+       memcpy(&mtu_info, IP6CBMTU(skb), sizeof(mtu_info));
+
+       sin = (struct sockaddr_in6 *)msg->msg_name;
+       if (sin) {
+               sin->sin6_family = AF_INET6;
+               sin->sin6_flowinfo = 0;
+               sin->sin6_port = 0;
+               sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id;
+               ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr);
+       }
+
+       put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info);
+
+       err = copied;
+
+out_free_skb:
+       kfree_skb(skb);
+out:
+       return err;
+}
 
 
 int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)