Featured image of post Optimizing multi-wan internet access using BGP full table on RouterOS.

Optimizing multi-wan internet access using BGP full table on RouterOS.

Playing with BGP at home too!

Recently, I installed a 1000M Shanghai Mobile broadband in the place I am currently staying. In order to make full use of the new broadband and the existing Shanghai Telecom broadband, it’s necessary to use some magic to utilize both upstreams. Here, we will take the example of the CCR2004-16G-2S+ router to explain how to optimize network access using BGP full table.

Our target strategy is very clear:

  1. For IP addresses targeting China Telecom (AS4134, AS4812), use China Telecom as default gateway.
  2. For all remaining non-China ASNs, use a special gateway within the local network tunneling all traffic to one server located in Hong Kong.
  3. The rest should go through China Mobile.

Basic network configuration

First, we need to connect our router to the internet. Assuming the China Telecom network is connected to ether1 and the China Mobile network is connected to ether2, we can create two PPPoE clients like this:

1
2
/interface/pppoe-client/add name="pppoe-chinanet" interface=ether1 profile=default add-default-route=no dial-on-demand=no use-peer-dns=no allow=pap,chap,mschap1,mschap2
/interface/pppoe-client/add name="pppoe-chinamobile" interface=ether2 profile=default add-default-route=no dial-on-demand=no use-peer-dns=no allow=pap,chap,mschap1,mschap2

The part about the PPPoE account and password is omitted above. You can fill it in as needed. It is worth noting that the newly created PPPoE connection does not use the default gateway. Here, we can add a static route as the default gateway configuration. Assuming we plan to use mobile broadband as the default upstream connection:

1
/ip/route/add dst-address=0.0.0.0/0 routing-table=main gateway=pppoe-chinamobile distance=100

At this point, our router should be able to access the network correctly. Let’s use traceroute to verify it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[admin@shanghai-router] > /tool/traceroute 223.5.5.5
Columns: ADDRESS, LOSS, SENT, LAST, AVG, BEST, WORST, STD-DEV
#  ADDRESS         LOSS   SENT  LAST     AVG   BEST  WORST  STD-DEV
1  <redacted>      0%        9  3.9ms    4.7   3.6   8.9    1.9
2  <redacted>      75%       9  timeout  3.7   3.6   3.7    0.1
3  <redacted>      12.5%     8  timeout  3.7   3.6   3.8    0.1
4  <redacted>      0%        8  12.9ms   7.8   3.9   20.3   5.5
5                  100%      8  timeout
6  116.251.116.93  0%        8  6.2ms    12.9  5.6   38.3   10.6
7                  100%      8  timeout
8                  100%      8  timeout
9  223.5.5.5       0%        8  5.5ms    5.4   5.2   5.6    0.1

At the same time, we can also test the PPPoE connection of the China Telecom network by specifying the interface in parameters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[admin@shanghai-router] > /tool/traceroute 223.5.5.5 interface=pppoe-chinanet
Columns: ADDRESS, LOSS, SENT, LAST, AVG, BEST, WORST, STD-DEV
 #  ADDRESS         LOSS   SENT  LAST     AVG   BEST  WORST  STD-DEV
 1  <redacted>      7.7%     13  3.8ms    4.7   1.8   7.7    1.8
 2  <redacted>      0%       13  10.3ms   8.7   4.6   18.4   3.2
 3  <redacted>      66.7%    13  timeout  6.6   3.9   14.2   4.4
 4  <redacted>      66.7%    12  timeout  5.5   3.8   8.7    1.9
 5  <redacted>      0%       12  3.2ms    6.3   3.1   25.3   6.7
 6  <redacted>      0%       12  5.8ms    18.2  5.5   150.8  40
 7                  100%     12  timeout
 8                  100%     12  timeout
 9                  100%     12  timeout
10  223.5.5.5       0%       12  5.4ms    5.5   5.3   5.8    0.2

Of course, we also need to create some NAT rules in /ip/firewall/nat, so that devices in our LAN can also access the internet. This part is relatively basic, so I won’t go into detail.

Obtain realtime BGP full routing table

