Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Sources/SwiftNetwork/Protocols/Checksum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ enum ChecksumError: Error {
case invalidBuffer
}

struct ChecksumFlags: OptionSet {
let rawValue: UInt8
static let partial = ChecksumFlags(rawValue: 0x01)
static let zeroInvert = ChecksumFlags(rawValue: 0x02)
static let ip = ChecksumFlags(rawValue: 0x04)
static let tcpIPv4 = ChecksumFlags(rawValue: 0x08)
static let udpIPv4 = ChecksumFlags(rawValue: 0x10)
static let tcpIPv6 = ChecksumFlags(rawValue: 0x20)
static let udpIPv6 = ChecksumFlags(rawValue: 0x40)
}

struct InterfaceChecksumFlags: OptionSet {
let rawValue: UInt32
static let udpIPv4 = InterfaceChecksumFlags(rawValue: 0x0000_0004)
static let udpIPv6 = InterfaceChecksumFlags(rawValue: 0x0000_0040)
}

@available(Network 0.1.0, *)
extension IPv6Address {
func checksum() -> UInt32 {
Expand Down Expand Up @@ -141,6 +158,11 @@ extension Frame {
return ((~value) & 0xffff)
}

mutating func setInternetChecksum(flags: ChecksumFlags, startOffset: UInt16, checksumOffset: UInt16) -> Bool {
checksumOffloadFlags |= flags.rawValue
return false
}

mutating func finalizeIPChecksum(checksumOffset: Int, zeroInvert: Bool) throws(ChecksumError) {
let unclaimedLength = self.unclaimedLength
if unclaimedLength == 0 {
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftNetwork/Protocols/HarnessProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ public class UpperHarness<LinkageType: InboundDataLinkage>: UpperHarnessProtocol
return invokeGetMetadata() as? ProtocolMetadata<P>
}

final public func getMetrics(requestedNetworkMetric: RequestedNetworkMetrics) -> NetworkMetrics? {
lower.invokeGetMetrics(reference, requestedNetworkMetric: requestedNetworkMetric)
}

public func setApplicationError(_ applicationError: UInt64, applicationErrorReason: String) {
if let metadata: ProtocolMetadata<QUICProtocol> = self.getMetadata() {
metadata.perProtocolMetadata?.quicConnectionMetadata?.applicationError = applicationError
Expand Down
92 changes: 77 additions & 15 deletions Sources/SwiftNetwork/Protocols/UDPProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public struct UDPProtocol: NetworkProtocol {
static public let noMetadata = UDPOptions(rawValue: 1 << 1)
static public let ignoreInboundChecksum = UDPOptions(rawValue: 1 << 2)
static public let useQUICStats = UDPOptions(rawValue: 1 << 3)
static public let fullChecksumOffload = UDPOptions(rawValue: 1 << 4)

#if NETWORK_PRIVATE
var privateStorage = UDPProtocolOptionsPrivateStorage()
Expand Down Expand Up @@ -97,7 +98,9 @@ public struct UDPProtocol: NetworkProtocol {
var eventManager = ProtocolEventManager()

// Only called by newProtocolInstance()
fileprivate static func registerNewUDP(on context: NetworkContext) -> ProtocolInstanceReference {
fileprivate static func registerNewUDP(
on context: NetworkContext,
) -> ProtocolInstanceReference {
let udp = UDPInstance(context: context)
let registeredIndex = context.registerUDPInstance(udp)
context.udpInstances[registeredIndex].udpInstanceIndex = registeredIndex
Expand All @@ -121,6 +124,9 @@ public struct UDPProtocol: NetworkProtocol {

var serviceClass = Parameters.ServiceClass.bestEffort
var maximumDatagramSize: Int = 0
var isIPv4: Bool {
flags.contains(.isIPv4)
}

struct Flags: OptionSet {
init(rawValue: Self.RawValue) {
Expand Down Expand Up @@ -153,6 +159,13 @@ public struct UDPProtocol: NetworkProtocol {
if self.maximumDatagramSize > UDPProtocol.headerLength {
self.maximumDatagramSize -= UDPProtocol.headerLength
}
let udpChecksumOffload: UInt32 = path.hardwareChecksumFlags
if isIPv4 && ((udpChecksumOffload & InterfaceChecksumFlags.udpIPv4.rawValue) != 0)
|| !isIPv4 && ((udpChecksumOffload & InterfaceChecksumFlags.udpIPv6.rawValue) != 0)
{
self.flags.insert(.fullChecksumOffload)
self.flags.remove(.partialChecksumOffload)
}
}

mutating func setup(
Expand Down Expand Up @@ -202,6 +215,21 @@ public struct UDPProtocol: NetworkProtocol {
if udpOptions.ignoreInboundChecksum {
self.flags.insert(.ignoreInboundChecksum)
}
if udpOptions.fullChecksumOffload {
self.flags.insert(.fullChecksumOffload)
}
#if !NETWORK_EMBEDDED
if let transport = parameters.defaultStack.transport {
if transport.options == udpOptions, udpOptions.useQUICStats {
self.flags.insert(.upperTransportIsQUIC)
} else if let quicOptions = transport.options,
quicOptions.matches(identifier: QUICStreamProtocol.identifier)
|| quicOptions.matches(identifier: QUICConnectionProtocol.identifier)
{
self.flags.insert(.upperTransportIsQUIC)
}
}
#endif
}
}

Expand All @@ -213,7 +241,7 @@ public struct UDPProtocol: NetworkProtocol {
let inbound = (inboundChecksum != nil)
let existingChecksum: UInt16 = inboundChecksum ?? 0
var checksumValue: UInt16 = 0
if self.flags.contains(.isIPv4) {
if isIPv4 {
checksumValue = Checksum.ipv4PseudoHeader(
source: inbound ? ipv4Remote : ipv4Local,
dest: inbound ? ipv4Local : ipv4Remote,
Expand Down Expand Up @@ -295,7 +323,7 @@ public struct UDPProtocol: NetworkProtocol {
return .removeFrameAndContinue
}

guard self.flags.contains(.isIPv4) || checksum != 0 else {
guard isIPv4 || checksum != 0 else {
log.error("Received an IPv6 packet with zero checksum")
frame.finalize(success: false)
return .removeFrameAndContinue
Expand Down Expand Up @@ -391,7 +419,7 @@ public struct UDPProtocol: NetworkProtocol {
frame.serviceClass = self.serviceClass
}

if !self.flags.contains(.isIPv4) || !self.flags.contains(.noChecksum) {
if !isIPv4 || !self.flags.contains(.noChecksum) {
// Always insert pseudo header checksum
let checksumValue = pseudoHeaderChecksum(inboundChecksum: nil, length: length)

Expand All @@ -407,23 +435,41 @@ public struct UDPProtocol: NetworkProtocol {
return .removeFrameAndContinue
}

let finalizedChecksum = false
if self.flags.contains(.fullChecksumOffload) {
// TODO: Checksum offload
} else if self.flags.contains(.partialChecksumOffload) {
// TODO: Checksum offload
let csumFlags: ChecksumFlags =
isIPv4
? [.udpIPv4, .zeroInvert]
: [.udpIPv6, .zeroInvert]
frame.checksumOffloadFlags |= csumFlags.rawValue
}

if !finalizedChecksum {
do throws(ChecksumError) {
try frame.finalizeIPChecksum(checksumOffset: checksumOffset, zeroInvert: true)
} catch {
log.error("Failed to finalize UDP checksum")
frame.finalize(success: false)
return .removeFrameAndContinue
if !self.flags.contains(.fullChecksumOffload) {
var checksumOffloadDisabled = !self.flags.contains(.partialChecksumOffload)
if !checksumOffloadDisabled {
let csumStart = UInt16(isIPv4 ? IPProtocol.ipv4HeaderLength : IPProtocol.ipv6HeaderLength)
let csumWrite = csumStart + UInt16(checksumOffset)
let csumFlags: ChecksumFlags = [.partial, .zeroInvert]
if !frame.setInternetChecksum(
flags: csumFlags,
startOffset: csumStart,
checksumOffset: csumWrite
) {
checksumOffloadDisabled = true
}
}

if checksumOffloadDisabled {
do throws(ChecksumError) {
try frame.finalizeIPChecksum(checksumOffset: checksumOffset, zeroInvert: true)
} catch {
log.error("Failed to finalize UDP checksum")
frame.finalize(success: false)
return .removeFrameAndContinue
}
}
}
}
transmitByteCount += length - UDPProtocol.headerLength

return .continueIterating
}
Expand All @@ -434,6 +480,11 @@ public struct UDPProtocol: NetworkProtocol {
#if !NETWORK_EMBEDDED
var metadata: AbstractProtocolMetadata? { nil }
#endif

func updateDataTransferSnapshot(_ snapshot: inout DataTransferSnapshot) {
snapshot.receivedTransportByteCount = UInt64(receiveByteCount)
snapshot.sentTransportByteCount = UInt64(transmitByteCount)
}
}

public init() {}
Expand Down Expand Up @@ -506,4 +557,15 @@ extension ProtocolOptions<UDPProtocol> {
}
}
}

public var fullChecksumOffload: Bool {
get { perProtocolOptions!.contains(.fullChecksumOffload) }
set {
if newValue {
perProtocolOptions!.insert(.fullChecksumOffload)
} else {
perProtocolOptions!.remove(.fullChecksumOffload)
}
}
}
}
12 changes: 8 additions & 4 deletions Tests/SwiftNetworkTests/SwiftNetworkMultiplexingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import XCTest
@available(Network 0.1.0, *)
final class SwiftNetworkMultiplexingTests: NetTestCase {

// 10.0.0.20
static let localIPv4Address: [UInt8] = [0x0a, 0x00, 0x00, 0x14]
// 10.0.0.117
static let remoteIPv4Address: [UInt8] = [0x0a, 0x00, 0x00, 0x75]
static let outputMessage: [UInt8] = [0x0a, 0x0b, 0x0c, 0x0d]
static let inputMessage: [UInt8] = [0x0d, 0x0c, 0x0b, 0x0a, 0x01]

Expand All @@ -33,8 +37,8 @@ final class SwiftNetworkMultiplexingTests: NetTestCase {
let context = parameters.context
let path = PathProperties(parameters: parameters)

let localEndpoint = Endpoint(address: IPv4Address(SwiftNetworkUDPTests.localIPv4Address)!, port: 1234)
let remoteEndpoint = Endpoint(address: IPv4Address(SwiftNetworkUDPTests.localIPv4Address)!, port: 2345)
let localEndpoint = Endpoint(address: IPv4Address(SwiftNetworkMultiplexingTests.localIPv4Address)!, port: 1234)
let remoteEndpoint = Endpoint(address: IPv4Address(SwiftNetworkMultiplexingTests.localIPv4Address)!, port: 2345)

var instance: TestMultiplexingProtocol? = nil
var upperHarness1: DatagramUpperHarness?
Expand Down Expand Up @@ -217,8 +221,8 @@ final class SwiftNetworkMultiplexingTests: NetTestCase {
let context = parameters.context
let path = PathProperties(parameters: parameters)

let localEndpoint = Endpoint(address: IPv4Address(SwiftNetworkUDPTests.localIPv4Address)!, port: 1234)
let remoteEndpoint = Endpoint(address: IPv4Address(SwiftNetworkUDPTests.localIPv4Address)!, port: 2345)
let localEndpoint = Endpoint(address: IPv4Address(SwiftNetworkMultiplexingTests.localIPv4Address)!, port: 1234)
let remoteEndpoint = Endpoint(address: IPv4Address(SwiftNetworkMultiplexingTests.localIPv4Address)!, port: 2345)

// Use a high number of streams to ensure that we don't have poor scaling
let upperHarnessCount = 1000
Expand Down
Loading
Loading