diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index 275e336846..a071731847 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -12,6 +12,7 @@ import socket import threading import time import tempfile +import subprocess from collections import namedtuple from functools import partial from typing import Any, Dict @@ -27,7 +28,7 @@ from common.file_helpers import CallbackReader from common.basedir import PERSIST from common.params import Params from common.realtime import sec_since_boot -from selfdrive.hardware import HARDWARE, PC +from selfdrive.hardware import HARDWARE, PC, TICI from selfdrive.loggerd.config import ROOT from selfdrive.loggerd.xattr_cache import getxattr, setxattr from selfdrive.swaglog import cloudlog, SWAGLOG_DIR @@ -329,6 +330,18 @@ def primeActivated(activated): return {"success": 1} +@dispatcher.add_method +def setUploadLimit(speed_kbps): + if not TICI: + return {"success": 0, "error": "only supported on comma three"} + + try: + HARDWARE.set_upload_limit(speed_kbps) + return {"success": 1} + except subprocess.CalledProcessError as e: + return {"success": 0, "error": "failed to set limit", "stdout": e.stdout, "stderr": e.stderr} + + def startLocalProxy(global_end_event, remote_ws_uri, local_port): try: if local_port not in LOCAL_PORT_WHITELIST: diff --git a/selfdrive/hardware/base.py b/selfdrive/hardware/base.py index 77ddcbc2d4..eec09f0754 100644 --- a/selfdrive/hardware/base.py +++ b/selfdrive/hardware/base.py @@ -67,6 +67,10 @@ class HardwareBase(ABC): def get_network_strength(self, network_type): pass + @staticmethod + def set_upload_limit(speed_kbps: int) -> None: + pass + @abstractmethod def get_battery_capacity(self): pass diff --git a/selfdrive/hardware/tici/hardware.py b/selfdrive/hardware/tici/hardware.py index 31bfa736c0..6e0e56247a 100644 --- a/selfdrive/hardware/tici/hardware.py +++ b/selfdrive/hardware/tici/hardware.py @@ -218,6 +218,35 @@ class Tici(HardwareBase): return network_strength + @staticmethod + def set_upload_limit(speed_kbps: int) -> None: + speed_kbps = int(speed_kbps) # Ensure integer value + + adapter = "wwan0" + tc = ["sudo", "tc"] + + # check, cmd + commands = [ + # Clean up old rules + (False, tc + ["qdisc", "del", "dev", adapter, "root"]), + + # Create root Hierarchy Token Bucket that sends all trafic to 1:20 + (True, tc + ["qdisc", "add", "dev", adapter, "root", "handle", "1:", "htb", "default", "20"]), + + # Create class 1:20 with specified rate limit + (True, tc + ["class", "add", "dev", adapter, "parent", "1:", "classid", "1:20", "htb", "rate", f"{speed_kbps}kbit"]), + + # Create universal 32 bit filter on adapter that sends all outbound ip traffic through the class + (True, tc + ["filter", "add", "dev", adapter, "parent", "1:", "protocol", "ip", "prio", "10", "u32", "match", "ip", "dst", "0.0.0.0/0", "flowid", "1:20"]), + ] + + # Disable limits + if speed_kbps == -1: + commands = commands[:1] + + for check, cmd in commands: + subprocess.run(cmd, check=check) + def get_modem_version(self): try: modem = self.get_modem()