udp socket connect一个不存在的地址后调用sendto返回111错误(connect refused)

udp socket connect一个不存在的地址后调用sendto返回111错误(connect refused)


udp socket 可以调用connect,这个就不说,不了解的可以网上查资料,下面直接说返回111错误的原因


udp socket 发送流程 sendto->sock_sendmsg->__sock_sendmsg->udp_sendmsg->ip_make_skb->__ip_append_data->sock_alloc_send_skb->sock_alloc_send_pskb

到了sock_alloc_send_pskb这个函数里,在发送前会调用sock_error检查该socket是否有错误,如果有错误,就直接返回了,由于链接一个不存在的地址,所以sock_error检查失败,返回错误111(connection refused),sock_error代码如下,检查sk->sk_err


static inline int sock_error(struct sock *sk)
{
	int err;
	if (likely(!sk->sk_err))
		return 0;
	err = xchg(&sk->sk_err, 0);
	return -err;
}

接下来再说一下sk->sk_err何时被置成111,通过用systemtap捕捉到

0xffffffff8148ad40 : udp_err+0x0/0x20 [kernel]
 0xffffffff8148f6c0 : icmp_unreach+0x140/0x2d0 [kernel]
 0xffffffff8148f430 : icmp_rcv+0x290/0x330 [kernel]
 0xffffffff814621fd : ip_local_deliver_finish+0xdd/0x2d0 [kernel]
 0xffffffff81462488 : ip_local_deliver+0x98/0xa0 [kernel]
 0xffffffff8146194d : ip_rcv_finish+0x12d/0x440 [kernel]
 0xffffffff81461ed5 : ip_rcv+0x275/0x350 [kernel]
 0xffffffff8142bedb : __netif_receive_skb+0x49b/0x6e0 [kernel]
 0xffffffff8142df88 : netif_receive_skb+0x58/0x60 [kernel]
 0xffffffff8142e090 : napi_skb_finish+0x50/0x70 [kernel]
 0xffffffff81430719 : napi_gro_receive+0x39/0x50 [kernel]

udp_err调用的是__udp4_lib_err,由于链接一个不存在的地址,在发送数据包的时候,会返回一个icms 目标地址不可达的错误ICMP_DEST_UNREACH,在__udp4_lib_err函数的最后倒数第4行,将sk->sk_err置成了ECONNREFUSED(111),所以在发送数据包的时候,就检测到了这个错误

void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
{
	struct inet_sock *inet;
	struct iphdr *iph = (struct iphdr *)skb->data;
	struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2));
	const int type = icmp_hdr(skb)->type;
	const int code = icmp_hdr(skb)->code;
	struct sock *sk;
	int harderr;
	int err;
	struct net *net = dev_net(skb->dev);

	sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
			iph->saddr, uh->source, skb->dev->ifindex, udptable);
	if (sk == NULL) {
		ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
		return;	/* No socket for error */
	}

	err = 0;
	harderr = 0;
	inet = inet_sk(sk);

	switch (type) {
	default:
	case ICMP_TIME_EXCEEDED:
		err = EHOSTUNREACH;
		break;
	case ICMP_SOURCE_QUENCH:
		goto out;
	case ICMP_PARAMETERPROB:
		err = EPROTO;
		harderr = 1;
		break;
	case ICMP_DEST_UNREACH:
		if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
			if (inet->pmtudisc != IP_PMTUDISC_DONT) {
				err = EMSGSIZE;
				harderr = 1;
				break;
			}
			goto out;
		}
		err = EHOSTUNREACH;
		if (code <= NR_ICMP_UNREACH) {
			harderr = icmp_err_convert[code].fatal;
			err = icmp_err_convert[code].errno;
		}
		break;
	}

	/*
	 *      RFC1122: OK.  Passes ICMP errors back to application, as per
	 *	4.1.3.3.
	 */
	if (!inet->recverr) {
		if (!harderr || sk->sk_state != TCP_ESTABLISHED)
			goto out;
	} else {
		ip_icmp_error(sk, skb, err, uh->dest, info, (u8 *)(uh+1));
	}
	sk->sk_err = err;
	sk->sk_error_report(sk);
out:
	sock_put(sk);
}


注意:非阻塞的socket,调用connect后,立马调用sendto,一般不会返回错误,因为icms的不可达错误消息还没到达,等icmp的不可达消息到达后,后续调用sendto才会返回错误,这就是为什么第一次调用sendto没有问题,后面调用的就返回错误了


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值