mirror of
https://github.com/apple/container.git
synced 2026-09-23 16:15:37 +00:00
Add documentation for netlink (#21)
This commit is contained in:
@@ -19,20 +19,23 @@ import ContainerizationExtras
|
||||
import ContainerizationOS
|
||||
import Logging
|
||||
|
||||
/// `NetlinkSession` facilitates interacting with netlink via a provided
|
||||
/// `NetlinkSocket`. This is the core high level type offered to perform
|
||||
/// actions to the netlink surface in the kernel.
|
||||
/// `NetlinkSession` facilitates interacting with netlink via a provided `NetlinkSocket`. This is the
|
||||
/// core high-level type offered to perform actions to the netlink surface in the kernel.
|
||||
public struct NetlinkSession {
|
||||
private static let receiveDataLength = 65536
|
||||
|
||||
private let socket: any NetlinkSocket
|
||||
|
||||
private let log: Logger
|
||||
|
||||
/// Creates a new `NetlinkSession`.
|
||||
/// - Parameters:
|
||||
/// - socket: The `NetlinkSocket` to use for netlink interaction.
|
||||
/// - log: The logger to use. The default value is `nil`.
|
||||
public init(socket: any NetlinkSocket, log: Logger? = nil) {
|
||||
self.socket = socket
|
||||
self.log = log ?? Logger(label: "com.apple.containerization.netlink")
|
||||
}
|
||||
|
||||
/// Errors that may occur during netlink interaction.
|
||||
public enum Error: Swift.Error, CustomStringConvertible, Equatable {
|
||||
case invalidIpAddress
|
||||
case invalidPrefixLength
|
||||
@@ -41,6 +44,7 @@ public struct NetlinkSession {
|
||||
case unexpectedResidualPackets
|
||||
case unexpectedResultSet(count: Int, expected: Int)
|
||||
|
||||
/// The description of the errors.
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .invalidIpAddress:
|
||||
@@ -59,8 +63,12 @@ public struct NetlinkSession {
|
||||
}
|
||||
}
|
||||
|
||||
/// ip link set dev [interface] [up|down]
|
||||
/// Performs a link set command on an interface.
|
||||
/// - Parameters:
|
||||
/// - interface: The name of the interface.
|
||||
/// - up: The value to set the interface state to.
|
||||
public func linkSet(interface: String, up: Bool) throws {
|
||||
// ip link set dev [interface] [up|down]
|
||||
let interfaceIndex = try getInterfaceIndex(interface)
|
||||
let requestSize = NetlinkMessageHeader.size + InterfaceInfo.size
|
||||
var requestBuffer = [UInt8](repeating: 0, count: requestSize)
|
||||
@@ -92,8 +100,11 @@ public struct NetlinkSession {
|
||||
}
|
||||
}
|
||||
|
||||
/// ip link ip show
|
||||
/// Performs a link get command on an interface.
|
||||
/// Returns information about the interface.
|
||||
/// - Parameter interface: The name of the interface to query.
|
||||
public func linkGet(interface: String? = nil) throws -> [LinkResponse] {
|
||||
// ip link ip show
|
||||
let maskAttr = RTAttribute(
|
||||
len: UInt16(RTAttribute.size + MemoryLayout<UInt32>.size), type: LinkAttributeType.IFLA_EXT_MASK)
|
||||
let interfaceName = try interface.map { try getInterfaceName($0) }
|
||||
@@ -157,17 +168,21 @@ public struct NetlinkSession {
|
||||
return linkResponses
|
||||
}
|
||||
|
||||
/// ip addr add [addr] dev [interface]
|
||||
/// ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ] [ CONFFLAG-LIST ]
|
||||
/// IFADDR := PREFIX | ADDR peer PREFIX
|
||||
/// [ broadcast ADDR ] [ anycast ADDR ]
|
||||
/// [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]
|
||||
/// SCOPE-ID := [ host | link | global | NUMBER ]
|
||||
/// CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG
|
||||
/// CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]
|
||||
/// LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]
|
||||
/// LFT := forever | SECONDS
|
||||
/// Adds an IPv4 address to an interface.
|
||||
/// - Parameters:
|
||||
/// - interface: The name of the interface.
|
||||
/// - address: The IPv4 address to add.
|
||||
public func addressAdd(interface: String, address: String) throws {
|
||||
// ip addr add [addr] dev [interface]
|
||||
// ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ] [ CONFFLAG-LIST ]
|
||||
// IFADDR := PREFIX | ADDR peer PREFIX
|
||||
// [ broadcast ADDR ] [ anycast ADDR ]
|
||||
// [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]
|
||||
// SCOPE-ID := [ host | link | global | NUMBER ]
|
||||
// CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG
|
||||
// CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]
|
||||
// LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]
|
||||
// LFT := forever | SECONDS
|
||||
let parsed = try parseCIDR(cidr: address)
|
||||
let interfaceIndex = try getInterfaceIndex(interface)
|
||||
let ipAddressBytes = try IPv4Address(parsed.address).networkBytes
|
||||
@@ -231,12 +246,17 @@ public struct NetlinkSession {
|
||||
return (address, prefixLength)
|
||||
}
|
||||
|
||||
/// ip route add [dest-cidr] dev [interface] src [src-addr] proto kernel
|
||||
/// Adds a route to an interface.
|
||||
/// - Parameters:
|
||||
/// - interface: The name of the interface.
|
||||
/// - destinationAddress: The destination address to route to.
|
||||
/// - srcAddr: The source address to route from.
|
||||
public func routeAdd(
|
||||
interface: String,
|
||||
destinationAddress: String,
|
||||
srcAddr: String
|
||||
) throws {
|
||||
// ip route add [dest-cidr] dev [interface] src [src-addr] proto kernel
|
||||
let parsed = try parseCIDR(cidr: destinationAddress)
|
||||
let interfaceIndex = try getInterfaceIndex(interface)
|
||||
let dstAddrBytes = try IPv4Address(parsed.address).networkBytes
|
||||
@@ -303,11 +323,15 @@ public struct NetlinkSession {
|
||||
}
|
||||
}
|
||||
|
||||
/// ip route add default via [dst-address] src [src-address]
|
||||
/// Adds a default route to an interface.
|
||||
/// - Parameters:
|
||||
/// - interface: The name of the interface.
|
||||
/// - gateway: The gateway address.
|
||||
public func routeAddDefault(
|
||||
interface: String,
|
||||
gateway: String
|
||||
) throws {
|
||||
// ip route add default via [dst-address] src [src-address]
|
||||
let dstAddrBytes = try IPv4Address(gateway).networkBytes
|
||||
let dstAddrAttrSize = RTAttribute.size + dstAddrBytes.count
|
||||
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A protocol for interacting with a netlink socket.
|
||||
public protocol NetlinkSocket {
|
||||
var pid: UInt32 { get }
|
||||
func send(buf: UnsafeRawPointer!, len: Int, flags: Int32) throws -> Int
|
||||
func recv(buf: UnsafeMutableRawPointer!, len: Int, flags: Int32) throws -> Int
|
||||
}
|
||||
|
||||
/// A netlink socket provider.
|
||||
public typealias NetlinkSocketProvider = () throws -> any NetlinkSocket
|
||||
|
||||
/// Errors thrown when interacting with a netlink socket.
|
||||
public enum NetlinkSocketError: Swift.Error, CustomStringConvertible, Equatable {
|
||||
case socketFailure(rc: Int32)
|
||||
case bindFailure(rc: Int32)
|
||||
@@ -30,6 +33,7 @@ public enum NetlinkSocketError: Swift.Error, CustomStringConvertible, Equatable
|
||||
case recvFailure(rc: Int32)
|
||||
case notImplemented
|
||||
|
||||
/// The description of the errors.
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .socketFailure(let rc):
|
||||
@@ -53,11 +57,14 @@ let osBind = Musl.bind
|
||||
let osSend = Musl.send
|
||||
let osRecv = Musl.recv
|
||||
|
||||
/// A default implementation of `NetlinkSocket`.
|
||||
public class DefaultNetlinkSocket: NetlinkSocket {
|
||||
private let sockfd: Int32
|
||||
|
||||
/// The process identifier of the process creating this socket.
|
||||
public let pid: UInt32
|
||||
|
||||
/// Creates a new instance.
|
||||
public init() throws {
|
||||
pid = UInt32(getpid())
|
||||
sockfd = osSocket(Int32(AddressFamily.AF_NETLINK), SocketType.SOCK_RAW, NetlinkProtocol.NETLINK_ROUTE)
|
||||
@@ -80,6 +87,12 @@ public class DefaultNetlinkSocket: NetlinkSocket {
|
||||
close(sockfd)
|
||||
}
|
||||
|
||||
/// Sends a request to a netlink socket.
|
||||
/// Returns the number of bytes sent.
|
||||
/// - Parameters:
|
||||
/// - buf: The buffer to send.
|
||||
/// - len: The length of the buffer to send.
|
||||
/// - flags: The send flags.
|
||||
public func send(buf: UnsafeRawPointer!, len: Int, flags: Int32) throws -> Int {
|
||||
let count = osSend(sockfd, buf, len, flags)
|
||||
guard count >= 0 else {
|
||||
@@ -89,6 +102,12 @@ public class DefaultNetlinkSocket: NetlinkSocket {
|
||||
return count
|
||||
}
|
||||
|
||||
/// Receives a response from a netlink socket.
|
||||
/// Returns the number of bytes received.
|
||||
/// - Parameters:
|
||||
/// - buf: The buffer to receive into.
|
||||
/// - len: The maximum number of bytes to receive.
|
||||
/// - flags: The receive flags.
|
||||
public func recv(buf: UnsafeMutableRawPointer!, len: Int, flags: Int32) throws -> Int {
|
||||
let count = osRecv(sockfd, buf, len, flags)
|
||||
guard count >= 0 else {
|
||||
|
||||
@@ -519,6 +519,7 @@ struct RouteInfo: Bindable {
|
||||
}
|
||||
}
|
||||
|
||||
/// A route information.
|
||||
public struct RTAttribute: Bindable {
|
||||
static let size = 4
|
||||
|
||||
@@ -557,22 +558,26 @@ public struct RTAttribute: Bindable {
|
||||
}
|
||||
}
|
||||
|
||||
/// A route information with data.
|
||||
public struct RTAttributeData {
|
||||
public let attribute: RTAttribute
|
||||
public let data: [UInt8]
|
||||
}
|
||||
|
||||
/// A response from the get link command.
|
||||
public struct LinkResponse {
|
||||
public let interfaceIndex: Int32
|
||||
public let attrDatas: [RTAttributeData]
|
||||
}
|
||||
|
||||
/// Errors thrown when parsing netlink data.
|
||||
public enum NetlinkDataError: Swift.Error, CustomStringConvertible, Equatable {
|
||||
case sendMarshalFailure
|
||||
case recvUnmarshalFailure
|
||||
case responseError(rc: Int32)
|
||||
case unsupportedPlatform
|
||||
|
||||
/// The description of the errors.
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .sendMarshalFailure:
|
||||
|
||||
Reference in New Issue
Block a user