Browse code

Add IPv6 support for tenant data network

Define IP_VERSION with one of the three values 4, 6, or 4+6 in
your localrc to indicate if you intend to run your tenant data network
as either IPv4, IPv6, or dual stack respectively. Default value is 4.

If your IP_VERSION is set to 6 or 4+6, then the following variables
should be defined in your localrc:
- FIXED_RANGE_V6: The IPv6 prefix for your tenant network
- IPV6_PRIVATE_NETWORK_GATEWAY: The gateway IP with the same prefix
- IPV6_RA_MODE (with default as slaac)
- IPV6_ADDRESS_MODE (with default as slaac)

If you're going to use IPV6_RA_MODE/IPV6_ADDRESS_MODE settings other
than the defaults then you should make sure your VM image has dhcpv6
client enabled at bootup, otherwise you'll need to run it manually
after the VM is booted.

It's recommended to run the latest version of dnsmasq 2.68.
If you intend to enable internet access in your VM, make sure
your network node has IPv6 internet access, and the IPv6 prefix for
your tenant network is a GUA and routable.

Implements: blueprint ipv6-support
Change-Id: I848abf18e00e2a869697c5ef6366bc567dde448a
Co-Authored-By: John Davidge <jodavidg@cisco.com>

John Davidge authored on 2014/06/30 22:55:11
Showing 2 changed files
... ...
@@ -321,6 +321,35 @@ API rate limits
321 321
 
322 322
         API_RATE_LIMIT=False
323 323
 
324
+IP Version
325
+    | Default: ``IP_VERSION=4``
326
+    | This setting can be used to configure DevStack to create either an IPv4,
327
+      IPv6, or dual stack tenant data network by setting ``IP_VERSION`` to
328
+      either ``IP_VERSION=4``, ``IP_VERSION=6``, or ``IP_VERSION=4+6``
329
+      respectively. This functionality requires that the Neutron networking
330
+      service is enabled by setting the following options:
331
+    |
332
+
333
+    ::
334
+
335
+        disable_service n-net
336
+        enable_service q-svc q-agt q-dhcp q-l3
337
+
338
+    | The following optional variables can be used to alter the default IPv6
339
+      behavior:
340
+    |
341
+
342
+    ::
343
+
344
+        IPV6_RA_MODE=slaac
345
+        IPV6_ADDRESS_MODE=slaac
346
+        FIXED_RANGE_V6=fd$IPV6_GLOBAL_ID::/64
347
+        IPV6_PRIVATE_NETWORK_GATEWAY=fd$IPV6_GLOBAL_ID::1
348
+
349
+    | *Note: ``FIXED_RANGE_V6`` and ``IPV6_PRIVATE_NETWORK_GATEWAY``
350
+      can be configured with any valid IPv6 prefix. The default values make
351
+      use of an auto-generated ``IPV6_GLOBAL_ID`` to comply with RFC 4193.*
352
+
324 353
 Examples
325 354
 ~~~~~~~~
326 355
 
... ...
@@ -51,10 +51,22 @@
51 51
 #
52 52
 # With Neutron networking the NETWORK_MANAGER variable is ignored.
53 53
 
54
+# Settings
55
+# --------
56
+
57
+# Timeout value in seconds to wait for IPv6 gateway configuration
58
+GATEWAY_TIMEOUT=30
59
+
54 60
 
55 61
 # Neutron Network Configuration
56 62
 # -----------------------------
57 63
 
64
+# Subnet IP version
65
+IP_VERSION=${IP_VERSION:-4}
66
+# Validate IP_VERSION
67
+if [[ $IP_VERSION != "4" ]] && [[ $IP_VERSION != "6" ]] && [[ $IP_VERSION != "4+6" ]]; then
68
+    die $LINENO "IP_VERSION must be either 4, 6, or 4+6"
69
+fi
58 70
 # Gateway and subnet defaults, in case they are not customized in localrc
59 71
 NETWORK_GATEWAY=${NETWORK_GATEWAY:-10.0.0.1}
60 72
 PUBLIC_NETWORK_GATEWAY=${PUBLIC_NETWORK_GATEWAY:-172.24.4.1}
... ...
@@ -65,6 +77,22 @@ if is_ssl_enabled_service "neutron" || is_service_enabled tls-proxy; then
65 65
     Q_PROTOCOL="https"
66 66
 fi
67 67
 
