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 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没有问题,后面调用的就返回错误了
相关文章推荐
- [Socket网络编程]由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。
- 由于套接 字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址
- Windows UDP socket recvfrom返回10054错误的解决办法
- udp socket 调用connect的作用是什么
- ASP调用存储过程返回了一个参数和一个记录集时出现ADODB.Recordset 错误 '800a0e78'
- UDP怎么会返回Connection refused错误
- UDP socket也可以使用connect系统调用
- 我以前总是错误地以为---“阻塞socket和非阻塞socket对应的connect函数都会立即返回”
- 由于套接 字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址
- Windows UDP socket recvfrom返回10054错误的解决办法
- UDP socket也可以使用connect系统调用
- Windows UDP socket recvfrom返回10054错误的解决办法
- 我以前总是错误地以为---“阻塞socket和非阻塞socket对应的connect函数都会立即返回”
- python中os模块path.abspath()返回的并不是绝对值,而是个错误的不存在的拼接地址
- Socket进程处理被中断的系统调用及Accept函数返回EINTR错误处理
- UDP怎么会返回Connection refused错误
- 由于套接 字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址
- 一个关于WCF调用远程链接返回405错误不允许使用此方法的问题
- atl写的程序,客户调用的时候出现一个联结错误
- [Silverlight] 一个易犯的错误:关于调用 WCF 服务