@@ -3,22 +3,206 @@ myname=${0##*/}
33
44set -e
55
6- install_rattan () {
7- local binary_path=${1:- " rattan" }
8- install_binary " $binary_path "
9- config_networkd
6+ GITHUB_REPO=" stack-rs/rattan"
7+ APP_NAME=" rattan"
8+
9+ # --- Utility functions ---
10+
11+ err () {
12+ local red reset
13+ red=$( tput setaf 1 2> /dev/null || echo ' ' )
14+ reset=$( tput sgr0 2> /dev/null || echo ' ' )
15+ echo " ${red} ERROR${reset} : $1 " >&2
16+ exit 1
17+ }
18+
19+ warn () {
20+ local yellow reset
21+ yellow=$( tput setaf 3 2> /dev/null || echo ' ' )
22+ reset=$( tput sgr0 2> /dev/null || echo ' ' )
23+ echo " ${yellow} WARN${reset} : $1 " >&2
24+ }
25+
26+ need_cmd () {
27+ if ! command -v " $1 " > /dev/null 2>&1 ; then
28+ err " need '$1 ' (command not found)"
29+ fi
30+ }
31+
32+ # This wraps curl or wget. Try curl first, if not installed, use wget instead.
33+ downloader () {
34+ local _dld
35+ local _snap_curl=0
36+
37+ if command -v curl > /dev/null 2>&1 ; then
38+ if echo " $( command -v curl) " | grep -q ' /snap/' ; then
39+ _snap_curl=1
40+ fi
41+ fi
42+
43+ if command -v curl > /dev/null 2>&1 && [ " $_snap_curl " = " 0" ]; then
44+ _dld=curl
45+ elif command -v wget > /dev/null 2>&1 ; then
46+ _dld=wget
47+ elif [ " $_snap_curl " = " 1" ]; then
48+ err " curl installed via snap cannot download files due to missing permissions. Please reinstall curl with apt or another package manager."
49+ else
50+ err " need 'curl' or 'wget' (command not found)"
51+ fi
52+
53+ if [ " $_dld " = " curl" ]; then
54+ curl -sSfL " $1 " -o " $2 "
55+ elif [ " $_dld " = " wget" ]; then
56+ wget -q " $1 " -O " $2 "
57+ fi
58+ }
59+
60+ # --- Architecture detection ---
61+ check_glibc () {
62+ local _min_major=" $1 "
63+ local _min_minor=" $2 "
64+ local _local_glibc
65+
66+ _local_glibc=" $( ldd --version 2> /dev/null | awk -F' ' ' FNR==1 { print $NF }' ) "
67+ if [ -z " $_local_glibc " ]; then
68+ return 1
69+ fi
70+
71+ local _major _minor
72+ _major=" $( echo " $_local_glibc " | awk -F. ' { print $1 }' ) "
73+ _minor=" $( echo " $_local_glibc " | awk -F. ' { print $2 }' ) "
74+
75+ if [ " $_major " = " $_min_major " ] && [ " $_minor " -ge " $_min_minor " ] 2> /dev/null; then
76+ return 0
77+ else
78+ warn " System glibc version ('${_local_glibc} ') may be too old; trying musl variant"
79+ return 1
80+ fi
81+ }
82+
83+ get_target_triple () {
84+ local _ostype _cputype _clibtype
85+
86+ need_cmd uname
87+
88+ _ostype=" $( uname -s) "
89+ _cputype=" $( uname -m) "
90+
91+ if [ " $_ostype " != " Linux" ]; then
92+ err " $APP_NAME only supports Linux (requires network namespaces and XDP). Detected OS: $_ostype "
93+ fi
94+
95+ # Detect musl vs glibc
96+ if ldd --version 2>&1 | grep -q ' musl' ; then
97+ _clibtype=" musl"
98+ else
99+ _clibtype=" gnu"
100+ fi
101+
102+ case " $_cputype " in
103+ x86_64 | x86-64 | x64 | amd64)
104+ _cputype=x86_64
105+ ;;
106+ aarch64 | arm64)
107+ _cputype=aarch64
108+ ;;
109+ * )
110+ err " Unsupported CPU architecture: $_cputype . Only x86_64 and aarch64 are supported."
111+ ;;
112+ esac
113+
114+ # For glibc targets, check the minimum required version.
115+ # Fall back to musl if glibc is too old.
116+ if [ " $_clibtype " = " gnu" ]; then
117+ if ! check_glibc " 2" " 39" ; then
118+ _clibtype=" musl"
119+ fi
120+ fi
121+
122+ echo " ${_cputype} -unknown-linux-${_clibtype} "
10123}
11124
125+ # --- GitHub release download ---
126+ get_latest_release_tag () {
127+ local _api_url=" https://api.github.com/repos/${GITHUB_REPO} /releases/latest"
128+ local _tag
129+
130+ if command -v curl > /dev/null 2>&1 ; then
131+ _tag=" $( curl -sSfL " $_api_url " | grep ' "tag_name"' | head -1 | sed ' s/.*"tag_name": *"\([^"]*\)".*/\1/' ) "
132+ elif command -v wget > /dev/null 2>&1 ; then
133+ _tag=" $( wget -qO- " $_api_url " | grep ' "tag_name"' | head -1 | sed ' s/.*"tag_name": *"\([^"]*\)".*/\1/' ) "
134+ else
135+ err " need 'curl' or 'wget' to fetch the latest release"
136+ fi
137+
138+ if [ -z " $_tag " ]; then
139+ err " Failed to determine the latest release tag from GitHub. Check your network connection."
140+ fi
141+
142+ echo " $_tag "
143+ }
144+
145+ download_rattan () {
146+ need_cmd uname
147+ need_cmd mktemp
148+ need_cmd tar
149+
150+ local _target
151+ _target=" $( get_target_triple) "
152+ echo " Detected platform: $_target " >&2
153+
154+ local _tag
155+ _tag=" $( get_latest_release_tag) "
156+ echo " Latest release: $_tag " >&2
157+
158+ local _archive=" ${APP_NAME} -${_target} .tar.xz"
159+ local _url=" https://github.com/${GITHUB_REPO} /releases/download/${_tag} /${_archive} "
160+
161+ local _dir
162+ _dir=" $( mktemp -d) "
163+ local _file=" ${_dir} /${_archive} "
164+
165+ echo " Downloading ${_url} ..." >&2
166+ if ! downloader " $_url " " $_file " ; then
167+ # Try .tar.gz if .tar.xz fails
168+ _archive=" ${APP_NAME} -${_target} .tar.gz"
169+ _url=" https://github.com/${GITHUB_REPO} /releases/download/${_tag} /${_archive} "
170+ _file=" ${_dir} /${_archive} "
171+ echo " Retrying with ${_url} ..." >&2
172+ if ! downloader " $_url " " $_file " ; then
173+ rm -rf " $_dir "
174+ err " Failed to download release archive. No compatible release found for platform '${_target} '."
175+ fi
176+ fi
177+
178+ echo " Extracting archive..." >&2
179+ tar xf " $_file " --strip-components 1 -C " $_dir "
180+
181+ local _binary=" ${_dir} /${APP_NAME} "
182+ if [ ! -f " $_binary " ]; then
183+ rm -rf " $_dir "
184+ err " Binary '${APP_NAME} ' not found in extracted archive."
185+ fi
186+
187+ echo " $_binary "
188+ # Caller is responsible for cleanup; pass dir via global so we can rm it after install
189+ _DOWNLOAD_TMPDIR=" $_dir "
190+ }
191+
192+ # --- Install steps ---
12193install_binary () {
13- sudo install -m 755 " $1 " /usr/local/bin/rattan
14- sudo setcap ' cap_dac_override,cap_dac_read_search,cap_sys_ptrace,cap_net_admin,cap_sys_admin,cap_net_raw+ep' /usr/local/bin/rattan
194+ sudo install -m 755 " $1 " /usr/local/bin/rattan
195+ sudo setcap ' cap_dac_override,cap_dac_read_search,cap_sys_ptrace,cap_net_admin,cap_sys_admin,cap_net_raw+ep' /usr/local/bin/rattan
15196}
16197
17198# Config systemd-networkd to not change MAC address of veth interfaces
18199# Ref: https://github.com/stack-rs/rattan/issues/42
19200config_networkd () {
20- if [ -d " /lib/systemd/network" ]; then
21- sudo sh -c " cat <<EOF >/lib/systemd/network/80-rattan.link
201+ if [ -d " /lib/systemd/network" ]; then
202+ if [ -f " /lib/systemd/network/80-rattan.link" ]; then
203+ return
204+ fi
205+ sudo sh -c " cat <<EOF >/lib/systemd/network/80-rattan.link
22206[Match]
23207OriginalName=ns*-v*-*
24208Driver=veth
@@ -27,45 +211,70 @@ Driver=veth
27211MACAddressPolicy=none
28212EOF
29213"
30- sudo systemctl daemon-reload
31- sudo systemctl restart systemd-networkd.service
32- fi
214+ sudo systemctl daemon-reload
215+ sudo systemctl restart systemd-networkd.service
216+ fi
33217}
34218
219+ install_rattan () {
220+ local _binary_path=" $1 "
221+ install_binary " $_binary_path "
222+ config_networkd
223+ echo " $APP_NAME installed successfully to /usr/local/bin/$APP_NAME "
224+ }
225+
226+ # --- Entry point ---
227+
35228usage () {
36- cat >&2 << EOL
229+ cat >&2 << EOL
37230Install rattan and grant necessary capabilities
231+
38232Usage:
39- $myname options RATTAN_BINARY_PATH ...
233+ $myname [ options] [ RATTAN_BINARY_PATH]
40234
41- options:
42- --help|-h Print this help message
235+ If RATTAN_BINARY_PATH is not provided, the latest release is downloaded
236+ automatically from https://github.com/ ${GITHUB_REPO} /releases
43237
44- Example:
45- $myname target/release/rattan
238+ Options:
239+ --help|-h Print this help message
240+
241+ Examples:
242+ $myname # Download and install latest release
243+ $myname target/release/rattan # Install a locally-built binary
46244
47245EOL
48- exit 1
246+ exit 1
49247}
50248
51249POSITIONAL_ARGS=()
52-
53250while [[ $# -gt 0 ]]; do
54- case $1 in
55- --help | -h)
56- usage
57- ;;
58- --* | -* )
59- echo " Unknown option $1 "
60- usage
61- ;;
62- * )
63- POSITIONAL_ARGS+=(" $1 " ) # save positional arg
64- shift # past argument
65- ;;
66- esac
251+ case $1 in
252+ --help | -h)
253+ usage
254+ ;;
255+ --* | -* )
256+ echo " Unknown option $1 "
257+ usage
258+ ;;
259+ * )
260+ POSITIONAL_ARGS+=(" $1 " )
261+ shift
262+ ;;
263+ esac
67264done
68265
69- set -- " ${POSITIONAL_ARGS[@]} " # restore positional parameters
266+ set -- " ${POSITIONAL_ARGS[@]+" ${POSITIONAL_ARGS[@]} " } "
267+
268+ _DOWNLOAD_TMPDIR=" "
70269
71- install_rattan " $@ "
270+ if [[ $# -gt 0 ]]; then
271+ # Local binary provided
272+ install_rattan " $1 "
273+ else
274+ # Download from GitHub
275+ _binary=" $( download_rattan) "
276+ install_rattan " $_binary "
277+ if [ -n " $_DOWNLOAD_TMPDIR " ]; then
278+ rm -rf " $_DOWNLOAD_TMPDIR "
279+ fi
280+ fi
0 commit comments