通过libvirt virtual network配置dhcpv6的环境,并通过tcpdump抓包,观察RA 和 DHCP相关的包,了解RA的flag, 以及DHCPv6 是怎样工作的。 IPv6 provisioning works a bit differently than IPv4 provisioning. The first and most important part of IPv6 provisioning is the Router Advertisement (RA). It is sent by a router to advertise its presence and to communicate the basic network settings of the local network. It contains (amongst other information):

  • whether the router can be used as a default gateway and for how long (R flag)
  • whether there is a stateful DHCPv6 server available (M flag)
  • whether there is a stateless DHCPv6 server available(O flag)
  • which prefixes are used on the local network(prefix)
  • for each prefix: whether that prefix may be used for auto-configuration
  • it may also contain DNS domains and resolvers

Steps:

  1. Prepare a vm on the host;
  2. Prepare virtual networks on the host:
[root@host ~]# cat dhcpv6_net.xml 
<network>
  <name>dhcpv6_net</name>
  <ip address='192.168.100.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.100.2' end='192.168.100.254'/>
    </dhcp>
  </ip>
  <ip family='ipv6' address='fd12:3456:789a:1::1' prefix='64'>
    <dhcp>
      <range start='fd12:3456:789a:1::100' end='fd12:3456:789a:1::1ff'/>
    </dhcp>
  </ip>
</network>
[root@host]# virsh net-define dhcpv6_net.xml
Network dhcpv6_net defined from dhcpv6_net.xml

[root@host]# virsh net-start dhcpv6_net
Network dhcpv6_net started
  1. Prepare vm with 2 interfaces connected to default network and dhcpv6_net separately: Note: network “default” is the libvirt built-in one, it has no ipv6 setting.
[root@host]# virsh dumpxml rhel --xpath //interface 
<interface type="network">
  <mac address="52:54:00:03:d6:88"/>
  <source network="dhcpv6_net"/>
  <model type="virtio"/>
  <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
</interface>
<interface type="network">
  <mac address="52:54:00:4f:1e:2a"/>
  <source network="default"/>
  <model type="virtio"/>
  <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>
</interface>
  1. Start and login the vm via ssh(use the default network interface, root@192.168.122.59), and check on the VM for the ip address:
[root@host]# virsh start rhel 
Domain 'rhel' started

[root@host]# virsh domiflist rhel 
 Interface   Type      Source       Model    MAC
----------------------------------------------------------------
 vnet4       network   dhcpv6_net   virtio   52:54:00:03:d6:88
 vnet5       network   default      virtio   52:54:00:4f:1e:2a

[root@host]# virsh domifaddr rhel 
 Name       MAC address          Protocol     Address
-------------------------------------------------------------------------------
 vnet4      52:54:00:03:d6:88    ipv6         fd12:3456:789a:1::179/64
 -          -                    ipv4         192.168.100.13/24
 vnet5      52:54:00:4f:1e:2a    ipv4         192.168.122.59/24
