int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name; struct sk_buff *skb; int copied, err; if (flags & MSG_OOB) return -EOPNOTSUPP; if (addr_len) *addr_len=sizeof(*sin6); if (flags & MSG_ERRQUEUE) return ipv6_recv_error(sk, msg, len); skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; copied = skb->tail - skb->h.raw; if (copied > len) { copied = len; msg->msg_flags |= MSG_TRUNC; } err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); sk->stamp=skb->stamp; if (err) goto out_free; /* Copy the address. */ if (sin6) { sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, sizeof(struct in6_addr)); sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin6->sin6_scope_id = ((struct inet6_skb_parm *) skb->cb)->iif; } if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); err = copied; out_free: skb_free_datagram(sk, skb); out: return err; }
int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len) { struct sk_buff *skb; int copied, err; if (addr_len) *addr_len=sizeof(struct sockaddr_in6); if (flags & MSG_ERRQUEUE) return ipv6_recv_error(sk, msg, len); skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; copied = skb->len - sizeof(struct udphdr); if (copied > len) { copied = len; msg->msg_flags |= MSG_TRUNC; } if (skb->ip_summed==CHECKSUM_UNNECESSARY) { err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); } else if (msg->msg_flags&MSG_TRUNC) { if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) goto csum_copy_err; err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); } else { err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov); if (err == -EINVAL) goto csum_copy_err; } if (err) goto out_free; sock_recv_timestamp(msg, sk, skb); /* Copy the address. */ if (msg->msg_name) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *) msg->msg_name; sin6->sin6_family = AF_INET6; sin6->sin6_port = skb->h.uh->source; sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; if (skb->protocol == __constant_htons(ETH_P_IP)) { ipv6_addr_set(&sin6->sin6_addr, 0, 0, __constant_htonl(0xffff), skb->nh.iph->saddr); if (sk->protinfo.af_inet.cmsg_flags) ip_cmsg_recv(msg, skb); } else { memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, sizeof(struct in6_addr)); if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; sin6->sin6_scope_id = opt->iif; } } } err = copied; out_free: skb_free_datagram(sk, skb); out: return err; csum_copy_err: /* Clear queue. */ if (flags&MSG_PEEK) { int clear = 0; spin_lock_irq(&sk->receive_queue.lock); if (skb == skb_peek(&sk->receive_queue)) { __skb_unlink(skb, &sk->receive_queue); clear = 1; } spin_unlock_irq(&sk->receive_queue.lock); if (clear) kfree_skb(skb); } /* Error for blocking case is chosen to masquerade as some normal condition. */ err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; UDP6_INC_STATS_USER(UdpInErrors); goto out_free; }