<feed xmlns='http://www.w3.org/2005/Atom'>
<title>passt/pif.c, branch bug209</title>
<subtitle>Plug A Simple Socket Transport</subtitle>
<link rel='alternate' type='text/html' href='https://passt.top/passt/'/>
<entry>
<title>pif, util: Move listen(2) call from sock_l4_() to pif_listen()</title>
<updated>2026-06-16T21:50:00+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2026-06-16T01:09:36+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=ab2260a0c73623f51e95f922002393170d0abe1f'/>
<id>ab2260a0c73623f51e95f922002393170d0abe1f</id>
<content type='text'>
It's a bit odd to have the listen(2) call for TCP listening sockets, down
deep in sock_l4_() conditional upon the epoll type passed in.  Move it to
pif_listen(), which is at least about listening, although it does still
need to be conditional on TCP.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
It's a bit odd to have the listen(2) call for TCP listening sockets, down
deep in sock_l4_() conditional upon the epoll type passed in.  Move it to
pif_listen(), which is at least about listening, although it does still
need to be conditional on TCP.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>fwd, pif: Remove duplicated logic between tcp_listen() and udp_listen()</title>
<updated>2026-06-16T21:49:57+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2026-06-16T01:09:35+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=50e3dcc44074dea26ed8486f65aa02d1dc2291ec'/>
<id>50e3dcc44074dea26ed8486f65aa02d1dc2291ec</id>
<content type='text'>
tcp_listen() and udp_listen() have only some simple logic around a call to
pif_listen(), and it's basically identical in each case.  If we move the
common logic into pif_listen() we can then remove {tcp,udp}_listen()
entirely, with their caller in fwd_sync_one() calling pif_listen()
directly.

We also move the logic converting from a protocol id to an epoll type from
fwd_sync_one() into pif_listen().  It's a bit arbitrary, but seems slightly
nicer that way.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
tcp_listen() and udp_listen() have only some simple logic around a call to
pif_listen(), and it's basically identical in each case.  If we move the
common logic into pif_listen() we can then remove {tcp,udp}_listen()
entirely, with their caller in fwd_sync_one() calling pif_listen()
directly.

We also move the logic converting from a protocol id to an epoll type from
fwd_sync_one() into pif_listen().  It's a bit arbitrary, but seems slightly
nicer that way.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>pif: Limit pif names to 128 bytes</title>
<updated>2026-05-07T06:06:30+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2026-05-03T21:55:49+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=cbcd4284111f56a233b0c68fba403650c1fb0ccc'/>
<id>cbcd4284111f56a233b0c68fba403650c1fb0ccc</id>
<content type='text'>
All current pif names are quite short, and we expect them to remain short
when/if we allow arbitrary pifs.  However, because of the structure of
the current code we don't enforce any limit on the length.

This will become more important with dynamic configuration updates, so
start enforcing a length limit.  Specifically we allow pif names to be up
to 128 bytes (PIF_NAME_SIZE), including the terminating \0.  This is
more or less arbitrary, but seems like it should be comfortably enough for
all the cases we have in mind.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Reviewed-by: Laurent Vivier &lt;lvivier@redhat.com&gt;
[sbrivio: Fixed typo in comment, reported by Laurent]
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
All current pif names are quite short, and we expect them to remain short
when/if we allow arbitrary pifs.  However, because of the structure of
the current code we don't enforce any limit on the length.

