Bug 277435 - [Feature request] Add an option to destroy the tap/tun interface when the descriptor is closed
Summary: [Feature request] Add an option to destroy the tap/tun interface when the des...
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: Unspecified
Hardware: Any Any
: --- Affects Some People
Assignee: freebsd-net (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-03-02 15:35 UTC by Nikolay Borodin
Modified: 2024-03-04 12:33 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Nikolay Borodin 2024-03-02 15:35:02 UTC
When developing the tap library, I ran into the problem that you cannot destroy an interface (ex. tap0) while the application is running (via SIOCIFDESTROY).

The ioctl(SIOCIFDESTROY) call will simply hang until the application using this descriptor finishes (see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242841#c0).

Opening "/dev/tap" does not check if existing interfaces are being used and creates tap(N+1) every time.

It would be nice to make a parameter that will control the automatic destruction of the interface when the descriptor is closed.

For example:

if (ioctl(fd, TAPSIFDCLOSE, 1) < 0) {
    printf("error: ioctl(TAPSIFDCLOSE)\n");
}

close(fd);

This mechanism is implemented by default in the Linux driver for tuntap:

"A network device will appear as tunXX or tapXX, depending on the options chosen. When the program closes the file descriptor, the network device and all corresponding routes will disappear."

Source: https://docs.kernel.org/networking/tuntap.html
Comment 1 Alan Somers freebsd_committer freebsd_triage 2024-03-03 14:08:02 UTC
This is from tap(4).  How is it different from what you are proposing?

     On the last close of the data device, the interface is brought down (as
     if with “ifconfig tapN down”) and has all of its configured addresses
     deleted unless the device is a VMnet device, or has IFF_LINK0 flag set.
     All queued frames are thrown away.  If the interface is up when the data
     device is not open, output frames are thrown away rather than letting
     them pile up.
Comment 2 Nikolay Borodin 2024-03-03 14:22:33 UTC
(In reply to Alan Somers from comment #1)
> On the last close of the data device, the interface is brought down (as if with “ifconfig tapN down”)

It's completely different. What I am suggesting refers to the automatic "ifconfig tapN destroy" when closing the descriptor.

If you use tap/tun via open("/dev/tap") without a number you will get a new device each time (tap0, tap1, tap2, etc.) which cannot be destroyed during the lifetime of the application.

Doing this manually is inconvenient for the user, so we need to make the application delete the interface when it closes the descriptor.

Fortunately, the Linux driver equivalent does this by default.
Comment 3 Kyle Evans freebsd_committer freebsd_triage 2024-03-03 14:34:48 UTC
I agree that this would be neat (though you can destroy it, you just have to close it first).
Comment 4 Nikolay Borodin 2024-03-03 14:50:11 UTC
(In reply to Kyle Evans from comment #3)
> though you can destroy it, you just have to close it first

You can't. If you try to do this via SIOCIFDESTROY inside an application which uses the descriptor, the application will hang, even if you do ioctl after close(fd)

The sample code from my application:

JFUNC(void, close) {
    close(getFd(env, this));

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strcpy(ifr.ifr_name, "tap0");
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    ioctl(sock, SIOCIFDESTROY, &ifr); // hangs
}

After that, you can only terminate the application via kill -9 <pid>.
Comment 5 Kyle Evans freebsd_committer freebsd_triage 2024-03-03 14:56:07 UTC
(In reply to Nikolay Borodin from comment #4)

That is a bug, then, unless you have a dangling reference that you didn't expect.
Comment 6 Nikolay Borodin 2024-03-03 15:06:36 UTC
(In reply to Kyle Evans from comment #5)

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242841#c0(In reply to Kyle Evans from comment #5)
I understand, in any case it doesn't apply to the automatic interface destruction functionality. The ioctl should have most likely returned an error in that code sample.
Comment 7 Kyle Evans freebsd_committer freebsd_triage 2024-03-03 19:01:47 UTC
(In reply to Nikolay Borodin from comment #6)

Right, but the automatic destroy functionality wouldn't work until the process closes anyways if you leaked an fd to the tap device somewhere, which may or may not be OK.
Comment 8 Kyle Evans freebsd_committer freebsd_triage 2024-03-03 20:20:55 UTC
Something like this should do the trick: https://reviews.freebsd.org/D44200 (tun(4)/tap(4): allow devices to be configured as transient)
Comment 9 Nikolay Borodin 2024-03-03 22:59:40 UTC
(In reply to Kyle Evans from comment #8)

After this option is set, the system hangs when the application is closed.

i = 1;
if (ioctl(fd, TAPSTRANSIENT, &i) < 0) {
    printf("error: ioctl(TAPSTRANSIENT)\n"); // hangs
    return 1;
}

with a 100% CPU load:

> 1503846 monster+  20   0 3583480 610420 494700 S 100,0   1,9   1:21.39 VirtualBoxVM
Comment 10 Nikolay Borodin 2024-03-03 23:04:10 UTC
(In reply to Kyle Evans from comment #8)
(In reply to Nikolay Borodin from comment #9)
To clarify, it's not the ioctl call that hangs, it's the code in the kernel.
Comment 11 Kyle Evans freebsd_committer freebsd_triage 2024-03-04 00:01:52 UTC
(In reply to Nikolay Borodin from comment #10)

That's fascinating. Can you tell us a little more about your application? Is it doing some fork or fork/exec? Any fd passing or anything funky? Interface renaming?
Comment 12 Nikolay Borodin 2024-03-04 00:22:04 UTC
(In reply to Kyle Evans from comment #11)
> Is it doing some fork or fork/exec? Any fd passing or anything funky?

I doubt it.

> Interface renaming?

Not yet. :)

Here's the code (minus TAPSTRANSIENT) for the library I'm working on. Nothing out of the ordinary.

https://gitlab.com/Monsterovich/lanemu/-/blob/4e4c5012c8838b7780c2d96efcc622170839a6e3/native/tapLinux/org_p2pvpn_tuntap_TunTapLinux.c
Comment 13 Nikolay Borodin 2024-03-04 10:04:02 UTC
(In reply to Kyle Evans from comment #11)
After applying these changes, everything works as it should.

https://reviews.freebsd.org/D44199
Comment 14 Nikolay Borodin 2024-03-04 12:33:04 UTC
(In reply to Kyle Evans from comment #11)
If I set net.link.tap.user_open to 1 and try to open tap device (e.g. "/dev/tap"), open returns -1, but the tapN interface still exists. It also creates devices and doesn't destroy them. I think this behavior could be fixed as well.