If you are a BGP player, you can totally skip this part, but if you are not familiar with BGP or don’t have an ASN, you may encounter some difficulties. Here, you can acquire the complete BGP routing table by purchasing the cheapest Vultr server, and it does not require you to have your own ASN. It offers great value for money. There are many online resources available that provide step-by-step instructions on how to perform the related operations, you can google it by yourself.

After completing the operations on Vultr, we can follow the official guide and establish a BGP session with Vultr to obtain full routing table data. It is very easy. The key configurations are as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
protocol device {}

protocol kernel kernel4 {
  ipv4 {
    import none;
    export where source != RTS_DEVICE && proto != "bgp_vultr_v4";
  };
}

protocol kernel kernel6 {
  ipv6 {
    import none;
    export filter {
      if source = RTS_DEVICE then reject;
      if proto = "bgp_vultr_v4" then reject;
      reject;
    };
  };
}

protocol static static_bgp_neighbor_v6 {
  ipv6;
  route 2001:19f0:ffff::1/128 via fe80::fc00:4ff:fe08:ee5f % enp1s0;
}

protocol static static_bgp_neighbor_v4 {
  ipv4;
  route 169.254.169.254/32 via a.b.c.1 % enp1s0;
}

protocol bgp bgp_vultr_v4 {
  local as 142641;
  neighbor 169.254.169.254 as 64515;
  source address a.b.c.d;
  multihop 2;
  password "xxxxxxxx";

  ipv4 {
    import all;
    export none;
  };
}

protocol bgp bgp_vultr_v6 {
  local as 142641;
  neighbor 2001:19f0:ffff::1 as 64515;
  source address 2001:db8::1;
  multihop 2;
  password "xxxxxxx";

  ipv6 {
    import all;
    export none;
  };
}

If everything goes well, we can use the command show protocol in birdc to see that our session status is already “Established”.

1
2
3
Name         Proto      Table      State  Since         Info
bgp_vultr_v4 BGP        ---        up     2023-01-21    Established
bgp_vultr_v6 BGP        ---        up     2023-01-21    Established

We can also use the “show route for” command to view specific forwarding rule, for example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
bird> show route for 1.1.1.1 all
Table master4:
1.1.1.0/24           unicast [bgp_vultr_v4 2023-08-01 from 169.254.169.254] * (100/?) [AS13335i]
	via 45.32.28.1 on enp1s0
	Type: BGP univ
	BGP.origin: IGP
	BGP.as_path: 64515 20473 13335
	BGP.next_hop: 169.254.169.254
	BGP.local_pref: 100
	BGP.aggregator: 10.34.7.24 AS13335
	BGP.community: (20473,200) (64515,44)
	BGP.large_community: (20473, 200, 13335) (20473, 200, 45686)

Thus we are now ready with our own BGP speaker.

Establish a tunnel between the BGP feeder and the router.

Home broadband usually does not have a static IP address. To minimize configuration changes, the simplest way is to set up a tunnel between the router at home and a server with full BGP table, and WireGuard can be used for this purpose. Its advantage is that it has native support in newer kernels and RouterOS. Additionally, it uses UDP protocol, which makes it convenient to use various IEPL/IPLC forwarding services to improve reliability.

On the feeder, let’s create a new VPN interface with the following configuration:

1
2
3
4
5
6
7
8
9
[Interface]
PrivateKey = xxx
Address = 10.22.33.1/24, fd34:38a:f295::1/64
MTU = 1400
ListenPort = 114514

[Peer]
PublicKey = yyy
AllowedIPs = 10.22.33.2/32, fd34:38a:f295::2/128

Then, create a WireGuard interface on RouterOS and peering it with our BGP speaker:

1
2
3
4
5
/interface/wireguard/add name="wg-bgp-speaker" private-key="xxx" mtu=1400
/interface/wireguard/peers/add endpoint-address=172.16.1.1 endpoint-port=3000 interface=wg-bgp-speaker public-key="qzgKhWy8u0DBvXcrhfFDYPwKzKipWzCsxR5ecNIYZFs=" persistent-keepalive=60 allowed-address=10.22.33.1/32,fd34:38a:f295::1/128

/ip/address/add interface=wg-bgp-speaker address=10.22.33.2/24
/ipv6/address/add interface=wg-bgp-speaker address=fd34:38a:f295::2/64

So we have created a direct connected tunnel, which can only allow our router and server to communicate point-to-point. But that’s enough.

Prepare the routing filters

