forked from ravinet/mahimahi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpacketshell.cc
130 lines (105 loc) · 4.79 KB
/
packetshell.cc
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <utility>
#include <sys/socket.h>
#include <net/route.h>
#include <signal.h>
#include "packetshell.hh"
#include "netdevice.hh"
#include "nat.hh"
#include "util.hh"
#include "get_address.hh"
#include "address.hh"
#include "make_pipe.hh"
#include "ferry.cc"
using namespace std;
using namespace PollerShortNames;
template <class FerryType>
PacketShell<FerryType>::PacketShell( const std::string & device_prefix )
: egress_ingress( two_unassigned_addresses() ),
nameserver_( first_nameserver() ),
egress_tun_( device_prefix + "-" + to_string( getpid() ) , egress_addr(), ingress_addr() ),
dns_outside_( new DNSProxy( egress_addr(), nameserver_, nameserver_ ) ),
nat_rule_( ingress_addr() ),
pipe_( make_pipe() ),
child_processes_()
{
/* make sure environment has been cleared */
assert( environ == nullptr );
/* constructor will throw exception if it fails, so we should not be able to get nullptr */
assert( dns_outside_ );
}
template <class FerryType>
template <typename... Targs>
void PacketShell<FerryType>::start_uplink( const string & shell_prefix,
char ** const user_environment,
Targs&&... Fargs )
{
/* g++ bug 55914 makes this hard before version 4.9 */
auto ferry_maker = std::bind( []( Targs&&... Fargs ) { return FerryType( forward<Targs>( Fargs )... ); },
forward<Targs>( Fargs )... );
/* Fork */
child_processes_.emplace_back( [&]() {
TunDevice ingress_tun( "ingress", ingress_addr(), egress_addr() );
/* bring up localhost */
Socket ioctl_socket( UDP );
interface_ioctl( ioctl_socket.fd(), SIOCSIFFLAGS, "lo",
[] ( ifreq &ifr ) { ifr.ifr_flags = IFF_UP; } );
/* create default route */
rtentry route;
zero( route );
route.rt_gateway = egress_addr().raw_sockaddr();
route.rt_dst = route.rt_genmask = Address().raw_sockaddr();
route.rt_flags = RTF_UP | RTF_GATEWAY;
SystemCall( "ioctl SIOCADDRT", ioctl( ioctl_socket.fd().num(), SIOCADDRT, &route ) );
/* create DNS proxy if nameserver address is local */
auto dns_inside = DNSProxy::maybe_proxy( nameserver_,
dns_outside_->udp_listener().local_addr(),
dns_outside_->tcp_listener().local_addr() );
/* Fork again after dropping root privileges */
drop_privileges();
ChildProcess bash_process( [&]() {
/* restore environment and tweak bash prompt */
environ = user_environment;
prepend_shell_prefix( shell_prefix );
const string shell = shell_path();
SystemCall( "execl", execl( shell.c_str(), shell.c_str(), static_cast<char *>( nullptr ) ) );
return EXIT_FAILURE;
} );
FerryType uplink_queue = ferry_maker();
return packet_ferry( uplink_queue, ingress_tun.fd(), pipe_.first, move( dns_inside ),
move( bash_process ) );
}, true ); /* new network namespace */
}
template <class FerryType>
template <typename... Targs>
void PacketShell<FerryType>::start_downlink( Targs&&... Fargs )
{
auto ferry_maker = std::bind( []( Targs&&... Fargs ) { return FerryType( forward<Targs>( Fargs )... ); },
forward<Targs>( Fargs )... );
child_processes_.emplace_back( [&] () {
drop_privileges();
FerryType downlink_queue = ferry_maker();
return packet_ferry( downlink_queue, egress_tun_.fd(),
pipe_.second, move( dns_outside_ ), {} );
} );
}
template <class FerryType>
int PacketShell<FerryType>::wait_for_exit( void )
{
/* wait for either child to finish */
Poller poller;
SignalMask signals_to_listen_for = { SIGCHLD, SIGCONT, SIGHUP, SIGTERM };
signals_to_listen_for.block(); /* don't let them interrupt us */
SignalFD signal_fd( signals_to_listen_for );
poller.add_action( Poller::Action( signal_fd.fd(), Direction::In,
[&] () {
return handle_signal( signal_fd.read_signal(),
child_processes_ );
} ) );
while ( true ) {
auto poll_result = poller.poll( -1 );
if ( poll_result.result == Poller::Result::Type::Exit ) {
return poll_result.exit_status;
}
}
}