mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
nic_router: replace term "uplink" with "NIC client"
BREAKS CONFIG COMPATIBILITY: This commit changes the configuration interface of the NIC router in a way that may break systems that use the component without proper adjustment! HOW TO ADJUST: At each occurrence of the '<uplink ...>' tag in a NIC router configuration replace the tag name 'uplink' with 'nic-client'. The rest of the tag stays the same. The term "uplink" for network interfaces in the router that have a NIC session client as back end was introduced in a time when Uplink sessions didn't yet exist. Now, they do and, although both an uplink and an Uplink session normally describe a network session between router and network device driver, they are based on two different service types (NIC and Uplink). This can easily cause confusion when integrating the router (the <uplink> is not related to Uplink sessions) or trying to understand its functioning (an 'Uplink' object has nothing to do with the Uplink service). Therefore, this commit introduces the more specific term "NIC client" for an interface that is based on a NIC session requested by the router. This doesn't imply any semantic changes at the NIC router. However, the commit also brings a broader update of the router's README and removes the term "downlink" that was used only in documentation to refer to interfaces backed by a NIC session provided by the router. The term was only associated with this meaning because it is the natural counterpart to an uplink. This isn't appropriate anymore as the terms for interface types have moved to a more technical level. The commit adjusts all scenarios in the basic Genode repositories properly. Fixes #4238
This commit is contained in:
parent
fce525f122
commit
f8953de7ac
@ -196,7 +196,7 @@ proc test_7_config { } {
|
||||
<config>
|
||||
|
||||
<policy label_prefix="t7_d1" domain="downlink" />
|
||||
<uplink domain="uplink" />
|
||||
<nic-client domain="uplink" />
|
||||
|
||||
<domain name="uplink">
|
||||
<nat domain="downlink" tcp-ports="6" />
|
||||
@ -312,7 +312,7 @@ append config {
|
||||
link_state_triggers="yes"
|
||||
interval_sec="60" />
|
||||
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink"
|
||||
interface="10.0.2.55/24"
|
||||
|
@ -231,7 +231,7 @@ append config {
|
||||
|
||||
<report/>
|
||||
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink"
|
||||
interface="10.0.2.55/24"
|
||||
@ -269,7 +269,7 @@ append config {
|
||||
tcp_idle_timeout_sec="30"
|
||||
tcp_max_segm_lifetime_sec="15">
|
||||
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink"
|
||||
interface="10.0.2.55/24"
|
||||
@ -311,7 +311,7 @@ append config {
|
||||
|
||||
<report interval_sec="2" bytes="yes" config="no" />
|
||||
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink"
|
||||
interface="10.0.2.55/24"
|
||||
@ -345,7 +345,7 @@ append config {
|
||||
|
||||
<report interval_sec="2" bytes="yes" config="no" />
|
||||
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink"
|
||||
interface="10.0.2.55/24"
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
<config verbose_domain_state="yes">
|
||||
<default-policy domain="default" />
|
||||
<uplink domain="uplink" />
|
||||
<nic-client domain="uplink" />
|
||||
<domain name="uplink">
|
||||
<nat domain="default"
|
||||
tcp-ports="1000"
|
||||
|
@ -131,7 +131,7 @@ append config {
|
||||
|
||||
<policy label_prefix="ping_1" domain="ping_1"/>
|
||||
<policy label_prefix="ping_2" domain="ping_2"/>
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink">
|
||||
<nat domain="ping_1" icmp-ids="100"/>
|
||||
@ -166,7 +166,7 @@ append config {
|
||||
<config>
|
||||
|
||||
<policy label_prefix="ping_2" domain="ping_2"/>
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink" interface="10.0.4.2/24" gateway="10.0.4.1">
|
||||
<nat domain="ping_2" icmp-ids="100" udp-ports="100"/>
|
||||
|
@ -186,7 +186,7 @@ append_if [expr ![nic_router_2_managed]] config {
|
||||
<config verbose_packets="no">
|
||||
|
||||
<policy label="test_client -> " domain="downlink"/>
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink"/>
|
||||
<domain name="downlink" interface="10.0.3.1/24">
|
||||
|
@ -113,7 +113,7 @@ append config {
|
||||
icmp_idle_timeout_sec="10">
|
||||
|
||||
<policy label_prefix="ping_2" domain="ping_2"/>
|
||||
<uplink domain="uplink"/>
|
||||
<nic-client domain="uplink"/>
|
||||
|
||||
<domain name="uplink">
|
||||
<nat domain="ping_2" icmp-ids="100" udp-ports="100"/>
|
||||
|
@ -1,68 +1,118 @@
|
||||
The 'nic_router' component can be used to achieve a controlled mediation
|
||||
between multiple NIC sessions on network or transport level. NIC sessions are
|
||||
assigned to domains. The rules configured by the user then mediate between
|
||||
these domains. This is a brief overview of the features thereby provided:
|
||||
between multiple NIC and Uplink sessions on network or transport level.
|
||||
Sessions are assigned to router domains. The rules configured by the user then
|
||||
mediate between these domains. This is a brief overview of the features thereby
|
||||
provided:
|
||||
|
||||
* Acting as hub between NIC session with the same domain,
|
||||
* routing of UDP and TCP according to destination IP address and port,
|
||||
* routing of ICMP and IPv4 according to destination IP address,
|
||||
* port forwarding for UDP and TCP,
|
||||
* NAPT for UDP, TCP and ICMP "Echo",
|
||||
* forwarding of ICMP "Destination Unreachable" according to the UDP, TCP or
|
||||
ICMP "Echo" connection it refers to,
|
||||
* acting as ICMP echo server per domain
|
||||
* acting as DHCP server or client per domain,
|
||||
* Acting as hub among NIC and Uplink sessions of the same domain,
|
||||
* routing of IPv4-based UDP and TCP according to destination IP address & port,
|
||||
* routing of ICMPv4 and IPv4 according to destination IP address,
|
||||
* port forwarding for IPv4-based UDP and TCP,
|
||||
* NAPT for IPv4-based UDP, TCP and ICMPv4 echo,
|
||||
* forwarding of ICMPv4 "Destination Unreachable" according to the UDP, TCP or
|
||||
ICMPv4 echo connection it refers to,
|
||||
* acting as ICMPv4 echo server per domain
|
||||
* acting as DHCPv4 server or client per domain,
|
||||
* provide per-domain network statistics via a report session,
|
||||
* print out header information for each packet received or sent,
|
||||
* and be fully re-configurable at runtime.
|
||||
|
||||
|
||||
Basics
|
||||
~~~~~~
|
||||
Functional description
|
||||
======================
|
||||
|
||||
The NIC router can act as server of multiple NIC session clients (downlinks)
|
||||
and at the same time as client of multiple NIC session servers (uplinks).
|
||||
Besides the decision which side initiates the NIC session and provides MAC
|
||||
address respectively link state, uplinks and downlinks are equal to the NIC
|
||||
router.
|
||||
|
||||
The routing algorithm is ultimately controlled through the configuration. NIC
|
||||
sessions are assigned to domains. Each domain represents one subnet and a
|
||||
corresponding routing configuration. The assignment of downlink NIC sessions
|
||||
to domains is controlled through the policy tag that is also known from other
|
||||
Genode components:
|
||||
Interfaces and domains
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
! <policy label_prefix="vlan_" domain="vlan" />
|
||||
The NIC router acts as server for both Genode's NIC and Uplink service. For
|
||||
both services it can manage an arbitrary number of clients at a time.
|
||||
Furthermore, the NIC router can itself request multiple NIC sessions and act as
|
||||
client at them. Among these types of network sessions at the router, there are
|
||||
some minor differences regarding the roles when initializing the session,
|
||||
determining a MAC address or communicating the link state. However, each
|
||||
network session at the router, regardless of its type, is an "interface" (like
|
||||
a network interface in Linux-based systems) and in all regards except the
|
||||
mentioned subtleties treated the same as other interfaces.
|
||||
|
||||
The behavior of the router regarding interfaces is ultimately controlled
|
||||
through the router's configuration. An interface that uses a NIC session that
|
||||
is requested by the router (subsequently called NIC client) can be created
|
||||
using the '<nic-client>' tag with an optional attribute for the session label:
|
||||
|
||||
! <nic-client ... />
|
||||
! <nic-client label="wifi" ... />
|
||||
! <nic-client label="wired" ... />
|
||||
|
||||
An interface that uses a NIC or Uplink session provided by the NIC router
|
||||
(subsequently called NIC server respectively Uplink server) is created whenever
|
||||
a client requests a session of one of the two service types at the router
|
||||
(given the session arguments are valid).
|
||||
|
||||
New interfaces, although having a network session established, remain dead ends
|
||||
towards the router (all packets are dropped by the router) as long as they are
|
||||
not assigned a router domain. A domain is a set of router rules that is applied
|
||||
as a whole to all interfaces assigned to that domain. Domains can be created by
|
||||
using the '<domain>' tag with a unique name:
|
||||
|
||||
! <domain name="default" ...>...<domain/>
|
||||
! <domain name="servers" ...>...<domain/>
|
||||
! <domain name="home_net" ...>...<domain/>
|
||||
! <domain name="travel_net" ...>...<domain/>
|
||||
|
||||
The assignment of NIC clients to domains is controlled through the 'domain'
|
||||
attribute in the '<nic-client>' tag:
|
||||
|
||||
! <nic-client domain="home_net" />
|
||||
! <nic-client label="mobile_drv" domain="travel_net" />
|
||||
! <nic-client label="nic_bridge" domain="home_net" />
|
||||
|
||||
The assignment of NIC and Uplink servers is controlled through the tags
|
||||
'<policy>' and '<default-policy>' that are known from other Genode components:
|
||||
|
||||
! <default-policy domain="default" />
|
||||
! <policy label_prefix="vbox_" domain="servers" />
|
||||
! <policy label_suffix="_server" domain="servers" />
|
||||
! <policy label="nic_bridge_1" domain="wired_bridge" />
|
||||
! <policy label="nic_bridge_2" domain="wired_bridge" />
|
||||
! <policy label="nic_drv -> " domain="home_net" />
|
||||
! <policy label="wifi_drv -> " domain="public" />
|
||||
|
||||
The domain name can be freely choosen but must be unique.
|
||||
The uplink tag instructs the NIC router to create an uplink NIC session that
|
||||
is assigned to the give domain:
|
||||
Each of these tags applies to all NIC and Uplink servers whose session label
|
||||
matches. A domain can be assigned any number of interfaces and interfaces of
|
||||
different types.
|
||||
|
||||
! <uplink domain="wired_bridge" />
|
||||
! <uplink label="wired" domain="wired_bridge" />
|
||||
! <uplink label="wifi" domain="wifi_uplink" />
|
||||
Besides defining the router rules for assigned interfaces, domains have a
|
||||
second purpose. All interfaces assigned the same domain are assumed to be in
|
||||
the same IPv4 subnet. So, from the router's perspective, each domain is a
|
||||
distinct IPv4 subnet. At each domain the router is represented through an
|
||||
IPv4 identity local to the corresponding IPv4 subnet.
|
||||
|
||||
The label is the session label that is used when requesting the uplink NIC
|
||||
session. The label attribute is optional. It is perfectly fine to have a
|
||||
domain with uplinks and downlinks assigned to at the same time. For each
|
||||
domain there must be a domain tag:
|
||||
Besides being an IPv4 peer at each domain. The router is also a hub at each
|
||||
domain: If a network packet must be sent at a domain, it is always sent at all
|
||||
interfaces assigned that domain. If a network packet from a domain addresses a
|
||||
host in the same IPv4 subnet and it's not the routers IPv4 peer in that domain,
|
||||
the packet is again sent at all interfaces assigned that domain
|
||||
|
||||
! <domain name="uplink" interface="10.0.2.55/24" />
|
||||
! <domain name="http_servers" interface="192.168.1.18/24" />
|
||||
! <domain name="imap_servers" interface="192.168.2.17/24" />
|
||||
A good analogy would be that interfaces assigned a single domain are like
|
||||
network cables that are all plugged into the same hub device. There's an
|
||||
additional cable going away from the hub device that directly leads to the
|
||||
routers IPv4 peer in the subnet.
|
||||
|
||||
The 'interface' attribute defines two things at once. First, it tells the
|
||||
router which subnet can be found behind this domain, and second, which IP
|
||||
identity the router shall use in case it has to communicate as itself with
|
||||
the subnet. If the 'interface' attribute is not set in a 'domain' tag, the
|
||||
router acts as DHCP client (Section [Configuring DHCP client functionality]).
|
||||
A domain enters an IPv4 subnet by receing an IPv4 configuration for the routers
|
||||
IPv4 peer in that subnet. This configuration can be obtained statically or
|
||||
dynamically using DHCP. A static configuration is applied if an 'interface'
|
||||
attribute is found in the '<domain>' tag:
|
||||
|
||||
! <domain name="servers" interface="10.0.2.55/24" />
|
||||
|
||||
The attribute defines the IPv4 address of the routers peer and the address
|
||||
prefix length of the corresponding subnet. If the attribute is missing the
|
||||
domain automatically sends DHCP requests at all assigned interfaces in order
|
||||
to obtain a dynamic IPv4 configuration (Section [Configuring DHCP client
|
||||
functionality]).
|
||||
|
||||
Additionaly, the optional 'gateway' attribute can be set for a domain:
|
||||
|
||||
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1" />
|
||||
! <domain name="home_lan" interface="10.0.2.55/24" gateway="10.0.2.1" />
|
||||
|
||||
It defines the standard gateway of the subnet behind this domain. If a packet
|
||||
shall be routed to this domain and its final IP destination does not match
|
||||
@ -155,9 +205,9 @@ These are examples for IP rules:
|
||||
|
||||
! <ip dst="10.0.2.0/24" domain="intranet" />
|
||||
! <ip dst="192.168.1.18/32" domain="my_server" />
|
||||
! <ip dst="0.0.0.0/0" domain="uplink" />
|
||||
! <ip dst="0.0.0.0/0" domain="default" />
|
||||
|
||||
IP rules only apply to IPv4 packets from the session of the surrounding
|
||||
IP rules only apply to IPv4 packets from interfaces of the surrounding
|
||||
domain. The 'dst' attribute is compared with the IP destination of the packet.
|
||||
The rule with the longest prefix match is taken. The packet is then routed to
|
||||
the domain given in the rule.
|
||||
@ -179,9 +229,9 @@ These are examples for ICMP rules:
|
||||
|
||||
! <icmp dst="10.0.2.0/24" domain="intranet" />
|
||||
! <icmp dst="192.168.1.18/32" domain="my_server" />
|
||||
! <icmp dst="0.0.0.0/0" domain="uplink" />
|
||||
! <icmp dst="0.0.0.0/0" domain="default" />
|
||||
|
||||
ICMP rules only apply to ICMP "Echo" packets from sessions of the surrounding
|
||||
ICMP rules only apply to ICMP "Echo" packets from interfaces of the surrounding
|
||||
domain. The 'dst' attribute is compared with the IP destination of the packet.
|
||||
The rule with the longest prefix match is taken. The packet is then routed to
|
||||
the domain given in the rule.
|
||||
@ -212,12 +262,12 @@ rules to get effective:
|
||||
! <permit port="80" domain="http_servers" />
|
||||
! </tcp>
|
||||
! <udp dst="10.0.2.0/24">
|
||||
! <permit-any domain="uplink" />
|
||||
! <permit-any domain="default" />
|
||||
! </udp>
|
||||
|
||||
TCP rules only apply to TCP packets and UDP rules only to UDP packets from the
|
||||
session of the surrounding domain. The 'dst' attribute is compared with the IP
|
||||
destination of the packet. The rule with the longest prefix match is taken.
|
||||
TCP rules only apply to TCP packets and UDP rules only to UDP packets from
|
||||
interfaces of the surrounding domain. The 'dst' attribute is compared with the
|
||||
IP destination of the packet. The rule with the longest prefix match is taken.
|
||||
If the rule contains a 'permit-any' subrule or a 'permit' subrule whose 'port'
|
||||
attribute matches the destination port of the packet, the packet is routed to
|
||||
the domain given in the subrule.
|
||||
@ -242,7 +292,7 @@ These are examples for port-forwarding rules:
|
||||
! <tcp-forward port="80" domain="http_servers" to="192.168.1.18" to_port="1234" />
|
||||
! <udp-forward port="69" domain="tftp_servers" to="192.168.2.23" />
|
||||
|
||||
Port-forwarding rules only apply to packets that come from the session of the
|
||||
Port-forwarding rules only apply to packets that come from interfaces of the
|
||||
surrounding domain and are addressed to the router's IP identity at this domain
|
||||
(Section [Basics]). Amongst those, 'tcp-forward' rules only apply to the TCP
|
||||
packets and 'udp-forward' rules only to the UDP packets. The 'port' attribute
|
||||
@ -273,7 +323,7 @@ Each time a packet gets routed by using a TCP, UDP, ICMP or port-forwarding
|
||||
rule, the router creates a link state. From then on, all packets that belong
|
||||
to the exchange this first packet initiated and come from one of the two
|
||||
involved domains are routed by the link state and not by a rule. The costs for
|
||||
the link state are paid by the session that sent the first packet.
|
||||
the link state are paid by the interface that sent the first packet.
|
||||
|
||||
If a link state exists for a packet, it is unambiguously correlated either
|
||||
through source IP and port plus destination IP and port or, for ICMP, through
|
||||
@ -281,7 +331,7 @@ source and destination IP plus ICMP query ID. This is also the case if the
|
||||
transfer includes NAT no matter of what kind or for which side.
|
||||
|
||||
It is desirable to discard a link state as soon as it is not needed anymore.
|
||||
The more precise this is done, the more efficient can NIC sessions use their
|
||||
The more precise this is done, the more efficient can interfaces use their
|
||||
resources (ports, RAM), and the less is the risk for DoS attacks. Therefore,
|
||||
the NIC router keeps track of the idle time of a link. Idle time means the
|
||||
time passed since the last packet was routed using that link regardless of
|
||||
@ -326,23 +376,23 @@ Configuring NAT
|
||||
In contrast to routing rules that affect packets coming from their domain,
|
||||
NAT rules affect packets that go to their domain:
|
||||
|
||||
! <domain name="uplink" interface="10.0.2.55/24">
|
||||
! <domain name="home_lan" interface="10.0.2.55/24">
|
||||
! <nat domain="http_client" tcp-ports="6" />
|
||||
! </domain>
|
||||
|
||||
This would tell the router to apply NAT for the HTTP client when it speaks to
|
||||
the uplink. This means, it affects all packets from the HTTP client that get
|
||||
routed to the uplink by using a UDP, TCP, or port-forwarding rule respectively
|
||||
a corresponding link state. If this is the case, the packet's source IP
|
||||
address is changed to "10.0.2.55" and the source port is replaced by a free
|
||||
source port of the router. When saying "free source port" this actually means
|
||||
a port that the router currently doesn't use at the destination domain. So,
|
||||
at each domain, the router has two complete port spaces for source NAT
|
||||
the home LAN. This means, it affects all packets from the HTTP client that get
|
||||
routed to the home LAN by using a UDP, TCP, or port-forwarding rule
|
||||
respectively a corresponding link state. If this is the case, the packet's
|
||||
source IP address is changed to "10.0.2.55" and the source port is replaced by
|
||||
a free source port of the router. When saying "free source port" this actually
|
||||
means a port that the router currently doesn't use at the destination domain.
|
||||
So, at each domain, the router has two complete port spaces for source NAT
|
||||
available. One for UDP and one for TCP. Each port space contains the IANA
|
||||
dynamic port range 49152 to 65535.
|
||||
|
||||
As you can see, the NAT rule also has a 'tcp-ports' attribute. It restricts
|
||||
how many TCP source ports of the uplink the HTTP client may use at a time. The
|
||||
As you can see, the NAT rule also has a 'tcp-ports' attribute. It restricts how
|
||||
many TCP source ports of the home LAN the HTTP client may use at a time. The
|
||||
same goes also for UDP:
|
||||
|
||||
! <nat domain="tftp_client" udp-ports="13" />
|
||||
@ -407,7 +457,7 @@ or like this:
|
||||
! <dhcp-server ip_first="10.0.1.80"
|
||||
! ip_last="10.0.1.100"
|
||||
! ip_lease_time_sec="3600"
|
||||
! dns_server_from="uplink" />
|
||||
! dns_server_from="home_lan" />
|
||||
! ...
|
||||
!
|
||||
! </domain>
|
||||
@ -560,7 +610,7 @@ described below:
|
||||
A boolean value that controls whether the attributes 'rx_bytes' and 'tx_bytes'
|
||||
of the <domain> tag in the state report are generated. These attributes
|
||||
provide the number of bytes that were sent respectively recieved at all
|
||||
sessions/interfaces of that domain beginning with the creation of the domain.
|
||||
interfaces of that domain beginning with the creation of the domain.
|
||||
|
||||
'stats'
|
||||
|
||||
@ -581,26 +631,26 @@ itself terminated the connection).
|
||||
The <destroyed> subtag can occur in <*-links> subtags in both the <interface>
|
||||
and the <domain> tag. It shows the number of links of the protocol type that
|
||||
once existed but were already destroyed. In case of the <interface> tag, the
|
||||
corresponding session/interface still exists at that domain. Once the
|
||||
session/interface gets destroyed or disconnected from the domain, the number is
|
||||
transferred to the <destroyed> subtag in the <*-links> subtag in the <domain>
|
||||
tag. I.e. the <destroyed> subtags in <*-links> subtags in the <domain> tag
|
||||
provide the number of destroyed links that can't be correlated anymore to any
|
||||
session/interface of the domain.
|
||||
corresponding interface still exists at that domain. Once the interface gets
|
||||
destroyed or disconnected from the domain, the number is transferred to the
|
||||
<destroyed> subtag in the <*-links> subtag in the <domain> tag. I.e. the
|
||||
<destroyed> subtags in <*-links> subtags in the <domain> tag provide the number
|
||||
of destroyed links that can't be correlated anymore to any interface of the
|
||||
domain.
|
||||
|
||||
The same applies for the <refused_*> subtags of <*-links> tags. They show the
|
||||
number of links that couldn't be established through the router because of the
|
||||
lack of a certain quota. Thereby, <refused_for_ram> refers to a lack of RAM
|
||||
quota at the source session/interface of the link. The <refused_for_ports>, at
|
||||
the other hand, refers to a lack of UDP/TCP-NAT-ports respectively
|
||||
ICMP-NAT-IDs at the target domain (see section [Configuring NAT]).
|
||||
quota at the source interface of the link. The <refused_for_ports>, at the
|
||||
other hand, refers to a lack of UDP/TCP-NAT-ports respectively ICMP-NAT-IDs at
|
||||
the target domain (see section [Configuring NAT]).
|
||||
|
||||
The subtags <arp-waiters>, and <dhcp-allocations> list the number of still
|
||||
active (<active> subtag) and already destroyed (<destroyed> subtag) objects
|
||||
for pending ARP requests respectively DHCP-address allocations at a
|
||||
session/interface when in an <interface> tag. When in a <domain> tag, they
|
||||
only list the number of already destroyed objects of these types that can't
|
||||
be correlated anymore to any session/interface of the domain.
|
||||
active (<active> subtag) and already destroyed (<destroyed> subtag) objects for
|
||||
pending ARP requests respectively DHCP-address allocations at an interface when
|
||||
in an <interface> tag. When in a <domain> tag, they only list the number of
|
||||
already destroyed objects of these types that can't be correlated anymore to
|
||||
any interface of the domain.
|
||||
|
||||
'quota'
|
||||
|
||||
@ -609,18 +659,18 @@ A boolean value that controls whether the subtags <ram> and <cap> of the
|
||||
tag are generated.
|
||||
|
||||
The former two show the capability quota respectively RAM quota of the router
|
||||
that isn't accounted to any of the sessions/interfaces connected to the router
|
||||
(i.e., the routers "own" quota). The 'quota' attribute denotes the total amount
|
||||
of quota available to the router, the 'used' attribute the part of the total
|
||||
that isn't accounted to any of the interfaces connected to the router (i.e.,
|
||||
the routers "own" quota). The 'quota' attribute denotes the total amount of
|
||||
quota available to the router, the 'used' attribute the part of the total
|
||||
amount of quota that is currently spent or in use, and the 'shared' attribute
|
||||
the part of the spent quota that, although it was spent for session/interface-
|
||||
specific things, can't be accounted to one session/interface technical reasons.
|
||||
the part of the spent quota that, although it was spent for interface-specific
|
||||
things, can't be accounted to one interface for technical reasons.
|
||||
|
||||
The subtags <ram-quota> and <cap-quota> in the <interface> tag, however, show
|
||||
the capability quota respectively RAM quota accounted to that
|
||||
session/interface. The 'limit' and 'used' attributes are equal to the 'quota'
|
||||
and 'used' attributes of the <ram> and <cap> subtags of the <state> tag. The
|
||||
'avail' attribute contains simply the 'limit' value minus the 'used' value.
|
||||
the capability quota respectively RAM quota accounted to that interface. The
|
||||
'limit' and 'used' attributes are equal to the 'quota' and 'used' attributes of
|
||||
the <ram> and <cap> subtags of the <state> tag. The 'avail' attribute contains
|
||||
simply the 'limit' value minus the 'used' value.
|
||||
|
||||
'config'
|
||||
|
||||
@ -645,17 +695,16 @@ changes. I.e., whenever the IP configuration of any domain has changed.
|
||||
|
||||
A boolean value that controls whether the attribute 'link_state' of the
|
||||
<interface> tag is generated. The 'link_state' attribute shows the current real
|
||||
link state of the session/interface. Note, that in case of a NIC session, this
|
||||
is not necessarily the same value as the one that the session client sees. For
|
||||
more information about that, refer to [Behavior regarding the NIC-session link
|
||||
state].
|
||||
link state of the interface. Note, that in case of a NIC server, this is not
|
||||
necessarily the same value as the one that the session client sees. For more
|
||||
information on that, refer to [Behavior regarding the NIC-session link state].
|
||||
|
||||
'link_state_triggers'
|
||||
|
||||
A boolean value that controls whether to enforce sending a report each time the
|
||||
state that is controlled through the 'link_state' attribute of the <report> tag
|
||||
changes. I.e., whenever the real link state of any session/interface at the
|
||||
router has changed.
|
||||
changes. I.e., whenever the real link state of any interface at the router has
|
||||
changed.
|
||||
|
||||
'interval_sec'
|
||||
|
||||
@ -690,8 +739,8 @@ affects all domains without a <domain> local value.
|
||||
|
||||
! <config verbose_domain_state="no">
|
||||
|
||||
Whether to log most important changes in the state of a domain (number of NIC
|
||||
sessions connected, current IPv4 config).
|
||||
Whether to log most important changes in the state of a domain (number of
|
||||
interfaces assigned, current IPv4 config).
|
||||
|
||||
|
||||
Other configuration attributes
|
||||
@ -701,16 +750,16 @@ Other configuration attributes
|
||||
Maximum number of packets handled per signal
|
||||
--------------------------------------------
|
||||
|
||||
If possible, the NIC router normally handles multiple packets from a NIC
|
||||
session per signal. However, if one NIC session has a high packet rate and a
|
||||
big buffer, this can lead to starvation of the other NIC sessions. Thus, the
|
||||
maximum number of packets handled per signal is limited by default. This limit
|
||||
can be configured as follows (default value shown):
|
||||
If possible, the NIC router normally handles multiple packets from an interface
|
||||
per signal. However, if one interface has a high packet rate and a big buffer,
|
||||
this can lead to starvation of the other interfaces. Thus, the maximum number
|
||||
of packets handled per signal is limited by default. This limit can be
|
||||
configured as follows (default value shown):
|
||||
|
||||
! <config max_packets_per_signal="32">
|
||||
|
||||
When set to zero, the limit is deactivated, meaning that the router always
|
||||
handles all available packets of a NIC session.
|
||||
handles all available packets of an interface.
|
||||
|
||||
|
||||
Disable requesting address resolutions via ARP
|
||||
@ -736,14 +785,14 @@ interfaces with the 'NOARP' flag set.
|
||||
Behavior regarding the NIC-session link state
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
At downlinks, the NIC router applies a feedback-driven state machine for the
|
||||
At NIC servers, the NIC router applies a feedback-driven state machine for the
|
||||
link state in order to ensure that clients recognize state transitions. This
|
||||
means that the NIC router guarantees that a new link state remains fixed until
|
||||
the client has read it using the corresponding RPC at the NIC-session
|
||||
interface. If, in the meantime, further "up" and "down" edges occur, they are
|
||||
memorized and executed as soon as the former state has been read by the
|
||||
client. Such postponed link state edges are merged in a way that they result in
|
||||
two contrary edges at a max. The following diagrams demonstrate this:
|
||||
memorized and executed as soon as the former state has been read by the client.
|
||||
Such postponed link state edges are merged in a way that they result in two
|
||||
contrary edges at a max. The following diagrams demonstrate this:
|
||||
|
||||
|
||||
! client reads: 0 1 0 1 0 1
|
||||
@ -774,13 +823,17 @@ two contrary edges at a max. The following diagrams demonstrate this:
|
||||
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
========
|
||||
|
||||
|
||||
Scripted scenarios
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In-action examples of how to use the router are provided through the following
|
||||
automated run scripts:
|
||||
|
||||
* libports/run/nic_router.run (basic functionality)
|
||||
* dde_linux/run/nic_router_uplinks.run (dynamically switching uplinks)
|
||||
* dde_linux/run/nic_router_uplinks.run (dynamically switching between wifi and wired driver)
|
||||
* os/run/ping_nic_router.run (ICMP routing)
|
||||
* os/run/nic_router_disable_arp.run ('use_arp' configuration flag)
|
||||
* os/run/nic_router_dhcp_unmanaged.run (DHCP + link states without a manager)
|
||||
@ -789,12 +842,12 @@ automated run scripts:
|
||||
* os/run/nic_router_stress.run (client misbehaving on session level)
|
||||
|
||||
The rest of this section will list and explain some smaller configuration
|
||||
snippets. The environment for these examples shall be as follows. There are
|
||||
two virtual subnets 192.168.1.0/24 and 192.168.2.0/24 that connect as Virtnet A
|
||||
and B to the router. The standard gateway of the virtual networks is the NIC
|
||||
router with IP 192.168.*.1 . The router's uplink leads to the NIC driver that
|
||||
connects the machine with your home network 10.0.2.0/24. Your home network is
|
||||
connected to the internet through its standard gateway 10.0.2.1 .
|
||||
snippets. The environment for these examples shall be as follows. There are two
|
||||
virtual subnets 192.168.1.0/24 and 192.168.2.0/24 that connect as Virtnet A and
|
||||
B to the router. The standard gateway of the virtual networks is the NIC router
|
||||
with IP 192.168.*.1 . The NIC driver connects the machine with your home
|
||||
network 10.0.2.0/24 and has an Uplink session to the NIC router. Your home
|
||||
network is connected to the internet through its standard gateway 10.0.2.1 .
|
||||
|
||||
|
||||
Connecting local networks
|
||||
@ -807,33 +860,33 @@ following configuration:
|
||||
|
||||
! <policy label_prefix="virtnet_a" domain="virtnet_a" />
|
||||
! <policy label_prefix="virtnet_b" domain="virtnet_b" />
|
||||
! <uplink domain="uplink" />
|
||||
! <policy label_prefic="nic_drv" domain="home_lan" />
|
||||
!
|
||||
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1/24">
|
||||
! <domain name="home_lan" interface="10.0.2.55/24" gateway="10.0.2.1/24">
|
||||
! <ip dst="192.168.1.0/24" domain="virtnet_a"/>
|
||||
! <ip dst="192.168.2.0/24" domain="virtnet_b"/>
|
||||
! </domain>
|
||||
!
|
||||
! <domain name="virtnet_a" interface="192.168.1.1/24">
|
||||
! <ip dst="192.168.2.0/24" domain="virtnet_b"/>
|
||||
! <ip dst="0.0.0.0/0" domain="uplink"/>
|
||||
! <ip dst="0.0.0.0/0" domain="home_lan"/>
|
||||
! </domain>
|
||||
!
|
||||
! <domain name="virtnet_b" interface="192.168.2.1/24">
|
||||
! <ip dst="192.168.1.0/24" domain="virtnet_a"/>
|
||||
! <ip dst="0.0.0.0/0" domain="uplink"/>
|
||||
! <ip dst="0.0.0.0/0" domain="home_lan"/>
|
||||
! </domain>
|
||||
|
||||
IP packets from Virtnet A and uplink that target an IP address 192.168.2.* are
|
||||
routed to Virtnet B. IP packets from Virtnet B and uplink that target an IP
|
||||
address 192.168.1.* are routed to Virtnet A. Packets that are addressed to
|
||||
hosts in the same local network should never reach the router as they can be
|
||||
transmitted directly. If there's a packet from one of the virtual networks
|
||||
that doesn't target 192.168.1.* or 192.168.2.*, the IP 0.0.0.0/0 rules route
|
||||
them to the uplink. If these packets target an IP 10.0.2.*, the router sends
|
||||
them directly to the host in your home network. Otherwise, the router sends
|
||||
them to your gateway 10.0.2.1 . Note that none of the packets is modified on
|
||||
layer 2 or higher, so, no NAT is done by the router to hide the virtual
|
||||
IP packets from Virtnet A and the home LAN that target an IP address
|
||||
192.168.2.* are routed to Virtnet B. IP packets from Virtnet B and the home LAN
|
||||
that target an IP address 192.168.1.* are routed to Virtnet A. Packets that are
|
||||
addressed to hosts in the same local network should never reach the router as
|
||||
they can be transmitted directly. If there's a packet from one of the virtual
|
||||
networks that doesn't target 192.168.1.* or 192.168.2.*, the IP 0.0.0.0/0 rules
|
||||
route them to the home LAN. If these packets target an IP 10.0.2.*, the router
|
||||
sends them directly to the host in your home network. Otherwise, the router
|
||||
sends them to your gateway 10.0.2.1 . Note that none of the packets is modified
|
||||
on layer 2 or higher, so, no NAT is done by the router to hide the virtual
|
||||
networks.
|
||||
|
||||
|
||||
@ -847,48 +900,49 @@ internet. The router would have the following configuration:
|
||||
|
||||
! <policy label_prefix="virtnet_a" domain="virtnet_a" />
|
||||
! <policy label_prefix="virtnet_b" domain="virtnet_b" />
|
||||
! <uplink domain="uplink" />
|
||||
! <policy label_prefic="nic_drv" domain="home_lan" />
|
||||
!
|
||||
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1/24">
|
||||
! <domain name="home_lan" interface="10.0.2.55/24" gateway="10.0.2.1/24">
|
||||
! <nat domain="virtnet_a" tcp_ports="1000" udp_ports="1000">
|
||||
! </domain>
|
||||
!
|
||||
! <domain name="virtnet_a" interface="192.168.1.1/24">
|
||||
! <tcp dst="10.0.2.0/24"><permit-any domain="uplink" /></tcp>
|
||||
! <udp dst="10.0.2.0/24"><permit-any domain="uplink" /></udp>
|
||||
! <tcp dst="10.0.2.0/24"><permit-any domain="home_lan" /></tcp>
|
||||
! <udp dst="10.0.2.0/24"><permit-any domain="home_lan" /></udp>
|
||||
! <tcp dst="0.0.0.0/0">
|
||||
! <permit port="443" domain="uplink" />
|
||||
! <permit port="993" domain="uplink" />
|
||||
! <permit port="443" domain="home_lan" />
|
||||
! <permit port="993" domain="home_lan" />
|
||||
! </tcp>
|
||||
! </domain>
|
||||
|
||||
From the packets that come from Virtnet A, those that target an IP 10.0.2.*
|
||||
are routed to the uplink without inspecting the port. At the uplink, the
|
||||
router notices that it shall apply NAT for Virtnet A. It replaces the source
|
||||
IP with 10.0.2.55 and allocates one of its uplink source ports for the
|
||||
exchange. On replies to Virtnet-A packets from the home network, the router
|
||||
translates IP and port back using the corresponding link state. For packets
|
||||
from Virtnet A that target other IPs, only the 0.0.0.0/0 rule applies and only
|
||||
if the packet targets TCP port 443 or 993. Both ports route the packet to the
|
||||
uplink where, again, NAT is applied and the packets are sent to the gateway
|
||||
10.0.2.1 .
|
||||
From the packets that come from Virtnet A, those that target an IP 10.0.2.* are
|
||||
routed to the home LAN without inspecting the port. At the home LAN, the router
|
||||
notices that it shall apply NAT for Virtnet A. It replaces the source IP with
|
||||
10.0.2.55 and allocates a free source port of the home LAN for the exchange. On
|
||||
replies to Virtnet-A packets from the home network, the router translates IP
|
||||
and port back using the corresponding link state. For packets from Virtnet A
|
||||
that target other IPs, only the 0.0.0.0/0 rule applies and only if the packet
|
||||
targets TCP port 443 or 993. Both ports route the packet to the home LAN where,
|
||||
again, NAT is applied and the packets are sent to the gateway 10.0.2.1 .
|
||||
|
||||
|
||||
Servers in a private network
|
||||
----------------------------
|
||||
|
||||
In this example, we assume that there are three servers in Virtnet A. An HTTP
|
||||
server at port 80 with IP 192.168.1.2, a GOPHER server at port 70 with IP
|
||||
192.168.1.3, and a TFTP server at port 69 with IP 192.168.1.4 . Now you want
|
||||
the servers (and only them) to be reachable to the home network via the
|
||||
router's IP and to the internet via your gateway. The router would have the
|
||||
following configuration:
|
||||
In this example, we assume that the NIC router has to request a NIC session at
|
||||
a NIC bridge instance in order to reach the NIC driver (the driver serves the
|
||||
NIC bridge) and gain access to the home LAN. Furthermore, there are three
|
||||
servers in Virtnet A. An HTTP server at port 80 with IP 192.168.1.2, a GOPHER
|
||||
server at port 70 with IP 192.168.1.3, and a TFTP server at port 69 with IP
|
||||
192.168.1.4 . Now, you want the servers (and only them) to be reachable to the
|
||||
home network via the router's IP and to the internet via your gateway. The
|
||||
router would have the following configuration:
|
||||
|
||||
! <policy label_prefix="virtnet_a" domain="virtnet_a" />
|
||||
! <policy label_prefix="virtnet_b" domain="virtnet_b" />
|
||||
! <uplink domain="uplink" />
|
||||
! <nic-client domain="home_lan" />
|
||||
!
|
||||
! <domain name="uplink" interface="10.0.2.55/24" gateway="10.0.2.1">
|
||||
! <domain name="home_lan" interface="10.0.2.55/24" gateway="10.0.2.1">
|
||||
! <tcp-forward port="80" domain="virtnet_a" to="192.168.1.2" />
|
||||
! <tcp-forward port="70" domain="virtnet_a" to="192.168.1.3" />
|
||||
! <udp-forward port="69" domain="virtnet_a" to="192.168.1.4" to_port="2048"/>
|
||||
@ -897,7 +951,7 @@ following configuration:
|
||||
! <domain name="virtnet_a" interface="192.168.1.1/24" />
|
||||
! <domain name="virtnet_b" interface="192.168.1.1/24" />
|
||||
|
||||
Amongst the packets that come from the uplink, only those that are addressed
|
||||
Amongst the packets that come from the home LAN, only those that are addressed
|
||||
to 10.0.2.55 and TCP port 80, TCP port 70, or UDP port 69 are forwarded.
|
||||
All these packets are forwarded to Virtnet A. But beforehand, their IP
|
||||
destination is adapted. TCP-port-80 packets are redirected to 192.168.1.2,
|
||||
@ -905,7 +959,7 @@ TCP-port-70 packets to 192.168.1.3, and UDP-port-69 packets to
|
||||
192.168.1.4:2048.
|
||||
|
||||
Amongst the packets that come from Virtnet A, only those that match a link
|
||||
state at the uplink are forwarded, because the Virtnet-A domain contains no
|
||||
rules. Thus, Virtnet A can only talk to the uplink in the context of
|
||||
state at the home LAN are forwarded, because the Virtnet-A domain contains no
|
||||
rules. Thus, Virtnet A can only talk to the home LAN in the context of
|
||||
TCP-connections or UDP pseudo-connections that were opened by clients behind
|
||||
the uplink. The servers IP addresses never leave Virtnet A.
|
||||
the home LAN. The servers IP addresses never leave Virtnet A.
|
||||
|
@ -84,12 +84,12 @@
|
||||
</xs:complexType>
|
||||
</xs:element><!-- policy -->
|
||||
|
||||
<xs:element name="uplink">
|
||||
<xs:element name="nic-client">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="label" type="Session_label" />
|
||||
<xs:attribute name="domain" type="Domain_name" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- uplink -->
|
||||
</xs:element><!-- nic-client -->
|
||||
|
||||
<xs:element name="domain">
|
||||
<xs:complexType>
|
||||
|
@ -48,14 +48,14 @@ Configuration::Configuration(Xml_node const node,
|
||||
{ }
|
||||
|
||||
|
||||
void Configuration::_invalid_uplink(Uplink &uplink,
|
||||
char const *reason)
|
||||
void Configuration::_invalid_nic_client(Nic_client &nic_client,
|
||||
char const *reason)
|
||||
{
|
||||
if (_verbose) {
|
||||
log("[", uplink.domain(), "] invalid uplink: ", uplink, " (", reason, ")"); }
|
||||
log("[", nic_client.domain(), "] invalid NIC client: ", nic_client, " (", reason, ")"); }
|
||||
|
||||
_uplinks.remove(uplink);
|
||||
destroy(_alloc, &uplink);
|
||||
_nic_clients.remove(nic_client);
|
||||
destroy(_alloc, &nic_client);
|
||||
}
|
||||
|
||||
|
||||
@ -160,26 +160,26 @@ Configuration::Configuration(Env &env,
|
||||
}
|
||||
catch (Genode::Xml_node::Nonexistent_sub_node) { }
|
||||
|
||||
/* initialize uplinks */
|
||||
_node.for_each_sub_node("uplink", [&] (Xml_node const node) {
|
||||
/* initialize NIC clients */
|
||||
_node.for_each_sub_node("nic-client", [&] (Xml_node const node) {
|
||||
try {
|
||||
Uplink &uplink = *new (_alloc)
|
||||
Uplink { node, alloc, old_config._uplinks, env, timer,
|
||||
interfaces, *this };
|
||||
Nic_client &nic_client = *new (_alloc)
|
||||
Nic_client { node, alloc, old_config._nic_clients, env, timer,
|
||||
interfaces, *this };
|
||||
|
||||
try { _uplinks.insert(uplink); }
|
||||
catch (Uplink_tree::Name_not_unique exception) {
|
||||
_invalid_uplink(uplink, "label not unique");
|
||||
_invalid_uplink(exception.object, "label not unique");
|
||||
try { _nic_clients.insert(nic_client); }
|
||||
catch (Nic_client_tree::Name_not_unique exception) {
|
||||
_invalid_nic_client(nic_client, "label not unique");
|
||||
_invalid_nic_client(exception.object, "label not unique");
|
||||
}
|
||||
}
|
||||
catch (Uplink::Invalid) { }
|
||||
catch (Nic_client::Invalid) { }
|
||||
});
|
||||
/*
|
||||
* Destroy old uplinks to ensure that uplink interfaces that were not
|
||||
* Destroy old NIC clients to ensure that NIC client interfaces that were not
|
||||
* re-used are not re-attached to the new domains.
|
||||
*/
|
||||
old_config._uplinks.destroy_each(_alloc);
|
||||
old_config._nic_clients.destroy_each(_alloc);
|
||||
}
|
||||
|
||||
|
||||
@ -203,8 +203,8 @@ void Configuration::start_reporting()
|
||||
|
||||
Configuration::~Configuration()
|
||||
{
|
||||
/* destroy uplinks */
|
||||
_uplinks.destroy_each(_alloc);
|
||||
/* destroy NIC clients */
|
||||
_nic_clients.destroy_each(_alloc);
|
||||
|
||||
/* destroy reporter */
|
||||
try { destroy(_alloc, &_reporter()); }
|
||||
|
@ -17,7 +17,7 @@
|
||||
/* local includes */
|
||||
#include <domain.h>
|
||||
#include <report.h>
|
||||
#include <uplink.h>
|
||||
#include <nic_client.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/duration.h>
|
||||
@ -47,14 +47,14 @@ class Net::Configuration
|
||||
Genode::Microseconds const _udp_idle_timeout;
|
||||
Genode::Microseconds const _tcp_idle_timeout;
|
||||
Genode::Microseconds const _tcp_max_segm_lifetime;
|
||||
Pointer<Report> _report { };
|
||||
Pointer<Genode::Reporter> _reporter { };
|
||||
Domain_tree _domains { };
|
||||
Uplink_tree _uplinks { };
|
||||
Pointer<Report> _report { };
|
||||
Pointer<Genode::Reporter> _reporter { };
|
||||
Domain_tree _domains { };
|
||||
Nic_client_tree _nic_clients { };
|
||||
Genode::Xml_node const _node;
|
||||
|
||||
void _invalid_uplink(Uplink &uplink,
|
||||
char const *reason);
|
||||
void _invalid_nic_client(Nic_client &nic_client,
|
||||
char const *reason);
|
||||
|
||||
void _invalid_domain(Domain &domain,
|
||||
char const *reason);
|
||||
|
@ -20,7 +20,6 @@
|
||||
/* local includes */
|
||||
#include <nic_session_root.h>
|
||||
#include <uplink_session_root.h>
|
||||
#include <uplink.h>
|
||||
#include <configuration.h>
|
||||
|
||||
using namespace Net;
|
||||
|
180
repos/os/src/server/nic_router/nic_client.cc
Normal file
180
repos/os/src/server/nic_router/nic_client.cc
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* \brief Interface back-end using a NIC session requested by the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
|
||||
/* local includes */
|
||||
#include <nic_client.h>
|
||||
#include <configuration.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*********************
|
||||
** Nic_client_base **
|
||||
*********************/
|
||||
|
||||
Net::Nic_client_base::Nic_client_base(Xml_node const &node)
|
||||
:
|
||||
_label { node.attribute_value("label", Session_label::String()) },
|
||||
_domain { node.attribute_value("domain", Domain_name()) }
|
||||
{ }
|
||||
|
||||
|
||||
/****************
|
||||
** Nic_client **
|
||||
****************/
|
||||
|
||||
void Nic_client::_invalid(char const *reason) const
|
||||
{
|
||||
if (_config.verbose()) {
|
||||
log("[", domain(), "] invalid NIC client: ", *this, " (", reason, ")"); }
|
||||
|
||||
throw Invalid();
|
||||
}
|
||||
|
||||
|
||||
Net::Nic_client::Nic_client(Xml_node const &node,
|
||||
Allocator &alloc,
|
||||
Nic_client_tree &old_nic_clients,
|
||||
Env &env,
|
||||
Timer::Connection &timer,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config)
|
||||
:
|
||||
Nic_client_base { node },
|
||||
Avl_string_base { label().string() },
|
||||
_alloc { alloc },
|
||||
_config { config }
|
||||
{
|
||||
/* if an interface with this label already exists, reuse it */
|
||||
try {
|
||||
Nic_client &old_nic_client = old_nic_clients.find_by_name(label());
|
||||
Nic_client_interface &interface = old_nic_client._interface();
|
||||
old_nic_client._interface = Pointer<Nic_client_interface>();
|
||||
interface.domain_name(domain());
|
||||
_interface = interface;
|
||||
}
|
||||
/* if not, create a new one */
|
||||
catch (Nic_client_tree::No_match) {
|
||||
if (config.verbose()) {
|
||||
log("[", domain(), "] create NIC client: ", *this); }
|
||||
|
||||
try {
|
||||
_interface = *new (_alloc)
|
||||
Nic_client_interface { env, timer, alloc, interfaces, config,
|
||||
domain(), label() };
|
||||
}
|
||||
catch (Insufficient_ram_quota) { _invalid("NIC session RAM quota"); }
|
||||
catch (Insufficient_cap_quota) { _invalid("NIC session CAP quota"); }
|
||||
catch (Service_denied) { _invalid("NIC session denied"); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Net::Nic_client::~Nic_client()
|
||||
{
|
||||
/* if the interface was yet not reused by another NIC client, destroy it */
|
||||
try {
|
||||
Nic_client_interface &interface = _interface();
|
||||
if (_config.verbose()) {
|
||||
log("[", domain(), "] destroy NIC client: ", *this); }
|
||||
|
||||
destroy(_alloc, &interface);
|
||||
}
|
||||
catch (Pointer<Nic_client_interface>::Invalid) { }
|
||||
}
|
||||
|
||||
|
||||
void Net::Nic_client::print(Output &output) const
|
||||
{
|
||||
if (label() == Session_label()) {
|
||||
Genode::print(output, "?"); }
|
||||
else {
|
||||
Genode::print(output, label()); }
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
** Nic_client_interface_base **
|
||||
*******************************/
|
||||
|
||||
Net::Nic_client_interface_base::
|
||||
Nic_client_interface_base(Domain_name const &domain_name,
|
||||
Session_label const &label,
|
||||
bool const &session_link_state)
|
||||
:
|
||||
_domain_name { domain_name },
|
||||
_label { label },
|
||||
_session_link_state { session_link_state }
|
||||
{ }
|
||||
|
||||
|
||||
void Net::Nic_client_interface_base::interface_unready()
|
||||
{
|
||||
_interface_ready = false;
|
||||
};
|
||||
|
||||
|
||||
void Net::Nic_client_interface_base::interface_ready()
|
||||
{
|
||||
_interface_ready = true;
|
||||
};
|
||||
|
||||
|
||||
bool Net::Nic_client_interface_base::interface_link_state() const
|
||||
{
|
||||
return _interface_ready && _session_link_state;
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
** Nic_client_interface **
|
||||
**************************/
|
||||
|
||||
Net::Nic_client_interface::Nic_client_interface(Env &env,
|
||||
Timer::Connection &timer,
|
||||
Genode::Allocator &alloc,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config,
|
||||
Domain_name const &domain_name,
|
||||
Session_label const &label)
|
||||
:
|
||||
Nic_client_interface_base { domain_name, label, _session_link_state },
|
||||
Nic::Packet_allocator { &alloc },
|
||||
Nic::Connection { env, this, BUF_SIZE, BUF_SIZE, label.string() },
|
||||
_session_link_state_handler { env.ep(), *this,
|
||||
&Nic_client_interface::_handle_session_link_state },
|
||||
_interface { env.ep(), timer, mac_address(), alloc,
|
||||
Mac_address(), config, interfaces, *rx(), *tx(),
|
||||
*this }
|
||||
{
|
||||
/* install packet stream signal handlers */
|
||||
rx_channel()->sigh_ready_to_ack (_interface.sink_ack());
|
||||
rx_channel()->sigh_packet_avail (_interface.sink_submit());
|
||||
tx_channel()->sigh_ack_avail (_interface.source_ack());
|
||||
tx_channel()->sigh_ready_to_submit(_interface.source_submit());
|
||||
|
||||
/* initialize link state handling */
|
||||
Nic::Connection::link_state_sigh(_session_link_state_handler);
|
||||
_session_link_state = Nic::Connection::link_state();
|
||||
}
|
||||
|
||||
|
||||
void Net::Nic_client_interface::_handle_session_link_state()
|
||||
{
|
||||
_session_link_state = Nic::Connection::link_state();
|
||||
_interface.handle_interface_link_state();
|
||||
}
|
178
repos/os/src/server/nic_router/nic_client.h
Normal file
178
repos/os/src/server/nic_router/nic_client.h
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* \brief Interface back-end using a NIC session requested by the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _NIC_CLIENT_H_
|
||||
#define _NIC_CLIENT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <nic_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
|
||||
/* local includes */
|
||||
#include <avl_string_tree.h>
|
||||
#include <interface.h>
|
||||
#include <ipv4_address_prefix.h>
|
||||
|
||||
namespace Net {
|
||||
|
||||
using Domain_name = Genode::String<160>;
|
||||
class Nic_client_base;
|
||||
class Nic_client;
|
||||
class Nic_client_tree;
|
||||
class Nic_client_interface_base;
|
||||
class Nic_client_interface;
|
||||
}
|
||||
|
||||
|
||||
class Net::Nic_client_tree
|
||||
:
|
||||
public Avl_string_tree<Nic_client, Genode::Session_label>
|
||||
{ };
|
||||
|
||||
|
||||
class Net::Nic_client_base
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Session_label const _label;
|
||||
Domain_name const _domain;
|
||||
|
||||
public:
|
||||
|
||||
Nic_client_base(Genode::Xml_node const &node);
|
||||
|
||||
virtual ~Nic_client_base() { }
|
||||
|
||||
|
||||
/**************
|
||||
** Acessors **
|
||||
**************/
|
||||
|
||||
Genode::Session_label const &label() const { return _label; }
|
||||
Domain_name const &domain() const { return _domain; }
|
||||
};
|
||||
|
||||
|
||||
class Net::Nic_client : public Nic_client_base,
|
||||
private Genode::Avl_string_base
|
||||
{
|
||||
friend class Avl_string_tree<Nic_client, Genode::Session_label>;
|
||||
friend class Genode::List<Nic_client>;
|
||||
|
||||
private:
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
Configuration const &_config;
|
||||
Pointer<Nic_client_interface> _interface { };
|
||||
|
||||
void _invalid(char const *reason) const;
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid : Genode::Exception { };
|
||||
|
||||
Nic_client(Genode::Xml_node const &node,
|
||||
Genode::Allocator &alloc,
|
||||
Nic_client_tree &old_nic_clients,
|
||||
Genode::Env &env,
|
||||
Timer::Connection &timer,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config);
|
||||
|
||||
~Nic_client();
|
||||
|
||||
|
||||
/*********
|
||||
** log **
|
||||
*********/
|
||||
|
||||
void print(Genode::Output &output) const;
|
||||
};
|
||||
|
||||
|
||||
class Net::Nic_client_interface_base : public Interface_policy
|
||||
{
|
||||
private:
|
||||
|
||||
Const_reference<Domain_name> _domain_name;
|
||||
Genode::Session_label const _label;
|
||||
bool const &_session_link_state;
|
||||
bool _interface_ready { false };
|
||||
|
||||
|
||||
/***************************
|
||||
** Net::Interface_policy **
|
||||
***************************/
|
||||
|
||||
Domain_name determine_domain_name() const override { return _domain_name(); };
|
||||
void handle_config(Configuration const &) override { }
|
||||
Genode::Session_label const &label() const override { return _label; }
|
||||
void interface_unready() override;
|
||||
void interface_ready() override;
|
||||
bool interface_link_state() const override;
|
||||
|
||||
public:
|
||||
|
||||
Nic_client_interface_base(Domain_name const &domain_name,
|
||||
Genode::Session_label const &label,
|
||||
bool const &session_link_state);
|
||||
|
||||
virtual ~Nic_client_interface_base() { }
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
void domain_name(Domain_name const &v) { _domain_name = v; }
|
||||
};
|
||||
|
||||
|
||||
class Net::Nic_client_interface : public Nic_client_interface_base,
|
||||
public Nic::Packet_allocator,
|
||||
public Nic::Connection
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
|
||||
BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE,
|
||||
};
|
||||
|
||||
bool _session_link_state { false };
|
||||
Genode::Signal_handler<Nic_client_interface> _session_link_state_handler;
|
||||
Net::Interface _interface;
|
||||
|
||||
Ipv4_address_prefix _read_interface();
|
||||
|
||||
void _handle_session_link_state();
|
||||
|
||||
public:
|
||||
|
||||
Nic_client_interface(Genode::Env &env,
|
||||
Timer::Connection &timer,
|
||||
Genode::Allocator &alloc,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config,
|
||||
Domain_name const &domain_name,
|
||||
Genode::Session_label const &label);
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Mac_address const &router_mac() const { return _interface.router_mac(); }
|
||||
};
|
||||
|
||||
#endif /* _NIC_CLIENT_H_ */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief NIC session server role of the NIC router
|
||||
* \brief Interface back-end using NIC sessions provided by the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief NIC session server role of the NIC router
|
||||
* \brief Interface back-end using NIC sessions provided by the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@ SRC_CC += \
|
||||
nat_rule.cc \
|
||||
main.cc \
|
||||
ipv4_config.cc \
|
||||
uplink.cc \
|
||||
nic_client.cc \
|
||||
interface.cc \
|
||||
arp_cache.cc \
|
||||
configuration.cc \
|
||||
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* \brief Uplink interface in form of a NIC session component
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
|
||||
/* local includes */
|
||||
#include <uplink.h>
|
||||
#include <configuration.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*****************
|
||||
** Uplink_base **
|
||||
*****************/
|
||||
|
||||
Net::Uplink_base::Uplink_base(Xml_node const &node)
|
||||
:
|
||||
_label { node.attribute_value("label", Session_label::String()) },
|
||||
_domain { node.attribute_value("domain", Domain_name()) }
|
||||
{ }
|
||||
|
||||
|
||||
/************
|
||||
** Uplink **
|
||||
************/
|
||||
|
||||
void Uplink::_invalid(char const *reason) const
|
||||
{
|
||||
if (_config.verbose()) {
|
||||
log("[", domain(), "] invalid uplink: ", *this, " (", reason, ")"); }
|
||||
|
||||
throw Invalid();
|
||||
}
|
||||
|
||||
|
||||
Net::Uplink::Uplink(Xml_node const &node,
|
||||
Allocator &alloc,
|
||||
Uplink_tree &old_uplinks,
|
||||
Env &env,
|
||||
Timer::Connection &timer,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config)
|
||||
:
|
||||
Uplink_base { node },
|
||||
Avl_string_base { label().string() },
|
||||
_alloc { alloc },
|
||||
_config { config }
|
||||
{
|
||||
/* if an interface with this label already exists, reuse it */
|
||||
try {
|
||||
Uplink &old_uplink = old_uplinks.find_by_name(label());
|
||||
Uplink_interface &interface = old_uplink._interface();
|
||||
old_uplink._interface = Pointer<Uplink_interface>();
|
||||
interface.domain_name(domain());
|
||||
_interface = interface;
|
||||
}
|
||||
/* if not, create a new one */
|
||||
catch (Uplink_tree::No_match) {
|
||||
if (config.verbose()) {
|
||||
log("[", domain(), "] request uplink NIC session: ", *this); }
|
||||
|
||||
try {
|
||||
_interface = *new (_alloc)
|
||||
Uplink_interface { env, timer, alloc, interfaces, config,
|
||||
domain(), label() };
|
||||
}
|
||||
catch (Insufficient_ram_quota) { _invalid("NIC session RAM quota"); }
|
||||
catch (Insufficient_cap_quota) { _invalid("NIC session CAP quota"); }
|
||||
catch (Service_denied) { _invalid("NIC session denied"); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Net::Uplink::~Uplink()
|
||||
{
|
||||
/* if the interface was yet not reused by another uplink, destroy it */
|
||||
try {
|
||||
Uplink_interface &interface = _interface();
|
||||
if (_config.verbose()) {
|
||||
log("[", domain(), "] close uplink NIC session: ", *this); }
|
||||
|
||||
destroy(_alloc, &interface);
|
||||
}
|
||||
catch (Pointer<Uplink_interface>::Invalid) { }
|
||||
}
|
||||
|
||||
|
||||
void Net::Uplink::print(Output &output) const
|
||||
{
|
||||
if (label() == Session_label()) {
|
||||
Genode::print(output, "?"); }
|
||||
else {
|
||||
Genode::print(output, label()); }
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** Uplink_interface_base **
|
||||
***************************/
|
||||
|
||||
Net::Uplink_interface_base::Uplink_interface_base(Domain_name const &domain_name,
|
||||
Session_label const &label,
|
||||
bool const &session_link_state)
|
||||
:
|
||||
_domain_name { domain_name },
|
||||
_label { label },
|
||||
_session_link_state { session_link_state }
|
||||
{ }
|
||||
|
||||
|
||||
void Net::Uplink_interface_base::interface_unready()
|
||||
{
|
||||
_interface_ready = false;
|
||||
};
|
||||
|
||||
|
||||
void Net::Uplink_interface_base::interface_ready()
|
||||
{
|
||||
_interface_ready = true;
|
||||
};
|
||||
|
||||
|
||||
bool Net::Uplink_interface_base::interface_link_state() const
|
||||
{
|
||||
return _interface_ready && _session_link_state;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
** Uplink_interface **
|
||||
**********************/
|
||||
|
||||
Net::Uplink_interface::Uplink_interface(Env &env,
|
||||
Timer::Connection &timer,
|
||||
Genode::Allocator &alloc,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config,
|
||||
Domain_name const &domain_name,
|
||||
Session_label const &label)
|
||||
:
|
||||
Uplink_interface_base { domain_name, label, _session_link_state },
|
||||
Nic::Packet_allocator { &alloc },
|
||||
Nic::Connection { env, this, BUF_SIZE, BUF_SIZE, label.string() },
|
||||
_session_link_state_handler { env.ep(), *this,
|
||||
&Uplink_interface::_handle_session_link_state },
|
||||
_interface { env.ep(), timer, mac_address(), alloc,
|
||||
Mac_address(), config, interfaces, *rx(), *tx(),
|
||||
*this }
|
||||
{
|
||||
/* install packet stream signal handlers */
|
||||
rx_channel()->sigh_ready_to_ack (_interface.sink_ack());
|
||||
rx_channel()->sigh_packet_avail (_interface.sink_submit());
|
||||
tx_channel()->sigh_ack_avail (_interface.source_ack());
|
||||
tx_channel()->sigh_ready_to_submit(_interface.source_submit());
|
||||
|
||||
/* initialize link state handling */
|
||||
Nic::Connection::link_state_sigh(_session_link_state_handler);
|
||||
_session_link_state = Nic::Connection::link_state();
|
||||
}
|
||||
|
||||
|
||||
void Net::Uplink_interface::_handle_session_link_state()
|
||||
{
|
||||
_session_link_state = Nic::Connection::link_state();
|
||||
_interface.handle_interface_link_state();
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* \brief Uplink interface in form of a NIC session component
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _UPLINK_H_
|
||||
#define _UPLINK_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <nic_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
|
||||
/* local includes */
|
||||
#include <avl_string_tree.h>
|
||||
#include <interface.h>
|
||||
#include <ipv4_address_prefix.h>
|
||||
|
||||
namespace Net {
|
||||
|
||||
using Domain_name = Genode::String<160>;
|
||||
class Uplink_base;
|
||||
class Uplink;
|
||||
class Uplink_tree;
|
||||
class Uplink_interface_base;
|
||||
class Uplink_interface;
|
||||
}
|
||||
|
||||
|
||||
class Net::Uplink_tree
|
||||
:
|
||||
public Avl_string_tree<Uplink, Genode::Session_label>
|
||||
{ };
|
||||
|
||||
|
||||
class Net::Uplink_base
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Session_label const _label;
|
||||
Domain_name const _domain;
|
||||
|
||||
public:
|
||||
|
||||
Uplink_base(Genode::Xml_node const &node);
|
||||
|
||||
virtual ~Uplink_base() { }
|
||||
|
||||
|
||||
/**************
|
||||
** Acessors **
|
||||
**************/
|
||||
|
||||
Genode::Session_label const &label() const { return _label; }
|
||||
Domain_name const &domain() const { return _domain; }
|
||||
};
|
||||
|
||||
|
||||
struct Net::Uplink : public Uplink_base,
|
||||
private Genode::Avl_string_base
|
||||
{
|
||||
friend class Avl_string_tree<Uplink, Genode::Session_label>;
|
||||
friend class Genode::List<Uplink>;
|
||||
|
||||
private:
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
Configuration const &_config;
|
||||
Pointer<Uplink_interface> _interface { };
|
||||
|
||||
void _invalid(char const *reason) const;
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid : Genode::Exception { };
|
||||
|
||||
Uplink(Genode::Xml_node const &node,
|
||||
Genode::Allocator &alloc,
|
||||
Uplink_tree &old_uplinks,
|
||||
Genode::Env &env,
|
||||
Timer::Connection &timer,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config);
|
||||
|
||||
~Uplink();
|
||||
|
||||
|
||||
/*********
|
||||
** log **
|
||||
*********/
|
||||
|
||||
void print(Genode::Output &output) const;
|
||||
};
|
||||
|
||||
|
||||
class Net::Uplink_interface_base : public Interface_policy
|
||||
{
|
||||
private:
|
||||
|
||||
Const_reference<Domain_name> _domain_name;
|
||||
Genode::Session_label const _label;
|
||||
bool const &_session_link_state;
|
||||
bool _interface_ready { false };
|
||||
|
||||
|
||||
/***************************
|
||||
** Net::Interface_policy **
|
||||
***************************/
|
||||
|
||||
Domain_name determine_domain_name() const override { return _domain_name(); };
|
||||
void handle_config(Configuration const &) override { }
|
||||
Genode::Session_label const &label() const override { return _label; }
|
||||
void interface_unready() override;
|
||||
void interface_ready() override;
|
||||
bool interface_link_state() const override;
|
||||
|
||||
public:
|
||||
|
||||
Uplink_interface_base(Domain_name const &domain_name,
|
||||
Genode::Session_label const &label,
|
||||
bool const &session_link_state);
|
||||
|
||||
virtual ~Uplink_interface_base() { }
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
void domain_name(Domain_name const &v) { _domain_name = v; }
|
||||
};
|
||||
|
||||
|
||||
class Net::Uplink_interface : public Uplink_interface_base,
|
||||
public Nic::Packet_allocator,
|
||||
public Nic::Connection
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
|
||||
BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE,
|
||||
};
|
||||
|
||||
bool _session_link_state { false };
|
||||
Genode::Signal_handler<Uplink_interface> _session_link_state_handler;
|
||||
Net::Interface _interface;
|
||||
|
||||
Ipv4_address_prefix _read_interface();
|
||||
|
||||
void _handle_session_link_state();
|
||||
|
||||
public:
|
||||
|
||||
Uplink_interface(Genode::Env &env,
|
||||
Timer::Connection &timer,
|
||||
Genode::Allocator &alloc,
|
||||
Interface_list &interfaces,
|
||||
Configuration &config,
|
||||
Domain_name const &domain_name,
|
||||
Genode::Session_label const &label);
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Mac_address const &router_mac() const { return _interface.router_mac(); }
|
||||
};
|
||||
|
||||
#endif /* _UPLINK_H_ */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief Downlink interface in form of an Uplink session component
|
||||
* \brief Interface back-end using Uplink sessions provided by the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief Downlink interface in form of an Uplink session component
|
||||
* \brief Interface back-end using Uplink sessions provided by the NIC router
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-23
|
||||
*/
|
||||
|
@ -147,7 +147,7 @@ void Local::Main::_handle_router_state()
|
||||
xml.attribute("label", "test_client -> ");
|
||||
xml.attribute("domain", "downlink");
|
||||
});
|
||||
xml.node("uplink", [&] () {
|
||||
xml.node("nic-client", [&] () {
|
||||
xml.attribute("domain", "uplink");
|
||||
});
|
||||
xml.node("domain", [&] () {
|
||||
|
Loading…
x
Reference in New Issue
Block a user