1# $OpenBSD: Tap.pm,v 1.1 2016/09/28 12:40:35 bluhm Exp $ 2 3# Copyright (c) 2014 Alexander Bluhm <bluhm@openbsd.org> 4# 5# Permission to use, copy, modify, and distribute this software for any 6# purpose with or without fee is hereby granted, provided that the above 7# copyright notice and this permission notice appear in all copies. 8# 9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 17# Encapsulate tap interface handling into separate module. 18 19use strict; 20use warnings; 21 22package Tap; 23use parent 'Exporter'; 24our @EXPORT_OK = qw(opentap); 25 26use Carp; 27use Fcntl; 28use File::Basename; 29use POSIX qw(_exit); 30use PassFd 'recvfd'; 31use Socket; 32 33sub opentap { 34 my ($tap_number) = @_; 35 my $tap_device = "/dev/tap$tap_number"; 36 37 if ($> == 0) { 38 sysopen(my $tap, $tap_device, O_RDWR) 39 or croak "Open $tap_device failed: $!"; 40 return $tap; 41 } 42 43 if (!$ENV{SUDO}) { 44 die "To open the device $tap_device you must run as root or\n". 45 "set the SUDO environment variable and allow closefrom_override.\n"; 46 } 47 48 my $opentap; 49 my $curdir = dirname($0) || "."; 50 if (-x "$curdir/opentap") { 51 $opentap = "$curdir/opentap"; 52 } elsif (-x "./opentap") { 53 $opentap = "./opentap"; 54 } else { 55 die "To open the device $tap_device the tool opentap is needed.\n". 56 "Executable opentap not found in $curdir or current directory.\n"; 57 } 58 59 socketpair(my $parent, my $child, AF_UNIX, SOCK_STREAM, PF_UNSPEC) 60 or croak "Socketpair failed: $!"; 61 $child->fcntl(F_SETFD, 0) 62 or croak "Fcntl setfd failed: $!"; 63 64 defined(my $pid = fork()) 65 or croak "Fork failed: $!"; 66 67 unless ($pid) { 68 # child process 69 close($parent) or do { 70 warn "Close parent socket failed: $!"; 71 _exit(3); 72 }; 73 my @cmd = ($ENV{SUDO}, '-C', $child->fileno()+1, $opentap, 74 $child->fileno(), $tap_number); 75 exec(@cmd); 76 warn "Exec '@cmd' failed: $!"; 77 _exit(3); 78 } 79 80 # parent process 81 close($child) 82 or croak "Close child socket failed: $!"; 83 my $tap = recvfd($parent) 84 or croak "Recvfd failed: $!"; 85 wait() 86 or croak "Wait failed: $!"; 87 $? == 0 88 or croak "Child process failed: $?"; 89 90 return $tap; 91} 92 931; 94