Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add methods to create and parse cmsghdr structured data. #1590

Merged
merged 6 commits into from
Jul 14, 2020

Conversation

PeterAdams-A
Copy link
Contributor

Motivation:

cmsghdrs can be used to send an receive extra data on UDP packets.
For example ECN data.

Modifications:

Map in Linux and Darwin versions of cmsghdr macros as functions.
Create strctures for holding a received collection; parsing ecn
data from a received collection and building a collection suitable
for sending.

Result:

Functions to manipulate cmsghdr and data exist.

Motivation:

cmsghdrs can be used to send an receive extra data on UDP packets.
For example ECN data.

Modifications:

Map in Linux and Darwin versions of cmsghdr macros as functions.
Create strctures for holding a received collection; parsing ecn
data from a received collection and building a collection suitable
for sending.

Result:

Functions to manipulate cmsghdr and data exist.
@PeterAdams-A PeterAdams-A requested a review from Lukasa July 13, 2020 13:30
@PeterAdams-A
Copy link
Contributor Author

@Lukasa The obvious points for discussion are

  1. Send and receive collections are currently different - this is because I felt more confortable using the msghdr based functions when I had received them that way.
  2. No windows support - should I implement blank functions which just assert on windows?

Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

Great, looks like a good first start. Notes in the diff.

/// Unsafe as captures pointers and must not escape the scope where those pointers are valid.
struct UnsafeControlMessage {
var level: Int32
var type: Int32
Copy link
Contributor

Choose a reason for hiding this comment

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

These should probably be CInt, not Int32.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes.

/// Collection representation of `cmsghdr` structures and associated data from `recvmsg`
/// Unsafe as captures pointers held in msghdr structure which must not escape scope of validity.
struct UnsafeControlMessageCollection: Collection {
typealias Index = ControlMessageIndex
Copy link
Contributor

Choose a reason for hiding this comment

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

You can elide this typealias by renaming ControlMessageIndex to Index.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree.

data: Posix.cmsgData(for: cmsg))
}

private var messageHeader: msghdr
Copy link
Contributor

Choose a reason for hiding this comment

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

It's quite confusing to have stored properties, computed properties, and functions scattered around this type definition. Can we refactor it? Something like:

struct UnsafeControlMessageCollection {
    private var messageHeader: msghdr

    init(messageHeader: msghdr)
}

extension UnsafeControlMessageCollection: Collection {
    var startIndex: Index

    var endIndex: Index  // This should not be a stored property as it's a global static, let the compiler handle it

    func index(after:)

    subscript
}

This would much more clearly separate concerns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK


func testCMsgNextHeader() {
var exampleCmsgHrd = SystemTest.cmsghdrExample
exampleCmsgHrd.withUnsafeMutableBytes { pCmsgHdr in
Copy link
Contributor

Choose a reason for hiding this comment

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

Same mutable note here too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same reason.

let second = Posix.cmsgNextHeader(inside: pMsgHdr, from: first)
let expectedSecondSlice = UnsafeMutableRawBufferPointer(
rebasing: pCmsgHdr[SystemTest.cmsghdr_secondStartPosition...])
XCTAssertEqual(expectedSecondSlice.baseAddress, second)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not bother creating a slice if we don't need one, let's just use pCmsgHdr.baseAddress! + SystemTest.cmsghdr_secondStartPosition

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

@PeterAdams-A PeterAdams-A requested a review from Lukasa July 13, 2020 17:33
@PeterAdams-A
Copy link
Contributor Author

@Lukasa I have taken most of your suggested changes. There are a few where I have not done but have replied to your comment.

@Lukasa
Copy link
Contributor

Lukasa commented Jul 14, 2020

There are no visible comment replies @PeterAdams-A: did you submit them?

@Lukasa Lukasa added the 🔨 semver/patch No public API change. label Jul 14, 2020
@Lukasa Lukasa added this to the 2.20.0 milestone Jul 14, 2020
private static let ipv4TosType = IP_TOS // Linux
#endif

static func readCInt(data: UnsafeRawBufferPointer) -> CInt {
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefix with an underscore to indicate this is not supposed to be part of the public API of this type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK


extension CInt {
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
fileprivate static let notCapableValue = IPTOS_ECN_NOTECT
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be private.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok


struct UnsafeOutboundControlBytes {
private var controlBytes: UnsafeMutableRawBufferPointer
private var writePosition: UnsafeMutableRawBufferPointer.Index = 0
Copy link
Contributor

Choose a reason for hiding this comment

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

For the sake of keeping this as correct as possible, let's initialise this in init with self.controlBytes.startIndex.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good thinking.

cmsghdrPtr.pointee.cmsg_type = type
cmsghdrPtr.pointee.cmsg_len = .init(Posix.cmsgLen(payloadSize: MemoryLayout.size(ofValue: payload)))

let dataPointer = Posix.cmsgData(for: cmsghdrPtr)
Copy link
Contributor

Choose a reason for hiding this comment

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

We can bring the force-unwrap to here and save ourselves two more below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

/// It's assumed the caller has checked that congestion information is required before calling.
internal init(from controlMessagesReceived: UnsafeControlMessageCollection) {
var controlMessageReceiver = ControlMessageParser()
controlMessagesReceived.forEach { controlMessage in controlMessageReceiver.receiveMessage(controlMessage) }
Copy link
Contributor

Choose a reason for hiding this comment

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

for message in controlMessagesReceived {
    controlMessageReceiver.receiveMessage(message)
}

In general forEach is not really worth using: it's kinda needless sugar over the more straightforward looping operation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, should this loop move into an init on ControlMessageParser? That object can really only be used with one thing anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

@PeterAdams-A PeterAdams-A requested a review from Lukasa July 14, 2020 09:50
Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

One note, otherwise good.

var endIndex: Index { return Index(cmsgPointer: nil) }

func index(after: Index) -> Index {
precondition(after.cmsgPointer != nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

This precondition is unnecessary: we force-unwrap this pointer before its first use anyway, so the correctness of the code isn't enforced here. If you wanted to match the message below you can move the force-unwrap to this line instead, if that helps.

@PeterAdams-A PeterAdams-A merged commit f12803d into apple:master Jul 14, 2020
@PeterAdams-A PeterAdams-A deleted the cmsg_rw branch July 14, 2020 11:29
slashmo pushed a commit to slashmo/swift-nio that referenced this pull request Aug 18, 2020
Motivation:

cmsghdrs can be used to send an receive extra data on UDP packets.
For example ECN data.

Modifications:

Map in Linux and Darwin versions of cmsghdr macros as functions.
Create strctures for holding a received collection; parsing ecn
data from a received collection and building a collection suitable
for sending.

Result:

Functions to manipulate cmsghdr and data exist.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants