# Lixonet BIRD configuration
# This is a templated file that automatically generates values at configuration time
# If you edit this file, it will be overwritten. Changes to the general structure of
# this configuration file that should be persisted should be committed to Git.
# For BIRD 2.0 configuration reference, see:
#   https://bird.network.cz/?get_doc&f=bird.html&v=20
# For a great example configuration file, see:
#   https://fossies.org/linux/bird/doc/bird.conf.example

# Global variables
# See: https://bird.network.cz/?get_doc&v=20&f=bird-3.html#ss3.2

log stderr all;         # Using docker; defer logging to stderr (to Docker logs)
debug protocols all;    # Enable debugging (this should be switched off in prod)
router id ${tinc_peer_address};

# Custom routing tables
# See: https://bird.network.cz/?get_doc&v=20&f=bird-2.html (recommended read)
ipv4 table ${network_name:-lixonet}_v4;
roa4 table r4; # ROA RPKI

# RPKI
# See: https://brooks.sh/2019/11/11/validating-bgp-routes-with-rpki-in-bird/
#protocol rpki {
#    roa4 { table r4; };

#    {{ range files "bird/rpki/hosts" }} # {{.}}
#    {{ file (print "bird/rpki/hosts/" .) }}
#    {{ end }}

#    # Time period in seconds between a failed query the next attempt
#   retry keep ${bgp_rpki_retry:-90};

#    # Tells how long to wait before attempting to poll the cache
#    refresh keep ${bgp_rpki_refresh:-900};

#    # How long to keep any records locally cached before they are deleted
#    # The "refresh" interval will control how often records are refreshed
#    # and not considered expired.
#    expire keep ${bgp_rpki_expire:-172800};

#    # Enable SSH transport, disable TCP transport (insecure)
#    transport ssh {
#        remote public key "${bgp_rpki_known_hosts:-/etc/bird/rpki/known_hosts}";
#        user "lixonet";
#        #bird private key "</path/to/id_rsa>";
#    };
#}

# Filters
# Define a series of filters for Lixonet routing policies:
#  - Cannot advertise a route which is in the router subnet: typically 172.x.0.0/24
# See: https://gitlab.labs.nic.cz/labs/bird/wikis/BGP_filtering
# For syntax docs, see: https://bird.network.cz/?get_doc&v=16&f=bird-5.html

# Returns TRUE if the given prefix is found in a kept RPKI cache.
# Returns FALSE if the given prefix is either,
#  - Valid in the cached RPKI list, or
#  - RPKI has not yet been established and the route cannot be validated
# From BIRD documentation (https://bird.network.cz/?get_doc&v=20&f=bird-5.html):
#   roa_check: Checks the current route (which should be from BGP to
#   have AS_PATH argument) in the specified ROA table and returns
#   ROA_UNKNOWN if there is no relevant ROA, ROA_VALID if there is
#   a matching ROA, or ROA_INVALID if there are some relevant ROAs
#   but none of them match.
function is_rpki_invalid_v4 () {
  return roa_check(r4, net, bgp_path.last_nonaggregated) = ROA_INVALID;
}

# Returns TRUE if the given tested network is within the global network prefix for
# Lixonet.  Used to filter networks outside of this range as they are not within
# the global mesh network.
function is_lixonet_global_v4()
{
    return net ~ [ ${network_address}/${global_prefix:-16}+ ];
}

# Returns TRUE if the given tested network is within the router network prefix for
# Lixonet.  Used to filter these routes from BGP as Tinc statically assigns them
# for us.  Helps prevent a security vulnerability of hijacking another router.
function is_lixonet_router_v4()
{
    return net ~ [ ${network_address}/${router_prefix:-24}+ ];
}

function is_own_route_v4()
{
    {{ if len "${bgp_routes:-}" }}{{ range "$bgp_routes" | split "," }}if net ~ [ {{.}}+ ] then return true;
    {{ end }}{{ end }}
    return false;
}

filter bgp_import_filter_v4
{
    if source ~ [RTS_STATIC] then reject; # Reject our own routes
    if is_rpki_invalid_v4() then reject; # Reject posions
    if is_lixonet_router_v4() then reject; # Reject poisons
    if is_own_route_v4() then reject; # Reject poisons
    if is_lixonet_global_v4() then accept; # Accept anything else
    reject; # Reject anything else (non-Lixonet)
}

