mirror of
https://github.com/apple/container.git
synced 2026-09-21 07:05:57 +00:00
Make MTU configurable for Netlink (#204)
Here we add the implementation to make MTU value configurable instead of hardcoding in `linkSet`.
This commit is contained in:
@@ -198,10 +198,11 @@ extension Vminitd: VirtualMachineAgent {
|
||||
_ = try await client.deleteProcess(request)
|
||||
}
|
||||
|
||||
public func up(name: String) async throws {
|
||||
public func up(name: String, mtu: UInt32? = nil) async throws {
|
||||
let request = Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest.with {
|
||||
$0.interface = name
|
||||
$0.up = true
|
||||
if let mtu { $0.mtu = mtu }
|
||||
}
|
||||
_ = try await client.ipLinkSet(request)
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ extension LinuxContainer {
|
||||
for (index, i) in self.interfaces.enumerated() {
|
||||
let name = "eth\(index)"
|
||||
try await agent.addressAdd(name: name, address: i.address)
|
||||
try await agent.up(name: name)
|
||||
try await agent.up(name: name, mtu: 1280)
|
||||
if let gateway = i.gateway {
|
||||
try await agent.routeAddDefault(name: name, gateway: gateway)
|
||||
}
|
||||
|
||||
@@ -658,9 +658,20 @@ public struct Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: Sendable {
|
||||
|
||||
public var up: Bool = false
|
||||
|
||||
public var mtu: UInt32 {
|
||||
get {return _mtu ?? 0}
|
||||
set {_mtu = newValue}
|
||||
}
|
||||
/// Returns true if `mtu` has been explicitly set.
|
||||
public var hasMtu: Bool {return self._mtu != nil}
|
||||
/// Clears the value of `mtu`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearMtu() {self._mtu = nil}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
||||
fileprivate var _mtu: UInt32? = nil
|
||||
}
|
||||
|
||||
public struct Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse: Sendable {
|
||||
@@ -1992,6 +2003,7 @@ extension Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: SwiftProtobuf.
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "interface"),
|
||||
2: .same(proto: "up"),
|
||||
3: .same(proto: "mtu"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
@@ -2002,24 +2014,33 @@ extension Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: SwiftProtobuf.
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()
|
||||
case 2: try { try decoder.decodeSingularBoolField(value: &self.up) }()
|
||||
case 3: try { try decoder.decodeSingularUInt32Field(value: &self._mtu) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
if !self.interface.isEmpty {
|
||||
try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)
|
||||
}
|
||||
if self.up != false {
|
||||
try visitor.visitSingularBoolField(value: self.up, fieldNumber: 2)
|
||||
}
|
||||
try { if let v = self._mtu {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, rhs: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest) -> Bool {
|
||||
if lhs.interface != rhs.interface {return false}
|
||||
if lhs.up != rhs.up {return false}
|
||||
if lhs._mtu != rhs._mtu {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ message MkdirResponse {}
|
||||
message IpLinkSetRequest {
|
||||
string interface = 1;
|
||||
bool up = 2;
|
||||
optional uint32 mtu = 3;
|
||||
}
|
||||
|
||||
message IpLinkSetResponse {}
|
||||
|
||||
@@ -53,7 +53,7 @@ public protocol VirtualMachineAgent: Sendable {
|
||||
func deleteProcess(id: String, containerID: String?) async throws
|
||||
|
||||
// Networking
|
||||
func up(name: String) async throws
|
||||
func up(name: String, mtu: UInt32?) async throws
|
||||
func down(name: String) async throws
|
||||
func addressAdd(name: String, address: String) async throws
|
||||
func routeAddDefault(name: String, gateway: String) async throws
|
||||
|
||||
@@ -67,12 +67,17 @@ public struct NetlinkSession {
|
||||
/// - Parameters:
|
||||
/// - interface: The name of the interface.
|
||||
/// - up: The value to set the interface state to.
|
||||
public func linkSet(interface: String, up: Bool) throws {
|
||||
public func linkSet(interface: String, up: Bool, mtu: UInt32? = nil) throws {
|
||||
// ip link set dev [interface] [up|down]
|
||||
let interfaceIndex = try getInterfaceIndex(interface)
|
||||
let mtuAttr = RTAttribute(
|
||||
len: UInt16(RTAttribute.size + MemoryLayout<UInt32>.size), type: LinkAttributeType.IFLA_MTU)
|
||||
let requestSize = NetlinkMessageHeader.size + InterfaceInfo.size + mtuAttr.paddedLen
|
||||
// build the attribute only when mtu is supplied
|
||||
let attr: RTAttribute? =
|
||||
(mtu != nil)
|
||||
? RTAttribute(
|
||||
len: UInt16(RTAttribute.size + MemoryLayout<UInt32>.size),
|
||||
type: LinkAttributeType.IFLA_MTU)
|
||||
: nil
|
||||
let requestSize = NetlinkMessageHeader.size + InterfaceInfo.size + (attr?.paddedLen ?? 0)
|
||||
var requestBuffer = [UInt8](repeating: 0, count: requestSize)
|
||||
var requestOffset = 0
|
||||
|
||||
@@ -91,16 +96,16 @@ public struct NetlinkSession {
|
||||
change: InterfaceFlags.DEFAULT_CHANGE)
|
||||
requestOffset = try requestInfo.appendBuffer(&requestBuffer, offset: requestOffset)
|
||||
|
||||
requestOffset = try mtuAttr.appendBuffer(&requestBuffer, offset: requestOffset)
|
||||
guard
|
||||
let newRequestOffset = requestBuffer.copyIn(
|
||||
as: UInt32.self,
|
||||
value: Self.mtu,
|
||||
offset: requestOffset)
|
||||
else {
|
||||
throw NetlinkDataError.sendMarshalFailure
|
||||
if let attr = attr, let m = mtu {
|
||||
requestOffset = try attr.appendBuffer(&requestBuffer, offset: requestOffset)
|
||||
guard
|
||||
let newRequestOffset =
|
||||
requestBuffer.copyIn(as: UInt32.self, value: m, offset: requestOffset)
|
||||
else {
|
||||
throw NetlinkDataError.sendMarshalFailure
|
||||
}
|
||||
requestOffset = newRequestOffset
|
||||
}
|
||||
requestOffset = newRequestOffset
|
||||
|
||||
guard requestOffset == requestSize else {
|
||||
throw Error.unexpectedOffset(offset: requestOffset, size: requestSize)
|
||||
|
||||
@@ -26,23 +26,29 @@ struct NetlinkSessionTest {
|
||||
|
||||
// Lookup interface by name, truncated response with no attributes (not needed at present).
|
||||
let expectedLookupRequest =
|
||||
"3400000012000100000000000cc00cc0110000000000000001000000ffffffff08001d00090000000c0003006574683000000000"
|
||||
mockSocket.responses.append([
|
||||
0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x0c, 0xc0,
|
||||
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
"3400000012000100000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d00090000000c0003006574683000000000" // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2000000010000000000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000100020000004310010000000000" // struct ifinfomsg (16 B) – no RT attrs
|
||||
)
|
||||
)
|
||||
|
||||
// Network down for interface.
|
||||
let expectedDownRequest = "2800000010000500000000000cc00cc0110000000200000000000000ffffffff0800040000050000"
|
||||
mockSocket.responses.append([
|
||||
0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x0c, 0xc0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x00, 0x00, 0x00,
|
||||
])
|
||||
// Link‑down request – 32‑byte payload, no attributes.
|
||||
let expectedDownRequest =
|
||||
"2000000010000500000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "110000000200000000000000ffffffff" // struct ifinfomsg (16 B) – no RT attrs
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2400000002000001000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000000200000001000050000000000" // nlmsg_err payload (16 B)
|
||||
+ "0c000000" // first 4 B of echoed header
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
try session.linkSet(interface: "eth0", up: false)
|
||||
@@ -61,26 +67,74 @@ struct NetlinkSessionTest {
|
||||
|
||||
// Lookup interface by name, truncated response with no attributes (not needed at present).
|
||||
let expectedLookupRequest =
|
||||
"340000001200010000000000c00cc00c110000000000000001000000ffffffff08001d00090000000c0003006574683000000000"
|
||||
mockSocket.responses.append([
|
||||
0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xc0, 0x0c, 0xc0, 0x0c,
|
||||
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
"340000001200010000000000c00cc00c" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d00090000000c0003006574683000000000" // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"200000001000000000000000c00cc00c" // Netlink header (16 B)
|
||||
+ "00000100020000004310010000000000" // struct ifinfomsg (16 B) – no attributes
|
||||
)
|
||||
)
|
||||
|
||||
// Network up for interface.
|
||||
let expectedUpRequest = "280000001000050000000000c00cc00c110000000200000001000000ffffffff0800040000050000"
|
||||
mockSocket.responses.append([
|
||||
0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0xc0, 0x0c, 0xc0, 0x0c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x00, 0x00, 0x00,
|
||||
])
|
||||
let expectedUpRequest =
|
||||
"280000001000050000000000c00cc00c" // Netlink header (16 B)
|
||||
+ "110000000200000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "0800040000050000" // RT attr: IFLA_MTU = 1280 (8 B)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"240000000200000100000000c00cc00c" // Netlink header (16 B)
|
||||
+ "00000000200000001000050000000000" // nlmsg_err payload (16 B)
|
||||
+ "11000000" // 1st 4 B of echoed offending header
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
try session.linkSet(interface: "eth0", up: true)
|
||||
try session.linkSet(interface: "eth0", up: true, mtu: 1280)
|
||||
|
||||
#expect(mockSocket.requests.count == 2)
|
||||
#expect(mockSocket.responseIndex == 2)
|
||||
mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
|
||||
#expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
|
||||
mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
|
||||
#expect(expectedUpRequest == mockSocket.requests[1].hexEncodedString())
|
||||
}
|
||||
|
||||
@Test func testNetworkLinkUpLoopback() throws {
|
||||
let mockSocket = try MockNetlinkSocket()
|
||||
mockSocket.pid = 0xc00c_c00c
|
||||
|
||||
// Lookup loopback interface
|
||||
let expectedLookupRequest =
|
||||
"3000000012000100000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d0009000000080003006c6f0000" // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“lo”)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2000000010000000000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000100010000004310010000000000" // struct ifinfomsg (16 B) – no attributes
|
||||
)
|
||||
)
|
||||
|
||||
// Link up request for loopback, 32‑byte payload and no attributes
|
||||
let expectedUpRequest =
|
||||
"2000000010000500000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "110000000100000001000000ffffffff" // struct ifinfomsg (16 B) – no RT attrs
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2400000002000001000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000000200000001000050000000000" // nlmsg_err payload (16 B)
|
||||
+ "0c000000" // first 4 B of echoed offending header
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
try session.linkSet(interface: "lo", up: true)
|
||||
|
||||
#expect(mockSocket.requests.count == 2)
|
||||
#expect(mockSocket.responseIndex == 2)
|
||||
@@ -96,17 +150,19 @@ struct NetlinkSessionTest {
|
||||
|
||||
// Lookup interface by name, truncated response with three attributes.
|
||||
let expectedLookupRequest =
|
||||
"34000000120001000000000078563412110000000000000001000000ffffffff08001d00090000000c0003006574683000000000"
|
||||
mockSocket.responses.append([
|
||||
0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12,
|
||||
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x09, 0x00, 0x03, 0x00, 0x65, 0x74, 0x68, 0x30,
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00,
|
||||
0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00,
|
||||
0x06, 0x00, 0x00, 0x00,
|
||||
])
|
||||
"34000000120001000000000078563412" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d00090000000c0003006574683000000000" // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"3c000000100000000000000078563412" // Netlink header (16 B)
|
||||
+ "00000100020000004300010000000000" // struct ifinfomsg (16 B)
|
||||
+ "090003006574683000000000" // IFLA_IFNAME (“eth0”) attr (12 B)
|
||||
+ "08000d00e8030000" // IFLA_MTU = 1000 attr (8 B)
|
||||
+ "0500100006000000" // attr type 0x0010 (8 B)
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
let links = try session.linkGet(interface: "eth0")
|
||||
@@ -135,27 +191,33 @@ struct NetlinkSessionTest {
|
||||
mockSocket.pid = 0x8765_4321
|
||||
|
||||
// Lookup all interfaces, responses with only the interface name attribute.
|
||||
let expectedLookupRequest = "28000000120001030000000021436587110000000000000001000000ffffffff08001d0009000000"
|
||||
mockSocket.responses.append([
|
||||
0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87,
|
||||
0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00,
|
||||
0x49, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, 0x00,
|
||||
])
|
||||
mockSocket.responses.append([
|
||||
0x2c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87,
|
||||
0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0a, 0x00, 0x03, 0x00, 0x74, 0x75, 0x6e, 0x6c,
|
||||
0x30, 0x00, 0x00, 0x00,
|
||||
])
|
||||
mockSocket.responses.append([
|
||||
0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
let expectedLookupRequest =
|
||||
"28000000120001030000000021436587" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d0009000000" // RT attr: IFLA_EXT_MASK (8 B)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"28000000100002000000000021436587" // Netlink header (16 B)
|
||||
+ "00000403010000004900010000000000" // struct ifinfomsg (16 B)
|
||||
+ "070003006c6f0000" // IFLA_IFNAME “lo” (8 B, padded)
|
||||
)
|
||||
)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2c000000100002000000000021436587" // Netlink header (16 B)
|
||||
+ "00000003040000008000000000000000" // struct ifinfomsg (16 B)
|
||||
+ "0a00030074756e6c30000000" // IFLA_IFNAME “tunl0” attr (12 B, padded)
|
||||
)
|
||||
)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"14000000030002000000000021436587" // Netlink header (16 B) – NLMSG_DONE
|
||||
+ "00000000" // 4-byte payload
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
let links = try session.linkGet()
|
||||
@@ -185,23 +247,31 @@ struct NetlinkSessionTest {
|
||||
|
||||
// Lookup interface by name, truncated response with no attributes (not needed at present).
|
||||
let expectedLookupRequest =
|
||||
"3400000012000100000000000cc00cc0110000000000000001000000ffffffff08001d00090000000c0003006574683000000000"
|
||||
mockSocket.responses.append([
|
||||
0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x0c, 0xc0,
|
||||
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
"3400000012000100000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d00090000000c0003006574683000000000" // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2000000010000000000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000100020000004310010000000000" // struct ifinfomsg (16 B) – no attributes
|
||||
)
|
||||
)
|
||||
|
||||
// Network down for interface.
|
||||
let expectedAddRequest = "2800000014000506000000000cc00cc0021800000200000008000200c0a840fa08000100c0a840fa"
|
||||
mockSocket.responses.append([
|
||||
0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x0c, 0xc0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
|
||||
0x14, 0x00, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1f, 0x00, 0x00, 0x00,
|
||||
])
|
||||
let expectedAddRequest =
|
||||
"2800000014000506000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "0218000002000000" // ifaddrmsg (8 B): AF_INET, /24, ifindex 2
|
||||
+ "08000200c0a840fa" // RT attr: IFA_LOCAL 192.168.64.250
|
||||
+ "08000100c0a840fa" // RT attr: IFA_ADDRESS 192.168.64.250
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2400000002000001000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000000280000001400050600000000" // nlmsg_err payload (16 B)
|
||||
+ "1f000000" // first 4 B of echoed offending header
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
try session.addressAdd(interface: "eth0", address: "192.168.64.250/24")
|
||||
@@ -219,24 +289,34 @@ struct NetlinkSessionTest {
|
||||
|
||||
// Lookup interface by name, truncated response with no attributes (not needed at present).
|
||||
let expectedLookupRequest =
|
||||
"3400000012000100000000000cc00cc0110000000000000001000000ffffffff08001d00090000000c0003006574683000000000"
|
||||
mockSocket.responses.append([
|
||||
0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x0c, 0xc0,
|
||||
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
"3400000012000100000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "110000000000000001000000ffffffff" // struct ifinfomsg (16 B)
|
||||
+ "08001d00090000000c0003006574683000000000" // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2000000010000000000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000100020000004310010000000000" // struct ifinfomsg (16 B) – no attributes
|
||||
)
|
||||
)
|
||||
|
||||
// Add link route.
|
||||
let expectedAddRequest =
|
||||
"3400000018000506000000000cc00cc002180000fe02fd010000000008000100c0a8400008000700c0a840030800040002000000"
|
||||
mockSocket.responses.append([
|
||||
0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x0c, 0xc0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
|
||||
0x14, 0x00, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1f, 0x00, 0x00, 0x00,
|
||||
])
|
||||
"3400000018000506000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "02180000fe02fd0100000000" // struct rtmsg (12 B): AF_INET, dst/24,
|
||||
// table=RT_TABLE_MAIN (0xfe), proto=RTPROT_BOOT (0x02),
|
||||
// scope=RT_SCOPE_UNIVERSE (0xfd), type=RTN_UNICAST (0x01)
|
||||
+ "08000100c0a84000" // RTA_DST 192.168.64.0
|
||||
+ "08000700c0a84003" // RTA_PREFSRC 192.168.64.3
|
||||
+ "0800040002000000" // RTA_OIF ifindex 2 (eth0)
|
||||
mockSocket.responses.append(
|
||||
[UInt8](
|
||||
hex:
|
||||
"2400000002000001000000000cc00cc0" // Netlink header (16 B)
|
||||
+ "00000000280000001400050600000000" // nlmsg_err payload (16 B)
|
||||
+ "1f000000" // first 4 B of echoed offending header
|
||||
)
|
||||
)
|
||||
|
||||
let session = NetlinkSession(socket: mockSocket)
|
||||
try session.routeAdd(
|
||||
@@ -253,3 +333,14 @@ struct NetlinkSessionTest {
|
||||
#expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == UInt8 {
|
||||
/// Initializes `[UInt8]` from an even-length hex string
|
||||
init(hex: String) {
|
||||
self = stride(from: 0, to: hex.count, by: 2).compactMap {
|
||||
UInt8(
|
||||
hex[hex.index(hex.startIndex, offsetBy: $0)...]
|
||||
.prefix(2), radix: 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,7 +622,8 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid
|
||||
do {
|
||||
let socket = try DefaultNetlinkSocket()
|
||||
let session = NetlinkSession(socket: socket, log: log)
|
||||
try session.linkSet(interface: request.interface, up: request.up)
|
||||
let mtuValue: UInt32? = request.hasMtu ? request.mtu : nil
|
||||
try session.linkSet(interface: request.interface, up: request.up, mtu: mtuValue)
|
||||
} catch {
|
||||
log.error(
|
||||
"ip-link-set",
|
||||
|
||||
Reference in New Issue
Block a user