{% from 'macros.j2' import write_prefix_list %}
{% from 'macros.j2' import write_prefix_list_entry %}
{% from 'macros.j2' import add_communities %}
{% from 'macros.j2' import match_communities %}
{% from 'macros.j2' import bird_fnc_type %}

{%- macro write_max_pref_action(action) -%}
{% if action == "shutdown" %}disable;
{% elif action == "restart" %}restart;
{% elif action == "block" %}block;
{% else %}warn;
{% endif %}
{%- endmacro -%}

{%- macro write_custom_config_lines(client, af, section) %}
{% 	 if client.cfg.custom_options and
		"bird" in client.cfg.custom_options and
		af in client.cfg.custom_options["bird"] and
		section in client.cfg.custom_options["bird"][af] and
		client.cfg.custom_options["bird"][af][section].get("config_lines") %}
# Custom options for AF {{ af }} follow
{%     for custom_config_line in client.cfg.custom_options["bird"][af][section]["config_lines"] %}
{{ custom_config_line }}
{%     endfor %}
{%   endif %}
{%- endmacro -%}

{%- macro reject(client, cause, err_msg, avoid_braces=False) %}
{% if client.cfg.filtering.reject_policy.policy == "reject" %}
reject {{ err_msg }};
{% elif client.cfg.filtering.reject_policy.policy == "tag_and_reject" %}
{%	if not avoid_braces %}{{ "{ " }}{% endif %}tag_and_reject({{ cause}}, {{ client.asn }}); reject {{ err_msg }};{% if not avoid_braces %}{{ " }" }}{% endif %}
{% else %}
{%	if not avoid_braces %}{{ "{ " }}{% endif %}tag_and_reject({{ cause}}, {{ client.asn }}); print {{ err_msg }}; accept;{% if not avoid_braces %}{{ " }" }}{% endif %}
{% endif %}
{%- endmacro -%}
# ---------------------------------------------------------
# MEMBERS

{% for client in clients|sort(attribute="id") if client.ip is current_ipver %}

# AS-SET for {{ client.id }}
function origin_as_is_in_{{ client.id }}_as_set(){{ bird_fnc_type("bool") }} {
{% if client.cfg.filtering.irrdb.as_set_bundle_ids %}
{%	for as_set_bundle_id in client.cfg.filtering.irrdb.as_set_bundle_ids|sort %}
{%		if irrdb_info[as_set_bundle_id].asns %}
	if bgp_path.last ~ AS_SET_{{ irrdb_info[as_set_bundle_id].name }}_asns then
		return true;
{%		else %}
	# AS-SET {{ irrdb_info[as_set_bundle_id].name }} referenced but empty.
{%		endif %}
{%	endfor %}
{% endif %}
	return false;
}

# R-SET for {{ client.id }}
function prefix_is_in_{{ client.id }}_as_set(){{ bird_fnc_type("bool") }} {
{% if client.cfg.filtering.irrdb.as_set_bundle_ids %}
{%     for as_set_bundle_id in client.cfg.filtering.irrdb.as_set_bundle_ids|sort %}
{%         if "2.0"|target_version_ge and client.cfg.rfc8950 and client.ip|ipaddr_ver == 6 %}
{%             set afis = [4, 6] %}
{%         else %}
{%             set afis = [ client.ip|ipaddr_ver ] %}
{%         endif %}
{%         for this_ip_ver in afis %}
{%             set prefixes = irrdb_info[as_set_bundle_id].prefixes|selectattr("prefix", "is_ipver", this_ip_ver)|list %}
{%             if prefixes %}
{%                 if "2.0"|target_version_ge %}
    if net.type = NET_IP{{ this_ip_ver }} then
        if net ~ AS_SET_{{ irrdb_info[as_set_bundle_id].name }}_prefixes_{{ this_ip_ver }} then
            return true;
{%                 else %}
    if net ~ AS_SET_{{ irrdb_info[as_set_bundle_id].name }}_prefixes_{{ this_ip_ver }} then
        return true;
{%                 endif %}
{%             else %}
	# AS-SET {{ irrdb_info[as_set_bundle_id].name }} referenced but empty.
{%             endif %}
{%	   endfor %}
{%     endfor %}
{% endif %}
    return false;
}

