接着前一篇,我们来看这个应用程序背后,内核真正做了一些什么事情。
代表MY_INET域套接字的结构体struct inet_sock有一个成员recverr,它占1bit长度,可能的取值是1或0,当为0时表示socket上出错时,只通过系统调用向应用程序返回 错误号,不提供进一步的详细信息。当取值为1时,则表示socket上出错时,则向struct inet_sock的成员sk_error_queue(一个sk_buff的队列)存入一个特殊的struct sk_buff,在sk_buff的成员cb中放入详细的错误信息,应用程序通过特定的系统调用可以取得详细的出错信息。
recverr的值可以通过套接字选项操作进行设置,它是一个IP层的选项,对应的选项名是IP_RECVERR。下面的代码就是将它的值设为1(打开选项):
int val = 1;
if( setsockopt( fd, SOL_IP, IP_RECVERR, &val, sizeof(val) ) == -1 )
;//deal with error
当打开了这个选项后,我们在该socket上发送UDP数据报,按照前面文章提及的测试环境运行,172.16.48.2继续会收到ICMP目的不可达报 文,在差错数据报处理时,会达到函数myudp_err,该函数会设置socket的成员sk_err,同时,它也会检查recverr成员,如果为1, 则要在sk_error_queue队列中放入一个特殊的出错信息sk_buff。该sk_buff保留了出错的那个源UDP数据报,同时在它的cb成员 中保存了一个结构体struct sock_exterr_skb,该结构体记录了详细的出错信息,下面是其定义:
struct sock_exterr_skb
{
union {
struct inet_skb_parm h4;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
struct inet6_skb_parm h6;
#endif
} header;
struct sock_extended_err ee;
u16 addr_offset;
u16 port;
};
addr_offset和port是出错UDP数据报的地址和端口号,ee的定义如下:
struct sock_extended_err
{
__u32 ee_errno; //错误号。
__u8 ee_origin; //产生错误的源,我们的环境下,产生错误的源为一个ICMP包。
__u8 ee_type; //ICMP类型。
__u8 ee_code; //ICMP代码。
__u8 ee_pad;
__u32 ee_info; //用于EMSGSIZE时找到的MTU。
__u32 ee_data;
};
我们保存了出错信息,应用程序要取得这个出错信息,必须使用特定的系统调用,recvmsg可以获得详细的出错信息,同时,调用接口上必须使用标志MSG_ERRQUEUE表示取错误队列,下面是recvmsg的定义:
ssize_t recvmsg(int s, struct msghdr *msg, int flags);
flags置MSG_ERRQUEUE,msg结构控制信息成员msg_control和msg_controllen需要分配一个缓存,用于辅助信息的传递。关于接收,可以查看前面一篇的源代码和man recvmsg,这里不再重复。
2007年10月22日星期一
订阅:
博文评论 (Atom)
没有评论:
发表评论