mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-18 20:57:56 +00:00
Add Route REST API (fixes #96)
Consists of a single, one-shot request, GET /restful/route/all.json, for the time being. A "newsince" request can be added later. Add a Markdown tech doc that specifies the new Route REST API and update the MDP tech doc a little. Add the 'routerestful' test script, which uses some test utility fuctions that have been factored out of the 'routing' test script into the new testdefs_routing.sh. Add the new 'allrestful' test script.
This commit is contained in:
parent
1a091aa8a1
commit
8345d896a6
@ -8,11 +8,11 @@ make it particularly suitable for use in Ad Hoc wireless networks, which can
|
|||||||
suffer high levels of packet loss due to weak signal, interference and
|
suffer high levels of packet loss due to weak signal, interference and
|
||||||
congestion.
|
congestion.
|
||||||
|
|
||||||
MDP carries [messages](#mdp-message) from [sender](#sender) to
|
MDP carries [messages](#mdp-message) from [sender](#sender) to [recipient](#recipient)
|
||||||
[recipient](#recipient) [node](#node)s, or [broadcasts](#broadcast) to all
|
[node](#node)s, or [broadcasts](#broadcast) to all nodes. MDP guarantees that
|
||||||
nodes. MDP guarantees that message contents will be correct if delivered, but
|
message contents will be correct if delivered, but does not guarantee exactly
|
||||||
does not guarantee one delivery (messages may be lost or delivered more than
|
one delivery (messages may be lost or delivered more than once), arrival time,
|
||||||
once), arrival time, or message order.
|
or message order.
|
||||||
|
|
||||||
MDP can be carried over any wireless or wired data link, whether a shared
|
MDP can be carried over any wireless or wired data link, whether a shared
|
||||||
medium (eg, [CSMA/CA][] used in [Wi-Fi][]) or a dedicated medium (eg, [AX.25
|
medium (eg, [CSMA/CA][] used in [Wi-Fi][]) or a dedicated medium (eg, [AX.25
|
||||||
@ -78,7 +78,7 @@ called a *subscriber*), then strictly speaking, the Serval mesh network could
|
|||||||
be said to carry messages between *users* not between *devices*. There is
|
be said to carry messages between *users* not between *devices*. There is
|
||||||
nothing to prevent a [keyring][] entry from being copied from one device to
|
nothing to prevent a [keyring][] entry from being copied from one device to
|
||||||
another, thus it is possible for two or more devices to have the same MDP
|
another, thus it is possible for two or more devices to have the same MDP
|
||||||
Address. At present, Serval routing does not handle this case, so it could
|
Address. At present, Serval [routing][] does not handle this case, so it could
|
||||||
cause unwanted effects such as route flapping or dropped messages.
|
cause unwanted effects such as route flapping or dropped messages.
|
||||||
|
|
||||||
In practice, the “duplicate MDP Address” problem is rare for the time being,
|
In practice, the “duplicate MDP Address” problem is rare for the time being,
|
||||||
@ -261,15 +261,9 @@ MDP transmits a [MDP message](#mdp-message) over a [link](#link) by
|
|||||||
encapsulating it into an **overlay packet** (also called **MDP packet** or
|
encapsulating it into an **overlay packet** (also called **MDP packet** or
|
||||||
**overlay frame**). The MDP overlay packet is a [layer 2][] concept; it is
|
**overlay frame**). The MDP overlay packet is a [layer 2][] concept; it is
|
||||||
only concerned with transporting MDP messages across a single link to a
|
only concerned with transporting MDP messages across a single link to a
|
||||||
neighbouring *peer* [node](#node). Once an overlay packet arrives, the
|
neighbouring [peer][] node. Once an overlay packet arrives, the receiver
|
||||||
receiver unpacks all of its MDP messages, consumes those for which it (or one
|
unpacks all of its MDP messages, consumes those for which it (or one of its
|
||||||
of its zero-hop identities) is the [recipient](#recipient) and independently
|
[zero-hop][] identities) is the [recipient](#recipient) and independently
|
||||||
routes each of the remaining messages to its next appropriate peer.
|
|
||||||
|
|
||||||
Every overlay packet contains the [MDP address](#mdp-address)es of its
|
|
||||||
[transmitter](#transmitter) and [receiver](#receiver).
|
|
||||||
|
|
||||||
An overlay packet may contain many MDP messages. The header of each MDP
|
|
||||||
message in an overlay packet is constructed afresh when it is embedded into the
|
message in an overlay packet is constructed afresh when it is embedded into the
|
||||||
packet, setting its [flag bits](#mdp-message-flags) and re-writing the [address
|
packet, setting its [flag bits](#mdp-message-flags) and re-writing the [address
|
||||||
fields](#mdp-address-fields) within the context of the overlay packet, in order
|
fields](#mdp-address-fields) within the context of the overlay packet, in order
|
||||||
@ -340,6 +334,7 @@ Available under the [Creative Commons Attribution 4.0 International licence][CC
|
|||||||
[layer 2]: https://en.wikipedia.org/wiki/Data_link_layer
|
[layer 2]: https://en.wikipedia.org/wiki/Data_link_layer
|
||||||
[layer 3]: https://en.wikipedia.org/wiki/Network_layer
|
[layer 3]: https://en.wikipedia.org/wiki/Network_layer
|
||||||
[layer 4]: https://en.wikipedia.org/wiki/Transport_layer
|
[layer 4]: https://en.wikipedia.org/wiki/Transport_layer
|
||||||
|
[peer]: ./REST-API-Route.md#peer
|
||||||
[UDP]: http://en.wikipedia.org/wiki/User_Datagram_Protocol
|
[UDP]: http://en.wikipedia.org/wiki/User_Datagram_Protocol
|
||||||
[MTU]: http://en.wikipedia.org/wiki/Maximum_transmission_unit
|
[MTU]: http://en.wikipedia.org/wiki/Maximum_transmission_unit
|
||||||
[SID]: ./REST-API-Keyring.md#serval-id
|
[SID]: ./REST-API-Keyring.md#serval-id
|
||||||
@ -349,6 +344,8 @@ Available under the [Creative Commons Attribution 4.0 International licence][CC
|
|||||||
[configured]: ./Servald-Configuration.md
|
[configured]: ./Servald-Configuration.md
|
||||||
[network interfaces]: ./Servald-Configuration.md#network-interfaces
|
[network interfaces]: ./Servald-Configuration.md#network-interfaces
|
||||||
[keyring]: ./REST-API-Keyring.md
|
[keyring]: ./REST-API-Keyring.md
|
||||||
|
[routing]: ./REST-API-Route.md
|
||||||
|
[zero-hop]: ./REST-API-Route.md#zero-hop
|
||||||
[encrypted]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:security_framework
|
[encrypted]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:security_framework
|
||||||
[signed]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:security_framework
|
[signed]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:security_framework
|
||||||
[local sockets]: https://en.wikipedia.org/wiki/Unix_domain_socket
|
[local sockets]: https://en.wikipedia.org/wiki/Unix_domain_socket
|
||||||
|
@ -43,6 +43,7 @@ DNA][] component of the [Serval mesh network][].
|
|||||||
REST APIs through which applications access the services of the Serval mesh
|
REST APIs through which applications access the services of the Serval mesh
|
||||||
network, including:
|
network, including:
|
||||||
* [Keyring REST API](./REST-API-Keyring.md) -- identity management
|
* [Keyring REST API](./REST-API-Keyring.md) -- identity management
|
||||||
|
* [Route REST API](./REST-API-Route.md) -- network routing and peers
|
||||||
* [Rhizome REST API](./REST-API-Rhizome.md) -- decentralised content distribution
|
* [Rhizome REST API](./REST-API-Rhizome.md) -- decentralised content distribution
|
||||||
* [MeshMS REST API](./REST-API-MeshMS.md) -- secure, one-to-one messaging
|
* [MeshMS REST API](./REST-API-MeshMS.md) -- secure, one-to-one messaging
|
||||||
|
|
||||||
|
208
doc/REST-API-Route.md
Normal file
208
doc/REST-API-Route.md
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
Route REST API
|
||||||
|
==============
|
||||||
|
[Serval Project][], September 2016
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
Every [Serval DNA][] daemon running on a node in the [Serval mesh network][]
|
||||||
|
maintains its own dynamic *routing table*, which it uses to choose the [network
|
||||||
|
interface][] on which to send each outgoing [MDP message][]. The mesh routing
|
||||||
|
algorithm updates the routing table whenever Serval DNA receives an [overlay
|
||||||
|
packet][] and whenever system time advances. For more details, see [Mesh
|
||||||
|
Datagram Protocol][].
|
||||||
|
|
||||||
|
The [Serval DNA][] daemon gives applications access to its routing table via
|
||||||
|
the **Route REST API** described in this document. Applications can use this
|
||||||
|
information to:
|
||||||
|
|
||||||
|
* discover the current [network neighbours](#neighbour)
|
||||||
|
* discover all currently (and recently) [reachable nodes](#reachable)
|
||||||
|
* estimate the quality of service to any given [reachable node](#reachable)
|
||||||
|
|
||||||
|
Basic concepts
|
||||||
|
--------------
|
||||||
|
|
||||||
|
### Routing table
|
||||||
|
|
||||||
|
Every [node](#node) in the [Serval mesh network][] maintains its own *routing
|
||||||
|
table*, which identifies a single [path](#path) to every [reachable](#reachable)
|
||||||
|
[node](#node). The mesh routing algorithm chooses the best path based on
|
||||||
|
routing information received from other nodes along the path.
|
||||||
|
|
||||||
|
Whenever a [node](#node) receives an [overlay packet][], it knows that, at the
|
||||||
|
time the packet was received, there existed a direct incoming [link](#link)
|
||||||
|
from the [transmitting node][transmitter]. It updates its routing table to
|
||||||
|
mark the transmitter's [primary SID](#primary-sid) as a [neighbour](#neighbour).
|
||||||
|
|
||||||
|
Whenever a [node](#node) receives nothing from a given neighbour for longer
|
||||||
|
than a [configured][] time interval, it presumes that the [link](#link) is
|
||||||
|
broken. It updates its routing table to mark the neighbour's [primary
|
||||||
|
SID](#primary-sid) as no longer a neighbour.
|
||||||
|
|
||||||
|
### Tick
|
||||||
|
|
||||||
|
Every daemon ensures that all of its nearby nodes remain aware of its presence
|
||||||
|
by sending regular [overlay packet][]s to every [neighbour](#neighbour). Every
|
||||||
|
[overlay packet][] contains its own [primary SID](#primary-sid) as the sender
|
||||||
|
address.
|
||||||
|
|
||||||
|
Every [network interface][] has a [configured][] *tick interval*, which is the
|
||||||
|
maximum time period that may elapse between messages sent to any neighbour on
|
||||||
|
that interface. If no message has been sent to a given neighbour for a whole
|
||||||
|
tick interval, then the daemon sends an empty [overlay packet][], called a
|
||||||
|
*tick packet*, to the neighbour.
|
||||||
|
|
||||||
|
As a result, while an interface is quiescent (no traffic), depending on whether
|
||||||
|
the [link](#link) to each neighbour is *broadcast* or *unicast*, at every tick
|
||||||
|
the daemon will send either a single broadcast Wi-Fi packet, or several unicast
|
||||||
|
Wi-Fi packets, or a mixture.
|
||||||
|
|
||||||
|
### Node
|
||||||
|
|
||||||
|
A *node* in the [Serval mesh network][] is any device with its own link-layer
|
||||||
|
address (eg, a UDP/IP address or a Wi-Fi MAC address) that is running a [Serval
|
||||||
|
DNA][] daemon configured to use that network interface.
|
||||||
|
|
||||||
|
### Neighbour
|
||||||
|
|
||||||
|
A [node's](#node) *neighbour* in the [Serval mesh network][] is any node from
|
||||||
|
which its [Serval DNA][] daemon directly receives [overlay packet][]s through a
|
||||||
|
[network interface][].
|
||||||
|
|
||||||
|
Note that a neighbour is not necessarily [reachable](#reachable), because
|
||||||
|
wireless links are not always symmetrical; even though station A receives from
|
||||||
|
station B, it does not necessarily mean that B can receive from A, because of
|
||||||
|
factors like different transmitter power and antenna gain.
|
||||||
|
|
||||||
|
### Link
|
||||||
|
|
||||||
|
A *link* is a one-way connection from a [node](#node) to one of its [neighbour
|
||||||
|
nodes](#neighbour). Links are represented in the routing table by the [primary
|
||||||
|
SID](#primary-sid) of their receiving end.
|
||||||
|
|
||||||
|
A link may be either *broadcast* or *unicast*, which is chosen by the receiver
|
||||||
|
[node](#node) during link negotiation.
|
||||||
|
|
||||||
|
Each link is characterised by a metric that represents the dynamic link quality
|
||||||
|
(eg, the proportion of recent packets successfully received). Every
|
||||||
|
[node](#node) dynamically computes the quality of all its incoming links by
|
||||||
|
counting the gaps in the sequence numbers on received [overlay packet][]s. It
|
||||||
|
continuously informs each [neighbour node](#neighbour) of the measured quality
|
||||||
|
of its incoming link by periodically sending a [routing
|
||||||
|
message](#routing-message) to each neighbour at a [configured][] time interval.
|
||||||
|
|
||||||
|
### Primary SID
|
||||||
|
|
||||||
|
Every [node](#node) identifies itself by its *primary SID*, which is usually
|
||||||
|
the [SID][] of the first identity that was [unlocked][] since the daemon was
|
||||||
|
started.
|
||||||
|
|
||||||
|
### Secondary SID
|
||||||
|
|
||||||
|
A [node](#node) may have more than one [SID][], ie more than one [unlocked][]
|
||||||
|
identity. All its SIDs except the [primary SID](#primary-sid) are called
|
||||||
|
*secondary*.
|
||||||
|
|
||||||
|
A [node](#node) announces all of its secondary SIDs by representing them in its
|
||||||
|
[routing messages](#routing-message) as [reachable](#reachable)
|
||||||
|
[neighbours](#neighbour) on a private network interface (ie, not available to
|
||||||
|
other nodes) with a 100% link quality.
|
||||||
|
|
||||||
|
### Routing message
|
||||||
|
|
||||||
|
Every [node](#node) in the [Serval mesh network][] informs other nodes of the
|
||||||
|
presence and quality of all of its incoming and outgoing [links](#link) by
|
||||||
|
sending *routing messages*. A routing message is a one-hop message that goes
|
||||||
|
to all [neighbouring](#neighbour) nodes but no further:
|
||||||
|
|
||||||
|
* whenever a node detects the presence (received packet) or absence (timeout)
|
||||||
|
of an incoming link, or revises the measured quality of an incoming link,
|
||||||
|
it sends a routing message with the single link's state/quality to the
|
||||||
|
single [neighbour](#neighbour) at the transmitting end of the link;
|
||||||
|
|
||||||
|
* whenever a node receives a routing message from one of its neighbours, it
|
||||||
|
incorporates the new link state (up/down) and quality information into its own
|
||||||
|
routing table, re-evaluates its [paths](#path), and, shortly afterwards,
|
||||||
|
sends a routing message containing the state/quality information of all its
|
||||||
|
outgoing and incoming links to all of its [neighbours](#neighbour).
|
||||||
|
|
||||||
|
Routing messages are not forwarded directly, but the information they carry
|
||||||
|
propagates beyond the node's immediate neighbours because each neighbour, upon
|
||||||
|
receiving a routing message, updates its own [routing table](#routing-table)
|
||||||
|
and sends out its own routing messages that arise as a result of the update.
|
||||||
|
|
||||||
|
### Path
|
||||||
|
|
||||||
|
A *path* is a one-way route from a *sender* [node](#node) to a *recipient*
|
||||||
|
[node](#node), expressed as a sequence of non-repeating [links](#link). The
|
||||||
|
first link in a path always leads to a [reachable](#reachable) [neighbour
|
||||||
|
node](#neighbour).
|
||||||
|
|
||||||
|
The routing algorithm constructs paths by choosing links whose existence and
|
||||||
|
quality has been revealed by a recently-received *routing message* from a
|
||||||
|
neighbour.
|
||||||
|
|
||||||
|
### Reachable
|
||||||
|
|
||||||
|
A [node](#node) in the [Serval mesh network][] considers another node to be
|
||||||
|
*reachable* if its own [routing table](#routing-table) contains a [path](#path)
|
||||||
|
to the second node.
|
||||||
|
|
||||||
|
REST Requests
|
||||||
|
-------------
|
||||||
|
|
||||||
|
### GET /restful/route/all.json
|
||||||
|
|
||||||
|
Returns a list of all currently known identities, in [JSON table][] format.
|
||||||
|
The table columns are:
|
||||||
|
|
||||||
|
| heading | type | content |
|
||||||
|
|:--------------------- |:---------------- |:------------------------------------------------------------------------------ |
|
||||||
|
| `sid` | string | the [SID][] of the identity, as 64 uppercase hex digits |
|
||||||
|
| `did` | string or `null` | the [DID][] of the identity if known (eg, for a local [keyring][] identity) |
|
||||||
|
| `name` | string or `null` | the [Name][] of the identity if known (eg, for a local [keyring][] identity) |
|
||||||
|
| `is_self` | boolean | true if the identity is a self-identity, ie, in the local [keyring][] |
|
||||||
|
| `reachable_broadcast` | boolean | true if the identity is [reachable](#reachable) by broadcast [link](#link) |
|
||||||
|
| `reachable_unicast` | boolean | true if the identity is [reachable](#reachable) by unicast [link](#link) |
|
||||||
|
| `reachable_indirect` | boolean | true if the identity is [reachable](#reachable) only via another [node](#node) |
|
||||||
|
| `interface` | string or `null` | the name of the local network interface on which the identity is reachable |
|
||||||
|
| `hop_count` | integer | the number of hops to reach the identity |
|
||||||
|
| `first_hop` | string or `null` | if `hop_count > 1`, then the [SID][] of the first identity in the route |
|
||||||
|
| `penultimate_hop` | string or `null` | if `hop_count > 2`, then the [SID][] of the penultimate identity in the route |
|
||||||
|
|
||||||
|
-----
|
||||||
|
**Copyright 2015 Serval Project Inc.**
|
||||||
|
**Copyright 2016-2018 Flinders University**
|
||||||
|
![CC-BY-4.0](./cc-by-4.0.png)
|
||||||
|
Available under the [Creative Commons Attribution 4.0 International licence][CC BY 4.0].
|
||||||
|
|
||||||
|
|
||||||
|
[Serval Project]: http://www.servalproject.org/
|
||||||
|
[CC BY 4.0]: ../LICENSE-DOCUMENTATION.md
|
||||||
|
[Serval Mesh network]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:mesh_network
|
||||||
|
[Serval DNA]: ../README.md
|
||||||
|
[REST-API]: ./REST-API.md
|
||||||
|
[keyring]: ./REST-API-Keyring.md
|
||||||
|
[SID]: ./REST-API-Keyring.md#serval-id
|
||||||
|
[DID]: ./REST-API-Keyring.md#did
|
||||||
|
[Name]: ./REST-API-Keyring.md#name
|
||||||
|
[unlocked]: ./REST-API-Keyring.md#identity-unlocking
|
||||||
|
[overlay packet]: ./Mesh-Datagram-Protocol.md#overlay-packet
|
||||||
|
[sender]: ./Mesh-Datagram-Protocol.md#sender
|
||||||
|
[transmitter]: ./Mesh-Datagram-Protocol.md#transmitter
|
||||||
|
[MDP message]: ./Mesh-Datagram-Protocol.md#mdp-message
|
||||||
|
[broadcast]: ./Mesh-Datagram-Protocol.md#broadcast
|
||||||
|
[Mesh Datagram Protocol]: ./Mesh-Datagram-Protocol.md
|
||||||
|
[JSON table]: ./REST-API.md#json-table
|
||||||
|
[configured]: ./Servald-Configuration.md
|
||||||
|
[network interface]: ./Servald-Configuration.md#network-interfaces
|
||||||
|
[200]: ./REST-API.md#200-ok
|
||||||
|
[201]: ./REST-API.md#201-created
|
||||||
|
[202]: ./REST-API.md#202-accepted
|
||||||
|
[400]: ./REST-API.md#400-bad-request
|
||||||
|
[404]: ./REST-API.md#404-not-found
|
||||||
|
[419]: ./REST-API.md#419-authentication-timeout
|
||||||
|
[422]: ./REST-API.md#422-unprocessable-entity
|
||||||
|
[423]: ./REST-API.md#423-locked
|
||||||
|
[500]: ./REST-API.md#500-server-error
|
11
httpd.h
11
httpd.h
@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||||||
|
|
||||||
#include "http_server.h"
|
#include "http_server.h"
|
||||||
#include "keyring.h"
|
#include "keyring.h"
|
||||||
|
#include "overlay_address.h"
|
||||||
#include "meshms.h"
|
#include "meshms.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ typedef struct httpd_request
|
|||||||
*/
|
*/
|
||||||
struct rhizome_read read_state;
|
struct rhizome_read read_state;
|
||||||
|
|
||||||
/* For responses that list SIDs.
|
/* For responses that list identities in the keyring.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct {
|
||||||
enum list_phase phase;
|
enum list_phase phase;
|
||||||
@ -169,6 +170,14 @@ typedef struct httpd_request
|
|||||||
}
|
}
|
||||||
sidlist;
|
sidlist;
|
||||||
|
|
||||||
|
/* For responses that list known subscribers, eg, routing table.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
enum list_phase phase;
|
||||||
|
subscriber_iterator it;
|
||||||
|
}
|
||||||
|
subscriberlist;
|
||||||
|
|
||||||
/* For responses that list manifests.
|
/* For responses that list manifests.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct {
|
||||||
|
@ -152,9 +152,6 @@ static int restful_keyring_identitylist_json_content(struct http_request *hr, un
|
|||||||
static int restful_keyring_identitylist_json_content_chunk(struct http_request *hr, strbuf b)
|
static int restful_keyring_identitylist_json_content_chunk(struct http_request *hr, strbuf b)
|
||||||
{
|
{
|
||||||
httpd_request *r = (httpd_request *) hr;
|
httpd_request *r = (httpd_request *) hr;
|
||||||
// The "my_sid" and "their_sid" per-conversation fields allow the same JSON structure to be used
|
|
||||||
// in a future, non-SID-specific request, eg, to list all conversations for all currently open
|
|
||||||
// identities.
|
|
||||||
const char *headers[] = {
|
const char *headers[] = {
|
||||||
"sid",
|
"sid",
|
||||||
"identity",
|
"identity",
|
||||||
|
@ -173,6 +173,33 @@ struct subscriber *find_subscriber(const uint8_t *sidp, int len, int create)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iterate over subscribers in ascending binary order
|
||||||
|
|
||||||
|
void subscriber_iterator_start(subscriber_iterator *it)
|
||||||
|
{
|
||||||
|
tree_iterator_start(&it->tree_iterator, &root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscriber_iterator_advance_to(subscriber_iterator *it, const sid_t *sid)
|
||||||
|
{
|
||||||
|
tree_iterator_advance_to(&it->tree_iterator, sid->binary, sizeof sid->binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct subscriber **subscriber_iterator_get_current(subscriber_iterator *it)
|
||||||
|
{
|
||||||
|
return (struct subscriber **) tree_iterator_get_node(&it->tree_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscriber_iterator_advance(subscriber_iterator *it)
|
||||||
|
{
|
||||||
|
tree_iterator_advance(&it->tree_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscriber_iterator_free(subscriber_iterator *it)
|
||||||
|
{
|
||||||
|
tree_iterator_free(&it->tree_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
walk the tree, starting at start inclusive, calling the supplied callback function
|
walk the tree, starting at start inclusive, calling the supplied callback function
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Serval DNA MDP addressing
|
Serval DNA MDP addressing
|
||||||
Copyright (C) 2012-2013 Serval Project Inc.
|
Copyright (C) 2012-2013 Serval Project Inc.
|
||||||
|
Copyright (C) 2018 Flinders University
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License
|
modify it under the terms of the GNU General Public License
|
||||||
@ -125,6 +126,16 @@ extern __thread struct subscriber *directory_service;
|
|||||||
|
|
||||||
struct subscriber *find_subscriber(const uint8_t *sid, int len, int create);
|
struct subscriber *find_subscriber(const uint8_t *sid, int len, int create);
|
||||||
|
|
||||||
|
typedef struct subscriber_iterator {
|
||||||
|
tree_iterator tree_iterator;
|
||||||
|
} subscriber_iterator;
|
||||||
|
|
||||||
|
void subscriber_iterator_start(subscriber_iterator *it);
|
||||||
|
void subscriber_iterator_advance_to(subscriber_iterator *it, const sid_t *sid);
|
||||||
|
struct subscriber **subscriber_iterator_get_current(subscriber_iterator *it);
|
||||||
|
void subscriber_iterator_advance(subscriber_iterator *it);
|
||||||
|
void subscriber_iterator_free(subscriber_iterator *it);
|
||||||
|
|
||||||
void enum_subscribers(struct subscriber *start, walk_callback callback, void *context);
|
void enum_subscribers(struct subscriber *start, walk_callback callback, void *context);
|
||||||
int set_reachable(struct subscriber *subscriber, struct network_destination *destination, struct subscriber *next_hop, int hop_count, struct subscriber *prior_hop);
|
int set_reachable(struct subscriber *subscriber, struct network_destination *destination, struct subscriber *next_hop, int hop_count, struct subscriber *prior_hop);
|
||||||
struct network_destination *load_subscriber_address(struct subscriber *subscriber);
|
struct network_destination *load_subscriber_address(struct subscriber *subscriber);
|
||||||
|
179
route_restful.c
Normal file
179
route_restful.c
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
Serval DNA Routing HTTP RESTful interface
|
||||||
|
Copyright (C) 2018 Flinders University
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lang.h" // for bool_t, FALLTHROUGH
|
||||||
|
#include "serval.h"
|
||||||
|
#include "conf.h"
|
||||||
|
#include "httpd.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "strbuf_helpers.h"
|
||||||
|
#include "dataformats.h"
|
||||||
|
#include "overlay_address.h"
|
||||||
|
#include "overlay_interface.h"
|
||||||
|
|
||||||
|
DEFINE_FEATURE(http_rest_route);
|
||||||
|
|
||||||
|
DECLARE_HANDLER("/restful/route/", restful_route_);
|
||||||
|
|
||||||
|
static HTTP_HANDLER restful_route_list_json;
|
||||||
|
|
||||||
|
static int restful_route_(httpd_request *r, const char *remainder)
|
||||||
|
{
|
||||||
|
r->http.response.header.content_type = &CONTENT_TYPE_JSON;
|
||||||
|
int ret = authorize_restful(&r->http);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (r->http.verb == HTTP_VERB_GET && strcmp(remainder, "all.json") == 0)
|
||||||
|
return restful_route_list_json(r, "");
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void finalise_union_subscriberlist(httpd_request *r)
|
||||||
|
{
|
||||||
|
subscriber_iterator_free(&r->u.subscriberlist.it);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HTTP_CONTENT_GENERATOR restful_route_list_json_content;
|
||||||
|
|
||||||
|
static int restful_route_list_json(httpd_request *r, const char *remainder)
|
||||||
|
{
|
||||||
|
if (*remainder)
|
||||||
|
return 404;
|
||||||
|
r->u.subscriberlist.phase = LIST_HEADER;
|
||||||
|
subscriber_iterator_start(&r->u.subscriberlist.it);
|
||||||
|
r->finalise_union = finalise_union_subscriberlist;
|
||||||
|
http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_route_list_json_content);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HTTP_CONTENT_GENERATOR_STRBUF_CHUNKER restful_route_list_json_content_chunk;
|
||||||
|
|
||||||
|
static int restful_route_list_json_content(struct http_request *hr, unsigned char *buf, size_t bufsz, struct http_content_generator_result *result)
|
||||||
|
{
|
||||||
|
return generate_http_content_from_strbuf_chunks(hr, (char *)buf, bufsz, result, restful_route_list_json_content_chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int restful_route_list_json_content_chunk(struct http_request *hr, strbuf b)
|
||||||
|
{
|
||||||
|
httpd_request *r = (httpd_request *) hr;
|
||||||
|
const char *headers[] = {
|
||||||
|
"sid",
|
||||||
|
"did",
|
||||||
|
"name",
|
||||||
|
"is_self",
|
||||||
|
"reachable_broadcast",
|
||||||
|
"reachable_unicast",
|
||||||
|
"reachable_indirect",
|
||||||
|
"interface",
|
||||||
|
"hop_count",
|
||||||
|
"first_hop",
|
||||||
|
"penultimate_hop"
|
||||||
|
};
|
||||||
|
switch (r->u.subscriberlist.phase) {
|
||||||
|
case LIST_HEADER:
|
||||||
|
strbuf_puts(b, "{\n\"header\":[");
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i != NELS(headers); ++i) {
|
||||||
|
if (i)
|
||||||
|
strbuf_putc(b, ',');
|
||||||
|
strbuf_json_string(b, headers[i]);
|
||||||
|
}
|
||||||
|
strbuf_puts(b, "],\n\"rows\":[");
|
||||||
|
if (!strbuf_overrun(b)){
|
||||||
|
r->u.subscriberlist.phase = LIST_FIRST;
|
||||||
|
if (!subscriber_iterator_get_current(&r->u.subscriberlist.it))
|
||||||
|
r->u.subscriberlist.phase = LIST_END;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case LIST_ROWS:
|
||||||
|
strbuf_putc(b, ',');
|
||||||
|
FALLTHROUGH;
|
||||||
|
case LIST_FIRST:
|
||||||
|
r->u.subscriberlist.phase = LIST_ROWS;
|
||||||
|
struct subscriber **subscriberp = subscriber_iterator_get_current(&r->u.subscriberlist.it);
|
||||||
|
assert(subscriberp);
|
||||||
|
struct subscriber *subscriber = *subscriberp;
|
||||||
|
const char *did = NULL;
|
||||||
|
const char *name = NULL;
|
||||||
|
if (subscriber->identity)
|
||||||
|
keyring_identity_extract(subscriber->identity, &did, &name);
|
||||||
|
// sid
|
||||||
|
strbuf_puts(b, "\n[");
|
||||||
|
strbuf_json_string(b, alloca_tohex_sid_t(subscriber->sid));
|
||||||
|
// did
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_string(b, did);
|
||||||
|
// name
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_string(b, name);
|
||||||
|
// is_self
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_boolean(b, subscriber->reachable & REACHABLE_SELF);
|
||||||
|
// reachable_broadcast
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_boolean(b, subscriber->reachable & REACHABLE_BROADCAST);
|
||||||
|
// reachable_unicast
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_boolean(b, subscriber->reachable & REACHABLE_UNICAST);
|
||||||
|
// reachable_indirect
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_boolean(b, subscriber->reachable & REACHABLE_INDIRECT);
|
||||||
|
// interface
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
if (subscriber->destination && subscriber->destination->interface)
|
||||||
|
strbuf_json_string(b, subscriber->destination->interface->name);
|
||||||
|
else
|
||||||
|
strbuf_json_null(b);
|
||||||
|
// hop_count
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
strbuf_json_integer(b, subscriber->hop_count);
|
||||||
|
// first_hop
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
if (subscriber->hop_count > 1)
|
||||||
|
strbuf_json_string(b, alloca_tohex_sid_t(subscriber->next_hop->sid));
|
||||||
|
else
|
||||||
|
strbuf_json_null(b);
|
||||||
|
// penultimate_hop
|
||||||
|
strbuf_puts(b, ",");
|
||||||
|
if (subscriber->hop_count > 2)
|
||||||
|
strbuf_json_string(b, alloca_tohex_sid_t(subscriber->prior_hop->sid));
|
||||||
|
else
|
||||||
|
strbuf_json_null(b);
|
||||||
|
strbuf_puts(b, "]");
|
||||||
|
if (!strbuf_overrun(b)) {
|
||||||
|
subscriber_iterator_advance(&r->u.subscriberlist.it);
|
||||||
|
if (!subscriber_iterator_get_current(&r->u.subscriberlist.it))
|
||||||
|
r->u.subscriberlist.phase = LIST_END;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case LIST_END:
|
||||||
|
strbuf_puts(b, "\n]\n}\n");
|
||||||
|
if (strbuf_overrun(b))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
r->u.subscriberlist.phase = LIST_DONE;
|
||||||
|
// fall through...
|
||||||
|
case LIST_DONE:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -54,6 +54,7 @@ void servald_features()
|
|||||||
USE_FEATURE(http_rhizome);
|
USE_FEATURE(http_rhizome);
|
||||||
USE_FEATURE(http_rhizome_direct);
|
USE_FEATURE(http_rhizome_direct);
|
||||||
USE_FEATURE(http_rest_keyring);
|
USE_FEATURE(http_rest_keyring);
|
||||||
|
USE_FEATURE(http_rest_route);
|
||||||
USE_FEATURE(http_rest_rhizome);
|
USE_FEATURE(http_rest_rhizome);
|
||||||
USE_FEATURE(http_rest_meshms);
|
USE_FEATURE(http_rest_meshms);
|
||||||
USE_FEATURE(http_rest_meshmb);
|
USE_FEATURE(http_rest_meshmb);
|
||||||
|
@ -99,6 +99,7 @@ SERVAL_DAEMON_SOURCES = \
|
|||||||
overlay_packetformats.c \
|
overlay_packetformats.c \
|
||||||
overlay_payload.c \
|
overlay_payload.c \
|
||||||
route_link.c \
|
route_link.c \
|
||||||
|
route_restful.c \
|
||||||
rhizome.c \
|
rhizome.c \
|
||||||
rhizome_bundle.c \
|
rhizome_bundle.c \
|
||||||
rhizome_crypto.c \
|
rhizome_crypto.c \
|
||||||
|
@ -424,9 +424,9 @@ servald_start() {
|
|||||||
# Utility function:
|
# Utility function:
|
||||||
# - fetch the daemon's primary SID
|
# - fetch the daemon's primary SID
|
||||||
get_servald_primary_sid() {
|
get_servald_primary_sid() {
|
||||||
local _instance="$2"
|
local _instance="$1"
|
||||||
[ -z "$_instance" ] || push_and_set_instance $_instance || return $?
|
[ -z "$_instance" ] || push_and_set_instance $_instance || return $?
|
||||||
local _var="$1"
|
local _var="$2"
|
||||||
local _sid=$(<"$SERVALINSTANCE_PATH/proc/primary_sid")
|
local _sid=$(<"$SERVALINSTANCE_PATH/proc/primary_sid")
|
||||||
assert --message="instance $instance_name primary SID is known" [ -n "$_sid" ]
|
assert --message="instance $instance_name primary SID is known" [ -n "$_sid" ]
|
||||||
if [ -n "$_var" ]; then
|
if [ -n "$_var" ]; then
|
||||||
|
95
testdefs_routing.sh
Normal file
95
testdefs_routing.sh
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Common definitions for routing tests.
|
||||||
|
#
|
||||||
|
# Copyright 2012-2015 Serval Project, Inc.
|
||||||
|
# Copyright 2016-2018 Flinders University
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
interface_is_up() {
|
||||||
|
$GREP "Interface .* is up" $instance_servald_log || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
link_matches() {
|
||||||
|
local interface_ex=".*"
|
||||||
|
local link_type="(BROADCAST|UNICAST)"
|
||||||
|
local via=".*"
|
||||||
|
while [ $# -ne 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--interface) interface_ex="$2"; shift 2;;
|
||||||
|
--broadcast) link_type="BROADCAST"; shift;;
|
||||||
|
--unicast) link_type="UNICAST"; shift;;
|
||||||
|
--via) link_type="INDIRECT"; via="$2"; interface_ex=""; shift 2;;
|
||||||
|
--any) via=".*"; link_type=".*"; shift;;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
local oIFS="$IFS"
|
||||||
|
IFS='|'
|
||||||
|
local sids="$*"
|
||||||
|
IFS="$oIFS"
|
||||||
|
local rexp="^(${sids}):${link_type}:${interface_ex}:${via}:"
|
||||||
|
tfw_log "Looking for $rexp"
|
||||||
|
if ! $GREP -E "$rexp" "$TFWSTDOUT"; then
|
||||||
|
tfw_log "Link not found"
|
||||||
|
tfw_cat --stdout
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
has_link() {
|
||||||
|
executeOk_servald route print
|
||||||
|
link_matches $@
|
||||||
|
}
|
||||||
|
|
||||||
|
has_no_link() {
|
||||||
|
has_link --any $@ || return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
path_exists() {
|
||||||
|
local dest
|
||||||
|
eval dest=\$$#
|
||||||
|
local dest_sidvar=SID${dest#+}
|
||||||
|
local dest_sids
|
||||||
|
eval 'dest_sids=("${'$dest_sidvar'[@]}")'
|
||||||
|
local first_inst=$1
|
||||||
|
local next_inst=$first_inst
|
||||||
|
shift
|
||||||
|
local I
|
||||||
|
for I; do
|
||||||
|
local sidvar=SID${I#+}
|
||||||
|
local sids
|
||||||
|
eval 'sids=("${'$sidvar'[@]}")'
|
||||||
|
[ "${#sids[@]}" -gt 0 ] || error "no SIDs known for identity $I"
|
||||||
|
set_instance $next_inst
|
||||||
|
executeOk_servald route print
|
||||||
|
link_matches "${sids[@]}" || return 1
|
||||||
|
[ $I = $dest ] && break
|
||||||
|
link_matches --via ${!sidvar} "${dest_sids[@]}" || return 1
|
||||||
|
next_inst=$I
|
||||||
|
done
|
||||||
|
# so we think this path should exist, check that it works
|
||||||
|
set_instance $first_inst
|
||||||
|
executeOk_servald --stderr mdp trace --timeout=20 "${dest_sids[0]}"
|
||||||
|
# assertStdoutGrep "^[0-9]+:$dest_sids\$"
|
||||||
|
tfw_cat --stdout
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
log_routing_table() {
|
||||||
|
executeOk_servald route print
|
||||||
|
tfw_cat --stdout --stderr
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
# Aggregation of all tests except high-load stress tests.
|
# Aggregation of all tests except high-load stress tests.
|
||||||
#
|
#
|
||||||
# Copyright 2012-2014 Serval Project Inc.
|
# Copyright 2012-2014 Serval Project Inc.
|
||||||
# Copyright 2017 Flinders University
|
# Copyright 2017-2018 Flinders University
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@ -37,10 +37,9 @@ includeTests meshms
|
|||||||
includeTests meshmb
|
includeTests meshmb
|
||||||
includeTests directory_service
|
includeTests directory_service
|
||||||
includeTests vomp
|
includeTests vomp
|
||||||
includeTests keyringrestful
|
|
||||||
includeTests rhizomerestful
|
includeTests allrestful
|
||||||
includeTests meshmsrestful
|
|
||||||
includeTests meshmbrestful
|
|
||||||
if type -p "$JAVAC" >/dev/null; then
|
if type -p "$JAVAC" >/dev/null; then
|
||||||
includeTests alljava
|
includeTests alljava
|
||||||
fi
|
fi
|
||||||
|
30
tests/allrestful
Executable file
30
tests/allrestful
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Aggregation of all REST API tests.
|
||||||
|
#
|
||||||
|
# Copyright 2018 Flinders University
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
source "${0%/*}/../testframework.sh"
|
||||||
|
source "${0%/*}/../testdefs.sh"
|
||||||
|
|
||||||
|
includeTests keyringrestful
|
||||||
|
includeTests routerestful
|
||||||
|
includeTests rhizomerestful
|
||||||
|
includeTests meshmsrestful
|
||||||
|
includeTests meshmbrestful
|
||||||
|
|
||||||
|
runTests "$@"
|
125
tests/routerestful
Executable file
125
tests/routerestful
Executable file
@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Tests for Serval DNA Route REST API
|
||||||
|
#
|
||||||
|
# Copyright 2018 Flinders University
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
source "${0%/*}/../testframework.sh"
|
||||||
|
source "${0%/*}/../testdefs.sh"
|
||||||
|
source "${0%/*}/../testdefs_rest.sh"
|
||||||
|
source "${0%/*}/../testdefs_routing.sh"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
setup_rest_utilities
|
||||||
|
setup_servald
|
||||||
|
assert_no_servald_processes
|
||||||
|
setup_rest_config +A
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_servald_server() {
|
||||||
|
executeOk_servald config \
|
||||||
|
set debug.mdprequests yes \
|
||||||
|
set debug.linkstate yes \
|
||||||
|
set debug.subscriber yes \
|
||||||
|
set debug.verbose yes \
|
||||||
|
set debug.overlayrouting yes \
|
||||||
|
set log.console.level debug \
|
||||||
|
set log.console.show_pid on \
|
||||||
|
set log.console.show_time on \
|
||||||
|
set rhizome.enable no
|
||||||
|
}
|
||||||
|
|
||||||
|
finally() {
|
||||||
|
stop_all_servald_servers
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
foreach_instance_with_pidfile log_routing_table
|
||||||
|
stop_all_servald_servers
|
||||||
|
kill_all_servald_processes
|
||||||
|
assert_no_servald_processes
|
||||||
|
report_all_servald_servers
|
||||||
|
}
|
||||||
|
|
||||||
|
doc_AuthBasicMissing="REST API missing Basic Authentication credentials"
|
||||||
|
setup_AuthBasicMissing() {
|
||||||
|
setup
|
||||||
|
set_instance +A
|
||||||
|
start_servald_server
|
||||||
|
wait_until_rest_server_ready
|
||||||
|
}
|
||||||
|
test_AuthBasicMissing() {
|
||||||
|
rest_request GET "/restful/route/all.json" 401 --no-auth
|
||||||
|
assertGrep response.headers "^WWW-Authenticate: Basic realm=\"Serval RESTful API\"$CR\$"
|
||||||
|
assertJq response.json 'contains({"http_status_code": 401})'
|
||||||
|
assertJq response.json 'contains({"http_status_message": ""})'
|
||||||
|
}
|
||||||
|
|
||||||
|
doc_AuthBasicWrong="REST API incorrect Basic Authentication credentials"
|
||||||
|
setup_AuthBasicWrong() {
|
||||||
|
setup
|
||||||
|
set_instance +A
|
||||||
|
start_servald_server
|
||||||
|
wait_until_rest_server_ready
|
||||||
|
}
|
||||||
|
test_AuthBasicWrong() {
|
||||||
|
rest_request GET "/restful/route/all.json" 401 --user=fred:nurks
|
||||||
|
assertGrep response.headers "^WWW-Authenticate: Basic realm=\"Serval RESTful API\"$CR\$"
|
||||||
|
assertJq response.json 'contains({"http_status_code": 401})'
|
||||||
|
assertJq response.json 'contains({"http_status_message": ""})'
|
||||||
|
rest_request GET "/restful/route/all.json" 200 --user=ron:weasley
|
||||||
|
}
|
||||||
|
|
||||||
|
doc_RouteListAll="REST API list entire routing table"
|
||||||
|
setup_RouteListAll() {
|
||||||
|
setup
|
||||||
|
foreach_instance +A +B create_identities 2
|
||||||
|
foreach_instance +A +B add_servald_interface 1
|
||||||
|
foreach_instance +A +B start_servald_server
|
||||||
|
wait_until_rest_server_ready +A
|
||||||
|
get_servald_primary_sid +B PRIMARY_SIDB
|
||||||
|
wait_until --timeout=20 path_exists +A +B
|
||||||
|
wait_until --timeout=10 path_exists +B +A
|
||||||
|
set_instance +A
|
||||||
|
}
|
||||||
|
test_RouteListAll() {
|
||||||
|
rest_request GET "/restful/route/all.json"
|
||||||
|
transform_list_json response.json routes.json
|
||||||
|
assert [ "$(jq 'length' routes.json)" = 4 ]
|
||||||
|
assertJq routes.json 'contains([{"sid": "'$SIDA1'",
|
||||||
|
"is_self": true,
|
||||||
|
"hop_count": 0}])'
|
||||||
|
assertJq routes.json 'contains([{"sid": "'$SIDA2'",
|
||||||
|
"is_self": true,
|
||||||
|
"hop_count": 0}])'
|
||||||
|
assertJq routes.json 'contains([{"sid": "'$PRIMARY_SIDB'",
|
||||||
|
"is_self": false,
|
||||||
|
"reachable_unicast": true,
|
||||||
|
"reachable_indirect": false,
|
||||||
|
"hop_count": 1}])'
|
||||||
|
for SID in "${SIDB[@]}"; do
|
||||||
|
if [ "$SID" != "$PRIMARY_SIDB" ]; then
|
||||||
|
assertJq routes.json 'contains([{"sid": "'$SID'",
|
||||||
|
"is_self": false,
|
||||||
|
"reachable_unicast": false,
|
||||||
|
"reachable_indirect": true,
|
||||||
|
"hop_count": 2}])'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests "$@"
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
# Tests for Route discovery
|
# Tests for Route discovery
|
||||||
#
|
#
|
||||||
# Copyright 2012 Serval Project, Inc.
|
# Copyright 2012-2015 Serval Project, Inc.
|
||||||
|
# Copyright 2016-2018 Flinders University
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@ -18,73 +19,9 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
|
||||||
source "${0%/*}/../testframework.sh"
|
source "${0%/*}/../testframework.sh"
|
||||||
source "${0%/*}/../testdefs.sh"
|
source "${0%/*}/../testdefs.sh"
|
||||||
|
source "${0%/*}/../testdefs_routing.sh"
|
||||||
interface_up() {
|
|
||||||
$GREP "Interface .* is up" $instance_servald_log || return 1
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
link_matches() {
|
|
||||||
local interface_ex=".*"
|
|
||||||
local link_type="\(BROADCAST\|UNICAST\)"
|
|
||||||
local via="0*"
|
|
||||||
local sid=""
|
|
||||||
while [ $# -ne 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--interface) interface_ex="$2"; shift 2;;
|
|
||||||
--broadcast) link_type="BROADCAST"; shift;;
|
|
||||||
--unicast) link_type="UNICAST"; shift;;
|
|
||||||
--via) link_type="INDIRECT"; via="$2"; interface_ex=""; shift 2;;
|
|
||||||
--any) via=".*"; link_type=".*"; shift;;
|
|
||||||
*) break;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
sid="$1"
|
|
||||||
tfw_log "Looking for link ${sid}, ${link_type}, ${interface_ex}, ${via}"
|
|
||||||
if ! $GREP "^${sid}:${link_type}:${interface_ex}:${via}:" "$TFWSTDOUT"; then
|
|
||||||
tfw_log "Link not found"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
has_link() {
|
|
||||||
executeOk_servald route print
|
|
||||||
link_matches $@
|
|
||||||
}
|
|
||||||
|
|
||||||
has_no_link() {
|
|
||||||
has_link --any $@ || return 0
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
path_exists() {
|
|
||||||
local dest
|
|
||||||
eval dest=\$$#
|
|
||||||
local dest_sidvar=SID${dest#+}
|
|
||||||
local first_inst=$1
|
|
||||||
local next_inst=$first_inst
|
|
||||||
shift
|
|
||||||
local I
|
|
||||||
for I; do
|
|
||||||
local sidvar=SID${I#+}
|
|
||||||
[ -n "${!sidvar}" ] || break
|
|
||||||
set_instance $next_inst
|
|
||||||
executeOk_servald route print
|
|
||||||
link_matches ${!sidvar} || return 1
|
|
||||||
[ $I = $dest ] && break
|
|
||||||
link_matches --via ${!sidvar} ${!dest_sidvar} || return 1
|
|
||||||
next_inst=$I
|
|
||||||
done
|
|
||||||
# so we think this path should exist, check that it works
|
|
||||||
set_instance $first_inst
|
|
||||||
executeOk_servald mdp trace --timeout=20 "${!dest_sidvar}"
|
|
||||||
# assertStdoutGrep "^[0-9]+:${!dest_sidvar}\$"
|
|
||||||
tfw_cat --stdout
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
configure_servald_server() {
|
configure_servald_server() {
|
||||||
executeOk_servald config \
|
executeOk_servald config \
|
||||||
@ -99,11 +36,6 @@ configure_servald_server() {
|
|||||||
set rhizome.enable no
|
set rhizome.enable no
|
||||||
}
|
}
|
||||||
|
|
||||||
log_routing_table() {
|
|
||||||
executeOk_servald route print
|
|
||||||
tfw_cat --stdout --stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
teardown() {
|
teardown() {
|
||||||
foreach_instance_with_pidfile log_routing_table
|
foreach_instance_with_pidfile log_routing_table
|
||||||
stop_all_servald_servers
|
stop_all_servald_servers
|
||||||
@ -406,7 +338,7 @@ setup_scan() {
|
|||||||
set interfaces.1.broadcast.drop on
|
set interfaces.1.broadcast.drop on
|
||||||
foreach_instance +A +B +C start_servald_server
|
foreach_instance +A +B +C start_servald_server
|
||||||
foreach_instance +A +B +C \
|
foreach_instance +A +B +C \
|
||||||
wait_until interface_up
|
wait_until interface_is_up
|
||||||
}
|
}
|
||||||
test_scan() {
|
test_scan() {
|
||||||
set_instance +A
|
set_instance +A
|
||||||
@ -470,7 +402,7 @@ setup_fixedAddress() {
|
|||||||
touch "$SERVALD_VAR/dummy1"
|
touch "$SERVALD_VAR/dummy1"
|
||||||
foreach_instance +A +B start_servald_server
|
foreach_instance +A +B start_servald_server
|
||||||
foreach_instance +A +B \
|
foreach_instance +A +B \
|
||||||
wait_until interface_up
|
wait_until interface_is_up
|
||||||
}
|
}
|
||||||
doc_fixedAddress="Establish a link from a configured fixed address"
|
doc_fixedAddress="Establish a link from a configured fixed address"
|
||||||
test_fixedAddress() {
|
test_fixedAddress() {
|
||||||
|
Loading…
Reference in New Issue
Block a user