net: decode setsockopt() multicast arguments
* configure.ac (AC_CHECK_FUNCS): Add inet_pton.
* net.c (print_mreq, print_mreq6): New functions.
(print_setsockopt): Use them to decode IP_ADD_MEMBERSHIP,
IP_DROP_MEMBERSHIP, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP,
IPV6_JOIN_ANYCAST, and IPV6_LEAVE_ANYCAST.
* tests/ip_mreq.c: New file.
* tests/ip_mreq.expected: Likewise.
* tests/ip_mreq.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add ip_mreq.
(TESTS): Add ip_mreq.test.
(EXTRA_DIST): ip_mreq.expected.
* tests/.gitignore: Add ip_mreq.
Based on patch by Ben Noordhuis <info@bnoordhuis.nl>.
diff --git a/configure.ac b/configure.ac
index 060a120..bb8bf46 100644
--- a/configure.ac
+++ b/configure.ac
@@ -233,6 +233,7 @@
fputs_unlocked
if_indextoname
inet_ntop
+ inet_pton
pipe2
prctl
preadv
diff --git a/net.c b/net.c
index fc325df..ff7b456 100644
--- a/net.c
+++ b/net.c
@@ -1307,6 +1307,64 @@
return 0;
}
+#ifdef IP_ADD_MEMBERSHIP
+static void
+print_mreq(struct tcb *tcp, long addr, unsigned int len)
+{
+ struct ip_mreq mreq;
+
+ if (len < sizeof(mreq)) {
+ printstr(tcp, addr, len);
+ return;
+ }
+ if (umove(tcp, addr, &mreq) < 0) {
+ tprintf("%#lx", addr);
+ return;
+ }
+ tprints("{imr_multiaddr=inet_addr(");
+ print_quoted_string(inet_ntoa(mreq.imr_multiaddr),
+ 16, QUOTE_0_TERMINATED);
+ tprints("), imr_interface=inet_addr(");
+ print_quoted_string(inet_ntoa(mreq.imr_interface),
+ 16, QUOTE_0_TERMINATED);
+ tprints(")}");
+}
+#endif /* IP_ADD_MEMBERSHIP */
+
+#ifdef IPV6_ADD_MEMBERSHIP
+static void
+print_mreq6(struct tcb *tcp, long addr, unsigned int len)
+{
+ struct ipv6_mreq mreq;
+
+ if (len < sizeof(mreq))
+ goto fail;
+
+ if (umove(tcp, addr, &mreq) < 0) {
+ tprintf("%#lx", addr);
+ return;
+ }
+
+#ifdef HAVE_INET_NTOP
+ const struct in6_addr *in6 = &mreq.ipv6mr_multiaddr;
+ char address[INET6_ADDRSTRLEN];
+
+ if (!inet_ntop(AF_INET6, in6, address, sizeof(address)))
+ goto fail;
+
+ tprints("{ipv6mr_multiaddr=inet_pton(");
+ print_quoted_string(address, sizeof(address), QUOTE_0_TERMINATED);
+ tprints("), ipv6mr_interface=");
+ print_ifindex(mreq.ipv6mr_interface);
+ tprints("}");
+ return;
+#endif /* HAVE_INET_NTOP */
+
+fail:
+ printstr(tcp, addr, len);
+}
+#endif /* IPV6_ADD_MEMBERSHIP */
+
#ifdef MCAST_JOIN_GROUP
static void
print_group_req(struct tcb *tcp, long addr, int len)
@@ -1420,6 +1478,12 @@
case SOL_IP:
switch (name) {
+#ifdef IP_ADD_MEMBERSHIP
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ print_mreq(tcp, addr, len);
+ goto done;
+#endif /* IP_ADD_MEMBERSHIP */
#ifdef MCAST_JOIN_GROUP
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
@@ -1429,6 +1493,23 @@
}
break;
+ case SOL_IPV6:
+ switch (name) {
+#ifdef IPV6_ADD_MEMBERSHIP
+ case IPV6_ADD_MEMBERSHIP:
+ case IPV6_DROP_MEMBERSHIP:
+# ifdef IPV6_JOIN_ANYCAST
+ case IPV6_JOIN_ANYCAST:
+# endif
+# ifdef IPV6_LEAVE_ANYCAST
+ case IPV6_LEAVE_ANYCAST:
+# endif
+ print_mreq6(tcp, addr, len);
+ goto done;
+#endif /* IPV6_ADD_MEMBERSHIP */
+ }
+ break;
+
case SOL_PACKET:
switch (name) {
#ifdef PACKET_RX_RING
diff --git a/tests/.gitignore b/tests/.gitignore
index 6903a5c..c3eada9 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -3,6 +3,7 @@
getrandom
inet-accept-connect-send-recv
ioctl
+ip_mreq
ipc_msg
ipc_sem
ipc_shm
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7ace705..f1e8820 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -14,6 +14,7 @@
getrandom \
inet-accept-connect-send-recv \
ioctl \
+ ip_mreq \
ipc_msg \
ipc_sem \
ipc_shm \
@@ -61,6 +62,7 @@
getdents.test \
getrandom.test \
ioctl.test \
+ ip_mreq.test \
ipc_msg.test \
ipc_shm.test \
ipc_sem.test \
@@ -109,6 +111,7 @@
getdents.out \
getrandom.awk \
ioctl.expected \
+ ip_mreq.expected \
ipc.sh \
mmsg.expected \
net.expected \
diff --git a/tests/ip_mreq.c b/tests/ip_mreq.c
new file mode 100644
index 0000000..db208b8
--- /dev/null
+++ b/tests/ip_mreq.c
@@ -0,0 +1,45 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <assert.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+int
+main(void)
+{
+#if defined IP_ADD_MEMBERSHIP && defined IPV6_ADD_MEMBERSHIP \
+ && defined IPV6_JOIN_ANYCAST && defined HAVE_INET_PTON
+ struct ip_mreq m4;
+ struct ipv6_mreq m6;
+
+ inet_pton(AF_INET, "224.0.0.3", &m4.imr_multiaddr);
+ inet_pton(AF_INET, "127.0.0.1", &m4.imr_interface);
+ inet_pton(AF_INET6, "ff01::c", &m6.ipv6mr_multiaddr);
+ m6.ipv6mr_interface = 1;
+
+ (void) close(0);
+ assert(socket(AF_INET, SOCK_DGRAM, 0) == 0);
+
+ assert(setsockopt(0, SOL_IP, IP_ADD_MEMBERSHIP, &m4, 1) == -1);
+ assert(setsockopt(0, SOL_IP, IP_DROP_MEMBERSHIP, &m4, 1) == -1);
+ assert(setsockopt(0, SOL_IP, IP_ADD_MEMBERSHIP, &m4, sizeof(m4)) == 0);
+ assert(setsockopt(0, SOL_IP, IP_DROP_MEMBERSHIP, &m4, sizeof(m4)) == 0);
+
+ assert(setsockopt(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, 1) == -1);
+ assert(setsockopt(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, 1) == -1);
+ assert(setsockopt(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, sizeof(m6)) == -1);
+ assert(setsockopt(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, sizeof(m6)) == -1);
+
+ assert(setsockopt(0, SOL_IPV6, IPV6_JOIN_ANYCAST, &m6, 1) == -1);
+ assert(setsockopt(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, &m6, 1) == -1);
+ assert(setsockopt(0, SOL_IPV6, IPV6_JOIN_ANYCAST, &m6, sizeof(m6)) == -1);
+ assert(setsockopt(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, &m6, sizeof(m6)) == -1);
+
+ return 0;
+#else
+ return 77;
+#endif
+}
diff --git a/tests/ip_mreq.expected b/tests/ip_mreq.expected
new file mode 100644
index 0000000..e694c73
--- /dev/null
+++ b/tests/ip_mreq.expected
@@ -0,0 +1,12 @@
+setsockopt\(0, SOL_IP, IP_ADD_MEMBERSHIP, "\\340", 1\) = -1 EINVAL .*
+setsockopt\(0, SOL_IP, IP_DROP_MEMBERSHIP, "\\340", 1\) = -1 EINVAL .*
+setsockopt\(0, SOL_IP, IP_ADD_MEMBERSHIP, \{imr_multiaddr=inet_addr\("224\.0\.0\.3"\), imr_interface=inet_addr\("127\.0\.0\.1"\)\}, 8\) = 0
+setsockopt\(0, SOL_IP, IP_DROP_MEMBERSHIP, \{imr_multiaddr=inet_addr\("224\.0\.0\.3"\), imr_interface=inet_addr\("127\.0\.0\.1"\)\}, 8\) = 0
+setsockopt\(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_JOIN_ANYCAST, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_JOIN_ANYCAST, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
diff --git a/tests/ip_mreq.test b/tests/ip_mreq.test
new file mode 100755
index 0000000..d423b1b
--- /dev/null
+++ b/tests/ip_mreq.test
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Check {IP,IPV6}_{ADD,DROP}_MEMBERSHIP setsockopt decoding.
+
+. "${srcdir=.}/init.sh"
+
+run_prog
+run_strace -e setsockopt $args
+match_grep
+
+exit 0