function next_hop_is_valid_for_{{ client.id }}(){{ bird_fnc_type("bool") }}
{
	# Checks if NEXT_HOP is one of those allowed for routes announced by {{ client.id }}
{%	if client.ip|ipaddr_ver == 6 and (client.ip|lower).startswith("fe80::") %}
	# The client address is an IPv6 link-local one; we can't check it since
	# BIRD doesn't handle it well and returns "::" as the next-hop of the route.
	return true;
{%	else %}
{%		if client.cfg.filtering.next_hop.policy == "strict" %}
	if bgp_next_hop = {{ client.ip }} then return true;
{%		elif client.cfg.filtering.next_hop.policy == "authorized_addresses" %}
{%			for addr in client.cfg.filtering.next_hop.authorized_addresses_list|sort if addr|ipaddr_ver == client.ip|ipaddr_ver %}
	if bgp_next_hop = {{ addr }} then return true;
{%			endfor %}
{%		else %}
{%			for asn, same_as_clients in clients|groupby("asn") if asn == client.asn %}
{%				for same_as_client in same_as_clients|sort(attribute="ip") %}
{%					if (
						"2.0"|target_version_ge and
						client.cfg.rfc8950
					) or (
						same_as_client.ip|ipaddr_ver == client.ip|ipaddr_ver
					) %}
	if bgp_next_hop = {{ same_as_client.ip }} then return true;	# {{ same_as_client.id }}
{%					endif %}
{%				endfor %}
{%			endfor %}
{%		endif %}
	return false;
{%	endif %}
}

{% if client.cfg.filtering.black_list_pref %}
function prefix_is_in_{{ client.id }}_blacklist(){{ bird_fnc_type("bool") }}
{% for this_ip_ver in list_ip_vers %}
{%   set prefixes = client.cfg.filtering.black_list_pref|selectattr("prefix", "is_ipver", this_ip_ver )|list %}
{%   if prefixes|length > 0 %}
prefix set {{ client.id }}_blacklist_{{ this_ip_ver }};
{%   endif %}
{% endfor %}
{
{% for this_ip_ver in list_ip_vers %}
{%   set prefixes = client.cfg.filtering.black_list_pref|selectattr("prefix", "is_ipver", this_ip_ver )|list %}
{%   if prefixes|length > 0 %}
    {{ client.id }}_blacklist_{{ this_ip_ver }} = [
{{ write_prefix_list(prefixes) }}
    ];
{%     if "2.0"|target_version_ge %}
    if net.type = NET_IP{{ this_ip_ver }} then
        if net ~ {{ client.id }}_blacklist_{{ this_ip_ver }} then return true;
{%     else %}
    if net ~ {{ client.id }}_blacklist_{{ this_ip_ver }} then return true;
{%     endif %}
{%   endif %}
{% endfor %}
	return false;
}
{% endif %}

