Interfaces
===========================

All network programming in aionetiface starts with the network interface card. 
Your computer will have a interface that traffic is sent down based on various
routing methods. Let's start by loading the OSes preferred interface. Starting the interface looks its addresses and relevant meta data.

.. literalinclude:: ../../examples/example_2.py
    :language: python3

.. code-block:: text

    Interface.from_dict({
        "name": "Intel(R) Wi-Fi 6 AX200 160MHz",
        "nat": {
            "type": 5,
            "nat_info": "restrict port",
            "delta": {
                "type": 6,
                "value": 0
            },
            "delta_info": "random delta (local port == rand port)"
        },
        "rp": {
            "2": [
                {
                    "af": 2,
                    "nic_ips": [
                        {
                            "ip": "192.168.21.21",
                            "cidr": 32,
                            "af": 2
                        }
                    ],
                    "ext_ips": [
                        {
                            "ip": "1.3.3.7",
                            "cidr": 32,
                            "af": 2
                        }
                    ]
                }
            ],
            "23": []
        }
    })

Repr shows a serializable dict representation of the interface after it's
been loaded. You can see a list of interfaces available on your machine
by using the **list_interfaces** function. Interfaces may be virtual,
contain loopback devices, and other adapters that aren't directly
useful for networking. Often we are only interested in the adapters
that are usable for WAN or LAN networking.

.. literalinclude:: ../../examples/example_3.py
    :language: python3

Now you know how to lookup interfaces and start them. It's time to
learn about 'routes.'

----

The addressing problem
-----------------------

Modern event loops make it easy to write high-performance networking code.
The engineers of today are spoiled by such elegant features compared to the
tools available in the early days. But there is still something very basic
missing from the networking toolbox:

**The ability to see your external addressing information**

There are many cases where this information is needed. For example: imagine
a server that listens on multiple IPs such that it is available on more
than one external IP. The server may wish to know what external IPs are
available to it in case it needs to refer a client to another server. The
STUN protocol is the perfect instance where a client can request
a connection back 'from a different IP address' in order to determine
what type of NAT they have.

.. HINT::
    aionetiface makes external addressing details available to the programmer.
    Such information avoids having to manually pass details to bind() 
    to use a given external IP.

Routes to the rescue
---------------------

aionetiface solves the addressing problem by introducing mappings called 'Routes'.
A Route describes how interface-assigned addresses relate to external addresses.
Each route is indexed by address family. Either IPv4 or IPv6. 

----

**Example 1 -- IPv4 routes**

.. code-block:: text

    NIC IPs:
        192.168.0.20/32 (1 IP)
        193.168.0.0/16 (65024 IPs)
        7.7.7.7/32 (1 IP)
        8.8.0.0/16 (65024 IPs)
    
    EXT IPs:
        1.3.3.7/32 (1 IP)
        8.8.0.0/16 (65024 IPs)
    
    ---------------------------------------------------------------
    Routes:
        [...20, 193..., 7.7.7.7] -> [1.3.3.7]
        [8.8.0.0] -> [8.8.0.0]

The software starts by grouping private IPs. It binds to the first
and checks the external IP. The result is a new route with the external IP.
If it finds a public IP for a NIC address it binds to the first IP
in it's range (range if it's a block) and checks the external IP.
If the IPs match it assumes the range is valid. If it matches the
previous route it groups them as the same route. 

Example: the software finds a block of NIC IPs '8.8.0.0/16' with 'public IPs.'
It binds to the first address and sees the external address matches. Seeing that
it assumes this means the whole block is valid without checking every IP.
This becomes a new route. This shows how some machines set their NIC IPs to their
external addresses.

----

**Example 2 -- IPv6 routes**

.. code-block:: text
    
    NIC IPS:
        2020:DEED:BEEF::0000/128 (global scope) (1 IP)
        2020:DEED:DEED::0000/64 (global scope) (a lot of IPs)
        FE80:DEED:BEEF::0000/128 (link-local) (1 IP)
    
    EXT IPS:
        2020:DEED:BEEF::0000/128 (global scope) (1 IP)
        2020:DEED:DEED::0000/64 (global scope) (a lot of IPs)
    
    ---------------------------------------------------------------
    Routes:
        [FE80:DEED:BEEF::0000/128] -> [2020:DEED:BEEF::0000/128]
        [FE80:DEED:BEEF::0000/128] -> [2020:DEED:DEED::0000/64]
    
The algorithm for IPv6 routes is slightly different.
All link-local addresses are copied to each route.
While every global address 'EXT' forms a new route.

aionetiface uses the EXT portion for IPv6 servers. While it uses the NIC portion
for IPv4. It is assumed that all servers should be publicly reachable.
Though this can be bypassed by specifying IPs directly for bind calls
which is indeed what the aionetiface REST server does. 

If that sounds difficult :doc:`daemons` make this easier.

.. HINT::
    There's other 'types' of addresses in IPv6 though they're not
    supported in aionetiface for now. Just the equivalent of 'private' and
    'public' addresses.

----

Using a route with a pipe
----------------------------

Connect to Google.com and get a response from it.

.. literalinclude:: ../../examples/example_4.py
    :language: python3

Look closely at the route part. What this code is doing is it's asking for
the first route in the route pool for that address family. The route points
to one or more external addresses (more if it's a block) and 'knowns'
how to setup the tuples for bind to use that external address. Once a route
is bound it can be used in the familiar open_pipe call.

.. HINT::
    You can see from this example that aionetiface supports duel-stack networking,
    multiple network interface cards, external addressing, DNS / IP / target parsing,
    and publish-subscribe. But there are many more useful features for
    network programming.

Route pools
--------------

Every interface with Internet access has at least one route in its
route pool. If you don't care about what route you're using you can call the
route function from an interface object. But through
the RoutePool class every single possible external address can be used.

.. code-block:: python

    nic = await Interface()
    x = nic.rp[IP4] # IP4 RoutePool
    y = nic.rp[IP6] # IP6 RoutePool
    print(len(x))
    for r in x:
        # A route to use a single external IP from the pool.
        print(r)

The route pool is designed to make it easy to group multiple external addresses
that may be themselves either single addresses or blocks of IPs into one
object that can be indexed, counted, iterated, and used. In this way its easy
to 'grab an IP' and use it. 

Default interface
----------------------

When you await loading an interface the software uses STUN to lookup external
addressing information . This is useful for more advanced network programming 
(like connecting to link-local addresses where scope-IDs are needed),
but sometimes you want a more lightweight approach. Maybe you don't care about
underlying addresses or topology, or you simply don't want to incur the performance penalty of loading addresses to use any software written with it.

In this case: if you specify "default" as the interface name the software does
the bare minimum to make the interface usable.

.. code-block:: python

    # No await needed -- does no IO.
    # Adding await won't break it though.
    nic = Interface("default")