function apply_communities ()
{
  {{- $prefix_advertisements_key := ""}}
  {{- $node_prefix_advertisements_key := printf "/bgp/v1/host/%s/prefix_advertisements/ip_v4" (getenv "NODENAME")}}
  {{- if exists $node_prefix_advertisements_key}}
    {{- $prefix_advertisements_key = $node_prefix_advertisements_key}}
  {{- else if exists "/bgp/v1/global/prefix_advertisements/ip_v4"}}
    {{- $prefix_advertisements_key = "/bgp/v1/global/prefix_advertisements/ip_v4"}}
  {{- end}}
  {{- if ne "" $prefix_advertisements_key}}
    {{- range gets $prefix_advertisements_key}}
      {{- $arr:= jsonArray .Value}}
      {{- range $data := $arr}}
      if ( net ~ {{$data.cidr}} ) then {
        {{- range $dt := $data.communities }}
        {{- $i := split $dt ":"}}
        {{- $length := len $i}}
        {{- if eq $length 2}}
          bgp_community.add(({{index $i 0}}, {{index $i 1}}));
        {{- else}}
          bgp_large_community.add(({{index $i 0}}, {{index $i 1}}, {{index $i 2}}));
        {{- end}}
        {{- end}}
      }
      {{- end}}
    {{- end}}
  {{- end}}
}

# Generated by confd
include "bird_aggr.cfg";
include "bird_ipam.cfg";

{{- $node_ip_key := printf "/bgp/v1/host/%s/ip_addr_v4" (getenv "NODENAME")}}{{$node_ip := getv $node_ip_key}}
{{- $router_id := getenv "CALICO_ROUTER_ID" ""}}

{{- $node_name := getenv "NODENAME"}}

router id {{if eq "hash" ($router_id) -}}
	{{hashToIPv4 $node_name}};
{{- else -}}
	{{if ne "" ($router_id)}}{{$router_id}}{{else}}{{$node_ip}}{{end}};
{{- end}}

{{- $listen_address := ""}}
{{- $bind_mode := ""}}
{{- $host_bind_mode_key := printf "/bgp/v1/host/%s/bind_mode" (getenv "NODENAME")}}
{{- if exists $host_bind_mode_key }}
{{- $bind_mode = getv $host_bind_mode_key}}
{{- else if exists "/bgp/v1/global/bind_mode" }}
{{- $bind_mode = getv "/bgp/v1/global/bind_mode"}}
{{- end}}
{{- if and (eq $bind_mode "NodeIP") (ne $node_ip "")}}
{{- $listen_address = print " address " $node_ip}}
{{- end}}

{{- $listen_port := ""}}
{{- $node_listen_port_key := printf "/bgp/v1/host/%s/listen_port" (getenv "NODENAME")}}
{{- if exists $node_listen_port_key}}
# Set node listen_port
{{- $listen_port = print " port " (getv $node_listen_port_key)}}
{{- else if exists "/bgp/v1/global/listen_port" }}
# Set global listen_port
{{- $listen_port = print " port " (getv "/bgp/v1/global/listen_port")}}
{{- end}}

{{- if or (ne $listen_address "") (ne $listen_port "")}}
listen bgp{{$listen_address}}{{$listen_port}};
{{- end}}

{{- define "LOGGING"}}
{{- $node_logging_key := printf "/bgp/v1/host/%s/loglevel" (getenv "NODENAME")}}
{{- if exists $node_logging_key}}
{{- $logging := getv $node_logging_key}}
{{- if eq $logging "debug"}}
  debug all;
{{- else if ne $logging "none"}}
  debug { states };
{{- end}}
{{- else if exists "/bgp/v1/global/loglevel"}}
{{- $logging := getv "/bgp/v1/global/loglevel"}}
{{- if eq $logging "debug"}}
  debug all;
{{- else if ne $logging "none"}}
  debug { states };
{{- end}}
{{- else}}
  debug { states };
{{- end}}
{{- end}}