{% if client.cfg.filtering.irrdb.enforce_origin_in_as_set or client.cfg.filtering.irrdb.enforce_prefix_in_as_set or cfg.filtering.irrdb.tag_as_set %}
function verify_{{ client.id }}_irrdb()
bool origin_ok;
bool prefix_ok;
bool validated;
{
	origin_ok = false;
	prefix_ok = false;
	validated = false;

{% 	if client.cfg.filtering.irrdb.enforce_origin_in_as_set or cfg.filtering.irrdb.tag_as_set %}
	origin_ok = origin_as_is_in_{{ client.id }}_as_set();
{%	endif %}
{%	if client.cfg.filtering.irrdb.enforce_prefix_in_as_set or cfg.filtering.irrdb.tag_as_set %}
	prefix_ok = prefix_is_in_{{ client.id }}_as_set();
{%	endif %}

{%	if cfg.filtering.irrdb.tag_as_set and cfg.communities.origin_not_present_in_as_set|community_is_set %}
	if !origin_ok then {
		{{ add_communities(cfg.communities.origin_not_present_in_as_set, tabs=2) }}
	}
{%	endif %}
{%	if cfg.filtering.irrdb.tag_as_set and cfg.communities.origin_present_in_as_set|community_is_set %}
	if origin_ok then {
		{{ add_communities(cfg.communities.origin_present_in_as_set, tabs=2) }}
	}
{%	endif %}
{%	if cfg.filtering.irrdb.tag_as_set and cfg.communities.prefix_not_present_in_as_set|community_is_set %}
	if !prefix_ok then {
		{{ add_communities(cfg.communities.prefix_not_present_in_as_set, tabs=2) }}
	}
{%	endif %}
{%	if cfg.filtering.irrdb.tag_as_set and cfg.communities.prefix_present_in_as_set|community_is_set %}
	if prefix_ok then {
		{{ add_communities(cfg.communities.prefix_present_in_as_set, tabs=2) }}
	}
{%	endif %}

{%	if cfg.filtering.irrdb.use_rpki_roas_as_route_objects.enabled %}
	# Origin ASN is valid, prefix is not: check if a RPKI ROAs exists.
	if origin_ok && prefix_in_rpki_roas_as_route_objects() then {
{%		if cfg.filtering.irrdb.tag_as_set and
			cfg.communities.prefix_validated_via_rpki_roas|community_is_set %}
		{{ add_communities(cfg.communities.prefix_validated_via_rpki_roas, tabs=2) }}
{%		endif %}
		validated = true;
	}
{%	endif %}

{%	if cfg.filtering.irrdb.use_arin_bulk_whois_data.enabled and arin_whois_records %}
	# Origin ASN is valid, prefix is not: check if an ARIN Whois record exists.
	if origin_ok && prefix_in_arin_whois_db() then {
{%		if cfg.filtering.irrdb.tag_as_set and
			cfg.communities.prefix_validated_via_arin_whois_db_dump|community_is_set %}
		{{ add_communities(cfg.communities.prefix_validated_via_arin_whois_db_dump, tabs=2) }}
{%		endif %}
		validated = true;
	}
{%	endif %}

{%	if cfg.filtering.irrdb.use_registrobr_bulk_whois_data.enabled and registrobr_whois_records %}
	# Origin ASN is valid, prefix is not: check if a NIC.BR Whois record exists.
	if origin_ok && prefix_in_registrobr_whois_db() then {
{%		if cfg.filtering.irrdb.tag_as_set and
			cfg.communities.prefix_validated_via_registrobr_whois_db_dump|community_is_set %}
		{{ add_communities(cfg.communities.prefix_validated_via_registrobr_whois_db_dump, tabs=2) }}
{%		endif %}
		validated = true;
	}
{%	endif %}

{%	if client.cfg.filtering.irrdb.white_list_route %}
	# Client's white list
{%          if "2.0"|target_version_ge and client.cfg.rfc8950 and client.ip|ipaddr_ver == 6 %}
{%              set routes = client.cfg.filtering.irrdb.white_list_route|sort(attribute="prefix") %}
{%          else %}
{%              set routes = client.cfg.filtering.irrdb.white_list_route|selectattr("prefix", "is_ipver", client.ip|ipaddr_ver)|sort(attribute="prefix") %}
{%          endif %}
{%		for route in routes %}
	if !validated && net ~ [ {{ write_prefix_list_entry(route) }} ] then {
{%			if route.asn %}
		if bgp_path.last = {{ route.asn }} then {
{%				if cfg.filtering.irrdb.tag_as_set and cfg.communities.route_validated_via_white_list|community_is_set %}
			{{ add_communities(cfg.communities.route_validated_via_white_list, tabs=3) }}
{%				endif %}
			validated = true;
		}
{%			else %}
{%				if cfg.filtering.irrdb.tag_as_set and cfg.communities.route_validated_via_white_list|community_is_set %}
		{{ add_communities(cfg.communities.route_validated_via_white_list, tabs=2) }}
{%				endif %}
		validated = true;
{%			endif %}
	}
{%		endfor %}
{%	endif %}

{%	if client.cfg.filtering.irrdb.enforce_origin_in_as_set %}
	if !validated && !origin_ok then {
		{{ reject(client, 9, '"origin ASN [", bgp_path.last, "] not in allowed as-sets - REJECTING ", net', avoid_braces=True) }}
	}
{%	endif %}
{%	if client.cfg.filtering.irrdb.enforce_prefix_in_as_set %}
	if !validated && !prefix_ok then {
		{{ reject(client, 12, '"prefix not in client\'s r_set - REJECTING ", net', avoid_braces=True) }}
	}
{%	endif %}
}
{% endif %}

