Corrected handling of IP_RECVERR

This commit is contained in:
Gerhard Rieger 2021-10-28 21:21:07 +02:00
parent bc32acb5fe
commit d84c22be7a
6 changed files with 177 additions and 28 deletions

View file

@ -50,6 +50,10 @@ Corrections:
Print a message when readbytes option causes EOF Print a message when readbytes option causes EOF
The ip-recverr option had no effect. Corrected and improved its
handling of ancilliary messages, so it is able to analyze ICMP error
packets (Linux only?)
Testing: Testing:
Prevent the TIMESTAMP tests from sporadically failing due do seconds Prevent the TIMESTAMP tests from sporadically failing due do seconds
overflow overflow

35
filan.c
View file

@ -35,6 +35,7 @@ bool filan_rawoutput;
int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile); int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile);
int tcpan(int fd, FILE *outfile); int tcpan(int fd, FILE *outfile);
int tcpan2(int fd, FILE *outfile);
const char *getfiletypestring(int st_mode); const char *getfiletypestring(int st_mode);
static int printtime(FILE *outfile, time_t time); static int printtime(FILE *outfile, time_t time);
@ -895,10 +896,44 @@ int tcpan(int fd, FILE *outfile) {
sockoptan(fd, optname, SOL_TCP, outfile); sockoptan(fd, optname, SOL_TCP, outfile);
++optname; ++optname;
} }
tcpan2(fd, outfile);
return 0; return 0;
} }
#endif /* WITH_TCP */ #endif /* WITH_TCP */
#if WITH_TCP
int tcpan2(int fd, FILE *outfile) {
struct tcp_info tcpinfo;
socklen_t tcpinfolen = sizeof(tcpinfo);
int result;
result = Getsockopt(fd, SOL_TCP, TCP_INFO, &tcpinfo, &tcpinfolen);
if (result < 0) {
Debug4("getsockopt(%d, SOL_TCP, TCP_INFO, %p, {"F_socklen"}): %s",
fd, &tcpinfo, sizeof(tcpinfo), strerror(errno));
return -1;
}
fprintf(outfile, "%s={%u}\t", "TCPI_STATE", tcpinfo.tcpi_state);
#if 0 /* on BSD these components are prefixed with __ - I get tired... */
fprintf(outfile, "%s={%u}\t", "TCPI_CA_STATE", tcpinfo.tcpi_ca_state);
fprintf(outfile, "%s={%u}\t", "TCPI_RETRANSMITS", tcpinfo.tcpi_retransmits);
fprintf(outfile, "%s={%u}\t", "TCPI_PROBES", tcpinfo.tcpi_probes);
fprintf(outfile, "%s={%u}\t", "TCPI_BACKOFF", tcpinfo.tcpi_backoff);
#endif
fprintf(outfile, "%s={%u}\t", "TCPI_OPTIONS", tcpinfo.tcpi_options);
fprintf(outfile, "%s={%u}\t", "TCPI_SND_WSCALE", tcpinfo.tcpi_snd_wscale);
fprintf(outfile, "%s={%u}\t", "TCPI_RCV_WSCALE", tcpinfo.tcpi_rcv_wscale);
//fprintf(outfile, "%s={%u}\t", "TCPI_DELIVERY_RATE_APP_LIMITED", tcpinfo.tcpi_delivery_rate_app_limited);
//fprintf(outfile, "%s={%u}\t", "TCPI_FASTOPEN_CLIENT_FAIL", tcpinfo.tcpi_fastopen_client_fail);
// fprintf(outfile, "%s={%u}\t", "TCPI_", tcpinfo.tcpi_);
return 0;
}
#endif /* WITH_TCP */
#if _WITH_SOCKET #if _WITH_SOCKET
int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile) { int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile) {

View file

@ -464,16 +464,16 @@ int xiogetaddrinfo(const char *node, const char *service,
#if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
/* converts the ancillary message in *cmsg into a form useable for further /* Converts the ancillary message in *cmsg into a form useable for further
processing. knows the specifics of common message types. processing. knows the specifics of common message types.
these are valid for IPv4 and IPv6 These are valid for IPv4 and IPv6
returns the number of resulting syntax elements in *num Returns the number of resulting syntax elements in *num
returns a sequence of \0 terminated type strings in *typbuff Returns a sequence of \0 terminated type strings in *typbuff
returns a sequence of \0 terminated name strings in *nambuff Returns a sequence of \0 terminated name strings in *nambuff
returns a sequence of \0 terminated value strings in *valbuff Returns a sequence of \0 terminated value strings in *valbuff
the respective len parameters specify the available space in the buffers The respective len parameters specify the available space in the buffers
returns STAT_OK on success Returns STAT_OK on success
returns STAT_WARNING if a buffer was too short and data truncated. Returns STAT_WARNING if a buffer was too short and data truncated.
*/ */
int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
char *typbuff, int typlen, char *typbuff, int typlen,
@ -519,14 +519,31 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
'\0', '\0',
inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr), inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr),
scratch3, sizeof(scratch3))); scratch3, sizeof(scratch3)));
Notice3("Ancillary message: interface \"%s\", locaddr=%s, dstaddr=%s",
xiogetifname(pktinfo->ipi_ifindex, scratch1, -1),
#if HAVE_PKTINFO_IPI_SPEC_DST
inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr),
scratch2, sizeof(scratch2)),
#else
"",
#endif
inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr),
scratch3, sizeof(scratch3)));
} }
return STAT_OK; return STAT_OK;
#endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */ #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
#endif /* WITH_IP4 */ #endif /* WITH_IP4 */
#if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR
case IP_RECVERR: { case IP_RECVERR: {
struct sock_extended_err *err = struct xio_extended_err {
(struct sock_extended_err *)CMSG_DATA(cmsg); struct sock_extended_err see;
__u32 data0;
__u32 data1;
__u32 data2;
__u32 data3;
} ;
struct xio_extended_err *err =
(struct xio_extended_err *)CMSG_DATA(cmsg);
*num = 6; *num = 6;
typbuff[0] = '\0'; strncat(typbuff, "IP_RECVERR", typlen-1); typbuff[0] = '\0'; strncat(typbuff, "IP_RECVERR", typlen-1);
snprintf(nambuff, namlen, "%s%c%s%c%s%c%s%c%s%c%s", snprintf(nambuff, namlen, "%s%c%s%c%s%c%s%c%s%c%s",
@ -537,8 +554,33 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
"IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0', "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0',
"IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA"); "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA");
snprintf(valbuff, vallen, "%u%c%u%c%u%c%u%c%u%c%u", snprintf(valbuff, vallen, "%u%c%u%c%u%c%u%c%u%c%u",
err->ee_errno, '\0', err->ee_origin, '\0', err->ee_type, '\0', err->see.ee_errno, '\0', err->see.ee_origin, '\0', err->see.ee_type, '\0',
err->ee_code, '\0', err->ee_info, '\0', err->ee_data); err->see.ee_code, '\0', err->see.ee_info, '\0', err->see.ee_data);
/* semantic part */
switch (err->see.ee_origin) {
char addrbuff[40];
#if WITH_IP4
case SO_EE_ORIGIN_ICMP:
if (1) {
inet4addr_info(ntohl(err->data1), addrbuff, sizeof(addrbuff));
Notice6("received ICMP from %s, type %d, code %d, info %d, data %d, resulting in errno %d",
addrbuff, err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno);
}
break;
#endif /* WITH_IP4 */
#if WITH_IP6
case SO_EE_ORIGIN_ICMP6:
if (1) {
Notice5("received ICMP type %d, code %d, info %d, data %d, resulting in errno %d",
err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno);
}
break;
#endif /* WITH_IP6 */
default:
Notice6("received error message origin %d, type %d, code %d, info %d, data %d, generating errno %d",
err->see.ee_origin, err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno);
break;
}
return STAT_OK; return STAT_OK;
} }
#endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */ #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */
@ -553,6 +595,7 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
valbuff[0] = '\0'; valbuff[0] = '\0';
strncat(valbuff, strncat(valbuff,
xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1); xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1);
Notice1("IP_RECVIF: %s", valbuff);
return STAT_OK; return STAT_OK;
} }
#endif /* defined(IP_RECVIF) */ #endif /* defined(IP_RECVIF) */
@ -564,6 +607,7 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
nambuff[0] = '\0'; strncat(nambuff, "dstaddr", namlen-1); nambuff[0] = '\0'; strncat(nambuff, "dstaddr", namlen-1);
envbuff[0] = '\0'; strncat(envbuff, "IP_DSTADDR", envlen-1); envbuff[0] = '\0'; strncat(envbuff, "IP_DSTADDR", envlen-1);
inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen); inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen);
Notice1("IP_RECVDSTADDR: %s", valbuff);
return STAT_OK; return STAT_OK;
#endif #endif
#endif /* WITH_IP4 */ #endif /* WITH_IP4 */
@ -571,9 +615,12 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
#ifdef IP_RECVOPTS #ifdef IP_RECVOPTS
case IP_RECVOPTS: case IP_RECVOPTS:
#endif #endif
cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1; break; cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1;
/*!!!*/
break;
case IP_TOS: case IP_TOS:
cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen; break; cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen;
break;
case IP_TTL: /* Linux */ case IP_TTL: /* Linux */
#ifdef IP_RECVTTL #ifdef IP_RECVTTL
case IP_RECVTTL: /* FreeBSD */ case IP_RECVTTL: /* FreeBSD */
@ -581,7 +628,7 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
cmsgtype = "IP_TTL"; cmsgname = "ttl"; cmsgctr = msglen; break; cmsgtype = "IP_TTL"; cmsgname = "ttl"; cmsgctr = msglen; break;
} }
/* when we come here we provide a single parameter /* when we come here we provide a single parameter
with type in cmsgtype, name in cmsgname, value length in msglen */ with name in cmsgname, value length in msglen */
*num = 1; *num = 1;
if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING; if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING;
typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1); typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1);
@ -595,9 +642,13 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
} }
switch (cmsgctr) { switch (cmsgctr) {
case sizeof(char): case sizeof(char):
snprintf(valbuff, vallen, "%u", *(unsigned char *)CMSG_DATA(cmsg)); break; snprintf(valbuff, vallen, "%u", *(unsigned char *)CMSG_DATA(cmsg));
Notice2("Ancillary message: %s=%u", cmsgname, *(unsigned char *)CMSG_DATA(cmsg));
break;
case sizeof(int): case sizeof(int):
snprintf(valbuff, vallen, "%u", (*(unsigned int *)CMSG_DATA(cmsg))); break; snprintf(valbuff, vallen, "%u", (*(unsigned int *)CMSG_DATA(cmsg)));
Notice2("Ancillary message: %s=%u", cmsgname, *(unsigned int *)CMSG_DATA(cmsg));
break;
case 0: case 0:
xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); break; xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); break;
default: break; default: break;