# Configure synchronization between routing tables and kernel.
protocol kernel {
  learn;             # Learn all alien routes from the kernel
  persist;           # Don't remove routes on bird shutdown
  scan time 2;       # Scan kernel routing table every 2 seconds
  import all;
  export filter calico_kernel_programming; # Default is export none
  graceful restart;  # Turn on graceful restart to reduce potential flaps in
                     # routes when reloading BIRD configuration.  With a full
                     # automatic mesh, there is no way to prevent BGP from
                     # flapping since multiple nodes update their BGP
                     # configuration at the same time, GR is not guaranteed to
                     # work correctly in this scenario.
  merge paths on;    # Allow export multipath routes (ECMP)
}

# Watch interface up/down events.
protocol device {
{{- template "LOGGING"}}
  scan time 2;    # Scan interfaces every 2 seconds
}

{{- $ignored_interfaces := ""}}
{{- $node_ignored_interfaces_key := printf "/bgp/v1/host/%s/ignored_interfaces" (getenv "NODENAME")}}
{{- if exists $node_ignored_interfaces_key }}{{$ignored_interfaces = getv $node_ignored_interfaces_key}}
{{- else if exists "/bgp/v1/global/ignored_interfaces" }}{{$ignored_interfaces = getv "/bgp/v1/global/ignored_interfaces"}}
{{- end}}

protocol direct {
{{- template "LOGGING"}}
{{- if ne "" $ignored_interfaces}}{{$ifaces := split $ignored_interfaces ","}}
  interface {{range $ifaces}}-"{{.}}", {{end}}-"cali*", -"kube-ipvs*", "*";
{{- else}}
  interface -"cali*", -"kube-ipvs*", "*"; # Exclude cali* and kube-ipvs* but
                                          # include everything else.  In
                                          # IPVS-mode, kube-proxy creates a
                                          # kube-ipvs0 interface. We exclude
                                          # kube-ipvs0 because this interface
                                          # gets an address for every in use
                                          # cluster IP. We use static routes
                                          # for when we legitimately want to
                                          # export cluster IPs.
{{- end}}
}

{{if eq "" ($node_ip)}}# IPv4 disabled on this node.
{{else}}{{$node_as_key := printf "/bgp/v1/host/%s/as_num" (getenv "NODENAME")}}
# Template for all BGP clients
template bgp bgp_template {
{{- $as_key := or (and (exists $node_as_key) $node_as_key) "/bgp/v1/global/as_num"}}
{{- $node_as_num := getv $as_key}}
{{- template "LOGGING"}}
  description "Connection to BGP peer";
  local as {{$node_as_num}};
  gateway recursive; # This should be the default, but just in case.
  add paths on;
  graceful restart;  # See comment in kernel section about graceful restart.
  connect delay time 2;
  connect retry time 5;
  error wait time 5,30;
}

# -------------- BGP Filters ------------------
{{- range $line := bgpFilterBIRDFuncs (gets "/resources/v3/projectcalico.org/bgpfilters/*") 4 }}
{{ $line }}
{{- end }}

