Class: OsCtld::NetConfig
- Inherits:
-
Object
- Object
- OsCtld::NetConfig
- Defined in:
- lib/osctld/net_config.rb
Overview
Generate network configuration for a container and apply using netlink
NetConfig has to be created before switching to container user and attaching to the container. Call NetConfig.create while running as root and #setup while attached into a container.
Defined Under Namespace
Instance Attribute Summary collapse
-
#netifs ⇒ Object
readonly
Returns the value of attribute netifs.
Class Method Summary collapse
Instance Method Summary collapse
- #add_netif(netif) ⇒ Object
- #default_route_addr(ip_v) ⇒ Object protected
- #export ⇒ Object
- #import(data) ⇒ Object
-
#initialize ⇒ NetConfig
constructor
A new instance of NetConfig.
-
#setup ⇒ Object
Apply configuration using netlink.
- #wait_for_ipv6_addresses(nl, timeout: 10) ⇒ Object protected
- #wait_for_netifs(nl, timeout: 10) ⇒ Object protected
Constructor Details
#initialize ⇒ NetConfig
Returns a new instance of NetConfig.
32 33 34 |
# File 'lib/osctld/net_config.rb', line 32 def initialize @netifs = [] end |
Instance Attribute Details
#netifs ⇒ Object (readonly)
Returns the value of attribute netifs.
17 18 19 |
# File 'lib/osctld/net_config.rb', line 17 def netifs @netifs end |
Class Method Details
.create(ct) ⇒ Object
20 21 22 23 24 |
# File 'lib/osctld/net_config.rb', line 20 def self.create(ct) cfg = new ct.netifs.each { |netif| cfg.add_netif(netif) } cfg end |
.import(data) ⇒ Object
26 27 28 29 30 |
# File 'lib/osctld/net_config.rb', line 26 def self.import(data) cfg = new cfg.import(data) cfg end |
Instance Method Details
#add_netif(netif) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/osctld/net_config.rb', line 37 def add_netif(netif) n = NetIf.new(netif.name, [], []) [4, 6].each do |ip_v| if netif.respond_to?(:ips) netif.ips(ip_v).each do |ip| n.ips << Addr.new(ip_v, ip.to_s, ip.prefix.to_i) end end case netif.type when :bridge if netif.has_gateway?(ip_v) n.routes << Route.new(ip_v, default_route_addr(ip_v), 0, netif.gateway(ip_v)) end when :routed # An IPv6-only interface cannot supply an IPv4 gateway/source, and # vice versa. Do not install defaults for unconfigured families. next unless n.ips.any? { |ip| ip.version == ip_v } begin via = netif.default_via(ip_v).to_s n.routes << Route.new(ip_v, via, ip_v == 4 ? 32 : 128, nil) n.routes << Route.new(ip_v, default_route_addr(ip_v), 0, via) rescue RuntimeError # IPv6 is routed via link-local address on the host interface, which # is not known when the container is stopped. next if ip_v == 6 end end end netifs << n end |
#default_route_addr(ip_v) ⇒ Object (protected)
184 185 186 |
# File 'lib/osctld/net_config.rb', line 184 def default_route_addr(ip_v) ip_v == 4 ? '0.0.0.0' : '::' end |
#export ⇒ Object
100 101 102 103 104 105 106 107 108 |
# File 'lib/osctld/net_config.rb', line 100 def export netifs.map do |netif| { name: netif.name, ips: netif.ips.map(&:to_h), routes: netif.routes.map(&:to_h) } end end |
#import(data) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/osctld/net_config.rb', line 110 def import(data) data.each do |netif_hash| netifs << NetIf.new( netif_hash[:name], netif_hash[:ips].map do |v| Addr.new(v[:version], v[:address], v[:prefix]) end, netif_hash[:routes].map do |v| Route.new(v[:version], v[:address], v[:prefix], v[:via]) end ) end end |
#setup ⇒ Object
Apply configuration using netlink
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/osctld/net_config.rb', line 74 def setup nl = Linux::Netlink::Route::Socket.new wait_for_netifs(nl) netifs.each do |netif| netif.ips.each do |ip| nl.addr.add(index: netif.name, local: ip.address, prefixlen: ip.prefix) rescue Errno::EEXIST next end netif.routes.each do |route| nl.route.add( oif: netif.name, dst: route.address, dst_len: route.prefix, gateway: route.via ) rescue Errno::EEXIST next end end wait_for_ipv6_addresses(nl) end |
#wait_for_ipv6_addresses(nl, timeout: 10) ⇒ Object (protected)
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/osctld/net_config.rb', line 126 def wait_for_ipv6_addresses(nl, timeout: 10) expected = netifs.flat_map do |netif| netif.ips.select { |ip| ip.version == 6 }.map { |ip| [netif.name, IPAddr.new(ip.address)] } end return if expected.empty? deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout loop do # AddrHandler memoizes dumps: DAD progress requires a fresh snapshot. nl.addr.clear_cache addresses = expected.map(&:first).uniq.to_h do |name| [name, nl.addr.list(index: name, family: Socket::AF_INET6).to_a] end # Neighbor discovery also uses the interface's automatic link-local # address, whose DAD can finish after the configured global address. link_local = addresses.flat_map do |name, entries| entries.select { |entry| entry.address.link_local? }.map { |entry| [name, entry.address] } end pending = (expected + link_local).uniq.reject do |name, ip| address = addresses.fetch(name).find { |entry| entry.address == ip } next false unless address if address.flags.anybits?(Linux::IFA_F_DADFAILED) raise "IPv6 duplicate address detection failed: #{name}=#{ip}" end address.flags.nobits?(Linux::IFA_F_TENTATIVE) end return if pending.empty? if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline raise "IPv6 addresses not ready: #{pending.map { |name, ip| "#{name}=#{ip}" }.join(', ')}" end sleep(0.1) end end |
#wait_for_netifs(nl, timeout: 10) ⇒ Object (protected)
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/osctld/net_config.rb', line 165 def wait_for_netifs(nl, timeout: 10) names = netifs.map(&:name).uniq return if names.empty? deadline = Time.now + timeout missing = [] loop do existing = nl.link.list.map(&:ifname) missing = names - existing return if missing.empty? break if Time.now >= deadline sleep(0.1) end raise "network interfaces not found: #{missing.join(', ')}" end |