Compare commits

...

117 Commits
v1.0 ... main

Author SHA1 Message Date
l0nia
d3aa7ae988
Merge pull request #48 from hlst2/main
Main
2025-02-24 08:24:32 +01:00
l0nia
5e0f652e40
Add steps to upgrade blue-merle to README.md 2025-02-08 20:19:34 +01:00
l0nia
39cda00dbf
Merge pull request #54 from yannis-srl/fix-gh-actions-deprecation-notice
Fix: GH actions artifact actions deprecation warning
2025-01-30 08:05:19 +01:00
yannis-srl
822bca6f4f
Bumping upload to v4 2025-01-10 14:42:18 +01:00
muelli
f3ef189b26
Firmeware Version 4.3.19 Compatible 2024-10-01 08:16:51 +02:00
hlst2
770072b531 Merge branch 'main' of https://github.com/hlst2/blue-merle 2024-09-30 21:52:31 -04:00
muelli
037ef9f306 added full tac list from osmocom 2024-09-30 21:51:41 -04:00
nestire
6d61722bf2
change version to 2.0.4 2024-09-23 11:50:27 +02:00
nestire
bc67b8107c
support fw 4.3.19 2024-09-23 11:48:53 +02:00
muelli
53d8f3afe7
Merge pull request #41 from nestire/main
bump for version 4.3.17 of mudi firmware
2024-07-01 10:50:43 +02:00
nestire
c31b0b1779
change version to 2.0.3 2024-06-24 12:11:36 +02:00
nestire
8a20a370c8
support fw 4.3.17 2024-06-24 12:02:36 +02:00
nestire
76c0fe482b
bump for version 4.3.12 of mudi firmware 2024-05-21 14:35:48 +02:00
muelli
1aa741ffd8
Merge pull request #28 from nestire/main
mcu msg stage2 made more clear, add new + old IMEI shown in the end to visualy check + Firmware 4.3.9 check
2024-04-25 13:01:47 +02:00
nestire
8f33aeac76
add firmware 4.3.9 to supported firmware 2024-02-27 17:11:35 +01:00
nestire
68e15a863c
mcu messages stage2 made more clear, add new + old IMEI shown in the end to visualy check 2024-01-30 16:23:58 +01:00
Jakob Rieck
a2770e81c3 doc: Updates PDF documentation for v2.0 2023-10-19 17:57:16 +02:00
muelli
b18e73fe9e
README: Fix link to the modem's documentation 2023-10-19 10:28:48 +02:00
Jakob Rieck
d5721f3279 readme: Adds offline installation instructions 2023-10-19 07:43:16 +02:00
Jakob Rieck
feddf2ec55 readme: Update for v2.0 2023-10-19 07:13:17 +02:00
Tobias Mueller
bdc8837660 start services earlier to make the client tracking work
My hypothesis is that our script does not fully finish before the next
daemon starts. I don't fully understand why our script does not seem to
finish before the next script runs. But this appears to be working
better.
2023-10-18 22:37:12 +02:00
Tobias Mueller
948db4b6fa mac: actually delete the clients.db
This hopefully makes the client tracking work better. Before this
change, the Web UI would not be able to track clients, probably because
the database contains garbage. Now that we delete the file properly, it
will be re-generated (in the volatile tmpfs) and the Web UI should show
everything nicely.
2023-10-18 21:06:46 +02:00
Tobias Mueller
9a3f072c23 functions: provide a Lua-based IMEI generation
Soon, we can retire the relatively expensive Python script and use shell
and Lua.
2023-10-18 21:00:23 +02:00
Tobias Mueller
4b427bbcfc lua: print a random IMEI from the seed provided
I don't know how much stronger we can see the LUA RNG. It seems to
accept an "int" which I assume is 32 bit. It will complain if the seed
is too big.
2023-10-18 20:59:33 +02:00
Tobias Mueller
a3fff24042 lua: make a 15 digit IMEI 2023-10-18 20:58:05 +02:00
Tobias Mueller
1cd1125cfe lua: remove debug prints 2023-10-18 20:57:51 +02:00
Tobias Mueller
dfbcbe7506 functions: Provide a method to read the SIM ID
We can use the identifier of the SIM as a stable token to derive a
deterministic IMEI rather than the IMSI which is only available once the
SIM has been (PIN) unlocked.
2023-10-18 20:57:19 +02:00
Jakob Rieck
1a052c57e2 web: Fixes spinner, updates instructions 2023-10-18 18:01:40 +02:00
Jakob Rieck
ea05c4441b blue-merle: Syntax fix 2023-10-18 17:39:29 +02:00
Tobias Mueller
488fdaa18f Merged shutdown of device 2023-10-18 16:01:03 +02:00
Jakob Rieck
b97c0f06eb blue-merle: Fixes shutdown 2023-10-18 15:48:11 +02:00
Tobias Mueller
f8b8fa4805 Merged CI fixes 2023-10-18 15:30:34 +02:00
Jakob Rieck
5d8efbb4e3 ci: Fixes build 2023-10-18 15:11:33 +02:00
Tobias Mueller
5c6976aa11 change MAC address in repeater mode
Now, the hosting WiFi sees random a random MAC address every time the
device is rebooted.
2023-10-18 14:58:26 +02:00
Tobias Mueller
c68661e1a5 switch: guard the IMEI generation with a timeout
So that we can be reasonably sure that the script terminates and the
switch works again.
2023-10-18 14:51:46 +02:00
Tobias Mueller
9c08efc19a Merged changes for the v4.3.8 firmware 2023-10-18 14:31:02 +02:00
Tobias Mueller
d4886a54a9 volatile-macs: define the service self-sufficiently
This makes it hopefully a bit easier to see what we're doing because you
don't need to chase the files down. We don't re-use that functionality
anywhere. Neither would we.
A more important change is not restarting the gl-tertf service.
First of all, there seems to be no process attached to gl-tertf. It is
the "Bandwidth Monitor" and part of the kmod-gl-sdk4-tertf package, so
it's kernel module. It does not appear to be holding the clients.db.
There is, however, gl_clients which also makes sense, naming wise.
That service defines that /usr/bin/gl_clients_update ought to be run.
And stracing it shows that it does indeed touch the database:
open("/etc/oui-tertf/client.db",
O_RDWR|O_CREAT|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC, 0644) = 7

It also appears to be re-creating the file when it's missing.

Anyway, we have the service stopped during installation so that we can
safely delete the file without the process complaining. We also install
our volatile mac service s.t. it runs ahead of the gl-client service so
that the clientdb gets saved in volatile memory.
2023-10-18 14:27:55 +02:00
Tobias Mueller
d47916552d volatile macs: match our service's priority to our dependencies
The value of 20 is probably a copy and paste leftover from the
blue-merle service. The real START value that we care about is 60.
2023-10-18 13:24:47 +02:00
Tobias Mueller
03c262a8c3 blue-merle: generate a temporary IMEI before the the SIM switch
After shutting down the modem and before replacing the SIM, we generate
a temporary random IMEI to prevent accidental disclosure of the new IMSI
under the old IMEI. It should not happen but we do not control the modem
as much as we'd like, e.g. fully power it down. So it may not be
necessary but we consider it defense in depth.
2023-10-18 11:02:28 +02:00
Tobias Mueller
fe4021feb9 blue-merle: restore old scrpit for SSH use
We restore from fa11fc45ca9191219c3965ad559ee8060f34a358.
We know that this worked well enough the last time as we run out of time
to test new things.
Ideally, though, we manage to consolidate the SSH use and the switch use
(and the Web use) to not have different implementations of the same
functionality so that testing gets much simpler.
2023-10-18 10:43:05 +02:00
Tobias Mueller
f8d35b15d5 mac-wipe: Do not stop terft as it cannot be stopped
The service does not implement stop() as it throws a scary warning when
installing. So we do not even attempt to stop it.
2023-10-18 10:36:33 +02:00
Tobias Mueller
7b64f3aa9b functions: provide SET IMEI capability from shellscripts
It doesn't work for me but I also haven't tried very hard as we do not
migrate off the Python script now.
2023-10-18 10:35:39 +02:00
Tobias Mueller
c8634591b2 added Lua script to generate an IMEI
We currently use a Python script to generate IMEIs. Loading Python is
relatively expensive on our target platform so I hope we can use
something quicker.
2023-10-18 10:31:49 +02:00
Tobias Mueller
512da23200 web: stop spinning when reboot is ready 2023-10-17 22:02:24 +02:00
Tobias Mueller
a0fad3a833 mac-wipe: fix syntax error for argument comparison 2023-10-17 22:02:04 +02:00
Tobias Mueller
9466707119 web: fix syntax error 2023-10-17 20:03:31 +02:00
Tobias Mueller
87c420e2de web: remove unused handleOpkg function
It served as an example of how we could do things. Now we don't need it
any longer as we know what we need to do. Or so we think.
2023-10-17 20:00:41 +02:00
Tobias Mueller
db8de09293 web: remove unused handleRemove function 2023-10-17 19:59:18 +02:00
Tobias Mueller
8b4d371c9f functions: Do not automatically restart the wifi on RESET_BSSIDS
Instead of restarting the service we have the service started *after*
our modification to its configuration.
This makes it slightly more inconvenient to reset the WiFi BSSIDs while
the device is booted but that capability can be restored and made better
through an executable, say, /usr/bin/reset-wifi-bssids or something.

