From d84c22be7ab9c5c337bb0f92b5eca8f577a6fc33 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Thu, 28 Oct 2021 21:21:07 +0200 Subject: [PATCH] Corrected handling of IP_RECVERR --- CHANGES | 4 +++ filan.c | 35 +++++++++++++++++++++ xio-ip.c | 87 +++++++++++++++++++++++++++++++++++++++++----------- xio-socket.c | 63 ++++++++++++++++++++++++++++++++----- xio-socket.h | 2 +- xioread.c | 14 +++++++-- 6 files changed, 177 insertions(+), 28 deletions(-) diff --git a/CHANGES b/CHANGES index dfd0b74..815811b 100644 --- a/CHANGES +++ b/CHANGES @@ -50,6 +50,10 @@ Corrections: 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: Prevent the TIMESTAMP tests from sporadically failing due do seconds overflow diff --git a/filan.c b/filan.c index cef23c1..980b1a9 100644 --- a/filan.c +++ b/filan.c @@ -35,6 +35,7 @@ bool filan_rawoutput; int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile); int tcpan(int fd, FILE *outfile); +int tcpan2(int fd, FILE *outfile); const char *getfiletypestring(int st_mode); static int printtime(FILE *outfile, time_t time); @@ -895,10 +896,44 @@ int tcpan(int fd, FILE *outfile) { sockoptan(fd, optname, SOL_TCP, outfile); ++optname; } + + tcpan2(fd, outfile); return 0; } #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 int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile) { diff --git a/xio-ip.c b/xio-ip.c index 289c68e..dd288ad 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -464,16 +464,16 @@ int xiogetaddrinfo(const char *node, const char *service, #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. - these are valid for IPv4 and IPv6 - 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 name strings in *nambuff - returns a sequence of \0 terminated value strings in *valbuff - the respective len parameters specify the available space in the buffers - returns STAT_OK on success - returns STAT_WARNING if a buffer was too short and data truncated. + These are valid for IPv4 and IPv6 + 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 name strings in *nambuff + Returns a sequence of \0 terminated value strings in *valbuff + The respective len parameters specify the available space in the buffers + Returns STAT_OK on success + Returns STAT_WARNING if a buffer was too short and data truncated. */ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, @@ -519,14 +519,31 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, '\0', inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr), 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; #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */ #endif /* WITH_IP4 */ #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR case IP_RECVERR: { - struct sock_extended_err *err = - (struct sock_extended_err *)CMSG_DATA(cmsg); + struct xio_extended_err { + 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; typbuff[0] = '\0'; strncat(typbuff, "IP_RECVERR", typlen-1); 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_INFO", '\0', "IP_RECVERR_DATA"); 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->ee_code, '\0', err->ee_info, '\0', err->ee_data); + err->see.ee_errno, '\0', err->see.ee_origin, '\0', err->see.ee_type, '\0', + 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; } #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'; strncat(valbuff, xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1); + Notice1("IP_RECVIF: %s", valbuff); return STAT_OK; } #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); envbuff[0] = '\0'; strncat(envbuff, "IP_DSTADDR", envlen-1); inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen); + Notice1("IP_RECVDSTADDR: %s", valbuff); return STAT_OK; #endif #endif /* WITH_IP4 */ @@ -571,9 +615,12 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, #ifdef IP_RECVOPTS case IP_RECVOPTS: #endif - cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1; break; + cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1; + /*!!!*/ + break; case IP_TOS: - cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen; break; + cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen; + break; case IP_TTL: /* Linux */ #ifdef IP_RECVTTL 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; } /* 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; if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING; typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1); @@ -595,9 +642,13 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, } switch (cmsgctr) { 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): - 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: xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); break; default: break; diff --git a/xio-socket.c b/xio-socket.c index 6e5103a..d55d8d3 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -693,6 +693,49 @@ int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts, #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 to a peer address. @@ -919,6 +962,10 @@ int _xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, errno = _errno; return -1; } else { + /* try to find details about error, especially from ICMP */ + xiogetpacketinfo(xfd->fd); + + /* continue mainstream */ Msg4(level, "connect(%d, %s, "F_Zd"): %s", xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); @@ -1408,7 +1455,13 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN msgh.msg_controllen = sizeof(ctrlbuff); #endif - if (xiogetpacketsrc(xfd->fd, &msgh) < 0) { + if (xiogetpacketsrc(xfd->fd, + &msgh, + MSG_PEEK +#ifdef MSG_TRUNC + |MSG_TRUNC +#endif + ) < 0) { return STAT_RETRYLATER; } 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 the arriving packet. in msgh the msg_name pointer must refer to an (empty) sockaddr storage. */ -int xiogetpacketsrc(int fd, struct msghdr *msgh) { +int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags) { char peekbuff[1]; #if HAVE_STRUCT_IOVEC struct iovec iovec; @@ -1641,11 +1694,7 @@ int xiogetpacketsrc(int fd, struct msghdr *msgh) { #if HAVE_STRUCT_MSGHDR_MSGFLAGS msgh->msg_flags = 0; #endif - if (Recvmsg(fd, msgh, MSG_PEEK -#ifdef MSG_TRUNC - |MSG_TRUNC -#endif - ) < 0) { + if (Recvmsg(fd, msgh, flags) < 0) { Warn1("recvmsg(): %s", strerror(errno)); return STAT_RETRYLATER; } diff --git a/xio-socket.h b/xio-socket.h index 62cfe72..1a612a8 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -116,7 +116,7 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags, extern int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv); extern -int xiogetpacketsrc(int fd, struct msghdr *msgh); +int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags); extern int xiocheckpeer(xiosingle_t *xfd, union sockaddr_union *pa, union sockaddr_union *la); diff --git a/xioread.c b/xioread.c index aa029a9..04a8286 100644 --- a/xioread.c +++ b/xioread.c @@ -128,7 +128,12 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN msgh.msg_controllen = sizeof(ctrlbuff); #endif - if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { + if (xiogetpacketsrc(pipe->fd, &msgh, + MSG_PEEK +#ifdef MSG_TRUNC + |MSG_TRUNC +#endif + ) < 0) { return -1; } do { @@ -325,7 +330,12 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN msgh.msg_controllen = sizeof(ctrlbuff); #endif - if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { + if (xiogetpacketsrc(pipe->fd, &msgh, + MSG_PEEK +#ifdef MSG_TRUNC + |MSG_TRUNC +#endif + ) < 0) { return -1; } xiodopacketinfo(&msgh, true, false);