Last active
June 7, 2025 08:01
-
-
Save Geofferey/d105ce5e2b4c557e8fb3003dafd448eb to your computer and use it in GitHub Desktop.
ttlset.sh from Inseego Tool modified for use outside of tookkit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
## This script was originally part of the Inseego Tool | |
# Script supplies methods to spoof/set TTL+DPI+IMEI | |
# I did not write this script but I am using it as part | |
# of a larger set of modifications to the Inseego M2000B | |
# Script version | |
script_version="1.4.4" | |
download_url="gist.githubusercontent.com/Geofferey/d105ce5e2b4c557e8fb3003dafd448eb/raw/inseegokit.sh" | |
# Log file path | |
log_file="/tmp/inseegokit.log" | |
web_root="/data/var/www" | |
hstk_load_file="/tmp/hstk_last_load" | |
# Set the library path to include the directory where libwatchdog_api.so is located | |
LD_LIBRARY_PATH="/opt/nvtl/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" | |
export LD_LIBRARY_PATH | |
# Ensure the script can find the modem2_cli command | |
PATH="${PATH}${PATH:+:}/opt/nvtl/bin" | |
export PATH | |
# Function to log messages (POSIX-compliant) | |
log_message() { | |
timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Portable date format | |
printf "%s - %s\n" "$timestamp" "$1" >> "$log_file" | |
printf "%s\n" "$1" | |
} | |
# Function to check for and apply script updates | |
check_for_updates() { | |
# URL to download the latest script | |
download_url="https://gist.githubusercontent.com/Geofferey/d105ce5e2b4c557e8fb3003dafd448eb/raw/inseegokit" | |
log_message "Checking for script updates..." | |
# Download the script to a temporary file (POSIX-safe temp file) | |
tmpfile="/tmp/inseegokit_update_$$" | |
wget -q "$download_url" -O "$tmpfile" 2>/dev/null | |
if [ $? -ne 0 ]; then | |
log_message "Error: Failed to download the updated script." | |
rm -f "$tmpfile" | |
return 1 | |
fi | |
# Extract the version from the downloaded script (POSIX-compliant) | |
latest_version=$(sed -n 's/^script_version="\([^"]*\)"/\1/p' "$tmpfile") | |
if [ -z "$latest_version" ]; then | |
log_message "Error: Could not determine version of the downloaded script. Aborting update." | |
rm -f "$tmpfile" | |
return 1 | |
fi | |
log_message "Downloaded script version: $latest_version" | |
# Compare with the current version | |
if [ "$latest_version" != "$script_version" ]; then | |
log_message "New version available: $latest_version. Updating script..." | |
# Replace the current script with the new version | |
mv "$tmpfile" "$0" | |
chmod +x "$0" | |
log_message "Script updated to version $latest_version. Restarting..." | |
exec "$0" "$@" | |
else | |
log_message "Script is already up to date (version $script_version)." | |
rm -f "$tmpfile" | |
fi | |
} | |
log_imsi() { | |
# Load auto_mode from config file, default to 'yes' if not set | |
config_file="/data/etc/ttl.conf" | |
auto_mode=$(grep '^auto_mode=' "$config_file" 2>/dev/null | cut -d'=' -f2) | |
#auto_mode=${auto_mode:-no} # Default to auto mode if unset | |
log_message "Auto Mode is set to auto_mode=${auto_mode}" | |
if [ ${auto_mode} = 'yes' ]; then | |
active_imsi=$(modem2_cli get_imsi | grep -w "imsi :" | cut -d ':' -f2 | cut -d ' ' -f2 2>/dev/null) | |
else | |
unset active_imsi | |
fi | |
if [[ ${auto_mode} = "yes" ]] && [[ -z "$active_imsi" ]]; then | |
log_message "IMSI: Unable to retrieve active IMSI" | |
carrier="Unknown" | |
fi | |
if [[ ${auto_mode} = "yes" ]] && [[ ! -z "$active_imsi" ]]; then | |
log_message "IMSI: $active_imsi" | |
# Extract MCC (first 3 digits) and MNC (next 2-3 digits) | |
mcc="${active_imsi:0:3}" | |
mnc="${active_imsi:3:3}" # Some carriers use 2 or 3 digits for MNC | |
# Determine Carrier Based on MCC-MNC | |
case "$mcc$mnc" in | |
## T-Mobile MNCs | |
310260|310160|310200|310210|310220|310230|310240|310250|310270|310310|310490|311660) | |
carrier="T-Mobile" | |
default_ttl_value=65 | |
default_hl_value=64 | |
;; | |
## Verizon MNCs | |
311480|310012|311270|310590|310890|311272|310980|311273|311271|312530) | |
carrier="Verizon" | |
default_ttl_value=65 # Updated to 65 for tethering | |
default_hl_value=64 # Updated to 64 for standard IPv6 | |
;; | |
## AT&T MNCs | |
310410|310150|310170|310380|310980) | |
carrier="AT&T" | |
default_ttl_value=64 | |
default_hl_value=64 | |
;; | |
## Dish Wireless MNCs | |
311490|312530|312770) | |
carrier="Dish Wireless" | |
default_ttl_value=64 | |
default_hl_value=64 | |
;; | |
## Total Wireless (Verizon MVNO - MNC 118 or 88) | |
311180|310118|311088|310088) | |
carrier="Total Wireless" | |
default_ttl_value=118 | |
default_hl_value=255 | |
;; | |
## Visible (Different TTL from Verizon) | |
311480|311272|311271|310012) | |
carrier="Visible" | |
default_ttl_value=66 | |
default_hl_value=255 | |
;; | |
## Default case for unknown carriers | |
*) | |
carrier="Unknown" | |
default_ttl_value=65 # Generic Safe Defaults | |
default_hl_value=64 | |
;; | |
esac | |
log_message "Detected Carrier: $carrier (MCC: $mcc, MNC: $mnc)" | |
fi | |
# Set TTL/HL values based on auto_mode | |
if [ "$auto_mode" = "yes" ]; then | |
# In auto mode, use carrier defaults only if no manual values are set | |
new_ttl_value=${new_ttl_value:-$default_ttl_value} | |
new_hl_value=${new_hl_value:-$default_hl_value} | |
log_message "Auto mode: Using carrier defaults (TTL: $new_ttl_value, HL: $new_hl_value) unless overridden" | |
else | |
# In manual mode, do not override existing values with any defaults | |
if [ -z "$new_ttl_value" ] || [ -z "$new_hl_value" ]; then | |
log_message "Manual mode: No manual TTL/HL values set, falling back to generic defaults" | |
new_ttl_value= | |
new_hl_value= | |
else | |
log_message "Manual mode: Using pre-set TTL: $new_ttl_value, HL: $new_hl_value" | |
fi | |
fi | |
} | |
check_interface() { | |
iface="$1" | |
if [ -e "/sys/class/net/$iface" ]; then | |
log_message "Interface $iface found in /sys/class/net" | |
return 0 | |
elif [ -e "/dev/$iface" ]; then | |
log_message "Interface $iface found in /dev" | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
# Function to set TTL & HL values and apply iptables rules | |
apply_ttl_mod() { | |
log_message "Applying iptables rules" | |
# Retrieve IMSI and determine carrier-specific TTL/HL defaults | |
log_imsi | |
# Override only if ttlvol/hlvol exist and are non-empty | |
if [ -s /data/etc/ttlvol.conf ]; then | |
new_ttl_value=$(cat /data/etc/ttlvol.conf) | |
fi | |
if [ -s /data/etc/hlvol.conf ]; then | |
new_hl_value=$(cat /data/etc/hlvol.conf) | |
fi | |
# Ensure TTL and HL values are correctly set | |
log_message "Final TTL setting: $new_ttl_value" | |
log_message "Final HL setting: $new_hl_value" | |
# Define interfaces | |
rmnet_interfaces="rmnet_data0 rmnet_data1 rmnet_data2 rmnet_data3 rmnet_data4 rmnet_data5" | |
eth0_interface="eth0" | |
wlan0_interface="wlan0" | |
wlan1_interface="wlan1" | |
# Remove existing iptables rules | |
for interface in $rmnet_interfaces $eth0_interface $wlan0_interface $wlan1_interface; do | |
if check_interface "$interface"; then | |
iptables -t mangle -D POSTROUTING -o "$interface" ! -p icmp -j TTL --ttl-set "$new_ttl_value" 2>/dev/null | |
iptables -t mangle -D PREROUTING -i "$interface" -j TTL --ttl-set "$new_ttl_value" 2>/dev/null | |
ip6tables -t mangle -D POSTROUTING -o "$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "$new_hl_value" 2>/dev/null | |
ip6tables -t mangle -D PREROUTING -i "$interface" -j HL --hl-set "$new_hl_value" 2>/dev/null | |
fi | |
done | |
# Apply new iptables rules dynamically per carrier | |
rmnet_interfaces=$(ip -o link show | grep -E 'rmnet_data[0-9]+@' | grep 'UP' | cut -d':' -f2 | cut -d'@' -f1 | tr -d ' ' || true) | |
if [ -n "$rmnet_interfaces" ]; then | |
for interface in $rmnet_interfaces; do | |
if check_interface "$interface"; then | |
# IPv4 TTL | |
iptables -t mangle -I POSTROUTING -o "$interface" ! -p icmp -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || \ | |
log_message "Failed to set IPv4 POSTROUTING TTL for $interface" | |
iptables -t mangle -I PREROUTING -i "$interface" -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || \ | |
log_message "Failed to set IPv4 PREROUTING TTL for $interface" | |
# IPv6 HL | |
ip6tables -t mangle -I POSTROUTING -o "$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "$new_hl_value" 2>/dev/null || \ | |
log_message "Failed to set IPv6 POSTROUTING HL for $interface" | |
ip6tables -t mangle -I PREROUTING -i "$interface" -j HL --hl-set "$new_hl_value" 2>/dev/null || \ | |
log_message "Failed to set IPv6 PREROUTING HL for $interface" | |
log_message "iptables rules applied for interface $interface" | |
printf "iptables rules applied for interface %s\n" "$interface" | |
else | |
log_message "Interface $interface not found or down, skipping" | |
fi | |
done | |
else | |
log_message "No active rmnet_data interfaces found � applying fallback TTL/DPI rules (not bound to any interface)" | |
# Fallback: Apply rules without interface matching | |
iptables -t mangle -I POSTROUTING ! -p icmp -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || \ | |
log_message "Failed to set fallback IPv4 POSTROUTING TTL" | |
iptables -t mangle -I PREROUTING -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || \ | |
log_message "Failed to set fallback IPv4 PREROUTING TTL" | |
ip6tables -t mangle -I POSTROUTING ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "$new_hl_value" 2>/dev/null || \ | |
log_message "Failed to set fallback IPv6 POSTROUTING HL" | |
ip6tables -t mangle -I PREROUTING -j HL --hl-set "$new_hl_value" 2>/dev/null || \ | |
log_message "Failed to set fallback IPv6 PREROUTING HL" | |
log_message "Fallback TTL and HL rules applied (not interface-specific)" | |
fi | |
for interface in $eth0_interface $wlan0_interface $wlan1_interface; do | |
if check_interface "$interface"; then | |
iptables -t mangle -I POSTROUTING -o "$interface" ! -p icmp -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || log_message "Failed to set IPv4 POSTROUTING TTL for $interface" | |
ip6tables -t mangle -I POSTROUTING -o "$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "$new_hl_value" 2>/dev/null || log_message "Failed to set IPv6 POSTROUTING HL for $interface" | |
log_message "iptables rules applied for interface $interface" | |
printf "iptables rules applied for interface %s\n" "$interface" | |
else | |
log_message "Interface $interface not found or down, skipping" | |
fi | |
done | |
} | |
remove_iptables_rules() { | |
log_message "Removing all iptables rules" | |
rmnet_interfaces="rmnet_data0 rmnet_data1 rmnet_data2 rmnet_data3 rmnet_data4 rmnet_data5" | |
eth0_interface="eth0" | |
wlan0_interface="wlan0" | |
wlan1_interface="wlan1" | |
# Assume new_ttl_value and new_hl_value are set globally or passed; log if unset | |
if [ -z "$new_ttl_value" ] || [ -z "$new_hl_value" ]; then | |
log_message "Warning: TTL or HL value is empty, removal may be incomplete." | |
fi | |
# Remove iptables rules for all interfaces with checking | |
for interface in $rmnet_interfaces $eth0_interface $wlan0_interface $wlan1_interface; do | |
if check_interface "$interface"; then | |
iptables -t mangle -D POSTROUTING -o "$interface" ! -p icmp -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || log_message "Failed to remove IPv4 POSTROUTING TTL for $interface" | |
ip6tables -t mangle -D POSTROUTING -o "$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "$new_hl_value" 2>/dev/null || log_message "Failed to remove IPv6 POSTROUTING HL for $interface" | |
# Only rmnet interfaces have PREROUTING rules | |
case "$interface" in | |
rmnet_data*) | |
iptables -t mangle -D PREROUTING -i "$interface" -j TTL --ttl-set "$new_ttl_value" 2>/dev/null || log_message "Failed to remove IPv4 PREROUTING TTL for $interface" | |
ip6tables -t mangle -D PREROUTING -i "$interface" -j HL --hl-set "$new_hl_value" 2>/dev/null || log_message "Failed to remove IPv6 PREROUTING HL for $interface" | |
;; | |
esac | |
else | |
log_message "Interface $interface not found, skipping removal" | |
fi | |
done | |
log_message "All iptables rules removed" | |
} | |
log_imei() { | |
imei=$(modem2_cli get_info 2>/dev/null | sed -n 's/.*imei:.*\[\([^]]*\)\].*/\1/p') | |
if [ -z "$imei" ]; then | |
log_message "IMEI: Unable to retrieve IMEI" | |
else | |
log_message "IMEI: $imei" | |
fi | |
} | |
start_http_server() { | |
mkdir -p "$web_root/cgi-bin" | |
cat <<INDEX_EOF > "$web_root/index.html" | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Device Management</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
display: flex; | |
min-height: 100vh; | |
margin: 0; | |
background-color: #000000; | |
} | |
.sidebar { | |
width: 250px; | |
background-color: #333; | |
color: white; | |
padding: 15px; | |
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); | |
} | |
.sidebar a { | |
color: white; | |
text-decoration: none; | |
display: block; | |
padding: 10px 0; | |
margin: 5px 0; | |
} | |
.sidebar a:hover { | |
background-color: #575757; | |
} | |
.main-content { | |
flex: 1; | |
padding: 20px; | |
} | |
h1, h2 { | |
color: #333; | |
} | |
.form-group { | |
margin: 15px 0; | |
} | |
.form-group label { | |
display: block; | |
margin-bottom: 5px; | |
} | |
.form-group input, .form-group select { | |
width: 100%; | |
padding: 10px; | |
border: 1px solid #ccc; | |
border-radius: 5px; | |
} | |
button { | |
background-color: #9e42fd; | |
color: white; | |
padding: 10px 15px; | |
border: none; | |
border-radius: 5px; | |
cursor: pointer; | |
} | |
button:hover { | |
background-color: #45a049; | |
} | |
.section { | |
display: none; | |
} | |
.section.active { | |
display: block; | |
} | |
.info-output { | |
background-color: #fff; | |
padding: 10px; | |
border: 1px solid #ccc; | |
border-radius: 5px; | |
margin-top: 15px; | |
white-space: pre-wrap; | |
} | |
.band-checkbox-group { | |
display: grid; | |
grid-template-columns: repeat(3, 1fr); | |
gap: 10px; | |
max-width: 600px; | |
} | |
.band-checkbox-group div { | |
display: flex; | |
align-items: center; | |
} | |
.band-checkbox-group input { | |
margin-right: 5px; | |
} | |
</style> | |
<script> | |
function showSection(sectionId) { | |
var sections = document.querySelectorAll('.section'); | |
for (var i = 0; i < sections.length; i++) { | |
sections[i].classList.remove('active'); | |
} | |
document.getElementById(sectionId).classList.add('active'); | |
} | |
function toggleCustomFields() { | |
var useAuto = document.getElementById('use_auto').checked; | |
document.getElementById('custom-fields').style.display = useAuto ? 'none' : 'block'; | |
} | |
function toggleDpiFields() { | |
var dpiBlocking = document.getElementById('dpi_blocking').checked; | |
document.getElementById('dpi-fields').style.display = dpiBlocking ? 'block' : 'none'; | |
} | |
async function setTtlHl(event) { | |
event.preventDefault(); | |
var useAuto = document.getElementById('use_auto').checked ? 'yes' : 'no'; | |
var ttl = document.getElementById('ttl').value; | |
var hl = document.getElementById('hl').value; | |
var dpiBlocking = document.getElementById('dpi_blocking').checked ? 'yes' : 'no'; | |
var mss = document.getElementById('mss').value; | |
var response = await fetch('/cgi-bin/set_ttl_hl.sh', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
body: 'use_auto=' + useAuto + '&ttl=' + ttl + '&hl=' + hl + '&dpi_blocking=' + dpiBlocking + '&mss=' + mss | |
}); | |
var data = await response.text(); | |
document.getElementById('ttl-hl-output').innerText = data; | |
// Refresh displayed values after setting | |
await fetchCurrentSettings(); | |
} | |
async function applyTtlHl() { | |
var response = await fetch('/cgi-bin/apply.sh'); | |
var data = await response.text(); | |
document.getElementById('ttl-hl-output').innerText = data; | |
} | |
async function fetchInformation() { | |
var response = await fetch('/cgi-bin/get_info.sh'); | |
var data = await response.text(); | |
document.getElementById('info-output').innerHTML = data; | |
} | |
async function fetchDiagnostics() { | |
var response = await fetch('/cgi-bin/run_diagnostics.sh'); | |
var data = await response.text(); | |
document.getElementById('diagnostics-output').innerHTML = data; | |
} | |
async function reboot() { | |
fetch('/cgi-bin/reboot.sh'); | |
} | |
async function adbd() { | |
fetch('/cgi-bin/adbd.sh'); | |
} | |
async function fetchBands() { | |
var response = await fetch('/cgi-bin/get_bands.sh'); | |
var data = await response.text(); | |
document.getElementById('bands-output').innerText = data; | |
var supportedLteBands = []; | |
var enabledLteBands = []; | |
var supportedNr5gBands = []; | |
var enabledNr5gNsaBands = []; | |
var enabledNr5gSaBands = []; | |
var lines = data.split('\n'); | |
var isLte = false; | |
var isNr5g = false; | |
var isEnabledLte = false; | |
var isEnabledNr5gNsa = false; | |
var isEnabledNr5gSa = false; | |
for (var i = 0; i < lines.length; i++) { | |
var line = lines[i]; | |
if (line.includes('Supported Bands (LTE)')) { | |
isLte = true; isNr5g = false; isEnabledLte = false; isEnabledNr5gNsa = false; isEnabledNr5gSa = false; | |
} else if (line.includes('Supported Bands (5G)')) { | |
isLte = false; isNr5g = true; isEnabledLte = false; isEnabledNr5gNsa = false; isEnabledNr5gSa = false; | |
} else if (line.includes('Enabled LTE Bands')) { | |
isEnabledLte = true; isLte = false; isNr5g = false; isEnabledNr5gNsa = false; isEnabledNr5gSa = false; | |
} else if (line.includes('Enabled NR5G NSA Bands')) { | |
isEnabledNr5gNsa = true; isEnabledLte = false; isLte = false; isNr5g = false; isEnabledNr5gSa = false; | |
} else if (line.includes('Enabled NR5G SA Bands')) { | |
isEnabledNr5gSa = true; isEnabledNr5gNsa = false; isEnabledLte = false; isLte = false; isNr5g = false; | |
} else if (line.includes('band[')) { | |
var band = line.split(': ')[1].replace('[', '').replace(']', ''); | |
if (isLte && band != '0') supportedLteBands.push(band); | |
else if (isNr5g && band != '0') supportedNr5gBands.push(band); | |
else if (isEnabledLte && band != '0') enabledLteBands.push(band); | |
else if (isEnabledNr5gNsa && band != '0') enabledNr5gNsaBands.push(band); | |
else if (isEnabledNr5gSa && band != '0') enabledNr5gSaBands.push(band); | |
} | |
} | |
function isBandEnabled(band, enabledBands) { | |
return enabledBands.includes(band); | |
} | |
var isFirstRun = enabledLteBands.length === 0 && enabledNr5gNsaBands.length === 0 && enabledNr5gSaBands.length === 0; | |
var lteContainer = document.getElementById('lte-bands'); | |
lteContainer.innerHTML = supportedLteBands.map(function(band) { | |
var isChecked = isFirstRun || isBandEnabled(band, enabledLteBands) ? 'checked' : ''; | |
return '<div><input type="checkbox" name="lte-bands" value="' + band + '" ' + isChecked + '> Band ' + band + '</div>'; | |
}).join(''); | |
var nr5gContainer = document.getElementById('nr5g-bands'); | |
nr5gContainer.innerHTML = supportedNr5gBands.map(function(band) { | |
var isCheckedNsa = isBandEnabled(band, enabledNr5gNsaBands) ? 'checked' : ''; | |
var isCheckedSa = isBandEnabled(band, enabledNr5gSaBands) ? 'checked' : ''; | |
return '<div><input type="checkbox" name="nr5g-nsa-bands" value="' + band + '" ' + isCheckedNsa + '> NSA Band ' + band + | |
'<input type="checkbox" name="nr5g-sa-bands" value="' + band + '" ' + isCheckedSa + '> SA Band ' + band + '</div>'; | |
}).join(''); | |
} | |
async function setHomepage(event) { | |
event.preventDefault(); | |
var homepage = document.getElementById('homepage').value; | |
var response = await fetch('/cgi-bin/set_homepage.sh', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
body: 'homepage=' + encodeURIComponent(homepage) | |
}); | |
var data = await response.text(); | |
document.getElementById('homepage-output').innerText = data; | |
} | |
async function repairImei(event) { | |
event.preventDefault(); | |
var imei = document.getElementById('imei').value; | |
var response = await fetch('/cgi-bin/repair_imei.sh', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
body: 'imei=' + encodeURIComponent(imei) | |
}); | |
var data = await response.text(); | |
document.getElementById('imei-output').innerText = data; | |
} | |
async function setModeSwitch(event) { | |
event.preventDefault(); | |
var mode = document.getElementById('mode').value; | |
var response = await fetch('/cgi-bin/set_mode_switch.sh', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
body: 'mode=' + mode | |
}); | |
var data = await response.text(); | |
document.getElementById('mode-output').innerText = data; | |
} | |
async function fetchCurrentSettings() { | |
try { | |
var response = await fetch('/cgi-bin/get_ttl_hl.sh'); | |
var data = await response.text(); | |
// Parse the response (e.g., "ttl=65\nhl=64\nauto_mode=yes\n...") | |
var lines = data.split('\n'); | |
var settings = {}; | |
lines.forEach(line => { | |
var [key, value] = line.split('='); | |
if (key && value !== undefined) settings[key] = value; | |
}); | |
// Update UI fields with current values | |
document.getElementById('use_auto').checked = settings.auto_mode === 'yes'; | |
document.getElementById('ttl').value = settings.ttl || ''; | |
document.getElementById('hl').value = settings.hl || ''; | |
document.getElementById('dpi_blocking').checked = settings.dpi_blocking === 'yes'; | |
document.getElementById('mss').value = settings.mss || '500'; | |
// Toggle visibility based on current state | |
toggleCustomFields(); | |
toggleDpiFields(); | |
} catch (error) { | |
console.error('Error fetching current settings:', error); | |
document.getElementById('ttl-hl-output').innerText = 'Error fetching current settings'; | |
} | |
} | |
let autoRefresh = false; | |
let autoRefreshInterval = null; | |
function setPreset(cmd) { | |
document.getElementById('at-command-input').value = cmd; | |
} | |
function updateHistory(cmd) { | |
let history = JSON.parse(localStorage.getItem('at_command_history') || '[]'); | |
if (!history.includes(cmd)) { | |
history.unshift(cmd); | |
if (history.length > 10) history.pop(); | |
localStorage.setItem('at_command_history', JSON.stringify(history)); | |
} | |
renderHistory(); | |
} | |
function renderHistory() { | |
let history = JSON.parse(localStorage.getItem('at_command_history') || '[]'); | |
let ul = document.getElementById('at-command-history'); | |
ul.innerHTML = ''; | |
history.forEach(cmd => { | |
let li = document.createElement('li'); | |
li.textContent = cmd; | |
li.style.cursor = 'pointer'; | |
li.onclick = () => { | |
document.getElementById('at-command-input').value = cmd; | |
}; | |
ul.appendChild(li); | |
}); | |
} | |
function clearHistory() { | |
localStorage.removeItem('at_command_history'); | |
renderHistory(); | |
} | |
async function sendAtCommand(event) { | |
if (event) event.preventDefault(); | |
const command = document.getElementById('at-command-input').value.trim(); | |
if (!command) return; | |
updateHistory(command); | |
const response = await fetch('/cgi-bin/send_at.sh', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
body: 'command=' + encodeURIComponent(command) | |
}); | |
const data = await response.text(); | |
document.getElementById('at-command-output').value = data; | |
scrollToBottom(); | |
} | |
function toggleAutoRefresh() { | |
autoRefresh = !autoRefresh; | |
const input = document.getElementById('at-command-input').value.trim(); | |
if (autoRefresh && input) { | |
autoRefreshInterval = setInterval(() => sendAtCommand(null), 3000); | |
} else { | |
clearInterval(autoRefreshInterval); | |
} | |
} | |
function scrollToBottom() { | |
const textarea = document.getElementById('at-command-output'); | |
textarea.scrollTop = textarea.scrollHeight; | |
} | |
function copyOutput() { | |
const textarea = document.getElementById('at-command-output'); | |
textarea.select(); | |
document.execCommand('copy'); | |
alert('Output copied to clipboard.'); | |
} | |
function clearOutput() { | |
document.getElementById('at-command-output').value = ''; | |
} | |
document.addEventListener('DOMContentLoaded', () => { | |
fetchCurrentSettings(); | |
renderHistory(); | |
}); | |
</script> | |
</head> | |
<body> | |
<div class="sidebar"> | |
<h1>Device Management</h1> | |
<a href="#" onclick="showSection('information')">Information</a> | |
<a href="#" onclick="showSection('diagnostics')">Diagnostics</a> | |
<a href="#" onclick="showSection('ttl-mod')">TTL Spoof</a> | |
<a href="#" onclick="showSection('start-adbd')">Enable DIAG</a> | |
<a href="#" onclick="showSection('mode-switch')">Mode Switch</a> | |
<a href="#" onclick="showSection('band-locking')">Band Locking</a> | |
<a href="#" onclick="showSection('default-homepage')">Default Homepage</a> | |
<a href="#" onclick="showSection('repair-imei')">IMEI Reapir</a> | |
<a href="#" onclick="showSection('reboot-device')">Reboot</a> | |
</div> | |
<div class="main-content"> | |
<div id="ttl-mod" class="section active"> | |
<h2>TTL Mod</h2> | |
<form onsubmit="setTtlHl(event)"> | |
<div class="form-group"> | |
<label for="use_auto">Use Auto-Detected Settings:</label> | |
<input type="checkbox" id="use_auto" name="use_auto" onchange="toggleCustomFields()"> | |
</div> | |
<div id="custom-fields"> | |
<div class="form-group"> | |
<label for="ttl">TTL Value:</label> | |
<input type="text" id="ttl" name="ttl" placeholder="Enter custom TTL"> | |
</div> | |
<div class="form-group"> | |
<label for="hl">HL Value:</label> | |
<input type="text" id="hl" name="hl" placeholder="Enter custom HL"> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="dpi_blocking">Enable DPI Blocking:</label> | |
<input type="checkbox" id="dpi_blocking" name="dpi_blocking" onchange="toggleDpiFields()"> | |
</div> | |
<div id="dpi-fields"> | |
<div class="form-group"> | |
<label for="mss">MSS Value (for fragmentation):</label> | |
<input type="text" id="mss" name="mss" value="500" placeholder="Enter MSS (e.g., 500)"> | |
</div> | |
</div> | |
<button type="submit">Set Values</button> | |
<button type="button" onclick="applyTtlHl()">Apply Current Settings</button> | |
</form> | |
<div id="ttl-hl-output" class="info-output"></div> | |
</div> | |
<div id="band-locking" class="section"> | |
<h2>Band Locking</h2> | |
<button onclick="fetchBands()">Get Supported Bands</button> | |
<div id="bands-output" class="info-output"></div> | |
<form action="/cgi-bin/set_band_locking.sh" method="post"> | |
<div class="form-group"> | |
<label for="lte-bands">Select LTE Bands:</label> | |
<div id="lte-bands" class="band-checkbox-group"></div> | |
</div> | |
<div class="form-group"> | |
<label for="nr5g-bands">Select NR5G Bands:</label> | |
<div id="nr5g-bands" class="band-checkbox-group"></div> | |
</div> | |
<button type="submit">Lock Bands</button> | |
</form> | |
</div> | |
<div id="diagnostics" class="section"> | |
<h2>Diagnostics</h2> | |
<div id="diagnostics-output" class="info-output"></div> | |
<div></br></div> | |
<button onclick="fetchDiagnostics()">Run Diagnostics</button> | |
</div> | |
<div id="information" class="section"> | |
<h2>Information</h2> | |
<div id="info-output" class="info-output"></div> | |
<div></br></div> | |
<button onclick="fetchInformation()">Get Information</button> | |
</div> | |
<div id="default-homepage" class="section"> | |
<h2>Set Default Homepage</h2> | |
<form onsubmit="setHomepage(event)"> | |
<div class="form-group"> | |
<label for="homepage">Homepage URL:</label> | |
<input type="text" id="homepage" name="homepage"> | |
</div> | |
<button type="submit">Set Homepage</button> | |
</form> | |
<div id="homepage-output" class="info-output"></div> | |
</div> | |
<div id="repair-imei" class="section"> | |
<h2>Repair IMEI</h2> | |
<form onsubmit="repairImei(event)"> | |
<div class="form-group"> | |
<label for="imei">IMEI Number:</label> | |
<input type="text" id="imei" name="imei"> | |
</div> | |
<button type="submit">Repair IMEI</button> | |
</form> | |
<div id="imei-output" class="info-output"></div> | |
</div> | |
<div id="reboot-device" class="section"> | |
<h2>Reboot Device</h2> | |
<form onsubmit="reboot(event)"> | |
<button type="submit">Reboot</button> | |
</form> | |
</div> | |
<div id="start-adbd" class="section"> | |
<h2>Enable DIAG+ADB</h2> | |
<form onsubmit="adbd(event)"> | |
<button type="submit">Enable</button> | |
</form> | |
</div> | |
<div id="mode-switch" class="section"> | |
<h2>Mode Switch</h2> | |
<form onsubmit="setModeSwitch(event)"> | |
<div class="form-group"> | |
<label for="mode">Select Mode:</label> | |
<select id="mode" name="mode"> | |
<option value="0">EUM (End User Mode)</option> | |
<option value="1">DBG (Debug Mode)</option> | |
<option value="2">ENT (Engineering Mode)</option> | |
<option value="3">P_ENT (Protected Engineering Mode)</option> | |
</select> | |
</div> | |
<button type="submit">Set Mode</button> | |
</form> | |
<div id="mode-output" class="info-output"></div> | |
</div> | |
<div id="at-command" class="section"> | |
<h2>Send AT Command</h2> | |
<form onsubmit="sendAtCommand(event)"> | |
<div class="form-group"> | |
<label for="at-command-input">AT Command:</label> | |
<input type="text" id="at-command-input" name="command" placeholder="e.g., AT+CSQ"> | |
</div> | |
<button type="submit">Send</button> | |
<button type="button" onclick="clearHistory()">Clear History</button> | |
<button type="button" onclick="toggleAutoRefresh()">Toggle Auto-Refresh</button> | |
</form> | |
<div class="form-group"> | |
<label>Presets:</label> | |
<div> | |
<button type="button" onclick="setPreset('AT+CSQ')">AT+CSQ</button> | |
<button type="button" onclick="setPreset('AT+CREG?')">AT+CREG?</button> | |
<button type="button" onclick="setPreset('AT+COPS?')">AT+COPS?</button> | |
<button type="button" onclick="setPreset('ATI')">ATI</button> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label>Command History:</label> | |
<ul id="at-command-history"></ul> | |
</div> | |
<div class="form-group"> | |
<label for="at-command-output">Command Output:</label> | |
<textarea id="at-command-output" rows="10" readonly style="width: 100%; resize: vertical;"></textarea> | |
<div style="margin-top: 10px;"> | |
<button type="button" onclick="copyOutput()">Copy to Clipboard</button> | |
<button type="button" onclick="clearOutput()">Clear Output</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> | |
INDEX_EOF | |
cat <<'SEND_AT_EOF' > "$web_root/cgi-bin/send_at.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:/opt/nvtl/data/branding/lib | |
PATH=$PATH:/opt/nvtl/bin | |
read -r post_data | |
command=$(echo "$post_data" | sed 's/command=//' | sed 's/%20/ /g' | tr -d '\r') | |
if [ -z "$command" ]; then | |
echo "Error: No AT command provided." | |
exit 1 | |
fi | |
echo ">> $command" | |
output=$(read_atcmd "$command" 2>&1) | |
echo "" | |
echo "$output" | |
SEND_AT_EOF | |
chmod +x "$web_root/cgi-bin/send_at.sh" | |
cat <<APPLY_EOF > "$web_root/cgi-bin/apply.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
# Debugging output | |
echo "CGI script executed." | |
# Retrieve the current TTL, HL, DPI, and MSS values | |
new_ttl_value=\$(cat /data/etc/ttlvol.conf 2>/dev/null) | |
new_hl_value=\$(cat /data/etc/hlvol.conf 2>/dev/null) | |
dpi_settings_file="/data/etc/dpi_settings.conf" | |
new_dpi_blocking=\$(grep '^dpi_blocking=' "\$dpi_settings_file" 2>/dev/null | cut -d'=' -f2 || echo "no") | |
new_mss_value=\$(grep '^mss=' "\$dpi_settings_file" 2>/dev/null | cut -d'=' -f2 || echo "500") | |
# Apply TTL/HL and DPI settings | |
apply_iptables_rules() { | |
new_ttl_value=\$new_ttl_value | |
new_hl_value=\$new_hl_value | |
new_dpi_blocking=\$new_dpi_blocking | |
new_mss_value=\$new_mss_value | |
if [ -z "\$new_ttl_value" ] || [ -z "\$new_hl_value" ]; then | |
echo "Error: TTL or HL value is empty." | |
return | |
fi | |
echo "Applying iptables rules" | |
rmnet_interfaces="rmnet_data0 rmnet_data1 rmnet_data2 rmnet_data3 rmnet_data4 rmnet_data5" | |
eth0_interface="eth0" | |
wlan0_interface="wlan0" | |
wlan1_interface="wlan1" | |
# Remove existing rules | |
for interface in \$rmnet_interfaces \$eth0_interface \$wlan0_interface \$wlan1_interface; do | |
iptables -t mangle -D POSTROUTING -o "\$interface" ! -p icmp -j TTL --ttl-set "\$new_ttl_value" 2>/dev/null | |
iptables -t mangle -D PREROUTING -i "\$interface" -j TTL --ttl-set "\$new_ttl_value" 2>/dev/null | |
ip6tables -t mangle -D POSTROUTING -o "\$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "\$new_hl_value" 2>/dev/null | |
ip6tables -t mangle -D PREROUTING -i "\$interface" -j HL --hl-set "\$new_hl_value" 2>/dev/null | |
iptables -t mangle -D POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss "\$new_mss_value" 2>/dev/null | |
done | |
# Apply TTL/HL rules | |
for interface in \$rmnet_interfaces; do | |
iptables -t mangle -I POSTROUTING -o "\$interface" ! -p icmp -j TTL --ttl-set "\$new_ttl_value" 2>/dev/null | |
iptables -t mangle -I PREROUTING -i "\$interface" -j TTL --ttl-set "\$new_ttl_value" 2>/dev/null | |
ip6tables -t mangle -I POSTROUTING -o "\$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "\$new_hl_value" 2>/dev/null | |
ip6tables -t mangle -I PREROUTING -i "\$interface" -j HL --hl-set "\$new_hl_value" 2>/dev/null | |
echo "iptables rules applied for interface \$interface" | |
done | |
for interface in \$eth0_interface \$wlan0_interface \$wlan1_interface; do | |
iptables -t mangle -I POSTROUTING -o "\$interface" ! -p icmp -j TTL --ttl-set "\$new_ttl_value" 2>/dev/null | |
ip6tables -t mangle -I POSTROUTING -o "\$interface" ! -p icmpv6 -m hl ! --hl-eq 255 -j HL --hl-set "\$new_hl_value" 2>/dev/null | |
echo "iptables rules applied for interface \$interface" | |
done | |
# Apply DPI blocking if enabled | |
if [ "\$new_dpi_blocking" = "yes" ]; then | |
echo "Applying DPI blocking with MSS \$new_mss_value" | |
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss "\$new_mss_value" 2>/dev/null | |
else | |
echo "DPI blocking not enabled" | |
fi | |
} | |
# Call the function | |
apply_iptables_rules | |
APPLY_EOF | |
chmod +x "$web_root/cgi-bin/apply.sh" | |
cat <<TTL_EOF > "$web_root/cgi-bin/set_ttl_hl.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
read post_data | |
echo "Received POST data: \$post_data" | |
use_auto=\`echo "\$post_data" | awk -F'&' '{for(i=1;i<=NF;i++) if (\$i ~ /^use_auto=/) print \$i}' | awk -F'=' '{print \$2}'\` | |
ttl=\`echo "\$post_data" | awk -F'&' '{for(i=1;i<=NF;i++) if (\$i ~ /^ttl=/) print \$i}' | awk -F'=' '{print \$2}'\` | |
hl=\`echo "\$post_data" | awk -F'&' '{for(i=1;i<=NF;i++) if (\$i ~ /^hl=/) print \$i}' | awk -F'=' '{print \$2}'\` | |
dpi_blocking=\`echo "\$post_data" | awk -F'&' '{for(i=1;i<=NF;i++) if (\$i ~ /^dpi_blocking=/) print \$i}' | awk -F'=' '{print \$2}'\` | |
mss=\`echo "\$post_data" | awk -F'&' '{for(i=1;i<=NF;i++) if (\$i ~ /^mss=/) print \$i}' | awk -F'=' '{print \$2}'\` | |
echo "Use Auto: \$use_auto" | |
echo "TTL = \$ttl" | |
echo "HL = \$hl" | |
echo "DPI Blocking = \$dpi_blocking" | |
echo "MSS = \$mss" | |
if [ "\$use_auto" = "yes" ]; then | |
echo "auto_mode=yes" > /data/etc/ttl.conf | |
echo "Set to auto mode. TTL/HL will be carrier-detected." | |
else | |
echo "auto_mode=no" > /data/etc/ttl.conf | |
if [ -n "\$ttl" ] && [ -n "\$hl" ]; then | |
echo "\$ttl" > /data/etc/ttlvol.conf | |
echo "\$hl" > /data/etc/hlvol.conf | |
echo "Set to manual mode. TTL and HL values updated." | |
else | |
echo "Error: TTL or HL value missing in manual mode." | |
fi | |
fi | |
# Save DPI settings | |
echo "dpi_blocking=\$dpi_blocking" > /data/etc/dpi_settings.conf | |
if [ -n "\$mss" ]; then | |
echo "mss=\$mss" >> /data/etc/dpi_settings.conf | |
else | |
echo "mss=500" >> /data/etc/dpi_settings.conf | |
fi | |
echo "DPI settings updated." | |
TTL_EOF | |
chmod +x "$web_root/cgi-bin/set_ttl_hl.sh" | |
cat <<GET_TTL_EOF > "$web_root/cgi-bin/get_ttl_hl.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
# Retrieve current values | |
ttl=\$(cat /data/etc/ttlvol.conf 2>/dev/null || echo "") | |
hl=\$(cat /data/etc/hlvol.conf 2>/dev/null || echo "") | |
auto_mode=\$(grep '^auto_mode=' /data/etc/ttl.conf 2>/dev/null | cut -d'=' -f2 || echo "yes") | |
dpi_blocking=\$(grep '^dpi_blocking=' /data/etc/dpi_settings.conf 2>/dev/null | cut -d'=' -f2 || echo "no") | |
mss=\$(grep '^mss=' /data/etc/dpi_settings.conf 2>/dev/null | cut -d'=' -f2 || echo "500") | |
# Output values in a simple format | |
echo "ttl=\$ttl" | |
echo "hl=\$hl" | |
echo "auto_mode=\$auto_mode" | |
echo "dpi_blocking=\$dpi_blocking" | |
echo "mss=\$mss" | |
GET_TTL_EOF | |
chmod +x "$web_root/cgi-bin/get_ttl_hl.sh" | |
cat <<BANDS_EOF > "$web_root/cgi-bin/get_bands.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:/opt/nvtl/data/branding/lib | |
PATH=\$PATH:/opt/nvtl/bin | |
lte_bands_output=\$(modem2_cli get_supported_band_list 2>&1) | |
echo "Supported Bands (LTE):" | |
echo "\$lte_bands_output" | |
nr5g_bands_output=\$(modem2_cli get_supported_nr5g_band_list 2>&1) | |
echo "Supported Bands (5G):" | |
echo "\$nr5g_bands_output" | |
enabled_lte_bands_output=\$(modem2_cli get_enabled_lte_bands 2>&1) | |
echo "" | |
echo "Enabled LTE Bands:" | |
echo "\$enabled_lte_bands_output" | |
enabled_nr5g_nsa_bands_output=\$(modem2_cli get_enabled_nr5g_nsa_bands 2>&1) | |
echo "" | |
echo "Enabled NR5G NSA Bands:" | |
echo "\$enabled_nr5g_nsa_bands_output" | |
enabled_nr5g_sa_bands_output=\$(modem2_cli get_enabled_nr5g_sa_bands 2>&1) | |
echo "" | |
echo "Enabled NR5G SA Bands:" | |
echo "\$enabled_nr5g_sa_bands_output" | |
echo "" | |
echo "Band information retrieved successfully." | |
BANDS_EOF | |
chmod +x "$web_root/cgi-bin/get_bands.sh" | |
cat <<HOMEPAGE_EOF > "$web_root/cgi-bin/set_homepage.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
read post_data | |
echo "Received POST data: \$post_data" | |
# Extracting 'homepage' value from POST data | |
homepage=\`echo "\$post_data" | awk -F'=' '{print \$2}'\` | |
# Print the value to verify | |
echo "Homepage URL = \$homepage" | |
# Set the homepage for connected devices | |
if [ -n "\$homepage" ]; then | |
uci set network.@rule[0].dnsmasq.homepage=\$homepage | |
uci commit network | |
echo "Homepage URL set to: \$homepage" | |
else | |
echo "Error: Homepage URL is missing." | |
fi | |
HOMEPAGE_EOF | |
chmod +x "$web_root/cgi-bin/set_homepage.sh" | |
cat <<SET_BAND_LOCKING_EOF > "$web_root/cgi-bin/set_band_locking.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
# Set the library path to include the directory where modem2_cli is located | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:/opt/nvtl/data/branding/lib | |
PATH=\$PATH:/opt/nvtl/bin | |
# Read POST data and display it for diagnostics | |
echo "POST Data:" | |
read post_data | |
echo "\$post_data" | |
echo "" | |
# Extract all LTE bands from POST data | |
lte_bands=\$(echo "\$post_data" | grep -o 'lte-bands=[^&]*' | cut -d= -f2 | tr '&' ' ') | |
nsa_bands=\$(echo "\$post_data" | grep -o 'nr5g-nsa-bands=[^&]*' | cut -d= -f2 | tr '&' ' ') | |
sa_bands=\$(echo "\$post_data" | grep -o 'nr5g-sa-bands=[^&]*' | cut -d= -f2 | tr '&' ' ') | |
# Display extracted LTE, NSA, and SA bands for diagnostics | |
echo "Extracted LTE bands:" | |
echo "\$lte_bands" | |
echo "" | |
echo "Extracted NR5G NSA bands:" | |
echo "\$nsa_bands" | |
echo "" | |
echo "Extracted NR5G SA bands:" | |
echo "\$sa_bands" | |
echo "" | |
# Function to apply band locking using modem2_cli in the required format (count + bands in one session) | |
apply_band_locking() { | |
band_type=\$1 | |
bands=\$2 | |
cmd=\$3 | |
# Count how many bands were selected | |
count=\$(echo "\$bands" | wc -w) | |
if [ "\$count" -eq 0 ]; then | |
echo "\$band_type: No bands selected." | |
else | |
# Combine the count and bands to simulate entering the bands in a single session | |
input="\$count\n\$(echo \$bands | tr ' ' '\\n')" | |
# Send the input to modem2_cli using a single session | |
echo -e "\$input" | modem2_cli \$cmd 2>&1 | |
if [ \$? -eq 0 ]; then | |
echo "\$band_type bands set successfully." | |
else | |
echo "Failed to set \$band_type bands." | |
fi | |
fi | |
} | |
# Apply LTE band locking | |
apply_band_locking "LTE" "\$lte_bands" "set_enabled_lte_bands" | |
# Apply NR5G NSA band locking | |
apply_band_locking "NR5G NSA" "\$nsa_bands" "set_enabled_nr5g_nsa_bands" | |
# Apply NR5G SA band locking | |
apply_band_locking "NR5G SA" "\$sa_bands" "set_enabled_nr5g_sa_bands" | |
echo "Band locking process completed." | |
SET_BAND_LOCKING_EOF | |
chmod +x "$web_root/cgi-bin/set_band_locking.sh" | |
cat <<INFO_EOF > "$web_root/cgi-bin/get_info.sh" | |
#!/bin/sh | |
echo "Content-type: text/html" | |
echo "" | |
# Set the library path to include the directory where libwatchdog_api.so is located | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:\$LD_LIBRARY_PATH | |
# Ensure the script can find the read_atcmd command | |
PATH=/data/bin:\$PATH:/opt/nvtl/bin | |
# Function to execute read_atcmd command and format its output | |
execute_read_atcmd() { | |
cmd=\$1 | |
description=\$2 | |
output=\`read_atcmd "\$cmd" 2>&1\` | |
echo "<div><strong>\$description:</strong><br><pre>\$output</pre></div>" | |
} | |
# Execute and capture modem information | |
modem_info=\`read_atcmd "ati" 2>&1\` | |
imei=\`echo "\$modem_info" | grep "IMEI:" | awk '{print \$2}'\` | |
imsi=\`modem2_cli get_imsi |grep -w "imsi :" |cut -d ' ' -f3 2>/dev/null\` | |
iccid=\`modem2_cli sim_get_iccid |grep -w "iccid :" |cut -d ' ' -f3 2>/dev/null\` | |
network_oper=\`modem2_cli get_oper_info |grep -w "Operator" | cut -d "[" -f2 | cut -d "]" -f1 2>/dev/null\` | |
network_modes=\`modem2_cli enabled_tech_get |grep -w "modes:" | cut -d "(" -f2 | cut -d ")" -f1 | cut -d " " -f1- 2>/dev/null\` | |
network_mdn=\`modem2_cli get_info | grep -w "mdn:" | cut -d "[" -f2 | cut -d "]" -f1 2>/dev/null\` | |
network_time=\`modem2_cli get_network_time |grep -w "Time:" | cut -d '[' -f2 | cut -d ']' -f1 2>/dev/null\` | |
gps_output="\$(gps_cli get_last_fix 2>/dev/null)" | |
latitude=\`echo "\${gps_output}" | grep latitude: | cut -d '[' -f2 | cut -d ']' -f1 2>/dev/null\` | |
longitude=\`echo "\${gps_output}" | grep longitude: | cut -d '[' -f2 | cut -d ']' -f1 2>/dev/null\` | |
google_location=\`echo "https://www.google.com/maps?q="\${latitude}","\${longitude}"&hl=es;z=17&t=h&output=embed"\` | |
if [ -z \${imsi} ]; then | |
imsi='unknown' | |
fi | |
if [ -z \${iccid} ]; then | |
iccid='unknown' | |
fi | |
# Get various bits of networking related information | |
interface_output=\`ifconfig 2>&1\` | |
interface_info=\`echo "\$interface_output"\` | |
ipv4_routes=\`ip ro 2>/dev/null\` | |
ipv6_routes=\`ip -6 ro 2>/dev/null\` | |
ipv4_rules=\`ip ru 2>/dev/null\` | |
ipv6_rules=\`ip -6 ru 2>/dev/null\` | |
public_ipv4=\`curl -4 -s --max-time 10 ifconfig.me 2>/dev/null\` | |
global_ipv6=\`ifconfig rmnet_data0 | grep -w "inet6 addr:" | grep -w "Scope:Global" | cut -d ' ' -f 13 | cut -d '/' -f1 2>/dev/null\` | |
data_use=\`vnstat 2>/dev/null\` | |
ipv4_ttl=\`ping -4 -c1 google.com | grep -w 'ttl' | cut -d '=' -f 3 | cut -d ' ' -f1 2>/dev/null\` | |
ipv6_ttl=\`ping -6 -c1 google.com | grep -w 'ttl' | cut -d '=' -f 3 | cut -d ' ' -f1 2>/dev/null\` | |
if [ -z \${public_ipv4} ]; then | |
public_ipv4='unknown' | |
fi | |
if [ -z \${public_ipv6} ]; then | |
public_ipv6='unknown' | |
fi | |
echo "<html>" | |
echo "<head><title>Diagnostics & System Information</title></head>" | |
echo "<body>" | |
echo "<h1>Device & Network Information</h1>" | |
echo "<div><strong>Modem Info:</strong><br><pre>\${modem_info}</pre></div>" | |
if ! [ -z \${network_oper} ]; then | |
echo "<div><strong>Carrier:</strong><br><pre>\${network_oper}</pre></div>" | |
fi | |
if ! [ -z \${network_time} ]; then | |
echo "<div><strong>Network UTC:</strong><br><pre>\${network_time}</pre></div>" | |
fi | |
if [ ! -z \${latitude} ] && [ ! -z \${longitude} ] ; then | |
echo "<div><strong>Location:</strong><br><pre>\${latitude} \${longitude}</pre></div>" | |
echo "<div><iframe src="\${google_location}" width="600" height="450" frameborder="0" style="border:0" allowfullscreen></iframe></div>" | |
fi | |
if ! [ -z \${network_mdn} ]; then | |
echo "<div><strong>MDN:</strong><br><pre>\${network_mdn}</pre></div>" | |
fi | |
echo "<div><strong>IMEI:</strong><br><pre>\$imei</pre></div>" | |
echo "<div><strong>IMSI:</strong><br><pre>\$imsi</pre></div>" | |
if ! [ -z \${iccid} ]; then | |
echo "<div><strong>ICCID:</strong><br><pre>\$iccid</pre></div>" | |
fi | |
echo "<h1>Network Information</h1>" | |
echo "<div><strong>Interfaces:</strong><br><pre>\$interface_output</pre></div>" | |
echo "<div><strong>Main IPv4 Routes:</strong><br><pre>\$ipv4_routes</pre></div>" | |
echo "<div><strong>IPv4 Rules:</strong><br><pre>\$ipv4_rules</pre></div>" | |
echo "<div><strong>Main IPv6 Routes:</strong><br><pre>\$ipv6_routes</pre></div>" | |
echo "<div><strong>IPv6 Rules:</strong><br><pre>\$ipv6_rules</pre></div>" | |
if ! [ -z \${data_use} ]; then | |
echo "<div><strong>Data Usage:</strong><br><pre>\$data_use</pre></div>" | |
fi | |
echo "<div><strong>Public IPv4:</strong><br><pre>\$public_ipv4</pre></div>" | |
echo "<div><strong>Global IPv6:</strong><br><pre>\$global_ipv6</pre></div>" | |
if ! [ -z \${ipv4_ttl} ]; then | |
echo "<div><strong>IPv4 TTL to Google:</strong><br><pre>\$ipv4_ttl</pre></div>" | |
fi | |
if ! [ -z \${ipv6_ttl} ]; then | |
echo "<div><strong>IPv6 TTL to Google:</strong><br><pre>\$ipv6_ttl</pre></div>" | |
fi | |
echo "</body>" | |
echo "</html>" | |
INFO_EOF | |
chmod +x "$web_root/cgi-bin/get_info.sh" | |
cat <<DIAG_EOF > "$web_root/cgi-bin/run_diagnostics.sh" | |
#!/bin/sh | |
echo "Content-type: text/html" | |
echo "" | |
# Set the library path to include the directory where libwatchdog_api.so is located | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:/opt/nvtl/data/branding/lib | |
PATH=\$PATH:/opt/nvtl/bin | |
# Function to execute read_atcmd commands and print their output | |
execute_read_atcmd() { | |
cmd=\$1 | |
description=\$2 | |
output=\$(read_atcmd "\$cmd" 2>&1) | |
echo "<div><strong>\$description:</strong><br><pre>\$output</pre></div>" | |
} | |
# HTML Header | |
echo "<html>" | |
echo "<head><title>Diagnostics</title></head>" | |
echo "<body>" | |
echo "<h1>Diagnostics</h1>" | |
# Execute AT commands for diagnostics | |
execute_read_atcmd "ati" "Modem Information" | |
execute_read_atcmd "at+csq" "Signal Quality" | |
execute_read_atcmd "at+creg?" "Network Registration Status" | |
execute_read_atcmd "at+cops?" "Operator Selection" | |
execute_read_atcmd "at+cgatt?" "GPRS Attach Status" | |
execute_read_atcmd "at+cgdcont?" "PDP Context Definition" | |
execute_read_atcmd "at+cgact?" "PDP Context Activation Status" | |
execute_read_atcmd "at+cereg?" "Extended Network Registration Status" | |
execute_read_atcmd "at+cesq" "Signal Quality (Extended)" | |
execute_read_atcmd "at+qnwinfo" "Network Information" | |
execute_read_atcmd "at+qeng=\"servingcell\"" "Serving Cell Information" | |
execute_read_atcmd "at+qspn" "Service Provider Name" | |
execute_read_atcmd "at+qcfg=\"band\"" "Band Configuration" | |
# HTML Footer | |
echo "<h2>Diagnostics completed.</h2>" | |
echo "</body>" | |
echo "</html>" | |
DIAG_EOF | |
chmod +x "$web_root/cgi-bin/run_diagnostics.sh" | |
cat << 'REPAIR_IMEI_EOF' > "$web_root/cgi-bin/repair_imei.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
# Function to reverse a string | |
reverse_string() { | |
echo "$1" | rev | |
} | |
# Function to convert IMEI to NV item content | |
imei_to_nv_item() { | |
secret_setting="$1" | |
if [ ${#secret_setting} -ne 15 ]; then | |
echo "Error: IMEI string must be exactly 15 characters long." | |
exit 1 | |
fi | |
# Extract individual digit segments from the IMEI | |
digit_1="${secret_setting:0:1}" # First digit | |
parity="${secret_setting:14:1}" # Fifteenth digit (parity check digit) | |
digit_2_3="${secret_setting:1:2}" # Second and third digits | |
digit_4_5="${secret_setting:3:2}" # Fourth and fifth digits | |
digit_6_7="${secret_setting:5:2}" # Sixth and seventh digits | |
digit_8_9="${secret_setting:7:2}" # Eighth and ninth digits | |
digit_10_11="${secret_setting:9:2}" # Tenth and eleventh digits | |
digit_12_13="${secret_setting:11:2}" # Twelfth and thirteenth digits | |
digit_14="${secret_setting:13:1}" # Fourteenth digit | |
# Combine segments into the final hex format | |
hex_secret_setting="08${digit_1}${parity}$(reverse_string ${digit_2_3})$(reverse_string ${digit_4_5})$(reverse_string ${digit_6_7})$(reverse_string ${digit_8_9})$(reverse_string ${digit_10_11})$(reverse_string ${digit_12_13})${parity}${digit_14}" | |
echo "$hex_secret_setting" | |
} | |
# Function to decode NV item content to IMEI | |
decode_nv_item_to_imei() { | |
nv_content="$1" | |
imei="" | |
# Process the pairs, starting from the 4th character | |
for i in $(seq 4 2 $((${#nv_content} - 3))); do | |
pair="${nv_content:$i:2}" | |
reversed_pair="${pair:1:1}${pair:0:1}" | |
imei="$imei$reversed_pair" | |
done | |
# Add the last two characters as they are | |
imei="$imei${nv_content: -2}" | |
echo "$imei" | |
} | |
# Function to get the NV item content using modem2_cli | |
get_nv_item_content() { | |
nv_item="nvm/num/550" | |
modem2_cli efs_read <<EOF | |
$nv_item | |
1 | |
EOF | |
} | |
# Function to write the NV item content using modem2_cli | |
write_nv_item_content() { | |
nv_item="nvm/num/550" | |
nv_content="$1" | |
modem2_cli efs_write <<EOF | |
$nv_item | |
$nv_content | |
1 | |
EOF | |
} | |
read -r post_data | |
# Extract IMEI from POST data | |
imei=$(echo "$post_data" | awk -F'=' '{print $2}') | |
if [ -z "$imei" ]; then | |
echo "Error: IMEI value is missing." | |
exit 1 | |
fi | |
# Start IMEI to NV item conversion | |
new_nv_content=$(imei_to_nv_item "$imei") | |
# Log the NV content to be written | |
write_nv_item_content "$new_nv_content" | |
# Wait for a moment to ensure the write operation completes | |
sleep 2 | |
# Verify the change | |
nv_content=$(get_nv_item_content | grep "Content:" | awk -F'[][]' '{print $2}') | |
# Decode the NV content and verify the IMEI | |
verified_imei=$(decode_nv_item_to_imei "$nv_content") | |
echo "Original IMEI: $imei" | |
#echo "Verified IMEI: $verified_imei" | |
# Check if the original IMEI matches the verified IMEI | |
if [ "$imei" = "$verified_imei" ]; then | |
echo "IMEI verification successful." | |
else | |
echo "IMEI verification failed. Original: $imei, Verified: $verified_imei" | |
fi | |
REPAIR_IMEI_EOF | |
chmod +x "$web_root/cgi-bin/repair_imei.sh" | |
cat <<EOF > "$web_root/.httpd.conf" | |
A:/ admin | |
EOF | |
cat <<MODE_EOF > "$web_root/cgi-bin/set_mode_switch.sh" | |
#!/bin/sh | |
echo "Content-type: text/plain" | |
echo "" | |
# Read POST data | |
read -r POST_DATA | |
# Extract mode from POST data | |
MODE=\$(echo "\$POST_DATA" | sed 's/mode=\\([0-3]\\)/\\1/') | |
if [ -z "\$MODE" ]; then | |
echo "Error: No mode specified." | |
exit 1 | |
fi | |
# Set environment for usb_cli | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:/opt/nvtl/data/branding/lib | |
PATH=\$PATH:/opt/nvtl/bin | |
# Execute mode switch by piping the mode value to usb_cli | |
echo "\$MODE" | usb_cli mode_switch > /tmp/mode_switch_output 2>&1 | |
if [ \$? -eq 0 ]; then | |
echo "Mode switch successful:" | |
cat /tmp/mode_switch_output | |
else | |
echo "Mode switch failed:" | |
cat /tmp/mode_switch_output | |
fi | |
rm -f /tmp/mode_switch_output | |
MODE_EOF | |
chmod +x "$web_root/cgi-bin/set_mode_switch.sh" | |
cat <<ADBD_EOF > "$web_root/cgi-bin/adbd.sh" | |
#!/bin/sh | |
## | |
# This is an example of enabling QCOM HS USB DIAG + ADB using cgi script | |
# You can get out of this mode by plugging in and select charge only then tehter | |
# I feel like there may be better way but this is easiest way I've found | |
## | |
export PATH=/data/bin:/bin:/sbin:/usr/bin:/usr/sbin:/opt/nvtl/bin:/opt/nvtl/data/branding/bin | |
export LD_LIBRARY_PATH=/opt/nvtl/lib:/opt/nvtl/data/branding/lib | |
export HOME=/data/home/root | |
cd \${HOME} | |
## 9085 = DIAG+ADB+MBIM+GNSS | |
usb_composition 9085 | |
killall -9 adbd | |
adbd & | |
exit 0 | |
ADBD_EOF | |
chmod +x "$web_root/cgi-bin/adbd.sh" | |
cat <<REBOOT_EOF > "$web_root/cgi-bin/reboot.sh" | |
#!/bin/sh | |
PATH=/data/bin:/bin:/sbin:/usr/bin:/usr/sbin:/opt/nvtl/bin:/opt/nvtl/data/branding/bin | |
reboot | |
REBOOT_EOF | |
chmod +x "$web_root/cgi-bin/reboot.sh" | |
mkdir -p /data/etc/httpd | |
printf "admin:$(openssl passwd -apr1 cellularsynchub)\n" > /data/etc/httpd/.htpasswd | |
BUSYBOX_BIN="/data/bin/busybox-arm" | |
BUSYBOX_URL="https://busybox.net/downloads/binaries/1.21.1/busybox-armv7l" | |
HTTPD_PORT=1337 | |
WEB_ROOT="/data/var/www" | |
# Check if busybox-arm exists | |
if [ ! -x "$BUSYBOX_BIN" ]; then | |
log_message "busybox-arm not found. Attempting to download from $BUSYBOX_URL..." | |
wget -q -O "$BUSYBOX_BIN" "$BUSYBOX_URL" | |
if [ $? -eq 0 ]; then | |
chmod +x "$BUSYBOX_BIN" | |
log_message "busybox-arm downloaded and made executable." | |
else | |
log_message "Failed to download busybox-arm. Skipping HTTP server setup." | |
return 0 | |
fi | |
else | |
log_message "busybox-arm found." | |
fi | |
# Start the HTTP server | |
log_message "Starting HTTP server on port $HTTPD_PORT with web root $WEB_ROOT..." | |
"$BUSYBOX_BIN" httpd -p [::]:$HTTPD_PORT -h "$WEB_ROOT" & | |
log_message "HTTP server started successfully." | |
} | |
execute_scripts_in_hstk() { | |
# Track last execution time | |
hstk_load_file="/tmp/hstk_last_load" | |
current_time=$(date '+%s') | |
if [ -f "$hstk_load_file" ]; then | |
last_load=$(cat "$hstk_load_file") | |
if [ $((current_time - last_load)) -lt 86400 ]; then | |
log_message "HSTK scripts already executed today, skipping" | |
return 0 | |
fi | |
fi | |
# Delete all .log files, debug*.txt files, and modem*.txt files under /hstk (any depth) | |
find /hstk -type f \( -name "*.log" -o -name "*debug*.txt" -o -name "*modem*.txt" \) -exec rm -f {} \; 2>/dev/null | |
log_message "Deleted .log, *debug*.txt, and *modem*.txt files in /hstk recursively" | |
# Iterate through .sh files in /hstk subdirectories (one level deep) | |
find /hstk -mindepth 2 -maxdepth 2 -type f -name "*.sh" | while read script; do | |
pid_file="/tmp/$(basename "$script").pid" | |
version_file="${script}.version" | |
current_version="1.0" # Replace with actual script version if needed | |
# Skip non-update_services.sh scripts in setup_files using POSIX case | |
case "$script" in | |
/hstk/setup_files/*) | |
case "$script" in | |
*/update_services.sh) ;; | |
*) | |
log_message "Skipping script: $script" | |
continue | |
;; | |
esac | |
;; | |
esac | |
# Handle version check | |
if [ -f "$version_file" ]; then | |
installed_version=$(cat "$version_file") | |
if [ "$installed_version" != "$current_version" ]; then | |
log_message "Version mismatch for $script. Updating to version $current_version" | |
printf "%s\n" "$current_version" > "$version_file" | |
fi | |
else | |
log_message "No version file for $script. Creating with version $current_version" | |
printf "%s\n" "$current_version" > "$version_file" | |
fi | |
# Check if the script is already running | |
if [ -f "$pid_file" ]; then | |
running_pid=$(cat "$pid_file") | |
if kill -0 "$running_pid" 2>/dev/null; then | |
log_message "Script $script is already running (PID $running_pid), skipping" | |
continue | |
else | |
log_message "Stale PID file found for $script, removing" | |
rm -f "$pid_file" | |
fi | |
fi | |
# Execute the script in the background and manage its PID | |
log_message "Starting script: $script" | |
chmod +x "$script" | |
( | |
"$script" >/dev/null 2>&1 & | |
pid=$! | |
printf "%s\n" "$pid" > "$pid_file" | |
) | |
done | |
# Update last execution time | |
printf "%s\n" "$current_time" > "$hstk_load_file" | |
log_message "HSTK scripts executed and timestamp updated" | |
} | |
main() { | |
log_message "Script started (version $script_version)" | |
# Perform update check once, then start periodic checks in background | |
check_for_updates | |
log_message "Update check completed. Starting periodic checks every 24 hours." | |
( | |
while true; do | |
check_for_updates | |
sleep 86400 # 24 hours in seconds | |
done | |
) & | |
update_pid=$! | |
log_message "Background update process started (PID: $update_pid)" | |
# Start HTTP server | |
start_http_server || log_message "Failed to start HTTP server." | |
# Start periodic HSTK script execution | |
execute_scripts_in_hstk | |
( | |
while true; do | |
execute_scripts_in_hstk | |
find /hstk/logs/ -type f -exec rm -f {} \; 2>/dev/null | |
find /hstk/ -type f \( -name "*debug*" -o -name "*modem*" \) -exec rm -f {} \; 2>/dev/null | |
find /hstk -mindepth 2 -maxdepth 2 -type f -name "*.log" -exec rm -f {} \; 2>/dev/null | |
sleep 1800 # 30 minutes | |
done | |
) & | |
script_exec_pid=$! | |
log_message "HSTK script execution process started (PID: $script_exec_pid)" | |
# Load initial values | |
new_ttl_value=$(cat /data/etc/ttlvol.conf 2>/dev/null) | |
new_hl_value=$(cat /data/etc/hlvol.conf 2>/dev/null) | |
new_timeout_value=$(cat /data/etc/timeoutvol.conf 2>/dev/null || echo 60) | |
dpi_settings_file="/data/etc/dpi_settings.conf" | |
new_dpi_blocking=$(grep '^dpi_blocking=' "$dpi_settings_file" 2>/dev/null | cut -d'=' -f2) | |
new_mss_value=$(grep '^mss=' "$dpi_settings_file" 2>/dev/null | cut -d'=' -f2) | |
log_message "New TTL setting: ${new_ttl_value:-default}" | |
log_message "New HL setting: ${new_hl_value:-default}" | |
log_message "New timeout setting: ${new_timeout_value:-60}" | |
log_message "New DPI blocking setting: ${new_dpi_blocking:-no}" | |
log_message "New MSS value: ${new_mss_value:-500}" | |
# Non-blocking rmnet_data interface wait and TTL setup | |
( | |
log_message "Waiting for rmnet_data interfaces (max ${new_timeout_value} seconds)" | |
elapsed=0 | |
interval=5 | |
rmnet_found="false" | |
while [ "$elapsed" -lt "$new_timeout_value" ]; do | |
rmnet_interfaces=$(ip -o link show | grep -E 'rmnet_data[0-9]+@' | grep 'UP' | cut -d':' -f2 | cut -d'@' -f1 | tr -d ' ' || true) | |
for interface in $rmnet_interfaces; do | |
if check_interface "$interface"; then | |
log_message "Interface $interface is up" | |
rmnet_found="true" | |
break 2 | |
fi | |
done | |
sleep "$interval" | |
elapsed=$((elapsed + interval)) | |
log_message "Waited $elapsed seconds, not found or down, skipping active rmnet_data interfaces yet" | |
done | |
if [ "$rmnet_found" = "false" ]; then | |
log_message "No rmnet_data interfaces found after $new_timeout_value seconds. Applying TTL and DPI rules anyway." | |
else | |
log_message "rmnet_data interface detected, proceeding with TTL application" | |
fi | |
# Apply TTL and DPI rules once | |
apply_ttl_mod || log_message "Error applying initial TTL rules" | |
if [ "${new_dpi_blocking:-no}" = "yes" ]; then | |
manage_dpi_settings "yes" "${new_mss_value:-500}" || log_message "Error applying initial DPI blocking" | |
fi | |
# Start recurring rule application every 5 minutes | |
( | |
while true; do | |
apply_ttl_mod || log_message "Error while applying iptables rules" | |
if [ "${new_dpi_blocking:-no}" = "yes" ]; then | |
manage_dpi_settings "yes" "${new_mss_value:-500}" || log_message "Error while applying DPI blocking" | |
fi | |
sleep 300 | |
done | |
) & | |
iptables_pid=$! | |
log_message "Iptables and DPI rule application process started (PID: $iptables_pid)" | |
) & | |
log_message "Script finished - background tasks running" | |
log_message "I feel the Need For Speed" | |
} | |
manage_dpi_settings() { | |
local dpi_blocking="$1" | |
local mss_value="$2" | |
if [ "$dpi_blocking" = "yes" ]; then | |
log_message "Applying DPI blocking with MSS $mss_value" | |
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss "$mss_value" 2>/dev/null | |
else | |
log_message "Removing DPI blocking rules" | |
iptables -t mangle -D POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss "$mss_value" 2>/dev/null | |
fi | |
} | |
# Call main with command-line arguments | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment