IPv6 with Ziggo (DS-Lite) and a Ubiquiti Edgerouter

At home I have Ziggo as my internet provider witch I do not have any problems with. Only their support on IPv6 is somewhat limited. So I decided to do this writeup. I wanted IPv6 enabled….well…because of…reasons. So I decided to give Ziggo a call to enable it. Weird thing was…they started to talk me out of it. Stating it has no benefits over IPv4. I beg to differ.

When it was enabled (after them sending me a new modem) I noticed that the implementation was a DS-lite one. Witch is sorta dual-stack. Only, your IPv4 traffic is tunneled over IPv6 and then CG NAT-ed at the providers router. Great downside of this is you no longer are able to do port forwarding for IPv4. But for that I have some tricks up my sleeve so not a problem.

Overview of DS-lite

But as a networking guy I wanted to have my own router witch is completely under my control. And I wanted to have multiple subnets (ie a server and clients vlan). For that I had my Ubiquiti EdgeRouter lite. No comes the fun part.

Normally when you connect multiple routers you have to tell each router where each subnet can be reached. For that you have something called a routing protocol or static routes. With a Ziggo modem you have none of that. So even if you know what IPv6 pre-fixes you can use. You have no way of telling your ziggo modem that they are behind your own router. But there is this nifty little protocol just for this occasion. Enter DHCPv6-PD. Or Also know as Prefix-delegation. In easy to understand terms it is basically your router asking for an entire subnet (or pre-fix) instead of just a single IP address. nifty ay. Al that a side here is how you configure on your Ubiquiti router.

interfaces {
ethernet eth0 {
address dhcp
description "Ziggo"
dhcpv6-pd {
pd 0 {
interface eth1 {
host-address ::1
prefix-id :1
service slaac
interface eth2 {
host-address ::1
prefix-id :2
service slaac
prefix-length /60
rapid-commit disable
duplex auto
firewall {
in {
ipv6-name WANv6_IN
name WAN_IN
local {
ipv6-name WANv6_LOCAL
mtu 9000
speed auto

And, that’s it. Nothing to it. In my case eth0 is my internet connection. Connected to the modem. On that interface you tell the upstream modem to grab two prefixes ont for eth1 and one for eth2. Als you tell to use slaac (stateless IPv6 address asignment) for assigning IPv6 addresses to your clients. it is as simple as that.

Verifing if it works

To verify if everything is working you can do the folowing on you router:

@router:~$ show interfaces
Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
Interface IP Address S/L Description
--------- ---------- --- -----------
eth0 u/u Peering: Ziggo [400Mbit]
eth1 u/u Cust: LAN1 [1Gbit]
eth2 u/u Cust: Servers1 [1Gbit]
lo u/u

The show interfaces shows that all interfaces have an IPv6 address.

router:~$ show ipv6 route
IPv6 Routing Table
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
IA - OSPF inter area, E1 - OSPF external type 1,
E2 - OSPF external type 2, N1 - OSPF NSSA external type 1,
N2 - OSPF NSSA external type 2, B - BGP
Timers: Uptime
IP Route Table for VRF "default"
K ::/0 [0/1024] via fe80::4ad3:43ff:fecf:a910, eth0, 3d09h02m
C ::1/128 via ::, lo, 3d08h04m
C 2001:1c02:2f01:f100:xxxx:2bf6/128 via ::, eth0, 3d09h02m
C 2001:1c02:2f01:xxxx::/64 via ::, eth1, 3d09h02m
C 2001:1c02:2f01:xxxy::/64 via ::, eth2, 3d09h02m
C fe80::/64 via ::, eth2, 3d08h04m

The above command shows that the necessary routes are in place. Most importantly the default gateway ::/0. And if all is well in the world you should be able to do this:

router:~$ ping6 ipv6.google.com
PING ipv6.google.com(ams16s29-in-x0e.1e100.net) 56 data bytes
64 bytes from ams16s29-in-x0e.1e100.net: icmp_seq=1 ttl=54 time=15.4 ms
64 bytes from ams16s29-in-x0e.1e100.net: icmp_seq=2 ttl=54 time=13.0 ms
--- ipv6.google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 13.006/14.238/15.471/1.238 ms

Using telegraf to add metadata to measurements

At my job (An ISP in NL) we use Telegraf/InfluxDB for storing and monitoring data of our network elements. We are also in the process of automating a lot of our processes for obvious reasons. The same InfluxDB we use for representing monitoring data on our customer facing portals. To associate the measurements with a customers subscription and instance we use the port description to store service-id’s etc. However to search a service-id from an ifAlias you’ll have to use regex. As you can imagine this is an “expensive” type of query in InfluxDB. We decided to add the service ID’s as separate tags. This way we can efficiently create queries bij only knowing the service ID. This is how we do it.

Of each port we measure obvious things like

  • IF-MIB::ifTable
  • IF-MIB::ifXTable
  • EtherLike-MIB::dot3StatsTable

We store things like ifAlias as a tag in InfluxDB so measurements are made searchable by it. And this also represents a problem to search datapoints by a service id we’ll have to regex every ifAlias. Witch is quite expensive in terms of resources. The most efficient thing to do is extract the needed meta-data before we store it in InfluxDB. Lucky enough telegraf has these capabilities. By utilizing the plugins processors.regex and processors.strings we can extract services id’s and sanitize them when needed.

In our case an ifAlias would look like this:

EVPN:<Customer_Name> subid:<UUID> instid:<UUID>

The subid is the service ID and the instid is the instance of the service (a subid can have more than one instance). We are interested in extracting the servicetype (EVPN) the subid and instid as separate tags. The service id’s are an UUID, so easily extractble.

In /etc/telegraf/telegraf.d/ we’ve created an conf file ie. processor-ifAlias.conf. witch will look something like this:

# only apply this processor to only this measurement only to tags with the name "ifAlias"
  namepass = ["NetworkMeasurements"]
  tagpass = ["ifAlias"]

  # processor to extract the servicestype if applicaple and store in result_key = servicetype
    key = "ifAlias"
    pattern = "^(?P<servicetype>AM|PTP|AM|CU|IP|EX|EVPN|IP|EXIP|OT|CR|L3VPN)+:.*"
    replacement = "${servicetype}"
    result_key = "servicetype"

    # processor to extract the subid if applicaple and store in result_key = subid only when UUID is correctly formatted
    key = "ifAlias"
    pattern = ".*subid:(?P<subid>[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}).*"
    replacement = "${subid}"
    result_key = "subid"

    # processor to extract the instid if applicaple and store in result_key = instid only when UUID is correctly formatted
    key = "ifAlias"
    pattern = ".*instid:(?P<instid>[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}).*"
    replacement = "${instid}"
    result_key = "instid"

  # seperate process
  # Convert the UUID to all lowercase if needed.
  namepass = ["NetworkMeasurements"]
    tag = "subid"
    tag = "instid"

With this in place we can easily create queries in our customer portals based on subscriptions an split them on instances and/or other metadata. Hope this helps. Any comments, suggestions or questions, do not hesitate to drop me a note.

Creating a decent RE protection filter on a Juniper ACX5048 ACX5096

tl;dr We got our hands on a Juniper ACX5048. We were testing it as an access router for our new network and immideatly ran into trouble with. It turns out you cannot apply an input filter on lo0 interfaces. Witch is a huge problem for hardening the device it self with an RE (routing engine) protection filter. And there are some other issues you need to be aware of when deploying this router with multiple routing-instances. In this article I will go into getting around limitations of the ACX50xx in regard to RE protection.
Continue reading “Creating a decent RE protection filter on a Juniper ACX5048 ACX5096”