# ------------- Node-to-node mesh -------------
{{- $node_cid_key := printf "/bgp/v1/host/%s/rr_cluster_id" (getenv "NODENAME")}}
{{- $node_cluster_id := getv $node_cid_key}}
{{- if ne "" ($node_cluster_id)}}
# This node ({{getenv "NODENAME"}}) is configured as a route reflector with cluster ID {{$node_cluster_id}};
# ignore node-to-node mesh setting.
{{- else}}
{{if (json (getv "/bgp/v1/global/node_mesh")).enabled}}
{{range $host := lsdir "/bgp/v1/host"}}
{{$onode_as_key := printf "/bgp/v1/host/%s/as_num" .}}
{{$onode_ip_key := printf "/bgp/v1/host/%s/ip_addr_v4" .}}{{if exists $onode_ip_key}}{{$onode_ip := getv $onode_ip_key}}
{{- $listen_port := ""}}
{{- $onode_listen_port_key := printf "/bgp/v1/host/%s/listen_port" .}}
{{- if exists $onode_listen_port_key}}
{{- $listen_port = getv $onode_listen_port_key}}
{{- else if exists "/bgp/v1/global/listen_port"}}
{{- $listen_port = getv "/bgp/v1/global/listen_port"}}
{{- end}}
{{- $onode_cid_key := printf "/bgp/v1/host/%s/rr_cluster_id" .}}
{{- $onode_cluster_id := getv $onode_cid_key}}
{{$nums := split $onode_ip "."}}{{$id := join $nums "_"}}
# For peer {{$onode_ip_key}}
{{if eq $onode_ip ($node_ip) }}# Skipping ourselves ({{$node_ip}})
{{- else if ne "" ($onode_cluster_id)}}# Skipping {{$onode_ip}} ({{.}}) as it is a route reflector with cluster ID {{$onode_cluster_id}}
{{else if ne "" $onode_ip}}protocol bgp Mesh_{{$id}} from bgp_template {
  neighbor {{$onode_ip}} {{if ne "" $listen_port}}port {{$listen_port}} {{end}}as {{if exists $onode_as_key}}{{getv $onode_as_key}}{{else}}{{getv "/bgp/v1/global/as_num"}}{{end}};
  source address {{$node_ip}};  # The local address we use for the TCP connection
  import all;        # Import all routes, since we don't know what the upstream
                     # topology is and therefore have to trust the ToR/RR.
  export filter {
    calico_export_to_bgp_peers(true);
    reject;
  };  # Only want to export routes for workloads.
  {{- /*
       Make the peering unidirectional. This avoids a race where
       - peer A opens a connection and begins a graceful restart
       - before the restart completes, peer B opens its connection
       - peer A sees the new connection and aborts the graceful restart, causing a route flap.
  */ -}}
  {{if gt $onode_ip $node_ip}}
  passive on; # Mesh is unidirectional, peer will connect to us.
  {{- end}}
  {{- if exists "/bgp/v1/global/node_mesh_restart_time"}}{{$node_mesh_restart_time := getv "/bgp/v1/global/node_mesh_restart_time"}}
  {{- if ne ($node_mesh_restart_time) ""}}
  graceful restart time {{$node_mesh_restart_time}};
  {{- end}}{{end}}
  {{- if exists "/bgp/v1/global/node_mesh_password"}}{{$node_mesh_password := getv "/bgp/v1/global/node_mesh_password"}}
  {{- if ne ($node_mesh_password) ""}}
  password "{{$node_mesh_password}}";
  {{- end}}{{end}}
}{{end}}{{end}}{{end}}
{{else}}
# Node-to-node mesh disabled
{{end}}
{{- end}}


# ------------- Global peers -------------
{{if ls "/bgp/v1/global/peer_v4"}}
{{range gets "/bgp/v1/global/peer_v4/*"}}{{$data := json .Value}}
{{$nums := split $data.ip "."}}{{$id := join $nums "_"}}
{{- if $data.port}}
{{- $id = printf "%s_port_%.0f" $id $data.port}}
{{- end}}
# For peer {{.Key}}
{{- if eq $data.ip ($node_ip) }}
# Skipping ourselves ({{$node_ip}})
{{- else}}
protocol bgp Global_{{$id}} from bgp_template {
{{- if $data.ttl_security }}
  ttl security on;
  multihop {{ $data.ttl_security }};
{{- else }}
  ttl security off;
  multihop;
{{- end }}
  neighbor {{$data.ip}} {{if $data.port }}port {{ $data.port }} {{end}}as {{$data.as_num}};
{{- if eq $data.source_addr "UseNodeIP"}}
  source address {{$node_ip}};  # The local address we use for the TCP connection
{{- end}}
  import filter {
    {{- range $filter := $data.filters }}
    {{- $filterKey := printf "/resources/v3/projectcalico.org/bgpfilters/%s" $filter }}
    {{- if exists $filterKey }}
    {{- $filterVal := json (getv $filterKey) }}
    {{- if $filterVal.spec.importV4 }}
    {{ bgpFilterFunctionName $filter "import" "4" }}();
    {{- end }}
    {{- end }}
    {{- end }}
    accept; # Prior to introduction of BGP Filters we used "import all" so use default accept behaviour on import
  };
  export filter {
    {{- range $filter := $data.filters }}
    {{- $filterKey := printf "/resources/v3/projectcalico.org/bgpfilters/%s" $filter }}
    {{- if exists $filterKey }}
    {{- $filterVal := json (getv $filterKey) }}
    {{- if $filterVal.spec.exportV4 }}
    {{ bgpFilterFunctionName $filter "export" "4" }}();
    {{- end }}
    {{- end }}
    {{- end }}
    calico_export_to_bgp_peers({{eq $data.as_num $node_as_num}});
    reject;{{/* Prior to introduction of BGP Filters anything not explicitly exported through calico_export_to_bgp_peers()
                was rejected so use default reject behaviour on export */}}
  };  # Only want to export routes for workloads.
{{- if and ($data.calico_node) (gt $data.ip $node_ip)}}
  passive on; # Peering is unidirectional, peer will connect to us.
{{- end}}
{{- if ne $data.restart_time ""}}
  graceful restart time {{$data.restart_time}};
{{- end}}
{{- if and (eq $data.as_num $node_as_num) (ne "" ($node_cluster_id)) (ne $data.rr_cluster_id ($node_cluster_id))}}
  rr client;
  rr cluster id {{$node_cluster_id}};
{{- end}}
{{- if $data.password}}
  password "{{$data.password}}";
{{- end}}
{{- if and (ne $data.as_num $node_as_num) ($data.keep_next_hop)}}
  next hop keep;
{{- end}}
{{- if $data.num_allow_local_as}}
  allow local as {{$data.num_allow_local_as}};
{{- end}}
}
{{- end}}
{{end}}
{{else}}# No global peers configured.{{end}}


