Hello,

I still need to configure wireguard tunnel over IPv6 just to
try it out first so I can understand things better.

to get to this code path one needs to set wg(4) mtu to 9k and try to
send ICMPv6 echo-requests 32k big. then we should get to 'double
fragmentation' path:

        32k -> 9k -> ~1.5k (physical ethernet)

receiving reply goes in reverse direction:

    ip6_input_if() on ethernet
        pf_test(PF_IN, 1.5k)
            add PACKET_TAG_PF_REASSEMBLED
            reassemble packet to 9k UDP datagram with wg(4) payload

        ip_ours()
            wg_input()
                the PACKET_TAG_PF_REASSEMBLED is left at mbuf
                pf_pkt_addr_changed(m)
                put packet to wg_deliver_in() task which calls ip6_input_if()


the ip6_input_if() task then does:

    pf_test(PF_IN, 9k)

        add PACKET_TAG_PF_REASSEMBLED after 9k frags are
        reassembled to 32k ICMPv6 fragment. my understanding
        is the packet has two PACKET_TAG_PF_REASSEMBLED tags now.
        one for ethernet, the other for wireguard.

    ip_ours() for local bound packets or ip6_forward() for forwarding
    local bound traffic should should be fine here.

    it's forwarding case what brings us to code path where we hit
    error. for forwarding case the IP stack calls pf_test() again

    pf_test(PF_FWD, 32k). the code reaches the area touched by your diff:

8786 
8787 #ifdef INET6
8788         /* if reassembled packet passed, create new fragments */
8789         if (pf_status.reass && action == PF_PASS && pd.m && fwdir == 
PF_FWD &&
8790             pd.af == AF_INET6) {
8791                 struct m_tag    *mtag;
8792 
8793                 if ((mtag = m_tag_find(pd.m, PACKET_TAG_PF_REASSEMBLED, 
NULL)))
8794                         action = pf_refragment6(&pd.m, mtag, NULL, NULL, 
NULL);
8795         }
8796 #endif  /* INET6 */

     the pf_refragment() creates a chain of brand new packets. the frag sizes
     match the MTU of outbound interface.

So this is my understadning. I was trying to think of some observable error.
but apart from having a packet with two REASSEMBLED tags attached I can not
think of anything else that goes wrong here.

I'm more concerned the things may actually break when the tag is deleted
for PF_IN packets. that's just my gut feeling about it.


thanks and
regards
sashan


On Sun, Aug 09, 2026 at 04:35:58PM +0000, Zixu Wu wrote:
</snip>
> diff --git a/sys/net/pf.c b/sys/net/pf.c
> index 0fd00c0dbf3..493b7385730 100644
> --- a/sys/net/pf.c
> +++ b/sys/net/pf.c
> @@ -8785,13 +8785,17 @@ done:
>   }
> 
>  #ifdef INET6
> - /* if reassembled packet passed, create new fragments */
> - if (pf_status.reass && action == PF_PASS && pd.m && fwdir == PF_FWD &&
> -     pd.af == AF_INET6) {
> + /* create new fragments if necessary */
> + if (pd.m && pd.af == AF_INET6) {
>     struct m_tag  *mtag;
> 
> -   if ((mtag = m_tag_find(pd.m, PACKET_TAG_PF_REASSEMBLED, NULL)))
> -     action = pf_refragment6(&pd.m, mtag, NULL, NULL, NULL);
> +   if ((mtag = m_tag_find(pd.m, PACKET_TAG_PF_REASSEMBLED, NULL))) {
> +     if (action == PF_PASS && fwdir == PF_FWD) {
> +       action = pf_refragment6(&pd.m, mtag, NULL, NULL, NULL);
> +     } else {
> +       m_tag_delete(pd.m, mtag);
> +     }
> +   }
>   }
>  #endif /* INET6 */
>   if (st && action != PF_DROP) {
> 

Reply via email to