SDK. Shared library.

Router SDK provides a shared library (with a C header file) that can be integrated with Applications using C calls.

SDK C API

Initialize library

int afwrt_init(const char *params_json, void (event_callback)(const char *event_str));

Initialize library. Must be called once before using the library.

Arguments

  • params_json - configuration in JSON format. See the "Configuration" section below. Can't be NULL;

  • event_callback - callback function to receive periodic SDK events. See the "Callback events" section below. Can be NULL.

Return value

  • 0 on success;

  • -1 on failure.

Free library resources

int afwrt_deinit(void); 

Deinitialize library. Must be called once after using the library.

Return value

  • 0 on success;

  • -1 on failure.

Start VPN

int afwrt_start(void);

Return value

  • 0 on success;

  • -1 on failure.

Stop VPN

Start library main loop. Blocking call.

int afwrt_stop(void);

Return value

  • Always returns 0.

Verify user credentials

int afwrt_verify_user(const char *backend, const char *username, const char *password)

Verify that the user's credentials are valid.

Arguments

  • backend - URL to backend to interact with;

  • username - user's account login name;

  • password - user's password.

Return value

  • -1 - user credentials are not valid;

  • 0 - user credentials are valid;

  • 1 - failed to verify user credentials.

Get available locations list

Stop library event loop.

char* afwrt_get_locations(void);

Returns the same JSON response as /user/locations.

Return value

  • On success JSON string with locations list as provided by backend side;

  • NULL on failure.

The caller is responsible for freeing the returned string.

Protect specific IP address and bind to specific location

int afwrt_protect_ip_addr(const char *ip_addr, const char *location);

Add a rule to route traffic from a specified IP address via a specified VPN location. Safe to call multiple times to change the location.

Arguments

  • ip_addr - IP address to protect;

  • location - VPN location to route traffic to.

Return value

  • 0 on success;

  • -1 on failure.

Does not work with wireguard (see Wireguard traffic routing)

Remove protecting rule for specific IP address

int afwrt_unprotect_ip_addr(const char *ip_addr);

Remove rule to route traffic from specified IP address via VPN.

Arguments

  • ip_addr - IP address to unprotect.

Return value

  • 0 on success;

  • -1 on failure.

Does not work with wireguard (see Wireguard traffic routing)

Protect specific MAC address and bind to specific location

int afwrt_protect_mac_addr(const char *mac_addr, const char *location);

Add a rule to route traffic from a specified MAC address via a specified VPN location. Safe to call multiple times to change the location.

Arguments

  • mac_addr - MAC address to protect;

  • location - VPN location to route traffic to.

Return value

  • 0 on success;

  • -1 on failure.

Does not work with wireguard (see Wireguard traffic routing)

Remove protecting rule for specific MAC address

int afwrt_unprotect_mac_addr(const char *mac_addr);

Remove rule to route traffic from specified MAC address via VPN.

Arguments

mac_addr - MAC address to unprotect.

Return value

  • 0 on success;

  • -1 on failure.

Does not work with wireguard (see Wireguard traffic routing)

Protect specific interface and bind it to specific location

int afwrt_protect_iface(const char *iface, const char *location);

Add a rule to route traffic from a specified interface via a specified VPN location. Safe to call multiple times to change the location.

Arguments

  • iface - interface to protect;

  • location - VPN location to route traffic to.

Return value

  • 0 on success;

  • -1 on failure.

Does not work with wireguard (see Wireguard traffic routing)

Remove protecting role for specific interface

int afwrt_unprotect_iface(const char *iface);

Remove rule to route traffic from specified interface via VPN.

Arguments

iface - interface to unprotect.

Return value

  • 0 on success;

  • -1 on failure.

Does not work with wireguard (see Wireguard traffic routing)

Register device

const char *afwrt_register_device(const char *auth_method, 
                                  const char *auth_token);

Register a new device and returns an access token, that will also be included in the configuration. Saving a received token value would be considered a good practice.

Arguments

  • auth_method - any supported OAuth provider (i.e. Firebase) or anonymous;

  • auth_token - OAuth token (if a relevant OAuth provider is set for the auth method).

