# ==========================================================
# BIRD 3.3.1 - PE architecture with MPLS (RFC 4364)
# Router: shaper/PE, 100.127.1.200, AS 197296
# RR: 100.127.1.2 (core L3 switch)
#
# NOTE: passwords redacted. Addresses and ASN kept real for
#       consistency with the report and the mail thread.
#
# Prerequisites:
#   - MPLS kernel modules: mpls_router, mpls_iptunnel
#   - sysctl: net.mpls.conf.lan.input=1, net.mpls.platform_labels=100000
#   - sysctl: net.mpls.conf.lo.input=1
#   - ip rule add iif lan lookup 20 priority 100
#   - ip rule add oif wan lookup 20 priority 102
#   - ip rule add iif wan lookup 20 priority 101
#   - ip rule add from 100.127.1.200 lookup main priority 10
#
# Architecture:
#   UNDERLAY: OSPF (IGP) + BGP-LU (transport labels)
#   OVERLAY:  BGP VPNv4 / VPNv6 (customer prefixes)
#
# VPN packet flow (PE -> internet):
#   PE receives [VPN] (label allocated by the shaper via l3vpn)
#   transport to 100.127.1.200: LDP in the core + BGP-LU on the RR->shaper segment
#   implicit-null (label 3) on the loopback -> RR strips the transport label (PHP)
#   shaper receives ONLY [VPN] on lan
#   kernel (MPLS route from l3vpn): POP VPN -> lookup in table_vpn_overlay -> via wan
#   -> internet
# ==========================================================

router id 100.127.1.200;

# --- Tables ---
ipv4 table table_vpn_overlay;
ipv4 table master4_lu;
vpn4 table vpntab4;
mpls table mplstab;
ipv6 table table_vpn6_overlay;
vpn6 table vpntab6;


# --- MPLS domain ---
mpls domain mdom {};

# --- Basic protocols ---
protocol device {}

protocol direct {
    ipv4 {
        import filter {
            if net = 100.127.1.200/32 then reject;
            accept;
        };
    };
}

# --- UNDERLAY: OSPF on LAN ---
protocol ospf v2 proto_ospf_lan {
    ipv4 {
        import all;
        export none;
    };
    area 0 {
        interface "lan" {
            cost 10000;
            hello 1;
            dead 4;
            type ptp;
            authentication cryptographic;
            password "<redacted>" {
                id 1;
                algorithm keyed md5;
            };
        };
        interface "lo" {
            stub;
        };
    };
}

# --- KERNEL 1: main table ---
protocol kernel proto_kernel_main {
    ipv4 {
        export all;
        import none;
    };
}

# --- KERNEL 2: overlay table (Linux table 20) ---
protocol kernel proto_kernel_overlay {
    ipv4 {
        table table_vpn_overlay;
        export all;
        import none;
    };
    kernel table 20;
}

# --- KERNEL 3: MPLS table ---
protocol kernel proto_kernel_mpls {
    mpls {
        export all;
    };
}

# --- KERNEL 4: IPv6 overlay table (Linux table 20) ---
# This is where the 6VPE kernel-export problem shows up: routes here
# get flagged '!' (Netlink: Invalid argument) because the resolved
# gateway is IPv4-family on an IPv6 route. The patch under discussion
# substitutes the neighbor's IPv6 link-local at export time.
protocol kernel proto_kernel_overlay6 {
    ipv6 {
        table table_vpn6_overlay;
        export all;
        import none;
    };
    kernel table 20;
}

# --- Route Target definitions ---
define RT_OVERLAY_IMPORT = (rt, 197296, 200);
define RT_OVERLAY_EXPORT = (rt, 197296, 100);

# --- Allowed customer ranges ---
define NETS_IPv4_OVERLAY_IMPORT = [
    100.64.0.0/10+,
    10.0.0.0/8+,
    46.23.58.184/29+,
    46.23.63.248/29+
];

define NETS_IPv6_OVERLAY_IMPORT = [
    2a02:d280::/29+,
    2a05:1220::/29+
];

