|
@@ -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)
|