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;