fix ip packet

This commit is contained in:
anlicheng 2026-03-10 13:35:45 +08:00
parent 702eb1e608
commit 4cccd411e0
4 changed files with 252 additions and 126 deletions

View File

@ -566,22 +566,23 @@ actor SDLContextActor {
//
let identitySnapshot = self.snapshotPublisher.current()
if let ruleMap = identitySnapshot.lookup(data.identityID) {
SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), ruleMap: \(ruleMap)", level: .debug)
let proto = ipPacket.header.proto
switch TransportProtocol(rawValue: proto) {
case .udp, .tcp:
if let dstPort = ipPacket.getDstPort(), ruleMap.isAllow(proto: proto, port: dstPort) {
switch ipPacket.transportPacket() {
case .tcp(let tcpPacket):
let dstPort = tcpPacket.header.dstPort
if ruleMap.isAllow(proto: proto, port: dstPort) {
let packet = NEPacket(data: ipPacket.data, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([packet])
} else {
if let dstPort = ipPacket.getDstPort() {
SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), dst port: \(dstPort) not allow", level: .debug)
} else {
SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), invalid ip packet, not allow", level: .debug)
}
SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), ruleMap: \(ruleMap), dstPort: \(dstPort) allow", level: .debug)
}
case .icmp:
case .udp(let udpPacket):
let dstPort = udpPacket.dstPort
if ruleMap.isAllow(proto: proto, port: dstPort) {
let packet = NEPacket(data: ipPacket.data, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([packet])
SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), ruleMap: \(ruleMap), dstPort: \(dstPort) allow", level: .debug)
}
case .icmp(_):
let packet = NEPacket(data: ipPacket.data, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([packet])
default:
@ -638,8 +639,6 @@ actor SDLContextActor {
let networkAddr = self.config.networkAddress
if SDLDNSClient.Helper.isDnsRequestPacket(ipPacket: packet) {
let destIp = packet.header.destination_ip
SDLLogger.shared.log("[DNSQuery] destIp: \(destIp), int: \(packet.header.destination.asIpAddress())", level: .debug)
self.dnsClient?.forward(ipPacket: packet)
return
}

View File

@ -7,35 +7,6 @@
import Foundation
struct IPHeader {
let version: UInt8
let headerLength: UInt8
let typeOfService: UInt8
let totalLength: UInt16
let id: UInt16
let offset: UInt16
let timeToLive: UInt8
let proto: UInt8
let checksum: UInt16
let source: UInt32
let destination: UInt32
var source_ip: String {
return SDLUtil.int32ToIp(source)
}
var destination_ip: String {
return SDLUtil.int32ToIp(destination)
}
public var description: String {
"""
IPHeader version: \(version), header length: \(headerLength), type of service: \(typeOfService), total length: \(totalLength),
id: \(id), offset: \(offset), time ot live: \(timeToLive), proto: \(proto), checksum: \(checksum), source ip: \(source_ip), destination ip:\(destination_ip)
"""
}
}
enum IPVersion: UInt8 {
case ipv4 = 4
case ipv6 = 6
@ -43,46 +14,236 @@ enum IPVersion: UInt8 {
enum TransportProtocol: UInt8 {
case icmp = 1
case tcp = 6
case udp = 17
case tcp = 6
case udp = 17
}
// MARK: - IP Header
struct IPHeader {
let version: UInt8
let headerLength: UInt8
let typeOfService: UInt8
let totalLength: UInt16
let id: UInt16
let offset: UInt16
let ttl: UInt8
let proto: UInt8
let checksum: UInt16
let source: UInt32
let destination: UInt32
var headerBytes: Int {
Int(headerLength)
}
}
// MARK: - IP Packet
struct IPPacket {
let header: IPHeader
let data: Data
var payload: Data.SubSequence {
let offset = Int(header.headerLength)
return data[offset..<data.count]
}
init?(_ data: Data) {
guard data.count >= 20 else {
return nil
}
self.header = IPHeader(version: data[0] >> 4,
headerLength: (data[0] & 0b1111) * 4,
typeOfService: data[1],
totalLength: UInt16(bytes: (data[2], data[3])),
id: UInt16(bytes: (data[4], data[5])),
offset: 1,
timeToLive: data[8],
proto: data[9],
checksum: UInt16(bytes: (data[10], data[11])),
source: UInt32(bytes: (data[12], data[13], data[14], data[15])),
destination: UInt32(bytes: (data[16], data[17], data[18], data[19])))
self.data = data
}
//
func getPayload() -> Data {
return data.subdata(in: 20..<data.count)
}
// ip
func getDstPort() -> UInt16? {
guard case .ipv4 = IPVersion(rawValue: self.header.version), self.data.count >= 24 else {
let version = data[0] >> 4
let headerLen = (data[0] & 0x0F) * 4
guard data.count >= headerLen else {
return nil
}
self.header = IPHeader(
version: version,
headerLength: headerLen,
typeOfService: data[1],
totalLength: UInt16(bytes: (data[2], data[3])),
id: UInt16(bytes: (data[4], data[5])),
offset: UInt16(bytes: (data[6], data[7])),
ttl: data[8],
proto: data[9],
checksum: UInt16(bytes: (data[10], data[11])),
source: UInt32(bytes: (data[12], data[13], data[14], data[15])),
destination: UInt32(bytes: (data[16], data[17], data[18], data[19]))
)
self.data = data
}
}
// MARK: - TCP Flags
struct TCPFlags: OptionSet {
let rawValue: UInt16
static let fin = TCPFlags(rawValue: 1 << 0)
static let syn = TCPFlags(rawValue: 1 << 1)
static let rst = TCPFlags(rawValue: 1 << 2)
static let psh = TCPFlags(rawValue: 1 << 3)
static let ack = TCPFlags(rawValue: 1 << 4)
static let urg = TCPFlags(rawValue: 1 << 5)
static let ece = TCPFlags(rawValue: 1 << 6)
static let cwr = TCPFlags(rawValue: 1 << 7)
}
// MARK: - TCP Header
struct TCPHeader {
let srcPort: UInt16
let dstPort: UInt16
let seq: UInt32
let ack: UInt32
let dataOffset: UInt8
let flags: TCPFlags
let window: UInt16
let checksum: UInt16
let urgentPointer: UInt16
var headerLength: Int {
Int(dataOffset) * 4
}
}
// MARK: - TCP Packet
struct TCPPacket {
let header: TCPHeader
let payload: Data
init?(_ data: Data) {
guard data.count >= 20 else {
return nil
}
let srcPort = UInt16(bytes: (data[0], data[1]))
let dstPort = UInt16(bytes: (data[2], data[3]))
let seq = UInt32(bytes: (data[4], data[5], data[6], data[7]))
let ack = UInt32(bytes: (data[8], data[9], data[10], data[11]))
let offsetAndFlags = UInt16(bytes: (data[12], data[13]))
let dataOffset = UInt8(offsetAndFlags >> 12)
let flags = TCPFlags(rawValue: offsetAndFlags & 0x01FF)
let window = UInt16(bytes: (data[14], data[15]))
let checksum = UInt16(bytes: (data[16], data[17]))
let urgent = UInt16(bytes: (data[18], data[19]))
let header = TCPHeader(
srcPort: srcPort,
dstPort: dstPort,
seq: seq,
ack: ack,
dataOffset: dataOffset,
flags: flags,
window: window,
checksum: checksum,
urgentPointer: urgent
)
let headerLen = header.headerLength
guard data.count >= headerLen else {
return nil
}
self.header = header
self.payload = data.subdata(in: headerLen..<data.count)
}
}
// MARK: - UDP Packet
struct UDPPacket {
let srcPort: UInt16
let dstPort: UInt16
let length: UInt16
let checksum: UInt16
let payload: Data
init?(_ data: Data) {
guard data.count >= 8 else {
return nil
}
self.srcPort = UInt16(bytes: (data[0], data[1]))
self.dstPort = UInt16(bytes: (data[2], data[3]))
self.length = UInt16(bytes: (data[4], data[5]))
self.checksum = UInt16(bytes: (data[6], data[7]))
self.payload = data.subdata(in: 8..<data.count)
}
}
// MARK: - ICMP Packet
struct ICMPPacket {
let type: UInt8
let code: UInt8
let checksum: UInt16
let payload: Data
init?(_ data: Data) {
guard data.count >= 4 else {
return nil
}
self.type = data[0]
self.code = data[1]
self.checksum = UInt16(bytes: (data[2], data[3]))
self.payload = data.subdata(in: 4..<data.count)
}
}
// MARK: - IPPacket Transport Parsing
extension IPPacket {
enum TransportPacket {
case tcp(TCPPacket)
case udp(UDPPacket)
case icmp(ICMPPacket)
case unsupported(UInt8)
case malformed
}
func transportPacket() -> TransportPacket {
guard let proto = TransportProtocol(rawValue: header.proto) else {
return .unsupported(header.proto)
}
// ipv4(srcPort:16, dstPort:16, ...)
return UInt16(bytes: (self.data[22], self.data[23]))
switch proto {
case .tcp:
guard let tcp = TCPPacket(payload) else {
return .malformed
}
return .tcp(tcp)
case .udp:
guard let udp = UDPPacket(payload) else {
return .malformed
}
return .udp(udp)
case .icmp:
guard let icmp = ICMPPacket(payload) else {
return .malformed
}
return .icmp(icmp)
}
}
}

View File

@ -1,38 +0,0 @@
//
// UDPPacket.swift
// Tun
//
// Created by on 2025/12/13.
//
import Foundation
struct UDPHeader {
let sourcePort: UInt16
let destinationPort: UInt16
let length: UInt16
let checksum: UInt16
}
struct UDPPacket {
let header: UDPHeader
let payload: Data
init?(_ data: Data) {
// UDP header 8
guard data.count >= 8 else {
return nil
}
let header = UDPHeader(sourcePort: UInt16(bytes: (data[0], data[1])),
destinationPort: UInt16(bytes: (data[2], data[3])),
length: UInt16(bytes: (data[4], data[5])),
checksum: UInt16(bytes: (data[6], data[7]))
)
// UDP payload = length - 8
let payloadLength = Int(header.length) - 8
self.header = header
self.payload = data.subdata(in: 8..<(8 + payloadLength))
}
}

View File

@ -50,22 +50,22 @@ struct punchnetApp: App {
RootView()
.frame(width: 800, height: 500)
.onAppear {
//
guard let screenFrame = NSScreen.main?.frame else {
return
}
//
NSApplication.shared.windows.forEach { window in
//
let windowWidth = window.frame.width
let windowHeight = window.frame.height
let centerX = (screenFrame.width - windowWidth) / 2
let centerY = (screenFrame.height - windowHeight) / 2
//
window.setFrameOrigin(NSPoint(x: centerX, y: centerY))
}
// //
// guard let screenFrame = NSScreen.main?.frame else {
// return
// }
//
// //
// NSApplication.shared.windows.forEach { window in
// //
// let windowWidth = window.frame.width
// let windowHeight = window.frame.height
// let centerX = (screenFrame.width - windowWidth) / 2
// let centerY = (screenFrame.height - windowHeight) / 2
//
// //
// window.setFrameOrigin(NSPoint(x: centerX, y: centerY))
// }
}
//.toolbar(.hidden)
.navigationTitle("")
@ -83,25 +83,29 @@ struct punchnetApp: App {
}
.windowResizability(.contentSize)
.windowToolbarStyle(.unified)
.defaultPosition(.center)
Window("设置", id: "settings") {
SettingsView()
.environment(self.userContext)
}
.defaultSize(width: 800, height: 500)
.defaultPosition(.center)
Window("重置密码", id: "resetPassword") {
ResetPasswordRootView()
.environment(self.userContext)
}
.defaultSize(width: 800, height: 500)
.defaultPosition(.center)
Window("注册", id: "register") {
ResetPasswordRootView()
.environment(self.userContext)
}
.defaultSize(width: 800, height: 500)
.defaultPosition(.center)
MenuBarExtra("punchnet", image: "logo_32") {
VStack {
Button(action: {