From dfc66b11d7e8f43b66b95a87e737fcf1996d47eb Mon Sep 17 00:00:00 2001 From: Shigeru Yasuda Date: Thu, 11 Sep 2014 02:19:15 +0900 Subject: [PATCH] Implement TCP/UDP checksum recalculation. This patch enables flow filter to modify IPv4 address and TCP/UDP port number in packets. Change-Id: Ibf2deb423dac9ec5b90f57f4faae2a5394aa9331 Signed-off-by: Shigeru Yasuda --- .../flow/action/InetAddressAction.java | 9 + .../vtn/manager/flow/filter/FlowFilter.java | 12 - .../flow/action/SetInet4DstActionTest.java | 2 + .../flow/action/SetInet4SrcActionTest.java | 2 + .../flow/action/SetTpDstActionTest.java | 2 +- .../flow/action/SetTpSrcActionTest.java | 2 +- .../vtn/manager/internal/ActionList.java | 12 +- .../vtn/manager/internal/MiscUtils.java | 98 ++ .../vtn/manager/internal/PacketContext.java | 106 ++- .../vtn/manager/internal/StatsReader.java | 19 +- .../internal/cluster/FlowActionImpl.java | 12 +- .../internal/cluster/FlowFilterMap.java | 20 +- .../cluster/InetAddressActionImpl.java | 12 +- .../internal/cluster/SetDlDstActionImpl.java | 3 + .../internal/cluster/SetDlSrcActionImpl.java | 3 + .../internal/cluster/SetDscpActionImpl.java | 3 + .../cluster/SetIcmpCodeActionImpl.java | 6 +- .../cluster/SetIcmpTypeActionImpl.java | 6 +- .../cluster/SetInet4DstActionImpl.java | 25 +- .../cluster/SetInet4SrcActionImpl.java | 25 +- .../internal/cluster/SetTpDstActionImpl.java | 25 +- .../internal/cluster/SetTpSrcActionImpl.java | 25 +- .../cluster/SetVlanPcpActionImpl.java | 2 + .../internal/cluster/TpPortActionImpl.java | 12 +- .../internal/cluster/VTerminalImpl.java | 45 +- .../manager/internal/packet/EtherPacket.java | 42 +- .../manager/internal/packet/IcmpPacket.java | 72 +- .../manager/internal/packet/Inet4Packet.java | 350 +++++++- .../vtn/manager/internal/packet/L4Packet.java | 39 + .../internal/packet/PortProtoPacket.java | 429 ++++++++- .../manager/internal/packet/TcpPacket.java | 84 +- .../manager/internal/packet/UdpPacket.java | 92 +- .../vtn/manager/internal/ArpHandlerTest.java | 9 +- .../internal/GlobalResourceManagerTest.java | 11 +- .../manager/internal/MacAddressTableTest.java | 36 +- .../vtn/manager/internal/MiscUtilsTest.java | 846 ++++++++++++++++++ .../vtn/manager/internal/TestBase.java | 251 +++++- .../internal/VTNManagerImplClusterTest.java | 21 +- .../VTNManagerImplDisableNodesTest.java | 2 +- .../manager/internal/VTNManagerImplTest.java | 14 +- .../internal/VTNManagerImplWithNodesTest.java | 2 +- .../internal/cluster/FlowActionImplTest.java | 43 +- .../internal/cluster/MacTableEntryTest.java | 4 +- .../cluster/SetDlDstActionImplTest.java | 27 +- .../cluster/SetDlSrcActionImplTest.java | 27 +- .../cluster/SetInet4DstActionImplTest.java | 140 +++ .../cluster/SetInet4SrcActionImplTest.java | 140 +++ .../cluster/SetTpDstActionImplTest.java | 125 +++ .../cluster/SetTpSrcActionImplTest.java | 125 +++ .../internal/packet/EtherPacketTest.java | 605 +++++++++++++ .../internal/packet/IcmpPacketTest.java | 297 ++++++ .../internal/packet/Inet4PacketTest.java | 425 +++++++++ .../internal/packet/TcpPacketTest.java | 527 +++++++++++ .../internal/packet/UdpPacketTest.java | 471 ++++++++++ .../integrationtest/VtnNorthboundIT.java | 18 +- 55 files changed, 5469 insertions(+), 293 deletions(-) create mode 100644 manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/L4Packet.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MiscUtilsTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImplTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImplTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImplTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImplTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/EtherPacketTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacketTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/Inet4PacketTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/TcpPacketTest.java create mode 100644 manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/UdpPacketTest.java diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/action/InetAddressAction.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/action/InetAddressAction.java index 469cdc91..c8910103 100644 --- a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/action/InetAddressAction.java +++ b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/action/InetAddressAction.java @@ -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(); diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java index 7de97fa6..514209ee 100644 --- a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java +++ b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java @@ -235,9 +235,6 @@ public final class FlowFilter implements Serializable { * The type of this element must be {@link SetInet4SrcAction}. * *
  • This element does not affect packets without IPv4 header.
  • - *
  • - * This element is not yet supported. - *
  • * * *
    inet4dst @@ -249,9 +246,6 @@ public final class FlowFilter implements Serializable { * The type of this element must be {@link SetInet4DstAction}. * *
  • This element does not affect packets without IPv4 header.
  • - *
  • - * This element is not yet supported. - *
  • * * *
    dscp @@ -276,9 +270,6 @@ public final class FlowFilter implements Serializable { *
  • * This element does not affect packets without TCP or UDP header. *
  • - *
  • - * This element is not yet supported. - *
  • * * *
    tpdst @@ -292,9 +283,6 @@ public final class FlowFilter implements Serializable { *
  • * This element does not affect packets without TCP or UDP header. *
  • - *
  • - * This element is not yet supported. - *
  • * * *
    icmptype diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4DstActionTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4DstActionTest.java index ff7b697e..10cb951d 100644 --- a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4DstActionTest.java +++ b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4DstActionTest.java @@ -123,6 +123,8 @@ public class SetInet4DstActionTest extends TestBase { // Specifying invalid address. String[] invalid = { // Invalid address + "", + " ", "invalid_address", "100.200.300.400", diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4SrcActionTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4SrcActionTest.java index cba92f91..5c037670 100644 --- a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4SrcActionTest.java +++ b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetInet4SrcActionTest.java @@ -123,6 +123,8 @@ public class SetInet4SrcActionTest extends TestBase { // Specifying invalid address. String[] invalid = { // Invalid address + "", + " ", "invalid_address", "100.200.300.400", diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpDstActionTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpDstActionTest.java index aaa9dd8c..4af270bf 100644 --- a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpDstActionTest.java +++ b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpDstActionTest.java @@ -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); diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpSrcActionTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpSrcActionTest.java index 83c2a93f..bc6cf41d 100644 --- a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpSrcActionTest.java +++ b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/action/SetTpSrcActionTest.java @@ -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); diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/ActionList.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/ActionList.java index 8e38f2b5..23ac52f1 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/ActionList.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/ActionList.java @@ -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 list) { - if (list != null) { - actionList.addAll(list); + public ActionList addAll(Collection c) { + if (c != null) { + actionList.addAll(c); } return this; } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/MiscUtils.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/MiscUtils.java index 910b5d6f..e93b9ffa 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/MiscUtils.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/MiscUtils.java @@ -10,12 +10,14 @@ 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 Type of packet. + * @return {@code dst}. + * @throws VTNException + * Failed to copy the packet. + */ + public static 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; + } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/PacketContext.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/PacketContext.java index 2346f9b2..c678f3dc 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/PacketContext.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/PacketContext.java @@ -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 filterActions; + private Map, 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(); + filterActions = + new LinkedHashMap, 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 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 getFilterActions() { - return filterActions; + public Collection 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(); } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/StatsReader.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/StatsReader.java index f6027557..ff95f66a 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/StatsReader.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/StatsReader.java @@ -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; *

    */ 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 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) { diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImpl.java index cc24b955..86da47ec 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImpl.java @@ -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, 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, }; diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowFilterMap.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowFilterMap.java index 88b426c2..dd97d941 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowFilterMap.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/FlowFilterMap.java @@ -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; diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/InetAddressActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/InetAddressActionImpl.java index 288f9a03..ca6fd3a9 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/InetAddressActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/InetAddressActionImpl.java @@ -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. * diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImpl.java index fb02052f..bdac8042 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImpl.java @@ -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; } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImpl.java index aff3e801..9926414a 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImpl.java @@ -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; } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDscpActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDscpActionImpl.java index 18c14835..5c3f2799 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDscpActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetDscpActionImpl.java @@ -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; } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpCodeActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpCodeActionImpl.java index c00c0411..54f9737f 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpCodeActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpCodeActionImpl.java @@ -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; } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpTypeActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpTypeActionImpl.java index fadec00d..ce6b2b55 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpTypeActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetIcmpTypeActionImpl.java @@ -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; } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImpl.java index 505ef045..6ad5e544 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImpl.java @@ -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; + } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImpl.java index 56ce16be..6e609016 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImpl.java @@ -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; + } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImpl.java index 53154187..8f59d3f6 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImpl.java @@ -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; + } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImpl.java index 026b65ed..2bf6357e 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImpl.java @@ -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; + } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetVlanPcpActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetVlanPcpActionImpl.java index 7aef12b0..b6a95a13 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetVlanPcpActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/SetVlanPcpActionImpl.java @@ -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; } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/TpPortActionImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/TpPortActionImpl.java index ef47d3db..023cc4fd 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/TpPortActionImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/TpPortActionImpl.java @@ -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. * diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/VTerminalImpl.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/VTerminalImpl.java index 346ca29c..bcc2c7b3 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/VTerminalImpl.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/cluster/VTerminalImpl.java @@ -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 { /** * 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 { */ @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(); diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/EtherPacket.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/EtherPacket.java index adbef214..295106e9 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/EtherPacket.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/EtherPacket.java @@ -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); } } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacket.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacket.java index 61a8b730..53807855 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacket.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacket.java @@ -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. + * + *

    + * This method does nothing because the ICMP checksum is computed by + * {@link ICMP} class. + *

    + * + * @param ipv4 Never used. + * @return {@code false}. + */ + @Override + public boolean updateChecksum(Inet4Packet ipv4) { + return false; + } } diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/Inet4Packet.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/Inet4Packet.java index b44f9ba5..902d294e 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/Inet4Packet.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/Inet4Packet.java @@ -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 index 00000000..57038167 --- /dev/null +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/L4Packet.java @@ -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(); +} diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/PortProtoPacket.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/PortProtoPacket.java index ab0cf611..16ea3115 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/PortProtoPacket.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/PortProtoPacket.java @@ -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 Type of packet. */ -public abstract class PortProtoPacket implements CachedPacket { +public abstract class PortProtoPacket 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. + * + *

    + * Field values already cached in this instance are preserved. + *

    + * + * @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. * + *

    + * Note that this method creates match fields that matches the original + * packet. Any modification to the packet is ignored. + *

    + * * @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 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); diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/TcpPacket.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/TcpPacket.java index ad8ff341..5cb6ca86 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/TcpPacket.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/TcpPacket.java @@ -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 { + /** + * 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 /** diff --git a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/UdpPacket.java b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/UdpPacket.java index b35adcb3..624a7395 100644 --- a/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/UdpPacket.java +++ b/manager/implementation/src/main/java/org/opendaylight/vtn/manager/internal/packet/UdpPacket.java @@ -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 { + /** + * 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 /** diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/ArpHandlerTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/ArpHandlerTest.java index ce27dba8..d9e3bdcb 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/ArpHandlerTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/ArpHandlerTest.java @@ -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}; diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/GlobalResourceManagerTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/GlobalResourceManagerTest.java index 9542743d..0c948bdb 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/GlobalResourceManagerTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/GlobalResourceManagerTest.java @@ -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 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); diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MacAddressTableTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MacAddressTableTest.java index 7d1bcfd9..aaa9b337 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MacAddressTableTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MacAddressTableTest.java @@ -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 ipSet = new HashSet(); // 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 ipSet = new HashSet(); 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 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 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 index 00000000..433fce79 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/MiscUtilsTest.java @@ -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 badRequests = new HashMap(); + 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 badCharacters = new ArrayList(); + 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 badNames = new ArrayList(); + 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 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 invalid = new ArrayList(); + 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}. + * + *

    + * This method expects that {@link StatusCode#BADREQUEST} is configured + * in the given exception. + *

    + * + * @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}. + * + *

    + * This method expects that {@link StatusCode#BADREQUEST} is configured + * in the given status. + *

    + * + * @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); + } +} diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestBase.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestBase.java index d2f2eba2..64ed4f23 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestBase.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/TestBase.java @@ -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 Type of the packet. + * @return {@code dst}. + */ + protected static 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 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 createInet4Addresses(boolean setNull) { + List list = new ArrayList(); + 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. + * + *

    + * Fixed values are used for the source and destination MAC address. + *

    + * + * @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. + * + *

    + * Fixed values are used for the source and destination IP address, + * and DSCP field. + *

    + * + * @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. * diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplClusterTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplClusterTest.java index 6a7e1ac0..1caebc33 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplClusterTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplClusterTest.java @@ -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 obj = new ObjectPair(ia, Boolean.FALSE); stateDB.put(tpathNull, obj); @@ -313,8 +312,8 @@ public class VTNManagerImplClusterTest extends VTNManagerImplTestCommon { stateDB = (ConcurrentMap)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(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 evIdSet = new HashSet(); - 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 evIdSet = new HashSet(); - 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 evIdSet = new HashSet(); - 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 evIdSet = new HashSet(); diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplDisableNodesTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplDisableNodesTest.java index bbf70af5..9602bef5 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplDisableNodesTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplDisableNodesTest.java @@ -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(); diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplTest.java index 92b16f14..59d23c7b 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplTest.java @@ -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 evIdSet = new HashSet(); - 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); diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplWithNodesTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplWithNodesTest.java index 78ff014a..1491cd28 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplWithNodesTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/VTNManagerImplWithNodesTest.java @@ -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 { diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImplTest.java index 338a9074..23f86939 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImplTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/FlowActionImplTest.java @@ -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> implClasses = new HashMap, 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), }; diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/MacTableEntryTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/MacTableEntryTest.java index 38d57531..d1aedf90 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/MacTableEntryTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/MacTableEntryTest.java @@ -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); diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImplTest.java index 18a7787d..79969088 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImplTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlDstActionImplTest.java @@ -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 = + "" + + ""; + 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()); + } } /** diff --git a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImplTest.java b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImplTest.java index 791c4873..3d1d247e 100644 --- a/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImplTest.java +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetDlSrcActionImplTest.java @@ -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 = + "" + + ""; + 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 index 00000000..860d3e23 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4DstActionImplTest.java @@ -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 = + ""; + for (String addr: badAddrs) { + StringBuilder builder = new StringBuilder(xmlDef); + if (addr == null) { + builder.append(""); + } else { + builder.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 set = new HashSet(); + List 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 index 00000000..168e1c80 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetInet4SrcActionImplTest.java @@ -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 = + ""; + for (String addr: badAddrs) { + StringBuilder builder = new StringBuilder(xmlDef); + if (addr == null) { + builder.append(""); + } else { + builder.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 set = new HashSet(); + List 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 index 00000000..fe2bf1ee --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpDstActionImplTest.java @@ -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 set = new HashSet(); + 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 index 00000000..9db679d3 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/cluster/SetTpSrcActionImplTest.java @@ -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 set = new HashSet(); + 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 index 00000000..665b1157 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/EtherPacketTest.java @@ -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, Action> salActions = + new LinkedHashMap, 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 actions = new ArrayList(); + for (Action act: salActions.values()) { + if (!(vlan == MatchType.DL_VLAN_NONE && + SetVlanPcp.class.isInstance(act))) { + actions.add(act); + } + } + assertEquals(mod, ether1.commit(pctx)); + + List filterActions = + new ArrayList(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(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 dlFields = + new HashMap(); + 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 fields = EnumSet.noneOf(MatchType.class); + ether.setMatch(match, fields); + List matches = match.getMatchesList(); + assertEquals(1, matches.size()); + assertEquals(vlanVid, match.getField(MatchType.DL_VLAN)); + + for (Map.Entry 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 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 index 00000000..2392bc46 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/IcmpPacketTest.java @@ -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. + * + *
      + *
    • {@link IcmpPacket#clone()}
    • + *
    • {@link IcmpPacket#updateChecksum(Inet4Packet)}
    • + *
    + * + * @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, Action> salActions = + new LinkedHashMap, 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 filterActions = + new ArrayList(pctx.getFilterActions()); + assertEquals(new ArrayList(salActions.values()), + filterActions); + + // Actions for unchanged field will be removed if corresponding + // match type is configured in PacketContext. + List actions = new ArrayList(); + 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(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 tpFields = + new HashMap(); + 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 fields = EnumSet.noneOf(MatchType.class); + icmp.setMatch(match, fields); + List matches = match.getMatchesList(); + assertEquals(0, matches.size()); + + for (Map.Entry 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 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 index 00000000..295fbde9 --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/Inet4PacketTest.java @@ -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, Action> salActions = + new LinkedHashMap, 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 filterActions = + new ArrayList(pctx.getFilterActions()); + assertEquals(new ArrayList(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 actions = new ArrayList(); + 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(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 nwFields = + new HashMap(); + 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 fields = EnumSet.noneOf(MatchType.class); + ipv4.setMatch(match, fields); + List matches = match.getMatchesList(); + assertEquals(0, matches.size()); + + for (Map.Entry 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 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 index 00000000..826a6e3b --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/TcpPacketTest.java @@ -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, Action> salActions = + new LinkedHashMap, 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 filterActions = + new ArrayList(pctx.getFilterActions()); + assertEquals(new ArrayList(salActions.values()), + filterActions); + + // Actions for unchanged field will be removed if corresponding + // match type is configured in PacketContext. + List actions = new ArrayList(); + 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(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 tpFields = + new HashMap(); + 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 fields = EnumSet.noneOf(MatchType.class); + tcp.setMatch(match, fields); + List matches = match.getMatchesList(); + assertEquals(0, matches.size()); + + for (Map.Entry 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 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 list = new ArrayList(); + 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 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 index 00000000..62301bcb --- /dev/null +++ b/manager/implementation/src/test/java/org/opendaylight/vtn/manager/internal/packet/UdpPacketTest.java @@ -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, Action> salActions = + new LinkedHashMap, 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 filterActions = + new ArrayList(pctx.getFilterActions()); + assertEquals(new ArrayList(salActions.values()), + filterActions); + + // Actions for unchanged field will be removed if corresponding + // match type is configured in PacketContext. + List actions = new ArrayList(); + 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(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 tpFields = + new HashMap(); + 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 fields = EnumSet.noneOf(MatchType.class); + udp.setMatch(match, fields); + List matches = match.getMatchesList(); + assertEquals(0, matches.size()); + + for (Map.Entry 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 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 list = new ArrayList(); + 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 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)); + } + } +} diff --git a/manager/northboundIT/src/test/java/org/opendaylight/vtn/manager/northbound/integrationtest/VtnNorthboundIT.java b/manager/northboundIT/src/test/java/org/opendaylight/vtn/manager/northbound/integrationtest/VtnNorthboundIT.java index 0b4e7cee..049fa196 100644 --- a/manager/northboundIT/src/test/java/org/opendaylight/vtn/manager/northbound/integrationtest/VtnNorthboundIT.java +++ b/manager/northboundIT/src/test/java/org/opendaylight/vtn/manager/northbound/integrationtest/VtnNorthboundIT.java @@ -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)); -- 2.36.6