Implement TCP/UDP checksum recalculation. 04/11004/1
authorShigeru Yasuda <s-yasuda@da.jp.nec.com>
Wed, 10 Sep 2014 17:19:15 +0000 (02:19 +0900)
committerShigeru Yasuda <s-yasuda@da.jp.nec.com>
Wed, 10 Sep 2014 17:19:15 +0000 (02:19 +0900)
This patch enables flow filter to modify IPv4 address and TCP/UDP
port number in packets.

Change-Id: Ibf2deb423dac9ec5b90f57f4faae2a5394aa9331
Signed-off-by: Shigeru Yasuda <s-yasuda@da.jp.nec.com>
55 files changed:
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/action/InetAddressAction.java
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4DstActionTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4SrcActionTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpDstActionTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpSrcActionTest.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/ActionList.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/MiscUtils.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/PacketContext.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/StatsReader.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowFilterMap.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/InetAddressActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDscpActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpCodeActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpTypeActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetVlanPcpActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/TpPortActionImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/VTerminalImpl.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/EtherPacket.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacket.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/Inet4Packet.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/L4Packet.java [new file with mode: 0644]
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/PortProtoPacket.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/TcpPacket.java
manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/UdpPacket.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/ArpHandlerTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/GlobalResourceManagerTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MacAddressTableTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MiscUtilsTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestBase.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplClusterTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplDisableNodesTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplWithNodesTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImplTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/MacTableEntryTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImplTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImplTest.java
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImplTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImplTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImplTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImplTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/EtherPacketTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacketTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/Inet4PacketTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/TcpPacketTest.java [new file with mode: 0644]
manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/UdpPacketTest.java [new file with mode: 0644]
manager/northboundIT/src/test/java/org/opendaylight/vtn/manager/northbound/integrationtest/VtnNorthboundIT.java