Return value

  • Access token. Can be used for later afwrt_set_access_token() call.

Use specific access token

int afwrt_set_access_token(const char *access_token)

Add access token value to the current configuration

Arguments

  • access_token - platform API access token, received from afwrt_register_device method.

Auth token value may only consist of Latin letters, numbers, and dot, underscore, minus, tilde symbols.

"url encode" must be applied for any other characters (especially for /, ?, &, =, % symbols)

Return value

  • 0 on success;

  • -1 when invalid access token provided.

Bring up connection for specific location

int afwrt_setup_tunnel(const char *location, const char *iface_name);

Initiate tunnel connection to specific virtual location.

Arguments

  • location - virtual location name, as provided by afwrt_get_locations() call;

  • iface_name - system interface name.

Return value

  • 0 on success;

  • -1 on failure.

Failure error codes

When -1 is returned, errno is set to one of values:

  • EINVAL - NULL location, NULL interface for wireguard or afwrt_init() was not called;

  • ENOENT - unknown location ID;

  • EBUSY - provided iface_name already in use.

  • EEXIST - tunnel to specified location is already set.

Hydra-specific behavior

  • If the VPN protocol is hydra-tcp, iface_name is ignored.

  • If location connection was interrupted with calling appropriate event, this function should be used to initiate reconnection.

  • When this function ls called, no actual connection is done, it will be started with first network packet.

Wireguard-specific behavior

  • Initiates connection immediately.

  • It's expected to receive route_failed or route_connected event soon after the function is called.

Teardown connection to specific location

int afwrt_teardown_tunnel(const char *location);

Terminate tunnel connection to specific virtual location.

Arguments

  • location - virtual location name, same as given to afwrt_setup_tunnel() call.

Return value

  • 0 on success;

  • -1 on failure.

Failure error codes

When -1 is returned, errno is set to one of values:

  • EINVAL - NULL location or afwrt_init() was not called;

  • ENOENT - there is no known connection, established to provided location;

  • EBUSY - at least one active rule for this location exists (hydra only).

Get library version

const char *afwrt_version()

Get an SDK version number in MAJOR.MINOR.PATCH format (for example: 1.0.0)

Return value

  • Always returns statically allocated version string, do not free it.

Typical workflow

You should implement your own OAuth token (auth_token) obtaining mechanism and use it before afwrt_start() or afwrt_register_device() call.

Callback events

"ready"

Wireguard-specific event. Emitted when SDK is fully initialized and ready to receive control commands. This event added since wireguard can be started without default location and no "route_connected" event is emitted in this case.

Example: {"event":"ready"}

"route_connected"

This event is sent when a link to one of the virtual locations became active.

Example: { "event": "route_connected", "route_name": "us" }

"route_disconnected"

This event is sent when an active link to one of the virtual locations became inactive due to SDK stop, network condition, or for another external reason.

Example: { "event": "route_disconnected", "route_name": "us" }

"route_failed"

This event is sent when a link to one of the virtual locations can not be established.

Example: { "event": "route_failed", "route_name": "fr", "reason_details": "connection failed" }

"bandwidth_info"

An event is sent periodically to the client application.

Example: { "event": "bandwidth_info", "bandwidth_info": [ { "data_interval_ms": 100, "n_points": 100, "points_downstream": 0, "points_upstream": 0, "route_name": "us" }, { "data_interval_ms": 100, "n_points": 100, "points_downstream": 0, "points_upstream": 0, "route_name": "de" } ] }

"credentials_expire"

This event occurs when internal SDK credentials were expired (usually in 24 hours) and SDK failed to update them automatically. In this case application must stop or restart SDK.

Example: {"event": "credentials_expire"}

"auth_success"

This event happens when SDK successfully receives access_token while registering device using provided auth_method and auth_token. Event also returns access_token, it should be stored in persistent storage and used during next SDK start.

Example: {"event":"auth_success", "access_token":"keeMeroohohfiefai8AhXaesh9eetheeX8neilu5ichaichuzu"}

