[ONOS-5944] Add LISP Geo Coordindate LCAF address type

Change-Id: I110eb57aded5fe2a8cf60e1d2e758737f4cbf139
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispCanonicalAddressFormatEnum.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispCanonicalAddressFormatEnum.java
index b6cfa71..7191a2e 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispCanonicalAddressFormatEnum.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispCanonicalAddressFormatEnum.java
@@ -25,18 +25,19 @@
  * https://tools.ietf.org/html/draft-ietf-lisp-lcaf-22
  */
 public enum LispCanonicalAddressFormatEnum {
+    UNKNOWN(-1),                // Unknown Type
+    UNSPECIFIED(0),             // Unspecified Type
     LIST(1),                    // AFI LIST Type
     SEGMENT(2),                 // Instance ID Type
     AS(3),                      // AS Number Type
     APPLICATION_DATA(4),        // Application Data Type
+    GEO_COORDINATE(5),          // Geo Coordinate Type
     NAT(7),                     // NAT Traversal Type
     NONCE(8),                   // Nonce Locator Type
     MULTICAST(9),               // Multi-cast Info Type
     TRAFFIC_ENGINEERING(10),    // Explicit Locator Path Type
     SECURITY(11),               // Security Key Type
-    SOURCE_DEST(12),            // Source/Dest Key Type
-    UNSPECIFIED(0),             // Unspecified Type
-    UNKNOWN(-1);                // Unknown Type
+    SOURCE_DEST(12);            // Source/Dest Key Type
 
     private byte lispCode;
 
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispGeoCoordinateLcafAddress.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispGeoCoordinateLcafAddress.java
new file mode 100644
index 0000000..d70fd03
--- /dev/null
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispGeoCoordinateLcafAddress.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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 org.onosproject.lisp.msg.types.lcaf;
+
+import io.netty.buffer.ByteBuf;
+import org.onlab.util.ByteOperator;
+import org.onosproject.lisp.msg.exceptions.LispParseError;
+import org.onosproject.lisp.msg.exceptions.LispReaderException;
+import org.onosproject.lisp.msg.exceptions.LispWriterException;
+import org.onosproject.lisp.msg.types.LispAddressReader;
+import org.onosproject.lisp.msg.types.LispAddressWriter;
+import org.onosproject.lisp.msg.types.LispAfiAddress;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Geo Coordinate type LCAF address class.
+ * <p>
+ * Geo Coordinate type is defined in draft-ietf-lisp-lcaf-22
+ * https://tools.ietf.org/html/draft-ietf-lisp-lcaf-22#page-11
+ * <p>
+ * <pre>
+ * {@literal
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |           AFI = 16387         |     Rsvd1     |     Flags     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |   Type = 5    |     Rsvd2     |            Length             |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |N|     Latitude Degrees        |    Minutes    |    Seconds    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |E|     Longitude Degrees       |    Minutes    |    Seconds    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                            Altitude                           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |              AFI = x          |         Address  ...          |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * }</pre>
+ */
+public final class LispGeoCoordinateLcafAddress extends LispLcafAddress {
+
+    private final boolean north;
+    private final short latitudeDegree;
+    private final byte latitudeMinute;
+    private final byte latitudeSecond;
+    private final boolean east;
+    private final short longitudeDegree;
+    private final byte longitudeMinute;
+    private final byte longitudeSecond;
+    private final int altitude;
+    private final LispAfiAddress address;
+
+    /**
+     * Initializes geo coordinate type LCAF address.
+     *
+     * @param north           north flag
+     * @param latitudeDegree  latitude degree
+     * @param latitudeMinute  latitude minute
+     * @param latitudeSecond  latitude second
+     * @param east            east flag
+     * @param longitudeDegree longitude degree
+     * @param longitudeMinute longitude minute
+     * @param longitudeSecond longitude second
+     * @param altitude        altitude
+     * @param address         AFI address
+     */
+    private LispGeoCoordinateLcafAddress(boolean north, short latitudeDegree,
+                                         byte latitudeMinute, byte latitudeSecond,
+                                         boolean east, short longitudeDegree,
+                                         byte longitudeMinute, byte longitudeSecond,
+                                         int altitude, LispAfiAddress address) {
+        super(LispCanonicalAddressFormatEnum.GEO_COORDINATE);
+        this.north = north;
+        this.latitudeDegree = latitudeDegree;
+        this.latitudeMinute = latitudeMinute;
+        this.latitudeSecond = latitudeSecond;
+        this.east = east;
+        this.longitudeDegree = longitudeDegree;
+        this.longitudeMinute = longitudeMinute;
+        this.longitudeSecond = longitudeSecond;
+        this.altitude = altitude;
+        this.address = address;
+    }
+
+    /**
+     * Obtains north flag value.
+     *
+     * @return north flag value
+     */
+    public boolean isNorth() {
+        return north;
+    }
+
+    /**
+     * Obtains latitude degree.
+     *
+     * @return latitude degree
+     */
+    public short getLatitudeDegree() {
+        return latitudeDegree;
+    }
+
+    /**
+     * Obtains latitude minute.
+     *
+     * @return latitude minute
+     */
+    public byte getLatitudeMinute() {
+        return latitudeMinute;
+    }
+
+    /**
+     * Obtains latitude second.
+     *
+     * @return latitude second
+     */
+    public byte getLatitudeSecond() {
+        return latitudeSecond;
+    }
+
+    /**
+     * Obtains east flag value.
+     *
+     * @return east flag vlaue
+     */
+    public boolean isEast() {
+        return east;
+    }
+
+    /**
+     * Obtains longitude degree.
+     *
+     * @return longitude degree
+     */
+    public short getLongitudeDegree() {
+        return longitudeDegree;
+    }
+
+    /**
+     * Obtains longitude minute.
+     *
+     * @return longitude minute
+     */
+    public byte getLongitudeMinute() {
+        return longitudeMinute;
+    }
+
+    /**
+     * Obtains longitude second.
+     *
+     * @return longitude second
+     */
+    public byte getLongitudeSecond() {
+        return longitudeSecond;
+    }
+
+    /**
+     * Obtains altitude.
+     *
+     * @return altitude
+     */
+    public int getAltitude() {
+        return altitude;
+    }
+
+    /**
+     * Obtains AFI address.
+     *
+     * @return AFI address
+     */
+    public LispAfiAddress getAddress() {
+        return address;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(north, latitudeDegree, latitudeMinute, latitudeSecond,
+                            east, longitudeDegree, longitudeMinute, longitudeSecond,
+                            altitude, address);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof LispGeoCoordinateLcafAddress) {
+            final LispGeoCoordinateLcafAddress other =
+                    (LispGeoCoordinateLcafAddress) obj;
+            return Objects.equals(this.north, other.north) &&
+                    Objects.equals(this.latitudeDegree, other.latitudeDegree) &&
+                    Objects.equals(this.latitudeMinute, other.latitudeMinute) &&
+                    Objects.equals(this.latitudeSecond, other.latitudeSecond) &&
+                    Objects.equals(this.east, other.east) &&
+                    Objects.equals(this.longitudeDegree, other.longitudeDegree) &&
+                    Objects.equals(this.longitudeMinute, other.longitudeMinute) &&
+                    Objects.equals(this.longitudeSecond, other.longitudeSecond) &&
+                    Objects.equals(this.altitude, other.altitude) &&
+                    Objects.equals(this.address, other.address);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("north", north)
+                .add("latitude degree", latitudeDegree)
+                .add("latitude minute", latitudeMinute)
+                .add("latitude second", latitudeSecond)
+                .add("east", east)
+                .add("longitude degree", longitudeDegree)
+                .add("longitude minute", longitudeMinute)
+                .add("longitude second", longitudeSecond)
+                .add("altitude", altitude)
+                .add("address", address)
+                .toString();
+    }
+
+    public static final class GeoCoordinateAddressBuilder
+                        extends LcafAddressBuilder<GeoCoordinateAddressBuilder> {
+        private boolean north;
+        private short latitudeDegree;
+        private byte latitudeMinute;
+        private byte latitudeSecond;
+        private boolean east;
+        private short longitudeDegree;
+        private byte longitudeMinute;
+        private byte longitudeSecond;
+        private int altitude;
+        private LispAfiAddress address;
+
+        /**
+         * Sets north flag value.
+         *
+         * @param north north flag value
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withIsNorth(boolean north) {
+            this.north = north;
+            return this;
+        }
+
+        /**
+         * Sets latitude degree.
+         *
+         * @param latitudeDegree latitude degree
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withLatitudeDegree(short latitudeDegree) {
+            this.latitudeDegree = latitudeDegree;
+            return this;
+        }
+
+        /**
+         * Sets latitude minute.
+         *
+         * @param latitudeMinute latitude minute
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withLatitudeMinute(byte latitudeMinute) {
+            this.latitudeMinute = latitudeMinute;
+            return this;
+        }
+
+        /**
+         * Sets latitude second.
+         *
+         * @param latitudeSecond latitude second
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withLatitudeSecond(byte latitudeSecond) {
+            this.latitudeSecond = latitudeSecond;
+            return this;
+        }
+
+        /**
+         * Sets east flag value.
+         *
+         * @param east east flag
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withIsEast(boolean east) {
+            this.east = east;
+            return this;
+        }
+
+        /**
+         * Sets longitude degree.
+         *
+         * @param longitudeDegree longitude degree
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withLongitudeDegree(short longitudeDegree) {
+            this.longitudeDegree = longitudeDegree;
+            return this;
+        }
+
+        /**
+         * Sets longitude minute.
+         *
+         * @param longitudeMinute longitude minute
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withLongitudeMinute(byte longitudeMinute) {
+            this.longitudeMinute = longitudeMinute;
+            return this;
+        }
+
+        /**
+         * Sets longitude second.
+         *
+         * @param longitudeSecond longitude second
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withLongitudeSecond(byte longitudeSecond) {
+            this.longitudeSecond = longitudeSecond;
+            return this;
+        }
+
+        /**
+         * Sets altitude.
+         *
+         * @param altitude altitude
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withAltitude(int altitude) {
+            this.altitude = altitude;
+            return this;
+        }
+
+        /**
+         * Sets AFI address.
+         *
+         * @param address AFI address
+         * @return GeoCoordinateAddressBuilder object
+         */
+        public GeoCoordinateAddressBuilder withAddress(LispAfiAddress address) {
+            this.address = address;
+            return this;
+        }
+
+        /**
+         * Builds LispGeoCoordinateLcafAddress instance.
+         *
+         * @return LispGeoCoordinateLcafAddress instance
+         */
+        public LispGeoCoordinateLcafAddress build() {
+
+            checkNotNull(address, "Must specify an AFI address");
+
+            return new LispGeoCoordinateLcafAddress(north, latitudeDegree,
+                        latitudeMinute, latitudeSecond, east, longitudeDegree,
+                        longitudeMinute, longitudeSecond, altitude, address);
+        }
+    }
+
+    /**
+     * GeoCoordinate LCAF address reader class.
+     */
+    public static class GeoCoordinateLcafAddressReader
+                    implements LispAddressReader<LispGeoCoordinateLcafAddress> {
+
+        private static final int NORTH_INDEX = 7;
+        private static final int EAST_INDEX = 7;
+        private static final int FLAG_SHIFT = 8;
+
+        @Override
+        public LispGeoCoordinateLcafAddress readFrom(ByteBuf byteBuf)
+                                    throws LispParseError, LispReaderException {
+
+            LispLcafAddress.deserializeCommon(byteBuf);
+
+            // north flag -> 1 bit
+            byte flagWithLatitude = byteBuf.readByte();
+
+            boolean north = ByteOperator.getBit(flagWithLatitude, NORTH_INDEX);
+
+            // latitude degree -> 15 bits
+            short latitudeFirst = flagWithLatitude;
+            if (north) {
+                latitudeFirst = (short) (flagWithLatitude & 0x7F);
+            }
+            short latitude = (short) ((latitudeFirst << FLAG_SHIFT) + byteBuf.readByte());
+
+            // latitude minute -> 8 bits
+            byte latitudeMinute = byteBuf.readByte();
+
+            // latitude second -> 8 bits
+            byte latitudeSecond = byteBuf.readByte();
+
+            // east flag -> 1 bit
+            byte flagWithLongitude = byteBuf.readByte();
+
+            boolean east = ByteOperator.getBit(flagWithLongitude, EAST_INDEX);
+
+            // longitude degree -> 15 bits
+            short longitudeFirst = flagWithLongitude;
+            if (east) {
+                longitudeFirst = (short) (flagWithLongitude & 0x7F);
+            }
+            short longitude = (short) ((longitudeFirst << FLAG_SHIFT) + byteBuf.readByte());
+
+            // longitude minute -> 8 bits
+            byte longitudeMinute = byteBuf.readByte();
+
+            // longitude second -> 8 bits
+            byte longitudeSecond = byteBuf.readByte();
+
+            // altitude -> 32 bits
+            int altitude = byteBuf.readInt();
+
+            LispAfiAddress address = new AfiAddressReader().readFrom(byteBuf);
+
+            return new GeoCoordinateAddressBuilder()
+                            .withIsNorth(north)
+                            .withLatitudeDegree(latitude)
+                            .withLatitudeMinute(latitudeMinute)
+                            .withLatitudeSecond(latitudeSecond)
+                            .withIsEast(east)
+                            .withLongitudeDegree(longitude)
+                            .withLongitudeMinute(longitudeMinute)
+                            .withLongitudeSecond(longitudeSecond)
+                            .withAltitude(altitude)
+                            .withAddress(address)
+                            .build();
+        }
+    }
+
+    /**
+     * GeoCoordinate LCAF address writer class.
+     */
+    public static class GeoCoordinateLcafAddressWriter
+                    implements LispAddressWriter<LispGeoCoordinateLcafAddress> {
+
+        private static final int NORTH_SHIFT_BIT = 15;
+        private static final int EAST_SHIFT_BIT = 15;
+
+        private static final int ENABLE_BIT = 1;
+        private static final int DISABLE_BIT = 0;
+
+        @Override
+        public void writeTo(ByteBuf byteBuf, LispGeoCoordinateLcafAddress address)
+                                                    throws LispWriterException {
+
+            int lcafIndex = byteBuf.writerIndex();
+            LispLcafAddress.serializeCommon(byteBuf, address);
+
+            // north flag + latitude degree
+            short north = DISABLE_BIT;
+            if (address.isNorth()) {
+                north = (short) (ENABLE_BIT << NORTH_SHIFT_BIT);
+            }
+
+            byteBuf.writeShort(north + address.latitudeDegree);
+
+            // latitude minute
+            byteBuf.writeByte(address.latitudeMinute);
+
+            // latitude second
+            byteBuf.writeByte(address.latitudeSecond);
+
+            // east flag + longitude degree
+            short east = DISABLE_BIT;
+            if (address.isEast()) {
+                east = (short) (ENABLE_BIT << EAST_SHIFT_BIT);
+            }
+
+            byteBuf.writeShort(east + address.longitudeDegree);
+
+            // longitude minute
+            byteBuf.writeByte(address.longitudeMinute);
+
+            // longitude second
+            byteBuf.writeByte(address.longitudeSecond);
+
+            // altitude
+            byteBuf.writeInt(address.altitude);
+
+            // address
+            AfiAddressWriter writer = new AfiAddressWriter();
+            writer.writeTo(byteBuf, address.getAddress());
+
+            LispLcafAddress.updateLength(lcafIndex, byteBuf);
+        }
+    }
+}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispLcafAddress.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispLcafAddress.java
index 920d356..cd63b8d 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispLcafAddress.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/types/lcaf/LispLcafAddress.java
@@ -30,6 +30,7 @@
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static org.onosproject.lisp.msg.types.lcaf.LispCanonicalAddressFormatEnum.APPLICATION_DATA;
+import static org.onosproject.lisp.msg.types.lcaf.LispCanonicalAddressFormatEnum.GEO_COORDINATE;
 import static org.onosproject.lisp.msg.types.lcaf.LispCanonicalAddressFormatEnum.LIST;
 import static org.onosproject.lisp.msg.types.lcaf.LispCanonicalAddressFormatEnum.MULTICAST;
 import static org.onosproject.lisp.msg.types.lcaf.LispCanonicalAddressFormatEnum.NAT;
@@ -413,6 +414,10 @@
                 return new LispSegmentLcafAddress.SegmentLcafAddressReader().readFrom(byteBuf);
             }
 
