您的位置:首页 > 其它

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

2017-01-17 13:16 946 查看
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没有问题,后面调用的就返回错误了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  udp connect sendto f
相关文章推荐