Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
slapos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
92
Merge Requests
92
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
nexedi
slapos
Commits
67fbb6c2
Commit
67fbb6c2
authored
Mar 25, 2024
by
Joanne Hugé
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add tunsplit
parent
db54a9c8
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
177 additions
and
0 deletions
+177
-0
software/ors-amarisoft/ru/tunsplit
software/ors-amarisoft/ru/tunsplit
+177
-0
No files found.
software/ors-amarisoft/ru/tunsplit
0 → 100755
View file @
67fbb6c2
#!/usr/bin/env python
# Copyright (C) 2023-2024 Nexedi SA and Contributors.
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""Program tunsplit brings tun interface into state with several children
interfaces each covering part of original interface address space.
Usage: tunsplit <interface> <nchildren>
"""
# TODO Relying on tunsplit should be removed once SlapOS is improved to provide
# several TAP interfaces to instances. See discussion at
# https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
# for details.
import
netifaces
import
netaddr
from
socket
import
AF_INET6
from
math
import
log2
,
ceil
import
sys
import
subprocess
import
json
# LinkDB represents snapshot of state of all network interfaces.
class
LinkDB
:
def
__init__
(
db
):
db
.
linkv
=
ip
(
'link'
,
'show'
)
# ifget returns information about interface with specified name.
def
ifget
(
db
,
ifname
):
for
link
in
db
.
linkv
:
if
link
[
'ifname'
]
==
ifname
:
return
link
raise
KeyError
(
'interface %r not found'
%
ifname
)
def
main
():
tun
=
sys
.
argv
[
1
]
n
=
int
(
sys
.
argv
[
2
])
assert
n
>=
0
,
n
# determine tun's network address and owner
ldb
=
LinkDB
()
_
=
ldb
.
ifget
(
tun
)
owner
=
_
[
'linkinfo'
][
'info_data'
][
'user'
]
net
=
ifnet6
(
tun
)
print
(
'%s: split %s by %d'
%
(
tun
,
net
,
n
))
# do the split
# with leaving first range for the original tun
subtun_set
=
set
()
for
i
,
subnet
in
enumerate
(
netsplit
(
net
,
1
+
n
)):
if
i
==
0
:
print
(
'preserve %s'
%
subnet
)
continue
# leave this range for original tun
subtun
=
'%s-%d'
%
(
tun
,
i
)
subtun_set
.
add
(
subtun
)
print
(
'-> %s %s'
%
(
subtun
,
subnet
))
def
note
(
msg
):
print
(
' # %s: %s'
%
(
subtun
,
msg
))
# create subtun
try
:
link
=
ldb
.
ifget
(
subtun
)
except
KeyError
:
run
(
'ip'
,
'tuntap'
,
'add'
,
'dev'
,
subtun
,
'mode'
,
'tun'
,
'user'
,
owner
)
link
=
ip
(
'link'
,
'show'
,
'dev'
,
subtun
)[
0
]
else
:
note
(
'already exists'
)
# set it up
if
'UP'
not
in
link
[
'flags'
]:
run
(
'ip'
,
'link'
,
'set'
,
subtun
,
'up'
)
else
:
note
(
'already up'
)
# add subnet address
addrv
=
[]
for
_
in
ip
(
'-6'
,
'addr'
,
'show'
,
'dev'
,
subtun
):
addrv
.
extend
(
_
[
'addr_info'
])
for
addr
in
addrv
:
_
=
netaddr
.
IPNetwork
(
'%s/%s'
%
(
addr
[
'local'
],
addr
[
'prefixlen'
]))
if
_
==
subnet
and
addr
[
'noprefixroute'
]:
note
(
'already has %s addr'
%
str
(
subnet
))
break
else
:
run
(
'ip'
,
'addr'
,
'add'
,
str
(
subnet
),
'dev'
,
subtun
,
'noprefixroute'
)
# add /128 route to subnet::1
rtv
=
ip
(
'-6'
,
'route'
,
'show'
,
'dev'
,
subtun
)
for
rt
in
rtv
:
if
rt
[
'dst'
]
==
str
(
subnet
[
1
])
and
'gateway'
not
in
rt
:
note
(
'already has %s route'
%
str
(
subnet
[
1
]))
break
else
:
run
(
'ip'
,
'route'
,
'add'
,
str
(
subnet
[
1
]),
'dev'
,
subtun
)
# add route to subnet via subnet::1
for
rt
in
rtv
:
if
rt
[
'dst'
]
==
str
(
subnet
)
and
rt
.
get
(
'gateway'
)
==
str
(
subnet
[
1
]):
note
(
'already has %s route'
%
str
(
subnet
))
break
else
:
run
(
'ip'
,
'route'
,
'add'
,
str
(
subnet
),
'dev'
,
subtun
,
'via'
,
str
(
subnet
[
1
]))
# remove other existing children
for
ifname
in
netifaces
.
interfaces
():
if
ifname
.
startswith
(
'%s-'
%
tun
)
and
(
ifname
not
in
subtun_set
):
print
(
'-> del %s'
%
ifname
)
run
(
'ip'
,
'link'
,
'del'
,
ifname
)
# netsplit splits network into n subnetworks.
def
netsplit
(
net
,
n
):
# -> []subnet
# see how much prefix bits we need to take to be able to divide by n
ptake
=
ceil
(
log2
(
n
))
return
list
(
net
.
subnet
(
net
.
prefixlen
+
ptake
)
)[:
n
]
# ifnet6 returns IPv6 network address associated with given interface.
def
ifnet6
(
ifname
):
addr
=
None
net
=
None
prefixlen
=
None
for
iaddr
in
netifaces
.
ifaddresses
(
ifname
)[
AF_INET6
]:
a
=
iaddr
[
'addr'
]
if
'%'
in
a
:
# link-local
a
=
a
.
split
(
'%'
)[
0
]
a
=
netaddr
.
IPAddress
(
a
)
assert
a
.
is_link_local
(),
a
continue
if
addr
is
not
None
:
raise
RuntimeError
(
'%s: multiple addresses: %s and %s'
%
(
ifname
,
addr
,
a
))
addr
=
netaddr
.
IPAddress
(
a
)
netmask
,
plen
=
iaddr
[
'netmask'
].
split
(
'/'
)
prefixlen
=
int
(
plen
)
net
=
netaddr
.
IPNetwork
(
'%s/%d'
%
(
a
,
prefixlen
))
if
addr
is
None
:
raise
RuntimeError
(
'%s: no non link-local addresses'
%
ifname
)
# normalize network
# ex 2401:5180:0:66:a7ff:ffff:ffff:ffff/71 -> 2401:5180:0:66:a600::/71
net
=
net
.
cidr
return
net
# run executes `*argv` as action.
def
run
(
*
argv
):
print
(
' # %s'
%
' '
.
join
(
argv
))
subprocess
.
check_call
(
argv
)
# ip returns decoded output of `ip -details *argv`
def
ip
(
*
argv
):
_
=
subprocess
.
check_output
([
'ip'
,
'-json'
,
'-details'
]
+
list
(
argv
))
return
json
.
loads
(
_
)
if
__name__
==
'__main__'
:
main
()
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