From b2a8da082d81cf650238b927817b443bfbd6ccbd Mon Sep 17 00:00:00 2001
From: Darya Baklanova <darya.baklanova@gmail.com>
Date: Mon, 12 Feb 2018 12:16:11 +0700
Subject: [PATCH] feat(security-group): Support IPv6 (closes #671) (#957)

---
 package.json                                  |   2 +
 .../sg-rules/sg-rule.component.html           |  20 +-
 .../sg-rules/sg-rule.component.ts             |  19 +-
 .../sg-rules/sg-rules.component.html          |  31 ++-
 .../sg-rules/sg-rules.component.spec.ts       | 122 ++++++++-
 .../sg-rules/sg-rules.component.ts            | 126 ++++++---
 src/app/security-group/sg.model.ts            |   7 +-
 src/app/shared/icmp/icmp-types.ts             | 175 ++++++-------
 .../shared/services/utils/utils.service.ts    |  25 +-
 src/i18n/en.json                              | 239 +++++++++++++++++-
 src/i18n/ru.json                              | 239 +++++++++++++++++-
 .../mock-security-group.service.spec.ts       |   5 +-
 yarn.lock                                     |  42 ++-
 13 files changed, 863 insertions(+), 189 deletions(-)

diff --git a/package.json b/package.json
index 5f0a3db271..84f56bd81d 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
     "@ngx-translate/http-loader": "^2.0.0",
     "chart.js": "2.7.0",
     "core-js": "^2.4.1",
+    "ip-address": "^5.8.9",
     "jasmine": "^2.8.0",
     "jasmine-marbles": "^0.2.0",
     "lodash": "^4.17.4",
@@ -48,6 +49,7 @@
     "reflect-metadata": "^0.1.10",
     "rxjs": "^5.5.2",
     "showdown": "^1.8.4",
+    "sprintf-js": "^1.1.1",
     "uuid": "^3.1.0",
     "web-animations-js": "2.3.1",
     "zone.js": "0.8.12"
diff --git a/src/app/security-group/sg-rules/sg-rule.component.html b/src/app/security-group/sg-rules/sg-rule.component.html
index 07d79c8ed8..d033b0443f 100644
--- a/src/app/security-group/sg-rules/sg-rule.component.html
+++ b/src/app/security-group/sg-rules/sg-rule.component.html
@@ -3,29 +3,39 @@
     <ng-template [ngIf]="item.protocol === NetworkProtocols.ICMP">
       <ng-template [ngIf]="!ruleParams['icmpTypeText'] && !ruleParams['icmpCodeText']">
       <span
-        [innerHTML]="'SECURITY_GROUP_PAGE.RULES.NO_TEXT_ICMP_RULE' | translate: (ruleParams)"
+        [innerHTML]="(item.type === NetworkRuleTypes.Ingress
+        ? 'SECURITY_GROUP_PAGE.RULES.NO_TEXT_INGRESS_ICMP_RULE'
+        : 'SECURITY_GROUP_PAGE.RULES.NO_TEXT_EGRESS_ICMP_RULE') | translate: (ruleParams)"
       ></span>
       </ng-template>
       <ng-template [ngIf]="!!ruleParams['icmpTypeText'] && !ruleParams['icmpCodeText']">
       <span
-        [innerHTML]="'SECURITY_GROUP_PAGE.RULES.NO_CODE_ICMP_RULE' | translate: (ruleParams)"
+        [innerHTML]="(item.type === NetworkRuleTypes.Ingress
+        ? 'SECURITY_GROUP_PAGE.RULES.NO_CODE_INGRESS_ICMP_RULE'
+        : 'SECURITY_GROUP_PAGE.RULES.NO_CODE_EGRESS_ICMP_RULE') | translate: (ruleParams)"
       ></span>
       </ng-template>
       <ng-template [ngIf]="!!ruleParams['icmpTypeText'] && !!ruleParams['icmpCodeText']">
       <span
-        [innerHTML]="'SECURITY_GROUP_PAGE.RULES.DEFAULT_ICMP_RULE' | translate: (ruleParams)"
+        [innerHTML]="(item.type === NetworkRuleTypes.Ingress
+        ? 'SECURITY_GROUP_PAGE.RULES.INGRESS_ICMP_RULE'
+        : 'SECURITY_GROUP_PAGE.RULES.EGRESS_ICMP_RULE') | translate: (ruleParams)"
       ></span>
       </ng-template>
     </ng-template>
     <ng-template [ngIf]="item.protocol !== NetworkProtocols.ICMP">
       <ng-template [ngIf]="item.startPort === item.endPort">
       <span
-        [innerHTML]=" 'SECURITY_GROUP_PAGE.RULES.DEFAULT_RULE' | translate: (ruleParams)"
+        [innerHTML]="(item.type === NetworkRuleTypes.Ingress
+        ? 'SECURITY_GROUP_PAGE.RULES.INGRESS_RULE'
+        : 'SECURITY_GROUP_PAGE.RULES.EGRESS_RULE') | translate: (ruleParams)"
       ></span>
       </ng-template>
       <ng-template [ngIf]="item.startPort !== item.endPort">
       <span
-        [innerHTML]="'SECURITY_GROUP_PAGE.RULES.DEFAULT_RULE_PORT_RANGE' | translate: (ruleParams)"
+        [innerHTML]="(item.type === NetworkRuleTypes.Ingress
+        ? 'SECURITY_GROUP_PAGE.RULES.INGRESS_RULE_PORT_RANGE'
+        : 'SECURITY_GROUP_PAGE.RULES.EGRESS_RULE_PORT_RANGE') | translate: (ruleParams)"
       ></span>
       </ng-template>
     </ng-template>
diff --git a/src/app/security-group/sg-rules/sg-rule.component.ts b/src/app/security-group/sg-rules/sg-rule.component.ts
index 460907377f..ab5652745f 100644
--- a/src/app/security-group/sg-rules/sg-rule.component.ts
+++ b/src/app/security-group/sg-rules/sg-rule.component.ts
@@ -5,12 +5,15 @@ import {
   Input,
   Output
 } from '@angular/core';
+import { Utils } from '../../shared/services/utils/utils.service';
 import { NetworkProtocol, NetworkRule } from '../network-rule.model';
 import { TranslateService } from '@ngx-translate/core';
 import {
   GetICMPCodeTranslationToken,
-  GetICMPTypeTranslationToken
+  GetICMPTypeTranslationToken, GetICMPV6CodeTranslationToken,
+  GetICMPV6TypeTranslationToken
 } from '../../shared/icmp/icmp-types';
