User Tools

Site Tools


doc:appunti:net:ipv6_on_ppp

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<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1488
        inet 145.56.63.214  netmask 255.255.255.255  destination 146.31.204.24
        inet6 fe80::1  prefixlen 128  scopeid 0x20<link>
        inet6 2a02:2425:134:1b0::1  prefixlen 64  scopeid 0x0<global>
        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<UP,BROADCAST,RUNNING,MULTICAST>  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<link>
        inet6 2a02:2427:513:1c03::1  prefixlen 64  scopeid 0x0<global>
        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<UP,BROADCAST,RUNNING,MULTICAST>  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<global>
        inet6 fe80::3a2c:4aff:fe0e:feb2  prefixlen 64  scopeid 0x20<link>
        ...

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<UP,BROADCAST,RUNNING,MULTICAST>  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<global>
        inet6 2a02:2427:513:1c03:3a2c:4aff:fe0e:feb2  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::3a2c:4aff:fe0e:feb2  prefixlen 64  scopeid 0x20<link>

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 <mru 1488> <auth pap> <magic 0xd18deef6>]

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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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

doc/appunti/net/ipv6_on_ppp.txt · Last modified: 2024/07/05 13:37 by niccolo