We also split the volatile client MACs into its own service to have
a bit of a clearer separation of duties. This will allow us to eventually
split the package more easily into sub-packages with finer-grained control.
2023-10-17 19:52:11 +02:00
Tobias Mueller
1c3c5f79e1 web: fix syntax error for missing comma 2023-10-17 17:20:26 +02:00
Tobias Mueller
0fbb00612c blue-merle: provide a somewhat interactive way to set a new IMEI via SSH
This can currently only provide a random IMEI only. But we could easily
recover the deleted script. But it'd be nicer to have a central
configuration for the mode.
2023-10-17 17:18:08 +02:00
Tobias Mueller
c88d04003b web: forcefully timout the python script
sometimes it has a hickup.
2023-10-17 16:54:43 +02:00
Tobias Mueller
f7a9494a0c web: logging calls to helper binary 2023-10-17 16:54:18 +02:00
Tobias Mueller
958a0ecc99 web: actually changing the UI when the script returns
When it doesn't time out it seems to be working. Weird.
2023-10-17 16:51:51 +02:00
Tobias Mueller
580d456ed1 web: try calling random-imei
It fails from the "SIM Swap" button, I don't know yet, why.
2023-10-17 16:38:57 +02:00
Tobias Mueller
fa8e630714 web: remove unused functions
they are not referenced from the rest of the file.
2023-10-17 16:37:55 +02:00
Tobias Mueller
242235757a switch: log when stage1 has finished running 2023-10-17 15:49:41 +02:00
Tobias Mueller
0fa4d89602 switch: do not run the lock contention check in the background
It actually prevents out main script from running because the flock
process is still running in the background.
2023-10-17 15:49:25 +02:00
Tobias Mueller
8053d44ce7 switch: removing stale switch file
We have separated it into stages so we don't need the monolith
2023-10-17 15:48:51 +02:00
Tobias Mueller
0b4b04c48b switch: log in stage2
So that we can notice whether our script has successfully run.
You can read the log with something like
logread -l 10000 -e blue
2023-10-17 15:16:49 +02:00
Tobias Mueller
ed6856c490 switch: split the switch action into separate stages
When toggling the switch, a lock is held for a relatively long time,
preventing another toggling of the switch to be noticed. With this
change, I hope we can first shutdown the modem, wait for a toggle, and
then continue.
We're losing the abort function but I currently don't know how we would
be able to keep that functionality given that the toggle is queued and
we don't get the notification.
2023-10-17 15:15:28 +02:00
Tobias Mueller
eab0633ad9 switch: Asynchronously execute blue merle when toggled
I hope that this allows us to use the toggle again to advance the Blue
Merle logic.
If all goes well, the script finishes execution and the switch lock in
/var/lock/gl-switch.lock is released so that the button can be used
again.
2023-10-17 13:51:51 +02:00
Tobias Mueller
db1c0c4c69 switch: limit execution time to 90 seconds
We don't want to let it run forever because it blocks the toggle from
working. But even if it's not, we wouldn't want to have the script run
eternally.
2023-10-17 13:02:40 +02:00
Tobias Mueller
ae40dcec1f web: weeding out the opkg functionality
We don't need any of that but I let the functions live just in case they
are referenced anywhere.
2023-10-17 13:00:35 +02:00
Tobias Mueller
396ff7ea0e switch: shutdown in time
Now it's more in sync with the display.
2023-10-17 10:16:10 +02:00
Tobias Mueller
2cf6495aad switch: re-format messages to appear more nicely on the screen
We cannot control whitespace :(
2023-10-17 10:12:17 +02:00
Tobias Mueller
6b7e11137b switch: send SIM swap script to background
I think we can only toggle while the handler is not active.
I toggled to ON and got the script running. But then I couldn't toggle
OFF, presumingly because the script was still running.
By sending it to the background I hope it will allow me to toggle OFF.
2023-10-17 09:57:47 +02:00
Tobias Mueller
881e9792c7 switch: call blue-merle-switch when toggling the button to on 2023-10-17 09:23:58 +02:00
Tobias Mueller
29db0fab27 switch: show message on v4 MCU 2023-10-17 09:20:36 +02:00
Tobias Mueller
df8578e402 functions: CHECK_ABORT for v4 MCU 2023-10-17 09:18:00 +02:00
Tobias Mueller
11a8afd0a9 python: Added a --generate-only flag to not set the IMEI
We can probably set the IMEI through the gl_modem command.
Currently, the Web interface times out when calling random-imei. I want
to separate the steps so that each step does not take as long.
2023-10-17 09:05:02 +02:00
Tobias Mueller
53bf156fd0 python: only get the IMSI when in deterministic mode
We don't need the IMSI otherwise so let's not spend the time obtaining
it.
2023-10-17 09:01:31 +02:00
Tobias Mueller
4a16277ac2 python: debug print reading IMSI
It seems to timeout somewhere and I want to know where.
2023-10-17 09:00:56 +02:00
Tobias Mueller
6ac26e1a33 web ui: Fix a syntax error by closing the E() 2023-10-16 17:26:46 +02:00
Tobias Mueller
955d0f993e blue-merle: make it return the new IMEI after generating a random IMEI 2023-10-16 16:58:15 +02:00
Tobias Mueller
3b87b1b8f4 web: offer to shutdown on SIM swap
It currently times out when generating an IMEI.
I wonder what the timeouts are.
2023-10-16 16:57:42 +02:00
Tobias Mueller
b02faa7adb web: stab at a Web UI for SIM swap 2023-10-16 16:36:33 +02:00
Tobias Mueller
fa11fc45ca blue-merle: Display a shutdown message before finally halting the system 2023-10-16 16:25:45 +02:00
Tobias Mueller
b15a9390ec Install Python script as executable
This makes it a tiny bit easier to develop on the device because we can
execute the script directly rather than having to prefix it.
2023-10-16 16:25:23 +02:00
Tobias Mueller
259bffc515 switch: show a message on toggling the button
To see more easily whether we have control.
2023-10-16 16:10:17 +02:00
Tobias Mueller
119a287304 postinst: show a brief message
Somehow it disconnects me from the wifi, I guess it's related to
restarting the gl-tertf service. I wonder whether the installation
completes so I make a message appear.
2023-10-16 16:09:55 +02:00
Tobias Mueller
4636d77222 web: rename files from opkg2 to blue-merle
This makes it easier to find our own files...
2023-10-16 16:05:58 +02:00
Tobias Mueller
ce77151081 switch: log action on the toggle
It's a bit easier to follow than looking at the generated files.
2023-10-16 16:03:11 +02:00
Tobias Mueller
55a6f8d10f fix syntax error in web interface
I want to see it in the menu before further developing the
functionality.
2023-10-16 15:54:46 +02:00
Tobias Mueller
9bd78b4a1d postinst: Define button action after install
I've seen this in /etc/rc.button/switch
2023-10-16 15:16:51 +02:00
Tobias Mueller
4a4b5a4e0b dependencies: Depend on python3-serial only to avoid heavy python package
python3-serial depends on python3-light which is hopefully, well,
lighter.
2023-10-16 15:16:00 +02:00
Tobias Mueller
4f584b4b6c mac-wipe: avoid accumulating mounts
If we mount over and over again we may consume memory unnecessarily.
2023-10-16 15:15:29 +02:00
Tobias Mueller
4beae781a2 mac-wipe: copy existing database into a volatile memory
rather than deleting everything.
It seems that the device stops working when deleting the database. That
is, the connection to the Internet stops working which is very safe as
it does not leak any data but arguably defeats the purpose of the
device.
2023-10-16 14:51:59 +02:00
Tobias Mueller
6137fc0ea7 remove bash and patch dependency
I couldn't find a file requiring bash. And we don't patch anything
during installation so I hope we can live without those dependencies.
2023-10-16 14:21:56 +02:00
Tobias Mueller
8327c706d6 EXTRA_DEPENDS with comma and space
So with the commas only, it installed without complaining. Which is
weird, because what does it recognise as dependency that it think is
fulfilled? The long name with all the commas?
2023-10-16 14:16:09 +02:00
Tobias Mueller
e02de82dba mac-wipe: log execution to debug where it's stuck
When installing the package, it appears to be stuck somewhere. I want to
know where and why, so I use the logging facilities.
2023-10-16 14:05:09 +02:00
Tobias Mueller
0e6af2eb77 install: make script in /etc/gl-switch.d/ executable
Then we should be able to have it executed when the button is switched.
2023-10-16 14:03:10 +02:00
Tobias Mueller
7531d2b691 encode dependencies with commas
The intention is to have the system recognise our dependencies and to
reject installation if the dependencies are not met. Better yet, have it
install the dependencies!
2023-10-16 13:57:26 +02:00
Tobias Mueller
e867547c4f preinst: remove MCU version check
The gl-sdk4-mcu looks sufficiently recent so we skip the MCU parts in
the preinst. This makes the code easier and less error-prone.
2023-10-16 13:37:49 +02:00
Tobias Mueller
04b1e6dfcb Depend on luci rather than GL
In v4 firmware neither gl-ui nor gl-e750-mcu exists.
2023-10-16 13:28:13 +02:00
Tobias Mueller
6e6a7fdca6 Merge branch 'main' of https://github.com/srlabs/blue-merle into luci 2023-10-16 12:49:22 +02:00
Tobias Mueller
226687590e adapt opkg luci web interface for blue-merle
This is a snapshot only. It does not work and serves as a prototype
only. Now, we can see how to add a menu item and how to call our
executable on the flash.
2023-10-16 12:49:19 +02:00
muelli
5163b47811
Merge pull request #14 from rieck-srlabs/ci-dev
Initial CI implementation
2023-10-16 12:46:14 +02:00
Tobias Mueller
9894f936e0 Prepare for a new version with v4 compatibility 2023-10-16 12:44:11 +02:00
Tobias Mueller
ea66e67d07 Merge remote-tracking branch 'origin/main' into luci 2023-10-16 12:39:14 +02:00
Tobias Mueller
3e686c79d3 bridge blue merle functionality to the Web
This follows the pattern of opkg. I took the luci-app-opkg package and
tried to follow its behaviour.
2023-10-16 12:35:55 +02:00
Tobias Mueller
4c29fe2732 removing v3 patches
The switch doesn't work yet but "uci  get switch-button.@main[0].func"
gets the currently active configured action for button.
I guess that /etc/rc.button/switch is called on every press of the
button.
2023-10-16 12:25:19 +02:00
Tobias Mueller
843ddf9e7d switch: attempt to react to the hardware button
I don't know whether this works. On the v4 firmware, we can see files in
/etc/gl-switch.d but I don't know yet how the system determines which
file to choose. I assume it's dependend on the "page" of the display.
But I haven't figured out yet how or rather where those pages are
organised.
2023-10-16 12:14:00 +02:00
Tobias Mueller
1a17e06a12 MAC address removal for v4 firmware
I have rsynced the whole device before associating with a new device and
after. The only file that got modified was /etc/oui-tertf/client.db.
We intend to have it stored in memory rather than on flash. This should
be okay since the kernel also holds the MAC addresses in memory.
2023-10-16 12:09:00 +02:00
Tobias Mueller
9c9ab738ac functions: Remove unsued restore function
This is not referenced anywhere so I guess that we don't need it.
2023-10-16 12:04:46 +02:00
Tobias Mueller
61a7466117 initial PoC for a Web interface 2023-10-16 11:53:08 +02:00
muelli
406360fad2
Merge pull request #11 from srlabs/verifyimei_len
Added IMEI character length validation check
2023-10-16 09:51:31 +02:00
Jakob Rieck
862ad1f7e3 Fixes Makefile (EXTRA_DEPENDS) 2023-10-16 09:12:23 +02:00
Jakob Rieck
ef35ca9899 Initial CI implementation
- Triggers on push events to any branch
- Builds the blue-merle package using the 23.05.0 SDK
- Currently does not sign the package
- Adapts Makefile: switches from Runtime + Buildtime dependencies
  to Runtime dependencies only
2023-10-14 11:27:31 +02:00
Tobias Mueller
da1a2f071c luci: first customisation of the Web UI
This is a very small baby step towards our own page.
2023-10-13 13:13:30 +02:00
nicholas
187378a1d6 Added IMEI character length validation check in
imei_generate.py which ensures IMEI has 14-char
length, including validation digit
2023-10-13 13:05:40 +02:00
Tobias Mueller
75b1291e00 vendorise upstream opkg app
This is for experimenting with building a LuCI app.
I hope we can take this as a base and extend it to our needs.
The source is
https://github.com/openwrt/luci/raw/openwrt-22.03/applications/luci-app-opkg/htdocs/luci-static/resources/view/opkg.js
2023-10-13 12:48:20 +02:00
Linuzifer
09d2f8c087
Merge pull request #10 from muelli/v4-bail
hard fail on v4 and soft-fail on v3 of the firmwares
2023-09-12 15:00:14 +02:00
Tobias Mueller
928d4360ca depend on gl-ui and gl-e750-mcu because we modify those packages
We do need the MCU package and we modify the web UI so let's actually
depend on the relevant packages.
2023-08-28 10:41:13 +02:00
Tobias Mueller
d934a6c028 check for the actual version 3.215
I've made a typo in the previous attempt. Now I'm checking for the
correct version and it works much better.
2023-08-28 10:39:49 +02:00
Tobias Mueller
7aaf65e8bd support a clean uninstall of blue-merle
Before this change, uninstalling the package would complain about
/tmp/sim_change_start not existing:

root@GL-E750:/tmp# opkg remove blue-merle
Removing package blue-merle from root...
The /tmp/ directory exists.
The /etc/ directory exists.
killall: gltertf: no process killed
No file found within /tmp/tertf. No shredding to be done there.
No file found within /etc/tertf. No shredding to be done there.
Looks like /tmp/ is clean!
Looks like /etc/ is clean!
rm: can't remove '/tmp/sim_change_start': No such file or directory
rm: can't remove '/tmp/sim_change_switch': No such file or directory
No packages removed.
Collected errors:
 * pkg_run_script: package "blue-merle" postrm script returned status 1.
root@GL-E750:/tmp#

So we use -f to not raise an error if the file does not exist.
2023-08-28 09:55:41 +02:00
Tobias Mueller
0f2b358b31 Makefile: detect version 4 and bail out while soft-failing for version 3
We assume that the version 3 series is compatible, but still prompt the
user if they are using a version that we have not yet tested.

We know that version 4 is not compatible, so we bail out directly.

The double-dollar is for escaping the Makefile.
2023-08-28 09:33:28 +02:00
24 changed files with 1054 additions and 484 deletions

63
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,63 @@
name: CI
on:
push:
paths-ignore:
- 'README.md'
workflow_dispatch:
concurrency:
group: CI
cancel-in-progress: false
jobs:
build-packages:
name: Build Packages
runs-on: ubuntu-latest
strategy:
fail-fast: false
env:
owrt-package: blue-merle
SDK_URL: "https://downloads.openwrt.org/releases/23.05.0/targets/ath79/nand/openwrt-sdk-23.05.0-ath79-nand_gcc-12.3.0_musl.Linux-x86_64.tar.xz"
SDK_FILENAME: "openwrt-sdk-23.05.0-ath79-nand_gcc-12.3.0_musl.Linux-x86_64.tar.xz"
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event_name == 'push' && github.ref || 'main' }}
- name: Get the SDK
run: |
mkdir sdk
cd sdk
wget "${SDK_URL}"
tar xf "${SDK_FILENAME}"
- name: Set up package dir
run: |
cd "sdk/${SDK_FILENAME%.tar.xz}"
mkdir package/${{ env.owrt-package }}
ln -s "${GITHUB_WORKSPACE}"/Makefile package/${{ env.owrt-package }}/
ln -s "${GITHUB_WORKSPACE}"/files package/${{ env.owrt-package }}/
- name: Update packages feed
run: |
cd "sdk/${SDK_FILENAME%.tar.xz}"
scripts/feeds update packages >/dev/null
- name: Set up OpenWrt config
run: |
cd "sdk/${SDK_FILENAME%.tar.xz}"
echo "CONFIG_SIGNED_PACKAGES=n" > .config
make defconfig
- name: Build package
run: |
cd "sdk/${SDK_FILENAME%.tar.xz}"
make -j$(nproc) V=s package/${{ env.owrt-package }}/compile
make -j1 V=s package/index
- name: Generate artifact name
id: generate-name
run: echo "artifact-name=${{ github.event.repository.name }}-${{ github.ref_name }}-${{ github.sha }}" >> $GITHUB_OUTPUT
- name: Prepare artifact
run: |
mkdir package-output
rsync -av sdk/"${SDK_FILENAME%.tar.xz}"/bin/packages/mips_24kc/base/"${{ env.owrt-package }}"*.ipk package-output/
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.generate-name.outputs.artifact-name }}
path: package-output/