View file

@ -693,6 +693,49 @@ int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts,
#endif /* WITH_GENERICSOCKET */ #endif /* WITH_GENERICSOCKET */
int xiogetpacketinfo(int fd)
{
#if defined(MSG_ERRQUEUE)
int _errno = errno;
char peername[256];
union sockaddr_union _peername;
/* union sockaddr_union _sockname; */
union sockaddr_union *pa = &_peername; /* peer address */
/* union sockaddr_union *la = &_sockname; */ /* local address */
socklen_t palen = sizeof(_peername); /* peer address size */
char ctrlbuff[1024]; /* ancillary messages */
struct msghdr msgh = {0};
msgh.msg_name = pa;
msgh.msg_namelen = palen;
#if HAVE_STRUCT_MSGHDR_MSGCONTROL
msgh.msg_control = ctrlbuff;
#endif
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
#endif
if (xiogetpacketsrc(fd,
&msgh,
MSG_ERRQUEUE
#ifdef MSG_TRUNC
|MSG_TRUNC
#endif
) >= 0
) {
palen = msgh.msg_namelen;
Notice1("receiving packet from %s"/*"src"*/,
sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*,
sockaddr_info(&la->soa, sockname, sizeof(sockname))*/);
xiodopacketinfo(&msgh, true, true);
}
errno = _errno;
#endif /* defined(MSG_ERRQUEUE) */
return 0;
}
/* a subroutine that is common to all socket addresses that want to connect /* a subroutine that is common to all socket addresses that want to connect
to a peer address. to a peer address.
@ -919,6 +962,10 @@ int _xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen,
errno = _errno; errno = _errno;
return -1; return -1;
} else { } else {
/* try to find details about error, especially from ICMP */
xiogetpacketinfo(xfd->fd);
/* continue mainstream */
Msg4(level, "connect(%d, %s, "F_Zd"): %s", Msg4(level, "connect(%d, %s, "F_Zd"): %s",
xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
themlen, strerror(errno)); themlen, strerror(errno));
@ -1408,7 +1455,13 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff); msgh.msg_controllen = sizeof(ctrlbuff);
#endif #endif
if (xiogetpacketsrc(xfd->fd, &msgh) < 0) { if (xiogetpacketsrc(xfd->fd,
&msgh,
MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
#endif
) < 0) {
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
palen = msgh.msg_namelen; palen = msgh.msg_namelen;
@ -1626,7 +1679,7 @@ int retropt_socket_pf(struct opt *opts, int *pf) {
/* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about /* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
the arriving packet. in msgh the msg_name pointer must refer to an (empty) the arriving packet. in msgh the msg_name pointer must refer to an (empty)
sockaddr storage. */ sockaddr storage. */
int xiogetpacketsrc(int fd, struct msghdr *msgh) { int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags) {
char peekbuff[1]; char peekbuff[1];
#if HAVE_STRUCT_IOVEC #if HAVE_STRUCT_IOVEC
struct iovec iovec; struct iovec iovec;
@ -1641,11 +1694,7 @@ int xiogetpacketsrc(int fd, struct msghdr *msgh) {
#if HAVE_STRUCT_MSGHDR_MSGFLAGS #if HAVE_STRUCT_MSGHDR_MSGFLAGS
msgh->msg_flags = 0; msgh->msg_flags = 0;
#endif #endif
if (Recvmsg(fd, msgh, MSG_PEEK if (Recvmsg(fd, msgh, flags) < 0) {
#ifdef MSG_TRUNC
|MSG_TRUNC
#endif
) < 0) {
Warn1("recvmsg(): %s", strerror(errno)); Warn1("recvmsg(): %s", strerror(errno));
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }

View file

@ -116,7 +116,7 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags,
extern extern
int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv); int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv);
extern extern
int xiogetpacketsrc(int fd, struct msghdr *msgh); int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags);
extern extern
int xiocheckpeer(xiosingle_t *xfd, int xiocheckpeer(xiosingle_t *xfd,
union sockaddr_union *pa, union sockaddr_union *la); union sockaddr_union *pa, union sockaddr_union *la);

View file

@ -128,7 +128,12 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff); msgh.msg_controllen = sizeof(ctrlbuff);
#endif #endif
if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { if (xiogetpacketsrc(pipe->fd, &msgh,
MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
#endif
) < 0) {
return -1; return -1;
} }
do { do {
@ -325,7 +330,12 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff); msgh.msg_controllen = sizeof(ctrlbuff);
#endif #endif
if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { if (xiogetpacketsrc(pipe->fd, &msgh,
MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
#endif
) < 0) {
return -1; return -1;
} }
xiodopacketinfo(&msgh, true, false); xiodopacketinfo(&msgh, true, false);