Add peer creation time tracking and enhance DEAD peer handling
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:waylume_server/wireguard/peers.dart';
|
||||||
|
|
||||||
class VpnSessionService {
|
class VpnSessionService {
|
||||||
|
|
||||||
@@ -47,13 +48,14 @@ class VpnSessionService {
|
|||||||
/// Parses keepalive info string into structured data
|
/// Parses keepalive info string into structured data
|
||||||
static Map<String, dynamic> _parseKeepaliveInfo(String keepaliveInfo, String publicKey, String ipAddress) {
|
static Map<String, dynamic> _parseKeepaliveInfo(String keepaliveInfo, String publicKey, String ipAddress) {
|
||||||
if (keepaliveInfo.startsWith('DEAD')) {
|
if (keepaliveInfo.startsWith('DEAD')) {
|
||||||
final regex = RegExp(r'DEAD - Last handshake: ([^(]+) \((\d+)m (\d+)s ago\)');
|
// Handle regular DEAD peers (with handshake)
|
||||||
final match = regex.firstMatch(keepaliveInfo);
|
final handshakeRegex = RegExp(r'DEAD - Last handshake: ([^(]+) \((\d+)m (\d+)s ago\)');
|
||||||
|
final handshakeMatch = handshakeRegex.firstMatch(keepaliveInfo);
|
||||||
|
|
||||||
if (match != null) {
|
if (handshakeMatch != null) {
|
||||||
final lastHandshake = match.group(1)?.trim();
|
final lastHandshake = handshakeMatch.group(1)?.trim();
|
||||||
final minutes = int.tryParse(match.group(2) ?? '0') ?? 0;
|
final minutes = int.tryParse(handshakeMatch.group(2) ?? '0') ?? 0;
|
||||||
final seconds = int.tryParse(match.group(3) ?? '0') ?? 0;
|
final seconds = int.tryParse(handshakeMatch.group(3) ?? '0') ?? 0;
|
||||||
final totalMinutes = minutes + (seconds / 60).round();
|
final totalMinutes = minutes + (seconds / 60).round();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -64,6 +66,24 @@ class VpnSessionService {
|
|||||||
'status': 'DEAD'
|
'status': 'DEAD'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle DEAD peers without handshake (never connected)
|
||||||
|
final creationRegex = RegExp(r'DEAD - Last handshake: Never \((\d+)m (\d+)s since creation\)');
|
||||||
|
final creationMatch = creationRegex.firstMatch(keepaliveInfo);
|
||||||
|
|
||||||
|
if (creationMatch != null) {
|
||||||
|
final minutes = int.tryParse(creationMatch.group(1) ?? '0') ?? 0;
|
||||||
|
final seconds = int.tryParse(creationMatch.group(2) ?? '0') ?? 0;
|
||||||
|
final totalMinutes = minutes + (seconds / 60).round();
|
||||||
|
|
||||||
|
return {
|
||||||
|
'public_key': publicKey,
|
||||||
|
'ip_address': ipAddress,
|
||||||
|
'last_handshake': 'Never',
|
||||||
|
'minutes_since_handshake': totalMinutes,
|
||||||
|
'status': 'DEAD'
|
||||||
|
};
|
||||||
|
}
|
||||||
} else if (keepaliveInfo.startsWith('ALIVE')) {
|
} else if (keepaliveInfo.startsWith('ALIVE')) {
|
||||||
final regex = RegExp(r'ALIVE - Last handshake: ([^(]+) \((\d+)s ago\)');
|
final regex = RegExp(r'ALIVE - Last handshake: ([^(]+) \((\d+)s ago\)');
|
||||||
final match = regex.firstMatch(keepaliveInfo);
|
final match = regex.firstMatch(keepaliveInfo);
|
||||||
@@ -115,6 +135,17 @@ class VpnSessionService {
|
|||||||
final handshakeTime = int.tryParse(latestHandshake) ?? 0;
|
final handshakeTime = int.tryParse(latestHandshake) ?? 0;
|
||||||
|
|
||||||
if (handshakeTime == 0) {
|
if (handshakeTime == 0) {
|
||||||
|
// Check peer creation time to see if it's stale
|
||||||
|
final creationTime = await getPeerCreationTime(publicKey);
|
||||||
|
if (creationTime != null) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final duration = now.difference(creationTime);
|
||||||
|
|
||||||
|
// Mark as DEAD if created more than 2 minutes 30 seconds ago (same threshold as handshake)
|
||||||
|
if (duration.inSeconds > 150) {
|
||||||
|
return 'DEAD - Last handshake: Never (${duration.inMinutes}m ${duration.inSeconds % 60}s since creation)';
|
||||||
|
}
|
||||||
|
}
|
||||||
return 'No handshake yet';
|
return 'No handshake yet';
|
||||||
} else {
|
} else {
|
||||||
final handshakeDateTime = DateTime.fromMillisecondsSinceEpoch(handshakeTime * 1000);
|
final handshakeDateTime = DateTime.fromMillisecondsSinceEpoch(handshakeTime * 1000);
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ Future<WireGuardPeer> createPeer() async {
|
|||||||
// Add peer to WireGuard
|
// Add peer to WireGuard
|
||||||
await Process.run('wg', ['set', 'wg0', 'peer', publicKey.trim(), 'allowed-ips', '$ip/32']);
|
await Process.run('wg', ['set', 'wg0', 'peer', publicKey.trim(), 'allowed-ips', '$ip/32']);
|
||||||
|
|
||||||
|
// Track peer creation time
|
||||||
|
await _trackPeerCreation(publicKey.trim());
|
||||||
|
|
||||||
return WireGuardPeer(
|
return WireGuardPeer(
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
publicKey: publicKey.trim(),
|
publicKey: publicKey.trim(),
|
||||||
@@ -83,6 +86,10 @@ Future<bool> deletePeer(String publicKey) async {
|
|||||||
print('Error removing peer: ${result.stderr}');
|
print('Error removing peer: ${result.stderr}');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up peer creation tracking
|
||||||
|
await _cleanupPeerCreation(publicKey);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,3 +141,56 @@ Future<String> _getNextIP() async {
|
|||||||
}
|
}
|
||||||
throw Exception('No available IPs');
|
throw Exception('No available IPs');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tracks when a peer was created
|
||||||
|
Future<void> _trackPeerCreation(String publicKey) async {
|
||||||
|
try {
|
||||||
|
final file = File('/tmp/peer_creation_times.json');
|
||||||
|
Map<String, dynamic> creationTimes = {};
|
||||||
|
|
||||||
|
if (await file.exists()) {
|
||||||
|
final content = await file.readAsString();
|
||||||
|
creationTimes = jsonDecode(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
creationTimes[publicKey] = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
await file.writeAsString(jsonEncode(creationTimes));
|
||||||
|
} catch (e) {
|
||||||
|
print('Error tracking peer creation: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the creation time for a peer
|
||||||
|
Future<DateTime?> getPeerCreationTime(String publicKey) async {
|
||||||
|
try {
|
||||||
|
final file = File('/tmp/peer_creation_times.json');
|
||||||
|
if (!await file.exists()) return null;
|
||||||
|
|
||||||
|
final content = await file.readAsString();
|
||||||
|
final creationTimes = jsonDecode(content) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
final timestamp = creationTimes[publicKey];
|
||||||
|
if (timestamp == null) return null;
|
||||||
|
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||||
|
} catch (e) {
|
||||||
|
print('Error getting peer creation time: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cleans up peer creation tracking when peer is deleted
|
||||||
|
Future<void> _cleanupPeerCreation(String publicKey) async {
|
||||||
|
try {
|
||||||
|
final file = File('/tmp/peer_creation_times.json');
|
||||||
|
if (!await file.exists()) return;
|
||||||
|
|
||||||
|
final content = await file.readAsString();
|
||||||
|
final creationTimes = jsonDecode(content) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
creationTimes.remove(publicKey);
|
||||||
|
await file.writeAsString(jsonEncode(creationTimes));
|
||||||
|
} catch (e) {
|
||||||
|
print('Error cleaning up peer creation tracking: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user