Class: OsCtld::BpfFs

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
OsCtl::Lib::Utils::Log, OsCtl::Lib::Utils::System, Singleton
Defined in:
lib/osctld/bpf_fs.rb

Constant Summary collapse

FS =
'/run/osctl/bpf'.freeze
CT_FS =
'/run/osctl/ct-bpf'.freeze
ROOT_DIR =
File.join(FS, 'osctl')
PROG_DIR =
File.join(ROOT_DIR, 'progs')
POOL_DIR =
File.join(ROOT_DIR, 'pools')

Instance Method Summary collapse

Instance Method Details

#add_pool(pool_name) ⇒ Object



37
38
39
40
# File 'lib/osctld/bpf_fs.rb', line 37

def add_pool(pool_name)
  FileUtils.mkdir_p(File.join(pool_dir(pool_name), 'links'))
  FileUtils.mkdir_p(ct_pool_dir(pool_name))
end

#ct_mount_options(root_uid, root_gid) ⇒ Object (protected)



148
149
150
151
152
153
154
155
156
157
# File 'lib/osctld/bpf_fs.rb', line 148

def ct_mount_options(root_uid, root_gid)
  [
    'nosuid',
    'nodev',
    'noexec',
    "uid=#{Integer(root_uid)}",
    "gid=#{Integer(root_gid)}",
    'mode=700'
  ].join(',')
end

#ct_mount_path(pool_name, ct_id) ⇒ Object



89
90
91
# File 'lib/osctld/bpf_fs.rb', line 89

def ct_mount_path(pool_name, ct_id)
  File.join(ct_pool_dir(pool_name), ct_id)
end

#ct_pool_dir(pool_name) ⇒ Object (protected)



144
145
146
# File 'lib/osctld/bpf_fs.rb', line 144

def ct_pool_dir(pool_name)
  File.join(CT_FS, pool_name)
end


71
72
73
# File 'lib/osctld/bpf_fs.rb', line 71

def link_pin_path(pool_name, link_name)
  File.join(pool_dir(pool_name), 'links', link_name)
end

Returns:

  • (Boolean)


75
76
77
# File 'lib/osctld/bpf_fs.rb', line 75

def link_pinned?(pool_name, link_name)
  File.file?(link_pin_path(pool_name, link_name))
end


79
80
81
82
83
84
85
86
87
# File 'lib/osctld/bpf_fs.rb', line 79

def list_links(pool_name)
  dir = File.join(pool_dir(pool_name), 'links')

  Dir.entries(dir).select do |f|
    !%w[. ..].include?(f) && File.file?(File.join(dir, f))
  end
rescue Errno::ENOENT
  []
end

#list_progs ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/osctld/bpf_fs.rb', line 61

def list_progs
  dir = PROG_DIR

  Dir.entries(dir).select do |f|
    !%w[. ..].include?(f) && File.file?(File.join(dir, f))
  end
rescue Errno::ENOENT
  []
end

#log_type ⇒ Object



119
120
121
# File 'lib/osctld/bpf_fs.rb', line 119

def log_type
  'bpf-fs'
end

#mount_type(path) ⇒ Object (protected)



159
160
161
162
163
164
165
166
167
168
169
# File 'lib/osctld/bpf_fs.rb', line 159

def mount_type(path)
  File.readlines('/proc/self/mountinfo').any? do |line|
    parts = line.split
    next unless parts.fetch(4) == path

    sep = parts.index('-')
    return parts.fetch(sep + 1) if sep
  end

  nil
end

#pool_dir(pool_name) ⇒ Object (protected)



140
141
142
# File 'lib/osctld/bpf_fs.rb', line 140

def pool_dir(pool_name)
  File.join(POOL_DIR, pool_name)
end

#prog_pin_path(prog_name) ⇒ Object



53
54
55
# File 'lib/osctld/bpf_fs.rb', line 53

def prog_pin_path(prog_name)
  File.join(PROG_DIR, prog_name)
end

#prog_pinned?(prog_name) ⇒ Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/osctld/bpf_fs.rb', line 57

def prog_pinned?(prog_name)
  File.exist?(prog_pin_path(prog_name))
end

#remove_ct(pool_name, ct_id) ⇒ Object



112
113
114
115
116
117
# File 'lib/osctld/bpf_fs.rb', line 112

def remove_ct(pool_name, ct_id)
  path = ct_mount_path(pool_name, ct_id)

  syscmd("umount -f #{Shellwords.escape(path)}", valid_rcs: [32]) if Dir.exist?(path)
  FileUtils.rm_rf(path, secure: true)
end

#remove_pool(pool_name) ⇒ Object



42
43
44
45
46
47
48
49
50
51
# File 'lib/osctld/bpf_fs.rb', line 42

def remove_pool(pool_name)
  if Dir.exist?(ct_pool_dir(pool_name))
    Dir.each_child(ct_pool_dir(pool_name)) do |ct_id|
      remove_ct(pool_name, ct_id)
    end
  end

  FileUtils.rm_rf(ct_pool_dir(pool_name), secure: true)
  FileUtils.rm_rf(pool_dir(pool_name), secure: true)
end

#setup ⇒ Object



31
32
33
34
35
# File 'lib/osctld/bpf_fs.rb', line 31

def setup
  setup_host_mount
  FileUtils.mkdir_p(PROG_DIR)
  FileUtils.mkdir_p(CT_FS)
end

#setup_ct(pool_name, ct_id, root_uid:, root_gid:) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/osctld/bpf_fs.rb', line 93

def setup_ct(pool_name, ct_id, root_uid:, root_gid:)
  path = ct_mount_path(pool_name, ct_id)
  opts = ct_mount_options(root_uid, root_gid)

  FileUtils.mkdir_p(path)

  mounted_type = mount_type(path)

  if mounted_type == 'bpf'
    syscmd("mount -o remount,#{Shellwords.escape(opts)} #{Shellwords.escape(path)}")
  else
    syscmd("umount -f #{Shellwords.escape(path)}", valid_rcs: [32]) if mounted_type
    syscmd("mount -t bpf -o #{Shellwords.escape(opts)} bpf #{Shellwords.escape(path)}")
  end

  syscmd("mount --make-rprivate #{Shellwords.escape(path)}")
  path
end

#setup_host_mount ⇒ Object (protected)

A generation switch does not run the boot-time mount setup. Bind the predecessor's existing bpffs so device programs and links stay pinned; mounting a fresh filesystem here would lose their userspace identities.



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/osctld/bpf_fs.rb', line 128

def setup_host_mount
  mounted_type = mount_type(FS)
  return if mounted_type == 'bpf'

  raise "#{FS} is not a BPF filesystem" if mounted_type
  raise 'legacy BPF filesystem is not mounted' unless mount_type('/sys/fs/bpf') == 'bpf'

  FileUtils.mkdir_p(FS)
  syscmd("mount --bind /sys/fs/bpf #{FS}")
  syscmd("mount --make-rprivate #{FS}")
end