DN42&OneManISP - Troubleshooting OSPF Source Address in a Coexistence Environment
Backstory As mentioned in the previous post of this series, because the VRF solution was too isolating, the DNS service I deployed on the HKG node (172.20.234.225) became inaccessible from the DN42 network. Research indicated this could be achieved by setting up veth or NAT forwarding, but due to the scarcity of available documentation, I ultimately abandoned the VRF approach. Structure Analysis This time, I planned to place both DN42 and clearnet BGP routes into the system's main routing table, then separate them for export using filters to distinguish which should be exported. For clarity, I stored the configuration for the DN42 part and the clearnet part (hereinafter referred to as inet) separately, and then included them from the main configuration file. Also, since there should ideally only be one kernel configuration per routing table, I merged the DN42 and inet kernel parts, keeping only one instance. After multiple optimizations and revisions, my final directory structure is as follows: /etc/bird/ ├─envvars ├─bird.conf: Main Bird config file, defines basic info (ASN, IP, etc.), includes sub-configs below ├─kernel.conf: Kernel config, imports routes into the system routing table ├─dn42 | ├─defs.conf: DN42 function definitions, e.g., is_self_dn42_net() | ├─ibgp.conf: DN42 iBGP template | ├─rpki.conf: DN42 RPKI route validation | ├─ospf.conf: DN42 OSPF internal network | ├─static.conf: DN42 static routes | ├─ebgp.conf: DN42 Peer template | ├─ibgp | | └<ibgp configs>: DN42 iBGP configs for each node | ├─ospf | | └backbone.conf: OSPF area | ├─peers | | └<ibgp configs>: DN42 Peer configs for each node ├─inet | ├─peer.conf: Clearnet Peer | ├─ixp.conf: Clearnet IXP connection | ├─defs.conf: Clearnet function definitions, e.g., is_self_inet_v6() | ├─upstream.conf: Clearnet upstream | └static.conf: Clearnet static routes I separated the function definitions because I needed to reference them in the filters within kernel.conf, so I isolated them for early inclusion. After filling in the respective configurations and setting up the include relationships, I ran birdc configure and it started successfully. So, case closed... right? Problems occurred After running for a while, I suddenly found that I couldn't ping the HKG node from my internal devices, nor could I ping my other internal nodes from the HKG node. Strangely, external ASes could ping my other nodes or other external ASes through my HKG node, and my internal nodes could also ping other non-directly connected nodes (e.g., 226(NKG)->225(HKG)->229(LAX)) via the HKG node. Using ip route get <other internal node address> revealed: root@iYoRoyNetworkHKG:/etc/bird# ip route get 172.20.234.226 172.20.234.226 via 172.20.234.226 dev dn42_nkg src 23.149.120.51 uid 0 cache See the problem? The src address should have been the HKG node's own DN42 address (configured on the OSPF stub interface), but here it showed the HKG node's clearnet address instead. Attempting to read the route learned by Bird using birdc s r for 172.20.234.226: root@iYoRoyNetworkHKGBGP:/etc/bird/dn42/ospf# birdc s r for 172.20.234.226 BIRD 2.17.1 ready. Table master4: 172.20.234.226/32 unicast [dn42_ospf_iyoroynet_v4 00:30:29.307] * I (150/50) [172.20.234.226] via 172.20.234.226 on dn42_nkg onlink Looks seemingly normal...? Theoretically, although the DN42 source IP is different from the usual, DN42 rewrites krt_prefsrc when exporting to the kernel to inform the kernel of the correct source address, so this issue shouldn't occur: protocol kernel kernel_v4{ ipv4 { import none; export filter { if source = RTS_STATIC then reject; + if is_valid_dn42_network() then krt_prefsrc = DN42_OWNIP; accept; }; }; } protocol kernel kernel_v6 { ipv6 { import none; export filter { if source = RTS_STATIC then reject; + if is_valid_dn42_network_v6() then krt_prefsrc = DN42_OWNIPv6; accept; }; }; } Regarding krt_prefsrc, it stands for Kernel Route Preferred Source. This attribute doesn't manipulate the route directly but instead attaches a piece of metadata to it. This metadata directly instructs the Linux kernel to prioritize the specified IP address as the source address for packets sent via this route. I was stuck on this for a long time. The Solution Finally, during an unintentional attempt, I added the krt_prefsrc rewrite to the OSPF import configuration as well: protocol ospf v3 dn42_ospf_iyoroynet_v4 { router id DN42_OWNIP; ipv4 { - import where is_self_dn42_net() && source != RTS_BGP; + import filter { + if is_self_dn42_net() && source != RTS_BGP then { + krt_prefsrc=DN42_OWNIP; + accept; + } + reject; + }; export where is_self_dn42_net() && source != RTS_BGP; }; include "ospf/*"; }; protocol ospf v3 dn42_ospf_iyoroynet_v6 { router id DN42_OWNIP; ipv6 { - import where is_self_dn42_net_v6() && source != RTS_BGP; + import filter { + if is_self_dn42_net_v6() && source != RTS_BGP then { + krt_prefsrc=DN42_OWNIPv6; + accept; + } + reject; + }; export where is_self_dn42_net_v6() && source != RTS_BGP; }; include "ospf/*"; }; After running this, the src address became correct, and mutual pinging worked. Configuration files for reference: KaguraiYoRoy/Bird2-Configuration
DN42&OneManISP - Using VRF to Run Clearnet BGP and DN42 on the Same Machine
Background Currently, clearnet BGP and DN42 each use a separate VPS in the same region, meaning two machines are required per region. After learning about VRF from a group member, I explored using VRF to enable a single machine to handle both clearnet BGP and DN42 simultaneously. Note: Due to its isolation nature, the VRF solution will prevent DN42 from accessing services on the host. If you need to run services (like DNS) on the server for DN42, you might need additional port forwarding or veth configuration, which is beyond the scope of this article. (This is also the reason why I ultimately did not adopt VRF in my production environment). Advantages of VRF Although DN42 uses private IP ranges and internal ASNs, which theoretically shouldn't interfere with clearnet BGP, sharing the same routing table can lead to issues like route pollution and management complexity. VRF (Virtual Routing and Forwarding) allows creating multiple routing tables on a single machine. This means we can isolate DN42 routes into a separate routing table, keeping them apart from the clearnet routing table. The advantages include: Absolute Security and Policy Isolation: The DN42 routing table is isolated from the clearnet routing table, fundamentally preventing route leaks. Clear Operation and Management: Use commands like birdc show route table t_dn42 and birdc show route table t_inet to view and debug two completely independent routing tables, making things clear at a glance. Fault Domain Isolation: If a DN42 peer flaps, the impact is confined to the dn42 routing table. It won't consume routing computation resources for the clearnet instance nor affect clearnet forwarding performance. Alignment with Modern Network Design Principles: Using VRF for different routing domains (production, testing, customer, partner) is standard practice in modern network engineering. It logically divides your device into multiple virtual routers. Configuration System Part Creating the VRF Interface Use the following commands to create a VRF device named dn42-vrf and associate it with the system's routing table number 1042: ip link add dn42-vrf type vrf table 1042 ip link set dev dn42-vrf up # Enable it You can change the routing table number according to your preference, but avoid the following reserved routing table IDs: Name ID Description unspec 0 Unspecified, rarely used main 254 Main routing table, where most ordinary routes reside default 253 Generally unused, reserved local 255 Local routing table, contains 127.0.0.1/8, local IPs, broadcast addresses, etc. Cannot be modified Associating Existing Network Interfaces with VRF In my current DN42 setup, several WireGuard interfaces and a dummy interface are used for DN42. Therefore, associate these interfaces with the VRF: ip link set dev <interface_name> master dn42-vrf Note: After associating an interface with a VRF, it might lose its IP addresses. Therefore, you need to readd the addresses, for example: ip addr add 172.20.234.225 dev dn42 After completion, ip a should show the corresponding interface's master as dn42-vrf: 156: dn42: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue master dn42-vrf state UNKNOWN group default qlen 1000 link/ether b6:f5:28:ed:23:04 brd ff:ff:ff:ff:ff:ff inet 172.20.234.225/32 scope global dn42 valid_lft forever preferred_lft forever inet6 fd18:3e15:61d0::1/128 scope global valid_lft forever preferred_lft forever inet6 fe80::b4f5:28ff:feed:2304/64 scope link valid_lft forever preferred_lft forever Persistence I use ifupdown2 to automatically load the dummy interface and VRF device on boot. auto dn42-vrf iface dn42-vrf inet manual vrf-table 1042 auto dn42 iface dn42 inet static pre-up ip link add $IFACE type dummy || true vrf dn42-vrf address <IPv4 Address>/32 address <IPv6 Address>/128 post-down ip link del $IFACE My dummy interface is named dn42; modify accordingly if yours is different. After creation, use ifup dn42-vrf && ifup dn42 to start the dummy interface. Note: The number prefix for the VRF device file should be smaller than that of the dummy interface file, ensuring the VRF device starts first. WireGuard Tunnels Add PostUp commands to associate them with the VRF and readd their addresses. Example: [Interface] PrivateKey = [Data Redacted] ListenPort = [Data Redacted] Table = off Address = fe80::2024/64 + PostUp = ip link set dev %i master dn42-vrf + PostUp = ip addr add fe80::2024/64 dev %i PostUp = sysctl -w net.ipv6.conf.%i.autoconf=0 [Peer] PublicKey = [Data Redacted] Endpoint = [Data Redacted] AllowedIPs = 10.0.0.0/8, 172.20.0.0/14, 172.31.0.0/16, fd00::/8, fe00::/8 Then restart the tunnel. Bird2 Part First, define two routing tables for DN42's IPv4 and IPv6: ipv4 table dn42_table_v4; ipv6 table dn42_table_v6 Then, specify the VRF and system routing table number in the kernel protocol, and specify the previously created v4/v6 routing tables in the IPv4/IPv6 sections: protocol kernel dn42_kernel_v6{ + vrf "dn42-vrf"; + kernel table 1042; scan time 20; ipv6 { + table dn42_table_v6; import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = DN42_OWNIPv6; accept; }; }; }; protocol kernel dn42_kernel_v4{ + vrf "dn42-vrf"; + kernel table 1042; scan time 20; ipv4 { + table dn42_table_v4; import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = DN42_OWNIP; accept; }; }; } For protocols other than kernel, add the VRF and the independent IPv4/IPv6 tables, but do not specify the system routing table number: protocol static dn42_static_v4{ + vrf "dn42-vrf"; route DN42_OWNNET reject; ipv4 { + table dn42_table_v4; import all; export none; }; } protocol static dn42_static_v6{ + vrf "dn42-vrf"; route DN42_OWNNETv6 reject; ipv6 { + table dn42_table_v6; import all; export none; }; } In summary: Configure a VRF and the previously defined routing tables for everything related to DN42. Only the kernel protocol needs the system routing table number specified; others do not. Apply the same method to BGP, OSPF, etc. However, I chose to use separate Router IDs for the clearnet and DN42, so a separate Router ID needs to be configured: # /etc/bird/dn42/ospf.conf protocol ospf v3 dn42_ospf_iyoroynet_v4 { + vrf "dn42-vrf"; + router id DN42_OWNIP; ipv4 { + table dn42_table_v4; import where is_self_dn42_net() && source != RTS_BGP; export where is_self_dn42_net() && source != RTS_BGP; }; include "ospf/*"; }; protocol ospf v3 dn42_ospf_iyoroynet_v6 { + vrf "dn42-vrf"; + router id DN42_OWNIP; ipv6 { + table dn42_table_v6; import where is_self_dn42_net_v6() && source != RTS_BGP; export where is_self_dn42_net_v6() && source != RTS_BGP; }; include "ospf/*"; }; # /etc/bird/dn42/ebgp.conf ... template bgp dnpeers { + vrf "dn42-vrf"; + router id DN42_OWNIP; local as DN42_OWNAS; path metric 1; ipv4 { + table dn42_table_v4; ... }; ipv6 { + table dn42_table_v6; ... }; } include "peers/*"; After completion, reload the configuration with birdc c. Now, we can view the DN42 routing table separately using ip route show vrf dn42-vrf: root@iYoRoyNetworkHKGBGP:~# ip route show vrf dn42-vrf 10.26.0.0/16 via inet6 fe80::ade0 dev dn42_4242423914 proto bird src 172.20.234.225 metric 32 10.29.0.0/16 via inet6 fe80::ade0 dev dn42_4242423914 proto bird src 172.20.234.225 metric 32 10.37.0.0/16 via inet6 fe80::ade0 dev dn42_4242423914 proto bird src 172.20.234.225 metric 32 ... You can also ping through the VRF using the -I dn42-vrf parameter: root@iYoRoyNetworkHKGBGP:~# ping 172.20.0.53 -I dn42-vrf ping: Warning: source address might be selected on device other than: dn42-vrf PING 172.20.0.53 (172.20.0.53) from 172.20.234.225 dn42-vrf: 56(84) bytes of data. 64 bytes from 172.20.0.53: icmp_seq=1 ttl=64 time=3.18 ms 64 bytes from 172.20.0.53: icmp_seq=2 ttl=64 time=3.57 ms 64 bytes from 172.20.0.53: icmp_seq=3 ttl=64 time=3.74 ms 64 bytes from 172.20.0.53: icmp_seq=4 ttl=64 time=2.86 ms ^C --- 172.20.0.53 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3006ms rtt min/avg/max/mdev = 2.863/3.337/3.740/0.341 ms Important Notes If the VRF device is reloaded, all devices originally associated with the VRF need to be reloaded as well, otherwise they won't function correctly. Currently, DN42 cannot access services inside the host configured with VRF. A future article might explain how to allow traffic within the VRF to access host services (Adding to the TODO list). I learned from a friend that by setting net.ipv4.tcp_l3mdev_accept=1 and net.ipv4.udp_l3mdev_accept=1, it is possible to allow the listening sockets in the global space to accept connection requests from the VRF domain, thus achieving cross-vrf listening services. Reference Articles:： Run your MPLS network with BIRD
OneManISP - Ep.2 Announcing Our Own IP Prefix to the World
Preface In the previous article, we successfully registered an ASN and obtained an IPv6 address block. Now, we will announce this block to the world. Setting Up the Subnet Object in the RIPE Database It's important to note that the minimum IPv6 prefix allowed for announcement on the public internet is /48. This means if you only have a single /48 block, you cannot break it down into smaller segments. Therefore, I later leased a separate /40 block, intending to split it into multiple /48s for announcement. The IPv6 block I obtained is 2a14:7583:f200::/40, and I plan to split out 2a14:7583:f203::/48 for use with Vultr. If you don't need to split your block, please skip directly to the "Creating the Route Object" section. Splitting the Prefix First, go to Create "inet6num" object - RIPE Database and fill in the following: inet6num: The IP block you want to split out, in CIDR format. netname: Network name. country: The country to which the IP block belongs, must conform to the ISO 3166 standard (can be selected directly in the RIPE DB). admin-c: The primary key value of the Role object created earlier. tech-c: The primary key value of the Role object created earlier. status: Keep ASSIGNED This step splits a smaller /48 address block from your obtained allocation. Creating the Route Object Go to Create "route6" object - RIPE Database and fill in the following: route6: The IPv6 address block you intend to announce, in CIDR format. origin: The ASN you applied for, including the 'AS' prefix. This step declares that your ASN is permitted to use this address block for originating BGP routes. Applying for BGP Session with a VPS Provider This time I'm using a machine from Vultr. Their BGP Session setup is very beginner-friendly, with their own validation system. Furthermore, their upstream has good filters ensuring that incorrect route advertisements generally won't affect the public internet. (I forgot to take screenshots during my configuration, but you can refer to the section 申请 Vultr 的 BGP 广播功能 in Bao Shuo's article 年轻人的第一个 ASN for reference.) Go to BGP - Vultr.com, select Get Started, and fill in your ASN and IPv6 block information as required. For the LOA (Letter Of Authorization), you can refer to this template: LOA-template.docx (I rewrote one for individuals as most templates found online are for companies). After submission, the system will automatically create a ticket, and you will see your ASN and IP block in a pending verification state: Click Start, and the system will send a verification email to the abuse-mailbox email address registered with your Role object: The received email looks like this: The top link represents approving the authorization for Vultr to announce your IP block, and the bottom one is for disapproval. Click the top link, which will take you to Vultr's webpage: Then click Approve Announcement. Both the ASN and the IP block need to be verified once. Next, wait for the Vultr staff to review and complete the process. Then, in your VPS control panel, you will see the BGP tab, where you can find the upstream information: I must commend Vultr's ticket efficiency here; it took me an average of only about 10 minutes from creating the ticket requesting authorization to completion. (In contrast, the average weekday ticket response time at iFog GmbH was around 1 day, which is much slower in comparison). The process with other VPS providers is generally similar. You need to inform their staff of the ASN and IP block you want to announce. After verifying ownership, the staff will configure the corresponding BGP Session for you. Advertisement! You should have received the following information from your upstream: Upstream's ASN Upstream's IP address for the BGP Session (Optional) Password The operating system I use is Debian 12 Bookworm, using Bird2 as the routing software. I updated Bird2 to the latest version following the section "Update Bird2 to v2.16 or above" in this article. The upstream ASN Vultr gave me is 64515, the upstream BGP Session address is 2001:19f0:ffff::1, and the VPS's BGP Session address is 2001:19f0:0006:0ff5:5400:05ff:fe96:881f. My Bird2 configuration file is modified from the configuration file used in DN42: log syslog all; define OWNAS = 205369; define OWNIPv6 = 2a14:7583:f203::1; define OWNNETv6 = 2a14:7583:f203::/48; define OWNNETSETv6 = [ 2a14:7583:f203::/48+ ]; router id 45.77.x.x; protocol device { scan time 10; } function is_self_net_v6() { return net ~ OWNNETSETv6; } protocol kernel { scan time 20; ipv6 { import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = OWNIPv6; accept; }; }; }; protocol static { route OWNNETv6 reject; ipv6 { import all; export none; }; } template bgp upstream { local as OWNAS; path metric 1; multihop; ipv6 { import filter { if net ~ [::/0] then reject; accept; }; export filter { if is_self_net_v6() then accept; reject; }; import limit 1000 action block; }; graceful restart; } protocol bgp 'Vultr_v6' from upstream{ local 2001:19f0:0006:0ff5:5400:05ff:fe96:881f as OWNAS; password "123456"; neighbor 2001:19f0:ffff::1 as 64515; } A few noteworthy points: The import rule in the upstream template here rejects the default route. This prevents the routing table sent by the upstream from overwriting local default gateway routes and other routing information. If we have multiple BGP neighbors, this could cause detours or even routing loops. The upstream template specifies multihop (multihop;) because Vultr's BGP peer is not directly reachable. Without setting multihop, the BGP session would get stuck in the Idle state. If your BGP upstream is directly connected, you can omit this line or set it to direct;. After filling in the configuration file, run birdc configure to load the configuration. Run birdc show protocols to check the status. If all goes well, you should see the BGP session state as Established: At this point, you can take a break and wait for global routing convergence. After about half an hour, open bgp.tools and query your /48 block. You should see that it has been successfully received by the global internet, and you can see our upstream information: Next, we create a dummy interface on the VPS and assign a single IPv6 address from the block allocated for this machine. For example, I assigned 2a14:7583:f203::1 to my machine: ip link add dummy0 type dummy ip addr add 2a14:7583:f203::1/128 dev dummy0 Then, using your own PC, you should be able to ping this address, and traceroute will show the complete routing path: Thanks to Mi Lu for the technical support! Reference Articles: 自己在家开运营商 Part.2 - 向世界宣告 IP 段 (BGP Session & BIRD) 年轻人的第一个 ASN - 宝硕博客 BGPlayer 从零开始速成指北 - 开通 Vultr 的 BGP 广播功能 - AceSheep BGP (2) 在 Vultr 和 HE 使用自己的 IPV6 地址 - 131's Blog
Introduction This article documents my complete process of applying for an ASN through the RIPE NCC. The content is suitable for beginners. If you find any errors, please feel free to contact me via email, and I will correct them promptly. Now that we've learned the basic BGP concepts on DN42, it seems a bit of a waste not to play with the public internet, right? Basic Concepts Currently, the allocation of public ASN and IP resources is managed by five Regional Internet Registries (RIRs) worldwide: ARIN: Manages the North American region. RIPE NCC: Manages the European region. APNIC: Manages the Asia-Pacific region. LACNIC: Manages the Latin American region. AfriNIC: Manages the African region. RIRs do not provide services directly to end users. Instead, they allocate resources to Local Internet Registries (LIRs), which then assign them to end users. Of course, individual users can also register as an LIR, but this is generally not cost-effective. If you're willing to pay thousands of dollars in annual fees, then forget I said that. Among these, RIPE NCC is considered more friendly towards individual applications, followed by ARIN and APNIC. Compared to RIPE NCC, APNIC's fees are generally about 30% higher. Furthermore, RIPE NCC provides an online management system allowing users to modify information and check progress themselves, whereas with APNIC, you typically need to contact an LIR for changes. Overall, I chose to apply for an ASN through the RIPE NCC. The resources obtained (both ASN and IPs) are generally categorized into two types: PA (Provider Aggregatable) Resources: Belong to the LIR and are assigned for your use by the LIR. PI (Provider Independent) Resources: Belong to you directly. These are generally more expensive. Preparation Stage Choosing an LIR Search online for LIR Service to find many companies offering such services. Currently, RIPE NCC charges an annual administrative fee of 50 EUR for PI resources. This means the cost from an LIR for registering an ASN generally won't be lower than 50 EUR per year (approximately 60 USD at the time of writing). Here, I chose NoPKT LLC, recommended by peers. Their pricing is quite reasonable and includes a /48 block of PA IPv6 addresses with the ASN. The activation speed was also very fast – it only took half a day from submitting the required documents to getting the ASN. Preparing Documents Proof of Identity Individual: Provide an ID card or passport (I submitted photos of the front and back of my national ID card). Company: Provide a valid business license. If the applicant is a minor, usually written consent from their legal guardian is required, and the guardian must fulfill corresponding responsibilities. All submitted documents must be authentic and valid, and should be originals or notarized copies. Contact Information Postal Address: Used for registration in the RIPE Database. Technical Contact Email. Abuse Contact Email. Technical Justification Billing from a BGP-capable provider within the European region. Options include Vultr, BuyVM, iFog, V.PS, etc. Note: Vultr uses a post-payment system, generating invoices at the beginning of the month. If you need the documents ready quickly, consider other providers. ASNs of two upstream providers you plan to connect to. (In practice, the reviewers won't strictly verify the specific upstream ASNs you list. Therefore, you can fill in common, publicly known ASNs to make it look reasonable. Don't overthink it too much. You can even put mine.) Registering a RIPE DB Account and Creating Objects Go to the RIPE Database and register an account. For Chinese, it's recommended to use the 拼音(Pinyin) of your real legal name. Enabling 2FA is mandatory, so please install a TOTP app on your phone beforehand. Creating a Role Object and Maintainer Object Go to Create role and maintainer pair - RIPE Database to create a role object. Here, a 'role' is an abstract concept describing the contact information for a team, department, or functional role – it represents a role, such as NOC (Network Operations Center), Abuse Team, Hostmaster, etc. mntner: The identifier for the maintainer object. It can contain uppercase/lowercase letters, numbers, and -_. For example, I used IYOROY-MNT. role: The name for the role object. It can contain uppercase/lowercase letters, numbers, and ][)(._"*@,&:!'+/-. For example, I used IYOROY-NETWORK-NOC. address: The office address for this role. e-mail: The email address for this role. Click SUBMIT after filling out the form to create both the role object and the maintainer object. Please note the returned primary key name, which usually ends with -RIPE. You will need this for future modifications and submissions to the LIR. The maintainer object identifier here is conceptually different from the role object. The maintainer signifies who has the authority to maintain (create/modify/delete) objects in the database – it's the maintaining entity. The relationship between different concepts in the RIPE Database can be referenced in the diagram later in the article. Adding an Abuse Contact Mailbox Go to Query - RIPE Database and search for the primary key of the role you just created. You should find the entry you created. Click "Update Object" on the right. Click the plus sign (+) next to the email field to add an abuse-mailbox attribute and fill in your abuse contact email address: Click SUBMIT to save. Note: RIPE periodically checks if the abuse-mailbox is functional. Please ensure you provide a real, active email address. Creating an Organization Object The Organization object here is an abstraction of a legal entity or organization (company, university, ISP, individual user, etc.). It serves as the top-level ownership information for resource objects (like aut-num, inetnum, inet6num) in the RIPE Database. This means subsequent ASN and IP resources will be assigned to this Organization object. Go to Create Organization - RIPE Database and fill in the following information: organisation: A unique ID. Keep it as the default AUTO-1 to let RIPE NCC assign one. org-name: The name of the organization. For Chinese individuals, use your full name in Pinyin. address: Postal address. country: Country code, refer to ISO 3166. For China, use CN. e-mail: The organization's email address. admin-c / tech-c: Administrative and technical contact objects (referencing the role handle). abuse-c: Specifies the abuse contact (must be a role object linked to the abuse-mailbox in that role). mnt-ref: Specifies which maintainer(s) can create objects referencing this organisation. mnt-by: Specifies who can maintain this organisation object itself. Click SUBMIT after filling out the form and note the returned object identifier, which follows a format like ORG-XXXX-RIPE. If you need to make changes after submission, go to Query - RIPE Database and search for the previously noted Role primary key or the Organization object identifier to find the update option. Paying the LIR Fee and Submitting Documents Submit the following documents to your chosen LIR: Proof of Identity Full Name Address (recommended to match your ID document) Photos of the front and back of your ID card RIPE Database Information org: Organization object identifier as-name: AS Name admin-c: Primary key of the role object created earlier tech-c: Primary key of the role object created earlier abuse-c: Primary key of the role object created earlier nic-hdl: Primary key of the role object created earlier mnt-by: Name of the maintainer object created earlier Technical Justification VPS Bill/Invoice Upstream ASNs The LIR will likely ask you to add a mnt-ref attribute to your Organization object, pointing to the LIR's maintainer. This allows the LIR to assign the AS and IP resources to your Organization. Once the LIR reviews and approves your application, they will submit the request to RIPE. Then, it's a waiting game. Generally, it takes 3-5 working days to get your ASN. At this point, we have successfully registered our own ASN on the public internet. Supplement: Relationships Between Concepts in the RIPE Database graph LR %% ========== ORG Layer ========== subgraph Org["Organisation"] ORG["organisation\n(ORG-XXX-RIPE)"] end %% ========== Resource Layer ========== subgraph Resource["Resources"] INETNUM["inetnum\n(IPv4 Block)"] INET6NUM["inet6num\n(IPv6 Block)"] AUTNUM["aut-num\n(ASN)"] ASSET["as-set\n(ASN Set)"] end %% ========== Routing Layer ========== subgraph Routing["Routing"] ROUTE["route\n(IPv4 Route Announcement)"] ROUTE6["route6\n(IPv6 Route Announcement)"] end %% ========== Contact Layer ========== subgraph Contact["Contacts"] ROLE["role\n(Team/Function)\nnic-hdl"] PERSON["person\n(Individual)\nnic-hdl"] end %% ========== Authorization Layer ========== subgraph Maintainer["Authorization"] MNT["mntner\n(Maintainer)"] end %% ========== Contact Links ========== INETNUM --> ROLE INET6NUM --> ROLE AUTNUM --> ROLE ASSET --> ROLE ROUTE --> ROLE ROUTE6 --> ROLE ROLE --> PERSON %% ========== Organization Assignment ========== ORG --> INETNUM ORG --> INET6NUM ORG --> AUTNUM ORG --> ASSET %% ========== Authorization ========== ORG --> MNT INETNUM --> MNT INET6NUM --> MNT AUTNUM --> MNT ASSET --> MNT ROUTE --> MNT ROUTE6 --> MNT ROLE --> MNT PERSON --> MNT %% ========== Route Binding ========== ROUTE -->|origin| AUTNUM ROUTE6 -->|origin| AUTNUM %% ========== Route Scope ========== ROUTE -->|belongs to| INETNUM ROUTE6 -->|belongs to| INET6NUM Special thanks to Mi Lu for providing technical support and answering questions! Reference Articles: 自己在家开运营商 Part.1 - 注册一个 ASN - LYC8503 从0开始注册一个ASN并广播IP | Pysio's Home 青年人的第一个运营商：注册一个 ASN | liuzhen932 的小窝
Foreword I am a novice in BGP. This article may contain imprecise content/naive understandings/elementary mistakes. I kindly ask the experts to be lenient. If you find any issues, you are welcome to contact me via email, and I will correct them as soon as possible. If you find this unacceptable, it is recommended to close this article now. What are BGP Communities? TL;DR: BGP Communities "tag" routes, allowing others to use these tags for route optimization. This concept might be unfamiliar to newcomers (like me), who might not initially understand its purpose. Simply put, BGP Communities are a mechanism for tagging routes, similar to adding labels. They allow network administrators to attach one or more "tags" (i.e., community values) to routes propagated via BGP. These tags themselves do not alter the route's path attributes (like AS_PATH, LOCAL_PREF, MED, etc.), but they provide a signaling mechanism to indicate what policy or processing should be applied to that route by other routers within the same AS or in downstream peer ASes. BGP Communities can be used to: Simplify Policy Configuration: Routers inside the network or in downstream ASes only need to configure policies based on community values (like setting LOCAL_PREF, adding NO_EXPORT, applying route-maps, etc.), without needing to know the specific prefix details. This makes policies more centralized, easier to manage, and less prone to errors. Convey Policy Intent to Downstream ASes: An AS can attach community values to routes it advertises to its downstream customer or peer ASes. These communities convey requirements or suggestions on how these routes should be handled, such as route optimization based on geographic location, latency, or bandwidth. Coordinate Policies within an AS: Inside a large AS, when using IBGP full-mesh or route reflectors, edge routers (receiving EBGP routes or redistributing routes) can tag routes with community values. Core routers or route reflectors within the AS can recognize these communities and apply corresponding internal policies (like setting LOCAL_PREF, MED, deciding whether to advertise to certain IBGP peers, adding other communities, etc.), without needing complex prefix-based policies on every internal router. DN42 has its own set of Communities specifications. For details, please refer to: BGP-communities - DN42 Wiki Configuration The general idea and approach in this article are largely based on Xe_iu's method, focusing on adding BGP Communities for geographic information and performing route optimization. Generally, this is sufficient. (Another reason is that I haven't fully grasped the others yet) Note: We should ONLY add geographic information-related BGP Communities to routes originating from our own AS. We should not add such entries to routes received from neighbors. Adding our own regional Communities to a neighbor's routes constitutes forging the route origin, potentially leading to route hijacking. Downstream networks might misjudge the traffic path, routing traffic that should be direct through your network, increasing latency and consuming your network's bandwidth. (Large Communities are an exception, as they have a verification mechanism to prevent this, but that's beyond the scope of this article). The idea is clear. When exporting routes, we first need to verify if it's our own route. If it is, we add the communities tag to the route. The sample configuration provided by DN42 already includes two functions, is_self_net() and is_self_net_v6(), to check if a route is our own. Therefore, writing the configuration part is straightforward. Adding Communities to Routes First, we need to define the geographic region information for the current node at the beginning of the node configuration file. Please check BGP-communities - DN42 Wiki for details: define DN42_REGION = 52; # 52 represents East Asia define DN42_COUNTRY= 1344; # 1344 represents Hong Kong Please modify these values according to your node's actual geographic location. Then, modify the export filter in the dnpeers template: }; export filter { - if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then accept; + if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then{ + if (is_self_net()) then { # Check if it's our own route + bgp_community.add((64511, DN42_REGION)); # Add continent-level region info + bgp_community.add((64511, DN42_COUNTRY)); # Add country/region info + } + accept; + } reject; }; import limit 1000 action block; During export, it checks if it's our own route. If it is, it sets the bgp_community according to the defined DN42_REGION and DN42_COUNTRY. Here, 64511 is the reserved public AS number identifier specifically for geographic tags (Region/Country); you can just copy it. Apply the same method to the IPv6 export rules, but replace is_self_net() with is_self_net_v6(). Route Optimization Based on Communities Here we need to introduce another concept: local_pref (Local Preference). It is used within an AS to indicate the priority of a route. Its default value is 100, and a higher value indicates higher priority. Furthermore, in BGP route selection logic, local_pref has the highest priority, even higher than AS_PATH length. This means that by setting local_pref, we can adjust the priority of routes to achieve route optimization. Combining this with the BGP Communities mentioned above, we can set the corresponding local_pref based on Communities for optimization. Also, since BGP.local_pref is propagated within the AS, we need to modify the import logic for both eBGP and iBGP routes. My logic for handling BGP.local_pref here references (basically copies) Xe_iu's approach: For routes from the same region, priority +10 For routes from the same country, priority +5 additionally For routes received via direct peering with us, priority +20 additionally Create a function to calculate the priority: function ebgp_calculate_priority() { int priority = 100; # Base priority # Same region detection (+10) if bgp_community ~ [(64511, DN42_REGION)] then priority = priority + 10; # Same country detection (+5) if bgp_community ~ [(64511, DN42_COUNTRY)] then priority = priority + 5; # Direct eBGP neighbor detection (+20) if bgp_path.len = 1 then priority = priority + 20; return priority; } Then, in the import filter within the dnpeers template, set bgp_local_pref to the value calculated by the function: template bgp dnpeers { local as OWNAS; path metric 1; ipv4 { import filter { if is_valid_network() && !is_self_net() then { if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } + bgp_local_pref = ebgp_calculate_priority(); accept; } reject; }; Apply the same method for IPv6. After running birdc configure, we should be able to see that our routes have been tagged with Communities labels at our neighbors: (Screenshot source:https://lg.milu.moe/route_all/hk/172.20.234.224) Special thanks to Nuro Trace and Xe_iu. They helped deepen my understanding of BGP Communities and provided much assistance. Reference Articles: [DN42] bird2的配置文件 – Xe_iu's Blog | Xe_iu的杂物间 [DN42] 谈一谈如何配置 BGP community – Xe_iu's Blog | Xe_iu的杂物间 BGP-communities - DN42 Wiki
Foreword I am a novice in BGP. This article may contain imprecise content/naive understandings/elementary mistakes. I kindly ask the experts to be lenient. If you find any issues, you are welcome to contact me via email, and I will correct them as soon as possible. If you find this unacceptable, it is recommended to close this article now. Welcome to peer with me! For details, please visit: iYoRoy DN42 Network I wanted to study BGP, but renting an ASN and IP block is too expensive, and I was afraid of taking down half the internet due to a configuration error. So, I decided to look into DN42, a virtual network. DN42 is a large decentralized network that uses the BGP protocol for routing, making its structure very similar to today's internet. This makes it suitable for learning networking technologies like BGP. In DN42, everyone plays the role of an ISP (Internet Service Provider), peering with other users to join and participate in building the entire DN42 network. DN42 operates on 172.20.0.0/14 and fd00::/8, both of which are private address ranges, so it won't affect the clearnet. Registration You need to know basic git commands, GPG, and Linux. It's also best to sign your commits with GPG. If you have basic Git knowledge, you can refer to my commit: Add AS4242422024 · d1f9046ecb - registry - dn42 git Fork and Clone the DN42 Registry Git Repository Register an account on dn42 git and Fork the dn42/registry repository. Clone your forked repository and enter it. Register a Contact Create a file named <Nickname>-DN42 under data/person and fill in the following content: person: <Nickname> e-mail: <Email Address> pgp-fingerprint: <GPG Key Fingerprint> nic-hdl: <NIC Handle> mnt-by: <Maintainer> source: DN42 person: Nickname. e-mail: Email address. pgp-fingerprint: GPG key fingerprint, used for some authentication services. nic-hdl: NIC handle, points to the file itself, just use the current filename. mnt-by: Maintained by, points to the maintainer information from the Register Maintainer section below. source: Source, keep it as DN42. www: Optional, your website URL. {collapse} {collapse-item label="Example"} data/person/IYOROY-DN42 @ dn42/registry@master person: Kagura iYoRoy e-mail: i@iyoroy.cn www: https://www.iyoroy.cn pgp-fingerprint: 3ECCFFDEC2CB4CB8DA8089BE9AF2F2E03CE8FD67 nic-hdl: IYOROY-DN42 mnt-by: IYOROY-MNT source: DN42 {/collapse-item} {/collapse} Register a Maintainer Create a file data/mntner/<Nickname>-MNT and fill in the following content: mntner: <Nickname>-MNT admin-c: <Contact> tech-c: <Contact> auth: <Authentication Method> mnt-by: <Maintainer> source: DN42 mntner: Maintainer name, generally the same as the filename. admin-c: Administrator contact information, points to a file in the person folder, use the filename from the Register Contact section above. tech-c: Technical contact information, points to a file in the person folder, use the filename from the Register Contact section above. auth: Authentication method, supports pgp or ssh-key. mnt-by: Maintained by, generally points to the filename itself. source: Source, keep it as DN42. {collapse} {collapse-item label="Example"} data/mntner/IYOROY-MNT @ dn42/registry@master mntner: IYOROY-MNT admin-c: IYOROY-DN42 tech-c: IYOROY-DN42 auth: pgp-fingerprint 3ECCFFDEC2CB4CB8DA8089BE9AF2F2E03CE8FD67 mnt-by: IYOROY-MNT source: DN42 {/collapse-item} {/collapse} Register an ASN On the clearnet, 4200000000 - 4294967294 is the reserved range for ASNs. DN42 uses 4242420000 - 4242429999, and currently, 4242420000 - 4242423999 is open for registration.The official recommendation is not to manually find an available ASN but to use Burble's DN42 Free ASN Explorer to select an available ASN for registration. After choosing your desired ASN, create a file <ASN, including AS letters> in data/aut-num and fill in the following content: aut-num: <ASN> as-name: <Autonomous System Name> descr: <Autonomous System Description> admin-c: <Administrator NIC Handle> tech-c: <Technical Contact NIC Handle> mnt-by: <Maintainer> source: DN42 aut-num: Your chosen ASN, including the AS prefix. It should be in the format AS424242xxxx. as-name: Autonomous System Name. descr: Autonomous System Description, can contain spaces. admin-c: Administrator contact information, points to a file in the person folder, use the filename from the Register Contact section above. tech-c: Technical contact information, points to a file in the person folder, use the filename from the Register Contact section above. mnt-by: Maintained by, points to the filename from the Create Maintainer section above. source: Source, keep it as DN42. {collapse} {collapse-item label="示例"} data/aut-num/AS4242422024 @ dn42/registry@master aut-num: AS4242422024 as-name: IYOROYNET-AS-DN42 descr: iYoRoy DN42 Network admin-c: IYOROY-DN42 tech-c: IYOROY-DN42 mnt-by: IYOROY-MNT source: DN42 {/collapse-item} {/collapse} Register an IPv4 Block and Add Route You can skip this section if you don't want to register IPv4 The official provides a tool for finding available address blocks: DN42 Free IPv4 Explorer. It is also not recommended to manually specify but to use the available blocks found through the search tool. DN42 also faces IPv4 address shortages, so please register according to your needs. Generally, a /27 block containing 30 usable IPv4 addresses is sufficient. For smaller networks, you can apply for /28 (14 usable addresses) and /29 (6 usable addresses). The largest block that can be directly applied for is /26, which is 62 addresses. Applying for /25, /24 requires submitting an application and waiting for review. For details, refer to: DN42 Experimental Network: Intro and Registration (Updated 2022-12) - Lan Tian @ Blog After selecting your IPv4 block, create a file in data/inetnum named after the CIDR format of the IPv4 block, using _ instead of / (e.g., if the applied IPv4 block is 172.20.234.224/28, the filename is 172.20.234.224_28), and fill in the content: inetnum: <First IPv4 Address in Block> - <Last IPv4 Address in Block> cidr: <IPv4 Block in CIDR Format> netname: <IPv4 Block Name> descr: <IPv4 Block Description> country: <Country Code for IPv4 Block> admin-c: <Administrator NIC Handle> tech-c: <Technical Contact NIC Handle> mnt-by: <Maintainer> status: ASSIGNED source: DN42 inetnum: IPv4 address range, from the first address to the last address, connected with -. cidr: IPv4 block in CIDR format, do not replace / with _. netname: IPv4 block name, fill in as desired, no special requirements. descr: IPv4 block description, can contain spaces. country: Two-character country code from ISO 3166, for China, fill in CN. admin-c: Administrator contact information, points to a file in the person folder, use the filename from the Register Contact section above. tech-c: Technical contact information, points to a file in the person folder, use the filename from the Register Contact section above. mnt-by: Maintained by, points to the filename from the Create Maintainer section above. status: Status, keep it as ASSIGNED. source: Source, keep it as DN42. {collapse} {collapse-item label="Example"} data/inetnum/172.20.234.224_28 @ dn42/registry@master inetnum: 172.20.234.224 - 172.20.234.239 cidr: 172.20.234.224/28 netname: IYOROYNET-DN42-V4 descr: iYoRoy DN42 Network IPv4 Block country: CN admin-c: IYOROY-DN42 tech-c: IYOROY-DN42 mnt-by: IYOROY-MNT status: ASSIGNED source: DN42 {/collapse-item} {/collapse} Next, under data/route, create a file also named after the IPv4 block's CIDR format, using _ instead of /, and fill in the content: route: <IPv4 Block in CIDR Format> origin: <ASN> max-length: <IPv4 Prefix Length> mnt-by: <Maintainer> source: DN42 route: IPv4 block, fill in CIDR format, do not convert /. origin: ASN, needs to include AS prefix. max-lenth: Prefix Length, i.e., the number after / in the CIDR format. mnt-by: Maintained by, points to the filename from the Create Maintainer section above. source: Source, keep it as DN42. {collapse} {collapse-item label="Example"} data/route/172.20.234.224_28 @ dn42/registry@master route: 172.20.234.224/28 origin: AS4242422024 max-length: 28 mnt-by: IYOROY-MNT source: DN42 {/collapse-item} {/collapse} Register an IPv6 Block and Add Route You can skip this section if you don't want to register IPv6 Similarly, use the official tool to find available IPv6: DN42 Free IPv6 Explorer 。 After selecting the address block, create a file under data/inet6num named after the IPv6 block's CIDR format, using _ instead of / (e.g., if the IPv6 block is fd18:3e15:61d0::/48, the filename is fd18:3e15:61d0::_48), and fill in the content: inet6num: <First IPv6 Address in Block> - <Last IPv6 Address in Block> cidr: <IPv6 Block in CIDR Format> netname: <IPv6 Block Name> descr: <IPv6 Block Description> country: <Country Code for IPv6 Block> admin-c: <Administrator NIC Handle> tech-c: <Technical Contact NIC Handle> mnt-by: <Maintainer> status: ASSIGNED source: DN42 inet6num: IPv6 address range, from the first address to the last address, cannot use :: shorthand, connected with -. For example, if my block is fd18:3e15:61d0::/48, I need to fill in fd18:3e15:61d0:0000:0000:0000:0000:0000 - fd18:3e15:61d0:ffff:ffff:ffff:ffff:ffff. cidr: IPv6 block in CIDR format, do not replace / with _. netname: IPv6 block name, fill in as desired, no special requirements descr: IPv6 block description, can contain spaces country: Two-character country code from ISO 3166, for China, fill in CN. admin-c: Administrator contact information, points to a file in the person folder, use the filename from the Register Contact section above. tech-c: Technical contact information, points to a file in the person folder, use the filename from the Register Contact section above. mnt-by: Maintained by, points to the filename from the Create Maintainer section above. status: Status, keep it as ASSIGNED. source: Source, keep it as DN42. {collapse} {collapse-item label="Example"} data/inet6num/fd18:3e15:61d0::_48 @ dn42/registry@master inet6num: fd18:3e15:61d0:0000:0000:0000:0000:0000 - fd18:3e15:61d0:ffff:ffff:ffff:ffff:ffff cidr: fd18:3e15:61d0::/48 netname: IYOROYNET-DN42-V6 descr: iYoRoy DN42 Network IPv6 Block country: CN admin-c: IYOROY-DN42 tech-c: IYOROY-DN42 mnt-by: IYOROY-MNT status: ASSIGNED source: DN42 {/collapse-item} {/collapse} Next, under data/route6, create a file also named after the IPv6 block's CIDR format, using _ instead of /, and fill in the content: route6: <IPv6 Block in CIDR Format> origin: <ASN> max-length: <IPv6 Prefix Length> mnt-by: <Maintainer> source: DN42 route6: IPv6 block, fill in CIDR format, do not convert /. origin: ASN, needs to include AS prefix. max-lenth: Prefix Length, i.e., the number after / in the CIDR format. mnt-by: Maintained by, points to the filename from the Create Maintainer section above. source: Source, keep it as DN42. {collapse} {collapse-item label="Example"} data/route6/fd18:3e15:61d0::_48 @ dn42/registry@master route6: fd18:3e15:61d0::/48 origin: AS4242422024 max-length: 48 mnt-by: IYOROY-MNT source: DN42 {/collapse-item} {/collapse} Create and Push Commit, Submit PR It's best to sign your commits with GPG and upload your public key to dn42 git. After filling in the above information, return to the root directory of the repository and run the script ./fmt-my-stuff <Maintainer> to automatically format the configuration files. Note: Here <Maintainer> is the value of mntner from the Register Maintainer chapter. Then follow the normal git commit process: git add data/, git commit -S -m "<commit-msg>" to create the commit, git push origin master to push. Then, go to the dn42 git web interface, open your forked repository, and submit a Pull Request. If there are configuration issues, the automated review bot and administrators will inform you. Please patiently modify according to the requirements and merge into the original commit, do not create a new commit. It is recommended to use git commit --amend after git add. After modification, force push directly (git push origin master --force). The working language of the DN42 Registry is English. Please use English for the entire process to avoid unnecessary trouble. Configuring Bird First, install bird. Using Ubuntu 22.04 as an example: apt install bird2 -y If you also want to install WireGuard for later use, add the wireguard and wireguard-tools packages: apt install bird2 wireguard wireguard-tools -y Create Configuration Files bird.conf Modify /etc/bird/bird.conf： define OWNAS = <AS Number>; define OWNIP = <DN42 IPv4 Address>; define OWNIPv6 = <DN42 IPv6 Address>; define OWNNET = <DN42 IPv4 Block, CIDR Format>; define OWNNETv6 = <DN42 IPv6 Block, CIDR Format>; define OWNNETSET = [ <DN42 IPv4 Block, CIDR Format>+ ]; define OWNNETSETv6 = [ <DN42 IPv6 Block, CIDR Format>+ ]; router id OWNIP; protocol device { scan time 10; } function is_self_net() { return net ~ OWNNETSET; } function is_self_net_v6() { return net ~ OWNNETSETv6; } function is_valid_network() { return net ~ [ 172.20.0.0/14{21,29}, # dn42 172.20.0.0/24{28,32}, # dn42 Anycast 172.21.0.0/24{28,32}, # dn42 Anycast 172.22.0.0/24{28,32}, # dn42 Anycast 172.23.0.0/24{28,32}, # dn42 Anycast 172.31.0.0/16+, # ChaosVPN 10.100.0.0/14+, # ChaosVPN 10.127.0.0/16{16,32}, # neonetwork 10.0.0.0/8{15,24} # Freifunk.net ]; } function is_valid_network_v6() { return net ~ [ fd00::/8{44,64} # ULA address space as per RFC 4193 ]; } protocol kernel { scan time 20; ipv6 { import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = OWNIPv6; accept; }; }; }; protocol kernel { scan time 20; ipv4 { import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = OWNIP; accept; }; }; } protocol static { route OWNNET reject; ipv4 { import all; export none; }; } protocol static { route OWNNETv6 reject; ipv6 { import all; export none; }; } include "rpki.conf"; include "ebgp.conf"; Here, the OWNIP and OWNIPV6 parameters refer to the DN42 IPv4 and IPv6 addresses assigned to this machine, not the address of the block. For example, if I obtained the address segment 172.20.234.224/28 and assigned 172.20.234.225 to machine A, then OWNIP is 172.20.234.225. The same applies to IPv6. rpki.conf You might have seen other tutorials using cron jobs + reloading Bird to update ROAs. Configuring RPKI here is another way to configure ROAs. Create /etc/bird/rpki.conf and fill in the following content: roa4 table dn42_roa; roa6 table dn42_roa_v6; protocol rpki dn42_rpki_akix { roa4 { table dn42_roa; }; roa6 { table dn42_roa_v6; }; remote "<rpki server address>" port 8082; # change it refresh 30; retry 5; expire 600; } For the RPKI server, you can use the service provided by my friend's AkaereIX: rpki.akae.re. {collapse} {collapse-item label="Example"} roa4 table dn42_roa; roa6 table dn42_roa_v6; protocol rpki dn42_rpki_akix { roa4 { table dn42_roa; }; roa6 { table dn42_roa_v6; }; remote "rpki.akae.re" port 8082; refresh 30; retry 5; expire 600; } {/collapse-item} {/collapse} ebgp.conf Create /etc/bird/ebgp.conf and fill in the following content: template bgp dnpeers { local as OWNAS; path metric 1; ipv4 { import filter { if is_valid_network() && !is_self_net() then { if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } accept; } reject; }; export filter { if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then accept; reject; }; import limit 1000 action block; }; ipv6 { import filter { if is_valid_network_v6() && !is_self_net_v6() then { if (roa_check(dn42_roa_v6, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } accept; } reject; }; export filter { if is_valid_network_v6() && source ~ [RTS_STATIC, RTS_BGP] then accept; reject; }; import limit 1000 action block; }; } include "peers/*"; This configuration defines a template for peering within DN42 and references all configuration files under /etc/bird/peers. Later, when we peer, we only need to create configuration files here according to the template and write them. Create the /etc/bird/peers folder for subsequent peering. System Configuration Kernel Because each node may act as a router for other nodes, Linux kernel packet forwarding needs to be enabled. Also, because WireGuard is used, which doesn't forward through physical network cards, the rp_filter strict mode needs to be disabled. echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf echo "net.ipv6.conf.default.forwarding=1" >> /etc/sysctl.conf echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf echo "net.ipv4.conf.default.rp_filter=0" >> /etc/sysctl.conf echo "net.ipv4.conf.all.rp_filter=0" >> /etc/sysctl.conf sysctl -p DN42 Virtual Network Interface Because MP-BGP typically uses LLA addresses to establish BGP sessions, we need to set up a dummy network interface in the system and bind the DN42 IP we assigned to this machine. Run the following commands: ip link add dn42 type dummy ip addr add <DN42 IPv4 assigned to this machine> dev dn42 ip addr add <DN42 IPv6 assigned to this machine> dev dn42 If persistent configuration is needed, you can refer to the following three methods: {tabs} {tabs-pane label="systemd-networkd"} tee /etc/systemd/network/10-dn42.netdev > /dev/null <<EOF [NetDev] Name=dn42 Kind=dummy EOF tee /etc/systemd/network/20-dn42.network > /dev/null <<EOF [Match] Name=dn42 [Network] Address=<DN42 IPv4 assigned to this machine>/32 Address=<DN42 IPv6 assigned to this machine>/128 EOF systemctl enable systemd-networkd systemctl restart systemd-networkd {/tabs-pane} {tabs-pane label="netplan"} tee /etc/netplan/99-dn42.yaml > /dev/null <<EOF network: version: 2 renderer: networkd ethernets: dn42: match: name: dn42 addresses: - <DN42 IPv4 assigned to this machine>/32 - "<DN42 IPv6 assigned to this machine>/128" accept-ra: no EOF netplan apply {/tabs-pane} {tabs-pane label="/etc/network/interfaces"} tee -a /etc/network/interfaces > /dev/null <<EOF auto dn42 iface dn42 inet static address <DN42 IPv4 assigned to this machine> netmask 255.255.255.255 iface dn42 inet6 static address <DN42 IPv6 assigned to this machine>/128 EOF ifup dn42 {/tabs-pane} {/tabs} Peering with Other Members A user-friendly option for beginners is Potat0's self-service peering service. For details, refer to his DN42 Network page. Follow the instructions to find his Automatic Peering Bot, register your account with the bot by providing your DN42 ASN, the email recorded in the Maintainer during registration above (this is why it's best to fill in the email during registration, as many verification services use it), and receive the verification code with that email. Then, follow the bot's requirements to establish the peer. Install WireGuard apt install wireguard wireguard-tools -y Note: Modern package managers usually automatically install wireguard-tools when installing wireguard to use the wg-quick command. Generate Key Pair wg genkey | tee privatekey | wg pubkey > publickey Note down the contents of privatekey and publickey, representing the private and public keys respectively. Establishing a Peer Generally, there are two modes for establishing a peer: using MP-BGP (Multiprotocol BGP) and not using MP-BGP. Personally, I find MP-BGP more common because its configuration is relatively simple. The configurations are slightly different, so please refer according to your needs. Using MP-BGP Exchanging Information with the Peer You need to provide the other party with the following information, and you also need to know the other party's corresponding information: Public Key Public IP Address (non-DN42 address) Public WireGuard Port, usually listening on the last 5 digits of the other party's ASN DN42 ASN LLA (Link-Local Address), generally fe80::<last 4 digits of peer's ASN> Whether ENH (Extended Next Hop) is supported. Note: If using v6 to exchange routes without enabling ENH, v4 routes cannot be exchanged. Sometimes the public IP address and public WG port are combined and called the Endpoint. Configuring WireGuard Create a configuration file under /etc/wireguard. The name is arbitrary, but my naming convention is dn42-<last 4 digits of peer's ASN>.conf, where dn42-xxxx is the tunnel name. Fill the file with the following content: [Interface] PrivateKey = <Private key generated in the "Generate Key Pair" section above> ListenPort = <Public WireGuard Listen Port> Table = off Address = <Your LLA Address>/64 PostUp = sysctl -w net.ipv6.conf.%i.autoconf=0 [Peer] PublicKey = <Peer's Public Key> Endpoint = <Peer's Endpoint, i.e., IP:Port> AllowedIPs = 10.0.0.0/8, 172.20.0.0/14, 172.31.0.0/16, fd00::/8, fe00::/8 After editing and saving, run: wg-quick up <Tunnel Name> This will start the WG tunnel. Use wg show <Tunnel Name> to check the connection status. Running wg directly shows the status of all tunnels. If you want the tunnel to start automatically on boot, run: systemctl enable wg-quick@<Tunnel Name> Configuring the Bird2 Peer Section Create a new file <Name>.conf under /etc/bird/peers and fill in the following content: protocol bgp <BGP Session Name> from dnpeers{ neighbor <Peer's LLA Address> % '<WireGuard Tunnel Name>' as <Peer's ASN, without AS prefix>; }; If Extended Next Hop is used (as negotiated during information exchange above), configure: protocol bgp <BGP Session Name> from dnpeers{ neighbor <Peer's LLA Address> % '<WireGuard Tunnel Name>' as <Peer's ASN, without AS prefix>; ipv4{ extended next hop; }; }; Applying Configuration Run birdc configure (or birdc c, equivalent) to reload the Bird configuration, then run birdc show protocols (or birdc s p, equivalent) to view the currently established BGP connections. root@hkg2-202501092021514df2f0:~# birdc show protocols BIRD 2.0.12 ready. Name Proto Table State Since Info device1 Device --- up 07:24:14.255 static1 Static dn42_roa up 07:24:14.255 static2 Static dn42_roa_v6 up 07:24:14.255 kernel1 Kernel master6 up 07:24:14.255 kernel2 Kernel master4 up 07:24:14.255 static3 Static master4 up 07:24:14.255 static4 Static master6 up 07:24:14.255 dn42-0298 BGP --- up 08:03:13.347 Established dn42-1816 BGP --- up 07:53:05.028 Established Not Using MP-BGP Exchanging Information with the Peer You need to provide the other party with the following information, and you also need to know the other party's corresponding information: Public Key Public IP Address (non-DN42 address) Public WireGuard Port, usually listening on the last 5 digits of the other party's ASN DN42 ASN DN42 IP, if exchanging IPv4 routes, an IPv4 address is needed; if exchanging IPv6 routes, an IPv6 address is needed. Sometimes the public IP address and public WG port are combined and called the Endpoint. Configuring WireGuard Create a configuration file under /etc/wireguard. The name is arbitrary, but my naming convention is dn42-<last 4 digits of peer's ASN>.conf, where dn42-xxxx is the tunnel name. Fill the file with the following content: [Interface] PrivateKey = <Private key generated in the "Generate Key Pair" section above> ListenPort = <Public WireGuard Listen Port> Table = off PostUp = ip addr add <LLA>/64 dev %i PostUp = ip addr add <Local DN42 IPv6> dev %i PostUp = ip addr add <Local DN42 IPv4> peer <Peer's DN42 IPv4> dev %i PostUp = sysctl -w net.ipv6.conf.%i.autoconf=0 [Peer] PublicKey = <Peer's Public Key> Endpoint = <Peer's Endpoint, i.e., IP:Port> AllowedIPs = 10.0.0.0/8, 172.20.0.0/14, 172.31.0.0/16, fd00::/8, fe00::/8 After editing and saving, run: wg-quick up <Tunnel Name> This will start the WG tunnel. Use wg show <Tunnel Name> to check the connection status. Running wg directly shows the status of all tunnels. If you want the tunnel to start automatically on boot, run: systemctl enable wg-quick@<Tunnel Name> Configuring the Bird2 Peer Section Create a new file <Name>.conf under /etc/bird/peers and fill in the following content: protocol bgp <v4 BGP Session Name> from dnpeers{ neighbor <Peer's DN42 IPv4 Address> as <Peer's ASN, without AS prefix>; direct; ipv6{ import none; export none; }; }; protocol bgp <v6 BGP Session Name> from dnpeers{ neighbor <Peer's DN42 IPv6 Address> % '<WireGuard Tunnel Name>' as <Peer's ASN, without AS prefix>; direct; ipv4{ import none; export none; }; }; Applying Configuration Run birdc configure (or birdc c, equivalent) to reload the Bird configuration, then run birdc show protocols (or birdc s p, equivalent) to view the currently established BGP connections. At this point, we have successfully joined the DN42 network. To make the network more stable, we can peer with more users, establishing multiple BGP lines to prevent disconnection if some nodes fail. You can join the DN42 Unofficial Telegram Group for more information. If BGP is Established but you cannot Ping internal DN42 IPs, check if the IP set in bird.conf matches the IP assigned to the dummy interface or the local IP in WireGuard. Configuring DNS DN42 has its own public DNS with the anycast address 172.20.0.53, which can also resolve normal internet domains. To access internal domains ending with .dn42, you need to put this address at the top of resolv.conf: nameserver 172.20.0.53 nameserver 223.5.5.5 # Below are normal DNS server configurations After this, you can resolve DN42 internal domain names. Also, commands like ping, traceroute, and mtr will show rDNS resolution results when querying internal IPs. Welcome to peer with me! For details, please visit: iYoRoy DN42 Network References: https://blog.baoshuo.ren/post/dn42-network/ https://dn42.dev/howto/Bird2#example-configuration https://dn42.eu/howto/wireguard https://blog.udon.eu.org/archives/dbf21067.html https://blog.byteloid.one/2025/06/02/babeld-over-wireguard/ https://blog.wcysite.com/2021/%E8%B8%A9%E5%9D%91DN42-p2-peer/ https://blog.chs.pub/p/23-14-joindn42/ https://www.cnblogs.com/FengZeng666/p/15583434.html