filter receive_from_{{ client.id }} {
	if !(source = RTS_BGP ) then
		{{ reject(client, 65535, '"source != RTS_BGP - REJECTING ", net') }}

	{% if "2.0"|target_version_ge %}
	{%     if client.cfg.rfc8950 and client.ip|ipaddr_ver == 6%}
	if !(net.type = NET_IP6 || net.type = NET_IP4) then
	{%     else %}
	if !(net.type = NET_IP{{ client.ip|ipaddr_ver }}) then
	{%     endif %}
		{{ reject(client, 65535, '"AFI not enabled for this peer - REJECTING ", net') }}
	{% endif %}

	{% if "pre_receive_from_client"|hook_is_set %}
	if !hook_pre_receive_from_client({{ client.asn }}, {{ client.ip }}, "{{ client.id }}") then
		{{ reject(client, 65535, '"hook_pre_receive_from_client returned false - REJECTING ", net') }}
	{% endif %}

	scrub_communities_in();

{%	if client.cfg.attach_custom_communities %}
	# Attach custom BGP communities
{%		for name in client.cfg.attach_custom_communities|sort %}
	# {{ name }}
	{{ add_communities(cfg.custom_communities[name], tabs=1) }}
{%		endfor %}
{%	endif %}

	# NEXT_HOP
	if !next_hop_is_valid_for_{{ client.id }}() then
		{{ reject(client, 5, '"NEXT_HOP [", bgp_next_hop, "] not allowed - REJECTING ", net') }}

	# AS_PATH: length
	if bgp_path.len > {{ cfg.filtering.max_as_path_len }} then
		{{ reject(client, 1, '"AS_PATH len [", bgp_path.len ,"] longer than ' ~ cfg.filtering.max_as_path_len ~ ' - REJECTING ", net') }}

	# AS_PATH: left-most ASN
	if bgp_path.first != {{ client.asn }} then
		{{ reject(client, 6, '"invalid left-most ASN [", bgp_path.first, "] - REJECTING ", net') }}

	{% if client.cfg.filtering.reject_invalid_as_in_as_path %}
	# AS_PATH: invalid ASNs
	if as_path_contains_invalid_asn() then
		{{ reject(client, 7, '"AS_PATH [", bgp_path ,"] contains invalid ASN - REJECTING ", net') }}
	{% endif %}

	{% set client_transit_free_asns = cfg.filtering.transit_free.asns|select("ne", client.asn) %}
	{% if cfg.filtering.transit_free.action and client_transit_free_asns %}
	# AS_PATH: transit-free ASNs
	if bgp_path ~ [{{ client_transit_free_asns|sort|join(", ")}}] then
	{%	if cfg.filtering.transit_free.action == "reject" %}
		{{ reject(client, 8, '"AS_PATH [", bgp_path ,"] contains transit-free ASN - REJECTING ", net') }}
	{%	else %}
		print "AS_PATH [", bgp_path ,"] contains transit-free ASN - WARNING ", net;
	{%	endif %}
	{% endif %}

	{% if never_via_route_servers_asns %}
	# AS_PATH: never via route-servers ASNs
	if bgp_path ~ [{{ never_via_route_servers_asns|sort|join(", ")}}] then
	{{ reject(client, 15, '"AS_PATH [", bgp_path ,"] contains never via route-servers ASN - REJECTING ", net') }}
	{% endif %}

	{% if client.ip|ipaddr_ver == 6 %}
	# Prefix: only IPv6 Global Unicast space allowed
	{%   if "2.0"|target_version_ge %}
	if net.type = NET_IP6 && !(net ~ [2000::/3+]) then
		{{ reject(client, 10, '"prefix is not in IPv6 Global Unicast space - REJECTING ", net') }}
	{%   else %}
	if !(net ~ [2000::/3+]) then
		{{ reject(client, 10, '"prefix is not in IPv6 Global Unicast space - REJECTING ", net') }}
	{%   endif %}
	{% endif %}

	# Prefix: global blacklist
	if prefix_is_in_global_blacklist() then
		{{ reject(client, 3, '"prefix is in global blacklist - REJECTING ", net') }}

	{% if client.cfg.filtering.black_list_pref %}
	# Prefix: client's blacklist
	if prefix_is_in_{{ client.id }}_blacklist() then
		{{ reject(client, 11, '"prefix is in client\'s blacklist - REJECTING ", net') }}
	{% endif %}

	# Prefix: bogon
	if prefix_is_bogon() then
		{{ reject(client, 2, '"prefix is bogon - REJECTING ", net') }}

	{% if client.cfg.filtering.irrdb.enforce_origin_in_as_set or client.cfg.filtering.irrdb.enforce_prefix_in_as_set or cfg.filtering.irrdb.tag_as_set %}
	# IRRdb checks
	verify_{{ client.id }}_irrdb();
	{% endif %}

	# Blackhole request?
	if is_blackhole_filtering_request() then {
		{% if cfg.communities["rpki_bgp_origin_validation_not_performed"]|community_is_set %}
		# rpki_bgp_origin_validation_not_performed communities
		{{ add_communities(cfg.communities["rpki_bgp_origin_validation_not_performed"], tabs=2) }}
		{% endif %}
		accept "blackhole filtering request from {{ client.id }} - ACCEPTING ", net;
	} else {
		{% if cfg.filtering.rpki_bgp_origin_validation.enabled %}
		perform_rpki_validation();

		{%	if client.cfg.filtering.rpki_bgp_origin_validation.reject_invalid %}
		if route_is_rpki_invalid() then
			{{ reject(client, 14, '"RPKI, route is INVALID - REJECTING ", net') }}
		{%	endif %}
		{% else %}
			{% if cfg.communities["rpki_bgp_origin_validation_not_performed"]|community_is_set %}
			# rpki_bgp_origin_validation_not_performed communities
			{{ add_communities(cfg.communities["rpki_bgp_origin_validation_not_performed"], tabs=3) }}
			{% endif %}
		{% endif %}

		# Prefix: length
		{% if "2.0.0"|target_version_ge and client.cfg.rfc8950 and client.ip|ipaddr_ver == 6 %}
		{%     set afis = [4, 6] %}
		{% else %}
		{%     set afis = [ client.ip|ipaddr_ver ] %}
		{% endif %}
		{% for current_afi in afis %}
		{%	 set min_pref_len = client.cfg.filtering["ipv" ~ current_afi ~ "_pref_len"].min %}
		{%   set max_pref_len = client.cfg.filtering["ipv" ~ current_afi ~ "_pref_len"].max %}
		if {%- if "2.0"|target_version_ge %} net.type = NET_IP{{ current_afi }} && {% else %} {% endif -%}
			!prefix_len_is_valid({{ min_pref_len }}, {{ max_pref_len }}) then
			{{ reject(client, 13, '"prefix len [", net.len, "] not in ' ~ min_pref_len ~ '-' ~ max_pref_len ~ ' - REJECTING ", net') }}
		{% endfor %}

		{% if cfg.graceful_shutdown.enabled %}
		{%	if client.cfg.graceful_shutdown.enabled %}
		honor_graceful_shutdown();
		{%	else %}
		prevent_graceful_shutdown();
		{%	endif %}
		{% endif %}
	}

	{% if "post_receive_from_client"|hook_is_set %}
	if !hook_post_receive_from_client({{ client.asn }}, {{ client.ip }}, "{{ client.id }}") then
		{{ reject(client, 65535, '"hook_post_receive_from_client returned false - REJECTING ", net') }}
	{% endif %}

	accept;
}

