-
Notifications
You must be signed in to change notification settings - Fork 6
/
start-fire
executable file
·262 lines (199 loc) · 7.04 KB
/
start-fire
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/bin/bash
# This is the entrypoint for firecracker microVM configuration and
# attaches network bridges to the VM and starts microVM with the
# selected options.
# Refer to README.md for further info.
# If no arguments, the VM will lauch with default values for vCPU
# count (=1) and memory size (=128 MiB)
: ${DEBUG:='N'}
: ${LAUNCHER:='/usr/local/bin/firectl'}
: ${FIRECTL_KERNEL:='--kernel=/tmp/micro-vmlinux.bin'}
: ${FIRECTL_KERNEL_OPTS:='--kernel-opts=console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw'}
: ${FIRECTL_ROOT_DRIVE:='--root-drive=/rootfs/image'}
log () {
case "$1" in
INFO | WARNING | ERROR )
echo "$1: ${@:2}"
;;
DEBUG)
[[ $DEBUG -eq 1 ]] && echo "$1: ${@:2}"
;;
*)
echo "-- $@"
;;
esac
}
# ContainsElement: checks if first parameter is among the array given as second parameter
# returns 0 if the element is found in the list and 1 if not
# usage: containsElement $item $list
containsElement () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
# Generate random MAC address
genMAC () {
hexchars="0123456789ABCDEF"
end=$( for i in {1..8} ; do echo -n ${hexchars:$(( $RANDOM % 16 )):1} ; done | sed -e 's/\(..\)/:\1/g' )
echo "FE:05$end"
}
# atoi: Returns the integer representation of an IP arg, passed in ascii
# dotted-decimal notation (x.x.x.x)
atoi() {
IP=$1
IPnum=0
for (( i=0 ; i<4 ; ++i ))
do
((IPnum+=${IP%%.*}*$((256**$((3-${i}))))))
IP=${IP#*.}
done
echo $IPnum
}
# itoa: returns the dotted-decimal ascii form of an IP arg passed in integer
# format
itoa() {
echo -n $(($(($(($((${1}/256))/256))/256))%256)).
echo -n $(($(($((${1}/256))/256))%256)).
echo -n $(($((${1}/256))%256)).
echo $((${1}%256))
}
cidr2mask() {
local i mask=""
local full_octets=$(($1/8))
local partial_octet=$(($1%8))
for ((i=0;i<4;i+=1)); do
if [ $i -lt $full_octets ]; then
mask+=255
elif [ $i -eq $full_octets ]; then
mask+=$((256 - 2**(8-$partial_octet)))
else
mask+=0
fi
test $i -lt 3 && mask+=.
done
echo $mask
}
# Generates and returns a new IP and MASK in a superset (inmediate wider range)
# of the given IP/MASK
# usage: getNonConflictingIP IP MASK
# returns NEWIP MASK
getNonConflictingIP () {
local IP="$1"
local CIDR="$2"
let "newCIDR=$CIDR-1"
local i=$(atoi $IP)
let "j=$i^(1<<(32-$CIDR))"
local newIP=$(itoa j)
echo $newIP $newCIDR
}
# generates unused, random names for macvlan or bridge devices
# usage: generateNetDevNames DEVICETYPE
# DEVICETYPE must be either 'macvlan' or 'bridge'
# returns:
# - bridgeXXXXXX if DEVICETYPE is 'bridge'
# - macvlanXXXXXX, macvtapXXXXXX if DEVICETYPE is 'macvlan'
generateNetdevNames () {
devicetype=$1
local netdevinterfaces=($(ip link show | awk "/$devicetype/ { print \$2 }" | cut -d '@' -f 1 | tr -d :))
local randomID=$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 6 | head -n 1)
# check if the device already exists and regenerate the name if so
while containsElement "$devicetype$randomID" "${netdevinterfaces[@]}"; do randomID=$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 6 | head -n 1); done
echo "$randomID"
}
# Crate tap device with interface mac and ip
setupTap () {
set -x
local iface="$1"
local tapName="$2"
NAMESERVER=`grep "nameserver " /etc/resolv.conf | cut -f 2 -d ' '`
NAMESERVERS=`echo ${NAMESERVER[*]} | sed "s/ /,/g"`
NETWORK_IP="${NETWORK_IP:-$(echo 172.$((RANDOM%(31-16+1)+16)).$((RANDOM%256)).$((RANDOM%(254-2+1)+2)))}"
NETWORK_SUB=`echo $NETWORK_IP | cut -f1,2,3 -d\.`
NETWORK_GW="${NETWORK_GW:-$(echo ${NETWORK_SUB}.1)}"
ip tuntap add mode tap $tapName
dnsmasq --user=root \
--dhcp-range=$NETWORK_IP,$NETWORK_IP \
--dhcp-option=option:router,$NETWORK_GW \
--dhcp-option=option:dns-server,$NAMESERVERS
ip addr add $NETWORK_GW/24 dev $tapName
ip link set dev "$tapName" up
iptables -t nat -A POSTROUTING -o $iface -j MASQUERADE
iptables -I FORWARD 1 -i $tapName -j ACCEPT
iptables -I FORWARD 1 -o $tapName -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A PREROUTING -d $IP -j DNAT --to-destination $NETWORK_IP
# Append MAC and respective IP address to the net-conf file
echo -e "IP=$NETWORK_IP" >> /tmp/net-conf
echo -e "GATEWAY=$NETWORK_GW" >> /tmp/net-conf
}
# Setup tap device to connect to the VM for the given interface
# and setup the ip addresses
configureNetworks () {
local i=0
local GATEWAY=$(ip r | grep default | awk '{print $3}')
local IP
/sbin/sysctl -w net.ipv4.conf.all.forwarding=1
# Create the net-conf file that will be mounted as env drive
for iface in "${local_ifaces[@]}"; do
IPs=$(ip address show dev $iface | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
IPs=($IPs)
MAC=$(ip link show $iface | awk '/ether/ { print $2 }')
log "DEBUG" "Container original MAC address: $MAC"
IP=${IPs[0]}
local CIDR=$(ip address show dev $iface | awk "/inet $IP/ { print \$2 }" | cut -f2 -d/)
# use container MAC address ($MAC) for tap device
# and generate a new one for the local interface
ip link set $iface down
ip link set $iface address $(genMAC)
ip link set $iface up
FIRECTL_NET_OPTS=""
local deviceID=$(generateNetdevNames "bridge")
local tapName="tap-$deviceID"
# setup the tap device for connecting the VM
setupTap $iface $tapName
# firectl configuration:
FIRECTL_NET_OPTS="$FIRECTL_NET_OPTS --tap-device=$tapName/$MAC"
log "DEBUG" "tapName: $tapName"
let i++
done
}
# Create docker host conatiner configuration image file that will be mounted as a drive
createHostConfDrive() {
drivePath="/tmp/hostconf"
dd if=/dev/zero of=$drivePath bs=1M count=2
mkfs.ext4 $drivePath
mkdir /tmp/hostconffs-mount
mount $drivePath /tmp/hostconffs-mount -o loop
cp /tmp/net-conf /tmp/hostconffs-mount/net-conf
cp /etc/hosts /tmp/hostconffs-mount/hosts
cp /etc/resolv.conf /tmp/hostconffs-mount/resolv.conf
umount /tmp/hostconffs-mount
FIRECTL_DRIVE_OPTS="$FIRECTL_DRIVE_OPTS --add-drive=$drivePath:ro"
}
echo "Starting Configuration... \n"
FIRECTL_DRIVE_OPTS=""
# Debugging mode
if [ "$1" = "bash" ]; then
export DEBUG=1
export ENABLE_DHCP=$ENABLE_DHCP
export DNS_SERVERS=$DNS_SERVERS
exec bash
fi
case "$DEBUG" in
[Yy1]* ) DEBUG=1;;
[Nn0]* ) DEBUG=0;;
* ) log "ERROR" "DEBUG incorrect or undefined. It must be one of [Yy1Nn0]"; exit 1;;
esac
# Get all interfaces:
local_ifaces=($(ip link show | grep -v noop | grep state | grep -v LOOPBACK | awk '{print $2}' | tr -d : | sed 's/@.*$//'))
local_bridges=($(brctl show | tail -n +2 | awk '{print $1}'))
# Get non-bridge interfaces:
for i in "${local_bridges[@]}"
do
local_ifaces=(${local_ifaces[@]//*$i*})
done
sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
configureNetworks
createHostConfDrive
log "INFO" "Launching Firecracker"
log "DEBUG" "Launching $LAUNCHER $FIRECTL_KERNEL $FIRECTL_KERNEL_OPTS $FIRECTL_ROOT_DRIVE $FIRECTL_DRIVE_OPTS $@ $FIRECTL_NET_OPTS"
eval exec $LAUNCHER $FIRECTL_KERNEL $FIRECTL_KERNEL_OPTS $FIRECTL_ROOT_DRIVE $FIRECTL_DRIVE_OPTS "$@" $FIRECTL_NET_OPTS