According to the rules mentioned earlier, we can prepare the routing filters on the RouterOS side. In this filter, we directly choose the gateway used by this route based on the end of the AS path. Taking IPv4 as an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
if (dst-len == 0) {
  reject
}

set distance 50;

if (not bgp-as-path [[:china_asn_list:]]$) {
  set gw 198.18.1.1;
  set gw-interface ipip-internet-proxy;

  accept;
} else {
  if (bgp-as-path 4134$|4812$) {
    set gw %pppoe-chinanet;
  } else {
    set gw %pppoe-chinamobile;
  }
}

accept;

There are two things that need clarification:

  1. china_asn_list is an ASN (Autonomous System Number) list obtained from GitHub, which is used in mainland China. It can be maintained in /routing/filter/num-list, for example, by adding AS4134: /routing/filter/num-list/add list=china_asn_list range=4134

  2. You can directly set an interface as a gateway using set gw %interface command, which allows the kernel to pay attention to which next hop to use, and it can reduce a lot of maintenance costs.

For IPv6 filter, the content remains largely the same, with only a few adjustments required for the cross-border access gateway.

Once these two filters are prepared, the final step is to bring the full table home.

Bring full-table home

The final task is to establish a BGP session between our feeder and router. First, we need to add two new protocols on the speaker.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
protocol bgp bgp_speaker_v4 {
  local as 65000;
  neighbor 10.22.33.2 as 65001;
  source address 10.22.33.1;
  passive;
  ipv4 {
    import none;
    export where proto = "bgp_vultr_v4" && 223.5.5.5 !~ net;
  };
};

protocol bgp bgp_speaker_v6 {
  local as 65000;
  neighbor fd34:38a:f295::2 as 65001;
  source address fd34:38a:f295::1;
  passive;
  multihop;
  ipv6 {
    import none;
    export where proto = "bgp_vultr_v6";
  };
};

Then add the corresponding BGP connection on RouterOS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/routing/bgp/connection/add remote.address=10.22.33.1/32 remote.as=65000
  local.address=10.22.33.2 remote.role=ebgp
  routing-table=main router-id=10.22.33.2 as=65001 multihop=yes hold-time=infinity address-families=ip
  output.filter-chain=bgp-speaker-export
  input.filter=bgp-speaker-v4

/routing/bgp/connection/add remote.address=fd34:38a:f295::1/128 .as=65000
  local.address=fd34:38a:f295::2 .role=ebgp
  routing-table=main router-id=10.22.33.2 as=65001 multihop=yes hold-time=infinity address-families=ipv6
  output.filter-chain=bgp-speaker-export
  input.filter=bgp-speaker-v6

After waiting for the entire table to enter FIB, we can start verifying the effectiveness of our traffic diversion. We will use a China Telecom/China Mobile/China Unicom/Cloudflare addresses respectively for testing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# China Telecom
[admin@shanghai-router] > /ip/route/print where 221.227.153.1 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS     GATEWAY         DISTANCE
DAb 221.224.0.0/13  pppoe-chinanet        50

# China Mobile
[admin@shanghai-router] > /ip/route/print where 120.232.236.5 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS       GATEWAY            DISTANCE
DAb 120.192.0.0/10    pppoe-chinamobile        50
DAb 120.232.0.0/16    pppoe-chinamobile        50
DAb 120.232.236.0/22  pppoe-chinamobile        50

# China Unicom
[admin@shanghai-router] > /ip/route/print where 103.45.78.1 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS     GATEWAY            DISTANCE
DAb 103.45.76.0/22  pppoe-chinamobile        50

# Cloudflare
[admin@shanghai-router] > /ip/route/print where 1.1.1.1 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS  GATEWAY                         DISTANCE
DAb 1.1.1.0/24   198.18.1.1%ipip-internet-proxy        50

In this way, our dual-line broadband IPv4 is fully utilized, although v6 achieves traffic diversion through these methods, there are still issues because there is no NAT and devices cannot intelligently choose which prefix of the IP address to use. The subsequent article will introduce how to solve this problem. Finally, let’s show the results:

Screenshot of ip.skk.moe

comments powered by Disqus
Except where otherwise noted, content on this blog is licensed under CC-BY 2.0.
Built with Hugo
Theme Stack designed by Jimmy