Procházet zdrojové kódy

feat(routing): align BGP Connection modal and OSPF interface with RouterOS v7 WinBox screenshots

Proxmox Router před 1 týdnem
rodič
revize
c5883d9566

+ 33 - 15
internal/dynamicrouting/types.go

@@ -47,21 +47,39 @@ type BGPPeer struct {
 	RedistStatic     bool     `json:"redist_static"`
 	RedistOSPF       bool     `json:"redist_ospf"`
 	RedistBGP        bool     `json:"redist_bgp"`
-	RedistRIP        bool     `json:"redist_rip,omitempty"`
-	RedistVPN        bool     `json:"redist_vpn,omitempty"`
-	RedistDHCP       bool     `json:"redist_dhcp,omitempty"`
-	DefaultOriginate bool     `json:"default_originate"`
-	NexthopChoice    string   `json:"nexthop_choice,omitempty"` // default, force-self
-	NexthopSelf      bool     `json:"nexthop_self"`
-	DefaultPrepend   int      `json:"default_prepend,omitempty"`
-	AllowASIn        int      `json:"allow_as_in,omitempty"`
-	RemovePrivateAS  bool     `json:"remove_private_as"`
-	ASOverride       bool     `json:"as_override,omitempty"`
-	IgnoreASPathLen  bool     `json:"ignore_as_path_len,omitempty"`
-	InFilter         string   `json:"in_filter,omitempty"`        // Routing Filter chain for incoming routes
-	OutFilter        string   `json:"out_filter,omitempty"`       // Routing Filter chain for outgoing routes
-	OutputNetwork    string   `json:"output_network,omitempty"`
-	OutputNetworks   []string `json:"output_networks,omitempty"` // Dynamic [+] / [-] output networks
+	RedistRIP                  bool     `json:"redist_rip,omitempty"`
+	RedistVPN                  bool     `json:"redist_vpn,omitempty"`
+	RedistDHCP                 bool     `json:"redist_dhcp,omitempty"`
+	RedistFantasy              bool     `json:"redist_fantasy,omitempty"`
+	RedistModem                bool     `json:"redist_modem,omitempty"`
+	RedistBgpMplsVpn           bool     `json:"redist_bgp_mpls_vpn,omitempty"`
+	RedistSlaac                bool     `json:"redist_slaac,omitempty"`
+	DefaultOriginate           bool     `json:"default_originate"`
+	NetworkBlackhole           bool     `json:"network_blackhole,omitempty"`
+	NoEarlyCut                 bool     `json:"no_early_cut,omitempty"`
+	KeepSentAttrs              bool     `json:"keep_sent_attrs,omitempty"`
+	InputAffinity              string   `json:"input_affinity,omitempty"`
+	OutputAffinity             string   `json:"output_affinity,omitempty"`
+	NexthopChoice              string   `json:"nexthop_choice,omitempty"` // default, force-self
+	NexthopSelf                bool     `json:"nexthop_self"`
+	DefaultPrepend             int      `json:"default_prepend,omitempty"`
+	AllowASIn                  int      `json:"allow_as_in,omitempty"`
+	RemovePrivateAS            bool     `json:"remove_private_as"`
+	ASOverride                 bool     `json:"as_override,omitempty"`
+	IgnoreASPathLen            bool     `json:"ignore_as_path_len,omitempty"`
+	InFilter                   string   `json:"in_filter,omitempty"`        // Routing Filter chain for incoming routes
+	InAcceptNLRI               string   `json:"in_accept_nlri,omitempty"`
+	InAcceptCommunities        string   `json:"in_accept_communities,omitempty"`
+	InAcceptExtCommunities     string   `json:"in_accept_ext_communities,omitempty"`
+	InAcceptLargeCommunities   string   `json:"in_accept_large_communities,omitempty"`
+	InFilterCommunities        string   `json:"in_filter_communities,omitempty"`
+	InFilterExtCommunities     string   `json:"in_filter_ext_communities,omitempty"`
+	InFilterLargeCommunities   string   `json:"in_filter_large_communities,omitempty"`
+	InFilterUnknown            bool     `json:"in_filter_unknown,omitempty"`
+	OutFilter                  string   `json:"out_filter,omitempty"`       // Routing Filter chain for outgoing routes
+	OutSelectionPolicy         string   `json:"out_selection_policy,omitempty"`
+	OutputNetwork              string   `json:"output_network,omitempty"`
+	OutputNetworks             []string `json:"output_networks,omitempty"` // Dynamic [+] / [-] output networks
 	Disabled         bool     `json:"disabled"`
 	Passive          bool     `json:"passive"`
 	// Runtime telemetry

+ 70 - 0
internal/web/static/app.js

@@ -8060,7 +8060,14 @@ function openAddBgpPeerModal() {
   document.getElementById('modal-bgp-peer-redist-bgp').checked = false;
   if (document.getElementById('modal-bgp-peer-redist-vpn')) document.getElementById('modal-bgp-peer-redist-vpn').checked = false;
   if (document.getElementById('modal-bgp-peer-redist-dhcp')) document.getElementById('modal-bgp-peer-redist-dhcp').checked = false;
+  if (document.getElementById('modal-bgp-peer-redist-bgp-mpls-vpn')) document.getElementById('modal-bgp-peer-redist-bgp-mpls-vpn').checked = false;
+  if (document.getElementById('modal-bgp-peer-redist-slaac')) document.getElementById('modal-bgp-peer-redist-slaac').checked = false;
   document.getElementById('modal-bgp-peer-default-orig').checked = false;
+  if (document.getElementById('modal-bgp-peer-network-blackhole')) document.getElementById('modal-bgp-peer-network-blackhole').checked = false;
+  if (document.getElementById('modal-bgp-peer-no-early-cut')) document.getElementById('modal-bgp-peer-no-early-cut').checked = false;
+  if (document.getElementById('modal-bgp-peer-keep-sent-attrs')) document.getElementById('modal-bgp-peer-keep-sent-attrs').checked = false;
+  if (document.getElementById('modal-bgp-peer-input-affinity')) document.getElementById('modal-bgp-peer-input-affinity').value = '';
+  if (document.getElementById('modal-bgp-peer-output-affinity')) document.getElementById('modal-bgp-peer-output-affinity').value = '';
   
   // Attributes Tab
   document.getElementById('modal-bgp-peer-nexthop-choice').value = 'force-self';
@@ -8071,8 +8078,18 @@ function openAddBgpPeerModal() {
   document.getElementById('modal-bgp-peer-as-override').checked = false;
 
   // Filter Tab
+  populateFilterChainsDatalist();
   document.getElementById('modal-bgp-peer-in-filter').value = '';
+  if (document.getElementById('modal-bgp-peer-in-accept-nlri')) document.getElementById('modal-bgp-peer-in-accept-nlri').value = '';
+  if (document.getElementById('modal-bgp-peer-in-accept-comm')) document.getElementById('modal-bgp-peer-in-accept-comm').value = '';
+  if (document.getElementById('modal-bgp-peer-in-accept-ext-comm')) document.getElementById('modal-bgp-peer-in-accept-ext-comm').value = '';
+  if (document.getElementById('modal-bgp-peer-in-accept-large-comm')) document.getElementById('modal-bgp-peer-in-accept-large-comm').value = '';
+  if (document.getElementById('modal-bgp-peer-in-filter-comm')) document.getElementById('modal-bgp-peer-in-filter-comm').value = '';
+  if (document.getElementById('modal-bgp-peer-in-filter-ext-comm')) document.getElementById('modal-bgp-peer-in-filter-ext-comm').value = '';
+  if (document.getElementById('modal-bgp-peer-in-filter-large-comm')) document.getElementById('modal-bgp-peer-in-filter-large-comm').value = '';
+  if (document.getElementById('modal-bgp-peer-in-filter-unknown')) document.getElementById('modal-bgp-peer-in-filter-unknown').checked = false;
   document.getElementById('modal-bgp-peer-out-filter').value = '';
+  if (document.getElementById('modal-bgp-peer-out-selection-policy')) document.getElementById('modal-bgp-peer-out-selection-policy').value = 'disabled';
   if (document.getElementById('modal-bgp-peer-out-network-name')) document.getElementById('modal-bgp-peer-out-network-name').value = '';
 
   populateBgpRoutingTables();
@@ -8090,11 +8107,32 @@ function openAddBgpPeerModal() {
   document.getElementById('bgp-peer-modal').classList.remove('hidden');
 }
 
+function populateFilterChainsDatalist() {
+  const dl = document.getElementById('filter-chains-list');
+  if (!dl) return;
+  const chains = new Set();
+  (state.routingFilters || []).forEach(f => {
+    if (f.chain) chains.add(f.chain);
+  });
+  if (chains.size === 0) {
+    chains.add('awmn');
+    chains.add('bgp-in');
+    chains.add('bgp-out');
+  }
+  dl.innerHTML = '';
+  chains.forEach(c => {
+    const opt = document.createElement('option');
+    opt.value = c;
+    dl.appendChild(opt);
+  });
+}
+
 function openEditBgpPeerModal(id) {
   const p = (state.bgpPeers || []).find(item => item.id === id);
   if (!p) return;
 
   populateBgpRoutingTables();
+  populateFilterChainsDatalist();
 
   document.getElementById('modal-bgp-peer-id').value = p.id;
   document.getElementById('bgp-peer-modal-title').innerText = p.name || p.id;
@@ -8137,7 +8175,14 @@ function openEditBgpPeerModal(id) {
   document.getElementById('modal-bgp-peer-redist-bgp').checked = !!p.redist_bgp;
   if (document.getElementById('modal-bgp-peer-redist-vpn')) document.getElementById('modal-bgp-peer-redist-vpn').checked = !!p.redist_vpn;
   if (document.getElementById('modal-bgp-peer-redist-dhcp')) document.getElementById('modal-bgp-peer-redist-dhcp').checked = !!p.redist_dhcp;
+  if (document.getElementById('modal-bgp-peer-redist-bgp-mpls-vpn')) document.getElementById('modal-bgp-peer-redist-bgp-mpls-vpn').checked = !!p.redist_bgp_mpls_vpn;
+  if (document.getElementById('modal-bgp-peer-redist-slaac')) document.getElementById('modal-bgp-peer-redist-slaac').checked = !!p.redist_slaac;
   document.getElementById('modal-bgp-peer-default-orig').checked = !!p.default_originate;
+  if (document.getElementById('modal-bgp-peer-network-blackhole')) document.getElementById('modal-bgp-peer-network-blackhole').checked = !!p.network_blackhole;
+  if (document.getElementById('modal-bgp-peer-no-early-cut')) document.getElementById('modal-bgp-peer-no-early-cut').checked = !!p.no_early_cut;
+  if (document.getElementById('modal-bgp-peer-keep-sent-attrs')) document.getElementById('modal-bgp-peer-keep-sent-attrs').checked = !!p.keep_sent_attrs;
+  if (document.getElementById('modal-bgp-peer-input-affinity')) document.getElementById('modal-bgp-peer-input-affinity').value = p.input_affinity || '';
+  if (document.getElementById('modal-bgp-peer-output-affinity')) document.getElementById('modal-bgp-peer-output-affinity').value = p.output_affinity || '';
 
   // Attributes Tab
   document.getElementById('modal-bgp-peer-nexthop-choice').value = p.nexthop_choice || (p.nexthop_self ? 'force-self' : 'default');
@@ -8149,7 +8194,16 @@ function openEditBgpPeerModal(id) {
 
   // Filter Tab
   document.getElementById('modal-bgp-peer-in-filter').value = p.in_filter || '';
+  if (document.getElementById('modal-bgp-peer-in-accept-nlri')) document.getElementById('modal-bgp-peer-in-accept-nlri').value = p.in_accept_nlri || '';
+  if (document.getElementById('modal-bgp-peer-in-accept-comm')) document.getElementById('modal-bgp-peer-in-accept-comm').value = p.in_accept_communities || '';
+  if (document.getElementById('modal-bgp-peer-in-accept-ext-comm')) document.getElementById('modal-bgp-peer-in-accept-ext-comm').value = p.in_accept_ext_communities || '';
+  if (document.getElementById('modal-bgp-peer-in-accept-large-comm')) document.getElementById('modal-bgp-peer-in-accept-large-comm').value = p.in_accept_large_communities || '';
+  if (document.getElementById('modal-bgp-peer-in-filter-comm')) document.getElementById('modal-bgp-peer-in-filter-comm').value = p.in_filter_communities || '';
+  if (document.getElementById('modal-bgp-peer-in-filter-ext-comm')) document.getElementById('modal-bgp-peer-in-filter-ext-comm').value = p.in_filter_ext_communities || '';
+  if (document.getElementById('modal-bgp-peer-in-filter-large-comm')) document.getElementById('modal-bgp-peer-in-filter-large-comm').value = p.in_filter_large_communities || '';
+  if (document.getElementById('modal-bgp-peer-in-filter-unknown')) document.getElementById('modal-bgp-peer-in-filter-unknown').checked = !!p.in_filter_unknown;
   document.getElementById('modal-bgp-peer-out-filter').value = p.out_filter || '';
+  if (document.getElementById('modal-bgp-peer-out-selection-policy')) document.getElementById('modal-bgp-peer-out-selection-policy').value = p.out_selection_policy || 'disabled';
   if (document.getElementById('modal-bgp-peer-out-network-name')) document.getElementById('modal-bgp-peer-out-network-name').value = p.output_network || '';
 
   // Output Networks
@@ -8238,7 +8292,14 @@ function getBgpPeerFormData() {
     redist_bgp: document.getElementById('modal-bgp-peer-redist-bgp').checked,
     redist_vpn: document.getElementById('modal-bgp-peer-redist-vpn')?.checked || false,
     redist_dhcp: document.getElementById('modal-bgp-peer-redist-dhcp')?.checked || false,
+    redist_bgp_mpls_vpn: document.getElementById('modal-bgp-peer-redist-bgp-mpls-vpn')?.checked || false,
+    redist_slaac: document.getElementById('modal-bgp-peer-redist-slaac')?.checked || false,
     default_originate: document.getElementById('modal-bgp-peer-default-orig').checked,
+    network_blackhole: document.getElementById('modal-bgp-peer-network-blackhole')?.checked || false,
+    no_early_cut: document.getElementById('modal-bgp-peer-no-early-cut')?.checked || false,
+    keep_sent_attrs: document.getElementById('modal-bgp-peer-keep-sent-attrs')?.checked || false,
+    input_affinity: (document.getElementById('modal-bgp-peer-input-affinity')?.value || '').trim(),
+    output_affinity: (document.getElementById('modal-bgp-peer-output-affinity')?.value || '').trim(),
     nexthop_choice: document.getElementById('modal-bgp-peer-nexthop-choice').value,
     nexthop_self: document.getElementById('modal-bgp-peer-nexthop-choice').value === 'force-self',
     default_prepend: parseInt(document.getElementById('modal-bgp-peer-default-prepend').value, 10) || 0,
@@ -8247,7 +8308,16 @@ function getBgpPeerFormData() {
     remove_private_as: document.getElementById('modal-bgp-peer-remove-priv-as').checked,
     as_override: document.getElementById('modal-bgp-peer-as-override').checked,
     in_filter: (document.getElementById('modal-bgp-peer-in-filter')?.value || '').trim(),
+    in_accept_nlri: (document.getElementById('modal-bgp-peer-in-accept-nlri')?.value || '').trim(),
+    in_accept_communities: (document.getElementById('modal-bgp-peer-in-accept-comm')?.value || '').trim(),
+    in_accept_ext_communities: (document.getElementById('modal-bgp-peer-in-accept-ext-comm')?.value || '').trim(),
+    in_accept_large_communities: (document.getElementById('modal-bgp-peer-in-accept-large-comm')?.value || '').trim(),
+    in_filter_communities: (document.getElementById('modal-bgp-peer-in-filter-comm')?.value || '').trim(),
+    in_filter_ext_communities: (document.getElementById('modal-bgp-peer-in-filter-ext-comm')?.value || '').trim(),
+    in_filter_large_communities: (document.getElementById('modal-bgp-peer-in-filter-large-comm')?.value || '').trim(),
+    in_filter_unknown: document.getElementById('modal-bgp-peer-in-filter-unknown')?.checked || false,
     out_filter: (document.getElementById('modal-bgp-peer-out-filter')?.value || '').trim(),
+    out_selection_policy: document.getElementById('modal-bgp-peer-out-selection-policy')?.value || 'disabled',
     output_network: (document.getElementById('modal-bgp-peer-out-network-name')?.value || '').trim(),
     output_networks: outputNetworks
   };

+ 129 - 3
internal/web/static/index.html

@@ -7924,6 +7924,14 @@
                     <input type="checkbox" id="modal-bgp-peer-redist-modem" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
                     <span>modem</span>
                   </label>
+                  <label class="flex items-center gap-2 cursor-pointer">
+                    <input type="checkbox" id="modal-bgp-peer-redist-bgp-mpls-vpn" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
+                    <span>bgp-mpls-vpn</span>
+                  </label>
+                  <label class="flex items-center gap-2 cursor-pointer">
+                    <input type="checkbox" id="modal-bgp-peer-redist-slaac" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
+                    <span>slaac</span>
+                  </label>
                 </div>
               </div>
 
@@ -7938,6 +7946,50 @@
                   </label>
                 </div>
               </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Network Blackhole</label>
+                <div class="col-span-2">
+                  <label class="flex items-center gap-2 cursor-pointer text-slate-300">
+                    <input type="checkbox" id="modal-bgp-peer-network-blackhole" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
+                    <span>Drop unroutable traffic via blackhole</span>
+                  </label>
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">No Early Cut</label>
+                <div class="col-span-2">
+                  <label class="flex items-center gap-2 cursor-pointer text-slate-300">
+                    <input type="checkbox" id="modal-bgp-peer-no-early-cut" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
+                    <span>Prevent early cut on prefix withdrawal</span>
+                  </label>
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Keep Sent Attributes</label>
+                <div class="col-span-2">
+                  <label class="flex items-center gap-2 cursor-pointer text-slate-300">
+                    <input type="checkbox" id="modal-bgp-peer-keep-sent-attrs" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
+                    <span>Retain outbound advertised attributes</span>
+                  </label>
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Affinities</label>
+                <div class="col-span-2 grid grid-cols-2 gap-3">
+                  <div>
+                    <span class="text-[10px] text-slate-500 block mb-0.5">Input Affinity</span>
+                    <input type="text" id="modal-bgp-peer-input-affinity" placeholder="none" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1 text-white font-mono">
+                  </div>
+                  <div>
+                    <span class="text-[10px] text-slate-500 block mb-0.5">Output Affinity</span>
+                    <input type="text" id="modal-bgp-peer-output-affinity" placeholder="none" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1 text-white font-mono">
+                  </div>
+                </div>
+              </div>
             </div>
 
             <!-- TAB 3: ATTRIBUTES -->
@@ -8009,23 +8061,97 @@
             </div>
 
             <!-- TAB 4: FILTER -->
-            <div id="bgp-peer-tab-filter" class="space-y-4 hidden">
+            <div id="bgp-peer-tab-filter" class="space-y-3.5 hidden">
+              <datalist id="filter-chains-list"></datalist>
+
               <div class="grid grid-cols-3 items-center gap-3">
                 <label class="text-slate-400 font-medium">Input Filter</label>
                 <div class="col-span-2">
-                  <input type="text" id="modal-bgp-peer-in-filter" placeholder="e.g. awmn or bgp-in" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                  <input type="text" list="filter-chains-list" id="modal-bgp-peer-in-filter" placeholder="e.g. awmn" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
                   <p class="text-[10px] text-slate-500 mt-0.5">Routing Filter chain applied to incoming routes</p>
                 </div>
               </div>
 
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Accept NLRI</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-accept-nlri" placeholder="Optional NLRI prefixes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Accept Communities</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-accept-comm" placeholder="e.g. 65001:100" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Accept Ext Communities</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-accept-ext-comm" placeholder="e.g. rt:65001:10" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Accept Large Communities</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-accept-large-comm" placeholder="e.g. 65001:1:1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Filter Communities</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-filter-comm" placeholder="Filter pattern" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Filter Ext Communities</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-filter-ext-comm" placeholder="Filter pattern" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Filter Large Communities</label>
+                <div class="col-span-2">
+                  <input type="text" id="modal-bgp-peer-in-filter-large-comm" placeholder="Filter pattern" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                </div>
+              </div>
+
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Input Filter Unknown</label>
+                <div class="col-span-2">
+                  <label class="flex items-center gap-2 cursor-pointer text-slate-300">
+                    <input type="checkbox" id="modal-bgp-peer-in-filter-unknown" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
+                    <span>Filter unknown BGP attributes</span>
+                  </label>
+                </div>
+              </div>
+
+              <hr class="border-slate-800/80 my-2">
+
               <div class="grid grid-cols-3 items-center gap-3">
                 <label class="text-slate-400 font-medium">Output Filter</label>
                 <div class="col-span-2">
-                  <input type="text" id="modal-bgp-peer-out-filter" placeholder="e.g. awmn or bgp-out" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                  <input type="text" list="filter-chains-list" id="modal-bgp-peer-out-filter" placeholder="e.g. awmn" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
                   <p class="text-[10px] text-slate-500 mt-0.5">Routing Filter chain applied to outgoing routes</p>
                 </div>
               </div>
 
+              <div class="grid grid-cols-3 items-center gap-3">
+                <label class="text-slate-400 font-medium">Output Selection Policy</label>
+                <div class="col-span-2">
+                  <select id="modal-bgp-peer-out-selection-policy" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
+                    <option value="disabled">disabled</option>
+                    <option value="passive">passive</option>
+                    <option value="force">force</option>
+                  </select>
+                </div>
+              </div>
+
               <div class="grid grid-cols-3 items-center gap-3">
                 <label class="text-slate-400 font-medium">Output Network</label>
                 <div class="col-span-2">