diff --git a/CHANGES b/CHANGES index 97a0cbf..879bf7a 100644 --- a/CHANGES +++ b/CHANGES @@ -57,6 +57,15 @@ Features: messages in LISTEN and CONNECT type sub processes. Test: CHILDREN_SHUTUP + New option retrieve-vlan for supporting VLANs in INTERFACE addresses: + Linux normally keeps VLAN tags in outgoing raw packets, but appears to + strip them from incoming packets and makes them available in + PACKET_AUXDATA ancillary messages only. + Up do version 1.7.4.5 Socat did not handle this situation, so the VLAN + tags where effectively stripped off incoming packets. + With this option Socat restores the VLAN tag. + Feature inspired by Zhao Dong. + Corrections: When a sub process (EXEC, SYSTEM) terminated with exit code other than 0, its last sent data might have been lost depending on timing of read/ diff --git a/VERSION b/VERSION index 7fd3594..ceaf471 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.7.4.5+vlans" +"1.7.4.5+" diff --git a/config.h.in b/config.h.in index dfeaab8..5535e9b 100644 --- a/config.h.in +++ b/config.h.in @@ -430,6 +430,12 @@ /* define if you have struct in6_pktinfo */ #undef HAVE_STRUCT_IN6_PKTINFO +/* define if you have struct tpacket_auxdata */ +#undef HAVE_STRUCT_TPACKET_AUXDATA + +/* define if you have struct tpacket_auxdata and it has tp_vlan_tpid */ +#undef HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TPID + /* define if your struct ip has ip_hl; otherwise assume ip_vhl */ #undef HAVE_STRUCT_IP_IP_HL diff --git a/configure.ac b/configure.ac index 42ac783..eaad7b4 100644 --- a/configure.ac +++ b/configure.ac @@ -1477,6 +1477,29 @@ if test $sc_cv_struct_in6_pktinfo = yes; then fi AC_MSG_RESULT($sc_cv_struct_in6_pktinfo) +dnl check for struct tpacket_auxdata +AC_MSG_CHECKING(for struct tpacket_auxdata) +AC_CACHE_VAL(sc_cv_struct_tpacket_auxdata, +[AC_TRY_COMPILE([#include "sysincludes.h"], +[struct tpacket_auxdata s;], +[sc_cv_struct_tpacket_auxdata=yes], +[sc_cv_struct_tpacket_auxdata=no])]) +if test $sc_cv_struct_tpacket_auxdata = yes; then + AC_DEFINE(HAVE_STRUCT_TPACKET_AUXDATA) +fi +AC_MSG_RESULT($sc_cv_struct_tpacket_auxdata) + +AC_MSG_CHECKING(for tp_vlan_tpid in struct tpacket_auxdata) +AC_CACHE_VAL(sc_cv_struct_tpacket_auxdata_tp_vlan_tpid, +[AC_TRY_COMPILE([#include "sysincludes.h"], +[struct tpacket_auxdata s; s.tp_vlan_tpid=0], +[sc_cv_struct_tpacket_auxdata_tp_vlan_tpid=yes], +[sc_cv_struct_tpacket_auxdata_tp_vlan_tpid=no])]) +if test $sc_cv_struct_tpacket_auxdata_tp_vlan_tpid = yes; then + AC_DEFINE(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TPID) +fi +AC_MSG_RESULT($sc_cv_struct_tpacket_auxdata) + dnl check for ip_hl in struct ip AC_MSG_CHECKING(for struct ip.ip_hl) AC_CACHE_VAL(sc_cv_struct_ip_ip_hl, diff --git a/doc/socat.yo b/doc/socat.yo index 3e8ce58..7ec9b5a 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -3004,9 +3004,20 @@ startdit()enddit()nl() label(GROUP_INTERFACE)em(bf(INTERFACE option group)) -Options that control Linux INTERFACE addresses. +These options may be applied to addresses link(INTERFACE)(ADDRESS_INTERFACE) and +link(TUN)(ADDRESS_TUN). These address types and options are currently only +implemented on Linux operating system. + +Note regarding VLANs: On incoming packets the Linux kernel strips off the VLAN +tag before passing the data to the user space program on raw sockets. Special +measures are required to get the VLAN information, see NOEXPAND(packet(7)) PACKET_AUXDATA, +and to optionally insert the tag into the packet again, use option +link(retrieve-vlan)(OPTION_RETRIEVE_VLAN) when you need this. + +label(OPTION_RETRIEVE_VLAN)dit(bf(tt(retrieve-vlan))) + On packets incoming on raw sockets, retrieve the VLAN information and insert + it into the packets for further processing (Linux) -startdit() label(OPTION_IFF_UP)dit(bf(tt(iff-up))) Sets the TUN network interface status UP. Strongly recommended. label(OPTION_IFF_BROADCAST)dit(bf(tt(iff-broadcast))) diff --git a/xio-interface.c b/xio-interface.c index d0d10c1..d9d2e1c 100644 --- a/xio-interface.c +++ b/xio-interface.c @@ -10,6 +10,7 @@ #include "xioopen.h" #include "xio-socket.h" +#include "xio-ascii.h" #include "xio-interface.h" @@ -45,6 +46,9 @@ const struct optdesc opt_iff_portsel = { "iff-portsel", "portsel", O const struct optdesc opt_iff_automedia = { "iff-automedia", "automedia", OPT_IFF_AUTOMEDIA, GROUP_INTERFACE, PH_FD, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.interface.iff_opts), XIO_SIZEOF(para.interface.iff_opts), IFF_AUTOMEDIA }; #endif /*const struct optdesc opt_iff_dynamic = { "iff-dynamic", "dynamic", OPT_IFF_DYNAMIC, GROUP_INTERFACE, PH_FD, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.interface.iff_opts), XIO_SIZEOF(short), IFF_DYNAMIC };*/ +#ifdef PACKET_AUXDATA +const struct optdesc opt_retrieve_vlan = { "retrieve-vlan", NULL, OPT_RETRIEVE_VLAN, GROUP_INTERFACE, PH_LATE, TYPE_INT, OFUNC_SPEC }; +#endif #if LATER const struct optdesc opt_route = { "route", NULL, OPT_ROUTE, GROUP_INTERFACE, PH_INIT, TYPE_STRING, OFUNC_SPEC }; #endif @@ -122,6 +126,23 @@ int _xioopen_interface(const char *ifname, return 0; } +int _interface_setsockopt_auxdata(int fd, int auxdata) { +#ifdef PACKET_AUXDATA + /* Linux strips VLAN tag off incoming packets and makes it available per + ancillary data as auxdata. Apply option packet-auxdata if you want the + VLAN tag to be restored by Socat in the received packet */ + if (auxdata) { + int rc; + rc = Setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &auxdata, sizeof(auxdata)); + if (rc < 0) { + Error3("setsockopt(%d, SOL_PACKET, PACKET_AUXDATA, , {%d}): %s", + fd, auxdata, strerror(errno)); + } + } +#endif /* defined(PACKET_AUXDATA) */ + return 0; +} + static int xioopen_interface(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, groups_t groups, @@ -228,4 +249,69 @@ int _xiointerface_apply_iff( return 0; } + +#if HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA +/* Converts the ancillary message in *cmsg into a form useable for further + processing. Knows the specifics of common message types. + On PACKET_AUXDATA it stored the ancillary data in the XFD. + For other types: + 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 or other STAT_* + */ +int +xiolog_ancillary_packet(struct single *sfd, + struct cmsghdr *cmsg, int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen) { +#if LATER + const char *cmsgtype, *cmsgname, *cmsgenvn; + size_t msglen; +#endif + struct tpacket_auxdata *auxp; + int rc = STAT_OK; + + *num = 0; + +#if defined(CMSG_DATA) + +#if LATER + msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg); +#endif + switch (cmsg->cmsg_type) { +#if HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TPID + case PACKET_AUXDATA: +#if LATER + cmsgname = "packet_auxdata"; + cmsgtype = "auxdata"; + cmsgenvn = "AUXDATA"; +#endif + auxp = (struct tpacket_auxdata *)CMSG_DATA(cmsg); + Info8("%s(): Ancillary message: PACKET_AUXDATA: status="F_uint32_t", len="F_uint32_t", snaplen="F_uint32_t", mac="F_uint16_t", net="F_uint16_t", vlan_tci="F_uint16_t", vlan_tpid="F_uint16_t"", __func__, auxp->tp_status, auxp->tp_len, auxp->tp_snaplen, auxp->tp_mac, auxp->tp_net, auxp->tp_vlan_tci, auxp->tp_vlan_tpid); + sfd->para.socket.ancill_data_packet_auxdata = *auxp; + sfd->para.socket.ancill_flag.packet_auxdata = 1; + snprintf(typbuff, typlen, "PACKET.%u", cmsg->cmsg_type); + nambuff[0] = '\0'; strncat(nambuff, "vlan", namlen-1); + snprintf(strchr(valbuff, '\0')-1/*def \n*/, vallen-strlen(valbuff)+1, ", %d", auxp->tp_vlan_tci); + break; +#endif /* HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TPID */ + default: /* binary data */ + Warn1("xiolog_ancillary_packet(): INTERNAL: cmsg_type=%d not handled", cmsg->cmsg_type); + return rc; + } + return rc; + +#else /* !defined(CMSG_DATA) */ + + return STAT_NORETRY; + +#endif /* !defined(CMSG_DATA) */ +} +#endif /* HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA */ + #endif /* _WITH_INTERFACE */ diff --git a/xio-interface.h b/xio-interface.h index aafd5e5..19de3c9 100644 --- a/xio-interface.h +++ b/xio-interface.h @@ -25,11 +25,13 @@ extern const struct optdesc opt_iff_multicast; extern const struct optdesc opt_iff_portsel; extern const struct optdesc opt_iff_automedia; /*extern const struct optdesc opt_iff_dynamic;*/ +extern const struct optdesc opt_retrieve_vlan; extern int xiolog_ancillary_packet(struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen); extern int _xiointerface_get_iff(int sockfd, const char *name, short *save_iff); extern int _xiointerface_set_iff(int sockfd, const char *name, short new_iff); extern int _xiointerface_apply_iff(int sockfd, const char *name, short iff_opts[2]); +extern int _interface_setsockopt_auxdata(int fd, int auxdata); #endif /* !defined(__xio_interface_h_included) */ diff --git a/xio-ip.c b/xio-ip.c index 0d595e9..7962332 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -475,11 +475,15 @@ int xiogetaddrinfo(const char *node, const char *service, 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, - char *nambuff, int namlen, - char *envbuff, int envlen, - char *valbuff, int vallen) { +int xiolog_ancillary_ip( + struct single *sfd, + struct cmsghdr *cmsg, + int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen) +{ int cmsgctr = 0; const char *cmsgtype, *cmsgname = NULL, *cmsgenvn = NULL; size_t msglen; diff --git a/xio-ip.h b/xio-ip.h index ad6c8cb..7ae2092 100644 --- a/xio-ip.h +++ b/xio-ip.h @@ -43,12 +43,7 @@ extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, union sockaddr_union *sa, socklen_t *socklen, unsigned long res_opts0, unsigned long res_opts1); -extern -int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, - char *typbuff, int typlen, - char *nambuff, int namlen, - char *envbuff, int envlen, - char *valbuff, int vallen); +extern int xiolog_ancillary_ip(struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen); extern int xiotype_ip_add_membership(char *token, const struct optname *ent, struct opt *opt); extern int xioapply_ip_add_membership(xiosingle_t *xfd, struct opt *opt); extern int xiotype_ip_add_source_membership(char* token, const struct optname *ent, struct opt *opt); diff --git a/xio-ip6.c b/xio-ip6.c index fbcadcf..f648b2a 100644 --- a/xio-ip6.c +++ b/xio-ip6.c @@ -252,11 +252,15 @@ int xiocheckrange_ip6(struct sockaddr_in6 *pa, struct xiorange *range) { the respective len parameters specify the available space in the buffers returns STAT_OK on success */ -int xiolog_ancillary_ip6(struct cmsghdr *cmsg, int *num, - char *typbuff, int typlen, - char *nambuff, int namlen, - char *envbuff, int envlen, - char *valbuff, int vallen) { +int xiolog_ancillary_ip6( + struct single *xfd, + struct cmsghdr *cmsg, + int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen) +{ char scratch1[42]; /* can hold an IPv6 address in ASCII */ char scratch2[32]; size_t msglen; diff --git a/xio-ip6.h b/xio-ip6.h index d5dc7c2..fb25cb5 100644 --- a/xio-ip6.h +++ b/xio-ip6.h @@ -40,11 +40,7 @@ extern int xiorange_ip6andmask(struct xiorange *range); extern int xiocheckrange_ip6(struct sockaddr_in6 *pa, struct xiorange *range); extern -int xiolog_ancillary_ip6(struct cmsghdr *cmsg, int *num, - char *typbuff, int typlen, - char *nambuff, int namlen, - char *envbuff, int envlen, - char *valbuff, int vallen); +int xiolog_ancillary_ip6(struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen); extern int xiosetsockaddrenv_ip6(int idx, char *namebuff, size_t namelen, char *valuebuff, size_t valuelen, diff --git a/xio-ipapp.c b/xio-ipapp.c index b6c7866..98201ee 100644 --- a/xio-ipapp.c +++ b/xio-ipapp.c @@ -110,7 +110,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ } - while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + while ((pid = xio_fork(false, level, xfd->shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } diff --git a/xio-listen.c b/xio-listen.c index 4392242..c818c9f 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -20,7 +20,7 @@ const struct optdesc opt_backlog = { "backlog", NULL, OPT_BACKLOG, GROUP_LISTEN, PH_LISTEN, TYPE_INT, OFUNC_SPEC }; const struct optdesc opt_fork = { "fork", NULL, OPT_FORK, GROUP_CHILD, PH_PASTACCEPT, TYPE_BOOL, OFUNC_SPEC }; const struct optdesc opt_max_children = { "max-children", NULL, OPT_MAX_CHILDREN, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_SPEC }; -const struct optdesc opt_children_shutup = { "children-shutup", "child-shutup", OPT_CHILDREN_SHUTUP, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.shutup) }; +const struct optdesc opt_children_shutup = { "children-shutup", "child-shutup", OPT_CHILDREN_SHUTUP, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(shutup) }; /**/ #if (WITH_UDP || WITH_TCP) const struct optdesc opt_range = { "range", NULL, OPT_RANGE, GROUP_RANGE, PH_ACCEPT, TYPE_STRING, OFUNC_SPEC }; @@ -346,7 +346,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN, - xfd->para.socket.shutup)) + xfd->shutup)) < 0) { Close(xfd->fd); Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); diff --git a/xio-openssl.c b/xio-openssl.c index e83c203..3558ad2 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -409,7 +409,7 @@ static int if (xfd->forever || xfd->retry) { level = E_WARN; } - while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + while ((pid = xio_fork(false, level, xfd->shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } diff --git a/xio-proxy.c b/xio-proxy.c index 0e7b9b4..179d08f 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -199,7 +199,7 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, if (xfd->forever || xfd->retry) { level = E_WARN; } - while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + while ((pid = xio_fork(false, level, xfd->shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } diff --git a/xio-socket.c b/xio-socket.c index 543122e..f0a62a8 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -62,7 +62,7 @@ int _xioopen_socket_sendto(const char *pfname, const char *type, groups_t groups); static int -xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num, +xiolog_ancillary_socket(struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, @@ -713,7 +713,7 @@ int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts, #endif /* WITH_GENERICSOCKET */ /* EINTR not handled specially */ -int xiogetpacketinfo(int fd) +int xiogetpacketinfo(struct single *sfd, int fd) { #if defined(MSG_ERRQUEUE) int _errno = errno; @@ -748,7 +748,7 @@ int xiogetpacketinfo(int fd) sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*, sockaddr_info(&la->soa, sockname, sizeof(sockname))*/); - xiodopacketinfo(&msgh, true, true); + xiodopacketinfo(sfd, &msgh, true, true); } errno = _errno; #endif /* defined(MSG_ERRQUEUE) */ @@ -898,7 +898,7 @@ int _xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, return STAT_RETRYLATER; } else { /* try to find details about error, especially from ICMP */ - xiogetpacketinfo(xfd->fd); + xiogetpacketinfo(xfd, xfd->fd); /* continue mainstream */ Msg4(level, "connect(%d, %s, "F_Zd"): %s", @@ -994,7 +994,7 @@ int xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, so Notice is too weak */ } - while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + while ((pid = xio_fork(false, level, xfd->shutup)) < 0) { --xfd->retry; if (xfd->forever || xfd->retry) { dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); @@ -1079,7 +1079,7 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ applyopts_named(us->un.sun_path, opts, PH_LATE); } #endif - /*applyopts(xfd->fd, opts, PH_LATE);*/ + /*0 applyopts(xfd->fd, opts, PH_LATE); */ /* xfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */ Notice1("successfully prepared local socket %s", @@ -1260,7 +1260,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*, sockaddr_info(&la->soa, sockname, sizeof(sockname))*/); - xiodopacketinfo(&msgh, true, true); + xiodopacketinfo(xfd, &msgh, true, true); if (xiocheckpeer(xfd, pa, la) < 0) { /* drop packet */ @@ -1289,7 +1289,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, Error1("socketpair(PF_UNIX, SOCK_STREAM, 0, ...): %s", strerror(errno)); } - if ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + if ((pid = xio_fork(false, level, xfd->shutup)) < 0) { Close(trigger[0]); Close(trigger[1]); Close(xfd->fd); @@ -1461,7 +1461,12 @@ int xiogetancillary(int fd, struct msghdr *msgh, int flags) { and logs the relevant information (E_DEBUG, E_INFO). calls protocol/layer specific functions for handling the messages creates appropriate environment vars if withenv is set */ -int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) { +int xiodopacketinfo( + struct single *sfd, + struct msghdr *msgh, + bool withlog, + bool withenv) +{ #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) struct cmsghdr *cmsg; @@ -1490,14 +1495,14 @@ int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) { dependent */ switch (cmsg->cmsg_level) { case SOL_SOCKET: - xiolog_ancillary_socket(cmsg, &num, typbuff, sizeof(typbuff)-1, + xiolog_ancillary_socket(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, nambuff, sizeof(nambuff)-1, envbuff, sizeof(envbuff)-1, valbuff, sizeof(valbuff)-1); break; #if WITH_IP4 || WITH_IP6 case SOL_IP: - xiolog_ancillary_ip(cmsg, &num, typbuff, sizeof(typbuff)-1, + xiolog_ancillary_ip(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, nambuff, sizeof(nambuff)-1, envbuff, sizeof(envbuff)-1, valbuff, sizeof(valbuff)-1); @@ -1505,12 +1510,20 @@ int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) { #endif /* WITH_IP4 || WITH_IP6 */ #if WITH_IP6 case SOL_IPV6: - xiolog_ancillary_ip6(cmsg, &num, typbuff, sizeof(typbuff)-1, + xiolog_ancillary_ip6(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, nambuff, sizeof(nambuff)-1, envbuff, sizeof(envbuff)-1, valbuff, sizeof(valbuff)-1); break; #endif /* WITH_IP6 */ +#if HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA + case SOL_PACKET: + xiolog_ancillary_packet(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, + nambuff, sizeof(nambuff)-1, + envbuff, sizeof(envbuff)-1, + valbuff, sizeof(valbuff)-1); + break; +#endif /* HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA */ default: num = 1; snprintf(typbuff, sizeof(typbuff)-1, "LEVEL%u", cmsg->cmsg_level); @@ -1680,11 +1693,15 @@ int xiocheckpeer(xiosingle_t *xfd, returns STAT_OK or other STAT_* */ static int -xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num, - char *typbuff, int typlen, - char *nambuff, int namlen, - char *envbuff, int envlen, - char *valbuff, int vallen) { +xiolog_ancillary_socket( + struct single *sfd, + struct cmsghdr *cmsg, + int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen) +{ const char *cmsgtype, *cmsgname, *cmsgenvn; size_t msglen; struct timeval *tv; diff --git a/xio-socket.h b/xio-socket.h index 31d51ed..eb3a037 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -116,8 +116,7 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen, struct opt *opts, int pf, int socktype, int proto, int level); -extern -int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv); +extern int xiodopacketinfo(struct single *sfd, struct msghdr *msgh, bool withlog, bool withenv); extern int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags); extern diff --git a/xio-socks.c b/xio-socks.c index a9c8840..fd54b9a 100644 --- a/xio-socks.c +++ b/xio-socks.c @@ -175,7 +175,7 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ } - while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + while ((pid = xio_fork(false, level, xfd->shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; diff --git a/xio-udp.c b/xio-udp.c index 095fe16..26e3fb2 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -214,7 +214,7 @@ int _xioopen_ipdgram_listen(struct single *sfd, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (dofork) { - pid = xio_fork(false, E_ERROR, sfd->para.socket.shutup); + pid = xio_fork(false, E_ERROR, sfd->shutup); if (pid < 0) { return STAT_RETRYLATER; } diff --git a/xio-unix.c b/xio-unix.c index 17d41bc..3cc4f36 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -315,7 +315,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, so Notice is too weak */ } - while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { + while ((pid = xio_fork(false, level, xfd->shutup)) < 0) { --xfd->retry; if (xfd->forever || xfd->retry) { dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); diff --git a/xio.h b/xio.h index 71f82e3..596d101 100644 --- a/xio.h +++ b/xio.h @@ -206,6 +206,7 @@ typedef struct single { int (*sigchild)(struct single *); /* callback after sigchild */ int escape; /* escape character; -1 for no escape */ bool actescape; /* escape character found in input data */ + int shutup; /* children-shutup option */ union { struct { int fdout; /* use fd for output */ @@ -224,8 +225,14 @@ typedef struct single { #if _WITH_IP4 || _WITH_IP6 struct para_ip ip; #endif /* _WITH_IP4 || _WITH_IP6 */ - int shutup; - /* up to here, keep consistent copy in openssl part !!! */ +#if HAVE_STRUCT_TPACKET_AUXDATA + struct { + int packet_auxdata; + } ancill_flag; +#endif +#if HAVE_STRUCT_TPACKET_AUXDATA + struct tpacket_auxdata ancill_data_packet_auxdata; +#endif #if WITH_UNIX struct { bool tight; @@ -265,7 +272,6 @@ typedef struct single { #if _WITH_IP4 || _WITH_IP6 struct para_ip ip; #endif /* _WITH_IP4 || _WITH_IP6 */ - int shutup; /* end of the para.socket structure copy */ SSL_CTX* ctx; /* for freeing on close */ SSL *ssl; diff --git a/xioopts.c b/xioopts.c index e11aa6c..b9da980 100644 --- a/xioopts.c +++ b/xioopts.c @@ -1397,6 +1397,9 @@ const struct optname optionnames[] = { IF_PROXY ("resolve", &opt_proxy_resolve) #ifdef IP_RETOPTS IF_IP ("retopts", &opt_ip_retopts) +#endif +#if WITH_INTERFACE && defined(PACKET_AUXDATA) + IF_SOCKET ("retrieve-vlan", &opt_retrieve_vlan) #endif IF_RETRY ("retry", &opt_retry) IF_SOCKET ("reuseaddr", &opt_so_reuseaddr) @@ -3710,6 +3713,17 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) { } break; +#if _WITH_INTERFACE + case OPT_RETRIEVE_VLAN: + if (!xioparms.experimental) { + Warn("option retrieve-vlan is experimental"); + } + if (_interface_setsockopt_auxdata(fd, 1) < 0) { + return -1; + } + break; +#endif /* _WITH_INTERFACE */ + default: Error1("applyopts(): option \"%s\" not implemented", opt->desc->defname); opt->desc = ODESC_ERROR; ++opt; continue; diff --git a/xioopts.h b/xioopts.h index dd98ae7..0aa17e6 100644 --- a/xioopts.h +++ b/xioopts.h @@ -614,6 +614,7 @@ enum e_optcode { OPT_RES_RECURSE, /* resolver(3) */ OPT_RES_STAYOPEN, /* resolver(3) */ OPT_RES_USEVC, /* resolver(3) */ + OPT_RETRIEVE_VLAN, /* Linux: get VLAN info on raw sockets per auxdata */ OPT_RETRY, OPT_SANE, /* termios */ OPT_SCTP_MAXSEG, diff --git a/xioread.c b/xioread.c index c3d2da0..36bbe0b 100644 --- a/xioread.c +++ b/xioread.c @@ -198,6 +198,28 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { } #endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */ +#if defined(PF_PACKET) && HAVE_STRUCT_TPACKET_AUXDATA + if (from.soa.sa_family == PF_PACKET) { + Debug3("xioread(FD=%d, ...): auxdata: flag=%d, vlan-id=%d", + pipe->fd, pipe->para.socket.ancill_flag.packet_auxdata, + pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci); + if (pipe->para.socket.ancill_flag.packet_auxdata && + pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci != 0) { + int offs = 12; /* packet type id in Ethernet header */ + Debug1("xioread(%d, ...): restoring VLAN id from auxdata->tp_vlan_tci", + pipe->fd); + if (bytes+4 > bufsiz) { + Error("buffer too small to restore VLAN id"); + } + memmove((char *)buff+offs+4, (char *)buff+offs, bytes-offs); + ((unsigned short *)((char *)buff+offs))[0] = htons(ETH_P_8021Q); + ((unsigned short *)((char *)buff+offs))[1] = + htons(pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci); + bytes += 4; + } + } +#endif /* defined(PF_PACKET && HAVE_STRUCT_TPACKET_AUXDATA */ + Notice2("received packet with "F_Zu" bytes from %s", bytes, sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff))); @@ -384,7 +406,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { errno == EINTR) ; if (rc < 0) return -1; - xiodopacketinfo(&msgh, true, false); + xiodopacketinfo(pipe, &msgh, true, false); if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) { Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); /* drop */ errno = EAGAIN; return -1; @@ -420,6 +442,29 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { } #endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */ +#if defined(PF_PACKET) && HAVE_STRUCT_TPACKET_AUXDATA + if (from.soa.sa_family == PF_PACKET) { + Debug3("xioread(%d, ...): auxdata: flag=%d, vlan-id=%d", + pipe->fd, pipe->para.socket.ancill_flag.packet_auxdata, + pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci); + if (pipe->para.socket.ancill_flag.packet_auxdata && + pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci && + pipe->para.socket.ancill_data_packet_auxdata.tp_net >= 2) { + Debug2("xioread(%d, ...): restoring VLAN id %d", pipe->fd, pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci); + int offs = pipe->para.socket.ancill_data_packet_auxdata.tp_net - 2; + if (bytes+4 > bufsiz) { + Error("buffer too small to restore VLAN id"); + } + Debug3("xioread(): memmove(%p, %p, "F_Zu")", (char *)buff+offs+4, (char *)buff+offs, bytes-offs); + memmove((char *)buff+offs+4, (char *)buff+offs, bytes-offs); + ((unsigned short *)((char *)buff+offs))[0] = htons(ETH_P_8021Q); + ((unsigned short *)((char *)buff+offs))[1] = + htons(pipe->para.socket.ancill_data_packet_auxdata.tp_vlan_tci); + bytes += 4; + } + } +#endif /* defined(PF_PACKET) &&& HAVE_STRUCT_TPACKET_AUXDATA */ + Notice2("received packet with "F_Zu" bytes from %s", bytes, sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)));