filter bgp_export_filter_v4
{
    if is_lixonet_router_v4() then reject; # Reject poisons
    if is_lixonet_global_v4() then accept; # Accept anything else
    reject; # Reject anything else (non-Lixonet)
}

filter kernel_export_filter_v4
{
    if is_own_route_v4() then reject; # Reject poisons
    if is_lixonet_global_v4() then accept; # Accept anything else
    reject; # Reject anything else (non-Lixonet)
}

# Static routes
# Define propagated routes here from the lixonet.conf "routes" variable
# Attached to the above "lixonet" routing table; "provide" these routes into it
# See how dn42 does it; we're very similar: https://dn42.net/howto/Bird
protocol static {
    ipv4 {
        table ${network_name:-lixonet}_v4;
        import all;
        export none;
    };

    # Announced networks
    {{ if len "${bgp_routes:-}" }}{{ range "$bgp_routes" | split "," }}route {{.}} reject;
    {{ end }}{{ end }}
};

# Device
# See: https://bird.network.cz/?get_doc&v=20&f=bird-6.html#ss6.4
# This controls which interfaces BGP, etc. will bind to for communication
# This prevents BGP from listening on eth0/off-network
protocol device {
    scan time 10;       # Scan the interfaces often
    interface "${network_name:-lixonet}" {
        preferred ${tinc_peer_address};
    };
};

# Direct (unnecessary for Lixonet)
# See: https://bird.network.cz/?get_doc&v=20&f=bird-6.html#ss6.5
# Disable automatically generating direct routes to all network interfaces.
protocol direct {
    disabled;               # Disable by default
};

# Kernel routing table
# See: https://bird.network.cz/?get_doc&v=20&f=bird.html#toc6.6
protocol kernel {               # Primary routing table
    learn;                  # Learn alien routes from the kernel
    persist;                # Don't remove routes on bird shutdown
    scan time 10;           # Scan kernel routing table every 10 seconds
    ipv4 {
        table ${network_name:-lixonet}_v4;
        import none;                        # Don't try to import any routes from the kernel
        export filter kernel_export_filter_v4; # Export everything we are told to the kernel
    };
};

# BGP (primary Lixonet routing protocol)
# This is a template to use when connecting to other BGP clients on the EE network
# This template is applied to ALL neighbors, so consider these global settings that
# apply to all neighbors.  See "Neighbors" section of this configuration for
# individual neighbor configurations where per-neighbor configurations (such as their
# ASN) are applied.
template bgp lixonet_client {
    local as ${bgp_asn};                    # Local AS advertised to peers

    source address ${tinc_peer_address};    # What local IP address we use for any outbound TCP
                                            #  connections on port 179

    path metric ${bgp_path_metric:-1}; # 1 = Prefer routes with shorter paths (like Cisco does)

    # BGP channels
    # See: Channel configuration (BIRD BGP configuration) and the table shown there.

    # BGP IPv4 channel settings
    ipv4 {
        table ${network_name:-lixonet}_v4;

        # Always advertise our own local address as a next hop, even in cases where the
        # current Next Hop attribute should be used unchanged.
        # Reason: tinc NEEDS this, otherwise Layer3 inter-routing on the mesh will be broken
        next hop self ebgp;

        # aigp (see: http://www.rfc-editor.org/info/rfc7311)
        # Lixonet default: originate AIGP
        # This not only allows AIGP attribute propagation, but also new AIGP attributes are
        # automatically attached to non-BGP routes with valid IGP metric (e.g. ospf_metric1)
        # as they are exported to the BGP session.
        # Thank-you, BIRD <3 - mane and nurd
        aigp ${bgp_aigp:-originate};

        # Set filters for both exported (sent) and imported (received) BGP prefixes.
        # This is explicitly required per RFC 8212, at least on export.
        # See: https://gitlab.labs.nic.cz/labs/bird/commit/3831b619661d08d935fd78656732cd2f339ff811
        export filter bgp_export_filter_v4;
        import filter bgp_import_filter_v4;
    };
};

# Neighbors
{{ range files "bird/peers" }} {{ if ne . "${tinc_peer_name}" }}
protocol bgp {{ . }} from lixonet_client {
    description "Lixonet BGP link to {{ . }}";
    {{ include (print "bird/peers/" .) }}
};{{ end }}{{ end }}