+            if (lcafType == GEO_COORDINATE.getLispCode()) {
+                return new LispGeoCoordinateLcafAddress.GeoCoordinateLcafAddressReader().readFrom(byteBuf);
+            }
+
             if (lcafType == NONCE.getLispCode()) {
                 return new LispNonceLcafAddress.NonceLcafAddressReader().readFrom(byteBuf);
             }
@@ -461,6 +466,10 @@
                     new LispSegmentLcafAddress.SegmentLcafAddressWriter().writeTo(byteBuf,
                             (LispSegmentLcafAddress) address);
                     break;
+                case GEO_COORDINATE:
+                    new LispGeoCoordinateLcafAddress.GeoCoordinateLcafAddressWriter().writeTo(byteBuf,
+                            (LispGeoCoordinateLcafAddress) address);
+                    break;
                 case NONCE:
                     new LispNonceLcafAddress.NonceLcafAddressWriter().writeTo(byteBuf,
                             (LispNonceLcafAddress) address);
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/types/lcaf/LispGeoCoordinateLcafAddressTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/types/lcaf/LispGeoCoordinateLcafAddressTest.java
new file mode 100644
index 0000000..fe65c9c
--- /dev/null
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/types/lcaf/LispGeoCoordinateLcafAddressTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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 org.onosproject.lisp.msg.types.lcaf;
+
+import com.google.common.testing.EqualsTester;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.lisp.msg.exceptions.LispParseError;
+import org.onosproject.lisp.msg.exceptions.LispReaderException;
+import org.onosproject.lisp.msg.exceptions.LispWriterException;
+import org.onosproject.lisp.msg.types.LispIpv4Address;
+import org.onosproject.lisp.msg.types.lcaf.LispGeoCoordinateLcafAddress.GeoCoordinateAddressBuilder;
+import org.onosproject.lisp.msg.types.lcaf.LispGeoCoordinateLcafAddress.GeoCoordinateLcafAddressReader;
+import org.onosproject.lisp.msg.types.lcaf.LispGeoCoordinateLcafAddress.GeoCoordinateLcafAddressWriter;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for LispGeoCoordinateLcafAddress class.
+ */
+public class LispGeoCoordinateLcafAddressTest {
+
+    private static final String IP_ADDRESS_1 = "192.168.1.1";
+    private static final String IP_ADDRESS_2 = "192.168.1.2";
+
+    private LispGeoCoordinateLcafAddress address1;
+    private LispGeoCoordinateLcafAddress sameAsAddress1;
+    private LispGeoCoordinateLcafAddress address2;
+
+    @Before
+    public void setup() {
+
+        GeoCoordinateAddressBuilder builder1 = new GeoCoordinateAddressBuilder();
+
+        LispIpv4Address ipv4Address1 =
+                        new LispIpv4Address(IpAddress.valueOf(IP_ADDRESS_1));
+
+        address1 = builder1
+                        .withIsNorth(true)
+                        .withLatitudeDegree((short) 1)
+                        .withLatitudeMinute((byte) 1)
+                        .withLatitudeSecond((byte) 1)
+                        .withIsEast(false)
+                        .withLongitudeDegree((short) 1)
+                        .withLongitudeMinute((byte) 1)
+                        .withLongitudeSecond((byte) 1)
+                        .withAltitude(1)
+                        .withAddress(ipv4Address1)
+                        .build();
+
+        GeoCoordinateAddressBuilder builder2 = new GeoCoordinateAddressBuilder();
+
+        sameAsAddress1 = builder2
+                            .withIsNorth(true)
+                            .withLatitudeDegree((short) 1)
+                            .withLatitudeMinute((byte) 1)
+                            .withLatitudeSecond((byte) 1)
+                            .withIsEast(false)
+                            .withLongitudeDegree((short) 1)
+                            .withLongitudeMinute((byte) 1)
+                            .withLongitudeSecond((byte) 1)
+                            .withAltitude(1)
+                            .withAddress(ipv4Address1)
+                            .build();
+
+        GeoCoordinateAddressBuilder builder3 = new GeoCoordinateAddressBuilder();
+
+        LispIpv4Address ipv4Address2 =
+                        new LispIpv4Address(IpAddress.valueOf(IP_ADDRESS_2));
+
+        address2 = builder3
+                        .withIsNorth(false)
+                        .withLatitudeDegree((short) 2)
+                        .withLatitudeMinute((byte) 2)
+                        .withLatitudeSecond((byte) 2)
+                        .withIsEast(true)
+                        .withLongitudeDegree((short) 2)
+                        .withLongitudeMinute((byte) 2)
+                        .withLongitudeSecond((byte) 2)
+                        .withAltitude(2)
+                        .withAddress(ipv4Address2)
+                        .build();
+    }
+
+    @Test
+    public void testEquality() {
+        new EqualsTester()
+                .addEqualityGroup(address1, sameAsAddress1)
+                .addEqualityGroup(address2).testEquals();
+    }
+
+    @Test
+    public void testConstruction() {
+        LispGeoCoordinateLcafAddress address = address1;
+
+        LispIpv4Address ipv4Address =
+                        new LispIpv4Address(IpAddress.valueOf(IP_ADDRESS_1));
+
+        assertThat(address.isNorth(), is(true));
+        assertThat(address.getLatitudeDegree(), is((short) 1));
+        assertThat(address.getLatitudeMinute(), is((byte) 1));
+        assertThat(address.getLatitudeSecond(), is((byte) 1));
+        assertThat(address.isEast(), is(false));
+        assertThat(address.getLongitudeDegree(), is((short) 1));
+        assertThat(address.getLongitudeMinute(), is((byte) 1));
+        assertThat(address.getLongitudeSecond(), is((byte) 1));
+        assertThat(address.getAltitude(), is(1));
+        assertThat(address.getAddress(), is(ipv4Address));
+    }
+
+    @Test
+    public void testSerialization() throws LispWriterException,
+                                           LispParseError, LispReaderException {
+
+        ByteBuf byteBuf = Unpooled.buffer();
+
+        GeoCoordinateLcafAddressWriter writer = new GeoCoordinateLcafAddressWriter();
+        writer.writeTo(byteBuf, address1);
+
+        GeoCoordinateLcafAddressReader reader = new GeoCoordinateLcafAddressReader();
+        LispGeoCoordinateLcafAddress deserialized = reader.readFrom(byteBuf);
+
+        new EqualsTester().addEqualityGroup(address1, deserialized).testEquals();
+    }
+}