Add RawSocketBootstrap#2320
Conversation
Lukasa
left a comment
There was a problem hiding this comment.
Thanks, this looks great! I think we need a few extra tests though, as we're currently not coming close to testing all the new code that was added here.
|
|
||
| let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1) | ||
| defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } | ||
| let channel = try RawSocketBootstrap(group: elg) |
There was a problem hiding this comment.
We probably need a test of the ip_hdrincl path.
There was a problem hiding this comment.
Tested now in RawSocketBootstrapTests.testIpHdrincl
|
|
||
| let receivedMessages = Set(try channel.waitForDatagrams(count: 10).map(\.data).map { buffer in | ||
| String( | ||
| decoding: buffer.readableBytesView.dropFirst(4 * 5), // skip the IPv4 header |
There was a problem hiding this comment.
Ideally we'd actually validate that this is an IPv4 header.
There was a problem hiding this comment.
Tested in now in all tests of RawSocketBootstrapTests
0498fed to
5b2dfd6
Compare
…ctually create a socket and skipping if we fail because of `EPERM`
| let headerChecksum: UInt16 = buffer.readInteger(), | ||
| let sourceIpAddress: UInt32 = buffer.readInteger(), | ||
| let destinationIpAddress: UInt32 = buffer.readInteger() | ||
| else { return nil } |
There was a problem hiding this comment.
this is incorrect because it forgets to reset the buffer if you half-parsed something.
Also this doesn't follow the usual pattern of read*/write* methods. And lastly, there's an implementation of this already:
- check
PCAP.swifton this PR: more PCAP swift-nio-extras#158 : https://github.com/apple/swift-nio-extras/pull/158/files#diff-8b0f17fa27d694721788c7adab20151f44ff3672becc16dd728bd530c0de44ba - also here (this code is changed by the above PR): https://github.com/apple/swift-nio-extras/blob/main/Tests/NIOExtrasTests/WritePCAPHandlerTest.swift#L746
| // like we do. | ||
| XCTAssertEqual(Int(header.totalLength), data.readableBytes) | ||
| // On BSD the checksum is always zero | ||
| XCTAssertEqual(header.headerChecksum, 0) |
There was a problem hiding this comment.
I don't think we should make this platform specific. I think we should have a bsdHeaderChecksum and bsdTotalLength etc properties. And maybe maybe a headerChecksumForPlatformRawSockets or so properties that then (platform specifically) return the correct one.
There was a problem hiding this comment.
I thought about this too but couldn't come up with good names. These properties should return the correct value (as specified in RFC-791). The problem though is not only naming but also that the workaround (i.e. adding 20 to totalLength) requires knowing if we have just parsed the IPv4 packet from a ByteBuffer we got from a raw socket or not. If we create a IPv4Header ourselves e.g. for sending through the Raw Socket API, the totalLength will be correct and adding 20 will result in a wrong value.
The options I see are:
- store this information in the type and dynamically return the correct value
- This works awful if we reuse the same value to e.g. echo a packet
- use different types for receiving and sending.
- stores this information statically and we will need to explicitly convert the type when we want to echo a packet where we can get the
totalLengthfixed up
- stores this information statically and we will need to explicitly convert the type when we want to echo a packet where we can get the
- use some rather long name which conveys that this is only for the receiving side e.g.
platformIndependetTotalLengthForRecivedPacket - add some documentation and leave it to the call site to handle it
I kind of want to use the "rather long name" variant but don't yet have name I like much.
There was a problem hiding this comment.
I also don't think we need to worry too much about this, as this is testing code. It's ok if the parsing is a bit janky.
There was a problem hiding this comment.
I want to eventually make IPv4Header public API so we can still discuss this here. However, I will make this in a separate PR so it is not blocking RawSocketBootstrap to land.
There was a problem hiding this comment.
I'd go for some long names to explain this. And +1 for the public API, this should be reconciled with the already-existing IPv4/6 parsing code in swift-nio-extras.
|
|
||
| extension ByteBuffer { | ||
| @discardableResult | ||
| mutating func readIPv4Header() -> IPv4Header? { |
There was a problem hiding this comment.
As discussed offline this should IMHO really not be platform specific.
I think we should have readIPv4Header which reads it correctly. And then we can have another one which is readIPv4HeaderWithDarwinRawSocketQuirks() or something which does the weird thing.
Finally we can then have a readIPv4HeaderFromOSRawSocket() or something which chooses (in a platform specific way) the right one for the platform
But any readFoo that can't read something written by a writeFoo even if on a different platform is a major red flag
There was a problem hiding this comment.
Is there a reason someone would want readIPv4HeaderWithDarwinRawSocketQuirks on non Darwin/BSD platforms?
There was a problem hiding this comment.
Yes, unless impossible we should offer everything on every platform. There's no reason not to have it.
There was a problem hiding this comment.
There's no reason we shouldn't unit test the darwin quirks on Linux too.
| // like e.g. we do now too. | ||
| return totalLength + 20 | ||
| #elseif os(Linux) | ||
| return totalLength |
There was a problem hiding this comment.
This pull broke building the tests for Android. I just confirmed that adding Android here fixes it again, finagolfin/swift-android-sdk#83.
@dnadoba, mind adding these Android checks in a subsequent commit?
There was a problem hiding this comment.
Oh yeah sure, sorry about that. I think we can also just remove os(Linux) and just use an #else
Motivation:
We want to support custom IP protocols through POSIX Raw Socket API. To allow this, users will need to bootstrap a
DatagramChannelwith a socket in raw mode.Modifications:
RawSocketBoostrapwhich configures aDatagramChannelwith a raw socket.Result:
NIO can send and receive IP packets.
Alternatives
RawSocketBootstrapis inspired by the socket mode name (SOCK_RAW). Instead we could also call itIPBootstrapto reflect that the resulting channel will operate on IP packets.