Skip to content
Open
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
112 changes: 109 additions & 3 deletions Sources/SwiftNetwork/Protocols/Frame.swift
Original file line number Diff line number Diff line change
Expand Up @@ -507,13 +507,35 @@ public struct Frame: ~Copyable {
static let isChecksumIPChecked = Flags(rawValue: 1 << 1)
static let isChecksumIPValid = Flags(rawValue: 1 << 2)
static let fragmentationOverride = Flags(rawValue: 1 << 3)
// RX: the hardware validated the transport (TCP/UDP) checksum.
static let isChecksumDataValid = Flags(rawValue: 1 << 4)
// RX: the validated checksum value already folds in the pseudo-header.
static let isChecksumPseudoHeader = Flags(rawValue: 1 << 5)
// TX: the TSO segment size below describes an IPv6 super-packet.
static let isTSOIPv6 = Flags(rawValue: 1 << 6)
}
var flags: Flags = Flags()
var serviceClass = Parameters.ServiceClass.bestEffort
var ecnFlag: IPProtocol.ECN = .nonECT
var dscpValue: UInt8?
var hopLimit: UInt8 = 0
var checksumOffloadFlags: UInt8 = 0

// TX partial-checksum offload: byte offset (from the packet start / IP header)
// where the NIC begins the 1's-complement sum, and where it stores the result.
// Zero for the IP-header-only case, which the NIC locates itself.
var checksumStartOffset: UInt16 = 0
var checksumStoreOffset: UInt16 = 0

// TX segmentation offload (TSO): the per-segment TCP payload size the
// hardware should cut this frame into, or 0 for an ordinary single-segment
// frame. A non-zero value means the frame's payload deliberately exceeds
// the path MTU.
var tsoSegmentSize: UInt16 = 0

// RX: the final/partial transport checksum value the hardware reported.
var hardwareChecksumValue: UInt16 = 0

var departureTime: UInt64 = 0 // departure time at which kernel should send the packet, used for kernel pacing
var isLastPacket: Bool {
get { flags.contains(.isLastPacket) }
Expand All @@ -527,6 +549,14 @@ public struct Frame: ~Copyable {
get { flags.contains(.isChecksumIPValid) }
set { if newValue { flags.insert(.isChecksumIPValid) } else { flags.remove(.isChecksumIPValid) } }
}
var isChecksumDataValid: Bool {
get { flags.contains(.isChecksumDataValid) }
set { if newValue { flags.insert(.isChecksumDataValid) } else { flags.remove(.isChecksumDataValid) } }
}
var isChecksumPseudoHeader: Bool {
get { flags.contains(.isChecksumPseudoHeader) }
set { if newValue { flags.insert(.isChecksumPseudoHeader) } else { flags.remove(.isChecksumPseudoHeader) } }
}
var fragmentationOverride: Bool? {
get { flags.contains(.fragmentationOverride) ? true : nil }
set {
Expand Down Expand Up @@ -581,14 +611,14 @@ public struct Frame: ~Copyable {

var packetChainTotalLength: Int {
get {
guard !isSingleIPAggregate else {
guard isSingleIPAggregate else {
Logger.proto.fault("Attempt to get aggregate buffer length on a non-single IP aggregate")
return 0
}
return aggregateBufferLength
}
set {
guard !isSingleIPAggregate else {
guard isSingleIPAggregate else {
Logger.proto.fault("Attempt to get aggregate buffer length on a non-single IP aggregate")
return
}
Expand Down Expand Up @@ -697,6 +727,82 @@ public struct Frame: ~Copyable {
}
}

var isChecksumDataValid: Bool {
get { ipPacketValues?.isChecksumDataValid ?? false }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
ipPacketValues!.isChecksumDataValid = newValue
}
}

var isChecksumPseudoHeader: Bool {
get { ipPacketValues?.isChecksumPseudoHeader ?? false }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
ipPacketValues!.isChecksumPseudoHeader = newValue
}
}

var checksumStartOffset: UInt16 {
get { ipPacketValues?.checksumStartOffset ?? 0 }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
ipPacketValues!.checksumStartOffset = newValue
}
}

var checksumStoreOffset: UInt16 {
get { ipPacketValues?.checksumStoreOffset ?? 0 }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
ipPacketValues!.checksumStoreOffset = newValue
}
}

var hardwareChecksumValue: UInt16 {
get { ipPacketValues?.hardwareChecksumValue ?? 0 }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
ipPacketValues!.hardwareChecksumValue = newValue
}
}

// Per-segment TCP payload size for a TSO super-packet, 0 when this frame is an
// ordinary single-segment frame.
var tsoSegmentSize: UInt16 {
get { ipPacketValues?.tsoSegmentSize ?? 0 }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
ipPacketValues!.tsoSegmentSize = newValue
}
}

var isTSOIPv6: Bool {
get { ipPacketValues?.flags.contains(.isTSOIPv6) ?? false }
set {
if ipPacketValues == nil {
ipPacketValues = IPPacketValues()
}
if newValue {
ipPacketValues!.flags.insert(.isTSOIPv6)
} else {
ipPacketValues!.flags.remove(.isTSOIPv6)
}
}
}

struct FrameProtocolMetadata: ~Copyable {
var uuid: SystemUUID
var metadata: AbstractProtocolMetadata
Expand Down Expand Up @@ -726,7 +832,7 @@ public struct Frame: ~Copyable {

mutating func reduceAggregateBufferLength(by length: Int) {
if isSingleIPAggregate {
guard aggregateBufferLength < length else {
guard length <= aggregateBufferLength else {
let existingLength = aggregateBufferLength
Logger.proto.fault("Aggregate buffer length \(existingLength) cannot remove \(length)")
aggregateBufferLength = 0
Expand Down
4 changes: 2 additions & 2 deletions Tests/SwiftNetworkTests/SwiftNetworkFrameTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import XCTest
@available(anyAppleOS 27, *)
final class SwiftNetworkFrameTests: XCTestCase {
func testFrameLayout() {
XCTAssertEqual(MemoryLayout<Frame>.size, 136)
XCTAssertEqual(MemoryLayout<Frame>.stride, 136)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to find a way to not increase the size for most frames, but I don't see a great option at this point.

@rpaulo rpaulo Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a problem for embedded, maybe we could not support checksum offloading there and make this structure smaller?

XCTAssertEqual(MemoryLayout<Frame>.size, 144)
XCTAssertEqual(MemoryLayout<Frame>.stride, 144)
}
}
Loading