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
19 changes: 19 additions & 0 deletions Sources/ContainerizationExtras/IPv4Address.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,30 @@
public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {
public let value: UInt32

/// Creates an IPv4Address from an unsigned integer.
///
/// - Parameter string: The integer representation of the address.
@inlinable
public init(_ value: UInt32) {
self.value = value
}

/// Creates an IPv4Address from 4 bytes.
///
/// - Parameters:
/// - bytes: 4-byte array in network byte order representing the IPv4 address
/// - Throws: `AddressError.unableToParse` if the byte array length is not 4
@inlinable
public init(_ bytes: [UInt8]) throws {
guard bytes.count == 4 else {
throw AddressError.unableToParse
}
self.value = (UInt32(bytes[0]) << 24)
| (UInt32(bytes[1]) << 16)
| (UInt32(bytes[2]) << 16)
| UInt32(bytes[3])
}

/// Creates an IPv4Address from a string representation.
///
/// - Parameter string: The IPv4 address string in dotted decimal notation (e.g., "192.168.1.1")
Expand Down
4 changes: 2 additions & 2 deletions Sources/ContainerizationExtras/IPv6Address+Parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ extension IPv6Address {
if remainingAddress.isEmpty {
// If we have IPv4 suffix, ipBytes already has the IPv4 data, just return
if hasIPv4Suffix {
return Self(ipBytes, zone: zone)
return try Self(ipBytes, zone: zone)
}

// Pure "::" - Return the unspecified address, handling zone identifiers
if let zone = zone, !zone.isEmpty {
return Self(ipBytes, zone: zone)
return try Self(ipBytes, zone: zone)
}
return .unspecified
}
Expand Down
9 changes: 6 additions & 3 deletions Sources/ContainerizationExtras/IPv6Address.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ public struct IPv6Address: Sendable, Hashable, CustomStringConvertible, Equatabl
/// Creates an IPv6Address from 16 bytes.
///
/// - Parameters:
/// - bytes: 16-byte array representing the IPv6 address
/// - bytes: 16-byte array in network byte order representing the IPv6 address
/// - zone: Optional zone identifier (e.g., "eth0")
/// - Throws: `AddressError.unableToParse` if the byte array length is not 16
@inlinable
public init(_ bytes: [UInt8], zone: String? = nil) {
precondition(bytes.count == 16, "IPv6 address must be exactly 16 bytes")
public init(_ bytes: [UInt8], zone: String? = nil) throws {
guard bytes.count == 16 else {
throw AddressError.unableToParse
}

// Build UInt128 value in chunks to avoid compiler complexity
let hh =
Expand Down
22 changes: 20 additions & 2 deletions Sources/ContainerizationExtras/MACAddress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ public struct MACAddress: Sendable, Hashable, CustomStringConvertible, Equatable
self.value = value & 0x0000_ffff_ffff_ffff
}

/// Creates an IPv4Address from 6 bytes.
///
/// - Parameters:
/// - bytes: 6-byte array in network byte order representing the IPv4 address
/// - Throws: `AddressError.unableToParse` if the byte array length is not 6
@inlinable
public init(_ bytes: [UInt8]) throws {
guard bytes.count == 6 else {
throw AddressError.unableToParse
}
self.value = (UInt64(bytes[0]) << 40)
| (UInt64(bytes[1]) << 32)
| (UInt64(bytes[2]) << 24)
| (UInt64(bytes[3]) << 16)
| (UInt64(bytes[4]) << 8)
| UInt64(bytes[5])
}

/// Creates an MACAddress from a string representation.
///
/// - Parameter string: The MAC address string with colon or dash delimiters.
Expand Down Expand Up @@ -225,9 +243,9 @@ public struct MACAddress: Sendable, Hashable, CustomStringConvertible, Equatable
/// - Parameter network: The IPv6 address to use for the network prefix
/// - Returns: The link local IP address for the MAC address
@inlinable
public func ipv6Address(network: IPv6Address) -> IPv6Address {
public func ipv6Address(network: IPv6Address) throws -> IPv6Address {
let prefixBytes = network.bytes
return IPv6Address([
return try IPv6Address([
prefixBytes[0], prefixBytes[1], prefixBytes[2], prefixBytes[3],
prefixBytes[4], prefixBytes[5], prefixBytes[6], prefixBytes[7],
bytes[0] ^ 0x02, bytes[1], bytes[2], 0xff,
Expand Down
32 changes: 27 additions & 5 deletions Sources/ContainerizationExtras/UInt8+DataBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

import Foundation

package enum BindError: Error, CustomStringConvertible {
case recvMarshalFailure(type: String, field: String)
case sendMarshalFailure(type: String, field: String)

package var description: String {
switch self {
case .recvMarshalFailure(let type, let field):
return "failed to unmarshal \(type).\(field)"
case .sendMarshalFailure(let type, let field):
return "failed to marshal \(type).\(field)"
}
}
}

package protocol Bindable: Sendable {
static var size: Int { get }
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int
}

extension ArraySlice<UInt8> {
package func hexEncodedString() -> String {
self.map { String(format: "%02hhx", $0) }.joined()
Expand All @@ -35,7 +57,7 @@ extension [UInt8] {

package mutating func copyIn<T>(as type: T.Type, value: T, offset: Int = 0, size: Int? = nil) -> Int? {
let size = size ?? MemoryLayout<T>.size
guard self.count >= size - offset else {
guard self.count >= size + offset else {
return nil
}

Expand All @@ -45,12 +67,12 @@ extension [UInt8] {
}
}

package mutating func copyOut<T>(as type: T.Type, offset: Int = 0, size: Int? = nil) -> (Int, T)? {
guard self.count >= (size ?? MemoryLayout<T>.size) - offset else {
package func copyOut<T>(as type: T.Type, offset: Int = 0, size: Int? = nil) -> (Int, T)? {
guard self.count >= (size ?? MemoryLayout<T>.size) + offset else {
return nil
}

return self.withUnsafeMutableBytes {
return self.withUnsafeBytes {
guard let value = $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self).pointee else {
return nil
}
Expand All @@ -67,7 +89,7 @@ extension [UInt8] {
return offset + buffer.count
}

package mutating func copyOut(buffer: inout [UInt8], offset: Int = 0) -> Int? {
package func copyOut(buffer: inout [UInt8], offset: Int = 0) -> Int? {
guard offset + buffer.count <= self.count else {
return nil
}
Expand Down
24 changes: 12 additions & 12 deletions Sources/ContainerizationNetlink/NetlinkSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public struct NetlinkSession {
let newRequestOffset =
requestBuffer.copyIn(as: UInt32.self, value: m, offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFLA_MTU")
}
requestOffset = newRequestOffset
}
Expand Down Expand Up @@ -162,15 +162,15 @@ public struct NetlinkSession {
value: filters,
offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFLA_EXT_MASK")
}

if let interfaceNameAttr {
if let interfaceName {
requestOffset = try interfaceNameAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard let updatedRequestOffset = requestBuffer.copyIn(buffer: interfaceName, offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFLA_IFNAME")
}
requestOffset = updatedRequestOffset
}
Expand Down Expand Up @@ -239,13 +239,13 @@ public struct NetlinkSession {
let ipLocalAttr = RTAttribute(len: UInt16(addressAttrSize), type: AddressAttributeType.IFA_LOCAL)
requestOffset = try ipLocalAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: ipAddressBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFA_LOCAL")
}

let ipAddressAttr = RTAttribute(len: UInt16(addressAttrSize), type: AddressAttributeType.IFA_ADDRESS)
requestOffset = try ipAddressAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard let requestOffset = requestBuffer.copyIn(buffer: ipAddressBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFA_ADDRESS")
}

guard requestOffset == requestSize else {
Expand Down Expand Up @@ -305,13 +305,13 @@ public struct NetlinkSession {
let dstAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.DST)
requestOffset = try dstAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: dstAddrBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_DST")
}

let srcAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.PREFSRC)
requestOffset = try srcAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: srcAddrBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_PREFSRC")
}

let interfaceAttr = RTAttribute(len: UInt16(interfaceAttrSize), type: RouteAttributeType.OIF)
Expand All @@ -322,7 +322,7 @@ public struct NetlinkSession {
value: UInt32(interfaceIndex),
offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_OIF")
}

guard requestOffset == requestSize else {
Expand Down Expand Up @@ -378,7 +378,7 @@ public struct NetlinkSession {
let dstAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.GATEWAY)
requestOffset = try dstAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: dstAddrBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_GATEWAY")
}
let interfaceAttr = RTAttribute(len: UInt16(interfaceAttrSize), type: RouteAttributeType.OIF)
requestOffset = try interfaceAttr.appendBuffer(&requestBuffer, offset: requestOffset)
Expand All @@ -388,7 +388,7 @@ public struct NetlinkSession {
value: UInt32(interfaceIndex),
offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_OIF")
}

guard requestOffset == requestSize else {
Expand All @@ -404,7 +404,7 @@ public struct NetlinkSession {

private func getInterfaceName(_ interface: String) throws -> [UInt8] {
guard let interfaceNameData = interface.data(using: .utf8) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "String", field: "interface")
}

var interfaceName = [UInt8](interfaceNameData)
Expand Down Expand Up @@ -503,7 +503,7 @@ public struct NetlinkSession {

private func parseErrorCode(buffer: inout [UInt8], offset: Int) throws -> (Int32, Int) {
guard let errorPtr = buffer.bind(as: Int32.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkErrorMessage", field: "error")
}

let rc = errorPtr.pointee
Expand Down
Loading
Loading