Use PACKET_IGNORE_OUTGOING when available; a few corrections and renamings for raw sockets and ancillary messages

This commit is contained in:
Gerhard Rieger 2023-10-26 19:12:38 +02:00
parent 47af17dbf2
commit 11d1e9e11f
12 changed files with 185 additions and 42 deletions

View file

@ -108,6 +108,10 @@ Porting:
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 @@ Documentation:
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:

View file

@ -1 +1 @@
"1.7.4.5+"
"1.7.4.5+vlans"

View file

@ -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

View file

@ -321,10 +321,10 @@ AC_ARG_ENABLE(interface, [ --disable-interface disable network interface suppo
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,

View file

@ -389,7 +389,7 @@ label(ADDRESS_IP_SENDTO)dit(bf(tt(IP-SENDTO:<host>:<protocol>)))
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),

View file

@ -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>

117
test.sh
View file

@ -760,7 +760,8 @@ testaddrs () {
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 @@ elif ! grep "ancillary message: $SCM_TYPE: $SCM_NAME=$SCM_VALUE\$" ${te}0 >/dev/
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 @@ elif ! expr "$(cat "$tf")" : "$SCM_VALUE\$" >/dev/null; then
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
fi ;; # NUMCOND, feats
esac
N=$((N+1))
set +vx
# test the DTLS server feature
NAME=OPENSSL_DTLS_SERVER
@ -15590,6 +15592,99 @@ esac
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 @@ if [ "$OPT_EXPECT_FAIL" ]; then
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

View file

@ -111,13 +111,14 @@ int _xioopen_interface(const char *ifname,
_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;
}

View file

@ -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 @@ int xiogetpacketinfo(int fd)
#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 @@ 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);
/*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 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
#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 @@ int retropt_socket_pf(struct opt *opts, int *pf) {
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 @@ int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) {
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),

View file

@ -84,6 +84,7 @@ extern
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,

View file

@ -81,7 +81,10 @@ int xioclose1(struct single *pipe) {
#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;
}

View file

@ -42,7 +42,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
#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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
#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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
#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 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
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;