68
+# Generate 40-bit IPv6 Global ID to comply with RFC 4193
69
+IPV6_GLOBAL_ID=`uuidgen | sed s/-//g | cut -c 23- | sed -e "s/\(..\)\(....\)\(....\)/\1:\2:\3/"`
70
+
71
+# IPv6 gateway and subnet defaults, in case they are not customized in localrc
72
+IPV6_RA_MODE=${IPV6_RA_MODE:-slaac}
73
+IPV6_ADDRESS_MODE=${IPV6_ADDRESS_MODE:-slaac}
74
+IPV6_PUBLIC_SUBNET_NAME=${IPV6_PUBLIC_SUBNET_NAME:-ipv6-public-subnet}
75
+IPV6_PRIVATE_SUBNET_NAME=${IPV6_PRIVATE_SUBNET_NAME:-ipv6-private-subnet}
76
+FIXED_RANGE_V6=${FIXED_RANGE_V6:-fd$IPV6_GLOBAL_ID::/64}
77
+IPV6_PRIVATE_NETWORK_GATEWAY=${IPV6_PRIVATE_NETWORK_GATEWAY:-fd$IPV6_GLOBAL_ID::1}
78
+IPV6_PUBLIC_RANGE=${IPV6_PUBLIC_RANGE:-fe80:cafe:cafe::/64}
79
+IPV6_PUBLIC_NETWORK_GATEWAY=${IPV6_PUBLIC_NETWORK_GATEWAY:-fe80:cafe:cafe::2}
80
+# IPV6_ROUTER_GW_IP must be defined when IP_VERSION=4+6 as it cannot be
81
+# obtained conventionally until the l3-agent has support for dual-stack
82
+# TODO (john-davidge) Remove once l3-agent supports dual-stack
83
+IPV6_ROUTER_GW_IP=${IPV6_ROUTER_GW_IP:-fe80:cafe:cafe::1}
68 84
 
69 85
 # Set up default directories
70 86
 GITDIR["python-neutronclient"]=$DEST/python-neutronclient