filter announce_to_{{ client.id }}
{% if asn3216_map %}
int mapped_16bit_asn;
{% endif %}
{
	{% if "pre_announce_to_client"|hook_is_set %}
	if !hook_pre_announce_to_client({{ client.asn }}, {{ client.ip }}, "{{ client.id }}") then
		reject "hook_pre_announce_to_client returned false - NOT ANNOUNCING ", net, " TO {{ client.id}}";
	{% endif %}

	{% if cfg.communities.reject_cause|community_is_set %}
	{{- match_communities(cfg.communities.reject_cause, "reject;", replace_dyn_val="0") }}
	{% endif %}

	# Blackhole request?
	if is_blackhole_filtering_request() then {
	{% if not client.cfg.blackhole_filtering.announce_to_client %}
		reject "client {{ client.id }} not enabled to receive blackhole prefixes - NOT ANNOUNCING ", net, " TO {{ client.id}}";
	{% else %}
		apply_blackhole_filtering_policy();
	{% endif %}
	{% if cfg.filtering.rpki_bgp_origin_validation.enabled %}
	} else {
		# RPKI: do not announce INVALID to clients
		if route_is_rpki_invalid() then {
			if !announce_rpki_invalid_to_client({{ client.asn }}, {{ client.ip }}, "{{ client.id }}") then
				reject "route is INVALID - NOT ANNOUNCING ", net, " TO {{ client.id}}";
		}
	}
	{% else %}
	}
	{% endif %}

	# BGP control communities
	if !route_can_be_announced_to({{ client.asn }}, {{ client.ip }}, "{{ client.id }}") then
		reject "route didn't pass control communities checks - NOT ANNOUNCING ", net, " TO {{ client.id }}";

	apply_prepend({{ client.asn }}, {{ client.ip }});

	{% if client.cfg.prepend_rs_as %}
	# {{ client.id }} has been configured with 'prepend_rs_as'
	bgp_path.prepend({{ cfg.rs_as }});
	{% endif %}

	add_noexport_noadvertise({{ client.asn }});

	scrub_communities_out();

	{% if perform_graceful_shutdown %}
	perform_graceful_shutdown();
	{% endif %}

	{% if "post_announce_to_client"|hook_is_set %}
	if !hook_post_announce_to_client({{ client.asn }}, {{ client.ip }}, "{{ client.id }}") then
		reject "hook_post_announce_to_client returned false - NOT ANNOUNCING ", net, " TO {{ client.id}}";
	{% endif %}

	accept;
}

