HONEYCOMB-229 Introduce NAT to HC
authorMaros Marsalek <[email protected]>
Wed, 5 Oct 2016 13:03:33 +0000 (15:03 +0200)
committerMarek Gradzki <[email protected]>
Fri, 14 Oct 2016 05:42:54 +0000 (05:42 +0000)
Reflects SNAT from VPP:
- 1:1 Static IPv4 mapping
- interface in/out NAT feature management

Bonus:
- Support presence containers in infra

Change-Id: Ieb38526f83edbae5e605d5c7e39bb22bbafc50e5
Signed-off-by: Maros Marsalek <[email protected]>
36 files changed:
nat/asciidoc/Readme.adoc [new file with mode: 0644]
nat/nat-api/asciidoc/Readme.adoc [new file with mode: 0644]
nat/nat-api/pom.xml [new file with mode: 0644]
nat/nat-api/src/main/yang/ietf-nat.yang [new file with mode: 0644]
nat/nat-api/src/main/yang/interface-nat.yang [new file with mode: 0644]
nat/nat-api/src/main/yang/nat-context.yang [new file with mode: 0644]
nat/nat2vpp/asciidoc/Readme.adoc [new file with mode: 0644]
nat/nat2vpp/pom.xml [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/NatModule.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/init/NatInitializer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/jvpp/JVppSnatProvider.java [new file with mode: 0755]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/MappingEntryCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/NatInstanceCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/NatReaderFactory.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/IfcNatReaderFactory.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/util/MappingEntryContext.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/MappingEntryCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/NatInstaceCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/NatWriterFactory.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/AbstractInterfaceNatCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/IfcNatWriterFactory.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/InterfaceInboundNatCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/InterfaceOutboundNatCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/NatModuleTest.java [new file with mode: 0644]
nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/util/MappingEntryContextTest.java [new file with mode: 0644]
nat/nat2vpp/src/test/resources/nat.json [new file with mode: 0644]
nat/pom.xml [new file with mode: 0644]
nat/postman_rest_collection.json [new file with mode: 0644]
nsh/impl/src/main/java/io/fd/honeycomb/vppnsh/impl/VppNshModule.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java
vpp-common/vpp-impl-parent/pom.xml
vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/Ipv4Translator.java
vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/NamingContext.java
vpp-integration/minimal-distribution/pom.xml

diff --git a/nat/asciidoc/Readme.adoc b/nat/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..eced90f
--- /dev/null
@@ -0,0 +1,12 @@
+= SNAT
+
+Support fot VPP's SNAT plugin:
+https://docs.fd.io/vpp/16.12/plugins_snat-plugin_snat.html
+
+== Supported features
+
+=== 1:1 NAT
+- Create/Read/Delete static 1:1 IPv4 (no port) mappings
+
+=== Interface NAT feature
+- Enable/Disable NAT feature for interface
\ No newline at end of file
diff --git a/nat/nat-api/asciidoc/Readme.adoc b/nat/nat-api/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..300d838
--- /dev/null
@@ -0,0 +1,11 @@
+= nat-api
+
+The APIs are based on ietf-nat YANG model draft:
+https://tools.ietf.org/html/draft-sivakumar-yang-nat-05
+
+== Notes on model
+* Nat-instances map to VRF in VPP (nat-instance/id mapps to VRF-ID)
+
+== Deviations
+* nat-state/nat-instances/nat-instance/id type changed to uint32 from int32
+* "mandatory true" statements commented to support partial model implementation
diff --git a/nat/nat-api/pom.xml b/nat/nat-api/pom.xml
new file mode 100644 (file)
index 0000000..13d8f0c
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>io.fd.honeycomb.common</groupId>
+    <artifactId>api-parent</artifactId>
+    <relativePath>../../common/api-parent</relativePath>
+    <version>1.16.12-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>io.fd.honeycomb.nat</groupId>
+  <artifactId>nat-api</artifactId>
+  <name>${project.artifactId}</name>
+  <version>1.16.12-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <naming.context.version>1.16.12-SNAPSHOT</naming.context.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>iana-if-type-2014-05-08</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>ietf-yang-types-20130715</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>ietf-interfaces</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>ietf-inet-types-2013-07-15</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>yang-ext</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.fd.honeycomb.vpp</groupId>
+      <artifactId>naming-context-api</artifactId>
+      <version>${naming.context.version}</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/nat/nat-api/src/main/yang/ietf-nat.yang b/nat/nat-api/src/main/yang/ietf-nat.yang
new file mode 100644 (file)
index 0000000..5470770
--- /dev/null
@@ -0,0 +1,1074 @@
+module ietf-nat {
+
+    namespace "urn:ietf:params:xml:ns:yang:ietf-nat";
+    //namespace to be assigned by IANA
+    prefix "nat";
+        import ietf-inet-types {
+       prefix "inet";
+    }
+
+    organization "IETF NetMod Working Group";
+    contact
+      "Senthil Sivakumar <[email protected]>
+       Mohamed Boucadair <[email protected]>
+       Suresh Vinapamula <[email protected]>";
+
+     description
+        "This module is a YANG module for NAT implementations
+        (including both NAT44 and NAT64 flavors.
+
+        Copyright (c) 2015 IETF Trust and the persons identified as
+        authors of the code.  All rights reserved.
+
+        Redistribution and use in source and binary forms, with or
+        without modification, is permitted pursuant to, and subject
+        to the license terms contained in, the Simplified BSD License
+        set forth in Section 4.c of the IETF Trust's Legal Provisions
+        Relating to IETF Documents
+        (http://trustee.ietf.org/license-info).
+
+        This version of this YANG module is part of RFC XXXX; see
+        the RFC itself for full legal notices.";
+
+     revision 2015-09-08 {
+       description "Fixes few YANG errors.";
+       reference "-02";
+     }
+
+     revision 2015-09-07 {
+       description "Completes the NAT64 model.";
+       reference "01";
+     }
+
+     revision 2015-08-29 {
+       description "Initial version.";
+       reference "00";
+     }
+
+     typedef percent {
+          type uint8 {
+               range "0 .. 100";
+          }
+          description
+              "Percentage";
+     }
+
+     /*
+      * Grouping
+      */
+
+     grouping timeouts {
+         description
+         "Configure values of various timeouts.";
+
+         leaf udp-timeouts {
+           type uint32;
+           default 300;
+           description
+            "UDP inactivity timeout.";
+         }
+
+         leaf tcp-idle-timeout {
+             type uint32;
+             default 7440;
+             description
+                "TCP Idle timeout, as per RFC 5382 should be no
+                 2 hours and 4 minutes.";
+         }
+
+         leaf tcp-trans-open-timeout {
+             type uint32;
+             default 240;
+             description
+            "The value of the transitory open connection
+            idle-timeout.";
+         }
+
+         leaf tcp-trans-close-timeout {
+             type uint32;
+              default 240;
+              description
+                "The value of the transitory close connection
+                 idle-timeout.";
+          }
+
+          leaf tcp-in-syn-timeout {
+              type uint32;
+              default 6;
+              description
+                "6 seconds, as defined in [RFC5382].";
+          }
+
+          leaf fragment-min-timeout {
+              type uint32;
+              default 2;
+              description
+                "As long as the NAT has available resources,
+                the NAT allows the fragments to arrive
+                over fragment-min-timeout interval.
+                The default value is inspired from RFC6146.";
+          }
+
+          leaf icmp-timeout {
+              type uint32;
+              default 60;
+              description
+                  "60 seconds, as defined in [RFC5508].";
+          }
+     }
+
+     // port numbers: single or port range
+
+     grouping port-number {
+         description
+        "Individual port or a range of ports.";
+
+         choice port-type {
+             default single-port-number;
+             description
+                 "Port type: single or port-range.";
+
+             case single-port-number {
+                 leaf single-port-number {
+                     type inet:port-number;
+                     description
+                         "Used for single port numbers.";
+                 }
+             }
+
+             case port-range {
+                 leaf start-port-number {
+                     type inet:port-number;
+                     description
+                         "Begining of the port range.";
+                 }
+
+                 leaf end-port-number {
+                     type inet:port-number;
+                     description
+                         "End of the port range.";
+                 }
+             }
+         }
+     }
+
+     grouping mapping-entry {
+          description
+          "NAT mapping entry.";
+
+          leaf index {
+              type uint32;
+              description
+                "A unique identifier of a mapping entry.";
+          }
+
+          leaf type {
+               type enumeration {
+                   enum "static"  {
+                      description
+                         "The mapping entry is manually configured.";
+                   }
+
+                   enum "dynamic" {
+                      description
+                       "This mapping is created by an outgoing
+                       packet.";
+                   }
+               }
+               description
+                 "Indicates the type of a mapping entry. E.g.,
+                 a mapping can be: static or dynamic";
+          }
+
+          leaf internal-src-address {
+              type inet:ip-address;
+              mandatory true;
+              description
+               "Corresponds to the source IPv4/IPv6 address
+                of the IPv4 packet";
+          }
+
+          container internal-src-port {
+              description
+                 "Corresponds to the source port of the
+                  IPv4 packet.";
+              uses port-number;
+          }
+
+          leaf external-src-address {
+               type inet:ipv4-address;
+               mandatory true;
+               description
+                "External IPv4 address assigned by NAT";
+          }
+
+          container external-src-port {
+             description
+            "External source port number assigned by NAT.";
+             uses port-number;
+          }
+
+          leaf transport-protocol {
+              type uint8;
+              // mandatory true;
+              description
+                "Upper-layer protocol associated with this mapping.
+                 Values are taken from the IANA protocol registry.
+                 For example, this field contains 6 (TCP) for a TCP
+                 mapping or 17 (UDP) for a UDP mapping.";
+          }
+
+          leaf internal-dst-address {
+              type inet:ipv4-prefix;
+              description
+               "Corresponds to the destination IPv4 address
+                of the IPv4 packet, for example, some NAT
+                implementation support translating both source
+                and destination address and ports referred to as
+                Twice NAT";
+          }
+
+          container internal-dst-port {
+              description
+                 "Corresponds to the destination port of the
+                  IPv4 packet.";
+               uses port-number;
+          }
+
+          leaf external-dst-address {
+               type inet:ipv4-address;
+               description
+                "External destination IPv4 address";
+          }
+
+          container external-dst-port {
+             description
+            "External source port number.";
+             uses port-number;
+          }
+
+          leaf lifetime {
+               type uint32;
+               // mandatory true;
+               description
+                 "Lifetime of the mapping.";
+          }
+     }
+
+     grouping nat-parameters {
+          description
+            "NAT parameters for a given instance";
+
+              list external-ip-address-pool {
+                   key pool-id;
+
+
+                   description
+                 "Pool of external IP addresses used to service
+                  internal hosts.
+                  Both contiguous and non-contiguous pools
+                  can be configured for NAT.";
+
+                   leaf pool-id {
+                        type uint32;
+                        description
+                          "An identifier of the address pool.";
+                    }
+
+                    leaf external-ip-pool {
+                         type inet:ipv4-prefix;
+                         description
+                           "An IPv4 prefix used for NAT purposes.";
+                    }
+              }
+
+
+              leaf subscriber-mask-v6 {
+                  type uint8 {
+                      range "0 .. 128";
+                  }
+                  description
+                   "The subscriber-mask is an integer that indicates
+                   the length of significant bits to be applied on
+                   the source IP address (internal side) to
+                   unambiguously identify a CPE.
+
+                   Subscriber-mask is a system-wide configuration
+                   parameter that is used to enforce generic
+                   per-subscriberpolicies (e.g., port-quota).
+
+                   The enforcement of these generic policies does not
+                   require the configuration of every subscriber's
+                   prefix.
+
+                   Example: suppose the 2001:db8:100:100::/56 prefix
+                   is assigned to a NAT64 serviced CPE. Suppose also
+                   that 2001:db8:100:100::1 is the IPv6 address used
+                   by the client that resides in that CPE. When the
+                   NAT64 receives a packet from this client,
+                   it applies the subscriber-mask (e.g., 56) on
+                   the source IPv6 address to compute the associated
+                   prefix for this client (2001:db8:100:100::/56).
+                   Then, the NAT64 enforces policies based on that
+                   prefix (2001:db8:100:100::/56), not on the exact
+                   source IPv6 address.";
+              }
+
+
+              list subscriber-mask-v4 {
+
+                   key sub-mask-id;
+
+                   description
+                      "IPv4 subscriber mask.";
+
+                   leaf sub-mask-id {
+                        type uint32;
+                        description
+                          "An identifier of the subscriber masks.";
+                   }
+                   leaf sub-mask {
+                         type inet:ipv4-prefix;
+                         // mandatory true;
+                         description
+                          "The IP address subnets that matches
+                          should be translated. E.g., If the
+                          private realms that are to be translated
+                          by NAT would be 192.0.2.0/24";
+                   }
+               }
+
+               leaf paired-address-pooling {
+                   type boolean;
+                   default true;
+                   description
+                    "Paired address pooling is indicating to NAT
+                    that all the flows from an internal IP
+                    address must be assigned the same external
+                    address. This is defined in RFC 4007.";
+               }
+
+               leaf nat-mapping-type {
+                    type enumeration {
+                        enum "eim"  {
+                           description
+                              "endpoint-independent-mapping.
+                              Refer section 4 of RFC 4787.";
+                        }
+
+                        enum "adm"  {
+                           description
+                              "address-dependent-mapping.
+                              Refer section 4 of RFC 4787.";
+                        }
+
+                        enum "edm"  {
+                           description
+                              "address-and-port-dependent-mapping.
+                              Refer section 4 of RFC 4787.";
+                        }
+                     }
+                    description
+                      "Indicates the type of a NAT mapping.";
+               }
+               leaf nat-filtering-type {
+                    type enumeration {
+                        enum "eif"  {
+                           description
+                              "endpoint-independent- filtering.
+                              Refer section 5 of RFC 4787.";
+                        }
+
+                        enum "adf"  {
+                           description
+                              "address-dependent- filtering.
+                              Refer section 5 of RFC 4787.";
+                        }
+
+                        enum "edf"  {
+                           description
+                              "address-and-port-dependent- filtering.
+                              Refer section 5 of RFC 4787.";
+                        }
+                       }
+                    description
+                      "Indicates the type of a NAT filtering.";
+               }
+
+               leaf port-quota {
+                    type uint16;
+                    description
+                      "Configures a port quota to be assigned per
+                      subscriber.";
+               }
+
+               container port-set {
+                    description
+                     "Manages port-set assignments.";
+
+                    leaf port-set-enable {
+                        type boolean;
+                        description
+                           "Enable/Disable port set assignment.";
+                    }
+
+                    leaf port-set-size {
+                         type uint16;
+                         description
+                          "Indicates the size of assigned port
+                          sets.";
+                    }
+
+                    leaf port-set-timeout {
+                       type uint32;
+                       description
+                           "Inactivty timeout for port sets.";
+                    }
+               }
+
+              leaf port-randomization-enable {
+                 type boolean;
+                 description
+                   "Enable/disable port randomization
+                     feature.";
+              }
+
+              leaf port-preservation-enable {
+                 type boolean;
+                 description
+                   "Indicates whether the PCP server should
+                     preserve the internal port number.";
+              }
+
+              leaf port-range-preservation-enable {
+                   type boolean;
+                   description
+                    "Indicates whether the NAT device should
+                    preserve the internal port range.";
+               }
+
+              leaf port-parity-preservation-enable {
+                 type boolean;
+                 description
+                   "Indicates whether the PCP server should
+                     preserve the port parity of the
+                     internal port number.";
+              }
+              leaf address-roundrobin-enable {
+                 type boolean;
+                 description
+                   "Enable/disable address allocation
+                   round robin.";
+               }
+
+          uses timeouts;
+          container logging-info {
+               description
+                 "Information about Logging NAT events";
+
+               leaf destination-address {
+                    type inet:ipv4-prefix;
+                    // mandatory true;
+                    description
+                      "Address of the collector that receives
+                      the logs";
+               }
+               leaf destination-port {
+                    type inet:port-number;
+                    // mandatory true;
+                    description
+                       "Destination port of the collector.";
+               }
+
+          }
+          container connection-limit {
+               description
+                 "Information on the config parameters that
+                  rate limit the translations based on various
+                  criteria";
+
+               leaf limit-per-subscriber {
+                    type uint32;
+                    description
+                      "Maximum number of NAT mappings per
+                      subscriber.";
+               }
+               leaf limit-per-vrf {
+                    type uint32;
+                    description
+                      "Maximum number of NAT mappings per
+                      VLAN/VRF.";
+               }
+               leaf limit-per-subnet {
+                    type inet:ipv4-prefix;
+                    description
+                     "Maximum number of NAT mappings per
+                      subnet.";
+               }
+               leaf limit-per-instance {
+                    type uint32;
+                    // mandatory true;
+                    description
+                      "Maximum number of NAT mappings per
+                      instance.";
+               }
+          }
+          container mapping-limit {
+               description
+                 "Information on the config parameters that
+                  rate limit the mappings based on various
+                  criteria";
+
+               leaf limit-per-subscriber {
+                    type uint32;
+                    description
+                      "Maximum number of NAT mappings per
+                      subscriber.";
+               }
+               leaf limit-per-vrf {
+                    type uint32;
+                    description
+                      "Maximum number of NAT mappings per
+                      VLAN/VRF.";
+               }
+               leaf limit-per-subnet {
+                    type inet:ipv4-prefix;
+                    description
+                     "Maximum number of NAT mappings per
+                      subnet.";
+               }
+               leaf limit-per-instance {
+                    type uint32;
+                    // mandatory true;
+                    description
+                      "Maximum number of NAT mappings per
+                      instance.";
+               }
+          }
+          leaf ftp-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable FTP ALG";
+          }
+
+          leaf dns-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable DNSALG";
+          }
+
+          leaf tftp-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable TFTP ALG";
+          }
+
+          leaf msrpc-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable MS-RPC ALG";
+          }
+
+          leaf netbios-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable NetBIOS ALG";
+          }
+
+          leaf rcmd-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable rcmd ALG";
+          }
+
+          leaf ldap-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable LDAP ALG";
+          }
+
+          leaf sip-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable SIP ALG";
+          }
+
+          leaf rtsp-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable RTSP ALG";
+          }
+
+          leaf h323-alg-enable {
+               type boolean;
+               description
+                  "Enable/Disable H323 ALG";
+          }
+
+          leaf all-algs-enable {
+               type boolean;
+               description
+                  "Enable/Disable all the ALGs";
+          }
+
+          container notify-pool-usage {
+               description
+                  "Notification of Pool usage when certain criteria
+                   is met";
+
+               leaf pool-id {
+                    type uint32;
+                    description
+                      "Pool-ID for which the notification
+                      criteria is defined";
+               }
+
+               leaf notify-pool-hi-threshold {
+                    type percent;
+                    // mandatory true;
+                    description
+                     "Notification must be generated when the
+                     defined high threshold is reached.
+                     For example, if a notification is
+                     required when the pool utilization reaches
+                     90%, this configuration parameter must
+                     be set to 90%";
+               }
+
+               leaf notify-pool-low-threshold {
+                    type percent;
+                    description
+                     "Notification must be generated when the defined
+                     low threshold is reached.
+                     For example, if a notification is required when
+                     the pool utilization reaches below 10%,
+                     this configuration parameter must be set to
+                     10%";
+               }
+          }
+          list nat64-prefixes {
+               key nat64-prefix-id;
+
+               description
+                "Provides one or a list of NAT64 prefixes
+                With or without a list of destination IPv4 prefixes.
+
+                Destination-based Pref64::/n is discussed in
+                Section 5.1 of [RFC7050]). For example:
+                192.0.2.0/24 is mapped to 2001:db8:122:300::/56.
+                198.51.100.0/24 is mapped to 2001:db8:122::/48.";
+
+               leaf nat64-prefix-id {
+                   type uint32;
+                   description
+                     "An identifier of the NAT64 prefix.";
+               }
+
+               leaf nat64-prefix {
+                   type inet:ipv6-prefix;
+                   default "64:ff9b::/96";
+                   description
+                     "A NAT64 prefix. Can be NSP or WKP [RFC6052].";
+               }
+
+               list destination-ipv4-prefix {
+
+                    key ipv4-prefix-id;
+
+                    description
+                      "An IPv4 prefix/address.";
+
+                    leaf ipv4-prefix-id {
+                       type uint32;
+                       description
+                        "An identifier of the IPv4 prefix/address.";
+                    }
+
+                    leaf ipv4-prefix {
+                       type inet:ipv4-prefix;
+                       description
+                        "An IPv4 address/prefix. ";
+                    }
+               }
+          }
+     } //nat-parameters group
+
+     container nat-config {
+         description
+          "NAT";
+
+         container nat-instances {
+            description
+              "nat instances";
+
+             list nat-instance {
+
+                 key "id";
+
+                 description
+                    "A NAT instance.";
+
+                 leaf id {
+                     type uint32;
+                     description
+                      "NAT instance identifier.";
+                 }
+
+                 leaf enable {
+                     type boolean;
+                     description
+                      "Status of the the NAT instance.";
+                 }
+
+                 uses nat-parameters;
+
+                 container mapping-table {
+                    description
+                      "NAT dynamic mapping table used to track
+                      sessions";
+
+                      list mapping-entry {
+                           key "index";
+                           description
+                             "NAT mapping entry.";
+                           uses mapping-entry;
+                      }
+                 }
+             }
+         }
+     }
+
+     /*
+      * NAT State
+      */
+
+     container nat-state {
+
+          config false;
+
+          description
+             "nat-state";
+
+          container nat-instances {
+              description
+                  "nat instances";
+
+              list nat-instance {
+                  key "id";
+
+                  description
+                   "nat instance";
+
+                  leaf id {
+                      // FIXME changed int32 to uint32 to align with nat-config (authors of draft notified)
+                      type uint32;
+                       description
+                        "The identifier of the nat instance.";
+                  }
+
+                  container nat-capabilities {
+                     description
+                        "NAT Capabilities";
+
+                      leaf nat44-support {
+                          type boolean;
+                           description
+                             "Indicates NAT44 support";
+                       }
+
+                       leaf nat64-support {
+                           type boolean;
+                            description
+                             "Indicates NAT64 support";
+                       }
+
+                       leaf static-mapping-support {
+                          type boolean;
+                           description
+                             "Indicates whether static mappings are
+                             supported.";
+                       }
+
+                       leaf port-set-support {
+                           type boolean;
+                            description
+                             "Indicates port set assignment
+                             support ";
+                       }
+
+                       leaf port-randomization-support {
+                          type boolean;
+                          description
+                           "Indicates whether port randomization is
+                             supported.";
+                       }
+
+                       leaf port-range-preservation-support {
+                            type boolean;
+                            description
+                            "Indicates whether port range
+                            preservation is supported.";
+                       }
+
+                       leaf port-preservation-suport {
+                            type boolean;
+                            description
+                             "Indicates whether port preservation
+                               is supported.";
+                       }
+
+                       leaf port-parity-preservation-support {
+                            type boolean;
+                            description
+                             "Indicates whether port parity
+                             preservation is supported.";
+                       }
+
+                       leaf address-roundrobin-support {
+                            type boolean;
+                            description
+                             "Indicates whether address allocation
+                             round robin is supported.";
+                       }
+
+          leaf ftp-alg-support {
+               type boolean;
+               description
+                  "Indicates whether FTP ALG is supported";
+          }
+
+          leaf dns-alg-support {
+               type boolean;
+               description
+                  "Indicates whether DNSALG is supported";
+          }
+
+          leaf tftp-support {
+               type boolean;
+               description
+                  "Indicates whether TFTP ALG is supported";
+          }
+
+          leaf msrpc-alg-support {
+               type boolean;
+               description
+                  "Indicates whether MS-RPC ALG is supported";
+          }
+
+          leaf netbios-alg-support {
+               type boolean;
+               description
+                  "Indicates whether NetBIOS ALG is supported";
+          }
+
+          leaf rcmd-alg-support {
+               type boolean;
+               description
+                  "Indicates whether rcmd ALG is supported";
+          }
+
+          leaf ldap-alg-support {
+               type boolean;
+               description
+                  "Indicates whether LDAP ALG is supported";
+          }
+
+          leaf sip-alg-support {
+               type boolean;
+               description
+                  "Indicates whether SIP ALG is supported";
+          }
+
+          leaf rtsp-alg-support {
+               type boolean;
+               description
+                  "Indicates whether RTSP ALG is supported";
+          }
+
+          leaf h323-alg-support {
+               type boolean;
+               description
+                  "Indicates whether H323 ALG is supported";
+          }
+
+          leaf paired-address-pooling-support {
+               type boolean;
+               description
+                "Indicates whether paired-address-pooling is
+                supported";
+          }
+
+          leaf endpoint-independent-mapping-support {
+                type boolean;
+                description
+                "Indicates whether endpoint-independent-mapping
+                in Section 4 of RFC 4787 is supported.";
+          }
+
+          leaf address-dependent-mapping-support {
+               type boolean;
+               description
+               "Indicates whether endpoint-independent-mapping
+               in Section 4 of RFC 4787 is supported.";
+          }
+
+          leaf address-and-port-dependent-mapping-support {
+               type boolean;
+               description
+               "Indicates whether endpoint-independent-mapping in
+               section 4 of RFC 4787 is supported.";
+         }
+
+         leaf endpoint-independent-filtering-support {
+               type boolean;
+              description
+               "Indicates whether endpoint-independent-mapping in
+               section 5 of RFC 4787 is supported.";
+          }
+
+          leaf address-dependent-filtering {
+              type boolean;
+              description
+              "Indicates whether endpoint-independent-mapping in
+              section 5 of RFC 4787 is supported.";
+          }
+
+          leaf address-and-port-dependent-filtering {
+              type boolean;
+              description
+              "Indicates whether endpoint-independent-mapping in
+              section 5 of RFC 4787 is supported.";
+          }
+
+          leaf stealth-mode-support {
+              type boolean;
+              description
+              "Indicates whether to respond for unsolicited
+              traffic.";
+          }
+
+                 }
+
+                  container nat-current-config {
+                     description
+                          "current config";
+
+                     uses nat-parameters;
+                  }
+
+                  container mapping-table {
+                      description
+                          "Mapping table";
+                      list mapping-entry {
+                          key "index";
+                          description
+                        "mapping entry";
+                          uses mapping-entry;
+                      }
+                  }
+
+                  container statistics {
+                       description
+                         "Statistics related to the NAT instance";
+
+                       leaf total-mappings {
+                            type uint32;
+                            description
+                             "Total number of NAT Mappings present
+                             at the time. This includes all the
+                             static and dynamic mappings";
+                       }
+                       leaf total-tcp-mappings {
+                            type uint32;
+                            description
+                             "Total number of TCP Mappings present
+                             at the time.";
+                       }
+                       leaf total-udp-mappings {
+                            type uint32;
+                            description
+                             "Total number of UDP Mappings present
+                             at the time.";
+                       }
+                       leaf total-icmp-mappings {
+                            type uint32;
+                            description
+                             "Total number of ICMP Mappings present
+                             at the time.";
+                       }
+                       container pool-stats {
+                            description
+                               "Statistics related to Pool usage";
+                            leaf pool-id {
+                                 type uint32;
+                                 description
+                                  "Unique Identifier that represents
+                                  a pool";
+                            }
+                            leaf address-allocated {
+                                 type uint32;
+                                 description
+                                    "Number of allocated addresses in
+                                    the pool";
+                            }
+                            leaf address-free {
+                                 type uint32;
+                                 description
+                                   "Number of free addresses in
+                                   the pool.The sum of free
+                                   addresses and allocated
+                                   addresses are the total
+                                   addresses in the pool";
+                            }
+                            container port-stats {
+                                 description
+                                   "Statistics related to port
+                                   usage.";
+
+                                 leaf ports-allocated {
+                                      type uint32;
+                                      description
+                                         "Number of allocated ports
+                                         in the pool";
+                                 }
+
+                                 leaf ports-free {
+                                      type uint32;
+                                      description
+                                         "Number of free addresses
+                                         in the pool";
+                                 }
+                            }
+                       }
+                  } //statistics
+              } //nat-instance
+          } //nat-instances
+     } //nat-state
+     /*
+      * Notifications
+      */
+     notification nat-event {
+          description
+           "Notifications must be generated when the defined
+            high/low threshold is reached. Related configuration
+            parameters must be provided to trigger
+            the notifications.";
+
+          leaf id {
+                type leafref {
+                path
+                "/nat-state/nat-instances/"
+               + "nat-instance/id";
+                }
+                description
+                 "NAT instance ID.";
+          }
+
+          leaf notify-pool-threshold {
+               type percent;
+                // mandatory true;
+                  description
+                       "A treshhold has been fired.";
+          }
+     }
+} //module nat
\ No newline at end of file
diff --git a/nat/nat-api/src/main/yang/interface-nat.yang b/nat/nat-api/src/main/yang/interface-nat.yang
new file mode 100644 (file)
index 0000000..43a6bf0
--- /dev/null
@@ -0,0 +1,44 @@
+module interface-nat {
+  yang-version 1;
+  namespace "urn:opendaylight:params:xml:ns:yang:interface:nat";
+  prefix "ifc-nat";
+
+  revision "2016-12-14" {
+    description "Initial revision of v3po model";
+  }
+
+  import ietf-interfaces {
+    prefix "if";
+  }
+  import ietf-nat {
+    prefix "nat";
+  }
+  import yang-ext {
+    prefix "ext";
+  }
+
+  description "Augmentations to interfaces model to connect interfaces with nat configuration";
+
+  grouping interface-nat-attributes {
+    container nat {
+        container inbound {
+            presence "Enables inbound NAT";
+        }
+        container outbound {
+            presence "Enables outbound NAT";
+        }
+    }
+  }
+
+  augment /if:interfaces/if:interface {
+    ext:augment-identifier "nat-interface-augmentation";
+
+    uses interface-nat-attributes;
+  }
+
+  augment /if:interfaces-state/if:interface {
+    ext:augment-identifier "nat-interface-state-augmentation";
+
+    uses interface-nat-attributes;
+  }
+}
\ No newline at end of file
diff --git a/nat/nat-api/src/main/yang/nat-context.yang b/nat/nat-api/src/main/yang/nat-context.yang
new file mode 100644 (file)
index 0000000..dd17ed5
--- /dev/null
@@ -0,0 +1,64 @@
+module nat-context {
+    yang-version 1;
+    namespace "urn:honeycomb:params:xml:ns:yang:nat:context";
+    prefix "nc";
+
+    description "Context for nat mapping";
+
+    revision "2016-12-14" {
+        description "Initial revision.";
+    }
+
+    import ietf-inet-types {
+       prefix "inet";
+    }
+
+    import naming-context {
+       prefix "nc";
+    }
+
+    import yang-ext {
+       prefix "ext";
+    }
+
+    grouping mapping-entry-context-attributes {
+        container nat-mapping-entry-context {
+            list nat-instance {
+                key "id";
+
+                leaf id {
+                    type uint32;
+                    description "ID of the NAT instance from ietf-nat. Maps to VRF-ID in VPP";
+                }
+
+                container mapping-table {
+                    list mapping-entry {
+
+                        key "internal external";
+                        unique "index";
+
+                        leaf internal {
+                            type inet:ip-address;
+                            description "Local IP address set in VPP";
+                        }
+
+                        leaf external {
+                            type inet:ip-address;
+                            description "Extarnal IP address set in VPP";
+                        }
+
+                        leaf index {
+                            type uint32;
+                            description "ID of the NAT's mapping entry from ietf-nat";
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    augment /nc:contexts {
+        ext:augment-identifier "nat-mapping-entry-ctx-augmentation";
+        uses mapping-entry-context-attributes;
+    }
+}
\ No newline at end of file
diff --git a/nat/nat2vpp/asciidoc/Readme.adoc b/nat/nat2vpp/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..7324011
--- /dev/null
@@ -0,0 +1,3 @@
+= nat2vpp
+
+Uses jvpp-snat to work with VPP's SNAT plugin.
\ No newline at end of file
diff --git a/nat/nat2vpp/pom.xml b/nat/nat2vpp/pom.xml
new file mode 100644 (file)
index 0000000..ba8204d
--- /dev/null
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>io.fd.honeycomb.vpp</groupId>
+        <artifactId>vpp-impl-parent</artifactId>
+        <relativePath>../../vpp-common/vpp-impl-parent</relativePath>
+        <version>1.16.12-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>io.fd.honeycomb.nat</groupId>
+    <artifactId>nat2vpp</artifactId>
+    <name>${project.artifactId}</name>
+    <version>1.16.12-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <honeycomb.infra.version>1.16.12-SNAPSHOT</honeycomb.infra.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>nat-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!--VPP common-->
+        <dependency>
+            <groupId>io.fd.honeycomb.vpp</groupId>
+            <artifactId>vpp-translate-utils</artifactId>
+        </dependency>
+
+        <!-- JVPP -->
+        <dependency>
+            <groupId>io.fd.vpp</groupId>
+            <artifactId>jvpp-registry</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.vpp</groupId>
+            <artifactId>jvpp-snat</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+        <!-- Honeycomb infrastructure-->
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>minimal-distribution</artifactId>
+            <version>${honeycomb.infra.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>translate-api</artifactId>
+            <version>${honeycomb.infra.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>translate-spi</artifactId>
+            <version>${honeycomb.infra.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>cfg-init</artifactId>
+            <version>${honeycomb.infra.version}</version>
+        </dependency>
+
+        <!-- DI -->
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.jmob</groupId>
+            <artifactId>guice.conf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject.extensions</groupId>
+            <artifactId>guice-multibindings</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject.extensions</groupId>
+            <artifactId>guice-testlib</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/NatModule.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/NatModule.java
new file mode 100644 (file)
index 0000000..b33f900
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.Multibinder;
+import io.fd.honeycomb.data.init.DataTreeInitializer;
+import io.fd.honeycomb.nat.init.NatInitializer;
+import io.fd.honeycomb.nat.jvpp.JVppSnatProvider;
+import io.fd.honeycomb.nat.read.NatReaderFactory;
+import io.fd.honeycomb.nat.read.ifc.IfcNatReaderFactory;
+import io.fd.honeycomb.nat.util.MappingEntryContext;
+import io.fd.honeycomb.nat.write.NatWriterFactory;
+import io.fd.honeycomb.nat.write.ifc.IfcNatWriterFactory;
+import io.fd.honeycomb.translate.read.ReaderFactory;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Module class instantiating nat plugin components.
+ */
+public final class NatModule extends AbstractModule {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NatModule.class);
+    private final Class<? extends Provider<FutureJVppSnatFacade>> jvppSnatProviderClass;
+
+    public NatModule() {
+        this(JVppSnatProvider.class);
+    }
+
+    @VisibleForTesting
+    NatModule(Class<? extends Provider<FutureJVppSnatFacade>> jvppSnatProvider) {
+        this.jvppSnatProviderClass = jvppSnatProvider;
+    }
+
+    @Override
+    protected void configure() {
+        // Mapping entry context util
+        bind(MappingEntryContext.class).toInstance(new MappingEntryContext());
+
+        LOG.debug("Installing NAT module");
+
+        // Bind to Plugin's JVPP
+        bind(FutureJVppSnatFacade.class).toProvider(jvppSnatProviderClass).in(Singleton.class);
+
+        final Multibinder<ReaderFactory> readBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class);
+        readBinder.addBinding().to(IfcNatReaderFactory.class).in(Singleton.class);
+        readBinder.addBinding().to(NatReaderFactory.class).in(Singleton.class);
+
+        final Multibinder<WriterFactory> writeBinder = Multibinder.newSetBinder(binder(), WriterFactory.class);
+        writeBinder.addBinding().to(IfcNatWriterFactory.class).in(Singleton.class);
+        writeBinder.addBinding().to(NatWriterFactory.class).in(Singleton.class);
+
+        Multibinder.newSetBinder(binder(), DataTreeInitializer.class)
+                .addBinding().to(NatInitializer.class).in(Singleton.class);
+        LOG.info("Module NAT successfully configured");
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/init/NatInitializer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/init/NatInitializer.java
new file mode 100644 (file)
index 0000000..37c4561
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.init;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import io.fd.honeycomb.data.init.AbstractDataTreeConverter;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatConfig;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatConfigBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.NatInstancesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstanceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.MappingTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Initialize nat-config from nat-state.
+ */
+public final class NatInitializer extends AbstractDataTreeConverter<NatState, NatConfig> {
+
+    @Inject
+    public NatInitializer(@Named("honeycomb-initializer") @Nonnull final DataBroker bindingDataBroker) {
+        super(bindingDataBroker, InstanceIdentifier.create(NatState.class), InstanceIdentifier.create(NatConfig.class));
+    }
+
+    @Override
+    public NatConfig convert(final NatState operationalData) {
+        return new NatConfigBuilder()
+                .setNatInstances(new NatInstancesBuilder()
+                        .setNatInstance(operationalData.getNatInstances().getNatInstance().stream()
+                                .map(operNatInstance -> new NatInstanceBuilder()
+                                        .setId(operNatInstance.getId())
+                                        .setMappingTable(new MappingTableBuilder()
+                                                .setMappingEntry(
+                                                        operNatInstance.getMappingTable().getMappingEntry().stream()
+                                                                .map(operEntry -> new MappingEntryBuilder(operEntry).build())
+                                                                .collect(Collectors.toList()))
+                                                .build())
+                                        .build())
+                                .collect(Collectors.toList()))
+                        .build())
+                .build();
+        // TODO implement initialization for nat inbound/outbound NAT feature after VPP-459
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/jvpp/JVppSnatProvider.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/jvpp/JVppSnatProvider.java
new file mode 100755 (executable)
index 0000000..b836656
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.jvpp;
+
+import com.google.inject.Inject;
+import io.fd.honeycomb.infra.distro.ProviderTrait;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.snat.JVppSnatImpl;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides future API for jvpp-nsh plugin. Must be a singleton due to shutdown hook usage.
+ * Registers shutdown hook to free plugin's resources on shutdown.
+ */
+public final class JVppSnatProvider extends ProviderTrait<FutureJVppSnatFacade> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JVppSnatProvider.class);
+
+    @Inject
+    private JVppRegistry registry;
+
+    @Override
+    protected FutureJVppSnatFacade create() {
+        try {
+            final JVppSnatImpl jvppSnat = new JVppSnatImpl();
+            // Free jvpp-nsh plugin's resources on shutdown
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    LOG.info("Unloading jvpp-snat plugin");
+                    jvppSnat.close();
+                    LOG.info("Successfully unloaded jvpp-snat plugin");
+                }
+            });
+
+            LOG.info("Successfully loaded jvpp-snat plugin");
+            return new FutureJVppSnatFacade(registry, jvppSnat);
+        } catch (IOException e) {
+            throw new IllegalStateException("Unable to open VPP management connection", e);
+        }
+    }
+}
+
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/MappingEntryCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/MappingEntryCustomizer.java
new file mode 100644 (file)
index 0000000..469d361
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.read;
+
+import io.fd.honeycomb.nat.util.MappingEntryContext;
+import io.fd.honeycomb.translate.read.ReadContext;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor;
+import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
+import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetails;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetailsReplyDump;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDump;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.mapping.entry.ExternalSrcPortBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.mapping.entry.InternalSrcPortBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.MappingTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.mapping.table.MappingEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.mapping.table.MappingEntryKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.port.number.port.type.SinglePortNumberBuilder;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MappingEntryCustomizer implements Ipv4Translator,
+        ListReaderCustomizer<MappingEntry, MappingEntryKey, MappingEntryBuilder> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MappingEntryCustomizer.class);
+
+    private final DumpCacheManager<SnatStaticMappingDetailsReplyDump, Void> dumpCacheManager;
+    private final MappingEntryContext mappingEntryContext;
+
+    public MappingEntryCustomizer(final DumpCacheManager<SnatStaticMappingDetailsReplyDump, Void> dumpCacheManager,
+                                  final MappingEntryContext mappingEntryContext) {
+        this.dumpCacheManager = dumpCacheManager;
+        this.mappingEntryContext = mappingEntryContext;
+    }
+
+    @Nonnull
+    @Override
+    public MappingEntryBuilder getBuilder(@Nonnull final InstanceIdentifier<MappingEntry> id) {
+        return new MappingEntryBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
+                                      @Nonnull final MappingEntryBuilder builder, @Nonnull final ReadContext ctx)
+            throws ReadFailedException {
+        LOG.trace("Reading current attributes for mapping-entry: {}", id);
+
+        final int idx = id.firstKeyOf(MappingEntry.class).getIndex().intValue();
+        final int natInstanceId = id.firstKeyOf(NatInstance.class).getId().intValue();
+        final List<SnatStaticMappingDetails> details =
+                dumpCacheManager.getDump(id, getClass().getName(), ctx.getModificationCache(), null)
+                        .or(new SnatStaticMappingDetailsReplyDump()).snatStaticMappingDetails;
+        final SnatStaticMappingDetails snatStaticMappingDetails =
+                mappingEntryContext.findDetails(details, natInstanceId, idx, ctx.getMappingContext());
+
+        builder.setIndex((long) idx);
+        builder.setType(
+                org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.MappingEntry.Type.Static);
+        // Snat only supports ipv4 for now
+        builder.setExternalSrcAddress(arrayToIpv4AddressNoZoneReversed(snatStaticMappingDetails.externalIpAddress));
+        builder.setInternalSrcAddress(
+                new IpAddress(arrayToIpv4AddressNoZoneReversed(snatStaticMappingDetails.localIpAddress)));
+
+        if (snatStaticMappingDetails.addrOnly == 0) {
+            builder.setExternalSrcPort(new ExternalSrcPortBuilder()
+                    .setPortType(new SinglePortNumberBuilder().setSinglePortNumber(new PortNumber(
+                            (int) snatStaticMappingDetails.externalPort))
+                            .build())
+                    .build());
+            builder.setInternalSrcPort(new InternalSrcPortBuilder()
+                    .setPortType(new SinglePortNumberBuilder().setSinglePortNumber(new PortNumber(
+                            (int) snatStaticMappingDetails.localPort))
+                            .build())
+                    .build());
+        }
+
+        LOG.trace("Mapping-entry read as: {}", builder);
+    }
+
+    @Nonnull
+    @Override
+    public List<MappingEntryKey> getAllIds(@Nonnull final InstanceIdentifier<MappingEntry> id,
+                                           @Nonnull final ReadContext context) throws ReadFailedException {
+        final Long natInstanceId = id.firstKeyOf(NatInstance.class).getId();
+        LOG.trace("Listing IDs for all mapping-entries within nat-instance(vrf):{}", natInstanceId);
+
+        final List<MappingEntryKey> entryKeys =
+                dumpCacheManager.getDump(id, getClass().getName(), context.getModificationCache(), null)
+                        .or(new SnatStaticMappingDetailsReplyDump()).snatStaticMappingDetails.stream()
+                        .filter(detail -> natInstanceId == detail.vrfId)
+                        .map(detail -> mappingEntryContext
+                                .getStoredOrArtificialIndex(natInstanceId, detail, context.getMappingContext()))
+                        .map(MappingEntryKey::new)
+                        .collect(Collectors.toList());
+        LOG.debug("List of mapping-entry keys within nat-instance(vrf):{} : {}", natInstanceId, entryKeys);
+
+        return entryKeys;
+    }
+
+    @Override
+    public void merge(@Nonnull final Builder<? extends DataObject> builder,
+                      @Nonnull final List<MappingEntry> readData) {
+        ((MappingTableBuilder) builder).setMappingEntry(readData);
+    }
+
+    static final class MappingEntryDumpExecutor
+            implements EntityDumpExecutor<SnatStaticMappingDetailsReplyDump, Void>, JvppReplyConsumer {
+
+        private final FutureJVppSnatFacade jvppSnat;
+
+        MappingEntryDumpExecutor(final FutureJVppSnatFacade jvppSnat) {
+            this.jvppSnat = jvppSnat;
+        }
+
+        @Nonnull
+        @Override
+        public SnatStaticMappingDetailsReplyDump executeDump(final InstanceIdentifier<?> identifier, final Void params)
+                throws ReadFailedException {
+            return getReplyForRead(jvppSnat.snatStaticMappingDump(new SnatStaticMappingDump()).toCompletableFuture(),
+                    identifier);
+        }
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/NatInstanceCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/NatInstanceCustomizer.java
new file mode 100644 (file)
index 0000000..cb639b9
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.read;
+
+import io.fd.honeycomb.translate.read.ReadContext;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetailsReplyDump;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.NatInstancesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.NatInstanceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.NatInstanceKey;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Nat instance ID is mapped to VRF-ID in VPP.
+ */
+final class NatInstanceCustomizer implements ListReaderCustomizer<NatInstance, NatInstanceKey, NatInstanceBuilder> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NatInstanceCustomizer.class);
+
+    private final DumpCacheManager<SnatStaticMappingDetailsReplyDump, Void> dumpCacheManager;
+
+    public NatInstanceCustomizer(final DumpCacheManager<SnatStaticMappingDetailsReplyDump, Void> dumpCacheManager) {
+        this.dumpCacheManager = dumpCacheManager;
+    }
+
+    @Nonnull
+    @Override
+    public NatInstanceBuilder getBuilder(@Nonnull final InstanceIdentifier<NatInstance> id) {
+        return new NatInstanceBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<NatInstance> id,
+                                      @Nonnull final NatInstanceBuilder builder, @Nonnull final ReadContext ctx)
+            throws ReadFailedException {
+        LOG.trace("Reading current attributes for nat-instance: {}", id);
+        builder.setId(id.firstKeyOf(NatInstance.class).getId());
+    }
+
+    @Nonnull
+    @Override
+    public List<NatInstanceKey> getAllIds(@Nonnull final InstanceIdentifier<NatInstance> id,
+                                          @Nonnull final ReadContext context) throws ReadFailedException {
+        LOG.trace("Listing IDs for all nat-instances");
+        final List<NatInstanceKey> vrfIds =
+                dumpCacheManager.getDump(id, getClass().getName(), context.getModificationCache(), null)
+                        .or(new SnatStaticMappingDetailsReplyDump()).snatStaticMappingDetails.stream()
+                        .map(detail -> detail.vrfId)
+                        .map(vrfId -> new NatInstanceKey((long)vrfId))
+                        .collect(Collectors.toList());
+
+        LOG.debug("List of nat-instance keys (vrf-ids): {}", vrfIds);
+        return vrfIds;
+    }
+
+    @Override
+    public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<NatInstance> readData) {
+        ((NatInstancesBuilder) builder).setNatInstance(readData);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/NatReaderFactory.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/NatReaderFactory.java
new file mode 100644 (file)
index 0000000..949009c
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.read;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import io.fd.honeycomb.nat.util.MappingEntryContext;
+import io.fd.honeycomb.translate.impl.read.GenericListReader;
+import io.fd.honeycomb.translate.read.ReaderFactory;
+import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetailsReplyDump;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.mapping.entry.ExternalSrcPort;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.mapping.entry.InternalSrcPort;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.NatInstances;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.NatInstancesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.MappingTable;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.MappingTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.state.nat.instances.nat.instance.mapping.table.MappingEntry;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NatReaderFactory implements ReaderFactory {
+
+    private static final InstanceIdentifier<NatState> NAT_OPER_ID = InstanceIdentifier.create(NatState.class);
+    private static final InstanceIdentifier<NatInstances> NAT_INSTANCES_ID = NAT_OPER_ID.child(NatInstances.class);
+    private static final InstanceIdentifier<NatInstance> NAT_INSTANCE_ID = NAT_INSTANCES_ID.child(NatInstance.class);
+    private static final InstanceIdentifier<MappingTable> MAP_TABLE_ID = NAT_INSTANCE_ID.child(MappingTable.class);
+    private static final InstanceIdentifier<MappingEntry> MAP_ENTRY_ID = MAP_TABLE_ID.child(MappingEntry.class);
+
+    private final MappingEntryContext mappingEntryContext;
+    private final DumpCacheManager<SnatStaticMappingDetailsReplyDump, Void> dumpCacheManager;
+
+    @Inject
+    public NatReaderFactory(final FutureJVppSnatFacade jvppSnat,
+                            final MappingEntryContext mappingEntryContext) {
+        this.mappingEntryContext = mappingEntryContext;
+        this.dumpCacheManager = new DumpCacheManager.DumpCacheManagerBuilder<SnatStaticMappingDetailsReplyDump, Void>()
+                .withExecutor(new MappingEntryCustomizer.MappingEntryDumpExecutor(jvppSnat))
+                .build();
+    }
+
+    @Override
+    public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) {
+        registry.addStructuralReader(NAT_OPER_ID, NatStateBuilder.class);
+        registry.addStructuralReader(NAT_INSTANCES_ID, NatInstancesBuilder.class);
+        registry.add(new GenericListReader<>(NAT_INSTANCE_ID, new NatInstanceCustomizer(dumpCacheManager)));
+        registry.addStructuralReader(MAP_TABLE_ID, MappingTableBuilder.class);
+        registry.subtreeAdd(Sets.newHashSet(InstanceIdentifier.create(MappingEntry.class).child(ExternalSrcPort.class),
+                InstanceIdentifier.create(MappingEntry.class).child(InternalSrcPort.class)),
+                new GenericListReader<>(MAP_ENTRY_ID,
+                        new MappingEntryCustomizer(dumpCacheManager, mappingEntryContext)));
+
+        // TODO VPP-453 Implement address range read
+
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/IfcNatReaderFactory.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/IfcNatReaderFactory.java
new file mode 100644 (file)
index 0000000..293a3df
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.read.ifc;
+
+
+import io.fd.honeycomb.translate.impl.read.GenericReader;
+import io.fd.honeycomb.translate.read.ReaderFactory;
+import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceStateAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceStateAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.Nat;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Inbound;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Outbound;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Factory producing readers for nat plugin's data.
+ */
+public final class IfcNatReaderFactory implements ReaderFactory {
+
+    private static final InstanceIdentifier<Interface>
+            IFC_ID = InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
+    private static final InstanceIdentifier<NatInterfaceStateAugmentation> NAT_AUG_ID =
+            IFC_ID.augmentation(NatInterfaceStateAugmentation.class);
+    private static final InstanceIdentifier<Nat> NAT_AUG_CONTAINER_ID = NAT_AUG_ID.child(Nat.class);
+
+    @Override
+    public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) {
+        registry.addStructuralReader(NAT_AUG_ID, NatInterfaceStateAugmentationBuilder.class);
+        registry.addStructuralReader(NAT_AUG_CONTAINER_ID, NatBuilder.class);
+
+        registry.addAfter(
+                new GenericReader<>(NAT_AUG_CONTAINER_ID.child(Inbound.class), new InterfaceInboundNatCustomizer()), IFC_ID);
+        registry.addAfter(
+                new GenericReader<>(NAT_AUG_CONTAINER_ID.child(Outbound.class), new InterfaceOutboundNatCustomizer()), IFC_ID);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizer.java
new file mode 100644 (file)
index 0000000..e1ebdd6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.read.ifc;
+
+import io.fd.honeycomb.translate.read.ReadContext;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Inbound;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.InboundBuilder;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class InterfaceInboundNatCustomizer implements ReaderCustomizer<Inbound, InboundBuilder> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InterfaceInboundNatCustomizer.class);
+
+    @Nonnull
+    @Override
+    public InboundBuilder getBuilder(@Nonnull final InstanceIdentifier<Inbound> id) {
+        return new InboundBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Inbound> id,
+                                      @Nonnull final InboundBuilder builder,
+                                      @Nonnull final ReadContext ctx)
+            throws ReadFailedException {
+        // FIXME HONEYCOMB-248 VPP-459 Implement when read is available in VPP/Snat
+        LOG.debug("Unable to read Inbound NAT feature state for an interface");
+    }
+
+    @Override
+    public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Inbound readValue) {
+        ((NatBuilder) parentBuilder).setInbound(readValue);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizer.java
new file mode 100644 (file)
index 0000000..fe28584
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.read.ifc;
+
+import io.fd.honeycomb.translate.read.ReadContext;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Outbound;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.OutboundBuilder;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class InterfaceOutboundNatCustomizer implements ReaderCustomizer<Outbound, OutboundBuilder> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InterfaceOutboundNatCustomizer.class);
+
+    @Nonnull
+    @Override
+    public OutboundBuilder getBuilder(@Nonnull final InstanceIdentifier<Outbound> id) {
+        return new OutboundBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Outbound> id,
+                                      @Nonnull final OutboundBuilder builder, @Nonnull final ReadContext ctx)
+            throws ReadFailedException {
+        // FIXME HONEYCOMB-248 VPP-459 Implement when read is available in VPP/Snat
+        LOG.debug("Unable to read Outbound NAT feature state for an interface");
+    }
+
+    @Override
+    public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Outbound readValue) {
+        ((NatBuilder) parentBuilder).setOutbound(readValue);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/util/MappingEntryContext.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/util/MappingEntryContext.java
new file mode 100644 (file)
index 0000000..0f2df7e
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetails;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.NatMappingEntryCtxAugmentation;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.NatMappingEntryContext;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.NatInstanceKey;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.MappingTable;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntryKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Context tracker for Nat Mapping entries.
+ */
+@ThreadSafe
+public class MappingEntryContext implements Ipv4Translator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MappingEntryContext.class);
+
+    /**
+     * Add mapping entry to index mapping to context.
+     */
+    public synchronized void addEntry(final long natInstanceId,
+                                      final long entryId,
+                                      @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
+                                      @Nonnull final MappingContext mappingContext) {
+        final InstanceIdentifier<MappingEntry> id = getId(natInstanceId, entryToKey(entry));
+        checkArgument(!containsEntry(natInstanceId, entry, mappingContext), "Mapping for %s already present", id);
+        mappingContext.put(id, toCtxMapEntry(entry, entryId));
+    }
+
+    /**
+     * Check whether mapping entry to index mapping already exists in context.
+     */
+    public synchronized boolean containsEntry(final long natInstanceId,
+                                              @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
+                                              @Nonnull final MappingContext mappingContext) {
+        final InstanceIdentifier<MappingEntry> id = getId(natInstanceId, entryToKey(entry));
+        return mappingContext.read(id).isPresent();
+    }
+
+    @VisibleForTesting
+    static InstanceIdentifier<MappingEntry> getId(final Long natInstanceId, final MappingEntryKey key) {
+        return getTableId(natInstanceId).child(MappingEntry.class, key);
+    }
+
+    @VisibleForTesting
+    static InstanceIdentifier<MappingTable> getTableId(final long natInstanceId) {
+        return InstanceIdentifier.create(Contexts.class)
+                .augmentation(NatMappingEntryCtxAugmentation.class)
+                .child(NatMappingEntryContext.class)
+                .child(NatInstance.class, new NatInstanceKey(natInstanceId))
+                .child(MappingTable.class);
+    }
+
+    @VisibleForTesting
+    static MappingEntryKey entryToKey(
+            final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry) {
+        // Only IPv4
+        return new MappingEntryKey(new IpAddress(entry.getExternalSrcAddress()), entry.getInternalSrcAddress());
+    }
+
+    private MappingEntryKey entryToKey(final SnatStaticMappingDetails entry) {
+        // Only IPv4
+        return new MappingEntryKey(
+                new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(entry.externalIpAddress))),
+                new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(entry.localIpAddress))));
+    }
+
+    private boolean equalEntries(final SnatStaticMappingDetails detail, final MappingEntry ctxMappingEntry) {
+        final IpAddress internalAddrFromDetails =
+                new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(detail.localIpAddress)));
+        // Only IPv4
+        if (!ctxMappingEntry.getInternal().equals(internalAddrFromDetails)) {
+            return false;
+        }
+        // Only IPv4
+        final IpAddress externalAddrFromDetails =
+                new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(detail.externalIpAddress)));
+        if (!ctxMappingEntry.getExternal().equals(externalAddrFromDetails)) {
+            return false;
+        }
+        return true;
+    }
+
+    @VisibleForTesting
+    static MappingEntry toCtxMapEntry(
+            @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
+            final long entryId) {
+        return new MappingEntryBuilder()
+                .setKey(entryToKey(entry))
+                .setIndex(entryId)
+                .build();
+    }
+
+    private MappingEntry toCtxMapEntry(@Nonnull final SnatStaticMappingDetails details, final long entryId) {
+        return new MappingEntryBuilder()
+                .setKey(entryToKey(details))
+                .setIndex(entryId)
+                .build();
+    }
+
+    /**
+     * Delete mapping of mapping entry to index from context.
+     */
+    public synchronized void removeEntry(final long natInstanceId,
+                                         @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
+                                         @Nonnull final MappingContext mappingContext) {
+        mappingContext.delete(getId(natInstanceId, entryToKey(entry)));
+    }
+
+    /**
+     * Find specific details in provided collection identified with provided index.
+     */
+    public synchronized SnatStaticMappingDetails findDetails(@Nonnull final List<SnatStaticMappingDetails> details,
+                                                             final long natInstanceId, final long idx,
+                                                             @Nonnull final MappingContext mappingContext) {
+        // Find mapping entry for Index
+        final MappingEntry ctxMappingEntry = mappingContext.read(getTableId(natInstanceId))
+                .transform(MappingTable::getMappingEntry)
+                .or(Collections.emptyList())
+                .stream()
+                .filter(entry -> entry.getIndex() == idx)
+                .findFirst()
+                .orElseThrow(() -> new IllegalStateException("Unable to find context mapping for nat-instance: "
+                        + natInstanceId + " and ID: " + idx));
+
+        // Find which details matches the context stored entry under index
+        return details.stream()
+                .filter(detail -> equalEntries(detail, ctxMappingEntry))
+                .findFirst()
+                .orElseThrow(() -> new IllegalStateException("Unable to match mapping for nat-instance: "
+                        + natInstanceId + " and match: " + ctxMappingEntry + " in: " + details));
+    }
+
+    /**
+     * Get index for a mapping entry details or create an artificial one.
+     */
+    public synchronized long getStoredOrArtificialIndex(final Long natInstanceId,
+                                                        @Nonnull final SnatStaticMappingDetails details,
+                                                        @Nonnull final MappingContext mappingContext) {
+        return mappingContext.read(getId(natInstanceId, entryToKey(details)))
+                .transform(MappingEntry::getIndex)
+                .or(() -> getArtificialId(details, natInstanceId, mappingContext));
+    }
+
+    /**
+     * Get index for a stored mapping entry.
+     */
+    public synchronized Optional<Long> getStoredIndex(final long natInstanceId,
+                                                      @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
+                                                      @Nonnull final MappingContext mappingContext) {
+        return mappingContext.read(getId(natInstanceId, entryToKey(entry)))
+                .transform(MappingEntry::getIndex);
+    }
+
+    private long getArtificialId(final SnatStaticMappingDetails details, final Long natInstanceId,
+                                 final MappingContext mappingContext) {
+        LOG.trace("Assigning artificial ID for {}", details);
+        final long artificialIdx = findFreeIndex(natInstanceId, mappingContext);
+        LOG.debug("Artificial ID for {} assigned as: {}", details, artificialIdx);
+        mappingContext.put(getId(natInstanceId, entryToKey(details)), toCtxMapEntry(details, artificialIdx));
+        return artificialIdx;
+    }
+
+    private long findFreeIndex(final long natInstanceId, final MappingContext mappingContext) {
+        return mappingContext.read(getTableId(natInstanceId))
+                .transform(MappingTable::getMappingEntry)
+                .or(Collections.emptyList())
+                .stream()
+                .map(MappingEntry::getIndex)
+                .max(Comparator.naturalOrder())
+                .map(i -> i + 1)
+                .orElse(0L);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/MappingEntryCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/MappingEntryCustomizer.java
new file mode 100644 (file)
index 0000000..7856069
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.nat.util.MappingEntryContext;
+import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
+import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.snat.dto.SnatAddStaticMapping;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.port.number.PortType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.port.number.port.type.SinglePortNumber;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry, MappingEntryKey>,
+        JvppReplyConsumer, Ipv4Translator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MappingEntryCustomizer.class);
+
+    private final FutureJVppSnatFacade jvppSnat;
+    private final MappingEntryContext mappingEntryContext;
+
+    MappingEntryCustomizer(final FutureJVppSnatFacade jvppSnat, final MappingEntryContext mappingEntryContext) {
+        this.jvppSnat = jvppSnat;
+        this.mappingEntryContext = mappingEntryContext;
+    }
+
+    @Override
+    public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
+                                       @Nonnull final MappingEntry dataAfter,
+                                       @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        // Only static mapping supported by SNAT for now
+        checkArgument(dataAfter.getType() ==
+                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.MappingEntry.Type.Static,
+                "Only static NAT entries are supported currently. Trying to write: %s entry", dataAfter.getType());
+        final Long natInstanceId = id.firstKeyOf(NatInstance.class).getId();
+        final Long mappingEntryId = id.firstKeyOf(MappingEntry.class).getIndex();
+        LOG.debug("Writing mapping entry: {} for nat-instance(vrf): {}", natInstanceId, mappingEntryId);
+
+        final SnatAddStaticMapping request = getRequest(id, dataAfter, natInstanceId, true);
+        getReplyForWrite(jvppSnat.snatAddStaticMapping(request).toCompletableFuture(), id);
+
+        // Store context mapping only if not already present under the same exact mapping
+        synchronized (mappingEntryContext) {
+            if (shouldStoreContextMapping(natInstanceId, mappingEntryId, dataAfter, writeContext)) {
+                mappingEntryContext
+                        .addEntry(natInstanceId, mappingEntryId, dataAfter, writeContext.getMappingContext());
+            }
+        }
+        LOG.trace("Mapping entry: {} for nat-instance(vrf): {} written successfully", request.vrfId, id);
+    }
+
+    /**
+     * Check whether entry is already stored in context under the same index.
+     *
+     * @return true if it's not yet stored under same index, false otherwise.
+     */
+    private boolean shouldStoreContextMapping(final long natInstanceId, final long mappingEntryId,
+                                              final MappingEntry dataAfter,
+                                              final WriteContext writeCtx) {
+        if (!mappingEntryContext.containsEntry(natInstanceId, dataAfter, writeCtx.getMappingContext())) {
+            return true;
+        }
+
+        final Optional<Long> storedIndex =
+                mappingEntryContext.getStoredIndex(natInstanceId, dataAfter, writeCtx.getMappingContext());
+        if (!storedIndex.isPresent()) {
+            return true;
+        }
+
+        if (storedIndex.get() != mappingEntryId) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
+                                        @Nonnull final MappingEntry dataBefore,
+                                        @Nonnull final MappingEntry dataAfter,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
+                new UnsupportedOperationException("Mapping entry update not supported"));
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
+                                        @Nonnull final MappingEntry dataBefore,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        final long natInstanceId = id.firstKeyOf(NatInstance.class).getId();
+        final MappingEntryKey mappingEntryKey = id.firstKeyOf(MappingEntry.class);
+        LOG.debug("Deleting mapping entry: {} for nat-instance(vrf): {}", natInstanceId, mappingEntryKey);
+
+        final SnatAddStaticMapping request = getRequest(id, dataBefore, natInstanceId, false);
+        getReplyForWrite(jvppSnat.snatAddStaticMapping(request).toCompletableFuture(), id);
+        mappingEntryContext.removeEntry(natInstanceId, dataBefore, writeContext.getMappingContext());
+        LOG.trace("Mapping entry: {} for nat-instance(vrf): {} deleted successfully", request.vrfId, id);
+    }
+
+    private SnatAddStaticMapping getRequest(final InstanceIdentifier<MappingEntry> id,
+                                            final MappingEntry dataAfter,
+                                            final Long natInstanceId,
+                                            final boolean isAdd)
+            throws WriteFailedException.CreateFailedException {
+        final SnatAddStaticMapping request = new SnatAddStaticMapping();
+        request.isAdd = isAdd
+                ? (byte) 1
+                : 0;
+        request.isIp4 = 1;
+        // VPP uses int, model long
+        request.vrfId = natInstanceId.intValue();
+
+        // Snat supports only ipv4 now
+        if (dataAfter.getInternalSrcAddress().getIpv4Address() == null) {
+            throw new WriteFailedException.CreateFailedException(id, dataAfter,
+                    new UnsupportedOperationException(
+                            String.format("No Ipv4 present for in address %s. Ipv6 not supported",
+                                    dataAfter.getInternalSrcAddress())));
+        }
+
+        request.addrOnly = 1;
+        request.localIpAddress =
+                ipv4AddressNoZoneToArray(dataAfter.getInternalSrcAddress().getIpv4Address().getValue());
+        request.externalIpAddress = ipv4AddressNoZoneToArray(dataAfter.getExternalSrcAddress().getValue());
+
+        Optional<Short> internalPortNumber = getPortNumber(id, dataAfter,
+                (entry) -> Optional.fromNullable(entry.getInternalSrcPort()).transform(PortNumber::getPortType));
+        Optional<Short> externalPortNumber = getPortNumber(id, dataAfter,
+                (entry) -> Optional.fromNullable(entry.getExternalSrcPort()).transform(PortNumber::getPortType));
+        if (internalPortNumber.isPresent() && externalPortNumber.isPresent()) {
+            request.addrOnly = 0;
+            request.localPort = internalPortNumber.get();
+            request.externalPort = externalPortNumber.get();
+        }
+        return request;
+    }
+
+    private Optional<Short> getPortNumber(final InstanceIdentifier<MappingEntry> id, final MappingEntry dataAfter,
+                                          final PortGetter portGetter) {
+        return portGetter.getPortType(dataAfter).transform(port -> {
+            if (port instanceof SinglePortNumber) {
+                return ((SinglePortNumber) port).getSinglePortNumber().getValue().shortValue();
+            } else {
+                throw new IllegalArgumentException(
+                        String.format("Only single port number supported. Submitted: %s for entry: %s",
+                                dataAfter.getInternalSrcPort(), id));
+            }
+        });
+    }
+
+    interface PortGetter {
+        Optional<PortType> getPortType(MappingEntry entry);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/NatInstaceCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/NatInstaceCustomizer.java
new file mode 100644 (file)
index 0000000..3cc477d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write;
+
+import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstanceKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class NatInstaceCustomizer implements ListWriterCustomizer<NatInstance, NatInstanceKey> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NatInstaceCustomizer.class);
+
+    @Override
+    public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<NatInstance> id,
+                                       @Nonnull final NatInstance dataAfter, @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        LOG.trace("Writing nat-instance: {}", id);
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<NatInstance> id,
+                                        @Nonnull final NatInstance dataBefore, @Nonnull final NatInstance dataAfter,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        LOG.trace("Updating nat-instance: {}", id);
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<NatInstance> id,
+                                        @Nonnull final NatInstance dataBefore, @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        LOG.trace("Deleting nat-instance: {}", id);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/NatWriterFactory.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/NatWriterFactory.java
new file mode 100644 (file)
index 0000000..ecc886b
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import io.fd.honeycomb.nat.util.MappingEntryContext;
+import io.fd.honeycomb.translate.impl.write.GenericListWriter;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatConfig;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.mapping.entry.ExternalSrcPort;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.mapping.entry.InternalSrcPort;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.NatInstances;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.MappingTable;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Nat Writers registration.
+ */
+public final class NatWriterFactory implements WriterFactory {
+
+    private static final InstanceIdentifier<NatConfig> NAT_CFG_ID = InstanceIdentifier.create(NatConfig.class);
+    private static final InstanceIdentifier<NatInstance> NAT_INSTANCE_ID =
+            NAT_CFG_ID.child(NatInstances.class).child(NatInstance.class);
+    private static final InstanceIdentifier<MappingEntry> MAP_ENTRY_ID =
+            NAT_INSTANCE_ID.child(MappingTable.class).child(MappingEntry.class);
+
+    private final FutureJVppSnatFacade jvppSnat;
+    private final MappingEntryContext mappingEntryContext;
+
+    @Inject
+    public NatWriterFactory(final FutureJVppSnatFacade jvppSnat,
+                            final MappingEntryContext mappingEntryContext) {
+        this.jvppSnat = jvppSnat;
+        this.mappingEntryContext = mappingEntryContext;
+    }
+
+    @Override
+    public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) {
+        // Nat-instance
+        registry.add(new GenericListWriter<>(NAT_INSTANCE_ID, new NatInstaceCustomizer()));
+        //  Mapping-entry
+        registry.subtreeAdd(Sets.newHashSet(InstanceIdentifier.create(MappingEntry.class).child(ExternalSrcPort.class),
+                InstanceIdentifier.create(MappingEntry.class).child(InternalSrcPort.class)),
+                new GenericListWriter<>(MAP_ENTRY_ID, new MappingEntryCustomizer(jvppSnat, mappingEntryContext)));
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/AbstractInterfaceNatCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/AbstractInterfaceNatCustomizer.java
new file mode 100644 (file)
index 0000000..64e7142
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write.ifc;
+
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeature;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeatureReply;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import java.util.concurrent.CompletionStage;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+
+abstract class AbstractInterfaceNatCustomizer<D extends DataObject> implements JvppReplyConsumer, WriterCustomizer<D> {
+
+    private final FutureJVppSnatFacade jvppSnat;
+    private final NamingContext ifcContext;
+
+    AbstractInterfaceNatCustomizer(@Nonnull final FutureJVppSnatFacade jvppSnat,
+                                   @Nonnull final NamingContext ifcContext) {
+        this.jvppSnat = jvppSnat;
+        this.ifcContext = ifcContext;
+    }
+
+    @Override
+    public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataAfter,
+                                       @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        final String ifcName = id.firstKeyOf(Interface.class).getName();
+        getLog().debug("Enabling " + getType() + " NAT on interface: {}", ifcName);
+        getLog().debug("Enabling " + getType() + " NAT: {}", id);
+
+        final int ifcIndex = ifcContext.getIndex(ifcName, writeContext.getMappingContext());
+        final SnatInterfaceAddDelFeature request = getRequest(ifcIndex, (byte)1);
+        final CompletionStage<SnatInterfaceAddDelFeatureReply> future = jvppSnat.snatInterfaceAddDelFeature(request);
+
+        final SnatInterfaceAddDelFeatureReply reply = getReplyForWrite(future.toCompletableFuture(), id);
+        getLog().debug("NAT " + getType() + " enabled successfully on: {}, reply: {}", ifcName, reply);
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                        @Nonnull final D dataBefore, @Nonnull final D dataAfter,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
+                new UnsupportedOperationException("Unable to update NAT feature"));
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                        @Nonnull final D dataBefore, @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        final String ifcName = id.firstKeyOf(Interface.class).getName();
+        getLog().debug("Disabling " + getType() + " NAT on interface: {}", ifcName);
+        getLog().debug("Disabling " + getType() + " NAT: {}", id);
+
+        final int ifcIndex = ifcContext.getIndex(ifcName, writeContext.getMappingContext());
+        final SnatInterfaceAddDelFeature request = getRequest(ifcIndex, (byte)0);
+        final CompletionStage<SnatInterfaceAddDelFeatureReply> future = jvppSnat.snatInterfaceAddDelFeature(request);
+
+        final SnatInterfaceAddDelFeatureReply reply = getReplyForWrite(future.toCompletableFuture(), id);
+        getLog().debug("NAT " + getType() + " disabled successfully on: {}, reply: {}", ifcName, reply);
+    }
+
+    enum NatType {
+        INBOUND((byte)1), OUTBOUND((byte)0);
+
+        private final byte isInside;
+
+        NatType(final byte isInside) {
+            this.isInside = isInside;
+        }
+    }
+
+    abstract NatType getType();
+    abstract Logger getLog();
+
+    private SnatInterfaceAddDelFeature getRequest(final int ifcIdx, final byte isAdd) {
+        final SnatInterfaceAddDelFeature request = new SnatInterfaceAddDelFeature();
+        request.isAdd = isAdd;
+        request.isInside = getType().isInside;
+        request.swIfIndex = ifcIdx;
+        return request;
+    }
+
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/IfcNatWriterFactory.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/IfcNatWriterFactory.java
new file mode 100644 (file)
index 0000000..bf2c600
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write.ifc;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import io.fd.honeycomb.translate.impl.write.GenericWriter;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.Nat;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Inbound;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Outbound;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Nat Writers registration.
+ */
+public final class IfcNatWriterFactory implements WriterFactory {
+
+    private static final InstanceIdentifier<Interface>
+            IFC_ID = InstanceIdentifier.create(Interfaces.class).child(Interface.class);
+    private static final InstanceIdentifier<Nat> NAT_AUG_ID =
+            IFC_ID .augmentation(NatInterfaceAugmentation.class).child(Nat.class);
+
+    private final FutureJVppSnatFacade jvppSnat;
+    private final NamingContext ifcContext;
+
+    @Inject
+    public IfcNatWriterFactory(final FutureJVppSnatFacade jvppSnat,
+                               @Named("interface-context") final NamingContext ifcContext) {
+        this.jvppSnat = jvppSnat;
+        this.ifcContext = ifcContext;
+    }
+
+    @Override
+    public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) {
+        registry.addAfter(new GenericWriter<>(NAT_AUG_ID.child(Inbound.class),
+                new InterfaceInboundNatCustomizer(jvppSnat, ifcContext)), IFC_ID);
+        registry.addAfter(new GenericWriter<>(NAT_AUG_ID.child(Outbound.class),
+                new InterfaceOutboundNatCustomizer(jvppSnat, ifcContext)), IFC_ID);
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/InterfaceInboundNatCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/InterfaceInboundNatCustomizer.java
new file mode 100644 (file)
index 0000000..8ab1c28
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write.ifc;
+
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Inbound;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InterfaceInboundNatCustomizer extends AbstractInterfaceNatCustomizer<Inbound> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InterfaceInboundNatCustomizer.class);
+
+    InterfaceInboundNatCustomizer(@Nonnull final FutureJVppSnatFacade jvppSnat,
+                                         @Nonnull final NamingContext ifcContext) {
+        super(jvppSnat, ifcContext);
+    }
+
+    @Override
+    NatType getType() {
+        return NatType.INBOUND;
+    }
+
+    @Override
+    Logger getLog() {
+        return LOG;
+    }
+}
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/InterfaceOutboundNatCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/write/ifc/InterfaceOutboundNatCustomizer.java
new file mode 100644 (file)
index 0000000..fdd174e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.write.ifc;
+
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Outbound;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InterfaceOutboundNatCustomizer extends AbstractInterfaceNatCustomizer<Outbound> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InterfaceOutboundNatCustomizer.class);
+
+    InterfaceOutboundNatCustomizer(@Nonnull final FutureJVppSnatFacade jvppSnat,
+                                          @Nonnull final NamingContext ifcContext) {
+        super(jvppSnat, ifcContext);
+    }
+
+    @Override
+    NatType getType() {
+        return NatType.OUTBOUND;
+    }
+
+    @Override
+    Logger getLog() {
+        return LOG;
+    }
+}
diff --git a/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/NatModuleTest.java b/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/NatModuleTest.java
new file mode 100644 (file)
index 0000000..774dd6f
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
+import com.google.inject.testing.fieldbinder.Bind;
+import com.google.inject.testing.fieldbinder.BoundFieldModule;
+import io.fd.honeycomb.translate.read.ReaderFactory;
+import io.fd.honeycomb.translate.util.read.registry.CompositeReaderRegistryBuilder;
+import io.fd.honeycomb.translate.util.write.registry.FlatWriterRegistryBuilder;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NatModuleTest {
+
+    @Named("honeycomb-context")
+    @Bind
+    @Mock
+    private DataBroker honeycombContext;
+
+    @Bind
+    private ReaderFactory ietfIfcReaderFactory;
+
+    @Named("honeycomb-initializer")
+    @Bind
+    @Mock
+    private DataBroker honeycombInitializer;
+
+    @Named("interface-context")
+    @Bind
+    private NamingContext ifcContext;
+
+    @Inject
+    private Set<ReaderFactory> readerFactories = new HashSet<>();
+
+    @Inject
+    private Set<WriterFactory> writerFactories = new HashSet<>();
+
+    @Before
+    public void setUp() throws Exception {
+        ietfIfcReaderFactory = registry -> {
+            registry.addStructuralReader(InstanceIdentifier.create(InterfacesState.class), InterfacesStateBuilder.class);
+            registry.addStructuralReader(InstanceIdentifier.create(InterfacesState.class).child(Interface.class), InterfaceBuilder.class);
+        };
+        initMocks(this);
+        ifcContext = new NamingContext("interface-", "interface-context");
+        // Nat Module adds readers under InterfacesState/Interface and since readers for parents that do nothing need to
+        // be present, add structural readers (or add V3poModule here, but adding the full Module is not the best solution)
+        Guice.createInjector(binder -> Multibinder.newSetBinder(binder, ReaderFactory.class)
+                .addBinding().toInstance(registry -> {
+                    registry.addStructuralReader(InstanceIdentifier.create(InterfacesState.class),
+                            InterfacesStateBuilder.class);
+                    registry.addStructuralReader(InstanceIdentifier.create(InterfacesState.class).child(Interface.class),
+                            InterfaceBuilder.class);
+                }), new NatModule(MockJVppSnatProvider.class), BoundFieldModule.of(this)).injectMembers(this);
+    }
+
+    @Test
+    public void testReaderFactories() throws Exception {
+        assertThat(readerFactories, is(not(empty())));
+
+        // Test registration process (all dependencies present, topological order of readers does exist, etc.)
+        final CompositeReaderRegistryBuilder registryBuilder = new CompositeReaderRegistryBuilder();
+        readerFactories.forEach(factory -> factory.init(registryBuilder));
+        assertNotNull(registryBuilder.build());
+    }
+
+    @Test
+    public void testWriterFactories() throws Exception {
+        assertThat(writerFactories, is(not(empty())));
+
+        // Test registration process (all dependencies present, topological order of writers does exist, etc.)
+        final FlatWriterRegistryBuilder registryBuilder = new FlatWriterRegistryBuilder();
+        writerFactories.forEach(factory -> factory.init(registryBuilder));
+        assertNotNull(registryBuilder.build());
+    }
+
+    private static final class MockJVppSnatProvider implements Provider<FutureJVppSnatFacade> {
+
+        @Override
+        public FutureJVppSnatFacade get() {
+            return mock(FutureJVppSnatFacade.class);
+        }
+    }
+}
\ No newline at end of file
diff --git a/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/util/MappingEntryContextTest.java b/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/util/MappingEntryContextTest.java
new file mode 100644 (file)
index 0000000..fee8def
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.nat.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
+import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetails;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.MappingTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@SuppressWarnings("unchecked")
+public class MappingEntryContextTest implements Ipv4Translator {
+
+    private MappingEntryContext ctx = new MappingEntryContext();
+    @Mock
+    private MappingContext mappingCtx;
+
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+    }
+
+    @Test
+    public void testAdd() throws Exception {
+        when(mappingCtx.read(any(InstanceIdentifier.class))).thenReturn(Optional.absent());
+        final long natId = 7;
+        final long entryId = 99;
+        final MappingEntry entry = getEntry(natId, "192.168.1.5", "17.14.4.6");
+
+        ctx.addEntry(natId, entryId, entry, mappingCtx);
+
+        verify(mappingCtx).put(MappingEntryContext.getId(natId, MappingEntryContext.entryToKey(entry)), MappingEntryContext.toCtxMapEntry(entry, entryId));
+    }
+
+    @Test
+    public void testRemove() throws Exception {
+        final long natId = 0;
+        final MappingEntry entry = getEntry(natId, "192.168.1.5", "17.14.4.6");
+
+        ctx.removeEntry(natId, entry, mappingCtx);
+
+        verify(mappingCtx).delete(MappingEntryContext.getId(natId, MappingEntryContext.entryToKey(entry)));
+    }
+
+    @Test
+    public void testGetExistingIndex() throws Exception {
+        final long natId = 0;
+        final long entryId = 12;
+        final MappingEntry entry = getEntry(entryId, "192.168.1.5", "17.14.4.6");
+        final SnatStaticMappingDetails details = getDetails(entryId, "192.168.1.5", "17.14.4.6");
+
+        when(mappingCtx.read(MappingEntryContext.getId(natId, MappingEntryContext.entryToKey(entry))))
+                .thenReturn(Optional.of(MappingEntryContext.toCtxMapEntry(entry, entryId)));
+
+        assertEquals(12, ctx.getStoredOrArtificialIndex(natId, details, mappingCtx));
+        verify(mappingCtx).read(MappingEntryContext.getId(natId, MappingEntryContext.entryToKey(entry)));
+    }
+
+    @Test
+    public void testFindDetails() throws Exception {
+        final long natId = 0;
+        final MappingEntry entry = getEntry(0, "192.168.1.5", "17.14.4.6");
+        final SnatStaticMappingDetails details = getDetails(0, "192.168.1.5", "17.14.4.6");
+        final MappingEntry entry2 = getEntry(1, "192.168.1.8", "17.14.4.10");
+        final SnatStaticMappingDetails details2 = getDetails(1, "192.168.1.8", "17.14.4.10");
+
+        final List<SnatStaticMappingDetails> someDetails = Lists.newArrayList(details, details2);
+
+        when(mappingCtx.read(MappingEntryContext.getTableId(natId)))
+                .thenReturn(Optional.of(new MappingTableBuilder()
+                        .setMappingEntry(Lists.newArrayList(
+                                MappingEntryContext.toCtxMapEntry(entry, 0),
+                                MappingEntryContext.toCtxMapEntry(entry2, 1)))
+                        .build()));
+
+        assertSame(details, ctx.findDetails(someDetails, natId, 0, mappingCtx));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testFindDetailsNoMappingStored() throws Exception {
+        final long natId = 0;
+        final long entryId = 12;
+        final SnatStaticMappingDetails details = getDetails(entryId, "192.168.1.5", "17.14.4.6");
+        final List<SnatStaticMappingDetails> someDetails = Lists.newArrayList(details);
+        when(mappingCtx.read(MappingEntryContext.getTableId(natId))).thenReturn(Optional.absent());
+
+        ctx.findDetails(someDetails, natId, entryId, mappingCtx);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testFindDetailsNoMappingStored2() throws Exception {
+        final long natId = 0;
+        final long entryId = 12;
+        final SnatStaticMappingDetails details = getDetails(entryId, "192.168.1.5", "17.14.4.6");
+        final List<SnatStaticMappingDetails> someDetails = Lists.newArrayList(details);
+
+        when(mappingCtx.read(MappingEntryContext.getTableId(natId)))
+                .thenReturn(Optional.of(new MappingTableBuilder().setMappingEntry(Collections.emptyList()).build()));
+
+        ctx.findDetails(someDetails, natId, entryId, mappingCtx);
+    }
+
+    @Test
+    public void testGetArtificialIndex() throws Exception {
+        final long natId = 0;
+        final long entryId = 0;
+        final MappingEntry entry = getEntry(entryId, "192.168.1.5", "17.14.4.6");
+        final long entryId2 = 55;
+        final MappingEntry entry2 = getEntry(entryId2, "192.168.1.6", "17.14.4.7");
+        final long entryId3 = 18954;
+        final MappingEntry entry3 = getEntry(entryId3, "192.168.1.7", "17.14.4.8");
+        final long entryId4 = 18955;
+        final MappingEntry entry4 = getEntry(entryId4, "192.168.1.8", "17.14.4.9");
+
+        final long newEntryId = 18956;
+        final MappingEntry newEntry = getEntry(newEntryId, "192.168.1.99", "17.14.4.99");
+        final SnatStaticMappingDetails newDetails = getDetails(newEntryId, "192.168.1.99", "17.14.4.99");
+        when(mappingCtx.read(MappingEntryContext.getId(natId, MappingEntryContext.entryToKey(newEntry))))
+                .thenReturn(Optional.absent());
+
+        when(mappingCtx.read(MappingEntryContext.getTableId(natId)))
+                .thenReturn(Optional.of(new MappingTableBuilder()
+                        .setMappingEntry(Lists.newArrayList(
+                                MappingEntryContext.toCtxMapEntry(entry, entryId),
+                                MappingEntryContext.toCtxMapEntry(entry3, entryId3),
+                                MappingEntryContext.toCtxMapEntry(entry4, entryId4),
+                                MappingEntryContext.toCtxMapEntry(entry2, entryId2)))
+                        .build()));
+
+        assertFalse(ctx.getStoredIndex(natId, newEntry, mappingCtx).isPresent());
+        assertEquals(newEntryId, ctx.getStoredOrArtificialIndex(natId, newDetails, mappingCtx));
+    }
+
+    private SnatStaticMappingDetails getDetails(final long vrfId, final String localIp, final String externIp) {
+        final SnatStaticMappingDetails snatStaticMappingDetails = new SnatStaticMappingDetails();
+        snatStaticMappingDetails.vrfId = (int) vrfId;
+        snatStaticMappingDetails.addrOnly = 1;
+        snatStaticMappingDetails.isIp4 = 1;
+        snatStaticMappingDetails.localIpAddress = ipv4AddressNoZoneToArray(localIp);
+        snatStaticMappingDetails.externalIpAddress = ipv4AddressNoZoneToArray(externIp);
+        return snatStaticMappingDetails;
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddExisting() throws Exception {
+        final long natId = 7;
+        final long entryId = 99;
+        final MappingEntry entry = getEntry(natId, "192.168.1.5", "17.14.4.6");
+        final org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntry
+                data = MappingEntryContext.toCtxMapEntry(entry, entryId);
+        when(mappingCtx.read(any(InstanceIdentifier.class))).thenReturn(Optional.of(data));
+
+        ctx.addEntry(natId, entryId, entry, mappingCtx);
+    }
+
+    private static MappingEntry getEntry(final long id, final String longernalIpv4, final String externalIpv4) {
+        return new MappingEntryBuilder()
+                .setKey(new MappingEntryKey(id))
+                .setType(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.MappingEntry.Type.Static)
+                .setInternalSrcAddress(new IpAddress(new Ipv4Address(longernalIpv4)))
+                .setExternalSrcAddress(new Ipv4Address(externalIpv4))
+                .build();
+    }
+}
\ No newline at end of file
diff --git a/nat/nat2vpp/src/test/resources/nat.json b/nat/nat2vpp/src/test/resources/nat.json
new file mode 100644 (file)
index 0000000..c7d9afe
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "enabled": "true"
+}
\ No newline at end of file
diff --git a/nat/pom.xml b/nat/pom.xml
new file mode 100644 (file)
index 0000000..94d035d
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <parent>
+    <groupId>io.fd.honeycomb.common</groupId>
+    <artifactId>honeycomb-parent</artifactId>
+    <version>1.16.12-SNAPSHOT</version>
+    <relativePath>../common/honeycomb-parent</relativePath>
+  </parent>
+
+  <groupId>io.fd.honeycomb.nat</groupId>
+  <artifactId>nat-aggregator</artifactId>
+  <version>1.16.12-SNAPSHOT</version>
+  <name>nat-aggregator</name>
+  <packaging>pom</packaging>
+  <modelVersion>4.0.0</modelVersion>
+
+  <modules>
+    <module>nat-api</module>
+    <module>nat2vpp</module>
+  </modules>
+
+  <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-install-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/nat/postman_rest_collection.json b/nat/postman_rest_collection.json
new file mode 100644 (file)
index 0000000..348d603
--- /dev/null
@@ -0,0 +1,222 @@
+{
+       "id": "f9258895-49e9-596f-d413-698cbcadfbde",
+       "name": "Honeycomb NAT RESTCONF calls",
+       "description": "Common management operations for Honeycomb + VPP + SNAT plugin",
+       "order": [
+               "d2961af7-47f7-8960-1b00-f5dcd2beafb9",
+               "c00f25d2-f194-2e90-30e5-ca31e8fbd630",
+               "1ffc71e4-9d1e-0c99-7901-605ac8e364b6",
+               "f3a7e1b5-c1d0-ab2f-3cef-3c902f8e72d5"
+       ],
+       "folders": [
+               {
+                       "id": "ce93ade0-47c3-a82d-a768-058a2e18e2bb",
+                       "name": "1:1 static",
+                       "description": "",
+                       "order": [
+                               "bca19675-d674-0cde-8639-df3d2ecbfd27",
+                               "153f5720-fafc-22e6-6282-cf9e18226dbc",
+                               "830c4ab3-2e48-5654-8aa5-2d2f9eed84b4",
+                               "2c53651a-e31c-24da-122e-c0ce65544c8e",
+                               "b54693d5-9226-1c27-77b2-8b1313da2e80"
+                       ],
+                       "owner": "45557",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde"
+               }
+       ],
+       "timestamp": 1475147449190,
+       "owner": "45557",
+       "public": false,
+       "published": false,
+       "requests": [
+               {
+                       "id": "153f5720-fafc-22e6-6282-cf9e18226dbc",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/operational/naming-context:contexts",
+                       "pathVariables": {},
+                       "preRequestScript": "",
+                       "method": "GET",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "data": [],
+                       "dataMode": "raw",
+                       "name": "Get NAT context -context",
+                       "description": "Read nat context ",
+                       "descriptionFormat": "html",
+                       "time": 1475678940763,
+                       "version": 2,
+                       "responses": [],
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "folder": "ce93ade0-47c3-a82d-a768-058a2e18e2bb",
+                       "rawModeData": "{\r\n    \r\n        \"inbound\" : {}\r\n    \r\n}"
+               },
+               {
+                       "id": "1ffc71e4-9d1e-0c99-7901-605ac8e364b6",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-interfaces:interfaces/interface/local0/interface-nat:nat/outbound",
+                       "preRequestScript": "",
+                       "pathVariables": {},
+                       "method": "PUT",
+                       "data": [],
+                       "dataMode": "raw",
+                       "version": 2,
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "time": 1475154424741,
+                       "name": "Set NAT outbound for ifc - cfg",
+                       "description": "Setting intarface NAT outbound feature\n\nCLI: set interface snat in <intfc> out <intfc> [del]\n\nMore information: https://wiki.fd.io/view/VPP/SNAT",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "responses": [],
+                       "rawModeData": "{\r\n    \r\n        \"outbound\" : {}\r\n    \r\n}"
+               },
+               {
+                       "id": "2c53651a-e31c-24da-122e-c0ce65544c8e",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-nat:nat-config",
+                       "preRequestScript": "",
+                       "pathVariables": {},
+                       "method": "GET",
+                       "data": [],
+                       "dataMode": "raw",
+                       "version": 2,
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "time": 1475678997603,
+                       "name": "Get NAT - config",
+                       "description": "Read nat-state",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "responses": [],
+                       "rawModeData": "{\r\n    \r\n        \"inbound\" : {}\r\n    \r\n}"
+               },
+               {
+                       "id": "830c4ab3-2e48-5654-8aa5-2d2f9eed84b4",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/operational/ietf-nat:nat-state",
+                       "pathVariables": {},
+                       "preRequestScript": "",
+                       "method": "GET",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "data": [],
+                       "dataMode": "raw",
+                       "name": "Get NAT -state",
+                       "description": "Read nat-state\n\nCLI: show snat [detail|verbose]\n\nMore information: https://wiki.fd.io/view/VPP/SNAT",
+                       "descriptionFormat": "html",
+                       "time": 1475677867416,
+                       "version": 2,
+                       "responses": [],
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "folder": "ce93ade0-47c3-a82d-a768-058a2e18e2bb",
+                       "rawModeData": "{\r\n    \r\n        \"inbound\" : {}\r\n    \r\n}"
+               },
+               {
+                       "id": "b54693d5-9226-1c27-77b2-8b1313da2e80",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-nat:nat-config/nat-instances/nat-instance/0/mapping-table/mapping-entry/1/",
+                       "pathVariables": {},
+                       "preRequestScript": "",
+                       "method": "DELETE",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "data": [],
+                       "dataMode": "raw",
+                       "name": "Delete SNAT 1:1 static entry IPv4 -cfg",
+                       "description": "",
+                       "descriptionFormat": "html",
+                       "time": 1475761660317,
+                       "version": 2,
+                       "responses": [],
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "folder": "ce93ade0-47c3-a82d-a768-058a2e18e2bb",
+                       "rawModeData": "{\r\n\t\"mapping-entry\" : {\r\n\t\t\"index\": 1,\r\n\t\t\"type\": \"static\",\r\n\t\t\"internal-src-address\": \"192.168.1.87\",\r\n\t\t\"external-src-address\": \"45.1.5.7\"\r\n\t}\r\n}"
+               },
+               {
+                       "id": "bca19675-d674-0cde-8639-df3d2ecbfd27",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-nat:nat-config/nat-instances/nat-instance/0/mapping-table/mapping-entry/1/",
+                       "preRequestScript": "",
+                       "pathVariables": {},
+                       "method": "PUT",
+                       "data": [],
+                       "dataMode": "raw",
+                       "version": 2,
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "time": 1475758450889,
+                       "name": "Add SNAT 1:1 static entry IPv4 -cfg",
+                       "description": "CLI: snat add static mapping local 10.0.0.3 external 4.4.4.4\n\nMore information: https://wiki.fd.io/view/VPP/SNAT",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "responses": [],
+                       "rawModeData": "{\r\n\t\"mapping-entry\" : {\r\n\t\t\"index\": 1,\r\n\t\t\"type\": \"static\",\r\n\t\t\"internal-src-address\": \"192.168.1.87\",\r\n\t\t\"external-src-address\": \"45.1.5.7\"\r\n\t}\r\n}"
+               },
+               {
+                       "id": "c00f25d2-f194-2e90-30e5-ca31e8fbd630",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-interfaces:interfaces/interface/local0/interface-nat:nat/inbound",
+                       "pathVariables": {},
+                       "preRequestScript": "",
+                       "method": "DELETE",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "data": [],
+                       "dataMode": "raw",
+                       "name": "Disable NAT inbound for ifc - cfg",
+                       "description": "Unsetting intarface NAT inbound feature\n\nCLI: set interface snat in <intfc> out <intfc> [del]\n\nMore information: https://wiki.fd.io/view/VPP/SNAT",
+                       "descriptionFormat": "html",
+                       "time": 1475835912474,
+                       "version": 2,
+                       "responses": [],
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "rawModeData": "{\r\n    \r\n        \"inbound\" : {}\r\n    \r\n}"
+               },
+               {
+                       "id": "d2961af7-47f7-8960-1b00-f5dcd2beafb9",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-interfaces:interfaces/interface/local0/interface-nat:nat/inbound",
+                       "pathVariables": {},
+                       "preRequestScript": "",
+                       "method": "PUT",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "data": [],
+                       "dataMode": "raw",
+                       "name": "Set NAT inbound for ifc - cfg",
+                       "description": "Setting intarface NAT inbound feature\n\nCLI: set interface snat in <intfc> out <intfc> [del]\n\nMore information: https://wiki.fd.io/view/VPP/SNAT",
+                       "descriptionFormat": "html",
+                       "time": 1475154374071,
+                       "version": 2,
+                       "responses": [],
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "rawModeData": "{\r\n    \r\n        \"inbound\" : {}\r\n    \r\n}"
+               },
+               {
+                       "id": "f3a7e1b5-c1d0-ab2f-3cef-3c902f8e72d5",
+                       "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+                       "url": "http://localhost:8183/restconf/config/ietf-interfaces:interfaces/interface/local0/interface-nat:nat/outbound",
+                       "pathVariables": {},
+                       "preRequestScript": "",
+                       "method": "DELETE",
+                       "collectionId": "f9258895-49e9-596f-d413-698cbcadfbde",
+                       "data": [],
+                       "dataMode": "raw",
+                       "name": "Disable NAT outbound for ifc - cfg",
+                       "description": "Unsetting intarface NAT outbound feature\n\nCLI: set interface snat in <intfc> out <intfc> [del]\n\nMore information: https://wiki.fd.io/view/VPP/SNAT",
+                       "descriptionFormat": "html",
+                       "time": 1475835941689,
+                       "version": 2,
+                       "responses": [],
+                       "tests": "",
+                       "currentHelper": "normal",
+                       "helperAttributes": {},
+                       "rawModeData": "{\r\n    \r\n        \"outbound\" : {}\r\n    \r\n}"
+               }
+       ]
+}
\ No newline at end of file
index e7d9d15..94399a5 100755 (executable)
@@ -41,7 +41,7 @@ public final class VppNshModule extends AbstractModule {
 
     @Override
     protected void configure() {
-        LOG.info("Configuring VppNsh module");
+        LOG.debug("Installing NSH module");
 
         // Naming contexts
         bind(NamingContext.class)
@@ -59,6 +59,6 @@ public final class VppNshModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), WriterFactory.class).addBinding().to(VppNshWriterFactory.class);
         Multibinder.newSetBinder(binder(), ReaderFactory.class).addBinding().to(VppNshReaderFactory.class);
         Multibinder.newSetBinder(binder(), DataTreeInitializer.class).addBinding().to(VppNshInitializer.class);
-        LOG.info("NSH module successfully configured");
+        LOG.info("Module NSH successfully configured");
     }
 }
