RFC 2622

Routing Policy Specification Language (RPSL)

πŸ“… published June 1999, from ietf

RFC 2622: Routing Policy Specification Language (RPSL) πŸ’–

Network Working Group, June 1999 πŸ—“οΈ Category: Standards Track ✨

Eight authors wrote this one, each with their own affiliation 🫢:

  • C. Alaettinoglu, USC/Information Sciences Institute
  • C. Villamizar, Avici Systems
  • E. Gerich, At Home Network
  • D. Kessens, Qwest Communications
  • D. Meyer, University of Oregon
  • T. Bates, Cisco Systems
  • D. Karrenberg, RIPE NCC
  • M. Terpstra, Bay Networks

It obsoletes RFC 2280 πŸ₯€, and its header doesn't list any RFC it updates. Okay, on to the good stuff πŸŽ€


Status of this Memo πŸ“œ

okay so this doc specifies an Internet standards track protocol for the whole Internet community 🌐, and it literally asks for discussion and suggestions for improvements πŸ’¬. For the standardization state and status of this protocol, check the "Internet Official Protocol Standards" (STD 1) 🧚. Distribution of this memo is unlimited, so share it with your bestie 🫢

Copyright (C) The Internet Society (1999). All Rights Reserved.

Abstract πŸ¦‹

RPSL lets a network operator specify routing policies at various levels in the Internet hierarchy, for example at the Autonomous System (AS) level πŸ’œ. Policies can also be detailed enough that low level router configurations can be generated from them πŸ”₯. And RPSL is extensible: new routing protocols and new protocol features can be introduced at any time πŸ’…

Table of Contents πŸ“

   1 Introduction                                                      3
   2 RPSL Names, Reserved Words, and Representation                    4
   3 Contact Information                                               7
     3.1 mntner Class . . . . . . . . . . . . . . . . . . . . . . . .  7
     3.2 person Class . . . . . . . . . . . . . . . . . . . . . . . . 10
     3.3 role Class . . . . . . . . . . . . . . . . . . . . . . . . . 11
   4 route Class                                                      12
   5 Set Classes                                                      13
     5.1 as-set Class . . . . . . . . . . . . . . . . . . . . . . . . 14
     5.2 route-set Class. . . . . . . . . . . . . . . . . . . . . . . 15
     5.3 Predefined Set Objects . . . . . . . . . . . . . . . . . . . 17
     5.4 Filters and filter-set Class . . . . . . . . . . . . . . . . 17
     5.5 rtr-set Class. . . . . . . . . . . . . . . . . . . . . . . . 22
     5.6 Peerings and peering-set Class . . . . . . . . . . . . . . . 24
   6 aut-num Class                                                    27
     6.1 import Attribute:  Import Policy Specification . . . . . . . 27
       6.1.1 Action Specification . . . . . . . . . . . . . . . . . . 28
     6.2 export Attribute:  Export Policy Specification . . . . . . . 29
      6.3 Other Routing Protocols, Multi-Protocol Routing Protocols,
       and Injecting Routes Between Protocols . . . . . . . . . . . . 29
     6.4 Ambiguity Resolution . . . . . . . . . . . . . . . . . . . . 31
     6.5 default Attribute: Default Policy Specification  . . . . . . 33
     6.6 Structured Policy Specification. . . . . . . . . . . . . . . 33
   7 dictionary Class                                                 37
     7.1 Initial RPSL Dictionary and Example Policy Actions and
       Filters. . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
   8 Advanced route Class                                             45
     8.1 Specifying Aggregate Routes. . . . . . . . . . . . . . . . . 45
       8.1.1Interaction with policies in aut-num class. . . . . . . . 49
       8.1.2Ambiguity resolution with overlapping aggregates. . . . . 50
     8.2 Specifying Static Routes . . . . . . . . . . . . . . . . . . 52
   9 inet-rtr Class                                                   52
   10 Extending RPSL                                                  54
     10.1 Extensions by changing the dictionary class . . . . . . . . 54
     10.2 Extensions by adding new attributes to existing classes . . 55
     10.3 Extensions by adding new classes  . . . . . . . . . . . . . 55
     10.4 Extensions by changing the syntax of existing RPSL
        attributes. . . . . . . . . . . . . . . . . . . . . . . . . . 55
   11 Security Considerations                                         56
   12 Acknowledgements                                                56
   References                                                         56
   A Routing Registry Sites                                           59
   B Grammar Rules                                                    59
   C Changes from RFC 2280                                            67
   D Authors' Addresses                                               68
   Full Copyright Statement                                           69

1 Introduction ✨

okay so this memo is the reference doc for the Routing Policy Specification Language (RPSL) πŸ“–. RPSL lets a network operator specify routing policies at various levels in the Internet hierarchy, for example at the Autonomous System (AS) level. Policies can be detailed enough that low level router configurations can be generated from them. RPSL is extensible, so new routing protocols and new protocol features can be introduced at any time πŸ¦‹

RPSL is a replacement for the current Internet policy specification language known as RIPE-181 [6] or RFC-1786 [7]. RIPE-81 [8] was the first language deployed in the Internet for specifying routing policies, and it was later replaced by RIPE-181 [6]. Using RIPE-181 in the real world made it clear that certain policies cannot be specified, so a more enhanced and generalized language was needed πŸ™ˆ. RPSL fixes RIPE-181's limitations πŸ’…

RPSL was designed so that a view of the global routing policy can live in a single cooperatively maintained distributed database, which improves the integrity of the Internet's routing 🌸. RPSL is not designed to be a router configuration language πŸ™…β€β™€οΈ. Instead, router configurations can be generated from the description of the policy for one autonomous system (aut-num class) combined with the description of a router (inet-rtr class). The inet-rtr class mainly provides the router ID, the autonomous system number of the router, and the interfaces and peers of the router. You then combine that with a global database that maps AS sets to ASes (as-set class), and origin ASes and route sets to route prefixes (route and route-set classes). A well-populated RPSL database helps with goals like router configurations that protect against accidental (or malicious) distribution of inaccurate routing information, verification of the Internet's routing, and aggregation boundaries beyond a single AS πŸ‘‘

RPSL is object oriented, which means objects contain pieces of policy and administrative information 🧚. These objects are registered in the Internet Routing Registry (IRR) by the authorized organizations. The registration process is beyond the scope of this document, so check [1, 17, 4] for more details on the IRR πŸ“š

In the following sections, we present the classes used to define various policy and administrative objects πŸŽ€. The "mntner" class defines entities authorized to add, delete and modify a set of objects. The "person" and "role" classes describe technical and administrative contact personnel. Autonomous systems (ASes) are specified using the "aut-num" class, and routes are specified using the "route" class. Sets of objects can be defined using the "as-set", "route-set", "filter-set", "peering-set", and "rtr-set" classes. The "dictionary" class provides the extensibility of the language, and the "inet-rtr" class is used to specify routers. Many of these classes were originally defined in earlier documents [6, 13, 16, 12, 5], and they've all been enhanced 🌈

This document is self-contained, but the reader is encouraged to read RIPE-181 [7] and the associated documents [13, 16, 12, 5]. They give significant background on the motivation and underlying principles behind RIPE-181 and, as a result, RPSL 🫢 For a tutorial on RPSL, read the RPSL applications document [4] πŸ’œ


2. RPSL Names, Reserved Words, and Representation ✨

okay so every class has a bunch of attributes πŸŽ€, which are basically little boxes that hold info about the objects in that class. Attributes can be mandatory (they have to be defined for every object of the class) or optional (you can skip them πŸ’…). Attributes can also be single or multiple valued. Each object gets uniquely identified by a set of attributes called the class "key" πŸ”‘.

Every attribute value has a type. Here are the big ones 🌸. RPSL is case insensitive πŸ™ˆ, and only ASCII characters are allowed πŸ’€.

<object-name>: lots of RPSL objects have a name πŸ“. An object name is made of letters, digits, the underscore "_", and the hyphen "-". The first character has to be a letter, and the last character has to be a letter or a digit. These words are reserved by RPSL, so you can't use them as names:

any as-any rs-any peeras
and or not
atomic from to at action accept announce except refine
networks into inbound outbound

Some prefixes are reserved for certain object types, so the name has to match the vibe 🧚:

  • Names starting with as- are reserved for as set names.
  • Names starting with rs- are reserved for route set names.
  • Names starting with rtrs- are reserved for router set names.
  • Names starting with fltr- are reserved for filter set names.
  • Names starting with prng- are reserved for peering set names.

<as-number>: an AS number x is written as ASx. So AS 226 becomes AS226 ✨.

<ipv4-address>: an IPv4 address is four integers from 0 to 255, separated by dots ".". For example, 128.9.128.5 is a valid IPv4 address πŸ¦‹. For the rest of this document, we may refer to IPv4 addresses as IP addresses, so don't be confused if you see that 😭.

<address-prefix>: an address prefix is an IPv4 address, then a slash /, then an integer from 0 to 32. These are valid:

128.9.128.5/32, 128.9.0.0/16, 0.0.0.0/0

These are invalid, bestie, because 0 and 128.9 are not strings with four integers in them πŸ™…β€β™€οΈ:

0/0, 128.9/16

<address-prefix-range>: an address prefix range is an address prefix followed by an optional range operator. The range operators are below, and they're honestly so iconic πŸ‘‘.

  • ^- is the exclusive more specifics operator. It means all the more specifics of the address prefix, but NOT the address prefix itself. For example, 128.9.0.0/16^- contains all the more specifics of 128.9.0.0/16 excluding 128.9.0.0/16.
  • ^+ is the inclusive more specifics operator. It means all the more specifics of the address prefix, INCLUDING the address prefix itself. For example, 5.0.0.0/8^+ contains all the more specifics of 5.0.0.0/8 including 5.0.0.0/8.
  • ^n, where n is an integer, means all the length n specifics of the address prefix. For example, 30.0.0.0/8^16 contains all the more specifics of 30.0.0.0/8 that are length 16, such as 30.9.0.0/16.
  • ^n-m, where n and m are integers, means all the length n to length m specifics of the address prefix. For example, 30.0.0.0/8^24-32 contains all the more specifics of 30.0.0.0/8 that are length 24 to 32, such as 30.9.9.96/28.

