Page MenuHomeFreeBSD

D35909.id108633.diff
No OneTemporary

D35909.id108633.diff

Index: lib/libc/sys/recv.2
===================================================================
--- lib/libc/sys/recv.2
+++ lib/libc/sys/recv.2
@@ -163,6 +163,7 @@
.Bl -column ".Dv MSG_CMSG_CLOEXEC" -offset indent
.It Dv MSG_OOB Ta process out-of-band data
.It Dv MSG_PEEK Ta peek at incoming message
+.It Dv MSG_TRUNC Ta return real packet or datagram length
.It Dv MSG_WAITALL Ta wait for full request or error
.It Dv MSG_DONTWAIT Ta do not block
.It Dv MSG_CMSG_CLOEXEC Ta set received fds close-on-exec
@@ -185,6 +186,17 @@
data from the queue.
Thus, a subsequent receive call will return the same data.
The
+.Dv MSG_TRUNC
+flag causes the receive operation to return the full length of the packet
+or datagram even if larger than provided buffer. The flag is supported
+on SOCK_DGRAM sockets for
+.Dv AF_INET
+,
+.Dv AF_INET6
+and
+.Dv AF_UNIX
+families.
+The
.Dv MSG_WAITALL
flag requests that the operation block until
the full request is satisfied.
Index: sys/kern/uipc_socket.c
===================================================================
--- sys/kern/uipc_socket.c
+++ sys/kern/uipc_socket.c
@@ -1896,15 +1896,18 @@
struct mbuf *nextrecord;
int moff, type = 0;
ssize_t orig_resid = uio->uio_resid;
+ bool report_real_len = false;
mp = mp0;
if (psa != NULL)
*psa = NULL;
if (controlp != NULL)
*controlp = NULL;
- if (flagsp != NULL)
+ if (flagsp != NULL) {
+ report_real_len = *flagsp & MSG_TRUNC;
+ *flagsp &= ~MSG_TRUNC;
flags = *flagsp &~ MSG_EOR;
- else
+ } else
flags = 0;
if (flags & MSG_OOB)
return (soreceive_rcvoob(so, uio, flags));
@@ -1978,7 +1981,7 @@
error = ENOTCONN;
goto release;
}
- if (uio->uio_resid == 0) {
+ if (uio->uio_resid == 0 && !report_real_len) {
SOCKBUF_UNLOCK(&so->so_rcv);
goto release;
}
@@ -2326,6 +2329,8 @@
SOCKBUF_LOCK_ASSERT(&so->so_rcv);
if (m != NULL && pr->pr_flags & PR_ATOMIC) {
+ if (report_real_len)
+ uio->uio_resid -= m_length(m, NULL) - moff;
flags |= MSG_TRUNC;
if ((flags & MSG_PEEK) == 0)
(void) sbdroprecord_locked(&so->so_rcv);
@@ -2624,7 +2629,7 @@
* For any complicated cases, fall back to the full
* soreceive_generic().
*/
- if (mp0 != NULL || (flags & MSG_PEEK) || (flags & MSG_OOB))
+ if (mp0 != NULL || (flags & (MSG_PEEK | MSG_OOB | MSG_TRUNC)))
return (soreceive_generic(so, psa, uio, mp0, controlp,
flagsp));
Index: sys/kern/uipc_usrreq.c
===================================================================
--- sys/kern/uipc_usrreq.c
+++ sys/kern/uipc_usrreq.c
@@ -1417,7 +1417,7 @@
uipc_peek_dgram(struct socket *so, struct mbuf *m, struct sockaddr **psa,
struct uio *uio, struct mbuf **controlp, int *flagsp)
{
- ssize_t len;
+ ssize_t len = 0;
int error;
so->so_rcv.uxdg_peeked = m;
@@ -1459,8 +1459,16 @@
}
SOCK_IO_RECV_UNLOCK(so);
- if (m != NULL && flagsp != NULL)
- *flagsp |= MSG_TRUNC;
+ if (flagsp != NULL) {
+ if (m != NULL) {
+ if (*flagsp & MSG_TRUNC) {
+ /* Report real length of the packet */
+ uio->uio_resid -= m_length(m, NULL) - len;
+ }
+ *flagsp |= MSG_TRUNC;
+ } else
+ *flagsp &= ~MSG_TRUNC;
+ }
return (0);
}
@@ -1475,7 +1483,7 @@
struct sockbuf *sb = NULL;
struct mbuf *m;
int flags, error;
- ssize_t len;
+ ssize_t len = 0;
bool nonblock;
MPASS(mp0 == NULL);
@@ -1619,11 +1627,16 @@
SOCK_IO_RECV_UNLOCK(so);
if (m != NULL) {
- flags |= MSG_TRUNC;
+ if (flagsp != NULL) {
+ if (flags & MSG_TRUNC) {
+ /* Report real length of the packet */
+ uio->uio_resid -= m_length(m, NULL);
+ }
+ *flagsp |= MSG_TRUNC;
+ }
m_freem(m);
- }
- if (flagsp != NULL)
- *flagsp |= flags;
+ } else if (flagsp != NULL)
+ *flagsp &= ~MSG_TRUNC;
return (0);
}
Index: sys/netinet6/ip6_input.c
===================================================================
--- sys/netinet6/ip6_input.c
+++ sys/netinet6/ip6_input.c
@@ -824,7 +824,7 @@
ip6_sprintf(ip6bufd, &ip6->ip6_dst)));
goto bad;
}
- if (V_ip6_sav && !(rcvif->if_flags & IFF_LOOPBACK) &&
+ if (V_ip6_sav && !(m->m_flags & M_LOOP) &&
__predict_false(in6_localip_fib(&ip6->ip6_src,
rcvif->if_fib))) {
IP6STAT_INC(ip6s_badscope); /* XXX */
Index: sys/netinet6/ip6_output.c
===================================================================
--- sys/netinet6/ip6_output.c
+++ sys/netinet6/ip6_output.c
@@ -688,8 +688,8 @@
if (ro->ro_nh != NULL && fwd_tag == NULL &&
ro->ro_dst.sin6_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) {
+ /* Nexthop is valid and contains valid ifp */
nh = ro->ro_nh;
- ifp = nh->nh_ifp;
} else {
if (ro->ro_lle)
LLE_FREE(ro->ro_lle); /* zeros ro_lle */
@@ -708,8 +708,11 @@
in6_ifstat_inc(ifp, ifs6_out_discard);
goto bad;
}
- if (ifp != NULL)
- mtu = ifp->if_mtu;
+ /*
+ * At this point at least @ifp is not NULL
+ * Can be the case when dst is multicast, link-local or
+ * interface is explicitly specificed by the caller.
+ */
}
if (nh == NULL) {
/*
@@ -717,9 +720,11 @@
* dst may not have been updated.
*/
*dst = dst_sa; /* XXX */
+ origifp = ifp;
+ mtu = ifp->if_mtu;
} else {
- if (nh->nh_flags & NHF_HOST)
- mtu = nh->nh_mtu;
+ ifp = nh->nh_ifp;
+ origifp = nh->nh_aifp;
ia = (struct in6_ifaddr *)(nh->nh_ifa);
counter_u64_add(nh->nh_pksent, 1);
}
@@ -740,6 +745,7 @@
(ifp = im6o->im6o_multicast_ifp) != NULL) {
/* We do not need a route lookup. */
*dst = dst_sa; /* XXX */
+ origifp = ifp;
goto nonh6lookup;
}
@@ -754,6 +760,7 @@
goto bad;
}
*dst = dst_sa; /* XXX */
+ origifp = ifp;
goto nonh6lookup;
}
}
@@ -768,7 +775,7 @@
}
ifp = nh->nh_ifp;
- mtu = nh->nh_mtu;
+ origifp = nh->nh_aifp;
ia = ifatoia6(nh->nh_ifa);
if (nh->nh_flags & NHF_GATEWAY)
dst->sin6_addr = nh->gw6_sa.sin6_addr;
@@ -777,8 +784,21 @@
nonh6lookup:
;
}
+ /*
+ * At this point ifp MUST be pointing to the valid transmit ifp.
+ * origifp MUST be valid and pointing to either the same ifp or,
+ * in case of loopback output, to the interface which ip6_src
+ * belongs to.
+ * Examples:
+ * fe80::1%em0 -> fe80::2%em0 -> ifp=em0, origifp=em0
+ * fe80::1%em0 -> fe80::1%em0 -> ifp=lo0, origifp=em0
+ * ::1 -> ::1 -> ifp=lo0, origifp=lo0
+ *
+ * mtu can be 0 and will be refined later.
+ */
+ KASSERT((ifp != NULL), ("output interface must not be NULL"));
+ KASSERT((origifp != NULL), ("output address interface must not be NULL"));
- /* Then nh (for unicast) and ifp must be non-NULL valid values. */
if ((flags & IPV6_FORWARDING) == 0) {
/* XXX: the FORWARDING flag can be set for mrouting. */
in6_ifstat_inc(ifp, ifs6_out_request);
@@ -799,39 +819,22 @@
dst_sa.sin6_addr = ip6->ip6_dst;
/* Check for valid scope ID. */
- if (in6_setscope(&src0, ifp, &zone) == 0 &&
+ if (in6_setscope(&src0, origifp, &zone) == 0 &&
sa6_recoverscope(&src_sa) == 0 && zone == src_sa.sin6_scope_id &&
- in6_setscope(&dst0, ifp, &zone) == 0 &&
+ in6_setscope(&dst0, origifp, &zone) == 0 &&
sa6_recoverscope(&dst_sa) == 0 && zone == dst_sa.sin6_scope_id) {
/*
* The outgoing interface is in the zone of the source
* and destination addresses.
*
- * Because the loopback interface cannot receive
- * packets with a different scope ID than its own,
- * there is a trick to pretend the outgoing packet
- * was received by the real network interface, by
- * setting "origifp" different from "ifp". This is
- * only allowed when "ifp" is a loopback network
- * interface. Refer to code in nd6_output_ifp() for
- * more details.
*/
- origifp = ifp;
-
- /*
- * We should use ia_ifp to support the case of sending
- * packets to an address of our own.
- */
- if (ia != NULL && ia->ia_ifp)
- ifp = ia->ia_ifp;
-
- } else if ((ifp->if_flags & IFF_LOOPBACK) == 0 ||
+ } else if ((origifp->if_flags & IFF_LOOPBACK) == 0 ||
sa6_recoverscope(&src_sa) != 0 ||
sa6_recoverscope(&dst_sa) != 0 ||
dst_sa.sin6_scope_id == 0 ||
(src_sa.sin6_scope_id != 0 &&
src_sa.sin6_scope_id != dst_sa.sin6_scope_id) ||
- (origifp = ifnet_byindex(dst_sa.sin6_scope_id)) == NULL) {
+ ifnet_byindex(dst_sa.sin6_scope_id) == NULL) {
/*
* If the destination network interface is not a
* loopback interface, or the destination network
@@ -842,7 +845,7 @@
* pair is considered invalid.
*/
IP6STAT_INC(ip6s_badscope);
- in6_ifstat_inc(ifp, ifs6_out_discard);
+ in6_ifstat_inc(origifp, ifs6_out_discard);
if (error == 0)
error = EHOSTUNREACH; /* XXX */
goto bad;
Index: tests/sys/kern/Makefile
===================================================================
--- tests/sys/kern/Makefile
+++ tests/sys/kern/Makefile
@@ -29,6 +29,7 @@
ATF_TESTS_C+= sched_affinity
ATF_TESTS_C+= sigaltstack
ATF_TESTS_C+= sigwait
+ATF_TESTS_C+= socket_msg_trunc
TEST_METADATA.sigwait+= is_exclusive="true"
.if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH:Mpowerpc*} == ""
ATF_TESTS_C+= subr_physmem_test
Index: tests/sys/kern/socket_msg_trunc.c
===================================================================
--- /dev/null
+++ tests/sys/kern/socket_msg_trunc.c
@@ -0,0 +1,169 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <poll.h>
+
+#include <atf-c.h>
+
+static void
+check_recvmsg(const char *test_name)
+{
+ int ss, cs, rc;
+ struct sockaddr *sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_un saun;
+ int *sizes, sizes_count;
+ int one = 1;
+
+
+ if (!strcmp(test_name, "udp")) {
+ ss = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(6666);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sa = (struct sockaddr *)&sin;
+ rc = bind(ss, sa, sa->sa_len);
+ ATF_CHECK_EQ(0, rc);
+
+ cs = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_CHECK(cs >= 0);
+ int inet_sizes[] = {80, 255, 256, 1024, 4096, 9000};
+ sizes_count = sizeof(inet_sizes) / sizeof(int);
+ sizes = malloc(sizeof(inet_sizes));
+ memcpy(sizes, inet_sizes, sizeof(inet_sizes));
+
+ } else if (!strcmp(test_name, "udp6")) {
+ ss = socket(PF_INET6, SOCK_DGRAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = htons(6666);
+ const struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT;
+ sin6.sin6_addr = in6loopback;
+ sa = (struct sockaddr *)&sin6;
+ rc = bind(ss, sa, sa->sa_len);
+ ATF_CHECK_EQ(0, rc);
+
+ cs = socket(PF_INET6, SOCK_DGRAM, 0);
+ ATF_CHECK(cs >= 0);
+ int inet_sizes[] = {80, 255, 256, 1024, 4096, 9000};
+ sizes_count = sizeof(inet_sizes) / sizeof(int);
+ sizes = malloc(sizeof(inet_sizes));
+ memcpy(sizes, inet_sizes, sizeof(inet_sizes));
+
+ } else if (!strcmp(test_name, "unix")) {
+ const char *PATH = "/tmp/test_check_recvmsg_socket";
+ ss = socket(PF_UNIX, SOCK_DGRAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&saun, sizeof(saun));
+ saun.sun_family = AF_UNIX;
+ strcpy(saun.sun_path, PATH);
+ saun.sun_len = sizeof(saun);
+ sa = (struct sockaddr *)&saun;
+ unlink(PATH);
+ rc = bind(ss, sa, sa->sa_len);
+ ATF_CHECK_EQ(0, rc);
+
+ cs = socket(PF_UNIX, SOCK_DGRAM, 0);
+ ATF_CHECK(cs >= 0);
+ int unix_sizes[] = {80, 255, 256, 1024, 2000};
+ sizes_count = sizeof(unix_sizes) / sizeof(int);
+ sizes = malloc(sizeof(unix_sizes));
+ memcpy(sizes, unix_sizes, sizeof(unix_sizes));
+ } else
+ return;
+
+ char buf[4096];
+ memset(buf, 0xFF, sizeof(buf));
+ for (int i = 0; i < sizes_count; i++) {
+ int sz = sizes[i];
+ char tbuf[1];
+ rc = sendto(cs, buf, sz, 0, sa, sa->sa_len);
+ ATF_REQUIRE_EQ(rc, sz);
+
+ rc = recv(ss, NULL, 0, MSG_PEEK | MSG_TRUNC);
+ ATF_CHECK_EQ(rc, sz);
+
+ rc = recv(ss, tbuf, sizeof(tbuf), MSG_PEEK | MSG_TRUNC);
+ ATF_CHECK_EQ(rc, sz);
+
+ rc = recv(ss, tbuf, sizeof(tbuf), MSG_TRUNC);
+ ATF_CHECK_EQ(rc, sz);
+ }
+
+ close(ss);
+ close(cs);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_udp_recv_trunc);
+ATF_TC_BODY(socket_afinet_udp_recv_trunc, tc)
+{
+ check_recvmsg("udp");
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet6_udp_recv_trunc);
+ATF_TC_BODY(socket_afinet6_udp_recv_trunc, tc)
+{
+ check_recvmsg("udp6");
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afunix_recv_trunc);
+ATF_TC_BODY(socket_afunix_recv_trunc, tc)
+{
+ check_recvmsg("unix");
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, socket_afinet_udp_recv_trunc);
+ ATF_TP_ADD_TC(tp, socket_afinet6_udp_recv_trunc);
+ ATF_TP_ADD_TC(tp, socket_afunix_recv_trunc);
+
+ return atf_no_error();
+}
Index: tests/sys/netinet6/test_ip6_output.py
===================================================================
--- tests/sys/netinet6/test_ip6_output.py
+++ tests/sys/netinet6/test_ip6_output.py
@@ -447,7 +447,7 @@
"source_validation",
[
pytest.param(0, id="no_sav"),
- pytest.param(1, id="sav", marks=pytest.mark.skip(reason="fails")),
+ pytest.param(1, id="sav"),
],
)
@pytest.mark.parametrize("scope", ["gu", "ll", "lo"])
@@ -495,7 +495,7 @@
"source_validation",
[
pytest.param(0, id="no_sav"),
- pytest.param(1, id="sav", marks=pytest.mark.skip(reason="fails")),
+ pytest.param(1, id="sav"),
],
)
@pytest.mark.parametrize("scope", ["gu", "ll", "lo"])

File Metadata

Mime Type
text/plain
Expires
Mon, Feb 9, 12:42 AM (12 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28517267
Default Alt Text
D35909.id108633.diff (14 KB)

Event Timeline