... ...
@@ -531,8 +559,16 @@ function create_neutron_initial_network {
531 531
     else
532 532
         NET_ID=$(neutron net-create --tenant-id $TENANT_ID "$PRIVATE_NETWORK_NAME" | grep ' id ' | get_field 2)
533 533
         die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $PHYSICAL_NETWORK $TENANT_ID"
534
-        SUBNET_ID=$(neutron subnet-create --tenant-id $TENANT_ID --ip_version 4 --gateway $NETWORK_GATEWAY --name $PRIVATE_SUBNET_NAME $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
535
-        die_if_not_set $LINENO SUBNET_ID "Failure creating SUBNET_ID for $TENANT_ID"
534
+
535
+        if [[ "$IP_VERSION" =~ 4.* ]]; then
536
+            # Create IPv4 private subnet
537
+            SUBNET_ID=$(_neutron_create_private_subnet_v4)
538
+        fi
539
+
540
+        if [[ "$IP_VERSION" =~ .*6 ]]; then
541
+            # Create IPv6 private subnet
542
+            IPV6_SUBNET_ID=$(_neutron_create_private_subnet_v6)
543
+        fi
536 544
     fi
537 545
 
538 546
     if [[ "$Q_L3_ENABLED" == "True" ]]; then
... ...
@@ -546,7 +582,7 @@ function create_neutron_initial_network {
546 546
             ROUTER_ID=$(neutron router-create $Q_ROUTER_NAME | grep ' id ' | get_field 2)
547 547
             die_if_not_set $LINENO ROUTER_ID "Failure creating ROUTER_ID for $Q_ROUTER_NAME"
548 548
         fi
549
-        neutron router-interface-add $ROUTER_ID $SUBNET_ID
549
+
550 550
         # Create an external network, and a subnet. Configure the external network as router gw
551 551
         if [ "$Q_USE_PROVIDERNET_FOR_PUBLIC" = "True" ]; then
552 552
             EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True --provider:network_type=flat --provider:physical_network=${PUBLIC_PHYSICAL_NETWORK} | grep ' id ' | get_field 2)
... ...
@@ -554,35 +590,15 @@ function create_neutron_initial_network {
554 554
             EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True | grep ' id ' | get_field 2)
555 555
         fi
556 556
         die_if_not_set $LINENO EXT_NET_ID "Failure creating EXT_NET_ID for $PUBLIC_NETWORK_NAME"
557
-        EXT_GW_IP=$(neutron subnet-create --ip_version 4 ${Q_FLOATING_ALLOCATION_POOL:+--allocation-pool $Q_FLOATING_ALLOCATION_POOL} --gateway $PUBLIC_NETWORK_GATEWAY --name $PUBLIC_SUBNET_NAME $EXT_NET_ID $FLOATING_RANGE -- --enable_dhcp=False | grep 'gateway_ip' | get_field 2)
558
-        die_if_not_set $LINENO EXT_GW_IP "Failure creating EXT_GW_IP"
559
-        neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
560 557
 
561
-        if is_service_enabled q-l3; then
562
-            # logic is specific to using the l3-agent for l3
563
-            if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
564
-                local ext_gw_interface
565
-
566
-                if [[ "$Q_USE_PUBLIC_VETH" = "True" ]]; then
567
-                    ext_gw_interface=$Q_PUBLIC_VETH_EX
568
-                else
569
-                    # Disable in-band as we are going to use local port
570
-                    # to communicate with VMs
571
-                    sudo ovs-vsctl set Bridge $PUBLIC_BRIDGE \
572
-                        other_config:disable-in-band=true
573
-                    ext_gw_interface=$PUBLIC_BRIDGE
574
-                fi
575
-                CIDR_LEN=${FLOATING_RANGE#*/}
576
-                sudo ip addr add $EXT_GW_IP/$CIDR_LEN dev $ext_gw_interface
577
-                sudo ip link set $ext_gw_interface up
578
-                ROUTER_GW_IP=`neutron port-list -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' '{ print $8; }'`
579
-                die_if_not_set $LINENO ROUTER_GW_IP "Failure retrieving ROUTER_GW_IP"
580
-                sudo route add -net $FIXED_RANGE gw $ROUTER_GW_IP
581
-            fi
582
-            if [[ "$Q_USE_NAMESPACE" == "False" ]]; then
583
-                # Explicitly set router id in l3 agent configuration
584
-                iniset $Q_L3_CONF_FILE DEFAULT router_id $ROUTER_ID
585
-            fi
558
+        if [[ "$IP_VERSION" =~ 4.* ]]; then
559
+            # Configure router for IPv4 public access
560
+            _neutron_configure_router_v4
561
+        fi
562
+
563
+        if [[ "$IP_VERSION" =~ .*6 ]]; then
564
+            # Configure router for IPv6 public access
565
+            _neutron_configure_router_v6
586 566
         fi
587 567
     fi
588 568
 }
... ...
@@ -1050,6 +1066,172 @@ function _neutron_setup_interface_driver {
1050 1050
     neutron_plugin_setup_interface_driver $1
1051 1051
 }
1052 1052
 
1053
+# Create private IPv4 subnet
1054
+function _neutron_create_private_subnet_v4 {
1055
+    local subnet_params="--tenant-id $TENANT_ID "
1056
+    subnet_params+="--ip_version 4 "
1057
+    subnet_params+="--gateway $NETWORK_GATEWAY "
1058
+    subnet_params+="--name $PRIVATE_SUBNET_NAME "
1059
+    subnet_params+="$NET_ID $FIXED_RANGE"
1060
+    local subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
1061
+    die_if_not_set $LINENO subnet_id "Failure creating private IPv4 subnet for $TENANT_ID"
1062
+    echo $subnet_id
1063
+}
1064
+
1065
+# Create private IPv6 subnet
1066
+function _neutron_create_private_subnet_v6 {
1067
+    die_if_not_set $LINENO IPV6_RA_MODE "IPV6 RA Mode not set"
1068
+    die_if_not_set $LINENO IPV6_ADDRESS_MODE "IPV6 Address Mode not set"
1069
+    local ipv6_modes="--ipv6-ra-mode $IPV6_RA_MODE --ipv6-address-mode $IPV6_ADDRESS_MODE"
1070
+    local subnet_params="--tenant-id $TENANT_ID "
1071
+    subnet_params+="--ip_version 6 "
1072
+    subnet_params+="--gateway $IPV6_PRIVATE_NETWORK_GATEWAY "
1073
+    subnet_params+="--name $IPV6_PRIVATE_SUBNET_NAME "
1074
+    subnet_params+="$NET_ID $FIXED_RANGE_V6 $ipv6_modes"
1075
+    local ipv6_subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
1076
+    die_if_not_set $LINENO ipv6_subnet_id "Failure creating private IPv6 subnet for $TENANT_ID"
1077
+    echo $ipv6_subnet_id
1078
+}
1079
+
1080
+# Create public IPv4 subnet
1081
+function _neutron_create_public_subnet_v4 {
1082
+    local subnet_params+="--ip_version 4 "
1083
+    subnet_params+="${Q_FLOATING_ALLOCATION_POOL:+--allocation-pool $Q_FLOATING_ALLOCATION_POOL} "
1084
+    subnet_params+="--gateway $PUBLIC_NETWORK_GATEWAY "
1085
+    subnet_params+="--name $PUBLIC_SUBNET_NAME "
1086
+    subnet_params+="$EXT_NET_ID $FLOATING_RANGE "
1087
+    subnet_params+="-- --enable_dhcp=False"
1088
+    local id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
1089
+    die_if_not_set $LINENO id_and_ext_gw_ip "Failure creating public IPv4 subnet"
1090
+    echo $id_and_ext_gw_ip
1091
+}
1092
+
1093
+# Create public IPv6 subnet
1094
+function _neutron_create_public_subnet_v6 {
1095
+    local subnet_params="--ip_version 6 "
1096
+    subnet_params+="--gateway $IPV6_PUBLIC_NETWORK_GATEWAY "
1097
+    subnet_params+="--name $IPV6_PUBLIC_SUBNET_NAME "
1098
+    subnet_params+="$EXT_NET_ID $IPV6_PUBLIC_RANGE "
1099
+    subnet_params+="-- --enable_dhcp=False"
1100
+    local ipv6_id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
1101
+    die_if_not_set $LINENO ipv6_id_and_ext_gw_ip "Failure creating an IPv6 public subnet"
1102
+    echo $ipv6_id_and_ext_gw_ip
1103
+}
1104
+
1105
+# Configure neutron router for IPv4 public access
1106
+function _neutron_configure_router_v4 {
1107
+    neutron router-interface-add $ROUTER_ID $SUBNET_ID
1108
+    # Create a public subnet on the external network
1109
+    local id_and_ext_gw_ip=$(_neutron_create_public_subnet_v4 $EXT_NET_ID)
1110
+    local ext_gw_ip=$(echo $id_and_ext_gw_ip  | get_field 2)
1111
+    PUB_SUBNET_ID=$(echo $id_and_ext_gw_ip | get_field 5)
1112
+    # Configure the external network as the default router gateway
1113
+    neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
1114
+
1115
+    # This logic is specific to using the l3-agent for layer 3
1116
+    if is_service_enabled q-l3; then
1117
+        # Configure and enable public bridge
1118
+        if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
1119
+            local ext_gw_interface=$(_neutron_get_ext_gw_interface)
1120
+            local cidr_len=${FLOATING_RANGE#*/}
1121
+            sudo ip addr add $ext_gw_ip/$cidr_len dev $ext_gw_interface
1122
+            sudo ip link set $ext_gw_interface up
1123
+            ROUTER_GW_IP=`neutron port-list -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$PUB_SUBNET_ID '$4 == subnet_id { print $8; }'`
1124
+            die_if_not_set $LINENO ROUTER_GW_IP "Failure retrieving ROUTER_GW_IP"
1125
+            sudo route add -net $FIXED_RANGE gw $ROUTER_GW_IP
1126
+        fi
1127
+        _neutron_set_router_id
1128
+    fi
1129
+}
1130
+
1131
+# Configure neutron router for IPv6 public access
1132
+function _neutron_configure_router_v6 {
1133
+    neutron router-interface-add $ROUTER_ID $IPV6_SUBNET_ID
1134
+    # Create a public subnet on the external network
1135
+    local ipv6_id_and_ext_gw_ip=$(_neutron_create_public_subnet_v6 $EXT_NET_ID)
1136
+    local ipv6_ext_gw_ip=$(echo $ipv6_id_and_ext_gw_ip | get_field 2)
1137
+    local ipv6_pub_subnet_id=$(echo $ipv6_id_and_ext_gw_ip | get_field 5)
1138
+
1139
+    # If the external network has not already been set as the default router
1140
+    # gateway when configuring an IPv4 public subnet, do so now
1141
+    if [[ "$IP_VERSION" == "6" ]]; then
1142
+        neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
1143
+    fi
1144
+
1145
+    # This logic is specific to using the l3-agent for layer 3
1146
+    if is_service_enabled q-l3; then
1147
+        local ipv6_router_gw_port
1148
+        # Ensure IPv6 forwarding is enabled on the host
1149
+        sudo sysctl -w net.ipv6.conf.all.forwarding=1
1150
+        # Configure and enable public bridge
1151
+        if [[ "$IP_VERSION" = "6" ]]; then
1152
+            # Override global IPV6_ROUTER_GW_IP with the true value from neutron
1153
+            IPV6_ROUTER_GW_IP=`neutron port-list -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$ipv6_pub_subnet_id '$4 == subnet_id { print $8; }'`
1154
+            die_if_not_set $LINENO IPV6_ROUTER_GW_IP "Failure retrieving IPV6_ROUTER_GW_IP"
1155
+            ipv6_router_gw_port=`neutron port-list -c id -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$ipv6_pub_subnet_id '$4 == subnet_id { print $1; }' | awk -F ' | ' '{ print $2; }'`
1156
+            die_if_not_set $LINENO ipv6_router_gw_port "Failure retrieving ipv6_router_gw_port"
1157
+        else
1158
+            ipv6_router_gw_port=`neutron port-list -c id -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$PUB_SUBNET_ID '$4 == subnet_id { print $1; }' | awk -F ' | ' '{ print $2; }'`
1159
+            die_if_not_set $LINENO ipv6_router_gw_port "Failure retrieving ipv6_router_gw_port"
1160
+        fi
1161
+
1162
+        # The ovs_base_configure_l3_agent function flushes the public
1163
+        # bridge's ip addresses, so turn IPv6 support in the host off
1164
+        # and then on to recover the public bridge's link local address
1165
+        sudo sysctl -w net.ipv6.conf.${PUBLIC_BRIDGE}.disable_ipv6=1
1166
+        sudo sysctl -w net.ipv6.conf.${PUBLIC_BRIDGE}.disable_ipv6=0
1167
+        if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
1168
+            local ext_gw_interface=$(_neutron_get_ext_gw_interface)
1169
+            local ipv6_cidr_len=${IPV6_PUBLIC_RANGE#*/}
1170
+
1171
+            # Define router_ns based on whether DVR is enabled
1172
+            local router_ns=qrouter
1173
+            if [[ "$Q_DVR_MODE" == "dvr_snat" ]]; then
1174
+                router_ns=snat
1175
+            fi
1176
+
1177
+            # Configure interface for public bridge
1178
+            sudo ip -6 addr add $ipv6_ext_gw_ip/$ipv6_cidr_len dev $ext_gw_interface
1179
+
1180
+            # Wait until layer 3 agent has configured the gateway port on
1181
+            # the public bridge, then add gateway address to the interface
1182
+            # TODO (john-davidge) Remove once l3-agent supports dual-stack
1183
+            if [[ "$IP_VERSION" == "4+6" ]]; then
1184
+                if ! timeout $GATEWAY_TIMEOUT sh -c "until sudo ip netns exec $router_ns-$ROUTER_ID ip addr show qg-${ipv6_router_gw_port:0:11} | grep $ROUTER_GW_IP; do sleep 1; done"; then
1185
+                    die $LINENO "Timeout retrieving ROUTER_GW_IP"
1186
+                fi
1187
+                # Configure the gateway port with the public IPv6 adress
1188
+                sudo ip netns exec $router_ns-$ROUTER_ID ip -6 addr add $IPV6_ROUTER_GW_IP/$ipv6_cidr_len dev qg-${ipv6_router_gw_port:0:11}
1189
+                # Add a default IPv6 route to the neutron router as the
1190
+                # l3-agent does not add one in the dual-stack case
1191
+                sudo ip netns exec $router_ns-$ROUTER_ID ip -6 route replace default via $ipv6_ext_gw_ip dev qg-${ipv6_router_gw_port:0:11}
1192
+            fi
1193
+            sudo ip -6 route add $FIXED_RANGE_V6 via $IPV6_ROUTER_GW_IP dev $ext_gw_interface
1194
+        fi
1195
+        _neutron_set_router_id
1196
+    fi
1197
+}
1198
+
1199
+# Explicitly set router id in l3 agent configuration
1200
+function _neutron_set_router_id {
1201
+    if [[ "$Q_USE_NAMESPACE" == "False" ]]; then
1202
+        iniset $Q_L3_CONF_FILE DEFAULT router_id $ROUTER_ID
1203
+    fi
1204
+}
1205
+
1206
+# Get ext_gw_interface depending on value of Q_USE_PUBLIC_VETH
1207
+function _neutron_get_ext_gw_interface {
1208
+    if [[ "$Q_USE_PUBLIC_VETH" == "True" ]]; then
1209
+        echo $Q_PUBLIC_VETH_EX
1210
+    else
1211
+        # Disable in-band as we are going to use local port
1212
+        # to communicate with VMs
1213
+        sudo ovs-vsctl set Bridge $PUBLIC_BRIDGE \
1214
+            other_config:disable-in-band=true
1215
+        echo $PUBLIC_BRIDGE
1216
+    fi
1217
+}
1218
+
1053 1219
 # Functions for Neutron Exercises
1054 1220
 #--------------------------------
1055 1221