"invalid_auth"

This event happens when SDK does device registration using provided auth_method and auth_token and receives reject from backend side. Application then must stop or restart SDK with new auth_method and auth_token.

Configuration

Minimal working configuration

{
    "tun_ifname" : "wrt0",
    "wan_ifname" : "eth1"
}

All configuration parameters

"tun_ifname"

Name of virtual TUN interface created by SDK.

Example: "wrt0"

"hydra_tun_fd"

Hydra-specific option. Tells SDK not to create interface but reuse existing one instead. Option value is file descriptor of interface to use.

Example: 42

"wan_ifname"

Name of virtual TUN interface created by SDK.

Example: "eth1"

"tun_ifname" still should be specified for successful interface management.

"vl_default"

Default virtual location. By default, all traffic routed to the SDK TUN interface goes through this location. If not specified or empty, SDK will try to get an optimal location that can be configured on the backend side.

Default: ""(empty, see above)

Example: "us"

"backend"

Backend server address. Caller should not rely on default value and should specify it explicitly.

"type"

VPN protocol to use. Possible values:

  • "hydra-tcp"

  • "wireguard"

Default: "hydra-tcp"

"auth_method"

Authentication method, used by client. Value is updated with each afwrt_register_device() call.

"auth_token"

Authentication token. Value is updated with each afwrt_register_device() call.

"data_report_interval"

Event report interval in milliseconds. The time interval between bandwidth events (see "Callback events" section above).

Default: 30000 (30 seconds)

"default_gateway"

Redirect all traffic including router to SDK TUN interface. Do not create any specific iptables rule for protected IP, MAC, or interface(see "Routing" section below), but add a global traffic redirection rule.

Default: 0

"v4v6_policy"

Sets ipv4/ipv6 protocol usage policy for connecting to VPN nodes. Possible values:

  • "legacy" - used for backward compatibility. Shouldn't be set manually, it will be removed in future releases.

  • "ipv4_only" - use only ipv4 protocol.

  • "ipv6_only" - use only ipv6 protocol.

  • "both" - use both ipv4 and ipv6 protocols.

Default: "legacy"

"ipv6_output_only"

Use only servers which have assigned ipv6 exit pools. Set to 1 to enable.

Default: 0

"hydra_default_vl_reconnect_interval"

Hydra reconnect interval when default route fails to connect.

Default: 10

"config_dir"

Location of VPN core temporary files. SDK creates or updates a set of temporary files on each start. A directory should be already created for them.

Default: "/etc/afwrt"

"tun_dev_path"

Path to OS tun device.

Default: "/dev/net/tun"

"tun_addr"

IPv4 address of SDK TUN interface.

Default: "100.73.0.73"

"tun_mtu"

MTU value of SDK TUN interface. If set to 0 - interface MTU value is not updated.

Default: 0

"no_iptables"

Do not configure iptables rules.

Default: 0

"no_socket_setup"

Do not configure interface IP addresses.

Default: 0

"protected_ip_addrs"

Array of protected IP addresses with not empty "ip_addr" and "vl" fields.

Default: ""(empty, no IP addresses to protect).

Example: "protected_ip_addrs": [ { "ip_addr": "192.168.50.159", "vl": "us-new-york" }, { "ip_addr": "192.168.50.139", "vl": "de-berlin" } ]

"protected_mac_addrs"

An array of protected MAC addresses with no empty "mac_addr" and "vl" fields.

Default: ""(empty, no MAC addresses to protect).

Example: "protected_mac_addrs": [ { "mac_addr": "08:00:27:AA:D7:17", "vl": "us-new-york" }, { "mac_addr": "09:01:28:AB:D8:18", "vl": "de-berlin" } ]

"protected_ifaces"

An array of interfaces with not empty "iface"and "vl" fields.

Default: ""(empty, no interface to protect).

Example: "protected_ifaces": [ { "iface": "eth1", "vl": "us-new-york" }, { "iface": "br0", "vl": "de-berlin" } ]

OS level routing

