====== IPv6 over PPP with a Debian GNU/Linux firewall ====== In this page we will see how to configure a GNU/Linux firewall to handle an IPv6 prefix delegation obtained from the internet provider, distributing it to the local area network. The firewall is configured as follow: * The internet connection is a **PPPoE** through a **FTTC modem** connected to a DSL provider. The Ethernet card is named **net0** and the internet interface is named **ppp0**. * The local network LAN is connected through a second Ethernet card named **lan0**, but we use it in a logical bridge named **br0**. * The firewall runs the **Debian 12 Bookworm** operating system. * The firewall runs the **shorewall** and **shorewall6** firewall progams and the **bind9** Internet Domain Name Server. The packages that we will install on the GNU/Linux firewall are: * **wide-dhcpv6-client** - This is required to obtain the IPv6 //prefix delegation// (IPv6 subnet) from the internet service provider. * **radvd** - This is required to do //Router Advertisements// on the LAN interface. Clients will be able to do //Stateless Address Auto-configuration// (SLAAC). * **wide-dhcpv6-server** - This is required to give persistent addresses to IPv6 clients independent of the MAC address and to register AAAA and PTR records into the dynamic DNS. As for June 2024 these are the known limitations of **radvd** and **wide-dhcpv6-server** software: * The **radvd** server can do IPv6 stateless configuration of the clients using a **persistent address** (derived from the Ethernet MAC address) or a **random** one. It is not possibile to keep the same IPv6 address if you change the Ethernet device. * The **radvd** is not capable to register IPv6 clients to a **dynamic DNS server**. * **Android clients** connecting to the LAN can obtain IPv6 addresses from the **radvd** server, but not from the DHCPv6 server. * The **wide-dhcpv6-server** can assign **persistent addresses** to the IPv6 clients that do not depend on the MAC address. It also can register the clients to a **dynamic DNS server**. ===== PPPD options ===== To allow the ppp daemon to receive the basic IPv6 configuration from the internet service provider, you must add some options into the default configuration file **/etc/ppp/peers/provider**. # Enable IPv6 configuration. +ipv6 defaultroute6 # Interface ID (host part) of the IPv6 address to get. ipv6 00:00:00:00:00:00:00:01 # Derive the interface ID of the IPv6 address from the Ethernet MAC address. #ipv6cp-use-persistent Generally the ISP will delegate a **64 bit prefix** to be configured on the **ppp0 interface**. The default behaviour of the pppd is to generate a random interface ID (a 64 bit address) to form the full 128-bit IPv6 address. So whenever the ppp0 interface goes up, you have a new random IPv6 address automatically associated to it. If you want to associate a persistent address, you can specify it using the option **ipv6** or you may want to derive it from the MAC address of the Ethernet interface using the **ipv6cp-use-persistent** option. **WARNING**: The assignment of an IPv6 address to the ppp0 interface is negotiated via the **Router Advertisement** protocol. The GNU/Linux kernel has a parameter that control if those messages are accepted or rejected; you can check the current setting using: cat /proc/sys/net/ipv6/conf/ppp0/accept_ra The meaning of that value is: ^ 0 | Do not accept Router Advertisements. | ^ 1 | Accept Router Advertisements if forwarding is disabled (default). | ^ 2 | Accept Router Advertisements even if forwarding is enabled. | Generally the ppp0 interface on a GNU/Linux firewall is the WAN one (connecting to the internet), so forwarding is enabled on it by some way; check the current forwarding status with: cat /proc/sys/net/ipv6/conf/ppp0/forwarding 1 This is the reason why the IPv6 global (public) address is propbably not eccepted on that interface. To change (temporary) the ''accept_ra'' option for the ppp0 interface you can use the command: sysctl -w net.ipv6.conf.ppp0.accept_ra=2 If you want that option to be enabled on each reboot, you can create a configuration file e.g. **/etc/sysctl.d/99-local.conf** with: net.ipv6.conf.ppp0.accept_ra = 2 But even the last configuration **does not work in our case**, because the setting can be changed only when the interface ppp0 does exist. The ppp0 interface does not exist at bootstrap and it is volatile: it can be destroyed at any time if the connection with the ISP has a problem. And when the interface is created again it will have lost its configuration. We need to hook a script to the **ppp0 up event** and configure the options there. Because we are using the Shorewall firewall it is possibile to set the required actions into the Shorewall configuration and simply restart the Shorewall service on the ppp0 interface up event. See below an example of a **/etc/ppp/ipv6-up.d/ipv6-addresses-up** script. ===== Shorewall firewall ===== If you want the IPv6 address automatically configured on the ppp0 interface and you need to receive a Prefix Delegation, you must pay attention to allow the protocols traffic to flow (Router Advertisements and DHCPv6). If you use the **Shorewall6** firewall everything can be configured with some options into the **/etc/shorewall6/interfaces** configuration file: #ZONE INTERFACE OPTIONS net NET_IF dhcp,tcpflags,forward=1,accept_ra=2,sourceroute=0,physical=ppp0 * The **dhcp** option allows the traffic on ports **546/UDP** and **547/UDP**. * The **forward=1** option enables the forwarding of traffic through the ppp0 interface, from LAN to internet and viceversa. * The **accept_ra=2** option allows the Router Advertisements protocol to assing an IPv6 address to ppp0, even if routing is enabled. However there is a **big warning**: if you start the shorewall6 service **when the ppp0 interface is absent**, all the above settings are ignored. You need to restart the service upon the IPv6 stack is ready on the ppp0 interface. Fortunately the restart is fast enough that the auto-configuration will succeed (indeed all the protocols will try more than one time, if the first attempt fails). Here it is an example script **/etc/ppp/ipv6-up.d/ipv6-addresses-up**, which is executed whenever the link is available for sending and receiving IPv6 packets: #!/bin/sh # Accept Router Advertisements. # Not actually required because "shorewall6 restart" will do that. #sysctl -w "net.ipv6.conf.${PPP_IFACE}.accept_ra=2" # Restart the Shorewall6 service to set some options on the ppp0 interface. # Options that must be set every time that the interface is created are: # forward=1,accept_ra=2,sourceroute=0 /usr/sbin/shorewall6 restart # Restart the DHCPv6 client to get the Prefix Delegation. /usr/bin/systemctl restart wide-dhcpv6-client.service ===== DHCPv6 client: the wide-dhcpv6-client.service ===== If your ISP assigns to you a **Prefix Delegation** (an IPv6 subnet), you may need to run a **DHCPv6 client** to obtain it. Generally it is not sufficient to configure manually the subnet because if the internet provider does not see the DHCPv6 client request, it does not propagate the routing information globally. In Debian 12 you need to install the **wide-dhcpv6-client** package and configure it to obtain the prefix. The package must be configured in two files: the **/etc/default/wide-dhcpv6-client** and the **/etc/wide-dhcpv6/dhcp6c.conf**. In the first file **/etc/default/wide-dhcpv6-client** declare what is the interface that gets DHCPv6 messages and eventually enable some debug logging: INTERFACES="ppp0" VERBOSE=2 Per default the main configuration file **/etc/wide-dhcpv6/dhcp6c.conf** contains only a **profile default** statement, which is used if you don't declare a more specific **interface ppp0** statement. # The "default" profile statement is enabled when a specific interface # statement is not configured. Per Debian default the client is called # with the option "dhcp6c -Pdefault". profile default { # Exchange informational parameters only (e.g. DNS, no IPv6 addresses). information-only; # What to include in DHCPv6 option-request. request domain-name-servers; request domain-name; # Script executed when the daemon receives a reply message. script "/etc/wide-dhcpv6/dhcp6c-script"; }; This configuration above has the effect that no prefixes delegations are negotiated (//information-only//), but information about the IPv6 DNS servers (and eventually domain names) are added to **/etc/resolv.conf** by the provided script. We add the **interface ppp0** statement to the file: interface ppp0 { # Send and Identity Association for Non-temporary Addresses option, ID #0. #send ia-na 0; # Send an option for "Identity Association for Prefix Delegation" with ID=0. send ia-pd 0; }; # Non-temporary Addresses ID=0. #id-assoc na 0 { #}; # Prefix Delegation ID=0. id-assoc pd 0 { prefix-interface br0 { # A subset (subnet) of the delegated prefix is assigned to a LAN interface. # The subnet is identified by the SLA (Site-level aggregator) ID, e.g. the # bits that are added to the delegated prefix to form the full 64 bit prefix. # # Example: # 2a02:2427:513:1c00::/56 Delegated Prefix # 2a02:2427:513:1c03::/64 Delegated Prefix with 8 bits SLA ID=3 # 2a02:2427:513:1c03::1 Interface Address ID=1 # # sla-len Length of the SLA address part, in bits. # sla-id Site-level aggregator ID (subnet address). # ifid The 64 bit address assigned to the interface. # sla-len 8; sla-id 3; ifid 1; }; }; As you can se we don't configure the **Non-temporary Addresses** (''ia-na'') option, because the IPv6 address is already assigned to the ppp0 interface through the router advertisements protocol. Instead we configure the **Prefix Delegation** (''ia-pd'') option to obtain the IPv6 subnet. That PD has ID=0 and it is configured into the **id-assoc pd 0** statement. The internet provider delegates to us a 56 bit prefix, we can subnet this by adding 8 bits of **Site-level aggregator ID**. Each one of those SLAs can have IPv6 hosts identified by further 64 bits of interface address. In our example only one SLA is configured (with ID=3), and it is assigned to the **internal interface br0**. The interface gets its IPv6 address composed by the **delegated prefix** + **sla ID** + **interface ID**. It is possible to test the configuration file executing the daemon in foreground: systemctl stop wide-dhcpv6-client.service dhcp6c -c /etc/wide-dhcpv6/dhcp6c.conf -d -D -f ppp0 The client daemon will communicate with the ISP server on ports **546/UDP** and **547/UDP**, you can see the packets using **tcpdump**: tcpdump -i ppp0 -n '(udp port 546 or 547) or icmp6' 05:16:08.101646 IP6 fe80::1.546 > ff02::1:2.547: dhcp6 solicit 05:16:08.116599 IP6 fe80::d678:9bff:feee:5640.547 > fe80::1.546: dhcp6 advertise 05:16:09.103236 IP6 fe80::1.546 > ff02::1:2.547: dhcp6 request 05:16:09.114693 IP6 fe80::d678:9bff:feee:5640.547 > fe80::1.546: dhcp6 reply If you have a firewall, you must allow UDP traffic on ports 546 and 547 (see above if you are using Shorewall6). ==== Restart the service whenever the ppp0 interface goes up or down ==== The service cannot be started before the interface ppp0 is up. The script **/etc/ppp/ipv6-up.d/ipv6-addresses-up** can be used to restart the DHCPv6 client service: #!/bin/sh # Restart the Shorewall6 service to set some options on the ppp0 interface. # Options that must be set every time that the interface is created are: # forward=1,accept_ra=2,sourceroute=0 /usr/sbin/shorewall6 restart # Restart the DHCPv6 client to get the Prefix Delegation. /usr/bin/systemctl restart wide-dhcpv6-client.service When the ppp0 interface is turned off, the DHCPv6 client service is no longer needed and can be stopped. Here it is the script **/etc/ppp/ipv6-down.d/ipv6-addresses-down**: #!/bin/sh # Stop the DHCPv6 client to remove the Prefix Delegation. /usr/bin/systemctl stop wide-dhcpv6-client.service ===== Checking current configuration ===== ==== Checking IPv6 on the GNU/Linux firewall ==== Check if the **ppp0 interface** got the expected IPv6 address: ifconfig ppp0 ppp0: flags=4305 mtu 1488 inet 145.56.63.214 netmask 255.255.255.255 destination 146.31.204.24 inet6 fe80::1 prefixlen 128 scopeid 0x20 inet6 2a02:2425:134:1b0::1 prefixlen 64 scopeid 0x0 ppp txqueuelen 3 (Point-to-Point Protocol) RX packets 147868 bytes 35436781 (33.7 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 142797 bytes 77749243 (74.1 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 The public IPv6 address is the one with the **global scopeid**. To check if an **IPv6 default route** exists: ip -6 route show ... default via fe80::d678:9bff:feee:5640 dev ppp0 proto ra metric 1024 expires 1766sec hoplimit 64 pref medium Check if the **br0 interface** (the LAN) got the IPv6 prefix delegation: ifconfig br0 br0: flags=4163 mtu 1500 inet 192.168.3.1 netmask 255.255.255.0 broadcast 192.168.3.255 inet6 fe80::d003:a6ff:fef2:6fe8 prefixlen 64 scopeid 0x20 inet6 2a02:2427:513:1c03::1 prefixlen 64 scopeid 0x0 ether d2:03:a6:f2:6f:e8 txqueuelen 1000 (Ethernet) RX packets 119786 bytes 17445585 (16.6 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 148706 bytes 108552323 (103.5 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ==== Configuring IPv6 manually on the hosts of the LAN ==== If we have an host connected to the LAN (interface br0 of the firewall), we can manually configure the IPv6 protocol with something like this: ip -6 addr add '2a02:2420:503:1c03::2/64' dev eth0 ip -6 route add default via '2a02:2427:513:1c03::1' Generally this is not required nor advisable: any modern operating system should automatically negotiate IPv6 configuration if it is available on the LAN. ===== Router Advertisement Daemon for IPv6: the radvd.service ===== Something similar to the DHCP service (which distributes IPv4 addresses to the LAN) for IPv6 addresses is the **Router Advertisement Daemon**. In Debian you can install the **radvd** package. An useful diagnostic tool is provided by the **radvdump** package. This is the simpler approach that achieves the **Stateless Address Auto-configuration (SLAAC)**; i.e. nobody keeps track of what address is assigned to whom. Clients are responsilble of asking for an unique IPv6 address and perform a **duplicate address detection**. Sometimes a **stateful configuration** is required instead. For example when you want to assign a static IPv6 address independent of the MAC address, or when you need to register an AAAA name into a dynamic DNS server. In this case you need a DHCPv6 server, e.g. the one provided by the **wide-dhcpv6-server** Debian package. :!: Beware that **Android clients** can do SLAAC configuration with radvd, but they cannot get IPv6 from the dhcpv6 server. To configure the radvd service crate a file named **/etc/radvd.conf** with the following: interface br0 { # Enable advertisements and respond to solicitations. AdvSendAdvert on; # Minimum time in seconds between sending unsolicited advertisements. MinRtrAdvInterval 10; # Maximum time in seconds between unsolicited advertisements. MaxRtrAdvInterval 60; prefix 2a02:2420:503:1c03::/64 { # The address of interface is sent instead of network prefix, # as is required by Mobile IPv6. AdvRouterAddr on; # Disable autonomous configuration if you want to disable SLAAC # and use DHCPv6 only. You must install the wide-dhcpv6-server. # WARNING: Android clients can do only SLAAC. #AdvAutonomous off; }; }; Once you start the service with **systemctl start radvd**, you can view the advertising messages on the network just running the tool **radvdump**; the program will reveal both the avertising from your Internet Provider on the **ppp0** WAN interface and the avertising of the Linux firewall itself on the **br0** LAN interface. A modern **host in the LAN** (e.g. a GNU/Linux box) will pick-up automatically a global IPv6 address on the LAN interface. You can check the interface configuration: ifconfig eth0 eth0: flags=4163 mtu 1500 inet 192.168.3.2 netmask 255.255.255.0 broadcast 192.168.3.255 inet6 2a02:2427:513:1c03:3a2c:4aff:fe0e:feb2 prefixlen 64 scopeid 0x0 inet6 fe80::3a2c:4aff:fe0e:feb2 prefixlen 64 scopeid 0x20 ... The IPv6 address assigned to the interface have the delegated prefix and the interface ID is **derived from the MAC address** of the Ethernet address, so it will be unique and persistent. The default IPv6 route is: ip -6 route show 2a02:2427:513:1c03::/64 dev eth0 proto kernel metric 256 expires 86394sec pref medium fe80::/64 dev eth0 proto kernel metric 256 pref medium default via fe80::d003:a6ff:fef2:6fe8 dev eth0 proto ra metric 1024 expires 24sec hoplimit 64 pref medium As you can see, the IPv6 default route is addressed to the GNU/Linux firewall through the //link address//, not the //global address//. You can verify that the routing is working by just pinging a public IPv6 address. ==== Advertising the DNS servers ==== It is advisable to include DNS server information into Router Advertising messages, so that clients can access network services that require name resolution. In general hosts on the LAN will have DNS servers already configured on IPv4, but it is preferable to let IPv6-only hosts to be fully configured. To get this, just add a **RDNSS** statement into the interface statement of **/etc/radvd.conf**. In our case a recursive DNS service (Bind9) is running on the firewall itself: interface br0 { ... RDNSS 2a02:2427:513:1c00::1 { AdvRDNSSLifetime 3600; }; }; If you want clients to be able to resolve **not fully-qualified domain names** by trying to add some suffixes, you can configure a DNS search list adding a **DNSSL** statement: interface br0 { ... DNSSL lan rigacci.org lan.rigacci.org { }; }; If the hosts in the LAN are configured to obtain IPv6 configuration automatically, the new DNS server(s) and search list will be **automatically added** into their **/etc/resolv.conf** files. ===== Stateful configuration with DHCPv6 server ===== On the GNU/Linux firewall we installed the **wide-dhcpv6-server** Debian package, together with the radvd package seen above. This is beacuse we want to perform dynamic DNS registration of IPv6 clients, which is not provided by radvd. Once we installed the **wide-dhcpv6-server** Debian package, we need to configure the **/etc/wide-dhcpv6/dhcp6s.conf** file: # DNS server IPv6 address. option domain-name-servers 2a02:2420:503:1c03::1; # Domain names of the DNS search path. option domain-name "lan"; option domain-name "rigacci.org"; option domain-name "lan.rigacci.org"; # NOTICE: You have to send router advertisements on this # interface (i.e. run radvd on it) otherwise a client cannot # know the prefix-length and the default router. # If you want to prevent stateless address configuration via RA, # please set the AdvAutonomous to off in your RA configuration. interface br0 { address-pool pool1 3600; }; pool pool1 { range 2a02:2420:503:1c03::1000 to 2a02:2420:503:1c03::2000 ; }; host epidauro { duid 00:01:00:01:2e:17:e3:48:00:25:22:dd:45:98; address 2a02:2420:503:1c03::2 infinity; }; Verify also that into **/etc/default/wide-dhcpv6-server** the **INTERFACES** parameter includes your LAN network interface, which is named **br0** in our case. To reload the configuration, execute **systemctl restart wide-dhcpv6-server.service**. On a GNU/Linux client you can try an una-tantum DHCPv6 configuration using the following command (beware that the **dhclient** program from the **isc-dhcp-client** Debian package, will continue to run in background once obtained the address): dhclient -6 eth0 If you sniff the traffic on the DHCPv6 server you will see: tcpdump -i br0 -n 'icmp6 or (port 546 or port 547)' IP6 fe80::3a2c:4aff:fe0e:feb2.546 > ff02::1:2.547: dhcp6 solicit IP6 fe80::d003:a6ff:fef2:6fe8.50536 > fe80::3a2c:4aff:fe0e:feb2.546: dhcp6 advertise IP6 fe80::3a2c:4aff:fe0e:feb2.546 > ff02::1:2.547: dhcp6 request IP6 fe80::d003:a6ff:fef2:6fe8.50536 > fe80::3a2c:4aff:fe0e:feb2.546: dhcp6 reply IP6 :: > ff02::1:ff00:1000: ICMP6, neighbor solicitation, who has 2a02:2427:513:1c03::1000, length 32 ... IP6 fe80::3a2c:4aff:fe0e:feb2.546 > ff02::1:2.547: dhcp6 confirm IP6 fe80::d003:a6ff:fef2:6fe8.50536 > fe80::3a2c:4aff:fe0e:feb2.546: dhcp6 reply IP6 :: > ff02::1:ff00:1000: ICMP6, neighbor solicitation, who has 2a02:2427:513:1c03::1000, length 32 As you can see the client sends several type of dhcp6 requests: **solicit**, **request** and **confirm**, the requests are sent to the all-nodes multicast address. The DHCPv6 server must accept packets destined to the **547/UDP** port (dhcpv6-server) from the LAN. If you have not disabled the SLAAC configuration in radvd, the client will obtain **two IPv6 global addresses**: eth0: flags=4163 mtu 1500 inet 192.168.3.2 netmask 255.255.255.0 broadcast 192.168.3.255 inet6 2a02:2427:513:1c03::1000 prefixlen 128 scopeid 0x0 inet6 2a02:2427:513:1c03:3a2c:4aff:fe0e:feb2 prefixlen 64 scopeid 0x0 inet6 fe80::3a2c:4aff:fe0e:feb2 prefixlen 64 scopeid 0x20 FIXME Configure dynamic DNS to add AAAA and PTR records. ===== Firewall rules ===== What ports must be open to allow Router Advertisement messages and DHCPv6 traffic from the firewall to the LAN clients? * **SLAAC** - Stateless configuration uses ICMPv6 traffic: * A router sends Router Advertisement messages: they have a value of **134** in the Type field of the **ICMPv6 packet header**. * A client sends a Router Solicitation message to the all-routers multicast address; they have a value of **133** in the Type field of the **ICMPv6 packet header**. * **DHCPv6** - Stateful configuration uses IPv6 UDP traffic: * The dhcpv6-client uses the port **546/UDP**. * The dhcpv6-server uses the port **547/UDP**. ==== Configuring Shorewall ==== In the **/etc/shorewall/interfaces** configuration file specify the **dhcp** option for the ppp interface because it receives the configuration from the internet provider. Specify the **dhcp** option also for the LAN interface because it is used to provide IPv6 configuration to hosts. In this way you don't have to add specific firewall rules to let the traffic flow. #ZONE INTERFACE BROADCAST OPTIONS net NET_IF - dhcp,routefilter,tcpflags,nosmurfs,physical=ppp0 loc br0 detect dhcp When the internet connection is through PPPoE it is often required to sepcify the **CLAMPMSS=Yes** option in **/etc/shorewall/shorewall.conf**. This is to overcome criminally brain-dead ISPs or servers which block **ICMP Fragmentation Needed** packets. Using this option, the Linux kernel will examine and alter the MSS value of TCP SYN packets (requires the kernel option CONFIG_NETFILTER_XT_TARGET_TCPMSS): CLAMPMSS=Yes ==== Configuring Shorewall6 ==== In the **/etc/shorewall6/interfaces** configuration file specify the **dhcp** option for the ppp interface because it receives the configuration from the internet provider. Specify the **dhcp** option also for the LAN interface because it is used to provide IPv6 configuration to hosts. In this way you don't have to add specific firewall rules to let the traffic flow. #ZONE INTERFACE OPTIONS net NET_IF dhcp,tcpflags,forward=1,accept_ra=2,sourceroute=0,physical=ppp0 loc LOC_IF dhcp,tcpflags,forward=1,physical=br0 In the **/etc/shorewall6/rules** it is possibile to specify exception rules to the default policy, e.g. it is possibile to open a single port to a single IPv6 address: ACCEPT net:<2a01:4f8:130:11c7::2> $FW tcp smtp Like for the IPv4, the **CLAMPMSS=Yes** is often required in **/etc/shorewall/shorewall.conf** (see above): CLAMPMSS=Yes ===== Configuring hosts in the LAN ===== For IPv6 //Stateless Address Auto-configuration// (SLAAC) the client does not need to install any special package: the network interface will automatically get its address from the server (e.g. the host running the **radvd** daemon). To disable this default behaviour you must configure some //sysctl// kernel parameters, e.g. by creating a file **/etc/sysctl.d/local.conf** with: # Disable Stateless Address Auto-configuration (SLAAC) on eth1. net.ipv6.conf.eth1.autoconf=0 You can operate on the specific network interface (**eth1** in the example above) or on the **all** name; the specific value will prevail. Beside the **autoconf** option, other settings that can interfere with IPv6 configuration are **accept_ra** and **disable_ipv6**. To enable a **stateful** IPv6 configuration (e.g. to let the DHCPv6 server assign a specific IPv6 address to the client) you need a DHCPv6 server on your LAN (installing the **wide-dhcpv6-server** package) and you need to install the **wide-dhcpv6-client** package on the client. On the client, in the configuration file is **/etc/default/wide-dhcpv6-client**, you need to add an **interface** section: profile default { information-only; request domain-name-servers; request domain-name; script "/etc/wide-dhcpv6/dhcp6c-script"; }; interface eth1 { # Send and Identity Association for Non-temporary Addresses option, ID #0. send ia-na 0; # Request DNS information and update /etc/resolv.conf. request domain-name-servers, domain-name; script "/etc/wide-dhcpv6/dhcp6c-script"; }; # Non-temporary Addresses ID=0. id-assoc na 0 { }; **NOTICE**: The **profile default** section is ignored because the more specific **interface eth1** exists. The **script** statement is required to update the **/etc/resolv.conf** with DNS server and search path received from the DHCPv6 server. The script relies on the existence of the **resolvconf** package to perform the update of the file. On the server you can set the **VERBOSE=2** option in the configuration file **/etc/default/wide-dhcpv6-server**, so every client request will be logged: dhcp6s[199125]: server6_recv: received request from fe80::225:22ff:fedd:4598%br0 dhcp6s[199125]: dhcp6_get_options: get DHCP option client ID, len 14 dhcp6s[199125]: DUID: 00:01:00:01:2e:17:e3:48:00:25:22:dd:45:98 ... dhcp6s[199125]: lease_address: addr=2a02:2420:503:1c03::1000 dhcp6s[199125]: add_binding: add a new binding [IA: duid=00:01:00:01:2e:17:e3:48:00:25:22:dd:45:98, type=NA, iaid=0, duration=3600] To give a **persistent addresses** to the client, take note of the DUID received by the server and put it into a **host** section of the server configuration **dhcp6s.conf** (see above). The DUID is composed of a 64 bit random string and the MAC address of the client's network interface; once generated, it is stored into a file named **/var/lib/dhcpv6/dhcp6c_duid**. FIXME: What to do to protect LAN hosts from the internet? ===== MRU and MTU for PPPoE ===== In Debian GNU/Linux the default configuration file for the **pppd** daemon is **/etc/ppp/peers/provider**. It is possibile to specify both the //maximum transmission unit// (MTU) and the //maximum receive unit// (MRU). The MTU is used to configure the ppp interface, the MRU is used instead during the nogotiation phase to ask the peer to send packets of no more than the specified bytes (i.e. to set its MTU). The standard MTU size for Ethernet is 1500 bytes; PPPoE header uses 6 bytes and the PPP protocol ID uses 2 bytes, so the default MTU on a PPPoE interface is **1492** bytes. On the receiving side we don't want to pose restrictions to the peer, so we stay with the 1500 bytes default: debug mtu 1492 mru 1500 During the negotiation phase the **pppd** daemon may receive specific MRU requests from the internet provider, here it is a //debug// log in syslog: pppd[975]: rcvd [LCP ConfReq id=0x1 ] In this case the pppd will configure the **ppp** interface with the requested **1488 MTU**, instead of the 1492 from the configuration file. ===== IPv6 Prefixes ===== ^ FE80::/64 | Link-local prefix. | ^ FF02::1 | All-nodes multicast address. | ^ FF02::2 | All-routers multicast address. | ^ FF02::1:FFxx:xxxx | Duplicate Address Detection multicast group. | ===== Troubleshooting IPv6 problems ===== In general, to troubleshoot any networking issue, you use the **ping** command to check if the remote address is responding: # ping -6 2a01:4f8:1c17:7636::1 In some cases it is useful also to **ping your own addresses** (you can have more than one). Discover all of them with: # ip -6 address show dev enp0s7 2: enp0s7: mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet6 2a02:2420:503:1c03::2/128 scope global dadfailed tentative valid_lft forever preferred_lft forever inet6 2a02:2420:503:1c03:225:22ff:fedd:4598/64 scope global dynamic mngtmpaddr valid_lft 86391sec preferred_lft 14391sec inet6 fe80::225:22ff:fedd:4598/64 scope link valid_lft forever preferred_lft forever In the example above the interface have two **global** scope addresses, one assigned via DHCPv6 and the other obtained via SLAAC. The first one has the **dadfailed** flag, which means that the **duplicate address detection** has detected a conflict in the network. This means that the IP address in question will not be used as the source address and it does not even respond to a self-ping. If you have only one global IP address which is **dadfailed**, you cannot reach remote hosts on IPv6. It may be a difficult situation to diagnose, because using the simple **ifconfig** command you see the assigned IPv6 address and the **default route** is OK: # ip -6 route show 2a02:2420:503:1c03::2 dev enp0s7 proto kernel metric 256 pref medium 2a02:2420:503:1c03::/64 dev enp0s7 proto kernel metric 256 expires 86387sec pref medium fe80::/64 dev enp0s7 proto kernel metric 256 pref medium default via fe80::d003:a6ff:fef2:6fe8 dev enp0s7 proto ra metric 1024 expires 167sec hoplimit 64 pref medium **NOTICE**: The default router can be reached via its **link** scope address (as seen above) or via its **global** scope address: both can be used. If you sniff the ping request on the router, you can see the problem more clearly: # tcpdump -i eth0 -n 'icmp6' ... IP6 fe80::225:22ff:fedd:4598 > 2a01:4f8:1c17:7636::1: ICMP6, echo request, id 56698, seq 1, length 64 IP6 fe80::d003:a6ff:fef2:6fe8 > fe80::225:22ff:fedd:4598: ICMP6, destination unreachable, beyond scope 2a01:4f8:1c17:7636::1, source address fe80::225:22ff:fedd:4598, length 112 The client uses its link scope address as source address, which causes the **beyond scope** error. You can confirm this problem on the client using the ''ip route get'' command: # ip -6 route get 2a01:4f8:1c17:7636::1 2a01:4f8:1c17:7636::1 from :: via 2a02:2420:503:1c03::1 dev enp0s7 src fe80::225:22ff:fedd:4598 metric 1024 pref medium You can compare the result when you have at least one working global IP address: # ip -6 route get 2a01:4f8:1c17:7636::1 2a01:4f8:1c17:7636::1 from :: via fe80::d003:a6ff:fef2:6fe8 dev enp0s7 proto ra src 2a02:2420:503:1c03:225:22ff:fedd:4598 metric 1024 hoplimit 64 pref medium In the first case the **src** address is the **link scope** one, not suitable for routing. In the second case it is the one received via SLAAC, which has a **global scope** and indeed it is working. Notice that the address of the router (shown as the **via** address) is not releveant, even the //link scope// one does work. ===== Web References ===== * **[[https://www.networkacademy.io/ccna/ipv6/stateless-address-autoconfiguration-slaac|IPv6 Stateless Address Auto-configuration (SLAAC)]]** * **[[https://superuser.com/questions/742792/how-do-i-deploy-ipv6-within-a-lan-using-a-debian-based-router-and-prefix-delegat|How do I deploy IPv6 within a LAN using a Debian based router and prefix delegation?]]** * **[[https://www.growse.com/2015/09/04/ipv4-and-ipv6-ddns-with-dhcp-at-home.html|IPv4 and IPv6 DDNS with DHCP at home]]** * **[[https://sophiedogg.com/radvd-and-dhcpd6-server-configuration-for-dynamic-dns/|Radvd and DHCPd6 Server Configuration for Dynamic DNS]]** * **[[https://serverfault.com/questions/905332/getting-ipv6-via-radvd-dhcpd6-in-an-lxc-guest-working|getting ipv6 via radvd/dhcpd6 in an LXC guest working]]** * **[[https://superuser.com/questions/1341523/how-to-update-dns-aaaa-record-when-slaac-clients-appear|How to update DNS AAAA record when SLAAC clients appear]]** * **[[https://www.rjsystems.nl/en/2100-dhcpv6-stateful-autocfg.php|DHCPv6 stateful autoconfiguration]]** * **[[https://www.networkacademy.io/ccna/ipv6/stateful-dhcpv6|Stateful DHCPv6]]** * **[[https://www.juniper.net/documentation/us/en/software/junos/subscriber-mgmt-vlan/topics/concept/pppoe-subscriber-access-mru-mtu-overview.html|Understanding MTU and MRU Configuration for PPP Subscribers]]**