summaryrefslogtreecommitdiff
path: root/regress/usr.sbin/ospfd/Tap.pm
blob: f30219e7d5911b7b30c32e150ae8cd71d3948b1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#	$OpenBSD: Tap.pm,v 1.1 2016/09/28 12:40:35 bluhm Exp $

# Copyright (c) 2014 Alexander Bluhm <bluhm@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# Encapsulate tap interface handling into separate module.

use strict;
use warnings;

package Tap;
use parent 'Exporter';
our @EXPORT_OK = qw(opentap);

use Carp;
use Fcntl;
use File::Basename;
use POSIX qw(_exit);
use PassFd 'recvfd';
use Socket;

sub opentap {
    my ($tap_number) = @_;
    my $tap_device = "/dev/tap$tap_number";

    if ($> == 0) {
	sysopen(my $tap, $tap_device, O_RDWR)
	    or croak "Open $tap_device failed: $!";
	return $tap;
    }

    if (!$ENV{SUDO}) {
	die "To open the device $tap_device you must run as root or\n".
	    "set the SUDO environment variable and allow closefrom_override.\n";
    }

    my $opentap;
    my $curdir = dirname($0) || ".";
    if (-x "$curdir/opentap") {
	$opentap = "$curdir/opentap";
    } elsif (-x "./opentap") {
	$opentap = "./opentap";
    } else {
	die "To open the device $tap_device the tool opentap is needed.\n".
	    "Executable opentap not found in $curdir or current directory.\n";
    }

    socketpair(my $parent, my $child, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
	or croak "Socketpair failed: $!";
    $child->fcntl(F_SETFD, 0)
	or croak "Fcntl setfd failed: $!";

    defined(my $pid = fork())
	or croak "Fork failed: $!";

    unless ($pid) {
	# child process
	close($parent) or do {
	    warn "Close parent socket failed: $!";
	    _exit(3);
	};
	my @cmd = ($ENV{SUDO}, '-C', $child->fileno()+1, $opentap,
	    $child->fileno(), $tap_number);
	exec(@cmd);
	warn "Exec '@cmd' failed: $!";
	_exit(3);
    }

    # parent process
    close($child)
	or croak "Close child socket failed: $!";
    my $tap = recvfd($parent)
	or croak "Recvfd failed: $!";
    wait()
	or croak "Wait failed: $!";
    $? == 0
	or croak "Child process failed: $?";

    return $tap;
}

1;