예제 #1
0
/*
 *	Handle MSG_ERRQUEUE
 */
int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct sock_exterr_skb *serr;
	struct sk_buff *skb;
	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin, msg->msg_name);
	struct {
		struct sock_extended_err ee;
		struct sockaddr_in6	 offender;
	} errhdr;
	int err;
	int copied;

	err = -EAGAIN;
	skb = sock_dequeue_err_skb(sk);
	if (!skb)
		goto out;

	copied = skb->len;
	if (copied > len) {
		msg->msg_flags |= MSG_TRUNC;
		copied = len;
	}
	err = skb_copy_datagram_msg(skb, 0, msg, copied);
	if (unlikely(err)) {
		kfree_skb(skb);
		return err;
	}
	sock_recv_timestamp(msg, sk, skb);

	serr = SKB_EXT_ERR(skb);

	if (sin && ipv6_datagram_support_addr(serr)) {
		const unsigned char *nh = skb_network_header(skb);
		sin->sin6_family = AF_INET6;
		sin->sin6_flowinfo = 0;
		sin->sin6_port = serr->port;
		if (skb->protocol == htons(ETH_P_IPV6)) {
			const struct ipv6hdr *ip6h = container_of((struct in6_addr *)(nh + serr->addr_offset),
								  struct ipv6hdr, daddr);
			sin->sin6_addr = ip6h->daddr;
			if (np->sndflow)
				sin->sin6_flowinfo = ip6_flowinfo(ip6h);
			sin->sin6_scope_id =
				ipv6_iface_scope_id(&sin->sin6_addr,
						    IP6CB(skb)->iif);
		} else {
			ipv6_addr_set_v4mapped(*(__be32 *)(nh + serr->addr_offset),
					       &sin->sin6_addr);
			sin->sin6_scope_id = 0;
		}
		*addr_len = sizeof(*sin);
	}
예제 #2
0
/*
 *	Handle MSG_ERRQUEUE
 */
int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
{
	struct sock_exterr_skb *serr;
	struct sk_buff *skb;
	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
	struct {
		struct sock_extended_err ee;
		struct sockaddr_in	 offender;
	} errhdr;
	int err;
	int copied;

	WARN_ON_ONCE(sk->sk_family == AF_INET6);

	err = -EAGAIN;
	skb = sock_dequeue_err_skb(sk);
	if (!skb)
		goto out;

	copied = skb->len;
	if (copied > len) {
		msg->msg_flags |= MSG_TRUNC;
		copied = len;
	}
	err = skb_copy_datagram_msg(skb, 0, msg, copied);
	if (unlikely(err)) {
		kfree_skb(skb);
		return err;
	}
	sock_recv_timestamp(msg, sk, skb);

	serr = SKB_EXT_ERR(skb);

	if (sin && ipv4_datagram_support_addr(serr)) {
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
						   serr->addr_offset);
		sin->sin_port = serr->port;
		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
		*addr_len = sizeof(*sin);
	}

	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
	sin = &errhdr.offender;
	memset(sin, 0, sizeof(*sin));

	if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
		if (inet_sk(sk)->cmsg_flags)
			ip_cmsg_recv(msg, skb);
	}

	put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);

	/* Now we could try to dump offended packet options */

	msg->msg_flags |= MSG_ERRQUEUE;
	err = copied;

	consume_skb(skb);
out:
	return err;
}
예제 #3
0
파일: ar-error.c 프로젝트: 020gzh/linux
/*
 * handle an error received on the local endpoint
 */
void rxrpc_UDP_error_report(struct sock *sk)
{
	struct sock_exterr_skb *serr;
	struct rxrpc_transport *trans;
	struct rxrpc_local *local = sk->sk_user_data;
	struct rxrpc_peer *peer;
	struct sk_buff *skb;
	__be32 addr;
	__be16 port;

	_enter("%p{%d}", sk, local->debug_id);

	skb = sock_dequeue_err_skb(sk);
	if (!skb) {
		_leave("UDP socket errqueue empty");
		return;
	}
	serr = SKB_EXT_ERR(skb);
	if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {
		_leave("UDP empty message");
		kfree_skb(skb);
		return;
	}

	rxrpc_new_skb(skb);

	addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset);
	port = serr->port;

	_net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port));
	_debug("Msg l:%d d:%d", skb->len, skb->data_len);

	peer = rxrpc_find_peer(local, addr, port);
	if (IS_ERR(peer)) {
		rxrpc_free_skb(skb);
		_leave(" [no peer]");
		return;
	}

	trans = rxrpc_find_transport(local, peer);
	if (!trans) {
		rxrpc_put_peer(peer);
		rxrpc_free_skb(skb);
		_leave(" [no trans]");
		return;
	}

	if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
	    serr->ee.ee_type == ICMP_DEST_UNREACH &&
	    serr->ee.ee_code == ICMP_FRAG_NEEDED
	    ) {
		u32 mtu = serr->ee.ee_info;

		_net("Rx Received ICMP Fragmentation Needed (%d)", mtu);

		/* wind down the local interface MTU */
		if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) {
			peer->if_mtu = mtu;
			_net("I/F MTU %u", mtu);
		}

		if (mtu == 0) {
			/* they didn't give us a size, estimate one */
			mtu = peer->if_mtu;
			if (mtu > 1500) {
				mtu >>= 1;
				if (mtu < 1500)
					mtu = 1500;
			} else {