# ------------- Node-specific peers -------------
{{$node_peers_key := printf "/bgp/v1/host/%s/peer_v4" (getenv "NODENAME")}}
{{if ls $node_peers_key}}
{{range gets (printf "%s/*" $node_peers_key)}}{{$data := json .Value}}
{{$nums := split $data.ip "."}}{{$id := join $nums "_"}}
{{- if $data.port}}
{{- $id = printf "%s_port_%.0f" $id $data.port}}
{{- end}}
# For peer {{.Key}}
{{- if eq $data.ip ($node_ip) }}
# Skipping ourselves ({{$node_ip}})
{{- else}}
protocol bgp Node_{{$id}} from bgp_template {
{{- if $data.ttl_security }}
  ttl security on;
  multihop {{ $data.ttl_security }};
{{- else }}
  ttl security off;
  multihop;
{{- end }}
  neighbor {{$data.ip}} {{if $data.port }}port {{ $data.port }} {{end}}as {{$data.as_num}};
{{- if eq $data.source_addr "UseNodeIP"}}
  source address {{$node_ip}};  # The local address we use for the TCP connection
{{- end}}
  import filter {
    {{- range $filter := $data.filters }}
    {{- $filterKey := printf "/resources/v3/projectcalico.org/bgpfilters/%s" $filter }}
    {{- if exists $filterKey }}
    {{- $filterVal := json (getv $filterKey) }}
    {{- if $filterVal.spec.importV4 }}
    {{ bgpFilterFunctionName $filter "import" "4" }}();
    {{- end }}
    {{- end }}
    {{- end }}
    accept; # Prior to introduction of BGP Filters we used "import all" so use default accept behaviour on import
  };
  export filter {
    {{- range $filter := $data.filters }}
    {{- $filterKey := printf "/resources/v3/projectcalico.org/bgpfilters/%s" $filter }}
    {{- if exists $filterKey }}
    {{- $filterVal := json (getv $filterKey) }}
    {{- if $filterVal.spec.exportV4 }}
    {{ bgpFilterFunctionName $filter "export" "4" }}();
    {{- end }}
    {{- end }}
    {{- end }}
    calico_export_to_bgp_peers({{eq $data.as_num $node_as_num}});
    reject;{{/* Prior to introduction of BGP Filters anything not explicitly exported through calico_export_to_bgp_peers()
                was rejected so use default reject behaviour on export */}}
  };  # Only want to export routes for workloads.
{{- if ne $data.restart_time ""}}
  graceful restart time {{$data.restart_time}};
{{- end}}
{{- if and (eq $data.as_num $node_as_num) (ne "" ($node_cluster_id)) (ne $data.rr_cluster_id ($node_cluster_id))}}
  rr client;
  rr cluster id {{$node_cluster_id}};
{{- end}}
{{- if $data.password}}
  password "{{$data.password}}";
{{- end}}
{{- if and (ne $data.as_num $node_as_num) ($data.keep_next_hop)}}
  next hop keep;
{{- end}}
{{- if $data.num_allow_local_as}}
  allow local as {{$data.num_allow_local_as}};
{{- end}}
}
{{- end}}
{{end}}
{{else}}# No node-specific peers configured.{{end}}
{{end}}{{/* End of IPv4 enable check */}}
