Config grew over years. Multiple engineers added policies. Nobody documented communities. New engineer joins, asks “what does community 65000:999 mean?” — silence. “Don’t touch it — it works.”
This is how routing policies become unmaintainable. Junos provides powerful policy tools, but power without structure creates chaos.
Junos Policy Mental Model
Terms Evaluate Top to Bottom
policy-statement EXAMPLE { term FIRST { from { ... } then accept; } term SECOND { from { ... } then reject; } term DEFAULT { then reject; # Explicit default }}First matching term wins. If FIRST matches, SECOND never evaluates. No match in any term? Implicit accept — the silent danger.
The Implicit Accept Problem
# Dangerous policy - implicit accept at endpolicy-statement FILTER-ROUTES { term BLOCK-BOGONS { from { route-filter 10.0.0.0/8 orlonger; } then reject; } # No default term = everything else ACCEPTED}Traffic you didn’t explicitly handle gets accepted. Always add explicit default:
# Safe policy - explicit defaultpolicy-statement FILTER-ROUTES { term BLOCK-BOGONS { from { route-filter 10.0.0.0/8 orlonger; } then reject; } term DEFAULT-DENY { then reject; }}Accept vs Next Policy vs Next Term
then accept; # Accept route, stop processing THIS policythen reject; # Reject route, stop processing THIS policythen next policy; # Continue to next policy in chainthen next term; # Continue to next term in THIS policyMultiple policies can be chained:
set protocols bgp group PEERS import [ POLICY-1 POLICY-2 POLICY-3 ]# Evaluates POLICY-1, then POLICY-2, then POLICY-3# First explicit accept/reject winsPolicy Building Blocks
Prefix Lists
Named lists of prefixes. Reusable across policies.
# Define prefix-listset policy-options prefix-list INTERNAL-NETWORKS 10.0.0.0/8set policy-options prefix-list INTERNAL-NETWORKS 172.16.0.0/12set policy-options prefix-list INTERNAL-NETWORKS 192.168.0.0/16
# Use in policyset policy-options policy-statement ALLOW-INTERNAL term MATCH from prefix-list INTERNAL-NETWORKSset policy-options policy-statement ALLOW-INTERNAL term MATCH then acceptset policy-options policy-statement ALLOW-INTERNAL term DEFAULT then rejectRoute Filters
More granular than prefix-lists. Match exact prefixes or ranges.
# Exact matchroute-filter 10.0.0.0/24 exact;
# Match this and longer (subnets)route-filter 10.0.0.0/16 orlonger;
# Match longer only (not the /16 itself)route-filter 10.0.0.0/16 longer;
# Match rangeroute-filter 10.0.0.0/16 prefix-length-range /24-/28;
# Match up to a lengthroute-filter 10.0.0.0/8 upto /24;Practical example:
policy-statement CUSTOMER-ROUTES { term ACCEPT-ALLOCATED { from { route-filter 203.0.113.0/24 orlonger; # Customer's allocation route-filter 198.51.100.0/24 orlonger; # Second allocation } then accept; } term REJECT-REST { then reject; }}AS Path Filters
Match routes by AS path patterns.
# Define AS path regexset policy-options as-path ORIGIN-65001 ".* 65001"set policy-options as-path DIRECT-PEER "^65001$"set policy-options as-path TRANSIT ".* 65001 .*"
# Use in policypolicy-statement PREFER-DIRECT { term DIRECT { from as-path DIRECT-PEER; then { local-preference 150; accept; } } term TRANSIT { from as-path TRANSIT; then { local-preference 100; accept; } }}AS path regex patterns:
^— start of path$— end of path.— any single AS.*— zero or more ASes[0-9]+— one or more digits (any AS number)
Communities
Tags attached to routes. The glue for policy communication.
# Define communitiesset policy-options community CUSTOMER-ROUTES members 65000:100set policy-options community NO-EXPORT members no-exportset policy-options community BLACKHOLE members 65000:666
# Match communitypolicy-statement CUSTOMER-IMPORT { term TAGGED { from community CUSTOMER-ROUTES; then accept; }}
# Set communitypolicy-statement TAG-OUTBOUND { term ADD-TAG { then { community add CUSTOMER-ROUTES; accept; } }}Community Design That Scales
Naming Convention
Without documentation, 65000:100 means nothing. Create a system:
# Pattern: ASN:TYPE+VALUE# Types:# 1xx = Origin (where route came from)# 2xx = Region# 3xx = Customer type# 4xx = Traffic engineering# 666 = Blackhole
# Examples:set policy-options community ORIGIN-CUSTOMER members 65000:100set policy-options community ORIGIN-PEER members 65000:101set policy-options community ORIGIN-TRANSIT members 65000:102
set policy-options community REGION-US-EAST members 65000:201set policy-options community REGION-US-WEST members 65000:202set policy-options community REGION-EU members 65000:203
set policy-options community TYPE-ENTERPRISE members 65000:301set policy-options community TYPE-RESIDENTIAL members 65000:302
set policy-options community TE-BACKUP-ONLY members 65000:401set policy-options community TE-PRIMARY members 65000:402
set policy-options community BLACKHOLE members 65000:666Document in Config
Junos supports description on most objects. Use it:
set policy-options community ORIGIN-CUSTOMER members 65000:100annotate policy-options community ORIGIN-CUSTOMER "Routes learned from direct customers"
set policy-options community BLACKHOLE members 65000:666annotate policy-options community BLACKHOLE "Trigger RTBH - null route this prefix"Community Groups
Group related communities for easier matching:
# Define community groupset policy-options community ALL-ORIGINS members "65000:10[0-9]"set policy-options community ALL-REGIONS members "65000:2[0-9][0-9]"
# Match any origin communitypolicy-statement CHECK-ORIGIN { term HAS-ORIGIN { from community ALL-ORIGINS; then accept; } term MISSING-ORIGIN { then reject; # Reject routes without origin tag }}Safe Defaults
Bogon Filtering
Always filter RFC1918, documentation prefixes, and other bogons:
# Bogon prefix-list (base prefixes)set policy-options prefix-list BOGONS 0.0.0.0/8set policy-options prefix-list BOGONS 10.0.0.0/8set policy-options prefix-list BOGONS 100.64.0.0/10set policy-options prefix-list BOGONS 127.0.0.0/8set policy-options prefix-list BOGONS 169.254.0.0/16set policy-options prefix-list BOGONS 172.16.0.0/12set policy-options prefix-list BOGONS 192.0.0.0/24set policy-options prefix-list BOGONS 192.0.2.0/24set policy-options prefix-list BOGONS 192.168.0.0/16set policy-options prefix-list BOGONS 198.18.0.0/15set policy-options prefix-list BOGONS 198.51.100.0/24set policy-options prefix-list BOGONS 203.0.113.0/24set policy-options prefix-list BOGONS 224.0.0.0/4set policy-options prefix-list BOGONS 240.0.0.0/4
# Apply with orlonger match (catches subnets too)policy-statement REJECT-BOGONS { term BOGONS { from { prefix-list-filter BOGONS orlonger; } then reject; }}Max Prefix Protection
Limit prefixes accepted from peers:
# Limit with teardownset protocols bgp group CUSTOMERS neighbor 192.0.2.1 family inet unicast prefix-limit maximum 1000set protocols bgp group CUSTOMERS neighbor 192.0.2.1 family inet unicast prefix-limit teardown 80 idle-timeout 30
# Teardown at 80% (800 prefixes), wait 30 minutes before retryReject Unless Explicitly Allowed
Default-deny at BGP group level:
# Import policy for customerpolicy-statement CUSTOMER-192-0-2-1-IMPORT { term ACCEPT-ANNOUNCED { from { prefix-list CUSTOMER-192-0-2-1-PREFIXES; } then { community add ORIGIN-CUSTOMER; accept; } } term REJECT-ALL { then reject; }}
# Customer's allowed prefixesset policy-options prefix-list CUSTOMER-192-0-2-1-PREFIXES 198.51.100.0/24AS Path Sanity
Reject private ASNs and your own ASN from external peers:
# Private ASN range (64512-65534)set policy-options as-path PRIVATE-ASN ".* (6451[2-9]|645[2-9][0-9]|6[5-9][0-4][0-9]{2}|655[0-2][0-9]|6553[0-4]) .*"
# Simpler alternative: match specific private ASNs you might seeset policy-options as-path PRIVATE-ASN-SIMPLE "64[5-9][0-9]{2}|65[0-4][0-9]{2}|655[0-3][0-4]"
# Own ASN in path (shouldn't happen from external)set policy-options as-path OWN-ASN ".* 65000 .*"
policy-statement SANITY-CHECK { term REJECT-PRIVATE-ASN { from as-path PRIVATE-ASN; then reject; } term REJECT-OWN-ASN { from as-path OWN-ASN; then reject; }}Note: AS path regex for full private range coverage is complex. Many operators maintain external prefix/AS-path lists (e.g., from Team Cymru or RIPE) rather than hand-crafted regex.
Policy Structure Patterns
Layered Import Policy
Build policies in layers for maintainability:
# Layer 1: Sanity checks (apply to all)policy-statement IMPORT-SANITY { term REJECT-BOGONS { from prefix-list BOGONS; then reject; } term REJECT-TOO-LONG { from route-filter 0.0.0.0/0 prefix-length-range /25-/32; then reject; } term REJECT-DEFAULT { from route-filter 0.0.0.0/0 exact; then reject; } term CONTINUE { then next policy; }}
# Layer 2: Peer-specific acceptancepolicy-statement IMPORT-PEER-65001 { term ACCEPT-PREFIXES { from prefix-list PEER-65001-PREFIXES; then { community add ORIGIN-PEER; local-preference 100; accept; } } term REJECT-REST { then reject; }}
# Apply bothset protocols bgp group PEERS neighbor 192.0.2.1 import [ IMPORT-SANITY IMPORT-PEER-65001 ]Export Policy Template
Consistent export structure:
policy-statement EXPORT-TO-PEERS { term EXPORT-CUSTOMERS { from community ORIGIN-CUSTOMER; then { community delete ALL-INTERNAL; # Strip internal communities accept; } } term EXPORT-OWN { from { protocol direct; prefix-list OWN-PREFIXES; } then accept; } term REJECT-REST { then reject; }}Debugging Policies
Test Policy Match
# Test which policy term matches a routetest policy POLICY-NAME 192.0.2.0/24
# Output shows:# Route 192.0.2.0/24# Term: ACCEPT-CUSTOMERS# Action: acceptShow Received vs Active
# What peer is sending (before import policy)show route receive-protocol bgp 192.0.2.1
# What we accepted (after import policy)show route protocol bgp neighbor 192.0.2.1
# Compare to find filtered routesHidden Routes
Routes filtered by policy become “hidden”:
# Show hidden routesshow route hidden
# Why is it hidden?show route 192.0.2.0/24 hidden extensivePolicy Decision Flow
┌─────────────────────────────────────────────────────────┐│ Import Policy Chain │├─────────────────────────────────────────────────────────┤│ ││ Policy 1 Policy 2 Policy 3 ││ ┌─────────┐ ┌─────────┐ ┌─────────┐ ││ │ Term A │─no─→│ Term A │─no─→│ Term A │ ││ └────┬────┘ └────┬────┘ └────┬────┘ ││ │yes │yes │yes ││ ↓ ↓ ↓ ││ [accept/reject] [accept/reject] [accept/reject] ││ ││ If no match in any term of any policy: ││ → IMPLICIT ACCEPT (danger!) ││ │└─────────────────────────────────────────────────────────┘The Lesson
Junos routing policy is powerful but requires discipline:
- Always explicit default — never rely on implicit accept
- Name everything meaningfully — communities, prefix-lists, policies
- Document in config — use
annotateliberally - Layer your policies — sanity checks separate from peer-specific logic
- Test before commit — use
test policycommand
A well-structured policy config is:
- Readable by new engineers
- Modifiable without fear
- Auditable for compliance
The goal isn’t clever regex — it’s maintainability. If you can’t explain what 65000:247 means without checking documentation, your community scheme needs work.
Policy-statement is an engineering system. Treat it like code: structured, documented, tested.