To protect traffic, SDK creates a TUN device and routes all packets or packets with specific source IP, MAC, or interface to it. To achieve this, SDK creates a set of routing and/or firewall (iptables) rules.

Simple scenario. "default_gateway" option enabled.

Redirect just all traffic including router to TUN device.

To be smarter, SDK not changing the default route of the router "main" table, but adding two big subnets with high metrics. 0.0.0.0/1 and 128.0.0.0/1 for IPv4, for example.

# ip route show table main
0.0.0.0/1 dev wrt0 scope link  metric 1000 
128.0.0.0/1 dev wrt0 scope link  metric 1000 

Advanced scenario. "default_gateway" option disabled.

Selective routing by IP, MAC, or interface name.

First, SDK creates routing table 47 with TUN device default gateway.

# ip route show table 47
default dev wrt0 

Second, SDK creates a rule to forward all packets with 0x8 mark to table 47.

# ip rule
10:	from all fwmark 0x8 lookup 47

Third, SDK creates a custom iptables chain that will contain all specific IP, MAC, and interface rules.

# iptables -L -t mangle
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
afwrt_chain  all  --  anywhere             anywhere 

Such rules can be loaded from config on the initialization phase or changed dynamically using the protection methods above. Each rule marks specified IP, MAC, or interface with 0x8 mark.

Example of the interface, MAC, and IP rules:

# iptables -L -t mangle -v
Chain afwrt_chain (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MARK       all  --  eth2   any     anywhere             anywhere             MARK set 0x8
    0     0 MARK       all  --  any    any     anywhere             anywhere             MAC 08:00:27:AA:D7:17 MARK set 0x8
    1    76 MARK       all  --  any    any     192.168.50.139       anywhere             MARK set 0x8

It doesn't matter in which order rules will appear. All routing priority logic is done by SDK internally (see below).

VPN Core level routing

Regardless of the OS-level routing mechanism, the core is always ready to do selective routing based on IP, MAC, and interface rules if any. If no rules are specified, traffic will go to "vl_default" route as described above.

Routing rules priority

MAC address rule has higher priority than IP address rule. IP address rule has higher priority than Interface rule.

For example, there are the following rules:

  • route traffic from a device with a MAC address "08:00:27:AA:D7:17" via "us"

  • route traffic from a device with an IP address "192.168.50.139" via "de"

If both addresses (MAC and IP) belongs to the same device, traffic will be routed via "us" VPN location.

MAC and Interface to IP resolution

To be able to distinguish device traffic on the L3 network layer SDK needs its IP address.

SDK maintains a mapping from MAC or Interface to IP addresses internally.

It subscribes to changes in neighbors table using a netlink, asking for ones with the following states: NUD_REACHABLE, NUD_DELAY, NUD_PROBE, NUD_PERMANENT, NUD_STALE.

Wireguard traffic routing

SDK library does not handle any traffic routing for wireguard protocol. It's user's responsibility to setup all required routing rules, routes and netfilter rules.

Typical configuration of networking stack is described below.

Configure netfliter.

Examples below assume that "tun_ifname" value is set to "wrt0".

For ipv4 protocol

iptables -t nat -A POSTROUTING -o wrt0 -j MASQUERADE

For ipv6 protocol

ip6tables -t nat -A POSTROUTING -o wrt0 -j MASQUERADE

Create routing tables with default routes

For ipv4 protocol

ip ro add table 142 default dev wrt0

For ipv6 protocol

ip -6 ro add table 142 default dev wrt0

Add source-based rules to forward specific devices

For ipv4 protocol

ip ru add from 192.168.56.101/32 lookup 142

For ipv6 protocol

ip -6 ru add from fc00::101/128 lookup 142

OpenWRT

Firewall

Depends on your firmware version, you may need to insert additional iptables rules manually or via OpenWRT CI. Such rules may look like this:

# iptables -I FORWARD -i bridge_iface -o tun_iface -j ACCEPT
# iptables -I FORWARD -i tun_iface -o bridge_iface -j ACCEPT

Dependencies

libc kmod-tun ca-bundle libnetfilter-conntrack

Last updated