You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ra...@apache.org on 2017/01/27 00:13:41 UTC

[1/4] git commit: updated refs/heads/master to f10c8bf

Repository: cloudstack
Updated Branches:
  refs/heads/master 4721c53ea -> f10c8bfe0


CLOUDSTACK-9359: IPv6 for Basic Networking with KVM

This commit adds the initial functionality for IPv6 in Basic Networking.

When a valid IPv6 CIDR is configured for the POD/VLAN the DirectPodBasedNetworkGuru
will use the EUI-64 calculation to calculate the IPv6 Address the Instance will obtain.

For this it is required that the physical routers in the Layer 2 network (POD/VLAN) send out
Router Advertisements with the same subnet as configured in CloudStack.

A example subnet could be 2001:db8::/64

Using radvd a Linux Router could send out Router Advertisements using this configuration:

  interface eth0
  {

	MinRtrAdvInterval 5;
	MaxRtrAdvInterval 60;
	AdvSendAdvert on;
        AdvOtherConfigFlag off;
        IgnoreIfMissing off;

	prefix 2001:db8::/64 {
	};

        RDNSS 2001:db8:ffff::53 {
        };
  };

A Instance with MAC Address 06:7a:88:00:00:8b will obtain IPv6 address 2001:db8:100::47a:88ff:fe00:8b

Both Windows, Linux and FreeBSD use the same calculation for their IPv6 Addresses, this is specified
in RFC4862 (IPv6 Stateless Address Autoconfiguration).

Under Linux it is mandatory that IPv6 Privacy Extensions are disabled:

$ sysctl -w net.ipv6.conf.all.use_tempaddr=0

Windows should be configured to use the MAC Address as the identifier for the EUI-64/SLAAC calculation.

$ netsh interface ipv6 set privacy state=disabled store=persistent
$ netsh interface ipv6 set global randomizeidentifiers=disabled store=persistent

The IPv6 address is stored in the 'nics' table and is then returned by the API and will be shown in the UI.

Searching for a conflicting IPv6 Address it NOT required as each IPv6 address is based on the MAC Address
of the Instance and therefor unique.

Security Grouping has not been implemented yet and will follow in a upcoming commit.

Signed-off-by: Wido den Hollander <wi...@widodh.nl>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/c0e77667
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/c0e77667
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/c0e77667

Branch: refs/heads/master
Commit: c0e7766713b2631a167c2ceea7d42b574a5cd1b9
Parents: 9513053
Author: Wido den Hollander <wi...@widodh.nl>
Authored: Wed Jul 20 12:10:53 2016 +0200
Committer: Wido den Hollander <wi...@widodh.nl>
Committed: Thu Jan 26 15:17:33 2017 +0100

----------------------------------------------------------------------
 api/src/com/cloud/agent/api/to/NetworkTO.java   |  31 ++++++
 .../cloud/hypervisor/HypervisorGuruBase.java    |   2 +
 .../network/guru/DirectPodBasedNetworkGuru.java | 104 ++++++++++++-------
 .../main/java/com/cloud/utils/net/NetUtils.java |  19 ++++
 .../java/com/cloud/utils/net/NetUtilsTest.java  |  21 ++++
 5 files changed, 140 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c0e77667/api/src/com/cloud/agent/api/to/NetworkTO.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/to/NetworkTO.java b/api/src/com/cloud/agent/api/to/NetworkTO.java
index be11dea..9c88805 100644
--- a/api/src/com/cloud/agent/api/to/NetworkTO.java
+++ b/api/src/com/cloud/agent/api/to/NetworkTO.java
@@ -38,6 +38,8 @@ public class NetworkTO {
     protected URI isolationUri;
     protected boolean isSecurityGroupEnabled;
     protected String name;
+    protected String ip6address;
+    protected String ip6cidr;
 
     public NetworkTO() {
     }
@@ -62,6 +64,14 @@ public class NetworkTO {
         this.ip = ip;
     }
 
+    public void setIp6Address(String addr) {
+        this.ip6address = addr;
+    }
+
+    public void setIp6Cidr(String cidr) {
+        this.ip6cidr = cidr;
+    }
+
     public void setNetmask(String netmask) {
         this.netmask = netmask;
     }
@@ -114,6 +124,7 @@ public class NetworkTO {
      * the full information about what is needed.
      *
      * @param ip
+     * @param ip6address
      * @param vlan
      * @param netmask
      * @param mac
@@ -130,10 +141,30 @@ public class NetworkTO {
         this.dns2 = dns2;
     }
 
+    public NetworkTO(String ip, String netmask, String mac, String gateway, String dns1, String dns2, String ip6address,
+                     String ip6cidr) {
+        this.ip = ip;
+        this.netmask = netmask;
+        this.mac = mac;
+        this.gateway = gateway;
+        this.dns1 = dns1;
+        this.dns2 = dns2;
+        this.ip6address = ip6address;
+        this.ip6cidr = ip6cidr;
+    }
+
     public String getIp() {
         return ip;
     }
 
+    public String getIp6Address() {
+        return ip6address;
+    }
+
+    public String getIp6Cidr() {
+        return ip6cidr;
+    }
+
     public String getNetmask() {
         return netmask;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c0e77667/server/src/com/cloud/hypervisor/HypervisorGuruBase.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java
index e48c1f5..c413edd 100644
--- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java
@@ -87,6 +87,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
         to.setNetworkRateMbps(profile.getNetworkRate());
         to.setName(profile.getName());
         to.setSecurityGroupEnabled(profile.isSecurityGroupEnabled());
+        to.setIp6Address(profile.getIPv6Address());
+        to.setIp6Cidr(profile.getIPv6Cidr());
 
         NetworkVO network = _networkDao.findById(profile.getNetworkId());
         to.setNetworkUuid(network.getUuid());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c0e77667/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
index 9154ece..6301ca5 100644
--- a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
@@ -30,6 +30,7 @@ import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.Pod;
 import com.cloud.dc.PodVlanMapVO;
 import com.cloud.dc.Vlan;
+import com.cloud.dc.VlanVO;
 import com.cloud.dc.Vlan.VlanType;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.dc.dao.PodVlanMapDao;
@@ -54,12 +55,14 @@ import com.cloud.utils.db.TransactionCallbackNoReturn;
 import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
 import com.cloud.utils.db.TransactionStatus;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.Nic;
 import com.cloud.vm.Nic.ReservationStrategy;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.ReservationContext;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
+import com.googlecode.ipv6.IPv6Address;
 
 public class DirectPodBasedNetworkGuru extends DirectNetworkGuru {
     private static final Logger s_logger = Logger.getLogger(DirectPodBasedNetworkGuru.class);
@@ -166,54 +169,81 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru {
     protected void getIp(final NicProfile nic, final Pod pod, final VirtualMachineProfile vm, final Network network) throws InsufficientVirtualNetworkCapacityException,
         InsufficientAddressCapacityException, ConcurrentOperationException {
         final DataCenter dc = _dcDao.findById(pod.getDataCenterId());
-        if (nic.getIPv4Address() == null) {
             Transaction.execute(new TransactionCallbackWithExceptionNoReturn<InsufficientAddressCapacityException>() {
                 @Override
                 public void doInTransactionWithoutResult(TransactionStatus status) throws InsufficientAddressCapacityException {
                     PublicIp ip = null;
                     List<PodVlanMapVO> podRefs = _podVlanDao.listPodVlanMapsByPod(pod.getId());
-                    String podRangeGateway = null;
-                    if (!podRefs.isEmpty()) {
-                        podRangeGateway = _vlanDao.findById(podRefs.get(0).getVlanDbId()).getVlanGateway();
-                    }
-                    //Get ip address from the placeholder and don't allocate a new one
-                    if (vm.getType() == VirtualMachine.Type.DomainRouter) {
-                        Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId());
-                        if (placeholderNic != null) {
-                            IPAddressVO userIp = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), placeholderNic.getIPv4Address());
-                            ip = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
-                            s_logger.debug("Nic got an ip address " + placeholderNic.getIPv4Address() + " stored in placeholder nic for the network " + network +
-                                " and gateway " + podRangeGateway);
+                    VlanVO vlan = _vlanDao.findById(podRefs.get(0).getVlanDbId());
+
+                    if (nic.getIPv4Address() == null) {
+                        String podRangeGateway = null;
+                        if (!podRefs.isEmpty()) {
+                            podRangeGateway = vlan.getVlanGateway();
+                        }
+                        //Get ip address from the placeholder and don't allocate a new one
+                        if (vm.getType() == VirtualMachine.Type.DomainRouter) {
+                            Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId());
+                            if (placeholderNic != null) {
+                                IPAddressVO userIp = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), placeholderNic.getIPv4Address());
+                                ip = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
+                                s_logger.debug("Nic got an ip address " + placeholderNic.getIPv4Address() + " stored in placeholder nic for the network " + network +
+                                    " and gateway " + podRangeGateway);
+                            }
                         }
-                    }
 
-                    if (ip == null) {
-                        ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), null, false);
-                    }
+                        if (ip == null) {
+                            ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), null, false);
+                        }
 
-                    nic.setIPv4Address(ip.getAddress().toString());
-                    nic.setFormat(AddressFormat.Ip4);
-                    nic.setIPv4Gateway(ip.getGateway());
-                    nic.setIPv4Netmask(ip.getNetmask());
-                    if (ip.getVlanTag() != null && ip.getVlanTag().equalsIgnoreCase(Vlan.UNTAGGED)) {
-                        nic.setIsolationUri(IsolationType.Ec2.toUri(Vlan.UNTAGGED));
-                        nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED));
-                        nic.setBroadcastType(BroadcastDomainType.Native);
-                    }
-                    nic.setReservationId(String.valueOf(ip.getVlanTag()));
-                    nic.setMacAddress(ip.getMacAddress());
-
-                    //save the placeholder nic if the vm is the Virtual router
-                    if (vm.getType() == VirtualMachine.Type.DomainRouter) {
-                        Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId());
-                        if (placeholderNic == null) {
-                            s_logger.debug("Saving placeholder nic with ip4 address " + nic.getIPv4Address() + " for the network " + network);
-                            _networkMgr.savePlaceholderNic(network, nic.getIPv4Address(), null, VirtualMachine.Type.DomainRouter);
+                        nic.setIPv4Address(ip.getAddress().toString());
+                        nic.setFormat(AddressFormat.Ip4);
+                        nic.setIPv4Gateway(ip.getGateway());
+                        nic.setIPv4Netmask(ip.getNetmask());
+                        if (ip.getVlanTag() != null && ip.getVlanTag().equalsIgnoreCase(Vlan.UNTAGGED)) {
+                            nic.setIsolationUri(IsolationType.Ec2.toUri(Vlan.UNTAGGED));
+                            nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED));
+                            nic.setBroadcastType(BroadcastDomainType.Native);
                         }
