예제 #1
0
파일: raw_ipv6.c 프로젝트: GNUHurdTR/hurd
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;
}
예제 #2
0
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;
}