{% if "rtt" in client and client["rtt"] %}
# RTT: {{ client["rtt"] }} ms (normalized value: {{ client["rtt"]|get_normalized_rtt }})
{% endif %}
protocol bgp {{ client.id }} {
	{% if client.description %}
	description "{{ client.description|replace("\"", "") }}";
	{% endif %}

	local as {{ cfg.rs_as }};
	neighbor {{ client.ip }} as {{ client.asn }};
	rs client;

	{% if client.cfg.filtering.roles.enabled and "2.0.11"|target_version_ge %}
	local role {{ client.cfg.filtering.roles.get("local_role")|default("rs")|to_bgp_role }};
	{% if client.cfg.filtering.roles.strict_mode %}
	require roles on;
	{% endif %}
	{% endif %}

	{% if client.password %}
	password "{{ client.password }}";
	{% endif -%}

	{% if client.cfg.passive %}
	passive on;
	{% else %}
	passive off;
	{% endif -%}

	{% if client.cfg.gtsm %}
	ttl security on;
	{% else %}
	ttl security off;
	{% endif -%}

	{% if client.cfg.multihop %}
	multihop {{ client.cfg.multihop }};
	{% endif %}

	{% if cfg.rfc1997_wellknown_communities.policy == "rfc1997" %}
	interpret communities on;
	{% else %}
	interpret communities off;
	{% endif %}

	{% if "2.0.0"|target_version_ge and client.cfg.rfc8950 and client.ip|ipaddr_ver == 6 %}
	{%     set afis = [4, 6] %}
	{% else %}
	{%     set afis = [ client.ip|ipaddr_ver ] %}
	{% endif %}
	{% for current_afi in afis %}
	{% if "2.0.0"|target_version_ge %}
	# ---------------------------------------
	ipv{{ current_afi }} {
	table master{{ current_afi }};
	{% endif %}

	{% if client.cfg.add_path %}
	add paths tx;
	{% endif %}

	{% if client.cfg.rfc8950 and client.ip|ipaddr_ver == 6 and current_afi == 4 %}
	extended next hop on;
	{% endif %}
	{% if cfg.path_hiding %}
	secondary;
	{% endif %}

	{% if client.cfg.filtering.max_prefix.action %}
	{%	if current_afi == 4 and client.cfg.filtering.max_prefix.limit_ipv4 %}
	{%		set max_pref_limit = client.cfg.filtering.max_prefix.limit_ipv4 %}
	{% 	elif current_afi == 6 and client.cfg.filtering.max_prefix.limit_ipv6 %}
	{%      	set max_pref_limit = client.cfg.filtering.max_prefix.limit_ipv6 %}
	{% 	else %}
	{%		set max_pref_limit = 0 %}
	{% 	endif %}
	{% 	if max_pref_limit > 0 %}
	{% 	  if client.cfg.filtering.max_prefix.count_rejected_routes %}
	receive limit {{ max_pref_limit }} action {{ write_max_pref_action(client.cfg.filtering.max_prefix.action) }}
	{% 	  else %}
	import limit {{ max_pref_limit }} action {{ write_max_pref_action(client.cfg.filtering.max_prefix.action) }}
	{% 	  endif %}
	{% 	endif %}
	{% endif %}

	{% if "2.0.0"|target_version_ge %}
	import table on;
	{% endif %}

	import keep filtered on;

	import filter receive_from_{{ client.id }};
	export filter announce_to_{{ client.id }};

	{%- if "2.0.0"|target_version_ge %}
	{{- write_custom_config_lines(client, "ipv" ~ current_afi, "channel")|indent("	") }}
	{{- write_custom_config_lines(client, "any", "channel")|indent("	") }}
	# ---------------------------------------
	};
	{% endif %}
	{% endfor %}

	{{- write_custom_config_lines(client, "ipv" ~ client.ip, "protocol")|indent("	") }}
	{{- write_custom_config_lines(client, "any", "protocol")|indent("	") }}
	{{ "client"|include_local_file -}}
	{% if client.ip|ipaddr_ver == 4 %}
	{{ "client4"|include_local_file -}}
	{% else %}
	{{ "client6"|include_local_file -}}
	{% endif %}
}

{% endfor -%}
