Use PACKET_IGNORE_OUTGOING when available; a few corrections and renamings for raw sockets and ancillary messages
diff --git a/CHANGES b/CHANGES
index 55dcaba..97a0cbf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -108,6 +108,10 @@
Removed Config/ because its contents have not been maintained for many
years.
+ Try to not receive outgoing packets on raw (PF_PACKET) sockets - use
+ PACKET_IGNORE_OUTGOING socket options when available.
+ Test: INTERFACE_IGNOREOUTGOING
+
Testing:
Removed obselete parts from test.sh
@@ -117,6 +121,8 @@
Added doc for option ipv6-join-group (ipv6-add-membership)
Thanks to Martin Buck for sending the patch.
+ Renamed xiogetpacketsrc() to xiogetancillary()
+
####################### V 1.7.4.5 (not released):
Corrections:
diff --git a/VERSION b/VERSION
index ceaf471..7fd3594 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-"1.7.4.5+"
+"1.7.4.5+vlans"
diff --git a/config.h.in b/config.h.in
index 6ca2507..dfeaab8 100644
--- a/config.h.in
+++ b/config.h.in
@@ -285,8 +285,8 @@
/* Define if you have the <linux/vm_sockets.h> header file. */
#undef HAVE_LINUX_VM_SOCKETS_H
-/* Define if you have the <netpacket/packet.h> header file. */
-#undef HAVE_NETPACKET_PACKET_H
+/* Define if you have the <linux/if_packet.h> header file. */
+#undef HAVE_LINUX_IF_PACKET_H
/* Define if you have the <netinet/if_ether.h> header file. */
#undef HAVE_NETINET_IF_ETHER_H
diff --git a/configure.ac b/configure.ac
index 60e269e..42ac783 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,10 +321,10 @@
esac],
[AC_MSG_RESULT(yes); WITH_INTERFACE=1 ])
if test "$WITH_INTERFACE"; then
- AC_CHECK_HEADER(netpacket/packet.h,
- AC_DEFINE(HAVE_NETPACKET_PACKET_H),
+ AC_CHECK_HEADER(linux/if_packet.h,
+ AC_DEFINE(HAVE_LINUX_IF_PACKET_H),
[WITH_INTERFACE=;
- AC_MSG_WARN([include file netpacket/packet.h not found, disabling interface])])
+ AC_MSG_WARN([include file linux/if_packet.h not found, disabling interface])])
fi
if test "$WITH_INTERFACE"; then
AC_CHECK_HEADER(netinet/if_ether.h,
diff --git a/doc/socat.yo b/doc/socat.yo
index 93f201a..3e8ce58 100644
--- a/doc/socat.yo
+++ b/doc/socat.yo
@@ -389,7 +389,7 @@
label(ADDRESS_INTERFACE)dit(bf(tt(INTERFACE:<interface>)))
Communicates with a network connected on an interface using raw packets
including link level data. link(<interface>)(TYPE_INTERFACE) is the name of
- the network interface. Currently only available on Linux.
+ the network interface. Currently only available on Linux.nl()
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET) nl()
Useful options:
link(pf)(OPTION_PROTOCOL_FAMILY),
diff --git a/sysincludes.h b/sysincludes.h
index 4d1078f..01acdb3 100644
--- a/sysincludes.h
+++ b/sysincludes.h
@@ -141,8 +141,8 @@
#if HAVE_LINUX_ERRQUEUE_H
#include <linux/errqueue.h> /* struct sock_extended_err */
#endif
-#if HAVE_NETPACKET_PACKET_H
-#include <netpacket/packet.h>
+#if HAVE_LINUX_IF_PACKET_H
+#include <linux/if_packet.h>
#endif
#if HAVE_NETINET_IF_ETHER_H
#include <netinet/if_ether.h>
diff --git a/test.sh b/test.sh
index 9fe7522..c873df6 100755
--- a/test.sh
+++ b/test.sh
@@ -760,7 +760,8 @@
local a A;
for a in $@; do
A=$(echo "$a" |tr 'a-z' 'A-Z')
- if ! $SOCAT $A /dev/null 2>&1 </dev/null |grep -q "E unknown device/address"; then
+ # the ::::: forces syntax errer and prevents the address from doing anything
+ if ! $SOCAT $A::::: /dev/null 2>&1 </dev/null |grep -q "E unknown device/address"; then
shift
continue
fi
@@ -9468,9 +9469,10 @@
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
- if [ -n "$debug" ]; then
- grep " $LEVELS " "${te}0"; echo; grep " $LEVELS " "${te}1";
- fi
+ if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
+ if [ "$DEBUG" ]; then grep " $LEVELS " "${te}0" >&2; fi
+ if [ "$VERBOSE" ]; then echo "echo XYZ |$CMD1"; fi
+ if [ "$DEBUG" ]; then grep " $LEVELS " "${te}1" >&2; fi
numOK=$((numOK+1))
fi
else # option is not supported
@@ -9716,9 +9718,10 @@
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
- if [ -n "$debug" ]; then
- cat "${te}0"; echo; cat "${te}1";
- fi
+ if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
+ if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
+ if [ "$VERBOSE" ]; then echo "{ echo XYZ; sleep 0.1; } |$CMD1"; fi
+ if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numOK=$((numOK+1))
fi
else # option is not supported
@@ -13148,7 +13151,6 @@
fi ;; # NUMCOND, feats
esac
N=$((N+1))
-set +vx
# test the DTLS server feature
NAME=OPENSSL_DTLS_SERVER
@@ -15590,6 +15592,99 @@
N=$((N+1))
+# Socats INTERFACE address has to ignore outgoing packets if possible.
+# On Linux is uses socket option PACKET_IGNORE_OUTGOING or it queries per
+# packet the PACKET_OUTGOING flag of struct sockaddr_ll.sll_pkttype
+NAME=INTERFACE_IGNOREOUTGOING
+case "$TESTS" in
+*%$N%*|*%functions%*|*%interface%*|*%tun%*|*%root%*|*%$NAME%*)
+TEST="$NAME: INTERFACE ignores outgoing packets"
+#idea: create a TUN interface and hook with INTERFACE.
+# Send a packet out the interface, should not be seen by INTERFACE
+if ! eval $NUMCOND; then :;
+elif [ $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then
+ $PRINTF "test $F_n $TEST... ${YELLOW}must be root${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+elif ! $(type ping >/dev/null 2>&1); then
+ $PRINTF "test $F_n $TEST... ${YELLOW}ping not available${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+elif ! feat=$(testfeats TUN STDIO INTERFACE); then
+ $PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+elif ! A=$(testaddrs - TUN STDIO INTERFACE); then
+ $PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+elif ! o=$(testoptions iff-up tun-type tun-name ) >/dev/null; then
+ $PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+elif ! runsip4 >/dev/null; then
+ $PRINTF "test $F_n $TEST... ${YELLOW}IPv4 not available${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+else
+tf="$td/test$N.stdout"
+te="$td/test$N.stderr"
+tdiff="$td/test$N.diff"
+tl="$td/test$N.lock"
+da="$(date) $RANDOM"
+TUNNET=10.255.255
+TUNNAME=tun9
+CMD0="$TRACE $SOCAT $opts -L $tl TUN:$TUNNET.1/24,iff-up=1,tun-type=tun,tun-name=$TUNNAME -"
+CMD1="$TRACE $SOCAT $opts -u INTERFACE:$TUNNAME -"
+CMD2="ping -c 1 -w 1 -b $TUNNET.255"
+printf "test $F_n $TEST... " $N
+sleep 1 |$CMD0 2>"${te}0" >/dev/null &
+pid0="$!"
+#waitinterface "$TUNNAME"
+usleep $MICROS
+$CMD1 >"${tf}1" 2>"${te}1" &
+pid1="$!"
+usleep $MICROS
+$CMD2 2>"${te}2" 1>&2
+kill $pid1 2>/dev/null
+usleep $MICROS
+kill $pid0 2>/dev/null
+wait
+if [ $? -ne 0 ]; then
+ $PRINTF "$FAILED: $TRACE $SOCAT:\n"
+ echo "$CMD0 &"
+ cat "${te}0" >&2
+ echo "$CMD1"
+ cat "${te}1" >&2
+ echo "$CMD2"
+ cat "${te}2" >&2
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+elif test -s "${tf}1"; then
+ $PRINTF "$FAILED\n"
+ echo "$CMD0 &"
+ cat "${te}0" >&2
+ echo "$CMD1"
+ cat "${te}1" >&2
+ echo "$CMD2"
+ cat "${te}2" >&2
+ numFAIL=$((numFAIL+1))
+ listFAIL="$listFAIL $N"
+else
+ $PRINTF "$OK\n"
+ if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
+ if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
+ if [ "$VERBOSE" ]; then echo "$CMD1"; fi
+ if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
+ if [ "$VERBOSE" ]; then echo "$CMD2"; fi
+ if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
+ numOK=$((numOK+1))
+fi
+fi ;; # NUMCOND, feats
+esac
+PORT=$((PORT+1))
+N=$((N+1))
+
# end of common tests
##################################################################################
@@ -15694,9 +15789,11 @@
grep "^< " "$td/failed.diff" |awk '{print($2);}' >"$td/ok.unexp"
ln -s "$td/ok.unexp" .
echo "OK unexpected: $(cat "$td/ok.unexp" |xargs echo)"
+else
+ touch "$td/failed.diff"
fi
-listFAIL=$(cat "$td/failed.lst" |xargs echo)
-numFAIL="$(wc -l "$td/failed.lst" |awk '{print($1);}')"
+#listFAIL=$(cat "$td/failed.lst" |xargs echo)
+#numFAIL="$(wc -l "$td/failed.lst" |awk '{print($1);}')"
! test -s "$td/failed.unexp"
exit
diff --git a/xio-interface.c b/xio-interface.c
index cfd467b..d0d10c1 100644
--- a/xio-interface.c
+++ b/xio-interface.c
@@ -111,13 +111,14 @@
_xiointerface_apply_iff(xfd->fd, ifname, xfd->para.interface.iff_opts);
#ifdef PACKET_IGNORE_OUTGOING
- /* Raw socket may even provide packets that are outbound - we are not
+ /* Raw socket might also provide packets that are outbound - we are not
interested in these and disable this "feature" in kernel if possible */
if (Setsockopt(xfd->fd, SOL_PACKET, PACKET_IGNORE_OUTGOING, &one, sizeof(one)) < 0) {
Warn2("setsockopt(%d, SOL_PACKET, PACKET_IGNORE_OUTGOING, {1}): %s",
xfd->fd, strerror(errno));
}
-#endif
+#endif /*defined(PACKET_IGNORE_OUTGOING) */
+
return 0;
}
diff --git a/xio-socket.c b/xio-socket.c
index 4497c1e..543122e 100644
--- a/xio-socket.c
+++ b/xio-socket.c
@@ -25,6 +25,7 @@
#endif /* WITH_IP6 */
#include "xio-ip.h"
#include "xio-listen.h"
+#include "xio-interface.h"
#include "xio-ipapp.h" /*! not clean */
#include "xio-tcpwrap.h"
@@ -733,7 +734,7 @@
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
#endif
- if (xiogetpacketsrc(fd,
+ if (xiogetancillary(fd,
&msgh,
MSG_ERRQUEUE
#ifdef MSG_TRUNC
@@ -1078,7 +1079,7 @@
applyopts_named(us->un.sun_path, opts, PH_LATE);
}
#endif
- applyopts(xfd->fd, opts, PH_LATE);
+ /*applyopts(xfd->fd, opts, PH_LATE);*/
/* xfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */
Notice1("successfully prepared local socket %s",
@@ -1244,7 +1245,7 @@
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
#endif
- while ((rc = xiogetpacketsrc(xfd->fd,
+ while ((rc = xiogetancillary(xfd->fd,
&msgh,
MSG_PEEK
#ifdef MSG_TRUNC
@@ -1431,8 +1432,9 @@
In msgh the msg_name pointer must refer to an (empty) sockaddr storage.
Returns STAT_OK on success, or STAT_RETRYLATER when an error occurred,
including EINTR.
+ (recvmsg() retrieves just meta info, not the data)
*/
-int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags) {
+int xiogetancillary(int fd, struct msghdr *msgh, int flags) {
char peekbuff[1];
#if HAVE_STRUCT_IOVEC
struct iovec iovec;
@@ -1473,6 +1475,8 @@
char valbuff[256], *valp;
char envbuff[256], *envp;
+ Info3("ancillary message in xiodopacketinfo(): len="F_Zu", level=%d, type=%d",
+ cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
if (withlog) {
xiodump(CMSG_DATA(cmsg),
cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
diff --git a/xio-socket.h b/xio-socket.h
index 2d36132..31d51ed 100644
--- a/xio-socket.h
+++ b/xio-socket.h
@@ -84,6 +84,7 @@
char *xiogetifname(int ind, char *val, int ins);
extern int retropt_socket_pf(struct opt *opts, int *pf);
+extern int xiogetancillary(int fd, struct msghdr *msgh, int flags);
extern int xioopen_connect(struct single *fd,
union sockaddr_union *us, size_t uslen,
diff --git a/xioclose.c b/xioclose.c
index 3bbfca7..2a236d1 100644
--- a/xioclose.c
+++ b/xioclose.c
@@ -81,7 +81,10 @@
#if WITH_INTERFACE
case END_INTERFACE:
{
- _xiointerface_set_iff(pipe->fd, pipe->para.interface.name, pipe->para.interface.save_iff);
+ if (pipe->para.interface.name[0] != '\0') {
+ _xiointerface_set_iff(pipe->fd, pipe->para.interface.name,
+ pipe->para.interface.save_iff);
+ }
if (Close(pipe->fd) < 0) {
Info2("close(%d): %s", pipe->fd, strerror(errno)); } break;
}
diff --git a/xioread.c b/xioread.c
index b5ec837..c3d2da0 100644
--- a/xioread.c
+++ b/xioread.c
@@ -42,7 +42,7 @@
if (pipe->readbytes) {
if (pipe->actbytes == 0) {
- Info("xioread(): readbytes consumed, inserting EOF");
+ Info1("xioread(%d, ...): readbytes consumed, inserting EOF", pipe->fd);
return 0; /* EOF by count */
}
@@ -135,6 +135,10 @@
#if _WITH_SOCKET
case XIOREAD_RECV:
if (pipe->dtype & XIOREAD_RECV_FROM) {
+ /* Receiving packets in addresses of RECVFROM types, the sender address
+ has already been determined in OPEN phase. */
+ Debug1("%s(): XIOREAD_RECV and XIOREAD_RECV_FROM (peer checks already done)",
+ __func__);
#if WITH_RAWIP || WITH_UDP || WITH_UNIX
struct msghdr msgh = {0};
union sockaddr_union from = {{0}};
@@ -151,8 +155,7 @@
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
#endif
-
- while ((rc = xiogetpacketsrc(pipe->fd, &msgh,
+ while ((rc = xiogetancillary(pipe->fd, &msgh,
MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
@@ -161,6 +164,9 @@
errno == EINTR) ;
if (rc < 0) return -1;
+ /* Note: we do not call xiodopacketinfo() and xiocheckpeer() here because
+ that already happened in xioopen() / _xioopen_dgram_recvfrom() ... */
+
do {
bytes =
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen);
@@ -175,16 +181,22 @@
errno = _errno;
return -1;
}
- /* on packet type we also receive outgoing packets, this is not desired
- */
-#if defined(PF_PACKET) && defined(PACKET_OUTGOING)
+
+#if defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING)
+ /* In future versions there may be an option that controls receiving of
+ outgoing packets, but currently it is hardcoded that we try to avoid
+ them - either by once setting socket option PACKET_IGNORE_OUTGOING
+ when available, otherwise by checking flag PACKET_OUTGOING per packet.
+ */
if (from.soa.sa_family == PF_PACKET) {
- if ((from.ll.sll_pkttype & PACKET_OUTGOING)
- == 0) {
- errno = EAGAIN; return -1;
+ if ((from.ll.sll_pkttype & PACKET_OUTGOING) != 0) {
+ Info2("%s(fd=%d): ignoring outgoing packet", __func__, pipe->fd);
+ errno = EAGAIN;
+ return -1;
}
+ Debug2("%s(fd=%d): packet is not outgoing - process it", __func__, pipe->fd);
}
-#endif /* defined(PF_PACKET) && defined(PACKET_OUTGOING) */
+#endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */
Notice2("received packet with "F_Zu" bytes from %s",
bytes,
@@ -336,19 +348,24 @@
continue;
}
#endif
-#else /* !WITH_RAWIP */
+#else /* !(WITH_RAWIP || WITH_UDP || WITH_UNIX) */
Fatal("address requires raw sockets, but they are not compiled in");
return -1;
-#endif /* !WITH_RAWIP || WITH_UDP || WITH_UNIX */
+#endif /* !(WITH_RAWIP || WITH_UDP || WITH_UNIX) */
} else /* ~XIOREAD_RECV_FROM */ {
- union sockaddr_union from; socklen_t fromlen = sizeof(from);
- char infobuff[256];
+ /* Receiving packets without planning to answer to the sender, but we
+ might need sender info for some checks, thus we use recvfrom() */
struct msghdr msgh = {0};
+ union sockaddr_union from = {{ 0 }};
+ socklen_t fromlen = sizeof(from);
+ char infobuff[256];
char ctrlbuff[1024]; /* ancillary messages */
int rc;
- socket_init(pipe->para.socket.la.soa.sa_family, &from);
+ Debug1("%s(): XIOREAD_RECV and not XIOREAD_RECV_FROM (peer checks to be done)",
+ __func__);
+
/* get source address */
msgh.msg_name = &from;
msgh.msg_namelen = fromlen;
@@ -358,7 +375,7 @@
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
msgh.msg_controllen = sizeof(ctrlbuff);
#endif
- while ((rc = xiogetpacketsrc(pipe->fd, &msgh,
+ while ((rc = xiogetancillary(pipe->fd, &msgh,
MSG_PEEK
#ifdef MSG_TRUNC
|MSG_TRUNC
@@ -390,9 +407,23 @@
errno = _errno;
return -1;
}
+
+#if defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING)
+ /* For remarks see similar section above */
+ if (from.soa.sa_family == PF_PACKET) {
+ if ((from.ll.sll_pkttype & PACKET_OUTGOING) != 0) {
+ Info2("%s(fd=%d): ignoring outgoing packet", __func__, pipe->fd);
+ errno = EAGAIN;
+ return -1;
+ }
+ Debug2("%s(fd=%d): packet is not outgoing - process it", __func__, pipe->fd);
+ }
+#endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */
+
Notice2("received packet with "F_Zu" bytes from %s",
bytes,
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)));
+
if (bytes == 0) {
if (!pipe->para.socket.null_eof) {
errno = EAGAIN; return -1;