Browse Source

refactor(bandwidth): move validation to Zod and simplify tc commands

pull/2368/head
Aaron Vu 6 months ago
parent
commit
c36d358f37
  1. 3
      src/server/database/repositories/general/types.ts
  2. 52
      src/server/utils/bandwidth.ts

3
src/server/database/repositories/general/types.ts

@ -18,7 +18,8 @@ const bandwidthEnabled = z.boolean({
});
const bandwidthLimit = z
.number({ message: t('zod.general.bandwidthLimit') })
.min(0);
.min(0)
.max(10000);
export const GeneralUpdateSchema = z.object({
sessionTimeout: sessionTimeout,

52
src/server/utils/bandwidth.ts

@ -14,28 +14,6 @@ export interface TcCommands {
postDown: string;
}
// Valid interface name pattern (e.g., wg0, eth0, enp0s3)
const VALID_INTERFACE_PATTERN = /^[a-zA-Z0-9_-]{1,15}$/;
// Bandwidth limits (1 Mbps to 10 Gbps)
const MIN_BANDWIDTH_MBPS = 1;
const MAX_BANDWIDTH_MBPS = 10000;
/**
* Validate interface name to prevent command injection
*/
function isValidInterfaceName(name: string): boolean {
return VALID_INTERFACE_PATTERN.test(name);
}
/**
* Clamp bandwidth value to safe range
*/
function clampBandwidth(value: number): number {
if (value <= 0) return 0;
return Math.min(Math.max(value, MIN_BANDWIDTH_MBPS), MAX_BANDWIDTH_MBPS);
}
/**
* Check if IFB kernel module is available for upload limiting
*/
@ -52,7 +30,8 @@ export async function checkIfbAvailable(): Promise<boolean> {
}
/**
* Generate tc commands for bandwidth limiting
* Generate tc commands for bandwidth limiting using simplified HTB qdisc
* Reference: https://www.procustodibus.com/blog/2022/12/limit-wireguard-bandwidth/
* @param interfaceName - WireGuard interface name (e.g., wg0)
* @param config - Bandwidth configuration
* @param ifbAvailable - Whether IFB module is available for upload limiting
@ -62,36 +41,28 @@ export function generateTcCommands(
config: BandwidthConfig,
ifbAvailable: boolean = true
): TcCommands {
// Validate interface name to prevent command injection
if (!isValidInterfaceName(interfaceName)) {
BW_DEBUG(`Invalid interface name: ${interfaceName}`);
return { postUp: '', postDown: '' };
}
// Clamp bandwidth values to safe range
const downloadMbps = clampBandwidth(config.downloadMbps);
const uploadMbps = clampBandwidth(config.uploadMbps);
const downloadMbps = config.downloadMbps;
const uploadMbps = config.uploadMbps;
if (!config.enabled || (downloadMbps === 0 && uploadMbps === 0)) {
if (!config.enabled || (downloadMbps <= 0 && uploadMbps <= 0)) {
return { postUp: '', postDown: '' };
}
const postUpParts: string[] = [];
const postDownParts: string[] = [];
// Download limit (egress on wg interface)
// Download limit (egress on wg interface) - simplified single class
if (downloadMbps > 0) {
postUpParts.push(
`tc qdisc add dev ${interfaceName} root handle 1: htb default 10`,
`tc class add dev ${interfaceName} parent 1: classid 1:1 htb rate ${downloadMbps}mbit ceil ${downloadMbps}mbit`,
`tc class add dev ${interfaceName} parent 1:1 classid 1:10 htb rate ${downloadMbps}mbit ceil ${downloadMbps}mbit`
`tc qdisc add dev ${interfaceName} root handle 1: htb default 1`,
`tc class add dev ${interfaceName} parent 1: classid 1:1 htb rate ${downloadMbps}mbit ceil ${downloadMbps}mbit`
);
postDownParts.push(
`tc qdisc del dev ${interfaceName} root 2>/dev/null || true`
);
}
// Upload limit (ingress via IFB mirror)
// Upload limit (ingress via IFB mirror) - simplified single class
if (uploadMbps > 0 && ifbAvailable) {
const ifbDev = 'ifb0';
postUpParts.push(
@ -99,9 +70,8 @@ export function generateTcCommands(
`ip link set ${ifbDev} up`,
`tc qdisc add dev ${interfaceName} handle ffff: ingress`,
`tc filter add dev ${interfaceName} parent ffff: protocol all u32 match u32 0 0 action mirred egress redirect dev ${ifbDev}`,
`tc qdisc add dev ${ifbDev} root handle 1: htb default 10`,
`tc class add dev ${ifbDev} parent 1: classid 1:1 htb rate ${uploadMbps}mbit ceil ${uploadMbps}mbit`,
`tc class add dev ${ifbDev} parent 1:1 classid 1:10 htb rate ${uploadMbps}mbit ceil ${uploadMbps}mbit`
`tc qdisc add dev ${ifbDev} root handle 1: htb default 1`,
`tc class add dev ${ifbDev} parent 1: classid 1:1 htb rate ${uploadMbps}mbit ceil ${uploadMbps}mbit`
);
postDownParts.push(
`tc qdisc del dev ${interfaceName} handle ffff: ingress 2>/dev/null || true`,

Loading…
Cancel
Save