+                        nic.setReservationId(String.valueOf(ip.getVlanTag()));
+                        nic.setMacAddress(ip.getMacAddress());
+
+                        //save the placeholder nic if the vm is the Virtual router
+                        if (vm.getType() == VirtualMachine.Type.DomainRouter) {
+                            Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId());
+                            if (placeholderNic == null) {
+                                s_logger.debug("Saving placeholder nic with ip4 address " + nic.getIPv4Address() + " for the network " + network);
+                                _networkMgr.savePlaceholderNic(network, nic.getIPv4Address(), null, VirtualMachine.Type.DomainRouter);
+                            }
+                        }
+                }
+
+                /**
+                 * Calculate the IPv6 Address the Instance will obtain using SLAAC and IPv6 EUI-64
+                 *
+                 * Linux, FreeBSD and Windows all calculate the same IPv6 address when configured properly.
+                 *
+                 * Using Router Advertisements the routers in the network should announce the IPv6 CIDR which is configured
+                 * in in the vlan table in the database.
+                 *
+                 * This way the NIC will be populated with a IPv6 address on which the Instance is reachable.
+                 */
+                if (vlan.getIp6Cidr() != null) {
+                    if (nic.getIPv6Address() == null) {
+                        s_logger.debug("Found IPv6 CIDR " + vlan.getIp6Cidr() + " for VLAN " + vlan.getId());
+                        nic.setIPv6Cidr(vlan.getIp6Cidr());
+                        nic.setIPv6Gateway(vlan.getIp6Gateway());
+
+                        IPv6Address ipv6addr = NetUtils.EUI64Address(vlan.getIp6Cidr(), nic.getMacAddress());
+                        s_logger.info("Calculated IPv6 address " + ipv6addr + " using EUI-64 for NIC " + nic.getUuid());
+                        nic.setIPv6Address(ipv6addr.toString());
                     }
+                } else {
+                    s_logger.debug("No IPv6 CIDR configured for VLAN " + vlan.getId());
                 }
-            });
-        }
+            }
+        });
+
         nic.setIPv4Dns1(dc.getDns1());
         nic.setIPv4Dns2(dc.getDns2());
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c0e77667/utils/src/main/java/com/cloud/utils/net/NetUtils.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
index a73813c..a2ec411 100644
--- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
@@ -1578,5 +1578,24 @@ public class NetUtils {
         return !isInRange;
     }
 
+    public static IPv6Address EUI64Address(final IPv6Network cidr, final String macAddress) {
+        if (cidr.getNetmask().asPrefixLength() > 64) {
+            throw new IllegalArgumentException("IPv6 subnet " + cidr.toString() + " is not 64 bits or larger in size");
+        }
+
+        String mac[] = macAddress.toLowerCase().split(":");
+
+        return IPv6Address.fromString(cidr.getFirst().toString() +
+                Integer.toHexString(Integer.parseInt(mac[0], 16) ^ 2) +
+                mac[1] + ":" + mac[2] + "ff:fe" + mac[3] +":" + mac[4] + mac[5]);
+    }
+
+    public static IPv6Address EUI64Address(final String cidr, final String macAddress) {
+        return EUI64Address(IPv6Network.fromString(cidr), macAddress);
+    }
+
+    public static IPv6Address ipv6LinkLocal(final String macAddress) {
+        return EUI64Address(IPv6Network.fromString("fe80::/64"), macAddress);
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/c0e77667/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
----------------------------------------------------------------------
diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
index 490d0df..4c43751 100644
--- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
@@ -526,4 +526,25 @@ public class NetUtilsTest {
         assertFalse(NetUtils.isNetworkorBroadcastIP("192.168.0.63","255.255.255.128"));
     }
 
+    @Test
+    public void testIPv6EUI64Address() {
+        assertEquals(IPv6Address.fromString("2001:db8:100::47a:88ff:fe00:8b"),
+                NetUtils.EUI64Address("2001:db8:100::/64", "06:7a:88:00:00:8b"));
+
+        assertEquals(IPv6Address.fromString("2a00:f10:121:b00:434:a0ff:fe00:1bc7"),
+                NetUtils.EUI64Address("2a00:f10:121:b00::/64", "06:34:a0:00:1b:c7"));
+
+        assertEquals(IPv6Address.fromString("2001:980:7936:0:ea2a:eaff:fe58:eb98"),
+                NetUtils.EUI64Address("2001:980:7936::/64", "e8:2a:ea:58:eb:98"));
+
+        assertEquals(IPv6Address.fromString("2001:980:7936:0:c23f:d5ff:fe68:2808"),
+                NetUtils.EUI64Address("2001:980:7936::/64", "c0:3f:d5:68:28:08"));
+    }
+
+    @Test
+    public void testIPv6LinkLocal() {
+        assertEquals(IPv6Address.fromString("fe80::fc54:ff:fe00:3e05"), NetUtils.ipv6LinkLocal("fe:54:00:00:3e:05"));
+        assertEquals(IPv6Address.fromString("fe80::42:e0ff:fee8:d6a3"), NetUtils.ipv6LinkLocal("02:42:e0:e8:d6:a3"));
+        assertEquals(IPv6Address.fromString("fe80::47a:88ff:fe00:8b"), NetUtils.ipv6LinkLocal("06:7a:88:00:00:8b"));
+    }
 }


[2/4] git commit: updated refs/heads/master to f10c8bf

Posted by ra...@apache.org.
CLOUDSTACK-676: IPv6 Basic Security Grouping for KVM

This commit implements basic Security Grouping for KVM in
Basic Networking.

It does not implement full Security Grouping yet, but it does:
- Prevent IP-Address source spoofing
- Allow DHCPv6 clients, but disallow DHCPv6 servers
- Disallow Instances to send out Router Advertisements

The Security Grouping allows ICMPv6 packets as described by RFC4890
as they are essential for IPv6 connectivity.

Following RFC4890 it allows:
- Router Solicitations
- Router Advertisements (incoming only)
- Neighbor Advertisements
- Neighbor Solicitations
- Packet Too Big
- Time Exceeded
- Destination Unreachable
- Parameter Problem
- Echo Request

ICMPv6 is a essential part of IPv6, without it connectivity will break or be very
unreliable.

For now it allows any UDP and TCP packet to be send in to the Instance which
effectively opens up the firewall completely.

Future commits will implement Security Grouping further which allows controlling UDP and TCP
ports for IPv6 like can be done with IPv4.

