Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Xavier Thompson
slapos.core
Commits
99e28608
Commit
99e28608
authored
Oct 28, 2022
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
slapformat: WIP: Add tap
parent
f933b3a6
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
192 additions
and
4 deletions
+192
-4
slapos/format.py
slapos/format.py
+192
-4
No files found.
slapos/format.py
View file @
99e28608
...
@@ -99,6 +99,7 @@ class Options(object):
...
@@ -99,6 +99,7 @@ class Options(object):
create_tap
:
bool
=
True
create_tap
:
bool
=
True
tap_base_name
:
str
=
'slaptap'
tap_base_name
:
str
=
'slaptap'
tap_ipv6
:
bool
=
True
tap_ipv6
:
bool
=
True
tap_ipv4_prefix
:
int
=
32
tap_gateway_interface
:
str
=
None
tap_gateway_interface
:
str
=
None
create_tun
:
bool
=
False
create_tun
:
bool
=
False
...
@@ -292,6 +293,7 @@ class Interface(object):
...
@@ -292,6 +293,7 @@ class Interface(object):
ipv6_interface
:
str
ipv6_interface
:
str
ipv4_network
:
ipaddress
.
IPv4Network
ipv4_network
:
ipaddress
.
IPv4Network
ipv6_network
:
ipaddress
.
IPv6Network
ipv6_network
:
ipaddress
.
IPv6Network
tap_ipv4_network
:
ipaddress
.
IPv4Network
conf
:
FormatConfig
conf
:
FormatConfig
def
__init__
(
self
,
conf
):
def
__init__
(
self
,
conf
):
...
@@ -300,6 +302,7 @@ class Interface(object):
...
@@ -300,6 +302,7 @@ class Interface(object):
self
.
ipv6_interface
=
conf
.
ipv6_interface
or
conf
.
interface_name
self
.
ipv6_interface
=
conf
.
ipv6_interface
or
conf
.
interface_name
self
.
ipv4_network
=
self
.
getIPv4Network
(
conf
.
ipv4_local_network
)
self
.
ipv4_network
=
self
.
getIPv4Network
(
conf
.
ipv4_local_network
)
self
.
ipv6_network
=
self
.
getIPv6Network
()
self
.
ipv6_network
=
self
.
getIPv6Network
()
self
.
tap_ipv4_network
=
self
.
getTapIPv4Network
(
conf
)
def
getIPv4Network
(
self
,
cidr
):
def
getIPv4Network
(
self
,
cidr
):
if
cidr
:
if
cidr
:
...
@@ -310,6 +313,45 @@ class Interface(object):
...
@@ -310,6 +313,45 @@ class Interface(object):
network
=
self
.
ipv4_network
network
=
self
.
ipv4_network
return
ipaddress
.
IPv4Interface
((
network
[
index
+
2
],
network
.
prefixlen
))
return
ipaddress
.
IPv4Interface
((
network
[
index
+
2
],
network
.
prefixlen
))
def
getTapIPv4Network
(
self
,
conf
):
tap_gateway_interface
=
conf
.
tap_gateway_interface
if
conf
.
create_tap
and
tap_gateway_interface
:
try
:
addresses
=
netifaces
.
ifaddresses
(
tap_gateway_interface
)[
AF_INET
]
except
ValueError
:
self
.
conf
.
abort
(
"Interface %s (intended as tap gateway interface) does not exist"
,
tap_gateway_interface
)
except
KeyError
:
self
.
conf
.
abort
(
"%s must have at least one IPv4 address assigned"
,
tap_gateway_interface
)
result
=
None
for
a
in
addresses
:
address
=
a
[
'addr'
].
split
(
'%'
)[
0
]
netmask
=
a
[
'netmask'
].
split
(
'/'
)[
-
1
]
ip
=
ipaddress
.
IPv4Interface
(
'%s/%s'
%
(
address
,
netmask
))
network
=
ip
.
network
if
not
result
or
network
.
prefixlen
<
result
.
prefixlen
:
result
=
network
return
result
def
getTapIPv4Gateway
(
self
,
_index
):
network
=
self
.
tap_ipv4_network
if
network
:
return
ipaddress
.
IPv4Interface
((
network
[
1
],
network
.
prefixlen
))
def
getTapIPv4Range
(
self
,
index
):
network
=
self
.
tap_ipv4_network
if
network
:
prefixlen
=
conf
.
tap_ipv4_prefix
i
=
index
+
2
if
prefixlen
==
32
else
index
+
1
k
=
1
if
prefixlen
<
31
else
0
addr
=
network
[(
i
<<
(
32
-
prefixlen
))
+
k
]
return
ipaddress
.
IPv4Interface
((
addr
,
prefixlen
))
def
getIPv6Network
(
self
):
def
getIPv6Network
(
self
):
try
:
try
:
addresses
=
netifaces
.
ifaddresses
(
self
.
ipv6_interface
)[
AF_INET6
]
addresses
=
netifaces
.
ifaddresses
(
self
.
ipv6_interface
)[
AF_INET6
]
...
@@ -329,6 +371,22 @@ class Interface(object):
...
@@ -329,6 +371,22 @@ class Interface(object):
result
=
network
result
=
network
return
result
return
result
def
getIPv6LinkLocalAddress
(
self
,
interface
):
try
:
addresses
=
netifaces
.
ifaddresses
(
interface
)[
AF_INET6
]
except
KeyError
:
addresses
=
()
for
a
in
addresses
:
address
=
a
[
'addr'
].
split
(
'%'
)[
0
]
netmask
=
a
[
'netmask'
].
split
(
'/'
)[
-
1
]
ip
=
ipaddress
.
IPv6Interface
(
'%s/%s'
%
(
address
,
netmask
))
if
ip
.
is_link_local
:
return
ip
self
.
conf
.
abort
(
"No link-local IPv6 address found on %s"
,
interface
)
def
getComputerIPv6Addr
(
self
):
def
getComputerIPv6Addr
(
self
):
network
=
self
.
ipv6_network
network
=
self
.
ipv6_network
return
ipaddress
.
IPv6Interface
((
network
[
1
],
network
.
prefixlen
))
return
ipaddress
.
IPv6Interface
((
network
[
1
],
network
.
prefixlen
))
...
@@ -340,12 +398,21 @@ class Interface(object):
...
@@ -340,12 +398,21 @@ class Interface(object):
def
getPartitionIPv6Range
(
self
,
index
):
def
getPartitionIPv6Range
(
self
,
index
):
network
=
self
.
ipv6_network
network
=
self
.
ipv6_network
prefixlen
=
network
.
prefixlen
+
16
prefixlen
=
network
.
prefixlen
+
16
if
prefixlen
>
128
:
if
prefixlen
>
128
:
# XXX move this check elsewhere
self
.
conf
.
abort
(
"IPv6 network %s is too small for IPv6 ranges"
,
network
)
self
.
conf
.
abort
(
"IPv6 network %s is too small for IPv6 ranges"
,
network
)
bits
=
128
-
network
.
prefixlen
bits
=
128
-
network
.
prefixlen
addr
=
network
[(
1
<<
(
bits
-
2
))
+
(
index
<<
(
128
-
prefixlen
))]
addr
=
network
[(
1
<<
(
bits
-
2
))
+
(
index
<<
(
128
-
prefixlen
))]
return
ipaddress
.
IPv6Network
((
addr
,
prefixlen
))
return
ipaddress
.
IPv6Network
((
addr
,
prefixlen
))
def
getTapIPv6Range
(
self
,
index
):
network
=
self
.
ipv6_network
prefixlen
=
network
.
prefixlen
+
16
if
prefixlen
>
128
:
# XXX move this check elsewhere
self
.
conf
.
abort
(
"IPv6 network %s is too small for IPv6 ranges"
,
network
)
bits
=
128
-
network
.
prefixlen
addr
=
network
[(
2
<<
(
bits
-
2
))
+
(
index
<<
(
128
-
prefixlen
))
+
1
]
return
ipaddress
.
IPv6Interface
((
addr
,
prefixlen
))
def
enableIPv6NonLocalBind
(
self
):
def
enableIPv6NonLocalBind
(
self
):
call
([
'sysctl'
,
'net.ipv6.ip_nonlocal_bind=1'
])
call
([
'sysctl'
,
'net.ipv6.ip_nonlocal_bind=1'
])
network
=
str
(
self
.
ipv6_network
)
network
=
str
(
self
.
ipv6_network
)
...
@@ -407,8 +474,8 @@ class Partition(object):
...
@@ -407,8 +474,8 @@ class Partition(object):
ipv4_list
:
list
[
ipaddress
.
IPv4Interface
]
ipv4_list
:
list
[
ipaddress
.
IPv4Interface
]
ipv6_list
:
list
[
ipaddress
.
IPv6Interface
]
ipv6_list
:
list
[
ipaddress
.
IPv6Interface
]
ipv6_range
:
ipaddress
.
IPv6Network
=
None
ipv6_range
:
ipaddress
.
IPv6Network
=
None
tap
:
Tap
tap
:
Tap
=
None
tun
:
Tun
tun
:
Tun
=
None
def
__init__
(
self
,
index
,
computer
,
definition
=
None
):
def
__init__
(
self
,
index
,
computer
,
definition
=
None
):
i
=
str
(
index
)
i
=
str
(
index
)
...
@@ -435,7 +502,12 @@ class Partition(object):
...
@@ -435,7 +502,12 @@ class Partition(object):
self
.
ipv6_range
=
ipaddress
.
IPv6Network
(
ipv6_range
,
strict
=
False
)
self
.
ipv6_range
=
ipaddress
.
IPv6Network
(
ipv6_range
,
strict
=
False
)
elif
conf
.
ipv6_range
:
elif
conf
.
ipv6_range
:
self
.
ipv6_range
=
interface
.
getPartitionIPv6Range
(
index
)
self
.
ipv6_range
=
interface
.
getPartitionIPv6Range
(
index
)
# Tap & Tun
# Tap
tap
=
options
[
'network_interface'
]
if
tap
or
conf
.
create_tap
:
tap
=
tap
or
conf
.
tap_base_name
+
i
self
.
tap
=
Tap
(
tap
,
index
,
computer
,
definition
)
# Tun
# XXX
# XXX
def
format
(
self
,
interface
):
def
format
(
self
,
interface
):
...
@@ -445,6 +517,8 @@ class Partition(object):
...
@@ -445,6 +517,8 @@ class Partition(object):
interface
.
addAddress
(
ip
,
self
.
reference
)
interface
.
addAddress
(
ip
,
self
.
reference
)
for
ip
in
self
.
ipv6_list
:
for
ip
in
self
.
ipv6_list
:
interface
.
addAddress
(
ip
,
self
.
reference
)
interface
.
addAddress
(
ip
,
self
.
reference
)
if
self
.
tap
:
self
.
tap
.
format
(
interface
,
self
.
user
)
def
checkFormat
(
self
,
interface
):
def
checkFormat
(
self
,
interface
):
for
ip
in
self
.
ipv4_list
:
for
ip
in
self
.
ipv4_list
:
...
@@ -510,6 +584,120 @@ class User(object):
...
@@ -510,6 +584,120 @@ class User(object):
return
False
return
False
class
Tap
(
object
):
name
:
str
ipv4_gateway
:
ipaddress
.
IPv4Interface
=
None
ipv4_address
:
ipaddress
.
IPv4Interface
=
None
ipv6_gateway
:
ipaddress
.
IPv6Interface
=
None
ipv6_address
:
ipaddress
.
IPv6Interface
=
None
def
__init__
(
self
,
name
,
index
,
computer
,
definition
):
conf
=
computer
.
conf
interface
=
computer
.
interface
options
=
definition
[
name
]
self
.
name
=
name
# Tap IPv4: tap-to-host gateway and host-to-tap address
ipv4_gateway
=
options
[
'ipv4_gateway'
]
ipv4_address
=
options
[
'ipv4_address'
]
if
bool
(
ipv4_gateway
)
!=
bool
(
ipv4_address
):
conf
.
wrong
(
"Options %r and %r for tap %s must either both be provided or neither"
,
'ipv4_gateway'
,
'ipv4_address'
,
name
)
elif
ipv4_address
:
self
.
ipv4_gateway
=
ipaddress
.
IPv4Interface
(
ipv4_gateway
)
self
.
ipv4_address
=
ipaddress
.
IPv4Interface
(
ipv4_address
)
elif
interface
.
tap_ipv4_network
:
self
.
ipv4_gateway
=
interface
.
getTapIPv4Gateway
(
index
)
self
.
ipv4_address
=
interface
.
getTapIPv4Range
(
index
)
# Tap IPv6: host-to-tap gateway and tap-to-host address
ipv6_gateway
=
options
[
'ipv6_gateway'
]
link_local
=
None
# link-local address resolved later
if
ipv6_gateway
:
self
.
ipv6_gateway
=
ipaddress
.
IPv4Interface
(
ipv6_gateway
)
ipv6_address
=
options
[
'ipv6_address'
]
if
ipv6_address
:
self
.
ipv6_address
=
ipaddress
.
IPv6Interface
(
ipv6_address
)
else
:
self
.
ipv6_address
=
link_local
elif
conf
.
tap_ipv6
:
self
.
ipv6_gateway
=
interface
.
getTapIPv6Range
(
index
)
self
.
ipv6_address
=
link_local
def
format
(
self
,
interface
,
user
):
tap
=
self
.
name
conf
=
interface
.
conf
# Create/ensure TAP interface
self
.
ensureTap
(
user
,
conf
)
# Find link local address if needed
if
self
.
ipv6_gateway
and
not
self
.
ipv6_address
:
self
.
ipv6_address
=
interface
.
getIPv6LinkLocalAddress
(
tap
)
# Create/ensure IPv4 routes
self
.
ensureIPv4Routes
(
conf
)
# Create/ensure IPv6 routes
self
.
ensureIPv6Routes
(
conf
)
def
ensureTap
(
self
,
user
,
conf
):
tap
=
self
.
name
user
=
user
.
name
check_file
=
'/sys/devices/virtual/net/%s/owner'
%
tap
if
os
.
path
.
exists
(
check_file
):
with
open
(
check_file
)
as
f
:
owner_id
=
f
.
read
().
strip
()
try
:
owner_id
=
int
(
owner_id
)
except
ValueError
:
owner_id
=
pwd
.
getpwnam
(
owner_id
).
pw_uid
user_id
=
pwd
.
getpwnam
(
user
).
pw_uid
if
owner_id
!=
user_id
:
conf
.
abort
(
"Wrong owner of TAP %s, expected %s (%s) got %s"
,
self
.
name
,
user_id
,
user
,
owner_id
)
else
:
call
([
'ip'
,
'tuntap'
,
'add'
,
'dev'
,
tap
,
'mode'
,
'TAP'
,
'user'
,
user
])
call
([
'ip'
,
'link'
,
'set'
,
tap
,
'up'
])
def
ensureIPv4Routes
(
self
,
conf
):
tap
=
self
.
name
ipv4
=
self
.
ipv4_address
if
ipv4
:
cidr
=
str
(
ipv4
)
code
,
out
=
call
([
'ip'
,
'route'
,
'show'
,
cidr
],
throw
=
False
)
if
code
!=
0
or
cidr
not
in
out
or
tap
not
in
out
:
call
([
'ip'
,
'route'
,
'add'
,
cidr
,
'dev'
,
tap
])
if
ipv4
.
network
.
prefixlen
<
32
:
network
=
str
(
ipv4
.
network
)
code
,
out
=
call
([
'ip'
,
'route'
,
'show'
,
network
],
throw
=
False
)
if
code
!=
0
or
'dev '
+
tap
not
in
out
:
call
([
'ip'
,
'route'
,
'add'
,
network
,
'dev'
,
tap
,
'via'
,
cidr
])
elif
'via '
+
cidr
not
in
out
:
conf
.
abort
(
"Route to network %s (%s) exists but is not via %s"
,
network
,
tap
,
cidr
)
def
ensureIPv6Routes
(
self
,
conf
):
tap
=
self
.
name
ipv6
=
self
.
ipv6_gateway
if
ipv6
:
cidr
=
str
(
ipv6
)
code
,
out
=
call
([
'ip'
,
'-6'
,
'route'
,
'show'
,
cidr
],
throw
=
False
)
if
code
!=
0
or
cidr
not
in
out
or
tap
not
in
out
:
call
([
'ip'
,
'-6'
,
'route'
,
'add'
,
cidr
,
'dev'
,
tap
])
network
=
str
(
ipv6
.
network
)
code
,
out
=
call
([
'ip'
,
'-6'
,
'route'
,
'show'
,
network
],
throw
=
False
)
if
code
!=
0
or
'dev '
+
tap
not
in
out
:
call
([
'ip'
,
'-6'
,
'route'
,
'add'
,
network
,
'dev'
,
tap
,
'via'
,
cidr
])
elif
'via '
+
cidr
not
in
out
:
conf
.
warn
(
"Route to network %s (%s) exists but is not via %s, fixing it"
,
network
,
tap
,
cidr
)
call
([
'ip'
,
'-6'
,
'route'
,
'del'
,
network
,
'dev'
,
tap
])
call
([
'ip'
,
'-6'
,
'route'
,
'add'
,
network
,
'dev'
,
tap
,
'via'
,
cidr
])
# Utilities
# Utilities
def
call
(
args
,
throw
=
True
):
def
call
(
args
,
throw
=
True
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment