0
0
mirror of https://github.com/cjdelisle/cjdns synced 2025-10-06 00:32:50 +02:00

doc: move admin and tunnel readmes to doc/

This commit is contained in:
Lars Gierth
2015-02-08 06:42:17 +01:00
parent 9a7fbbc01c
commit de691269d3
5 changed files with 1127 additions and 1134 deletions

View File

@@ -1,944 +0,0 @@
#Cjdns Admin API
Cjdns is inspected and configured through a UDP socket.
When cjdroute starts up, it reads the configuration file and spawns cjdns core. The core
knows nothing but the port which it should bind to and the private key which it should use.
All other information such as peers, interfaces and passwords is given to the core through the
admin UDP interface. When cjdroute is finished setting up the core, it exits leaving the core
running in the background.
You can call all of the functions which are called by cjdroute to collect information and alter
the core's configuration.
## How a function works
To call a function you send a udp packet containing a bencoded request to the core and it sends
back a bencoded response.
echo -n 'd1:q4:pinge' | nc6 -u -t 1 -n -w3 127.0.0.1 11234
If you are more comfortable writing json then benc, you can use benc2json in reverse mode to
preprocess your message. **Note**: benc2json has been removed in 2821c81d49 to speed up the build.
echo '{ "q": "ping" }' | ./build/benc2json -r
Stream the request from json into benc and then make the request to the core:
echo '{ "q": "ping" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234
Get the result back into json:
echo '{ "q": "ping" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
## Transaction ID
Because you can send multiple messages at once, you may add a transaction ID to a message and it
will be reflected back to you in the response.
echo '{ "q": "ping", "txid": "my request" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
Result:
{
"txid" : "my request",
"q" : "pong"
}
## Arguments
Some functions require arguments and others allow arguments but assume defaults if they are
not provided. Arguments are sent to a function through a benc *dictionary* called `args`.
The `Admin_availableFunctions()` function has an optional argument called `page`, this is
because there are too many functions to be described in a single UDP packet. The following
command will get the first page of functions from `Admin_availableFunctions` which will
describe other functions and their required and allowed arguments.
echo -n '
{
"q": "Admin_availableFunctions",
"args": {
"page": 0
}
}' | ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
## Authentication
Any function which changes the state of cjdns core requires authentication to carry out.
Authentication is done on a per-request basis. Functions which don't require authentication
can still be called with authentication and will still fail if the authentication is incorrect.
* Step 1: Request a cookie from the server.
* Step 2: Calculate the SHA-256 of the cookie and your admin password, place this hash and cookie
in the request.
* Step 3: Calculate the SHA-256 of the entire request with the hash and cookie added,
replace the hash in the request with this result.
Steps 1 and 2 securely bind the cookie to the password so that the password hash cannot
be taken and used again in another request later on, step 3 binds the cookie and password to
the request so that a man-in-the-middle cannot change the content of the request in flight.
### Anatomy of an authenticated request
A plain request such as `{"q": "ping"}` becomes `{"q":"auth", "aq":"ping", "hash":<calculated hash>}`.
The `q` field is moved to `aq` (authenticated query) and the `q` field says `auth`.
**NOTE:** A cookie is only valid for 10 seconds so requesting and using a cookie must be done
in the same script.
**NOTE2:** Cookies are reusable *for now* this is not part of the API and is considered a
bug, you should always request a new cookie for each authenticated request otherwise you may
be broke by changes in the future.
### By example
**Step 1:** Get the cookie
RESP=`echo -n 'd1:q6:cookiee' | nc6 -u -t 1 -n -w3 127.0.0.1 11234` \
echo response=${RESP}; \
COOKIE=`echo ${RESP} | sed 's/d6:cookie10:\([0-9]*\)e/\1/'` \
echo cookie=${COOKIE};
**Step 2:** Calculate the hash of the cookie and password:
For this step, you will need the admin password from your cjdroute.conf file, it's to be found
inside of the block which says `"admin": {`.
ADMIN_PASS=you_will_find_this_in_your_cjdroute_dot_conf \
REQUEST='{"q": "auth", "aq": "ping", "hash": "__HASH__", "cookie": "__COOKIE__"}' \
COOKIE_RESP=`echo -n 'd1:q6:cookiee' | nc6 -u -t 1 -n -w3 127.0.0.1 11234` \
COOKIE=`echo ${COOKIE_RESP} | sed 's/d6:cookie10:\([0-9]*\)e/\1/'` \
HASH_ONE=`echo -n "${ADMIN_PASS}${COOKIE}" | sha256sum -b | cut -d\ -f1` ; \
REQ_ONE=`echo $REQUEST | sed -e "s/__HASH__/${HASH_ONE}/" -e "s/__COOKIE__/${COOKIE}/" \
| ./build/benc2json -r | tr -d '\n'` ; \
echo "hash of password and cookie is ${HASH_ONE}" ; \
echo "Request with cookie and hash added:" ; \
echo "${REQ_ONE}" ; \
echo "JSON version of request:" ; \
echo "${REQ_ONE}" | ./build/benc2json
**Step 3:** Calculate the SHA-256 of the entire request and replace the one in the request:
This will calculate the final request and send it to cjdns.
ADMIN_PASS=you_will_find_this_in_your_cjdroute_dot_conf \
REQUEST='{"q": "auth", "aq": "ping", "hash": "__HASH__", "cookie": "__COOKIE__"}' \
COOKIE_RESP=`echo -n 'd1:q6:cookiee' | nc6 -u -t 1 -n -w3 127.0.0.1 11234` \
COOKIE=`echo ${COOKIE_RESP} | sed 's/d6:cookie10:\([0-9]*\)e/\1/'` \
HASH_ONE=`echo -n "${ADMIN_PASS}${COOKIE}" | sha256sum -b | cut -d\ -f1` \
REQ_ONE=`echo $REQUEST | sed -e "s/__HASH__/${HASH_ONE}/" -e "s/__COOKIE__/${COOKIE}/" \
| ./build/benc2json -r | tr -d '\n'` \
FINAL_HASH=`echo -n "$REQ_ONE" | sha256sum -b | cut -d\ -f1` \
FINAL_REQ=`echo $REQ_ONE | sed -e "s/${HASH_ONE}/${FINAL_HASH}/"` ; \
echo -n "$FINAL_REQ" \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
If you see this:
{
"q" : "pong"
}
then it has succeeded, if the password is incorrect, you will see this:
{
"error" : "Auth failed."
}
### Tools:
Obviously using bash to craft cjdns admin RPC calls is probably the most awkward way possible,
there are tools in cjdns/contrib which will help you craft requests, specifically there are
libraries written in python and perl which will allow users to call cjdns internal functions
as python/perl native functions. A tool called `cexec` is provided with the python library which
allows you to call cjdns functions from shell scripts or the command line as follows:
./contrib/python/cexec 'ping()'
## Cjdns Functions:
user@ubnta8:~/wrk/cjdns$ ./contrib/python/cexec 'functions()' | sort
Admin_asyncEnabled()
Admin_availableFunctions(page='')
Allocator_bytesAllocated()
Allocator_snapshot(includeAllocations='')
AuthorizedPasswords_add(password, user, authType='', ipv6=0)
AuthorizedPasswords_list()
AuthorizedPasswords_remove(user)
Core_exit()
Core_initTunnel(desiredTunName=0)
Core_pid()
ETHInterface_beacon(interfaceNumber='', state='')
ETHInterface_beginConnection(publicKey, macAddress, interfaceNumber='', password=0)
ETHInterface_new(bindDevice)
InterfaceController_disconnectPeer(pubkey)
InterfaceController_peerStats(page='')
IpTunnel_allowConnection(publicKeyOfAuthorizedNode, ip6Address=0, ip4Address=0)
IpTunnel_connectTo(publicKeyOfNodeToConnectTo)
IpTunnel_listConnections()
IpTunnel_removeConnection(connection)
IpTunnel_showConnection(connection)
memory()
NodeStore_dumpTable(page)
NodeStore_getLink(parent, linkNum)
NodeStore_getRouteLabel(pathParentToChild, pathToParent)
NodeStore_nodeForAddr(ip=0)
ping()
RainflyClient_addKey(ident)
RainflyClient_addServer(addr)
RainflyClient_minSignatures(count)
RouterModule_findNode(nodeToQuery, target, timeout='')
RouterModule_getPeers(path, nearbyPath=0, timeout='')
RouterModule_lookup(address)
RouterModule_pingNode(path, timeout='')
SearchRunner_showActiveSearch(number)
Security_checkPermissions()
Security_dropPermissions()
Security_setUser(user)
SessionManager_getHandles(page='')
SessionManager_sessionStats(handle)
SwitchPinger_ping(path, data=0, keyPing='', timeout='')
UDPInterface_beginConnection(publicKey, address, interfaceNumber='', password=0)
UDPInterface_new(bindAddress=0)
###RouterModule_pingNode()
**Auth Required**
Send a node a cjdns ping request.
Parameters:
* required String **path** may be a route such as "0000.0000.0000.1d53" or an ip address such as
"fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536", or an ip with explicit path
eg: "fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536@0000.0000.0000.1d53"
* Int **timeout** (optional) the number of milliseconds after which to timeout the ping
if there is no response. Defaults to router's adaptive ping timeout if unspecified.
Responses:
* **error**: `could not find node to ping` if there was no node by the given address found in the
routing table
* **result**: `timeout` gives timeout and number of milliseconds since the ping.
* **result**: `pong` gives `version` representing the git hash of the source code which built the
pinged node, and `ms` which is the number of milliseconds since the original ping.
Examples:
>>> cjdns.RouterModule_pingNode('fc38:4c2c:1a8f:3981:f2e7:c2b9:6870:6e84')
{'version': '5c5e84ccdba3f31f7c88077729700b4368320bc2', 'result': 'pong', 'ms': 79}
>>> cjdns.RouterModule_pingNode('fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536')
{'error': 'could not find node to ping'}
>>> cjdns.RouterModule_pingNode('0000.0000.0000.0013')
{'version': '2b62b9ae911f1044e45f3f28fdd63d0d5a7fc512', 'result': 'pong', 'ms': 0}
>>> cjdns.RouterModule_pingNode('a')
{'error': "Unexpected length, must be either 39 char ipv6 address (with leading zeros)
eg: 'fc4f:000d:e499:8f5b:c49f:6e6b:01ae:3120' or 19 char path eg: '0123.4567.89ab.cdef'"}
>>> cjdns.RouterModule_pingNode('aaaaaaaaaaaaaaaaaaa')
{'error': 'parse path failed'}
>>> cjdns.RouterModule_pingNode('aaaaaaaaaaaaaaaaaaazzzzzzzzzzzzzzzzzzzz')
{'error': 'parsing address failed'}
>>> cjdns.RouterModule_pingNode('fc38:4c2c:1a8f:3981:f2e7:c2b9:6870:6e84', 10)
{'result': 'timeout', 'ms': 10}
### ETHInterface Functions:
ETHInterface is a connector which allows cjdns nodes on the same lan to automatically connect
without the need to IP addresses on the LAN or sharing of connection credentials. It works on
wireless LANs as well as wired ethernet LANs.
#### ETHInterface_new()
Create a new ETHInterface and bind it to a device.
**NOTE**: this call will always fail with `'error': 'call to socket() failed. [permission denied]'`
unless it is running as root and will fail with `process cannot open more files` if
`Security_setUser()` has already been called.
**Auth Required**
Parameters:
* required String **bindDevice** the name of the ethernet device to bind to, eg: `eth0` or `wlan0`.
Returns:
* Int **interfaceNumber** an number which can be used to carry out other operations on the interface later.
#### ETHInterface_beginConnection()
Connect an ETHInterface to another computer which has an ETHInterface running.
**Auth Required**
Parameters:
* required String **publicKey** The public key of the other node, similar to `UDPInterface_beginConnection()`
* required String **macAddress** The mac address of the other node.
* Int **interfaceNumber** The interface number to use, assumed 0 (first ETHinterface created) if not supplied.
* String **password** A password for connecting to the other node if required.
Returns:
* String **error**: `none` if everything went well.
Other errors are self-explanitory.
#### ETHInterface_beacon()
Enable or disable sending or receiving of ETHInterface beacon messages.
ETHInterface uses periodic beacon messages to automatically peer nodes which are on the same LAN.
Be mindful that if your lan has is open wifi, enabling beaconing will allow anyone to peer with you.
**Auth Required**
Beacon States:
0. Disabled, no beacons are sent and incoming beacon messages are discarded.
1. Accepting, no beacons are sent but if an incoming beacon is received, it is acted upon.
2. Sending and Accepting, beacons are sent and accepted.
Parameters:
* Int **interfaceNumber** The number of the ETHInterface to change the state of, assumed 0 if not provided.
* Int **state** What state to switch to, if not provided, the current state will be queried only.
Returns:
* String **error**: `none` if all went well.
* Int **state**: the state number after the call is complete.
* String **stateName**: a description of the state.
Example:
$ ./contrib/python/cexec 'ETHInterface_beacon(2)'
{'txid': 'FYRKHAPIM3', 'error': 'invalid interfaceNumber'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0)'
{'txid': 'Z7KHE7SZ5R', 'state': 2, 'stateName': 'sending and accepting', 'error': 'none'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0, 0)'
{'txid': 'TP1R8PYCNS', 'state': 0, 'stateName': 'disabled', 'error': 'none'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0, 1)'
{'txid': 'UGKKGX4ZC9', 'state': 1, 'stateName': 'accepting', 'error': 'none'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0, 2)'
{'txid': '1B7RXJEH3N', 'state': 2, 'stateName': 'sending and accepting', 'error': 'none'}
### IpTunnel Functions
IPTunnel is designed to allow tunneling of IPv4 and IPv6 packets through a cjdns network
to the external internet or to a virtual LAN. It provides familiar VPN type functionality.
There are 2 nodes, a client and a server, the server uses `IPTunnel_allowConnection()` and the
client uses `IPTunnel_connectTo()` the server assigns IPv4 and/or IPv6 addresses to the client
and the client is required to use only these addresses, subnet assignment is not supported.
When the client uses `IPTunnel_connectTo()`, it sends a request to the server for addresses and
continues polling the server periodically until the addresses are provided.
#### IpTunnel_listConnections()
List the connection numbers of all IPTunnel connections.
**Auth Required**
Returns:
* List **connections**: A list of integers representing the connection numbers for each connection.
* String **error**: `none`
Example:
$ ./contrib/python/cexec 'IpTunnel_listConnections()'
{'connections': [0], 'txid': '5ZFPFJ60AT', 'error': 'none'}
#### IpTunnel_showConnection()
Show information about a perticular IPTunnel connection.
**Auth Required**
Parameters:
* required Int **connection**: the connection number for the connection to show information about.
Returns:
* Int **outgoing**: 1 if the connection is outgoing, 0 if it's incoming.
* String **key**: the cjdns public key of the foreign node.
* String **ip6Address**: the IPv6 address which is assigned to this IPTunnel if applicable.
* Int **ip6Prefix**: the IPv6 netmask/prefix length which is assigned to this IPTunnel if applicable.
* String **ip4Address**: the IPv4 address which is assigned to this IPTunnel if applicable.
* Int **ip4Prefix**: the IPv4 netmask/prefix length which is assigned to this IPTunnel if applicable.
* String **error**: `none` unless the connection number is invalid.
Examples:
# Prior to getting it's addresses from the server, they are not listed.
$ ./contrib/python/cexec 'IpTunnel_showConnection(0)'
{'outgoing': 1, 'txid': 'REIV40SXD9', 'key': 'd5d0wu0usrkufd8s98t19gt7m2ggvbz1xbnuxu82x63uqlnk2kb0.k', 'error': 'none'}
# After a short wait, the addresses are provided and they are now listed.
$ ./contrib/python/cexec 'IpTunnel_showConnection(0)'
{'outgoing': 1, 'txid': 'CAQCTWECRD', 'ip4Address': '192.168.10.2', 'key': 'd5d0wu0usrkufd8s98t19gt7m2ggvbz1xbnuxu82x63uqlnk2kb0.k', 'error': 'none', 'ip6Address': '2a02:2498:e000:20::144:3'}
#### IpTunnel_removeConnection()
Remove an IPTunnel connection from the list, the other end will nolonger be able to send traffic
over this connection.
**Auth Required**
**NOT IMPLEMENTED**
#### IpTunnel_connectTo()
Initiate an *outgoing* connection to another node and request IP addresses from them.
**Auth Required**
Parameters:
* required String **publicKeyOfNodeToConnectTo** the pubkey of the node to connect to.
Returns:
* String **error**: `none` if all went well
* Int **connection**: the connection number of the new connection
Examples:
$ ./contrib/python/cexec 'IpTunnel_connectTo("d5d0wu0usrkufd8s98t19gt7m2ggvbz1xbnuxu82x63uqlnk2kb0.k")'
{'connection': 1, 'txid': '9QXRQO1FG8', 'error': 'none'}
#### IpTunnel_allowConnection()
Allow in *incoming* connection from another node, they must also use `IPTunnel_connectTo()` in order
to complete the connection.
**Auth Required**
Parameters:
* required String **publicKeyOfAuthorizedNode** The key of the node which is authorized to connect.
* String **ip6Address** The IPv6 address to give them if applicable.
* String **ip4Address** The IPv4 address to give them if applicable.
Returns:
* String **error** `none` if all went well.
* Int **connection** the connection number for the new connection.
### UDPInterface Functions
UDPInterface is the basic cjdns interface which is used to link distant nodes over the internet.
It will work on a LAN as long as the nodes have IP addresses but for linking on a LAN, ETHInterface
is easier.
#### UDPInterface_new()
Create a new UDPInterface which is either bound to an address/port or not.
**NOTE**: This call will fail with `'error': 'call to socket() failed [process cannot open more files]'`
is `Security_noFiles()` has already been called.
Parameters:
* String **bindAddress**: the address/port to bind to, if unspecified, it is assumed to be `0.0.0.0`.
Returns:
* String **error** `none` if all went well
* Int **interfaceNumber** the number of the interface, usable with `UDPInterface_beginConnection()`
#### UDPInterface_beginConnection()
Start a direct connection to another node.
**Auth Required**
Parameters:
* required String **publicKey** the base32 public key for the node to connect to, ending in .k.
* required String **address** the ip address and port for the node, at this time DNS resolution
and IPv6 is not supported.
* Int **interfaceNumber** the number for the UDPInterface to use for connecting, provided by
*UDPInterface_new()* if not sent, 0 is assumed.
* String **password** a password to use when connecting.
Note: just because it returns `'error': 'none'` does not mean that the connection was successful.
The neighbor may still reject our connection attempts.
Example:
>>> cjdns.UDPInterface_beginConnection("v0zyvrjuc4xbzh4n9c4k3qpx7kg8xgndv2k45j9nfgb373m8sss0.k", "192.168.0.2:10000", "null")
{'error': 'none'}
>>> cjdns.UDPInterface_beginConnection("v0zyvrjuc4xbzh4n9c4k3qpx7kg8xgndv2k45j9nfgb373m8sss0.k", "x", "null")
{'error': 'unable to parse ip address and port.'}
>>> cjdns.UDPInterface_beginConnection("k", "x", "null")
{'error': 'publicKey is too short, must be 52 characters long.'}
>>> cjdns.UDPInterface_beginConnection("------------------------------------------------------", "x", "null")
{'error': 'failed to parse publicKey.'}
>>> cjdns.UDPInterface_beginConnection("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz0.k", "192.168.0.2:10000", "null")
{'error': 'invalid cjdns public key.'}
>>> cjdns.UDPInterface_beginConnection("v0zyvrjuc4xbzh4n9c4k3qpx7kg8xgndv2k45j9nfgb373m8sss0.k", "[1234::5]:10000", "null")
{'error': 'different address type than this socket is bound to.'}
### AdminLog Functions:
Since cjdns contains so many logging locations, logging to a file would not only be inefficient
but it would fill up your disk rather quickly. Because if this, cjdns logging is only enabled on
request, with these functions you can ask for logs to be enabled on a log level, per-file or even
per-line basis.
Log levels may be excluded at compile time in which case they will not be available.
Each log level implies inclusion of every higher level, if you subscribe to **INFO** logging, you
will also automatically get **WARN**, **ERROR**, and **CRITICAL**.
Cjdns log levels:
* **KEYS** Not compiled in by default, contains private keys and other secret information.
* **DEBUG** Default level, contains lots of information which is probably not useful unless you are
diagnosing an ongoing problem.
* **INFO** Shows starting and stopping of various components and general purpose information.
* **WARN** Generally this means some system has undergone a minor failure, this includes failures
due to network disturbance.
* **ERROR** This means there was a (possibly temporary) failure of a system within cjdns.
* **CRITICAL** This means something is broken such that the cjdns core will likely
have to exit immedietly.
To see an implementation of cjdns log consumer, look at `contrib/python/cjdnslog`.
#### AdminLog_subscribe()
Subscribe to logging of a level/file/line.
**Auth Required**
**NOTE**: Because this function responds asynchronously, using `netcat` or `cexec` to call it
will not work, additionally it will stop sending asynchronous messages unless an incoming message
comes in every 10 seconds so you must send periodic messages on the same UDP port.
See: `Admin_asyncEnabled()` for more information.
Parameters:
* Int **line**: If specified, the logging will be constrained to the log message which appers on
the given line number in the source file.
* String **file**: If specified, the logging will be constrained to the named file, names are not
fully qualified, use "CryptoAuth.c", not "/path/to/CryptoAuth.c".
* String **level**: If specified, the logging will be constrained to log lines which are of the
given level or higher.
Returns:
* String **error**: `none` if all goes well.
* String **streamId**: an opaque string which will be contained in each log message.
Log message structure:
* String **file** the name of the file where the log message came from, eg: "CryptoAuth.c".
* String **level** the log level, one of `["KEYS", "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]`
* Int **line** the line number of the line where the log function was called.
* String **message** the log message
* String **streamId** the streamId for the logging subscription.
* Int **time** the time in seconds since the unix epoch when the log message was created.
* String **txid** the same transaction which was used in the call to `AdminLog_subscribe()`.
#### AdminLog_unsubscribe()
Unsubscribe from logging.
**Auth Required**
Parameters:
* required String **streamId**: The id returned in the call to `AdminLog_subscribe()`.
Returns:
* String **error**: `none` if the subscription existed and was removed.
**Note**: If the subscription has already timed out, removing it will yield `'error': 'No such subscription.'`.
Example:
$ ./contrib/python/cexec 'AdminLog_subscribe()'
{'txid': '0EKWEP7VXI', 'streamId': 'f1a0e225183397f4', 'error': 'none'}
$ ./contrib/python/cexec 'AdminLog_unsubscribe("f1a0e225183397f4")'
{'txid': 'CB4V7KLYCC', 'error': 'none'}
### Admin Functions
These functions are for dealing with the Admin interface, the infrastructure which allows all
of the other functions throughout cjdns to be accessed from the admin socket.
#### Admin_availableFunctions()
Get a list of functions which are available to the admin socket as well as their required and
optional parameters, unfortunately their return values are not provided and can only be determined
by experimentation or by reading the source.
**Note**: The list of functions is paged to make sure each message fits inside of a UDP packet, in
order to get the whole list of functions, you must increment the `page` parameter until the result
nolonger contains the `more` field.
Parameters:
* Int **page**: the page of functions to request, if unspecified it will be assumed to be 0.
Returns:
* Dict **availableFunctions**: a map of function descriptions by function name.
* Int **more**: only present if there are more pages.
##### Function Description:
Each function description is a Dict of function parameters with the parameter name as the key and
the specifications as the value. The specification `required` is an Int which is either 0 meaning
the parameter is optional or 1 meaning it is required. `type` is a String which is one of
`["Int", "String", "Dict", "List"]` and defines the type which the parameter must be.
'AdminLog_subscribe': {
'line': {
'required': 0,
'type': 'Int'
},
'file': {
'required': 0,
'type': 'String'
},
'level': {
'required': 0,
'type': 'String'
}
}
#### Admin_asyncEnabled()
This function is for determining whether asynchronous communication is allowed.
Asynchronous communication, EG: AdminLog responses, is only allowed with clients which satisfy
certain requirements.
1. They must send an authenticated request, in the case of AdminLog this is no worry because
`AdminLog_subscribe()` requires authentication.
2. They must have sent something in the past 10 seconds, because of the statelessness of UDP,
there is no way to tell a client which is listening quietly from one which has wandered off so
in order to remain enabled, it must periodically ping (or periodically call `Admin_asyncEnabled()`).
These calls do not need to be authenticated, there just needs to have been one in history.
Returns:
* Int **asyncEnabled**: 1 if asynchronous communication is allowed for this session, 0 otherwise.
Example:
This example illustrates how using `cexec` to call it returns true because `cexec` uses
authenticated calls whereas manually calling it without authentication returns false.
$ ./contrib/python/cexec 'Admin_asyncEnabled()'
{'asyncEnabled': 1, 'txid': '74GF0SS2N0'}
echo '{ "q": "Admin_asyncEnabled" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc -u 127.0.0.1 11234 \
| ./build/benc2json
{
"asyncEnabled" : 0
}
### Security Functions
These functions are available for putting the cjdns core into a sandbox where
a security breach within the core would be less likely to cause a total system compromize.
#### Security_setUser()
Set the user ID which cjdns is running under to a different user. This function allows cjdns
to shed privileges after starting up.
**NOTE**: This function will always fail with an error about `process cannot open more files` if
`Security_noFiles()` has already been called.
Parameters:
* required String **user**: the name of the user to change to.
Return:
* String **error**: `none` if all went well, otherwise a description of the failure.
#### Security_noFiles()
Set the hard open file limit to zero, while this does not force closed file descriptors which are
already open, it makes any function requiring the opening of a file to fail providing a powerful
sandbox. By calling this function after cjdns is started, one can insure that cjdns core cannot
touch the filesystem or open network sockets which it does not already have open. This will however
prevent a number of other admin API functions fron working.
Returns:
* String **error**: `none`
Examples:
$ ./contrib/python/cexec 'UDPInterface_new("[::]:2048")'
{'interfaceNumber': 3, 'txid': 'NQGOZXJZIC', 'error': 'none'}
$ ./contrib/python/cexec 'Security_noFiles()'
{'txid': 'CQYQWA5SZY', 'error': 'none'}
$ ./contrib/python/cexec 'UDPInterface_new("[::]:5000")'
{'txid': 'UZH9LIUOG0', 'cause': 'process cannot open more files', 'error': 'call to socket() failed [process cannot open more files]'}
### Core_initTunnel()
This function is used during cjdns startup to initialize the TUN device, set it's IP address
and set the MTU, it is hastily designed and may be removed in the future.
Parameters:
* String **desiredTunName**: the name of the TUN device to use, if unspecified it will ask the
kernel for a new device.
Returns:
* String **error**: `none` if all went well, otherwise the error which occured.
**Note**: an error will be returned if anything goes wrong initializing the tunnel, setting it's
IP address or setting it's MTU, even if there is an error, the tunnel may work just fine and
even if the tunnel doesn't work, cjdns will function as a router only without the TUN device.
### Core_exit()
A function to stop cjdns.
Returns:
* String **error**: `none` before exiting.
### ping()
Returns:
{'q':'pong'}
For checking if the admin connection is functioning.
### RouterModule_lookup()
**Auth Required**
Parameters:
* String **address** a 39 character (zero padded) ipv6 address.
Returns:
* A route if one is found in the routing table.
* An address and route of the node which should be handed the packet,
if a route is not found in the local table.
* An error if the address is not parsable.
Examples:
>>> print cjdns.RouterModule_lookup('fc5d:baa5:61fc:6ffd:9554:67f0:e290:7535')
{'result': '0000.0000.0000.1953', 'error': 'none'}
>>> print cjdns.RouterModule_lookup('fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536')
{'result': 'fcf1:a7a8:8ec0:589b:c64c:cc95:1ced:3679@0000.0000.0000.0013', 'error': 'none'}
>>> print cjdns.RouterModule_lookup('f')
{'result': '', 'error': 'address wrong length'}
>>> print cjdns.RouterModule_lookup('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')
{'result': '', 'error': 'failed to parse address'}
### AuthorizedPasswords_add()
**Auth Required**
Parameters:
* String **password** a password which will allow neighbors to make direct connections.
* String **user** a friendly string to identify this password.
* Int **authType** (optional) the method for authenticating,
defaults to `1` (only currently supported method).
Returns:
* **error**:`none` if everything went well.
* **error**:`Specified auth type is not supported.` if the auth type is specified and not `1`.
* **error**:`Password already added.` if you try to add the same user or password twice.
* **error**:`Out of memory to store password.` if the buffer for storing
authorized passwords is full.
Examples:
$ ./contrib/python/cexec 'AuthorizedPasswords_add(user="test",password="yh14wl2ffgcqq6bvut12xrz7g3")'
{'error': 'none'}
$ ./contrib/python/cexec 'AuthorizedPasswords_add(user="test2",password="2yh14wl2ffgcqq6bvut12xrz7g3",authType=300)'
{'error': 'Specified auth type is not supported.'}
$ ./contrib/python/cexec 'AuthorizedPasswords_add(user="test",password="yh14wl2ffgcqq6bvut12xrz7g3")'
{'error': 'Password already added.'}
### AuthorizedPasswords_list()
**Auth Required**
Get a list of all the authorized users.
Example:
$ ./contrib/python/cexec 'AuthorizedPasswords_list()'
{'total': 2, 'users': ['Test User1', 'Local Peers'], 'txid': 'W0DUG0D50K'}
### memory()
Get the number of bytes of memory allocated by all memory allocators in the router.
Example:
>>> cjdns.memory()
{'bytes': 779259}
### NodeStore_dumpTable()
Parameters:
* Int **page** the page of the routing table to dump,
allowing you to get the whole table in a series of reasonably small requests.
Response:
* `routingTable` a key which contains a list of dictionaries, each containing `ip`,
`link` and `path`. `ip` is the IPv6 address of the node, `link` is a unitless number
between 0 inclusive and 2^32 exclusive, representing the router's opinion of the quality of
that path, higher is better. `path` is the route to the node.
* `more` to signal that there is another page of results, the engine will add a `more` key
with the integer 1, if there isn't another page of results, the `more` key will not be added.
What the data looks like:
{
'routingTable': [
{
'ip': 'fce5:de17:cbde:c87b:5289:0556:8b83:c9c8',
'link': 4294967295,
'path': '0000.0000.0000.0001'
}, {
'ip': 'fcfc:2ebe:346c:7fe7:95af:a58b:2631:dead',
'link': 235149061,
'path': '0000.0000.631a.3b53'
}, {
'ip': 'fc70:772a:f803:7c4e:38bd:981b:f791:60a1',
'link': 271119350,
'path': '0000.0000.017b.b333'
},
..............................
],
'more': 1
}
Example:
>>> cjdns.NodeStore_dumpTable(0)
{'routingTable': [{'ip': 'fce5:de17:cbde:c87b:5289:0556:8b83:c9c8', 'link': 4294967295,....
>>> cjdns.NodeStore_dumpTable(4)
{'routingTable': []}
### SwitchPinger_ping()
**Auth Required**
Send a switch level ping. There is no routing table lookup and the router is not involved.
Pinging IP addresses this way is not possible.
Parameters:
SwitchPinger_ping(required String path, String data, Int timeout)
* String **path** the route to the node to ping eg: `0000.0000.04f5.2555`
* String **data** (optional) for diagnosing data-dependent errors.
* Int **timeout** (optional) milliseconds to wait for a response.
If unspecified, will default to `DEFAULT_TIMEOUT` as defined in `SwitchPinger_admin.c` (2 seconds).
Examples:
>>> cjdns.SwitchPinger_ping('0000.0000.04f5.2555')
{'path': '0000.0000.04f5.2555', 'data': '', 'result': 'pong', 'ms': 281}
>>> cjdns.SwitchPinger_ping('fca5:9fe0:3fa2:d576:71e6:8373:7aeb:ea11')
{'error': 'path was not parsable.'}
>>> cjdns.SwitchPinger_ping('0000.0000.04f5.2555', '12345abcdefg')
{'path': '0000.0000.04f5.2555', 'data': '12345abcdefg', 'result': 'pong', 'ms': 326}
>>> cjdns.SwitchPinger_ping('0000.0000.0405.2555')
{'path': '0000.0000.0405.2555', 'data': '', 'result': 'ping message caused switch error', 'ms': 278}
>>> cjdns.SwitchPinger_ping('0000.0000.04f5.2555', '', 30)
{'result': 'timeout', 'ms': 77}

1
admin/README.md Symbolic link
View File

@@ -0,0 +1 @@
../doc/admin-api.md

944
doc/admin-api.md Normal file
View File

@@ -0,0 +1,944 @@
#Cjdns Admin API
Cjdns is inspected and configured through a UDP socket.
When cjdroute starts up, it reads the configuration file and spawns cjdns core. The core
knows nothing but the port which it should bind to and the private key which it should use.
All other information such as peers, interfaces and passwords is given to the core through the
admin UDP interface. When cjdroute is finished setting up the core, it exits leaving the core
running in the background.
You can call all of the functions which are called by cjdroute to collect information and alter
the core's configuration.
## How a function works
To call a function you send a udp packet containing a bencoded request to the core and it sends
back a bencoded response.
echo -n 'd1:q4:pinge' | nc6 -u -t 1 -n -w3 127.0.0.1 11234
If you are more comfortable writing json then benc, you can use benc2json in reverse mode to
preprocess your message. **Note**: benc2json has been removed in 2821c81d49 to speed up the build.
echo '{ "q": "ping" }' | ./build/benc2json -r
Stream the request from json into benc and then make the request to the core:
echo '{ "q": "ping" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234
Get the result back into json:
echo '{ "q": "ping" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
## Transaction ID
Because you can send multiple messages at once, you may add a transaction ID to a message and it
will be reflected back to you in the response.
echo '{ "q": "ping", "txid": "my request" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
Result:
{
"txid" : "my request",
"q" : "pong"
}
## Arguments
Some functions require arguments and others allow arguments but assume defaults if they are
not provided. Arguments are sent to a function through a benc *dictionary* called `args`.
The `Admin_availableFunctions()` function has an optional argument called `page`, this is
because there are too many functions to be described in a single UDP packet. The following
command will get the first page of functions from `Admin_availableFunctions` which will
describe other functions and their required and allowed arguments.
echo -n '
{
"q": "Admin_availableFunctions",
"args": {
"page": 0
}
}' | ./build/benc2json -r \
| tr -d '\n' \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
## Authentication
Any function which changes the state of cjdns core requires authentication to carry out.
Authentication is done on a per-request basis. Functions which don't require authentication
can still be called with authentication and will still fail if the authentication is incorrect.
* Step 1: Request a cookie from the server.
* Step 2: Calculate the SHA-256 of the cookie and your admin password, place this hash and cookie
in the request.
* Step 3: Calculate the SHA-256 of the entire request with the hash and cookie added,
replace the hash in the request with this result.
Steps 1 and 2 securely bind the cookie to the password so that the password hash cannot
be taken and used again in another request later on, step 3 binds the cookie and password to
the request so that a man-in-the-middle cannot change the content of the request in flight.
### Anatomy of an authenticated request
A plain request such as `{"q": "ping"}` becomes `{"q":"auth", "aq":"ping", "hash":<calculated hash>}`.
The `q` field is moved to `aq` (authenticated query) and the `q` field says `auth`.
**NOTE:** A cookie is only valid for 10 seconds so requesting and using a cookie must be done
in the same script.
**NOTE2:** Cookies are reusable *for now* this is not part of the API and is considered a
bug, you should always request a new cookie for each authenticated request otherwise you may
be broke by changes in the future.
### By example
**Step 1:** Get the cookie
RESP=`echo -n 'd1:q6:cookiee' | nc6 -u -t 1 -n -w3 127.0.0.1 11234` \
echo response=${RESP}; \
COOKIE=`echo ${RESP} | sed 's/d6:cookie10:\([0-9]*\)e/\1/'` \
echo cookie=${COOKIE};
**Step 2:** Calculate the hash of the cookie and password:
For this step, you will need the admin password from your cjdroute.conf file, it's to be found
inside of the block which says `"admin": {`.
ADMIN_PASS=you_will_find_this_in_your_cjdroute_dot_conf \
REQUEST='{"q": "auth", "aq": "ping", "hash": "__HASH__", "cookie": "__COOKIE__"}' \
COOKIE_RESP=`echo -n 'd1:q6:cookiee' | nc6 -u -t 1 -n -w3 127.0.0.1 11234` \
COOKIE=`echo ${COOKIE_RESP} | sed 's/d6:cookie10:\([0-9]*\)e/\1/'` \
HASH_ONE=`echo -n "${ADMIN_PASS}${COOKIE}" | sha256sum -b | cut -d\ -f1` ; \
REQ_ONE=`echo $REQUEST | sed -e "s/__HASH__/${HASH_ONE}/" -e "s/__COOKIE__/${COOKIE}/" \
| ./build/benc2json -r | tr -d '\n'` ; \
echo "hash of password and cookie is ${HASH_ONE}" ; \
echo "Request with cookie and hash added:" ; \
echo "${REQ_ONE}" ; \
echo "JSON version of request:" ; \
echo "${REQ_ONE}" | ./build/benc2json
**Step 3:** Calculate the SHA-256 of the entire request and replace the one in the request:
This will calculate the final request and send it to cjdns.
ADMIN_PASS=you_will_find_this_in_your_cjdroute_dot_conf \
REQUEST='{"q": "auth", "aq": "ping", "hash": "__HASH__", "cookie": "__COOKIE__"}' \
COOKIE_RESP=`echo -n 'd1:q6:cookiee' | nc6 -u -t 1 -n -w3 127.0.0.1 11234` \
COOKIE=`echo ${COOKIE_RESP} | sed 's/d6:cookie10:\([0-9]*\)e/\1/'` \
HASH_ONE=`echo -n "${ADMIN_PASS}${COOKIE}" | sha256sum -b | cut -d\ -f1` \
REQ_ONE=`echo $REQUEST | sed -e "s/__HASH__/${HASH_ONE}/" -e "s/__COOKIE__/${COOKIE}/" \
| ./build/benc2json -r | tr -d '\n'` \
FINAL_HASH=`echo -n "$REQ_ONE" | sha256sum -b | cut -d\ -f1` \
FINAL_REQ=`echo $REQ_ONE | sed -e "s/${HASH_ONE}/${FINAL_HASH}/"` ; \
echo -n "$FINAL_REQ" \
| nc6 -u -t 1 -n -w3 127.0.0.1 11234 \
| ./build/benc2json
If you see this:
{
"q" : "pong"
}
then it has succeeded, if the password is incorrect, you will see this:
{
"error" : "Auth failed."
}
### Tools:
Obviously using bash to craft cjdns admin RPC calls is probably the most awkward way possible,
there are tools in cjdns/contrib which will help you craft requests, specifically there are
libraries written in python and perl which will allow users to call cjdns internal functions
as python/perl native functions. A tool called `cexec` is provided with the python library which
allows you to call cjdns functions from shell scripts or the command line as follows:
./contrib/python/cexec 'ping()'
## Cjdns Functions:
user@ubnta8:~/wrk/cjdns$ ./contrib/python/cexec 'functions()' | sort
Admin_asyncEnabled()
Admin_availableFunctions(page='')
Allocator_bytesAllocated()
Allocator_snapshot(includeAllocations='')
AuthorizedPasswords_add(password, user, authType='', ipv6=0)
AuthorizedPasswords_list()
AuthorizedPasswords_remove(user)
Core_exit()
Core_initTunnel(desiredTunName=0)
Core_pid()
ETHInterface_beacon(interfaceNumber='', state='')
ETHInterface_beginConnection(publicKey, macAddress, interfaceNumber='', password=0)
ETHInterface_new(bindDevice)
InterfaceController_disconnectPeer(pubkey)
InterfaceController_peerStats(page='')
IpTunnel_allowConnection(publicKeyOfAuthorizedNode, ip6Address=0, ip4Address=0)
IpTunnel_connectTo(publicKeyOfNodeToConnectTo)
IpTunnel_listConnections()
IpTunnel_removeConnection(connection)
IpTunnel_showConnection(connection)
memory()
NodeStore_dumpTable(page)
NodeStore_getLink(parent, linkNum)
NodeStore_getRouteLabel(pathParentToChild, pathToParent)
NodeStore_nodeForAddr(ip=0)
ping()
RainflyClient_addKey(ident)
RainflyClient_addServer(addr)
RainflyClient_minSignatures(count)
RouterModule_findNode(nodeToQuery, target, timeout='')
RouterModule_getPeers(path, nearbyPath=0, timeout='')
RouterModule_lookup(address)
RouterModule_pingNode(path, timeout='')
SearchRunner_showActiveSearch(number)
Security_checkPermissions()
Security_dropPermissions()
Security_setUser(user)
SessionManager_getHandles(page='')
SessionManager_sessionStats(handle)
SwitchPinger_ping(path, data=0, keyPing='', timeout='')
UDPInterface_beginConnection(publicKey, address, interfaceNumber='', password=0)
UDPInterface_new(bindAddress=0)
###RouterModule_pingNode()
**Auth Required**
Send a node a cjdns ping request.
Parameters:
* required String **path** may be a route such as "0000.0000.0000.1d53" or an ip address such as
"fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536", or an ip with explicit path
eg: "fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536@0000.0000.0000.1d53"
* Int **timeout** (optional) the number of milliseconds after which to timeout the ping
if there is no response. Defaults to router's adaptive ping timeout if unspecified.
Responses:
* **error**: `could not find node to ping` if there was no node by the given address found in the
routing table
* **result**: `timeout` gives timeout and number of milliseconds since the ping.
* **result**: `pong` gives `version` representing the git hash of the source code which built the
pinged node, and `ms` which is the number of milliseconds since the original ping.
Examples:
>>> cjdns.RouterModule_pingNode('fc38:4c2c:1a8f:3981:f2e7:c2b9:6870:6e84')
{'version': '5c5e84ccdba3f31f7c88077729700b4368320bc2', 'result': 'pong', 'ms': 79}
>>> cjdns.RouterModule_pingNode('fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536')
{'error': 'could not find node to ping'}
>>> cjdns.RouterModule_pingNode('0000.0000.0000.0013')
{'version': '2b62b9ae911f1044e45f3f28fdd63d0d5a7fc512', 'result': 'pong', 'ms': 0}
>>> cjdns.RouterModule_pingNode('a')
{'error': "Unexpected length, must be either 39 char ipv6 address (with leading zeros)
eg: 'fc4f:000d:e499:8f5b:c49f:6e6b:01ae:3120' or 19 char path eg: '0123.4567.89ab.cdef'"}
>>> cjdns.RouterModule_pingNode('aaaaaaaaaaaaaaaaaaa')
{'error': 'parse path failed'}
>>> cjdns.RouterModule_pingNode('aaaaaaaaaaaaaaaaaaazzzzzzzzzzzzzzzzzzzz')
{'error': 'parsing address failed'}
>>> cjdns.RouterModule_pingNode('fc38:4c2c:1a8f:3981:f2e7:c2b9:6870:6e84', 10)
{'result': 'timeout', 'ms': 10}
### ETHInterface Functions:
ETHInterface is a connector which allows cjdns nodes on the same lan to automatically connect
without the need to IP addresses on the LAN or sharing of connection credentials. It works on
wireless LANs as well as wired ethernet LANs.
#### ETHInterface_new()
Create a new ETHInterface and bind it to a device.
**NOTE**: this call will always fail with `'error': 'call to socket() failed. [permission denied]'`
unless it is running as root and will fail with `process cannot open more files` if
`Security_setUser()` has already been called.
**Auth Required**
Parameters:
* required String **bindDevice** the name of the ethernet device to bind to, eg: `eth0` or `wlan0`.
Returns:
* Int **interfaceNumber** an number which can be used to carry out other operations on the interface later.
#### ETHInterface_beginConnection()
Connect an ETHInterface to another computer which has an ETHInterface running.
**Auth Required**
Parameters:
* required String **publicKey** The public key of the other node, similar to `UDPInterface_beginConnection()`
* required String **macAddress** The mac address of the other node.
* Int **interfaceNumber** The interface number to use, assumed 0 (first ETHinterface created) if not supplied.
* String **password** A password for connecting to the other node if required.
Returns:
* String **error**: `none` if everything went well.
Other errors are self-explanitory.
#### ETHInterface_beacon()
Enable or disable sending or receiving of ETHInterface beacon messages.
ETHInterface uses periodic beacon messages to automatically peer nodes which are on the same LAN.
Be mindful that if your lan has is open wifi, enabling beaconing will allow anyone to peer with you.
**Auth Required**
Beacon States:
0. Disabled, no beacons are sent and incoming beacon messages are discarded.
1. Accepting, no beacons are sent but if an incoming beacon is received, it is acted upon.
2. Sending and Accepting, beacons are sent and accepted.
Parameters:
* Int **interfaceNumber** The number of the ETHInterface to change the state of, assumed 0 if not provided.
* Int **state** What state to switch to, if not provided, the current state will be queried only.
Returns:
* String **error**: `none` if all went well.
* Int **state**: the state number after the call is complete.
* String **stateName**: a description of the state.
Example:
$ ./contrib/python/cexec 'ETHInterface_beacon(2)'
{'txid': 'FYRKHAPIM3', 'error': 'invalid interfaceNumber'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0)'
{'txid': 'Z7KHE7SZ5R', 'state': 2, 'stateName': 'sending and accepting', 'error': 'none'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0, 0)'
{'txid': 'TP1R8PYCNS', 'state': 0, 'stateName': 'disabled', 'error': 'none'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0, 1)'
{'txid': 'UGKKGX4ZC9', 'state': 1, 'stateName': 'accepting', 'error': 'none'}
$ ./contrib/python/cexec 'ETHInterface_beacon(0, 2)'
{'txid': '1B7RXJEH3N', 'state': 2, 'stateName': 'sending and accepting', 'error': 'none'}
### IpTunnel Functions
IPTunnel is designed to allow tunneling of IPv4 and IPv6 packets through a cjdns network
to the external internet or to a virtual LAN. It provides familiar VPN type functionality.
There are 2 nodes, a client and a server, the server uses `IPTunnel_allowConnection()` and the
client uses `IPTunnel_connectTo()` the server assigns IPv4 and/or IPv6 addresses to the client
and the client is required to use only these addresses, subnet assignment is not supported.
When the client uses `IPTunnel_connectTo()`, it sends a request to the server for addresses and
continues polling the server periodically until the addresses are provided.
#### IpTunnel_listConnections()
List the connection numbers of all IPTunnel connections.
**Auth Required**
Returns:
* List **connections**: A list of integers representing the connection numbers for each connection.
* String **error**: `none`
Example:
$ ./contrib/python/cexec 'IpTunnel_listConnections()'
{'connections': [0], 'txid': '5ZFPFJ60AT', 'error': 'none'}
#### IpTunnel_showConnection()
Show information about a perticular IPTunnel connection.
**Auth Required**
Parameters:
* required Int **connection**: the connection number for the connection to show information about.
Returns:
* Int **outgoing**: 1 if the connection is outgoing, 0 if it's incoming.
* String **key**: the cjdns public key of the foreign node.
* String **ip6Address**: the IPv6 address which is assigned to this IPTunnel if applicable.
* Int **ip6Prefix**: the IPv6 netmask/prefix length which is assigned to this IPTunnel if applicable.
* String **ip4Address**: the IPv4 address which is assigned to this IPTunnel if applicable.
* Int **ip4Prefix**: the IPv4 netmask/prefix length which is assigned to this IPTunnel if applicable.
* String **error**: `none` unless the connection number is invalid.
Examples:
# Prior to getting it's addresses from the server, they are not listed.
$ ./contrib/python/cexec 'IpTunnel_showConnection(0)'
{'outgoing': 1, 'txid': 'REIV40SXD9', 'key': 'd5d0wu0usrkufd8s98t19gt7m2ggvbz1xbnuxu82x63uqlnk2kb0.k', 'error': 'none'}
# After a short wait, the addresses are provided and they are now listed.
$ ./contrib/python/cexec 'IpTunnel_showConnection(0)'
{'outgoing': 1, 'txid': 'CAQCTWECRD', 'ip4Address': '192.168.10.2', 'key': 'd5d0wu0usrkufd8s98t19gt7m2ggvbz1xbnuxu82x63uqlnk2kb0.k', 'error': 'none', 'ip6Address': '2a02:2498:e000:20::144:3'}
#### IpTunnel_removeConnection()
Remove an IPTunnel connection from the list, the other end will nolonger be able to send traffic
over this connection.
**Auth Required**
**NOT IMPLEMENTED**
#### IpTunnel_connectTo()
Initiate an *outgoing* connection to another node and request IP addresses from them.
**Auth Required**
Parameters:
* required String **publicKeyOfNodeToConnectTo** the pubkey of the node to connect to.
Returns:
* String **error**: `none` if all went well
* Int **connection**: the connection number of the new connection
Examples:
$ ./contrib/python/cexec 'IpTunnel_connectTo("d5d0wu0usrkufd8s98t19gt7m2ggvbz1xbnuxu82x63uqlnk2kb0.k")'
{'connection': 1, 'txid': '9QXRQO1FG8', 'error': 'none'}
#### IpTunnel_allowConnection()
Allow in *incoming* connection from another node, they must also use `IPTunnel_connectTo()` in order
to complete the connection.
**Auth Required**
Parameters:
* required String **publicKeyOfAuthorizedNode** The key of the node which is authorized to connect.
* String **ip6Address** The IPv6 address to give them if applicable.
* String **ip4Address** The IPv4 address to give them if applicable.
Returns:
* String **error** `none` if all went well.
* Int **connection** the connection number for the new connection.
### UDPInterface Functions
UDPInterface is the basic cjdns interface which is used to link distant nodes over the internet.
It will work on a LAN as long as the nodes have IP addresses but for linking on a LAN, ETHInterface
is easier.
#### UDPInterface_new()
Create a new UDPInterface which is either bound to an address/port or not.
**NOTE**: This call will fail with `'error': 'call to socket() failed [process cannot open more files]'`
is `Security_noFiles()` has already been called.
Parameters:
* String **bindAddress**: the address/port to bind to, if unspecified, it is assumed to be `0.0.0.0`.
Returns:
* String **error** `none` if all went well
* Int **interfaceNumber** the number of the interface, usable with `UDPInterface_beginConnection()`
#### UDPInterface_beginConnection()
Start a direct connection to another node.
**Auth Required**
Parameters:
* required String **publicKey** the base32 public key for the node to connect to, ending in .k.
* required String **address** the ip address and port for the node, at this time DNS resolution
and IPv6 is not supported.
* Int **interfaceNumber** the number for the UDPInterface to use for connecting, provided by
*UDPInterface_new()* if not sent, 0 is assumed.
* String **password** a password to use when connecting.
Note: just because it returns `'error': 'none'` does not mean that the connection was successful.
The neighbor may still reject our connection attempts.
Example:
>>> cjdns.UDPInterface_beginConnection("v0zyvrjuc4xbzh4n9c4k3qpx7kg8xgndv2k45j9nfgb373m8sss0.k", "192.168.0.2:10000", "null")
{'error': 'none'}
>>> cjdns.UDPInterface_beginConnection("v0zyvrjuc4xbzh4n9c4k3qpx7kg8xgndv2k45j9nfgb373m8sss0.k", "x", "null")
{'error': 'unable to parse ip address and port.'}
>>> cjdns.UDPInterface_beginConnection("k", "x", "null")
{'error': 'publicKey is too short, must be 52 characters long.'}
>>> cjdns.UDPInterface_beginConnection("------------------------------------------------------", "x", "null")
{'error': 'failed to parse publicKey.'}
>>> cjdns.UDPInterface_beginConnection("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz0.k", "192.168.0.2:10000", "null")
{'error': 'invalid cjdns public key.'}
>>> cjdns.UDPInterface_beginConnection("v0zyvrjuc4xbzh4n9c4k3qpx7kg8xgndv2k45j9nfgb373m8sss0.k", "[1234::5]:10000", "null")
{'error': 'different address type than this socket is bound to.'}
### AdminLog Functions:
Since cjdns contains so many logging locations, logging to a file would not only be inefficient
but it would fill up your disk rather quickly. Because if this, cjdns logging is only enabled on
request, with these functions you can ask for logs to be enabled on a log level, per-file or even
per-line basis.
Log levels may be excluded at compile time in which case they will not be available.
Each log level implies inclusion of every higher level, if you subscribe to **INFO** logging, you
will also automatically get **WARN**, **ERROR**, and **CRITICAL**.
Cjdns log levels:
* **KEYS** Not compiled in by default, contains private keys and other secret information.
* **DEBUG** Default level, contains lots of information which is probably not useful unless you are
diagnosing an ongoing problem.
* **INFO** Shows starting and stopping of various components and general purpose information.
* **WARN** Generally this means some system has undergone a minor failure, this includes failures
due to network disturbance.
* **ERROR** This means there was a (possibly temporary) failure of a system within cjdns.
* **CRITICAL** This means something is broken such that the cjdns core will likely
have to exit immedietly.
To see an implementation of cjdns log consumer, look at `contrib/python/cjdnslog`.
#### AdminLog_subscribe()
Subscribe to logging of a level/file/line.
**Auth Required**
**NOTE**: Because this function responds asynchronously, using `netcat` or `cexec` to call it
will not work, additionally it will stop sending asynchronous messages unless an incoming message
comes in every 10 seconds so you must send periodic messages on the same UDP port.
See: `Admin_asyncEnabled()` for more information.
Parameters:
* Int **line**: If specified, the logging will be constrained to the log message which appers on
the given line number in the source file.
* String **file**: If specified, the logging will be constrained to the named file, names are not
fully qualified, use "CryptoAuth.c", not "/path/to/CryptoAuth.c".
* String **level**: If specified, the logging will be constrained to log lines which are of the
given level or higher.
Returns:
* String **error**: `none` if all goes well.
* String **streamId**: an opaque string which will be contained in each log message.
Log message structure:
* String **file** the name of the file where the log message came from, eg: "CryptoAuth.c".
* String **level** the log level, one of `["KEYS", "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]`
* Int **line** the line number of the line where the log function was called.
* String **message** the log message
* String **streamId** the streamId for the logging subscription.
* Int **time** the time in seconds since the unix epoch when the log message was created.
* String **txid** the same transaction which was used in the call to `AdminLog_subscribe()`.
#### AdminLog_unsubscribe()
Unsubscribe from logging.
**Auth Required**
Parameters:
* required String **streamId**: The id returned in the call to `AdminLog_subscribe()`.
Returns:
* String **error**: `none` if the subscription existed and was removed.
**Note**: If the subscription has already timed out, removing it will yield `'error': 'No such subscription.'`.
Example:
$ ./contrib/python/cexec 'AdminLog_subscribe()'
{'txid': '0EKWEP7VXI', 'streamId': 'f1a0e225183397f4', 'error': 'none'}
$ ./contrib/python/cexec 'AdminLog_unsubscribe("f1a0e225183397f4")'
{'txid': 'CB4V7KLYCC', 'error': 'none'}
### Admin Functions
These functions are for dealing with the Admin interface, the infrastructure which allows all
of the other functions throughout cjdns to be accessed from the admin socket.
#### Admin_availableFunctions()
Get a list of functions which are available to the admin socket as well as their required and
optional parameters, unfortunately their return values are not provided and can only be determined
by experimentation or by reading the source.
**Note**: The list of functions is paged to make sure each message fits inside of a UDP packet, in
order to get the whole list of functions, you must increment the `page` parameter until the result
nolonger contains the `more` field.
Parameters:
* Int **page**: the page of functions to request, if unspecified it will be assumed to be 0.
Returns:
* Dict **availableFunctions**: a map of function descriptions by function name.
* Int **more**: only present if there are more pages.
##### Function Description:
Each function description is a Dict of function parameters with the parameter name as the key and
the specifications as the value. The specification `required` is an Int which is either 0 meaning
the parameter is optional or 1 meaning it is required. `type` is a String which is one of
`["Int", "String", "Dict", "List"]` and defines the type which the parameter must be.
'AdminLog_subscribe': {
'line': {
'required': 0,
'type': 'Int'
},
'file': {
'required': 0,
'type': 'String'
},
'level': {
'required': 0,
'type': 'String'
}
}
#### Admin_asyncEnabled()
This function is for determining whether asynchronous communication is allowed.
Asynchronous communication, EG: AdminLog responses, is only allowed with clients which satisfy
certain requirements.
1. They must send an authenticated request, in the case of AdminLog this is no worry because
`AdminLog_subscribe()` requires authentication.
2. They must have sent something in the past 10 seconds, because of the statelessness of UDP,
there is no way to tell a client which is listening quietly from one which has wandered off so
in order to remain enabled, it must periodically ping (or periodically call `Admin_asyncEnabled()`).
These calls do not need to be authenticated, there just needs to have been one in history.
Returns:
* Int **asyncEnabled**: 1 if asynchronous communication is allowed for this session, 0 otherwise.
Example:
This example illustrates how using `cexec` to call it returns true because `cexec` uses
authenticated calls whereas manually calling it without authentication returns false.
$ ./contrib/python/cexec 'Admin_asyncEnabled()'
{'asyncEnabled': 1, 'txid': '74GF0SS2N0'}
echo '{ "q": "Admin_asyncEnabled" }' \
| ./build/benc2json -r \
| tr -d '\n' \
| nc -u 127.0.0.1 11234 \
| ./build/benc2json
{
"asyncEnabled" : 0
}
### Security Functions
These functions are available for putting the cjdns core into a sandbox where
a security breach within the core would be less likely to cause a total system compromize.
#### Security_setUser()
Set the user ID which cjdns is running under to a different user. This function allows cjdns
to shed privileges after starting up.
**NOTE**: This function will always fail with an error about `process cannot open more files` if
`Security_noFiles()` has already been called.
Parameters:
* required String **user**: the name of the user to change to.
Return:
* String **error**: `none` if all went well, otherwise a description of the failure.
#### Security_noFiles()
Set the hard open file limit to zero, while this does not force closed file descriptors which are
already open, it makes any function requiring the opening of a file to fail providing a powerful
sandbox. By calling this function after cjdns is started, one can insure that cjdns core cannot
touch the filesystem or open network sockets which it does not already have open. This will however
prevent a number of other admin API functions fron working.
Returns:
* String **error**: `none`
Examples:
$ ./contrib/python/cexec 'UDPInterface_new("[::]:2048")'
{'interfaceNumber': 3, 'txid': 'NQGOZXJZIC', 'error': 'none'}
$ ./contrib/python/cexec 'Security_noFiles()'
{'txid': 'CQYQWA5SZY', 'error': 'none'}
$ ./contrib/python/cexec 'UDPInterface_new("[::]:5000")'
{'txid': 'UZH9LIUOG0', 'cause': 'process cannot open more files', 'error': 'call to socket() failed [process cannot open more files]'}
### Core_initTunnel()
This function is used during cjdns startup to initialize the TUN device, set it's IP address
and set the MTU, it is hastily designed and may be removed in the future.
Parameters:
* String **desiredTunName**: the name of the TUN device to use, if unspecified it will ask the
kernel for a new device.
Returns:
* String **error**: `none` if all went well, otherwise the error which occured.
**Note**: an error will be returned if anything goes wrong initializing the tunnel, setting it's
IP address or setting it's MTU, even if there is an error, the tunnel may work just fine and
even if the tunnel doesn't work, cjdns will function as a router only without the TUN device.
### Core_exit()
A function to stop cjdns.
Returns:
* String **error**: `none` before exiting.
### ping()
Returns:
{'q':'pong'}
For checking if the admin connection is functioning.
### RouterModule_lookup()
**Auth Required**
Parameters:
* String **address** a 39 character (zero padded) ipv6 address.
Returns:
* A route if one is found in the routing table.
* An address and route of the node which should be handed the packet,
if a route is not found in the local table.
* An error if the address is not parsable.
Examples:
>>> print cjdns.RouterModule_lookup('fc5d:baa5:61fc:6ffd:9554:67f0:e290:7535')
{'result': '0000.0000.0000.1953', 'error': 'none'}
>>> print cjdns.RouterModule_lookup('fc5d:baa5:61fc:6ffd:9554:67f0:e290:7536')
{'result': 'fcf1:a7a8:8ec0:589b:c64c:cc95:1ced:3679@0000.0000.0000.0013', 'error': 'none'}
>>> print cjdns.RouterModule_lookup('f')
{'result': '', 'error': 'address wrong length'}
>>> print cjdns.RouterModule_lookup('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')
{'result': '', 'error': 'failed to parse address'}
### AuthorizedPasswords_add()
**Auth Required**
Parameters:
* String **password** a password which will allow neighbors to make direct connections.
* String **user** a friendly string to identify this password.
* Int **authType** (optional) the method for authenticating,
defaults to `1` (only currently supported method).
Returns:
* **error**:`none` if everything went well.
* **error**:`Specified auth type is not supported.` if the auth type is specified and not `1`.
* **error**:`Password already added.` if you try to add the same user or password twice.
* **error**:`Out of memory to store password.` if the buffer for storing
authorized passwords is full.
Examples:
$ ./contrib/python/cexec 'AuthorizedPasswords_add(user="test",password="yh14wl2ffgcqq6bvut12xrz7g3")'
{'error': 'none'}
$ ./contrib/python/cexec 'AuthorizedPasswords_add(user="test2",password="2yh14wl2ffgcqq6bvut12xrz7g3",authType=300)'
{'error': 'Specified auth type is not supported.'}
$ ./contrib/python/cexec 'AuthorizedPasswords_add(user="test",password="yh14wl2ffgcqq6bvut12xrz7g3")'
{'error': 'Password already added.'}
### AuthorizedPasswords_list()
**Auth Required**
Get a list of all the authorized users.
Example:
$ ./contrib/python/cexec 'AuthorizedPasswords_list()'
{'total': 2, 'users': ['Test User1', 'Local Peers'], 'txid': 'W0DUG0D50K'}
### memory()
Get the number of bytes of memory allocated by all memory allocators in the router.
Example:
>>> cjdns.memory()
{'bytes': 779259}
### NodeStore_dumpTable()
Parameters:
* Int **page** the page of the routing table to dump,
allowing you to get the whole table in a series of reasonably small requests.
Response:
* `routingTable` a key which contains a list of dictionaries, each containing `ip`,
`link` and `path`. `ip` is the IPv6 address of the node, `link` is a unitless number
between 0 inclusive and 2^32 exclusive, representing the router's opinion of the quality of
that path, higher is better. `path` is the route to the node.
* `more` to signal that there is another page of results, the engine will add a `more` key
with the integer 1, if there isn't another page of results, the `more` key will not be added.
What the data looks like:
{
'routingTable': [
{
'ip': 'fce5:de17:cbde:c87b:5289:0556:8b83:c9c8',
'link': 4294967295,
'path': '0000.0000.0000.0001'
}, {
'ip': 'fcfc:2ebe:346c:7fe7:95af:a58b:2631:dead',
'link': 235149061,
'path': '0000.0000.631a.3b53'
}, {
'ip': 'fc70:772a:f803:7c4e:38bd:981b:f791:60a1',
'link': 271119350,
'path': '0000.0000.017b.b333'
},
..............................
],
'more': 1
}
Example:
>>> cjdns.NodeStore_dumpTable(0)
{'routingTable': [{'ip': 'fce5:de17:cbde:c87b:5289:0556:8b83:c9c8', 'link': 4294967295,....
>>> cjdns.NodeStore_dumpTable(4)
{'routingTable': []}
### SwitchPinger_ping()
**Auth Required**
Send a switch level ping. There is no routing table lookup and the router is not involved.
Pinging IP addresses this way is not possible.
Parameters:
SwitchPinger_ping(required String path, String data, Int timeout)
* String **path** the route to the node to ping eg: `0000.0000.04f5.2555`
* String **data** (optional) for diagnosing data-dependent errors.
* Int **timeout** (optional) milliseconds to wait for a response.
If unspecified, will default to `DEFAULT_TIMEOUT` as defined in `SwitchPinger_admin.c` (2 seconds).
Examples:
>>> cjdns.SwitchPinger_ping('0000.0000.04f5.2555')
{'path': '0000.0000.04f5.2555', 'data': '', 'result': 'pong', 'ms': 281}
>>> cjdns.SwitchPinger_ping('fca5:9fe0:3fa2:d576:71e6:8373:7aeb:ea11')
{'error': 'path was not parsable.'}
>>> cjdns.SwitchPinger_ping('0000.0000.04f5.2555', '12345abcdefg')
{'path': '0000.0000.04f5.2555', 'data': '12345abcdefg', 'result': 'pong', 'ms': 326}
>>> cjdns.SwitchPinger_ping('0000.0000.0405.2555')
{'path': '0000.0000.0405.2555', 'data': '', 'result': 'ping message caused switch error', 'ms': 278}
>>> cjdns.SwitchPinger_ping('0000.0000.04f5.2555', '', 30)
{'result': 'timeout', 'ms': 77}

View File

@@ -1,9 +0,0 @@
IP Tunneling
============
Writing what I can, when I can. Please edit as you see fit :)
Concept
-------
An IP tunnel allows you to virtually connect one computer to another over an existing network and assign it the IP address you desire. This comes in handy when you want to connect to two different networks over one connection. In the case of cjdns, this usually means connecting to The Internet from within a cjdns network.

181
doc/tunnel.md Normal file
View File

@@ -0,0 +1,181 @@
# IpTunnel - Tunneling IPv4 and IPv6 through a cjdns network
IpTunnel is designed to make it easy to access The Old Internet through cjdns.
The way you get to the internet is via "gate" nodes which hand you an address
the same way as a traditional VPN service would. TOR users might think of it as
"exit nodes" for cjdns, the main difference is with cjdns you need to ask permission
from the gate operator before (ab)using their gateway.
## Updating your cjdroute.conf
First compare your cjdroute.conf file to a newly generated one, if your cjdroute.conf
file is old, there are two changes which you will need to include. In the `router` section
you will need to add a subsection called `IpTunnel`.
// System for tunneling IPv4 and ICANN IPv6 through cjdns.
// This is using the cjdns switch layer as a VPN carrier.
"ipTunnel":
{
lots
of stuff here
see the real version
by running ./cjdroute --genconf
},
You may also have to modity the `setuser` section in the `security` block, there is
a new field called `exemptAngel` which needs to be set in order for cjdns to have
permission to set the IPv6 and IPv4 addresses on the TUN device.
// Change the user id to this user after starting up and getting resources.
{
"setuser": "nobody"
// Exempt the Angel process from setting userId, the Angel is a small
// isolated piece of code which exists outside of the core's strict
// sandbox but does not handle network traffic.
// This must be enabled for IpTunnel to automatically set IP addresses
// for the TUN device.
"exemptAngel": 1
}
## Connecting to a gateway
To connect to an IPv6 gate, you must first ask the operator of the gate to add your
key to his gate, once he has added it, add their *key* to the `outgoingConnections`
section of the `IpTunnel` block in your cjdroute.conf like this:
"outgoingConnections":
[
// John's gate
"d5d0wu0usrkuThisIsJustAnExampleThisIsFake63uqlnk2kb0.k"
]
Then restart cjdns and after a few moments you should see it add IP addresses to your TUN device by running
`ifconfig` for example:
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:192.168.10.4 P-t-P:192.168.10.4 Mask:255.255.255.255
inet6 addr: fc88:dfd0:89d4:abfe:de2:a17a:6ed5:6fb1/8 Scope:Global
inet6 addr: 2a02:2498:e000:20::144:4/0 Scope:Global <--- new address
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1312 Metric:1
RX packets:22950 errors:0 dropped:0 overruns:0 frame:0
TX packets:22891 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:22689370 (22.6 MB) TX bytes:2460363 (2.4 MB)
Then you can try pinging a computer out on the internet like `ipv6.google.com` and see if
it works.
## Running a gateway
Running your own gateway is not automated, so you will want to implement some scripts to
set the addresses for you. Lets imagine your ISP has given you the IPv6 prefix
`1111:1111:1111:1111::/64` and your ISP's router is `1111:1111:1111:1111::1`. Your ethernet
card is probably set to `1111:1111:1111:1111::2` so you'll begin allocating above that.
First you will have to reserve one address (eg: `1111:1111:1111:1111::3`) for your `tun0`
device's address, then each client can have an address, so the first client will be issued
`1111:1111:1111:1111::4`, the second `1111:1111:1111:1111::5` and so on.
First edit your cjdroute.conf and add the clients who will be connecting to your gate.
It's always a good idea to add some identification with the connect block so you know who
it is for later.
"allowedConnections":
[
// Bill Smith's connection
{
"publicKey": "f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k",
"ip6Address": "1111:1111:1111:1111::4",
"ip6Prefix": 0
}
]
Note the `ip6Prefix` field: it specifies the netmask that the client should use.
We have set it to 0, so the client will think the entire IPv6 addfress space is
accessible over the tunnel (which it is, since we're building a
cjdns-to-clearnet gateway). This avoids us having to set up an IPv6 default
gateway manually on the client node. If you want to advertise a smaller network
to your clients (like just the `1111:1111:1111:1111::/64` network), set this to
the appropriate value (in this case, 64).
When you start cjdroute, the IP address for the TUN device will *not* be set automatically,
so you must set that next with the following command:
ip -6 addr add dev tun0 1111:1111:1111:1111::3
Now that your tun device has an address, your client should be able to connect to and
ping `1111:1111:1111:1111::3`, but it definitely won't be able to reach the rest of the
world until you add a static route on the gateway to your ISP's router's address: `1111:1111:1111:1111::1`.
This will make it route over the ethernet device and add a static route to allow the rest of
your /64 to route down the TUN device. Once you're finished, you'll want to set a default
route via your ISP's router's address so outgoing IPv6 packets are forwarded correctly.
ip -6 route add dev eth0 1111:1111:1111:1111::1
ip -6 route add dev tun0 1111:1111:1111:1111::0/64
ip -6 route add default via 1111:1111:1111:1111::1
Finally you will need to enable IPv6 forwarding, to do this, run:
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
and to make it permanent, edit your `/etc/sysctl.conf` file and *uncomment* the line which says:
#net.ipv6.conf.all.forwarding=1
Connect the client to the gateway using cjdns and wait a few moments until you've obtained the ipv6
address associated with the tunnel. Now, test the connection by attempting to ping the ipv6 address
associated with the gateway on the tunnel, and if this succeeds you can try to ping an external ipv6
address like `ipv6.google.com` too if you're expecting internet traffic to be routed through.
Now if all went well your job is done, but if the connection isn't working yet you should continue on to
the next section and try to figure out why.
## It doesn't work
First, check to make sure you don't have any iptables rules against forwarding or ones that might be
blocking your connection in any way. You should also make sure a processes called 'radvd' isn't running
(at least to rule out as a cause until you have things working) since it seems to be capable of causing
some routing issues.
Now ensure the client is still connected to the gateway through cjdns, and that it still has an ipv6
address associated with the tunnel, then try pinging the gateway or an internet ipv6 address again with
the client. On the gateway, run `tcpdump -n -i tun0` to see if any packets get as far as the tun device, and
if nothing scrolls with the ipv6 associated with your client's on the tunnel, you should check your routes
to make sure they're correct by running `ip -6 route`. The output should include four lines that look similar
to the ones below (take special note of which device is associated with each line).
1111:1111:1111:1111:1111:1111:1111:1 dev eth0 metric 1024
1111:1111:1111:1111:1111:1111:1111:3 dev tun0 proto kernel metric 256
1111:1111:1111:1111:1111:1111:1111:0/64 dev tun0 metric 1024
default via 1111:1111:1111:1111:1111:1111:1111:1 dev eth0 metric 1024
If your routes are correct and things still aren't working, continue to let the ping process run on the client
and run `tcpdump -n -i eth0 icmp6` on the gateway to check the traffic flowing through its ethernet device.
Look for any connections to or from the ipv6 address associated with your client on the tunnel, and if you see
any with strange messages about "neighbor solicitation" or "neighbor advertisement", the problem is that your
ISP's equipment is dropping replies instead of routing return traffic despite the addresses in use being
allocated to you. This problem exists because of something called NDP (Neighbor Discovery Protocol), in which a
request for 'neighbours' is made, and traffic isn't allowed to be sent back unless the ISP receives a response.
Thankfully, there is a workaround available that involves running a daemon called `npd6`, which provides a
response that satisfies NDP. Install npd6 through your distro's package management system if it's available,
(recent debian based distrobutions may be able to install the package located here:
http://code.google.com/p/npd6/downloads/list) otherwise you'll have to download and build it yourself by doing
the following:
wget http://npd6.googlecode.com/files/npd6-1.0.0.tar.gz
tar -xf ./npd-1.0.0.tar.gz
cd npd6-1.0.0/
make && sudo make install
Once this is built, copy `npd6-1.0.0/etc/npd6.conf.sample` to `/etc/ndp6.conf`, and change the prefix to
`1111:1111:1111:1111:0000:0000:0000:0002/64` (2 because you're announcing `1111:1111:1111:1111::2` and higher,
but not `1111:1111:1111:1111::1` which belongs to your ISP's router). Note that in this particular instance,
you can't ignore 0s when writing out your ipv6 or npd6 will complain when you attempt to run it. If npd6 fails
to start, try running `npd6 -l npd6.log` to write a log in the local directory.

View File

@@ -1,181 +0,0 @@
# IpTunnel - Tunneling IPv4 and IPv6 through a cjdns network
IpTunnel is designed to make it easy to access The Old Internet through cjdns.
The way you get to the internet is via "gate" nodes which hand you an address
the same way as a traditional VPN service would. TOR users might think of it as
"exit nodes" for cjdns, the main difference is with cjdns you need to ask permission
from the gate operator before (ab)using their gateway.
## Updating your cjdroute.conf
First compare your cjdroute.conf file to a newly generated one, if your cjdroute.conf
file is old, there are two changes which you will need to include. In the `router` section
you will need to add a subsection called `IpTunnel`.
// System for tunneling IPv4 and ICANN IPv6 through cjdns.
// This is using the cjdns switch layer as a VPN carrier.
"ipTunnel":
{
lots
of stuff here
see the real version
by running ./cjdroute --genconf
},
You may also have to modity the `setuser` section in the `security` block, there is
a new field called `exemptAngel` which needs to be set in order for cjdns to have
permission to set the IPv6 and IPv4 addresses on the TUN device.
// Change the user id to this user after starting up and getting resources.
{
"setuser": "nobody"
// Exempt the Angel process from setting userId, the Angel is a small
// isolated piece of code which exists outside of the core's strict
// sandbox but does not handle network traffic.
// This must be enabled for IpTunnel to automatically set IP addresses
// for the TUN device.
"exemptAngel": 1
}
## Connecting to a gateway
To connect to an IPv6 gate, you must first ask the operator of the gate to add your
key to his gate, once he has added it, add their *key* to the `outgoingConnections`
section of the `IpTunnel` block in your cjdroute.conf like this:
"outgoingConnections":
[
// John's gate
"d5d0wu0usrkuThisIsJustAnExampleThisIsFake63uqlnk2kb0.k"
]
Then restart cjdns and after a few moments you should see it add IP addresses to your TUN device by running
`ifconfig` for example:
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:192.168.10.4 P-t-P:192.168.10.4 Mask:255.255.255.255
inet6 addr: fc88:dfd0:89d4:abfe:de2:a17a:6ed5:6fb1/8 Scope:Global
inet6 addr: 2a02:2498:e000:20::144:4/0 Scope:Global <--- new address
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1312 Metric:1
RX packets:22950 errors:0 dropped:0 overruns:0 frame:0
TX packets:22891 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:22689370 (22.6 MB) TX bytes:2460363 (2.4 MB)
Then you can try pinging a computer out on the internet like `ipv6.google.com` and see if
it works.
## Running a gateway
Running your own gateway is not automated, so you will want to implement some scripts to
set the addresses for you. Lets imagine your ISP has given you the IPv6 prefix
`1111:1111:1111:1111::/64` and your ISP's router is `1111:1111:1111:1111::1`. Your ethernet
card is probably set to `1111:1111:1111:1111::2` so you'll begin allocating above that.
First you will have to reserve one address (eg: `1111:1111:1111:1111::3`) for your `tun0`
device's address, then each client can have an address, so the first client will be issued
`1111:1111:1111:1111::4`, the second `1111:1111:1111:1111::5` and so on.
First edit your cjdroute.conf and add the clients who will be connecting to your gate.
It's always a good idea to add some identification with the connect block so you know who
it is for later.
"allowedConnections":
[
// Bill Smith's connection
{
"publicKey": "f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k",
"ip6Address": "1111:1111:1111:1111::4",
"ip6Prefix": 0
}
]
Note the `ip6Prefix` field: it specifies the netmask that the client should use.
We have set it to 0, so the client will think the entire IPv6 addfress space is
accessible over the tunnel (which it is, since we're building a
cjdns-to-clearnet gateway). This avoids us having to set up an IPv6 default
gateway manually on the client node. If you want to advertise a smaller network
to your clients (like just the `1111:1111:1111:1111::/64` network), set this to
the appropriate value (in this case, 64).
When you start cjdroute, the IP address for the TUN device will *not* be set automatically,
so you must set that next with the following command:
ip -6 addr add dev tun0 1111:1111:1111:1111::3
Now that your tun device has an address, your client should be able to connect to and
ping `1111:1111:1111:1111::3`, but it definitely won't be able to reach the rest of the
world until you add a static route on the gateway to your ISP's router's address: `1111:1111:1111:1111::1`.
This will make it route over the ethernet device and add a static route to allow the rest of
your /64 to route down the TUN device. Once you're finished, you'll want to set a default
route via your ISP's router's address so outgoing IPv6 packets are forwarded correctly.
ip -6 route add dev eth0 1111:1111:1111:1111::1
ip -6 route add dev tun0 1111:1111:1111:1111::0/64
ip -6 route add default via 1111:1111:1111:1111::1
Finally you will need to enable IPv6 forwarding, to do this, run:
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
and to make it permanent, edit your `/etc/sysctl.conf` file and *uncomment* the line which says:
#net.ipv6.conf.all.forwarding=1
Connect the client to the gateway using cjdns and wait a few moments until you've obtained the ipv6
address associated with the tunnel. Now, test the connection by attempting to ping the ipv6 address
associated with the gateway on the tunnel, and if this succeeds you can try to ping an external ipv6
address like `ipv6.google.com` too if you're expecting internet traffic to be routed through.
Now if all went well your job is done, but if the connection isn't working yet you should continue on to
the next section and try to figure out why.
## It doesn't work
First, check to make sure you don't have any iptables rules against forwarding or ones that might be
blocking your connection in any way. You should also make sure a processes called 'radvd' isn't running
(at least to rule out as a cause until you have things working) since it seems to be capable of causing
some routing issues.
Now ensure the client is still connected to the gateway through cjdns, and that it still has an ipv6
address associated with the tunnel, then try pinging the gateway or an internet ipv6 address again with
the client. On the gateway, run `tcpdump -n -i tun0` to see if any packets get as far as the tun device, and
if nothing scrolls with the ipv6 associated with your client's on the tunnel, you should check your routes
to make sure they're correct by running `ip -6 route`. The output should include four lines that look similar
to the ones below (take special note of which device is associated with each line).
1111:1111:1111:1111:1111:1111:1111:1 dev eth0 metric 1024
1111:1111:1111:1111:1111:1111:1111:3 dev tun0 proto kernel metric 256
1111:1111:1111:1111:1111:1111:1111:0/64 dev tun0 metric 1024
default via 1111:1111:1111:1111:1111:1111:1111:1 dev eth0 metric 1024
If your routes are correct and things still aren't working, continue to let the ping process run on the client
and run `tcpdump -n -i eth0 icmp6` on the gateway to check the traffic flowing through its ethernet device.
Look for any connections to or from the ipv6 address associated with your client on the tunnel, and if you see
any with strange messages about "neighbor solicitation" or "neighbor advertisement", the problem is that your
ISP's equipment is dropping replies instead of routing return traffic despite the addresses in use being
allocated to you. This problem exists because of something called NDP (Neighbor Discovery Protocol), in which a
request for 'neighbours' is made, and traffic isn't allowed to be sent back unless the ISP receives a response.
Thankfully, there is a workaround available that involves running a daemon called `npd6`, which provides a
response that satisfies NDP. Install npd6 through your distro's package management system if it's available,
(recent debian based distrobutions may be able to install the package located here:
http://code.google.com/p/npd6/downloads/list) otherwise you'll have to download and build it yourself by doing
the following:
wget http://npd6.googlecode.com/files/npd6-1.0.0.tar.gz
tar -xf ./npd-1.0.0.tar.gz
cd npd6-1.0.0/
make && sudo make install
Once this is built, copy `npd6-1.0.0/etc/npd6.conf.sample` to `/etc/ndp6.conf`, and change the prefix to
`1111:1111:1111:1111:0000:0000:0000:0002/64` (2 because you're announcing `1111:1111:1111:1111::2` and higher,
but not `1111:1111:1111:1111::1` which belongs to your ISP's router). Note that in this particular instance,
you can't ignore 0s when writing out your ipv6 or npd6 will complain when you attempt to run it. If npd6 fails
to start, try running `npd6 -l npd6.log` to write a log in the local directory.

1
tunnel/README.md Symbolic link
View File

@@ -0,0 +1 @@
../doc/tunnel.md