Binary file not shown.

100
Makefile
View File

@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=blue-merle
PKG_VERSION:=1.0.0
PKG_VERSION:=2.0.4
PKG_RELEASE:=$(AUTORELEASE)
PKG_MAINTAINER:=Matthias <matthias@srlabs.de>
@ -12,7 +12,7 @@ include $(INCLUDE_DIR)/package.mk
define Package/blue-merle
SECTION:=utils
CATEGORY:=Utilities
DEPENDS:=+bash +coreutils-shred +python3 +python3-pyserial +patch
EXTRA_DEPENDS:=luci-base, gl-sdk4-mcu, coreutils-shred, python3-pyserial
TITLE:=Anonymity Enhancements for GL-E750 Mudi
endef
@ -29,8 +29,10 @@ endef
define Package/blue-merle/install
$(CP) ./files/* $(1)/
$(INSTALL_BIN) ./files/etc/init.d/* $(1)/etc/init.d/
$(INSTALL_BIN) ./files/lib/blue-merle/mac-wipe.sh $(1)/lib/blue-merle/mac-wipe.sh
$(INSTALL_BIN) ./files/usr/bin/blue-merle $(1)/usr/bin/blue-merle
$(INSTALL_BIN) ./files/etc/gl-switch.d/* $(1)/etc/gl-switch.d/
$(INSTALL_BIN) ./files/usr/bin/* $(1)/usr/bin/
$(INSTALL_BIN) ./files/usr/libexec/blue-merle $(1)/usr/libexec/blue-merle
$(INSTALL_BIN) ./files/lib/blue-merle/imei_generate.py $(1)/lib/blue-merle/imei_generate.py
endef
define Package/blue-merle/preinst
@ -42,7 +44,7 @@ define Package/blue-merle/preinst
if [ -f "/tmp/sysinfo/model" ] && [ -f "/etc/glversion" ]; then
echo "You have a `cat /tmp/sysinfo/model`, running firmware version `cat /etc/glversion`."
fi
echo "blue-merle has only been tested with GL-E750 Mudi Version 3.215."
echo "blue-merle has only been tested with GL-E750 Mudi Versions up to 4.3.19"
echo "The device or firmware version you are using have not been verified to work with blue-merle."
echo -n "Would you like to continue on your own risk? (y/N): "
read answer
@ -58,80 +60,46 @@ define Package/blue-merle/preinst
fi
}
UPDATE_MCU() {
echo "6e6b86e3ad7fec0d5e426eb9a41c51c6f0d6b68a4d341ec553edeeade3e4b470 /tmp/e750-mcu-V1.0.7.bin" > /tmp/e750-mcu.bin.sha256
wget -O /tmp/e750-mcu-V1.0.7.bin https://github.com/gl-inet/GL-E750-MCU-instruction/blob/master/e750-mcu-V1.0.7-56a1cad7f0eb8318ebe3c3c46a4cf3ff.bin?raw=true
if sha256sum -cs /tmp/e750-mcu.bin.sha256; then
ubus call service delete '{"name":"e750_mcu"}'
mcu_update /tmp/e750-mcu-V1.0.7.bin
else
echo "Failed to update MCU, verification of the binary failed."
echo "Your device needs to be connected to the Internet in order to download the MCU binary."
exit 1
fi
}
CHECK_MCUVERSION() {
function version { echo "$$@" | cut -d' ' -f2 | awk -F. '{ printf("%d%03d%03d%03d\n", $$1,$$2,$$3,$$4); }'; }
mcu_version=`echo \{\"version\": \"1\"} > /dev/ttyS0; sleep 0.1; cat /dev/ttyS0|tr -d '\n'`
if [ $$(version "$$mcu_version") -ge $$(version "V 1.0.7") ]; then
return 0
else
echo
echo "Your MCU version has not been verified to work with blue-merle."
echo "Automatic shutdown may not work."
echo "The install script can initiate an update of the MCU."
echo "The device will reboot and, after reboot, you need to run opkg install blue-merle again."
echo -n "Would you like to update your MCU? (y/N): "
read answer
case $$answer in
Y*) answer=0;;
y*) answer=0;;
*) answer=1;;
esac
if [[ "$$answer" -eq 0 ]]; then
UPDATE_MCU
fi
fi
}
if grep -q "GL.iNet GL-E750" /proc/cpuinfo; then
if grep -q -w "3.215" /etc/glversion; then
CHECK_MCUVERSION
echo "Device is supported, installing blue-merle."
exit 0
else
ABORT_GLVERSION
fi
GL_VERSION=$$(cat /etc/glversion)
case $$GL_VERSION in
4.3.19)
echo Version $$GL_VERSION is supported
exit 0
;;
4.*)
echo Version $$GL_VERSION is *probably* supported
ABORT_GLVERSION
;;
*)
echo Unknown version $$GL_VERSION
ABORT_GLVERSION
;;
esac
CHECK_MCUVERSION
else
ABORT_GLVERSION
fi
# Our volatile-mac service gets started during the installation
# but it modifies the client database held by the gl_clients process.
# So we stop that process now, have the database put onto volatile storage
# and start the service after installation
/etc/init.d/gl_clients stop
endef
define Package/blue-merle/postinst
#!/bin/sh
uci set switch-button.@main[0].func='sim'
uci commit switch-button
patch -b /www/src/temple/settings/index.js /lib/blue-merle/patches/index.js.patch
patch -b /www/src/temple/settings/index.html /lib/blue-merle/patches/index.html.patch
patch -b /usr/bin/switchaction /lib/blue-merle/patches/switchaction.patch
patch -b /usr/bin/switch_queue /lib/blue-merle/patches/switch_queue.patch
/etc/init.d/gl_clients start
uci set glconfig.switch_button='service'
uci set glconfig.switch_button.enable='1'
uci set glconfig.switch_button.function='sim'
uci commit glconfig
echo {\"msg\": \"Successfully installed Blue Merle\"} > /dev/ttyS0
endef
define Package/blue-merle/postrm
#!/bin/sh
mv /www/src/temple/settings/index.js.orig /www/src/temple/settings/index.js
mv /www/src/temple/settings/index.html.orig /www/src/temple/settings/index.html
mv /usr/bin/switchaction.orig /usr/bin/switchaction
mv /usr/bin/switch_queue.orig /usr/bin/switch_queue
rm /tmp/sim_change_start
rm /tmp/sim_change_switch
uci set switch-button.@main[0].func='tor'
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

116
README.md
View File

@ -5,26 +5,97 @@ The *blue-merle* software package enhances anonymity and reduces forensic tracea
*blue-merle* addresses the traceability drawbacks of the Mudi router by adding the following features to the Mudi router:
1. Mobile Equipment Identity (IMEI) changer
2. Media Access Control (MAC) address log wiper
3. Basic Service Set Identifier (BSSID) randomization
4. MAC Address randomization
## Installing prebuild package
## Compatibility
Download the [prebuild package](https://github.com/srlabs/blue-merle/releases) and copy it onto your Mudi, preferably into the /tmp folder. Then install the package file:
**This README covers the v2.0 release**, which has been verified to work with GL-E750 Mudi version 4.3.8 - 4.3.12 .
Refer back to the [v1.0 README file](https://github.com/srlabs/blue-merle/tree/cb4d73731fe432e0f101284307101c250ca4b845) for information about the first release, which works on older firmware releases.
```
A MCU version >= 1.0.7 is required. The MCU may be updated through the *blue-merle* package installer or [manually](https://github.com/gl-inet/GL-E750-MCU-instruction). SRLabs cannot guarantee that the project assets within this Git repository will be compatible with future firmware updates.
## Installation
### Online install & upgrade
The online install method requires an **active Internet connection** on your Mudi device to **download up-to-date dependencies**.
Download the [prebuilt v2.0 release package](https://github.com/srlabs/blue-merle/releases/download/v2.0/blue-merle_2.0.0-0_mips_24kc.ipk) and copy it onto your Mudi (e.g. via `scp`), preferably into the `/tmp` folder. Then install the package file:
```sh
opkg update
opkg install blue-merle*.ipk
```
Now you may initiate an IMEI update on the command line by running `blue-merle` or by using Mudi's toggle button. Both the command line and hardware button version of *blue-merle* will guide you through the IMEI update process in order to minimize the risk of IMEI leaks.
The *blue-merle* package has been verified to work with GL-E750 Mudi version 3.215. A MCU version >= 1.0.7 is required. The MCU may be updated through the *blue-merle* package installer or [manually](https://github.com/gl-inet/GL-E750-MCU-instruction). SRLabs cannot guarantee that the project assets within this Git repository will be compatible with future firmware updates.
## Build package
To uprade blue-merle, download the newest blue-merle*.ipk, copy it to your Mudi and reinstall with:
```sh
opkg install --force-reinstall blue-merle*.ipk
```
### Offline install
The offline install method does **not need an active Internet connection** on your Mudi device.
Download the [prebuilt v2.0 offline release package](https://github.com/srlabs/blue-merle/releases/download/v2.0/blue-merle_2.0.0-0_offline_install.zip), then execute the following commands:
```sh
## Execute the following commands on the computer connected to the Mudi via WiFi / LAN
unzip /path/to/downloaded.zip
# Copy the offline release package to your Mudi
# -O might be needed due to SSH daemon used by Mudi
scp -O -r blue_merle_install root@192.168.8.1:/tmp
# Connect to Mudi via SSH
ssh root@192.168.8.1
## Execute the following commands inside the SSH tunnel
# Install dependencies and blue-merle
cd /tmp/blue_merle_install
./install.sh
```
**Note**: The offline install package bundles dependencies collected in October 2023. These dependencies could be outdated at the time of installation and might not be compatible with future Mudi firmware versions.
## Usage
You may initiate an IMEI update in three different ways:
1. **CLI**: via SSH on the command line,
2. **Toggle**: using the Mudi's physical toggle switch, or
3. **Web**: via the LuCI web interface.
You can set a deterministic or randomized IMEI on the command line. *Blue-merle*'s web and toggle interfaces always set a randomized IMEI.
### CLI
Connect to the device via SSH, then execute the `blue-merle` command. The command guides you through the process of **changing your SIM card**. We advise you to **reboot the device** after changing the IMEI.
### Toggle
This is a two-stage process.
Flip the Mudi's hardware switch to initiate the first stage of changing your device's IMEI. Follow the instructions on the display, which will ask you to **replace the SIM card** at the end.
After replacing the SIM card, flip the switch again. The second stage **changes the IMEI** and then **powers off** the device. You should **change location** before booting again.
**Note**: Occasionally, commands may take longer than expected to execute on the device. This can result in the display switching off (standby) for a few seconds before displaying the expected final message (e.g. instructions to replace the SIM card). Wait for the final message to appear before pulling the switch again. If no message is displayed after a minute, the script might have exited or you might have missed the message. In this case, pull the switch to continue / restart the process.
### Web
Open LuCI from `System` > `Advanced Settings` in Mudi's web interface. Find the `Blue Merle` settings under the `Network` tab. The web interface displays the current IMEI and IMSI and provides a button (`"SIM Swap..."`) to set a new randomized IMEI.
**Shutdown the device** once the process is complete. Then **swap your SIM card** and **change location** before booting again.
## Building
This repository contains a CI script to auto-build the project using GitHub actions. Simply fork the repository or replicate the workflow on your local machine to build packages.
You can also setup a full OpenWRT development environment and build the *blue-merle* package using:
```sh
git clone https://github.com/openwrt/openwrt
cd openwrt
git clone https://github.com/srlabs/blue-merle package/blue-merle
@ -39,31 +110,30 @@ make menuconfig
make
make package/blue-merle/compile
```
You will find the package in `./bin/packages/mips_24kc/base/`
## Implementation details
### IMEI randomization
The Mudi router's baseband unit is a Quectel EP06-E/A Series LTE Cat 6 Mini PCIe [module](https://www.quectel.com/wp-content/uploads/pdfupload/Quectel_EP06_Series_LTE-A_Specification_V1.7.pdf>).
The Mudi router's baseband unit is a Quectel EP06-E/A Series LTE Cat 6 Mini PCIe [module](https://www.quectel.com/wp-content/uploads/pdfupload/Quectel_EP06_Series_LTE-A_Specification_V1.7.pdf).
The Mudi router's IMEI can be changed by issuing Quectel LTE series-standard AT commands. The AT command to write a new IMEI to a Quectel EP06-E/A-based device is `AT+EGMR`.
Our IMEI randomization functionality is built around this command and implements two approaches to IMEI generation. The first deterministic method seeds the new value with the user's ISMI, while the second generates a random IMEI.
To change the IMEI on the command-line, run `blue-merle` and follow the instructions. Alternatively, you can use the hardware switch button to set a random IMEI.
Our IMEI randomization functionality is built around this command and implements two approaches to IMEI generation. The deterministic IMEI generation method generates a pseudo-random IMEI based on the inserted SIM's IMSI. This method will generate the same IMEI for the same IMSI, regardless of which particular *blue-merle*-enabled Mudi device is used. The second approach generates a random IMEI.
SRLabs researchers verified that the Mudi router's IMEI can be changed persistently by connecting the device to a custom telco base station set-up. The changed IMEI is recorded within the new base station database entry, confirming that the IMEI change is observed both on the device- and ISP-level.
Furthermore, to ensure that there is no leakage of the old IMEI after changing the SIM card and setting a new IMEI, the Mudi router's radio is turned off in advance. Both the command-line and hardware switch version of *blue-merle* will guide you through the IMEI update process in order to minimize the risk of IMEI leaks.
Furthermore, to ensure that there is no leakage of the old IMEI after changing the SIM card and setting a new IMEI, the Mudi router's radio is turned off in advance and an interim randomized IMEI is set. Both the command-line and hardware switch version of *blue-merle* will guide you through the IMEI update process in order to minimize the risk of IMEI leaks.
Running *blue-merle* will disrupt the device's connection with the ISP during the time the IMEI is changed, and by default the connection is only reestablished once the device is rebooted.
Running *blue-merle* will disrupt the device's connection with the ISP during the time the IMEI is changed, and by default the connection is only reestablished once the device is rebooted.
This process can be observed in Figure 1, where there is a large break in connectivity between entries 70 and 80. This break is the result of turning the radio off.
![Figure 1. The router's radio is turned off and the IMEI is randomized between entries 70 and 80. The ISP cannot connect to it.](https://github.com/srlabs/blue-merle/blob/main/IMEI%20randomization.png)
![Figure 1. The router's radio is turned off and the IMEI is randomized between entries 70 and 80. The ISP cannot connect to it.](./IMEI%20randomization.png)
[Figure 1](https://github.com/srlabs/blue-merle/blob/main/IMEI%20randomization.png) The router's radio is turned off and the IMEI is randomized between entries 70 and 80. The ISP cannot connect to it.
[Figure 1](./IMEI%20randomization.png) The router's radio is turned off and the IMEI is randomized between entries 70 and 80. The ISP cannot connect to it.
### Basic Service Set Identifier (BSSID) randomization
@ -75,10 +145,12 @@ The BSSID randomization feature is run on boot, ensuring that a new BSSID is gen
### MAC address log wiping
Connecting devices' MAC addresses are stored within the Mudi router at `/tmp/tertf(_bak)` and `/etc/tertf(_bak)`. The MAC address log wiper first symbolically links the gl_tertf file responsible for the gltertf process, which reads and writes MAC addresses to the above-mentioned directories. It then kills the gltertf process if active, checks if either directory contains any data, and uses shred to delete any data if found.
Connecting devices' MAC addresses are stored persistently within the Mudi router at `/etc/oui-tertf`. On boot, *blue-merle* deletes (using `shred`) the client database, then mounts a `tmpfs` filesystem at this location and restarts the services that manage the client database. This ensures the client database is only retained in RAM and not on disk while retaining the web UI functionality.
### MAC Address Randomization
*Blue-merle* sets a randomized MAC address for the WAN interface. If you use the device in repeater mode to connect to another WiFI AP, the Mudi's MAC address will change after every boot. This might interfere with MAC filtering if enabled on the upstream WiFi AP.
The MAC address log wiper is run on boot, ensuring that the Mudi device's initial MAC read/write functionality is disrupted each time the device is started.
## Acknowledgement: blue merle
The Mudi device shares a name with a Hungarian dog breed typically used to guard and herd flocks of livestock. Mudi dogs are agile, fast-learners and extremely friendly.

View File

@ -0,0 +1,30 @@
#!/bin/sh
action=$1
logger -p notice -t blue-merle-toggle "Called... ${action}"
. /lib/functions/gl_util.sh
if [ "$action" = "on" ];then
mcu_send_message "Blue Merle ${action}"
echo "on" > /tmp/sim_change_switch
flock -n /tmp/blue-merle-switch.lock logger -p notice -t blue-merle-toggle "Running Stage 1" || logger -p notice -t blue-merle-toggle "Lockfile busy"
flock -n /tmp/blue-merle-switch.lock timeout 90 /usr/bin/blue-merle-switch-stage1
elif [ "$action" = "off" ];then
# We check for any previous run and eventually execute the second stage. We could check for the age of this marker and only activate the second stage is the marker is young enough.
if [ -f /tmp/blue-merle-stage1 ]; then
flock -n /tmp/blue-merle-switch.lock || logger -p notice -t blue-merle-toggle "Lockfile busy" &
flock -n /tmp/blue-merle-switch.lock timeout 90 /usr/bin/blue-merle-switch-stage2
else
logger -p notice -t blue-merle-toggle "No Stage 1; Toggling Off"
fi
echo "off" > /tmp/sim_change_switch
else
echo "off" > /tmp/sim_change_switch
fi
logger -p notice -t blue-merle-toggle "Finished Switch $action"
sleep 1

View File

@ -2,16 +2,15 @@
. /lib/blue-merle/functions.sh
START=81
# We intend to be started before the first network-related service is started.
# According to https://openwrt.org/docs/techref/initscripts, /etc/rc.d/ determines
# the order of the services to be started (or stopped). The lower the number,
# the earlier the service is started.
# We observe "repeater" having the value 15. "network" 20. We certainly want to ahead of those.
START=10
STOP=99
start() {
/lib/blue-merle/mac-wipe.sh
CHECKMACSYMLINK
RESET_BSSIDS
}
stop() {
/lib/blue-merle/mac-wipe.sh
}
start() {
RESET_BSSIDS
RANDOMIZE_MACADDR
}

View File

@ -0,0 +1,30 @@
#!/bin/sh /etc/rc.common
# MAC addresses of connected clients are stored in a sqlite database.
# Having the database seems to be necessary for the device to be working properly.
# We intent to have the device store the database in RAM rather than on flash.
# We replace the directory with a memory-backed tmpfs which is as volatile as we can make it.
# We want to run ahead of "gl-tertf" which, currently, has a prioprity of 60.
# We also want to run ahead of "gl_clients" which has 99.
START=9
STOP=99
start() {
tmpdir="$(mktemp -d)"
# We mount a tmpfs so that the client database will be stored in memory only
mount -t tmpfs / "$tmpdir"
cp -a /etc/oui-tertf/client.db "$tmpdir"
shred --force --remove /etc/oui-tertf/client.db || rm -f /etc/oui-tertf/client.db
# If this script runs multiple times, we accumulate mounts; we try to avoid having mounts over mounts, so we unmount any existing tmpfs
umount -t tmpfs -l /etc/oui-tertf
mount -t tmpfs / /etc/oui-tertf
cp -a "$tmpdir/client.db" /etc/oui-tertf/client.db
umount -t tmpfs -l "$tmpdir"
}
stop() {
shred /etc/oui-tertf/client.db || rm -f /etc/oui-tertf/client.db
}

View File

@ -2,27 +2,6 @@
# This script provides helper functions for blue-merle
# check that MAC wiping/linking to dev/null is still in place
CHECKMACSYMLINK () {
local loc_file="/etc/init.d/gl_tertf"
if [ $(readlink -f "$loc_file") == "/dev/null" ]
then
echo "TEST: EXISTS"
else
echo "TEST: DOES NOT EXIST"
cp "$loc_file" "$loc_file.bak" # todo: consider if we need to move this backup elsewhere?
ln -sf /dev/null "$loc_file"
fi
}
# Restore gl_tertf from back-up
RESTORE_GL_TERTF () {
local loc_file="/etc/init.d/gl_tertf"
local loc_backup="/etc/init.d/gl_tertf.bak"
#local loc_location="/etc/init.d"
rm "$loc_file"
mv "$loc_backup" "$loc_file"
}
UNICAST_MAC_GEN () {
loc_mac_numgen=`python3 -c "import random; print(f'{random.randint(0,2**48) & 0b111111101111111111111111111111111111111111111111:0x}'.zfill(12))"`
@ -35,9 +14,25 @@ RESET_BSSIDS () {
uci set wireless.@wifi-iface[1].macaddr=`UNICAST_MAC_GEN`
uci set wireless.@wifi-iface[0].macaddr=`UNICAST_MAC_GEN`
uci commit wireless
wifi # need to reset wifi for changes to apply
# you need to reset wifi for changes to apply, i.e. executing "wifi"
}
RANDOMIZE_MACADDR () {
# This changes the MAC address clients see when connecting to the WiFi spawned by the device.
# You can check with "arp -a" that your endpoint, e.g. your laptop, sees a different MAC after a reboot of the Mudi.
uci set network.@device[1].macaddr=`UNICAST_MAC_GEN`
# Here we change the MAC address the upstream wifi sees
uci set glconfig.general.macclone_addr=`UNICAST_MAC_GEN`
uci commit network
# You need to restart the network, i.e. /etc/init.d/network restart
}
READ_ICCID() {
gl_modem AT AT+CCID
}
READ_IMEI () {
local answer=1
while [[ "$answer" -eq 1 ]]; do
@ -82,11 +77,28 @@ READ_IMSI () {
echo $imsi
}
GENERATE_IMEI() {
local seed=$(head -100 /dev/urandom | tr -dc "0123456789" | head -c10)
local imei=$(lua /lib/blue-merle/luhn.lua $seed)
echo -n $imei
}
SET_IMEI() {
local imei="$1"
if [[ ${#imei} -eq 14 ]]; then
gl_modem AT AT+EGMR=1,7,${imei}
else
echo "IMEI is ${#imei} not 14 characters long"
fi
}
CHECK_ABORT () {
sim_change_switch=`cat /tmp/sim_change_switch`
if [[ "$sim_change_switch" = "off" ]]; then
e750-mcu "SIM change aborted."
sleep 1
exit 1
if [[ "$sim_change_switch" = "off" ]]; then
echo '{ "msg": "SIM change aborted." }' > /dev/ttyS0
sleep 1
exit 1
fi
}

View File

@ -6,6 +6,7 @@ import serial
import re
from functools import reduce
from enum import Enum
from tacs import tacs
class Modes(Enum):
@ -17,6 +18,8 @@ class Modes(Enum):
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--verbose", help="Enables verbose output",
action="store_true")
ap.add_argument("-g", "--generate-only", help="Only generates an IMEI rather than setting it",
action="store_true")
modes = ap.add_mutually_exclusive_group()
modes.add_argument("-d", "--deterministic", help="Switches IMEI generation to deterministic mode", action="store_true")
modes.add_argument("-s", "--static", help="Sets user-defined IMEI",
@ -27,11 +30,11 @@ modes.add_argument("-r", "--random", help="Sets random IMEI",
# Example IMEI: 490154203237518
imei_length = 14 # without validation digit
# IDEA: make prefix configurable via CLI option
imei_prefix = ["35674108", "35290611", "35397710", "35323210", "35384110",
"35982748", "35672011", "35759049", "35266891", "35407115",
"35538025", "35480910", "35324590", "35901183", "35139729",
"35479164"]
# imei_prefix = ["35674108", "35290611", "35397710", "35323210", "35384110",
# "35982748", "35672011", "35759049", "35266891", "35407115",
# "35538025", "35480910", "35324590", "35901183", "35139729",
# "35479164"]
imei_prefix = tacs
verbose = False
mode = None
@ -42,7 +45,11 @@ TIMEOUT = 3
def get_imsi():
if (verbose):
print(f'Obtaining Serial {TTY} with timeout {TIMEOUT}...')
with serial.Serial(TTY, BAUDRATE, timeout=TIMEOUT, exclusive=True) as ser:
if (verbose):
print('Getting IMSI')
ser.write(b'AT+CIMI\r')
# TODO: read loop until we have 'enough' of what to expect
output = ser.read(64)
@ -141,6 +148,10 @@ def generate_imei(imei_prefix, imsi_d):
def validate_imei(imei):
# before anything check if length is 14 characters
if len(imei) != 14:
print(f"NOT A VALID IMEI: {imei} - IMEI must be 14 characters in length")
return False
# cut off last digit
validation_digit = int(imei[-1])
imei_verify = imei[0:14]
@ -172,10 +183,12 @@ def validate_imei(imei):
if __name__ == '__main__':
args = ap.parse_args()
imsi_d = None
if args.verbose:
verbose = args.verbose
if args.deterministic:
mode = Modes.DETERMINISTIC
imsi_d = get_imsi()
if args.random:
mode = Modes.RANDOM
if args.static is not None:
@ -188,11 +201,11 @@ if __name__ == '__main__':
else:
exit(-1)
else:
imsi_d = get_imsi()
imei = generate_imei(imei_prefix, imsi_d)
if (verbose):
print(f"Generated new IMEI: {imei}")
if not set_imei(imei):
exit(-1)
if not args.generate_only:
if not set_imei(imei):
exit(-1)
exit(0)

View File

@ -0,0 +1,61 @@
---- Adapted from https://cybersecurity.att.com/blogs/labs-research/luhn-checksum-algorithm-lua-implementation
local bit = require("bit")
local band, bor, bxor = bit.band, bit.bor, bit.bxor
function luhn_checksum(card)
local num = 0
local nDigits = card:len()
odd = band(nDigits, 1)
for count = 0,nDigits-1 do
digit = tonumber(string.sub(card, count+1,count+1))
if (bxor(band(count, 1),odd)) == 0
then
digit = digit * 2
end
if digit > 9 then
digit = digit - 9
end
num = num + digit
end
return num
end
function luhn_digit (s)
local num = luhn_checksum (s)
return (10 - (num % 10))
end
function is_valid_luhn (s)
local num = luhn_checksum (s)
return ((num % 10) == 0)
end
function make_imei (premei)
local imei = premei .. tostring(luhn_digit(premei .. "0"))
return imei
end
function make_random_imei ()
local nDigits = 14
local premei = ""
for count = 0, nDigits-1 do
premei = premei .. tostring (math.random (0, 9))
end
local imei = make_imei (tostring (premei))
return imei
end
math.randomseed(tonumber(arg[1]))
print (make_random_imei ())

View File

@ -1,71 +0,0 @@
#!/usr/bin/env ash
# This script wipes all MAC address data from the device and is called upon boot
tmp_dir="/tmp/tertf"
tmp_file="/tmp/tertf/tertfinfo_bak"
etc_dir="/etc/tertf"
etc_file="/etc/tertf/tertfinfo_bak"
# Check for directories
CHECKDIR_TMP () {
if [ -d "$tmp_dir" ]; then
echo "The /tmp/ directory exists."
else
echo "The /tmp/ directory does not exist. This should be fine..."
fi
}
CHECKDIR_ETC () {
if [ -d "$etc_dir" ]; then
echo "The /etc/ directory exists."
else
echo "The /etc/ directory does not exist. Exiting..."
exit 1
fi
}
# trick the gl_tertf file into moving stuff to the void
GASLIGHT () { # good job lil dude you're doing so well
local file="/etc/init.d/gl_tertf"
ln -sf /dev/null "$file"
}
CHECKDIR_TMP
CHECKDIR_ETC
GASLIGHT
# Kills process responsible for manipulating (and protecting) the /tmp/ file instance
killall -9 gltertf
# shredding /tmp/tertf
if [ -f "$tmp_file" ];then
echo "Files found within /tmp/. Let's get to it."
shred -v -u "$tmp_file"
else
echo "No file found within /tmp/tertf. No shredding to be done there."
fi
# shredding /etc/tertf
if [ -f "$etc_file" ]; then
echo "Files found in /etc/. Let's get to it."
shred -v -u "$etc_file" #-v provides verbose output to ease my anxious mind and -u deletes files after they are overwritten
else
echo "No file found within /etc/tertf. No shredding to be done there."
fi
# check if the files have been removed
if [ ! -f "$tmp_file" ]; then
echo "Looks like /tmp/ is clean!"
else
echo "Something went wrong in /tmp/."
fi
if [ ! -f "$etc_file" ]; then
echo "Looks like /etc/ is clean!"
else
echo "Something went wrong in /etc/."
fi
exit 0

View File

@ -1,39 +0,0 @@
--- orig/index.html
+++ patch/index.html
@@ -6,7 +6,7 @@
<span class="circle active"></span>
<span>{{t($lang.setting.btnSetting)}}</span>
</h4>
- <gl-btn type="purple" class="pull-right moblieBtnLate" @click="checkType" :disabled="btnStatus">{{t($lang.button.apply)}}</gl-btn>
+ <gl-btn type="purple" class="pull-right moblieBtnLate" @click="checkType" :disabled="true">{{t($lang.button.apply)}}</gl-btn>
</div>
<div class="panel-body panel-status">
<span class="setting-box clearfix list-group">
@@ -64,6 +64,20 @@
</div>
</div>
</div>
+
+ <div v-show="parstatus=='sim'">
+ <div style="width:47%; display:inline-block;vertical-align:top;">
+ <div id="title_left" style=" font-weight:bold">SIM Change Start</div>
+ <div id="content_left" class="content_switch">{{'SIM change will be initiated'}}.
+ </div>
+ </div>
+ <div style="width:47%; display:inline-block;vertical-align:top">
+ <div id="title_right" style="font-weight:bold">SIM Change Stop</div>
+ <div id="content_right" class="content_switch">
+ </div>
+ </div>
+ </div>
+
</div>
<div style="display:none">
<label class="div-help-title">{{t($lang.setting.diy)}}</label>
@@ -84,4 +98,4 @@
</div>
</div>
-</div>
\ No newline at end of file
+</div>

View File

@ -1,41 +0,0 @@
--- orig/index.js
+++ patch/index.js
@@ -6,7 +6,7 @@
data: function data() {
return {
isShow: false,
- chooseState: ["No function (default)", "WireGuard® Client Toggle (On/Off)", "OpenVPN Client Toggle (On/Off)"],
+ chooseState: ["No function (default)", "WireGuard® Client Toggle (On/Off)", "OpenVPN Client Toggle (On/Off)", "SIM Change (Start/Stop)"],
// chooseState4G: ["No function (default)", "WireGuard® Client Toggle (On/Off)", "OpenVPN Client Toggle (On/Off)", ],
param: "",
btnStatus: true,
@@ -79,6 +79,9 @@
case "Tor Toggle (On/Off)":
status = "tor";
break;
+ case "SIM Change (Start/Stop)":
+ status="sim";
+ break;
}
return status;
},
@@ -108,6 +111,9 @@
case "tor":
_this.param = "Tor Toggle (On/Off)";
break;
+ case "sim":
+ _this.param = "SIM Change (Start/Stop)";
+ break;
}
}
});
@@ -170,6 +176,9 @@
case 'tor':
_this.openSetting();
break;
+ case 'sim':
+ _this.openSetting();
+ break;
}
},
openSetting: function openSetting() {

View File

@ -1,23 +0,0 @@
--- orig/switch_queue
+++ patch/switch_queue
@@ -81,6 +81,10 @@
"tor")
e750-mcu " Turning TOR ON"
;;
+ "sim")
+# e750-mcu " Starting SIM swap"
+ blue-merle-switch&
+ ;;
"*")
;;
esac
@@ -98,6 +102,9 @@
check_other_vpn tor
e750-mcu " Turning TOR OFF"
;;
+ "sim")
+# e750-mcu " Stopping SIM swap"
+ ;;
"*")
;;
esac

View File

@ -1,38 +0,0 @@
--- orig/switchaction
+++ patch/switchaction
@@ -10,6 +10,15 @@
fi
}
+toggle_sim(){
+ local action=$1
+ if [ "$action" = "OFF" ];then
+ sim_switch off
+ else
+ sim_switch on
+ fi
+}
+
check_other_vpn(){
wg_server=$(uci get wireguard_server.@servers[0].enable)
ov_server=$(uci get vpn_service.global.enable)
@@ -136,6 +145,9 @@
check_other_vpn tor
toggle_tor ON
;;
+ "sim")
+ toggle_sim ON
+ ;;
"*")
;;
esac
@@ -157,6 +169,9 @@
"tor")
toggle_tor OFF
;;
+ "sim")
+ toggle_sim OFF
+ ;;
"*")
;;
esac

File diff suppressed because one or more lines are too long

View File

@ -44,6 +44,14 @@ while [[ "$answer" -eq 1 ]]; do
fi
done
## We have just disabled the modem so it should not log on to any network.
## We set a random IMEI now only to have it overwritten very soon after
## the SIM card has been replaced. We intend to prevent an accidential
## leak of the new SIM's IMSI with the old IMEI just in case the modem
## accidentally tried to log in to the network.
python3 /lib/blue-merle/imei_generate.py -r
echo -n "Please now replace the SIM card and press any key to continue. "
read answer
@ -112,7 +120,8 @@ case $answer in
*) answer=1;;
esac
if [[ "$answer" -eq 1 ]]; then
echo {\"poweroff\": \"1\"} >/tmp/mcu_message && sleep 0.5 && killall -17 e750-mcu
echo '{ "msg": "Shutting down..." }' > /dev/ttyS0
echo '{ "poweroff": "1" }' > /dev/ttyS0
else
echo "Resetting modem..."
until gl_modem AT AT+QPOWD | grep -q OK

View File

@ -1,138 +0,0 @@
#!/bin/sh
. /lib/blue-merle/functions.sh
if [ ! -c "/dev/ttyUSB3" ]; then
e750-mcu "Error: /dev/ttyUSB3 does not exist."
sleep 3
e750-mcu "Please reboot & contact maintainer if problem persists."
exit 1;
fi
if [ ! -f "/tmp/sim_change_start" ]; then
echo 0 > /tmp/sim_change_start
fi
if [ ! -f "/tmp/sim_change_switch" ]; then
sim_switch off
fi
now=$(date +%s)
sim_change_last=`cat /tmp/sim_change_start`
sim_change_diff=$((now-sim_change_last))
if [[ "$sim_change_diff" -lt 60 ]]; then
e750-mcu "Please wait >1min between two SIM swaps. ($sim_change_diff s)"
exit 1
fi
echo "$now" > /tmp/sim_change_start
e750-mcu "Starting SIM swap."
sleep 3
i=5
until [[ $i -lt 0 ]]
do
e750-mcu "Pull switch to abort ($i). "
i=$((i-1))
sleep 1
CHECK_ABORT
done
e750-mcu "Continuing ..."
sleep 1
e750-mcu "Disabling the MEfrom transmit- ting and recei- ving RF signals."
sleep 3
old_imei=$(READ_IMEI)
old_imsi=$(READ_IMSI)
CHECK_ABORT
answer=1
while [[ "$answer" -eq 1 ]]; do
gl_modem AT AT+CFUN=4 | grep -q OK
if [[ $? -eq 1 ]]; then
e750-mcu "Disabling failed. Trying again."
CHECK_ABORT
else
answer=0
e750-mcu "Disabled."
sleep 2
fi
done
e750-mcu "Replace the SIM card. Then pull the switch."
while [[ `cat /tmp/sim_change_switch` = "on" ]]; do
e750-mcu "Replace the SIM card. Then pull the switch."
sleep 3
done
e750-mcu "Switch pulled. Continuing..."
sleep 1
sim_switch on
leak=0
until gl_modem AT AT+CFUN=0 | grep -q OK
do
e750-mcu "CFUN=0 failed. Trying again."
sleep 1
done
until gl_modem AT AT+CFUN=4 | grep -q OK
do
leak=1
e750-mcu "CFUN=4 failed. Trying again."
sleep 1
done
if [[ $leak -eq 1 ]]; then
e750-mcu "WARNING: Reset took longer than expected."
sleep 3
fi
sleep 1
new_imsi=$(READ_IMSI)
if [[ "$old_imsi" == "$new_imsi" ]]; then
e750-mcu "WARNING: Old IMSI equals new IMSI."
sleep 3
fi
e750-mcu "Setting random IMEI"
python3 /lib/blue-merle/imei_generate.py -r
new_imei=$(READ_IMEI)
if [[ "$old_imei" == "$new_imei" ]]; then
e750-mcu "WARNING: Old IMEI equals new IMEI."
sleep 3
else
mkdir -p /tmp/modem.1-1.2
echo "$new_imei" > /tmp/modem.1-1.2/modem-imei
fi
e750-mcu "The device will shutdown now."
sleep 3
e750-mcu "You should change your location before booting again."
sleep 5
i=5
until [[ $i -eq 0 ]]
do
i=$((i-1))
e750-mcu "Shutting down... ($i)"
sleep 1
done
echo {\"poweroff\": \"1\"} >/tmp/mcu_message && sleep 0.5 && killall -17 e750-mcu
exit 0

View File

@ -0,0 +1,73 @@
#!/bin/sh
. /lib/blue-merle/functions.sh
. /lib/functions/gl_util.sh
if [ ! -f "/tmp/sim_change_start" ]; then
echo 0 > /tmp/sim_change_start
fi
if [ ! -f "/tmp/sim_change_switch" ]; then
sim_switch off
fi
now=$(date +%s)
sim_change_last=`cat /tmp/sim_change_start`
sim_change_diff=$((now-sim_change_last))
if [[ "$sim_change_diff" -lt 60 ]]; then
mcu_send_message "Please wait >1min between two SIM swaps. ($sim_change_diff s)"
exit 1
fi
echo "$now" > /tmp/sim_change_start
mcu_send_message "Starting SIM swap."
sleep 3
## We're disabling this abort functionality for the moment because the switch keeps being blocked and we cannot notice the pulled switch. We could abort by default and require another toggle to continue.
#i=5
#until [[ $i -lt 0 ]]
#do
# mcu_send_message "Pull switch to abort ($i). "
# i=$((i-1))
# sleep 1
#
# CHECK_ABORT
#done
#
#mcu_send_message "Continuing ..."
#sleep 1
mcu_send_message "Disabling the ME from transmitting and receiving RF signals."
sleep 3
old_imei=$(READ_IMEI)
old_imsi=$(READ_IMSI)
#CHECK_ABORT
answer=1
while [[ "$answer" -eq 1 ]]; do
gl_modem AT AT+CFUN=4 | grep -q OK
if [[ $? -eq 1 ]]; then
mcu_send_message "Disabling failed. Trying again."
CHECK_ABORT
else
answer=0
mcu_send_message "Disabled."
sleep 2
fi
done
## We generate a random IMEI to prevent a leak of the
## new SIM's IMSI under the old IMEI in case the modem
## still talks to the network
timeout 15 python3 /lib/blue-merle/imei_generate.py -r
mcu_send_message "Replace the SIM card. Then pull the switch."
echo done > /tmp/blue-merle-stage1
logger -p notice -t blue-merle-toggle "Finished with Stage 1"

View File

@ -0,0 +1,79 @@
#!/bin/sh
. /lib/blue-merle/functions.sh
. /lib/functions/gl_util.sh
rm -f /tmp/blue-merle-stage1
mcu_send_message "Switch pulled. Continuing..."
sleep 1
sim_switch on
leak=0
until gl_modem AT AT+CFUN=0 | grep -q OK
do
mcu_send_message "CFUN=0 failed. Trying again."
sleep 1
done
until gl_modem AT AT+CFUN=4 | grep -q OK
do
leak=1
mcu_send_message "CFUN=4 failed. Trying again."
sleep 1
done
if [[ $leak -eq 1 ]]; then
mcu_send_message "WARNING: Reset took longer than expected."
sleep 3
fi
sleep 1
new_imsi=$(READ_IMSI)
if [[ "$old_imsi" == "$new_imsi" ]]; then
mcu_send_message "WARNING: Old IMSI equals new IMSI. Did you swap the SIM?"
sleep 3
fi
old_imei=$(READ_IMEI)
mcu_send_message "Setting random IMEI"
timeout 15 python3 /lib/blue-merle/imei_generate.py -r
new_imei=$(READ_IMEI)
if [[ "$old_imei" == "$new_imei" ]]; then
mcu_send_message "WARNING: Old IMEI equals new IMEI."
sleep 3
else
#FIXME old_imei is not the real old imei because in stage1 its already generated a new one
# so this is a bit missleading but for the prupose of visualy see the change it works
mcu_send_message "IMEI Old:...... ${old_imei}IMEI New:...... ${new_imei}"
sleep 5
mkdir -p /tmp/modem.1-1.2
echo "$new_imei" > /tmp/modem.1-1.2/modem-imei
fi
logger -p notice -t blue-merle-toggle "Changed IMEI from ${old_imei} to ${new_imei}"
mcu_send_message "The device will shutdown now."
sleep 3
mcu_send_message "You should change your location before booting again."
sleep 5
i=5
until [[ $i -eq 0 ]]
do
i=$((i-1))
mcu_send_message "Shutting down... ($i)"
sleep 1
done
logger -p notice -t blue-merle-toggle "Finished with Stage 2"
# Note: calling /sbin/poweroff directly results in the device rebooting into a broken state
echo '{ "poweroff": "1" }' >/dev/ttyS0

49
files/usr/libexec/blue-merle Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
. /lib/blue-merle/functions.sh
show_message() {
# There is mcu_send_message() in /lib/functions/gl_util.sh but we don't want to load the file, thinking that it will take too long
echo {\"msg\": \"$1\"} > /dev/ttyS0
}
logger -p notice -t blue-merle-libexec "Libexec $1"
if [ "$1" == "read-imei" ]; then
imei="$(READ_IMEI)"
echo -n $imei
show_message "My IMEI: $imei"
elif [ "$1" == "read-imsi" ]; then
imsi="$(READ_IMSI)"
if [ "x$imsi" == "x" ]; then
echo "No IMSI found $imsi" >&2
exit 1
else
echo -n $imsi
show_message "My IMSI: $imsi"
fi
elif [ "$1" == "random-imei" ]; then
flock -n /tmp/blue-merle-imei-generate.lock timeout 15 /lib/blue-merle/imei_generate.py --random
READ_IMEI
elif [ "$1" == "shutdown-modem" ]; then
exec gl_modem AT AT+CFUN=4
elif [ "$1" == "shutdown" ]; then
show_message "Shutting down..."
echo -n "Shutting down"
logger -p notice -t blue-merle-libexec "Shutting down"
echo '{ "poweroff": "1" }' > /dev/ttyS0
elif [ "$1" == "write-imei" ]; then
new_imei=$2
echo -n { "action": "write" }
else
echo -n '{"msg":"Hello, World!"}'
#echo 'foo'>&2
echo 0
fi

View File

@ -0,0 +1,13 @@
{
"admin/network/blue-merle": {
"title": "Blue Merle",
"order": 30,
"action": {
"type": "view",
"path": "blue-merle"
},
"depends": {
"acl": [ "luci-app-blue-merle" ]
}
}
}

View File

@ -0,0 +1,26 @@
{
"luci-app-blue-merle": {
"description": "Grant access to opkg management",
"read": {
"cgi-io": [ "exec" ],
"file": {
"/usr/libexec/blue-merle": [ "exec" ],
"/usr/libexec/blue-merle shred": [ "exec" ],
"/usr/libexec/blue-merle *": [ "exec" ],
"/etc/opkg.conf": [ "read" ],
"/etc/opkg/*.conf": [ "read" ]
},
"ubus": {
"luci": [ "getMountPoints" ]
}
},
"write": {
"file": {
"/usr/libexec/blue-merle": [ "exec" ],
"/usr/libexec/blue-merle shred": [ "exec" ],
"/usr/libexec/blue-merle *": [ "exec" ],
"/tmp/upload.ipk": [ "write" ]
}
}
}
}

View File

@ -0,0 +1,420 @@
'use strict';
'require view';
'require fs';
'require ui';
'require rpc';
var css = ' \
.controls { \
display: flex; \
margin: .5em 0 1em 0; \
flex-wrap: wrap; \
justify-content: space-around; \
} \
\
.controls > * { \
padding: .25em; \
white-space: nowrap; \
flex: 1 1 33%; \
box-sizing: border-box; \
display: flex; \
flex-wrap: wrap; \
} \
\
.controls > *:first-child, \
.controls > * > label { \
flex-basis: 100%; \
min-width: 250px; \
} \
\
.controls > *:nth-child(2), \
.controls > *:nth-child(3) { \
flex-basis: 20%; \
} \
\
.controls > * > .btn { \
flex-basis: 20px; \
text-align: center; \
} \
\
.controls > * > * { \
flex-grow: 1; \
align-self: center; \
} \
\
.controls > div > input { \
width: auto; \
} \
\
.td.version, \
.td.size { \
white-space: nowrap; \
} \
\
ul.deps, ul.deps ul, ul.errors { \
margin-left: 1em; \
} \
\
ul.deps li, ul.errors li { \
list-style: none; \
} \
\
ul.deps li:before { \
content: "↳"; \
display: inline-block; \
width: 1em; \
margin-left: -1em; \
} \
\
ul.deps li > span { \
white-space: nowrap; \
} \
\
ul.errors li { \
color: #c44; \
font-size: 90%; \
font-weight: bold; \
padding-left: 1.5em; \
} \
\
ul.errors li:before { \
content: "⚠"; \
display: inline-block; \
width: 1.5em; \
margin-left: -1.5em; \
} \
';
var isReadonlyView = !L.hasViewPermission() || null;
var callMountPoints = rpc.declare({
object: 'luci',
method: 'getMountPoints',
expect: { result: [] }
});
var packages = {
available: { providers: {}, pkgs: {} },
installed: { providers: {}, pkgs: {} }
};
var languages = ['en'];
var currentDisplayMode = 'available', currentDisplayRows = [];
function handleReset(ev)
{
}
function callBlueMerle(arg) {
const cmd = "/usr/libexec/blue-merle";
var prom = fs.exec(cmd, [arg]);
return prom.then(
function(res) {
console.log("Blue Merle arg", arg, "res", res);
if (res.code != 0) {
throw new Error("Return code " + res.code);
} else {
return res.stdout;
}
}
).catch(
function(err) {
console.log("Error calling Blue Merle", arg, err);
throw err;
}
);
}
function readIMEI() {
return callBlueMerle("read-imei");
}
function randomIMEI() {
callBlueMerle("random-imei").then(
function(res){
readIMEI().then(
console.log("new IMEI", imei)
);
}
).catch(
function(err){
console.log("Error", err);
}
);
}
function readIMSI() {
return callBlueMerle("read-imsi");
}
function handleConfig(ev)
{
var conf = {};
const cmd = "/usr/libexec/blue-merle";
var dlg = ui.showModal(_('Executing blue merle'), [
E('p', { 'class': 'spinning' },
_('Waiting for the <em>%h</em> command to complete…').format(cmd))
]);
var argv = ["random-imei"];
console.log("Calling ", cmd, argv);
// FIXME: Investigate whether we should be using fs.exec()
fs.exec_direct(cmd, argv, 'text').then(function(res) {
console.log("Res:", res, "stdout", res.stdout, "stderr", res.stderr, "code", res.code);
if (res.stdout)
dlg.appendChild(E('pre', [ res.stdout ]));
if (res.stderr) {
dlg.appendChild(E('h5', _('Errors')));
dlg.appendChild(E('pre', { 'class': 'errors' }, [ res.stderr ]));
}
console.log("Res.code: ", res.code);
if (res.code !== 0)
dlg.appendChild(E('p', _('The <em>%h %h</em> command failed with code <code>%d</code>.').format(cmd, argv, (res.code & 0xff) || -1)));
dlg.appendChild(E('div', { 'class': 'right' },
E('div', {
'class': 'btn',
'click': L.bind(function(res) {
if (ui.menu && ui.menu.flushCache)
ui.menu.flushCache();
ui.hideModal();
if (res.code !== 0)
rejectFn(new Error(res.stderr || 'opkg error %d'.format(res.code)));
else
resolveFn(res);
}, this, res)
}, _('Dismiss'))));
}).catch(function(err) {
ui.addNotification(null, E('p', _('Unable to execute <em>opkg %s</em> command: %s').format(cmd, err)));
ui.hideModal();
});
fs.list('/etc/opkg').then(function(partials) {
var files = [ '/etc/opkg.conf' ];
for (var i = 0; i < partials.length; i++)
if (partials[i].type == 'file' && partials[i].name.match(/\.conf$/))
files.push('/etc/opkg/' + partials[i].name);
return Promise.all(files.map(function(file) {
return fs.read(file)
.then(L.bind(function(conf, file, res) { conf[file] = res }, this, conf, file))
.catch(function(err) {
});
}));
}).then(function() {
var body = [
E('p', {}, _('Below is a listing of the various configuration files used by <em>opkg</em>. Use <em>opkg.conf</em> for global settings and <em>customfeeds.conf</em> for custom repository entries. The configuration in the other files may be changed but is usually not preserved by <em>sysupgrade</em>.'))
];
Object.keys(conf).sort().forEach(function(file) {
body.push(E('h5', {}, '%h'.format(file)));
body.push(E('textarea', {
'name': file,
'rows': Math.max(Math.min(L.toArray(conf[file].match(/\n/g)).length, 10), 3)
}, '%h'.format(conf[file])));
});
body.push(E('div', { 'class': 'right' }, [
E('div', {
'class': 'btn cbi-button-neutral',
'click': ui.hideModal
}, _('Cancel')),
' ',
E('div', {
'class': 'btn cbi-button-positive',
'click': function(ev) {
var data = {};
findParent(ev.target, '.modal').querySelectorAll('textarea[name]')
.forEach(function(textarea) {
data[textarea.getAttribute('name')] = textarea.value
});
ui.showModal(_('OPKG Configuration'), [
E('p', { 'class': 'spinning' }, _('Saving configuration data…'))
]);
Promise.all(Object.keys(data).map(function(file) {
return fs.write(file, data[file]).catch(function(err) {
ui.addNotification(null, E('p', {}, [ _('Unable to save %s: %s').format(file, err) ]));
});
})).then(ui.hideModal);
},
'disabled': isReadonlyView
}, _('Save')),
]));
//ui.showModal(_('OPKG Configuration'), body);
});
}
function handleShutdown(ev)
{
return callBlueMerle("shutdown")
}
function handleRemove(ev)
{
}
function handleSimSwap(ev) {
const spinnerID = 'swap-spinner-id';
var dlg = ui.showModal(_('Starting SIM swap...'),
[
E('p', { 'class': 'spinning', 'id': spinnerID },
_('Shutting down modem…')
)
]
);
callBlueMerle("shutdown-modem").then(
function(res) {
dlg.appendChild(
E('pre', { 'class': 'result'},
res
)
);
dlg.appendChild(
E('p', { 'class': 'text'},
_("Generating Random IMEI")
)
);
callBlueMerle("random-imei").then(
function(res) {
document.getElementById(spinnerID).style = "display:none";
dlg.appendChild(
E('div', { 'class': 'text'},
[
E('p', { 'class': 'text'},
_("IMEI set:") + " " + res
),
E('p', { 'class': 'text'},
_("Please shutdown the device, swap the SIM, then go to another place before booting")
),
E('button', { 'class': 'btn cbi-button-positive', 'click': handleShutdown, 'disabled': isReadonlyView },
[ _('Shutdown…') ]
)
]
)
)
}
).catch(
function(err) {
dlg.appendChild(
E('p',{'class': 'error'},
_('Error setting IMEI! ') + err
)
)
}
);
}
).catch(
function(err) {
dlg.appendChild(
E('p',{'class': 'error'},
_('Error! ') + err
)
)
}
);
}
function handleOpkg(ev)
{
}
function handleUpload(ev)
{
}
function handleInput(ev) {
}
return view.extend({
load: function() {
},
render: function(listData) {
var query = decodeURIComponent(L.toArray(location.search.match(/\bquery=([^=]+)\b/))[1] || '');
const imeiInputID = 'imei-input';
const imsiInputID = 'imsi-input';
var view = E([], [
E('style', { 'type': 'text/css' }, [ css ]),
E('h2', {}, _('Blue Merle')),
E('div', { 'class': 'controls' }, [
E('div', {}, [
E('label', {}, _('IMEI') + ':'),
E('span', { 'class': 'control-group' }, [
E('input', { 'id':imeiInputID, 'type': 'text', 'name': 'filter', 'placeholder': _('e.g. 31428392718429'), 'minlength':14, 'maxlenght':14, 'required':true, 'value': query, 'input': handleInput, 'disabled': true })
//, E('button', { 'class': 'btn cbi-button', 'click': handleReset }, [ _('Clear') ])
//, E('button', { 'class': 'btn cbi-button', 'click': randomIMEI }, [ _('Set Random') ])
])
]),
E('div', {}, [
E('label', {}, _('IMSI') + ':'),
E('span', { 'class': 'control-group' }, [
E('input', { 'id':imsiInputID, 'type': 'text', 'name': 'filter', 'placeholder': _('e.g. 31428392718429'), 'minlength':14, 'maxlenght':14, 'required':true, 'value': query, 'input': handleInput, 'disabled': true })
//, E('button', { 'class': 'btn cbi-button', 'click': handleReset }, [ _('Clear') ])
])
]),
]),
E('div', {}, [
E('label', {}, _('Actions') + ':'), ' ',
E('span', { 'class': 'control-group' }, [
E('button', { 'class': 'btn cbi-button-positive', 'data-command': 'update', 'click': handleSimSwap, 'disabled': isReadonlyView }, [ _('SIM swap…') ]), ' '
//, E('button', { 'class': 'btn cbi-button-action', 'click': handleUpload, 'disabled': isReadonlyView }, [ _('IMEI change…') ]), ' '
//, E('button', { 'class': 'btn cbi-button-neutral', 'click': handleConfig }, [ _('Shred config…') ])
])
])
]);
readIMEI().then(
function(imei) {
const e = document.getElementById(imeiInputID);
console.log("Input: ", e, e.placeholder, e.value);
e.value = imei;
}
).catch(
function(err){
console.log("Error: ", err)
}
)
readIMSI().then(
function(imsi) {
const e = document.getElementById(imsiInputID);
e.value = imsi;
}
).catch(
function(err){
const e = document.getElementById(imsiInputID);
e.value = "No IMSI found";
}
)
return view;
},
handleSave: null,
handleSaveApply: null,
handleReset: null
});