Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
nemu3
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
1
Issues
1
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
nexedi
nemu3
Commits
a6425bf6
Commit
a6425bf6
authored
Jul 20, 2010
by
Martín Ferrari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Interface class and friends, almost ready.
parent
374f505c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
190 additions
and
22 deletions
+190
-22
src/netns/interface.py
src/netns/interface.py
+161
-3
src/netns/node.py
src/netns/node.py
+29
-19
No files found.
src/netns/interface.py
View file @
a6425bf6
# vim:ts=4:sw=4:et:ai:sts=4
# vim:ts=4:sw=4:et:ai:sts=4
import
re
,
socket
import
os
,
re
,
socket
,
weakref
import
netns.iproute
__all__
=
[
'
interface'
,
'address'
,
'ipv6address'
,
'ipv4address
'
]
__all__
=
[
'
NodeInterface'
,
'P2PInterface'
,
'ExternalInterface
'
]
# helpers
# FIXME: should nodes register interfaces?
class
_Interface
(
object
):
"""Just a base class for the *Interface classes: assign names and handle
destruction."""
_nextid
=
0
@
staticmethod
def
_gen_next_id
():
n
=
_Interface
.
_nextid
_Interface
.
_nextid
+=
1
return
n
@
staticmethod
def
_gen_if_name
():
n
=
_Interface
.
_gen_next_id
()
# Max 15 chars
return
"NETNSif-%.4x%.3x"
%
(
os
.
getpid
(),
n
)
class
_NSInterface
(
_Interface
):
"""Add user-facing methods for interfaces that go into a netns."""
def
destroy
(
self
):
try
:
# no need to check _ns_if, exceptions are ignored anyways
self
.
_slave
.
del_if
(
self
.
_ns_if
)
except
:
# Maybe it already went away, or the slave died. Anyway, better
# ignore the error
pass
def
__del__
(
self
):
self
.
destroy
()
@
property
def
index
(
self
):
return
self
.
_ns_if
# some black magic to automatically get/set interface attributes
def
__getattr__
(
self
,
name
):
if
(
name
not
in
interface
.
changeable_attributes
):
raise
AttributeError
(
"'%s' object has no attribute '%s'"
%
(
self
.
__class__
.
__name__
,
name
))
# I can use attributes now, as long as they are not in
# changeable_attributes
iface
=
self
.
_slave
.
get_if_data
(
self
.
_ns_if
)
return
getattr
(
iface
,
name
)
def
__setattr__
(
self
,
name
,
value
):
if
(
name
not
in
interface
.
changeable_attributes
):
super
(
_Interface
,
self
).
__setattr__
(
name
,
value
)
return
iface
=
interface
(
index
=
self
.
_ns_if
)
setattr
(
iface
,
name
,
value
)
return
self
.
_slave
.
set_if
(
iface
)
def
add_v4_address
(
self
,
address
,
prefix_len
,
broadcast
=
None
):
pass
def
add_v6_address
(
self
,
address
,
prefix_len
):
pass
class
NodeInterface
(
_NSInterface
):
"""Class to create and handle a virtual interface inside a name space, it
can be connected to a Link object with emulation of link
characteristics."""
def
__init__
(
self
,
node
):
"""Create a new interface. `node' is the name space in which this
interface should be put."""
if1
=
interface
(
name
=
self
.
_gen_if_name
())
if2
=
interface
(
name
=
self
.
_gen_if_name
())
ctl
,
ns
=
netns
.
iproute
.
create_if_pair
(
if1
,
if2
)
try
:
netns
.
iproute
.
change_netns
(
ns
,
node
.
pid
)
except
:
netns
.
iproute
.
del_if
(
ctl
)
# the other interface should go away automatically
raise
self
.
_ctl_if
=
ctl
.
index
self
.
_ns_if
=
ns
.
index
self
.
_slave
=
node
.
_slave
node
.
_add_interface
(
self
)
@
property
def
control_index
(
self
):
return
self
.
_ctl_if
class
P2PInterface
(
_NSInterface
):
"""Class to create and handle point-to-point interfaces between name
spaces, without using Link objects. Those do not allow any kind of traffic
shaping.
As two interfaces need to be created, instead of using the class
constructor, use the P2PInterface.create_pair() static method."""
@
staticmethod
def
create_pair
(
node1
,
node2
):
"""Create and return a pair of connected P2PInterface objects, assigned
to name spaces represented by `node1' and `node2'."""
if1
=
interface
(
name
=
P2PInterface
.
_gen_if_name
())
if2
=
interface
(
name
=
P2PInterface
.
_gen_if_name
())
pair
=
netns
.
iproute
.
create_if_pair
(
if1
,
if2
)
try
:
netns
.
iproute
.
change_netns
(
pair
[
0
],
node1
.
pid
)
netns
.
iproute
.
change_netns
(
pair
[
1
],
node2
.
pid
)
except
:
netns
.
iproute
.
del_if
(
pair
[
0
])
# the other interface should go away automatically
raise
o1
=
P2PInterface
.
__new__
(
P2PInterface
)
o1
.
_slave
=
node1
.
_slave
o1
.
_ns_if
=
pair
[
0
].
index
node1
.
_add_interface
(
o1
)
o2
=
P2PInterface
.
__new__
(
P2PInterface
)
o2
.
_slave
=
node2
.
_slave
o2
.
_ns_if
=
pair
[
1
].
index
node2
.
_add_interface
(
o2
)
return
o1
,
o2
def
__init__
(
self
):
"Not to be called directly. Use P2PInterface.create_pair()"
raise
RuntimeError
(
P2PInterface
.
__init__
.
__doc__
)
class
ExternalInterface
(
_Interface
):
"""Class to handle already existing interfaces. This kind of interfaces can
only be connected to Link objects and not assigned to a name space.
On destruction, the code will try to restore the interface to the state it
was in before being imported into netns."""
def
__init__
(
self
,
iface
):
iface
=
netns
.
iproute
.
get_if
(
iface
)
self
.
_ctl_if
=
iface
.
index
self
.
_original_state
=
iface
def
destroy
(
self
):
# override: restore as much as possible
try
:
netns
.
iproute
.
set_if
(
self
.
_original_state
)
except
:
pass
@
property
def
control_index
(
self
):
return
self
.
_ctl_if
# don't look after this :-)
# helpers
def
_any_to_bool
(
any
):
def
_any_to_bool
(
any
):
if
isinstance
(
any
,
bool
):
if
isinstance
(
any
,
bool
):
return
any
return
any
...
@@ -33,9 +176,14 @@ def _make_setter(attr, conv = lambda x: x):
...
@@ -33,9 +176,14 @@ def _make_setter(attr, conv = lambda x: x):
setattr
(
self
,
attr
,
conv
(
value
))
setattr
(
self
,
attr
,
conv
(
value
))
return
setter
return
setter
# classes for internal use
class
interface
(
object
):
class
interface
(
object
):
"""Class for internal use. It is mostly a data container used to easily
pass information around; with some convenience methods."""
@
classmethod
@
classmethod
def
parse_ip
(
cls
,
line
):
def
parse_ip
(
cls
,
line
):
"""Parse a line of ouput from `ip -o link list' and construct and
return a new object with the data."""
match
=
re
.
search
(
r'^(\
d+): (
\S+): <(\
S+)> m
tu (\
d+) qdisc
\S+'
+
match
=
re
.
search
(
r'^(\
d+): (
\S+): <(\
S+)> m
tu (\
d+) qdisc
\S+'
+
r'.*link/\
S+ ([
0-9a-f:]+) brd ([0-9a-f:]+)'
,
line
)
r'.*link/\
S+ ([
0-9a-f:]+) brd ([0-9a-f:]+)'
,
line
)
flags
=
match
.
group
(
3
).
split
(
","
)
flags
=
match
.
group
(
3
).
split
(
","
)
...
@@ -49,6 +197,10 @@ class interface(object):
...
@@ -49,6 +197,10 @@ class interface(object):
broadcast
=
match
.
group
(
6
),
broadcast
=
match
.
group
(
6
),
multicast
=
"MULTICAST"
in
flags
)
multicast
=
"MULTICAST"
in
flags
)
# information for other parts of the code
changeable_attributes
=
[
"name"
,
"mtu"
,
"lladdr"
,
"broadcast"
,
"up"
,
"multicast"
,
"arp"
]
index
=
property
(
_make_getter
(
"_index"
),
_make_setter
(
"_index"
,
int
))
index
=
property
(
_make_getter
(
"_index"
),
_make_setter
(
"_index"
,
int
))
up
=
property
(
_make_getter
(
"_up"
),
_make_setter
(
"_up"
,
_any_to_bool
))
up
=
property
(
_make_getter
(
"_up"
),
_make_setter
(
"_up"
,
_any_to_bool
))
mtu
=
property
(
_make_getter
(
"_mtu"
),
_make_setter
(
"_mtu"
,
int
))
mtu
=
property
(
_make_getter
(
"_mtu"
),
_make_setter
(
"_mtu"
,
int
))
...
@@ -90,8 +242,14 @@ class interface(object):
...
@@ -90,8 +242,14 @@ class interface(object):
multicast
,
arp
)
multicast
,
arp
)
class
address
(
object
):
class
address
(
object
):
"""Class for internal use. It is mostly a data container used to easily
pass information around; with some convenience methods. __eq__ and __hash__
are defined just to be able to easily find duplicated addresses."""
@
classmethod
@
classmethod
def
parse_ip
(
cls
,
line
):
def
parse_ip
(
cls
,
line
):
"""Parse a line of ouput from `ip -o addr list' (after trimming the
index and interface name) and construct and return a new object with
the data."""
match
=
re
.
search
(
r'^inet ([0-9.]+)/(\
d+)(?:
brd ([0-9.]+))?'
,
line
)
match
=
re
.
search
(
r'^inet ([0-9.]+)/(\
d+)(?:
brd ([0-9.]+))?'
,
line
)
if
match
!=
None
:
if
match
!=
None
:
return
ipv4address
(
return
ipv4address
(
...
...
src/netns/node.py
View file @
a6425bf6
...
@@ -4,13 +4,15 @@
...
@@ -4,13 +4,15 @@
import
os
,
socket
,
sys
,
traceback
,
unshare
,
weakref
import
os
,
socket
,
sys
,
traceback
,
unshare
,
weakref
import
netns.protocol
,
netns
.
subprocess_
import
netns.protocol
,
netns
.
subprocess_
__all__
=
[
'Node'
,
'get_nodes'
]
class
Node
(
object
):
class
Node
(
object
):
_nodes
=
weakref
.
WeakValueDictionary
()
_nodes
=
weakref
.
WeakValueDictionary
()
_nextnode
=
0
_nextnode
=
0
@
class
method
@
static
method
def
get_nodes
(
cls
):
def
get_nodes
():
s
=
sorted
(
Node
.
_nodes
.
items
(),
key
=
lambda
x
:
x
[
0
])
s
=
sorted
(
Node
.
_nodes
.
items
(),
key
=
lambda
x
:
x
[
0
])
return
[
x
[
1
]
for
x
in
s
]
return
[
x
[
1
]
for
x
in
s
]
def
__init__
(
self
,
debug
=
False
,
nonetns
=
False
):
def
__init__
(
self
,
debug
=
False
,
nonetns
=
False
):
"""Create a new node in the emulation. Implemented as a separate
"""Create a new node in the emulation. Implemented as a separate
...
@@ -23,6 +25,8 @@ class Node(object):
...
@@ -23,6 +25,8 @@ class Node(object):
self
.
_pid
=
pid
self
.
_pid
=
pid
self
.
_slave
=
netns
.
protocol
.
Client
(
fd
,
fd
,
debug
)
self
.
_slave
=
netns
.
protocol
.
Client
(
fd
,
fd
,
debug
)
self
.
_processes
=
weakref
.
WeakValueDictionary
()
self
.
_processes
=
weakref
.
WeakValueDictionary
()
self
.
_interfaces
=
weakref
.
WeakValueDictionary
()
Node
.
_nodes
[
Node
.
_nextnode
]
=
self
Node
.
_nodes
[
Node
.
_nextnode
]
=
self
Node
.
_nextnode
+=
1
Node
.
_nextnode
+=
1
...
@@ -33,10 +37,17 @@ class Node(object):
...
@@ -33,10 +37,17 @@ class Node(object):
for
p
in
self
.
_processes
.
values
():
for
p
in
self
.
_processes
.
values
():
p
.
destroy
()
p
.
destroy
()
del
self
.
_processes
del
self
.
_processes
for
i
in
self
.
_interfaces
.
values
():
i
.
destroy
()
del
self
.
_interfaces
del
self
.
_pid
del
self
.
_pid
self
.
_slave
.
shutdown
()
self
.
_slave
.
shutdown
()
del
self
.
_slave
del
self
.
_slave
@
property
def
pid
(
self
):
return
self
.
_pid
# Subprocesses
# Subprocesses
def
_add_subprocess
(
self
,
subprocess
):
def
_add_subprocess
(
self
,
subprocess
):
self
.
_processes
[
subprocess
.
pid
]
=
subprocess
self
.
_processes
[
subprocess
.
pid
]
=
subprocess
...
@@ -52,12 +63,20 @@ class Node(object):
...
@@ -52,12 +63,20 @@ class Node(object):
def
backticks_raise
(
self
,
*
kargs
,
**
kwargs
):
def
backticks_raise
(
self
,
*
kargs
,
**
kwargs
):
return
netns
.
subprocess_
.
backticks_raise
(
self
,
*
kargs
,
**
kwargs
)
return
netns
.
subprocess_
.
backticks_raise
(
self
,
*
kargs
,
**
kwargs
)
@
property
# Interfaces
def
pid
(
self
):
def
_add_interface
(
self
,
interface
):
return
self
.
_pid
self
.
_interfaces
[
interface
.
index
]
=
interface
def
add_if
(
self
,
**
kwargs
):
i
=
netns
.
interface
.
NodeInterface
(
self
)
for
k
,
v
in
kwargs
.
items
():
setattr
(
i
,
k
,
v
)
return
i
def
get_interfaces
(
self
):
# FIXME: loopback et al
s
=
sorted
(
self
.
_interfaces
.
items
(),
key
=
lambda
x
:
x
[
0
])
return
[
x
[
1
]
for
x
in
s
]
def
add_if
(
self
,
mac_address
=
None
,
mtu
=
None
):
return
Interface
(
mac_address
,
mtu
)
def
add_route
(
self
,
prefix
,
prefix_len
,
nexthop
=
None
,
interface
=
None
):
def
add_route
(
self
,
prefix
,
prefix_len
,
nexthop
=
None
,
interface
=
None
):
assert
nexthop
or
interface
assert
nexthop
or
interface
def
add_default_route
(
self
,
nexthop
,
interface
=
None
):
def
add_default_route
(
self
,
nexthop
,
interface
=
None
):
...
@@ -65,17 +84,6 @@ class Node(object):
...
@@ -65,17 +84,6 @@ class Node(object):
def
get_routes
(
self
):
def
get_routes
(
self
):
return
set
()
return
set
()
class
Interface
(
object
):
def
__init__
(
self
,
mac_address
=
None
,
mtu
=
None
):
self
.
name
=
None
self
.
mac_address
=
mac_address
self
.
mtu
=
mtu
self
.
valid
=
True
def
add_v4_address
(
self
,
address
,
prefix_len
,
broadcast
=
None
):
pass
def
add_v6_address
(
self
,
address
,
prefix_len
):
pass
# Handle the creation of the child; parent gets (fd, pid), child creates and
# Handle the creation of the child; parent gets (fd, pid), child creates and
# runs a Server(); never returns.
# runs a Server(); never returns.
# Requires CAP_SYS_ADMIN privileges to run.
# Requires CAP_SYS_ADMIN privileges to run.
...
@@ -111,3 +119,5 @@ def _start_child(debug, nonetns):
...
@@ -111,3 +119,5 @@ def _start_child(debug, nonetns):
os
.
_exit
(
0
)
os
.
_exit
(
0
)
# NOTREACHED
# NOTREACHED
get_nodes
=
Node
.
get_nodes
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