Range operators can also go on address prefix sets. When they do, they distribute over every member of the set. For example, if rs-foo is a route-set (we'll get there later 🫢), then rs-foo^+ contains all the inclusive more specifics of every prefix in rs-foo.

Now for the rules, and yes, they matter πŸ€“. It is an error to follow a range operator with another range operator. For example, 30.0.0.0/8^24-28^+ is an error 😭. However, a range operator CAN be applied to an address prefix set that already has address prefix ranges in it. For example, {30.0.0.0/8^24-28}^27-30 is totally fine πŸ’–. In that case, the outer operator ^n-m distributes over the inner operator ^k-l and turns into ^max(n,k)-m, but only if m is greater than or equal to max(n,k). Otherwise, that prefix gets deleted from the set πŸ—‘οΈ.

A few equivalences to memorise, periodt πŸ’…:

  • ^n is equivalent to ^n-n.
  • prefix/l^+ is equivalent to prefix/l^l-32.
  • prefix/l^- is equivalent to prefix/l^(l+1)-32.
  • {prefix/l^n-m}^+ is equivalent to {prefix/l^n-32}.
  • {prefix/l^n-m}^- is equivalent to {prefix/l^(n+1)-32}.

And here are some examples, because they're so satisfying to look at ✨:

{128.9.0.0/16^+}^-     == {128.9.0.0/16^-}
{128.9.0.0/16^-}^+     == {128.9.0.0/16^-}
{128.9.0.0/16^17}^24   == {128.9.0.0/16^24}
{128.9.0.0/16^20-24}^26-28 == {128.9.0.0/16^26-28}
{128.9.0.0/16^20-24}^22-28 == {128.9.0.0/16^22-28}
{128.9.0.0/16^20-24}^18-28 == {128.9.0.0/16^20-28}
{128.9.0.0/16^20-24}^18-22 == {128.9.0.0/16^20-22}
{128.9.0.0/16^20-24}^18-19 == {}

<date>: a date is an eight digit integer in the form YYYYMMDD. YYYY is the year, MM is the month (01 through 12), and DD is the day of the month (01 through 31). All dates are in UTC unless it says otherwise 🌈. For example, June 24, 1996 is 19960624 πŸŽ‚.

<email-address>: this one is as described in RFC-822 [10] πŸ“§.

<dns-name>: this one is as described in RFC-1034 [17] 🌐.

<nic-handle>: a NIC handle is a uniquely assigned identifier word. Routing, address allocation, and other registries use it to refer to contact information without any confusion 🫢. The person and role classes map NIC handles to actual person names and contact info.

<free-form>: a sequence of ASCII characters. That's it, that's the whole vibe πŸ’….

<X-name>: a name of an object of type X. So <mntner-name> is a name of a mntner object πŸ”₯.

<registry-name>: a name of an IRR registry πŸ¦‹. The routing registries are listed in Appendix A.

An attribute value can also be a list of one of these types 🌸. You make a list by separating the members with commas ",". For example, "AS1, AS2, AS3, AS4" is a list of AS numbers. Now, here's the tea β˜•: being list valued and being multiple valued are NOT the same thing 😭. A multiple valued attribute has more than one value, and each of those values may or may not be a list. A single valued attribute, on the other hand, can still have a list value.

An RPSL object is written as a list of attribute-value pairs πŸ“. Each pair goes on its own line. The attribute name starts at column 0, followed by a colon ":", and then the attribute's value. The attribute with the same name as the object's class should be written first πŸ₯°. The object's representation ends when you hit a blank line. An attribute's value can be split across multiple lines by starting each continuation line with a space, a tab, or a plus ('+') character. The plus character for line continuation even lets attribute values contain blank lines 🀯. You may add more spaces after the continuation character to make things easier to read. The order of the attribute-value pairs matters, bestie ⚠️.

An object's description can contain comments πŸ’¬. A comment can go anywhere in the object's definition. It starts at the first "#" character on a line and runs until the end of that line. White space is fine to use for readability ✨.

An integer can be written in three ways πŸ”’:

  1. The C programming language notation, like 1 or 12345.
  2. A sequence of four 1-octet integers (each from 0 to 255) separated by dots ".", like 1.1.1.1 or 255.255.0.0. In this case, the 4-octet integer is formed by joining these 1-octet integers, most significant to least significant.
  3. A sequence of two 2-octet integers (each from 0 to 65535) separated by colons ":", like 3561:70 or 3582:10. In this case, the 4-octet integer is formed by joining these 2-octet integers, most significant to least significant.

3. Contact Information

the mntner, person, and role classes, plus the admin-c, tech-c, mnt-by, changed, and source attributes of all classes, are all about contact information πŸ“‡. The mntner class also holds the authentication information needed to create, delete, and update other objects πŸ”. These classes don't specify routing policies, and each registry may have different or extra requirements for them 🫠. We're showing the common denominator here, for completeness, based on the RIPE database implementation [16] πŸ¦‹. Please check your routing registry for the latest spec of these classes and attributes, because it can change πŸ’…. The "Routing Policy System Security" document [20] goes deeper into the authentication and authorization model πŸ”₯.

3.1 mntner Class

Okay so the mntner class is basically the bouncer πŸ’… It holds the authentication info needed to create, delete, and update RPSL objects. A provider has to make a mntner object before they can create any RPSL objects at all πŸ” The attributes are shown in Figure 1, and this class was first described in [13].

The mntner attribute is mandatory and it's the class key πŸ”‘ Its value is an RPSL name. The auth attribute tells you which scheme will be used to identify and authenticate update requests from this maintainer. Its syntax looks like this:

auth: <scheme-id> <auth-info>

E.g.

auth: NONE
Attribute  Value                   Type
mntner     <object-name>           mandatory, single-valued, class key
descr      <free-form>             mandatory, single-valued
auth       see description in text mandatory, multi-valued
upd-to     <email-address>         mandatory, multi-valued
mnt-nfy    <email-address>         optional, multi-valued
tech-c     <nic-handle>            mandatory, multi-valued
admin-c    <nic-handle>            optional, multi-valued
remarks    <free-form>             optional, multi-valued
notify     <email-address>         optional, multi-valued
mnt-by     list of <mntner-name>   mandatory, multi-valued
changed    <email-address> <date>  mandatory, multi-valued
source     <registry-name>         mandatory, single-valued

Figure 1: mntner Class Attributes

auth: CRYPT-PW dhjsdfhruewf
auth: MAIL-FROM .*@ripe\.net

The <scheme-id>'s that are currently defined are NONE, MAIL-FROM, PGP-KEY and CRYPT-PW ✨ The <auth-info> is the extra info a particular scheme needs. For MAIL-FROM it's a regular expression that matches valid email addresses πŸ“§ For CRYPT-PW it's a password in UNIX crypt format πŸ—οΈ And for PGP-KEY it's a pointer to a key-certif object [22] that holds the user's PGP public key. If you specify multiple auth attributes, an update request that satisfies any one of them is authenticated to be from the maintainer. Main character energy, honestly πŸ‘‘

The upd-to attribute is an email address. If someone makes an unauthorized update attempt on an object this maintainer looks after, an email message gets sent to that address 🚨 The mnt-nfy attribute is also an email address, and a notification message gets forwarded to it whenever an object maintained by this maintainer is added, changed or deleted πŸ’Œ

The descr attribute is a short, free-form description of the object πŸ“ The tech-c attribute is a technical contact NIC handle, aka the person you contact for technical problems like misconfiguration πŸ› οΈ The admin-c attribute is an administrative contact NIC handle. The remarks attribute is free text for explanations or clarifications. The notify attribute is an email address where notifications about changes to this object should be sent. The mnt-by attribute is a list of mntner object names, and changes to this object are governed by any of the maintainer objects referenced there πŸ¦‹ The changed attribute records who last changed the object and when. Its syntax looks like this:

changed: <email-address> <YYYYMMDD>

E.g.

changed: johndoe@terabit-labs.nn 19900401

The <email-address> identifies the person who made the last change, and <YYYYMMDD> is the date of that change πŸ“… The source attribute specifies the registry where the object is registered 🌸 Figure 2 shows an example mntner object, and this one uses UNIX crypt format password authentication.

mntner:      RIPE-NCC-MNT
descr:       RIPE-NCC Maintainer
admin-c:     DK58
tech-c:      OPS4-RIPE
upd-to:      ops@ripe.net
mnt-nfy:     ops-fyi@ripe.net
auth:        CRYPT-PW lz1A7/JnfkTtI
mnt-by:      RIPE-NCC-MNT
changed:     ripe-dbm@ripe.net 19970820
source:      RIPE

Figure 2: An example mntner object.

The descr, tech-c, admin-c, remarks, notify, mnt-by, changed and source attributes show up in all RPSL classes 🌈 Their syntax, semantics, and whether they're mandatory, optional, multi-valued, or single-valued are the same across every RPSL class. The only exception is admin-c, which is mandatory for the aut-num class. We won't go over them again in the other sections, so consider this your one-and-only intro πŸ’–

3.2 person Class

The person class describes information about people πŸ§‘ It doesn't describe routing policy, but we still cover it briefly here because lots of policy objects reference person objects. It was first described in [15].

The attributes of the person class are shown in Figure 3. The person attribute is the person's full name. The phone and fax-no attributes follow this syntax:

   phone: +<country-code> <city> <subscriber> [ext. <extension>]

E.g.:

   phone: +31 20 12334676
Attribute  Value                   Type
person     <free-form>             mandatory, single-valued
nic-hdl    <nic-handle>            mandatory, single-valued, class key
address    <free-form>             mandatory, multi-valued
phone      see description in text mandatory, multi-valued
fax-no     same as phone           optional, multi-valued
e-mail     <email-address>         mandatory, multi-valued

Figure 3: person Class Attributes

phone: +44 123 987654 ext. 4711

Figure 4 shows an example person object πŸŽ€

person:      Daniel Karrenberg
address:     RIPE Network Coordination Centre (NCC)
address:     Singel 258
address:     NL-1016 AB  Amsterdam
address:     Netherlands
phone:       +31 20 535 4444
fax-no:      +31 20 535 4445
e-mail:      Daniel.Karrenberg@ripe.net
nic-hdl:     DK58
changed:     Daniel.Karrenberg@ripe.net 19970616
source:      RIPE

Figure 4: An example person object.

3.3 role Class

The role class is kind of like the person object, except instead of describing a human being, it describes a role that one or more humans perform 🎭 Think help desks, network monitoring centers, system administrators, and so on. Role objects are super useful because the person doing a role often changes, but the role itself sticks around 🧚

The attributes of the role class are shown in Figure 5. The nic-hdl attributes of the person and role classes share the same name space, so they can't clash πŸ’… The trouble attribute of a role object can hold extra contact info for when a problem comes up in any object that references this role object πŸ†˜ Figure 6 shows an example role object.

Attribute  Value                    Type
role       <free-form>              mandatory, single-valued
nic-hdl    <nic-handle>             mandatory, single-valued,
                                    class key
trouble    <free-form>              optional, multi-valued
address    <free-form>              mandatory, multi-valued
phone      see description in text  mandatory, multi-valued
fax-no     same as phone            optional, multi-valued
e-mail     <email-address>          mandatory, multi-valued

Figure 5: role Class Attributes

role:        RIPE NCC Operations
trouble:
address:     Singel 258
address:     1016 AB Amsterdam
address:     The Netherlands
phone:       +31 20 535 4444
fax-no:      +31 20 545 4445
e-mail:      ops@ripe.net
admin-c:     CO19-RIPE
tech-c:      RW488-RIPE
tech-c:      JLSD1-RIPE
nic-hdl:     OPS4-RIPE
notify:      ops@ripe.net
changed:     roderik@ripe.net 19970926
source:      RIPE

Figure 6: An example role object.

4 route Class ✨

Each interAS route (also called an interdomain route) that an AS originates gets specified using a route object 🌐 The attributes of the route class are shown in Figure 7. The route attribute is the address prefix of the route, and the origin attribute is the AS number of the AS that originates the route into the interAS routing system. The route and origin pair is the class key, so that combo is the unique ID πŸ”‘

Figure 8 shows examples of four route objects. We skip contact attributes like admin-c and tech-c to keep it short πŸ™ˆ Notice that the last two route objects have the same address prefix, 128.8.0.0/16. They're still different route objects because different ASes originate them, meaning they have different keys. Iconic, right? πŸ¦‹

Attribute     Value                      Type
route         <address-prefix>           mandatory, single-valued,
                                         class key
origin        <as-number>                mandatory, single-valued,
                                         class key
member-of     list of <route-set-names>  optional, multi-valued
              see Section 5
inject        see Section 8              optional, multi-valued
components    see Section 8              optional, single-valued
aggr-bndry    see Section 8              optional, single-valued
aggr-mtd      see Section 8              optional, single-valued
export-comps  see Section 8              optional, single-valued
holes         see Section 8              optional, multi-valued

Figure 7: route Class Attributes

route: 128.9.0.0/16
origin: AS226

route: 128.99.0.0/16
origin: AS226

route: 128.8.0.0/16
origin: AS1

route: 128.8.0.0/16
origin: AS2

Figure 8: Route Objects

5 Set Classes 🧺

To write policies, it's often handy to define sets of objects πŸ“ For that we define the as-set, route-set, rtr-set, filter-set, and peering-set classes. These classes define a named set. The members of a set can be listed directly in the set's definition, pulled in indirectly by having member objects refer to the set's name, or both. Mix and match, bestie πŸ’œ

A set's name is an rpsl word with some rules: every as-set name starts with the prefix "as-", every route-set name starts with "rs-", every rtr-set name starts with "rtrs-", every filter-set name starts with "fltr-", and every peering-set name starts with "prng-". For example, as-foo is a valid as-set name βœ…

Set names can also be hierarchical πŸͺœ A hierarchical set name is a sequence of set names and AS numbers separated by colons ":". At least one component of that name has to be an actual set name, meaning it starts with one of the prefixes above. All the set name components of a hierarchical name have to be the same type. For example, these names are valid: AS1:AS-CUSTOMERS, AS1:RS-EXPORT:AS2, RS-EXCEPTIONS:RS-BOGUS.

The whole point of a hierarchical set name is to split up the set name space so that the maintainers of set X1 control everything underneath it, i.e. X1:...:Xn-1 🏰 So a set object named X1:...:Xn-1:Xn can only be created by the maintainer of the object named X1:...:Xn-1. Put simply, only the maintainer of AS1 can create a set named AS1:AS-FOO, and only the maintainer of AS1:AS-FOO can create a set named AS1:AS-FOO:AS-BAR. For the details, check out the RPS Security Document [20] πŸ” It's giving organized, and I'm obsessed.

5.1 as-set Class ✨

Okay bestie, the as-set class is where we group ASes together πŸ¦‹ Its attributes are shown in Figure 9. The as-set attribute is the name of the set, and it's an RPSL name that starts with as- πŸ’– The members attribute is the list of who's in the squad: AS numbers or other as-set names 🫢

      Attribute    Value                    Type
      as-set       <object-name>            mandatory, single-valued,
                                            class key
      members      list of <as-numbers> or  optional, multi-valued
                   <as-set-names>
      mbrs-by-ref  list of <mntner-names>   optional, multi-valued


                     Figure 9:  as-set Class Attributes

Figure 10 shows two as-set objects, and they're giving main character energy πŸ‘‘ The set as-foo has two ASes, AS1 and AS2. The set as-bar has everything in as-foo plus AS3, so it contains AS1, AS2, AS3 🌸 And as-empty? Literally no members πŸ’€

 as-set: as-foo           as-set: as-bar                as-set: as-empty
 members: AS1, AS2        members: AS3, as-foo


                        Figure 10:  as-set objects.

Now the mbrs-by-ref attribute is a list of maintainer names or the keyword ANY πŸ”₯ If you use it, the AS set also picks up ASes whose aut-num objects are registered by one of those maintainers and whose member-of attribute points to this AS set. If the value is ANY, any AS object that refers to the AS set is a member of the set ✨ And if mbrs-by-ref is missing entirely? Only the ASes listed in members get in πŸ™ˆ

    as-set: as-foo
    members: AS1, AS2
    mbrs-by-ref: MNTR-ME

    aut-num: AS3                          aut-num: AS4
    member-of: as-foo                     member-of: as-foo
    mnt-by: MNTR-ME                       mnt-by: MNTR-OTHER


                           Figure 11:  as-set objects.

Figure 11 is the example that uses mbrs-by-ref, and it's so good 😭 The set as-foo contains AS1, AS2 and AS3. AS4 is NOT in as-foo even though its aut-num object references as-foo, because MNTR-OTHER isn't listed in as-foo's mbrs-by-ref attribute. Not me being picky about who gets in πŸ’…

5.2 route-set Class πŸ¦‹

The route-set class works just like the as-set class, except it's for routes 🌈 Its attributes are in Figure 12. The route-set attribute is the name, and it's an RPSL name that starts with rs-. The members attribute lists the members, and those can be address prefixes or other route-set names. Quick heads up: the route-set class is a set of route prefixes, NOT a set of RPSL route objects πŸ‘€

 Attribute    Value                              Type
 route-set    <object-name>                      mandatory,
                                                 single-valued,
                                                 class key
 members      list of <address-prefix-range> or  optional, multi-valued
              <route-set-name> or
              <route-set-name><range-operator>
 mbrs-by-ref  list of <mntner-names>             optional, multi-valued


                   Figure 12:  route-set Class Attributes

Figure 13 has some example route-set objects. The set rs-foo has two address prefixes, 128.9.0.0/16 and 128.9.0.0/24. The set rs-bar has everything in rs-foo plus 128.7.0.0/16 🧚

route-set: rs-foo
members: 128.9.0.0/16, 128.9.0.0/24

route-set: rs-bar
members: 128.7.0.0/16, rs-foo


                       Figure 13:  route-set Objects

An address prefix or a route-set name in a members attribute can optionally be followed by a range operator, and this part is seriously fun πŸ“ Check out this one:

route-set: rs-bar
members: 5.0.0.0/8^+, 30.0.0.0/8^24-32, rs-foo^+

That set contains all the more specifics of 5.0.0.0/8, including 5.0.0.0/8 itself. It also contains all the more specifics of 30.0.0.0/8 that are of length 24 to 32, like 30.9.9.96/28, plus all the more specifics of the address prefixes in route set rs-foo ✨

The mbrs-by-ref attribute works the same way as before: a list of maintainer names or the keyword ANY πŸ’œ If you use it, the route set also includes address prefixes whose route objects are registered by one of those maintainers and whose member-of attribute refers to this route set name. If the value is ANY, any route object that refers to the route set name is a member. If mbrs-by-ref is missing, only the address prefixes listed in members are members of the set 🫢

route-set: rs-foo
mbrs-by-ref: MNTR-ME, MNTR-YOU

route-set: rs-bar
members: 128.7.0.0/16
mbrs-by-ref: MNTR-YOU

route: 128.9.0.0/16
origin: AS1
member-of: rs-foo
mnt-by: MNTR-ME

route: 128.8.0.0/16
origin: AS2
member-of: rs-foo, rs-bar
mnt-by: MNTR-YOU


                       Figure 14:  route-set objects.

Figure 14 is the example that uses mbrs-by-ref, and I'm obsessed 😍 The set rs-foo has two address prefixes, 128.8.0.0/16 and 128.9.0.0/16, because both of those route objects point to rs-foo in their member-of attribute. The set rs-bar has 128.7.0.0/16 and 128.8.0.0/16. The route 128.7.0.0/16 is listed right in the members attribute of rs-bar, and the route object for 128.8.0.0/16 points to rs-bar in its member-of attribute πŸŽ€

Also, if an address prefix is listed in the members attribute of a route set, it IS a member of that route set. The route object for that prefix doesn't need a member-of attribute pointing back at the set name. The member-of attribute of the route class is just an extra way to add members indirectly πŸ’…

5.3 Predefined Set Objects 🌈

When a route set is expected (like in the members attribute of the route-set class), an AS number ASx defines the set of routes originated by ASx, and an as-set AS-X defines the set of routes originated by the ASes in AS-X πŸ¦‹ A route p counts as originated by ASx if there's a route object for p with ASx as the value of its origin attribute. For example, in Figure 15, the route set rs-special contains 128.9.0.0/16, the routes of AS1 and AS2, and the routes of the ASes in the AS set AS-FOO πŸ“

route-set: rs-special
members: 128.9.0.0/16, AS1, AS2, AS-FOO


          Figure 15:  Use of AS numbers and AS sets in route sets.

Last but not least, the set rs-any contains all routes registered in IRR, and the set as-any contains all ASes registered in IRR 🌸

5.4 Filters and filter-set Class ✨

okay bestie, filters are where the magic happens πŸ¦‹ A filter-set object defines a set of routes that its filter matches. The filter-set attribute holds the name, and it's an RPSL name that starts with "fltr-" πŸ’–

Figure 16 shows the attributes:

Attribute   Value         Type
filter-set  <object-name> mandatory, single-valued, class key
filter      <filter>      mandatory, single-valued

Figure 16: filter Class Attributes

And Figure 17 has two examples:

filter-set: fltr-foo
filter: { 5.0.0.0/8, 6.0.0.0/8 }

filter-set: fltr-bar
filter: (AS1 or fltr-foo) and <AS2>

Figure 17: filter-set objects.

The filter attribute is the set's policy filter πŸ”₯ A policy filter is a logical expression. When you apply it to a set of routes, it returns a subset of them, and we say the policy filter matches that subset. It can match routes using any BGP path attribute, like the destination address prefix (or NLRI), AS-path, or community attributes. so many options 😭

Policy filters can be composite using the operators AND, OR, and NOT. Here's what you can use to select a subset of routes:

ANY

The keyword ANY matches all routes 🌈

Address-Prefix Set

This is an explicit list of address prefixes wrapped in braces { and }. The policy filter matches the routes whose destination address-prefix is in the set. For example:

{ 0.0.0.0/0 }
{ 128.9.0.0/16, 128.8.0.0/16, 128.7.128.0/17, 5.0.0.0/8 }
{ }

An address prefix can optionally be followed by a range operator (i.e.

{ 5.0.0.0/8^+, 128.9.0.0/16^-, 30.0.0.0/8^16, 30.0.0.0/8^24-32 }

This contains all the more specifics of 5.0.0.0/8 including 5.0.0.0/8, all the more specifics of 128.9.0.0/16 excluding 128.9.0.0/16, all the more specifics of 30.0.0.0/8 which are of length 16 (like 30.9.0.0/16), and all the more specifics of 30.0.0.0/8 which are of length 24 to 32 (like 30.9.9.96/28) 🧚

Route Set Name

A route set name matches the set of routes that are members of the set. It can be the name of a route-set object, an AS number, or the name of an as-set object. AS numbers and as-set names implicitly define route sets, so check out Section 5.3 for more on that πŸ’… For example:

aut-num: AS1
import: from AS2 accept AS2
import: from AS2 accept AS-FOO
import: from AS2 accept RS-FOO

The keyword PeerAS can be used instead of the AS number of the peer AS. It's super useful when the peering is specified using an AS expression 🫢 For example:

as-set: AS-FOO
members: AS2, AS3

aut-num: AS1
import: from AS-FOO accept PeerAS

is the same as:

aut-num: AS1
import: from AS2 accept AS2
import: from AS3 accept AS3

A route set name can also be followed by one of the operators ^- or ^+. For example, { 5.0.0.0/8, 6.0.0.0/8 }^+ equals { 5.0.0.0/8^+, 6.0.0.0/8^+ }, and AS1^- equals all the exclusive more specifics of routes originated by AS1 πŸŽ€

AS Path Regular Expressions

An AS-path regular expression can be used as a policy filter by enclosing the expression in < and >. An AS-path policy filter matches the set of routes that traverse a sequence of ASes matched by the AS-path regular expression. A router can check this using the AS_PATH attribute in the Border Gateway Protocol [19], or the RD_PATH attribute in the Inter-Domain Routing Protocol [18] 🌸

AS-path regular expressions are POSIX compliant regular expressions over the alphabet of AS numbers. The regular expression constructs are:

  • ASN: where ASN is an AS number. It matches the AS-path that is of length 1 and contains that AS number (e.g. the AS-path regular expression AS1 matches the AS-path "1"). The keyword PeerAS can be used instead of the AS number of the peer AS.
  • AS-set: where AS-set is an AS set name. It matches the AS-paths that are matched by one of the ASes in the AS-set.
  • .: matches the AS-paths matched by any AS number.
  • [...]: is an AS number set. It matches the AS-paths matched by the AS numbers listed between the brackets. The AS numbers in the set are separated by white space characters. If a - is used between two AS numbers in this set, all AS numbers between the two are included in the set. If an as-set name is listed, all AS numbers in the as-set are included.
  • [^...]: is a complemented AS number set. It matches any AS-path that is not matched by the AS numbers in the set.
  • ^: matches the empty string at the beginning of an AS-path.
  • $: matches the empty string at the end of an AS-path.

Next up are the regular expression operators, listed in decreasing order of evaluation. These operators are left associative, meaning they're performed left to right πŸ‘‘

  • Unary postfix operators * + ? {m} {m,n} {m,}: For a regular expression A, A* matches zero or more occurrences of A, A+ matches one or more occurrences of A, A? matches zero or one occurrence of A, A{m} matches m occurrences of A, A{m,n} matches m to n occurrences of A, and A{m,} matches m or more occurrences of A. For example, [AS1 AS2]{2} matches AS1 AS1, AS1 AS2, AS2 AS1, and AS2 AS2.
  • Unary postfix operators ~* ~+ ~{m} ~{m,n} ~{m,}: These have similar functionality to the operators above, but all occurrences of the regular expression have to match the same pattern. For example, [AS1 AS2]~{2} matches AS1 AS1 and AS2 AS2, but it does not match AS1 AS2 or AS2 AS1 😭
  • Binary catenation operator: This is an implicit operator that exists between two regular expressions A and B when no other explicit operator is specified. The resulting expression A B matches an AS-path if A matches some prefix of the AS-path and B matches the rest of it.
  • Binary alternative (or) operator |: For regular expressions A and B, A | B matches any AS-path that is matched by A or B.

Parentheses can be used to override the default order of evaluation. White spaces can be used to increase readability ✨

Here are some examples of AS-path filters:

<AS3>
<^AS1>
<AS2$>
<^AS1 AS2 AS3$>
<^AS1 .* AS2$>

The first example matches any route whose AS-path contains AS3. The second matches routes whose AS-path starts with AS1. The third matches routes whose AS-path ends with AS2. The fourth matches routes whose AS-path is exactly "1 2 3". And the fifth matches routes whose AS-path starts with AS1 and ends in AS2, with any number of AS numbers in between 🌈

Composite Policy Filters

The following operators (in decreasing order of evaluation) can be used to form composite policy filters πŸ“

  • NOT: Given a policy filter x, NOT x matches the set of routes that are not matched by x. It's the negation of policy filter x.
  • AND: Given two policy filters x and y, x AND y matches the intersection of the routes matched by x and the routes matched by y.
  • OR: Given two policy filters x and y, x OR y matches the union of the routes matched by x and the routes matched by y.

An OR operator can be implicit, so x y is equivalent to x OR y πŸ’œ

E.g.

NOT {128.9.0.0/16, 128.8.0.0/16}
AS226 AS227 OR AS228
AS226 AND NOT {128.9.0.0/16}
AS226 AND {0.0.0.0/0^0-18}

The first example matches any route except 128.9.0.0/16 and 128.8.0.0/16. The second matches the routes of AS226, AS227, and AS228. The third matches the routes of AS226 except 128.9.0.0/16. The fourth matches the routes of AS226 whose length is not longer than 18 πŸ’…

Routing Policy Attributes

Policy filters can also use the values of other attributes for comparison. The attributes whose values can be used in policy filters are specified in the RPSL dictionary. Please refer to Section 7 for details. Here's an example using the BGP community attribute:

aut-num: AS1
export: to AS2 announce AS1 AND NOT community(NO_EXPORT)

Filters using the routing policy attributes defined in the dictionary are evaluated before the AND, OR, and NOT operators are evaluated πŸ¦‹

Filter Set Name

A filter set name matches the set of routes that are matched by its filter attribute. The filter attribute of a filter set can recursively refer to other filter set names. For example, in Figure 17, fltr-foo matches { 5.0.0.0/8, 6.0.0.0/8 }, and fltr-bar matches AS1'S routes or { 5.0.0.0/8, 6.0.0.0/8 } if their AS path contained AS2 πŸŽ€

5.5 rtr-set Class

The attributes of the rtr-set class are shown in Figure 18. The rtr-set attribute defines the name of the set. It's an RPSL name that starts with "rtrs-" 🌸 The members attribute lists the members of the set. It's a list of inet-rtr names, ipv4_addresses, or other rtr-set names 🫢

Attribute    Value                        Type
rtr-set      <object-name>                mandatory, single-valued,
                                          class key
members      list of <inet-rtr-names> or  optional, multi-valued
             <rtr-set-names>
             or <ipv4_addresses>
mbrs-by-ref  list of <mntner-names>       optional, multi-valued

Figure 18: rtr-set Class Attributes

Figure 19 presents two rtr-set objects. The set rtrs-foo contains two routers, rtr1.isp.net and rtr2.isp.net. The set rtrs-bar contains the members of the set rtrs-foo plus rtr3.isp.net, so it contains rtr1.isp.net, rtr2.isp.net, and rtr3.isp.net πŸ‘‘

rtr-set: rtrs-foo                     rtr-set: rtrs-bar
members: rtr1.isp.net, rtr2.isp.net   members: rtr3.isp.net, rtrs-foo

Figure 19: rtr-set objects.

The mbrs-by-ref attribute is a list of maintainer names or the keyword ANY. If this attribute is used, the router set also includes routers whose inet-rtr objects are registered by one of these maintainers and whose member-of attribute refers to the name of this router set. If the value of a mbrs-by-ref attribute is ANY, any inet-rtr object referring to the router set is a member of the set. If the mbrs-by-ref attribute is missing, only the routers listed in the members attribute are members of the set 🧚

rtr-set: rtrs-foo
members: rtr1.isp.net, rtr2.isp.net
mbrs-by-ref: MNTR-ME

inet-rtr: rtr3.isp.net
local-as: as1
ifaddr: 1.1.1.1 masklen 30
member-of: rtrs-foo
mnt-by: MNTR-ME

Figure 20: rtr-set objects.

Figure 20 presents an example rtr-set object that uses the mbrs-by-ref attribute. The set rtrs-foo contains rtr1.isp.net, rtr2.isp.net, and rtr3.isp.net πŸ’–

5.6 Peerings and peering-set Class 🫢

okay so this is where peering-sets come in and it's giving organization πŸŽ€ Figure 21 shows the attributes. A peering-set object bundles up a bunch of peerings under one name, and that name lives in the peering-set attribute. It's an RPSL name that starts with "prng-" πŸ’…

Attribute    Value          Type
peering-set  <object-name>  mandatory, single-valued, class key
peering      <peering>      mandatory, multi-valued

              Figure 21:  filter Class Attributes

The peering attribute defines a peering that can be used for importing or

     ----------------------                   ----------------------
     |            7.7.7.1 |-------|   |-------| 7.7.7.2            |
     |                    |     ========      |                    |
     |   AS1              |      EX1  |-------| 7.7.7.3     AS2    |
     |                    |                   |                    |
     |            9.9.9.1 |------       ------| 9.9.9.2            |
     ----------------------     |       |     ----------------------
                               ===========
                                   |    EX2
     ----------------------        |
     |            9.9.9.3 |---------
     |                    |
     |   AS3              |
     ----------------------

  Figure 22: Example topology consisting of three ASes, AS1, AS2, and
        AS3; two exchange points, EX1 and EX2; and six routers.

exporting routes.

okay, picture this topology 🧚 we're using Figure 22 for every peering example. There are three ASes (AS1, AS2, and AS3), two exchange points (EX1 and EX2), and six routers. Routers connected to the same exchange point peer with each other and swap routing info. So 7.7.7.1, 7.7.7.2 and 7.7.7.3 all peer with each other, and 9.9.9.1, 9.9.9.2 and 9.9.9.3 peer with each other 🌸

The syntax of a peering specification is:

<as-expression> [<router-expression-1>] [at <router-expression-2>]
| <peering-set-name>

where <as-expression> is an expression over AS numbers and AS sets using the operators AND, OR, and EXCEPT. <router-expression-1> and <router-expression-2> are expressions over router IP addresses, inet-rtr names, and rtr-set names, also using AND, OR, and EXCEPT. The binary EXCEPT operator is set subtraction and has the same precedence as AND (it's semantically the same as an "AND NOT" combo). So (AS1 OR AS2) EXCEPT AS2 equals AS1 πŸ“

This form identifies all the peerings between any local router in <router-expression-2> and any of their peer routers in <router-expression-1> within the ASes in <as-expression>. If <router-expression-2> isn't specified, it defaults to all routers of the local AS that peer with ASes in <as-expression>. If <router-expression-1> isn't specified, it defaults to all routers of the peer ASes in <as-expression> that peer with the local AS ✨

If a <peering-set-name> is used, the peerings get listed in the matching peering-set object. Note that peering-set objects can be recursive 🌈

Okay so there are a LOT of special forms of this general peering specification 😭 The examples below show the most common ones, using the import attribute of the aut-num class. In the first one, 7.7.7.1 imports 128.9.0.0/16 from 7.7.7.2.

(1) aut-num: AS1
    import: from AS2 7.7.7.2 at 7.7.7.1 accept { 128.9.0.0/16 }

In this one, 7.7.7.1 imports 128.9.0.0/16 from 7.7.7.2 and 7.7.7.3.

(2) aut-num: AS1
    import: from AS2 at 7.7.7.1 accept { 128.9.0.0/16 }

In this one, 7.7.7.1 imports 128.9.0.0/16 from 7.7.7.2 and 7.7.7.3, and 9.9.9.1 imports 128.9.0.0/16 from 9.9.9.2.

(3) aut-num: AS1
    import: from AS2 accept { 128.9.0.0/16 }

In this one, 9.9.9.1 imports 128.9.0.0/16 from 9.9.9.2 and 9.9.9.3.

(4) as-set: AS-FOO
    members: AS2, AS3

    aut-num: AS1
    import: from AS-FOO      at 9.9.9.1 accept { 128.9.0.0/16 }

In this one, 9.9.9.1 imports 128.9.0.0/16 from 9.9.9.2 and 9.9.9.3, and 7.7.7.1 imports 128.9.0.0/16 from 7.7.7.2 and 7.7.7.3.

(5) aut-num: AS1
    import: from AS-FOO                 accept { 128.9.0.0/16 }

In this one, AS1 imports 128.9.0.0/16 from AS3 at router 9.9.9.1.

(6) aut-num: AS1
    import: from AS-FOO and not AS2 at not 7.7.7.1
            accept { 128.9.0.0/16 }

This is because "AS-FOO and not AS2" equals AS3 and "not 7.7.7.1" equals 9.9.9.1 πŸ™ˆ

In this one, 9.9.9.1 imports 128.9.0.0/16 from 9.9.9.2 and 9.9.9.3.

(7) peering-set: prng-bar
    peering: AS1 at 9.9.9.1

    peering-set: prng-foo
    peering: prng-bar
    peering: AS2 at 9.9.9.1

    aut-num: AS1
    import: from prng-foo accept { 128.9.0.0/16 }

6 aut-num Class πŸ¦‹

okay so routing policies are specified using the aut-num class πŸ’œ The aut-num attribute holds the AS number of the AS that the object describes. The as-name attribute is a symbolic name (in RPSL name syntax) for the AS. The import, export and default routing policies of the AS are set with the import, export and default attributes, respectively ✨ Figure 23 shows all the attributes.

Attribute  Value                  Type
aut-num    <as-number>            mandatory, single-valued, class key
as-name    <object-name>          mandatory, single-valued
member-of  list of <as-set-names> optional, multi-valued
import     see Section 6.1        optional, multi valued
export     see Section 6.2        optional, multi valued
default    see Section 6.5        optional, multi valued

                 Figure 23:  aut-num Class Attributes

6.1 import Attribute: Import Policy Specification

In RPSL, an import policy gets split into import policy expressions 🌸 Each import policy expression is written with an import attribute. The import attribute has the following syntax (we'll extend it later in Sections 6.3 and 6.6):

import: from <peering-1> [action <action-1>]
         . . .
         from <peering-N> [action <action-N>]
         accept <filter>

The action specification is optional. Here's how an import attribute works: the set of routes matched by <filter> gets imported from all the peers in <peerings>, and while importing routes at <peering-M>, <action-M> gets executed πŸ’…

E.g.

aut-num: AS1
import: from AS2 action pref = 1; accept { 128.9.0.0/16 }

In this example, the route 128.9.0.0/16 is accepted from AS2 with preference 1. We already covered how peerings (see Section 5.6) and filters (see Section 5.4) are specified. Next up, actions πŸ“

6.1.1 Action Specification

Policy actions in RPSL either set or modify route attributes, like giving a route a preference, adding a BGP community to the BGP community path attribute, or setting the MULTI-EXIT-DISCRIMINATOR attribute. Policy actions can also tell routers to do special operations, like route flap damping 🧚

The routing policy attributes whose values can be changed in policy actions are listed in the RPSL dictionary. Check out Section 7 for a list of them. Each action in RPSL ends with a semicolon (';'). You can make composite policy actions by listing them one after another. In a composite policy action, the actions run left to right. For example:

aut-num: AS1
import: from AS2
        action pref = 10; med = 0; community.append(10250, 3561:10);
        accept { 128.9.0.0/16 }

This sets pref to 10, med to 0, and then appends 10250 and 3561:10 to the BGP community path attribute πŸŽ€ The pref attribute is the inverse of the local-pref attribute (i.e. local-pref == 65535 - pref). A route with a local-pref attribute is always preferred over a route without one ✨

aut-num: AS1
import: from AS2 action pref = 1;
        from AS3 action pref = 2;
        accept AS4

The example above says AS4's routes get accepted from AS2 with preference 1, and from AS3 with preference 2. Routes with lower integer preference values are preferred over routes with higher ones πŸ‘‘

aut-num: AS1
import: from AS2 7.7.7.2 at 7.7.7.1 action pref = 1;
        from AS2                    action pref = 2;
        accept AS4

The example above says AS4's routes are accepted from AS2 on the peering 7.7.7.1-7.7.7.2 with preference 1, and on any other peering with AS2 with preference 2 πŸ’–

6.2 export Attribute: Export Policy Specification

Similarly, an export policy expression gets written with an export attribute. The export attribute has the following syntax:

export: to <peering-1> [action <action-1>]
        . . .
        to <peering-N> [action <action-N>]
        announce <filter>

The action specification is optional. The export attribute works like this: the set of routes matched by <filter> gets exported to all the peers listed in <peerings>, and while exporting routes at <peering-M>, <action-M> gets executed 🫢

E.g.

aut-num: AS1
export: to AS2 action med = 5; community .= { 70 };
        announce AS4

In this example, AS4's routes are announced to AS2 with the med attribute set to 5, and community 70 added to the community list 🌈

Example:

aut-num: AS1
export: to AS-FOO announce ANY

In this example, AS1 announces all of its routes to the ASes in the set AS-FOO πŸ“

6.3 Other Routing Protocols, Multi-Protocol Routing Protocols, and Injecting Routes Between Protocols ✨

okay so here's the fuller syntax for import and export πŸ¦‹

 import: [protocol <protocol-1>] [into <protocol-2>]
         from <peering-1> [action <action-1>]
         . . .
         from <peering-N> [action <action-N>]
         accept <filter>
 export: [protocol <protocol-1>] [into <protocol-2>]
         to <peering-1> [action <action-1>]
         . . .
         to <peering-N> [action <action-N>]
         announce <filter>

the optional protocol bits are what let you write policies for other routing protocols, inject routes from one protocol into another, or do multi-protocol routing policies πŸ’… The valid protocol names live in the dictionary. <protocol-1> is the protocol whose routes are being exchanged, and <protocol-2> is the protocol that's receiving them. Both default to the Internet Exterior Gateway Protocol, which is BGP right now 🌸

ex 1: dumping every interAS route into RIP πŸŽ€

 aut-num: AS1
 import: from AS2 accept AS2
 export: protocol BGP4 into RIP
         to AS1 announce ANY

ex 2: AS1 takes AS2's routes plus any more-specifics of them, but doesn't push those extra more-specifics into OSPF 🧚

 aut-num: AS1
 import: from AS2 accept AS2^+
 export: protocol BGP4 into OSPF
         to AS1 announce AS2

ex 3: AS1 pushes its static routes (anything in the set AS1:RS-STATIC-ROUTES) into the interAS protocol and tacks AS1 on to their AS paths twice πŸ”₯

 aut-num: AS1
 import: protocol STATIC into BGP4
         from AS1 action aspath.prepend(AS1, AS1);
         accept AS1:RS-STATIC-ROUTES

ex 4: AS1 pulls a different set of unicast routes from AS2 for multicast reverse path forwarding 🫢

 aut-num: AS1
 import: from AS2 accept AS2
 import: protocol IDMR
         from AS2 accept AS2:RS-RPF-ROUTES

6.4 Ambiguity Resolution

so sometimes the same peering gets covered by more than one peering specification in a policy πŸ‘€ this is not an error, but it's definitely not the vibe Here's an example:

 aut-num: AS1
 import: from AS2 7.7.7.2 at 7.7.7.1 action pref = 2;
         from AS2 7.7.7.2 at 7.7.7.1 action pref = 1;
         accept AS4

To break the tie, the action from the first peering specification is the one that gets used. So the routes get accepted with preference 2. We call this the specification-order rule πŸ’œ

Now check this one:

 aut-num: AS1
 import: from AS2                    action pref = 2;
         from AS2 7.7.7.2 at 7.7.7.1 action pref = 1; dpa = 5;
         accept AS4

Both peering specs cover the peering 7.7.7.1-7.7.7.2, but the second one is more specific about it. The specification-order rule still applies, so only pref = 2 runs. The second peering-action pair never does anything, because the first one always covers it first 😭 If you actually wanted preference 1 on that one specific peering and preference 2 everywhere else, you'd write it like this:

 aut-num: AS1
 import: from AS2 7.7.7.2 at 7.7.7.1 action pref = 1; dpa = 5;
         from AS2                    action pref = 2;
         accept AS4

It's also possible for more than one policy expression to cover the same routes on the same peering 🌈

 aut-num: AS1
 import: from AS2 action pref = 2; accept AS4
 import: from AS2 action pref = 1; accept AS4

The specification-order rule still wins here too. AS4's routes come in from AS2 with preference 2. If the filters overlap but aren't exactly the same:

 aut-num: AS1
 import: from AS2 action pref = 2; accept AS4
 import: from AS2 action pref = 1; accept AS4 OR AS5

then AS4's routes are accepted from AS2 with preference 2, and AS5's routes are also accepted, just with preference 1 ✨

Next up is the general specification-order rule, for the RPSL implementors out there πŸ€“ Say you have two policy expressions:

 aut-num: AS1
 import: from peerings-1 action action-1 accept filter-1
 import: from peerings-2 action action-2 accept filter-2

Those are equivalent to these three expressions, which have no ambiguity left:

 aut-num: AS1
 import: from peerings-1 action action-1 accept filter-1
 import: from peerings-3 action action-2 accept filter-2 AND NOT filter-1
 import: from peerings-4 action action-2 accept filter-2

where peerings-3 are the peerings covered by both peerings-1 and peerings-2, and peerings-4 are the ones covered by peerings-2 but not by peerings-1. (filter-2 AND NOT filter-1 matches the routes that filter-2 matches but filter-1 doesn't.) 🧁

Example time:

 aut-num: AS1
 import: from AS2 7.7.7.2 at 7.7.7.1
         action pref = 2;
         accept {128.9.0.0/16}
 import: from AS2
         action pref = 1;
         accept {128.9.0.0/16, 75.0.0.0/8}

Let's say there are two peerings with AS2: 7.7.7.1-7.7.7.2 and 9.9.9.1-9.9.9.2. Both policy expressions cover 7.7.7.1-7.7.7.2. On that peering, 128.9.0.0/16 is accepted with preference 2, and 75.0.0.0/8 is accepted with preference 1. The peering 9.9.9.1-9.9.9.2 is only covered by the second policy expression, so both 128.9.0.0/16 and 75.0.0.0/8 get accepted there with preference 1 πŸ¦‹

Last thing for this bit: the same ambiguity resolution rules also apply to export and default policy expressions πŸ’…


6.5 default Attribute: Default Policy Specification

default routing policies get set with the default attribute 🌸 It looks like this:

 default: to <peering> [action <action>] [networks <filter>]

The <action> and <filter> parts are both optional. Here's what they mean. The <peering> tells you which AS (and which router, if you include one) you're defaulting to. The <action>, if it's there, describes things about the default, like a relative preference when you've got multiple defaults. The <filter>, if it's there, is a policy filter. A router only uses the default policy if it got the routes matched by <filter> from that peer πŸŽ€

ex: AS1 defaults to AS2 for routing 🧚

 aut-num: AS1
 default: to AS2

ex: router 7.7.7.1 in AS1 defaults to router 7.7.7.2 in AS2 πŸ’œ

 aut-num: AS1
 default: to AS2 7.7.7.2 at 7.7.7.1

ex: AS1 defaults to both AS2 and AS3, but likes AS2 more πŸ‘‘

 aut-num: AS1
 default: to AS2 action pref = 1;
 default: to AS3 action pref = 2;

ex: AS1 defaults to AS2 and uses 128.9.0.0/16 as the default network πŸ“

 aut-num: AS1
 default: to AS2 networks { 128.9.0.0/16 }

6.6 Structured Policy Specification

okay so import and export policies can be structured 🧠 heads up though: structured policies are really only recommended for advanced RPSL users, so if that's not you, feel free to skip this whole section, no shade 🫢

The syntax for a structured policy is below:

 <import-factor> ::= from <peering-1> [action <action-1>]
                     . . .
                     from <peering-N> [action <action-N>]
                     accept <filter>;

 <import-term> ::=  <import-factor> |
                    LEFT-BRACE
                    <import-factor>
                    . . .
                    <import-factor>
                    RIGHT-BRACE

 <import-expression> ::= <import-term>                            |
                         <import-term> EXCEPT <import-expression> |
                         <import-term> REFINE <import-expression>

 import: [protocol <protocol1>] [into <protocol2>]
         <import-expression>

Please note the semicolon at the end of an <import-factor>. When a policy isn't structured (like every example in the other sections), that semicolon is optional. The syntax and semantics of an <import-factor> were already defined in Section 6.1 πŸ’…

An <import-term> is either a bunch of <import-factor>s wrapped in matching braces ({ and }), or just one single <import-factor>. The semantics of an <import-term> are the union of its <import-factor>s, using the specification-order rule. An <import-expression> is either one <import-term>, or an <import-term> followed by either the keyword "except" or the keyword "refine", followed by another <import-expression>. Nesting is allowed, so you can have exceptions to exceptions, refinements to refinements, or even refinements to exceptions, and so on πŸŒ€

Here's how the except operator works. The result of an except operation is another <import-term>. The resulting policy set has the policies from the right-hand side, but their filters get changed so they only include routes that the left-hand side also matches. The left-hand side's policies get added afterwards, and their filters get changed to exclude the routes the right-hand side matches. Filters get modified in this process, but actions are copied over word for word. With multiple levels of nesting, the operations (both except and refine) get performed right to left πŸ’

Let's look at this example:

 import: from AS1 action pref = 1; accept as-foo;
         except {
            from AS2 action pref = 2; accept AS226;
            except {
               from AS3 action pref = 3; accept {128.9.0.0/16};
            }
         }

Here, 128.9.0.0/16 is originated by AS226, and AS226 is a member of the as-set as-foo. So 128.9.0.0/16 gets accepted from AS3. Any other route (not 128.9.0.0/16) originated by AS226 gets accepted from AS2. And any other ASes' routes in as-foo get accepted from AS1 πŸŽ€

We can get to the same answer using the algebra from above. Start with the inner exception specification:

 from AS2 action pref = 2; accept AS226;
 except {
    from AS3 action pref = 3; accept {128.9.0.0/16};
 }

is equivalent to:

 {
    from AS3 action pref = 3; accept AS226 AND {128.9.0.0/16};
    from AS2 action pref = 2; accept AS226 AND NOT {128.9.0.0/16};
 }

So the original expression is equivalent to:

 import: from AS1 action pref = 1; accept as-foo;
         except {
            from AS3 action pref = 3; accept AS226 AND {128.9.0.0/16};
            from AS2 action pref = 2; accept AS226 AND NOT {128.9.0.0/16};
         }

which is equivalent to:

 import: {
    from AS3 action pref = 3;
             accept as-foo AND AS226 AND {128.9.0.0/16};
    from AS2 action pref = 2;
             accept as-foo AND AS226 AND NOT {128.9.0.0/16};
    from AS1 action pref = 1;
             accept as-foo AND NOT
               (AS226 AND NOT {128.9.0.0/16} OR AS226 AND {128.9.0.0/16});
 }

Since AS226 is in as-foo and 128.9.0.0/16 is in AS226, it simplifies to:

import: {
          from AS3 action pref = 3; accept {128.9.0.0/16};
          from AS2 action pref = 2; accept AS226 AND NOT {128.9.0.0/16};
          from AS1 action pref = 1; accept as-foo AND NOT AS226;
        }

Okay so with the refine operator, the new set gets built by taking the cartesian product of both sides πŸ’… For every policy l on the left and every policy r on the right, the resulting policy's peerings are the peerings r and l have in common 🀝 Its filter is the intersection of l's filter and r's filter ✨, and its action is l's action followed by r's action. If there are no common peerings, or if the intersection of filters is empty, then no resulting policy gets generated πŸ™ˆ

Here's an example that makes it click:

import: { from AS-ANY action pref = 1; accept community(3560:10);
          from AS-ANY action pref = 2; accept community(3560:20);
        } refine {
           from AS1 accept AS1;
           from AS2 accept AS2;
           from AS3 accept AS3;
        }

So any route tagged with community 3560:10 gets a preference of 1 πŸŽ€ and any route tagged with 3560:20 gets a preference of 2, no matter who it came from πŸ¦‹ BUT, and this is the plot twist, only AS1's routes get imported from AS1, only AS2's routes get imported from AS2, and only AS3's routes get imported from AS3 😭 Nothing gets imported from any other AS, periodt. You can get to the exact same answer using the algebra above, so this example is equivalent to:

import: {
  from AS1 action pref = 1; accept community(3560:10) AND AS1;
  from AS1 action pref = 2; accept community(3560:20) AND AS1;
  from AS2 action pref = 1; accept community(3560:10) AND AS2;
  from AS2 action pref = 2; accept community(3560:20) AND AS2;
  from AS3 action pref = 1; accept community(3560:10) AND AS3;
  from AS3 action pref = 2; accept community(3560:20) AND AS3;
}

Quick note: the common peerings between "from AS1" and "from AS-ANY" are just the peerings in "from AS1" πŸ’– We never formally define "common peerings", but you can totally work out the definition yourself from how peerings are defined (see Section 5.6) 🧚

Here's another one:

import: {
  from AS-ANY action med = 0; accept {0.0.0.0/0^0-18};
  } refine {
       from AS1 at 7.7.7.1 action pref = 1; accept AS1;
       from AS1            action pref = 2; accept AS1;
    }

Only routes of length 0 to 18 get accepted, and the med value is set to 0 so med doesn't affect anything on any peering πŸ™Œ On top of that, from AS1 only AS1's routes get imported, and AS1's routes imported at 7.7.7.1 are preferred over the other peerings πŸ‘‘ That's equivalent to:

import: {
     from AS1 at 7.7.7.1 action med=0; pref=1; accept {0.0.0.0/0^0-
18} AND AS1;
   from  AS1             action med=0; pref=2; accept {0.0.0.0/0^0-
18} AND AS1;
}

And yes, the same syntax and semantics also apply to structured export policies πŸ“€ Just swap "from" for "to" and "accept" for "announce" πŸ’…


7 dictionary Class ✨

The dictionary class is where RPSL gets its extensibility 🌈 Dictionary objects define routing policy attributes, types, and routing protocols. Routing policy attributes (we'll call them rp-attributes, no cap) can match real protocol attributes, like the BGP path attributes (such as community, dpa, and AS-path), or they can match router features (like BGP route flap damping). Whenever new protocols, new protocol attributes, or new router features show up, the dictionary object gets updated with the right rp-attribute and protocol definitions πŸ¦‹

An rp-attribute is an abstract class, which means there's no data representation you can grab directly. You access it through access methods instead πŸŽ€ For example, the rp-attribute for the BGP AS-path attribute is called aspath, and it has an access method called prepend that stuffs extra AS numbers into the AS-path attribute. Access methods can take arguments, and those arguments are strongly typed. Like, prepend takes AS numbers as its arguments, so it's not just vibes πŸ’€

Once an rp-attribute is defined in the dictionary, you can use it to describe policy filters and actions πŸ’œ Policy analysis tools are required to fetch the dictionary object and recognize newly defined rp-attributes, types, and protocols. Tools are allowed to approximate policy analyses for rp-attributes they don't understand: a filter method may always match, and an action method may always perform a no-operation πŸ™ˆ Analysis tools can even download code to run the right operations, using mechanisms that sit outside the scope of RPSL.

Next up is the syntax and semantics of the dictionary class. This part isn't essential for understanding dictionary objects, but it's essential for creating one 🧚 So feel free to skip ahead to the RPSL Initial Dictionary subsection (Section 7.1) if you just want the good stuff 🫢

The attributes of the dictionary class are shown in Figure 24. The dictionary attribute is the name of the dictionary object, and it follows the RPSL naming rules. There can be lots of dictionary objects, but there's always one well-known dictionary object called "RPSL", and every tool uses it by default πŸ‘‘

Attribute     Value                   Type
dictionary    <object-name>           mandatory, single-valued,
                                      class key
rp-attribute  see description in text optional, multi valued
typedef       see description in text optional, multi valued
protocol      see description in text optional, multi valued

                 Figure 24:  dictionary Class Attributes

The rp-attribute attribute has the following syntax:

rp-attribute: <name>
   <method-1>(<type-1-1>, ..., <type-1-N1> [, "..."])
   ...
   <method-M>(<type-M-1>, ..., <type-M-NM> [, "..."])

Here, <name> is the name of the rp-attribute, and <method-i> is the name of an access method for it, taking Ni arguments where the j-th argument has type <type-i-j>. A method name is either an RPSL name or one of the operators listed in Figure 25. Every operator method except operator() and operator[] can only take one argument πŸŽ€

operator=           operator==
operator<<=         operator<
operator>>=         operator>
operator+=          operator>=
operator-=          operator<=
operator*=          operator!=
operator/=          operator()
operator.=          operator[]


                        Figure 25:  Operators

An rp-attribute can have many methods, and some of them can even share a name as long as their arguments are different types πŸ¦‹ If the argument list ends in "...", the method takes a variable number of arguments. In that case, the actual arguments after the Nth argument are of type <type-N> ✨

Arguments are strongly typed. A <type> in RPSL is a predefined type, a union type, a list type, or a dictionary defined type πŸ’… The predefined types are listed in Figure 26.

integer[lower, upper]              ipv4_address
real[lower, upper]                 address_prefix
enum[name, name, ...]              address_prefix_range
string                             dns_name
boolean                            filter
rpsl_word                          as_set_name
free_text                          route_set_name
email                              rtr_set_name
as_number                          filter_set_name
                                   peering_set_name


                     Figure 26:  Predefined Types

The integer and real predefined types can be followed by a lower and an upper bound that specify the valid values of the argument. The range specification is optional 🌸 We use the ANSI C language conventions for representing integer, real and string values. The enum type is followed by a list of RPSL names, which are the valid values of the type. The boolean type can be true or false. The as_number, ipv4_address, address_prefix and dns_name types are as in Section 2. The filter type is a policy filter as in Section 6, and its value is suggested to be enclosed in parentheses πŸ’–

The syntax of a union type is:

union <type-1>, ... , <type-N>

Here <type-i> is an RPSL type. A union type is any one of the types <type-1> through <type-N>, which is analogous to unions in C[14] πŸ“

The syntax of a list type is:

list [<min_elems>:<max_elems>] of <type>

In this case the list elements are of <type>, and the list contains at least <min_elems> and at most <max_elems> elements. The size specification is optional. If it isn't specified, there's no restriction on how many list elements there can be ✨ A value of a list type is written as a sequence of elements separated by the character "," and enclosed by the characters "{" and "}".

The typedef attribute in the dictionary defines named types like this:

typedef: <name> <type>

Here <name> is a name for the type <type>. The typedef attribute is particularly useful when the type you're defining isn't a predefined type, like a list of unions or a list of lists 🧚

A protocol attribute of the dictionary class defines a protocol and a set of peering parameters for that protocol (these get used in the inet-rtr class in Section 9) 🌈 Its syntax is:

protocol: <name>
 MANDATORY | OPTIONAL <parameter-1>(<type-1-1>,...,
                      <type-1-N1> [,"..."])
   ...
 MANDATORY | OPTIONAL <parameter-M>(<type-M-1>,...,
                      <type-M-NM> [,"..."])

Here <name> is the name of the protocol, and MANDATORY and OPTIONAL are keywords. <parameter-i> is a peering parameter for this protocol, taking Ni arguments. The syntax and semantics of the arguments are the same as for rp-attribute. If the keyword MANDATORY is used, the parameter is mandatory and has to be specified for each peering of this protocol. If the keyword OPTIONAL is used, the parameter can be skipped 🫢

7.1 Initial RPSL Dictionary and Example Policy Actions and Filters

Time for the starter pack πŸŽ€ Here's the initial RPSL dictionary, with example policy actions and filters:

dictionary:   RPSL
rp-attribute: # preference, smaller values represent higher preferences
              pref
              operator=(integer[0, 65535])
rp-attribute: # BGP multi_exit_discriminator attribute
              med
              # to set med to 10: med = 10;
              # to set med to the IGP metric: med = igp_cost;
              operator=(union integer[0, 65535], enum[igp_cost])
rp-attribute: # BGP destination preference attribute (dpa)
              dpa
              operator=(integer[0, 65535])
rp-attribute: # BGP aspath attribute
              aspath
              # prepends AS numbers from last to first order
              prepend(as_number, ...)
typedef:      # a community value in RPSL is either
              #  - a 4 byte integer (ok to use 3561:70 notation)
              #  - internet, no_export, no_advertise (see RFC-1997)
              community_elm union
                  integer[1, 4294967295],
                  enum[internet, no_export, no_advertise],
typedef:      # list of community values { 40, no_export, 3561:70 }
              community_list list of community_elm

Pref, MED, and Route Stuff, Explained (Community Edition) πŸ’…

rp-attribute: # BGP community attribute
              community
              # set to a list of communities
              operator=(community_list)
              # append community values
              operator.=(community_list)
              append(community_elm, ...)
              # delete community values
              delete(community_elm, ...)
              # a filter: true if one of community values is contained
              contains(community_elm, ...)
              # shortcut to contains: community(no_export, 3561:70)
              operator()(community_elm, ...)
              # order independent equality comparison
              operator==(community_list)
rp-attribute: # next hop router in a static route
              next-hop
              # to set to 7.7.7.7: next-hop = 7.7.7.7;
              # to set to router's own address: next-hop = self;
              operator=(union ipv4_address, enum[self])
rp-attribute: # cost of a static route
              cost
              operator=(integer[0, 65535])
protocol: BGP4
          # as number of the peer router
          MANDATORY asno(as_number)
          # enable flap damping
          OPTIONAL flap_damp()
          OPTIONAL flap_damp(integer[0,65535],
                             # penalty per flap
                             integer[0,65535],
                             # penalty value for supression
                             integer[0,65535],
                             # penalty value for reuse
                             integer[0,65535],
                             # halflife in secs when up
                             integer[0,65535],
                             # halflife in secs when down
                             integer[0,65535])
                             # maximum penalty
protocol: OSPF
protocol: RIP
protocol: IGRP
protocol: IS-IS
protocol: STATIC
protocol: RIPng
protocol: DVMRP
protocol: PIM-DM
protocol: PIM-SM
protocol: CBT
protocol: MOSPF

Figure 27: RPSL Dictionary

okay so Figure 27 is the initial RPSL dictionary and it is giving full toolkit 🧰✨ It has seven rp-attributes. pref assigns local preference to the routes accepted 🌸 med assigns a value to the MULTI_EXIT_DISCRIMINATOR BGP attribute πŸ¦‹ dpa assigns a value to the DPA BGP attribute πŸ’œ aspath prepends a value to the AS_PATH BGP attribute πŸ›£οΈ community assigns a value to, or checks the value of, the community BGP attribute πŸŽ€ next-hop assigns next hop routers to static routes 🧚 and cost assigns a cost to static routes πŸ’°

The dictionary defines two types, community_elm and community_list. A community_elm is either a 4-byte unsigned integer, or one of the keywords internet, no_export or no_advertise (defined in [9]). An integer can be written as two 2-byte integers separated by ":", which lets a provider partition the community number space. The provider can use its AS number as the first two bytes and give the last two bytes whatever meaning it wants 🫢

The initial dictionary (Figure 27) only defines options for the Border Gateway Protocol: asno and flap_damp. The asno option is mandatory, and it is the AS number of the peer router πŸ”₯ The flap_damp option is optional, and it tells the router to damp route flaps [21] when importing routes from the peer router 🌈

flap_damp can be specified with or without parameters. If the parameters are missing, they default to:

flap_damp(1000, 2000, 750, 900, 900, 20000)

Here's the vibe: each route flap adds a penalty of 1000, and the route gets suppressed once the penalty hits 2000 😭 The penalty is cut in half after 15 minutes (900 seconds) of stability, whether the route is up or down. A suppressed route gets reused once the penalty drops below 750. The maximum penalty a route can collect is 20,000, which means the maximum suppress time after a route becomes stable is about 75 minutes ⏳ These numbers match the default flap damping parameters in several routers, so they're not random πŸ’…

Policy Actions and Filters Using RP-Attributes

The syntax for a policy action or filter that uses an rp-attribute x looks like this:

x.method(arguments)
x "op" argument

Here, method is a method, and "op" is an operator method of the rp-attribute x. If you use an operator method inside a composite policy filter, it evaluates before the composite policy filter operators (AND, OR, NOT, and the implicit or operator) 🧠✨

The pref rp-attribute can be assigned a positive integer like so:

pref = 10;

The med rp-attribute is giving two options: a positive integer, or the word "igp_cost" 🀭

med = 0;
med = igp_cost;

The dpa rp-attribute can be assigned a positive integer too:

dpa = 100;

The BGP community attribute is list-valued. It's a list of 4-byte integers, and each one represents a "community" πŸŽ€ Here's how you add communities to this rp-attribute:

community .= { 100 };
community .= { NO_EXPORT };
community .= { 3561:10 };

In that last case, the 4-byte integer gets built with the more significant two bytes equal to 3561 and the less significant two bytes equal to 10. Now, deleting communities from the community rp-attribute looks like this:

community.delete(100, NO_EXPORT, 3561:10);

Filters that use the community rp-attribute can be defined like this:

community.contains(100, NO_EXPORT, 3561:10);
community(100, NO_EXPORT, 3561:10);             # shortcut

You can also set the community rp-attribute to a whole list of communities:

community = {100, NO_EXPORT, 3561:10, 200};
community = {};

In the first case, the community rp-attribute holds the communities 100, NO_EXPORT, 3561:10, and 200 🌟 In the second case, the community rp-attribute gets cleared 🧹 And if you want to compare it against a list of communities, you do it like this:

community == {100, NO_EXPORT, 3561:10, 200};   # exact match

To influence route selection, the BGP as_path rp-attribute can be made longer by prepending AS numbers to it πŸ›£οΈπŸ’–

aspath.prepend(AS1);
aspath.prepend(AS1, AS1, AS1);
⚠️ Heads up bestie: these are INVALID, so don't copy them πŸ’€
med = -50;                     # -50 is not in the range
med = igp;                     # igp is not one of the enum values
med.assign(10);                # method assign is not defined
community.append(AS3561:20);   # the first argument should be 3561

Figure 28 is a more advanced example using the rp-attribute community πŸ‘‘ AS3561 bases its route selection preference on the community attribute, and other ASes can indirectly affect how AS3561 selects routes by putting the right communities in their route announcements. It's giving puppet-master energy 🎭

aut-num: AS1
export: to AS2 action community.={3561:90};
        to AS3 action community.={3561:80};
        announce AS1

as-set: AS3561:AS-PEERS
members: AS2, AS3

aut-num: AS3561
import: from AS3561:AS-PEERS
        action pref = 10;
        accept community(3561:90)
import: from AS3561:AS-PEERS
        action pref = 20;
        accept community(3561:80)
import: from AS3561:AS-PEERS
        action pref = 20;
        accept community(3561:70)
import: from AS3561:AS-PEERS
        action pref = 0;
        accept ANY

Figure 28: Policy example using the community rp-attribute.


8. Advanced route Class ✨

8.1 Specifying Aggregate Routes πŸŽ€

okay bestie, it's aggregation time πŸ¦‹ five attributes do the heavy lifting here: aggr-bndry, aggr-mtd, export-comps, inject, and holes [11]. A route object counts as an aggregate route if any of them shows up, except inject, which kinda goes its own way πŸ’…. The origin is the AS doing the aggregating, aka the aggregator AS. Quick vocab so nobody gets lost ✨: "aggregate" is the route that gets generated, "component" is a route used to build the aggregate's path attributes, and "more specifics" is any route that's more specific than the aggregate, whether or not it helped build it 🌸

The components attribute decides which component routes form the aggregate. Its syntax goes like this:

components: [ATOMIC] [[<filter>] [protocol <protocol> <filter> ...]]

Here <protocol> is a routing protocol name like BGP4, OSPF or RIP (the valid names live in the dictionary), and <filter> is a policy expression 🧠 Routes that match one of those filters and were learned from the matching protocol get used to form the aggregate. If you leave out <protocol>, it defaults to any protocol. Every <filter> secretly has an "AND" with the more specifics of the aggregate, so only the component routes get picked. If you use the ATOMIC keyword, the aggregation is done atomically [11]. No <filter>? It defaults to more specifics. And if the components attribute is missing entirely, all more specifics without the ATOMIC keyword get used 😭

Check out these two route objects:

route: 128.8.0.0/15
origin: AS1
components: <^AS2>

route: 128.8.0.0/15
origin: AS1
components: protocol BGP4 {128.8.0.0/16^+}
            protocol OSPF {128.9.0.0/16^+}


               Figure 29:  Two aggregate route objects.

In the first one, more specifics of 128.8.0.0/15 with AS paths starting with AS2 get aggregated 🌈 In the second one, some routes learned from BGP and some learned from OSPF get aggregated together πŸ’œ

The aggr-bndry attribute is an AS expression over AS numbers and sets (see Section 5.6). Its result is the set of ASes that make up the aggregation boundary πŸ“ If aggr-bndry is missing, the origin AS is the only one in the boundary. Outside the boundary, only the aggregate gets exported and the more specifics get suppressed πŸ™ˆ Inside the boundary, though, the more specifics still get swapped around.

The aggr-mtd attribute says how the aggregate gets generated. Its syntax:

aggr-mtd: inbound
        | outbound [<as-expression>]

Here <as-expression> is an expression over AS numbers and sets (see Section 5.6). If it's missing, it defaults to AS-ANY. With outbound aggregation, the more specifics stay inside the AS, and the aggregate gets formed at every inter-AS boundary with ASes in <as-expression> before it's exported. ASes inside the aggregating boundary are the exception, since aggr-bndry is enforced no matter what <as-expression> says. With inbound aggregation, the aggregate gets formed at all inter-AS boundaries before routes are imported into the aggregator AS. Heads up: <as-expression> can't be used with inbound aggregation 🚫 And if aggr-mtd is missing, it defaults to "outbound AS-ANY" πŸ’…

Here's an outbound multi-AS example:

route:      128.8.0.0/15            route:      128.8.0.0/15
origin:     AS1                     origin:     AS2
components: {128.8.0.0/15^-}        components: {128.8.0.0/15^-}
aggr-bndry: AS1 OR AS2              aggr-bndry: AS1 OR AS2
aggr-mtd:   outbound AS-ANY         aggr-mtd:   outbound AS-ANY


          Figure 30:  Outbound multi-AS aggregation example.

In this one, AS1 and AS2 team up and only announce the less specific 128.8.0.0/15 to the outside world, while swapping more specifics between themselves 🀝 This is super useful when some components live in AS1 and others live in AS2 ✨

When routes get aggregated, the goal is to export only the aggregate and suppress the more specifics outside the boundary. Sometimes, though, policy or topology stuff (like a multi-homed component) means some components have to be exported anyway πŸ₯Ί That's what export-comps is for. It's an RPSL filter that matches the more specifics that should go outside the boundary. If the attribute is missing, the more specifics don't get exported outside the boundary. Also, the export-comps filter has an implicit "AND" with the more specifics of the aggregate πŸ’–

This example shows an outbound aggregation with an export exception:

   route:      128.8.0.0/15
   origin:     AS1
   components: {128.8.0.0/15^-}
   aggr-mtd:   outbound AS-ANY
   export-comps: {128.8.8.0/24}


          Figure 31:  Outbound aggregation with export exception.

Here 128.8.8.0/24 gets exported outside AS1 alongside the aggregate. That's handy when 128.8.8.0/24 is a multi-homed site connecting to AS1 and some other AS 🧚

The inject attribute says which routers do the aggregation and when. Its syntax:

inject: [at <router-expression>] ...
        [action <action>]
        [upon <condition>]

Here <action> is an action specification (see Section 6.1.1), <condition> is a boolean expression (more on that below), and <router-expression> is described in Section 5.6 ✨

Every router in <router-expression> and in the aggregator AS does the aggregation. If you don't specify a <router-expression>, all routers inside the aggregator AS do it. The <action> can set path attributes on the aggregate, like giving it a preference πŸ”₯

The upon clause is a boolean condition, and the aggregate only gets generated if that condition is true. <condition> uses the logical operators AND and OR (NOT is not allowed πŸ™…β€β™€οΈ) over these:

HAVE-COMPONENTS { list of prefixes }
EXCLUDE { list of prefixes }
STATIC

The prefixes in HAVE-COMPONENTS can only be more specifics of the aggregate. It evaluates to true when all the listed prefixes are in the aggregating router's routing table. The list can also use prefix ranges (with the operators ^-, ^+, ^n, and ^n-m). In that case, at least one prefix from each range has to be in the routing table for it to be true πŸ‘‘ The prefixes in EXCLUDE can be anything. It evaluates to true when none of the listed prefixes are in the routing table. Ranges work here too, and no prefix in the range should be in the routing table. The keyword STATIC always evaluates to true πŸ’€ And if there's no upon clause, the aggregate gets generated if and only if there's a component in the routing table (a more specific that matches the components filter) 🌸

Here are two examples of inject:

route:      128.8.0.0/15
origin:     AS1
components: {128.8.0.0/15^-}
aggr-mtd:   outbound AS-ANY
inject:     at 1.1.1.1 action dpa = 100;
inject:     at 1.1.1.2 action dpa = 110;

route:      128.8.0.0/15
origin:     AS1
components: {128.8.0.0/15^-}
aggr-mtd:   outbound AS-ANY
inject:     upon HAVE-COMPONENTS {128.8.0.0/16, 128.9.0.0/16}
holes:      128.8.8.0/24


               Figure 32:  Examples of inject.

In the first case, the aggregate gets injected at two routers, and each one sets the dpa path attribute to a different value πŸ“ In the second case, the aggregate only gets generated if both 128.8.0.0/16 and 128.9.0.0/16 are in the routing table. In the first case, just one of them is enough for injection πŸ’œ

The holes attribute lists the component prefixes that aren't reachable through the aggregate (maybe that chunk of address space is unallocated 🫠). It's really useful for diagnosis πŸ” In Figure 32, the second example has a hole: 128.8.8.0/24. That could happen if a customer switches providers and takes that part of the address space with them 🧳

8.1.1 Interaction with policies in aut-num class ✨

An aggregate that gets formed is only announced to other ASes if the AS's export policies allow exporting the aggregate πŸ›‘οΈ Once the aggregate exists, the more specifics get suppressed from export, except to the ASes in aggr-bndry and except the components listed in export-comps. For those exceptions to actually happen, the AS's export policies have to explicitly allow them πŸ’…

If an aggregate doesn't get formed (because of the upon clause), then the more specifics can be exported to other ASes, but only if the AS's export policies allow it 🌈 So, before any route (aggregate or more specific) gets exported, it's filtered twice: once based on the route objects, and once based on the AS's export policies. Double check, no cap 🫢

Here's an interaction example:

route:        128.8.0.0/16
origin:       AS1

route:        128.9.0.0/16
origin:       AS1

route:        128.8.0.0/15
origin:       AS1
aggr-bndry:   AS1 or AS2 or AS3
aggr-mtd:     outbound AS3 or AS4 or AS5
components:   {128.8.0.0/16, 128.9.0.0/16}
inject:       upon HAVE-COMPONENTS {128.9.0.0/16, 128.8.0.0/16}

aut-num: AS1
export:  to AS2 announce AS1
export:  to AS3 announce AS1 and not {128.9.0.0/16}
export:  to AS4 announce AS1
export:  to AS5 announce AS1
export:  to AS6 announce AS1


       Figure 33:  Interaction with policies in aut-num class.

Looking at the route objects, the more specifics 128.8.0.0/16 and 128.9.0.0/16 should get exchanged between AS1, AS2 and AS3, since that's the aggregation boundary πŸ€“ Outbound aggregation goes to AS4 and AS5, but not to AS3, because AS3 is inside the boundary. The aut-num object lets both components go to AS2, but only 128.8.0.0/16 goes to AS3. The aggregate can only form if both components are available 😭 So in this case, only the aggregate gets announced to AS4 and AS5. But if one component is missing, the aggregate won't form, and any available component or more specific gets exported to AS4 and AS5 instead πŸ¦‹ Whether aggregation happens or not, AS6 only ever gets the more specifics, since it's not listed in aggr-mtd πŸ’–

When you're doing an inbound aggregation, configuration generators can drop the aggregation statements on routers where the AS's import policy blocks importing any more specifics 🧹

8.1.2. Ambiguity resolution with overlapping aggregates 🧩

okay bestie, what happens when you've got a bunch of aggregate routes and they overlap? 😭 Like, one is less specific than the other. Then they must be evaluated from more specific to less specific, no cap πŸ’…. When an outbound aggregation is done for a peer, the aggregate and the components listed in that peer's export-comps attribute are available for making the next less specific aggregate. Components that aren't in export-comps are not available, so they're out of the picture πŸ™ˆ. A route is exportable to an AS if it's the least specific aggregate exportable to that AS, or if it's listed in the export-comps attribute of an exportable route. This is a recursive definition, so it loops back on itself, and that's kind of iconic πŸŒ€.

route:        128.8.0.0/15
origin:       AS1
aggr-bndry:   AS1 or AS2
aggr-mtd:     outbound
inject:       upon HAVE-COMPONENTS {128.8.0.0/16, 128.9.0.0/16}

route:        128.10.0.0/15
origin:       AS1
aggr-bndry:   AS1 or AS3
aggr-mtd:     outbound
inject:       upon HAVE-COMPONENTS {128.10.0.0/16, 128.11.0.0/16}
export-comps: {128.11.0.0/16}

route:        128.8.0.0/14
origin:       AS1
aggr-bndry:   AS1 or AS2 or AS3
aggr-mtd:     outbound
inject:       upon HAVE-COMPONENTS {128.8.0.0/15, 128.10.0.0/15}
export-comps: {128.10.0.0/15}


                   Figure 34:  Overlapping aggregations.

Okay, so in Figure 34 πŸŽ€, AS1 and AS2 team up to aggregate 128.8.0.0/16 and 128.9.0.0/16 into 128.8.0.0/15. AS1 and AS3 do their own thing with 128.10.0.0/16 and 128.11.0.0/16, making 128.10.0.0/15. But all together, they squish those four routes into 128.8.0.0/14 ✨. Assuming all four components are available, a router in AS1 for an outside AS, let's say AS4, will first generate 128.8.0.0/15 and 128.10.0.0/15. That makes 128.8.0.0/15, 128.10.0.0/15, and its exception 128.11.0.0/16 available for generating 128.8.0.0/14. So the router builds 128.8.0.0/14 from those three routes. For AS4, that means 128.8.0.0/14 is exportable, and so are its exception 128.10.0.0/15 and that one's exception 128.11.0.0/16 πŸ¦‹.

For AS2, a router in AS1 will only generate 128.10.0.0/15. So 128.10.0.0/15 and its exception 128.11.0.0/16 are exportable. Also, 128.8.0.0/16 and 128.9.0.0/16 are still exportable, since they didn't take part in an aggregate that's exportable to AS2 🌸.

Similarly, for AS3, a router in AS1 will only generate 128.8.0.0/15. In this case, 128.8.0.0/15, 128.10.0.0/16, and 128.11.0.0/16 are exportable πŸ’œ.


8.2. Specifying Static Routes πŸ›£οΈ

okay so the inject attribute can be used to specify static routes by using "upon static" as the condition 🫢:

  inject: [at <router-expression>] ...
          [action <action>]
          upon static

Here's the vibe: the routers in <router-expression> run the <action> and inject the route into the interAS routing system statically πŸ”₯. The <action> can set certain route attributes, like a next-hop router or a cost.

In the following example, the router 7.7.7.1 injects the route 128.7.0.0/16 🧚. The next-hop routers for this route (there are two of them in this example) are 7.7.7.2 and 7.7.7.3. The route has a cost of 10 over 7.7.7.2 and a cost of 20 over 7.7.7.3 πŸ’–.

   route:  128.7.0.0/16
   origin: AS1
   inject: at 7.7.7.1 action next-hop = 7.7.7.2; cost = 10; upon static
   inject: at 7.7.7.1 action next-hop = 7.7.7.3; cost = 20; upon static

9. inet-rtr Class πŸ–₯️

okay so routers get specified using the inet-rtr class 🌈. The attributes of the inet-rtr class are shown in Figure 35. The inet-rtr attribute is a valid DNS name of the router being described. Each alias attribute, if present, is a canonical DNS name for the router. The local-as attribute specifies the AS number of the AS that owns and operates this router πŸ‘‘.

  Attribute  Value                    Type
  inet-rtr   <dns-name>               mandatory, single-valued, class key
  alias      <dns-name>               optional, multi-valued
  local-as   <as-number>              mandatory, single-valued
  ifaddr     see description in text  mandatory, multi-valued
  peer       see description in text  optional, multi-valued
  member-of  list of <rtr-set-names>  optional, multi-valued


                   Figure 35:  inet-rtr Class Attributes

The value of an ifaddr attribute has this syntax: <ipv4-address> masklen <integer> [action <action>]

The IP address and the mask length are mandatory for each interface πŸ™Œ. You can optionally include an action to set other parameters for that interface.

Figure 36 shows an example inet-rtr object πŸ“. The name of the router is "amsterdam.ripe.net", and "amsterdam1.ripe.net" is a canonical name for it. The router is connected to 4 networks, and its IP addresses and mask lengths in those networks are listed in the ifaddr attributes.

 inet-rtr: Amsterdam.ripe.net
 alias:    amsterdam1.ripe.net
 local-as: AS3333
 ifaddr:   192.87.45.190 masklen 24
 ifaddr:   192.87.4.28   masklen 24
 ifaddr:   193.0.0.222   masklen 27
 ifaddr:   193.0.0.158   masklen 27
 peer:     BGP4 192.87.45.195 asno(AS3334), flap_damp()


                        Figure 36:  inet-rtr Objects

Each peer attribute, if present, specifies a protocol peering with another router. The value of a peer attribute has this syntax:

  <protocol> <ipv4-address>      <options>
| <protocol> <inet-rtr-name>     <options>
| <protocol> <rtr-set-name>      <options>
| <protocol> <peering-set-name>  <options>

Here, <protocol> is a protocol name, <ipv4-address> is the IP address of the peer router, and <options> is a comma separated list of peering options for <protocol>. Instead of the peer's IP address, you can use its inet-rtr-name πŸ’…. Possible protocol names and attributes are defined in the dictionary (please see Section 7). In the example above, the router has a BGP peering with the router 192.87.45.195 in AS3334, and it turns flap damping on when importing routes from that router 😭.

You can also specify a whole group of peers instead of just one, using the <rtr-set-name> and <peering-set-name> forms πŸŽ€. If the <peering-set-name> form is used, only the peerings in the corresponding peering set that are with this router are included. Figure 37 shows an example inet-rtr object with peering groups.

 rtr-set: rtrs-ibgp-peers
 members: 1.1.1.1, 2.2.2.2, 3.3.3.3

 peering-set: prng-ebgp-peers
 peering: AS3334 192.87.45.195
 peering: AS3335 192.87.45.196

 inet-rtr: Amsterdam.ripe.net
 alias:    amsterdam1.ripe.net
 local-as: AS3333
 ifaddr:   192.87.45.190 masklen 24
 ifaddr:   192.87.4.28   masklen 24
 ifaddr:   193.0.0.222   masklen 27
 ifaddr:   193.0.0.158   masklen 27
 peer:     BGP4 rtrs-ibgp-peers asno(AS3333), flap_damp()
 peer:     BGP4 prng-ebgp-peers asno(PeerAS), flap_damp()


                 Figure 37:  inet-rtr Object with peering groups

10. Extending RPSL 🌱

okay so our experience with earlier routing policy languages and data formats (PRDB [2], RIPE-81 [8], and RIPE-181 [7]) taught us that RPSL had to be extensible πŸ™ˆ. That's why extensibility was a primary design goal for RPSL. New routing protocols, or new features for existing routing protocols, can be handled easily using RPSL's dictionary class. New classes, or new attributes for existing classes, can also be added ✨.

This section gives guidelines for extending RPSL. The guidelines are designed with an eye toward keeping backward compatibility with existing tools and databases 🫢. We next list the available options for extending RPSL, from most preferred to least preferred.

10.1. Extensions by changing the dictionary class πŸ“–

The dictionary class is the main mechanism provided for extending RPSL. Dictionary objects define routing policy attributes, types, and routing protocols.

We recommend updating the RPSL dictionary to include appropriate rp-attribute and protocol definitions whenever new path attributes or router features are introduced. For example, in an earlier version of the RPSL document, it was only possible to specify that a router performs route flap damping on a peer. It wasn't possible to specify the parameters of route flap damping. Later on, the parameters were added by changing the dictionary πŸ’œ.

When changing the dictionary, full compatibility should be maintained. For example, in our flap damping case, we made the parameter specification optional, in case some ISPs didn't want that level of detail. This also achieved compatibility. Any object registered without the parameters will continue to be valid. Any tool based on RPSL is expected to take a default action on routing policy attributes that it doesn't understand (e.g. issue a warning and otherwise ignore). So old tools that run into a flap damping specification with parameters will ignore the parameters 🌸.

10.2. Extensions by adding new attributes to existing classes 🧁

New attributes can be added to any class. To keep full compatibility, new attributes should not contradict the semantics of the objects they're attached to. Any tool that uses the IRR should be designed so that it ignores attributes it doesn't understand. Most existing tools already follow this design principle πŸ™Œ.

We recommend adding new attributes to existing classes when a new aspect of a class is discovered. For example, the RPSL route class extends its RIPE-181 predecessor by including several new attributes that enable aggregate and static route specification πŸ¦‹.

10.3. Extensions by adding new classes πŸ—οΈ

New classes can be added to RPSL to store new types of policy data. Providing full compatibility is straightforward as long as existing classes are still understood. Since a tool should only query the IRR for the classes it understands, full compatibility shouldn't be a problem in this case πŸ’….

Before adding a new class, ask yourself whether the information in the objects of the new class could have belonged better to some other class πŸ€”. For example, if the geographic location of a router needs to be stored in the IRR, it might be tempting to add a new class called, say, router-location class. But the information really belongs to the inet-rtr class, perhaps in a new attribute called location πŸ“.

10.4. Extensions by changing the syntax of existing RPSL attributes ⚠️

If all of the methods described above fail to provide the desired extension, it may be necessary to change the syntax of RPSL. Any change in RPSL syntax must provide backwards compatibility, and it should only be considered as a last resort, since full compatibility may not be achievable. However, we require that the old syntax still be valid πŸ₯Ή.


11. Security Considerations πŸ”

okay bestie, this part is serious, so read it carefully 😭πŸ”₯

This document describes RPSL, a language for expressing routing policies. The language defines a maintainer (mntner class) object, which is the entity that controls or "maintains" the objects stored in a database expressed by RPSL. Requests from maintainers can be authenticated with various techniques, as defined by the "auth" attribute of the maintainer object πŸ›‘οΈ.

The exact protocols that IRR's use to communicate RPSL objects are beyond the scope of this document. However, it's envisioned that several techniques may be used, ranging from interactive query/update protocols to store and forward protocols similar to or based on electronic mail (or even voice telephone calls) πŸ“ž. No matter which protocols are used in a given situation, it's expected that appropriate security techniques, such as IPSEC, TLS, or PGP/MIME, will be utilized πŸ”’πŸ’œ.

12. Acknowledgements ✨

okay bestie, big thank-you energy 🫢 the authors want to shoutout these legends for their comments and suggestions: Jessica Yu, Randy Bush, Alan Barrett, Bill Manning, Sue Hares, Ramesh Govindan, Kannan Varadhan, Satish Kumar, Craig Labovitz, Rusty Eddy, David J. LeRoy, David Whipple, Jon Postel, Deborah Estrin, Elliot Schwartz, Joachim Schmitz, Mark Prior, Tony Przygienda, David Woodgate, Rob Coltun, Sanjay Wadhwa, Ardas Cilingiroglu, and the participants of the IETF RPS Working Group πŸ’œ iconic group, no notes.

References

  • [1] Internet routing registry. procedures. http://www.ra.net/RADB.tools.docs/, http://www.ripe.net/db/doc.html.
  • [2] Nsfnet policy routing database (prdb). Maintained by MERIT Network Inc., Ann Arbor, Michigan. Contents available from nic.merit.edu.:/nsfnet/announced.networks/nets.tag.now by anonymous ftp.
  • [3] Alaettinouglu, C., Bates, T., Gerich, E., Karrenberg, D., Meyer, D., Terpstra, M. and C. Villamizer, "Routing Policy Specification Language (RPSL)", RFC 2280, January 1998.
  • [4] C. Alaettinouglu, D. Meyer, and J. Schmitz. Application of routing policy specification language (rpsl) on the internet. Work in Progress.
  • [5] T. Bates. Specifying an `internet router' in the routing registry. Technical Report RIPE-122, RIPE, RIPE NCC, Amsterdam, Netherlands, October 1994.
  • [6] T. Bates, E. Gerich, L. Joncheray, J-M. Jouanigot, D. Karrenberg, M. Terpstra, and J. Yu. Representation of ip routing policies in a routing registry. Technical Report ripe-181, RIPE, RIPE NCC, Amsterdam, Netherlands, October 1994.
  • [7] Bates, T., Gerich, E., Joncheray, L., Jouanigot, J-M., Karrenberg, D., Terpstra, M. and J. Yu, " Representation of IP Routing Policies in a Routing Registry", RFC 1786, March 1995.
  • [8] T. Bates, J-M. Jouanigot, D. Karrenberg, P. Lothberg, and M. Terpstra. Representation of ip routing policies in the ripe database. Technical Report ripe-81, RIPE, RIPE NCC, Amsterdam, Netherlands, February 1993.
  • [9] Chandra, R., Traina, P. and T. Li, "BGP Communities Attribute", RFC 1997, August 1996.
  • [10] Crocker, D., "Standard for ARPA Internet Text Messages", STD 11, RFC 822, August 1982.
  • [11] Fuller, V., Li, T., Yu, J. and K. Varadhan, "Classless Inter-Domain Routing (CIDR): an Address Assignment and Aggregation Strategy", RFC 1519, September 1993.
  • [12] D. Karrenberg and T. Bates. Description of inter-as networks in the ripe routing registry. Technical Report RIPE-104, RIPE, RIPE NCC, Amsterdam, Netherlands, December 1993.
  • [13] D. Karrenberg and M. Terpstra. Authorisation and notification of changes in the ripe database. Technical Report ripe-120, RIPE, RIPE NCC, Amsterdam, Netherlands, October 1994.
  • [14] B. W. Kernighan and D. M. Ritchie. The C Programming Language. Prentice-Hall, 1978.
  • [15] A. Lord and M. Terpstra. Ripe database template for networks and persons. Technical Report ripe-119, RIPE, RIPE NCC, Amsterdam, Netherlands, October 1994.
  • [16] A. M. R. Magee. Ripe ncc database documentation. Technical Report RIPE-157, RIPE, RIPE NCC, Amsterdam, Netherlands, May 1997.
  • [17] Mockapetris, P., "Domain names - concepts and facilities", STD 13, RFC 1034, November 1987.
  • [18] Y. Rekhter. Inter-domain routing protocol (idrp). Journal of Internetworking Research and Experience, 4:61--80, 1993.
  • [19] Rekhter Y. and T. Li, "A Border Gateway Protocol 4 (BGP-4)", RFC 1771, March 1995.
  • [20] C. Villamizar, C. Alaettinouglu, D. Meyer, S. Murphy, and C. Orange. Routing policy system security", Work in Progress.
  • [21] Villamizar, C., Chandra, R. and R. Govindan, "BGP Route Flap Damping", RFC 2439, November 1998.
  • [22] J. Zsako, "PGP authentication for ripe database updates", Work in Progress.

A. Routing Registry Sites 🌸

the routing registries as of November 1996 were RIPE, RADB, CANet, MCI and ANS. if you want the current list, you can contact one of these registries and ask them directly πŸ’…

B. Grammar Rules πŸ¦‹

okay so this section is the formal grammar for RPSL πŸŽ€ the basic data types are defined in Section 2, and we don't write formal grammar rules for attributes whose values are basic types or lists of basic types. the rules are written in the input language of the GNU Bison parser, which means you can literally cut and paste them into that program 😭 slay.

//**** Generic Attributes **********************************************

changed_attribute: ATTR_CHANGED TKN_EMAIL TKN_INT

//**** aut-num class ***************************************************

//// as_expression /////////////////////////////////////////////////////

opt_as_expression:
| as_expression

as_expression: as_expression OP_OR as_expression_term
| as_expression_term

as_expression_term: as_expression_term OP_AND as_expression_factor
| as_expression_term KEYW_EXCEPT as_expression_factor
| as_expression_factor

as_expression_factor: '(' as_expression ')'
| as_expression_operand

as_expression_operand: TKN_ASNO
| TKN_ASNAME

//// router_expression /////////////////////////////////////////////////

opt_router_expression:
| router_expression

opt_router_expression_with_at:
| KEYW_AT router_expression

router_expression: router_expression OP_OR router_expression_term
| router_expression_term
router_expression_term: router_expression_term OP_AND
                        router_expression_factor
| router_expression_term KEYW_EXCEPT router_expression_factor
| router_expression_factor

router_expression_factor: '(' router_expression ')'
| router_expression_operand

router_expression_operand: TKN_IPV4
| TKN_DNS
| TKN_RTRSNAME

//// peering ///////////////////////////////////////////////////////////

peering: as_expression opt_router_expression opt_router_expression_with_at
| TKN_PRNGNAME

//// action ////////////////////////////////////////////////////////////

opt_action:
| KEYW_ACTION action

action: single_action
| action single_action
single_action: TKN_RP_ATTR '.' TKN_WORD '(' generic_list ')' ';'
| TKN_RP_ATTR TKN_OPERATOR list_item ';'
| TKN_RP_ATTR '(' generic_list ')' ';'
| TKN_RP_ATTR '[' generic_list ']' ';'
| ';'

//// filter ////////////////////////////////////////////////////////////

filter: filter OP_OR filter_term
| filter filter_term %prec OP_OR
| filter_term

filter_term : filter_term OP_AND filter_factor
| filter_factor

filter_factor :  OP_NOT filter_factor
| '(' filter ')'
| filter_operand

filter_operand: KEYW_ANY
| '<' filter_aspath '>'
| filter_rp_attribute
| TKN_FLTRNAME
| filter_prefix
filter_prefix: filter_prefix_operand OP_MS
|  filter_prefix_operand

filter_prefix_operand: TKN_ASNO
| KEYW_PEERAS
| TKN_ASNAME
| TKN_RSNAME
| '{' opt_filter_prefix_list '}'

opt_filter_prefix_list:
| filter_prefix_list

filter_prefix_list: filter_prefix_list_prefix
| filter_prefix_list ',' filter_prefix_list_prefix

filter_prefix_list_prefix: TKN_PRFXV4
| TKN_PRFXV4RNG

filter_aspath: filter_aspath '|' filter_aspath_term
| filter_aspath_term

filter_aspath_term: filter_aspath_term filter_aspath_closure
| filter_aspath_closure

filter_aspath_closure: filter_aspath_closure '*'
| filter_aspath_closure '?'
| filter_aspath_closure '+'
| filter_aspath_factor

filter_aspath_factor: '^'
| '$'
| '(' filter_aspath ')'
| filter_aspath_no

filter_aspath_no: TKN_ASNO
| KEYW_PEERAS
| TKN_ASNAME
| '.'
| '[' filter_aspath_range ']'
| '[' '^' filter_aspath_range ']'

filter_aspath_range:
| filter_aspath_range TKN_ASNO
| filter_aspath_range KEYW_PEERAS
| filter_aspath_range '.'
| filter_aspath_range TKN_ASNO '-' TKN_ASNO
| filter_aspath_range TKN_ASNAME
filter_rp_attribute: TKN_RP_ATTR '.' TKN_WORD '(' generic_list ')'
| TKN_RP_ATTR TKN_OPERATOR list_item
| TKN_RP_ATTR '(' generic_list ')'
| TKN_RP_ATTR '[' generic_list ']'

//// peering action pair ///////////////////////////////////////////////

import_peering_action_list: KEYW_FROM peering opt_action
| import_peering_action_list KEYW_FROM peering opt_action

export_peering_action_list: KEYW_TO peering opt_action
| export_peering_action_list KEYW_TO peering opt_action

//// import/export factor //////////////////////////////////////////////

import_factor: import_peering_action_list KEYW_ACCEPT filter

import_factor_list: import_factor ';'
| import_factor_list import_factor ';'

export_factor: export_peering_action_list KEYW_ANNOUNCE filter

export_factor_list: export_factor ';'
| export_factor_list export_factor ';'

//// import/export term ////////////////////////////////////////////////

import_term: import_factor ';'
| '{' import_factor_list '}'

export_term: export_factor ';'
| '{' export_factor_list '}'

//// import/export expression //////////////////////////////////////////

import_expression: import_term
| import_term KEYW_REFINE import_expression
| import_term KEYW_EXCEPT import_expression

export_expression: export_term
| export_term KEYW_REFINE export_expression
| export_term KEYW_EXCEPT export_expression

//// protocol ///////////////////////////////////////////////////////////

opt_protocol_from:
| KEYW_PROTOCOL tkn_word
opt_protocol_into:
| KEYW_INTO tkn_word

//**** import/export attributes ****************************************

import_attribute: ATTR_IMPORT
| ATTR_IMPORT opt_protocol_from opt_protocol_into import_factor

export_attribute: ATTR_EXPORT
| ATTR_EXPORT opt_protocol_from opt_protocol_into export_factor

opt_default_filter:
| KEYW_NETWORKS filter

default_attribute: ATTR_DEFAULT KEYW_TO peering

filter_attribute: ATTR_FILTER filter

peering_attribute: ATTR_PEERING peering

//**** inet-rtr class **************************************************

ifaddr_attribute: ATTR_IFADDR TKN_IPV4 KEYW_MASKLEN TKN_INT opt_action

//// peer attribute ////////////////////////////////////////////////////

opt_peer_options:
| peer_options

peer_options: peer_option
| peer_options ',' peer_option

peer_option: tkn_word '(' generic_list ')'

peer_id: TKN_IPV4
| TKN_DNS
| TKN_RTRSNAME
| TKN_PRNGNAME

peer_attribute: ATTR_PEER tkn_word peer_id opt_peer_options

//**** route class *****************************************************

aggr_bndry_attribute: ATTR_AGGR_BNDRY as_expression

aggr_mtd_attribute: ATTR_AGGR_MTD KEYW_INBOUND
| ATTR_AGGR_MTD KEYW_OUTBOUND opt_as_expression
//// inject attribute //////////////////////////////////////////////////

opt_inject_expression:
| KEYW_UPON inject_expression

inject_expression: inject_expression OP_OR inject_expression_term
| inject_expression_term

inject_expression_term: inject_expression_term OP_AND
                        inject_expression_factor
| inject_expression_factor

inject_expression_factor: '(' inject_expression ')'
| inject_expression_operand

inject_expression_operand: KEYW_STATIC
| KEYW_HAVE_COMPONENTS '{' opt_filter_prefix_list '}'
| KEYW_EXCLUDE '{' opt_filter_prefix_list '}'

inject_attribute: ATTR_INJECT opt_router_expression_with_at opt_action
                              opt_inject_expression

//// components attribute //////////////////////////////////////////////

opt_atomic:
| KEYW_ATOMIC

components_list:
| filter
| components_list KEYW_PROTOCOL tkn_word filter

components_attribute: ATTR_COMPONENTS opt_atomic components_list

//**** route-set *******************************************************

opt_rs_members_list: /* empty list */
| rs_members_list

rs_members_list: rs_member
| rs_members_list ',' rs_member

rs_member: TKN_ASNO
| TKN_ASNO OP_MS
| TKN_ASNAME
| TKN_ASNAME OP_MS
| TKN_RSNAME
| TKN_RSNAME OP_MS
| TKN_PRFXV4
| TKN_PRFXV4RNG

rs_members_attribute: ATTR_RS_MEMBERS opt_rs_members_list

//**** dictionary ******************************************************

rpattr_attribute: ATTR_RP_ATTR TKN_WORD methods
| ATTR_RP_ATTR TKN_RP_ATTR methods

methods: method
| methods method

method: TKN_WORD '(' ')'
| TKN_WORD '(' typedef_type_list ')'
| TKN_WORD '(' typedef_type_list ',' TKN_3DOTS ')'
| KEYW_OPERATOR TKN_OPERATOR '(' typedef_type_list ')'
| KEYW_OPERATOR TKN_OPERATOR '(' typedef_type_list ',' TKN_3DOTS ')'

//// typedef attribute  ////////////////////////////////////////////////

typedef_attribute: ATTR_TYPEDEF TKN_WORD typedef_type

typedef_type_list: typedef_type
| typedef_type_list ',' typedef_type

typedef_type: KEYW_UNION typedef_type_list
| KEYW_RANGE KEYW_OF typedef_type
| TKN_WORD
| TKN_WORD '[' TKN_INT ',' TKN_INT ']'
| TKN_WORD '[' TKN_REAL ',' TKN_REAL ']'
| TKN_WORD '[' enum_list ']'
| KEYW_LIST '[' TKN_INT ':' TKN_INT ']' KEYW_OF typedef_type
| KEYW_LIST KEYW_OF typedef_type

enum_list: tkn_word
| enum_list ',' tkn_word

//// protocol attribute ////////////////////////////////////////////////

protocol_attribute: ATTR_PROTOCOL tkn_word protocol_options

protocol_options:
| protocol_options protocol_option

protocol_option: KEYW_MANDATORY method
| KEYW_OPTIONAL method

//**** Token Definitions ***********************************************
//// flex macros used in token definitions /////////////////////////////
INT            [[:digit:]]+
SINT           [+-]?{INT}
REAL           [+-]?{INT}?\.{INT}({WS}*E{WS}*[+-]?{INT})?
NAME           [[:alpha:]]([[:alnum:]_-]*[[:alnum:]])?
ASNO           AS{INT}
ASNAME         AS-[[:alnum:]_-]*[[:alnum:]]
RSNAME         RS-[[:alnum:]_-]*[[:alnum:]]
RTRSNAME       RTRS-[[:alnum:]_-]*[[:alnum:]]
PRNGNAME       PRNG-[[:alnum:]_-]*[[:alnum:]]
FLTRNAME       FLTR-[[:alnum:]_-]*[[:alnum:]]
IPV4           [0-9]+(\.[0-9]+){3,3}
PRFXV4         {IPV4}\/[0-9]+
PRFXV4RNG      {PRFXV4}("^+"|"^-"|"^"{INT}|"^"{INT}-{INT})
ENAMECHAR      [^()<>,;:\\\"\.[\] \t\r]
ENAME          ({ENAMECHAR}+(\.{ENAMECHAR}+)*\.?)|(\"[^\"@\\\r\n]+\")
DNAME          [[:alnum:]_-]+
//// Token Definitions ////////////////////////////////////////////////
TKN_INT         {SINT}
TKN_INT         {INT}:{INT}             if each {INT} is two octets
TKN_INT         {INT}.{INT}.{INT}.{INT} if each {INT} is one octet
TKN_REAL        {REAL}
TKN_STRING      Same as in programming language C
TKN_IPV4        {IPV4}
TKN_PRFXV4      {PRFXV4}
TKN_PRFXV4RNG   {PRFXV4RNG}
TKN_ASNO        {ASNO}
TKN_ASNAME      (({ASNO}|peeras|{ASNAME}):)*{ASNAME}\
                (:({ASNO}|peeras|{ASNAME}))*
TKN_RSNAME      (({ASNO}|peeras|{RSNAME}):)*{RSNAME}\
                (:({ASNO}|peeras|{RSNAME}))*
TKN_RTRSNAME    (({ASNO}|peeras|{RTRSNAME}):)*{RTRSNAME}\
                (:({ASNO}|peeras|{RTRSNAME}))*
TKN_PRNGNAME    (({ASNO}|peeras|{PRNGNAME}):)*{PRNGNAME}\
                (:({ASNO}|peeras|{PRNGNAME}))*
TKN_FLTRNAME    (({ASNO}|peeras|{FLTRNAME}):)*{FLTRNAME}\
                (:({ASNO}|peeras|{FLTRNAME}))*
TKN_BOOLEAN     true|false
TKN_RP_ATTR     {NAME} if defined in dictionary
TKN_WORD        {NAME}
TKN_DNS         {DNAME}("."{DNAME})+
TKN_EMAIL       {ENAME}@({DNAME}("."{DNAME})+|{IPV4})

C. Changes from RFC 2280 ✨

Okay bestie, so RFC 2280 [3] is the earlier version of RPSL 🌸 This section is the recap of everything that's changed since then, and honestly there's a lot of glow-up here πŸ’…

  • It's now possible to write integers as a sequence of four 1-octet integers (e.g. 1.1.1.1) or as a sequence of two 2-octet integers (e.g. 3561:70). Check out Section 2 for the deets πŸ¦‹
  • The address prefix range definition got extended, so an address prefix is also an address prefix range now. See Section 2 🌈
  • The semantics for a range operator applied to a set containing address prefix ranges are now defined (e.g. {30.0.0.0/8^24-28}^27-30). Go look at Section 2 πŸ”₯
  • All dates are in UTC now πŸ•°οΈ See Section 2.
  • The plus (+) character is added to space and tab characters for splitting an attribute's value across multiple lines. So you can start the following lines with a space, a tab, or a plus (+) character ✨ See Section 2.
  • The withdrawn attribute of the route class is removed from the language πŸ’€ bye bye.
  • The filter-set class is introduced 🧚 See Section 5.4.
  • The rtr-set class is introduced πŸŽ€ See Section 5.5.
  • The peering-set class is introduced πŸ“ See Section 5.6.
  • Filters can now refer to filter-set names. See Section 5.4 πŸ’œ
  • Peerings can now refer to peering-set and rtr-set names. Both local and peer routers can be specified using router expressions. See Section 5.6 πŸ‘‘
  • The peer attribute of the inet-rtr class can now refer to peering-set and rtr-set names. See Section 9 🫢
  • The syntax and semantics of union and list types and the typedef attribute have changed. See Section 7 😭
  • In the initial dictionary, the typedef attribute defining the community_elm, and the rp-attribute defining the community attribute, have changed. See Section 7 πŸ’–
  • Guidelines for extending RPSL are added. See Section 10 🌸
  • Formal grammar rules are added. See Appendix B πŸ¦‹

D. Authors' Addresses πŸ’–

Cengiz Alaettinoglu
USC/Information Sciences Institute

EMail: cengiz@isi.edu
Curtis Villamizar
Avici Systems

EMail: curtis@avici.com
Elise Gerich
At Home Network

EMail: epg@home.net
David Kessens
Qwest Communications

EMail: David.Kessens@qwest.net
David Meyer
University of Oregon

EMail: meyer@antc.uoregon.edu
Tony Bates
Cisco Systems, Inc.

EMail: tbates@cisco.com
Daniel Karrenberg
RIPE NCC

EMail: dfk@ripe.net
Marten Terpstra
c/o Bay Networks, Inc.

EMail: marten@BayNetworks.com

Copyright (C) The Internet Society (1999). All Rights Reserved.

Okay, so the legal bit, but make it cute πŸ™ˆ You're totally allowed to copy and share this document, and to make derivative works that comment on it, explain it, or help with its implementation. You can prepare, copy, publish, and distribute them, in whole or in part, with no restrictions of any kind, as long as the copyright notice and this paragraph are included on every copy and derivative work.

BUT the document itself can't be modified in any way. That includes removing the copyright notice or the references to the Internet Society or other Internet organizations. The exceptions are: using it to develop Internet standards, in which case the copyright procedures in the Internet Standards process must be followed, or translating it into languages other than English.

The permissions above are perpetual, and the Internet Society and its successors or assigns won't revoke them 🫢

This document and the information in it are provided on an "AS IS" basis, and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED. That includes, but isn't limited to, any warranty that using the information here won't infringe any rights, and any implied warranties of merchantability or fitness for a particular purpose 😭

Acknowledgement 🫢

Funding for the RFC Editor function is currently provided by the Internet Society ✨

πŸ“¦ where this page comes from