+import { IPVersion, NetworkRuleType } from '../sg.model';
 
 @Component({
   selector: 'cs-security-group-rule',
@@ -25,6 +28,7 @@ export class SgRuleComponent {
 
   public deleting = false;
   public NetworkProtocols = NetworkProtocol;
+  public NetworkRuleTypes = NetworkRuleType;
 
   public get typeTranslationToken(): string {
     const typeTranslations = {
@@ -46,18 +50,27 @@ export class SgRuleComponent {
   }
 
   public get icmpTypeTranslationToken(): string {
-    return GetICMPTypeTranslationToken(this.item.icmpType);
+    return Utils.cidrType(this.item.CIDR) === IPVersion.ipv4
+      ? GetICMPTypeTranslationToken(this.item.icmpType)
+      : GetICMPV6TypeTranslationToken(this.item.icmpType);
   }
 
   public get icmpCodeTranslationToken(): string {
-    return GetICMPCodeTranslationToken(this.item.icmpType, this.item.icmpCode);
+    return Utils.cidrType(this.item.CIDR) === IPVersion.ipv4
+      ? GetICMPCodeTranslationToken(this.item.icmpType, this.item.icmpCode)
+      : GetICMPV6CodeTranslationToken(this.item.icmpType, this.item.icmpCode);
   }
 
   public get ruleParams(): Object {
+    const ipVersion = Utils.cidrType(this.item.CIDR) === IPVersion.ipv4
+      ? IPVersion.ipv4
+      : IPVersion.ipv6;
+
     const params = {
       type: this.translateService.instant(this.typeTranslationToken),
       protocol: this.translateService.instant(this.protocolTranslationToken),
       cidr: this.item.CIDR,
+      ipVersion
     };
 
     let ruleParams;
diff --git a/src/app/security-group/sg-rules/sg-rules.component.html b/src/app/security-group/sg-rules/sg-rules.component.html
index fe6a120e13..7cc0b7c344 100644
--- a/src/app/security-group/sg-rules/sg-rules.component.html
+++ b/src/app/security-group/sg-rules/sg-rules.component.html
@@ -45,12 +45,13 @@
                 name="cidr"
                 type="text"
                 [(ngModel)]="cidr"
-                pattern="^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$"
+                (change)="onCidrChange()"
+                [errorStateMatcher]="cidrMatcher"
+                [placeholder]="'SECURITY_GROUP_PAGE.RULES.ENTER_CIDR' | translate"
                 required
-                (click)="onCidrClick()"
                 #cidrField="ngModel"
               >
-              <mat-error *ngIf="cidrField.hasError('pattern')">
+              <mat-error *ngIf="!isCidrValid(cidr)">
                 {{ 'SECURITY_GROUP_PAGE.RULES.ENTER_VALID_CIDR' | translate }}
               </mat-error>
             </mat-form-field>
@@ -62,7 +63,7 @@
                 <input
                   matInput
                   maxValue="255"
-                  minValue="-1"
+                  [minValue]="cidrIpVersion === IPVersion.ipv6 ? 0 : -1"
                   #icmpTypeField="ngModel"
                   name="icmpType"
                   [placeholder]="'SECURITY_GROUP_PAGE.RULES.ICMP_TYPE' | translate"
@@ -89,7 +90,7 @@
                 type="number"
                 [(ngModel)]="startPort"
                 [minValue]="0"
-                [maxValue]="65536"
+                [maxValue]="65535"
                 [placeholder]="'SECURITY_GROUP_PAGE.RULES.START_PORT' | translate"
                 integerValue
                 required
@@ -108,7 +109,7 @@
                   matInput
                   name="icmpCode"
                   maxValue="255"
-                  minValue="-1"
+                  [minValue]="cidrIpVersion === IPVersion.ipv6 ? 0 : -1"
                   #icmpCodeField="ngModel"
                   [placeholder]="'SECURITY_GROUP_PAGE.RULES.ICMP_CODE' | translate"
                   [matAutocomplete]="icmpCodesAuto"
@@ -137,7 +138,7 @@
                 type="number"
                 [(ngModel)]="endPort"
                 [minValue]="0"
-                [maxValue]="65536"
+                [maxValue]="65535"
                 [placeholder]="'SECURITY_GROUP_PAGE.RULES.END_PORT' | translate"
                 integerValue
                 required
@@ -169,6 +170,22 @@
       </table>
 
       <div *ngIf="!editMode">
+        <mat-form-field class="form-select-control">
+          <mat-select
+            multiple="true"
+            [placeholder]="'SECURITY_GROUP_PAGE.RULES.SELECT_IP_VERSION' | translate"
+            [(ngModel)]="selectedIPVersion"
+            (selectionChange)="filter()"
+            name="selectedIPVersion"
+          >
+            <mat-option
+              *ngFor="let t of IPversions"
+              [value]="t"
+            >
+              {{ t }}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
         <mat-form-field class="form-select-control">
           <mat-select
             multiple="true"
diff --git a/src/app/security-group/sg-rules/sg-rules.component.spec.ts b/src/app/security-group/sg-rules/sg-rules.component.spec.ts
index f109a051f0..0de1cfbda9 100644
--- a/src/app/security-group/sg-rules/sg-rules.component.spec.ts
+++ b/src/app/security-group/sg-rules/sg-rules.component.spec.ts
@@ -9,16 +9,20 @@ import { Observable } from 'rxjs/Observable';
 import { MockNotificationService } from '../../../testutils/mocks/mock-notification.service';
 import { MockTranslatePipe } from '../../../testutils/mocks/mock-translate.pipe.spec';
 import { MockTranslateService } from '../../../testutils/mocks/mock-translate.service.spec';
+import { ICMPv6Types } from '../../shared/icmp/icmp-types';
 import { NotificationService } from '../../shared/services/notification.service';
 import { SecurityGroupService } from '../services/security-group.service';
-import { NetworkRuleType, SecurityGroup } from '../sg.model';
+import { IPVersion, NetworkRuleType, SecurityGroup } from '../sg.model';
 import { SgRulesComponent } from './sg-rules.component';
-import { NetworkRule } from '../network-rule.model';
+import { NetworkProtocol, NetworkRule } from '../network-rule.model';
 import { NetworkRuleService } from '../services/network-rule.service';
 import { DialogService } from '../../dialog/dialog-service/dialog.service';
 import { Router } from '@angular/router';
 import { LoadingDirective } from '../../shared/directives/loading.directive';
 
+const securityGroupTemplates: Array<Object> = require(
+  '../../../testutils/mocks/model-services/fixtures/securityGroupTemplates.json');
+
 @Injectable()
 class MockRouter {
   public navigate(route: any): Promise<any> {
@@ -41,6 +45,66 @@ describe('Security group firewall rules component', () => {
     virtualMachinesCount: 0,
     virtualMachineIds: []
   });
+  const mockSecurityGroupWithRules = new SecurityGroup({
+    id: '9d1f0e3b-82a7-4528-b02e-70c4f9eff4b0',
+    name: 'test',
+    description: '',
+    account: 'develop',
+    domain: 'ROOT',
+    ingressRules: [
+      {
+        ruleId: 'ed4a91f5-35f2-48a3-b706-6a00dba50708',
+        protocol: 'icmp',
+        icmpType: 3,
+        icmpCode: 2,
+        CIDR: '2001:DB8::/128',
+        tags: []
+      },
+      {
+        ruleId: '293a8e35-7c26-4216-851e-c87a46c9620f',
+        protocol: 'tcp',
+        startPort: 0,
+        endPort: 65535,
+        CIDR: '2001:DB8::/128',
+        tags: []
+      },
+      {
+        ruleId: 'af34ea6c-dd50-4cab-9f5e-ca4e454e59d3',
+        protocol: 'icmp',
+        icmpType: 2,
+        icmpCode: 0,
+        CIDR: '2001:DB8::/128',
+        tags: []
+      },
+      {
+        ruleId: '41ce53d6-5274-49b0-a2e9-7b0ebc87c89a',
+        protocol: 'icmp',
+        icmpType: 132,
+        icmpCode: 0,
+        CIDR: '2001:DB8::/128',
+        tags: []
+      },
+      {
+        ruleId: '02990ed4-827f-49a1-bb27-4ea6d565c1fd',
+        protocol: 'icmp',
+        icmpType: 4,
+        icmpCode: 1,
+        CIDR: '2001:DB8::/128',
+        tags: []
+      },
+      {
+        ruleId: '787ee1c9-ec5f-4612-9894-1080acec515e',
+        protocol: 'icmp',
+        icmpType: 0,
+        icmpCode: 0,
+        CIDR: '0.0.0.0/0',
+        tags: []
+      }
+    ],
+    tags: [],
+    virtualMachinesCount: 0,
+    virtualMachineIds: []
+  });
 
   class SecurityGroupServiceMock {
     public getPredefinedTemplates(): Array<SecurityGroup> {
@@ -92,18 +156,62 @@ describe('Security group firewall rules component', () => {
     });
   }));
 
-  it('filter lists of ICMP types and codes', () => {
-    const filteredTypes = comp.filterTypes('-1');
+  it('filter lists of ICMP types and codes', async(() => {
+    comp.cidr = '::/128';
+    const filteredTypes = comp.filterTypes('255');
     expect(filteredTypes.length).toEqual(1);
 
     comp.selectedType = filteredTypes[0].type.toString();
     comp.setIcmpTypes(filteredTypes);
-    const filteredCodes = comp.filterCodes('-1');
+    const filteredCodes = comp.filterCodes('255');
     expect(filteredCodes.length).toEqual(1);
 
     comp.selectedCode = filteredCodes[0].toString();
     comp.setIcmpCodes(comp.selectedCode);
-    expect(comp.icmpType).toEqual(-1);
-    expect(comp.icmpCode).toEqual(-1);
+    expect(comp.icmpType).toEqual(255);
+    expect(comp.icmpCode).toEqual(0);
+  }));
+
+  it('filter network rules by IP version, type or protocol', () => {
+    comp.securityGroup = mockSecurityGroupWithRules;
+    comp.ngOnChanges();
+
+    expect(comp.visibleRules.length).toEqual(6);
+
+    comp.selectedIPVersion = [IPVersion.ipv6];
+    comp.filter();
+    expect(comp.visibleRules.length).toEqual(5);
+
+    comp.selectedType = [NetworkRuleType.Ingress];
+    comp.filter();
+    expect(comp.visibleRules.length).toEqual(5);
+    comp.selectedIPVersion = [];
+    comp.selectedType = [];
+    comp.filter();
+    expect(comp.visibleRules.length).toEqual(6);
+
+    comp.selectedProtocols = [NetworkProtocol.ICMP];
+    comp.filter();
+    expect(comp.visibleRules.length).toEqual(5);
   });
+
+  it('should filter predefined templates', async(() => {
+    comp.securityGroup = securityGroupTemplates[0];
+    expect(comp.isPredefinedTemplate).toBeTruthy();
+    comp.securityGroup = mockSecurityGroupWithRules;
+    expect(comp.isPredefinedTemplate).toBeFalsy();
+  }));
+
+  it('should select types by CIDR IP version', async(() => {
+    comp.cidr = '2001:DB8::/128';
+    comp.onCidrChange();
+    expect(comp.icmpTypes).toEqual(ICMPv6Types);
+  }));
+
+  it('should change view or edit mode', async(() => {
+    comp.editMode = false;
+    comp.securityGroup = securityGroupTemplates[0];
+    comp.confirmChangeMode();
+    expect(comp.editMode).toBeTruthy();
+  }));
 });
diff --git a/src/app/security-group/sg-rules/sg-rules.component.ts b/src/app/security-group/sg-rules/sg-rules.component.ts
index 6af3f17431..4e1cbf164a 100644
--- a/src/app/security-group/sg-rules/sg-rules.component.ts
+++ b/src/app/security-group/sg-rules/sg-rules.component.ts
@@ -6,22 +6,41 @@ import {
   Output,
   ViewChild
 } from '@angular/core';
-import { NgForm } from '@angular/forms';
+import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
+import { ErrorStateMatcher } from '@angular/material';
 import { TranslateService } from '@ngx-translate/core';
 import {
   GetICMPCodeTranslationToken,
   GetICMPTypeTranslationToken,
+  GetICMPV6CodeTranslationToken,
+  GetICMPV6TypeTranslationToken,
   ICMPType,
-  ICMPtypes
+  ICMPtypes,
+  ICMPv6Types
 } from '../../shared/icmp/icmp-types';
 import { NotificationService } from '../../shared/services/notification.service';
+import { Utils } from '../../shared/services/utils/utils.service';
 import { NetworkRuleService } from '../services/network-rule.service';
-import { NetworkRuleType, SecurityGroup, SecurityGroupType } from '../sg.model';
+import {
+  getType,
+  IPVersion, NetworkRuleType, SecurityGroup,
+  SecurityGroupType
+} from '../sg.model';
 import { NetworkProtocol, NetworkRule } from '../network-rule.model';
 import { DialogService } from '../../dialog/dialog-service/dialog.service';
 import { Router } from '@angular/router';
 import { SgRuleComponent } from './sg-rule.component';
 
+export class CidrStateMatcher implements ErrorStateMatcher {
+  isErrorState(
+    control: FormControl | null,
+    form: FormGroupDirective | NgForm | null
+  ): boolean {
+    const invalidCidr = control.value && !Utils.cidrIsValid(control.value);
+
+    return control && (control.dirty || control.touched) && invalidCidr;
+  }
+}
 
 @Component({
   selector: 'cs-security-group-rules',
@@ -29,7 +48,7 @@ import { SgRuleComponent } from './sg-rule.component';
   styleUrls: ['sg-rules.component.scss']
 })
 export class SgRulesComponent implements OnChanges {
-  @Input() public securityGroup: any;
+  @Input() public securityGroup: SecurityGroup;
   @Input() public editMode = false;
   @Input() public vmId: string;
   @Output() public onCloseDialog = new EventEmitter();
@@ -38,6 +57,7 @@ export class SgRulesComponent implements OnChanges {
   @ViewChild('rulesForm') public rulesForm: NgForm;
   public selectedType = '';
   public selectedCode = '';
+  public selectedIPVersion: string[] = [];
   public selectedTypes: string[] = [];
   public selectedProtocols: string[] = [];
 
@@ -45,17 +65,18 @@ export class SgRulesComponent implements OnChanges {
   public protocol: NetworkProtocol;
   public startPort: number;
   public icmpType: number;
-  public icmpTypes: ICMPType[] = ICMPtypes;
   public icmpCode: number;
   public icmpCodes: number[];
   public endPort: number;
   public cidr: string;
-  public ingressRules: NetworkRule[] = [];
-  public egressRules: NetworkRule[] = [];
+  public cidrMatcher = new CidrStateMatcher();
+  public ingressRules = [];
+  public egressRules = [];
   public visibleRules: NetworkRule[] = [];
 
   public adding: boolean;
 
+  public IPversions = [IPVersion.ipv4, IPVersion.ipv6];
   public NetworkProtocols = NetworkProtocol;
   public NetworkRuleTypes = NetworkRuleType;
 
@@ -90,8 +111,23 @@ export class SgRulesComponent implements OnChanges {
     { value: NetworkProtocol.ICMP, text: 'SECURITY_GROUP_PAGE.RULES.ICMP' }
   ];
 
-  public getIcmpTypeTranslationToken = GetICMPTypeTranslationToken;
-  public getIcmpCodeTranslationToken = GetICMPCodeTranslationToken;
+  private _icmpTypes: ICMPType[];
+
+  public get isPredefinedTemplate(): boolean {
+    return this.securityGroup && getType(this.securityGroup) === SecurityGroupType.PredefinedTemplate;
+  }
+
+  public get icmpTypes(): ICMPType[] {
+    return this._icmpTypes ? this._icmpTypes : this.typesByCIDR;
+  }
+
+  public isCidrValid(input: string) {
+    const test1 = '0.0.0.0/0';
+    const test2 = '2001:DB8::/128';
+    console.log(Utils.cidrType(test1), Utils.cidrType(test2));
+
+    return input && Utils.cidrIsValid(input);
+  }
 
   constructor(
     private networkRuleService: NetworkRuleService,
@@ -100,7 +136,6 @@ export class SgRulesComponent implements OnChanges {
     private dialogService: DialogService,
     private router: Router
   ) {
-    this.cidr = '0.0.0.0/0';
     this.protocol = NetworkProtocol.TCP;
     this.type = NetworkRuleType.Ingress;
 
@@ -119,10 +154,6 @@ export class SgRulesComponent implements OnChanges {
     this.update();
   }
 
-  public get isPredefinedTemplate(): boolean {
-    return this.securityGroup && this.securityGroup.type === SecurityGroupType.PredefinedTemplate;
-  }
-
   public addRule(e: Event): void {
     e.stopPropagation();
 
@@ -187,18 +218,12 @@ export class SgRulesComponent implements OnChanges {
       });
   }
 
-  public onCidrClick(): void {
-    if (!this.cidr) {
-      this.cidr = '0.0.0.0/0';
-    }
-  }
-
   public setIcmpTypes(value: ICMPType[]) {
-    this.icmpTypes = value;
+    this._icmpTypes = value;
 
     if (+this.selectedType <= 255 && +this.selectedType >= -1) {
       this.icmpType = +this.selectedType;
-      const type = ICMPtypes.find(_ => {
+      const type = this.typesByCIDR.find(_ => {
         return _.type === this.icmpType;
       });
       this.selectedCode = '';
@@ -214,22 +239,39 @@ export class SgRulesComponent implements OnChanges {
     }
   }
 
+  public get typesByCIDR(): ICMPType[] {
+    return this.cidrIpVersion === IPVersion.ipv6 ? ICMPv6Types : ICMPtypes;
+  }
+
+  public get IPVersion() {
+    return IPVersion;
+  }
+
+  public get cidrIpVersion(): IPVersion {
+    return this.cidr && Utils.cidrType(this.cidr) === IPVersion.ipv6
+      ? IPVersion.ipv6
+      : IPVersion.ipv4;
+  }
+
+  public onCidrChange() {
+    this._icmpTypes = this.typesByCIDR;
+  }
+
   public filter(): void {
     if (!this.securityGroup) {
       return;
     }
     const filteredEgressRules = this.filterRules(this.egressRules);
     const filteredIngressRules = this.filterRules(this.ingressRules);
-
     this.visibleRules = [...filteredIngressRules, ...filteredEgressRules];
   }
 
   public filterTypes(val: number | string) {
     const filterValue = val.toString().toLowerCase();
-    return !!val ? ICMPtypes.filter(_ => _.type.toString() === filterValue ||
+    return !!val ? this.typesByCIDR.filter(_ => _.type.toString() === filterValue ||
       this.translateService.instant(this.getIcmpTypeTranslationToken(_.type))
         .toLowerCase()
-        .indexOf(filterValue) !== -1) : ICMPtypes;
+        .indexOf(filterValue) !== -1) : this.typesByCIDR;
   }
 
   public filterCodes(val: number | string) {
@@ -238,12 +280,12 @@ export class SgRulesComponent implements OnChanges {
       _.toString().indexOf(filterValue) !== -1 ||
       this.translateService.instant(this.getIcmpCodeTranslationToken(this.icmpType, _))
         .toLowerCase()
-        .indexOf(filterValue) !== -1) : ICMPtypes.find(
+        .indexOf(filterValue) !== -1) : this.typesByCIDR.find(
       x => x.type === this.icmpType).codes;
   }
 
   public confirmChangeMode() {
-    if (!this.editMode && this.securityGroup.type === SecurityGroupType.Shared) {
+    if (!this.editMode && getType(this.securityGroup) === SecurityGroupType.Shared) {
       this.dialogService.confirm({
         message: !this.vmId
           ? 'DIALOG_MESSAGES.SECURITY_GROUPS.CONFIRM_EDIT'
@@ -300,18 +342,40 @@ export class SgRulesComponent implements OnChanges {
   private resetFilters() {
     this.selectedTypes = [];
     this.selectedProtocols = [];
+    this.selectedIPVersion = [];
     this.filter();
   }
 
   private filterRules(rules: NetworkRule[]) {
     return rules.filter((rule: NetworkRule) => {
-      return (!this.selectedProtocols.length
-        || this.selectedProtocols.find(protocol => protocol === rule.protocol))
-        && (!this.selectedTypes.length
-          || this.selectedTypes.find(type => rule.type === type));
+      const filterByIPversion = (item: NetworkRule) => {
+        const ruleIPversion = item.CIDR && Utils.cidrType(item.CIDR) === IPVersion.ipv6
+          ? IPVersion.ipv6
+          : IPVersion.ipv4;
+        return !this.selectedIPVersion.length
+          || this.selectedIPVersion.find(version => version === ruleIPversion);
+      };
+      const filterByProtocol = (item: NetworkRule) => !this.selectedProtocols.length
+        || this.selectedProtocols.find(protocol => protocol === item.protocol);
+      const filterByTypes = (item: NetworkRule) => !this.selectedTypes.length
+        || this.selectedTypes.find(type => item.type === type);
+
+      return filterByTypes(rule) && filterByIPversion(rule) && filterByProtocol(rule);
     });
   }
 
+  public getIcmpTypeTranslationToken(type: number) {
+    return this.cidrIpVersion === IPVersion.ipv6
+      ? GetICMPV6TypeTranslationToken(type)
+      : GetICMPTypeTranslationToken(type);
+  }
+
+  public getIcmpCodeTranslationToken(type: number, code: number) {
+    return this.cidrIpVersion === IPVersion.ipv6
+      ? GetICMPV6CodeTranslationToken(type, code)
+      : GetICMPCodeTranslationToken(type, code);
+  }
+
   private emitChanges() {
     const updatedSecurityGroup = new SecurityGroup(this.securityGroup);
     updatedSecurityGroup.ingressRules = this.ingressRules;
diff --git a/src/app/security-group/sg.model.ts b/src/app/security-group/sg.model.ts
index b06bace1b2..31ccfc3b98 100644
--- a/src/app/security-group/sg.model.ts
+++ b/src/app/security-group/sg.model.ts
@@ -18,6 +18,11 @@ export enum NetworkRuleType {
   Egress = 'Egress'
 }
 
+export enum IPVersion {
+  ipv4 = 'ipv4',
+  ipv6 = 'ipv6'
+}
+
 @FieldMapper({
   ingressrule: 'ingressRules',
   egressrule: 'egressRules',
@@ -97,5 +102,5 @@ export const isCustomTemplate = (securityGroup: SecurityGroup) => {
 export const isPrivate = (securityGroup: SecurityGroup) => {
   const typeTag = securityGroup.tags.find(tag => tag.key === SecurityGroupTagKeys.type);
 
-  return typeTag && typeTag.value === SecurityGroupType.Private
+  return typeTag && typeTag.value === SecurityGroupType.Private;
 };
diff --git a/src/app/shared/icmp/icmp-types.ts b/src/app/shared/icmp/icmp-types.ts
index 8b3f445e90..b00622879b 100644
--- a/src/app/shared/icmp/icmp-types.ts
+++ b/src/app/shared/icmp/icmp-types.ts
@@ -5,113 +5,82 @@ export interface ICMPType {
 }
 
 export const ICMPtypes = [
-  {
-    type: -1,
-    codes: [-1]
-  },
-  {
-    type: 0,
-    codes: [0]
-  },
-  {
-    type: 3,
-    codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
-  },
-  {
-    type: 4,
-    codes: [0]
-  },
-  {
-    type: 5,
-    codes: [0, 1, 2, 3]
-  }, {
-    type: 6,
-    codes: [0]
-  },
-  {
-    type: 8,
-    codes: [0]
-  },
-  {
-    type: 9,
-    codes: [0]
-  },
-  {
-    type: 10,
-    codes: [0]
-  },
-  {
-    type: 11,
-    codes: [0, 1]
-  },
-  {
-    type: 12,
-    codes: [0, 1, 2]
-  },
-  {
-    type: 13,
-    codes: [0]
-  },
-  {
-    type: 14,
-    codes: [0]
-  },
-  {
-    type: 15,
-    codes: [0]
-  },
-  {
-    type: 16,
-    codes: [0]
-  },
-  {
-    type: 17,
-    codes: [0]
-  },
-  {
-    type: 18,
-    codes: [0]
-  },
-  {
-    type: 30,
-    codes: [0]
-  }, {
-    type: 31,
-    codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
-  }, {
-    type: 32,
-    codes: [0]
-  }, {
-    type: 33,
-    codes: [0]
-  }, {
-    type: 34,
-    codes: [0]
-  }, {
-    type: 35,
-    codes: [0]
-  }, {
-    type: 36,
-    codes: [0]
-  }, {
-    type: 37,
-    codes: [0]
-  }, {
-    type: 38,
-    codes: [0]
-  }, {
-    type: 39,
-    codes: [0]
-  }, {
-    type: 40,
-    codes: [0, 1, 2, 3, 4, 5]
-  },
+  { type: -1, codes: [-1] },
+  { type: 0, codes: [0] },
+  { type: 3, codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] },
+  { type: 4, codes: [0] },
+  { type: 5, codes: [0, 1, 2, 3] },
+  { type: 6, codes: [0] },
+  { type: 8, codes: [0] },
+  { type: 9, codes: [0] },
+  { type: 10, codes: [0] },
+  { type: 11, codes: [0, 1] },
+  { type: 12, codes: [0, 1, 2] },
+  { type: 13, codes: [0] },
+  { type: 14, codes: [0] },
+  { type: 15, codes: [0] },
+  { type: 16, codes: [0] },
+  { type: 17, codes: [0] },
+  { type: 18, codes: [0] },
+  { type: 30, codes: [0] },
+  { type: 31, codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] },
+  { type: 32, codes: [0] },
+  { type: 33, codes: [0] },
+  { type: 34, codes: [0] },
+  { type: 35, codes: [0] },
+  { type: 36, codes: [0] },
+  { type: 37, codes: [0] },
+  { type: 38, codes: [0] },
+  { type: 39, codes: [0] },
+  { type: 40, codes: [0, 1, 2, 3, 4, 5] },
 ];
 
-export const GetICMPTypeTranslationToken = (type) => {
+export const ICMPv6Types = [
+  { type: 1, codes: [0, 1, 2, 3, 4, 5, 6] },
+  { type: 2, codes: [0] },
+  { type: 3, codes: [0, 1] },
+  { type: 4, codes: [0, 1, 2] },
+  { type: 100, codes: [0] },
+  { type: 101, codes: [0] },
+  { type: 127, codes: [0] },
+  { type: 128, codes: [0] },
+  { type: 129, codes: [0] },
+  { type: 130, codes: [0] },
+  { type: 131, codes: [0] },
+  { type: 132, codes: [0] },
+  { type: 133, codes: [0] },
+  { type: 134, codes: [0] },
+  { type: 135, codes: [0] },
+  { type: 136, codes: [0] },
+  { type: 137, codes: [0] },
+  { type: 138, codes: [0, 1, 255] },
+  { type: 139, codes: [0, 1, 2] },
+  { type: 140, codes: [0, 1, 2] },
+  { type: 141, codes: [0] },
+  { type: 142, codes: [0] },
+  { type: 144, codes: [0] },
+  { type: 145, codes: [0] },
+  { type: 146, codes: [0] },
+  { type: 147, codes: [0] },
+  { type: 160, codes: [0] },
+  { type: 161, codes: [0, 1, 2, 3, 4] },
+  { type: 200, codes: [0] },
+  { type: 201, codes: [0] },
+  { type: 255, codes: [0] }
+];
+
+export const GetICMPTypeTranslationToken = (type: number) => {
   return `SECURITY_GROUP_PAGE.ICMP_TYPES.${type}.description`;
 };
 
-export const GetICMPCodeTranslationToken = (type, code) => {
+export const GetICMPCodeTranslationToken = (type: number, code: number) => {
   return `SECURITY_GROUP_PAGE.ICMP_TYPES.${type}.codes.${code}`;
 };
+
+export const GetICMPV6TypeTranslationToken = (type: number) => {
+  return `SECURITY_GROUP_PAGE.ICMP_V6_TYPES.${type}.description`;
+};
+
+export const GetICMPV6CodeTranslationToken = (type: number, code: number) => {
+  return `SECURITY_GROUP_PAGE.ICMP_V6_TYPES.${type}.codes.${code}`;
+};
diff --git a/src/app/shared/services/utils/utils.service.ts b/src/app/shared/services/utils/utils.service.ts
index 1c6c6de8c1..00df79b645 100644
--- a/src/app/shared/services/utils/utils.service.ts
+++ b/src/app/shared/services/utils/utils.service.ts
@@ -1,11 +1,8 @@
-import * as uuid from 'uuid';
-import {
-  Params,
-  RouterState,
-  RouterStateSnapshot
-} from '@angular/router';
+import { Params, RouterState, RouterStateSnapshot } from '@angular/router';
 import { RouterStateSerializer } from '@ngrx/router-store';
-
+import { IPVersion } from '../../../security-group/sg.model';
+import * as uuid from 'uuid';
+import * as ipaddr from 'ip-address';
 
 export class Utils {
   public static getUniqueId(): string {
@@ -82,7 +79,7 @@ export class Utils {
     const g = parseInt(hex.substring(2, 4), 16);
     const b = parseInt(hex.substring(4, 6), 16);
 
-    const darkness = 1 - ( 0.299 * r + 0.587 * g + 0.114 * b) / 255;
+    const darkness = 1 - (0.299 * r + 0.587 * g + 0.114 * b) / 255;
 
     return darkness > 0.5;
   }
@@ -90,6 +87,18 @@ export class Utils {
   public static sortByName = (a, b) => {
     return a.name && a.name.localeCompare(b.name);
   };
+
+  public static cidrIsValid(range: string): boolean {
+    const ipAddressType = range.match(':') ? ipaddr.Address6 : ipaddr.Address4;
+    const cidr = new ipAddressType(range);
+    return cidr.isValid();
+  }
+
+  public static cidrType(range: string): IPVersion {
+    const ipAddressType = range.match(':') ? ipaddr.Address6 : ipaddr.Address4;
+    const cidr = new ipAddressType(range);
+    return cidr.isValid() && (cidr.v4 ? IPVersion.ipv4 : IPVersion.ipv6);
+  }
 }
 
 
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 883dd2a290..d3a48948ab 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -812,21 +812,33 @@
       "ENTER_VALID_PORT": "Enter a valid port",
       "ENTER_VALID_TYPE": "Enter a valid type",
       "ENTER_VALID_CODE": "Enter a valid code",
+      "ENTER_CIDR": "CIDR v4/v6",
       "ENTER_VALID_CIDR": "Enter a valid CIDR",
       "FAILED_TO_ADD_RULE": "Failed to add the rule",
       "FAILED_TO_REMOVE_RULE": "Failed to remove the rule",
+      "SELECT_IP_VERSION": "Select IP version",
       "SELECT_TYPE": "Select types",
       "SELECT_PROTOCOL": "Select protocols",
-      "DEFAULT_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to ip <b>{{ cidr }} to port {{ startPort }}</b>",
-      "DEFAULT_RULE_PORT_RANGE": "{{ type }} <b>{{ protocol }}</b> traffic to ip <b>{{ cidr }}</b> to <b>port range {{ startPort }}-{{ endPort }}</b>",
-      "DEFAULT_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to ip <b>{{ cidr }}</b> of ICMP type <b>{{ icmpTypeText }} [{{ icmpType }}]</b> and ICMP code <b>{{ icmpCodeText }} [{{ icmpCode }}]</b>",
-      "NO_TEXT_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to ip <b>{{ cidr }}</b> of ICMP type <b>{{ icmpType }}</b> and ICMP code <b>{{ icmpCode }}</b>",
-      "NO_CODE_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to ip <b>{{ cidr }}</b> of ICMP type <b>{{ icmpTypeText }} [{{ icmpType }}]</b> and ICMP code <b>{{ icmpCode }}</b>",
-      "DEFAULT_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to ip {{ cidr }} to port {{ startPort }}",
-      "DEFAULT_RULE_PORT_RANGE_NOMARKUP": "{{ type }} {{ protocol }} traffic to ip {{ cidr }} to port range {{ startPort }}-{{ endPort }}",
-      "DEFAULT_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to ip {{ cidr }} of ICMP type {{ icmpTypeText }} [{{ icmpType }}] and ICMP code {{ icmpCodeText }} [{{ icmpCode }}]",
-      "NO_TEXT_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to ip {{ cidr }} of ICMP type {{ icmpType }} and ICMP code {{ icmpCode }}",
-      "NO_CODE_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to ip {{ cidr }} of ICMP type {{ icmpTypeText }} [{{ icmpType }}] and ICMP code {{ icmpCode }}",
+      "EGRESS_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to {{ ipVersion }} <b>{{ cidr }} to port {{ startPort }}</b>",
+      "EGRESS_RULE_PORT_RANGE": "{{ type }} <b>{{ protocol }}</b> traffic to {{ ipVersion }} <b>{{ cidr }}</b> to <b>port range {{ startPort }}-{{ endPort }}</b>",
+      "EGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to {{ ipVersion }} <b>{{ cidr }}</b> of ICMP type <b>{{ icmpTypeText }} [{{ icmpType }}]</b> and ICMP code <b>{{ icmpCodeText }} [{{ icmpCode }}]</b>",
+      "NO_TEXT_EGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to {{ ipVersion }} <b>{{ cidr }}</b> of ICMP type <b>{{ icmpType }}</b> and ICMP code <b>{{ icmpCode }}</b>",
+      "NO_CODE_EGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic to {{ ipVersion }} <b>{{ cidr }}</b> of ICMP type <b>{{ icmpTypeText }} [{{ icmpType }}]</b> and ICMP code <b>{{ icmpCode }}</b>",
+      "EGRESS_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to {{ ipVersion }} {{ cidr }} to port {{ startPort }}",
+      "EGRESS_RULE_PORT_RANGE_NOMARKUP": "{{ type }} {{ protocol }} traffic to {{ ipVersion }} {{ cidr }} to port range {{ startPort }}-{{ endPort }}",
+      "EGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to {{ ipVersion }} {{ cidr }} of ICMP type {{ icmpTypeText }} [{{ icmpType }}] and ICMP code {{ icmpCodeText }} [{{ icmpCode }}]",
+      "NO_TEXT_EGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to {{ ipVersion }} {{ cidr }} of ICMP type {{ icmpType }} and ICMP code {{ icmpCode }}",
+      "NO_CODE_EGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic to {{ ipVersion }} {{ cidr }} of ICMP type {{ icmpTypeText }} [{{ icmpType }}] and ICMP code {{ icmpCode }}",
+      "INGRESS_RULE": "{{ type }} <b>{{ protocol }}</b> traffic from {{ ipVersion }} <b>{{ cidr }} to VM port {{ startPort }}</b>",
+      "INGRESS_RULE_PORT_RANGE": "{{ type }} <b>{{ protocol }}</b> traffic from {{ ipVersion }} <b>{{ cidr }}</b> to VM <b>port range {{ startPort }}-{{ endPort }}</b>",
+      "INGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic from <b>{{ ipVersion }}</b> <b>{{ cidr }}</b> of ICMP type <b>{{ icmpTypeText }} [{{ icmpType }}]</b> and ICMP code <b>{{ icmpCodeText }} [{{ icmpCode }}]</b>",
+      "NO_TEXT_INGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic from {{ ipVersion }} <b>{{ cidr }}</b> of ICMP type <b>{{ icmpType }}</b> and ICMP code <b>{{ icmpCode }}</b>",
+      "NO_CODE_INGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> traffic from {{ ipVersion }} <b>{{ cidr }}</b> of ICMP type <b>{{ icmpTypeText }} [{{ icmpType }}]</b> and ICMP code <b>{{ icmpCode }}</b>",
+      "INGRESS_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic from {{ ipVersion }} {{ cidr }} to port {{ startPort }}",
+      "INGRESS_RULE_PORT_RANGE_NOMARKUP": "{{ type }} {{ protocol }} traffic from {{ ipVersion }} {{ cidr }} to VM port range {{ startPort }}-{{ endPort }}",
+      "INGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic from {{ ipVersion }} {{ cidr }} of ICMP type {{ icmpTypeText }} [{{ icmpType }}] and ICMP code {{ icmpCodeText }} [{{ icmpCode }}]",
+      "NO_TEXT_INGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic from {{ ipVersion }} {{ cidr }} of ICMP type {{ icmpType }} and ICMP code {{ icmpCode }}",
+      "NO_CODE_INGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} traffic from {{ ipVersion }} {{ cidr }} of ICMP type {{ icmpTypeText }} [{{ icmpType }}] and ICMP code {{ icmpCode }}",
       "NO_FIREWALL_RULES": "No firewall rules"
     },
     "ICMP_TYPES": {
@@ -1037,6 +1049,213 @@
         }
       }
     },
+    "ICMP_V6_TYPES": {
+      "1": {
+        "description": "Destination Unreachable Message",
+        "codes": {
+          "0": "No route to destination",
+          "1": "Communication with the destination is administratively prohibited, such as a firewall filter",
+          "2": "Not assigned",
+          "3": "Address unreachable",
+          "4": "Port unreachable",
+          "5": "Source address failed ingress/egress policy",
+          "6": "Reject route to destination"
+        }
+      },
+      "2": {
+        "description": "Packet Too Big Message",
+        "codes": {
+          "0": ""
+        }
+      },
+      "3": {
+        "description": "Time Exceeded Message",
+        "codes": {
+          "0": "Hop limit exceeded in transit",
+          "1": "Fragment reassembly time exceeded"
+        }
+      },
+      "4": {
+        "description": "Parameter Problem Message",
+        "codes": {
+          "0": "Erroneous header field encountered",
+          "1": "Unrecognized next header type encountered",
+          "2": "Unrecognized IPv6 option encountered"
+        }
+      },
+      "100": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "101": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "127": {
+        "description": "Reserved for expansion of ICMPv6 error messages",
+        "codes": {
+          "0": ""
+        }
+      },
+      "128": {
+        "description": "Echo Request",
+        "codes": {
+          "0": ""
+        }
+      },
+      "129": {
+        "description": "Echo Reply",
+        "codes": {
+          "0": ""
+        }
+      },
+      "130": {
+        "description": "Multicast Listener Query",
+        "codes": {
+          "0": ""
+        }
+      },
+      "131": {
+        "description": "Multicast Listener Report",
+        "codes": {
+          "0": ""
+        }
+      },
+      "132": {
+        "description": "Multicast Listener Done",
+        "codes": {
+          "0": ""
+        }
+      },
+      "133": {
+        "description": "Router Solicitation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "134": {
+        "description": "Router Advertisement",
+        "codes": {
+          "0": ""
+        }
+      },
+      "135": {
+        "description": "Neighbor Solicitation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "136": {
+        "description": "Neighbor Advertisement",
+        "codes": {
+          "0": ""
+        }
+      },
+      "137": {
+        "description": "Redirect Message",
+        "codes": {
+          "0": ""
+        }
+      },
+      "138": {
+        "description": "Router Renumbering",
+        "codes": {
+          "0": "Router Renumbering Command",
+          "1": "Router Renumbering Result",
+          "255": "Sequence Number Reset"
+        }
+      },
+      "139": {
+        "description": "ICMP Node Information Query",
+        "codes": {
+          "0": "The Data field contains an IPv6 address which is the Subject of this Query.",
+          "1": "The Data field contains a name which is the Subject of this Query, or is empty, as in the case of a NOOP.",
+          "2": "The Data field contains an IPv4 address which is the Subject of this Query."
+        }
+      },
+      "140": {
+        "description": "ICMP Node Information Response",
+        "codes": {
+          "0": "A successful reply. The Reply Data field may or may not be empty.",
+          "1": "The Responder refuses to supply the answer. The Reply Data field will be empty.",
+          "2": "The Qtype of the Query is unknown to the Responder. The Reply Data field will be empty."
+        }
+      },
+      "141": {
+        "description": "Inverse Neighbor Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "142": {
+        "description": "Inverse Neighbor Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "144": {
+        "description": "Home Agent Address Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "145": {
+        "description": "Home Agent Address Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "146": {
+        "description": "Mobile Prefix Solicitation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "147": {
+        "description": "Mobile Prefix Advertisement",
+        "codes": {
+          "0": ""
+        }
+      },
+      "160": {
+        "description": "Extended Echo Request",
+        "codes": {
+          "0": "No error"
+        }
+      },
+      "161": {
+        "description": "Extended Echo Reply",
+        "codes": {
+          "0": "No error",
+          "1": "Malformed Query",
+          "2": "No Such Interface",
+          "3": "No Such Table Entry",
+          "4": "Multiple Interfaces Satisfy Query"
+        }
+      },
+      "200": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "201": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "255": {
+        "description": "Reserved for expansion of ICMPv6 informational messages",
+        "codes": {
+          "0": ""
+        }
+      }
+    },
     "TEMPLATE_CREATION": {
       "CREATE_NEW_TEMPLATE": "Create new template",
       "CREATE_NEW_SHARED_GROUP": "Create new shared group",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index db382cb0cc..08942f0d38 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -808,21 +808,33 @@
       "ENTER_VALID_PORT": "Некорректный порт",
       "ENTER_VALID_TYPE": "Некорректный тип",
       "ENTER_VALID_CODE": "Некорректный код",
+      "ENTER_CIDR": "CIDR v4/v6",
       "ENTER_VALID_CIDR": "Некорректный CIDR",
       "FAILED_TO_ADD_RULE": "Не удалось добавить правило",
       "FAILED_TO_REMOVE_RULE": "Не удалось удалить правило",
+      "SELECT_IP_VERSION": "Выберите версию IP",
       "SELECT_TYPE": "Выберите типы",
       "SELECT_PROTOCOL": "Выберите протоколы",
-      "DEFAULT_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до адреса <b>{{ cidr }}</b>, порт: <b>{{ startPort }}</b>",
-      "DEFAULT_RULE_PORT_RANGE": "{{ type }} <b>{{ protocol }}</b> трафик до адреса <b>{{ cidr }}</b>, интервал портов: <b>{{ startPort }}-{{ endPort }}</b>",
-      "DEFAULT_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpTypeText }} [{{ icmpType }}]</b>, ICMP код: <b>{{ icmpCodeText }} [{{ icmpCode }}]</b>",
-      "NO_TEXT_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpType }}</b>, ICMP код: <b>{{ icmpCode }}</b>",
-      "NO_CODE_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpTypeText }} [{{ icmpType }}]</b>, ICMP код: <b>{{ icmpCode }}</b>",
-      "DEFAULT_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до адреса {{ cidr }}, порт: {{ startPort }}",
-      "DEFAULT_RULE_PORT_RANGE_NOMARKUP": "{{ type }} {{ protocol }} трафик до адреса {{ cidr }}, интервал портов: {{ startPort }}-{{ endPort }}",
-      "DEFAULT_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до адреса {{ cidr }}, ICMP тип: {{ icmpTypeText }} [{{ icmpType }}], ICMP код: {{ icmpCodeText }} [{{ icmpCode }}]",
-      "NO_TEXT_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до адреса {{ cidr }}, ICMP тип: {{ icmpType }}, ICMP код: {{ icmpCode }}",
-      "NO_CODE_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до адреса {{ cidr }}, ICMP тип: {{ icmpTypeText }} [{{ icmpType }}], ICMP код: {{ icmpCode }}",
+      "EGRESS_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до {{ ipVersion }} адреса <b>{{ cidr }}</b>, порт: <b>{{ startPort }}</b>",
+      "EGRESS_RULE_PORT_RANGE": "{{ type }} <b>{{ protocol }}</b> трафик до {{ ipVersion }} адреса<b> {{ cidr }}</b>, интервал портов: <b>{{ startPort }}-{{ endPort }}</b>",
+      "EGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до {{ ipVersion }} адреса<b> {{ cidr }}</b>, ICMP тип: <b>{{ icmpTypeText }} [{{ icmpType }}]</b>, ICMP код: <b>{{ icmpCodeText }} [{{ icmpCode }}]</b>",
+      "NO_TEXT_EGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до {{ ipVersion }} адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpType }}</b>, ICMP код: <b>{{ icmpCode }}</b>",
+      "NO_CODE_EGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик до {{ ipVersion }} адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpTypeText }} [{{ icmpType }}]</b>, ICMP код: <b>{{ icmpCode }}</b>",
+      "EGRESS_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до {{ ipVersion }} адреса {{ cidr }}, порт: {{ startPort }}",
+      "EGRESS_RULE_PORT_RANGE_NOMARKUP": "{{ type }} {{ protocol }} трафик до {{ ipVersion }} адреса {{ cidr }}, интервал портов: {{ startPort }}-{{ endPort }}",
+      "EGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до {{ ipVersion }} адреса {{ cidr }}, ICMP тип: {{ icmpTypeText }} [{{ icmpType }}], ICMP код: {{ icmpCodeText }} [{{ icmpCode }}]",
+      "NO_TEXT_EGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до {{ ipVersion }} адреса {{ cidr }}, ICMP тип: {{ icmpType }}, ICMP код: {{ icmpCode }}",
+      "NO_CODE_EGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик до {{ ipVersion }} адреса {{ cidr }}, ICMP тип: {{ icmpTypeText }} [{{ icmpType }}], ICMP код: {{ icmpCode }}",
+      "INGRESS_RULE": "{{ type }} <b>{{ protocol }}</b> трафик с {{ ipVersion }} адреса <b>{{ cidr }}</b>, порт: <b>{{ startPort }}</b>",
+      "INGRESS_RULE_PORT_RANGE": "{{ type }} <b>{{ protocol }}</b> трафик с {{ ipVersion }} адреса <b>{{ cidr }}</b>, интервал портов: <b>{{ startPort }}-{{ endPort }}</b>",
+      "INGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик с {{ ipVersion }} адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpTypeText }} [{{ icmpType }}]</b>, ICMP код: <b>{{ icmpCodeText }} [{{ icmpCode }}]</b>",
+      "NO_TEXT_INGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик с {{ ipVersion }} адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpType }}</b>, ICMP код: <b>{{ icmpCode }}</b>",
+      "NO_CODE_INGRESS_ICMP_RULE": "{{ type }} <b>{{ protocol }}</b> трафик с {{ ipVersion }} адреса <b>{{ cidr }}</b>, ICMP тип: <b>{{ icmpTypeText }} [{{ icmpType }}]</b>, ICMP код: <b>{{ icmpCode }}</b>",
+      "INGRESS_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик с {{ ipVersion }} адреса {{ cidr }}, порт: {{ startPort }}",
+      "INGRESS_RULE_PORT_RANGE_NOMARKUP": "{{ type }} {{ protocol }} трафик с {{ ipVersion }} адреса {{ cidr }}, интервал портов: {{ startPort }}-{{ endPort }}",
+      "INGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик с {{ ipVersion }} адреса {{ cidr }}, ICMP тип: {{ icmpTypeText }} [{{ icmpType }}], ICMP код: {{ icmpCodeText }} [{{ icmpCode }}]",
+      "NO_TEXT_INGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик с {{ ipVersion }} адреса {{ cidr }}, ICMP тип: {{ icmpType }}, ICMP код: {{ icmpCode }}",
+      "NO_CODE_INGRESS_ICMP_RULE_NOMARKUP": "{{ type }} {{ protocol }} трафик с {{ ipVersion }} адреса {{ cidr }}, ICMP тип: {{ icmpTypeText }} [{{ icmpType }}], ICMP код: {{ icmpCode }}",
       "NO_FIREWALL_RULES": "Нет правил брандмауэра"
     },
     "ICMP_TYPES": {
@@ -1033,6 +1045,213 @@
         }
       }
     },
+    "ICMP_V6_TYPES": {
+      "1": {
+        "description": "Сообщение о недоступности назначения",
+        "codes": {
+          "0": "Никакой маршрут назначению",
+          "1": "Связь с назначением административно не запрещен, такие как фильтр межсетевого экрана",
+          "2": "Не назначенный",
+          "3": "Недостижимый адрес",
+          "4": "Недостижимый порт",
+          "5": "Source address failed ingress/egress policy",
+          "6": "Reject route to destination"
+        }
+      },
+      "2": {
+        "description": "Сообщение о слишком большом пакете",
+        "codes": {
+          "0": ""
+        }
+      },
+      "3": {
+        "description": "Time Exceeded Message",
+        "codes": {
+          "0": "Предел перехода превысил в транзите",
+          "1": "Время повторной сборки фрагмента превысило"
+        }
+      },
+      "4": {
+        "description": "Сообщение о проблеме параметра",
+        "codes": {
+          "0": "Ошибочное поле заголовка встретилось",
+          "1": "Неопознанный следующий тип заголовка встретился",
+          "2": "Unrecognized IPv6"
+        }
+      },
+      "100": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "101": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "127": {
+        "description": "Reserved for expansion of ICMPv6 error messages",
+        "codes": {
+          "0": ""
+        }
+      },
+      "128": {
+        "description": "Сообщение запроса эха",
+        "codes": {
+          "0": ""
+        }
+      },
+      "129": {
+        "description": "Сообщение эхо-ответа",
+        "codes": {
+          "0": ""
+        }
+      },
+      "130": {
+        "description": "Multicast Listener Query",
+        "codes": {
+          "0": ""
+        }
+      },
+      "131": {
+        "description": "Multicast Listener Report",
+        "codes": {
+          "0": ""
+        }
+      },
+      "132": {
+        "description": "Multicast Listener Done",
+        "codes": {
+          "0": ""
+        }
+      },
+      "133": {
+        "description": "Сообщение запроса маршрутизатора",
+        "codes": {
+          "0": ""
+        }
+      },
+      "134": {
+        "description": "Сообщение объявления маршрутизатора",
+        "codes": {
+          "0": ""
+        }
+      },
+      "135": {
+        "description": "Сообщение Neighbor Solicitation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "136": {
+        "description": "Сообщение об объявлении окружения",
+        "codes": {
+          "0": ""
+        }
+      },
+      "137": {
+        "description": "Сообщение перенаправления",
+        "codes": {
+          "0": ""
+        }
+      },
+      "138": {
+        "description": "Router Renumbering",
+        "codes": {
+          "0": "Router Renumbering Command",
+          "1": "Router Renumbering Result",
+          "255": "Sequence Number Reset"
+        }
+      },
+      "139": {
+        "description": "ICMP Node Information Query",
+        "codes": {
+          "0": "The Data field contains an IPv6 address which is the Subject of this Query.",
+          "1": "The Data field contains a name which is the Subject of this Query, or is empty, as in the case of a NOOP.",
+          "2": "The Data field contains an IPv4 address which is the Subject of this Query."
+        }
+      },
+      "140": {
+        "description": "ICMP Node Information Response",
+        "codes": {
+          "0": "A successful reply. The Reply Data field may or may not be empty.",
+          "1": "The Responder refuses to supply the answer. The Reply Data field will be empty.",
+          "2": "The Qtype of the Query is unknown to the Responder. The Reply Data field will be empty."
+        }
+      },
+      "141": {
+        "description": "Inverse Neighbor Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "142": {
+        "description": "Inverse Neighbor Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "144": {
+        "description": "Home Agent Address Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "145": {
+        "description": "Home Agent Address Discovery",
+        "codes": {
+          "0": ""
+        }
+      },
+      "146": {
+        "description": "Mobile Prefix Solicitation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "147": {
+        "description": "Mobile Prefix Advertisement",
+        "codes": {
+          "0": ""
+        }
+      },
+      "160": {
+        "description": "Extended Echo Request",
+        "codes": {
+          "0": "No error"
+        }
+      },
+      "161": {
+        "description": "Extended Echo Reply",
+        "codes": {
+          "0": "No error",
+          "1": "Malformed Query",
+          "2": "No Such Interface",
+          "3": "No Such Table Entry",
+          "4": "Multiple Interfaces Satisfy Query"
+        }
+      },
+      "200": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "201": {
+        "description": "Private experimentation",
+        "codes": {
+          "0": ""
+        }
+      },
+      "255": {
+        "description": "Reserved for expansion of ICMPv6 informational messages",
+        "codes": {
+          "0": ""
+        }
+      }
+    },
     "TEMPLATE_CREATION": {
       "CREATE_NEW_TEMPLATE": "Создать новый шаблон",
       "CREATE_NEW_SHARED_GROUP": "Создать новую разделяемую группу",
diff --git a/src/testutils/mocks/model-services/services/mock-security-group.service.spec.ts b/src/testutils/mocks/model-services/services/mock-security-group.service.spec.ts
index 6ee751d892..08792e8b12 100644
--- a/src/testutils/mocks/model-services/services/mock-security-group.service.spec.ts
+++ b/src/testutils/mocks/model-services/services/mock-security-group.service.spec.ts
@@ -1,12 +1,11 @@
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs/Observable';
-import { SecurityGroup, SecurityGroupType } from '../../../../app/security-group/sg.model';
-
+import { SecurityGroup } from '../../../../app/security-group/sg.model';
 
 const securityGroupTemplates: Array<Object> = require('../fixtures/securityGroupTemplates.json');
 
 @Injectable()
-export class MockServiceOfferingService {
+export class MockSecurityGroupService {
   public getList(): Observable<Array<SecurityGroup>> {
     return Observable.of(securityGroupTemplates.map(json => new SecurityGroup(json)));
   }
diff --git a/yarn.lock b/yarn.lock
index b730f23571..b63a5410e0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2914,6 +2914,18 @@ invert-kv@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
 
+ip-address@^5.8.9:
+  version "5.8.9"
+  resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-5.8.9.tgz#6379277c23fc5adb20511e4d23ec2c1bde105dfd"
+  dependencies:
+    jsbn "1.1.0"
+    lodash.find "^4.6.0"
+    lodash.max "^4.0.1"
+    lodash.merge "^4.6.0"
+    lodash.padstart "^4.6.1"
+    lodash.repeat "^4.1.0"
+    sprintf-js "1.1.0"
+
 ip@^1.1.0, ip@^1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -3270,6 +3282,10 @@ js-yaml@~3.7.0:
     argparse "^1.0.7"
     esprima "^2.6.0"
 
+jsbn@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
+
 jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -3544,14 +3560,34 @@ lodash.clonedeep@^4.3.2, lodash.clonedeep@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
 
+lodash.find@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
+
+lodash.max@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/lodash.max/-/lodash.max-4.0.1.tgz#8735566c618b35a9f760520b487ae79658af136a"
+
 lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
+lodash.merge@^4.6.0:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
+
 lodash.mergewith@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
 
+lodash.padstart@^4.6.1:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b"
+
+lodash.repeat@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/lodash.repeat/-/lodash.repeat-4.1.0.tgz#fc7de8131d8c8ac07e4b49f74ffe829d1f2bec44"
+
 lodash.tail@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
@@ -5459,7 +5495,11 @@ split@^1.0.0:
   dependencies:
     through "2"
 
-sprintf-js@^1.0.3:
+sprintf-js@1.1.0, sprintf-js@^1.0.3:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.0.tgz#cffcaf702daf65ea39bb4e0fa2b299cec1a1be46"
+
+sprintf-js@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c"