# --- OVERLAY: static IPv4 default GW from WAN ---
protocol static static_wan_overlay {
    ipv4 { table table_vpn_overlay; };
    route 0.0.0.0/0 via 46.23.58.137;
}

# --- OVERLAY: static IPv6 default GW from WAN ---
protocol static static_wan6_overlay {
    ipv6 { table table_vpn6_overlay; };
    route ::/0 via 2A02:D280:10::46:23:58:137;   # IPv6 equivalent of static_wan_overlay
}

# --- BGP-LU loopback advertisement ---
# MPLS_POLICY_PREFIX with label 3 (implicit-null / PHP):
#   -> PE strips the transport label before sending
#   -> shaper receives the VPN label only
#   -> kernel processes the VPN label correctly
protocol static static_loopback {
    ipv4 {
        table master4;
        import filter {
            mpls_policy = MPLS_POLICY_PREFIX;
#            mpls_label = 3;
            accept;
        };
    };
    mpls {
        table mplstab;
        domain mdom;
    };
    route 100.127.1.200/32 via "lo";
#    route 100.127.1.200/32 via 46.23.58.137;
}

protocol pipe pipe_lu {
    table master4;
    peer table master4_lu;
    import none;
    export filter {
        if defined(mpls_label) then accept;
        reject;
    };
}

protocol bgp proto_bgp_rr {
    local 172.21.1.2 as 197296;
    neighbor 172.21.1.1 as 197296;
    password "<redacted>";

    # Plain IPv4 unicast — required for BGP session establishment
    ipv4 {
        import none;
        export none;
    };

    # --- UNDERLAY: BGP-LU transport labels ---
    ipv4 mpls {
        table master4;

        import filter {
            if net ~ [100.127.0.0/16+] then accept;
            reject;
        };

        export filter {
            if net = 100.127.1.200/32 then {
                mpls_label = 3;
                accept;
            }
            reject;
        };
    };

    # --- OVERLAY: VPNv4 customer prefixes ---
    vpn4 mpls {
        table vpntab4;
        igp table master4_lu;

        import filter {
            if RT_OVERLAY_IMPORT ~ bgp_ext_community then accept;
            reject;
        };

        export filter {
            if (rt, 197296, 100) ~ bgp_ext_community && net ~ [ 0.0.0.0/0 ] then {
                accept;
            }
            reject;
        };

        next hop self;
        next hop address 100.127.1.200;
    };


    # --- OVERLAY: VPNv6 customer prefixes ---
    vpn6 mpls {
        table vpntab6;
        igp table master4_lu;
        extended next hop on;

        import filter {
            if RT_OVERLAY_IMPORT ~ bgp_ext_community then accept;
            reject;
        };

        export filter {
            if (rt, 197296, 100) ~ bgp_ext_community && net ~ [ ::/0 ] then {
                accept;
            }
            reject;
        };

        next hop self;
        next hop address 100.127.1.200;   # IPv4 next-hop; ::ffff-mapped on the wire (RFC 4659)
    };

    mpls {
        domain mdom;
        label policy aggregate;
    };
}

# ==========================================================
# L3VPN BRIDGE: VPNv4/VPNv6 <-> IPv4/IPv6 (overlay tables)
# ==========================================================
protocol l3vpn l3_overlay {
    vpn4 {
        table vpntab4;
    };
    ipv4 {
        table table_vpn_overlay;
        import filter {
            if net ~ NETS_IPv4_OVERLAY_IMPORT then accept;
            reject;
        };
    };

    vpn6 {
        table vpntab6;
    };
    ipv6 {
        table table_vpn6_overlay;
        import filter {
            if net ~ NETS_IPv6_OVERLAY_IMPORT then accept;
            reject;
        };
    };

    mpls {
        table mplstab;
        domain mdom;
        label policy aggregate;
    };
    import target [(rt, 197296, 200)];
    export target [(rt, 197296, 100)];
    rd 100.127.1.200:100;
}