Regardless of the egress filtering (which can't be done yet) it will always allow outbound DNS
to port 53 over UDP or TCP.

Signed-off-by: Wido den Hollander <wi...@widodh.nl>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/84e496b4
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/84e496b4
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/84e496b4

Branch: refs/heads/master
Commit: 84e496b4f9d06915fb07e3da330ca270e1e56ec2
Parents: c0e7766
Author: Wido den Hollander <wi...@widodh.nl>
Authored: Mon Oct 17 17:03:05 2016 +0200
Committer: Wido den Hollander <wi...@widodh.nl>
Committed: Thu Jan 26 15:36:08 2017 +0100

----------------------------------------------------------------------
 .../cloud/agent/api/SecurityGroupRulesCmd.java  |   7 +
 .../agent/api/SecurityGroupRulesCmdTest.java    |   3 +-
 debian/control                                  |   2 +-
 packaging/centos63/cloud.spec                   |   1 +
 packaging/centos7/cloud.spec                    |   1 +
 .../kvm/resource/LibvirtComputingResource.java  |   6 +-
 ...LibvirtSecurityGroupRulesCommandWrapper.java |   2 +-
 .../resource/LibvirtComputingResourceTest.java  |  11 +-
 .../xenbase/CitrixRequestWrapperTest.java       |   3 +-
 scripts/vm/network/security_group.py            | 186 ++++++++++++++++---
 .../security/SecurityGroupManagerImpl.java      |   6 +-
 .../security/SecurityGroupManagerImpl2.java     |   2 +-
 12 files changed, 188 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/core/src/com/cloud/agent/api/SecurityGroupRulesCmd.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/SecurityGroupRulesCmd.java b/core/src/com/cloud/agent/api/SecurityGroupRulesCmd.java
index 09f9266..fe4ac1c 100644
--- a/core/src/com/cloud/agent/api/SecurityGroupRulesCmd.java
+++ b/core/src/com/cloud/agent/api/SecurityGroupRulesCmd.java
@@ -41,6 +41,7 @@ public class SecurityGroupRulesCmd extends Command {
     private static final Logger LOGGER = Logger.getLogger(SecurityGroupRulesCmd.class);
 
     private final String guestIp;
+    private final String guestIp6;
     private final String vmName;
     private final String guestMac;
     private final String signature;
@@ -93,6 +94,7 @@ public class SecurityGroupRulesCmd extends Command {
 
     public SecurityGroupRulesCmd(
             final String guestIp,
+            final String guestIp6,
             final String guestMac,
             final String vmName,
             final Long vmId,
@@ -102,6 +104,7 @@ public class SecurityGroupRulesCmd extends Command {
             final IpPortAndProto[] egressRuleSet,
             final List<String> secIps) {
         this.guestIp = guestIp;
+        this.guestIp6 = guestIp6;
         this.vmName = vmName;
         setIngressRuleSet(ingressRuleSet);
         this.setEgressRuleSet(egressRuleSet);
@@ -148,6 +151,10 @@ public class SecurityGroupRulesCmd extends Command {
         return guestIp;
     }
 
+    public String getGuestIp6() {
+        return guestIp6;
+    }
+
     public List<String> getSecIps() {
         return secIps;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/core/test/com/cloud/agent/api/SecurityGroupRulesCmdTest.java
----------------------------------------------------------------------
diff --git a/core/test/com/cloud/agent/api/SecurityGroupRulesCmdTest.java b/core/test/com/cloud/agent/api/SecurityGroupRulesCmdTest.java
index 37b15eb..aba2460 100644
--- a/core/test/com/cloud/agent/api/SecurityGroupRulesCmdTest.java
+++ b/core/test/com/cloud/agent/api/SecurityGroupRulesCmdTest.java
@@ -45,6 +45,7 @@ public class SecurityGroupRulesCmdTest {
     @Before
     public void setUp() throws Exception {
         final String guestIp = "10.10.10.10";
+        final String guestIp6 = "2001:db8::cad:40ff:fefd:75c4";
         final String guestMac = "aa:aa:aa:aa:aa:aa";
         final String vmName = "vm";
         final Long vmId = 1L;
@@ -57,7 +58,7 @@ public class SecurityGroupRulesCmdTest {
         final IpPortAndProto[] ingressRuleSet = new IpPortAndProto[]{new IpPortAndProto(proto, startPort, endPort, allowedCidrs)};
         final IpPortAndProto[] egressRuleSet = new IpPortAndProto[]{new IpPortAndProto(proto, startPort, endPort, allowedCidrs)};
         final List<String> secIps = new Vector<String>();
-        securityGroupRulesCmd = new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
+        securityGroupRulesCmd = new SecurityGroupRulesCmd(guestIp, guestIp6, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/debian/control
----------------------------------------------------------------------
diff --git a/debian/control b/debian/control
index 157aa8d..1394b4f 100644
--- a/debian/control
+++ b/debian/control
@@ -9,7 +9,7 @@ Homepage: http://www.cloudstack.org/
 
 Package: cloudstack-common
 Architecture: all
-Depends: ${misc:Depends}, ${python:Depends}, genisoimage, nfs-common
+Depends: ${misc:Depends}, ${python:Depends}, genisoimage, nfs-common, python-netaddr
 Conflicts: cloud-scripts, cloud-utils, cloud-system-iso, cloud-console-proxy, cloud-daemonize, cloud-deps, cloud-python, cloud-setup
 Description: A common package which contains files which are shared by several CloudStack packages
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/packaging/centos63/cloud.spec
----------------------------------------------------------------------
diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec
index 5670193..83c1fac 100644
--- a/packaging/centos63/cloud.spec
+++ b/packaging/centos63/cloud.spec
@@ -97,6 +97,7 @@ management, and intelligence in CloudStack.
 Summary: Apache CloudStack common files and scripts
 Requires: python
 Requires: python-argparse
+Requires: python-netaddr
 Obsoletes: cloud-test < 4.1.0 
 Obsoletes: cloud-scripts < 4.1.0
 Obsoletes: cloud-utils < 4.1.0

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/packaging/centos7/cloud.spec
----------------------------------------------------------------------
diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec
index 00b5dbc..2e079a4 100644
--- a/packaging/centos7/cloud.spec
+++ b/packaging/centos7/cloud.spec
@@ -92,6 +92,7 @@ management, and intelligence in CloudStack.
 Summary: Apache CloudStack common files and scripts
 Requires: python
 Requires: python-argparse
+Requires: python-netaddr
 Group:   System Environment/Libraries
 %description common
 The Apache CloudStack files shared between agent and management server

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 38dabda..d69e8ba 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -3254,6 +3254,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         if (nic.getIp() != null) {
             cmd.add("--vmip", nic.getIp());
         }
+        if (nic.getIp6Address() != null) {
+            cmd.add("--vmip6", nic.getIp6Address());
+        }
         cmd.add("--vmmac", nic.getMac());
         cmd.add("--vif", vif);
         cmd.add("--brname", brname);
@@ -3316,7 +3319,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         return true;
     }
 
-    public boolean addNetworkRules(final String vmName, final String vmId, final String guestIP, final String sig, final String seq, final String mac, final String rules, final String vif, final String brname,
+    public boolean addNetworkRules(final String vmName, final String vmId, final String guestIP, final String guestIP6, final String sig, final String seq, final String mac, final String rules, final String vif, final String brname,
             final String secIps) {
         if (!_canBridgeFirewall) {
             return false;
@@ -3328,6 +3331,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         cmd.add("--vmname", vmName);
         cmd.add("--vmid", vmId);
         cmd.add("--vmip", guestIP);
+        cmd.add("--vmip6", guestIP6);
         cmd.add("--sig", sig);
         cmd.add("--seq", seq);
         cmd.add("--vmmac", mac);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSecurityGroupRulesCommandWrapper.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSecurityGroupRulesCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSecurityGroupRulesCommandWrapper.java
index ef9fd89..ded8ce3 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSecurityGroupRulesCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSecurityGroupRulesCommandWrapper.java
@@ -54,7 +54,7 @@ public final class LibvirtSecurityGroupRulesCommandWrapper extends CommandWrappe
             return new SecurityGroupRuleAnswer(command, false, e.toString());
         }
 
-        final boolean result = libvirtComputingResource.addNetworkRules(command.getVmName(), Long.toString(command.getVmId()), command.getGuestIp(), command.getSignature(),
+        final boolean result = libvirtComputingResource.addNetworkRules(command.getVmName(), Long.toString(command.getVmId()), command.getGuestIp(), command.getGuestIp6(), command.getSignature(),
                 Long.toString(command.getSeqNum()), command.getGuestMac(), command.stringifyRules(), vif, brname, command.getSecIpsString());
 
         if (!result) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index 6b322d7..93c130b 100644
--- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -2912,6 +2912,7 @@ public class LibvirtComputingResourceTest {
     @Test
     public void testSecurityGroupRulesCmdFalse() {
         final String guestIp = "127.0.0.1";
+        final String guestIp6 = "2001:db8::cad:40ff:fefd:75c4";
         final String guestMac = "00:00:00:00";
         final String vmName = "Test";
         final Long vmId = 1l;
@@ -2923,7 +2924,7 @@ public class LibvirtComputingResourceTest {
         final List<String> cidrs = new Vector<String>();
         cidrs.add("0.0.0.0/0");
 
-        final SecurityGroupRulesCmd command = new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
+        final SecurityGroupRulesCmd command = new SecurityGroupRulesCmd(guestIp, guestIp6, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
 
         final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class);
         final Connect conn = Mockito.mock(Connect.class);
@@ -2967,6 +2968,7 @@ public class LibvirtComputingResourceTest {
     @Test
     public void testSecurityGroupRulesCmdTrue() {
         final String guestIp = "127.0.0.1";
+        final String guestIp6 = "2001:db8::cad:40ff:fefd:75c4";
         final String guestMac = "00:00:00:00";
         final String vmName = "Test";
         final Long vmId = 1l;
@@ -2978,7 +2980,7 @@ public class LibvirtComputingResourceTest {
         final List<String> cidrs = new Vector<String>();
         cidrs.add("0.0.0.0/0");
 
-        final SecurityGroupRulesCmd command = new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
+        final SecurityGroupRulesCmd command = new SecurityGroupRulesCmd(guestIp, guestIp6, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
 
         final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class);
         final Connect conn = Mockito.mock(Connect.class);
@@ -3011,7 +3013,7 @@ public class LibvirtComputingResourceTest {
         when(egressRuleSet[0].getEndPort()).thenReturn(22);
         when(egressRuleSet[0].getAllowedCidrs()).thenReturn(cidrs);
 
-        when(libvirtComputingResource.addNetworkRules(command.getVmName(), Long.toString(command.getVmId()), command.getGuestIp(), command.getSignature(),
+        when(libvirtComputingResource.addNetworkRules(command.getVmName(), Long.toString(command.getVmId()), command.getGuestIp(), command.getGuestIp6(), command.getSignature(),
                 Long.toString(command.getSeqNum()), command.getGuestMac(), command.stringifyRules(), vif, brname, command.getSecIpsString())).thenReturn(true);
 
         final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
@@ -3032,6 +3034,7 @@ public class LibvirtComputingResourceTest {
     @Test
     public void testSecurityGroupRulesCmdException() {
         final String guestIp = "127.0.0.1";
+        final String guestIp6 = "2001:db8::cad:40ff:fefd:75c4";
         final String guestMac = "00:00:00:00";
         final String vmName = "Test";
         final Long vmId = 1l;
@@ -3041,7 +3044,7 @@ public class LibvirtComputingResourceTest {
         final IpPortAndProto[] egressRuleSet = new IpPortAndProto[]{Mockito.mock(IpPortAndProto.class)};
         final List<String> secIps = new Vector<String>();
 
-        final SecurityGroupRulesCmd command = new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
+        final SecurityGroupRulesCmd command = new SecurityGroupRulesCmd(guestIp, guestIp6, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
 
         final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class);
         final Connect conn = Mockito.mock(Connect.class);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java
index 66ae2ec..a78d279 100644
--- a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java
+++ b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java
@@ -743,6 +743,7 @@ public class CitrixRequestWrapperTest {
         final XsHost xsHost = Mockito.mock(XsHost.class);
 
         final String guestIp = "127.0.0.1";
+        final String guestIp6 = "2001:db8::cad:40ff:fefd:75c4";
         final String guestMac = "00:00:00:00";
         final String vmName = "Test";
         final Long vmId = 1l;
@@ -752,7 +753,7 @@ public class CitrixRequestWrapperTest {
         final IpPortAndProto[] egressRuleSet = new IpPortAndProto[]{Mockito.mock(IpPortAndProto.class)};
         final List<String> secIps = new Vector<String>();
 
-        final SecurityGroupRulesCmd sshCommand = new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
+        final SecurityGroupRulesCmd sshCommand = new SecurityGroupRulesCmd(guestIp, guestIp6, guestMac, vmName, vmId, signature, seqNum, ingressRuleSet, egressRuleSet, secIps);
 
         final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
         assertNotNull(wrapper);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/scripts/vm/network/security_group.py
----------------------------------------------------------------------
diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py
index 8283256..ca80a55 100755
--- a/scripts/vm/network/security_group.py
+++ b/scripts/vm/network/security_group.py
@@ -28,6 +28,9 @@ import re
 import libvirt
 import fcntl
 import time
+from netaddr import IPAddress
+from netaddr.core import AddrFormatError
+
 
 logpath = "/var/run/cloud/"        # FIXME: Logs should reside in /var/log/cloud
 lock_file = "/var/lock/cloudstack_security_group.lock"
@@ -178,6 +181,14 @@ def virshdumpxml(domain):
 
     return xml
 
+
+def ipv6_link_local_addr(mac=None):
+    eui64 = re.sub(r'[.:-]', '', mac).lower()
+    eui64 = eui64[0:6] + 'fffe' + eui64[6:]
+    eui64 = hex(int(eui64[0:2], 16) ^ 2)[2:].zfill(2) + eui64[2:]
+    return IPAddress('fe80::' + ':'.join(re.findall(r'.{4}', eui64)))
+
+
 def destroy_network_rules_for_vm(vm_name, vif=None):
     vmchain = vm_name
     vmchain_egress = egress_chain_name(vm_name)
@@ -193,18 +204,21 @@ def destroy_network_rules_for_vm(vm_name, vif=None):
     for chain in filter(None, chains):
         try:
             execute("iptables -F " + chain)
+            execute('ip6tables -F ' + chain)
         except:
             logging.debug("Ignoring failure to flush chain: " + chain)
 
     for chain in filter(None, chains):
         try:
             execute("iptables -X " + chain)
+            execute('ip6tables -X ' + chain)
         except:
             logging.debug("Ignoring failure to delete chain: " + chain)
 
     try:
-        execute("ipset -F " + vm_name)
-        execute("ipset -X " + vm_name)
+        for ipset in [vm_name, vm_name + '-6']:
+            execute('ipset -F ' + ipset)
+            execute('ipset -X ' + ipset)
     except:
         logging.debug("Ignoring failure to delete ipset " + vmchain)
 
@@ -371,16 +385,16 @@ def write_secip_log_for_vm (vmName, secIps, vmId):
 
     return result
 
-def create_ipset_forvm (ipsetname):
+def create_ipset_forvm(ipsetname, type='iphash', family='inet'):
     result = True
     try:
         logging.debug("Creating ipset chain .... " + ipsetname)
         execute("ipset -F " + ipsetname)
         execute("ipset -X " + ipsetname)
-        execute("ipset -N " + ipsetname + " iphash")
     except:
         logging.debug("ipset chain not exists creating.... " + ipsetname)
-        execute("ipset -N " + ipsetname + " iphash")
+    finally:
+        execute('ipset -N ' + ipsetname + ' ' + type + ' family ' + family)
 
     return result
 
@@ -388,10 +402,10 @@ def add_to_ipset(ipsetname, ips, action):
     result = True
     for ip in ips:
         try:
-            logging.debug("vm ip " + ip)
-            execute("ipset " + action + " " + ipsetname + " " + ip)
+            logging.debug("vm ip " + str(ip))
+            execute("ipset " + action + " " + ipsetname + " " + str(ip))
         except:
-            logging.debug("vm ip already in ip set " + ip)
+            logging.debug("vm ip already in ip set " + str(ip))
             continue
 
     return result
@@ -427,7 +441,7 @@ def ebtables_rules_vmip (vmname, ips, action):
         except:
             logging.debug("Failed to program ebtables rules for secondary ip %s for vm %s with action %s" % (ip, vmname, action))
 
-def default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, sec_ips):
+def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, sec_ips):
     if not addFWFramework(brname):
         return False
 
@@ -438,23 +452,20 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, sec_ips):
     vmchain = vm_name
     vmchain_egress = egress_chain_name(vm_name)
     vmchain_default = '-'.join(vmchain.split('-')[:-1]) + "-def"
+    ipv6_link_local = ipv6_link_local_addr(vm_mac)
 
     destroy_ebtables_rules(vmName, vif)
 
-    try:
-        execute("iptables -N " + vmchain)
-    except:
-        execute("iptables -F " + vmchain)
-
-    try:
-        execute("iptables -N " + vmchain_egress)
-    except:
-        execute("iptables -F " + vmchain_egress)
+    for chain in [vmchain, vmchain_egress, vmchain_default]:
+        try:
+            execute('iptables -N ' + chain)
+        except:
+            execute('iptables -F ' + chain)
 
-    try:
-        execute("iptables -N " + vmchain_default)
-    except:
-        execute("iptables -F " + vmchain_default)
+        try:
+            execute('ip6tables -N ' + chain)
+        except:
+            execute('ip6tables -F ' + chain)
 
     action = "-A"
     vmipsetName = vm_name
@@ -509,6 +520,79 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, sec_ips):
         if write_rule_log_for_vm(vmName, vm_id, vm_ip, domID, '_initial_', '-1') == False:
             logging.debug("Failed to log default network rules, ignoring")
 
+    vm_ip6_set_name = vm_name + '-6'
+
+    if not create_ipset_forvm(vm_ip6_set_name, family='inet6', type='hash:net'):
+       logging.debug(" failed to create ivp6 ipset for rule " + str(tokens))
+       return 'false'
+
+    vm_ip6_addr = [ipv6_link_local]
+    try:
+        ip6 = IPAddress(vm_ip6)
+        if ip6.version == 6:
+            vm_ip6_addr.append(ip6)
+    except AddrFormatError:
+        pass
+
+    add_to_ipset(vm_ip6_set_name, vm_ip6_addr, action)
+
+    try:
+        execute('ip6tables -A ' + brfw + '-OUT' + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -j ' + vmchain_default)
+        execute('ip6tables -A ' + brfw + '-IN' + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -j ' + vmchain_default)
+        execute('ip6tables -A ' + vmchain_default + ' -m state --state RELATED,ESTABLISHED -j ACCEPT')
+
+        # Allow Instances to receive Router Advertisements, send out solicitations, but block any outgoing Advertisement from a Instance
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' --src fe80::/64 --dst ff02::1 -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' --dst ff02::2 -p icmpv6 --icmpv6-type router-solicitation -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type router-advertisement -j DROP')
+
+        # Allow neighbor solicitations and advertisements
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT')
+
+        # Packets to allow as per RFC4890
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type packet-too-big -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT')
+
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type destination-unreachable -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT')
+
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type time-exceeded -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT')
+
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT')
+
+        # Allow Instances to send out DHCPv6 client messages, but block server messages
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --sport 546 --dst ff02::1:2 --src ' + str(ipv6_link_local) + ' -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p udp --src fe80::/64 --dport 546 --dst ' + str(ipv6_link_local) + ' -j ACCEPT')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --sport 547 ! --dst fe80::/64 -j DROP')
+
+        # Always allow outbound DNS over UDP and TCP
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --dport 53 -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p tcp --dport 53 -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
+
+        # Prevent source address spoofing
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -m set ! --match-set ' + vm_ip6_set_name + ' src -j DROP')
+
+        # Send proper traffic to the egress chain of the Instance
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -m set --match-set ' + vm_ip6_set_name + ' src -j ' + vmchain_egress)
+
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -j ' + vmchain)
+
+        # For now allow ICMPv6 echo-request, UDP and TCP traffic to the Unicast address of the Instance
+        execute('ip6tables -A ' + vmchain + ' -p icmpv6 --icmpv6-type echo-request -m set --match-set ' + vm_ip6_set_name + ' dst -j ACCEPT')
+        execute('ip6tables -A ' + vmchain + ' -p udp -m set --match-set ' + vm_ip6_set_name + ' dst -j ACCEPT')
+        execute('ip6tables -A ' + vmchain + ' -p tcp -m set --match-set ' + vm_ip6_set_name + ' dst -j ACCEPT')
+
+        # Drop all other traffic into the Instance
+        execute('ip6tables -A ' + vmchain + ' -j DROP')
+    except:
+        logging.debug('Failed to program default rules for vm ' + vm_name)
+        return 'false'
+
     logging.debug("Programmed default rules for vm " + vm_name)
     return 'true'
 
@@ -541,6 +625,7 @@ def post_default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, dhcpS
         pass
     if write_rule_log_for_vm(vm_name, vm_id, vm_ip, domID, '_initial_', '-1') == False:
             logging.debug("Failed to log default network rules, ignoring")
+
 def delete_rules_for_vm_in_bridge_firewall_chain(vmName):
     vm_name = vmName
     if vm_name.startswith('i-'):
@@ -556,6 +641,14 @@ def delete_rules_for_vm_in_bridge_firewall_chain(vmName):
         except:
               logging.exception("Ignoring failure to delete rules for vm " + vmName)
 
+    delcmd = """ip6tables-save | awk '/BF(.*)physdev-is-bridged(.*)%s/ { sub(/-A/, "-D", $1) ; print }'""" % vmchain
+    delcmds = filter(None, execute(delcmd).split('\n'))
+    for cmd in delcmds:
+        try:
+            execute('ip6tables ' + cmd)
+        except:
+              logging.exception("Ignoring failure to delete rules for vm " + vmName)
+
 def rewrite_rule_log_for_vm(vm_name, new_domid):
     logfilename = logpath + vm_name + ".log"
     if not os.path.exists(logfilename):
@@ -638,6 +731,8 @@ def network_rules_for_rebooted_vm(vmName):
     for v in vifs:
         execute("iptables -A " + getBrfw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j "+ vmchain_default)
         execute("iptables -A " + getBrfw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j "+ vmchain_default)
+        execute("ip6tables -A " + getBrfw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j " + vmchain_default)
+        execute("ip6tables -A " + getBrfw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j " + vmchain_default)
 
     #change antispoof rule in vmchain
     try:
@@ -809,7 +904,7 @@ def remove_rule_log_for_vm(vmName):
 def egress_chain_name(vm_name):
     return vm_name + "-eg"
 
-def add_network_rules(vm_name, vm_id, vm_ip, signature, seqno, vmMac, rules, vif, brname, sec_ips):
+def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, rules, vif, brname, sec_ips):
   try:
     vmName = vm_name
     domId = getvmId(vmName)
@@ -822,7 +917,7 @@ def add_network_rules(vm_name, vm_id, vm_ip, signature, seqno, vmMac, rules, vif
         return 'true'
 
     if changes[0] or changes[1] or changes[2] or changes[3]:
-        default_network_rules(vmName, vm_id, vm_ip, vmMac, vif, brname, sec_ips)
+        default_network_rules(vmName, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips)
 
     if rules == "" or rules == None:
         lines = []
@@ -837,7 +932,7 @@ def add_network_rules(vm_name, vm_id, vm_ip, signature, seqno, vmMac, rules, vif
       execute("iptables -F " + egress_vmchain)
     except:
       logging.debug("Error flushing iptables rules for " + vmchain + ". Presuming firewall rules deleted, re-initializing." )
-      default_network_rules(vm_name, vm_id, vm_ip, vmMac, vif, brname, sec_ips)
+      default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips)
     egressrule = 0
     for line in lines:
         tokens = line.split(':')
@@ -1005,8 +1100,27 @@ def addFWFramework(brname):
         execute("iptables -N " + brfwin)
 
     try:
-        refs = execute("""iptables -n -L %s | awk '/%s(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip()
-        if refs == "0":
+        execute('ip6tables -L ' + brfw)
+    except:
+        execute('ip6tables -N ' + brfw)
+
+    brfwout = brfw + "-OUT"
+    try:
+        execute('ip6tables -L ' + brfwout)
+    except:
+        execute('ip6tables -N ' + brfwout)
+
+    brfwin = brfw + "-IN"
+    try:
+        execute('ip6tables -L ' + brfwin)
+    except:
+        execute('ip6tables -N ' + brfwin)
+
+    try:
+        refs = int(execute("""iptables -n -L %s | awk '/%s(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip())
+        refs6 = int(execute("""ip6tables -n -L %s | awk '/%s(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip())
+
+        if refs == 0:
             execute("iptables -I FORWARD -i " + brname + " -j DROP")
             execute("iptables -I FORWARD -o " + brname + " -j DROP")
             execute("iptables -I FORWARD -i " + brname + " -m physdev --physdev-is-bridged -j " + brfw)
@@ -1016,10 +1130,23 @@ def addFWFramework(brname):
             execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-in -j " + brfwin)
             execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-out -j " + brfwout)
             execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-out " + phydev + " -j ACCEPT")
+
+        if refs6 == 0:
+            execute('ip6tables -I FORWARD -i ' + brname + ' -j DROP')
+            execute('ip6tables -I FORWARD -o ' + brname + ' -j DROP')
+            execute('ip6tables -I FORWARD -i ' + brname + ' -m physdev --physdev-is-bridged -j ' + brfw)
+            execute('ip6tables -I FORWARD -o ' + brname + ' -m physdev --physdev-is-bridged -j ' + brfw)
+            phydev = execute("brctl show | awk '/^%s[ \t]/ {print $4}'" % brname ).strip()
+            execute('ip6tables -A ' + brfw + ' -m state --state RELATED,ESTABLISHED -j ACCEPT')
+            execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-is-in -j ' + brfwin)
+            execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-is-out -j ' + brfwout)
+            execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-out ' + phydev + ' -j ACCEPT')
+
         return True
     except:
         try:
             execute("iptables -F " + brfw)
+            execute('ip6tables -F ' + brfw)
         except:
             return False
         return False
@@ -1029,6 +1156,7 @@ if __name__ == '__main__':
     parser = OptionParser()
     parser.add_option("--vmname", dest="vmName")
     parser.add_option("--vmip", dest="vmIP")
+    parser.add_option("--vmip6", dest="vmIP6")
     parser.add_option("--vmid", dest="vmID")
     parser.add_option("--vmmac", dest="vmMAC")
     parser.add_option("--vif", dest="vif")
@@ -1059,7 +1187,7 @@ if __name__ == '__main__':
     if cmd == "can_bridge_firewall":
         can_bridge_firewall(args[1])
     elif cmd == "default_network_rules":
-        default_network_rules(option.vmName, option.vmID, option.vmIP, option.vmMAC, option.vif, option.brname, option.nicSecIps)
+        default_network_rules(option.vmName, option.vmID, option.vmIP, option.vmIP6, option.vmMAC, option.vif, option.brname, option.nicSecIps)
     elif cmd == "destroy_network_rules_for_vm":
         destroy_network_rules_for_vm(option.vmName, option.vif)
     elif cmd == "default_network_rules_systemvm":
@@ -1067,7 +1195,7 @@ if __name__ == '__main__':
     elif cmd == "get_rule_logs_for_vms":
         get_rule_logs_for_vms()
     elif cmd == "add_network_rules":
-        add_network_rules(option.vmName, option.vmID, option.vmIP, option.sig, option.seq, option.vmMAC, option.rules, option.vif, option.brname, option.nicSecIps)
+        add_network_rules(option.vmName, option.vmID, option.vmIP, option.vmIP6, option.sig, option.seq, option.vmMAC, option.rules, option.vif, option.brname, option.nicSecIps)
     elif cmd == "network_rules_vmSecondaryIp":
         network_rules_vmSecondaryIp(option.vmName, option.nicSecIps, option.action)
     elif cmd == "cleanup_rules":

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
index 442747a..1a4fd96 100644
--- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
+++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
@@ -496,7 +496,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro
         return affectedVms;
     }
 
-    protected SecurityGroupRulesCmd generateRulesetCmd(String vmName, String guestIp, String guestMac, Long vmId, String signature, long seqnum,
+    protected SecurityGroupRulesCmd generateRulesetCmd(String vmName, String guestIp, String guestIp6, String guestMac, Long vmId, String signature, long seqnum,
             Map<PortAndProto, Set<String>> ingressRules, Map<PortAndProto, Set<String>> egressRules, List<String> secIps) {
         List<IpPortAndProto> ingressResult = new ArrayList<IpPortAndProto>();
         List<IpPortAndProto> egressResult = new ArrayList<IpPortAndProto>();
@@ -516,7 +516,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro
                 egressResult.add(ipPortAndProto);
             }
         }
-        return new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqnum, ingressResult.toArray(new IpPortAndProto[ingressResult.size()]),
+        return new SecurityGroupRulesCmd(guestIp, guestIp6, guestMac, vmName, vmId, signature, seqnum, ingressResult.toArray(new IpPortAndProto[ingressResult.size()]),
                 egressResult.toArray(new IpPortAndProto[egressResult.size()]), secIps);
     }
 
@@ -1005,7 +1005,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro
                                     nicSecIps = _nicSecIpDao.getSecondaryIpAddressesForNic(nic.getId());
                                 }
                             }
-                            SecurityGroupRulesCmd cmd = generateRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(),
+                            SecurityGroupRulesCmd cmd = generateRulesetCmd(vm.getInstanceName(), nic.getIPv6Address(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(),
                                     generateRulesetSignature(ingressRules, egressRules), seqnum, ingressRules, egressRules, nicSecIps);
                             Commands cmds = new Commands(cmd);
                             try {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/84e496b4/server/src/com/cloud/network/security/SecurityGroupManagerImpl2.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl2.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl2.java
index 64bbb75..2d0ec61 100644
--- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl2.java
+++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl2.java
@@ -186,7 +186,7 @@ public class SecurityGroupManagerImpl2 extends SecurityGroupManagerImpl {
                     }
                 }
                 SecurityGroupRulesCmd cmd =
-                    generateRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(), null, work.getLogsequenceNumber(),
+                    generateRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), nic.getIPv6Address(), vm.getPrivateMacAddress(), vm.getId(), null, work.getLogsequenceNumber(),
                         ingressRules, egressRules, nicSecIps);
                 cmd.setMsId(_serverId);
                 if (s_logger.isDebugEnabled()) {


[3/4] git commit: updated refs/heads/master to f10c8bf

Posted by ra...@apache.org.
CLOUDSTACK-676: IPv6 In -and Egress filtering for Basic Networking

This commit implements Ingress and Egress filtering for IPv6 in
Basic Networking.

It allows for opening and closing ports just as can be done with IPv4.

Rules have to be specified twice, once for IPv4 and once for IPv6, for
example:

- 22 until 22: 0.0.0.0/0
- 22 until 22: ::/0

Egress filtering works the same as with IPv4. When no rule is applied all
traffic is allowed. Otherwise only the specified traffic (with DNS being
the exception) is allowed.

Signed-off-by: Wido den Hollander <wi...@widodh.nl>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/115d6d5d
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/115d6d5d
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/115d6d5d

Branch: refs/heads/master
Commit: 115d6d5dc774715b0d17238dc8e8d9f02017c690
Parents: 84e496b
Author: Wido den Hollander <wi...@widodh.nl>
Authored: Thu Oct 20 10:39:28 2016 +0200
Committer: Wido den Hollander <wi...@widodh.nl>
Committed: Thu Jan 26 15:36:20 2017 +0100

----------------------------------------------------------------------
 scripts/vm/network/security_group.py            | 161 +++++++++++--------
 ui/lib/jquery.validate.additional-methods.js    |   5 +-
 ui/scripts/network.js                           |   4 +-
 ui/scripts/sharedFunctions.js                   |  12 +-
 .../main/java/com/cloud/utils/net/NetUtils.java |   6 +
 .../java/com/cloud/utils/net/NetUtilsTest.java  |   6 +
 6 files changed, 123 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/115d6d5d/scripts/vm/network/security_group.py
----------------------------------------------------------------------
diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py
index ca80a55..0e81565 100755
--- a/scripts/vm/network/security_group.py
+++ b/scripts/vm/network/security_group.py
@@ -28,7 +28,7 @@ import re
 import libvirt
 import fcntl
 import time
-from netaddr import IPAddress
+from netaddr import IPAddress, IPNetwork
 from netaddr.core import AddrFormatError
 
 
@@ -542,15 +542,15 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
         execute('ip6tables -A ' + vmchain_default + ' -m state --state RELATED,ESTABLISHED -j ACCEPT')
 
         # Allow Instances to receive Router Advertisements, send out solicitations, but block any outgoing Advertisement from a Instance
-        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' --src fe80::/64 --dst ff02::1 -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT')
-        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' --dst ff02::2 -p icmpv6 --icmpv6-type router-solicitation -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' --src fe80::/64 --dst ff02::1 -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' --dst ff02::2 -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j RETURN')
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type router-advertisement -j DROP')
 
         # Allow neighbor solicitations and advertisements
-        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -j RETURN')
-        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT')
-        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
-        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m set --match-set ' + vm_ip6_set_name + ' src -m hl --hl-eq 255 -j RETURN')
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT')
 
         # Packets to allow as per RFC4890
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type packet-too-big -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
@@ -565,6 +565,9 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -m set --match-set ' + vm_ip6_set_name + ' src -j RETURN')
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT')
 
+        # MLDv2 discovery packets
+        execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p icmpv6 --dst ff02::16 -j RETURN')
+
         # Allow Instances to send out DHCPv6 client messages, but block server messages
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-in ' + vif + ' -p udp --sport 546 --dst ff02::1:2 --src ' + str(ipv6_link_local) + ' -j RETURN')
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -p udp --src fe80::/64 --dport 546 --dst ' + str(ipv6_link_local) + ' -j ACCEPT')
@@ -582,11 +585,6 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se
 
         execute('ip6tables -A ' + vmchain_default + ' -m physdev --physdev-is-bridged --physdev-out ' + vif + ' -j ' + vmchain)
 
-        # For now allow ICMPv6 echo-request, UDP and TCP traffic to the Unicast address of the Instance
-        execute('ip6tables -A ' + vmchain + ' -p icmpv6 --icmpv6-type echo-request -m set --match-set ' + vm_ip6_set_name + ' dst -j ACCEPT')
-        execute('ip6tables -A ' + vmchain + ' -p udp -m set --match-set ' + vm_ip6_set_name + ' dst -j ACCEPT')
-        execute('ip6tables -A ' + vmchain + ' -p tcp -m set --match-set ' + vm_ip6_set_name + ' dst -j ACCEPT')
-
         # Drop all other traffic into the Instance
         execute('ip6tables -A ' + vmchain + ' -j DROP')
     except:
@@ -904,6 +902,42 @@ def remove_rule_log_for_vm(vmName):
 def egress_chain_name(vm_name):
     return vm_name + "-eg"
 
+
+def parse_network_rules(rules):
+  ret = []
+
+  if rules is None or len(rules) == 0:
+    return ret
+
+  lines = rules.split(';')[:-1]
+  for line in lines:
+    tokens = line.split(':', 4)
+    if len(tokens) != 5:
+      continue
+
+    ruletype = tokens[0]
+    protocol = tokens[1]
+    start = int(tokens[2])
+    end = int(tokens[3])
+    cidrs = tokens.pop();
+
+    ipv4 = []
+    ipv6 = []
+    for ip in cidrs.split(","):
+        try:
+            network = IPNetwork(ip)
+            if network.version == 4:
+                ipv4.append(ip)
+            else:
+                ipv6.append(ip)
+        except:
+            pass
+
+    ret.append({'ipv4': ipv4, 'ipv6': ipv6, 'ruletype': ruletype,
+                'start': start, 'end': end, 'protocol': protocol})
+
+  return ret
+
 def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, rules, vif, brname, sec_ips):
   try:
     vmName = vm_name
@@ -919,83 +953,76 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru
     if changes[0] or changes[1] or changes[2] or changes[3]:
         default_network_rules(vmName, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips)
 
-    if rules == "" or rules == None:
-        lines = []
-    else:
-        lines = rules.split(';')[:-1]
-
     logging.debug("    programming network rules for IP: " + vm_ip + " vmname=" + vm_name)
+
+    vmchain = vm_name
+    egress_chain_name(vm_name)
     try:
-      vmchain = vm_name
-      execute("iptables -F " + vmchain)
-      egress_vmchain = egress_chain_name(vm_name)
-      execute("iptables -F " + egress_vmchain)
+      for chain in [vmchain, egress_vmchain]:
+          execute('iptables -F ' + chain)
+          execute('ip6tables -F ' + chain)
     except:
       logging.debug("Error flushing iptables rules for " + vmchain + ". Presuming firewall rules deleted, re-initializing." )
       default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips)
-    egressrule = 0
-    for line in lines:
-        tokens = line.split(':')
-        if len(tokens) != 5:
-          continue
-        ruletype = tokens[0]
-        protocol = tokens[1]
-        start = tokens[2]
-        end = tokens[3]
-        cidrs = tokens.pop();
-        ips = cidrs.split(",")
-        ips.pop()
-        allow_any = False
-        if ruletype == 'E':
+
+    egressrule_v4 = 0
+    egressrule_v6 = 0
+
+    for rule in parse_network_rules(rules):
+        start = rule['start']
+        end = rule['end']
+        protocol = rule['protocol']
+
+        if rule['ruletype'] == 'E':
             vmchain = egress_chain_name(vm_name)
             direction = "-d"
             action = "RETURN"
-            egressrule = egressrule + 1
+            if rule['ipv4']:
+                egressrule_v4 =+ 1
+
+            if rule['ipv6']:
+                egressrule_v6 +=1
+
         else:
             vmchain = vm_name
             action = "ACCEPT"
             direction = "-s"
-        if '0.0.0.0/0' in ips:
-            i = ips.index('0.0.0.0/0')
-            del ips[i]
-            allow_any = True
-        range = start + ":" + end
-        if ips:
+
+        range = str(start) + ':' + str(end)
+        if 'icmp' == protocol:
+            range = str(start) + '/' + str(end)
+            if start == -1:
+                range = 'any'
+
+        for ip in rule['ipv4']:
             if protocol == 'all':
-                for ip in ips:
-                    execute("iptables -I " + vmchain + " -m state --state NEW " + direction + " " + ip + " -j "+action)
+                execute('iptables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action)
             elif protocol != 'icmp':
-                for ip in ips:
-                    execute("iptables -I " + vmchain + " -p " + protocol + " -m " + protocol + " --dport " + range + " -m state --state NEW " + direction + " " + ip + " -j "+ action)
+                execute('iptables -I ' + vmchain + ' -p ' + protocol + ' -m ' + protocol + ' --dport ' + range + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action)
             else:
-                range = start + "/" + end
-                if start == "-1":
-                    range = "any"
-                for ip in ips:
-                    execute("iptables -I " + vmchain + " -p icmp --icmp-type " + range + " " + direction + " " + ip + " -j "+ action)
+                execute('iptables -I ' + vmchain + ' -p icmp --icmp-type ' + range + ' ' + direction + ' ' + ip + ' -j ' + action)
 
-        if allow_any:
+        for ip in rule['ipv6']:
             if protocol == 'all':
-                execute("iptables -I " + vmchain + " -m state --state NEW " + direction + " 0.0.0.0/0 -j "+action)
-            elif protocol != 'icmp':
-                execute("iptables -I " + vmchain + " -p " + protocol + " -m " + protocol + " --dport " + range + " -m state --state NEW -j "+ action)
+                execute('ip6tables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action)
+            elif 'icmp' != protocol:
+                execute('ip6tables -I ' + vmchain + ' -p ' + protocol + ' -m ' + protocol + ' --dport ' + range + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action)
             else:
-                range = start + "/" + end
-                if start == "-1":
-                    range = "any"
-                execute("iptables -I " + vmchain + " -p icmp --icmp-type " + range + " -j "+action)
+                execute('ip6tables -I ' + vmchain + ' -p icmpv6 --icmpv6-type ' + range + ' ' + direction + ' ' + ip + ' -j ' + action)
 
     egress_vmchain = egress_chain_name(vm_name)
-    if egressrule == 0 :
-        iptables = "iptables -A " + egress_vmchain + " -j RETURN"
-        execute(iptables)
+    if egressrule_v4 == 0 :
+        execute('iptables -A ' + egress_vmchain + ' -j RETURN')
     else:
-        iptables = "iptables -A " + egress_vmchain + " -j DROP"
-        execute(iptables)
+        execute('iptables -A ' + egress_vmchain + ' -j DROP')
 
-    vmchain = vm_name
-    iptables = "iptables -A " + vmchain + " -j DROP"
-    execute(iptables)
+    if egressrule_v6 == 0 :
+        execute('ip6tables -A ' + egress_vmchain + ' -j RETURN')
+    else:
+        execute('ip6tables -A ' + egress_vmchain + ' -j DROP')
+
+    execute('iptables -A ' + vm_name + ' -j DROP')
+    execute('ip6tables -A ' + vm_name + ' -j DROP')
 
     if write_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno) == False:
         return 'false'

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/115d6d5d/ui/lib/jquery.validate.additional-methods.js
----------------------------------------------------------------------
diff --git a/ui/lib/jquery.validate.additional-methods.js b/ui/lib/jquery.validate.additional-methods.js
index 7491439..811b4f7 100644
--- a/ui/lib/jquery.validate.additional-methods.js
+++ b/ui/lib/jquery.validate.additional-methods.js
@@ -497,6 +497,9 @@ $.validator.addMethod("ipv4", function(value, element) {
 }, "Please enter a valid IP v4 address.");
 
 $.validator.addMethod("ipv6", function(value, element) {
+        if (value == '::')
+            return true;
+
 	return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
 }, "Please enter a valid IP v6 address.");
 
@@ -925,4 +928,4 @@ $.validator.addMethod("ziprange", function(value, element) {
 	return this.optional(element) || /^90[2-5]\d\{2\}-\d{4}$/.test(value);
 }, "Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx");
 
-}));
\ No newline at end of file
+}));

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/115d6d5d/ui/scripts/network.js
----------------------------------------------------------------------
diff --git a/ui/scripts/network.js b/ui/scripts/network.js
index dd21cd2..7299a7b 100755
--- a/ui/scripts/network.js
+++ b/ui/scripts/network.js
@@ -4869,7 +4869,7 @@
                                             label: 'label.cidr',
                                             isHidden: true,
                                             validation: {
-                                                ipv4cidr: true
+                                                ipv46cidr: true
                                             }
                                         },
                                         'accountname': {
@@ -5079,7 +5079,7 @@
                                             label: 'label.cidr',
                                             isHidden: true,
                                             validation: {
-                                                ipv4cidr: true
+                                                ipv46cidr: true
                                             }
                                         },
                                         'accountname': {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/115d6d5d/ui/scripts/sharedFunctions.js
----------------------------------------------------------------------
diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js
index 88d728e..f845fd8 100644
--- a/ui/scripts/sharedFunctions.js
+++ b/ui/scripts/sharedFunctions.js
@@ -2318,7 +2318,7 @@ $.validator.addMethod("ipv6cidr", function(value, element) {
     if (parts[1] != Number(parts[1]).toString()) //making sure that "", " ", "00", "0 ","2  ", etc. will not pass
         return false;
 
-    if (Number(parts[1]) < 0 || Number(parts[1] > 32))
+    if (Number(parts[1]) < 0 || Number(parts[1] > 128))
         return false;
 
     return true;
@@ -2344,3 +2344,13 @@ $.validator.addMethod("ipv4cidr", function(value, element) {
 
     return true;
 }, "The specified IPv4 CIDR is invalid.");
+
+$.validator.addMethod("ipv46cidr", function(value, element) {
+    if (this.optional(element) && value.length == 0)
+        return true;
+
+    if ($.validator.methods.ipv4cidr.call(this, value, element) || $.validator.methods.ipv6cidr.call(this, value, element))
+        return true;
+
+    return false;
+}, "The specified IPv4/IPv6 CIDR is invalid.");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/115d6d5d/utils/src/main/java/com/cloud/utils/net/NetUtils.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
index a2ec411..a014bf7 100644
--- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
@@ -557,6 +557,12 @@ public class NetUtils {
         if (cidr == null || cidr.isEmpty()) {
             return false;
         }
+
+        try {
+            IPv6Network.fromString(cidr);
+            return true;
+        } catch (IllegalArgumentException e) {}
+
         final String[] cidrPair = cidr.split("\\/");
         if (cidrPair.length != 2) {
             return false;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/115d6d5d/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
----------------------------------------------------------------------
diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
index 4c43751..6d939d5 100644
--- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
@@ -245,6 +245,10 @@ public class NetUtilsTest {
         assertTrue(NetUtils.isValidCIDR(cidrFirst));
         assertTrue(NetUtils.isValidCIDR(cidrSecond));
         assertTrue(NetUtils.isValidCIDR(cidrThird));
+        assertTrue(NetUtils.isValidCIDR("2001:db8::/64"));
+        assertTrue(NetUtils.isValidCIDR("2001:db8::/48"));
+        assertTrue(NetUtils.isValidCIDR("2001:db8:fff::/56"));
+        assertFalse(NetUtils.isValidCIDR("2001:db8:gggg::/56"));
     }
 
     @Test
@@ -256,6 +260,8 @@ public class NetUtilsTest {
         assertTrue(NetUtils.isValidCidrList(cidrFirst));
         assertTrue(NetUtils.isValidCidrList(cidrSecond));
         assertTrue(NetUtils.isValidCidrList(cidrThird));
+        assertTrue(NetUtils.isValidCidrList("2001:db8::/64,2001:db8:ffff::/48"));
+        assertTrue(NetUtils.isValidCidrList("2001:db8::/64,2001:db8:ffff::/48,192.168.0.0/24"));
     }
 
     @Test


[4/4] git commit: updated refs/heads/master to f10c8bf

Posted by ra...@apache.org.
Merge pull request #1700 from wido/ipv6-basic-networking

CLOUDSTACK-9359: IPv6 for Basic NetworkingThis PR is a proposal for adding very basic IPv6 to Basic Networking. The main goal of this PR is that the API returns a valid IPv6 address over which the Instance is reachable.

The GUI will show the IPv6 address after deployment of the Instance.

![screenshot from 2016-10-03 16 34 56](https://cloud.githubusercontent.com/assets/326786/19070024/b06d2de6-8a29-11e6-8fe7-4902e2801ada.png)

If the table VLAN has a proper IPv6 CIDR configured the DirectPodBasedNetworkGuru will calculate the IPv6 Address the Instance will obtain using EUI-64 and SLAAC: https://tools.ietf.org/search/rfc4862

In this case the _vlan_ table contained:

<pre>mysql> select * from vlan \G
*************************** 1. row ***************************
                 id: 1
               uuid: 90e0716c-5261-4992-bb9d-0afd3006f476
            vlan_id: vlan://untagged
       vlan_gateway: 172.16.0.1
       vlan_netmask: 255.255.255.0
        description: 172.16.0.10-172.16.0.250
          vlan_type: DirectAttached
     data_center_id: 1
         network_id: 204
physical_network_id: 200
        ip6_gateway: 2001:980:7936:112::1
           ip6_cidr: 2001:980:7936:112::/64
          ip6_range: NULL
            removed: NULL
            created: 2016-07-19 20:39:41
1 row in set (0.00 sec)

mysql></pre>

It will then log:

<pre>2016-10-04 11:42:44,998 DEBUG [c.c.n.g.DirectPodBasedNetworkGuru] (Work-Job-Executor-1:ctx-1975ec54 job-186/job-187 ctx-0d967d88) (logid:275c4961) Found IPv6 CIDR 2001:980:7936:112::/64 for VLAN 1
2016-10-04 11:42:45,009 INFO  [c.c.n.g.DirectPodBasedNetworkGuru] (Work-Job-Executor-1:ctx-1975ec54 job-186/job-187 ctx-0d967d88) (logid:275c4961) Calculated IPv6 address 2001:980:7936:112:4ba:80ff:fe00:e9 using EUI-64 for NIC 6a05deab-b5d9-4116-80da-c94b48333e5e</pre>

The template has to be configured accordingly:
- No IPv6 Privacy Extensions
- Use SLAAC
- Follow RFC4862

This is also described in: https://cwiki.apache.org/confluence/display/CLOUDSTACK/IPv6+in+Basic+Networking

The next steps after this will be:
- Security Grouping to prevent IPv6 Address Spoofing
- Security Grouping to filter ICMP, UDP and TCP traffic

* pr/1700:
  CLOUDSTACK-676: IPv6 In -and Egress filtering for Basic Networking
  CLOUDSTACK-676: IPv6 Basic Security Grouping for KVM
  CLOUDSTACK-9359: IPv6 for Basic Networking with KVM

Signed-off-by: Rajani Karuturi <ra...@accelerite.com>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/f10c8bfe
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/f10c8bfe
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/f10c8bfe

Branch: refs/heads/master
Commit: f10c8bfe0c99a762c2606459413a47219614e775
Parents: 4721c53 115d6d5
Author: Rajani Karuturi <ra...@accelerite.com>
Authored: Fri Jan 27 05:42:44 2017 +0530
Committer: Rajani Karuturi <ra...@accelerite.com>
Committed: Fri Jan 27 05:42:44 2017 +0530

----------------------------------------------------------------------
 api/src/com/cloud/agent/api/to/NetworkTO.java   |  31 ++
 .../cloud/agent/api/SecurityGroupRulesCmd.java  |   7 +
 .../agent/api/SecurityGroupRulesCmdTest.java    |   3 +-
 debian/control                                  |   2 +-
 packaging/centos63/cloud.spec                   |   1 +
 packaging/centos7/cloud.spec                    |   1 +
 .../kvm/resource/LibvirtComputingResource.java  |   6 +-
 ...LibvirtSecurityGroupRulesCommandWrapper.java |   2 +-
 .../resource/LibvirtComputingResourceTest.java  |  11 +-
 .../xenbase/CitrixRequestWrapperTest.java       |   3 +-
 scripts/vm/network/security_group.py            | 323 ++++++++++++++-----
 .../cloud/hypervisor/HypervisorGuruBase.java    |   2 +
 .../network/guru/DirectPodBasedNetworkGuru.java | 104 +++---
 .../security/SecurityGroupManagerImpl.java      |   6 +-
 .../security/SecurityGroupManagerImpl2.java     |   2 +-
 ui/lib/jquery.validate.additional-methods.js    |   5 +-
 ui/scripts/network.js                           |   4 +-
 ui/scripts/sharedFunctions.js                   |  12 +-
 .../main/java/com/cloud/utils/net/NetUtils.java |  25 ++
 .../java/com/cloud/utils/net/NetUtilsTest.java  |  27 ++
 20 files changed, 439 insertions(+), 138 deletions(-)
----------------------------------------------------------------------