forked from semaja2/mikrotik-netinstall
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMakefile
More file actions
301 lines (271 loc) · 13.2 KB
/
Makefile
File metadata and controls
301 lines (271 loc) · 13.2 KB
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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
ARCH ?= arm
PKGS ?= wifi-qcom-ac
CHANNEL ?= stable
OPTS ?= -b -r
IFACE ?= eth0
# PKGS_CUSTOM ?= ""
# example: CLIENTIP ?= 172.17.9.101
NET_OPTS ?= $(if $(CLIENTIP),-a $(CLIENTIP),-i $(IFACE))
URLVER ?= https://upgrade.mikrotik.com/routeros/NEWESTa7
channel_ver = $(firstword $(shell for _i in 1 2 3 4 5; do _v=$$(wget -q -O - $(URLVER).$(1)) && [ -n "$$_v" ] && echo "$$_v" && break; sleep 2; done))
ver_ge = $(shell echo '$(1) $(2)' | awk -F'[. ]' '{if($$1>$$3||$$1==$$3&&$$2>=$$4)print 1}')
ifndef VER
VER := $(call channel_ver,$(CHANNEL))
endif
ifndef VER_NETINSTALL
VER_NETINSTALL := $(call channel_ver,$(CHANNEL))
endif
DLDIR ?= downloads
# NOTE: ARCH=x86 is not supported — MikroTik x86 .npk files omit the architecture
# suffix (e.g. routeros-7.22.npk, not routeros-7.22-x86.npk). The pattern below
# would generate routeros-VER-x86.npk which doesn't exist on download.mikrotik.com.
# x86 RouterOS devices (including CHR) rarely need netinstall.
ROUTEROS_FILES := $(foreach arch,$(ARCH),$(DLDIR)/routeros-$(VER)-$(arch).npk)
PKGS_FILES := $(foreach arch,$(ARCH),$(foreach pkg,$(PKGS),$(DLDIR)/$(pkg)-$(VER)-$(arch).npk))
ALL_PACKAGES_ZIPS := $(foreach arch,$(ARCH),$(DLDIR)/all_packages-$(arch)-$(VER).zip)
# Auto-set modescript for device-mode when both VER and VER_NETINSTALL >= 7.22
# Base: mode=advanced; conditionally adds container=yes and/or zerotier=yes based on PKGS
MODESCRIPT ?= $(if $(and $(call ver_ge,$(VER),7.22),$(call ver_ge,$(VER_NETINSTALL),7.22)),/system/device-mode update mode=advanced$(if $(findstring container,$(PKGS)), container=yes)$(if $(findstring zerotier,$(PKGS)), zerotier=yes))
PLATFORM ?= $(shell uname -m)
OS ?= $(shell uname -s)
# Auto-detect qemu-i386 for non-x86_64 platforms
# Priority: ./i386 (container), qemu-i386-static (Debian/Ubuntu), qemu-i386 (Alpine/Fedora)
find_qemu = $(firstword $(foreach q,./i386 qemu-i386-static qemu-i386,$(if $(shell if [ -x "$(q)" ] || command -v $(q) >/dev/null 2>&1; then echo y; fi),$(q))))
ifndef QEMU
QEMU := $(call find_qemu)
endif
CHANNELS := stable long-term testing development
ARCHS := arm arm64 mipsbe mmips smips ppc tile x86
.PHONY: run all service download clean nothing dump test wizard mknetinstall $(CHANNELS) $(ARCHS)
.PHONY: image image-all image-platform image-push image-clean vm-run
.SUFFIXES:
run: all
$(eval PKGS_FILES := $(shell for file in $(PKGS_FILES); do if [ -e "$$file" ]; then echo "$$file"; fi; done))
ifeq ($(OS),Darwin)
@if [ -z "$(QEMU_SYSTEM)" ]; then \
echo "Error: qemu-system-x86_64 required on macOS." >&2; \
echo " Install: brew install qemu" >&2; \
exit 1; \
fi
$(MAKE) vm-run VM_TARGET=run VER=$(VER) VER_NETINSTALL=$(VER_NETINSTALL) \
"ARCH=$(ARCH)" "PKGS=$(PKGS)" "OPTS=$(OPTS)" 'MODESCRIPT=$(MODESCRIPT)'
else
@if [ "$(PLATFORM)" != "x86_64" ] && [ -z "$(QEMU)" ]; then \
echo "Error: qemu-i386 not found. Required to run netinstall-cli on $(PLATFORM)." >&2; \
echo " Debian/Ubuntu: sudo apt install qemu-user-static" >&2; \
echo " Alpine: apk add qemu-i386" >&2; \
echo " Fedora: sudo dnf install qemu-user-static" >&2; \
echo " Or: make run QEMU=/path/to/qemu-i386" >&2; \
exit 1; \
fi
@echo starting netinstall... PLATFORM=$(PLATFORM) ARCH=$(ARCH) VER=$(VER) OPTS="$(OPTS)" NET_OPTS="$(NET_OPTS)" PKGS=$(PKGS) $(if $(MODESCRIPT),MODESCRIPT="$(MODESCRIPT)")
@echo using $(PKGS_FILES)
$(if $(MODESCRIPT),@printf '%s\n' '$(MODESCRIPT)' > .modescript.rsc)
$(if $(findstring x86_64, $(PLATFORM)), , $(QEMU)) $(DLDIR)/netinstall-cli-$(VER_NETINSTALL) $(OPTS) $(NET_OPTS) $(if $(MODESCRIPT),-sm .modescript.rsc) $(ROUTEROS_FILES) $(PKGS_FILES) $(PKGS_CUSTOM)
endif
service: all
ifeq ($(OS),Darwin)
@if [ -z "$(QEMU_SYSTEM)" ]; then \
echo "Error: qemu-system-x86_64 required on macOS." >&2; \
echo " Install: brew install qemu" >&2; \
exit 1; \
fi
$(MAKE) vm-run VM_TARGET=service VER=$(VER) VER_NETINSTALL=$(VER_NETINSTALL) \
"ARCH=$(ARCH)" "PKGS=$(PKGS)" "OPTS=$(OPTS)" 'MODESCRIPT=$(MODESCRIPT)'
else
while :; do $(MAKE) run "ARCH=$(ARCH)" VER=$(VER) 'MODESCRIPT=$(MODESCRIPT)'; done
endif
download: all
@echo use 'make' to run netinstall after connecting $(IFACE) or $(CLIENTIP) to router
all: $(ROUTEROS_FILES) $(DLDIR)/netinstall-cli-$(VER_NETINSTALL) $(ALL_PACKAGES_ZIPS)
@echo finished download ARCH=$(ARCH) VER=$(VER) PKGS=$(PKGS) PLATFORM=$(PLATFORM)
dump:
@echo ARCH=$(ARCH) VER=$(VER) CHANNEL=$(CHANNEL) PLATFORM=$(PLATFORM) OS=$(OS) QEMU=$(QEMU) QEMU_SYSTEM=$(QEMU_SYSTEM) $(if $(MODESCRIPT),MODESCRIPT="$(MODESCRIPT)")
test:
checkmake Makefile
shellcheck --shell=dash mknetinstall tools/container-manager.sh
wizard:
@./mknetinstall
mknetinstall:
@if [ "$$(id -u)" = "0" ]; then \
_dest=/usr/local/bin; \
else \
_dest=$$HOME/.local/bin; \
mkdir -p "$$_dest"; \
fi; \
cp mknetinstall "$$_dest/mknetinstall"; \
chmod +x "$$_dest/mknetinstall"; \
echo "Installed mknetinstall to $$_dest/mknetinstall"
clean:
rm -rf $(DLDIR) images .image-build .vm-build .modescript.rsc .vm-cmd.sh
$(DLDIR)/netinstall-$(VER_NETINSTALL).tar.gz:
mkdir -p $(DLDIR)
wget -O $@ https://download.mikrotik.com/routeros/$(VER_NETINSTALL)/netinstall-$(VER_NETINSTALL).tar.gz
$(DLDIR)/netinstall-cli-$(VER_NETINSTALL): $(DLDIR)/netinstall-$(VER_NETINSTALL).tar.gz
tar zxvf $< -C $(DLDIR)
mv $(DLDIR)/netinstall-cli $(DLDIR)/netinstall-cli-$(VER_NETINSTALL)
touch $@
$(DLDIR)/routeros-$(VER)-%.npk:
mkdir -p $(DLDIR)
wget -O $@ https://download.mikrotik.com/routeros/$(VER)/$(@F)
$(DLDIR)/all_packages-%-$(VER).zip:
mkdir -p $(DLDIR)
wget -O $@ https://download.mikrotik.com/routeros/$(VER)/$(@F)
unzip -o -d $(DLDIR) $@
$(CHANNELS):
$(if $(filter $(ARCHS),$(MAKECMDGOALS)),@:,$(MAKE) $(filter-out $(CHANNELS),$(MAKECMDGOALS)) CHANNEL=$@ ARCH=$(ARCH))
$(ARCHS):
$(MAKE) $(filter-out $(CHANNELS) $(ARCHS),$(MAKECMDGOALS)) CHANNEL=$(or $(filter $(CHANNELS),$(MAKECMDGOALS)),$(CHANNEL)) ARCH=$@
# --- OCI image building (requires: crane, wget) ---
IMAGE ?= tikoci/netinstall
IMAGE_TAG ?= latest
IMAGE_PLATFORMS ?= linux/arm64 linux/arm/v7 linux/amd64
ALPINE_MIRROR ?= https://dl-cdn.alpinelinux.org/alpine/latest-stable/main
BINFMT_IMAGE ?= tonistiigi/binfmt:latest
image image-all:
@for plat in $(IMAGE_PLATFORMS); do \
$(MAKE) image-platform "IMAGE_PLATFORM=$$plat"; \
done
image-platform:
@set -e; \
plat="$(IMAGE_PLATFORM)"; \
case "$$plat" in \
linux/arm64) apk_arch=aarch64; cfg_arch=arm64; need_qemu=1 ;; \
linux/arm/v7) apk_arch=armv7; cfg_arch=arm; need_qemu=1 ;; \
linux/amd64) apk_arch=x86_64; cfg_arch=amd64; need_qemu=0 ;; \
*) echo "Unsupported: $$plat" >&2; exit 1 ;; \
esac; \
ptag=$$(echo "$$plat" | tr '/' '-'); \
output="images/netinstall-$$ptag.tar"; \
echo "Building $$output"; \
rm -rf .image-build; \
mkdir -p .image-build/rootfs/app .image-build/image images; \
echo " alpine rootfs ($$plat)"; \
crane export --platform "$$plat" alpine:latest - | tar xf - -C .image-build/rootfs; \
while read -r _bpath; do \
mkdir -p ".image-build/rootfs/$$(dirname "$$_bpath")"; \
ln -sf /bin/busybox ".image-build/rootfs/$$_bpath"; \
done < .image-build/rootfs/etc/busybox-paths.d/busybox; \
wget -q "$(ALPINE_MIRROR)/$$apk_arch/APKINDEX.tar.gz" -O .image-build/apkindex.tar.gz; \
tar xzf .image-build/apkindex.tar.gz -C .image-build APKINDEX; \
make_ver=$$(awk '/^P:make$$/{f=1} f&&/^V:/{print substr($$0,3);exit}' .image-build/APKINDEX); \
echo " make $$make_ver ($$apk_arch)"; \
wget -q "$(ALPINE_MIRROR)/$$apk_arch/make-$${make_ver}.apk" -O .image-build/make.apk; \
tar xzf .image-build/make.apk -C .image-build/rootfs usr/bin/make; \
chmod +x .image-build/rootfs/usr/bin/make; \
if [ "$$need_qemu" = "1" ]; then \
echo " qemu-i386 from $(BINFMT_IMAGE) ($$plat)"; \
crane export --platform "$$plat" $(BINFMT_IMAGE) - | \
tar xf - -C .image-build usr/bin/qemu-i386; \
mv .image-build/usr/bin/qemu-i386 .image-build/rootfs/app/i386; \
chmod +x .image-build/rootfs/app/i386; \
fi; \
cp Makefile .image-build/rootfs/app/Makefile; \
echo " creating layer"; \
tar cf .image-build/image/layer.tar -C .image-build/rootfs .; \
_digest=$$( ( shasum -a 256 .image-build/image/layer.tar 2>/dev/null || \
sha256sum .image-build/image/layer.tar ) | cut -d' ' -f1); \
printf '{"architecture":"%s","os":"linux","config":{"WorkingDir":"/app","Cmd":["make","service"]},"rootfs":{"type":"layers","diff_ids":["sha256:%s"]}}' \
"$$cfg_arch" "$$_digest" > .image-build/image/config.json; \
printf '[{"Config":"config.json","RepoTags":["$(IMAGE):$(IMAGE_TAG)"],"Layers":["layer.tar"]}]' \
> .image-build/image/manifest.json; \
tar cf "$$output" -C .image-build/image config.json manifest.json layer.tar; \
rm -rf .image-build; \
echo "Built: $$output"
image-push:
@for plat in $(IMAGE_PLATFORMS); do \
ptag=$$(echo "$$plat" | tr '/' '-'); \
crane push "images/netinstall-$$ptag.tar" "$(IMAGE):$$ptag"; \
crane mutate --workdir /app --cmd "make,service" "$(IMAGE):$$ptag"; \
done
crane index append \
-t "$(IMAGE):$(IMAGE_TAG)" \
$(foreach p,$(IMAGE_PLATFORMS),-m "$(IMAGE):$(subst /,-,$(p))")
image-clean:
rm -rf images .image-build
# --- VM support for macOS (qemu-system-x86_64 with vmnet-bridged) ---
# On macOS, netinstall-cli (Linux ELF) cannot run natively. Instead, boot a
# lightweight QEMU VM with the Alpine rootfs from the amd64 OCI image,
# share the working directory via 9p, and bridge networking via vmnet.
QEMU_SYSTEM ?= $(shell command -v qemu-system-x86_64 2>/dev/null)
VMLINUZ ?= $(DLDIR)/vmlinuz-virt
VM_INITRAMFS ?= $(DLDIR)/initramfs-netinstall.gz
VM_TARGET ?= run
# Auto-build amd64 OCI image if needed (provides Alpine rootfs for VM)
images/netinstall-linux-amd64.tar:
$(MAKE) image-platform IMAGE_PLATFORM=linux/amd64
# Build vmlinuz + initramfs from linux-virt APK and OCI image.
# Single APK provides matched kernel + all modules (including 9p for virtfs).
$(VMLINUZ) $(VM_INITRAMFS): images/netinstall-linux-amd64.tar
@set -e; \
echo "Building VM kernel + initramfs..."; \
rm -rf .vm-build; mkdir -p .vm-build/rootfs $(DLDIR); \
tar xf $< -C .vm-build layer.tar; \
tar xf .vm-build/layer.tar -C .vm-build/rootfs; \
echo " resolving linux-virt version"; \
wget -q "$(ALPINE_MIRROR)/x86_64/APKINDEX.tar.gz" -O .vm-build/apkindex.tar.gz; \
tar xzf .vm-build/apkindex.tar.gz -C .vm-build APKINDEX; \
kvirt=$$(awk '/^P:linux-virt$$/{f=1} f&&/^V:/{print substr($$0,3);exit}' .vm-build/APKINDEX); \
echo " linux-virt $$kvirt"; \
wget -q "$(ALPINE_MIRROR)/x86_64/linux-virt-$${kvirt}.apk" -O .vm-build/linux-virt.apk; \
tar xzf .vm-build/linux-virt.apk -C .vm-build boot/vmlinuz-virt; \
cp .vm-build/boot/vmlinuz-virt $(VMLINUZ); \
echo " extracting kernel modules"; \
kver=$$(tar tzf .vm-build/linux-virt.apk | sed -n 's|^lib/modules/\([^/]*\)/.*|\1|p' | head -1); \
tar xzf .vm-build/linux-virt.apk -C .vm-build/rootfs \
"lib/modules/$$kver/kernel/drivers/virtio/" \
"lib/modules/$$kver/kernel/drivers/net/virtio_net.ko.gz" \
"lib/modules/$$kver/kernel/drivers/net/net_failover.ko.gz" \
"lib/modules/$$kver/kernel/net/core/failover.ko.gz" \
"lib/modules/$$kver/kernel/net/9p/" \
"lib/modules/$$kver/kernel/fs/9p/" \
"lib/modules/$$kver/kernel/fs/netfs/" 2>/dev/null || true; \
find .vm-build/rootfs/lib/modules -name '*.ko.gz' -exec gunzip {} \; 2>/dev/null; \
echo " creating init"; \
printf '%s\n' '#!/bin/sh' \
'mount -t proc none /proc' \
'mount -t sysfs none /sys' \
'mount -t devtmpfs none /dev' \
'kmod=/lib/modules/$$(uname -r)/kernel' \
'insmod $$kmod/drivers/virtio/virtio.ko 2>/dev/null' \
'insmod $$kmod/drivers/virtio/virtio_ring.ko 2>/dev/null' \
'insmod $$kmod/drivers/virtio/virtio_pci.ko 2>/dev/null' \
'insmod $$kmod/net/core/failover.ko 2>/dev/null' \
'insmod $$kmod/drivers/net/net_failover.ko 2>/dev/null' \
'insmod $$kmod/drivers/net/virtio_net.ko' \
'insmod $$kmod/fs/netfs/netfs.ko 2>/dev/null' \
'insmod $$kmod/net/9p/9pnet.ko' \
'insmod $$kmod/net/9p/9pnet_virtio.ko' \
'insmod $$kmod/fs/9p/9p.ko' \
'mkdir -p /host' \
'mount -t 9p -o trans=virtio,version=9p2000.L hostfs /host' \
'ip link set eth0 up' \
'ip addr add 169.254.1.1/16 dev eth0' \
'sh /host/.vm-cmd.sh' \
'poweroff -f' > .vm-build/rootfs/init; \
chmod +x .vm-build/rootfs/init; \
( cd .vm-build/rootfs && find . | cpio -o -H newc 2>/dev/null | gzip > ../../$(VM_INITRAMFS) ); \
rm -rf .vm-build; \
echo "Built: $(VMLINUZ) $(VM_INITRAMFS)"
# Boot QEMU VM with vmnet-bridged networking to run netinstall-cli
# IFACE = macOS interface to bridge to (e.g. en5); inside VM it becomes eth0
# Requires: brew install qemu, sudo for vmnet-bridged
vm-run: all $(VMLINUZ) $(VM_INITRAMFS)
$(eval PKGS_FILES := $(shell for file in $(PKGS_FILES); do if [ -e "$$file" ]; then echo "$$file"; fi; done))
@printf '%s\n' '#!/bin/sh' 'cd /host' \
'exec make $(VM_TARGET) IFACE=eth0 OS=Linux PLATFORM=x86_64 QEMU= VER=$(VER) VER_NETINSTALL=$(VER_NETINSTALL) "ARCH=$(ARCH)" "PKGS=$(PKGS)" "OPTS=$(OPTS)" $(if $(MODESCRIPT),"MODESCRIPT=$(MODESCRIPT)")' \
> .vm-cmd.sh
@echo "Starting QEMU VM ($(VM_TARGET)) bridged to $(IFACE)..."
sudo $(QEMU_SYSTEM) \
-m 256M \
-kernel $(VMLINUZ) \
-initrd $(VM_INITRAMFS) \
-append "console=ttyS0 quiet" \
-virtfs local,path=.,mount_tag=hostfs,security_model=none \
-netdev vmnet-bridged,id=n0,ifname=$(IFACE) \
-device virtio-net-pci,netdev=n0 \
-nographic \
-no-reboot
@rm -f .vm-cmd.sh
nothing:
while :; do sleep 3600; done