[root@host]# ssh root@192.168.122.59 
root@192.168.122.59's password: 
Last login: Thu Jan 18 13:30:12 2024
[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:03:d6:88 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.13/24 brd 192.168.100.255 scope global dynamic noprefixroute enp6s0
       valid_lft 3525sec preferred_lft 3525sec
    inet6 fd12:3456:789a:1::179/128 scope global dynamic noprefixroute 
       valid_lft 86326sec preferred_lft 86326sec
    inet6 fe80::e282:a881:aff1:78de/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: enp8s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:4f:1e:2a brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.59/24 brd 192.168.122.255 scope global dynamic noprefixroute enp8s0
       valid_lft 3525sec preferred_lft 3525sec
    inet6 fe80::bb2d:d71:9e7f:2464/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

lo:

  • 127.0.0.1/8 for ipv4 and ::1/128 for ipv6, scope host
  • The loopback device is a special, virtual network interface that your computer uses to communicate with itself.
  • This IP has the hostname of localhost mapped to it.(Try to ping localhost on the guest by “ping -4 localhost” and “ping -6 localhost”, check the outputs)

enp6s0:

  • Connected to the virtual network dhcpv6_net
  • inet6 fd12:3456:789a:1::179/128 scope global dynamic noprefixroute valid_lft 86393sec preferred_lft 86393sec
  • This is dhcpv6 address, prefix length is 128 since dhcpv6 do not provide the prefix length information, it is get form the RA instead;
  • “scope global” indicates it’s a global address, meaning it is reachable on the entire Internet.
  • “dynamic” indicates that the address was likely assigned dynamically, possibly through DHCP (Dynamic Host Configuration Protocol), rather than being statically configured.
  • “valid_lft 86134sec preferred_lft 86134sec” means the address is considered valid for 86134 seconds. And the preferred lifetime of the address, which is also 86134 seconds. During this time, the address is the preferred one to use.
  • inet6 fe80::e282:a881:aff1:78de/64 scope link noprefixroute valid_lft forever preferred_lft forever
  • “scope link” means it is a link local address; It is to be used for communication within a logical division of the network. Router will not forward a packet with source or target address is link local address.
  • FE80::/10 + EUI-64 interface id, autogenerated.

enp8s0:

  • The interface connected to the default network without ipv6 setting. The interface only has ipv6 link local address configured.
  1. Let's capture the ipv6 packet from the very beginning. Set the interface enp6s0 with connection.autoconnect=no, then reboot the VM; With this setting, after reboot, this interface will be up without any ip address.
[root@localhost ~]# nmcli con 
NAME                UUID                                  TYPE      DEVICE 
Wired connection 3  3ef2e14c-9f0d-3eed-8f4b-3352107b1ef0  ethernet  enp8s0 
Wired connection 2  4354b5ca-10e4-3f4d-90dc-f63235658c00  ethernet  enp6s0 
lo                  9f2a1a01-fe45-4102-9f9c-46e05193aec0  loopback  lo     

[root@localhost ~]# nmcli con mod 4354b5ca-10e4-3f4d-90dc-f63235658c00 connection.autoconnect no

[root@localhost ~]# nmcli con show 4354b5ca-10e4-3f4d-90dc-f63235658c00 | grep connection.autoconnect:
connection.autoconnect:                 no

[root@localhost ~]# reboot
  1. After reboot, login the guest via ssh and check enp6s0 should have no ip address. Run tcpdump on the guest to capture the ipv6 packets on enp6s0.
[root@host]# ssh root@192.168.122.59 
root@192.168.122.59's password: 
Last login: Thu Jan 18 13:35:10 2024 from 192.168.122.1
[root@localhost ~]# 
[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:03:d6:88 brd ff:ff:ff:ff:ff:ff
3: enp8s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:4f:1e:2a brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.59/24 brd 192.168.122.255 scope global dynamic noprefixroute enp8s0
       valid_lft 3590sec preferred_lft 3590sec
    inet6 fe80::2117:f76d:57a5:af89/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
[root@localhost ~]# tcpdump -i enp6s0 ip6 -vvv
dropped privs to tcpdump
tcpdump: listening on enp6s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
......

Note: you can save the output to a .pcap file and open it by wireshark which is more convenient.

# tcpdump -i enp6s0 ip6 -w dhcpv6.pcap
  1. Open another terminal on the host, and login guest via ssh, set connection.autoconnect=yes, the interface will be initialized, and then check it has get dhcpv6 address;
[root@host]# ssh root@192.168.122.59
root@192.168.122.59's password: 
Last login: Fri Jan 19 12:11:42 2024 from 192.168.122.1
[root@localhost ~]# nmcli con mod 4354b5ca-10e4-3f4d-90dc-f63235658c00 connection.autoconnect yes
[root@localhost ~]# ip a show enp6s0
2: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:03:d6:88 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.13/24 brd 192.168.100.255 scope global dynamic noprefixroute enp6s0
       valid_lft 3579sec preferred_lft 3579sec
    inet6 fd12:3456:789a:1::179/128 scope global dynamic noprefixroute 
       valid_lft 86380sec preferred_lft 86380sec
    inet6 fe80::7934:4fe4:48e6:2d70/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
  1. Check the tcpdump outputs on the vm; Or you can use wireshark to check it(download the file here and open it by wireshark on your laptop).
  • M-flag - if it is set to 1, this informs hosts that they can obtain a global address as well as DNS and a domain name from a Stateful DHCPv6 server. Typically this means that auto-addressing using SLAAC is not allowed on this segment and both the A-flag and the O-flag are set to 0.
  • O-flag - if it is set to 1, this informs hosts that they can obtain a DNS server list and a domain name from a Stateless DHCPv6 server, but not addressing information. Typically it works in conjunction with SLAAC for auto-addressing and both the A-flag and the O-flag are set to 1.
  • A-flag - if it is set to 1, this informs hosts that they can auto-generate GUA addresses using SLAAC. If it is set to 0 means that auto-configuration is not allowed for this segment. Screenshot from 2024-01-23 00-23-13.png In this case, M=1, O=1, A=0, use dhcp server to distribute and manage the ipv6 address, and provide other information like dns server, domain name.
  1. Check the dns server:
[root@localhost ~]# cat /etc/resolv.conf 
# Generated by NetworkManager
nameserver 192.168.122.1
nameserver 192.168.100.1
nameserver fd12:3456:789a:1::1 ---> virbr*'s GUA 
# NOTE: the libc resolver may not support more than 3 nameservers.
# The nameservers listed below may not be recognized.
nameserver fe80::5054:ff:fe25:c5c%enp6s0  ---> virbr*'s linklocal address
  1. Check the route, no default gw for ipv6.
[root@localhost ~]# ip -6 route 
fd12:3456:789a:1::179 dev enp6s0 proto kernel metric 101 pref medium
fd12:3456:789a:1::/64 dev enp6s0 proto ra metric 101 pref medium
fe80::/64 dev enp8s0 proto kernel metric 1024 pref medium
fe80::/64 dev enp6s0 proto kernel metric 1024 pref medium
  1. Check the ipv6 multicast group: (refer to https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xml)
[root@localhost ~]# netstat -g -6
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
IPv6/IPv4 Group Memberships
Interface       RefCnt Group
--------------- ------ ---------------------
lo              1      ff02::1   <----- All nodes address, link-local scope multicast address
lo              1      ff01::1   <----- All nodes address, Node-Local Scope Multicast Addresses
enp6s0          1      ff02::1:ff00:179  <----Solicited-node Multicast Address for dhcpv6 addr
enp6s0          1      ff02::1:fff1:78de <----Solicited-node Multicast Address for linklocal addr
enp6s0          1      ff02::1
enp6s0          1      ff01::1
enp8s0          1      ff02::1:ff7f:2464 <----Solicited-node Multicast Address for linklocal addr
enp8s0          1      ff02::1
enp8s0          1      ff01::1
[root@localhost ~]# ip a show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:03:d6:88 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.13/24 brd 192.168.100.255 scope global dynamic noprefixroute enp6s0
       valid_lft 3016sec preferred_lft 3016sec
    inet6 fd12:3456:789a:1::179/128 scope global dynamic noprefixroute 
       valid_lft 85813sec preferred_lft 85813sec
    inet6 fe80::e282:a881:aff1:78de/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: enp8s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:4f:1e:2a brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.59/24 brd 192.168.122.255 scope global dynamic noprefixroute enp8s0
       valid_lft 2847sec preferred_lft 2847sec
    inet6 fe80::bb2d:d71:9e7f:2464/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
  • Every interface will join the groups ff02::1 and ff01::1;
  • For interface except lo, for each ipv6 address, there will be a Solicited-node Multicast Address generated and joined;
  • Solicited-node Multicast Address is FF02::1:FF00:0/104 + unicast addr last 24 bits(last 6 hex digits); And in the picture above, we can see there are only 2 dhcp packets: solicit and reply, that's because the rapid commit is enabled.