index eaecdf0..e30f9e6 100644 (file)
@@ -35,11 +35,16 @@ import io.fd.honeycomb.translate.write.WriterFactory;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import net.jmob.guice.conf.core.ConfigurationModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class V3poModule extends AbstractModule {
 
+    private static final Logger LOG = LoggerFactory.getLogger(V3poModule.class);
+
     @Override
     protected void configure() {
+        LOG.debug("Installing V3PO module");
         install(ConfigurationModule.create());
         requestInjection(V3poConfiguration.class);
 
@@ -91,5 +96,6 @@ public class V3poModule extends AbstractModule {
         final Multibinder<ManagedNotificationProducer> notifiersBinder =
                 Multibinder.newSetBinder(binder(), ManagedNotificationProducer.class);
         notifiersBinder.addBinding().to(InterfaceChangeNotificationProducer.class);
+        LOG.info("Module V3PO successfully configured");
     }
 }
index 43e038c..83be57b 100644 (file)
         <artifactId>jvpp-registry</artifactId>
         <version>${jvpp.version}</version>
       </dependency>
+      <dependency>
+        <groupId>io.fd.honeycomb.vpp</groupId>
+        <artifactId>vpp-translate-utils</artifactId>
+        <version>${project.version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 </project>
index 56aa4f1..ff737d4 100644 (file)
@@ -26,7 +26,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
 
 /**
- * Trait providing logic for translation of ipv4-related data
+ * Trait providing logic for translation of ipv4-related data.
  */
 public interface Ipv4Translator extends ByteDataTranslator {
 
index aecd6ef..467ac51 100644 (file)
@@ -35,7 +35,7 @@ import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 /**
  * Utility adapter on top of {@link MappingContext} storing integer to string mappings according to naming-context yang
- * model
+ * model.
  */
 public final class NamingContext implements AutoCloseable {
 
index 36d1e0f..041c0a5 100644 (file)
@@ -34,6 +34,7 @@
     <lisp.version>1.16.12-SNAPSHOT</lisp.version>
     <vpp.common.integration.version>1.16.12-SNAPSHOT</vpp.common.integration.version>
     <vppnsh.version>1.16.12-SNAPSHOT</vppnsh.version>
+    <nat.version>1.16.12-SNAPSHOT</nat.version>
 
     <distribution.modules>
       io.fd.honeycomb.vpp.common.integration.VppCommonModule,
       <artifactId>vppnsh-impl</artifactId>
       <version>${vppnsh.version}</version>
     </dependency>
+    <dependency>
+      <groupId>io.fd.honeycomb.nat</groupId>
+      <artifactId>nat2vpp</artifactId>
+      <version>${nat.version}</version>
+    </dependency>
   </dependencies>
 </project>