index 469cdc91971e4ba03ad4bf740fc25bd6297636fd..c8910103378b041c1c6d291178e9578a17824079 100644 (file)
@@ -147,6 +147,15 @@ public abstract class InetAddressAction extends FlowAction {
     @SuppressWarnings("unused")
     private void setInetAddress(String addr) {
         if (addr != null) {
+            if (addr.length() == 0) {
+                StringBuilder builder =
+                    new StringBuilder(getClass().getSimpleName());
+                builder.append(": Address is empty.");
+                validationStatus =
+                    new Status(StatusCode.BADREQUEST, builder.toString());
+                return;
+            }
+
             try {
                 InetAddress iaddr = InetAddress.getByName(addr);
                 Class<?> cls = getAddressClass();
index 7de97fa6ed0413c4db6b53099b8f6ade53c3dd6c..514209ee6edaba665259bd85056a0c0a424fccc5 100644 (file)
@@ -235,9 +235,6 @@ public final class FlowFilter implements Serializable {
      *         The type of this element must be {@link SetInet4SrcAction}.
      *       </li>
      *       <li>This element does not affect packets without IPv4 header.</li>
-     *       <li>
-     *         <strong>This element is not yet supported.</strong>
-     *       </li>
      *     </ul>
      *
      *   <dt>inet4dst
@@ -249,9 +246,6 @@ public final class FlowFilter implements Serializable {
      *         The type of this element must be {@link SetInet4DstAction}.
      *       </li>
      *       <li>This element does not affect packets without IPv4 header.</li>
-     *       <li>
-     *         <strong>This element is not yet supported.</strong>
-     *       </li>
      *     </ul>
      *
      *   <dt>dscp
@@ -276,9 +270,6 @@ public final class FlowFilter implements Serializable {
      *       <li>
      *         This element does not affect packets without TCP or UDP header.
      *       </li>
-     *       <li>
-     *         <strong>This element is not yet supported.</strong>
-     *       </li>
      *     </ul>
      *
      *   <dt>tpdst
@@ -292,9 +283,6 @@ public final class FlowFilter implements Serializable {
      *       <li>
      *         This element does not affect packets without TCP or UDP header.
      *       </li>
-     *       <li>
-     *         <strong>This element is not yet supported.</strong>
-     *       </li>
      *     </ul>
      *
      *   <dt>icmptype
index ff7b697e1ec2abf8b4d1ff8febbcb8446d971fae..10cb951d41037c1440635ec964ba4090e7c2d80b 100644 (file)
@@ -123,6 +123,8 @@ public class SetInet4DstActionTest extends TestBase {
         // Specifying invalid address.
         String[] invalid = {
             // Invalid address
+            "",
+            "  ",
             "invalid_address",
             "100.200.300.400",
 
index cba92f915ab930bdc2c8b2c2d33aaf9b2cdca59a..5c037670c8bf8674b801c7edbe58a4b88ed58037 100644 (file)
@@ -123,6 +123,8 @@ public class SetInet4SrcActionTest extends TestBase {
         // Specifying invalid address.
         String[] invalid = {
             // Invalid address
+            "",
+            "  ",
             "invalid_address",
             "100.200.300.400",
 
index aaa9dd8c33e37bfb9ce0c75aba317490973c5f32..4af270bf6cb039d7ea57dd857161e9ef5f3af6bf 100644 (file)
@@ -32,7 +32,7 @@ public class SetTpDstActionTest extends TestBase {
             assertEquals(port, act.getPort());
         }
 
-        int[] valid = {1, 2, 100, 999, 10000, 29999, 49999, 65534, 65535};
+        int[] valid = {0, 1, 2, 100, 999, 10000, 29999, 49999, 65534, 65535};
         for (int port: valid) {
             SetTpDst sact = new SetTpDst(port);
             SetTpDstAction act = new SetTpDstAction(sact);
index 83c2a93f9923f169b67f5e4b972a58984264d13d..bc6cf41db6bced31a920dd114c362b5c09b2c9eb 100644 (file)
@@ -32,7 +32,7 @@ public class SetTpSrcActionTest extends TestBase {
             assertEquals(port, act.getPort());
         }
 
-        int[] valid = {1, 2, 100, 1000, 9999, 20000, 30000, 65534, 65535};
+        int[] valid = {0, 1, 2, 100, 1000, 9999, 20000, 30000, 65534, 65535};
         for (int port: valid) {
             SetTpSrc sact = new SetTpSrc(port);
             SetTpSrcAction act = new SetTpSrcAction(sact);
index 8e38f2b55b26c494bddfa545b94dd703d785e12c..23ac52f13607a22d1646a7ee691219ded0cb473d 100644 (file)
@@ -10,6 +10,7 @@
 package org.opendaylight.vtn.manager.internal;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import org.opendaylight.controller.sal.action.Action;
@@ -84,14 +85,15 @@ public class ActionList {
     }
 
     /**
-     * Append all SAL actions in the given list to the tail of the action list.
+     * Append all SAL actions in the given collection to the tail of the
+     * action list.
      *
-     * @param list  A SAL actions.
+     * @param c  A collection of SAL actions.
      * @return  This object is always returned.
      */
-    public ActionList addAll(List<? extends Action> list) {
-        if (list != null) {
-            actionList.addAll(list);
+    public ActionList addAll(Collection<? extends Action> c) {
+        if (c != null) {
+            actionList.addAll(c);
         }
         return this;
     }
index 910b5d6f1f0bf9b548b6bcbeafa3e972fec258fd..e93b9ffae76a4365cf202f4c3de28a79e5eed1ae 100644 (file)
 package org.opendaylight.vtn.manager.internal;
 
 import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.internal.cluster.MacVlan;
 
+import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.Status;
@@ -26,6 +28,16 @@ import org.opendaylight.controller.sal.utils.StatusCode;
  * methods.
  */
 public final class MiscUtils {
+    /**
+     * A mask value which represents all bits in a byte value.
+     */
+    public static final int  MASK_BYTE = (1 << Byte.SIZE) - 1;
+
+    /**
+     * A mask value which represents all bits in a short value.
+     */
+    public static final int  MASK_SHORT = (1 << Short.SIZE) - 1;
+
     /**
      * Maximum length of the resource name.
      */
@@ -59,6 +71,21 @@ public final class MiscUtils {
      */
     private static final int  MASK_TP_PORT = 0xffff;
 
+    /**
+     * The number of bits to be shifted to get the first octet in an integer.
+     */
+    private static final int  INT_SHIFT_OCTET1 = 24;
+
+    /**
+     * The number of bits to be shifted to get the second octet in an integer.
+     */
+    private static final int  INT_SHIFT_OCTET2 = 16;
+
+    /**
+     * The number of bits to be shifted to get the third octet in an integer.
+     */
+    private static final int  INT_SHIFT_OCTET3 = 8;
+
     /**
      * Private constructor that protects this class from instantiating.
      */
@@ -206,4 +233,75 @@ public final class MiscUtils {
             throw new IllegalStateException(builder.toString(), e);
         }
     }
+
+    /**
+     * Convert an IPv4 address into an integer.
+     *
+     * @param addr  A {@link InetAddress} instance which represents an IPv4
+     *              address.
+     * @return  An integer value.
+     * @throws IllegalStateException
+     *    An error occurred.
+     */
+    public static int toInteger(InetAddress addr) {
+        if (addr instanceof Inet4Address) {
+            byte[] bytes = addr.getAddress();
+            return NetUtils.byteArray4ToInt(bytes);
+        }
+
+        StringBuilder builder =
+            new StringBuilder("Unexpected InetAddress: addr=");
+        builder.append(addr);
+        throw new IllegalStateException(builder.toString());
+    }
+
+    /**
+     * Copy the contents of the given packet.
+     *
+     * @param src  The source {@link Packet} instance.
+     * @param dst  The destination {@link Packet} instance.
+     * @param <T>  Type of packet.
+     * @return  {@code dst}.
+     * @throws VTNException
+     *    Failed to copy the packet.
+     */
+    public static <T extends Packet> T copy(T src, T dst) throws VTNException {
+        try {
+            byte[] raw = src.serialize();
+            int nbits = raw.length * NetUtils.NumBitsInAByte;
+            dst.deserialize(raw, 0, nbits);
+            return dst;
+        } catch (Exception e) {
+            // This should never happen.
+            throw new VTNException("Failed to copy the packet.", e);
+        }
+    }
+
+    /**
+     * Set an integer value into the given byte array in network byte order.
+     *
+     * @param array  A byte array.
+     * @param off    Index of {@code array} to store value.
+     * @param value  An integer value.
+     */
+    public static void setInt(byte[] array, int off, int value) {
+        int index = off;
+        array[index++] = (byte)(value >>> INT_SHIFT_OCTET1);
+        array[index++] = (byte)(value >>> INT_SHIFT_OCTET2);
+        array[index++] = (byte)(value >>> INT_SHIFT_OCTET3);
+        array[index] = (byte)value;
+    }
+
+    /**
+     * Set a short integer value into the given byte array in network byte
+     * order.
+     *
+     * @param array  A byte array.
+     * @param off    Index of {@code array} to store value.
+     * @param value  A short integer value.
+     */
+    public static void setShort(byte[] array, int off, short value) {
+        array[off] = (byte)(value >>> Byte.SIZE);
+        array[off + 1] = (byte)value;
+    }
 }
index 2346f9b28a24a4f712080f153b85af40c0365cef..c678f3dc7ea3713f00d61ac08630fc56021a9395 100644 (file)
@@ -11,9 +11,12 @@ package org.opendaylight.vtn.manager.internal;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.slf4j.Logger;
@@ -32,10 +35,10 @@ import org.opendaylight.vtn.manager.internal.cluster.ObjectPair;
 import org.opendaylight.vtn.manager.internal.cluster.PortVlan;
 import org.opendaylight.vtn.manager.internal.cluster.RedirectFlowException;
 import org.opendaylight.vtn.manager.internal.cluster.VTNFlow;
-import org.opendaylight.vtn.manager.internal.packet.CachedPacket;
 import org.opendaylight.vtn.manager.internal.packet.EtherPacket;
 import org.opendaylight.vtn.manager.internal.packet.IcmpPacket;
 import org.opendaylight.vtn.manager.internal.packet.Inet4Packet;
+import org.opendaylight.vtn.manager.internal.packet.L4Packet;
 import org.opendaylight.vtn.manager.internal.packet.TcpPacket;
 import org.opendaylight.vtn.manager.internal.packet.UdpPacket;
 
@@ -139,10 +142,10 @@ public class PacketContext implements Cloneable {
     private Inet4Packet  inet4Packet;
 
     /**
-     * A {@link CachedPacket} instance which represents the layer 4 protocol
+     * A {@link L4Packet} instance which represents the layer 4 protocol
      * data.
      */
-    private CachedPacket  l4Packet;
+    private L4Packet  l4Packet;
 
     /**
      * Route resolver for this packet.
@@ -166,9 +169,9 @@ public class PacketContext implements Cloneable {
     private MapReference  mapReference;
 
     /**
-     * A list of SAL actions created by flow filters.
+     * A map that keeps SAL actions created by flow filters.
      */
-    private List<Action>  filterActions;
+    private Map<Class<? extends Action>, Action>  filterActions;
 
     /**
      * The number of virtual node hops caused by REDIRECT flow filter.
@@ -192,6 +195,11 @@ public class PacketContext implements Cloneable {
      */
     private boolean  filterDisabled;
 
+    /**
+     * Set {@code true} at least one flow filter is evaluated.
+     */
+    private boolean  filtered;
+
     /**
      * Determine whether the destination MAC address of this packet is equal to
      * the controller's MAC address or not.
@@ -574,13 +582,13 @@ public class PacketContext implements Cloneable {
     }
 
     /**
-     * Return a {@link CachedPacket} instance which represents layer 4
-     * protocol data.
+     * Return a {@link L4Packet} instance which represents layer 4 protocol
+     * data.
      *
-     * @return  A {@link CachedPacket} instance if found.
+     * @return  A {@link L4Packet} instance if found.
      *          {@code null} if not found.
      */
-    public CachedPacket getL4Packet() {
+    public L4Packet getL4Packet() {
         if (l4Packet == null) {
             Inet4Packet ipv4 = getInet4Packet();
             if (ipv4 != null) {
@@ -677,6 +685,18 @@ public class PacketContext implements Cloneable {
         }
     }
 
+    /**
+     * Determine whether the given match field will be configured in a flow
+     * entry or not.
+     *
+     * @param type  A match type to be tested.
+     * @return  {@code true} only if the given match type will be configured
+     *          in a flow entry.
+     */
+    public boolean hasMatchField(MatchType type) {
+        return matchFields.contains(type);
+    }
+
     /**
      * Add match fields to be configured into an unicast flow entry.
      */
@@ -705,7 +725,7 @@ public class PacketContext implements Cloneable {
         Inet4Packet ipv4 = getInet4Packet();
         if (ipv4 != null) {
             ipv4.setMatch(match, matchFields);
-            CachedPacket l4 = getL4Packet();
+            L4Packet l4 = getL4Packet();
             if (l4 != null) {
                 l4.setMatch(match, matchFields);
             }
@@ -917,21 +937,33 @@ public class PacketContext implements Cloneable {
     public void addFilterAction(Action act) {
         if (!flooding) {
             if (filterActions == null) {
-                filterActions = new ArrayList<Action>();
+                filterActions =
+                    new LinkedHashMap<Class<? extends Action>, Action>();
             }
-            filterActions.add(act);
+            filterActions.put(act.getClass(), act);
+        }
+    }
+
+    /**
+     * Remove the specified flow action from the flow filter action list.
+     *
+     * @param actClass  A class of SAL action to be removed.
+     */
+    public void removeFilterAction(Class<? extends Action> actClass) {
+        if (!flooding && filterActions != null) {
+            filterActions.remove(actClass);
         }
     }
 
     /**
      * Return a list of SAL actions created by flow filters.
      *
-     * @return  A list of SAL actions.
+     * @return  A collection of SAL actions.
      *          {@code null} is returned if no SAL action was created by
      *          flow filter.
      */
-    public List<Action> getFilterActions() {
-        return filterActions;
+    public Collection<Action> getFilterActions() {
+        return (filterActions == null) ? null : filterActions.values();
     }
 
     /**
@@ -941,11 +973,28 @@ public class PacketContext implements Cloneable {
      *    Failed to copy the packet.
      */
     public void commit() throws VTNException {
+        // Commit modification to the Ethernet header.
         etherFrame.commit(this);
-        CachedPacket l4 = l4Packet;
+
+        // Commit modification to layer 4 protocol header.
+        L4Packet l4 = l4Packet;
         Inet4Packet inet4 = inet4Packet;
-        boolean l4Changed = (l4 != null) ? l4.commit(this) : false;
+        boolean l4Changed;
+        if (l4 == null) {
+            l4Changed = false;
+        } else {
+            l4Changed = l4.commit(this);
+            if (l4Changed || inet4.isAddressModified()) {
+                // Update checksum.
+                if (l4.updateChecksum(inet4)) {
+                    l4Changed = true;
+                }
+            }
+        }
+
+        // Commit modification to IPv4 header.
         boolean inet4Changed = (inet4 != null) ? inet4.commit(this) : false;
+
         if (l4Changed) {
             Packet payload = l4.getPacket();
             inet4.getPacket().setPayload(payload);
@@ -1003,6 +1052,27 @@ public class PacketContext implements Cloneable {
         filterDisabled = b;
     }
 
+    /**
+     * Determine whether at least one flow filter is evaluated with this packet
+     * or not.
+     *
+     * @return  {@code true} only if at least one flow filter is evaluated.
+     */
+    public boolean isFiltered() {
+        return filtered;
+    }
+
+    /**
+     * Set a boolean value which determines whetehr at least one flow filter
+     * is evaluated with this packet or not.
+     *
+     * @param b  {@code true} means that at least one flow filter is evaluated
+     *           with this packet.
+     */
+    public void setFiltered(boolean b) {
+        filtered = b;
+    }
+
     /**
      * Determine whether the destination address of this packet is equal to
      * the controller address or not.
@@ -1102,7 +1172,7 @@ public class PacketContext implements Cloneable {
                 pctx.inet4Packet = inet4.clone();
             }
 
-            CachedPacket l4 = pctx.l4Packet;
+            L4Packet l4 = pctx.l4Packet;
             if (l4 != null) {
                 pctx.l4Packet = l4.clone();
             }
index f60275578bd231566eeefb240c420b71ce027820..ff95f66a93c039da75a7056cd7ba3bf456d673b6 100644 (file)
@@ -14,6 +14,9 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.concurrent.TimeUnit;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.opendaylight.vtn.manager.flow.FlowStats;
 
 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
@@ -29,6 +32,12 @@ import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
  * </p>
  */
 public class StatsReader {
+    /**
+     * Logger instance.
+     */
+    private static final Logger  LOG =
+        LoggerFactory.getLogger(StatsReader.class);
+
     /**
      * Statistics manager service.
      */
@@ -47,7 +56,7 @@ public class StatsReader {
     /**
      * Construct a new instance.
      *
-     * @param stMgr    VTN Manager service.
+     * @param stMgr    Statistics manager service.
      * @param update   if {@code true}, flow statistics are derived from
      *                 physical switch. Otherwise this instance will return
      *                 statistics cached in statistics manager.
@@ -73,11 +82,15 @@ public class StatsReader {
      *          the specified flow entry was not found.
      */
     public FlowStats get(FlowEntry fent) {
+        LOG.debug("Request for statistics: {}", fent);
         FlowOnNode stats = getStats(fent);
         if (stats == null) {
+            LOG.debug("Statistics not found: {}", fent);
             return null;
         }
 
+        LOG.debug("Statistics found: fent={}, stat={}", fent, stats);
+
         // Convert duration into the number of milliseconds.
         int sec = stats.getDurationSeconds();
         int nsec = stats.getDurationNanoseconds();
@@ -105,6 +118,8 @@ public class StatsReader {
         if (statsCache != null) {
             nodeMap = statsCache.get(node);
             if (nodeMap != null) {
+                LOG.trace("Use statistics cache: node={}, cache={}",
+                          node, nodeMap);
                 return nodeMap.get(fent);
             }
 
@@ -120,6 +135,8 @@ public class StatsReader {
         List<FlowOnNode> stats = (doUpdate)
             ? statsManager.getFlowsNoCache(node)
             : statsManager.getFlows(node);
+
+        LOG.trace("Statistics for node {}: {}", node, stats);
         for (FlowOnNode st: stats) {
             // Ignore flow tables other than table 0.
             if (st.getTableId() == 0) {
index cc24b955fbb69232ffd01369d5f53e69716eecfa..86da47ec1da61863fc3da825bd9517679c59bb82 100644 (file)
@@ -27,6 +27,10 @@ import org.opendaylight.vtn.manager.flow.action.SetDlSrcAction;
 import org.opendaylight.vtn.manager.flow.action.SetDscpAction;
 import org.opendaylight.vtn.manager.flow.action.SetIcmpCodeAction;
 import org.opendaylight.vtn.manager.flow.action.SetIcmpTypeAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
 import org.opendaylight.vtn.manager.flow.action.SetVlanPcpAction;
 
 import org.opendaylight.vtn.manager.internal.MiscUtils;
@@ -71,15 +75,15 @@ public abstract class FlowActionImpl implements Serializable {
      */
     static {
         CONSTRUCTORS = new HashMap<Class<?>, Constructor<?>>();
-
-        // REVISIT:
-        //   Currenlty flow actions that require recalculation of TCP/UDP
-        //   checksum are not yet supported.
         Class<?>[] classes = {
             SetDlSrcAction.class,
             SetDlDstAction.class,
             SetVlanPcpAction.class,
+            SetInet4SrcAction.class,
+            SetInet4DstAction.class,
             SetDscpAction.class,
+            SetTpSrcAction.class,
+            SetTpDstAction.class,
             SetIcmpTypeAction.class,
             SetIcmpCodeAction.class,
         };
index 88b426c2ff542ee2903559d7852c110d0bf4dbfd..dd97d941e609742665cf4dbe10d4d1ffd06f86dd 100644 (file)
@@ -360,17 +360,15 @@ public abstract class FlowFilterMap implements Serializable, Cloneable {
     public final PacketContext evaluate(VTNManagerImpl mgr, PacketContext pctx,
                                         short vid)
         throws DropFlowException, RedirectFlowException {
-        if (pctx.isFilterDisabled()) {
-            logDisabled(mgr, pctx);
-            return pctx;
-        }
-
-        PacketContext pc;
-        if (flowFilters.isEmpty()) {
-            pc = pctx;
-        } else {
-            pc = getPacketContext(pctx);
-            evaluateImpl(mgr, pc, vid);
+        PacketContext pc = pctx;
+        if (!flowFilters.isEmpty()) {
+            pctx.setFiltered(true);
+            if (pctx.isFilterDisabled()) {
+                logDisabled(mgr, pctx);
+            } else {
+                pc = getPacketContext(pctx);
+                evaluateImpl(mgr, pc, vid);
+            }
         }
 
         return pc;
index 288f9a03f7bfe7312c17763a77786b4c35a513f0..ca6fd3a901ce96ec434fa6571c28abd3e0154c87 100644 (file)
@@ -15,7 +15,6 @@ import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.flow.action.InetAddressAction;
 
 import org.opendaylight.vtn.manager.internal.MiscUtils;
-import org.opendaylight.vtn.manager.internal.PacketContext;
 
 import org.opendaylight.controller.sal.utils.Status;
 
@@ -32,7 +31,7 @@ public abstract class InetAddressActionImpl extends FlowActionImpl {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = -5303473358097612716L;
+    private static final long serialVersionUID = -6711928072439846526L;
 
     /**
      * IP address to be set.
@@ -72,15 +71,6 @@ public abstract class InetAddressActionImpl extends FlowActionImpl {
         return address;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean apply(PacketContext pctx) {
-        // REVISIT: Not yet supported.
-        return false;
-    }
-
     /**
      * Determine whether the given object is identical to this object.
      *
index fb02052f590359627af0cf6b69187ff8f06b5535..bdac8042333caeec7ad38e6dba211d6555b4005b 100644 (file)
@@ -15,6 +15,8 @@ import org.opendaylight.vtn.manager.flow.action.SetDlDstAction;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 import org.opendaylight.vtn.manager.internal.packet.EtherPacket;
 
+import org.opendaylight.controller.sal.action.SetDlDst;
+
 /**
  * Implementation of flow action that modifies destination MAC address in
  * Ethernet frame.
@@ -60,6 +62,7 @@ public final class SetDlDstActionImpl extends DlAddrActionImpl {
         EtherPacket ether = pctx.getEtherPacket();
         byte[] addr = getAddress();
         ether.setDestinationAddress(addr);
+        pctx.addFilterAction(new SetDlDst(addr));
         return true;
     }
 }
index aff3e80143d40559faf321b2168ed0b2de5fdecc..9926414a7c78ff462db158e2479909f511987550 100644 (file)
@@ -15,6 +15,8 @@ import org.opendaylight.vtn.manager.flow.action.SetDlSrcAction;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 import org.opendaylight.vtn.manager.internal.packet.EtherPacket;
 
+import org.opendaylight.controller.sal.action.SetDlSrc;
+
 /**
  * Implementation of flow action that modifies source MAC address in Ethernet
  * frame.
@@ -60,6 +62,7 @@ public final class SetDlSrcActionImpl extends DlAddrActionImpl {
         EtherPacket ether = pctx.getEtherPacket();
         byte[] addr = getAddress();
         ether.setSourceAddress(addr);
+        pctx.addFilterAction(new SetDlSrc(addr));
         return true;
     }
 }
index 18c1483573d95b091d8a5d9f814ec7880f20396b..5c3f2799ed4c7c36b4d96e3621a4a5553d7e5e76 100644 (file)
@@ -17,6 +17,7 @@ import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 import org.opendaylight.vtn.manager.internal.packet.Inet4Packet;
 
+import org.opendaylight.controller.sal.action.SetNwTos;
 import org.opendaylight.controller.sal.utils.StatusCode;
 
 /**
@@ -118,6 +119,8 @@ public final class SetDscpActionImpl extends FlowActionImpl {
         Inet4Packet ipv4 = pctx.getInet4Packet();
         if (ipv4 != null) {
             ipv4.setDscp(dscp);
+            int tos = (int)SetDscpAction.dscpToTos(dscp);
+            pctx.addFilterAction(new SetNwTos(tos));
             return true;
         }
 
index c00c0411e7cc74bbc4b0234923c0557e74b5a538..54f9737f3e7f02af9711db558cb571701b616146 100644 (file)
@@ -15,9 +15,10 @@ import org.opendaylight.vtn.manager.flow.action.SetIcmpCodeAction;
 
 import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
-import org.opendaylight.vtn.manager.internal.packet.CachedPacket;
 import org.opendaylight.vtn.manager.internal.packet.IcmpPacket;
+import org.opendaylight.vtn.manager.internal.packet.L4Packet;
 
+import org.opendaylight.controller.sal.action.SetTpDst;
 import org.opendaylight.controller.sal.utils.StatusCode;
 
 /**
@@ -115,10 +116,11 @@ public final class SetIcmpCodeActionImpl extends FlowActionImpl {
      */
     @Override
     public boolean apply(PacketContext pctx) {
-        CachedPacket packet = pctx.getL4Packet();
+        L4Packet packet = pctx.getL4Packet();
         if (packet instanceof IcmpPacket) {
             IcmpPacket icmp = (IcmpPacket)packet;
             icmp.setCode(code);
+            pctx.addFilterAction(new SetTpDst((int)code));
             return true;
         }
 
index fadec00dd43bc4afda8942053f844c625124fc14..ce6b2b5544c547ba75088363f57aec77bbb9a252 100644 (file)
@@ -15,9 +15,10 @@ import org.opendaylight.vtn.manager.flow.action.SetIcmpTypeAction;
 
 import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
-import org.opendaylight.vtn.manager.internal.packet.CachedPacket;
 import org.opendaylight.vtn.manager.internal.packet.IcmpPacket;
+import org.opendaylight.vtn.manager.internal.packet.L4Packet;
 
+import org.opendaylight.controller.sal.action.SetTpSrc;
 import org.opendaylight.controller.sal.utils.StatusCode;
 
 /**
@@ -115,10 +116,11 @@ public final class SetIcmpTypeActionImpl extends FlowActionImpl {
      */
     @Override
     public boolean apply(PacketContext pctx) {
-        CachedPacket packet = pctx.getL4Packet();
+        L4Packet packet = pctx.getL4Packet();
         if (packet instanceof IcmpPacket) {
             IcmpPacket icmp = (IcmpPacket)packet;
             icmp.setType(type);
+            pctx.addFilterAction(new SetTpSrc((int)type));
             return true;
         }
 
index 505ef045f8c584a44f7652819961d4b36179a213..6ad5e544c3d6036264c3eec54cddcb337c284a37 100644 (file)
@@ -9,9 +9,16 @@
 
 package org.opendaylight.vtn.manager.internal.cluster;
 
+import java.net.InetAddress;
+
 import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
 
+import org.opendaylight.vtn.manager.internal.PacketContext;
+import org.opendaylight.vtn.manager.internal.packet.Inet4Packet;
+
+import org.opendaylight.controller.sal.action.SetNwDst;
+
 /**
  * Implementation of flow action that modifies destination IP address in IPv4
  * header.
@@ -26,7 +33,7 @@ public final class SetInet4DstActionImpl extends InetAddressActionImpl {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 3548684343825850653L;
+    private static final long serialVersionUID = -846214438904204416L;
 
     /**
      * Construct a new instance.
@@ -48,4 +55,20 @@ public final class SetInet4DstActionImpl extends InetAddressActionImpl {
     public SetInet4DstAction getFlowAction() {
         return new SetInet4DstAction(getAddress());
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean apply(PacketContext pctx) {
+        Inet4Packet ipv4 = pctx.getInet4Packet();
+        if (ipv4 != null) {
+            InetAddress iaddr = getAddress();
+            ipv4.setDestinationAddress(iaddr);
+            pctx.addFilterAction(new SetNwDst(iaddr));
+            return true;
+        }
+
+        return false;
+    }
 }
index 56ce16be160f8c52a834908d543cd7d55b16046f..6e609016da6b157ea61d794971afdb02ef035cb4 100644 (file)
@@ -9,9 +9,16 @@
 
 package org.opendaylight.vtn.manager.internal.cluster;
 
+import java.net.InetAddress;
+
 import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
 
+import org.opendaylight.vtn.manager.internal.PacketContext;
+import org.opendaylight.vtn.manager.internal.packet.Inet4Packet;
+
+import org.opendaylight.controller.sal.action.SetNwSrc;
+
 /**
  * Implementation of flow action that modifies source IP address in IPv4
  * header.
@@ -26,7 +33,7 @@ public final class SetInet4SrcActionImpl extends InetAddressActionImpl {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 2930155952373937514L;
+    private static final long serialVersionUID = 2335727194215172858L;
 
     /**
      * Construct a new instance.
@@ -48,4 +55,20 @@ public final class SetInet4SrcActionImpl extends InetAddressActionImpl {
     public SetInet4SrcAction getFlowAction() {
         return new SetInet4SrcAction(getAddress());
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean apply(PacketContext pctx) {
+        Inet4Packet ipv4 = pctx.getInet4Packet();
+        if (ipv4 != null) {
+            InetAddress iaddr = getAddress();
+            ipv4.setSourceAddress(iaddr);
+            pctx.addFilterAction(new SetNwSrc(iaddr));
+            return true;
+        }
+
+        return false;
+    }
 }
index 531541873c4c0b91707c36daf129cac96649d880..8f59d3f667889af05adb08538084d498b33aae31 100644 (file)
@@ -12,6 +12,12 @@ package org.opendaylight.vtn.manager.internal.cluster;
 import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
 
+import org.opendaylight.vtn.manager.internal.PacketContext;
+import org.opendaylight.vtn.manager.internal.packet.L4Packet;
+import org.opendaylight.vtn.manager.internal.packet.PortProtoPacket;
+
+import org.opendaylight.controller.sal.action.SetTpDst;
+
 /**
  * Implementation of flow action that modifies destination port number in
  * TCP or UDP header.
@@ -26,7 +32,7 @@ public final class SetTpDstActionImpl extends TpPortActionImpl {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 6524364010316084813L;
+    private static final long serialVersionUID = -515773805142320193L;
 
     /**
      * Construct a new instance.
@@ -48,4 +54,21 @@ public final class SetTpDstActionImpl extends TpPortActionImpl {
     public SetTpDstAction getFlowAction() {
         return new SetTpDstAction(getPort());
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean apply(PacketContext pctx) {
+        L4Packet packet = pctx.getL4Packet();
+        if (packet instanceof PortProtoPacket) {
+            PortProtoPacket pkt = (PortProtoPacket)packet;
+            int port = getPort();
+            pkt.setDestinationPort(port);
+            pctx.addFilterAction(new SetTpDst(port));
+            return true;
+        }
+
+        return false;
+    }
 }
index 026b65ed2d94ba316046602747d3df29af2e0dac..2bf6357e6187e3d8f76759a3571f8b970d9209f9 100644 (file)
@@ -12,6 +12,12 @@ package org.opendaylight.vtn.manager.internal.cluster;
 import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
 
+import org.opendaylight.vtn.manager.internal.PacketContext;
+import org.opendaylight.vtn.manager.internal.packet.L4Packet;
+import org.opendaylight.vtn.manager.internal.packet.PortProtoPacket;
+
+import org.opendaylight.controller.sal.action.SetTpSrc;
+
 /**
  * Implementation of flow action that modifies source port number in
  * TCP or UDP header.
@@ -26,7 +32,7 @@ public final class SetTpSrcActionImpl extends TpPortActionImpl {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 644126347054787614L;
+    private static final long serialVersionUID = 531081632567093651L;
 
     /**
      * Construct a new instance.
@@ -48,4 +54,21 @@ public final class SetTpSrcActionImpl extends TpPortActionImpl {
     public SetTpSrcAction getFlowAction() {
         return new SetTpSrcAction(getPort());
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean apply(PacketContext pctx) {
+        L4Packet packet = pctx.getL4Packet();
+        if (packet instanceof PortProtoPacket) {
+            PortProtoPacket pkt = (PortProtoPacket)packet;
+            int port = getPort();
+            pkt.setSourcePort(port);
+            pctx.addFilterAction(new SetTpSrc(port));
+            return true;
+        }
+
+        return false;
+    }
 }
index 7aef12b0992dd4352e371ddd12d7d501f1af32f5..b6a95a136a8538844dec632e52ef1f4db6d8d5f5 100644 (file)
@@ -17,6 +17,7 @@ import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 import org.opendaylight.vtn.manager.internal.packet.EtherPacket;
 
+import org.opendaylight.controller.sal.action.SetVlanPcp;
 import org.opendaylight.controller.sal.utils.StatusCode;
 
 /**
@@ -118,6 +119,7 @@ public final class SetVlanPcpActionImpl extends FlowActionImpl {
     public boolean apply(PacketContext pctx) {
         EtherPacket ether = pctx.getEtherPacket();
         ether.setVlanPriority(priority);
+        pctx.addFilterAction(new SetVlanPcp((int)priority));
         return true;
     }
 }
index ef47d3dbcbbdc323d79a6834d634e69778fef3f7..023cc4fd64b24d7e458a9b31c24fc982629af72f 100644 (file)
@@ -13,7 +13,6 @@ import org.opendaylight.vtn.manager.VTNException;
 import org.opendaylight.vtn.manager.flow.action.TpPortAction;
 
 import org.opendaylight.vtn.manager.internal.MiscUtils;
-import org.opendaylight.vtn.manager.internal.PacketContext;
 
 import org.opendaylight.controller.sal.utils.StatusCode;
 
@@ -31,7 +30,7 @@ public abstract class TpPortActionImpl extends FlowActionImpl {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = -8000141230774734830L;
+    private static final long serialVersionUID = 2892201590657891707L;
 
     /**
      * A port number to be set.
@@ -64,15 +63,6 @@ public abstract class TpPortActionImpl extends FlowActionImpl {
         return port;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean apply(PacketContext pctx) {
-        // REVISIT: Not yet supported.
-        return false;
-    }
-
     /**
      * Determine whether the given object is identical to this object.
      *
index 346ca29c7cba3f517cfe778931f4e235c5e61b1b..bcc2c7b3d281ce51b79638e26ac958a759a4f83b 100644 (file)
@@ -32,6 +32,7 @@ import org.opendaylight.vtn.manager.VTerminalConfig;
 import org.opendaylight.vtn.manager.VTerminalIfPath;
 import org.opendaylight.vtn.manager.VTerminalPath;
 
+import org.opendaylight.vtn.manager.internal.LogProvider;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 import org.opendaylight.vtn.manager.internal.VTNManagerImpl;
 import org.opendaylight.vtn.manager.internal.VTNThreadData;
@@ -39,6 +40,7 @@ import org.opendaylight.vtn.manager.internal.VTNThreadData;
 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
 import org.opendaylight.controller.sal.core.NodeConnector;
 import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.match.MatchType;
 import org.opendaylight.controller.sal.packet.PacketResult;
 import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.NetUtils;
@@ -57,7 +59,7 @@ public final class VTerminalImpl extends PortBridge<VTerminalIfImpl> {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = -1930350835055234980L;
+    private static final long serialVersionUID = -4401735057418279006L;
 
     /**
      * Logger instance.
@@ -479,18 +481,47 @@ public final class VTerminalImpl extends PortBridge<VTerminalIfImpl> {
      */
     @Override
     protected PacketResult handlePacket(VTNManagerImpl mgr, PacketContext pctx,
-                                        VirtualMapNode vnode) {
+                                        final VirtualMapNode vnode) {
         RedirectFlowException rex = pctx.getFirstRedirection();
         if (rex == null) {
             // Notify source host of the packet.
             notifyHost(mgr, pctx);
 
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("{}:{}: Disable input from vTerminal interface.",
-                          getContainerName(), vnode.getPath());
+            if (pctx.isFiltered()) {
+                LOG.debug("{}:{}: Discard packet from vTerminal interface: " +
+                          "packet={}", getContainerName(), vnode.getPath(),
+                          pctx.getDescription());
+
+                if (!pctx.isUnicast()) {
+                    // In that case we should specify multicast address in a
+                    // drop flow entry, or it may discard packets to be
+                    // filtered by flow filter.
+                    pctx.addMatchField(MatchType.DL_TYPE);
+                    pctx.addMatchField(MatchType.DL_DST);
+                }
+
+                LogProvider lp = new LogProvider() {
+                    @Override
+                    public Logger getLogger() {
+                        return LOG;
+                    }
+
+                    @Override
+                    public String getLogPrefix() {
+                        StringBuilder builder =
+                            new StringBuilder(getContainerName());
+                        builder.append(':').append(vnode.getPath());
+                        return builder.toString();
+                    }
+                };
+                pctx.installDropFlow(mgr, getNodePath(), lp);
+            } else {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("{}:{}: Disable input from vTerminal interface.",
+                              getContainerName(), vnode.getPath());
+                }
+                vnode.disableInput(mgr, pctx);
             }
-
-            vnode.disableInput(mgr, pctx);
         } else {
             Logger logger = rex.getLogger();
             VNodePath path = getNodePath();
index adbef2141eb3d3ec5bce797080f056411d28cc36..295106e988107998383e931fe7ed7caf8762e4b5 100644 (file)
@@ -234,10 +234,10 @@ public final class EtherPacket implements CachedPacket {
          * @param tag    An {@link IEEE8021Q} instance.
          */
         private void fill(Ethernet ether, IEEE8021Q tag) {
-            if (sourceMac == MAC_NONE) {
+            if (sourceAddress == null) {
                 setSourceAddress(ether.getSourceMACAddress());
             }
-            if (destinationMac == MAC_NONE) {
+            if (destinationAddress == null) {
                 setDestinationAddress(ether.getDestinationMACAddress());
             }
             if (vlanPriority == VLANPRI_NONE && tag != null) {
@@ -283,7 +283,7 @@ public final class EtherPacket implements CachedPacket {
         } else {
             ethType = ether.getEtherType();
             vlanTag = null;
-            vid = 0;
+            vid = MatchType.DL_VLAN_NONE;
         }
 
         values = new Values(vid);
@@ -350,7 +350,7 @@ public final class EtherPacket implements CachedPacket {
     public long getSourceMacAddress() {
         Values v = getValues();
         long mac = v.getSourceMacAddress();
-        if (mac == MAC_NONE) {
+        if (v.getSourceAddress() == null) {
             byte[] addr = packet.getSourceMACAddress();
             v.setSourceAddress(addr);
             mac = v.getSourceMacAddress();
@@ -367,7 +367,7 @@ public final class EtherPacket implements CachedPacket {
     public long getDestinationMacAddress() {
         Values v = getValues();
         long mac = v.getDestinationMacAddress();
-        if (mac == MAC_NONE) {
+        if (v.getDestinationAddress() == null) {
             byte[] addr = packet.getDestinationMACAddress();
             v.setDestinationAddress(addr);
             mac = v.getDestinationMacAddress();
@@ -587,35 +587,47 @@ public final class EtherPacket implements CachedPacket {
      */
     @Override
     public boolean commit(PacketContext pctx) {
-        // We don't need to create a copy of original packet and action to
-        // configure VLAN ID.
-        //   - PacketContext creates Ethernet header and VLAN tag from scratch.
-        //   - Flow action to configure VLAN ID is controller by VBridgeImpl.
+        // We don't need to set modified values to Ethernet and IEEE8021Q
+        // instances because PacketContext always creates Ethernet header and
+        // VLAN tag from scratch.
         boolean mod = false;
         if (modifiedValues != null) {
             if (values.getSourceMacAddress() !=
                 modifiedValues.getSourceMacAddress()) {
                 // Source MAC address was modified.
-                byte[] addr = modifiedValues.getSourceAddress();
-                pctx.addFilterAction(new SetDlSrc(addr));
                 mod = true;
+            } else if (pctx.hasMatchField(MatchType.DL_SRC)) {
+                // Source MAC address is not modified, and it will be specified
+                // in flow match. So we don't need to configure SET_DL_SRC
+                // action.
+                pctx.removeFilterAction(SetDlSrc.class);
             }
 
             if (values.getDestinationMacAddress() !=
                 modifiedValues.getDestinationMacAddress()) {
                 // Destination MAC address was modified.
-                byte[] addr = modifiedValues.getDestinationAddress();
-                pctx.addFilterAction(new SetDlDst(addr));
                 mod = true;
+            } else if (pctx.hasMatchField(MatchType.DL_DST)) {
+                // Destination MAC address is not modified, and it will be
+                // specified in flow match. So we don't need to configure
+                // SET_DL_DST action.
+                pctx.removeFilterAction(SetDlDst.class);
             }
 
             short vlan = modifiedValues.getVlan();
-            if (vlan != 0) {
+            if (vlan == MatchType.DL_VLAN_NONE) {
+                // SET_VLAN_PCP should never be applied to untagged frame.
+                pctx.removeFilterAction(SetVlanPcp.class);
+            } else {
                 byte pri = modifiedValues.getVlanPriority();
                 if (values.getVlanPriority() != pri) {
                     // VLAN priority was modified.
-                    pctx.addFilterAction(new SetVlanPcp((int)pri));
                     mod = true;
+                } else if (pctx.hasMatchField(MatchType.DL_VLAN_PR)) {
+                    // VLAN priority is not modified, and it will be specified
+                    // in flow match. So we don't need to configure
+                    // SET_VLAN_PCP action.
+                    pctx.removeFilterAction(SetVlanPcp.class);
                 }
             }
         }
index 61a8b730cf087a61f5ededf6aef44bd6c50d558f..5380785535ec6e363dd27f322e741156f22513cd 100644 (file)
@@ -13,6 +13,7 @@ import java.util.Set;
 
 import org.opendaylight.vtn.manager.VTNException;
 
+import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 
 import org.opendaylight.controller.sal.action.SetTpDst;
@@ -25,7 +26,7 @@ import org.opendaylight.controller.sal.utils.NetUtils;
 /**
  * {@code IcmpPacket} class implements a cache for an {@link ICMP} instance.
  */
-public final class IcmpPacket implements CachedPacket {
+public final class IcmpPacket implements L4Packet {
     /**
      * A pseudo short value which indicates the byte value is not specified.
      */
@@ -95,9 +96,11 @@ public final class IcmpPacket implements CachedPacket {
          * Set the ICMP type.
          *
          * @param value  A byte integer value which indicates the ICMP type.
+         * @return  A short value which represents the given ICMP type.
          */
-        private void setType(byte value) {
+        private short setType(byte value) {
             type = (short)NetUtils.getUnsignedByte(value);
+            return type;
         }
 
         /**
@@ -124,9 +127,11 @@ public final class IcmpPacket implements CachedPacket {
          * Set the ICMP code.
          *
          * @param value  A byte integer value which indicates the ICMP code.
+         * @return  A short value which represents the given ICMP code.
          */
-        private void setCode(byte value) {
+        private short setCode(byte value) {
             code = (short)NetUtils.getUnsignedByte(value);
+            return code;
         }
 
         /**
@@ -139,10 +144,10 @@ public final class IcmpPacket implements CachedPacket {
          * @param icmp  An {@link ICMP} instance.
          */
         private void fill(ICMP icmp) {
-            if (type != VALUE_NONE) {
+            if (type == VALUE_NONE) {
                 setType(icmp.getType());
             }
-            if (code != VALUE_NONE) {
+            if (code == VALUE_NONE) {
                 setCode(icmp.getCode());
             }
         }
@@ -182,7 +187,7 @@ public final class IcmpPacket implements CachedPacket {
         short type = v.getType();
         if (type == VALUE_NONE) {
             byte b = packet.getType();
-            v.setType(b);
+            type = v.setType(b);
         }
 
         return type;
@@ -208,7 +213,7 @@ public final class IcmpPacket implements CachedPacket {
         short code = v.getCode();
         if (code == VALUE_NONE) {
             byte b = packet.getCode();
-            v.setCode(b);
+            code = v.setCode(b);
         }
 
         return code;
@@ -259,16 +264,7 @@ public final class IcmpPacket implements CachedPacket {
     private ICMP getPacketForWrite() throws VTNException {
         if (cloned) {
             // Create a copy of the original packet.
-            try {
-                byte[] raw = packet.serialize();
-                int nbits = raw.length * NetUtils.NumBitsInAByte;
-                packet = new ICMP();
-                packet.deserialize(raw, 0, nbits);
-            } catch (Exception e) {
-                // This should never happen.
-                throw new VTNException("Failed to copy the packet.", e);
-            }
-
+            packet = MiscUtils.copy(packet, new ICMP());
             cloned = false;
         }
 
@@ -330,31 +326,37 @@ public final class IcmpPacket implements CachedPacket {
         boolean mod = false;
         ICMP icmp = null;
         if (modifiedValues != null) {
+            // At least one flow action that modifies ICMP header is
+            // configured.
+            pctx.addMatchField(MatchType.DL_TYPE);
+            pctx.addMatchField(MatchType.NW_PROTO);
+
             short type = modifiedValues.getType();
             if (values.getType() != type) {
                 // ICMP type was modified.
-                pctx.addFilterAction(new SetTpSrc((int)type));
                 icmp = getPacketForWrite();
                 icmp.setType((byte)type);
                 mod = true;
+            } else if (pctx.hasMatchField(MatchType.TP_SRC)) {
+                // ICMP type in the original packet is unchanged and it will be
+                // specified in flow match. So we don't need to configure
+                // SET_TP_SRC action.
+                pctx.removeFilterAction(SetTpSrc.class);
             }
 
             short code = modifiedValues.getCode();
             if (values.getCode() != code) {
                 // ICMP code was modifled.
-                pctx.addFilterAction(new SetTpDst((int)code));
                 if (icmp == null) {
                     icmp = getPacketForWrite();
                 }
                 icmp.setCode((byte)code);
                 mod = true;
-            }
-
-            if (mod) {
-                // Note that this action must be applied to only ICMPv4
-                // packets.
-                pctx.addMatchField(MatchType.DL_TYPE);
-                pctx.addMatchField(MatchType.NW_PROTO);
+            } else if (pctx.hasMatchField(MatchType.TP_DST)) {
+                // ICMP code in the original packet is unchanged and it will be
+                // specified in flow match. So we don't need to configure
+                // SET_TP_DST action.
+                pctx.removeFilterAction(SetTpDst.class);
             }
         }
 
@@ -383,4 +385,22 @@ public final class IcmpPacket implements CachedPacket {
             throw new IllegalStateException("clone() failed", e);
         }
     }
+
+    // L4Packet
+
+    /**
+     * Calculate the checksum of the packet.
+     *
+     * <p>
+     *   This method does nothing because the ICMP checksum is computed by
+     *   {@link ICMP} class.
+     * </p>
+     *
+     * @param ipv4  Never used.
+     * @return  {@code false}.
+     */
+    @Override
+    public boolean updateChecksum(Inet4Packet ipv4) {
+        return false;
+    }
 }
index b44f9ba5a3fe11a286379382129b07e4b2202147..902d294e79a8926c6f3d3654080f6a55a80eada8 100644 (file)
@@ -9,14 +9,16 @@
 
 package org.opendaylight.vtn.manager.internal.packet;
 
+import java.net.InetAddress;
 import java.util.Set;
 
 import org.opendaylight.vtn.manager.VTNException;
-import org.opendaylight.vtn.manager.flow.action.SetDscpAction;
 
 import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.action.SetNwSrc;
 import org.opendaylight.controller.sal.action.SetNwTos;
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
@@ -39,31 +41,44 @@ public final class Inet4Packet implements CachedPacket {
     private static final byte  DSCP_NONE = -1;
 
     /**
-     * An {@link IPv4} packet.
+     * The number of bytes in an IPv4 address.
      */
-    private IPv4  packet;
+    private static final int ADDR_SIZE = 4;
+
+    /**
+     * Byte offset to the source address in a pseudo IPv4 header used for
+     * computing TCP/UDP checksum.
+     */
+    private static final int CKSUM_OFF_SRC = 0;
+
+    /**
+     * Byte offset to the destination address in a pseudo IPv4 header used for
+     * computing TCP/UDP checksum.
+     */
+    private static final int CKSUM_OFF_DST = 4;
 
     /**
-     * An integer value which indicates the source IP address.
+     * Byte offset to the IP protocol number in a pseudo IPv4 header used for
+     * computing TCP/UDP checksum.
      */
-    private int  sourceAddress;
+    private static final int CKSUM_OFF_PROTO = 9;
 
     /**
-     * An integer value which indicates the destination IP address.
+     * Byte offset to the payload length in a pseudo IPv4 header used for
+     * computing TCP/UDP checksum.
      */
-    private int  destinationAddress;
+    private static final int CKSUM_OFF_LEN = 10;
 
     /**
-     * A boolean value which determines whether {@link #sourceAddress} is
-     * initialized or not.
+     * The number of bytes in a pseudo IPv4 header used for computing
+     * TCP/UDP checksum.
      */
-    private boolean sourceSet;
+    private static final int CKSUM_HEADER_SIZE = 12;
 
     /**
-     * A boolean value which determines whether {@link #destinationAddress} is
-     * initialized or not.
+     * An {@link IPv4} packet.
      */
-    private boolean destinationSet;
+    private IPv4  packet;
 
     /**
      * The IP protocol number in the IPv4 packet.
@@ -89,19 +104,134 @@ public final class Inet4Packet implements CachedPacket {
      * This class describes modifiable fields in IPv4 header.
      */
     private static final class Values implements Cloneable {
+        /**
+         * An integer value which indicates the source IP address.
+         */
+        private int  sourceAddress;
+
+        /**
+         * An integer value which indicates the destination IP address.
+         */
+        private int  destinationAddress;
+
+        /**
+         * An {@link InetAddress} instance which indicates the source IP
+         * address.
+         */
+        private InetAddress  sourceInetAddress;
+
+        /**
+         * An {@link InetAddress} instance which indicates the destination IP
+         * address.
+         */
+        private InetAddress  destinationInetAddress;
+
         /**
          * The DSCP field value in the IPv4 packet.
          */
         private byte  dscp = DSCP_NONE;
 
-        // REVISIT: Source and destination IP address are not yet supported.
-
         /**
          * Constructor.
          */
         private Values() {
         }
 
+        /**
+         * Return the source IP address.
+         *
+         * @return  An integer value which represents the source IP address.
+         *          Note that the returned value is unspecified if
+         *          {@link #getSourceInetAddress()} returns {@code null}.
+         */
+        private int getSourceAddress() {
+            return sourceAddress;
+        }
+
+        /**
+         * Return the source IP address.
+         *
+         * @return  An {@link InetAddress} instance which represents the
+         *          source IP address.
+         *         {@code null} is returned if not configured.
+         */
+        private InetAddress getSourceInetAddress() {
+            return sourceInetAddress;
+        }
+
+        /**
+         * Set the source IP address.
+         *
+         * @param addr  An {@link InetAddress} instance which represents the
+         *              source IPv4 address.
+         */
+        private void setSourceAddress(InetAddress addr) {
+            sourceAddress = MiscUtils.toInteger(addr);
+            sourceInetAddress = addr;
+        }
+
+        /**
+         * Set the source IP address.
+         *
+         * @param addr  An integer value which represents the source IPv4
+         *              address.
+         * @return  An {@link InetAddress} instance which represents the
+         *          given IP address.
+         */
+        private InetAddress setSourceAddress(int addr) {
+            sourceAddress = addr;
+            sourceInetAddress = MiscUtils.toInetAddress(addr);
+            return sourceInetAddress;
+        }
+
+        /**
+         * Return the destination IP address.
+         *
+         * @return  An integer value which represents the destination IP
+         *          address.
+         *          Note that the returned value is unspecified if
+         *          {@link #getDestinationInetAddress()} returns {@code null}.
+         */
+        private int getDestinationAddress() {
+            return destinationAddress;
+        }
+
+        /**
+         * Return the destination IP address.
+         *
+         * @return  An {@link InetAddress} instance which represents the
+         *          destination IP address.
+         *         {@code null} is returned if not configured.
+         */
+        private InetAddress getDestinationInetAddress() {
+            return destinationInetAddress;
+        }
+
+        /**
+         * Set the destination IP address.
+         *
+         * @param addr  An {@link InetAddress} instance which represents the
+         *              destination IPv4 address.
+         */
+        private void setDestinationAddress(InetAddress addr) {
+            destinationAddress = MiscUtils.toInteger(addr);
+            destinationInetAddress = addr;
+        }
+
+        /**
+         * Set the destination IP address.
+         *
+         * @param addr  An integer value which represents the destination IPv4
+         *              address.
+         * @return  An {@link InetAddress} instance which represents the
+         *          given IP address.
+         */
+        private InetAddress setDestinationAddress(int addr) {
+            destinationAddress = addr;
+            destinationInetAddress = MiscUtils.toInetAddress(addr);
+            return destinationInetAddress;
+        }
+
         /**
          * Return the DSCP field value.
          *
@@ -132,7 +262,13 @@ public final class Inet4Packet implements CachedPacket {
          * @param ipv4  An {@link IPv4} instance.
          */
         private void fill(IPv4 ipv4) {
-            if (dscp != DSCP_NONE) {
+            if (sourceInetAddress == null) {
+                setSourceAddress(ipv4.getSourceAddress());
+            }
+            if (destinationInetAddress == null) {
+                setDestinationAddress(ipv4.getDestinationAddress());
+            }
+            if (dscp == DSCP_NONE) {
                 dscp = ipv4.getDiffServ();
             }
         }
@@ -168,12 +304,44 @@ public final class Inet4Packet implements CachedPacket {
      * @return  An integer value which represents the source IP address.
      */
     public int getSourceAddress() {
-        if (!sourceSet) {
-            sourceAddress = packet.getSourceAddress();
-            sourceSet = true;
+        Values v = getValues();
+        int addr;
+        if (v.getSourceInetAddress() == null) {
+            addr = packet.getSourceAddress();
+            v.setSourceAddress(addr);
+        } else {
+            addr = v.getSourceAddress();
+        }
+
+        return addr;
+    }
+
+    /**
+     * Return the source IP address.
+     *
+     * @return  An {@link InetAddress} instance which represents the source
+     *          IP address.
+     */
+    public InetAddress getSourceInetAddress() {
+        Values v = getValues();
+        InetAddress addr = v.getSourceInetAddress();
+        if (addr == null) {
+            int address = packet.getSourceAddress();
+            addr = v.setSourceAddress(address);
         }
 
-        return sourceAddress;
+        return addr;
+    }
+
+    /**
+     * Set the source IP address.
+     *
+     * @param addr  An {@link InetAddress} instance which represents the
+     *              source IPv4 address.
+     */
+    public void setSourceAddress(InetAddress addr) {
+        Values v = getModifiedValues();
+        v.setSourceAddress(addr);
     }
 
     /**
@@ -182,12 +350,44 @@ public final class Inet4Packet implements CachedPacket {
      * @return  An integer value which represents the destination IP address.
      */
     public int getDestinationAddress() {
-        if (!destinationSet) {
-            destinationAddress = packet.getDestinationAddress();
-            destinationSet = true;
+        Values v = getValues();
+        int addr;
+        if (v.getDestinationInetAddress() == null) {
+            addr = packet.getDestinationAddress();
+            v.setDestinationAddress(addr);
+        } else {
+            addr = v.getDestinationAddress();
         }
 
-        return destinationAddress;
+        return addr;
+    }
+
+    /**
+     * Return the destination IP address.
+     *
+     * @return  An {@link InetAddress} instance which represents the
+     *          destination IP address.
+     */
+    public InetAddress getDestinationInetAddress() {
+        Values v = getValues();
+        InetAddress addr = v.getDestinationInetAddress();
+        if (addr == null) {
+            int address = packet.getDestinationAddress();
+            addr = v.setDestinationAddress(address);
+        }
+
+        return addr;
+    }
+
+    /**
+     * Set the destination IP address.
+     *
+     * @param addr  An {@link InetAddress} instance which represents the
+     *              destination IPv4 address.
+     */
+    public void setDestinationAddress(InetAddress addr) {
+        Values v = getModifiedValues();
+        v.setDestinationAddress(addr);
     }
 
     /**
@@ -229,6 +429,43 @@ public final class Inet4Packet implements CachedPacket {
         v.setDscp(dscp);
     }
 
+    /**
+     * Determine whether the source or destination address is modified or not.
+     *
+     * @return  {@code true} only if the source or destination address is
+     *          modified.
+     */
+    public boolean isAddressModified() {
+        if (modifiedValues == null) {
+            return false;
+        }
+
+        return (values.getSourceAddress() !=
+                modifiedValues.getSourceAddress() ||
+                values.getDestinationAddress() !=
+                modifiedValues.getDestinationAddress());
+    }
+
+    /**
+     * Create a pseudo IPv4 header used for computing TCP/UDP checksum.
+     *
+     * @param proto  An IP protocol number.
+     * @param len    The number of octets in a payload.
+     * @return  A byte array which represents the pseudo IPv4 header.
+     */
+    public byte[] getHeaderForChecksum(byte proto, short len) {
+        byte[] header = new byte[CKSUM_HEADER_SIZE];
+
+        int src = getSourceAddress();
+        int dst = getDestinationAddress();
+        MiscUtils.setInt(header, CKSUM_OFF_SRC, src);
+        MiscUtils.setInt(header, CKSUM_OFF_DST, dst);
+        header[CKSUM_OFF_PROTO] = proto;
+        MiscUtils.setShort(header, CKSUM_OFF_LEN, len);
+
+        return header;
+    }
+
     /**
      * Return a {@link Values} instance that keeps current values for
      * IPv4 header fields.
@@ -263,16 +500,8 @@ public final class Inet4Packet implements CachedPacket {
      */
     private IPv4 getPacketForWrite() throws VTNException {
         if (cloned) {
-            try {
-                byte[] raw = packet.serialize();
-                int nbits = raw.length * NetUtils.NumBitsInAByte;
-                packet = new IPv4();
-                packet.deserialize(raw, 0, nbits);
-            } catch (Exception e) {
-                // This should never happen.
-                throw new VTNException("Failed to copy the packet.", e);
-            }
-
+            // Create a copy of the original packet.
+            packet = MiscUtils.copy(packet, new IPv4());
             cloned = false;
         }
 
@@ -316,14 +545,13 @@ public final class Inet4Packet implements CachedPacket {
         MatchType type = MatchType.NW_SRC;
         if (fields.contains(type)) {
             // Test source IP address.
-            match.setField(type, MiscUtils.toInetAddress(getSourceAddress()));
+            match.setField(type, v.getSourceInetAddress());
         }
 
         type = MatchType.NW_DST;
         if (fields.contains(type)) {
             // Test destination IP address.
-            match.setField(type,
-                           MiscUtils.toInetAddress(getDestinationAddress()));
+            match.setField(type, v.getDestinationInetAddress());
         }
 
         type = MatchType.NW_PROTO;
@@ -345,17 +573,55 @@ public final class Inet4Packet implements CachedPacket {
     @Override
     public boolean commit(PacketContext pctx) throws VTNException {
         boolean mod = false;
+        IPv4 ipv4 = null;
         if (modifiedValues != null) {
+            // At least one flow action that modifies IPv4 header is
+            // configured.
+            pctx.addMatchField(MatchType.DL_TYPE);
+
+            if (values.getSourceAddress() !=
+                modifiedValues.getSourceAddress()) {
+                // Source address was modified.
+                InetAddress iaddr = modifiedValues.getSourceInetAddress();
+                ipv4 = getPacketForWrite();
+                ipv4.setSourceAddress(iaddr);
+                mod = true;
+            } else if (pctx.hasMatchField(MatchType.NW_SRC)) {
+                // Source IP address in the original packet is unchanged and
+                // it will be specified in flow match. So we don't need to
+                // configure SET_NW_SRC action.
+                pctx.removeFilterAction(SetNwSrc.class);
+            }
+
+            if (values.getDestinationAddress() !=
+                modifiedValues.getDestinationAddress()) {
+                // Destination address was modified.
+                InetAddress iaddr = modifiedValues.getDestinationInetAddress();
+                if (ipv4 == null) {
+                    ipv4 = getPacketForWrite();
+                }
+                ipv4.setDestinationAddress(iaddr);
+                mod = true;
+            } else if (pctx.hasMatchField(MatchType.NW_DST)) {
+                // Destination IP address in the original packet is unchanged
+                // and it will be specified in flow match. So we don't need to
+                // configure SET_NW_DST action.
+                pctx.removeFilterAction(SetNwDst.class);
+            }
+
             byte dscp = modifiedValues.getDscp();
             if (values.getDscp() != dscp) {
                 // DSCP field was modified.
-                // Note that this action must be applied to only IPv4 packets.
-                int tos = (int)SetDscpAction.dscpToTos(dscp);
-                pctx.addFilterAction(new SetNwTos(tos));
-                pctx.addMatchField(MatchType.DL_TYPE);
-                IPv4 ipv4 = getPacketForWrite();
+                if (ipv4 == null) {
+                    ipv4 = getPacketForWrite();
+                }
                 ipv4.setDiffServ(dscp);
                 mod = true;
+            } else if (pctx.hasMatchField(MatchType.NW_TOS)) {
+                // DSCP value in the original packet is unchanged and it will
+                // be specified in flow match. So we don't need to configure
+                // SET_NW_TOS action.
+                pctx.removeFilterAction(SetNwTos.class);
             }
         }
 
diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/L4Packet.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/L4Packet.java
new file mode 100644 (file)
index 0000000..5703816
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.packet;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+/**
+ * {@code L4Packet} defines interfaces that implements cache for layer 4
+ * protocol header.
+ */
+public interface L4Packet extends CachedPacket {
+    /**
+     * Calculate the checksum of the packet, and set the computed checksum
+     * into a {@link org.opendaylight.controller.sal.packet.Packet} instance
+     * configured in this instance.
+     *
+     * @param ipv4  An {@link Inet4Packet} instance that contains this
+     *              packet.
+     * @return  {@code true} is returned if the checksum field was updated.
+     *          {@code false} is returned if the packet was not modified.
+     * @throws VTNException
+     *    An error occurred.
+     */
+    boolean updateChecksum(Inet4Packet ipv4) throws VTNException;
+
+    /**
+     * Return a deep copy of this instance.
+     *
+     * @return  A deep copy of this instance.
+     */
+    L4Packet clone();
+}
index ab0cf6111b91b87a9d37cf1358b8816d429baf3b..16ea31153d9decc680b665ad1249c2e0147bd09d 100644 (file)
@@ -11,31 +11,271 @@ package org.opendaylight.vtn.manager.internal.packet;
 
 import java.util.Set;
 
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.MiscUtils;
 import org.opendaylight.vtn.manager.internal.PacketContext;
 
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.utils.NetUtils;
 
 /**
- * {@code TcpPacket} class implements a cache for layer 4 protocol header,
- * which identifies the service using 16-bit port number.
+ * {@code PortProtoPacket} class implements a cache for layer 4 protocol
+ * header, which identifies the service using 16-bit port number.
+ *
+ * @param <T>  Type of packet.
  */
-public abstract class PortProtoPacket implements CachedPacket {
+public abstract class PortProtoPacket<T extends Packet> implements L4Packet {
     /**
      * A pseudo port number which indicates the port number is not specified.
      */
     private static final int  PORT_NONE = -1;
 
     /**
-     * The source port number.
+     * Computed checksum that indicates the packet verification succeeded.
+     */
+    private static final int  CKSUM_OK = 0xffff;
+
+    /**
+     * The number of octets in TCP/UDP checksum.
+     */
+    private static final int  CKSUM_BYTES =
+        Short.SIZE / NetUtils.NumBitsInAByte;
+
+    /**
+     * A mask value used to clear LSB.
+     */
+    private static final int  MASK_CLEAR_LSB = ~1;
+
+    /**
+     * Cached values in a protocol header.
+     */
+    private Values  values = new Values();
+
+    /**
+     * Protocol header values to be set.
+     */
+    private Values  modifiedValues;
+
+    /**
+     * Set {@code true} if this instance is created by {@link #clone()}.
      */
-    private int  sourcePort = PORT_NONE;
+    private boolean  cloned;
 
     /**
-     * The destination port number.
+     * This class describes modifiable fields in a protocol hedaer.
      */
-    private int  destinationPort = PORT_NONE;
+    private static final class Values implements Cloneable {
+        /**
+         * The source port number.
+         */
+        private int  sourcePort = PORT_NONE;
+
+        /**
+         * The destination port number.
+         */
+        private int  destinationPort = PORT_NONE;
+
+        /**
+         * Constructor.
+         */
+        private Values() {
+        }
+
+        /**
+         * Return the source port number.
+         *
+         * @return  An integer value which indicates the source port.
+         *          {@link PortProtoPacket#PORT_NONE} is returned if not
+         *          configured.
+         */
+        private int getSourcePort() {
+            return sourcePort;
+        }
+
+        /**
+         * Set the source port number.
+         *
+         * @param port  An integer value which indicates the source port.
+         */
+        private void setSourcePort(int port) {
+            sourcePort = port;
+        }
+
+        /**
+         * Set the source port number.
+         *
+         * @param port  A short integer value which indicates the source port.
+         * @return  An integer value which indicates the source port.
+         */
+        private int setSourcePort(short port) {
+            sourcePort = NetUtils.getUnsignedShort(port);
+            return sourcePort;
+        }
+
+        /**
+         * Return the destination port number.
+         *
+         * @return  An integer value which indicates the destination port.
+         *          {@link PortProtoPacket#PORT_NONE} is returned if not
+         *          configured.
+         */
+        private int getDestinationPort() {
+            return destinationPort;
+        }
+
+        /**
+         * Set the destination port number.
+         *
+         * @param port  An integer value which indicates the destination port.
+         */
+        private void setDestinationPort(int port) {
+            destinationPort = port;
+        }
+
+        /**
+         * Set the destination port number.
+         *
+         * @param port  A short integer value which indicates the destination
+         *              port.
+         * @return  An integer value which indicates the destination port.
+         */
+        private int setDestinationPort(short port) {
+            destinationPort = NetUtils.getUnsignedShort(port);
+            return destinationPort;
+        }
+
+        /**
+         * Fetch all modifiable field values from the given packet.
+         *
+         * <p>
+         *   Field values already cached in this instance are preserved.
+         * </p>
+         *
+         * @param packet  A {@link PortProtoPacket} instance.
+         */
+        private void fill(PortProtoPacket packet) {
+            if (sourcePort == PORT_NONE) {
+                setSourcePort(packet.getRawSourcePort());
+            }
+            if (destinationPort == PORT_NONE) {
+                setDestinationPort(packet.getRawDestinationPort());
+            }
+        }
+
+        /**
+         * Return a shallow copy of this instance.
+         *
+         * @return  A shallow copy of this instance.
+         */
+        @Override
+        public Values clone() {
+            try {
+                return (Values)super.clone();
+            } catch (CloneNotSupportedException e) {
+                // This should never happen.
+                throw new IllegalStateException("clone() failed", e);
+            }
+        }
+    }
+
+    /**
+     * Compute the one's complement sum of all 16-bit words in the given
+     * packet.
+     *
+     * @param ipv4  An {@link Inet4Packet} instance that contains the given
+     *              packet.
+     * @param data  A serialized packet data.
+     * @return  A computed checksum.
+     */
+    private static int computeChecksum(Inet4Packet ipv4, byte[] data) {
+        // Create a pseudo IPv4 header.
+        byte proto = (byte)ipv4.getProtocol();
+        byte[] header = ipv4.getHeaderForChecksum(proto, (short)data.length);
+        int sum = 0;
+        for (int i = 0; i < header.length; i += CKSUM_BYTES) {
+            int v = ((header[i] & MiscUtils.MASK_BYTE) << Byte.SIZE) |
+                (header[i + 1] & MiscUtils.MASK_BYTE);
+            sum += v;
+        }
+
+        int rsize = (data.length & MASK_CLEAR_LSB);
+        for (int i = 0; i < rsize; i += CKSUM_BYTES) {
+            int v = ((data[i] & MiscUtils.MASK_BYTE) << Byte.SIZE) |
+                (data[i + 1] & MiscUtils.MASK_BYTE);
+            sum += v;
+        }
+        if (rsize < data.length) {
+            // Zero padding is needed.
+            int v = (data[rsize] & MiscUtils.MASK_BYTE) << Byte.SIZE;
+            sum += v;
+        }
+
+        int carry = (sum >>> Short.SIZE);
+        return (sum & MiscUtils.MASK_SHORT) + carry;
+    }
+
+    /**
+     * Compute the checksum of the given packet.
+     *
+     * @param ipv4    An {@link Inet4Packet} instance that contains the given
+     *                packet.
+     * @param packet  A {@link Packet} instance.
+     * @param sumOff  Offset in bytes to the checksum field.
+     * @return  A computed checksum.
+     * @throws VTNException
+     *    An error occurred.
+     */
+    protected static final short computeChecksum(Inet4Packet ipv4,
+                                                 Packet packet, int sumOff)
+        throws VTNException {
+        // Serialize the given packet.
+        byte[] data;
+        try {
+            data = packet.serialize();
+        } catch (Exception e) {
+            // This should never happen.
+            throw new VTNException("Failed to serialize the packet.", e);
+        }
+
+        // Clear checksum field.
+        MiscUtils.setShort(data, sumOff, (short)0);
+
+        // Compute checksum.
+        int sum = computeChecksum(ipv4, data);
+        return (short)~sum;
+    }
+
+    /**
+     * Verify the packet using the checksum.
+     *
+     * @param ipv4    An {@link Inet4Packet} instance that contains the given
+     *                packet.
+     * @param packet  A {@link Packet} to be verified.
+     * @return  {@code true} is returned only if the verification succeeded.
+     * @throws VTNException
+     *    An error occurred.
+     */
+    protected static final boolean verifyChecksum(Inet4Packet ipv4,
+                                                  Packet packet)
+        throws VTNException {
+        // Serialize the given packet.
+        byte[] data;
+        try {
+            data = packet.serialize();
+        } catch (Exception e) {
+            // This should never happen.
+            throw new VTNException("Failed to serialize the packet.", e);
+        }
+
+        // Compute checksum.
+        int sum = computeChecksum(ipv4, data);
+        return (sum == CKSUM_OK);
+    }
 
     /**
      * Construct a new instance.
@@ -49,12 +289,24 @@ public abstract class PortProtoPacket implements CachedPacket {
      * @return  An integer value which represents the source port number.
      */
     public final int getSourcePort() {
-        if (sourcePort == PORT_NONE) {
-            short port = getRawSourcePort();
-            sourcePort = NetUtils.getUnsignedShort(port);
+        Values v = getValues();
+        int port = v.getSourcePort();
+        if (port == PORT_NONE) {
+            short p = getRawSourcePort();
+            port = v.setSourcePort(p);
         }
 
-        return sourcePort;
+        return port;
+    }
+
+    /**
+     * Set the source port number.
+     *
+     * @param port  An integer value which indicates the source port.
+     */
+    public final void setSourcePort(int port) {
+        Values v = getModifiedValues();
+        v.setSourcePort(port);
     }
 
     /**
@@ -63,12 +315,37 @@ public abstract class PortProtoPacket implements CachedPacket {
      * @return  An integer value which represents the destination port number.
      */
     public final int getDestinationPort() {
-        if (destinationPort == PORT_NONE) {
-            short port = getRawDestinationPort();
-            destinationPort = NetUtils.getUnsignedShort(port);
+        Values v = getValues();
+        int port = v.getDestinationPort();
+        if (port == PORT_NONE) {
+            short p = getRawDestinationPort();
+            port = v.setDestinationPort(p);
         }
 
-        return destinationPort;
+        return port;
+    }
+
+    /**
+     * Set the destination port number.
+     *
+     * @param port  An integer value which indicates the destination port.
+     */
+    public final void setDestinationPort(int port) {
+        Values v = getModifiedValues();
+        v.setDestinationPort(port);
+    }
+
+    /**
+     * Return a {@link Packet} instance to set modified values.
+     *
+     * @return  A {@link Packet} instance.
+     * @throws VTNException
+     *    Failed to copy the packet.
+     */
+    protected final T getPacketForWrite() throws VTNException {
+        T pkt = getPacketForWrite(cloned);
+        cloned = false;
+        return pkt;
     }
 
     /**
@@ -76,7 +353,7 @@ public abstract class PortProtoPacket implements CachedPacket {
      *
      * @return  A short integer value which represents the source port number.
      */
-    public abstract short getRawSourcePort();
+    protected abstract short getRawSourcePort();
 
     /**
      * Derive the destination port number from the packet.
@@ -84,29 +361,89 @@ public abstract class PortProtoPacket implements CachedPacket {
      * @return  A short integer value which represents the destination port
      *          number.
      */
-    public abstract short getRawDestinationPort();
+    protected abstract short getRawDestinationPort();
+
+    /**
+     * Set the source port number to the given packet.
+     *
+     * @param pkt   A {@link Packet} instance.
+     * @param port  A short integer value which indicates the source port.
+     */
+    protected abstract void setRawSourcePort(T pkt, short port);
+
+    /**
+     * Set the destination port number to the given packet.
+     *
+     * @param pkt   A {@link Packet} instance.
+     * @param port  A short integer value which indicates the destination port.
+     */
+    protected abstract void setRawDestinationPort(T pkt, short port);
+
+    /**
+     * Return a {@link Packet} instance to set modified values.
+     *
+     * @param doCopy {@code true} is passed if the packet configured in this
+     *               instance needs to be copied.
+     * @return  A {@link Packet} instance.
+     * @throws VTNException
+     *    Failed to copy the packet.
+     */
+    protected abstract T getPacketForWrite(boolean doCopy) throws VTNException;
+
+    /**
+     * Return a {@link Values} instance that keeps current values for
+     * protocol header fields.
+     *
+     * @return  A {@link Values} instance.
+     */
+    private Values getValues() {
+        return (modifiedValues == null) ? values : modifiedValues;
+    }
+
+    /**
+     * Return a {@link Values} instance that keeps protocol header field values
+     * to be set.
+     *
+     * @return  A {@link Values} instance.
+     */
+    private Values getModifiedValues() {
+        if (modifiedValues == null) {
+            values.fill(this);
+            modifiedValues = values.clone();
+        }
+
+        return modifiedValues;
+    }
 
     // CachedPacket
 
     /**
      * Configure match fields to test TCP/UDP header in this packet.
      *
+     * <p>
+     *   Note that this method creates match fields that matches the original
+     *   packet. Any modification to the packet is ignored.
+     * </p>
+     *
      * @param match   A {@link Match} instance.
      * @param fields  A set of {@link MatchType} instances corresponding to
      *                match fields to be tested.
      */
     @Override
     public final void setMatch(Match match, Set<MatchType> fields) {
+        Values v = values;
+        v.fill(this);
+
         MatchType type = MatchType.TP_SRC;
         if (fields.contains(type)) {
             // Test source port number.
-            match.setField(type, (short)getSourcePort());
+            match.setField(type, (short)v.getSourcePort());
         }
 
         type = MatchType.TP_DST;
         if (fields.contains(type)) {
             // Test destination port number.
-            match.setField(type, (short)getDestinationPort());
+            match.setField(type, (short)v.getDestinationPort());
         }
     }
 
@@ -114,9 +451,45 @@ public abstract class PortProtoPacket implements CachedPacket {
      * {@inheritDoc}
      */
     @Override
-    public final boolean commit(PacketContext pctx) {
-        // REVISIT: Not yet supported.
-        return false;
+    public final boolean commit(PacketContext pctx) throws VTNException {
+        boolean mod = false;
+        T pkt = null;
+        if (modifiedValues != null) {
+            // At least one flow action that modifies TCP or UDP header is
+            // configured.
+            pctx.addMatchField(MatchType.DL_TYPE);
+            pctx.addMatchField(MatchType.NW_PROTO);
+
+            int src = modifiedValues.getSourcePort();
+            if (values.getSourcePort() != src) {
+                // Source port was modified.
+                pkt = getPacketForWrite();
+                setRawSourcePort(pkt, (short)src);
+                mod = true;
+            } else if (pctx.hasMatchField(MatchType.TP_SRC)) {
+                // Source port in the original packet is unchanged and it will
+                // be specified in flow match. So we don't need to configure
+                // SET_TP_SRC action.
+                pctx.removeFilterAction(SetTpSrc.class);
+            }
+
+            int dst = modifiedValues.getDestinationPort();
+            if (values.getDestinationPort() != dst) {
+                // Destination port was modified.
+                if (pkt == null) {
+                    pkt = getPacketForWrite();
+                }
+                setRawDestinationPort(pkt, (short)dst);
+                mod = true;
+            } else if (pctx.hasMatchField(MatchType.TP_DST)) {
+                // Destination port in the original packet is unchanged and
+                // it will be specified in flow match. So we don't need to
+                // configure SET_TP_DST action.
+                pctx.removeFilterAction(SetTpDst.class);
+            }
+        }
+
+        return mod;
     }
 
     /**
@@ -125,7 +498,17 @@ public abstract class PortProtoPacket implements CachedPacket {
     @Override
     public final PortProtoPacket clone() {
         try {
-            return (PortProtoPacket)super.clone();
+            PortProtoPacket p = (PortProtoPacket)super.clone();
+            Values v = p.values;
+            p.values = v.clone();
+
+            v = p.modifiedValues;
+            if (v != null) {
+                p.modifiedValues = v.clone();
+            }
+            p.cloned = true;
+
+            return p;
         } catch (CloneNotSupportedException e) {
             // This should never happen.
             throw new IllegalStateException("clone() failed", e);
index ad8ff341971764f03c69ec4da3b71dee2f58ac1d..5cb6ca86b1cae9baf38c035f91c2c1c5fcc84db6 100644 (file)
@@ -9,16 +9,25 @@
 
 package org.opendaylight.vtn.manager.internal.packet;
 
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.MiscUtils;
+
 import org.opendaylight.controller.sal.packet.TCP;
 
 /**
  * {@code TcpPacket} class implements a cache for a {@link TCP} instance.
  */
-public final class TcpPacket extends PortProtoPacket {
+public final class TcpPacket extends PortProtoPacket<TCP> {
+    /**
+     * Byte offset to the checksum field in TCP header.
+     */
+    private static final int  TCP_OFF_CHECKSUM = 16;
+
     /**
      * A {@link TCP} packet.
      */
-    private final TCP  packet;
+    private TCP  packet;
 
     /**
      * Construct a new instance.
@@ -29,13 +38,15 @@ public final class TcpPacket extends PortProtoPacket {
         packet = tcp;
     }
 
+    // PortProtoPacket
+
     /**
      * Derive the source port number from the packet.
      *
      * @return  A short integer value which represents the source port number.
      */
     @Override
-    public short getRawSourcePort() {
+    protected short getRawSourcePort() {
         return packet.getSourcePort();
     }
 
@@ -46,10 +57,75 @@ public final class TcpPacket extends PortProtoPacket {
      *          number.
      */
     @Override
-    public short getRawDestinationPort() {
+    protected short getRawDestinationPort() {
         return packet.getDestinationPort();
     }
 
+    /**
+     * Set the source port number to the given packet.
+     *
+     * @param pkt   A {@link TCP} instance.
+     * @param port  A short integer value which indicates the source port.
+     */
+    @Override
+    protected void setRawSourcePort(TCP pkt, short port) {
+        pkt.setSourcePort(port);
+    }
+
+    /**
+     * Set the destination port number to the given packet.
+     *
+     * @param pkt   A {@link TCP} instance.
+     * @param port  A short integer value which indicates the destination port.
+     */
+    @Override
+    protected void setRawDestinationPort(TCP pkt, short port) {
+        pkt.setDestinationPort(port);
+    }
+
+    /**
+     * Return a {@link TCP} instance to set modified values.
+     *
+     * @param doCopy {@code true} is passed if the packet configured in this
+     *               instance needs to be copied.
+     * @return  A {@link TCP} instance.
+     * @throws VTNException
+     *    Failed to copy the packet.
+     */
+    @Override
+    protected TCP getPacketForWrite(boolean doCopy) throws VTNException {
+        TCP pkt;
+        if (doCopy) {
+            pkt = MiscUtils.copy(packet, new TCP());
+            packet = pkt;
+        } else {
+            pkt = packet;
+        }
+
+        return pkt;
+    }
+
+    // L4Packet
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean updateChecksum(Inet4Packet ipv4) throws VTNException {
+        short sum = packet.getChecksum();
+        TCP pkt = getPacketForWrite();
+        short newSum = computeChecksum(ipv4, pkt, TCP_OFF_CHECKSUM);
+        boolean mod;
+        if (sum != newSum) {
+            pkt.setChecksum(newSum);
+            mod = true;
+        } else {
+            mod = false;
+        }
+
+        return mod;
+    }
+
     // CachedPacket
 
     /**
index b35adcb361bcd00a6f01f0209121a160c4e34618..624a7395893cd5233e26bd200f524652ca1a3364 100644 (file)
@@ -9,16 +9,30 @@
 
 package org.opendaylight.vtn.manager.internal.packet;
 
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.MiscUtils;
+
 import org.opendaylight.controller.sal.packet.UDP;
 
 /**
  * {@code UdpPacket} class implements a cache for a {@link UDP} instance.
  */
-public final class UdpPacket extends PortProtoPacket {
+public final class UdpPacket extends PortProtoPacket<UDP> {
+    /**
+     * Byte offset to the checksum field in UDP header.
+     */
+    private static final int  UDP_OFF_CHECKSUM = 6;
+
+    /**
+     * Checksum value which indicates the checksum is disabled.
+     */
+    private static final short  UDP_CKSUM_DISABLED = 0;
+
     /**
      * A {@link UDP} packet.
      */
-    private final UDP  packet;
+    private UDP  packet;
 
     /**
      * Construct a new instance.
@@ -35,7 +49,7 @@ public final class UdpPacket extends PortProtoPacket {
      * @return  A short integer value which represents the source port number.
      */
     @Override
-    public short getRawSourcePort() {
+    protected short getRawSourcePort() {
         return packet.getSourcePort();
     }
 
@@ -46,10 +60,80 @@ public final class UdpPacket extends PortProtoPacket {
      *          number.
      */
     @Override
-    public short getRawDestinationPort() {
+    protected short getRawDestinationPort() {
         return packet.getDestinationPort();
     }
 
+    /**
+     * Set the source port number to the given packet.
+     *
+     * @param pkt   A {@link UDP} instance.
+     * @param port  A short integer value which indicates the source port.
+     */
+    @Override
+    protected void setRawSourcePort(UDP pkt, short port) {
+        pkt.setSourcePort(port);
+    }
+
+    /**
+     * Set the destination port number to the given packet.
+     *
+     * @param pkt   A {@link UDP} instance.
+     * @param port  A short integer value which indicates the destination port.
+     */
+    @Override
+    protected void setRawDestinationPort(UDP pkt, short port) {
+        pkt.setDestinationPort(port);
+    }
+
+    /**
+     * Return a {@link UDP} instance to set modified values.
+     *
+     * @param doCopy {@code true} is passed if the packet configured in this
+     *               instance needs to be copied.
+     * @return  A {@link UDP} instance.
+     * @throws VTNException
+     *    Failed to copy the packet.
+     */
+    @Override
+    protected UDP getPacketForWrite(boolean doCopy) throws VTNException {
+        UDP pkt;
+        if (doCopy) {
+            pkt = MiscUtils.copy(packet, new UDP());
+            packet = pkt;
+        } else {
+            pkt = packet;
+        }
+
+        return pkt;
+    }
+
+    // L4Packet
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean updateChecksum(Inet4Packet ipv4) throws VTNException {
+        // Update checksum only if the UDP packet contains a valid checksum.
+        boolean mod = false;
+        short sum = packet.getChecksum();
+        if (sum != 0) {
+            UDP pkt = getPacketForWrite();
+            short newSum = computeChecksum(ipv4, pkt, UDP_OFF_CHECKSUM);
+            if (newSum == UDP_CKSUM_DISABLED) {
+                // Set all bits in the checksum field instead of zero.
+                newSum = (short)~newSum;
+            }
+            if (sum != newSum) {
+                pkt.setChecksum(newSum);
+                mod = true;
+            }
+        }
+
+        return mod;
+    }
+
     // CachedPacket
 
     /**
index ce27dba814f4810098185654a33164d7982bddb8..d9e3bdcbd1031aa0b70c9f7e2634a48c47f7d69c 100644 (file)
@@ -219,9 +219,8 @@ public class ArpHandlerTest extends VTNManagerImplTestCommon {
         swMgr.addSubnet(sconf);
 
         // in case VLAN ID doesn't match.
-        InetAddress ia
-            = getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                    (byte)0, (byte)254});
+        InetAddress ia = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)0, (byte)254});
 
         Subnet sub = swMgr.getSubnetByNetworkAddress(ia);
         sub.setVlan((short)2);
@@ -233,7 +232,7 @@ public class ArpHandlerTest extends VTNManagerImplTestCommon {
         assertEquals(PacketResult.IGNORED, result);
         assertEquals(0, dataList.size());
 
-        ia = getInetAddressFromAddress(HOST_IP);
+        ia = createInetAddress(HOST_IP);
         sub = swMgr.getSubnetByNetworkAddress(ia);
         sub.setVlan((short)0);
         for (Node node : existNodes) {
@@ -484,7 +483,7 @@ public class ArpHandlerTest extends VTNManagerImplTestCommon {
             unexpected(e);
         }
 
-        InetAddress ia = getInetAddressFromAddress(
+        InetAddress ia = createInetAddress(
             new byte[] {(byte)10, (byte)0, (byte)0, (byte)1});
 
         byte [] mac = new byte [] {0x00, 0x00, 0x00, 0x11, 0x22, 0x33};
index 9542743d34902d643f73933d0ae465e013d56a24..0c948bdbffba8d8687d7eabc1be377acbeb9e10c 100644 (file)
@@ -2341,7 +2341,7 @@ public class GlobalResourceManagerTest extends TestBase {
     @Test
     public void testGetRemoteClusterSize() {
         InetAddress loopbackAddr = InetAddress.getLoopbackAddress();
-        InetAddress otherAddr = getInetAddressFromAddress(
+        InetAddress otherAddr = createInetAddress(
             new byte[] {(byte)10, (byte)0, (byte)1, (byte)99});
 
         ComponentImpl c = new ComponentImpl(null, null, null);
@@ -2446,16 +2446,15 @@ public class GlobalResourceManagerTest extends TestBase {
         List<MacAddressEntry> entries = getMacEntries(vtnMgr, bpath);
         assertEquals(0, entries.size());
 
-        InetAddress ipaddr
-            = getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                    (byte)0, (byte)2});
+        InetAddress ipaddr = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)0, (byte)2});
         mac = 2L;
         MacTableEntryId eidRemote = new MacTableEntryId(ipaddr, 2L, bpath, mac);
         MacTableEntry entRemote = new MacTableEntry(eidRemote, nc, (short)0,
                                                     ipaddr);
 
-        ipaddr = getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                       (byte)0, (byte)3});
+        ipaddr = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)0, (byte)3});
         mac = 3L;
         MacTableEntryId eidRemote2 = new MacTableEntryId(ipaddr, 3L, bpath,
                                                          mac);
index 7d1bcfd9401465f4ed7688fa34f9e5f41699aae6..aaa9b337cd04b17110cae4d5c3b11f875db2e6c8 100644 (file)
@@ -171,8 +171,8 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
             PacketContext ncpctx = createARPPacketContext(src, dst, sender, target,
                                           (short)-1, connectors.get(1), ARP.REQUEST);
 
-            InetAddress ia = getInetAddressFromAddress(sender);
-            InetAddress ia2 = getInetAddressFromAddress(sender2);
+            InetAddress ia = createInetAddress(sender);
+            InetAddress ia2 = createInetAddress(sender2);
             Set<InetAddress> ipSet = new HashSet<InetAddress>();
 
             // get from empty table.
@@ -321,7 +321,7 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
             ARP.REQUEST);
         bnode.setPath(ipath1);
         tbl.add(pctx, bnode);
-        InetAddress ipaddr = getInetAddressFromAddress(sender);
+        InetAddress ipaddr = createInetAddress(sender);
 
         long key = 1000L;
         tent = new MacTableEntry(path1, key, connectors.get(0), (short)0,
@@ -439,7 +439,7 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
                                   (byte)0x00, (byte)0x00, (byte)0x01};
         byte [] target = new byte[] {(byte)192, (byte)168, (byte)0, (byte)250};
         byte [] sender = new byte[] {(byte)192, (byte)168, (byte)0, (byte)1};
-        InetAddress ia = getInetAddressFromAddress(sender);
+        InetAddress ia = createInetAddress(sender);
         Set<InetAddress> ipSet = new HashSet<InetAddress>();
 
         Node node = NodeCreator.createOFNode(Long.valueOf(0L));
@@ -476,7 +476,7 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
 
         // change IP Address
         sender = new byte[] {(byte)192, (byte)168, (byte)0, (byte)2};
-        ia = getInetAddressFromAddress(sender);
+        ia = createInetAddress(sender);
         pctx = createARPPacketContext(src, dst, sender, target,
                 vlan, nc, ARP.REQUEST);
         tbl.add(pctx, bnode);
@@ -584,12 +584,10 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
             tbl1, tbl2, tbl3, tbl4, tbl5, tbl6, tbl7,
         };
 
-        InetAddress contIpAddr1 =
-            getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                  (byte)100, (byte)254});
-        InetAddress contIpAddr2 =
-            getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                  (byte)100, (byte)253});
+        InetAddress contIpAddr1 = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)100, (byte)254});
+        InetAddress contIpAddr2 = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)100, (byte)253});
 
         List<NodeConnector> connectors = createNodeConnectors(3, false);
         short vlan = 0;
@@ -617,7 +615,7 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
                 tblArray[i].add(pctx, bnode);
             }
 
-            InetAddress ipaddr = getInetAddressFromAddress(sender);
+            InetAddress ipaddr = createInetAddress(sender);
             MacTableEntryId mentid = new MacTableEntryId(
                 ((id % 2) == 0) ? contIpAddr1 : contIpAddr2,
                 (long)id, path7, NetUtils.byteArray6ToLong(src));
@@ -742,7 +740,7 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
         VlanMapPath vpath = new VlanMapPath(path, ifName);
         MacAddressTable tbl = new MacAddressTable(mgr, path, 600);
 
-        InetAddress contIpAddr = getInetAddressFromAddress(
+        InetAddress contIpAddr = createInetAddress(
             new byte[] {(byte)192, (byte)168, (byte)100, (byte)254});
 
         List<NodeConnector> connectors = createNodeConnectors(3, false);
@@ -756,11 +754,11 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
                                         (byte)100, (byte)iphost};
 
             long key = NetUtils.byteArray6ToLong(src);
-            InetAddress ipaddr = getInetAddressFromAddress(sender);
+            InetAddress ipaddr = createInetAddress(sender);
             MacTableEntry tent = new MacTableEntry(
                 ipath, key, connectors.get(iphost % 3),
                 (short)(((vlan / 3) > 0) ? (vlan / 3) : -1), ipaddr);
-            InetAddress ipaddr2 = getInetAddressFromAddress(sender);
+            InetAddress ipaddr2 = createInetAddress(sender);
             MacTableEntry tent2 = new MacTableEntry(
                 ipath, key, connectors.get(iphost % 3),
                 (short)(((vlan / 3) > 0) ? (vlan / 3) : -1), ipaddr2);
@@ -877,11 +875,9 @@ public class MacAddressTableTest extends TestUseVTNManagerBase {
 
         // create MacTableEntry added by remote node.
         short vlanRemote = 0;
-        InetAddress ipRemoteNode
-            = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
-        InetAddress ipRemoteEnt
-            = getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                    (byte)100, (byte)1});
+        InetAddress ipRemoteNode = createInetAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipRemoteEnt = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)100, (byte)1});
         long macKey = 99L;
         MacTableEntryId evidRemote = new MacTableEntryId(ipRemoteNode, 0L,
                                                          path, macKey);
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MiscUtilsTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MiscUtilsTest.java
new file mode 100644 (file)
index 0000000..433fce7
--- /dev/null
@@ -0,0 +1,846 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.ICMP;
+import org.opendaylight.controller.sal.packet.IEEE8021Q;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.TCP;
+import org.opendaylight.controller.sal.packet.UDP;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * JUnit test for {@link MiscUtils}
+ */
+public class MiscUtilsTest extends TestBase {
+    /**
+     * Verify static constants.
+     */
+    @Test
+    public void testConstants() {
+        assertEquals(0xff, MiscUtils.MASK_BYTE);
+        assertEquals(0xffff, MiscUtils.MASK_SHORT);
+    }
+
+    /**
+     * Test case for {@link MiscUtils#hashCode(long)}.
+     */
+    @Test
+    public void testHashCode() {
+        Random rand = new Random();
+        for (int i = 0; i < 50; i++) {
+            int hi = rand.nextInt();
+            int lo = rand.nextInt();
+            long l = ((long)hi << Integer.SIZE) | (long)(lo & 0xffffffffL);
+            assertEquals(hi ^ lo, MiscUtils.hashCode(l));
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#formatMacAddress(long mac)}.
+     */
+    @Test
+    public void testFormatMacAddress() {
+        assertEquals("00:00:00:00:00:00", MiscUtils.formatMacAddress(0L));
+        assertEquals("00:00:00:00:00:01", MiscUtils.formatMacAddress(1L));
+        assertEquals("00:00:00:00:00:ff", MiscUtils.formatMacAddress(0xffL));
+        assertEquals("00:00:00:00:a0:ff", MiscUtils.formatMacAddress(0xa0ffL));
+        assertEquals("00:00:00:12:34:56",
+                     MiscUtils.formatMacAddress(0x123456L));
+        assertEquals("aa:bb:cc:dd:ee:ff",
+                     MiscUtils.formatMacAddress(0xaabbccddeeffL));
+    }
+
+    /**
+     * Test case for {@link MiscUtils#checkName(String, String)}.
+     */
+    @Test
+    public void testCheckName() {
+        String[] descriptions = {"desc-1", "desc-2", "desc-3"};
+
+        String[] good = {
+            "a",
+            "1",
+            "12",
+            "name",
+            "NaMe_",
+            "GooD_NamE1",
+            "1234567890123456789012345678901",
+        };
+
+        Map<String, String> badRequests = new HashMap<String, String>();
+        badRequests.put(null, "%s name cannot be null");
+        badRequests.put("", "%s name cannot be empty");
+        badRequests.put("12345678901234567890123456789012",
+                        "%s name is too long");
+        badRequests.put("_name", "%s name contains invalid character");
+
+        List<Character> badCharacters = new ArrayList<Character>();
+        for (char c = 0; c < 0x80; c++) {
+            if (c == '_') {
+                continue;
+            }
+
+            int type = Character.getType(c);
+            if (type != Character.DECIMAL_DIGIT_NUMBER &&
+                type != Character.UPPERCASE_LETTER &&
+                type != Character.LOWERCASE_LETTER) {
+                badCharacters.add(c);
+            }
+        }
+
+        // Unicode fullwidth digit zero.
+        badCharacters.add((char)0xff10);
+
+        // Unicode hiragana letter A.
+        badCharacters.add((char)0x3042);
+
+        List<String> badNames = new ArrayList<String>();
+        for (char bad: badCharacters) {
+            StringBuilder builder = new StringBuilder();
+            badNames.add(builder.append(bad).append("name").toString());
+
+            builder = new StringBuilder("na");
+            badNames.add(builder.append(bad).append("me").toString());
+
+            builder = new StringBuilder("name");
+            badNames.add(builder.append(bad).toString());
+        }
+
+        for (String desc: descriptions) {
+            for (String name: good) {
+                try {
+                    MiscUtils.checkName(desc, name);
+                } catch (Exception e) {
+                    unexpected(e);
+                }
+            }
+
+            for (Map.Entry<String, String> entry: badRequests.entrySet()) {
+                String name = entry.getKey();
+                String msg = String.format(entry.getValue(), desc);
+
+                try {
+                    MiscUtils.checkName(desc, name);
+                    unexpected();
+                } catch (VTNException e) {
+                    checkException(e, msg);
+                }
+            }
+
+            String msg = desc + " name contains invalid character";
+            for (String name: badNames) {
+                try {
+                    MiscUtils.checkName(desc, name);
+                    unexpected();
+                } catch (VTNException e) {
+                    checkException(e, msg);
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#checkVlan(short)}.
+     */
+    @Test
+    public void testCheckVlan() {
+        for (short vid = 0; vid <= 4095; vid++) {
+            try {
+                MiscUtils.checkVlan(vid);
+            } catch (Exception e) {
+                unexpected(e);
+            }
+        }
+
+        short[] badVids = {
+            Short.MIN_VALUE, -30000, -20000, -10000, -5000, -100, -23, -2, -1,
+            4096, 4097, 6000, 8000, 10000, 15000, 20000, 30000, 32000,
+            Short.MAX_VALUE,
+        };
+        for (short vid: badVids) {
+            try {
+                MiscUtils.checkVlan(vid);
+                unexpected();
+            } catch (VTNException e) {
+                checkException(e, "Invalid VLAN ID: " + vid);
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#isVlanPriorityValid(byte)}.
+     */
+    @Test
+    public void testIsVlanPriorityValid() {
+        for (int i = 0; i <= 0xff; i++) {
+            byte pri = (byte)i;
+            assertEquals(pri >= 0 && pri <= 7,
+                         MiscUtils.isVlanPriorityValid(pri));
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#isDscpValid(byte)}.
+     */
+    @Test
+    public void testIsDscpValid() {
+        for (int i = 0; i <= 0xff; i++) {
+            byte dscp = (byte)i;
+            assertEquals(dscp >= 0 && dscp <= 63, MiscUtils.isDscpValid(dscp));
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#isIcmpValueValid(short)}.
+     */
+    @Test
+    public void testIsIcmpValueValid() {
+        for (short v = 0; v <= 0xff; v++) {
+            assertTrue(MiscUtils.isIcmpValueValid(v));
+        }
+
+        short[] badValues = {
+            Short.MIN_VALUE, -30000, -20000, -10000, -5000, -100, -14, -2, -1,
+            256, 257, 300, 2000, 8000, 12000, 28000, 30000, Short.MAX_VALUE,
+        };
+        for (short v: badValues) {
+            assertFalse(MiscUtils.isIcmpValueValid(v));
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#isPortNumberValid(int)}.
+     */
+    @Test
+    public void testIsPortNumberValid() {
+        int[] validPorts = {
+            0, 1, 44, 555, 1000, 4000, 10000, 13000, 20000, 30000, 40000,
+            50000, 65000, 65534, 65535,
+        };
+        for (int port: validPorts) {
+            assertTrue(MiscUtils.isPortNumberValid(port));
+        }
+
+        int[] badValues = {
+            Integer.MIN_VALUE, (int)0xfff00000, (int)0xf0000000, -10000,
+            -100, -3, -2, -1,
+            0x10000, 0x10001, 0x11000, 0x100000, 0x333333,
+            0x444444, 0x500000, 0x1000000, 0xa000000, 0x10000000,
+            0x50000000, 0x7f000000, Integer.MAX_VALUE,
+        };
+        for (int port: badValues) {
+            assertFalse(MiscUtils.isPortNumberValid(port));
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#argumentIsNull(String)}.
+     */
+    @Test
+    public void testArgumentIsNull() {
+        String[] descriptions = {"desc-1", "desc-2", "desc-3"};
+        for (String desc: descriptions) {
+            Status st = MiscUtils.argumentIsNull(desc);
+            checkStatus(st, desc + " cannot be null");
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#toInetAddress(int)} and
+     * {@link MiscUtils#toInteger(InetAddress)}.
+     */
+    @Test
+    public void testToInetAddress() {
+        Random rand = new Random();
+        for (int i = 0; i < 100; i++) {
+            int v = rand.nextInt();
+            InetAddress iaddr = MiscUtils.toInetAddress(v);
+            assertTrue(iaddr instanceof Inet4Address);
+
+            byte[] raw = iaddr.getAddress();
+            assertEquals(4, raw.length);
+            for (int j = 0; j < raw.length; j++) {
+                byte b = (byte)(v >>> ((3 - j) * Byte.SIZE));
+                assertEquals(b, raw[j]);
+            }
+
+            assertEquals(v, MiscUtils.toInteger(iaddr));
+        }
+
+        List<InetAddress> invalid = new ArrayList<InetAddress>();
+        invalid.add(null);
+        try {
+            invalid.add(InetAddress.getByName("::1"));
+        } catch (Exception e) {
+            unexpected(e);
+        }
+
+        for (InetAddress iaddr: invalid) {
+            try {
+                MiscUtils.toInteger(iaddr);
+                unexpected();
+            } catch (IllegalStateException e) {
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#copy(Packet, Packet)}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testCopyPacket() throws Exception {
+        // Specifying null.
+        try {
+            MiscUtils.copy((Packet)null, (Packet)null);
+            unexpected();
+        } catch (VTNException e) {
+            checkException(e, "Failed to copy the packet.",
+                           StatusCode.INTERNALERROR);
+            assertTrue(e.getCause() instanceof NullPointerException);
+        }
+
+        byte[] raw = {
+            (byte)0x7c, (byte)0xe8, (byte)0x07, (byte)0xde,
+            (byte)0xc5, (byte)0x1b, (byte)0x40, (byte)0x86,
+            (byte)0xc0, (byte)0xa1, (byte)0xe3, (byte)0xe0,
+            (byte)0x90, (byte)0xdd, (byte)0x0c, (byte)0xe6,
+        };
+
+        // Ensure that Ethernet instance with raw payload can be copied.
+        byte[] srcMac = {
+            (byte)0x00, (byte)0x11, (byte)0x22,
+            (byte)0x33, (byte)0x44, (byte)0x55,
+        };
+        byte[] dstMac = {
+            (byte)0xf0, (byte)0xfa, (byte)0xfb,
+            (byte)0xfc, (byte)0xfd, (byte)0xfe,
+        };
+        short etype = 0xa00;
+        short vid = MatchType.DL_VLAN_NONE;
+        byte pcp = 1;
+        for (int loop = 0; loop < 5; loop++) {
+            Ethernet org = createEthernet(srcMac, dstMac, etype, vid, pcp, raw);
+            Ethernet copy = new Ethernet();
+            assertSame(copy, MiscUtils.copy(org, copy));
+            checkCopy(org, copy, srcMac, dstMac, etype, vid, pcp, raw);
+            etype++;
+            vid++;
+            srcMac[3]++;
+            dstMac[4]++;
+        }
+
+        // Ensure that IPv4 instance with raw payload can be copied.
+        int srcIp = (int)0x0a010203;
+        int dstIp = (int)0xc0a864c8;
+        short ipproto = 30;
+        byte dscp = 0;
+        for (int loop = 0; loop < 5; loop++) {
+            IPv4 org = createIPv4(srcIp, dstIp, ipproto, dscp);
+            org.setRawPayload(raw);
+
+            // Copy the original in order to fix up header fields, such as
+            // checksum.
+            org = MiscUtils.copy(org, new IPv4());
+
+            IPv4 copy = new IPv4();
+            assertSame(copy, MiscUtils.copy(org, copy));
+            checkCopy(org, copy, srcIp, dstIp, ipproto, dscp, raw);
+            srcIp++;
+            dstIp++;
+            ipproto++;
+            dscp++;
+        }
+
+        // Ensure that TCP instance with raw payload can be copied.
+        int seq = 0x3333;
+        int ack = 0x4567;
+        byte dataOff = 0;
+        byte resv = 0;
+        short win = (short)0xfc00;
+        short urp = (short)0x5678;
+        short cksum = (short)0x1234;
+        short flags = 0x18;
+        short srcPort = 17;
+        short dstPort = 30000;
+        for (int loop = 0; loop < 5; loop++) {
+            TCP org = new TCP();
+            org.setSourcePort(srcPort).setDestinationPort(dstPort).
+                setSequenceNumber(seq).setAckNumber(ack).
+                setDataOffset(dataOff).setHeaderLenFlags(flags).
+                setReserved(resv).setWindowSize(win).setChecksum(cksum).
+                setUrgentPointer(urp).setRawPayload(raw);
+            TCP copy = new TCP();
+            assertSame(copy, MiscUtils.copy(org, copy));
+            checkCopy(org, copy, srcPort, dstPort, cksum, raw);
+
+            seq++;
+            ack++;
+            dataOff = (byte)((dataOff + 1) & 0xf);
+            cksum += 7;
+            srcPort++;
+            dstPort++;
+        }
+
+        // Ensure that UDP instance with raw payload can be copied.
+        srcPort = (short)45678;
+        dstPort = 133;
+        cksum = (short)0xf1a0;
+        short udpLen = (short)(raw.length + 8);
+        for (int loop = 0; loop < 5; loop++) {
+            UDP org = new UDP();
+            org.setSourcePort(srcPort).setDestinationPort(dstPort).
+                setLength(udpLen).setChecksum(cksum).setRawPayload(raw);
+            UDP copy = new UDP();
+            assertSame(copy, MiscUtils.copy(org, copy));
+            checkCopy(org, copy, srcPort, dstPort, cksum, raw);
+
+            srcPort++;
+            dstPort++;
+            cksum++;
+        }
+
+        // Ensure that ICMP instance with raw payload can be copied.
+        short icmpSeq = 0x13a9;
+        short icmpId = 0x7c;
+        byte icmpType = 0;
+        byte icmpCode = 6;
+        for (int loop = 0; loop < 5; loop++) {
+            ICMP org = new ICMP();
+            org.setType(icmpType).setCode(icmpCode).setIdentifier(icmpId).
+                setSequenceNumber(icmpSeq).setRawPayload(raw);
+
+            // Copy the original in order to fix up header fields, such as
+            // checksum.
+            org = MiscUtils.copy(org, new ICMP());
+
+            ICMP copy = new ICMP();
+            assertSame(copy, MiscUtils.copy(org, copy));
+            checkCopy(org, copy, icmpType, icmpCode, icmpId, icmpSeq, raw);
+
+            icmpSeq++;
+            icmpId++;
+            icmpType++;
+            icmpCode++;
+        }
+
+        // Ensure that nested Etherner frame can be copied.
+        IPProtocols[] protos = {
+            IPProtocols.TCP,
+            IPProtocols.UDP,
+            IPProtocols.ICMP,
+        };
+        int protoIndex = 0;
+        vid = MatchType.DL_VLAN_NONE;
+        etype = EtherTypes.IPv4.shortValue();
+        pcp = 0;
+        dscp = 0;
+        srcPort = 128;
+        dstPort = 12345;
+        cksum = (short)0xed03;
+        for (int loop = 0; loop < protos.length * 5; loop++) {
+            IPProtocols proto = protos[protoIndex];
+            protoIndex++;
+            if (protoIndex >= protos.length) {
+                protoIndex = 0;
+            }
+
+            Packet l4 = null;
+            switch (proto) {
+            case TCP:
+                l4 = new TCP().setSourcePort(srcPort).
+                    setDestinationPort(dstPort).setSequenceNumber(seq).
+                    setAckNumber(ack).setDataOffset(dataOff).
+                    setHeaderLenFlags(flags).setReserved(resv).
+                    setWindowSize(win).setChecksum(cksum).
+                    setUrgentPointer(urp);
+                break;
+
+            case UDP:
+                l4 = new UDP().setSourcePort(srcPort).
+                    setDestinationPort(dstPort).setLength(udpLen).
+                    setChecksum(cksum);
+                break;
+
+            case ICMP:
+                l4 = new ICMP().setType(icmpType).setCode(icmpCode).
+                    setIdentifier(icmpId).setSequenceNumber(icmpSeq);
+                break;
+
+            default:
+                unexpected();
+                break;
+            }
+
+            l4.setRawPayload(raw);
+
+            ipproto = proto.byteValue();
+            IPv4 ipv4 = createIPv4(srcIp, dstIp, ipproto, dscp);
+            ipv4.setPayload(l4);
+            Ethernet ether = createEthernet(srcMac, dstMac, etype, vid, pcp,
+                                            ipv4);
+
+            // Copy the original in order to fix up header fields, such as
+            // IP checksum.
+            ether = MiscUtils.copy(ether, new Ethernet());
+
+            Ethernet copy = new Ethernet();
+            assertSame(copy, MiscUtils.copy(ether, copy));
+            checkCopy(ether, copy, srcMac, dstMac, etype, vid, pcp, null);
+
+            IPv4 ipCopy;
+            if (vid == MatchType.DL_VLAN_NONE) {
+                ipv4 = (IPv4)ether.getPayload();
+                ipCopy = (IPv4)copy.getPayload();
+            } else {
+                Packet pkt = ether.getPayload();
+                ipv4 = (IPv4)pkt.getPayload();
+                pkt = copy.getPayload();
+                ipCopy = (IPv4)pkt.getPayload();
+            }
+            vid = (short)((vid + 1) & 0xfff);
+            srcMac[5]++;
+            dstMac[2]++;
+
+            // Check IPv4 packet.
+            checkCopy(ipv4, ipCopy, srcIp, dstIp, ipproto, dscp, null);
+            srcIp++;
+            dstIp++;
+            dscp = (byte)((dscp + 1) & 0x3f);
+
+            switch (proto) {
+            case TCP:
+                // Check TCP packet.
+                TCP tcp = (TCP)ipv4.getPayload();
+                TCP tcpCopy = (TCP)ipCopy.getPayload();
+                checkCopy(tcp, tcpCopy, srcPort, dstPort, cksum, raw);
+                seq++;
+                ack++;
+                dataOff = (byte)((dataOff + 1) & 0xf);
+                cksum += 13;
+                srcPort++;
+                dstPort++;
+                break;
+
+            case UDP:
+                // Check UDP packet.
+                UDP udp = (UDP)ipv4.getPayload();
+                UDP udpCopy = (UDP)ipCopy.getPayload();
+                checkCopy(udp, udpCopy, srcPort, dstPort, cksum, raw);
+                srcPort++;
+                dstPort++;
+                cksum += 23;
+                break;
+
+            case ICMP:
+                // Check ICMP packet.
+                ICMP icmp = (ICMP)ipv4.getPayload();
+                ICMP icmpCopy = (ICMP)ipCopy.getPayload();
+                checkCopy(icmp, icmpCopy, icmpType, icmpCode, icmpId, icmpSeq,
+                          raw);
+                icmpSeq++;
+                icmpId++;
+                icmpType++;
+                icmpCode++;
+                break;
+
+            default:
+                unexpected();
+                break;
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#setInt(byte[], int, int)}.
+     */
+    @Test
+    public void testSetInt() {
+        int size = 32;
+        int value = 0xa1b2c3d4;
+        byte[] bytes = {(byte)0xa1, (byte)0xb2, (byte)0xc3, (byte)0xd4};
+        for (int off = 0; off <= size - 4; off++) {
+            byte[] buffer = new byte[size];
+            MiscUtils.setInt(buffer, off, value);
+            for (int i = 0; i < buffer.length; i++) {
+                if (i >= off && i < off + 4) {
+                    assertEquals(bytes[i - off], buffer[i]);
+                } else {
+                    assertEquals((byte)0, buffer[i]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link MiscUtils#setShort(byte[], int, short)}.
+     */
+    @Test
+    public void testSetShort() {
+        int size = 32;
+        short value = (short)0xa1b2;
+        byte[] bytes = {(byte)0xa1, (byte)0xb2};
+        for (int off = 0; off <= size - 2; off++) {
+            byte[] buffer = new byte[size];
+            MiscUtils.setShort(buffer, off, value);
+            for (int i = 0; i < buffer.length; i++) {
+                if (i >= off && i < off + 2) {
+                    assertEquals(bytes[i - off], buffer[i]);
+                } else {
+                    assertEquals((byte)0, buffer[i]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Verify the contents of the given {@link VTNException}.
+     *
+     * <p>
+     *   This method expects that {@link StatusCode#BADREQUEST} is configured
+     *   in the given exception.
+     * </p>
+     *
+     * @param e     A {@link VTNException} to be tested.
+     * @param desc  An expected description.
+     */
+    private void checkException(VTNException e, String desc) {
+        checkException(e, desc, StatusCode.BADREQUEST);
+    }
+
+    /**
+     * Verify the contents of the given {@link VTNException}.
+     *
+     * @param e     A {@link VTNException} to be tested.
+     * @param desc  An expected description.
+     * @param code  An expected status code.
+     */
+    private void checkException(VTNException e, String desc, StatusCode code) {
+        checkStatus(e.getStatus(), desc, code);
+    }
+
+    /**
+     * Verify the contents of the given {@link Status}.
+     *
+     * <p>
+     *   This method expects that {@link StatusCode#BADREQUEST} is configured
+     *   in the given status.
+     * </p>
+     *
+     * @param st    A {@link Status} instance to be tested.
+     * @param desc  An expected description.
+     */
+    private void checkStatus(Status st, String desc) {
+        checkStatus(st, desc, StatusCode.BADREQUEST);
+    }
+
+    /**
+     * Verify the contents of the given {@link Status}.
+     *
+     * @param st    A {@link Status} instance to be tested.
+     * @param desc  An expected description.
+     * @param code  An expected status code.
+     */
+    private void checkStatus(Status st, String desc, StatusCode code) {
+        assertEquals(desc, st.getDescription());
+        assertEquals(code, st.getCode());
+    }
+
+    /**
+     * Ensure that the raw payload of the packet was copied successfully.
+     *
+     * @param org   Original{@link Packet} instance.
+     * @param copy  A copy of {@code org}.
+     * @param raw    Expected raw payload.
+     */
+    private void checkRawPayloadCopy(Packet org, Packet copy, byte[] raw) {
+        byte[] rawPayload = copy.getRawPayload();
+        byte[] orgPayload = org.getRawPayload();
+        if (raw == null) {
+            assertEquals(null, rawPayload);
+            assertEquals(null, orgPayload);
+        } else {
+            assertEquals(null, org.getPayload());
+            assertEquals(null, copy.getPayload());
+            assertNotSame(raw, rawPayload);
+            assertNotSame(orgPayload, rawPayload);
+            assertArrayEquals(raw, rawPayload);
+            assertArrayEquals(raw, orgPayload);
+        }
+    }
+
+    /**
+     * Ensure that an {@link Ethernet} instance was copied successfully.
+     *
+     * @param org    Original {@link Ethernet} instance.
+     * @param copy   A copy of {@code org}.
+     * @param src    Expected source MAC address.
+     * @param dst    Expected destination MAC address.
+     * @param etype  Expected ethernet type.
+     * @param vid    Expected VLAN ID.
+     * @param pcp    Expected VLAN priority.
+     * @param raw    Expected raw payload.
+     */
+    private void checkCopy(Ethernet org, Ethernet copy, byte[] src, byte[] dst,
+                           short etype, short vid, byte pcp, byte[] raw) {
+        assertNotSame(copy, org);
+        assertEquals(copy, org);
+        assertArrayEquals(src, org.getSourceMACAddress());
+        assertArrayEquals(dst, org.getDestinationMACAddress());
+        assertArrayEquals(src, copy.getSourceMACAddress());
+        assertArrayEquals(dst, copy.getDestinationMACAddress());
+        Packet parent;
+        Packet orgParent;
+        if (vid == MatchType.DL_VLAN_NONE) {
+            assertEquals(etype, org.getEtherType());
+            assertEquals(etype, copy.getEtherType());
+            parent = copy;
+            orgParent = org;
+        } else {
+            assertEquals(EtherTypes.VLANTAGGED.shortValue(),
+                         org.getEtherType());
+            assertEquals(EtherTypes.VLANTAGGED.shortValue(),
+                         copy.getEtherType());
+            IEEE8021Q orgTag = (IEEE8021Q)org.getPayload();
+            IEEE8021Q vlanTag = (IEEE8021Q)copy.getPayload();
+            assertNotNull(vlanTag);
+            assertNotSame(orgTag, vlanTag);
+            assertEquals(orgTag, vlanTag);
+            assertEquals(etype, orgTag.getEtherType());
+            assertEquals(vid, orgTag.getVid());
+            assertEquals(pcp, orgTag.getPcp());
+            assertEquals((byte)0, orgTag.getCfi());
+            assertEquals(etype, vlanTag.getEtherType());
+            assertEquals(vid, vlanTag.getVid());
+            assertEquals(pcp, vlanTag.getPcp());
+            assertEquals((byte)0, vlanTag.getCfi());
+            parent = vlanTag;
+            orgParent = orgTag;
+        }
+
+        checkRawPayloadCopy(parent, orgParent, raw);
+    }
+
+    /**
+     * Ensure that an {@link IPv4} instance was copied successfully.
+     *
+     * @param org    Original {@link IPv4} instance.
+     * @param copy   A copy of {@code org}.
+     * @param src    Expected source IP address.
+     * @param dst    Expected destination IP address.
+     * @param proto  Expected IP protocol number.
+     * @param dscp   Expected DSCP field value.
+     * @param raw    Expected raw payload.
+     */
+    private void checkCopy(IPv4 org, IPv4 copy, int src, int dst, short proto,
+                           byte dscp, byte[] raw) {
+        assertNotSame(copy, org);
+        assertEquals(copy, org);
+        for (IPv4 ipv4: new IPv4[]{org, copy}) {
+            assertEquals(src, ipv4.getSourceAddress());
+            assertEquals(dst, ipv4.getDestinationAddress());
+            assertEquals((byte)proto, ipv4.getProtocol());
+            assertEquals(dscp, ipv4.getDiffServ());
+        }
+
+        checkRawPayloadCopy(org, copy, raw);
+    }
+
+    /**
+     * Ensure that an {@link TCP} instance was copied successfully.
+     *
+     * @param org    Original {@link TCP} instance.
+     * @param copy   A copy of {@code org}.
+     * @param src    Expected source port number.
+     * @param dst    Expected destination port number.
+     * @param cksum  Expected TCP checksum.
+     * @param raw    Expected raw payload.
+     */
+    private void checkCopy(TCP org, TCP copy, short src, short dst,
+                           short cksum, byte[] raw) {
+        assertNotSame(copy, org);
+        assertEquals(copy, org);
+        for (TCP tcp: new TCP[]{org, copy}) {
+            assertEquals(src, tcp.getSourcePort());
+            assertEquals(dst, tcp.getDestinationPort());
+            assertEquals(cksum, tcp.getChecksum());
+        }
+
+        checkRawPayloadCopy(org, copy, raw);
+    }
+
+    /**
+     * Ensure that an {@link UDP} instance was copied successfully.
+     *
+     * @param org    Original {@link UDP} instance.
+     * @param copy   A copy of {@code org}.
+     * @param src    Expected source port number.
+     * @param dst    Expected destination port number.
+     * @param cksum  Expected UDP checksum.
+     * @param raw    Expected raw payload.
+     */
+    private void checkCopy(UDP org, UDP copy, short src, short dst,
+                           short cksum, byte[] raw) {
+        assertNotSame(copy, org);
+        assertEquals(copy, org);
+        for (UDP udp: new UDP[]{org, copy}) {
+            assertEquals(src, udp.getSourcePort());
+            assertEquals(dst, udp.getDestinationPort());
+            assertEquals(cksum, udp.getChecksum());
+        }
+
+        checkRawPayloadCopy(org, copy, raw);
+    }
+
+    /**
+     * Ensure that an {@link ICMP} instance was copied successfully.
+     *
+     * @param org    Original {@link UDP} instance.
+     * @param copy   A copy of {@code org}.
+     * @param type   Expected ICMP type.
+     * @param code   Expected ICMP code.
+     * @param id     Expected identifier.
+     * @param seq    Expected sequence number.
+     * @param raw    Expected raw payload.
+     */
+    private void checkCopy(ICMP org, ICMP copy, byte type, byte code,
+                           short id, short seq, byte[] raw) {
+        assertNotSame(copy, org);
+        assertEquals(copy, org);
+        for (ICMP icmp: new ICMP[]{org, copy}) {
+            assertEquals(type, icmp.getType());
+            assertEquals(code, icmp.getCode());
+            assertEquals(id, icmp.getIdentifier());
+            assertEquals(seq, icmp.getSequenceNumber());
+        }
+
+        checkRawPayloadCopy(org, copy, raw);
+    }
+}
index d2f2eba23072c5a660a6d0710304ecbb63c4bf5f..64ed4f2383e7fe200dfb87041b13b361df241d47 100644 (file)
@@ -15,7 +15,6 @@ import java.io.File;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -47,10 +46,12 @@ import org.opendaylight.vtn.manager.internal.cluster.VlanMapPath;
 import org.opendaylight.controller.sal.core.Edge;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.match.MatchType;
 import org.opendaylight.controller.sal.packet.ARP;
 import org.opendaylight.controller.sal.packet.Ethernet;
 import org.opendaylight.controller.sal.packet.IEEE8021Q;
 import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.packet.RawPacket;
 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
 import org.opendaylight.controller.sal.utils.EtherTypes;
@@ -277,6 +278,28 @@ public abstract class TestBase extends Assert {
         return ref;
     }
 
+    /**
+     * Create a deep copy of the specified {@link Packet} instance.
+     *
+     * @param src  The source {@link Packet} instance.
+     * @param dst  The destination {@link Packet} instance.
+     * @param <T>  Type of the packet.
+     * @return  {@code dst}.
+     */
+    protected static <T extends Packet> T copy(T src, T dst) {
+        if (src != null) {
+            try {
+                byte[] raw = src.serialize();
+                int nbits = raw.length * NetUtils.NumBitsInAByte;
+                dst.deserialize(raw, 0, nbits);
+            } catch (Exception e) {
+                unexpected(e);
+            }
+        }
+
+        return dst;
+    }
+
     /**
      * Create a list of boolean values and a {@code null}.
      *
@@ -744,6 +767,45 @@ public abstract class TestBase extends Assert {
         return list;
     }
 
+    /**
+     * Create a list of IPv4 addresses and {@code null}.
+     *
+     * @return  A list of {@link InetAddress} instances.
+     */
+    protected static List<InetAddress> createInet4Addresses() {
+        return createInet4Addresses(true);
+    }
+
+    /**
+     * Create a list of IPv4 addresses.
+     *
+     * @param setNull  Set {@code null} to returned list if {@code true}.
+     * @return  A list of {@link InetAddress} instances.
+     */
+    protected static List<InetAddress> createInet4Addresses(boolean setNull) {
+        List<InetAddress> list = new ArrayList<InetAddress>();
+        if (setNull) {
+            list.add(null);
+        }
+
+        String[] addrs = {
+            "0.0.0.0",
+            "255.255.255.255",
+            "127.0.0.1",
+            "10.20.30.40",
+            "192.168.100.200",
+        };
+        for (String addr: addrs) {
+            try {
+                list.add(InetAddress.getByName(addr));
+            } catch (Exception e) {
+                unexpected(e);
+            }
+        }
+
+        return list;
+    }
+
     /**
      * Create a {@link InetAddress} instance which represents the specified
      * IP address.
@@ -847,6 +909,173 @@ public abstract class TestBase extends Assert {
         return pctx;
     }
 
+    /**
+     * Create an Ethernet frame.
+     *
+     * @param src      Source MAC address.
+     * @param dst      Destination MAC address.
+     * @param type     Ethernet type.
+     * @param vid      A VLAN ID.
+     * @param pcp      VLAN priority.
+     * @param payload  A {@link Packet} instance to be set as payload.
+     * @return  An {@link Ethernet} instance.
+     */
+    protected static Ethernet createEthernet(byte[] src, byte[] dst, int type,
+                                             short vid, byte pcp,
+                                             Packet payload) {
+        Ethernet pkt = new Ethernet();
+        pkt.setSourceMACAddress(src).setDestinationMACAddress(dst);
+        if (vid == MatchType.DL_VLAN_NONE) {
+            pkt.setEtherType((short)type).setPayload(payload);
+        } else {
+            IEEE8021Q tag = new IEEE8021Q();
+            tag.setCfi((byte)0).setPcp(pcp).setVid(vid).
+                setEtherType((short)type).setPayload(payload);
+            pkt.setEtherType(EtherTypes.VLANTAGGED.shortValue()).
+                setPayload(tag);
+        }
+
+        return pkt;
+    }
+
+    /**
+     * Create an Ethernet frame.
+     *
+     * @param src   Source MAC address.
+     * @param dst   Destination MAC address.
+     * @param type  Ethernet type.
+     * @param vid   A VLAN ID.
+     * @param pcp   VLAN priority
+     * @param raw   A byte array to be set as payload.
+     * @return  An {@link Ethernet} instance.
+     */
+    protected static Ethernet createEthernet(byte[] src, byte[] dst, int type,
+                                             short vid, byte pcp, byte[] raw) {
+        Ethernet pkt = new Ethernet();
+        pkt.setSourceMACAddress(src).setDestinationMACAddress(dst);
+        if (vid == MatchType.DL_VLAN_NONE) {
+            pkt.setEtherType((short)type).setRawPayload(raw);
+        } else {
+            IEEE8021Q tag = new IEEE8021Q();
+            tag.setCfi((byte)0).setPcp(pcp).setVid(vid).
+                setEtherType((short)type).setRawPayload(raw);
+            pkt.setEtherType(EtherTypes.VLANTAGGED.shortValue()).
+                setPayload(tag);
+        }
+
+        return pkt;
+    }
+
+    /**
+     * Create an untagged Ethernet frame that contains the given IPv4 packet.
+     *
+     * <p>
+     *   Fixed values are used for the source and destination MAC address.
+     * </p>
+     *
+     * @param ipv4  An {@link IPv4} to be configured as payload.
+     * @return  An {@link Ethernet} instance.
+     */
+    protected static Ethernet createEthernet(IPv4 ipv4) {
+        byte[] src = {
+            (byte)0x00, (byte)0x11, (byte)0x22,
+            (byte)0x33, (byte)0x44, (byte)0x55,
+        };
+        byte[] dst = {
+            (byte)0xa0, (byte)0xb0, (byte)0xc0,
+            (byte)0xdd, (byte)0xee, (byte)0xff,
+        };
+
+        return createEthernet(src, dst, EtherTypes.IPv4.intValue(),
+                              MatchType.DL_VLAN_NONE, (byte)0, ipv4);
+    }
+
+    /**
+     * Create an {@link IPv4} instance.
+     *
+     * @param src    Source IP address.
+     * @param dst    Destination IP address.
+     * @param proto  IP protocol number.
+     * @param dscp   DSCP field value.
+     * @return  An {@link IPv4} instance.
+     */
+    protected static IPv4 createIPv4(InetAddress src, InetAddress dst,
+                                     short proto, byte dscp) {
+        IPv4 pkt = new IPv4();
+        return pkt.setSourceAddress(src).setDestinationAddress(dst).
+            setProtocol((byte)proto).setDiffServ(dscp).
+            setTtl((byte)64);
+    }
+
+    /**
+     * Create an {@link IPv4} instance.
+     *
+     * @param src    A byte array which represents the source IP address.
+     * @param dst    A byte array which represents the destination IP address.
+     * @param proto  IP protocol number.
+     * @param dscp   DSCP field value.
+     * @return  An {@link IPv4} instance.
+     */
+    protected static IPv4 createIPv4(byte[] src, byte[] dst, short proto,
+                                     byte dscp) {
+        InetAddress srcInet = createInetAddress(src);
+        InetAddress dstInet = createInetAddress(dst);
+        return createIPv4(srcInet, dstInet, proto, dscp);
+    }
+
+    /**
+     * Create an {@link IPv4} instance.
+     *
+     * @param src    An integer value which represents the source IP address.
+     * @param dst    An integer value which represents the destination IP
+     *               address.
+     * @param proto  IP protocol number.
+     * @param dscp   DSCP field value.
+     * @return  An {@link IPv4} instance.
+     */
+    protected static IPv4 createIPv4(int src, int dst, short proto,
+                                     byte dscp) {
+        byte[] srcAddr = NetUtils.intToByteArray4(src);
+        byte[] dstAddr = NetUtils.intToByteArray4(dst);
+        return createIPv4(srcAddr, dstAddr, proto, dscp);
+    }
+
+    /**
+     * Create an {@link IPv4} instance.
+     *
+     * @param src      A byte array which represents the source IP address.
+     * @param dst      A byte array which represents the destination IP address.
+     * @param proto    IP protocol number.
+     * @param dscp     DSCP field value.
+     * @param payload  A {@link Packet} instance to be configured as payload.
+     * @return  An {@link IPv4} instance.
+     */
+    protected static IPv4 createIPv4(byte[] src, byte[] dst, short proto,
+                                     byte dscp, Packet payload) {
+        IPv4 pkt = createIPv4(src, dst, proto, dscp);
+        pkt.setPayload(payload);
+
+        return pkt;
+    }
+
+    /**
+     * Create an {@link IPv4} instance.
+     *
+     * <p>
+     *   Fixed values are used for the source and destination IP address,
+     *   and DSCP field.
+     * </p>
+     *
+     * @param proto    IP protocol number.
+     * @param payload  A {@link Packet} instance to be configured as payload.
+     * @return  An {@link IPv4} instance.
+     */
+    protected static IPv4 createIPv4(short proto, Packet payload) {
+        byte[] src = {(byte)10, (byte)1, (byte)2, (byte)30};
+        byte[] dst = {(byte)127, (byte)0, (byte)0, (byte)1};
+        return createIPv4(src, dst, proto, (byte)0, payload);
+    }
+
     /**
      * create a {@link RawPacket} object.
      *
@@ -889,8 +1118,8 @@ public abstract class TestBase extends Assert {
             setFragmentOffset((short)0).
             setTtl((byte)64);
 
-        ip.setDestinationAddress(getInetAddressFromAddress(target));
-        ip.setSourceAddress(getInetAddressFromAddress(sender));
+        ip.setDestinationAddress(createInetAddress(target));
+        ip.setSourceAddress(createInetAddress(sender));
 
         Ethernet eth = new Ethernet();
         eth.setSourceMACAddress(src).setDestinationMACAddress(dst);
@@ -1085,22 +1314,6 @@ public abstract class TestBase extends Assert {
         return list;
     }
 
-    /**
-     * get {@link InetAddress} object from byte arrays.
-     *
-     * @param ipaddr    byte arrays of IP Address.
-     * @return  A InetAddress object.
-     */
-    protected InetAddress getInetAddressFromAddress(byte[] ipaddr) {
-        InetAddress inet = null;
-        try {
-            inet = InetAddress.getByAddress(ipaddr);
-        } catch (UnknownHostException e) {
-            unexpected(e);
-        }
-        return inet;
-    }
-
     /**
      * Determine whether the specified two strings are identical or not.
      *
index 6a7e1ac0f488f5ce41f246c04ba9b75a047939ce..1caebc334fb87ade36014dc22c75e7766cdae319 100644 (file)
@@ -293,9 +293,8 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
             getCache(VTNManagerImpl.CACHE_STATE);
 
         VTenantPath tpathNull = new VTenantPath(null);
-        InetAddress ia
-            = getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                    (byte)0, (byte)2});
+        InetAddress ia = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)0, (byte)2});
         ObjectPair<InetAddress, Boolean> obj
             = new ObjectPair<InetAddress, Boolean>(ia, Boolean.FALSE);
         stateDB.put(tpathNull, obj);
@@ -313,8 +312,8 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         stateDB = (ConcurrentMap<VTenantPath, Object>)stubObj.
             getCache(VTNManagerImpl.CACHE_STATE);
 
-        ia = getInetAddressFromAddress(new byte[] {(byte)192, (byte)168,
-                                                   (byte)0, (byte)99});
+        ia = createInetAddress(
+            new byte[] {(byte)192, (byte)168, (byte)0, (byte)99});
         obj = new ObjectPair<InetAddress, Boolean>(ia, Boolean.FALSE);
         assertNull(stateDB.put(tpathNull, obj));
 
@@ -479,7 +478,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         assertEquals(1, vtnMgr.getFlowDB().size());
 
         // in case MAC Address Table cache remain.
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[]{10, 0, 0, 1});
+        InetAddress ipaddr = createInetAddress(new byte[]{10, 0, 0, 1});
         MacTableEntry tent = new MacTableEntry(bpath, 1L, innc,
                                                (short)0, ipaddr);
         vtnMgr.putMacTableEntry(tent);
@@ -498,7 +497,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         assertEquals(0, map.size());
 
         // in case remote entry is remained.
-        InetAddress ipaddrRemote = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddrRemote = createInetAddress(new byte[] {0, 0, 0, 0});
         MacTableEntryId evidRemote = new MacTableEntryId(ipaddrRemote, 1L, bpath, 1L);
 
         tent = new MacTableEntry(evidRemote, innc, (short)0, ipaddr);
@@ -1360,7 +1359,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         File configFile = new File(dir, configFileName);
 
         Set<ClusterEventId> evIdSet = new HashSet<ClusterEventId>();
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evidRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evidLocal = new ClusterEventId();
         evIdSet.add(evidLocal);
@@ -1449,7 +1448,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         File configFile = new File(dir, configFileName);
 
         Set<ClusterEventId> evIdSet = new HashSet<ClusterEventId>();
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evIdRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evIdLocal = new ClusterEventId();
         evIdSet.add(evIdLocal);
@@ -1502,7 +1501,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         File configFile = new File(dir, configFileName);
 
         Set<ClusterEventId> evIdSet = new HashSet<ClusterEventId>();
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evIdRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evIdLocal = new ClusterEventId();
         evIdSet.add(evIdLocal);
@@ -1546,7 +1545,7 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon {
         nodeSet.add(node0);
         nodeSet.add(node1);
 
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evidRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evidLocal = new ClusterEventId();
         Set<ClusterEventId> evIdSet = new HashSet<ClusterEventId>();
index bbf70af5a553903d265e858404e00dd322b8ff3c..9602bef59f3d25a25d795305ea9fef266927e98c 100644 (file)
@@ -446,7 +446,7 @@ public class VTNManagerImplDisableNodesTest extends TestBase {
 
         RawPacketEvent ev = new RawPacketEvent(pkt, outnc);
 
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evidRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evidLocal = new ClusterEventId();
 
index 92b16f14a0c22fc565d2760da7af8d76a0a1fbe1..59d23c7bf1b3ffb194a9ae731b35e811fba7a542 100644 (file)
@@ -332,7 +332,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
             getCache(VTNManagerImpl.CACHE_EVENT);
 
         FlowGroupId gid = new FlowGroupId("test_tenant");
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         FlowGroupId gidRemote = new FlowGroupId(ipaddr, 0L, "test_tenant2");
 
         VTNFlow flow = new VTNFlow(gid);
@@ -391,7 +391,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
         assertEquals(1, vtnMgr.getFlowDB().size());
 
         // in case MAC Address Table cache remain.
-        ipaddr = getInetAddressFromAddress(new byte[] {10, 0, 0, 1});
+        ipaddr = createInetAddress(new byte[] {10, 0, 0, 1});
         MacTableEntry tent = new MacTableEntry(bpath, 1L, innc, (short)0,
                                                ipaddr);
         vtnMgr.putMacTableEntry(tent);
@@ -5881,7 +5881,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
 
         RawPacketEvent ev = new RawPacketEvent(pkt, outnc);
 
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evidRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evidLocal = new ClusterEventId();
 
@@ -5972,7 +5972,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
         FlowRemoveEvent removeEvent = new FlowRemoveEvent(flow.getFlowEntries());
 
         Set<ClusterEventId> evIdSet = new HashSet<ClusterEventId>();
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evIdRemote = new ClusterEventId(ipaddr, 0);
         evIdSet.add(evIdRemote);
 
@@ -6203,7 +6203,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
         }
 
         InetAddress ipaddr
-            = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+            = createInetAddress(new byte[] {0, 0, 0, 0});
         ClusterEventId evidRemote = new ClusterEventId(ipaddr, 0);
         ClusterEventId evidLocal = new ClusterEventId();
 
@@ -6292,7 +6292,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
         VTNFlowDatabase fdb = vtnMgr.getTenantFlowDB(path.getTenantName());
 
         // create FlowGroupId.
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
         FlowGroupId evIdRemote = new FlowGroupId(ipaddr, 0L, "tenant");
         FlowGroupId evIdLocal = new FlowGroupId(InetAddress.getLoopbackAddress(),
                                                 0L, "tenant");
@@ -6443,7 +6443,7 @@ public class VTNManagerImplTest extends VTNManagerImplTestCommon {
         Node node = NodeCreator.createOFNode(Long.valueOf(0L));
         NodeConnector nc = NodeConnectorCreator
                 .createOFNodeConnector(Short.valueOf("1"), node);
-        InetAddress ipaddr = getInetAddressFromAddress(new byte[] {0, 0, 0, 0});
+        InetAddress ipaddr = createInetAddress(new byte[] {0, 0, 0, 0});
 
         MacTableEntryId evidRemote = new MacTableEntryId(ipaddr, 0L, bpath, mac);
         MacTableEntryId evidLocal = new MacTableEntryId(bpath, mac);
index 78ff014aa53b53bce0238dbf3ec353daf6173118..1491cd287e41a2fe028003d8a1b5bee65f34af60 100644 (file)
@@ -3133,7 +3133,7 @@ public class VTNManagerImplWithNodesTest extends VTNManagerImplTestCommon {
             }
         }
 
-        InetAddress ia = getInetAddressFromAddress(new byte[] {10, 0, 0, 1});
+        InetAddress ia = createInetAddress(new byte[] {10, 0, 0, 1});
 
         InetAddress ia6 = null;
         try {
index 338a90748a3e6dd936c736b614e162c748e8cf6c..23f8693932aa6b96c7ccff5cfcb567bb0e80dd90 100644 (file)
@@ -9,6 +9,7 @@
 
 package org.opendaylight.vtn.manager.internal.cluster;
 
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -25,6 +26,10 @@ import org.opendaylight.vtn.manager.flow.action.SetDlSrcAction;
 import org.opendaylight.vtn.manager.flow.action.SetDscpAction;
 import org.opendaylight.vtn.manager.flow.action.SetIcmpCodeAction;
 import org.opendaylight.vtn.manager.flow.action.SetIcmpTypeAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
 import org.opendaylight.vtn.manager.flow.action.SetVlanIdAction;
 import org.opendaylight.vtn.manager.flow.action.SetVlanPcpAction;
 
@@ -46,14 +51,18 @@ public class FlowActionImplTest extends TestBase {
      * Test case for {@link FlowActionImpl#create(FlowAction)}.
      */
     @Test
-    public void testCreate() {
+    public void testCreate() throws Exception {
         // Expected action implementation classes.
         HashMap<Class<?>, Class<?>> implClasses =
             new HashMap<Class<?>, Class<?>>();
         implClasses.put(SetDlSrcAction.class, SetDlSrcActionImpl.class);
         implClasses.put(SetDlDstAction.class, SetDlDstActionImpl.class);
         implClasses.put(SetVlanPcpAction.class, SetVlanPcpActionImpl.class);
+        implClasses.put(SetInet4SrcAction.class, SetInet4SrcActionImpl.class);
+        implClasses.put(SetInet4DstAction.class, SetInet4DstActionImpl.class);
         implClasses.put(SetDscpAction.class, SetDscpActionImpl.class);
+        implClasses.put(SetTpSrcAction.class, SetTpSrcActionImpl.class);
+        implClasses.put(SetTpDstAction.class, SetTpDstActionImpl.class);
         implClasses.put(SetIcmpTypeAction.class, SetIcmpTypeActionImpl.class);
         implClasses.put(SetIcmpCodeAction.class, SetIcmpCodeActionImpl.class);
 
@@ -86,11 +95,39 @@ public class FlowActionImplTest extends TestBase {
             actions.add(new SetVlanPcpAction(pri));
         }
 
+        count = 0;
+        do {
+            int v = rand.nextInt();
+            byte[] addr = NetUtils.intToByteArray4(v);
+            InetAddress iaddr = InetAddress.getByAddress(addr);
+            actions.add(new SetInet4SrcAction(iaddr));
+            count++;
+        } while (count < naddrs);
+
+        count = 0;
+        do {
+            int v = rand.nextInt();
+            byte[] addr = NetUtils.intToByteArray4(v);
+            InetAddress iaddr = InetAddress.getByAddress(addr);
+            actions.add(new SetInet4DstAction(iaddr));
+            count++;
+        } while (count < naddrs);
+
         byte[] dscps = {0, 18, 32, 63};
         for (byte dscp: dscps) {
             actions.add(new SetDscpAction(dscp));
         }
 
+        int[] ports = {0, 53, 200, 456, 20000, 40000, 65535};
+        for (int port: ports) {
+            actions.add(new SetTpSrcAction(port));
+        }
+
+        ports = new int[]{0, 31, 113, 789, 12345, 34567, 65535};
+        for (int port: ports) {
+            actions.add(new SetTpDstAction(port));
+        }
+
         short[] types = {0, 63, 112, 255};
         for (short type: types) {
             actions.add(new SetIcmpTypeAction(type));
@@ -140,7 +177,11 @@ public class FlowActionImplTest extends TestBase {
             new SetDlSrcAction(new byte[0]),
             new SetDlDstAction(new byte[]{0, 0, 0, 0, 0, 0}),
             new SetVlanPcpAction((byte)-1),
+            new SetInet4SrcAction((InetAddress)null),
+            new SetInet4DstAction((InetAddress)null),
             new SetDscpAction((byte)64),
+            new SetTpSrcAction(-1),
+            new SetTpDstAction(0x10000),
             new SetIcmpTypeAction((short)256),
             new SetIcmpCodeAction((short)-1),
         };
index 38d575318559247a2e170c2f6b511f35493b47a7..d1aedf90faee0b235a6cad976b1490ad89037882 100644 (file)
@@ -88,8 +88,8 @@ public class MacTableEntryTest extends TestBase {
 
                             checkMacTableEntry(me, ips, mac, nc, vlan);
 
-                            InetAddress ia
-                                = getInetAddressFromAddress(new byte[] {10, 1, 1, 100});
+                            InetAddress ia =
+                                createInetAddress(new byte[] {10, 1, 1, 100});
                             me = new MacTableEntry(path, mac, copy(nc),
                                                    vlan, ia);
                             me.setInetAddresses(ips);
index 18a7787d99c31439f41b67c67cd3508a0d6b5b40..79969088ddecbaf249e9044b4b2dd2e0e081de80 100644 (file)
@@ -9,10 +9,13 @@
 
 package org.opendaylight.vtn.manager.internal.cluster;
 
+import java.io.ByteArrayInputStream;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 
+import javax.xml.bind.JAXB;
+
 import org.junit.Test;
 
 import org.opendaylight.vtn.manager.VTNException;
@@ -31,20 +34,21 @@ import org.opendaylight.controller.sal.utils.StatusCode;
 public class SetDlDstActionImplTest extends TestBase {
     /**
      * Test case for getter methods.
+     *
+     * @throws Exception  An unexpected exception occurred.
      */
     @Test
-    public void testGetter() {
+    public void testGetter() throws Exception {
         for (EthernetAddress eaddr: createEthernetAddresses(false)) {
             byte[] bytes = eaddr.getValue();
             SetDlDstAction act = new SetDlDstAction(bytes);
             try {
                 SetDlDstActionImpl impl = new SetDlDstActionImpl(act);
+                assertEquals(act, impl.getFlowAction());
 
                 // Ensure that MAC address bytes are copied.
-                for (int i = 0; i < bytes.length; i++) {
-                    bytes[i] = 0;
-                }
-                assertEquals(act, impl.getFlowAction());
+                assertNotSame(bytes, impl.getAddress());
+                assertArrayEquals(bytes, impl.getAddress());
             } catch (Exception e) {
                 unexpected(e);
             }
@@ -95,6 +99,19 @@ public class SetDlDstActionImplTest extends TestBase {
                 assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
             }
         }
+
+        // Specify invalid MAC address via JAXB.
+        String xml =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
+            "<setdldst address=\"bad MAC address\" />";
+        ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes());
+        SetDlDstAction act = JAXB.unmarshal(in, SetDlDstAction.class);
+        try {
+            new SetDlDstActionImpl(act);
+            unexpected();
+        } catch (VTNException e) {
+            assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+        }
     }
 
     /**
index 791c48736f389b953f92c08ee764c0c575234f1e..3d1d247e898415823d55cb3276787f021212f8e5 100644 (file)
@@ -9,10 +9,13 @@
 
 package org.opendaylight.vtn.manager.internal.cluster;
 
+import java.io.ByteArrayInputStream;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 
+import javax.xml.bind.JAXB;
+
 import org.junit.Test;
 
 import org.opendaylight.vtn.manager.VTNException;
@@ -31,20 +34,21 @@ import org.opendaylight.controller.sal.utils.StatusCode;
 public class SetDlSrcActionImplTest extends TestBase {
     /**
      * Test case for getter methods.
+     *
+     * @throws Exception  An unexpected exception occurred.
      */
     @Test
-    public void testGetter() {
+    public void testGetter() throws Exception {
         for (EthernetAddress eaddr: createEthernetAddresses(false)) {
             byte[] bytes = eaddr.getValue();
             SetDlSrcAction act = new SetDlSrcAction(bytes);
             try {
                 SetDlSrcActionImpl impl = new SetDlSrcActionImpl(act);
+                assertEquals(act, impl.getFlowAction());
 
                 // Ensure that MAC address bytes are copied.
-                for (int i = 0; i < bytes.length; i++) {
-                    bytes[i] = 0;
-                }
-                assertEquals(act, impl.getFlowAction());
+                assertNotSame(bytes, impl.getAddress());
+                assertArrayEquals(bytes, impl.getAddress());
             } catch (Exception e) {
                 unexpected(e);
             }
@@ -95,6 +99,19 @@ public class SetDlSrcActionImplTest extends TestBase {
                 assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
             }
         }
+
+        // Specify invalid MAC address via JAXB.
+        String xml =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
+            "<setdlsrc address=\"bad MAC address\" />";
+        ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes());
+        SetDlSrcAction act = JAXB.unmarshal(in, SetDlSrcAction.class);
+        try {
+            new SetDlSrcActionImpl(act);
+            unexpected();
+        } catch (VTNException e) {
+            assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+        }
     }
 
     /**
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImplTest.java
new file mode 100644 (file)
index 0000000..860d3e2
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.cluster;
+
+import java.io.ByteArrayInputStream;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.xml.bind.JAXB;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VTNException;
+import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * JUnit test for {@link SetInet4DstActionImpl}.
+ */
+public class SetInet4DstActionImplTest extends TestBase {
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        for (InetAddress iaddr: createInet4Addresses(false)) {
+            SetInet4DstAction act = new SetInet4DstAction(iaddr);
+            SetInet4DstActionImpl impl = new SetInet4DstActionImpl(act);
+            assertEquals(act, impl.getFlowAction());
+            assertEquals(iaddr, impl.getAddress());
+        }
+
+        // null action.
+        try {
+            new SetInet4DstActionImpl(null);
+            unexpected();
+        } catch (VTNException e) {
+            assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+        }
+
+        // Specify invalid IP address via JAXB.
+        String[] badAddrs = {
+            null,
+            "",
+            "  ",
+            "::1",
+            "Bad IP address",
+        };
+
+        String xmlDef =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
+        for (String addr: badAddrs) {
+            StringBuilder builder = new StringBuilder(xmlDef);
+            if (addr == null) {
+                builder.append("<setinet4dst />");
+            } else {
+                builder.append("<setinet4dst address=\"").append(addr).
+                    append("\" />");
+            }
+            ByteArrayInputStream in =
+                new ByteArrayInputStream(builder.toString().getBytes());
+            SetInet4DstAction act =
+                JAXB.unmarshal(in, SetInet4DstAction.class);
+            try {
+                new SetInet4DstActionImpl(act);
+                unexpected();
+            } catch (VTNException e) {
+                assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link SetInet4DstActionImpl#equals(Object)} and
+     * {@link SetInet4DstActionImpl#hashCode()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testEquals() throws Exception {
+        HashSet<Object> set = new HashSet<Object>();
+        List<InetAddress> iaddrs = createInet4Addresses(false);
+        for (InetAddress iaddr: iaddrs) {
+            byte[] raw = iaddr.getAddress();
+            InetAddress iaddr2 = InetAddress.getByAddress(raw);
+            SetInet4DstAction act1 = new SetInet4DstAction(iaddr);
+            SetInet4DstAction act2 = new SetInet4DstAction(iaddr2);
+            SetInet4DstActionImpl impl1 = new SetInet4DstActionImpl(act1);
+            SetInet4DstActionImpl impl2 = new SetInet4DstActionImpl(act2);
+            testEquals(set, impl1, impl2);
+        }
+
+        assertEquals(iaddrs.size(), set.size());
+    }
+
+    /**
+     * Test case for {@link SetInet4DstActionImpl#toString()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testToString() throws Exception {
+        String prefix = "SetInet4DstActionImpl[";
+        String suffix = "]";
+        for (InetAddress iaddr: createInet4Addresses(false)) {
+            SetInet4DstAction act = new SetInet4DstAction(iaddr);
+            SetInet4DstActionImpl impl = new SetInet4DstActionImpl(act);
+            String a = "addr=" + iaddr.getHostAddress();
+            String required = joinStrings(prefix, suffix, ",", a);
+            assertEquals(required, impl.toString());
+        }
+    }
+
+    /**
+     * Ensure that {@link SetInet4DstActionImpl} is serializable.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testSerialize() throws Exception {
+        for (InetAddress iaddr: createInet4Addresses(false)) {
+            SetInet4DstAction act = new SetInet4DstAction(iaddr);
+            SetInet4DstActionImpl impl = new SetInet4DstActionImpl(act);
+            serializeTest(impl);
+        }
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImplTest.java
new file mode 100644 (file)
index 0000000..168e1c8
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.cluster;
+
+import java.io.ByteArrayInputStream;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.xml.bind.JAXB;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VTNException;
+import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * JUnit test for {@link SetInet4SrcActionImpl}.
+ */
+public class SetInet4SrcActionImplTest extends TestBase {
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        for (InetAddress iaddr: createInet4Addresses(false)) {
+            SetInet4SrcAction act = new SetInet4SrcAction(iaddr);
+            SetInet4SrcActionImpl impl = new SetInet4SrcActionImpl(act);
+            assertEquals(act, impl.getFlowAction());
+            assertEquals(iaddr, impl.getAddress());
+        }
+
+        // null action.
+        try {
+            new SetInet4SrcActionImpl(null);
+            unexpected();
+        } catch (VTNException e) {
+            assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+        }
+
+        // Specify invalid IP address via JAXB.
+        String[] badAddrs = {
+            null,
+            "",
+            "  ",
+            "::1",
+            "Bad IP address",
+        };
+
+        String xmlDef =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
+        for (String addr: badAddrs) {
+            StringBuilder builder = new StringBuilder(xmlDef);
+            if (addr == null) {
+                builder.append("<setinet4src />");
+            } else {
+                builder.append("<setinet4src address=\"").append(addr).
+                    append("\" />");
+            }
+            ByteArrayInputStream in =
+                new ByteArrayInputStream(builder.toString().getBytes());
+            SetInet4SrcAction act =
+                JAXB.unmarshal(in, SetInet4SrcAction.class);
+            try {
+                new SetInet4SrcActionImpl(act);
+                unexpected();
+            } catch (VTNException e) {
+                assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link SetInet4SrcActionImpl#equals(Object)} and
+     * {@link SetInet4SrcActionImpl#hashCode()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testEquals() throws Exception {
+        HashSet<Object> set = new HashSet<Object>();
+        List<InetAddress> iaddrs = createInet4Addresses(false);
+        for (InetAddress iaddr: iaddrs) {
+            byte[] raw = iaddr.getAddress();
+            InetAddress iaddr2 = InetAddress.getByAddress(raw);
+            SetInet4SrcAction act1 = new SetInet4SrcAction(iaddr);
+            SetInet4SrcAction act2 = new SetInet4SrcAction(iaddr2);
+            SetInet4SrcActionImpl impl1 = new SetInet4SrcActionImpl(act1);
+            SetInet4SrcActionImpl impl2 = new SetInet4SrcActionImpl(act2);
+            testEquals(set, impl1, impl2);
+        }
+
+        assertEquals(iaddrs.size(), set.size());
+    }
+
+    /**
+     * Test case for {@link SetInet4SrcActionImpl#toString()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testToString() throws Exception {
+        String prefix = "SetInet4SrcActionImpl[";
+        String suffix = "]";
+        for (InetAddress iaddr: createInet4Addresses(false)) {
+            SetInet4SrcAction act = new SetInet4SrcAction(iaddr);
+            SetInet4SrcActionImpl impl = new SetInet4SrcActionImpl(act);
+            String a = "addr=" + iaddr.getHostAddress();
+            String required = joinStrings(prefix, suffix, ",", a);
+            assertEquals(required, impl.toString());
+        }
+    }
+
+    /**
+     * Ensure that {@link SetInet4SrcActionImpl} is serializable.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testSerialize() throws Exception {
+        for (InetAddress iaddr: createInet4Addresses(false)) {
+            SetInet4SrcAction act = new SetInet4SrcAction(iaddr);
+            SetInet4SrcActionImpl impl = new SetInet4SrcActionImpl(act);
+            serializeTest(impl);
+        }
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImplTest.java
new file mode 100644 (file)
index 0000000..fe2bf1e
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.cluster;
+
+import java.util.HashSet;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VTNException;
+import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * JUnit test for {@link SetTpDstActionImpl}.
+ */
+public class SetTpDstActionImplTest extends TestBase {
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpDstAction act = new SetTpDstAction(port);
+            SetTpDstActionImpl impl = new SetTpDstActionImpl(act);
+            assertEquals(act, impl.getFlowAction());
+            assertEquals(port, impl.getPort());
+        }
+
+        // null action.
+        try {
+            new SetTpDstActionImpl(null);
+            unexpected();
+        } catch (VTNException e) {
+            assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+        }
+
+        // Specify invalid ports.
+        int[] badPorts = {
+            Integer.MIN_VALUE,
+            -1000,
+            -1,
+            65536,
+            65537,
+            Integer.MAX_VALUE
+        };
+
+        for (int port: badPorts) {
+            SetTpDstAction act = new SetTpDstAction(port);
+            try {
+                new SetTpDstActionImpl(act);
+                unexpected();
+            } catch (VTNException e) {
+                assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link SetTpDstActionImpl#equals(Object)} and
+     * {@link SetTpDstActionImpl#hashCode()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testEquals() throws Exception {
+        HashSet<Object> set = new HashSet<Object>();
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpDstAction act1 = new SetTpDstAction(port);
+            SetTpDstAction act2 = new SetTpDstAction(port);
+            SetTpDstActionImpl impl1 = new SetTpDstActionImpl(act1);
+            SetTpDstActionImpl impl2 = new SetTpDstActionImpl(act2);
+            testEquals(set, impl1, impl2);
+        }
+
+        assertEquals(ports.length, set.size());
+    }
+
+    /**
+     * Test case for {@link SetTpDstActionImpl#toString()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testToString() throws Exception {
+        String prefix = "SetTpDstActionImpl[";
+        String suffix = "]";
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpDstAction act = new SetTpDstAction(port);
+            SetTpDstActionImpl impl = new SetTpDstActionImpl(act);
+            String a = "port=" + port;
+            String required = joinStrings(prefix, suffix, ",", a);
+            assertEquals(required, impl.toString());
+        }
+    }
+
+    /**
+     * Ensure that {@link SetTpDstActionImpl} is serializable.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testSerialize() throws Exception {
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpDstAction act = new SetTpDstAction(port);
+            SetTpDstActionImpl impl = new SetTpDstActionImpl(act);
+            serializeTest(impl);
+        }
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImplTest.java
new file mode 100644 (file)
index 0000000..9db679d
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.cluster;
+
+import java.util.HashSet;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VTNException;
+import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * JUnit test for {@link SetTpSrcActionImpl}.
+ */
+public class SetTpSrcActionImplTest extends TestBase {
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpSrcAction act = new SetTpSrcAction(port);
+            SetTpSrcActionImpl impl = new SetTpSrcActionImpl(act);
+            assertEquals(act, impl.getFlowAction());
+            assertEquals(port, impl.getPort());
+        }
+
+        // null action.
+        try {
+            new SetTpSrcActionImpl(null);
+            unexpected();
+        } catch (VTNException e) {
+            assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+        }
+
+        // Specify invalid ports.
+        int[] badPorts = {
+            Integer.MIN_VALUE,
+            -1000,
+            -1,
+            65536,
+            65537,
+            Integer.MAX_VALUE
+        };
+
+        for (int port: badPorts) {
+            SetTpSrcAction act = new SetTpSrcAction(port);
+            try {
+                new SetTpSrcActionImpl(act);
+                unexpected();
+            } catch (VTNException e) {
+                assertEquals(StatusCode.BADREQUEST, e.getStatus().getCode());
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link SetTpSrcActionImpl#equals(Object)} and
+     * {@link SetTpSrcActionImpl#hashCode()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testEquals() throws Exception {
+        HashSet<Object> set = new HashSet<Object>();
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpSrcAction act1 = new SetTpSrcAction(port);
+            SetTpSrcAction act2 = new SetTpSrcAction(port);
+            SetTpSrcActionImpl impl1 = new SetTpSrcActionImpl(act1);
+            SetTpSrcActionImpl impl2 = new SetTpSrcActionImpl(act2);
+            testEquals(set, impl1, impl2);
+        }
+
+        assertEquals(ports.length, set.size());
+    }
+
+    /**
+     * Test case for {@link SetTpSrcActionImpl#toString()}.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testToString() throws Exception {
+        String prefix = "SetTpSrcActionImpl[";
+        String suffix = "]";
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpSrcAction act = new SetTpSrcAction(port);
+            SetTpSrcActionImpl impl = new SetTpSrcActionImpl(act);
+            String a = "port=" + port;
+            String required = joinStrings(prefix, suffix, ",", a);
+            assertEquals(required, impl.toString());
+        }
+    }
+
+    /**
+     * Ensure that {@link SetTpSrcActionImpl} is serializable.
+     *
+     * @throws Exception  An unexpected exception occurred.
+     */
+    @Test
+    public void testSerialize() throws Exception {
+        int[] ports = {0, 1, 50, 100, 2000, 30000, 40000, 50000, 65535};
+        for (int port: ports) {
+            SetTpSrcAction act = new SetTpSrcAction(port);
+            SetTpSrcActionImpl impl = new SetTpSrcActionImpl(act);
+            serializeTest(impl);
+        }
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/EtherPacketTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/EtherPacketTest.java
new file mode 100644 (file)
index 0000000..665b115
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.packet;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.PacketContext;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.SetDlDst;
+import org.opendaylight.controller.sal.action.SetDlSrc;
+import org.opendaylight.controller.sal.action.SetVlanPcp;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchField;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.ARP;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IEEE8021Q;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+
+/**
+ * JUnit test for {@link EtherPacket}.
+ */
+public class EtherPacketTest extends TestBase {
+    /**
+     * The flag bit which indicates the source MAC address.
+     */
+    private static final int  ETH_SRC = 0x1;
+
+    /**
+     * The flag bit which indicates the destination MAC address.
+     */
+    private static final int  ETH_DST = 0x2;
+
+    /**
+     * The flag bit which indicates the VLAN ID.
+     */
+    private static final int  ETH_VLAN_VID = 0x4;
+
+    /**
+     * The flag bit which indicates the VLAN priority.
+     */
+    private static final int  ETH_VLAN_PCP = 0x8;
+
+
+    /**
+     * The flag bits which indicates all modifyable fields.
+     */
+    private static final int  ETH_ALL =
+        (ETH_SRC | ETH_DST | ETH_VLAN_VID | ETH_VLAN_PCP);
+
+    /**
+     * A node connector for test.
+     */
+    static final NodeConnector  NODE_CONNECTOR;
+
+    /**
+     * A raw payload for test.
+     */
+    static final byte[] RAW_PAYLOAD = {
+        (byte)0xaa, (byte)0xbb, (byte)0xcc, (byte)0xdd,
+        (byte)0xee, (byte)0xff, (byte)0x01, (byte)0x23,
+        (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab,
+        (byte)0xcd, (byte)0xef, (byte)0x00, (byte)0x01,
+    };
+
+    /**
+     * Initialize test class.
+     */
+    static {
+        // Create a node connector.
+        Node node = NodeCreator.createOFNode(Long.valueOf(1L));
+        NODE_CONNECTOR = NodeConnectorCreator.
+            createNodeConnector(Short.valueOf((short)1), node);
+    }
+
+    /**
+     * VLAN priority for test.
+     */
+    private byte  vlanPcp;
+
+    /**
+     * Test case for getter methods.
+     */
+    @Test
+    public void testGetter() {
+        byte[] bytes = {
+            (byte)0x00, (byte)0x0a, (byte)0x7f, (byte)0x88, (byte)0xff,
+        };
+        int[] types = {0x0001, 0x0800, 0x86dd};
+        short[] vlans = {0, 1, 2, 4095};
+        boolean arrayFirst = true;
+        ARP arp = createArp();
+        for (byte src: bytes) {
+            byte[] srcAddr = {
+                (byte)0x00, (byte)0x11, (byte)0x22,
+                (byte)0x33, (byte)0x44, src,
+            };
+            long srcMac = NetUtils.byteArray6ToLong(srcAddr);
+            for (byte dst: bytes) {
+                byte[] dstAddr = {
+                    (byte)0xf0, (byte)0xf1, (byte)0xf2,
+                    (byte)0xf3, (byte)0xf4, dst,
+                };
+                long dstMac = NetUtils.byteArray6ToLong(dstAddr);
+                for (int type: types) {
+                    for (short vlan: vlans) {
+                        // Create Ethernet frame using ARP packet.
+                        Ethernet pkt = createEthernet(srcAddr, dstAddr, type,
+                                                      vlan, arp);
+                        EtherPacket ether = new EtherPacket(pkt);
+                        if (arrayFirst) {
+                            assertArrayEquals(srcAddr,
+                                              ether.getSourceAddress());
+                            assertArrayEquals(dstAddr,
+                                              ether.getDestinationAddress());
+                            assertEquals(srcMac, ether.getSourceMacAddress());
+                            assertEquals(dstMac,
+                                         ether.getDestinationMacAddress());
+                            arrayFirst = false;
+                        } else {
+                            assertEquals(srcMac, ether.getSourceMacAddress());
+                            assertEquals(dstMac,
+                                         ether.getDestinationMacAddress());
+                            assertArrayEquals(srcAddr,
+                                              ether.getSourceAddress());
+                            assertArrayEquals(dstAddr,
+                                              ether.getDestinationAddress());
+                            arrayFirst = true;
+                        }
+
+                        assertEquals(type, ether.getEtherType());
+                        assertEquals(vlan, ether.getOriginalVlan());
+                        assertEquals(vlan, ether.getVlan());
+                        IEEE8021Q vlanTag;
+                        if (vlan == MatchType.DL_VLAN_NONE) {
+                            assertTrue(ether.getVlanPriority() < 0);
+                            vlanTag = null;
+                        } else {
+                            assertEquals(vlanPcp, ether.getVlanPriority());
+                            vlanTag = new IEEE8021Q();
+                            vlanTag.setCfi((byte)0).setPcp(vlanPcp).
+                                setVid(vlan).setEtherType((short)type);
+                        }
+                        assertEquals(vlanTag, ether.getVlanTag());
+                        assertEquals(arp, ether.getPayload());
+                        assertEquals(null, ether.getRawPayload());
+                        assertSame(pkt, ether.getPacket());
+
+                        // Create another Ethernet frame using raw packet.
+                        pkt = createEthernet(srcAddr, dstAddr, type, vlan,
+                                             RAW_PAYLOAD);
+                        ether = new EtherPacket(pkt);
+                        assertArrayEquals(srcAddr, ether.getSourceAddress());
+                        assertArrayEquals(dstAddr,
+                                          ether.getDestinationAddress());
+                        assertEquals(srcMac, ether.getSourceMacAddress());
+                        assertEquals(dstMac, ether.getDestinationMacAddress());
+                        assertEquals(type, ether.getEtherType());
+                        assertEquals(vlan, ether.getOriginalVlan());
+                        if (vlan == MatchType.DL_VLAN_NONE) {
+                            assertTrue(ether.getVlanPriority() < 0);
+                            vlanTag = null;
+                        } else {
+                            assertEquals(vlanPcp, ether.getVlanPriority());
+                            vlanTag = new IEEE8021Q();
+                            vlanTag.setCfi((byte)0).setPcp(vlanPcp).
+                                setVid(vlan).setEtherType((short)type);
+                        }
+                        assertEquals(vlanTag, ether.getVlanTag());
+                        assertEquals(null, ether.getPayload());
+                        assertArrayEquals(RAW_PAYLOAD, ether.getRawPayload());
+                        assertSame(pkt, ether.getPacket());
+
+                        // commit() should return false.
+                        PacketContext pctx =
+                            createPacketContext(pkt, NODE_CONNECTOR);
+                        assertFalse(ether.commit(pctx));
+                        assertEquals(null, pctx.getFilterActions());
+
+                        for (MatchType mtype: MatchType.values()) {
+                            assertFalse(pctx.hasMatchField(mtype));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for setter methods and {@link EtherPacket#clone()}.
+     */
+    @Test
+    public void testSetter() {
+        int type = 0x0806;
+        short[] vids = {MatchType.DL_VLAN_NONE, (short)1, (short)4095};
+
+        byte[] src0 = {
+            (byte)0x10, (byte)0x20, (byte)0x30,
+            (byte)0x40, (byte)0x50, (byte)0x60,
+        };
+        long srcMac0 = NetUtils.byteArray6ToLong(src0);
+        byte[] dst0 = {
+            (byte)0xf0, (byte)0xf1, (byte)0xf2,
+            (byte)0xfa, (byte)0xfb, (byte)0xfc,
+        };
+        long dstMac0 = NetUtils.byteArray6ToLong(dst0);
+
+        byte[] src1 = {
+            (byte)0xf0, (byte)0xf1, (byte)0xf2,
+            (byte)0xf3, (byte)0xf4, (byte)0xf5,
+        };
+        long srcMac1 = NetUtils.byteArray6ToLong(src1);
+        byte[] dst1 = {
+            (byte)0xa0, (byte)0xbc, (byte)0xde,
+            (byte)0xf1, (byte)0x23, (byte)0x45,
+        };
+        long dstMac1 = NetUtils.byteArray6ToLong(dst1);
+
+        byte[] src2 = {
+            (byte)0xe0, (byte)0xe1, (byte)0xe2,
+            (byte)0xe3, (byte)0xe4, (byte)0xe5,
+        };
+        byte[] dst2 = {
+            (byte)0xb0, (byte)0xcd, (byte)0xef,
+            (byte)0x12, (byte)0x34, (byte)0x56,
+        };
+
+        Map<Class<? extends Action>, Action> salActions =
+            new LinkedHashMap<Class<? extends Action>, Action>();
+        salActions.put(SetDlSrc.class, new SetDlSrc(src2));
+        salActions.put(SetDlDst.class, new SetDlDst(dst2));
+        salActions.put(SetVlanPcp.class, new SetVlanPcp(7));
+
+        byte[] spa = {(byte)10, (byte)20, (byte)30, (byte)40};
+        byte[] tpa = {(byte)192, (byte)168, (byte)0, (byte)254};
+        ARP arp = createArp();
+        ARP anotherArp = createArp(src2, dst2, spa, tpa, ARP.REPLY);
+
+        for (int flags = ETH_SRC; flags <= ETH_ALL; flags++) {
+            for (short vid: vids) {
+                for (short outVid: vids) {
+                    Ethernet pkt =
+                        createEthernet(src0, dst0, type, vid, arp);
+                    EtherPacket ether = new EtherPacket(pkt);
+
+                    byte[] src = src0;
+                    byte[] dst = dst0;
+                    long srcMac = srcMac0;
+                    long dstMac = dstMac0;
+                    short vlan = vid;
+                    byte pcp = vlanPcp;
+                    boolean setPcp = false;
+                    boolean mod = false;
+
+                    PacketContext pctx =
+                        createPacketContext(pkt, NODE_CONNECTOR);
+                    for (Action act: salActions.values()) {
+                        pctx.addFilterAction(act);
+                    }
+                    EtherPacket ether1 = ether.clone();
+                    assertNotSame(ether, ether1);
+
+                    if ((flags & ETH_SRC) != 0) {
+                        // Modify source address.
+                        ether1.setSourceAddress(src1);
+                        src = src1;
+                        srcMac = srcMac1;
+                        mod = true;
+                    }
+                    if ((flags & ETH_DST) != 0) {
+                        // Modify destination address.
+                        ether1.setDestinationAddress(dst1);
+                        dst = dst1;
+                        dstMac = dstMac1;
+                        mod = true;
+                    }
+                    if ((flags & ETH_VLAN_VID) != 0) {
+                        // Modify VLAN ID.
+                        ether1.setVlan(outVid);
+                        vlan = outVid;
+                    }
+                    if ((flags & ETH_VLAN_PCP) != 0) {
+                        // Modify VLAN priority.
+                        pcp = (byte)((pcp + 1) & 0x7);
+                        ether1.setVlanPriority(pcp);
+                        setPcp = true;
+                        if (vlan != MatchType.DL_VLAN_NONE) {
+                            mod = true;
+                        }
+                    }
+                    ether1.setPayload(anotherArp);
+                    assertSame(anotherArp, ether1.getPayload());
+
+                    assertArrayEquals(src, ether1.getSourceAddress());
+                    assertEquals(srcMac, ether1.getSourceMacAddress());
+                    assertArrayEquals(dst, ether1.getDestinationAddress());
+                    assertEquals(dstMac, ether1.getDestinationMacAddress());
+                    assertEquals(type, ether1.getEtherType());
+                    assertEquals(vlan, ether1.getVlan());
+                    assertEquals(vid, ether1.getOriginalVlan());
+                    if (vid == MatchType.DL_VLAN_NONE && !setPcp) {
+                        assertTrue(ether1.getVlanPriority() < 0);
+                    } else {
+                        assertEquals(pcp, ether1.getVlanPriority());
+                    }
+
+                    // SET_VLAN_PCP action should be removed if a VLAN tag is
+                    // not configured in outgoing packet.
+                    List<Action> actions = new ArrayList<Action>();
+                    for (Action act: salActions.values()) {
+                        if (!(vlan == MatchType.DL_VLAN_NONE &&
+                              SetVlanPcp.class.isInstance(act))) {
+                            actions.add(act);
+                        }
+                    }
+                    assertEquals(mod, ether1.commit(pctx));
+
+                    List<Action> filterActions =
+                        new ArrayList<Action>(pctx.getFilterActions());
+                    assertEquals(actions, filterActions);
+
+                    // Actions for unchanged field will be removed if
+                    // corresponding match type is configured in PacketContext.
+                    actions.clear();
+                    if ((flags & ETH_SRC) != 0) {
+                        actions.add(salActions.get(SetDlSrc.class));
+                    }
+                    if ((flags & ETH_DST) != 0) {
+                        actions.add(salActions.get(SetDlDst.class));
+                    }
+                    if (vlan != MatchType.DL_VLAN_NONE &&
+                        (flags & ETH_VLAN_PCP) != 0) {
+                        actions.add(salActions.get(SetVlanPcp.class));
+                    }
+                    for (MatchType mt: MatchType.values()) {
+                        pctx.addMatchField(mt);
+                    }
+
+                    assertEquals(mod, ether1.commit(pctx));
+                    filterActions =
+                        new ArrayList<Action>(pctx.getFilterActions());
+                    assertEquals(actions, filterActions);
+
+                    // EtherPacket class should never modify the packet.
+                    assertSame(pkt, ether1.getPacket());
+                    assertArrayEquals(src0, pkt.getSourceMACAddress());
+                    assertArrayEquals(dst0, pkt.getDestinationMACAddress());
+                    if (vid == MatchType.DL_VLAN_NONE) {
+                        assertEquals((short)type, pkt.getEtherType());
+                        assertSame(arp, pkt.getPayload());
+                    } else {
+                        assertEquals(EtherTypes.VLANTAGGED.shortValue(),
+                                     pkt.getEtherType());
+                        IEEE8021Q tag = (IEEE8021Q)pkt.getPayload();
+                        assertEquals(vid, tag.getVid());
+                        assertEquals(vlanPcp, tag.getPcp());
+                        assertSame(arp, tag.getPayload());
+                    }
+
+                    // The original packet should not be affected.
+                    assertArrayEquals(src0, ether.getSourceAddress());
+                    assertEquals(srcMac0, ether.getSourceMacAddress());
+                    assertArrayEquals(dst0, ether.getDestinationAddress());
+                    assertEquals(dstMac0, ether.getDestinationMacAddress());
+                    assertEquals(type, ether.getEtherType());
+                    assertSame(arp, ether.getPayload());
+                    if (vid == MatchType.DL_VLAN_NONE) {
+                        assertTrue(ether.getVlanPriority() < 0);
+                    } else {
+                        assertEquals(vlanPcp, ether.getVlanPriority());
+                    }
+
+                    // Set values in the original packet.
+                    ether1.setSourceAddress(src0);
+                    ether1.setDestinationAddress(dst0);
+                    ether1.setVlan(vid);
+                    ether1.setVlanPriority(vlanPcp);
+                    assertEquals(false, ether1.commit(pctx));
+                    assertArrayEquals(src0, ether1.getSourceAddress());
+                    assertEquals(srcMac0, ether1.getSourceMacAddress());
+                    assertArrayEquals(dst0, ether1.getDestinationAddress());
+                    assertEquals(dstMac0, ether1.getDestinationMacAddress());
+                    assertEquals(vid, ether1.getVlan());
+                    assertEquals(vlanPcp, ether1.getVlanPriority());
+                    assertEquals(type, ether1.getEtherType());
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link EtherPacket#setMatch(Match, Set)}.
+     */
+    @Test
+    public void testSetMatch() {
+        byte[] src = {
+            (byte)0x10, (byte)0x20, (byte)0x30,
+            (byte)0x40, (byte)0x50, (byte)0x60,
+        };
+        byte[] dst = {
+            (byte)0xf0, (byte)0xf1, (byte)0xf2,
+            (byte)0xfa, (byte)0xfb, (byte)0xfc,
+        };
+        int type = 0x0806;
+        short[] vids = {MatchType.DL_VLAN_NONE, (short)1, (short)4095};
+        Map<MatchType, MatchField> dlFields =
+            new HashMap<MatchType, MatchField>();
+        dlFields.put(MatchType.DL_SRC, new MatchField(MatchType.DL_SRC, src));
+        dlFields.put(MatchType.DL_DST, new MatchField(MatchType.DL_DST, dst));
+        dlFields.put(MatchType.DL_TYPE,
+                     new MatchField(MatchType.DL_TYPE,
+                                    Short.valueOf((short)type)));
+
+        byte[] src1 = {
+            (byte)0x00, (byte)0x44, (byte)0x66,
+            (byte)0x88, (byte)0xaa, (byte)0xcc,
+        };
+        byte[] dst1 = {
+            (byte)0xf0, (byte)0x33, (byte)0x55,
+            (byte)0x77, (byte)0x99, (byte)0xbb,
+        };
+
+        ARP arp = createArp();
+
+        for (short vid: vids) {
+            Ethernet pkt = createEthernet(src, dst, type, vid, arp);
+            EtherPacket ether = new EtherPacket(pkt);
+
+            // VLAN ID must be configured even if the type set is empty.
+            MatchField vlanVid =
+                new MatchField(MatchType.DL_VLAN, Short.valueOf(vid));
+            dlFields.put(MatchType.DL_VLAN_PR,
+                         new MatchField(MatchType.DL_VLAN_PR,
+                                        Byte.valueOf(vlanPcp)));
+
+            Match match = new Match();
+            Set<MatchType> fields = EnumSet.noneOf(MatchType.class);
+            ether.setMatch(match, fields);
+            List<MatchType> matches = match.getMatchesList();
+            assertEquals(1, matches.size());
+            assertEquals(vlanVid, match.getField(MatchType.DL_VLAN));
+
+            for (Map.Entry<MatchType, MatchField> entry: dlFields.entrySet()) {
+                MatchType mtype = entry.getKey();
+
+                match = new Match();
+                fields = EnumSet.of(mtype);
+                ether.setMatch(match, fields);
+                matches = match.getMatchesList();
+                assertEquals(vlanVid, match.getField(MatchType.DL_VLAN));
+                if (vid == MatchType.DL_VLAN_NONE &&
+                    mtype == MatchType.DL_VLAN_PR) {
+                    assertEquals(1, matches.size());
+                    assertEquals(null, match.getField(mtype));
+                } else {
+                    MatchField mfield = entry.getValue();
+                    assertEquals(2, matches.size());
+                    assertEquals(mfield, match.getField(mtype));
+                }
+            }
+
+            // setMatch() always has to see the original.
+            byte pri = (byte)((vlanPcp + 1) & 0x7);
+            ether.setSourceAddress(src1);
+            ether.setDestinationAddress(dst1);
+            ether.setVlan((short)((vid + 1) & 0xfff));
+            ether.setVlanPriority(vlanPcp);
+            fields = EnumSet.noneOf(MatchType.class);
+            fields.addAll(dlFields.keySet());
+            fields.add(MatchType.DL_VLAN);
+            match = new Match();
+            ether.setMatch(match, fields);
+            matches = match.getMatchesList();
+            assertEquals(vlanVid, match.getField(MatchType.DL_VLAN));
+            if (vid == MatchType.DL_VLAN_NONE) {
+                assertEquals(dlFields.size(), matches.size());
+                assertEquals(null, match.getField(MatchType.DL_VLAN_PR));
+            } else {
+                assertEquals(dlFields.size() + 1, matches.size());
+                for (Map.Entry<MatchType, MatchField> entry:
+                         dlFields.entrySet()) {
+                    MatchType mtype = entry.getKey();
+                    MatchField mfield = entry.getValue();
+                    assertEquals(mfield, match.getField(mtype));
+                }
+            }
+        }
+    }
+
+    /**
+     * Create an {@link Ethernet} instance for test.
+     *
+     * @param src      Source MAC address.
+     * @param dst      Destination MAC address.
+     * @param type     Ethernet type.
+     * @param vid      A VLAN ID.
+     * @param payload  A {@link Packet} instance to be set as payload.
+     * @return  An {@link Ethernet} instance.
+     */
+    private Ethernet createEthernet(byte[] src, byte[] dst, int type,
+                                    short vid, Packet payload) {
+        byte pcp;
+        if (vid == MatchType.DL_VLAN_NONE) {
+            pcp = 0;
+        } else {
+            pcp = (byte)((vlanPcp + 1) & 7);
+            vlanPcp = pcp;
+        }
+
+        return createEthernet(src, dst, type, vid, pcp, payload);
+    }
+
+    /**
+     * Create an {@link Ethernet} instance for test.
+     *
+     * @param src   Source MAC address.
+     * @param dst   Destination MAC address.
+     * @param type  Ethernet type.
+     * @param vid   A VLAN ID.
+     * @param raw   A byte array to be set as payload.
+     * @return  An {@link Ethernet} instance.
+     */
+    private Ethernet createEthernet(byte[] src, byte[] dst, int type,
+                                    short vid, byte[] raw) {
+        byte pcp;
+        if (vid == MatchType.DL_VLAN_NONE) {
+            pcp = 0;
+        } else {
+            pcp = (byte)((vlanPcp + 1) & 7);
+            vlanPcp = pcp;
+        }
+
+        return createEthernet(src, dst, type, vid, pcp, raw);
+    }
+
+    /**
+     * Create an ARP packet for test.
+     *
+     * @return  An {@link ARP} instance.
+     */
+    private ARP createArp() {
+        byte[] sha = {
+            (byte)0x01, (byte)0x23, (byte)0x45,
+            (byte)0x67, (byte)0x89, (byte)0xab,
+        };
+        byte[] tha = {
+            (byte)0xff, (byte)0xff, (byte)0xff,
+            (byte)0xff, (byte)0xff, (byte)0xff,
+        };
+        byte[] spa = {(byte)10, (byte)0, (byte)1, (byte)2};
+        byte[] tpa = {(byte)192, (byte)168, (byte)100, (byte)200};
+
+        return createArp(sha, tha, spa, tpa, ARP.REQUEST);
+    }
+
+    /**
+     * Create an ARP packet for test.
+     *
+     * @param sha  Sender hardware address.
+     * @param tha  Target hardware address.
+     * @param spa  Sender protocol address.
+     * @param tpa  Target protocol address.
+     * @param op   ARP operation code.
+     * @return  An {@link ARP} instance.
+     */
+    private ARP createArp(byte[] sha, byte[] tha, byte[] spa, byte[] tpa,
+                          short op) {
+        ARP arp = new ARP();
+        arp.setHardwareType(ARP.HW_TYPE_ETHERNET).
+            setHardwareAddressLength((byte)NetUtils.MACAddrLengthInBytes).
+            setProtocolType(EtherTypes.IPv4.shortValue()).
+            setProtocolAddressLength((byte)4).
+            setOpCode(op).
+            setSenderHardwareAddress(sha).
+            setTargetHardwareAddress(tha).
+            setSenderProtocolAddress(spa).
+            setTargetProtocolAddress(tpa);
+
+        return arp;
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacketTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacketTest.java
new file mode 100644 (file)
index 0000000..2392bc4
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.packet;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.PacketContext;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchField;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.ICMP;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+
+/**
+ * JUnit test for {@link IcmpPacket}.
+ */
+public class IcmpPacketTest extends TestBase {
+    /**
+     * The flag bit which indicates the ICMP type.
+     */
+    private static final int  ICMP_TYPE = 0x1;
+
+    /**
+     * The flag bit which indicates the ICMP code.
+     */
+    private static final int  ICMP_CODE = 0x2;
+
+    /**
+     * The flag bits which indicates all modifyable fields.
+     */
+    private static final int  ICMP_ALL = (ICMP_TYPE | ICMP_CODE);
+
+    /**
+     * ICMP identifier.
+     */
+    private short  identifier;
+
+    /**
+     * ICMP sequence number.
+     */
+    private short  sequenceNumber = 0x777;
+
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        short[] types = {0, 1, 10, 50, 80, 100, 150, 200, 254, 255};
+        short[] codes = {0, 1, 33, 66, 88, 128, 143, 230, 254, 255};
+        for (short type: types) {
+            for (short code: codes) {
+                ICMP pkt = createICMP(type, code);
+                IcmpPacket icmp = new IcmpPacket(pkt);
+                assertEquals(type, icmp.getType());
+                assertEquals(code, icmp.getCode());
+
+                // commit() should return false.
+                Ethernet ether = createEthernet(pkt);
+                PacketContext pctx = createPacketContext(
+                    ether, EtherPacketTest.NODE_CONNECTOR);
+                assertFalse(icmp.commit(pctx));
+                assertEquals(null, pctx.getFilterActions());
+                for (MatchType mtype: MatchType.values()) {
+                    assertFalse(pctx.hasMatchField(mtype));
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for setter methods and below methods.
+     *
+     * <ul>
+     *   <li>{@link IcmpPacket#clone()}</li>
+     *   <li>{@link IcmpPacket#updateChecksum(Inet4Packet)}</li>
+     * </ul>
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testSetter() throws Exception {
+        short type0 = 0;
+        short code0 = 8;
+        short type1 = 10;
+        short code1 = 100;
+        short type2 = 33;
+        short code2 = -1;
+
+        Map<Class<? extends Action>, Action> salActions =
+            new LinkedHashMap<Class<? extends Action>, Action>();
+        salActions.put(SetTpSrc.class, new SetTpSrc(type2));
+        salActions.put(SetTpDst.class, new SetTpDst(code2));
+
+        for (int flags = ICMP_TYPE; flags <= ICMP_ALL; flags++) {
+            ICMP pkt = createICMP(type0, code0);
+            IcmpPacket icmp = new IcmpPacket(pkt);
+            short checksum = pkt.getChecksum();
+
+            short type = type0;
+            short code = code0;
+            Ethernet ether = createEthernet(pkt);
+            PacketContext pctx =
+                createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            IcmpPacket icmp1 = icmp.clone();
+
+            if ((flags & ICMP_TYPE) != 0) {
+                // Modify ICMP type.
+                icmp1.setType(type1);
+                type = type1;
+            }
+            if ((flags & ICMP_CODE) != 0) {
+                // Modify ICMP code.
+                icmp1.setCode(code1);
+                code = code1;
+            }
+
+            assertEquals(type, icmp1.getType());
+            assertEquals(code, icmp1.getCode());
+
+            // The packet should not be modified until commit() is called.
+            assertSame(pkt, icmp1.getPacket());
+
+            assertEquals(true, icmp1.commit(pctx));
+            ICMP newPkt = icmp1.getPacket();
+            assertNotSame(pkt, newPkt);
+            assertEquals((byte)type, newPkt.getType());
+            assertEquals((byte)code, newPkt.getCode());
+            assertEquals(checksum, newPkt.getChecksum());
+            assertEquals(identifier, newPkt.getIdentifier());
+            assertEquals(sequenceNumber, newPkt.getSequenceNumber());
+            assertEquals((byte)type0, pkt.getType());
+            assertEquals((byte)code0, pkt.getCode());
+            assertEquals(checksum, pkt.getChecksum());
+            assertEquals(identifier, pkt.getIdentifier());
+            assertEquals(sequenceNumber, pkt.getSequenceNumber());
+
+            assertTrue(pctx.hasMatchField(MatchType.DL_TYPE));
+            assertTrue(pctx.hasMatchField(MatchType.NW_PROTO));
+
+            // updateChecksum() must return false.
+            assertFalse(icmp.updateChecksum(null));
+
+            List<Action> filterActions =
+                new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(new ArrayList<Action>(salActions.values()),
+                         filterActions);
+
+            // Actions for unchanged field will be removed if corresponding
+            // match type is configured in PacketContext.
+            List<Action> actions = new ArrayList<Action>();
+            if ((flags & ICMP_TYPE) != 0) {
+                actions.add(salActions.get(SetTpSrc.class));
+            }
+            if ((flags & ICMP_CODE) != 0) {
+                actions.add(salActions.get(SetTpDst.class));
+            }
+
+            pctx = createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (MatchType mt: MatchType.values()) {
+                pctx.addMatchField(mt);
+            }
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            assertEquals(true, icmp1.commit(pctx));
+            assertSame(newPkt, icmp1.getPacket());
+            filterActions = new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(actions, filterActions);
+
+            // The original packet should not be affected.
+            assertEquals(type0, icmp.getType());
+            assertEquals(code0, icmp.getCode());
+            assertSame(pkt, icmp.getPacket());
+
+            assertEquals((byte)type0, pkt.getType());
+            assertEquals((byte)code0, pkt.getCode());
+            assertEquals(checksum, pkt.getChecksum());
+            assertEquals(identifier, pkt.getIdentifier());
+            assertEquals(sequenceNumber, pkt.getSequenceNumber());
+
+            // Set values in the original packet.
+            icmp1.setType(type0);
+            icmp1.setCode(code0);
+            assertEquals(false, icmp1.commit(pctx));
+            assertEquals(type0, icmp1.getType());
+            assertEquals(code0, icmp1.getCode());
+        }
+    }
+
+    /**
+     * Test case for {@link IcmpPacket#setMatch(Match, Set)}.
+     */
+    @Test
+    public void testSetMatch() {
+        short type = 33;
+        short code = 66;
+        Map<MatchType, MatchField> tpFields =
+            new HashMap<MatchType, MatchField>();
+        tpFields.put(MatchType.TP_SRC, new MatchField(MatchType.TP_SRC, type));
+        tpFields.put(MatchType.TP_DST, new MatchField(MatchType.TP_DST, code));
+
+        short type1 = 29;
+        short code1 = 1;
+        ICMP pkt = createICMP(type, code);
+        IcmpPacket icmp = new IcmpPacket(pkt);
+
+        Match match = new Match();
+        Set<MatchType> fields = EnumSet.noneOf(MatchType.class);
+        icmp.setMatch(match, fields);
+        List<MatchType> matches = match.getMatchesList();
+        assertEquals(0, matches.size());
+
+        for (Map.Entry<MatchType, MatchField> entry: tpFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+
+            match = new Match();
+            fields = EnumSet.of(mtype);
+            icmp.setMatch(match, fields);
+            matches = match.getMatchesList();
+            assertEquals(1, matches.size());
+            assertEquals(mfield, match.getField(mtype));
+        }
+
+        // setMatch() always has to see the original.
+        icmp.setType(type1);
+        icmp.setCode(code1);
+        fields = EnumSet.noneOf(MatchType.class);
+        fields.addAll(tpFields.keySet());
+        match = new Match();
+        icmp.setMatch(match, fields);
+        matches = match.getMatchesList();
+        assertEquals(tpFields.size(), matches.size());
+        for (Map.Entry<MatchType, MatchField> entry: tpFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+            assertEquals(mfield, match.getField(mtype));
+        }
+    }
+
+    /**
+     * Create an {@link ICMP} instance for test.
+     *
+     * @param type   ICMP type.
+     * @param code   ICMP code.
+     * @return  An {@link ICMP} instance.
+     */
+    private ICMP createICMP(short type, short code) {
+        ICMP pkt = new ICMP();
+        identifier++;
+        sequenceNumber++;
+        pkt.setType((byte)type).setCode((byte)code).
+            setIdentifier(identifier).setSequenceNumber(sequenceNumber);
+
+        // Serialize the packet to update checksum.
+        return copy(pkt, new ICMP());
+    }
+
+    /**
+     * Create an {@link Ethernet} instance that contains the given ICMP packet.
+     *
+     * @param icmp  An {@link ICMP} instance.
+     * @return  An {@link Ethernet} instance.
+     */
+    private Ethernet createEthernet(ICMP icmp) {
+        IPv4 ipv4 = createIPv4(IPProtocols.ICMP.shortValue(), icmp);
+        return createEthernet(ipv4);
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/Inet4PacketTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/Inet4PacketTest.java
new file mode 100644 (file)
index 0000000..295fbde
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.packet;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.PacketContext;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.action.SetNwSrc;
+import org.opendaylight.controller.sal.action.SetNwTos;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchField;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+/**
+ * JUnit test for {@link Inet4Packet}.
+ */
+public class Inet4PacketTest extends TestBase {
+    /**
+     * The flag bit which indicates the source IP address.
+     */
+    private static final int  IPV4_SRC = 0x1;
+
+    /**
+     * The flag bit which indicates the destination IP address.
+     */
+    private static final int  IPV4_DST = 0x2;
+
+    /**
+     * The flag bit which indicates the DSCP field.
+     */
+    private static final int  IPV4_DSCP = 0x4;
+
+    /**
+     * The flag bits which indicates all modifyable fields.
+     */
+    private static final int  IPV4_ALL = (IPV4_SRC | IPV4_DST | IPV4_DSCP);
+
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        byte[] bytes = {
+            (byte)0, (byte)1, (byte)33, (byte)128, (byte)192, (byte)255,
+        };
+        short[] protocols = {1, 6, 17, 100, 255};
+        byte[] dscps = {(byte)0, (byte)1, (byte)10, (byte)40, (byte)63};
+        boolean inetFirst = true;
+        for (byte s: bytes) {
+            byte[] srcBytes = {(byte)10, (byte)1, (byte)2, s};
+            InetAddress src = createInetAddress(srcBytes);
+            int srcAddr = NetUtils.byteArray4ToInt(srcBytes);
+            for (byte d: bytes) {
+                byte[] dstBytes = {(byte)192, (byte)168, (byte)200, d};
+                InetAddress dst = createInetAddress(dstBytes);
+                int dstAddr = NetUtils.byteArray4ToInt(dstBytes);
+                for (short proto: protocols) {
+                    for (byte dscp: dscps) {
+                        IPv4 pkt = createIPv4Packet(src, dst, proto, dscp);
+                        Inet4Packet ipv4 = new Inet4Packet(pkt);
+                        if (inetFirst) {
+                            assertEquals(src, ipv4.getSourceInetAddress());
+                            assertEquals(dst, ipv4.getDestinationInetAddress());
+                            assertEquals(srcAddr, ipv4.getSourceAddress());
+                            assertEquals(dstAddr, ipv4.getDestinationAddress());
+                            inetFirst = false;
+                        } else {
+                            assertEquals(srcAddr, ipv4.getSourceAddress());
+                            assertEquals(dstAddr, ipv4.getDestinationAddress());
+                            assertEquals(src, ipv4.getSourceInetAddress());
+                            assertEquals(dst, ipv4.getDestinationInetAddress());
+                            inetFirst = true;
+                        }
+
+                        assertEquals(proto, ipv4.getProtocol());
+                        assertEquals(dscp, ipv4.getDscp());
+                        assertEquals(false, ipv4.isAddressModified());
+
+                        // commit() should return false.
+                        Ethernet ether = createEthernet(pkt);
+                        PacketContext pctx = createPacketContext(
+                            ether, EtherPacketTest.NODE_CONNECTOR);
+                        assertFalse(ipv4.commit(pctx));
+                        assertEquals(null, pctx.getFilterActions());
+
+                        for (MatchType mtype: MatchType.values()) {
+                            assertFalse(pctx.hasMatchField(mtype));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for setter methods and {@link Inet4Packet#clone()}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testSetter() throws Exception {
+        short proto = 111;
+        byte dscp0 = 1;
+        byte dscp1 = 63;
+
+        byte[] srcBytes0 = {(byte)192, (byte)168, (byte)33, (byte)44};
+        byte[] dstBytes0 = {(byte)172, (byte)30, (byte)40, (byte)50};
+        InetAddress src0 = createInetAddress(srcBytes0);
+        InetAddress dst0 = createInetAddress(dstBytes0);
+        int srcAddr0 = NetUtils.byteArray4ToInt(srcBytes0);
+        int dstAddr0 = NetUtils.byteArray4ToInt(dstBytes0);
+
+        byte[] srcBytes1 = {(byte)127, (byte)0, (byte)0, (byte)1};
+        byte[] dstBytes1 = {(byte)10, (byte)100, (byte)220, (byte)254};
+        InetAddress src1 = createInetAddress(srcBytes1);
+        InetAddress dst1 = createInetAddress(dstBytes1);
+        int srcAddr1 = NetUtils.byteArray4ToInt(srcBytes1);
+        int dstAddr1 = NetUtils.byteArray4ToInt(dstBytes1);
+
+        byte[] srcBytes2 = {(byte)127, (byte)0, (byte)0, (byte)1};
+        byte[] dstBytes2 = {(byte)10, (byte)100, (byte)220, (byte)254};
+        InetAddress src2 = createInetAddress(srcBytes1);
+        InetAddress dst2 = createInetAddress(dstBytes2);
+        int srcAddr2 = NetUtils.byteArray4ToInt(srcBytes2);
+        int dstAddr2 = NetUtils.byteArray4ToInt(dstBytes2);
+        byte dscp2 = 30;
+
+        Map<Class<? extends Action>, Action> salActions =
+            new LinkedHashMap<Class<? extends Action>, Action>();
+        salActions.put(SetNwSrc.class, new SetNwSrc(src2));
+        salActions.put(SetNwDst.class, new SetNwDst(dst2));
+        salActions.put(SetNwTos.class, new SetNwTos(dscp2));
+
+        for (int flags = IPV4_SRC; flags <= IPV4_ALL; flags++) {
+            IPv4 pkt = createIPv4Packet(src0, dst0, proto, dscp0);
+            Inet4Packet ipv4 = new Inet4Packet(pkt);
+
+            InetAddress src = src0;
+            InetAddress dst = dst0;
+            int srcAddr = srcAddr0;
+            int dstAddr = dstAddr0;
+            byte dscp = dscp0;
+
+            Ethernet ether = createEthernet(pkt);
+            PacketContext pctx =
+                createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+
+            Inet4Packet ip = ipv4.clone();
+            boolean addrModified = false;
+            if ((flags & IPV4_SRC) != 0) {
+                // Modify source address.
+                ip.setSourceAddress(src1);
+                src = src1;
+                srcAddr = srcAddr1;
+                addrModified = true;
+            }
+            if ((flags & IPV4_DST) != 0) {
+                // Modify destination address.
+                ip.setDestinationAddress(dst1);
+                dst = dst1;
+                dstAddr = dstAddr1;
+                addrModified = true;
+            }
+            if ((flags & IPV4_DSCP) != 0) {
+                // Modify DSCP field.
+                ip.setDscp(dscp1);
+                dscp = dscp1;
+            }
+
+            assertEquals(src, ip.getSourceInetAddress());
+            assertEquals(srcAddr, ip.getSourceAddress());
+            assertEquals(dst, ip.getDestinationInetAddress());
+            assertEquals(dstAddr, ip.getDestinationAddress());
+            assertEquals(proto, ip.getProtocol());
+            assertEquals(dscp, ip.getDscp());
+            assertEquals(addrModified, ip.isAddressModified());
+
+            // The packet should not be modified until commit() is called.
+            assertSame(pkt, ip.getPacket());
+
+            assertEquals(true, ip.commit(pctx));
+            IPv4 newPkt = ip.getPacket();
+            assertNotSame(pkt, newPkt);
+            assertEquals(srcAddr, newPkt.getSourceAddress());
+            assertEquals(dstAddr, newPkt.getDestinationAddress());
+            assertEquals((byte)proto, newPkt.getProtocol());
+            assertEquals(dscp, newPkt.getDiffServ());
+
+            List<Action> filterActions =
+                new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(new ArrayList<Action>(salActions.values()),
+                         filterActions);
+            assertTrue(pctx.hasMatchField(MatchType.DL_TYPE));
+
+            // Actions for unchanged field will be removed if corresponding
+            // match type is configured in PacketContext.
+            List<Action> actions = new ArrayList<Action>();
+            if ((flags & IPV4_SRC) != 0) {
+                actions.add(salActions.get(SetNwSrc.class));
+            }
+            if ((flags & IPV4_DST) != 0) {
+                actions.add(salActions.get(SetNwDst.class));
+            }
+            if ((flags & IPV4_DSCP) != 0) {
+                actions.add(salActions.get(SetNwTos.class));
+            }
+
+            pctx = createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (MatchType mt: MatchType.values()) {
+                pctx.addMatchField(mt);
+            }
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            assertEquals(true, ip.commit(pctx));
+            assertSame(newPkt, ip.getPacket());
+            filterActions = new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(actions, filterActions);
+
+            // The original packet should not be affected.
+            assertEquals(srcAddr0, ipv4.getSourceAddress());
+            assertEquals(dstAddr0, ipv4.getDestinationAddress());
+            assertEquals(proto, ipv4.getProtocol());
+            assertEquals(dscp0, ipv4.getDscp());
+            assertSame(pkt, ipv4.getPacket());
+
+            assertEquals(srcAddr0, pkt.getSourceAddress());
+            assertEquals(dstAddr0, pkt.getDestinationAddress());
+            assertEquals((byte)proto, pkt.getProtocol());
+            assertEquals(dscp0, pkt.getDiffServ());
+
+            // Set values in the original packet.
+            ip.setSourceAddress(src0);
+            ip.setDestinationAddress(dst0);
+            ip.setDscp(dscp0);
+            assertEquals(false, ip.commit(pctx));
+            assertEquals(src0, ip.getSourceInetAddress());
+            assertEquals(srcAddr0, ip.getSourceAddress());
+            assertEquals(dst0, ip.getDestinationInetAddress());
+            assertEquals(dstAddr0, ip.getDestinationAddress());
+            assertEquals(proto, ip.getProtocol());
+            assertEquals(dscp0, ip.getDscp());
+        }
+    }
+
+    /**
+     * Test case for {@link Inet4Packet#setMatch(Match, Set)}.
+     */
+    @Test
+    public void testSetMatch() {
+        byte[] srcAddr = {(byte)10, (byte)1, (byte)2, (byte)3};
+        byte[] dstAddr = {(byte)192, (byte)168, (byte)111, (byte)222};
+        InetAddress src = createInetAddress(srcAddr);
+        InetAddress dst = createInetAddress(dstAddr);
+        byte dscp = 0;
+        short proto = 100;
+        Map<MatchType, MatchField> nwFields =
+            new HashMap<MatchType, MatchField>();
+        nwFields.put(MatchType.NW_SRC, new MatchField(MatchType.NW_SRC, src));
+        nwFields.put(MatchType.NW_DST, new MatchField(MatchType.NW_DST, dst));
+        nwFields.put(MatchType.NW_PROTO,
+                     new MatchField(MatchType.NW_PROTO,
+                                    Byte.valueOf((byte)proto)));
+        nwFields.put(MatchType.NW_TOS,
+                     new MatchField(MatchType.NW_TOS, Byte.valueOf(dscp)));
+
+        byte[] srcAddr1 = {(byte)100, (byte)200, (byte)33, (byte)44};
+        byte[] dstAddr1 = {(byte)172, (byte)16, (byte)123, (byte)234};
+        InetAddress src1 = createInetAddress(srcAddr1);
+        InetAddress dst1 = createInetAddress(dstAddr1);
+        byte dscp1 = 63;
+
+        IPv4 pkt = createIPv4Packet(src, dst, proto, dscp);
+        Inet4Packet ipv4 = new Inet4Packet(pkt);
+
+        Match match = new Match();
+        Set<MatchType> fields = EnumSet.noneOf(MatchType.class);
+        ipv4.setMatch(match, fields);
+        List<MatchType> matches = match.getMatchesList();
+        assertEquals(0, matches.size());
+
+        for (Map.Entry<MatchType, MatchField> entry: nwFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+
+            match = new Match();
+            fields = EnumSet.of(mtype);
+            ipv4.setMatch(match, fields);
+            matches = match.getMatchesList();
+            assertEquals(1, matches.size());
+            assertEquals(mfield, match.getField(mtype));
+        }
+
+        // setMatch() always has to see the original.
+        ipv4.setSourceAddress(src1);
+        ipv4.setDestinationAddress(dst1);
+        ipv4.setDscp(dscp1);
+        fields = EnumSet.noneOf(MatchType.class);
+        fields.addAll(nwFields.keySet());
+        match = new Match();
+        ipv4.setMatch(match, fields);
+        matches = match.getMatchesList();
+        assertEquals(nwFields.size(), matches.size());
+        for (Map.Entry<MatchType, MatchField> entry: nwFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+            assertEquals(mfield, match.getField(mtype));
+        }
+    }
+
+    /**
+     * Test case for {@link Inet4Packet#getHeaderForChecksum(byte, short)}.
+     */
+    @Test
+    public void testGetHeaderForChecksum() {
+        byte[] srcAddr = {(byte)100, (byte)200, (byte)33, (byte)44};
+        byte[] dstAddr = {(byte)172, (byte)16, (byte)123, (byte)234};
+        InetAddress src = createInetAddress(srcAddr);
+        InetAddress dst = createInetAddress(dstAddr);
+        short protocol = 100;
+        short len = 60;
+        IPv4 pkt = createIPv4Packet(src, dst, protocol, (byte)0);
+        Inet4Packet ipv4 = new Inet4Packet(pkt);
+        byte[] header = ipv4.getHeaderForChecksum((byte)protocol, len);
+        verifyChecksumHeader(header, srcAddr, dstAddr, (byte)protocol, len);
+
+        Random rand = new Random();
+
+        for (int i = 0; i < 30; i++) {
+            srcAddr = NetUtils.intToByteArray4(rand.nextInt());
+            dstAddr = NetUtils.intToByteArray4(rand.nextInt());
+            src = createInetAddress(srcAddr);
+            dst = createInetAddress(dstAddr);
+            short proto = (short)(rand.nextInt() & 0xff);
+            do {
+                len = (short)(rand.nextInt() & 0xffff);
+            } while (len < 20);
+
+            ipv4.setSourceAddress(src);
+            ipv4.setDestinationAddress(dst);
+            header = ipv4.getHeaderForChecksum((byte)proto, len);
+            verifyChecksumHeader(header, srcAddr, dstAddr, (byte)proto, len);
+        }
+    }
+
+    /**
+     * Create an {@link IPv4} instance for test.
+     *
+     * @param src    Source IP address.
+     * @param dst    Destination IP address.
+     * @param proto  IP protocol number.
+     * @param dscp   DSCP field value.
+     * @return  An {@link IPv4} instance.
+     */
+    private IPv4 createIPv4Packet(InetAddress src, InetAddress dst,
+                                  short proto, byte dscp) {
+        IPv4 pkt = createIPv4(src, dst, proto, dscp);
+        pkt.setRawPayload(EtherPacketTest.RAW_PAYLOAD);
+
+        return pkt;
+    }
+
+    /**
+     * Verify the contents of pseudo IPv4 header for computing checksum.
+     *
+     * @param header  Header to be tested.
+     * @param src     Expected source IP address.
+     * @param dst     Expected destination IP address.
+     * @param proto   Expected IP protocol number.
+     * @param len     Expected packet length.
+     */
+    private void verifyChecksumHeader(byte[] header, byte[] src, byte[] dst,
+                                      byte proto, short len) {
+        assertEquals(12, header.length);
+        for (int i = 0; i < src.length; i++) {
+            assertEquals(src[i], header[i]);
+        }
+
+        int off = src.length;
+        for (int i = 0; i < dst.length; i++) {
+            assertEquals(dst[i], header[off + i]);
+        }
+
+        off += dst.length;
+        assertEquals(0, header[off]);
+
+        off++;
+        assertEquals((byte)proto, header[off]);
+
+        off++;
+        assertEquals((byte)(len >>> Byte.SIZE), header[off]);
+        off++;
+        assertEquals((byte)(len & ((1 << Byte.SIZE) - 1)), header[off]);
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/TcpPacketTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/TcpPacketTest.java
new file mode 100644 (file)
index 0000000..826a6e3
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.packet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.PacketContext;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchField;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.TCP;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+
+/**
+ * JUnit test for {@link TcpPacket}.
+ */
+public class TcpPacketTest extends TestBase {
+    /**
+     * The flag bit which indicates the source port number.
+     */
+    private static final int  TCP_SRC = 0x1;
+
+    /**
+     * The flag bit which indicates the destination port number.
+     */
+    private static final int  TCP_DST = 0x2;
+
+    /**
+     * The flag bits which indicates all modifyable fields.
+     */
+    private static final int  TCP_ALL = (TCP_SRC | TCP_DST);
+
+    /**
+     * Sequence number.
+     */
+    private int  sequenceNumber = 3000;
+
+    /**
+     * Acknowledgement number.
+     */
+    private int  ackNumber = 4096;
+
+    /**
+     * Data offset.
+     */
+    private byte  dataOffset = 1;
+
+    /**
+     * Reserved field.
+     */
+    private byte  reserved = -1;
+
+    /**
+     * TCP flags.
+     */
+    private short tcpFlags;
+
+    /**
+     * Window size.
+     */
+    private short  windowSize = 0x777;
+
+    /**
+     * Checksum.
+     */
+    private short  checksum = 0x1234;
+
+    /**
+     * Urgent pointer.
+     */
+    private short  urgentPointer = (short)-43210;
+
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        int[] srcs = {0, 1, 53, 123, 12000, 23567, 45900, 58123, 60000, 65535};
+        int[] dsts = {0, 2, 35, 99, 390, 14567, 39734, 47614, 59198, 65535};
+        for (int src: srcs) {
+            for (int dst: dsts) {
+                TCP pkt = createTCP(src, dst);
+                TcpPacket tcp = new TcpPacket(pkt);
+                assertEquals(src, tcp.getSourcePort());
+                assertEquals(dst, tcp.getDestinationPort());
+
+                // commit() should return false.
+                Ethernet ether = createEthernet(pkt);
+                PacketContext pctx = createPacketContext(
+                    ether, EtherPacketTest.NODE_CONNECTOR);
+                assertFalse(tcp.commit(pctx));
+                assertEquals(null, pctx.getFilterActions());
+                for (MatchType mtype: MatchType.values()) {
+                    assertFalse(pctx.hasMatchField(mtype));
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for setter methods and {@link TcpPacket#clone()}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testSetter() throws Exception {
+        int src0 = 1;
+        int dst0 = 80;
+        int src1 = 52984;
+        int dst1 = 9999;
+        int src2 = 413;
+        int dst2 = 60345;
+
+        Map<Class<? extends Action>, Action> salActions =
+            new LinkedHashMap<Class<? extends Action>, Action>();
+        salActions.put(SetTpSrc.class, new SetTpSrc(src2));
+        salActions.put(SetTpDst.class, new SetTpDst(dst2));
+
+        for (int flags = TCP_SRC; flags <= TCP_ALL; flags++) {
+            TCP pkt = createTCP(src0, dst0);
+            TCP original = copy(pkt, new TCP());
+            TcpPacket tcp = new TcpPacket(pkt);
+
+            int src = src0;
+            int dst = dst0;
+            Ethernet ether = createEthernet(pkt);
+            PacketContext pctx =
+                createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            TcpPacket tcp1 = (TcpPacket)tcp.clone();
+            TCP pkt2 = copy(original, new TCP());
+            if ((flags & TCP_SRC) != 0) {
+                // Modify source port number.
+                tcp1.setSourcePort(src1);
+                pkt2.setSourcePort((short)src1);
+                src = src1;
+            }
+            if ((flags & TCP_DST) != 0) {
+                // Modify destination port number.
+                tcp1.setDestinationPort(dst1);
+                pkt2.setDestinationPort((short)dst1);
+                dst = dst1;
+            }
+
+            assertEquals(src, tcp1.getSourcePort());
+            assertEquals(dst, tcp1.getDestinationPort());
+
+            // The packet should not be modified until commit() is called.
+            assertSame(pkt, tcp1.getPacket());
+            assertEquals(original, pkt);
+
+            assertEquals(true, tcp1.commit(pctx));
+            TCP newPkt = tcp1.getPacket();
+            assertNotSame(pkt, newPkt);
+            assertEquals((short)src, newPkt.getSourcePort());
+            assertEquals((short)dst, newPkt.getDestinationPort());
+            assertEquals(pkt2, newPkt);
+
+            assertEquals((short)src0, pkt.getSourcePort());
+            assertEquals((short)dst0, pkt.getDestinationPort());
+            assertEquals(original, pkt);
+
+            assertTrue(pctx.hasMatchField(MatchType.DL_TYPE));
+            assertTrue(pctx.hasMatchField(MatchType.NW_PROTO));
+
+            List<Action> filterActions =
+                new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(new ArrayList<Action>(salActions.values()),
+                         filterActions);
+
+            // Actions for unchanged field will be removed if corresponding
+            // match type is configured in PacketContext.
+            List<Action> actions = new ArrayList<Action>();
+            if ((flags & TCP_SRC) != 0) {
+                actions.add(salActions.get(SetTpSrc.class));
+            }
+            if ((flags & TCP_DST) != 0) {
+                actions.add(salActions.get(SetTpDst.class));
+            }
+
+            pctx = createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (MatchType mt: MatchType.values()) {
+                pctx.addMatchField(mt);
+            }
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            assertEquals(true, tcp1.commit(pctx));
+            assertSame(newPkt, tcp1.getPacket());
+            filterActions = new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(actions, filterActions);
+
+            // The original packet should not be affected.
+            assertEquals(src0, tcp.getSourcePort());
+            assertEquals(dst0, tcp.getDestinationPort());
+            assertSame(pkt, tcp.getPacket());
+            assertEquals(original, pkt);
+
+            // Set values in the original packet.
+            tcp1.setSourcePort(src0);
+            tcp1.setDestinationPort(dst0);
+            assertEquals(false, tcp1.commit(pctx));
+            assertEquals(src0, tcp1.getSourcePort());
+            assertEquals(dst0, tcp1.getDestinationPort());
+        }
+    }
+
+    /**
+     * Test case for {@link TcpPacket#setMatch(Match, Set)}.
+     */
+    @Test
+    public void testSetMatch() {
+        int src = 12345;
+        int dst = 65432;
+        Map<MatchType, MatchField> tpFields =
+            new HashMap<MatchType, MatchField>();
+        tpFields.put(MatchType.TP_SRC,
+                     new MatchField(MatchType.TP_SRC,
+                                    Short.valueOf((short)src)));
+        tpFields.put(MatchType.TP_DST,
+                     new MatchField(MatchType.TP_DST,
+                                    Short.valueOf((short)dst)));
+
+        int src1 = 34012;
+        int dst1 = 25;
+        TCP pkt = createTCP(src, dst);
+        TcpPacket tcp = new TcpPacket(pkt);
+
+        Match match = new Match();
+        Set<MatchType> fields = EnumSet.noneOf(MatchType.class);
+        tcp.setMatch(match, fields);
+        List<MatchType> matches = match.getMatchesList();
+        assertEquals(0, matches.size());
+
+        for (Map.Entry<MatchType, MatchField> entry: tpFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+
+            match = new Match();
+            fields = EnumSet.of(mtype);
+            tcp.setMatch(match, fields);
+            matches = match.getMatchesList();
+            assertEquals(1, matches.size());
+            assertEquals(mfield, match.getField(mtype));
+        }
+
+        // setMatch() always has to see the original.
+        tcp.setSourcePort(src1);
+        tcp.setDestinationPort(dst1);
+        fields = EnumSet.noneOf(MatchType.class);
+        fields.addAll(tpFields.keySet());
+        match = new Match();
+        tcp.setMatch(match, fields);
+        matches = match.getMatchesList();
+        assertEquals(tpFields.size(), matches.size());
+        for (Map.Entry<MatchType, MatchField> entry: tpFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+            assertEquals(mfield, match.getField(mtype));
+        }
+    }
+
+    /**
+     * Test case for {@link TcpPacket#updateChecksum(Inet4Packet)}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testUpdateChecksum() throws Exception {
+        // Fixed TCP parameters.
+        int seq = 0xa2ffa3fc;
+        int ack = 0x7261bf41;
+        byte off = 5;
+        byte resv = 0;
+        short flags = 0x18;
+        short win = 16384;
+        short urp = 6585;
+        short checksumIni = (short)0xaaaa;
+        byte[] empty = new byte[0];
+
+        // TCP payload: even-numbered size.
+        byte[] even = {
+            (byte)0xef, (byte)0x49, (byte)0x7b, (byte)0xc3,
+            (byte)0xb6, (byte)0x61, (byte)0x14, (byte)0x2b,
+            (byte)0xff, (byte)0x2f, (byte)0x67, (byte)0xa9,
+            (byte)0x48, (byte)0x5f, (byte)0xdb, (byte)0x8e,
+            (byte)0x70, (byte)0xdc, (byte)0x0e, (byte)0xe2,
+            (byte)0xe4, (byte)0xee, (byte)0xfc, (byte)0x47,
+            (byte)0xad, (byte)0x69, (byte)0xcd, (byte)0x5c,
+            (byte)0x2d, (byte)0x00, (byte)0x42, (byte)0xe6,
+            (byte)0x15, (byte)0x2c, (byte)0x77, (byte)0xce,
+            (byte)0x1b, (byte)0x0e, (byte)0xf5, (byte)0xec,
+            (byte)0x0e, (byte)0xb5, (byte)0xab, (byte)0xd2,
+            (byte)0x59, (byte)0xbd, (byte)0x44, (byte)0x0c,
+            (byte)0xd5, (byte)0x49, (byte)0x8c, (byte)0xbd,
+            (byte)0x8c, (byte)0x66, (byte)0xca, (byte)0x53,
+            (byte)0x5b, (byte)0x8e, (byte)0xc0, (byte)0xe8,
+            (byte)0x5e, (byte)0x9e, (byte)0x53, (byte)0x75,
+        };
+
+        // TCP payload: odd-numbered size.
+        byte[] odd = {
+            (byte)0x46, (byte)0xb6, (byte)0x09, (byte)0x4f,
+            (byte)0x3b, (byte)0xae, (byte)0x15, (byte)0x5c,
+            (byte)0xa2, (byte)0x26, (byte)0x13, (byte)0x4f,
+            (byte)0x02, (byte)0xac, (byte)0x5b, (byte)0x1d,
+            (byte)0x7d, (byte)0x7f, (byte)0x30, (byte)0x89,
+            (byte)0x19, (byte)0xf9, (byte)0x58, (byte)0xa7,
+            (byte)0xdd, (byte)0xc2, (byte)0xa3, (byte)0xdb,
+            (byte)0x0f, (byte)0xed, (byte)0xe5,
+        };
+
+        // TCP payload: Large payload that causes so many checksum overflow.
+        byte[] large = new byte[2048];
+        Arrays.fill(large, (byte)0xff);
+
+        byte[] srcIp = {(byte)10, (byte)123, (byte)89, (byte)3};
+        byte[] dstIp = {(byte)192, (byte)168, (byte)35, (byte)11};
+        int src = 37397;
+        int dst = 9999;
+
+        TCP pkt = new TCP();
+        pkt.setSourcePort((short)src).setDestinationPort((short)dst).
+            setSequenceNumber(seq).setAckNumber(ack).setDataOffset(off).
+            setHeaderLenFlags(flags).setReserved(resv).setWindowSize(win).
+            setChecksum(checksumIni).setUrgentPointer(urp);
+        IPv4 ipv4 = createIPv4(srcIp, dstIp, pkt);
+        TcpPacket tcp = new TcpPacket(pkt);
+        Inet4Packet inet4 = new Inet4Packet(ipv4);
+
+        List<ChecksumData> list = new ArrayList<ChecksumData>();
+        list.add(new ChecksumData(empty, 0xdd1d));
+        list.add(new ChecksumData(even, 0x5346));
+        list.add(new ChecksumData(odd, 0x917a));
+        list.add(new ChecksumData(large, 0xd51d));
+        verifyChecksum(list, tcp, inet4);
+
+        // Change TCP port number.
+        pkt.setRawPayload(empty);
+        src = 18345;
+        dst = 53;
+        tcp.setSourcePort(src);
+        tcp.setDestinationPort(dst);
+        Ethernet ether = createEthernet(pkt);
+        PacketContext pctx =
+            createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+        assertEquals(true, tcp.commit(pctx));
+
+        list.clear();
+        list.add(new ChecksumData(empty, 0x4e64));
+        list.add(new ChecksumData(even, 0xc48c));
+        list.add(new ChecksumData(odd, 0x02c1));
+        list.add(new ChecksumData(large, 0x4664));
+        verifyChecksum(list, tcp, inet4);
+
+        // Change IP addresses.
+        pkt.setRawPayload(empty);
+        srcIp = new byte[]{(byte)212, (byte)39, (byte)43, (byte)254};
+        dstIp = new byte[]{(byte)127, (byte)0, (byte)0, (byte)1};
+        inet4.setSourceAddress(createInetAddress(srcIp));
+        assertTrue(inet4.isAddressModified());
+        inet4.setDestinationAddress(createInetAddress(dstIp));
+        assertTrue(inet4.isAddressModified());
+
+        list.clear();
+        list.add(new ChecksumData(empty, 0x166f));
+        list.add(new ChecksumData(even, 0x8c97));
+        list.add(new ChecksumData(odd, 0xcacb));
+        list.add(new ChecksumData(large, 0x0e6f));
+        verifyChecksum(list, tcp, inet4);
+    }
+
+    /**
+     * Create a {@link TCP} instance for test.
+     *
+     * @param src  Source port number.
+     * @param dst  Destination port number.
+     * @return  A {@link TCP} instance.
+     */
+    private TCP createTCP(int src, int dst) {
+        TCP pkt = new TCP();
+        sequenceNumber++;
+        ackNumber++;
+        dataOffset++;
+        reserved = (byte)((reserved + 1) & 0x3);
+        tcpFlags = (short)((tcpFlags + 1) & 0x1ff);
+        windowSize++;
+        checksum++;
+        urgentPointer++;
+
+        return pkt.setSourcePort((short)src).setDestinationPort((short)dst).
+            setSequenceNumber(sequenceNumber).setAckNumber(ackNumber).
+            setDataOffset(dataOffset).setHeaderLenFlags(tcpFlags).
+            setReserved(reserved).setWindowSize(windowSize).
+            setChecksum(checksum).setUrgentPointer(urgentPointer);
+    }
+
+    /**
+     * Create an {@link IPv4} instance that contains the given TCP packet.
+     *
+     * @param src  Source IP address.
+     * @param dst  Destination IP address.
+     * @param tcp  A {@link TCP} instance.
+     * @return  An {@link IPv4} instance.
+     */
+    private IPv4 createIPv4(byte[] src, byte[] dst, TCP tcp) {
+        return createIPv4(src, dst, IPProtocols.TCP.shortValue(), (byte)0,
+                          tcp);
+    }
+
+    /**
+     * Create an {@link Ethernet} instance that contains the given TCP packet.
+     *
+     * @param tcp  A {@link TCP} instance.
+     * @return  An {@link Ethernet} instance.
+     */
+    private Ethernet createEthernet(TCP tcp) {
+        IPv4 ipv4 = createIPv4(IPProtocols.TCP.shortValue(), tcp);
+        return createEthernet(ipv4);
+    }
+
+    /**
+     * Ensure that {@link TcpPacket#updateChecksum(Inet4Packet)} updates
+     * the checksum field correctly.
+     *
+     * @param list   A list of {@link ChecksumData} instances.
+     * @param tcp    A {@link TcpPacket} instance.
+     * @param inet4  An {@link Inet4Packet} instance.
+     * @throws Exception  An error occurred.
+     */
+    private void verifyChecksum(List<ChecksumData> list, TcpPacket tcp,
+                                Inet4Packet inet4) throws Exception {
+        TCP pkt = tcp.getPacket();
+
+        for (ChecksumData data: list) {
+            short expected = data.getChecksum();
+            byte[] payload = data.getPayload();
+
+            pkt.setRawPayload(payload);
+            TCP original = copy(pkt, new TCP());
+
+            assertTrue(tcp.updateChecksum(inet4));
+            assertEquals(expected, pkt.getChecksum());
+            assertTrue(PortProtoPacket.verifyChecksum(inet4, pkt));
+
+            // Ensure that other fields are retained.
+            original.setChecksum(expected);
+            assertEquals(original, pkt);
+
+            // updateChecksum() should return false if the checksum was not
+            // updated.
+            assertFalse(tcp.updateChecksum(inet4));
+        }
+    }
+}
+
+/**
+ * Checksum test information.
+ */
+final class ChecksumData {
+    /**
+     * A payload to be configured.
+     */
+    private final byte[]  payload;
+
+    /**
+     * An expected checksum.
+     */
+    private final short  checksum;
+
+    /**
+     * Construct a new instance with specifying description.
+     *
+     * @param data  A byte array to be configured as payload.
+     * @param sum   An expected checksum.
+     */
+    ChecksumData(byte[] data, int sum) {
+        payload = data;
+        checksum = (short)sum;
+    }
+
+    /**
+     * Return a byte array to be configured as payload.
+     *
+     * @return  A byte array.
+     */
+    byte[] getPayload() {
+        return payload;
+    }
+
+    /**
+     * Return an expecetd checksum.
+     *
+     * @return  An expected checksum.
+     */
+    short getChecksum() {
+        return checksum;
+    }
+}
diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/UdpPacketTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/UdpPacketTest.java
new file mode 100644 (file)
index 0000000..62301bc
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.vtn.manager.internal.packet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.PacketContext;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchField;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.UDP;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+
+/**
+ * JUnit test for {@link UdpPacket}.
+ */
+public class UdpPacketTest extends TestBase {
+    /**
+     * The size of the UDP header.
+     */
+    private static final int  UDP_HEADER_SIZE = 8;
+
+    /**
+     * The flag bit which indicates the source port number.
+     */
+    private static final int  UDP_SRC = 0x1;
+
+    /**
+     * The flag bit which indicates the destination port number.
+     */
+    private static final int  UDP_DST = 0x2;
+
+    /**
+     * The flag bits which indicates all modifyable fields.
+     */
+    private static final int  UDP_ALL = (UDP_SRC | UDP_DST);
+
+    /**
+     * Test case for getter methods.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testGetter() throws Exception {
+        int[] srcs = {0, 1, 53, 123, 12333, 23567, 45699, 58123, 60000, 65535};
+        int[] dsts = {0, 2, 35, 99, 390, 14567, 39734, 47614, 59198, 65535};
+        for (int src: srcs) {
+            for (int dst: dsts) {
+                UDP pkt = createUDP(src, dst);
+                UdpPacket udp = new UdpPacket(pkt);
+                assertEquals(src, udp.getSourcePort());
+                assertEquals(dst, udp.getDestinationPort());
+
+                // commit() should return false.
+                Ethernet ether = createEthernet(pkt);
+                PacketContext pctx = createPacketContext(
+                    ether, EtherPacketTest.NODE_CONNECTOR);
+                assertFalse(udp.commit(pctx));
+                assertEquals(null, pctx.getFilterActions());
+                for (MatchType mtype: MatchType.values()) {
+                    assertFalse(pctx.hasMatchField(mtype));
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for setter methods and {@link UdpPacket#clone()}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testSetter() throws Exception {
+        int src0 = 2;
+        int dst0 = 2800;
+        int src1 = 48321;
+        int dst1 = 8888;
+        int src2 = 64531;
+        int dst2 = 3271;
+
+        Map<Class<? extends Action>, Action> salActions =
+            new LinkedHashMap<Class<? extends Action>, Action>();
+        salActions.put(SetTpSrc.class, new SetTpSrc(src2));
+        salActions.put(SetTpDst.class, new SetTpDst(dst2));
+
+        for (int flags = UDP_SRC; flags <= UDP_ALL; flags++) {
+            UDP pkt = createUDP(src0, dst0);
+            UDP original = copy(pkt, new UDP());
+            UdpPacket udp = new UdpPacket(pkt);
+
+            int src = src0;
+            int dst = dst0;
+            Ethernet ether = createEthernet(pkt);
+            PacketContext pctx =
+                createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            UdpPacket udp1 = (UdpPacket)udp.clone();
+            UDP pkt2 = copy(original, new UDP());
+            if ((flags & UDP_SRC) != 0) {
+                // Modify source port number.
+                udp1.setSourcePort(src1);
+                pkt2.setSourcePort((short)src1);
+                src = src1;
+            }
+            if ((flags & UDP_DST) != 0) {
+                // Modify destination port number.
+                udp1.setDestinationPort(dst1);
+                pkt2.setDestinationPort((short)dst1);
+                dst = dst1;
+            }
+
+            assertEquals(src, udp1.getSourcePort());
+            assertEquals(dst, udp1.getDestinationPort());
+
+            // The packet should not be modified until commit() is called.
+            assertSame(pkt, udp1.getPacket());
+            assertEquals(original, pkt);
+
+            assertEquals(true, udp1.commit(pctx));
+            UDP newPkt = udp1.getPacket();
+            assertNotSame(pkt, newPkt);
+            assertEquals((short)src, newPkt.getSourcePort());
+            assertEquals((short)dst, newPkt.getDestinationPort());
+            assertEquals(pkt2, newPkt);
+
+            assertEquals((short)src0, pkt.getSourcePort());
+            assertEquals((short)dst0, pkt.getDestinationPort());
+            assertEquals(original, pkt);
+
+            assertTrue(pctx.hasMatchField(MatchType.DL_TYPE));
+            assertTrue(pctx.hasMatchField(MatchType.NW_PROTO));
+
+            List<Action> filterActions =
+                new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(new ArrayList<Action>(salActions.values()),
+                         filterActions);
+
+            // Actions for unchanged field will be removed if corresponding
+            // match type is configured in PacketContext.
+            List<Action> actions = new ArrayList<Action>();
+            if ((flags & UDP_SRC) != 0) {
+                actions.add(salActions.get(SetTpSrc.class));
+            }
+            if ((flags & UDP_DST) != 0) {
+                actions.add(salActions.get(SetTpDst.class));
+            }
+
+            pctx = createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+            for (MatchType mt: MatchType.values()) {
+                pctx.addMatchField(mt);
+            }
+            for (Action act: salActions.values()) {
+                pctx.addFilterAction(act);
+            }
+            assertEquals(true, udp1.commit(pctx));
+            assertSame(newPkt, udp1.getPacket());
+            filterActions = new ArrayList<Action>(pctx.getFilterActions());
+            assertEquals(actions, filterActions);
+
+            // The original packet should not be affected.
+            assertEquals(src0, udp.getSourcePort());
+            assertEquals(dst0, udp.getDestinationPort());
+            assertSame(pkt, udp.getPacket());
+            assertEquals(original, pkt);
+
+            // Set values in the original packet.
+            udp1.setSourcePort(src0);
+            udp1.setDestinationPort(dst0);
+            assertEquals(false, udp1.commit(pctx));
+            assertEquals(src0, udp1.getSourcePort());
+            assertEquals(dst0, udp1.getDestinationPort());
+        }
+    }
+
+    /**
+     * Test case for {@link UdpPacket#setMatch(Match, Set)}.
+     */
+    @Test
+    public void testSetMatch() {
+        int src = 34567;
+        int dst = 4321;
+        Map<MatchType, MatchField> tpFields =
+            new HashMap<MatchType, MatchField>();
+        tpFields.put(MatchType.TP_SRC,
+                     new MatchField(MatchType.TP_SRC,
+                                    Short.valueOf((short)src)));
+        tpFields.put(MatchType.TP_DST,
+                     new MatchField(MatchType.TP_DST,
+                                    Short.valueOf((short)dst)));
+
+        int src1 = 43982;
+        int dst1 = 3178;
+        UDP pkt = createUDP(src, dst);
+        UdpPacket udp = new UdpPacket(pkt);
+
+        Match match = new Match();
+        Set<MatchType> fields = EnumSet.noneOf(MatchType.class);
+        udp.setMatch(match, fields);
+        List<MatchType> matches = match.getMatchesList();
+        assertEquals(0, matches.size());
+
+        for (Map.Entry<MatchType, MatchField> entry: tpFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+
+            match = new Match();
+            fields = EnumSet.of(mtype);
+            udp.setMatch(match, fields);
+            matches = match.getMatchesList();
+            assertEquals(1, matches.size());
+            assertEquals(mfield, match.getField(mtype));
+        }
+
+        // setMatch() always has to see the original.
+        udp.setSourcePort(src1);
+        udp.setDestinationPort(dst1);
+        fields = EnumSet.noneOf(MatchType.class);
+        fields.addAll(tpFields.keySet());
+        match = new Match();
+        udp.setMatch(match, fields);
+        matches = match.getMatchesList();
+        assertEquals(tpFields.size(), matches.size());
+        for (Map.Entry<MatchType, MatchField> entry: tpFields.entrySet()) {
+            MatchType mtype = entry.getKey();
+            MatchField mfield = entry.getValue();
+            assertEquals(mfield, match.getField(mtype));
+        }
+    }
+
+    /**
+     * Test case for {@link UdpPacket#updateChecksum(Inet4Packet)}.
+     *
+     * @throws Exception  An error occurred.
+     */
+    @Test
+    public void testUpdateChecksum() throws Exception {
+        // Fixed UDP parameters.
+        short len = 0x1234;
+        short checksumIni = (short)0xaaaa;
+
+        byte[] empty = new byte[0];
+
+        // UDP payloads: even-numbered size.
+        byte[] even = {
+            (byte)0xab, (byte)0x59, (byte)0x24, (byte)0x9f,
+            (byte)0x27, (byte)0x0f, (byte)0xe9, (byte)0x28,
+            (byte)0xab, (byte)0x77, (byte)0x8a, (byte)0x34,
+            (byte)0xba, (byte)0x88, (byte)0x33, (byte)0x6e,
+            (byte)0x0e, (byte)0x30, (byte)0x7a, (byte)0xef,
+        };
+
+        // UDP payloads: odd-numbered size.
+        byte[] odd = {
+            (byte)0xc3, (byte)0x13, (byte)0x12, (byte)0x54,
+            (byte)0x68, (byte)0x70, (byte)0x9d, (byte)0x6d,
+            (byte)0x79, (byte)0xc2, (byte)0xec, (byte)0xac,
+            (byte)0x0f, (byte)0x87, (byte)0x56, (byte)0xfb,
+            (byte)0x81, (byte)0x9d, (byte)0x41, (byte)0x83,
+            (byte)0x9d, (byte)0x0a, (byte)0x9f, (byte)0x6e,
+            (byte)0xf1,
+        };
+
+        // UDP payloads: Large size that causes so many checksum overflow.
+        byte[] large = new byte[2048];
+        Arrays.fill(large, (byte)0xff);
+
+        byte[] srcIp = {(byte)172, (byte)16, (byte)253, (byte)150};
+        byte[] dstIp = {(byte)192, (byte)168, (byte)3, (byte)200};
+        int src = 54321;
+        int dst = 7777;
+
+        UDP pkt = new UDP();
+        pkt.setSourcePort((short)src).setDestinationPort((short)dst).
+            setChecksum(checksumIni).setLength(len);
+        IPv4 ipv4 = createIPv4(srcIp, dstIp, pkt);
+        UdpPacket udp = new UdpPacket(pkt);
+        Inet4Packet inet4 = new Inet4Packet(ipv4);
+
+        List<ChecksumData> list = new ArrayList<ChecksumData>();
+        list.add(new ChecksumData(empty, 0x8d07));
+
+        // computeChecksum() should return zero.
+        list.add(new ChecksumData(even, 0xffff));
+
+        list.add(new ChecksumData(odd, 0xf41b));
+        list.add(new ChecksumData(large, 0x8507));
+        verifyChecksum(list, udp, inet4);
+        verifyChecksumDisabled(udp, inet4, empty, even, odd, large);
+
+        // Change UDP port number.
+        pkt.setRawPayload(empty);
+        src = 48173;
+        dst = 871;
+        udp.setSourcePort(src);
+        udp.setDestinationPort(dst);
+        Ethernet ether = createEthernet(pkt);
+        PacketContext pctx =
+            createPacketContext(ether, EtherPacketTest.NODE_CONNECTOR);
+        assertEquals(true, udp.commit(pctx));
+
+        list.clear();
+        list.add(new ChecksumData(empty, 0xc005));
+        list.add(new ChecksumData(even, 0x32fe));
+        list.add(new ChecksumData(odd, 0x271a));
+        list.add(new ChecksumData(large, 0xb805));
+        pkt.setChecksum(checksumIni);
+        verifyChecksum(list, udp, inet4);
+        verifyChecksumDisabled(udp, inet4, empty, even, odd, large);
+
+        // Change IP addresses.
+        pkt.setRawPayload(empty);
+        srcIp = new byte[]{(byte)123, (byte)89, (byte)118, (byte)91};
+        dstIp = new byte[]{(byte)100, (byte)200, (byte)73, (byte)183};
+        inet4.setSourceAddress(createInetAddress(srcIp));
+        assertTrue(inet4.isAddressModified());
+        inet4.setDestinationAddress(createInetAddress(dstIp));
+        assertTrue(inet4.isAddressModified());
+
+        list.clear();
+        list.add(new ChecksumData(empty, 0x8de9));
+        list.add(new ChecksumData(even, 0x00e2));
+        list.add(new ChecksumData(odd, 0xf4fd));
+        list.add(new ChecksumData(large, 0x85e9));
+        pkt.setChecksum(checksumIni);
+        verifyChecksum(list, udp, inet4);
+        verifyChecksumDisabled(udp, inet4, empty, even, odd, large);
+    }
+
+    /**
+     * Create an {@link UDP} instance for test.
+     *
+     * @param src  Source port number.
+     * @param dst  Destination port number.
+     * @return  An {@link UDP} instance.
+     */
+    private UDP createUDP(int src, int dst) {
+        return createUDP(src, dst, null);
+    }
+
+    /**
+     * Create an {@link UDP} instance for test.
+     *
+     * @param src  Source port number.
+     * @param dst  Destination port number.
+     * @param raw  A byte array to be configured as payload.
+     * @return  An {@link UDP} instance.
+     */
+    private UDP createUDP(int src, int dst, byte[] raw) {
+        UDP pkt = new UDP();
+        short len = UDP_HEADER_SIZE;
+        pkt.setSourcePort((short)src).setDestinationPort((short)dst);
+        if (raw != null) {
+            pkt.setRawPayload(raw);
+            len += raw.length;
+        }
+
+        return pkt.setLength(len);
+    }
+
+    /**
+     * Create an {@link IPv4} instance that contains the given UDP packet.
+     *
+     * @param src  Source IP address.
+     * @param dst  Destination IP address.
+     * @param udp  A {@link UDP} instance.
+     * @return  An {@link IPv4} instance.
+     */
+    private IPv4 createIPv4(byte[] src, byte[] dst, UDP udp) {
+        return createIPv4(src, dst, IPProtocols.UDP.shortValue(), (byte)0,
+                          udp);
+    }
+
+    /**
+     * Create an {@link Ethernet} instance that contains the given UDP packet.
+     *
+     * @param udp  A {@link UDP} instance.
+     * @return  An {@link Ethernet} instance.
+     */
+    private Ethernet createEthernet(UDP udp) {
+        IPv4 ipv4 = createIPv4(IPProtocols.UDP.shortValue(), udp);
+        return createEthernet(ipv4);
+    }
+
+    /**
+     * Ensure that {@link UdpPacket#updateChecksum(Inet4Packet)} updates
+     * the checksum field correctly.
+     *
+     * @param list   A list of {@link ChecksumData} instances.
+     * @param udp    A {@link UdpPacket} instance.
+     * @param inet4  An {@link Inet4Packet} instance.
+     * @throws Exception  An error occurred.
+     */
+    private void verifyChecksum(List<ChecksumData> list, UdpPacket udp,
+                                Inet4Packet inet4) throws Exception {
+        for (ChecksumData data: list) {
+            short expected = data.getChecksum();
+            byte[] payload = data.getPayload();
+
+            UDP pkt = udp.getPacket();
+            pkt.setRawPayload(payload);
+            UDP original = copy(pkt, new UDP());
+
+            assertTrue(udp.updateChecksum(inet4));
+            assertEquals(expected, pkt.getChecksum());
+            assertTrue(PortProtoPacket.verifyChecksum(inet4, pkt));
+
+            // Ensure that other fields are retained.
+            original.setChecksum(expected);
+            assertEquals(original, pkt);
+
+            // updateChecksum() should return false if the checksum was not
+            // updated.
+            assertFalse(udp.updateChecksum(inet4));
+        }
+    }
+
+    /**
+     * Ensure that the UDP checksum is disabled if the checksum in the packet
+     * is zero.
+     *
+     * @param udp       A {@link UdpPacket} instance.
+     * @param inet4     An {@link Inet4Packet} instance.
+     * @param payloads  An array of payloads.
+     * @throws Exception  An error occurred.
+     */
+    private void verifyChecksumDisabled(UdpPacket udp, Inet4Packet inet4,
+                                        byte[] ... payloads) throws Exception {
+        UDP pkt = udp.getPacket();
+        short zero = 0;
+        pkt.setChecksum(zero);
+
+        for (byte[] payload: payloads) {
+            pkt.setRawPayload(payload);
+            UDP original = copy(pkt, new UDP());
+
+            assertFalse(udp.updateChecksum(inet4));
+            assertEquals(zero, pkt.getChecksum());
+
+            // Ensure that other fields are retained.
+            assertEquals(original, pkt);
+
+            assertFalse(udp.updateChecksum(inet4));
+        }
+    }
+}
index 0b4e7cee7bd460bdcdca0cdb8357bb8bf377d423..049fa196a19b5ff8c680381684f2ce0788d12f62 100644 (file)
@@ -4885,6 +4885,8 @@ public class VtnNorthboundIT extends TestBase {
                                  HexEncode.bytesToHexStringFormat(macAddr2))).
             put(createJSONObject("vlanpcp", "priority",
                                  cookie & MASK_VLAN_PCP)).
+            put(createJSONObject("inet4src", "address", "192.168.20.254")).
+            put(createJSONObject("inet4dst", "address", "10.20.30.40")).
             put(createJSONObject("dscp", "dscp", cookie & MASK_IP_DSCP)).
             put(createJSONObject("icmptype", "type", cookie & MASK_ICMP)).
             put(createJSONObject("icmpcode", "code", ~cookie & MASK_ICMP));
@@ -5068,18 +5070,22 @@ public class VtnNorthboundIT extends TestBase {
             createJSONObject("dldst", "address", "ff:ff:ff:ff:ff:ff"),
             createJSONObject("vlanpcp", "priority", 8),
             createJSONObject("vlanpcp", "priority", -1),
+            createJSONObject("inet4src", "address", "bad_ip_address"),
+            createJSONObject("inet4src", "address", "100.200.300.400"),
+            createJSONObject("inet4src", "address", "::1"),
+            createJSONObject("inet4dst", "address", "bad_ip_address"),
+            createJSONObject("inet4dst", "address", "100.200.1.256"),
+            createJSONObject("inet4dst", "address", "2400:683c:af13:801::2034"),
             createJSONObject("dscp", "dscp", 64),
             createJSONObject("dscp", "dscp", -1),
+            createJSONObject("tpsrc", "port", -1),
+            createJSONObject("tpsrc", "port", 65536),
+            createJSONObject("tpdst", "port", -1),
+            createJSONObject("tpdst", "port", 65536),
             createJSONObject("icmptype", "type", -1),
             createJSONObject("icmptype", "type", 256),
             createJSONObject("icmpcode", "code", -1),
             createJSONObject("icmpcode", "code", 256),
-
-            // Unsupported action.
-            createJSONObject("inet4src", "address", "10.0.0.1"),
-            createJSONObject("inet4dst", "address", "10.0.0.1"),
-            createJSONObject("tpsrc", "port", 10),
-            createJSONObject("tpdst", "port", 10),
         };
 
         bad.put("filterType", new JSONObject().put("pass", empty));