This will become more important with dynamic configuration updates, so
start enforcing a length limit.  Specifically we allow pif names to be up
to 128 bytes (PIF_NAME_SIZE), including the terminating \0.  This is
more or less arbitrary, but seems like it should be comfortably enough for
all the cases we have in mind.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Reviewed-by: Laurent Vivier &lt;lvivier@redhat.com&gt;
[sbrivio: Fixed typo in comment, reported by Laurent]
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>treewide: Spell ASSERT() as assert()</title>
<updated>2026-03-20T20:05:29+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2026-03-19T06:11:43+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=bc872d91765dfd6ff34b0e9a34bce410fac1cef3'/>
<id>bc872d91765dfd6ff34b0e9a34bce410fac1cef3</id>
<content type='text'>
The standard library assert(3), at least with glibc, hits our seccomp
filter and dies with SIGSYS before it's able to print a message, making it
near useless.  Therefore, since 7a8ed9459dfe ("Make assertions actually
useful") we've instead used our own implementation, named ASSERT().

This makes our code look slightly odd though - ASSERT() has the same
overall effect as assert(), it's just a different implementation.  More
importantly this makes it awkward to share code between passt/pasta proper
and things that compile in a more typical environment.  We're going to want
that for our upcoming dynamic configuration tool.

Address this by overriding the standard library's assert() implementation
with our own, instead of giving ours its own name.

The standard assert() is supposed to be omitted if NDEBUG is defined,
which ours doesn't do.  Implement that as well, so ours doesn't
unexpectedly differ.  For the -DNDEBUG case we do this by *not* overriding
assert(), since it will be a no-op anyway.  This requires a few places to
add a #include &lt;assert.h&gt; to let us compile (albeit with warnings) when
-DNDEBUG.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
[sbrivio: Fix some conflicts and missing conversions as a result of
 applying "vu_common: Move iovec management into vu_collect()" first]
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The standard library assert(3), at least with glibc, hits our seccomp
filter and dies with SIGSYS before it's able to print a message, making it
near useless.  Therefore, since 7a8ed9459dfe ("Make assertions actually
useful") we've instead used our own implementation, named ASSERT().

This makes our code look slightly odd though - ASSERT() has the same
overall effect as assert(), it's just a different implementation.  More
importantly this makes it awkward to share code between passt/pasta proper
and things that compile in a more typical environment.  We're going to want
that for our upcoming dynamic configuration tool.

Address this by overriding the standard library's assert() implementation
with our own, instead of giving ours its own name.

The standard assert() is supposed to be omitted if NDEBUG is defined,
which ours doesn't do.  Implement that as well, so ours doesn't
unexpectedly differ.  For the -DNDEBUG case we do this by *not* overriding
assert(), since it will be a no-op anyway.  This requires a few places to
add a #include &lt;assert.h&gt; to let us compile (albeit with warnings) when
-DNDEBUG.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
[sbrivio: Fix some conflicts and missing conversions as a result of
 applying "vu_common: Move iovec management into vu_collect()" first]
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>fwd, pif: Replace with pif_sock_l4() with pif_listen()</title>
<updated>2026-03-04T16:52:55+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2026-03-02T04:31:34+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=9ee780567310f2486e1510db502a96e5b1a81a1c'/>
<id>9ee780567310f2486e1510db502a96e5b1a81a1c</id>
<content type='text'>
It turns out all users of pif_sock_l4() use it for "listening" sockets,
which now all have a common epoll reference format.  We can take advantage
of that to pass the necessary epoll information into pif_sock_l4() in a
more natural way, rather than as an opaque u32.

That in turn allows union fwd_listen_ref can become a struct, since the
union only exist to allow the meaningful fields to be coerced into a u32
for pif_sock_l4().

Rename pif_sock_l4() to pif_listen() to reflect the new semantics.  While
we're there, remove the static_assert() on the fwd_listen_ref's size.  We
do still need it to fit into 32 bits, but that constraint is imposed only
by the fact that it needs to fit into the whole, epoll_ref structure,
which we already check with a static_assert() in epoll_ctl.h.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Reported-by: Peter Foley &lt;pefoley@google.com&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
It turns out all users of pif_sock_l4() use it for "listening" sockets,
which now all have a common epoll reference format.  We can take advantage
of that to pass the necessary epoll information into pif_sock_l4() in a
more natural way, rather than as an opaque u32.

That in turn allows union fwd_listen_ref can become a struct, since the
union only exist to allow the meaningful fields to be coerced into a u32
for pif_sock_l4().

Rename pif_sock_l4() to pif_listen() to reflect the new semantics.  While
we're there, remove the static_assert() on the fwd_listen_ref's size.  We
do still need it to fit into 32 bits, but that constraint is imposed only
by the fact that it needs to fit into the whole, epoll_ref structure,
which we already check with a static_assert() in epoll_ctl.h.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Reported-by: Peter Foley &lt;pefoley@google.com&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>pif: Correctly set scope_id for guest-side link local addresses</title>
<updated>2025-12-10T07:37:29+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2025-12-10T07:02:57+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=d04c48032bcf724550d0b8f652fd00efcd2dfad0'/>
<id>d04c48032bcf724550d0b8f652fd00efcd2dfad0</id>
<content type='text'>
pif_sockaddr() is supposed to generate a suitable socket address, either
for the host, or for the guest, depending on the 'pif' parameter.  When
given a link-local address, this means it needs to generate a suitable
scope_id to specify which link.  It does this for the host, but not for the
guest.

I think this was done on the assumption that we won't ever generate guest
side link local addresses when forwarding connections.  That, however, is
not the case, at least with the recent extensions to "local mode".  Fix the
problem by properly populating the scope_id field for guest addresses.

Link: https://bugs.passt.top/show_bug.cgi?id=181
Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
pif_sockaddr() is supposed to generate a suitable socket address, either
for the host, or for the guest, depending on the 'pif' parameter.  When
given a link-local address, this means it needs to generate a suitable
scope_id to specify which link.  It does this for the host, but not for the
guest.

I think this was done on the assumption that we won't ever generate guest
side link local addresses when forwarding connections.  That, however, is
not the case, at least with the recent extensions to "local mode".  Fix the
problem by properly populating the scope_id field for guest addresses.

Link: https://bugs.passt.top/show_bug.cgi?id=181
Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>util: Rename sock_l4_dualstack() to sock_l4_dualstack_any()</title>
<updated>2025-12-02T22:07:36+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2025-12-02T04:02:13+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=cec1ca854975e9f2201219946bf4aa624abd2ed0'/>
<id>cec1ca854975e9f2201219946bf4aa624abd2ed0</id>
<content type='text'>
Stefano correctly noted that the fact a socket is dual-stack doesn't
necessarily imply that it is bound to a wildcard address.  While that's the
only case we use for dual-stack sockets, there may be others.  Therefore
rename this function to make it clearer that it always uses a wildcard
bind.

Suggested-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Stefano correctly noted that the fact a socket is dual-stack doesn't
necessarily imply that it is bound to a wildcard address.  While that's the
only case we use for dual-stack sockets, there may be others.  Therefore
rename this function to make it clearer that it always uses a wildcard
bind.

Suggested-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>tcp, udp: Bind outbound listening sockets by interface instead of address</title>
<updated>2025-12-02T22:07:34+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2025-12-02T04:02:12+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=653705c10a2133c059b6d90024426220f43aef52'/>
<id>653705c10a2133c059b6d90024426220f43aef52</id>
<content type='text'>
Currently, outbound forwards (-T, -U) are handled by sockets bound to the
loopback address.  Typically we create two sockets, one for 127.0.0.1 and
one for ::1.

This has some disadvantages:
 * The guest can't connect via 127.0.0.0/8 addresses other than 127.0.0.1
 * We can't use dual-stack sockets, we have to have separate sockets for
   IPv4 and IPv6.

The restriction exists for a reason though.  If the guest has any
interfaces other than pasta (e.g. a VPN tunnel) external hosts could reach
the host via the forwards.  Especially combined with -T auto / -U auto this
would make it very easy to make a mistake with nasty security implications.

We can achieve this a different way, however.  Don't bind to a specific
address, but _do_ use SO_BINDTODEVICE to restrict the sockets to the "lo"
interface.  We fall back to the old behaviour for older kernels where
SO_BINDTODEVICE is not available unprivileged.

Note that although traffic to a local but non-loopback address is passed
over the 'lo' interface (as seen by netfilter and dumpcap), it doesn't
count as attached to that interface for the purposes of SO_BINDTODEVICE
(information from the routing table overrides the "physical" interface).
So, this change doesn't help for bug 100.

It's also not a complete fix for bug 113, it does however:
 * Get us a step closer to fixing bug 113
 * Slightly simplify the code
 * Make things a bit easier to allow more flexible binding on the guest in
   in future

Link: https://bugs.passt.top/show_bug.cgi?id=113

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Currently, outbound forwards (-T, -U) are handled by sockets bound to the
loopback address.  Typically we create two sockets, one for 127.0.0.1 and
one for ::1.

This has some disadvantages:
 * The guest can't connect via 127.0.0.0/8 addresses other than 127.0.0.1
 * We can't use dual-stack sockets, we have to have separate sockets for
   IPv4 and IPv6.

The restriction exists for a reason though.  If the guest has any
interfaces other than pasta (e.g. a VPN tunnel) external hosts could reach
the host via the forwards.  Especially combined with -T auto / -U auto this
would make it very easy to make a mistake with nasty security implications.

We can achieve this a different way, however.  Don't bind to a specific
address, but _do_ use SO_BINDTODEVICE to restrict the sockets to the "lo"
interface.  We fall back to the old behaviour for older kernels where
SO_BINDTODEVICE is not available unprivileged.

Note that although traffic to a local but non-loopback address is passed
over the 'lo' interface (as seen by netfilter and dumpcap), it doesn't
count as attached to that interface for the purposes of SO_BINDTODEVICE
(information from the routing table overrides the "physical" interface).
So, this change doesn't help for bug 100.

It's also not a complete fix for bug 113, it does however:
 * Get us a step closer to fixing bug 113
 * Slightly simplify the code
 * Make things a bit easier to allow more flexible binding on the guest in
   in future

Link: https://bugs.passt.top/show_bug.cgi?id=113

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>util, flow, pif: Simplify sock_l4_sa() interface</title>
<updated>2025-12-02T22:07:17+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2025-12-02T04:02:06+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=3b8b8342999b9dfd5f68adc7a5855c4e9a134892'/>
<id>3b8b8342999b9dfd5f68adc7a5855c4e9a134892</id>
<content type='text'>
sock_l4_sa() has a somewhat confusing 'v6only' option controlling whether
to set the IPV6_V6ONLY socket option.  Usually it's set when the given
address is IPv6, but not when we want to create a dual stack listening
socket.  The latter only makes sense when the address is :: however.

Clarify this by only keeping the v6only option in an internal helper
sock_l4_().  External users will call either sock_l4() which always creates
a socket bound to a specific IP version, or sock_l4_dualstack() which
creates a dual stack socket, but takes only a port not an address.

We drop the '_sa' suffix while we're at it - it exists because this used
to be an internal version with a sock_l4() wrapper.  The wrapper no longer
exists so the '_sa' is no longer useful.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
sock_l4_sa() has a somewhat confusing 'v6only' option controlling whether
to set the IPV6_V6ONLY socket option.  Usually it's set when the given
address is IPv6, but not when we want to create a dual stack listening
socket.  The latter only makes sense when the address is :: however.

Clarify this by only keeping the v6only option in an internal helper
sock_l4_().  External users will call either sock_l4() which always creates
a socket bound to a specific IP version, or sock_l4_dualstack() which
creates a dual stack socket, but takes only a port not an address.

We drop the '_sa' suffix while we're at it - it exists because this used
to be an internal version with a sock_l4() wrapper.  The wrapper no longer
exists so the '_sa' is no longer useful.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>inany: Let length of sockaddr_inany be implicit from the family</title>
<updated>2025-12-02T22:07:14+00:00</updated>
<author>
<name>David Gibson</name>
<email>david@gibson.dropbear.id.au</email>
</author>
<published>2025-12-02T04:02:05+00:00</published>
<link rel='alternate' type='text/html' href='https://passt.top/passt/commit/?id=b0523f6b0629358024e95b5d01dc81512cfa8e10'/>
<id>b0523f6b0629358024e95b5d01dc81512cfa8e10</id>
<content type='text'>
sockaddr_inany can contain either an IPv4 or IPv6 socket address, so the
relevant length for bind() or connect() can vary.  In pif_sockaddr() we
return that length, and in sock_l4_sa() we take it as an extra parameter.

However, sockaddr_inany always contains exactly a sockaddr_in or a
sockaddr_in6 each with a fixed size.  Therefore we can derive the relevant
length from the family, and don't need to pass it around separately.

Make a tiny helper to get the relevant address length, and update all
interfaces to use that approach instead.

In the process, fix a buglet in tcp_flow_repair_bind(): we passed
sizeof(union sockaddr_inany) to bind() instead of the specific length for
the address family.  Since the sizeof() is always longer than the specific
length, this is probably fine, but not theoretically correct.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
sockaddr_inany can contain either an IPv4 or IPv6 socket address, so the
relevant length for bind() or connect() can vary.  In pif_sockaddr() we
return that length, and in sock_l4_sa() we take it as an extra parameter.

However, sockaddr_inany always contains exactly a sockaddr_in or a
sockaddr_in6 each with a fixed size.  Therefore we can derive the relevant
length from the family, and don't need to pass it around separately.

Make a tiny helper to get the relevant address length, and update all
interfaces to use that approach instead.

In the process, fix a buglet in tcp_flow_repair_bind(): we passed
sizeof(union sockaddr_inany) to bind() instead of the specific length for
the address family.  Since the sizeof() is always longer than the specific
length, this is probably fine, but not theoretically correct.

Signed-off-by: David Gibson &lt;david@gibson.dropbear.id.au&gt;
Signed-off-by: Stefano Brivio &lt;sbrivio@redhat.com&gt;
</pre>
</div>
</content>
</entry>
</feed>
