From 913462da53f461a2241844f672f8e2ed1bd16954 Mon Sep 17 00:00:00 2001 From: Ed Warnicke Date: Thu, 29 Aug 2013 12:49:54 -0700 Subject: [PATCH] Added openflow-codec and openflowj_netty from openflowplugin Signed-off-by: Ed Warnicke --- pom.xml | 2 + third-party/openflow-codec/LICENSE | 29 + third-party/openflow-codec/Makefile | 18 + third-party/openflow-codec/README | 16 + .../openflow-codec/lib/commons-cli-1.2.jar | Bin 0 -> 41123 bytes .../openflow-codec/lib/junit-4.8.1.jar | Bin 0 -> 237047 bytes third-party/openflow-codec/pom.xml | 150 +++ .../codec/example/SelectListener.java | 24 + .../openflow/codec/example/SelectLoop.java | 160 +++ .../codec/example/SimpleController.java | 318 +++++ .../openflow/codec/example/cli/Option.java | 39 + .../openflow/codec/example/cli/Options.java | 66 ++ .../codec/example/cli/ParseException.java | 14 + .../openflow/codec/example/cli/SimpleCLI.java | 127 ++ .../org/openflow/codec/io/ByteDataBuffer.java | 256 ++++ .../org/openflow/codec/io/DataBuffers.java | 39 + .../org/openflow/codec/io/IDataBuffer.java | 583 +++++++++ .../codec/io/OFMessageAsyncStream.java | 118 ++ .../openflow/codec/io/OFMessageInStream.java | 51 + .../openflow/codec/io/OFMessageOutStream.java | 47 + .../openflow/codec/protocol/Instantiable.java | 15 + .../codec/protocol/OFBMatchFields.java | 153 +++ .../codec/protocol/OFPBarrierReply.java | 16 + .../codec/protocol/OFPBarrierRequest.java | 16 + .../openflow/codec/protocol/OFPEchoReply.java | 19 + .../codec/protocol/OFPEchoRequest.java | 53 + .../openflow/codec/protocol/OFPErrorMsg.java | 256 ++++ .../codec/protocol/OFPExperimenterHeader.java | 146 +++ .../openflow/codec/protocol/OFPFlowMod.java | 469 ++++++++ .../codec/protocol/OFPFlowModCommand.java | 70 ++ .../codec/protocol/OFPFlowModFlags.java | 73 ++ .../codec/protocol/OFPFlowRemoved.java | 344 ++++++ .../codec/protocol/OFPGetConfigReply.java | 13 + .../codec/protocol/OFPGetConfigRequest.java | 16 + .../openflow/codec/protocol/OFPGroupMod.java | 192 +++ .../org/openflow/codec/protocol/OFPHello.java | 160 +++ .../codec/protocol/OFPHelloElemHeader.java | 123 ++ .../codec/protocol/OFPHelloElemType.java | 88 ++ .../protocol/OFPHelloElemVersionBitmap.java | 118 ++ .../org/openflow/codec/protocol/OFPMatch.java | 267 +++++ .../codec/protocol/OFPMatchBeanInfo.java | 89 ++ .../openflow/codec/protocol/OFPMatchType.java | 69 ++ .../openflow/codec/protocol/OFPMessage.java | 197 ++++ .../protocol/OFPMultipartMessageBase.java | 141 +++ .../codec/protocol/OFPMultipartReply.java | 37 + .../codec/protocol/OFPMultipartRequest.java | 37 + .../openflow/codec/protocol/OFPPacketIn.java | 262 +++++ .../openflow/codec/protocol/OFPPacketOut.java | 247 ++++ .../org/openflow/codec/protocol/OFPPort.java | 417 +++++++ .../openflow/codec/protocol/OFPPortMod.java | 176 +++ .../openflow/codec/protocol/OFPPortNo.java | 26 + .../codec/protocol/OFPPortStatus.java | 109 ++ .../codec/protocol/OFPQueueConfigReply.java | 148 +++ .../codec/protocol/OFPQueueConfigRequest.java | 83 ++ .../openflow/codec/protocol/OFPSetConfig.java | 13 + .../codec/protocol/OFPSwitchConfig.java | 100 ++ .../protocol/OFPSwitchFeaturesReply.java | 205 ++++ .../protocol/OFPSwitchFeaturesRequest.java | 19 + .../openflow/codec/protocol/OFPTableMod.java | 136 +++ .../org/openflow/codec/protocol/OFPType.java | 257 ++++ .../org/openflow/codec/protocol/OXMClass.java | 60 + .../org/openflow/codec/protocol/OXMField.java | 438 +++++++ .../codec/protocol/action/OFPAction.java | 157 +++ .../action/OFPActionCopyTimeToLiveIn.java | 33 + .../action/OFPActionCopyTimeToLiveOut.java | 33 + .../action/OFPActionDecMplsTimeToLive.java | 33 + .../action/OFPActionDecNetworkTimeToLive.java | 33 + .../action/OFPActionExperimenterHeader.java | 73 ++ .../codec/protocol/action/OFPActionGroup.java | 73 ++ .../action/OFPActionMplsTimeToLive.java | 77 ++ .../action/OFPActionNetworkTimeToLive.java | 77 ++ .../protocol/action/OFPActionOutput.java | 146 +++ .../protocol/action/OFPActionPopMpls.java | 70 ++ .../protocol/action/OFPActionPopPbb.java | 33 + .../protocol/action/OFPActionPopVLAN.java | 33 + .../codec/protocol/action/OFPActionPush.java | 70 ++ .../protocol/action/OFPActionPushMpls.java | 14 + .../protocol/action/OFPActionPushPbb.java | 14 + .../protocol/action/OFPActionPushVLAN.java | 14 + .../protocol/action/OFPActionSetField.java | 114 ++ .../protocol/action/OFPActionSetQueue.java | 73 ++ .../codec/protocol/action/OFPActionType.java | 221 ++++ .../codec/protocol/action/OFPBucket.java | 208 ++++ .../protocol/action/OFPBucketCounter.java | 113 ++ .../codec/protocol/action/OFPGroupType.java | 34 + .../protocol/factory/OFPActionFactory.java | 53 + .../factory/OFPActionFactoryAware.java | 16 + .../protocol/factory/OFPBasicFactoryImpl.java | 284 +++++ .../factory/OFPInstructionFactory.java | 53 + .../factory/OFPInstructionFactoryAware.java | 16 + .../protocol/factory/OFPMessageFactory.java | 55 + .../factory/OFPMessageFactoryAware.java | 18 + .../factory/OFPQueuePropertyFactory.java | 54 + .../factory/OFPQueuePropertyFactoryAware.java | 16 + .../factory/OFPStatisticsFactory.java | 67 ++ .../factory/OFPStatisticsFactoryAware.java | 16 + .../protocol/instruction/OFPInstruction.java | 135 +++ .../instruction/OFPInstructionActions.java | 153 +++ .../OFPInstructionApplyActions.java | 15 + .../OFPInstructionClearActions.java | 44 + .../OFPInstructionExperimenter.java | 97 ++ .../instruction/OFPInstructionGoToTable.java | 102 ++ .../instruction/OFPInstructionMeter.java | 97 ++ .../instruction/OFPInstructionType.java | 132 +++ .../OFPInstructionWriteActions.java | 15 + .../OFPInstructionWriteMetaData.java | 124 ++ .../codec/protocol/queue/OFPPacketQueue.java | 172 +++ .../protocol/queue/OFPQueueProperty.java | 101 ++ .../queue/OFPQueuePropertyExperimenter.java | 112 ++ .../queue/OFPQueuePropertyMaxRate.java | 89 ++ .../queue/OFPQueuePropertyMinRate.java | 89 ++ .../protocol/queue/OFPQueuePropertyType.java | 166 +++ .../OFPAggregateStatisticsReply.java | 114 ++ .../OFPAggregateStatisticsRequest.java | 203 ++++ .../statistics/OFPDescriptionStatistics.java | 184 +++ .../OFPExperimenterMultipartHeader.java | 156 +++ .../protocol/statistics/OFPExtStatistics.java | 19 + .../statistics/OFPFlowStatisticsReply.java | 367 ++++++ .../statistics/OFPFlowStatisticsRequest.java | 202 ++++ .../statistics/OFPGroupStatisticsReply.java | 198 ++++ .../statistics/OFPMultipartTypes.java | 323 +++++ .../OFPPortDescriptionStatistics.java | 19 + .../statistics/OFPPortStatisticsReply.java | 386 ++++++ .../statistics/OFPPortStatisticsRequest.java | 72 ++ .../statistics/OFPQueueStatisticsReply.java | 208 ++++ .../statistics/OFPQueueStatisticsRequest.java | 92 ++ .../protocol/statistics/OFPStatistics.java | 31 + .../protocol/statistics/OFPTableFeatures.java | 323 +++++ .../statistics/OFPTableStatistics.java | 145 +++ .../table/OFPTableFeaturePropActions.java | 134 +++ .../OFPTableFeaturePropApplyActions.java | 18 + .../OFPTableFeaturePropApplyActionsMiss.java | 18 + .../OFPTableFeaturePropApplySetField.java | 18 + .../OFPTableFeaturePropApplySetFieldMiss.java | 18 + .../OFPTableFeaturePropExperimenter.java | 140 +++ .../OFPTableFeaturePropExperimenterMiss.java | 18 + .../table/OFPTableFeaturePropHeader.java | 125 ++ .../OFPTableFeaturePropInstructions.java | 135 +++ .../OFPTableFeaturePropInstructionsMiss.java | 17 + .../table/OFPTableFeaturePropMatch.java | 18 + .../table/OFPTableFeaturePropNextTables.java | 115 ++ .../OFPTableFeaturePropNextTablesMiss.java | 18 + .../table/OFPTableFeaturePropOXM.java | 111 ++ .../table/OFPTableFeaturePropType.java | 164 +++ .../table/OFPTableFeaturePropWildcards.java | 18 + .../OFPTableFeaturePropWriteActions.java | 18 + .../OFPTableFeaturePropWriteActionsMiss.java | 18 + .../OFPTableFeaturePropWriteSetField.java | 18 + .../OFPTableFeaturePropWriteSetFieldMiss.java | 18 + .../org/openflow/codec/util/HexString.java | 67 ++ .../openflow/codec/util/LRULinkedHashMap.java | 25 + .../org/openflow/codec/util/MatchUtil.java | 165 +++ .../codec/util/StringByteSerializer.java | 42 + .../java/org/openflow/codec/util/U16.java | 11 + .../java/org/openflow/codec/util/U32.java | 11 + .../java/org/openflow/codec/util/U64.java | 13 + .../main/java/org/openflow/codec/util/U8.java | 11 + .../org/openflow/codec/util/Unsigned.java | 230 ++++ .../codec/io/OFMessageAsyncStreamTest.java | 45 + .../codec/protocol/OFPBarrierReplyTest.java | 21 + .../codec/protocol/OFPBarrierRequestTest.java | 21 + .../protocol/OFPBasicFactoryImplTest.java | 32 + .../codec/protocol/OFPErrorMsgTest.java | 66 ++ .../protocol/OFPExperimenterHeaderTest.java | 24 + .../codec/protocol/OFPFlowModTest.java | 180 +++ .../codec/protocol/OFPFlowRemovedTest.java | 34 + .../codec/protocol/OFPGetConfigReplyTest.java | 23 + .../protocol/OFPGetConfigRequestTest.java | 21 + .../openflow/codec/protocol/OFPHelloTest.java | 67 ++ .../openflow/codec/protocol/OFPMatchTest.java | 156 +++ .../codec/protocol/OFPMultipartReplyTest.java | 44 + .../protocol/OFPMultipartRequestTest.java | 501 ++++++++ .../codec/protocol/OFPMultipartTypeTest.java | 23 + .../codec/protocol/OFPPActionTypeTest.java | 15 + .../codec/protocol/OFPPortConfigTest.java | 24 + .../codec/protocol/OFPPortStatusTest.java | 29 + .../codec/protocol/OFPQueueConfigTest.java | 87 ++ .../codec/protocol/OFPSetConfigTest.java | 23 + .../protocol/OFPSwitchFeaturesReplyTest.java | 41 + .../openflow/codec/protocol/OFPTypeTest.java | 23 + .../OFPInstructionActionsTest.java | 137 +++ .../OFPInstructionExperimenterTest.java | 80 ++ .../OFPInstructionGoToTableTest.java | 80 ++ .../instruction/OFPInstructionMeterTest.java | 80 ++ .../OFPInstructionWriteMetaDataTest.java | 85 ++ .../queue/OFPQueuePropertyTypeTest.java | 16 + .../openflow/codec/util/HexStringTest.java | 29 + .../org/openflow/codec/util/OFTestCase.java | 19 + .../java/org/openflow/codec/util/U16Test.java | 19 + .../java/org/openflow/codec/util/U32Test.java | 18 + .../java/org/openflow/codec/util/U64Test.java | 20 + .../java/org/openflow/codec/util/U8Test.java | 18 + .../org/openflow/codec/util/UnsignedTest.java | 70 ++ third-party/openflowj_netty/LICENSE | 29 + third-party/openflowj_netty/Makefile | 19 + third-party/openflowj_netty/README | 16 + .../openflowj_netty/eclipse_codestyle.xml | 269 +++++ .../openflowj_netty/lib/commons-cli-1.2.jar | Bin 0 -> 41123 bytes .../openflowj_netty/lib/junit-4.8.1.jar | Bin 0 -> 237047 bytes third-party/openflowj_netty/pom.xml | 120 ++ .../org/openflow/protocol/Instantiable.java | 31 + .../org/openflow/protocol/OFBarrierReply.java | 32 + .../openflow/protocol/OFBarrierRequest.java | 32 + .../org/openflow/protocol/OFEchoReply.java | 36 + .../org/openflow/protocol/OFEchoRequest.java | 101 ++ .../java/org/openflow/protocol/OFError.java | 325 ++++++ .../openflow/protocol/OFFeaturesReply.java | 257 ++++ .../openflow/protocol/OFFeaturesRequest.java | 36 + .../java/org/openflow/protocol/OFFlowMod.java | 389 ++++++ .../org/openflow/protocol/OFFlowRemoved.java | 294 +++++ .../openflow/protocol/OFGetConfigReply.java | 29 + .../openflow/protocol/OFGetConfigRequest.java | 32 + .../java/org/openflow/protocol/OFHello.java | 39 + .../java/org/openflow/protocol/OFMatch.java | 1040 +++++++++++++++++ .../openflow/protocol/OFMatchBeanInfo.java | 106 ++ .../openflow/protocol/OFMatchWithSwDpid.java | 55 + .../java/org/openflow/protocol/OFMessage.java | 324 +++++ .../protocol/OFMessageContextStore.java | 39 + .../org/openflow/protocol/OFPacketIn.java | 211 ++++ .../org/openflow/protocol/OFPacketOut.java | 260 +++++ .../org/openflow/protocol/OFPacketQueue.java | 142 +++ .../org/openflow/protocol/OFPhysicalPort.java | 465 ++++++++ .../java/org/openflow/protocol/OFPort.java | 43 + .../java/org/openflow/protocol/OFPortMod.java | 182 +++ .../org/openflow/protocol/OFPortStatus.java | 126 ++ .../protocol/OFQueueGetConfigReply.java | 125 ++ .../protocol/OFQueueGetConfigRequest.java | 95 ++ .../org/openflow/protocol/OFQueueProp.java | 175 +++ .../org/openflow/protocol/OFSetConfig.java | 29 + .../protocol/OFStatisticsMessageBase.java | 180 +++ .../openflow/protocol/OFStatisticsReply.java | 46 + .../protocol/OFStatisticsRequest.java | 32 + .../org/openflow/protocol/OFSwitchConfig.java | 118 ++ .../java/org/openflow/protocol/OFType.java | 249 ++++ .../java/org/openflow/protocol/OFVendor.java | 131 +++ .../java/org/openflow/protocol/Wildcards.java | 527 +++++++++ .../openflow/protocol/action/OFAction.java | 173 +++ .../protocol/action/OFActionDataLayer.java | 96 ++ .../action/OFActionDataLayerDestination.java | 35 + .../action/OFActionDataLayerSource.java | 35 + .../protocol/action/OFActionEnqueue.java | 124 ++ .../action/OFActionNetworkLayerAddress.java | 86 ++ .../OFActionNetworkLayerDestination.java | 35 + .../action/OFActionNetworkLayerSource.java | 35 + .../action/OFActionNetworkTypeOfService.java | 101 ++ .../protocol/action/OFActionOutput.java | 158 +++ .../action/OFActionStripVirtualLan.java | 53 + .../action/OFActionTransportLayer.java | 88 ++ .../OFActionTransportLayerDestination.java | 35 + .../action/OFActionTransportLayerSource.java | 35 + .../protocol/action/OFActionType.java | 203 ++++ .../protocol/action/OFActionVendor.java | 94 ++ .../action/OFActionVendorGeneric.java | 96 ++ .../action/OFActionVirtualLanIdentifier.java | 98 ++ .../OFActionVirtualLanPriorityCodePoint.java | 100 ++ .../protocol/factory/BasicFactory.java | 344 ++++++ .../factory/MessageParseException.java | 45 + .../protocol/factory/OFActionFactory.java | 61 + .../factory/OFActionFactoryAware.java | 31 + .../protocol/factory/OFMessageFactory.java | 55 + .../factory/OFMessageFactoryAware.java | 35 + .../protocol/factory/OFStatisticsFactory.java | 72 ++ .../factory/OFStatisticsFactoryAware.java | 31 + .../factory/OFVendorActionFactory.java | 43 + .../factory/OFVendorActionRegistry.java | 50 + .../protocol/factory/OFVendorDataFactory.java | 69 ++ .../factory/OFVendorDataFactoryAware.java | 28 + .../OFAggregateStatisticsReply.java | 128 ++ .../OFAggregateStatisticsRequest.java | 135 +++ .../statistics/OFDescriptionStatistics.java | 225 ++++ .../statistics/OFFlowStatisticsReply.java | 357 ++++++ .../statistics/OFFlowStatisticsRequest.java | 135 +++ .../statistics/OFPortStatisticsReply.java | 351 ++++++ .../statistics/OFPortStatisticsRequest.java | 88 ++ .../statistics/OFQueueStatisticsReply.java | 173 +++ .../statistics/OFQueueStatisticsRequest.java | 107 ++ .../protocol/statistics/OFStatistics.java | 45 + .../protocol/statistics/OFStatisticsType.java | 317 +++++ .../statistics/OFTableStatistics.java | 223 ++++ .../statistics/OFVendorStatistics.java | 83 ++ .../vendor/OFBasicVendorDataType.java | 71 ++ .../protocol/vendor/OFBasicVendorId.java | 162 +++ .../vendor/OFByteArrayVendorData.java | 94 ++ .../protocol/vendor/OFVendorData.java | 44 + .../protocol/vendor/OFVendorDataType.java | 79 ++ .../openflow/protocol/vendor/OFVendorId.java | 85 ++ .../java/org/openflow/util/HexString.java | 93 ++ .../java/org/openflow/util/IProducer.java | 9 + .../org/openflow/util/LRULinkedHashMap.java | 42 + .../org/openflow/util/ProducerConsumer.java | 223 ++++ .../openflow/util/StringByteSerializer.java | 58 + .../src/main/java/org/openflow/util/U16.java | 28 + .../src/main/java/org/openflow/util/U32.java | 28 + .../src/main/java/org/openflow/util/U64.java | 30 + .../src/main/java/org/openflow/util/U8.java | 28 + .../main/java/org/openflow/util/Unsigned.java | 212 ++++ .../vendor/nicira/OFNiciraVendorData.java | 98 ++ .../nicira/OFNiciraVendorExtensions.java | 30 + .../vendor/nicira/OFRoleReplyVendorData.java | 66 ++ .../nicira/OFRoleRequestVendorData.java | 66 ++ .../vendor/nicira/OFRoleVendorData.java | 113 ++ .../vendor/openflow/OFOpenFlowVendorData.java | 98 ++ .../openflow/OFOpenFlowVendorExtensions.java | 47 + .../openflow/OFQueueDeleteVendorData.java | 52 + .../openflow/OFQueueModifyVendorData.java | 52 + .../vendor/openflow/OFQueueVendorData.java | 119 ++ .../openflow/protocol/BasicFactoryTest.java | 134 +++ .../openflow/protocol/OFActionTypeTest.java | 37 + .../openflow/protocol/OFBarrierReplyTest.java | 36 + .../protocol/OFBarrierRequestTest.java | 36 + .../org/openflow/protocol/OFErrorTest.java | 88 ++ .../protocol/OFFeaturesReplyTest.java | 62 + .../openflow/protocol/OFFlowRemovedTest.java | 43 + .../protocol/OFGetConfigReplyTest.java | 38 + .../protocol/OFGetConfigRequestTest.java | 36 + .../org/openflow/protocol/OFMatchTest.java | 92 ++ .../protocol/OFMessageContextStoreTest.java | 31 + .../openflow/protocol/OFPacketOutTest.java | 45 + .../openflow/protocol/OFPortConfigTest.java | 39 + .../openflow/protocol/OFPortStatusTest.java | 44 + .../openflow/protocol/OFSetConfigTest.java | 38 + .../protocol/OFStatisticsReplyTest.java | 77 ++ .../protocol/OFStatisticsRequestTest.java | 79 ++ .../protocol/OFStatisticsTypeTest.java | 37 + .../org/openflow/protocol/OFTypeTest.java | 39 + .../org/openflow/protocol/OFVendorTest.java | 215 ++++ .../org/openflow/protocol/WildcardsTest.java | 162 +++ .../protocol/action/MockVendorAction.java | 41 + .../action/MockVendorActionFactory.java | 15 + .../action/OFVendorActionRegistryTest.java | 17 + .../java/org/openflow/util/HexStringTest.java | 88 ++ .../java/org/openflow/util/OFTestCase.java | 36 + .../test/java/org/openflow/util/U16Test.java | 33 + .../test/java/org/openflow/util/U32Test.java | 32 + .../test/java/org/openflow/util/U64Test.java | 34 + .../test/java/org/openflow/util/U8Test.java | 32 + .../java/org/openflow/util/UnsignedTest.java | 83 ++ 337 files changed, 34455 insertions(+) create mode 100644 third-party/openflow-codec/LICENSE create mode 100644 third-party/openflow-codec/Makefile create mode 100644 third-party/openflow-codec/README create mode 100644 third-party/openflow-codec/lib/commons-cli-1.2.jar create mode 100644 third-party/openflow-codec/lib/junit-4.8.1.jar create mode 100644 third-party/openflow-codec/pom.xml create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectListener.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectLoop.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/SimpleController.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Option.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Options.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/ParseException.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/SimpleCLI.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/io/ByteDataBuffer.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/io/DataBuffers.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/io/IDataBuffer.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/io/OFMessageAsyncStream.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/io/OFMessageInStream.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/io/OFMessageOutStream.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/Instantiable.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFBMatchFields.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPBarrierReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPBarrierRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPEchoReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPEchoRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPErrorMsg.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPExperimenterHeader.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPFlowMod.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPFlowModCommand.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPFlowModFlags.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPFlowRemoved.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPGetConfigReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPGetConfigRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPGroupMod.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPHello.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPHelloElemHeader.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPHelloElemType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPHelloElemVersionBitmap.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMatch.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMatchBeanInfo.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMatchType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMessage.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMultipartMessageBase.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMultipartReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPMultipartRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPPacketIn.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPPacketOut.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPPort.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPPortMod.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPPortNo.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPPortStatus.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPQueueConfigReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPQueueConfigRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPSetConfig.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPSwitchConfig.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPSwitchFeaturesReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPSwitchFeaturesRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPTableMod.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OFPType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OXMClass.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/OXMField.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPAction.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionCopyTimeToLiveIn.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionCopyTimeToLiveOut.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionDecMplsTimeToLive.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionDecNetworkTimeToLive.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionExperimenterHeader.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionGroup.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionMplsTimeToLive.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionNetworkTimeToLive.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionOutput.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPopMpls.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPopPbb.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPopVLAN.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPush.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPushMpls.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPushPbb.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionPushVLAN.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionSetField.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionSetQueue.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPActionType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPBucket.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPBucketCounter.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/action/OFPGroupType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPActionFactory.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPActionFactoryAware.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPBasicFactoryImpl.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPInstructionFactory.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPInstructionFactoryAware.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPMessageFactory.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPMessageFactoryAware.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPQueuePropertyFactory.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPQueuePropertyFactoryAware.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPStatisticsFactory.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/factory/OFPStatisticsFactoryAware.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstruction.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionApplyActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionClearActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionExperimenter.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionGoToTable.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionMeter.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionWriteActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/instruction/OFPInstructionWriteMetaData.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/queue/OFPPacketQueue.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/queue/OFPQueueProperty.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/queue/OFPQueuePropertyExperimenter.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/queue/OFPQueuePropertyMaxRate.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/queue/OFPQueuePropertyMinRate.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/queue/OFPQueuePropertyType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPAggregateStatisticsReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPAggregateStatisticsRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPDescriptionStatistics.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPExperimenterMultipartHeader.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPExtStatistics.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPFlowStatisticsReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPFlowStatisticsRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPGroupStatisticsReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPMultipartTypes.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPPortDescriptionStatistics.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPPortStatisticsReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPPortStatisticsRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPQueueStatisticsReply.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPQueueStatisticsRequest.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPStatistics.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPTableFeatures.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/OFPTableStatistics.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropApplyActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropApplyActionsMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropApplySetField.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropApplySetFieldMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropExperimenter.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropExperimenterMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropHeader.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropInstructions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropInstructionsMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropMatch.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropNextTables.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropNextTablesMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropOXM.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropType.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropWildcards.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropWriteActions.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropWriteActionsMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropWriteSetField.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/protocol/statistics/table/OFPTableFeaturePropWriteSetFieldMiss.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/HexString.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/LRULinkedHashMap.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/MatchUtil.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/StringByteSerializer.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/U16.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/U32.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/U64.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/U8.java create mode 100644 third-party/openflow-codec/src/main/java/org/openflow/codec/util/Unsigned.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/io/OFMessageAsyncStreamTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPBarrierReplyTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPBarrierRequestTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPBasicFactoryImplTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPErrorMsgTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPExperimenterHeaderTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPFlowModTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPFlowRemovedTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPGetConfigReplyTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPGetConfigRequestTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPHelloTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPMatchTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPMultipartReplyTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPMultipartRequestTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPMultipartTypeTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPPActionTypeTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPPortConfigTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPPortStatusTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPQueueConfigTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPSetConfigTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPSwitchFeaturesReplyTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/OFPTypeTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/instruction/OFPInstructionActionsTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/instruction/OFPInstructionExperimenterTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/instruction/OFPInstructionGoToTableTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/instruction/OFPInstructionMeterTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/instruction/OFPInstructionWriteMetaDataTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/protocol/queue/OFPQueuePropertyTypeTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/HexStringTest.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/OFTestCase.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/U16Test.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/U32Test.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/U64Test.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/U8Test.java create mode 100644 third-party/openflow-codec/src/test/java/org/openflow/codec/util/UnsignedTest.java create mode 100644 third-party/openflowj_netty/LICENSE create mode 100644 third-party/openflowj_netty/Makefile create mode 100644 third-party/openflowj_netty/README create mode 100644 third-party/openflowj_netty/eclipse_codestyle.xml create mode 100644 third-party/openflowj_netty/lib/commons-cli-1.2.jar create mode 100644 third-party/openflowj_netty/lib/junit-4.8.1.jar create mode 100644 third-party/openflowj_netty/pom.xml create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/Instantiable.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFError.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowMod.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowRemoved.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFHello.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatch.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessage.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessageContextStore.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketIn.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketOut.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketQueue.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPhysicalPort.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPort.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortMod.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortStatus.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueProp.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSetConfig.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSwitchConfig.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFType.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFVendor.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/Wildcards.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFAction.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionOutput.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionType.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendor.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/BasicFactory.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/MessageParseException.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFActionFactory.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatistics.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorId.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/HexString.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/IProducer.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/LRULinkedHashMap.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/ProducerConsumer.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/StringByteSerializer.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/U16.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/U32.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/U64.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/U8.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/util/Unsigned.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java create mode 100644 third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/BasicFactoryTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFActionTypeTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFErrorTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMatchTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPacketOutTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortConfigTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortStatusTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFSetConfigTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFTypeTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFVendorTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/WildcardsTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorAction.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/HexStringTest.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/OFTestCase.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/U16Test.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/U32Test.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/U64Test.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/U8Test.java create mode 100644 third-party/openflowj_netty/src/test/java/org/openflow/util/UnsignedTest.java diff --git a/pom.xml b/pom.xml index ea8ee768..98366c5f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,8 @@ openflow-protocol-api openflow-protocol-spi openflow-protocol-impl + third-party/openflow-codec + third-party/openflowj_netty Openflow Protocol Library diff --git a/third-party/openflow-codec/LICENSE b/third-party/openflow-codec/LICENSE new file mode 100644 index 00000000..ee6da46a --- /dev/null +++ b/third-party/openflow-codec/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +University + +We are making the OpenFlow specification and associated documentation +(Software) available for public use and benefit with the expectation that +others will use, modify and enhance the Software and contribute those +enhancements back to the community. However, since we would like to make the +Software available for broadest use, with as few restrictions as possible +permission is hereby granted, free of charge, to any person obtaining a copy of +this Software to deal in the Software under the copyrights without restriction, +including without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +The name and trademarks of copyright holder(s) may NOT be used in advertising +or publicity pertaining to the Software or any derivatives without specific, +written prior permission. diff --git a/third-party/openflow-codec/Makefile b/third-party/openflow-codec/Makefile new file mode 100644 index 00000000..f5f73a50 --- /dev/null +++ b/third-party/openflow-codec/Makefile @@ -0,0 +1,18 @@ +# `make` commands +all: + ant + +run: + ant run + +doc: + ant javadoc + +tests: + ant tests + +count: + @find . -name \*.java | xargs wc -l | sort -n + +clean: + ant clean diff --git a/third-party/openflow-codec/README b/third-party/openflow-codec/README new file mode 100644 index 00000000..ff164ce1 --- /dev/null +++ b/third-party/openflow-codec/README @@ -0,0 +1,16 @@ +OpenFlow Java - v1.3 + +A Java implementation of low-level OpenFlow packet marshalling/unmarshalling +and IO operations. Implements v1.3 of the OpenFlow specification at +http://www.openflow.org. + + - Yugandhar Sarraju (ysarraju@in.ibm.com) + - Anil Gujele (angujele@in.ibm.com) + +Building requires Maven 2.x+ (http://maven.apache.org/). + +To build: + mvn package + +To build javadocs: + mvn javadoc:javadoc diff --git a/third-party/openflow-codec/lib/commons-cli-1.2.jar b/third-party/openflow-codec/lib/commons-cli-1.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..ce4b9fffe40c41669797cd806ac989471b2acd84 GIT binary patch literal 41123 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%0D2u^7bcx|ezvBT@`6 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 CNzF?D literal 0 HcmV?d00001 diff --git a/third-party/openflow-codec/lib/junit-4.8.1.jar b/third-party/openflow-codec/lib/junit-4.8.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..524cd65ce5fa7bbbf03e6257c6cfe4b07a65cf8f GIT binary patch literal 237047 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
Yj}{=6(Gxg?3LC331(V69bELkqTIairs4apoLD zC>%o_=8|LH z`6HvcVK7MxbiBBFdeN`{hRP;pCk{*jbNxJ*oCjKG(vMZdQ`Mu`lR}19aDKO;y4sp! zOIsIw6`}l~h{j6#dlLzmW@)7eNigH`VeBHGttD;PIp+7KMXL_xnd;p{k4#02sJFpyW?k&?D?uRB z56fn-2l?v|4ec9(VbZ=)8g9}|!PCw^SksLQS$P2h~i-&lKWl9%Z_l@hF1WpO4z8@v7 zvhTnDk}HwW_igitHx9mfuRes+=94n1M9q$a!!)1S5$M}$Vz_H^Ah>v-G&hVazxX0k zh|qj^*^6AGTzyG$`M|T2@#GnrcvsfLIIl&+9`p~T&3IsB6mNNgM1@_vX z5aQU;M^CZ4`<-;m^+=WTRmWP_b)@Ft@fddQ{y_B3P*Zzx)!j|-)V;iy1=pm!h_Hr$ z$MBwkiWQBo>omA8LW_Nb&u4%13SaX-HH)oZ&U{?cVS>trg+ zVwO4%*WJovouz}ka+APzAms~dZ|KFc-6bLHCM-4g76In~KIZ^|W(UOMKEQ?Xi}Ibh zxpzjeon3)xJ^oA`guxu{Drfm@PUs;2J^zbh#z`Z~5~MJ4MtTMgl_StkyHJ%SAxZ0~ z5A=6|d7tQ9yu*rb8jT;=c^oal;h%qFwf`_UyuWd!L;)-xA%JK7cXC6Hw0{wN|72OG zWdH-W+(A7?x!}=$KY#>w40bRB!5~P|2hijrsj&ML9dH~D!ORnHH z!mpPM>XY2=eTO{s(xgtUyt@jeZy9qtp78WoYk@zj&5Op+ zF#%-i>wQZ1~=))Z!(J2)hz+bV&H56sF$F3Ay7)kXBwQZ!y zP0atSB`m7s7Yxh0)qR9s_d1?~T~}6-m2H)A5B{OkR@{7d?wJ4Waez@ZO12Q1*jv)? zhcJvtLR;=nsb%DAknnw-Pvo;8Ie3mrk&*O5OZ|x0q%yfYNChR~sLUpaid1^ez>`4c z@OCpeid=CwTH%Ag`QZK8g3}fzB{aYm90Q(zPqfPEJLp>*1Li@^J&cWr{`)`2zcwW( zwnqY>gcJPP&}6w--gv?N>M?AF>$Bo)tNNXy~0`5Hyrfq=S~2f{Mv#Vil4k=+V+8BpU0EoIDa_(#GaE z6OwnLN5Onxcwmv{hW6`uM}{121=1@UxPJT|OLN?0Kjc2$%klAfMeH_zYk;N2Gu@)A z5;IrPxdGKafc6TlP@q6d9cp}6>lWqUS*R`DO(3f}ZNbN9g<0z@0p&@E4Hp>3Cx{j>!Ny^p0|^$*25_jOg*7O686+F^cW-+}BKw=*P~EQJJs}!u@u7 zm91oB`fPJ!nH!b0xK_+`Y4ON(G#jIn_5tUBGm3TiLNn&;KRf?z@JSYQuuO+^P2KN_*vu&2y zkIVtvc!-!d^r1FaU)c@lPB<|klUE1^Pjj#4m1#67I=!V^oB)Y(A2Tg~`{+p<@ z+m-Xrv?r|I{tP!sQ9S}7;i0xLn6*46SF4`|vZv%8=C3XMoXOyWi2M#rI#u8Fwd_`$ zbk@RUU#lclAP^oS>?Of18PQ5WmQNOhKodAvq}K*eIXdUAlftU6qo7YTF>X)8wu+-K zrPlf$=fK1GD5Tzep-6u869*4}){CLVpI2A#w0K1+nPO}dh`CZYCo6q-zSUOlji^)I zUuvu*S+*b@m%R5(TPmTw|^s~>*AZj%%_&2TC0Yp<&SZE94f-6LME;-FrFoJ2j|UCg|~ z@E^neQ5Rsmy4P`_yk=U>c2T#bv!MBC>*&Y!PR=3a)7u80;2B()s>P8z61NuQ=F_Dm zN_2Y18I2ey6??~+bwBkkXK{wFcCtt4R3yVK-q(%waK4n7}6NQp6;K)s~blM7WcnvBZ zf2`id*!Eb|G*urYb^?D#Ep$m)T8Jh;1Qi~y4sfaGrnf?fSFsu2;ucDJEt>-le*|jK zt*wUg>1)Y(#Xc}NpCH~WlcrOldJn!a$mvwIt*upBWEXvbfGi$2f7sMMAg94! z6#%Lk*H#H$tZXYYS0*{`d2iDprek7PhSJ+A0uXnqTVLwwd;LA*j!HxM1~yj|k6OUq z8YJ)Z{x8u;Sf-ZQ8Q_ragZc7>`2Rkn|GNJwRaZ3;RpCCd5$bBJlI(83l95?}>(x{? zK`O-g8vrZhsWmhcs}Bn-NxQOwuVkdGENq;OhiiCNS3kV2_?1Ab)M5M@K?-jqL>vfXaZ0%VDjyt}1eXfP z9!QbE!tNJ1DSOsIh3V5-VyHzX%2m8?3c5uw((K9q$JjfDN7`;%xZOd=ww+XL+jdfM z(y?uJ$2L2*ZFKC8ZQHg^zHhC)&fe>+v#VLg8ujid}K4XkK>`$RPX{CACR2~j! zEs;D`l0gcr620lNLm*#&KT~(L+GIHs2c7(|Z*zedXhsmx1eofKg&_c6Wsqel3~hU?EZX{OV04TS~m3R?s`^u=H|9r1`S<271jcN zKF(51;cK%54r4)HQt@D8;k^w>a^<51>9X<=sgx5(?KEE}r(;SonJ>^3&Gg@FqdbFD zYn-UuNMQ@YL1YH>5=49w(i}+qy|vSsB^`7gK`yhy&_u(wH#6&s=|_G;Enn_W|Aq=;#D%t2-`cCuOHWc z`x(OqzlYxyIH+ZQ?Ub(}_DIS3VN91aeYABicRUba7;Wo~TuhZ zwJ0d??XDR~$wTD@*OX&z95i9l9~4A4@e=!9(rf^W$~<$tN7R(;aT2hFJ1)H#O$SxA z10wvw)XlnDMF#CA+>_=OzS0TUvi>bh!S9(T>4tHk7(P@E>HmXs0>+2*ceFGzUso^) zU)&`lU#uNg7L%2hK9EeiyZmPva^;ra&l6<6UO1?%9Wg3>woNCB9J<;ryn0wwoE3ti zfgDWOu{RGL8a`}QVdp|JEfMroy^K206Q>HgK1U$M7K|6=7PS}c77icFj#g0QNPyhA zBkQ-F(gLF{yfDmX$)aE&ol%o?v>4^tZ-VJ71#-jri1^E()o_wFs4Kdo>np|FafW)k z`o_nk_3rSt_!ZLEMz~h(<7Az!M@|vKxU9XB_cZ(V+wx6VItUM?__LwZ5i ze0}Cgm83)%#jTHq6TG=|2UqV7HzDYXz%@;m1ff9m^I3*p@+D7> zLy>mf*VvO^!WZLh=fye9kaFM4h%3(OfSwExN@dRjr?a9Mw?#bUgvPyPUlZ%b)qZzx z7T;HRB;oP-0n&pT_NUhML^Q=3^yMPpTlFl-)8;g#w{j&+|^ptT%fBdxBX^yMOknw`*}snRfRl`(D=(lSY68&Cytjf`{6FzD2W zm=Vm@EDnJvkqOLK4cEmbE-s^Nra-KhrYI6loH7m83_!XqlBOu&48}k!Z>@&}bz{PM z$Is=l#^)Yf3SDah)ZpVvdfa?xAgP%nC8$;73#@u_ihuYqhq#;{SgoS$|DV zLXFs}lb=&l&Zk7>Kjq>((tq~s#x_p>QyFTd2|c(tY!+}P)9A)WW&*TZPKUgwWju6>rvs7AVR;;~Xn7a1}t3Em8C7!hj- zj}Hpp>o{p2r$T=^qq-(`MFJ5^*q^HN;-YI&rt22Nys)O z*)}^l;k2_)LNn)r*<4Dujm!#ssy4DFa8GnOu9%7Q_U~D#5KNyIYqocb<4xmyWj%_?n;Bu$fGas`Cn;gVP1Wg9JPVZuYO95IVz~d zOh9w#=1A^z+SP&zYx?cg;4X-<#x5tDtCP7?eHvY0yMT`|v!G zai85YyLyV%Ew}s`dYI2}{=q&Tuh7XVxBZOyApwUL?_t;rkc;H?s;cu@E!)l!L9s-x zyCf@3iB0K^>m!k9)Ytq5LLHy*bp!*v4a*yD2u;01JE*d!Bh*8r@sxqjW$4~WncjSv z-{LMA3VlRPzgRg+tu69fa8r*y-P{SC>CaejJID=AiN+hM&UO0>#(7N~Q^blQQ$WM` zmN)V47WM*kXcwo2w(*sYFdj?<`#?;Z9ZGFTL9AVH&$&#aT#0AcH29xR)wiAj@JO$B zVGTLJNNjDeR^Z)AbFcdTha198VAS{A{+s`nTJqN!@rAQ-@c6SHs6I97|A#Yz`5!&y zr|;YH(?sIa=(<0BVo}*Jn`{xuYT;q-7)C%V3JEP#UXbYH;;0c1caXU%-JL~73hje< zXZSpXD6-=DAt5nG*Tq_u=uOYrx$#eK)NvG#;G;ew7CMq3=!Gl}5$& ziAilwFHthmq^QWu0zf8`#GaV-Xg5FF9$gd?7qeW}w$;^?#eLie}e=x>9@+FL2XBQ;;f z6}zbh$s5fW>bSmNzMG_(ak2kup7EKd3o=kAbS$gNrNhEXoiXfiGux|k3NpR>_zhA# zEyTex#Y8i~Jo+U+G+hWqr(V$UR&`TxmiX;#YKgr4lwIum`#z-O`#faQ2lzcjt(H2m zMy}asCQICI{|8h1f`ndH_3Fcqd1^Gd@%nm?Cx8|`lOik_9OTJ0Z?EUg=9~hs0GIp7IB$g+d+m9@SyynPhK8!m z*&tqNn{dzX%hfV`t9#oerE9ulE=x<}LU|jM4J8^^@(ZIvw(|UPl}7=woay0wG^?)p{F!@zlRfPr{An z87PR)!t!)g1_ghsbrUh=JDFfI8mIVE%|ojk)DS*g4EaKTTdI++$%vN5qWW;S+9-;M zT8d^deo%*F1M(fNg<3qeSNNy}IC9h59E#COT_dAV z7aUUt56`FO$%*M^jT|e!?91nwTVj#0sRoHNY8OFEym5U={H_4WRa_9T@5wHcaomipsTU({4}Zm`eQ2C>9*jTewVl{}p%yKI(7MD+#D6H| zASimg<3l(fbVc)$55z&Z{DXk@DiH|5zn`3Sd7ee}h!u@M^~f3BxjBK5xS9N@3gr{I zakD}WS8H4lZ+iF(y5yAjY+XQ1j9P`d4-m(J(JI1V8*GIFE?;w29pr5&)VO zX(tp7MKiX1K>#lj5j$l^s4qg|i5ibk2NogUJuXVERJ&@SR z6}$e7#`k#Oe9gE0>kke@tlPBBG#AKbHpI_(h$f^dPqARvqneJcN`?v*7jb{z%P4`- zO;^jQL`m61`vk5UaK_3f@Nh=>H2m?+=XFuui}ML*zeO^j!% zyYE-HBxV5-FMb48o71r1_eAAU@HUr|ZV~au&Q~0=#6C$GZ0u02b)Eit3rS9j-~_8{ z2^M`=OhM*^ArIc`jMZMvDx+UB`M)qCf_h0UVURF=tL+0<8!6Gl2ZokaBYy=MVmH}% zR}QhA)XL;>)gR-8i9Xm%NHBXcyUW)niqb7gFADhvbXYs7Ekd4$VCQ6-sae^TWnzW} zLH>z5t*d6nxM$);#NF<-QId1HW*Y?QeeN)`if&0H#J~(c10-5CO)A2Z=3a9^5F449 z^In%BUF^9l2%P4hGNcEza8Vn0MQ7&=XXppd1Tg3RCy<5Je2lPtn5Zp7$lgVJJvkA$&ammp-o$(%al z)kU{aY$;!PMr4Uh7kgI{eP?z*$x-?8sdvUc$pla%Z&V^cM1;&}_Jg9+uh=nlJM|m* zf&!6#rCIW7Wb(q!Ql&CGCYk`nfOOPZ8T>A z%qY7|IquZEhgg2&udsT3A(*uz8eq}>X&qKPEyCKUyq~&?96szq7;L+RL0yD`!>BAw z{|tt{C!0*;6dY7>IAzB?0FY58jf%@Bg6G)%H7I)Od&T3T?$Ld3&mzsL%Ly6 zq~5nQ@Ku+fT}IV{kev+`#jYF>guCeWWc{6mpQ}4sbjl@3UUy=hzGmi^1T;gul>b#Z zQtJOi(-B6?R=G7Yd1Gs1j2VtrBZ4B`&m_xI>(B3J$vQ>)JM7XtSA%AOA%sY_9MR{v zxG1BBRXaGkE(IM?^Nu4LLT%w`%py%ACgJEB`4oKVR;lyjC=Y#F= ziWuX~N-eFor4{Dvozv#2iR7qlNmZv-qKpgO_o*|4S|U;?aT48?_E<4rj!2gjY5H2qfeQ`TLdM6-B&BE9gC2n!=ORS|8=%H4Ubs+^!*sM}c*fxYrh@Dn-{;bbXj{@vpkUHq8pd8Q%mh zX7`6;TM0OoI)!$$mxbY=pzV>`b;NDKS?C_{#dFkf14lf;M8%1I@7O)e!5>@v>GRArg8DO~88fDieH+=L zgiiM5p8RFS_?A|{5hL=XT3QI9Ra<+{tlx_u+m-6*zD@9=t!B^%0fjY@KD8#!lZLG9 zw<^H-DJmy|e9s|HsUSiOQ5m78B4F#MuCZ{H5GslJWSQ0B>zM>{ux}OT@xCbbU0dy1i}Z>eETM_05t^3E5?$SF)3P{9w*OX= zN~^$7?wHFpf{TCI5POl)*^pyC4mU$M;S!>@8n&RPg&F!FUAQ5V#!DFM< z5X!Vck*{hA2&AZJ9RN|6qkM}eB{Wub>@=TaWjn+kRH{h{$OTuI(vhG~*qSzh{|QKJM+SK&=}#q~24P=ZqR8OYoF|1IGSJaiz>8uHuv3 z(9K<&_>Q01FO<5l!^s6Yfzl6yibLNMoP36>A?o(Hi#lzCf%hOkRqo5u5*y`BbVirE zWSY-6=0d}&Rih%c${#*au$N_`buzI711uznr+_!Rdu~p`@5O4t+aC5wNczV%A%>j*ODfFt-sYFH5cTIcz)2d~z!oUH{l$rfBT`c_z`Njo_V z-Inxj+kC`JY5_W0&}hQP@{(bsV-Xkd=o zCKuhIfxq7P1)dmVFKw@}Rnm02&%Ch(57s@|M@54)o#Zm2Hna`M@gL?fx4g?)WeZab zshyFUR}_puOZ`Dq&6j39?w4`vw|eZytq;Yn57#V5akOWPJn8pJr$?8-TY+3y+FW*m z*{U4$8o5YJK#ljgXr!%c%C9M)CJGH;m6w&A^=E6i6j#zQtvq^a z*?#rci8h8QSA+k!^8whwGnfvxL;6XW!D^s=xTj~>i#;lC(zWv4c-p`d4%>mPg8>DNCE^u zS5D%ei-iA_^zKNn>}>92O#1it-x?F+|B~{=Zv2gN>--z%rm-)KNhE$ZIaP-wf(iMC z*zR{}-ms;~`LJyxf2ntp!=KGhmD&ao$#JCba?p0O^ebbj>LObH3r4ZlVnelU4!J^# z6hFD|@$k{64NcMXRl$8aj-HsJboQcJW)e9(R*5lTvZavj&KrIXaZ|c6N@6Ep^(908i{3w z6%WO}ZI8b;KHE`4L&XC(%G?MH`&T~($u5D0(h@r2P))_a&Ag8)lYm$d( zf}3r%?txjR!oW8+6xT0;i$dTlu%|VNG5M>4b9Dbba{sUC`SF%#p6D}fAL`Sh_@6*; z|NJBVF*awYnY+%2p|ep~k2288<2KuF4I zv_MZR9{mJR+HI;bo6>Z9Tx9~2^r9kjn(u>=+FO$Nk+xZQn1Gw3$NPf z)GvcLoWu~K^tA@C#eD7<%zrM`JeVz`;w+BCsIoF|^~__Mn!5LcZ9==$0hCoM{N^}U zvzV(@5pdkd+!d2LX(~HcqKPeBS?O7=USsmq#xlT!oNyTf zw3cvc%tf02QMge36U$tL@^If^A|W%Ka56EuM##{su&b>rxbR)xe|)R&dP~XNn6X~m z*yjdY5YNXN7)wQb4sA-JnPX73-2OE5vqI7_M==Mk^p~5?Z)er>aP?H@0twO)%@P%g zykyy(yU^YS1x3tXBrQo|o=q82D#fpNfe|Md?Qk=xI<>cyvYSk}P{Ee{yt#x?VciC4 zH>JuBj6GVFK+d$`O~nk2G^$lWGU==->0B%OD#oib>k{&X&`H^eQOh4Yn6QKXM=czz zRf*|pXh?02KNmg_VJ|p`>;QgS1xS&l)H_th8}^`gaiG?@N)1xW<{1m-8mR4W+MvDj ztQN$0=FVj*g8Xs{EP7kF28)WIM4VB&^RF{Mx#*R3#uptEOBtfi_R6d)+-L` zm}b_E#^iL7C(*&cCdm@kRVb~u;9+)u8#VXTdo z^jz)w6@=5M9iopPw%<@Ll6v~8zhW7Tgz$5Hpbih;7aB`U1PWtU z>|zVEH?R}SO{^KYqgaD&2!XCbCrTn@!>!=dGnp?V&^kNUp4otqo&lfB*! z*!)t$aadyi0v}p~I3wdG$RTs)BV>QKM#!BdFU^)U!Yg>fw2c3&Eq~;a*GhDSK^MhDF65SG_W9^k*-3^xsfIH-8nTG4$O|mT_u3b^EJq*kt`QyNvf; zsvMMT(c72)-*>TE14&lz2v!x64oy8E!yU5M<;j(?%=H-lUoEac`(EzFzz%8ty7sZK zX9PdqS|2{WSbUb?u)~t=X+FFVakxCxiMat2I`C^cik?i1r*M>B1R^-_SGUByx5KKLR}^aB&Kn5qOdo6S zf)`qUYL?q-{8DnrD`nRtw_}lgLh$(zi8${gWR<%W#%}ka{h!ql;U$ z)#~;`CNUai3NFs4txf6U-|m%!t{;>0KVR@rpYNCd#Q1Tfx3)DhwxSpNOStHEn zZfs@rk5^5kipD3<1@+ygH6Kd9#;UgXC!}mRm|b$e;NUoi@>LEz!XxUkG-P3DVd&K@?Xl_a)m?{1>woiq?;?^8fa*Q z`=Nn>4dgpfb{mQI(L|mKjV;QiDaE+I2xNL0bw)0qe0wc@k*q6F%z9nUzK83#+EGev z7)$Wq9H;A78?<>*Se-h4G_+jAQ0QG*6xLZjH>wYgJK34BV?m=$G)3ga$T7T5I=5`F zVE64{8gE!#8czIpkr&_3u5j+!?ruEs@Woopl*YXQf0IpSs;e_i?*Zu6rOgS~o#Wj+ z&qTu(^ogH%!g}l44ZS66=l92uK-upVLO=hoNG2xi6>{CuJtIIe(BZ$xRe~ra5Z$F; zR?W(qeQX1mV?7x2d{>u>5z%r=2z09L$)WMv<1DwSNn=OA6+SnJ7Q^wADhwKT4uCQ zMA?I7PntGgYY25E3%xeWBZa(;Wsf`cI)zZ$EL$*?Gg4hy%~OzHhC#1+_1*n%+-K&VumD1{ zD)-t=>%rYg=Gp~+rx$p2xH#isGm1F6>rQiUx~2>s5Eg0I*@{`@p@8HKS$MHN3ah@z zMwx;1&ook^U=`hO>?&6%$;7p97x<3yrEie4uZ^e7>c$I}OBr90me#xM0~VS*`(_#t zc-K)VDj%LD0F}H!cN&>9?1q)wxuh=*%etW{K+PV0JKTkX4kx_0Sk?-)J@_9ZXLPQb zY*~G${N_&MF>e-W>}x1fUJid07%+gg?MCXAf0BCNe8uDP$R!mwOF7HAFO3)IMmh+x zQ#T{oge6R{rj?;4ytnK(RA(kVzcPo)k26n3B69aGYkyyTAMhH}7eMoA)bo7(F+(Fe zb|%_Rf?euUehmLRh$G?HW7_c`?D(fI8O;d*b5T$+*m{%C1&*gnIB3-m1h$-5_H6wG3|kRJA=S$@7N^$dpv%;%tM9o;l^YRex$AiA`A32 z{18)Z?5k;Y^lkuB5i9mjMa+8aUNG$*=%qf`IM_!z5;XPWU&NNEF#32FKLUAF_jxr` z7neH(Lbi(Sd>MMN*oLPtJBXBqFT8|qVUf;Wd#ebxZbB1O>==AysTxHK90C9u?mg_G zUj9abB~7rE(|q}15&2XX*p>05WL&};y{|aCaCLF<~AGVik>{}YVl|8xAx8atWU8vQGrM%mhS`jekYM~J5$2$$>FVI2k!trdJjCZ=OB zloXRKM)tB@B<%`eoSal2IQFApc(U(rfbi9-ALv(<<=gZX$IKg_^S`-4fE^7G>~rck z_jJ{pPZj$dPiQr9)^jE&GUND7-fP;;*Z^MIHCjK`3cI zHR@nISMtUz?F72w>;*>#rv*D=bSH=_{b%Gn96C9UD2;xUyh1+>*UQN>lMLP}QN@L- zudy~K?jBFNwd0H>*Z{6D3NOL=lNf~uMe6)1{2&Pw=W@AH??u6rS1dfD(T43k$F$`3 zy~L#}ig>5BTpZeNrn4<(E0fFOs!UZvXgbXmhdnWLQk9z8y>2ju+Q|B8G!Wx4;wT@i z&xS!0@NBF6h*I-oUX}L*=IPg#yH5>kYSYow?`68P$EuHpWvhM+oQ`LHJlYQi`tq4M z?c5h^*`m3_FWW5bF*7`pC^__jELX-{XGqaV6WIMYIQoVUwz*!*zRN%-0AEq5Q#a*5~+)BQ7ckZT~lr))!h{BqDrwM+YDheI-K z?chk!w^1;iUZ2_6}ZxS@nNRnd7&(u<~&k^(gcrgA|hyUyY)LvY1)R5li zm2yAr!&ZA+vo0Y*!WP4FB0|#U+mPwO-J4uCR&gAK^_J$=6(p8(Yl%16h|q!8bhdG* zkO@hbkTFR>IUG6oPJ4L%8;Hx~FB|P`np)JH08Y)d4DOpTcE^M9p^w*zmxr&oeH+Le zU$~>Z?F{TtLN;^Vw52wgDoFwFe(LR*y%>g%N+3{+RFz>D^kr_6UO&iuIXa#Zbq5v# z_7_mxz&8}GWYIR1uB_2EkftxlFL`ZGRM9pt=+#tS1^N;C_oKHT8GUlMi+b?9-(eT} zj{CtXcQj$y1D-8~u2S7zwST`0mHSZQvj-km89X-lE%wX=Y1M{7Ka(fjqVcNDQ-^b| zr~`1o*$>X}6sz6)%2RA~yDPgR=a@`UMWVGtg}alW>I`J&lMdiK#j@y?<5H`kJ)RHcr9`{PIi-%kpa<>j_LuUr2`y2mep?$zTKn# ziw`M@XeWO5*Gdhayty#Nj`GctJKywFz1;5D7NBCGstA0g%}g6)>6(oyg=J7f16-zG z->@`z-f=20u>Xcj{-;EIgi?27A)ruarJQCu+f|f4O>~X5(Bj6p&TfDyJbu2o9RNG- z`Hgjf=73ygFwrzSb-cc;%|y`GA=3+XrP1p82%egHamtG-xhyi?ohF~VHfl*`xRDOJ zfy7;?uV}14?0nuz6RrVqc}VV3rr}%R_juuBF-Hr36|;!)e2j{4u`A38JGbhco?!;0 zu;B>~9d(=GYEc8-;v-{n6VEhF#BWHJKA}f6M{>i|Cqv(Z?ltEc!y5dyLcBti)lI#B zmvcw4M726J6&c5)>n}+FjcS{~C5FI(2$n`tDv4XyLP!g-b4&~0+L|I4{pFF3>Xm9l z%&Q+!PpyG-{_U%&*k|}QwaS-nX>QyRW%lo{V#4r#cEOXL z2vlEAT(l2|RIsAD+=YA79<5=USa1iS;EEUu_3=+}v=)?b>Z#{}k5c$$zxHyQr5Gm^ zHG3~EJ;&qfm1`~b3|jN6Fpt6#E7`)5eLe`a@%ji1n=7{DIf}L>Qo^_BvH3!|eKPe+|vW3TtDFutnxa!*SIi6&+SSy}*%_4HX-;H6oAQ=gt-1Fvw$ zQdNM9VFt}*TQuYSm-s9C!N2A&J0K5v_E}bY>#$*I%+y6Bd(CBBgGfpXkHGvoC}+9` zo@5TZtLlivMQ{xu0?X*R~X2OjBaR% zB-6JZBW+DZ{=8PC9#dF_)-m$C#oz+H2INph*=o8BJP)3krC zo0XDlO~VJd+^t@4>aq(=n$ijdfru9b!ApNGJF0I{U0sy;dG5tGcvjzA2tpN1#QnDA z)S~<|Q052U#-O$hvF9-eobx4yJq;k;B`nMy%QM56kvICp7VJk4XZV60{6`nfJGidL z=y2W;ldre7>=Ze%(h>)1<*V=(l@mZ|+6!Q{>4VfEh-?4bP}tdvVMHD;y0)6QDHvGu z-~;5b30oMN`bz1J*VgyYl~e9De??UC=8)`g()kg!4lm_Ka6yGBjXZHPezMrF{~HHf zOktOdSsehLv?!$T-SZM)g6W-n`Ylok$B-^FVIg2<#MEp40_{z!COV(sX7L^GCB=^Z z$lsFX_rATaIUO_B$VsaBkInon$#H3lbmy}R6BD?pwltTOwkJ-WlZ`R=tlgiZn{ZYH z*JTA!r_O!3O^A`J@PE2`f7eE>33SCgaY zi%=wiMd9`G7~I2ARUetf5ATIv^!m8dlUc;#MS*%+cH$Qa(LJCaDIqeyI_J)0* zz2SdCYW!DkC~0HvWUg;z?(sKQQq;l0*5M!TJ!piO8d53T?Wi9H9et%? zU3Zy@*(*gudO0#d$wP6Q_V+8r0^7o(?Ho%}wa z>Vt+T-=ri-73I2^6uWieBj&*7Rr-Jf?O^j*wH-0=7p#G|coW|3ZwbEb-zC&|7Z93p zSlsDR?P2)_V%+rNr{npEPITUVSM)b;Pg{2&aA1dnX6)S&rMjz`kk)Y*dCy%=GkA}@ zcPla%7l{*Uj+*7F`x(?y^J&#owEp*9tfG6 zCMysqBpinl*Tia(3shDZd!oVgnYhw!6=bOaw z0&hj6d?dEv&Z+HT`=?hJeXBHerV1ZeI5AhV`Ma}=@VZq~I1qvo8mEUS&5iVCUEmqH z;*q%~;t$}?kd|f35KL4%h#W+KsP3Bdlkh$Lkcp(D#ABvU z!;!~23v=jZs&knnl~)?n_b^jyW(8MxHGJFRT= zkz6Vo<|h;pYf=QEeB-i8bV{Dmh6zVV#;HNB%F63KXItu4)}Zz$)WmvXDnTyjtYZS! zUJ}d-TBI9z-kMMUW_#1hXvO@C{{jZ{pZTZ%Rj~i?F$g)ETN(Xpwg$>-;e48qDP)+O zQl_tCf#tKaLMX_D4a@W7A)l!xgax#MG1Caa3|*7@-%QC>wcA`Tre&LM-Rp~a8o4pv zzy0{xDdc@IiinK-JS)1t&iRo3@cUpVb2#hc^&YwFrCQZ2Mdk8W7$$l2Cap|~*D8>q zVlxUF+;zncff-t=B1QGGC5&^l>=6WpN@W!zIJ{@u*Nq1?S{xd`>~J98?Mdm>tU_9W z&3U2PTfV2J#QCixv+A)hksixOu_rdJ#p1-LpOy4>AuB4ND#@m5ze=n1Aks)YJg;6d zBBk*tTFPt~c2qK^ihCrei_O}w^)vIax$ZemRPrD}dA@>kjkVvCG~takUS?W3sx4Ko z3K4Jtv|6!ifBj}({wj0H24t)-?k#A#zjRx*2SrBy#tadP27x_EjJaIK-a(0z)^W60 zKgPZomaS{Lesn}VF!~i58B}tfaICUre1VuRR&nF77y#amoxiSzr|oyKu(4gyeu49X;DCUBw~0l zIHJ>9!cl0U6>U|`bAI?u=?} zPVz$YVdctTKa&;RbU;ALlA3kJm*>=zV=bijCzI<1W&%s2_m5sRX}Nw{oE;OpvJ<+) z>aMBD@R`$l?@_JRrU9hf90*&>L*dSVO7ew342O*>^OqpY6QDExs-4bY1|u8Z0{hwA zWQAQres;M&7Rv-4nthKd< z%l{K~c2am&AhW?-o|tf6aH~>c@GRz(7kwdaZ)%W=;O(Q2}<{VgN1WX zU$Da>uzdVNaBiI4;epLW%=u=^0!IeRvR+(hD6!g`=RA`>lj+Y6IWZDdA+*Tb_POvWgNy7DDo`^ZpbNM++PRYzYnt%86|GZqS>5pNxvS0g-QhJ{^(Rb z#<6o-0+xSsmToq1obAcg42Cw}wf+`n+12h;)h4a9A-{4PP{~+W^K&*7u~dp|jGyLC zB|t=BX(SRmwzZ2QjrY$Ou9|PYXnxcn!^iJq&e3;{WxKk8GT>e5*7nybq1vpU(yKZH z;))<0MWo{mYO^_pd?UzCa^ceAzG)H-Aip3@T6pj}Y@JCyXV4nT0CF+cNjO*!`Y`QN7Ue;laF zhGxb_&Q||*r0PW0K!1juh+dR6lsMZXzJi??*txhOgTb7e<5)_`5Lv%+K=5}(BcmXJ zZCr2KowrXcynX%dPuUwlM3V3A9NRDxS3UGl(F|1?X#O~bMIuGpR2H8+{-=1|Ge8kv z0O&LhbSM(C!Phnqw~f^aM(?SugR{+01Z^V7IasJnO8qzrShBrWXspFX1|bq^2HdBG zeEk@d;32B+itYF}eK;aovHbgA``Ayp0mc8amsNJscQUsA6pH_&!3m71k?Z~ICtA+r z2TUd<5frQc{2Gom3*L>|Jz3g@Qa-O}Kte(JijIN|O7IzToeAoPB<-X2@YrtoDTA}U z_(Jy?GZ8Hr&-AgC6ice3Xi)y#gVMS+rc-*7zquYq;EqN!FTfq`_(Vg#eR9 zO9v%N`GhUkj8md{9&^t^(O!-+Dbh-%K*muLvowQ7>ptLdpHnS5vav9V!LYNwVq~u3 zJ|}M@=SJw<$nOhi&+7&?B+3T8lsVfrAGON3*?llj%QWk?&7afb72M}efG^W>NUj?B zWb+j5b{bDyyBGf4aESHErVHPIkg(8)Lm#~w{}SKXap&JGAD0W{l&6qizQm&c?~m|b zfgt~Qgzaiidbp-|K0&-D8srL5WI~Q#T9R~zmh*FHCozTmh`^P=QHUbPjIh4P8z+yB zlji_e2I}V;EqF`GfLeyMz%ue>0dpEz-f7K?9n%kpkFPs7@TZ=w)cOlG1`DK#@x zwotsms9ao)qG87TEuf&o&@{N3oMk?KGOmAs=aFC0`nZ<$g&QS*RWiLtRDg{jVe^c< zN`NeO`de-MAd9yIZ_=)msvcJ*IYOz2(CArzcHsa8%>?P}0n}_mO+GriLE}O~+3~X& zR1?9wKyH2#aqKL04r#Yw38Jkgea?8`ltV=7T0Wh1s?h0f$uMZu!lcuj#EDQ9W9;U6 z5RMRMzHvl>(M?9R(HtFwk%t9T!kp+(ST&TLdw-JjrA(sPu6G~$#_@zobg^`hwbw-vr@<3Nd9r{iyjLUE ze^}0{o#OxqVwrRLksn<{w%hidl?968!#0Vgp@Ep@jB62{t~e=C;Kgq!5O>YjaBxW) z^Fj;Z{s#m+_X>50s0o0VHDNu2E#fxclYl_qbX+pY?tyk7z<~<#?7NH9j4WGZbrQDp zSd6Ur0@G6dq5jj}Qo+e_ePj{t#Bpm96RF4=K-(8J%B7WkDwn3^YnxErgX2hJ9oOoq zgLN^jZUY?*Qwv0jG02)%oS?9L{=nHNe~grz>&-0NtyTIUWZUQ6cYz@wT4WJzLe^Z( zf2F%2-LO|yf+n1OU8YGCm`RQLnPb_Jc>A*XovUoHAv1tu<4?wnUD>9J9Rl5zZc_Lt zA1iY=v7#M?3!qv0H(vxd>MJb&!BgDKe432Kjzz6B!Gs)>=zV6Wr82N5C@?Ijd2$L- zO&QMIV2vRsbDCos`6DMYIkE^$;)<6sQYt?q->G&9@fur9KjDRc!)fSZX=tvad`UqBP-(VldpC_4PWwY14=uM=?|pn14q7Zr>*jVxo}kNFQYkz zVr9v4L$i7CEbDnN8x`QaaMC@oX?FafgVS+US>4Vge>m;lGN7|RedZjQ zCSj>8p)E=ZKZT;0I!QKmD zne+ThzS`Cv0sEz#5x&wcv{cZ+Rtv1H&0)9*eJ??K(DEVfuIR;xx-+=%R_U}m*hZ9y zZtxF$V;BX}+a{Xti2+#Y+u7PZsw%Mc<6Dg`wi&K9V+ZCp!LLLc;4Jg*!td5+OwKCs z3&S_Xxu_RBr4vsDOD7+M(qjS8T+mfC$8{NEAe+6AXeB!S>V@7Fb<&*DVTaOxCDDjpfA>YZ3)JN>C)& zANE2N>>5jAkG&gPeE-=#cjw++^7i{yH~GB3bLPyMGiT1sy?4^=(c99>6<)ix{(_R5 zsMX6adrlO`%~yRfrC?C6vhC+@y(oMgynF7e%HxNG?9O=bxBrLAz4u>j;CIqpxnXp@ z(0i5DYk$$UxNnnjEm*P(a! z4sYd=$Gls$yjHZ(D|_nUg|lLtT;1MqS;T#F}2=9GMQ{d_@r=+Lea?hD&QFC07}V%e?+)o+jKy)*HAQB993+Aj)SKED0) zp8<(u3K!=zKYV3SX0QClu3Z;>^VzQC$X>IXy1FN-vX}inPow;D`tVMB?#Z0rYd@pwOf(9QRCXHOorBSd?0MZ>KbgEp$WJMAm-Ke+gDsh9t~uz9Z4XQsN0-1cxw zoq{PT+Ya`0z8ZYE<&f<3yuZi&kw0p;cepzHhR5I)F`Jd1(zv!Uss$Ye?QI_QTk1N^ zuqrthzia)fYR=3#dvxiiw`}aZH2TyzpZhycWp&9b3agg+E?`Q`?E__l@leD+(t2EL zb7pMrp|QCKRUW@*)#^~xZsXKV0ezEN=9F&9+H|_drhrj#eO_YgwNhngr;m_dibPIG zuk}BSf7BGA%BKI+7d;3fKl+6w1a*jv4)65$Q4wy=H(gpoL^AXFqhI_t9{*@KzRu)d z8$Y?acKBt!v|mtkWO!J7LdSmL7J^7bUN~mDg8YrMH^vVU{xg1$(xRw`49_H0q?c;? zSLAPO)EPhIn$!4gxJHtxk;v6jg&a?f5V*X)>1q+V+zENN;qp||IUm8R>rhvBF<-6H zzdEG?mku>ws)6EKDRpU<^2(=XLHJ?nA(39^Z*ZwXl}24by2knnT2J!IBXUK$I3rnw z=i&)o+R)6=+n0N3vO*;hydG(|@k6e!f;uiGS}MnL5anW-Aj9CQ1Vb1-j12p}(bObb zf87Y`I!UGy4`3u~C_BR4DaZnTNHW%BX$qB8BJ6P=Q@k_$ybN)spjBE?pt^WK2C=Nk z`>k4sKGw=K!dlU=&jnz5*A|AkQkrG+!!+p<`v&`jq8)UA=8#j7qx^;sajH5PR6OQ0 zE2-8hl`^SBCEWN%Cam7+22fmw6l*~Nrm+Dd4OL@dRcO+G*`y4y1_cv4jiO0!*d&!? zj27WUSSQ;3yP-^OF}vA3hWIHAa-+Y&(^F#%=W6nDFTwaio*!(n$RyU~8DhCqsg>zB+$zA@&SFvT z+Y{<3dPaK5UrZZm~!bHWmotdWkDkP4OI z&Y~`c8Wop1;?Rbv3qWj&a+fDXy^g2i+M}|WRad73Y{r~)0Ykhs#oP&Jv|+|cloCvP zDMVK`@cg?1IeF_}Z2&zeU_OS24Um9BCCDno8q@YIx7mj>0|(xKG398wJ4F;B*ImWl3WI< z@jgi&%jIW}PKt(J9brAoWJO3X&@%?TxFqDe;bN&wl47;}&28`M(-VE$!h<8=dX~AB zaA&r$<%TFSlwy@st&r<~CG&R6qO-ooiK+=OBdJ}RKDoMg(4MQV1xxhRsw8Y{%3pZ5 z9FGxR9nMEX71#*iLJiM0CoU9}B32@?WJ7;@yRF6vXnF%+yy?#T$<=l2<^VKAjQNt! znRbRc4_Cvu(=YqinX;?~>Nu3xw8Rz`nFQRO=-~4iT#*;o@X*3Xh4q zmd?4n2x{)Z*Uh9Z{?66S?qkdCuTp3v$r|xU84vDU??k@^xV#u&IK26jtCIxU;`rl6 zz@B5z{Ln#uSi4weBBEB;AU20Ilx@1srDM9rB3kr7l%|`wCW6fpbjm{6`j@BButW?_ zcXI=YYTZ9)ojWb8bmq`?@00Ot37A4r088Ndh>wZkb2QhB>2^ z?GO-Y-9AW6FymT07~Vm=&9Fzv#w}ee{<{{MdH`R#W8=nga7L7H$3A;`wQ?p{8kmkA z3(GS(yhLOj2;OYH&OV)bEC7@IV5B~DhxQw9;uz}&2G8iP(x=jF+ECZj;qwst>tMDd zx6ztDxw=D>IW!@Ik%XwkcYb~xJnd&FMjLFwEJIplt}zh%hf37RDydS#Y$)Lk*-%wB z2L|)SNT3J37h(!Y(>+e|FF$#7ZYj9kio8UC3dDpon2)8SmV-o-+yT z+Y6$BpFlAu0BOZGF6F@NHZrfC-1{DkJ`p_HD;h56un@N~-)6BJc)DRuuj}9pvElgs zz~KlQN>_8fYz8>__`2b1`sC{R|H$FQOJu2r_KlRv)7WZ>XWUJC1&5a-czc*w_&ZnE z{3k9>13yi(Yp}A7?G;7vh z;4gFlJt%+N!sZ0Yvoz_v9eT8Z|5j%-^Z@jt2U1=Un-?}lE0(c4U`lGOR!gB=7IGxo zJKk?+!y@HaZOfA-eEkq|v&x&NAT~f8qP^qdE;cb*!N|DDDF5)gNq^9b69x?SuCYEd9*l9(lK3= zA>&4}O^16bwnDF)=utZR4?o0VnaeRjDwQ}Z68SWHQASJ`81ZL~{Rl+A!hy*RT&7R1 zF8C-PmX0QgVwskw{a%auO%8z0oncF4(dJLCF21w^aN!oXOBO~g2Kpkt&CMc6+`4g` zBZSYsWT`*-_?pC9xLgZkG^Q|xCkz($L~6fyFc*HLtc5hg zY!U)`bHf^Bvq1H*{2zgpFcFd4`_Sn`*sK6U%#9Lp`L3V{+oluTmde{#HAb`d+L%xE zz#12sCTE(DFl4LR58n+{nu0;^)9rX@&4@!$k*&oHmtjwviW&uKC6 zJi4$JPsCDB%6$t@0sR2e2_4ApKd%6ut^2P3+<4gw@S2!PJ5ajUe(f-vjb80@^k7#E z(%V>D1yShv?|9KxTUH_F-762{&#Z8%j2WYdnSOoLbA>DV@io4|l(;t7M70!Dsa{}< z3(HKFX;UO7sqE%8cy->5EpJto3d)O=J0S1(yDqUl=qV@kR9Cuv)hZ^B-JYBYiKTDB zodWxHrub`XR)Eh~8j#+}`{}x<6UaC5^{41hKH{a5{l*Z5R+fVNC{7|yA*&+vjf7{t zM&ty$W{o@w8B1U(y4salTTo6vaVEj@55tdF-wG#BC|Hje+Lmg;w{;yOv+bU>Jb%^# zWbE@0!@Mc3O9Mx^Z0aOQ(uDP(ky)D_*DpS{NBs=7A*}KE;!(&Z*)&fWVIpeEeJF+R zwr~_3hqJ=u`3*?%`3n}LKXq3VdPkryB?x}B-(Jd#L7=AD#G}>4eK=q>E4mg*HqsG5ntPgxa3z+5k71~9jsn!W(SCv2N6Ezjv#5= z<$wx`U<>%rXW5K-2yMO5-}I0k(uZGw@o+?#JXxktON`G2;F_mfaZX<(;G*up(i|s$DLAFh1q2SkpGJ9r- zeY*LToAJ^#xmd&8^&wL3-w1HRa20)y4yM<8oBLKoit)S#+fXqI|IKwE6^h0r zK-fS^kZ1pYCdiZxM3B7;yGcDBr9ey$*5tHfen0#liek5E#rf`wzDCekf~84E z%6tjwjtgS5KN{xM|1U7dz~$%+w6(%PHqW?K?#V6M334ltLny5!Sisur2}|uvCNn%d z%M>W!XY8gZSO2+xGP=Gu`XrGOI2o<`e*_vHmSm2;Je4+meE(+h;KPt#hSF6Kr9&nP zTK|U*>?X^Tuk`hVxV{ia$KW3)RYaUQHu6kcE^|Zc#$o`6Ac^ivHS1qEKI?F^%oZfC zlr|jQz*ZK|Q43M>k)l0#`N$`5oFv4; zK&o~Av;K(`hb%8SmCQX-c%;FZ;=2@(|#3 zhW&0n8?IJsGxRIeM5zJ?(Qxubc**)C?RW$!%mu;~+EK>3vxq6dqIrl4k9k%}#cW?* z;$M0fiGwHx(=WaBxVD@L4$H*uo}=`Tf2F_q6ZBvhhptCV{E?6B)2e)Jis*4 zh5~x#GJy#=N-woYe!B5|*=WF5LwkC$SZ|jwJQ^E_g6Ibizm|>ww#>%t z2X+gi^~Z1wQ@@~u9cMZn+z1JeF}CQX^tckHgbdS(aRe`%10XdG-U9}tUx)T~J(U6h zOV9*wYLsdB3kwLtQ9HhLsppYXZJ)#DkI6t&eM`&4$!nj9m_n~HVXI)oT{zD~lL zhT$Y;Q$(sm6xd+E!Ci7t5;v7d<;)FrVvhz_N|(Kb=C5tWY1b3NQsVUoCX9TZT<3<% zG|$nnwlms6N0|xbcIeuXYP|r#-h9$!O>qzOQ9e-maKwnE~B;6FsJA5tIi zASg~pU%xBD@OXtv17{JWCtN%aZ+W;c9R4J}ba=daPf!HO6*1}Y>` z8CpQkO2Z!rqIsVd#+|jdIk)&a5AlWE8%Wnndp;JFV3?G8SWSC;p=`yi0VzMgZ3@x3 z{i#sq|D>WKVr2Tm_n;HyUx{fB4G^N@sU}-6o=+3Shl)h<9`! zi9XmD(9Hn@c8{GoeH^^tgpC(CRjSO2+ol_g40F4+1fF8aoV^XzsmK!Dai*V+s-;d9 zlfbso*)vUly9OIwg^lQ_I=QYO-!Q?)=?6IfcG{kSBW8rb!{U&7(vfcqVisd!GZ~1+ zvvuEDZPh~!bSL+X(D1Ovj>CBj|33MbFB75lAZKpusMgeBI9u_p!@0I0{?g{IywuWZ$emq6}Tn55Z^Vo1ZI5JuM0M;- zOH*>mkYx99e`J&!KHT2mp4UM9@L+r}MhPo#XTVzfkP)Og1%eol7& zH=r?e;;}c{Q7Kq%>kk_8jQDi}hxwJEV;*7N{FF*Odd62kgdsuWk&zTJVS9ZTDh9JB zy-pjBLvj{lnU_-fIoY@b;bE4q`>>N#3R=NDG;_-!Yo_5IS$(2vT%M6_9(Jep_o;PD z0zD@ovU*Uqs6W&mlTBRJ{av$1&@v38_Y;cv%Wwu!KTfUcmwbxKUyts7!PsfR6!rlg zlWQ@kvGF!oLqM<+6mPnlnca-X!9Aac!q93g5WFaPo~aJVV?$5*j0&%gM$`bUJVUvf$K_X>Z-71 z8uKOIW0c>oEQy&2FM9}|XhYGeC~av8k_;@b#Hy@th23bMkq=D15 zqHyog+13rEaJDdVn)^gjY+g1Xh4|7Px_K%CX1wi%o#PPRowt)_XCmV~i*0C{H+~u~ zPvA`AAzAChW#Ihmy5XQ;bbV@K%I~-7W>3KDP|*JBBaasZJ}IEb2e~cP(}h^SJEAk&tS3rj3-iHi z0DI7N+eF-Mj~%TbOGh=1z@j8CunHo!@d#3FnSnNJ z$CtKxF(y~xHjpANe+v#@bk`k&_UkYnX!5tkj*!V63v9bjD4O^EDfDL=!V2A=E4DjE zW^;EYX)^x}ZR3#+)BEH*cQLrwpTz8l1OC|m=ww_&v%#8#pnNh<{(-rTt`?0fu?NO- zMmXkV=+KbAUrxFaO04nV6!svAzPxjAD zve=TEt$Gx&42`&3n>)TT&s9JY8~*Du-*HVy9K)ABuqD1^4<4^kD3#dF$r^xX-Uviu zl6GabyW57GWvz!Eaiz?4;@pd117W@&FRP2%rRb#%GrWdfCSyV% z%opc8cdOAoKegr-6V85QkIC+#Z3U6uxJ5z~0Zr*-XJ#`N(eNZ1{h#_hJ_(QW{`EZI ztQ!)jni$6PQpMSYm&;a>`yy%6P{`FOU0Ep3i*~JEE6uI*a9`SMArHfeaqbjidWK!N z=|Q&EukhufxvyWfpdYny2yOi`RxX_TPE8AZoVUYpwz0Xd2D6}Rb>>Gqy!Fq0k&^|# zvnxN|5D)~9TybA$Vgc9bVGqoCy$RRa+*gQL&@Tnh_DaB9F literal 0 HcmV?d00001 diff --git a/third-party/openflow-codec/pom.xml b/third-party/openflow-codec/pom.xml new file mode 100644 index 00000000..e29d6c05 --- /dev/null +++ b/third-party/openflow-codec/pom.xml @@ -0,0 +1,150 @@ + + 4.0.0 + + org.opendaylight.openflow + openflow-protocol-parent + 0.1-SNAPSHOT + ../../ + + + + org.opendaylight.openflowjava.thirdparty + org.openflow.codec + 0.0.1-SNAPSHOT + bundle + OpenFlow 1.3 Protocol Driver + A Java implementation of the OpenFlow v1.3 protocol driver + + + + + + David Erickson + daviderickson@cs.stanford.edu + + + Rob Sherwood + rob.sherwood@stanford.edu + + + Yugandhar Sarraju + ysarraju@in.ibm.com + + + Anil Gujele + angujele@in.ibm.com + + + Anil Vishnoi + avishnoi@in.ibm.com + + + + + UTF-8 + + + + + + jenkins + + + env.BUILD_NUMBER + + + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.5.1 + + + xml + + + + + package + + cobertura + + + + + + + + + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + org.openflow.example;version="1.3.1"; + uses:="org.openflow.example.cli, + org.openflow.protocol, + org.openflow.io, + org.openflow.protocol.factory", + org.openflow.io;version="1.3.1"; + uses:="org.openflow.protocol, + org.openflow.protocol.factory", + org.openflow.protocol;version="1.3.1"; + uses:="org.openflow.protocol.statistics, + org.openflow.protocol, + org.openflow.protocol.factory", + org.openflow.protocol.action;version="1.3.1"; + uses:="org.openflow.protocol", + org.openflow.protocol.factory;version="1.3.1"; + uses:="org.openflow.protocol.statistics, + org.openflow.protocol, + org.openflow.protocol.action, + org.openflow.protocol.queue", + org.openflow.protocol.queue;version="1.3.1"; + uses:="org.openflow.protocol, + org.openflow.protocol.factory", + org.openflow.protocol.statistics;version="1.3.1"; + uses:="org.openflow.protocol, + org.openflow.protocol.factory", + org.openflow.util;version="1.3.1" + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + + + junit + junit + 4.8.1 + test + + + \ No newline at end of file diff --git a/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectListener.java b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectListener.java new file mode 100644 index 00000000..6411b5ae --- /dev/null +++ b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectListener.java @@ -0,0 +1,24 @@ +/** + * + */ +package org.openflow.codec.example; + +import java.io.IOException; +import java.nio.channels.SelectionKey; + +/** + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ +public interface SelectListener { + /** + * Tell the select listener that an event took place on the passed object + * + * @param key + * the key used on the select + * @param arg + * some parameter passed by the caller when registering + * @throws IOException + */ + void handleEvent(SelectionKey key, Object arg) throws IOException; +} diff --git a/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectLoop.java b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectLoop.java new file mode 100644 index 00000000..0041d14b --- /dev/null +++ b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SelectLoop.java @@ -0,0 +1,160 @@ +package org.openflow.codec.example; + +import java.io.IOException; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +/*** + * Dirt simple SelectLoop for simple java controller + */ + +public class SelectLoop { + protected SelectListener callback; + protected boolean dontStop; + protected Object registrationLock; + protected int registrationRequests = 0; + protected Queue registrationQueue; + protected Selector selector; + protected long timeout; + + public SelectLoop(SelectListener cb) throws IOException { + callback = cb; + dontStop = true; + selector = SelectorProvider.provider().openSelector(); + registrationLock = new Object(); + registrationQueue = new ConcurrentLinkedQueue(); + timeout = 0; + } + + /** + * Initializes this SelectLoop + * + * @param cb + * the callback to call when select returns + * @param timeout + * the timeout value in milliseconds that select will be called + * with + * @throws IOException + */ + public SelectLoop(SelectListener cb, long timeout) throws IOException { + callback = cb; + dontStop = true; + selector = SelectorProvider.provider().openSelector(); + registrationLock = new Object(); + registrationQueue = new ConcurrentLinkedQueue(); + this.timeout = timeout; + } + + public void register(SelectableChannel ch, int ops, Object arg) throws ClosedChannelException { + registrationQueue.add(new Object[] { ch, ops, arg }); + } + + /** + * Registers the supplied SelectableChannel with this SelectLoop. Note this + * method blocks until registration proceeds. It is advised that SelectLoop + * is intialized with a timeout value when using this method. + * + * @param ch + * the channel + * @param ops + * interest ops + * @param arg + * argument that will be returned with the SelectListener + * @return + * @throws ClosedChannelException + */ + public synchronized SelectionKey registerBlocking(SelectableChannel ch, int ops, Object arg) + throws ClosedChannelException { + synchronized (registrationLock) { + registrationRequests++; + } + selector.wakeup(); + SelectionKey key = ch.register(selector, ops, arg); + synchronized (registrationLock) { + registrationRequests--; + registrationLock.notifyAll(); + } + return key; + } + + /**** + * Main top-level IO loop this dispatches all IO events and timer events + * together I believe this is fairly efficient + */ + public void doLoop() throws IOException { + int nEvents; + processRegistrationQueue(); + + while (dontStop) { + nEvents = selector.select(timeout); + if (nEvents > 0) { + for (Iterator i = selector.selectedKeys().iterator(); i.hasNext();) { + SelectionKey sk = i.next(); + i.remove(); + + if (!sk.isValid()) + continue; + + Object arg = sk.attachment(); + callback.handleEvent(sk, arg); + } + } + + if (this.registrationQueue.size() > 0) + processRegistrationQueue(); + + if (registrationRequests > 0) { + synchronized (registrationLock) { + while (registrationRequests > 0) { + try { + registrationLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + } + + protected void processRegistrationQueue() { + // add any elements in queue + for (Iterator it = registrationQueue.iterator(); it.hasNext();) { + Object[] args = it.next(); + SelectableChannel ch = (SelectableChannel) args[0]; + try { + ch.register(selector, (Integer) args[1], args[2]); + } catch (CancelledKeyException cke) { + continue; + } catch (ClosedChannelException e) { + } + it.remove(); + } + } + + /** + * Force this select loop to return immediately and re-enter select, useful + * for example if a new item has been added to the select loop while it was + * already blocked. + */ + public void wakeup() { + if (selector != null) { + selector.wakeup(); + } + } + + /** + * Shuts down this select loop, may return before it has fully shutdown + */ + public void shutdown() { + this.dontStop = false; + wakeup(); + } +} diff --git a/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SimpleController.java b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SimpleController.java new file mode 100644 index 00000000..01420d12 --- /dev/null +++ b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/SimpleController.java @@ -0,0 +1,318 @@ +/** + * + */ +package org.openflow.codec.example; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.openflow.codec.example.cli.Options; +import org.openflow.codec.example.cli.ParseException; +import org.openflow.codec.example.cli.SimpleCLI; +import org.openflow.codec.io.OFMessageAsyncStream; +import org.openflow.codec.protocol.OFPEchoReply; +import org.openflow.codec.protocol.OFPFlowMod; +import org.openflow.codec.protocol.OFPFlowModCommand; +import org.openflow.codec.protocol.OFPMatch; +import org.openflow.codec.protocol.OFPMessage; +import org.openflow.codec.protocol.OFPPacketIn; +import org.openflow.codec.protocol.OFPPacketOut; +import org.openflow.codec.protocol.OFPPortNo; +import org.openflow.codec.protocol.OFPType; +import org.openflow.codec.protocol.action.OFPAction; +import org.openflow.codec.protocol.action.OFPActionOutput; +import org.openflow.codec.protocol.factory.OFPBasicFactoryImpl; +import org.openflow.codec.protocol.instruction.OFPInstruction; +import org.openflow.codec.protocol.instruction.OFPInstructionActions; +import org.openflow.codec.protocol.instruction.OFPInstructionApplyActions; +import org.openflow.codec.util.LRULinkedHashMap; +import org.openflow.codec.util.U16; + +/** + * @author Rob Sherwood (rob.sherwood@stanford.edu), David Erickson + * (daviderickson@cs.stanford.edu) + * + */ +public class SimpleController implements SelectListener { + protected ExecutorService es; + protected OFPBasicFactoryImpl factory; + protected SelectLoop listenSelectLoop; + protected ServerSocketChannel listenSock; + protected List switchSelectLoops; + protected Map switchSockets; + protected Integer threadCount; + protected int port; + + protected class OFSwitch { + protected SocketChannel sock; + protected OFMessageAsyncStream stream; + protected Map macTable = new LRULinkedHashMap(64001, 64000); + + public OFSwitch(SocketChannel sock, OFMessageAsyncStream stream) { + this.sock = sock; + this.stream = stream; + } + + public void handlePacketIn(OFPPacketIn pi) { + // Build the Match + OFPMatch match = new OFPMatch(); + // match.loadFromPacket(pi.getPacketData(), pi.getInPort()); + // byte[] dlDst = match.getDataLayerDestination(); + // Integer dlDstKey = Arrays.hashCode(dlDst); + // byte[] dlSrc = match.getDataLayerSource(); + // Integer dlSrcKey = Arrays.hashCode(dlSrc); + int bufferId = pi.getBufferId(); + + // if the src is not multicast, learn it + // if ((dlSrc[0] & 0x1) == 0) { + // if (!macTable.containsKey(dlSrcKey) || + // !macTable.get(dlSrcKey).equals(pi.getInPort())) { + // macTable.put(dlSrcKey, pi.getInPort()); + // } + // } + // + Short outPort = null; + // // if the destination is not multicast, look it up + // if ((dlDst[0] & 0x1) == 0) { + // outPort = macTable.get(dlDstKey); + // } + + // push a flow mod if we know where the packet should be going + if (outPort != null) { + OFPFlowMod fm = (OFPFlowMod) factory.getMessage(OFPType.FLOW_MOD); + fm.setBufferId(bufferId); + fm.setCommand(OFPFlowModCommand.OFPFC_ADD); + fm.setCookie(0); + fm.setFlags((short) 0); + fm.setHardTimeout((short) 0); + fm.setIdleTimeout((short) 5); + // match.setInputPort(pi.getInPort()); + // match.setWildcards(0); + fm.setMatch(match); + fm.setOutPort(OFPPortNo.OFPP_ANY.getValue()); + fm.setPriority((short) 0); + OFPActionOutput action = new OFPActionOutput(); + action.setMaxLength((short) 0); + action.setPort(outPort); + List actions = new ArrayList(); + actions.add(action); + OFPInstructionApplyActions instructions = new OFPInstructionApplyActions(); + instructions.setActions(actions); + List instrList = new ArrayList(); + instrList.add(instructions); + fm.setInstructions(instrList); + fm.setLength(U16.t(OFPFlowMod.MINIMUM_LENGTH + instructions.getLength())); + try { + stream.write(fm); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // Send a packet out + if (outPort == null || pi.getBufferId() == 0xffffffff) { + OFPPacketOut po = new OFPPacketOut(); + po.setBufferId(bufferId); + // po.setInPort(pi.getInPort()); + + // set actions + OFPActionOutput action = new OFPActionOutput(); + action.setMaxLength((short) 0); + action.setPort((short) ((outPort == null) ? OFPPortNo.OFPP_FLOOD.getValue() : outPort)); + List actions = new ArrayList(); + actions.add(action); + po.setActions(actions); + po.setActionsLength((short) OFPActionOutput.MINIMUM_LENGTH); + + // set data if needed + if (bufferId == 0xffffffff) { + byte[] packetData = pi.getPacketData(); + po.setLength(U16.t(OFPPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length)); + po.setPacketData(packetData); + } else { + po.setLength(U16.t(OFPPacketOut.MINIMUM_LENGTH + po.getActionsLength())); + } + try { + stream.write(po); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public String toString() { + InetAddress remote = sock.socket().getInetAddress(); + return remote.getHostAddress() + ":" + sock.socket().getPort(); + } + + public OFMessageAsyncStream getStream() { + return stream; + } + } + + public SimpleController(int port) throws IOException { + listenSock = ServerSocketChannel.open(); + listenSock.configureBlocking(false); + listenSock.socket().bind(new java.net.InetSocketAddress(port)); + listenSock.socket().setReuseAddress(true); + this.port = port; + switchSelectLoops = new ArrayList(); + switchSockets = new ConcurrentHashMap(); + threadCount = 1; + listenSelectLoop = new SelectLoop(this); + // register this connection for accepting + listenSelectLoop.register(listenSock, SelectionKey.OP_ACCEPT, listenSock); + + this.factory = new OFPBasicFactoryImpl(); + } + + @Override + public void handleEvent(SelectionKey key, Object arg) throws IOException { + if (arg instanceof ServerSocketChannel) + handleListenEvent(key, (ServerSocketChannel) arg); + else + handleSwitchEvent(key, (SocketChannel) arg); + } + + protected void handleListenEvent(SelectionKey key, ServerSocketChannel ssc) throws IOException { + SocketChannel sock = listenSock.accept(); + OFMessageAsyncStream stream = new OFMessageAsyncStream(sock, factory); + switchSockets.put(sock, new OFSwitch(sock, stream)); + System.err.println("Got new connection from " + switchSockets.get(sock)); + List l = new ArrayList(); + l.add(factory.getMessage(OFPType.HELLO)); + l.add(factory.getMessage(OFPType.FEATURES_REQUEST)); + stream.write(l); + + int ops = SelectionKey.OP_READ; + if (stream.needsFlush()) + ops |= SelectionKey.OP_WRITE; + + // hash this switch into a thread + SelectLoop sl = switchSelectLoops.get(sock.hashCode() % switchSelectLoops.size()); + sl.register(sock, ops, sock); + // force select to return and re-enter using the new set of keys + sl.wakeup(); + } + + protected void handleSwitchEvent(SelectionKey key, SocketChannel sock) { + OFSwitch sw = switchSockets.get(sock); + OFMessageAsyncStream stream = sw.getStream(); + try { + if (key.isReadable()) { + List msgs = stream.read(); + if (msgs == null) { + key.cancel(); + switchSockets.remove(sock); + return; + } + + for (OFPMessage m : msgs) { + switch (m.getType()) { + case PACKET_IN: + sw.handlePacketIn((OFPPacketIn) m); + break; + case HELLO: + System.err.println("GOT HELLO from " + sw); + break; + case ECHO_REQUEST: + OFPEchoReply reply = (OFPEchoReply) stream.getMessageFactory().getMessage(OFPType.ECHO_REPLY); + reply.setXid(m.getXid()); + stream.write(reply); + break; + default: + System.err.println("Unhandled OF message: " + m.getType() + " from " + + sock.socket().getInetAddress()); + } + } + } + if (key.isWritable()) { + stream.flush(); + } + + /** + * Only register for interest in R OR W, not both, causes stream + * deadlock after some period of time + */ + if (stream.needsFlush()) + key.interestOps(SelectionKey.OP_WRITE); + else + key.interestOps(SelectionKey.OP_READ); + } catch (IOException e) { + // if we have an exception, disconnect the switch + key.cancel(); + switchSockets.remove(sock); + } + } + + public void run() throws IOException { + System.err.println("Starting " + this.getClass().getCanonicalName() + " on port " + this.port + " with " + + this.threadCount + " threads"); + // Static number of threads equal to processor cores + es = Executors.newFixedThreadPool(threadCount); + + // Launch one select loop per threadCount and start running + for (int i = 0; i < threadCount; ++i) { + final SelectLoop sl = new SelectLoop(this); + switchSelectLoops.add(sl); + es.execute(new Runnable() { + @Override + public void run() { + try { + sl.doLoop(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + // Start the listen loop + listenSelectLoop.doLoop(); + } + + public static void main(String[] args) throws IOException { + SimpleCLI cmd = parseArgs(args); + int port = Integer.valueOf(cmd.getOptionValue("p")); + SimpleController sc = new SimpleController(port); + sc.threadCount = Integer.valueOf(cmd.getOptionValue("t")); + sc.run(); + } + + public static SimpleCLI parseArgs(String[] args) { + Options options = new Options(); + options.addOption("h", "help", "print help"); + // unused? + // options.addOption("n", true, "the number of packets to send"); + options.addOption("p", "port", 6633, "the port to listen on"); + options.addOption("t", "threads", 1, "the number of threads to run"); + try { + SimpleCLI cmd = SimpleCLI.parse(options, args); + if (cmd.hasOption("h")) { + printUsage(options); + System.exit(0); + } + return cmd; + } catch (ParseException e) { + System.err.println(e); + printUsage(options); + } + + System.exit(-1); + return null; + } + + public static void printUsage(Options options) { + SimpleCLI.printHelp("Usage: " + SimpleController.class.getCanonicalName() + " [options]", options); + } +} diff --git a/third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Option.java b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Option.java new file mode 100644 index 00000000..27123727 --- /dev/null +++ b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Option.java @@ -0,0 +1,39 @@ +package org.openflow.codec.example.cli; + +public class Option { + String shortOpt; + String longOpt; + Object defaultVal; + String val; // current value of this option, string form + boolean specified; // was this option found in the cmdline? + String comment; + + /** + * Option information storrage + * + * @param shortOpt + * Short name for the option, e.g., "-p" + * @param longOpt + * Long name for option, e.g., "--port" + * @param defaultVal + * default value: "6633" or null if no default value + * @param comment + * String to print to explain this option, e.g., a help message + */ + public Option(String shortOpt, String longOpt, Object defaultVal, String comment) { + super(); + this.shortOpt = shortOpt; + this.longOpt = longOpt; + this.defaultVal = defaultVal; + this.comment = comment; + this.specified = false; + } + + public Option(String shortOpt, String longOpt, String comment) { + this(shortOpt, longOpt, null, comment); + } + + public boolean needsArg() { + return this.defaultVal != null; + } +} diff --git a/third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Options.java b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Options.java new file mode 100644 index 00000000..a83f51cf --- /dev/null +++ b/third-party/openflow-codec/src/main/java/org/openflow/codec/example/cli/Options.java @@ -0,0 +1,66 @@ +package org.openflow.codec.example.cli; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Very basic CLI options listing + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ + +public class Options { + Map shortOptionsMap; + Map longOptionsMap; + + public Options() { + this.shortOptionsMap = new HashMap(); + this.longOptionsMap = new HashMap(); + } + + public static Options make(Option opts[]) { + Options options = new Options(); + for (int i = 0; i < opts.length; i++) + options.addOption(opts[i]); + return options; + } + + private void addOption(Option option) { + if (option.shortOpt != null) + this.shortOptionsMap.put(option.shortOpt, option); + if (option.longOpt != null) + this.longOptionsMap.put(option.longOpt, option); + } + + protected void addOption(String shortName, String longName, Object o, String comment) { + Option option = new Option(shortName, longName, o, comment); + addOption(option); + } + + public void addOption(String shortName, String longName, boolean b, String comment) { + this.addOption(shortName, longName, Boolean.valueOf(b), comment); + } + + public void addOption(String shortName, String longName, int i, String comment) { + this.addOption(shortName, longName, Integer.valueOf(i), comment); + } + + public Option getOption(String shortName) { + return this.shortOptionsMap.get(shortName); + } + + public Option getOptionByLongName(String longName) { + return this.longOptionsMap.get(longName); + } + + public Collection
i}gX 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 CNzF?D literal 0 HcmV?d00001 diff --git a/third-party/openflowj_netty/lib/junit-4.8.1.jar b/third-party/openflowj_netty/lib/junit-4.8.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..524cd65ce5fa7bbbf03e6257c6cfe4b07a65cf8f GIT binary patch literal 237047 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
Yj}{=6(Gxg?3LC331(V69bELkqTIairs4apoLD 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 literal 0 HcmV?d00001 diff --git a/third-party/openflowj_netty/pom.xml b/third-party/openflowj_netty/pom.xml new file mode 100644 index 00000000..e1100a5f --- /dev/null +++ b/third-party/openflowj_netty/pom.xml @@ -0,0 +1,120 @@ + + 4.0.0 + + + org.opendaylight.openflow + openflow-protocol-parent + 0.1-SNAPSHOT + ../../ + + org.opendaylight.openflowjava.thirdparty + org.openflow.openflowj_netty + 1.0.2-SNAPSHOT + OpenFlow Java + A Java implemention of the OpenFlow v1.0 protocol + + + + + David Erickson + daviderickson@cs.stanford.edu + + + Rob Sherwood + rob.sherwood@stanford.edu + + + bundle + http://www.openflow.org + + + The OpenFlow License + http://www.openflowswitch.org/wp/legal/ + repo + + + + scm:git://gitosis.stanford.edu:openflowj.git + https://openflow.stanford.edu/fisheye/browse/OpenFlowJ + + + UTF-8 + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.1 + + + sign-artifacts + verify + + sign + + + + + forked-path + + + + + + + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + org.jboss.netty.* + + + org.openflow.* + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + + + junit + junit + 4.8.1 + test + + + org.jboss.netty + netty + 3.2.6.Final + + + diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/Instantiable.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/Instantiable.java new file mode 100644 index 00000000..1358ba75 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/Instantiable.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface Instantiable { + + /** + * Create a new instance of a given subclass. + * @return the new instance. + */ + public E instantiate(); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierReply.java new file mode 100644 index 00000000..a79a15f0 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierReply.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an OFPT_BARRIER_REPLY message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFBarrierReply extends OFMessage { + public OFBarrierReply() { + super(); + this.type = OFType.BARRIER_REPLY; + this.length = U16.t(OFMessage.MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierRequest.java new file mode 100644 index 00000000..99921863 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFBarrierRequest.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an OFPT_BARRIER_REQUEST message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFBarrierRequest extends OFMessage { + public OFBarrierRequest() { + super(); + this.type = OFType.BARRIER_REQUEST; + this.length = U16.t(OFMessage.MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoReply.java new file mode 100644 index 00000000..3e282a74 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoReply.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an ofp_echo_reply message + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ + +public class OFEchoReply extends OFEchoRequest { + public static int MINIMUM_LENGTH = 8; + + public OFEchoReply() { + super(); + this.type = OFType.ECHO_REPLY; + this.length = U16.t(MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoRequest.java new file mode 100644 index 00000000..295a3972 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFEchoRequest.java @@ -0,0 +1,101 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_echo_request message + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ + +public class OFEchoRequest extends OFMessage { + public static int MINIMUM_LENGTH = 8; + byte[] payload; + + public OFEchoRequest() { + super(); + this.type = OFType.ECHO_REQUEST; + this.length = U16.t(MINIMUM_LENGTH); + } + + @Override + public void readFrom(ChannelBuffer bb) { + super.readFrom(bb); + int datalen = this.getLengthU() - MINIMUM_LENGTH; + if (datalen > 0) { + this.payload = new byte[datalen]; + bb.readBytes(payload); + } + } + + /** + * @return the payload + */ + public byte[] getPayload() { + return payload; + } + + /** + * @param payload + * the payload to set + */ + public void setPayload(byte[] payload) { + this.payload = payload; + } + + @Override + public void writeTo(ChannelBuffer bb) { + super.writeTo(bb); + if (payload != null) + bb.writeBytes(payload); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(payload); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + OFEchoRequest other = (OFEchoRequest) obj; + if (!Arrays.equals(payload, other.payload)) + return false; + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFError.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFError.java new file mode 100644 index 00000000..df7b2360 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFError.java @@ -0,0 +1,325 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.Arrays; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.protocol.factory.OFMessageFactoryAware; +import org.openflow.util.U16; + +/** + * Represents an ofp_error_msg + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ +public class OFError extends OFMessage implements OFMessageFactoryAware { + public static int MINIMUM_LENGTH = 12; + + public enum OFErrorType { + // OFPET_VENDOR_ERROR is an extension that was added in Open vSwitch and isn't + // in the OF 1.0 spec, but it was easier to add it here instead of adding + // generic support for extensible vendor-defined error messages. + // It uses the random value 0xb0c2 to avoid conflicts with other possible new + // error types. Support for vendor-defined extended errors has been standardized + // in the OF 1.2 spec, so this workaround is only needed for 1.0. + OFPET_HELLO_FAILED, OFPET_BAD_REQUEST, OFPET_BAD_ACTION, OFPET_FLOW_MOD_FAILED, OFPET_PORT_MOD_FAILED, OFPET_QUEUE_OP_FAILED, OFPET_VENDOR_ERROR((short)0xb0c2); + + protected short value; + + private OFErrorType() { + this.value = (short) this.ordinal(); + } + + private OFErrorType(short value) { + this.value = value; + } + + public short getValue() { + return value; + } + } + + public enum OFHelloFailedCode { + OFPHFC_INCOMPATIBLE, OFPHFC_EPERM + } + + public enum OFBadRequestCode { + OFPBRC_BAD_VERSION, OFPBRC_BAD_TYPE, OFPBRC_BAD_STAT, OFPBRC_BAD_VENDOR, OFPBRC_BAD_SUBTYPE, OFPBRC_EPERM, OFPBRC_BAD_LEN, OFPBRC_BUFFER_EMPTY, OFPBRC_BUFFER_UNKNOWN + } + + public enum OFBadActionCode { + OFPBAC_BAD_TYPE, OFPBAC_BAD_LEN, OFPBAC_BAD_VENDOR, OFPBAC_BAD_VENDOR_TYPE, OFPBAC_BAD_OUT_PORT, OFPBAC_BAD_ARGUMENT, OFPBAC_EPERM, OFPBAC_TOO_MANY, OFPBAC_BAD_QUEUE + } + + public enum OFFlowModFailedCode { + OFPFMFC_ALL_TABLES_FULL, OFPFMFC_OVERLAP, OFPFMFC_EPERM, OFPFMFC_BAD_EMERG_TIMEOUT, OFPFMFC_BAD_COMMAND, OFPFMFC_UNSUPPORTED + } + + public enum OFPortModFailedCode { + OFPPMFC_BAD_PORT, OFPPMFC_BAD_HW_ADDR + } + + public enum OFQueueOpFailedCode { + OFPQOFC_BAD_PORT, OFPQOFC_BAD_QUEUE, OFPQOFC_EPERM + } + + protected short errorType; + protected short errorCode; + protected int vendor; + protected int vendorErrorType; + protected short vendorErrorCode; + protected OFMessageFactory factory; + protected byte[] error; + protected boolean errorIsAscii; + + public OFError() { + super(); + this.type = OFType.ERROR; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** convenience constructor */ + public OFError(OFErrorType errorType) { + this(); + setErrorType(errorType); + } + + /** + * @return the errorType + */ + public short getErrorType() { + return errorType; + } + + /** + * @param errorType + * the errorType to set + */ + public void setErrorType(short errorType) { + this.errorType = errorType; + } + + public void setErrorType(OFErrorType type) { + this.errorType = type.getValue(); + } + + /** + * @return true if the error is an extended vendor error + */ + public boolean isVendorError() { + return errorType == OFErrorType.OFPET_VENDOR_ERROR.getValue(); + } + + /** + * @return the errorCode + */ + public short getErrorCode() { + return errorCode; + } + + /** + * @param errorCode + * the errorCode to set + */ + public void setErrorCode(OFHelloFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(short errorCode) { + this.errorCode = errorCode; + } + + public void setErrorCode(OFBadRequestCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFBadActionCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFFlowModFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFPortModFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFQueueOpFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public int getVendorErrorType() { + return vendorErrorType; + } + + public void setVendorErrorType(int vendorErrorType) { + this.vendorErrorType = vendorErrorType; + } + + public short getVendorErrorCode() { + return vendorErrorCode; + } + + public void setVendorErrorCode(short vendorErrorCode) { + this.vendorErrorCode = vendorErrorCode; + } + + public OFMessage getOffendingMsg() throws MessageParseException { + // should only have one message embedded; if more than one, just + // grab first + if (this.error == null) + return null; + ChannelBuffer errorMsg = ChannelBuffers.wrappedBuffer(this.error); + if (factory == null) + throw new RuntimeException("MessageFactory not set"); + + List msglist = this.factory.parseMessage(errorMsg); + if (msglist == null) + return null; + return msglist.get(0); + } + + /** + * Write this offending message into the payload of the Error message + * + * @param offendingMsg + */ + + public void setOffendingMsg(OFMessage offendingMsg) { + if (offendingMsg == null) { + super.setLengthU(MINIMUM_LENGTH); + } else { + this.error = new byte[offendingMsg.getLengthU()]; + ChannelBuffer data = ChannelBuffers.wrappedBuffer(this.error); + data.writerIndex(0); + offendingMsg.writeTo(data); + super.setLengthU(MINIMUM_LENGTH + offendingMsg.getLengthU()); + } + } + + public OFMessageFactory getFactory() { + return factory; + } + + @Override + public void setMessageFactory(OFMessageFactory factory) { + this.factory = factory; + } + + /** + * @return the error + */ + public byte[] getError() { + return error; + } + + /** + * @param error + * the error to set + */ + public void setError(byte[] error) { + this.error = error; + } + + /** + * @return the errorIsAscii + */ + public boolean isErrorIsAscii() { + return errorIsAscii; + } + + /** + * @param errorIsAscii + * the errorIsAscii to set + */ + public void setErrorIsAscii(boolean errorIsAscii) { + this.errorIsAscii = errorIsAscii; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.errorType = data.readShort(); + this.errorCode = data.readShort(); + int dataLength = this.getLengthU() - MINIMUM_LENGTH; + if (dataLength > 0) { + this.error = new byte[dataLength]; + data.readBytes(this.error); + if (this.errorType == OFErrorType.OFPET_HELLO_FAILED.getValue()) + this.errorIsAscii = true; + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(errorType); + data.writeShort(errorCode); + if (error != null) + data.writeBytes(error); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(error); + result = prime * result + errorCode; + result = prime * result + (errorIsAscii ? 1231 : 1237); + result = prime * result + errorType; + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + OFError other = (OFError) obj; + if (!Arrays.equals(error, other.error)) + return false; + if (errorCode != other.errorCode) + return false; + if (errorIsAscii != other.errorIsAscii) + return false; + if (errorType != other.errorType) + return false; + return true; + } + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesReply.java new file mode 100644 index 00000000..ebd82b5e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesReply.java @@ -0,0 +1,257 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.ArrayList; +import java.util.List; + + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + + +/** + * Represents a features reply message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ + +public class OFFeaturesReply extends OFMessage { + public static int MINIMUM_LENGTH = 32; + + /** + * Corresponds to bits on the capabilities field + */ + public enum OFCapabilities { + OFPC_FLOW_STATS (1 << 0), + OFPC_TABLE_STATS (1 << 1), + OFPC_PORT_STATS (1 << 2), + OFPC_STP (1 << 3), + OFPC_RESERVED (1 << 4), + OFPC_IP_REASM (1 << 5), + OFPC_QUEUE_STATS (1 << 6), + OFPC_ARP_MATCH_IP (1 << 7); + + protected int value; + + private OFCapabilities(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + } + + protected long datapathId; + protected int buffers; + protected byte tables; + protected int capabilities; + protected int actions; + protected List ports; + + public OFFeaturesReply() { + super(); + this.type = OFType.FEATURES_REPLY; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the datapathId + */ + public long getDatapathId() { + return datapathId; + } + + /** + * @param datapathId the datapathId to set + */ + public void setDatapathId(long datapathId) { + this.datapathId = datapathId; + } + + /** + * @return the buffers + */ + public int getBuffers() { + return buffers; + } + + /** + * @param buffers the buffers to set + */ + public void setBuffers(int buffers) { + this.buffers = buffers; + } + + /** + * @return the tables + */ + public byte getTables() { + return tables; + } + + /** + * @param tables the tables to set + */ + public void setTables(byte tables) { + this.tables = tables; + } + + /** + * @return the capabilities + */ + public int getCapabilities() { + return capabilities; + } + + /** + * @param capabilities the capabilities to set + */ + public void setCapabilities(int capabilities) { + this.capabilities = capabilities; + } + + /** + * @return the actions + */ + public int getActions() { + return actions; + } + + /** + * @param actions the actions to set + */ + public void setActions(int actions) { + this.actions = actions; + } + + /** + * @return the ports + */ + public List getPorts() { + return ports; + } + + /** + * @param ports the ports to set + */ + public void setPorts(List ports) { + this.ports = ports; + if (ports == null) { + this.setLengthU(MINIMUM_LENGTH); + } else { + this.setLengthU(MINIMUM_LENGTH + ports.size() + * OFPhysicalPort.MINIMUM_LENGTH); + } + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.datapathId = data.readLong(); + this.buffers = data.readInt(); + this.tables = data.readByte(); + data.readerIndex(data.readerIndex() + 3); // pad + this.capabilities = data.readInt(); + this.actions = data.readInt(); + if (this.ports == null) { + this.ports = new ArrayList(); + } else { + this.ports.clear(); + } + int portCount = (super.getLengthU() - 32) + / OFPhysicalPort.MINIMUM_LENGTH; + OFPhysicalPort port; + for (int i = 0; i < portCount; ++i) { + port = new OFPhysicalPort(); + port.readFrom(data); + this.ports.add(port); + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeLong(this.datapathId); + data.writeInt(this.buffers); + data.writeByte(this.tables); + data.writeShort((short) 0); // pad + data.writeByte((byte) 0); // pad + data.writeInt(this.capabilities); + data.writeInt(this.actions); + if (this.ports != null) + for (OFPhysicalPort port : this.ports) { + port.writeTo(data); + } + } + + @Override + public int hashCode() { + final int prime = 139; + int result = super.hashCode(); + result = prime * result + actions; + result = prime * result + buffers; + result = prime * result + capabilities; + result = prime * result + (int) (datapathId ^ (datapathId >>> 32)); + result = prime * result + ((ports == null) ? 0 : ports.hashCode()); + result = prime * result + tables; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFFeaturesReply)) { + return false; + } + OFFeaturesReply other = (OFFeaturesReply) obj; + if (actions != other.actions) { + return false; + } + if (buffers != other.buffers) { + return false; + } + if (capabilities != other.capabilities) { + return false; + } + if (datapathId != other.datapathId) { + return false; + } + if (ports == null) { + if (other.ports != null) { + return false; + } + } else if (!ports.equals(other.ports)) { + return false; + } + if (tables != other.tables) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesRequest.java new file mode 100644 index 00000000..0a89e4f5 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFeaturesRequest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + + +/** + * Represents a features request message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public class OFFeaturesRequest extends OFMessage { + public static int MINIMUM_LENGTH = 8; + + public OFFeaturesRequest() { + super(); + this.type = OFType.FEATURES_REQUEST; + this.length = U16.t(MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowMod.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowMod.java new file mode 100644 index 00000000..06ef5479 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowMod.java @@ -0,0 +1,389 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.LinkedList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.factory.OFActionFactory; +import org.openflow.protocol.factory.OFActionFactoryAware; +import org.openflow.util.U16; + +/** + * Represents an ofp_flow_mod message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public class OFFlowMod extends OFMessage implements OFActionFactoryAware, Cloneable { + public static int MINIMUM_LENGTH = 72; + + public static final short OFPFC_ADD = 0; /* New flow. */ + public static final short OFPFC_MODIFY = 1; /* Modify all matching flows. */ + public static final short OFPFC_MODIFY_STRICT = 2; /* Modify entry strictly matching wildcards */ + public static final short OFPFC_DELETE=3; /* Delete all matching flows. */ + public static final short OFPFC_DELETE_STRICT =4; /* Strictly match wildcards and priority. */ + + // Open Flow Flow Mod Flags. Use "or" operation to set multiple flags + public static final short OFPFF_SEND_FLOW_REM = 0x1; // 1 << 0 + public static final short OFPFF_CHECK_OVERLAP = 0x2; // 1 << 1 + public static final short OFPFF_EMERG = 0x4; // 1 << 2 + + protected OFActionFactory actionFactory; + protected OFMatch match; + protected long cookie; + protected short command; + protected short idleTimeout; + protected short hardTimeout; + protected short priority; + protected int bufferId; + protected short outPort; + protected short flags; + protected List actions; + + public OFFlowMod() { + super(); + this.outPort = OFPort.OFPP_NONE.getValue(); + this.type = OFType.FLOW_MOD; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * Get buffer_id + * @return + */ + public int getBufferId() { + return this.bufferId; + } + + /** + * Set buffer_id + * @param bufferId + */ + public OFFlowMod setBufferId(int bufferId) { + this.bufferId = bufferId; + return this; + } + + /** + * Get cookie + * @return + */ + public long getCookie() { + return this.cookie; + } + + /** + * Set cookie + * @param cookie + */ + public OFFlowMod setCookie(long cookie) { + this.cookie = cookie; + return this; + } + + /** + * Get command + * @return + */ + public short getCommand() { + return this.command; + } + + /** + * Set command + * @param command + */ + public OFFlowMod setCommand(short command) { + this.command = command; + return this; + } + + /** + * Get flags + * @return + */ + public short getFlags() { + return this.flags; + } + + /** + * Set flags + * @param flags + */ + public OFFlowMod setFlags(short flags) { + this.flags = flags; + return this; + } + + /** + * Get hard_timeout + * @return + */ + public short getHardTimeout() { + return this.hardTimeout; + } + + /** + * Set hard_timeout + * @param hardTimeout + */ + public OFFlowMod setHardTimeout(short hardTimeout) { + this.hardTimeout = hardTimeout; + return this; + } + + /** + * Get idle_timeout + * @return + */ + public short getIdleTimeout() { + return this.idleTimeout; + } + + /** + * Set idle_timeout + * @param idleTimeout + */ + public OFFlowMod setIdleTimeout(short idleTimeout) { + this.idleTimeout = idleTimeout; + return this; + } + + /** + * Gets a copy of the OFMatch object for this FlowMod, changes to this + * object do not modify the FlowMod + * @return + */ + public OFMatch getMatch() { + return this.match; + } + + /** + * Set match + * @param match + */ + public OFFlowMod setMatch(OFMatch match) { + this.match = match; + return this; + } + + /** + * Get out_port + * @return + */ + public short getOutPort() { + return this.outPort; + } + + /** + * Set out_port + * @param outPort + */ + public OFFlowMod setOutPort(short outPort) { + this.outPort = outPort; + return this; + } + + /** + * Set out_port + * @param port + */ + public OFFlowMod setOutPort(OFPort port) { + this.outPort = port.getValue(); + return this; + } + + /** + * Get priority + * @return + */ + public short getPriority() { + return this.priority; + } + + /** + * Set priority + * @param priority + */ + public OFFlowMod setPriority(short priority) { + this.priority = priority; + return this; + } + + /** + * Returns read-only copies of the actions contained in this Flow Mod + * @return a list of ordered OFAction objects + */ + public List getActions() { + return this.actions; + } + + /** + * Sets the list of actions this Flow Mod contains + * @param actions a list of ordered OFAction objects + */ + public OFFlowMod setActions(List actions) { + this.actions = actions; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.cookie = data.readLong(); + this.command = data.readShort(); + this.idleTimeout = data.readShort(); + this.hardTimeout = data.readShort(); + this.priority = data.readShort(); + this.bufferId = data.readInt(); + this.outPort = data.readShort(); + this.flags = data.readShort(); + if (this.actionFactory == null) + throw new RuntimeException("OFActionFactory not set"); + this.actions = this.actionFactory.parseActions(data, getLengthU() - + MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + this.match.writeTo(data); + data.writeLong(cookie); + data.writeShort(command); + data.writeShort(idleTimeout); + data.writeShort(hardTimeout); + data.writeShort(priority); + data.writeInt(bufferId); + data.writeShort(outPort); + data.writeShort(flags); + if (actions != null) { + for (OFAction action : actions) { + action.writeTo(data); + } + } + } + + @Override + public void setActionFactory(OFActionFactory actionFactory) { + this.actionFactory = actionFactory; + } + + @Override + public int hashCode() { + final int prime = 227; + int result = super.hashCode(); + result = prime * result + ((actions == null) ? 0 : actions.hashCode()); + result = prime * result + bufferId; + result = prime * result + command; + result = prime * result + (int) (cookie ^ (cookie >>> 32)); + result = prime * result + flags; + result = prime * result + hardTimeout; + result = prime * result + idleTimeout; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + outPort; + result = prime * result + priority; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFFlowMod)) { + return false; + } + OFFlowMod other = (OFFlowMod) obj; + if (actions == null) { + if (other.actions != null) { + return false; + } + } else if (!actions.equals(other.actions)) { + return false; + } + if (bufferId != other.bufferId) { + return false; + } + if (command != other.command) { + return false; + } + if (cookie != other.cookie) { + return false; + } + if (flags != other.flags) { + return false; + } + if (hardTimeout != other.hardTimeout) { + return false; + } + if (idleTimeout != other.idleTimeout) { + return false; + } + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (outPort != other.outPort) { + return false; + } + if (priority != other.priority) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public OFFlowMod clone() throws CloneNotSupportedException { + OFMatch neoMatch = match.clone(); + OFFlowMod flowMod= (OFFlowMod) super.clone(); + flowMod.setMatch(neoMatch); + List neoActions = new LinkedList(); + for(OFAction action: this.actions) + neoActions.add((OFAction) action.clone()); + flowMod.setActions(neoActions); + return flowMod; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "OFFlowMod [actionFactory=" + actionFactory + ", actions=" + + actions + ", bufferId=" + bufferId + ", command=" + command + + ", cookie=" + cookie + ", flags=" + flags + ", hardTimeout=" + + hardTimeout + ", idleTimeout=" + idleTimeout + ", match=" + + match + ", outPort=" + outPort + ", priority=" + priority + + ", length=" + length + ", type=" + type + ", version=" + + version + ", xid=" + xid + "]"; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowRemoved.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowRemoved.java new file mode 100644 index 00000000..33570063 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFFlowRemoved.java @@ -0,0 +1,294 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_flow_removed message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public class OFFlowRemoved extends OFMessage { + public static int MINIMUM_LENGTH = 88; + + public enum OFFlowRemovedReason { + OFPRR_IDLE_TIMEOUT, + OFPRR_HARD_TIMEOUT, + OFPRR_DELETE + } + + protected OFMatch match; + protected long cookie; + protected short priority; + protected OFFlowRemovedReason reason; + protected int durationSeconds; + protected int durationNanoseconds; + protected short idleTimeout; + protected long packetCount; + protected long byteCount; + + public OFFlowRemoved() { + super(); + this.type = OFType.FLOW_REMOVED; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * Get cookie + * @return + */ + public long getCookie() { + return this.cookie; + } + + /** + * Set cookie + * @param cookie + */ + public void setCookie(long cookie) { + this.cookie = cookie; + } + + /** + * Get idle_timeout + * @return + */ + public short getIdleTimeout() { + return this.idleTimeout; + } + + /** + * Set idle_timeout + * @param idleTimeout + */ + public void setIdleTimeout(short idleTimeout) { + this.idleTimeout = idleTimeout; + } + + /** + * Gets a copy of the OFMatch object for this FlowMod, changes to this + * object do not modify the FlowMod + * @return + */ + public OFMatch getMatch() { + return this.match; + } + + /** + * Set match + * @param match + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * Get priority + * @return + */ + public short getPriority() { + return this.priority; + } + + /** + * Set priority + * @param priority + */ + public void setPriority(short priority) { + this.priority = priority; + } + + /** + * @return the reason + */ + public OFFlowRemovedReason getReason() { + return reason; + } + + /** + * @param reason the reason to set + */ + public void setReason(OFFlowRemovedReason reason) { + this.reason = reason; + } + + /** + * @return the durationSeconds + */ + public int getDurationSeconds() { + return durationSeconds; + } + + /** + * @param durationSeconds the durationSeconds to set + */ + public void setDurationSeconds(int durationSeconds) { + this.durationSeconds = durationSeconds; + } + + /** + * @return the durationNanoseconds + */ + public int getDurationNanoseconds() { + return durationNanoseconds; + } + + /** + * @param durationNanoseconds the durationNanoseconds to set + */ + public void setDurationNanoseconds(int durationNanoseconds) { + this.durationNanoseconds = durationNanoseconds; + } + + /** + * @return the packetCount + */ + public long getPacketCount() { + return packetCount; + } + + /** + * @param packetCount the packetCount to set + */ + public void setPacketCount(long packetCount) { + this.packetCount = packetCount; + } + + /** + * @return the byteCount + */ + public long getByteCount() { + return byteCount; + } + + /** + * @param byteCount the byteCount to set + */ + public void setByteCount(long byteCount) { + this.byteCount = byteCount; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.cookie = data.readLong(); + this.priority = data.readShort(); + int reasonIndex = (int)(0xff & data.readByte()); + if (reasonIndex >= OFFlowRemovedReason.values().length) { + reasonIndex = OFFlowRemovedReason.values().length - 1; + } + this.reason = OFFlowRemovedReason.values()[reasonIndex]; + data.readByte(); // pad + this.durationSeconds = data.readInt(); + this.durationNanoseconds = data.readInt(); + this.idleTimeout = data.readShort(); + data.readByte(); // pad + data.readByte(); // pad + this.packetCount = data.readLong(); + this.byteCount = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + this.match.writeTo(data); + data.writeLong(cookie); + data.writeShort(priority); + data.writeByte((byte) this.reason.ordinal()); + data.writeByte((byte) 0); + data.writeInt(this.durationSeconds); + data.writeInt(this.durationNanoseconds); + data.writeShort(idleTimeout); + data.writeByte((byte) 0); // pad + data.writeByte((byte) 0); // pad + data.writeLong(this.packetCount); + data.writeLong(this.byteCount); + } + + @Override + public int hashCode() { + final int prime = 271; + int result = super.hashCode(); + result = prime * result + (int) (byteCount ^ (byteCount >>> 32)); + result = prime * result + (int) (cookie ^ (cookie >>> 32)); + result = prime * result + durationNanoseconds; + result = prime * result + durationSeconds; + result = prime * result + idleTimeout; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + (int) (packetCount ^ (packetCount >>> 32)); + result = prime * result + priority; + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFFlowRemoved)) { + return false; + } + OFFlowRemoved other = (OFFlowRemoved) obj; + if (byteCount != other.byteCount) { + return false; + } + if (cookie != other.cookie) { + return false; + } + if (durationNanoseconds != other.durationNanoseconds) { + return false; + } + if (durationSeconds != other.durationSeconds) { + return false; + } + if (idleTimeout != other.idleTimeout) { + return false; + } + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (packetCount != other.packetCount) { + return false; + } + if (priority != other.priority) { + return false; + } + if (reason == null) { + if (other.reason != null) { + return false; + } + } else if (!reason.equals(other.reason)) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigReply.java new file mode 100644 index 00000000..257867af --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigReply.java @@ -0,0 +1,29 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +/** + * Represents an OFPT_GET_CONFIG_REPLY type message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFGetConfigReply extends OFSwitchConfig { + public OFGetConfigReply() { + super(); + this.type = OFType.GET_CONFIG_REPLY; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigRequest.java new file mode 100644 index 00000000..85c74992 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFGetConfigRequest.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an OFPT_GET_CONFIG_REQUEST type message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFGetConfigRequest extends OFMessage { + public OFGetConfigRequest() { + super(); + this.type = OFType.GET_CONFIG_REQUEST; + this.length = U16.t(OFMessage.MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFHello.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFHello.java new file mode 100644 index 00000000..e702ca4d --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFHello.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + + +/** + * Represents an ofp_hello message + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Feb 8, 2010 + */ +public class OFHello extends OFMessage { + public static int MINIMUM_LENGTH = 8; + + /** + * Construct a ofp_hello message + */ + public OFHello() { + super(); + this.type = OFType.HELLO; + this.length = U16.t(MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatch.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatch.java new file mode 100644 index 00000000..91930f11 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatch.java @@ -0,0 +1,1040 @@ +/** + * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior + * University + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package org.openflow.protocol; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.Arrays; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.HexString; +import org.openflow.util.U16; +import org.openflow.util.U8; + +/** + * Represents an ofp_match structure + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ +public class OFMatch implements Cloneable, Serializable { + + /** + * + */ + + public static final short VLAN_UNTAGGED = (short)0xffff; + + private static final long serialVersionUID = 1L; + public static int MINIMUM_LENGTH = 40; + final public static int OFPFW_ALL = ((1 << 22) - 1); + + final public static int OFPFW_IN_PORT = 1 << 0; /* Switch input port. */ + final public static int OFPFW_DL_VLAN = 1 << 1; /* VLAN id. */ + final public static int OFPFW_DL_SRC = 1 << 2; /* Ethernet source address. */ + final public static int OFPFW_DL_DST = 1 << 3; /* + * Ethernet destination + * address. + */ + final public static int OFPFW_DL_TYPE = 1 << 4; /* Ethernet frame type. */ + final public static int OFPFW_NW_PROTO = 1 << 5; /* IP protocol. */ + final public static int OFPFW_TP_SRC = 1 << 6; /* TCP/UDP source port. */ + final public static int OFPFW_TP_DST = 1 << 7; /* TCP/UDP destination port. */ + + /* + * IP source address wildcard bit count. 0 is exact match, 1 ignores the + * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard + * the entire field. This is the *opposite* of the usual convention where + * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. + */ + final public static int OFPFW_NW_SRC_SHIFT = 8; + final public static int OFPFW_NW_SRC_BITS = 6; + final public static int OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT; + final public static int OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT; + + /* IP destination address wildcard bit count. Same format as source. */ + final public static int OFPFW_NW_DST_SHIFT = 14; + final public static int OFPFW_NW_DST_BITS = 6; + final public static int OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT; + final public static int OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT; + + final public static int OFPFW_DL_VLAN_PCP = 1 << 20; /* VLAN priority. */ + final public static int OFPFW_NW_TOS = 1 << 21; /* + * IP ToS (DSCP field, 6 + * bits). + */ + + final public static int OFPFW_ALL_SANITIZED = (((1 << 22) - 1) + & ~OFPFW_NW_SRC_MASK & ~OFPFW_NW_DST_MASK) + | OFPFW_NW_SRC_ALL + | OFPFW_NW_DST_ALL; + + /* List of Strings for marshalling and unmarshalling to human readable forms */ + final public static String STR_IN_PORT = "in_port"; + final public static String STR_DL_DST = "dl_dst"; + final public static String STR_DL_SRC = "dl_src"; + final public static String STR_DL_TYPE = "dl_type"; + final public static String STR_DL_VLAN = "dl_vlan"; + final public static String STR_DL_VLAN_PCP = "dl_vlan_pcp"; + final public static String STR_NW_DST = "nw_dst"; + final public static String STR_NW_SRC = "nw_src"; + final public static String STR_NW_PROTO = "nw_proto"; + final public static String STR_NW_TOS = "nw_tos"; + final public static String STR_TP_DST = "tp_dst"; + final public static String STR_TP_SRC = "tp_src"; + + protected int wildcards; + protected short inputPort; + protected byte[] dataLayerSource; + protected byte[] dataLayerDestination; + protected short dataLayerVirtualLan; + protected byte dataLayerVirtualLanPriorityCodePoint; + protected short dataLayerType; + protected byte networkTypeOfService; + protected byte networkProtocol; + protected int networkSource; + protected int networkDestination; + protected short transportSource; + protected short transportDestination; + + /** + * By default, create a OFMatch that matches everything (mostly because it's + * the least amount of work to make a valid OFMatch) + */ + public OFMatch() { + this.wildcards = OFPFW_ALL; + this.dataLayerDestination = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0 }; + this.dataLayerSource = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + this.dataLayerVirtualLan = VLAN_UNTAGGED; + this.dataLayerVirtualLanPriorityCodePoint = 0; + this.dataLayerType = 0; + this.inputPort = 0; + this.networkProtocol = 0; + this.networkTypeOfService = 0; + this.networkSource = 0; + this.networkDestination = 0; + this.transportDestination = 0; + this.transportSource = 0; + } + + /** + * Get dl_dst + * + * @return an arrays of bytes + */ + public byte[] getDataLayerDestination() { + return this.dataLayerDestination; + } + + /** + * Set dl_dst + * + * @param dataLayerDestination + */ + public OFMatch setDataLayerDestination(byte[] dataLayerDestination) { + this.dataLayerDestination = dataLayerDestination; + return this; + } + + /** + * Set dl_dst, but first translate to byte[] using HexString + * + * @param mac + * A colon separated string of 6 pairs of octets, e..g., + * "00:17:42:EF:CD:8D" + */ + public OFMatch setDataLayerDestination(String mac) { + byte bytes[] = HexString.fromHexString(mac); + if (bytes.length != 6) + throw new IllegalArgumentException( + "expected string with 6 octets, got '" + + mac + + "'"); + this.dataLayerDestination = bytes; + return this; + } + + /** + * Get dl_src + * + * @return an array of bytes + */ + public byte[] getDataLayerSource() { + return this.dataLayerSource; + } + + /** + * Set dl_src + * + * @param dataLayerSource + */ + public OFMatch setDataLayerSource(byte[] dataLayerSource) { + this.dataLayerSource = dataLayerSource; + return this; + } + + /** + * Set dl_src, but first translate to byte[] using HexString + * + * @param mac + * A colon separated string of 6 pairs of octets, e..g., + * "00:17:42:EF:CD:8D" + */ + public OFMatch setDataLayerSource(String mac) { + byte bytes[] = HexString.fromHexString(mac); + if (bytes.length != 6) + throw new IllegalArgumentException( + "expected string with 6 octets, got '" + + mac + + "'"); + this.dataLayerSource = bytes; + return this; + } + + /** + * Get dl_type + * + * @return ether_type + */ + public short getDataLayerType() { + return this.dataLayerType; + } + + /** + * Set dl_type + * + * @param dataLayerType + */ + public OFMatch setDataLayerType(short dataLayerType) { + this.dataLayerType = dataLayerType; + return this; + } + + /** + * Get dl_vlan + * + * @return vlan tag; VLAN_NONE == no tag + */ + public short getDataLayerVirtualLan() { + return this.dataLayerVirtualLan; + } + + /** + * Set dl_vlan + * + * @param dataLayerVirtualLan + */ + public OFMatch setDataLayerVirtualLan(short dataLayerVirtualLan) { + this.dataLayerVirtualLan = dataLayerVirtualLan; + return this; + } + + /** + * Get dl_vlan_pcp + * + * @return + */ + public byte getDataLayerVirtualLanPriorityCodePoint() { + return this.dataLayerVirtualLanPriorityCodePoint; + } + + /** + * Set dl_vlan_pcp + * + * @param pcp + */ + public OFMatch setDataLayerVirtualLanPriorityCodePoint(byte pcp) { + this.dataLayerVirtualLanPriorityCodePoint = pcp; + return this; + } + + /** + * Get in_port + * + * @return + */ + public short getInputPort() { + return this.inputPort; + } + + /** + * Set in_port + * + * @param inputPort + */ + public OFMatch setInputPort(short inputPort) { + this.inputPort = inputPort; + return this; + } + + /** + * Get nw_dst + * + * @return + */ + public int getNetworkDestination() { + return this.networkDestination; + } + + /** + * Set nw_dst + * + * @param networkDestination + */ + public OFMatch setNetworkDestination(int networkDestination) { + this.networkDestination = networkDestination; + return this; + } + + /** + * Parse this match's wildcard fields and return the number of significant + * bits in the IP destination field. NOTE: this returns the number of bits + * that are fixed, i.e., like CIDR, not the number of bits that are free + * like OpenFlow encodes. + * + * @return a number between 0 (matches all IPs) and 63 ( 32>= implies exact + * match) + */ + public int getNetworkDestinationMaskLen() { + return Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT), + 0); + } + + /** + * Parse this match's wildcard fields and return the number of significant + * bits in the IP destination field. NOTE: this returns the number of bits + * that are fixed, i.e., like CIDR, not the number of bits that are free + * like OpenFlow encodes. + * + * @return a number between 0 (matches all IPs) and 32 (exact match) + */ + public int getNetworkSourceMaskLen() { + return Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT), + 0); + } + + /** + * Get nw_proto + * + * @return + */ + public byte getNetworkProtocol() { + return this.networkProtocol; + } + + /** + * Set nw_proto + * + * @param networkProtocol + */ + public OFMatch setNetworkProtocol(byte networkProtocol) { + this.networkProtocol = networkProtocol; + return this; + } + + /** + * Get nw_src + * + * @return + */ + public int getNetworkSource() { + return this.networkSource; + } + + /** + * Set nw_src + * + * @param networkSource + */ + public OFMatch setNetworkSource(int networkSource) { + this.networkSource = networkSource; + return this; + } + + /** + * Get nw_tos OFMatch stores the ToS bits as top 6-bits, so right shift by 2 + * bits before returning the value + * + * @return : 6-bit DSCP value (0-63) + */ + public byte getNetworkTypeOfService() { + return (byte) ((this.networkTypeOfService >> 2) & 0x3f); + } + + /** + * Set nw_tos OFMatch stores the ToS bits as top 6-bits, so left shift by 2 + * bits before storing the value + * + * @param networkTypeOfService + * : 6-bit DSCP value (0-63) + */ + public OFMatch setNetworkTypeOfService(byte networkTypeOfService) { + this.networkTypeOfService = (byte) (networkTypeOfService << 2); + return this; + } + + /** + * Get tp_dst + * + * @return + */ + public short getTransportDestination() { + return this.transportDestination; + } + + /** + * Set tp_dst + * + * @param transportDestination + */ + public OFMatch setTransportDestination(short transportDestination) { + this.transportDestination = transportDestination; + return this; + } + + /** + * Get tp_src + * + * @return + */ + public short getTransportSource() { + return this.transportSource; + } + + /** + * Set tp_src + * + * @param transportSource + */ + public OFMatch setTransportSource(short transportSource) { + this.transportSource = transportSource; + return this; + } + + /** + * Get wildcards + * + * @return + */ + public int getWildcards() { + return this.wildcards; + } + + /** + * Get wildcards + * + * @return + */ + public Wildcards getWildcardObj() { + return Wildcards.of(wildcards); + } + + /** + * Set wildcards + * + * @param wildcards + */ + public OFMatch setWildcards(int wildcards) { + this.wildcards = wildcards; + return this; + } + + /** set the wildcard using the Wildcards convenience object */ + public OFMatch setWildcards(Wildcards wildcards) { + this.wildcards = wildcards.getInt(); + return this; + } + + /** + * Initializes this OFMatch structure with the corresponding data from the + * specified packet. Must specify the input port, to ensure that + * this.in_port is set correctly. Specify OFPort.NONE or OFPort.ANY if input + * port not applicable or available + * + * @param packetData + * The packet's data + * @param inputPort + * the port the packet arrived on + */ + public OFMatch loadFromPacket(byte[] packetData, short inputPort) { + short scratch; + int transportOffset = 34; + ByteBuffer packetDataBB = ByteBuffer.wrap(packetData); + int limit = packetDataBB.limit(); + + this.wildcards = 0; // all fields have explicit entries + + this.inputPort = inputPort; + + if (inputPort == OFPort.OFPP_ALL.getValue()) + this.wildcards |= OFPFW_IN_PORT; + + assert (limit >= 14); + // dl dst + this.dataLayerDestination = new byte[6]; + packetDataBB.get(this.dataLayerDestination); + // dl src + this.dataLayerSource = new byte[6]; + packetDataBB.get(this.dataLayerSource); + // dl type + this.dataLayerType = packetDataBB.getShort(); + + if (getDataLayerType() != (short) 0x8100) { // need cast to avoid signed + // bug + setDataLayerVirtualLan((short) 0xffff); + setDataLayerVirtualLanPriorityCodePoint((byte) 0); + } else { + // has vlan tag + scratch = packetDataBB.getShort(); + setDataLayerVirtualLan((short) (0xfff & scratch)); + setDataLayerVirtualLanPriorityCodePoint((byte) ((0xe000 & scratch) >> 13)); + this.dataLayerType = packetDataBB.getShort(); + } + + switch (getDataLayerType()) { + case 0x0800: + // ipv4 + // check packet length + scratch = packetDataBB.get(); + scratch = (short) (0xf & scratch); + transportOffset = (packetDataBB.position() - 1) + + (scratch * 4); + // nw tos (dscp) + scratch = packetDataBB.get(); + setNetworkTypeOfService((byte) ((0xfc & scratch) >> 2)); + // nw protocol + packetDataBB.position(packetDataBB.position() + 7); + this.networkProtocol = packetDataBB.get(); + // nw src + packetDataBB.position(packetDataBB.position() + 2); + this.networkSource = packetDataBB.getInt(); + // nw dst + this.networkDestination = packetDataBB.getInt(); + packetDataBB.position(transportOffset); + break; + case 0x0806: + // arp + int arpPos = packetDataBB.position(); + // opcode + scratch = packetDataBB.getShort(arpPos + 6); + setNetworkProtocol((byte) (0xff & scratch)); + + scratch = packetDataBB.getShort(arpPos + 2); + // if ipv4 and addr len is 4 + if (scratch == 0x800 && packetDataBB.get(arpPos + 5) == 4) { + // nw src + this.networkSource = packetDataBB.getInt(arpPos + 14); + // nw dst + this.networkDestination = packetDataBB.getInt(arpPos + 24); + } else { + setNetworkSource(0); + setNetworkDestination(0); + } + break; + default: + setNetworkTypeOfService((byte) 0); + setNetworkProtocol((byte) 0); + setNetworkSource(0); + setNetworkDestination(0); + break; + } + + switch (getNetworkProtocol()) { + case 0x01: + // icmp + // type + this.transportSource = U8.f(packetDataBB.get()); + // code + this.transportDestination = U8.f(packetDataBB.get()); + break; + case 0x06: + // tcp + // tcp src + this.transportSource = packetDataBB.getShort(); + // tcp dest + this.transportDestination = packetDataBB.getShort(); + break; + case 0x11: + // udp + // udp src + this.transportSource = packetDataBB.getShort(); + // udp dest + this.transportDestination = packetDataBB.getShort(); + break; + default: + setTransportDestination((short) 0); + setTransportSource((short) 0); + break; + } + return this; + } + + /** + * Read this message off the wire from the specified ByteBuffer + * + * @param data + */ + public void readFrom(ChannelBuffer data) { + this.wildcards = data.readInt(); + this.inputPort = data.readShort(); + this.dataLayerSource = new byte[6]; + data.readBytes(this.dataLayerSource); + this.dataLayerDestination = new byte[6]; + data.readBytes(this.dataLayerDestination); + this.dataLayerVirtualLan = data.readShort(); + this.dataLayerVirtualLanPriorityCodePoint = data.readByte(); + data.readByte(); // pad + this.dataLayerType = data.readShort(); + this.networkTypeOfService = data.readByte(); + this.networkProtocol = data.readByte(); + data.readByte(); // pad + data.readByte(); // pad + this.networkSource = data.readInt(); + this.networkDestination = data.readInt(); + this.transportSource = data.readShort(); + this.transportDestination = data.readShort(); + } + + /** + * Write this message's binary format to the specified ByteBuffer + * + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeInt(wildcards); + data.writeShort(inputPort); + data.writeBytes(this.dataLayerSource); + data.writeBytes(this.dataLayerDestination); + data.writeShort(dataLayerVirtualLan); + data.writeByte(dataLayerVirtualLanPriorityCodePoint); + data.writeByte((byte) 0x0); // pad + data.writeShort(dataLayerType); + data.writeByte(networkTypeOfService); + data.writeByte(networkProtocol); + data.writeByte((byte) 0x0); // pad + data.writeByte((byte) 0x0); // pad + data.writeInt(networkSource); + data.writeInt(networkDestination); + data.writeShort(transportSource); + data.writeShort(transportDestination); + } + + @Override + public int hashCode() { + final int prime = 131; + int result = 1; + result = prime * result + Arrays.hashCode(dataLayerDestination); + result = prime * result + Arrays.hashCode(dataLayerSource); + result = prime * result + dataLayerType; + result = prime * result + dataLayerVirtualLan; + result = prime * result + dataLayerVirtualLanPriorityCodePoint; + result = prime * result + inputPort; + result = prime * result + networkDestination; + result = prime * result + networkProtocol; + result = prime * result + networkSource; + result = prime * result + networkTypeOfService; + result = prime * result + transportDestination; + result = prime * result + transportSource; + result = prime * result + wildcards; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFMatch)) { + return false; + } + OFMatch other = (OFMatch) obj; + if (!Arrays.equals(dataLayerDestination, other.dataLayerDestination)) { + return false; + } + if (!Arrays.equals(dataLayerSource, other.dataLayerSource)) { + return false; + } + if (dataLayerType != other.dataLayerType) { + return false; + } + if (dataLayerVirtualLan != other.dataLayerVirtualLan) { + return false; + } + if (dataLayerVirtualLanPriorityCodePoint != other.dataLayerVirtualLanPriorityCodePoint) { + return false; + } + if (inputPort != other.inputPort) { + return false; + } + if (networkDestination != other.networkDestination) { + return false; + } + if (networkProtocol != other.networkProtocol) { + return false; + } + if (networkSource != other.networkSource) { + return false; + } + if (networkTypeOfService != other.networkTypeOfService) { + return false; + } + if (transportDestination != other.transportDestination) { + return false; + } + if (transportSource != other.transportSource) { + return false; + } + if ((wildcards & OFMatch.OFPFW_ALL) != (other.wildcards & OFPFW_ALL)) { // only + // consider + // allocated + // part + // of + // wildcards + return false; + } + return true; + } + + /** + * Implement clonable interface + */ + @Override + public OFMatch clone() { + try { + OFMatch ret = (OFMatch) super.clone(); + ret.dataLayerDestination = this.dataLayerDestination.clone(); + ret.dataLayerSource = this.dataLayerSource.clone(); + return ret; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + /** + * Output a dpctl-styled string, i.e., only list the elements that are not + * wildcarded A match-everything OFMatch outputs "OFMatch[]" + * + * @return + * "OFMatch[dl_src:00:20:01:11:22:33,nw_src:192.168.0.0/24,tp_dst:80]" + */ + @Override + public String toString() { + String str = ""; + + // l1 + if ((wildcards & OFPFW_IN_PORT) == 0) + str += "," + STR_IN_PORT + "=" + + U16.f(this.inputPort); + + // l2 + if ((wildcards & OFPFW_DL_DST) == 0) + str += "," + + STR_DL_DST + + "=" + + HexString.toHexString(this.dataLayerDestination); + if ((wildcards & OFPFW_DL_SRC) == 0) + str += "," + + STR_DL_SRC + + "=" + + HexString.toHexString(this.dataLayerSource); + if ((wildcards & OFPFW_DL_TYPE) == 0) + str += "," + + STR_DL_TYPE + + "=0x" + + Integer.toHexString(U16.f(this.dataLayerType)); + if ((wildcards & OFPFW_DL_VLAN) == 0) + str += "," + + STR_DL_VLAN + + "=0x" + + Integer.toHexString(U16.f(this.dataLayerVirtualLan)); + if ((wildcards & OFPFW_DL_VLAN_PCP) == 0) + str += "," + + STR_DL_VLAN_PCP + + "=" + + Integer.toHexString(U8.f(this.dataLayerVirtualLanPriorityCodePoint)); + + // l3 + if (getNetworkDestinationMaskLen() > 0) + str += "," + + STR_NW_DST + + "=" + + cidrToString(networkDestination, + getNetworkDestinationMaskLen()); + if (getNetworkSourceMaskLen() > 0) + str += "," + + STR_NW_SRC + + "=" + + cidrToString(networkSource, + getNetworkSourceMaskLen()); + if ((wildcards & OFPFW_NW_PROTO) == 0) + str += "," + STR_NW_PROTO + + "=" + + this.networkProtocol; + if ((wildcards & OFPFW_NW_TOS) == 0) + str += "," + + STR_NW_TOS + + "=" + + this.getNetworkTypeOfService(); + + // l4 + if ((wildcards & OFPFW_TP_DST) == 0) + str += "," + + STR_TP_DST + + "=" + + this.transportDestination; + if ((wildcards & OFPFW_TP_SRC) == 0) + str += "," + STR_TP_SRC + "=" + + this.transportSource; + if ((str.length() > 0) && (str.charAt(0) == ',')) + str = str.substring(1); // trim + // the + // leading + // "," + // done + return "OFMatch[" + str + "]"; + } + + /** + * debug a set of wildcards + */ + public static String debugWildCards(int wildcards) { + String str = ""; + + // l1 + if ((wildcards & OFPFW_IN_PORT) != 0) str += "|" + STR_IN_PORT; + + // l2 + if ((wildcards & OFPFW_DL_DST) != 0) str += "|" + STR_DL_DST; + if ((wildcards & OFPFW_DL_SRC) != 0) str += "|" + STR_DL_SRC; + if ((wildcards & OFPFW_DL_TYPE) != 0) str += "|" + STR_DL_TYPE; + if ((wildcards & OFPFW_DL_VLAN) != 0) str += "|" + STR_DL_VLAN; + if ((wildcards & OFPFW_DL_VLAN_PCP) != 0) + str += "|" + + STR_DL_VLAN_PCP; + + int nwDstMask = Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT), + 0); + int nwSrcMask = Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT), + 0); + + // l3 + if (nwDstMask < 32) + str += "|" + STR_NW_DST + "(/" + nwDstMask + ")"; + + if (nwSrcMask < 32) + str += "|" + STR_NW_SRC + "(/" + nwSrcMask + ")"; + + if ((wildcards & OFPFW_NW_PROTO) != 0) str += "|" + STR_NW_PROTO; + if ((wildcards & OFPFW_NW_TOS) != 0) str += "|" + STR_NW_TOS; + + // l4 + if ((wildcards & OFPFW_TP_DST) != 0) str += "|" + STR_TP_DST; + if ((wildcards & OFPFW_TP_SRC) != 0) str += "|" + STR_TP_SRC; + if ((str.length() > 0) && (str.charAt(0) == '|')) + str = str.substring(1); // trim + // the + // leading + // "," + // done + return str; + } + + private String cidrToString(int ip, int prefix) { + String str; + if (prefix >= 32) { + str = ipToString(ip); + } else { + // use the negation of mask to fake endian magic + int mask = ~((1 << (32 - prefix)) - 1); + str = ipToString(ip & mask) + "/" + prefix; + } + + return str; + } + + /** + * Set this OFMatch's parameters based on a comma-separated key=value pair + * dpctl-style string, e.g., from the output of OFMatch.toString()
+ *

+ * Supported keys/values include
+ *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
KEY(s) + * VALUE + *
"in_port","input_port" + * integer + *
"dl_src","eth_src", "dl_dst","eth_dst" + * hex-string + *
"dl_type", "dl_vlan", "dl_vlan_pcp" + * integer + *
"nw_src", "nw_dst", "ip_src", "ip_dst" + * CIDR-style netmask + *
"tp_src","tp_dst" + * integer (max 64k) + *
+ *

+ * The CIDR-style netmasks assume 32 netmask if none given, so: + * "128.8.128.118/32" is the same as "128.8.128.118" + * + * @param match + * a key=value comma separated string, e.g. + * "in_port=5,ip_dst=192.168.0.0/16,tp_src=80" + * @throws IllegalArgumentException + * on unexpected key or value + */ + + public void fromString(String match) throws IllegalArgumentException { + if (match.equals("") || match.equalsIgnoreCase("any") + || match.equalsIgnoreCase("all") || match.equals("[]")) + match = "OFMatch[]"; + String[] tokens = match.split("[\\[,\\]]"); + String[] values; + int initArg = 0; + if (tokens[0].equals("OFMatch")) initArg = 1; + this.wildcards = OFPFW_ALL; + int i; + for (i = initArg; i < tokens.length; i++) { + values = tokens[i].split("="); + if (values.length != 2) + throw new IllegalArgumentException( + "Token " + + tokens[i] + + " does not have form 'key=value' parsing " + + match); + values[0] = values[0].toLowerCase(); // try to make this case insens + if (values[0].equals(STR_IN_PORT) + || values[0].equals("input_port")) { + this.inputPort = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_IN_PORT; + } else if (values[0].equals(STR_DL_DST) + || values[0].equals("eth_dst")) { + this.dataLayerDestination = HexString.fromHexString(values[1]); + this.wildcards &= ~OFPFW_DL_DST; + } else if (values[0].equals(STR_DL_SRC) + || values[0].equals("eth_src")) { + this.dataLayerSource = HexString.fromHexString(values[1]); + this.wildcards &= ~OFPFW_DL_SRC; + } else if (values[0].equals(STR_DL_TYPE) + || values[0].equals("eth_type")) { + if (values[1].startsWith("0x")) + this.dataLayerType = U16.t(Integer.valueOf(values[1].replaceFirst("0x", + ""), + 16)); + else + this.dataLayerType = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_DL_TYPE; + } else if (values[0].equals(STR_DL_VLAN)) { + if (values[1].startsWith("0x")) + this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1].replaceFirst("0x", + ""), + 16)); + else + this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_DL_VLAN; + } else if (values[0].equals(STR_DL_VLAN_PCP)) { + this.dataLayerVirtualLanPriorityCodePoint = U8.t(Short.valueOf(values[1])); + this.wildcards &= ~OFPFW_DL_VLAN_PCP; + } else if (values[0].equals(STR_NW_DST) + || values[0].equals("ip_dst")) { + setFromCIDR(values[1], STR_NW_DST); + } else if (values[0].equals(STR_NW_SRC) + || values[0].equals("ip_src")) { + setFromCIDR(values[1], STR_NW_SRC); + } else if (values[0].equals(STR_NW_PROTO)) { + if (values[1].startsWith("0x")) + this.networkProtocol = U8.t(Short.valueOf(values[1].replaceFirst("0x",""),16)); + else + this.networkProtocol = U8.t(Short.valueOf(values[1])); + this.wildcards &= ~OFPFW_NW_PROTO; + } else if (values[0].equals(STR_NW_TOS)) { + this.setNetworkTypeOfService(U8.t(Short.valueOf(values[1]))); + this.wildcards &= ~OFPFW_NW_TOS; + } else if (values[0].equals(STR_TP_DST)) { + this.transportDestination = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_TP_DST; + } else if (values[0].equals(STR_TP_SRC)) { + this.transportSource = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_TP_SRC; + } else { + throw new IllegalArgumentException("unknown token " + + tokens[i] + " parsing " + + match); + } + } + } + + /** + * Set the networkSource or networkDestionation address and their wildcards + * from the CIDR string + * + * @param cidr + * "192.168.0.0/16" or "172.16.1.5" + * @param which + * one of STR_NW_DST or STR_NW_SRC + * @throws IllegalArgumentException + */ + private + void + setFromCIDR(String cidr, String which) + throws IllegalArgumentException { + String values[] = cidr.split("/"); + String[] ip_str = values[0].split("\\."); + int ip = 0; + ip += Integer.valueOf(ip_str[0]) << 24; + ip += Integer.valueOf(ip_str[1]) << 16; + ip += Integer.valueOf(ip_str[2]) << 8; + ip += Integer.valueOf(ip_str[3]); + int prefix = 32; // all bits are fixed, by default + + if (values.length >= 2) prefix = Integer.valueOf(values[1]); + int mask = 32 - prefix; + if (which.equals(STR_NW_DST)) { + this.networkDestination = ip; + this.wildcards = (wildcards & ~OFPFW_NW_DST_MASK) + | (mask << OFPFW_NW_DST_SHIFT); + } else if (which.equals(STR_NW_SRC)) { + this.networkSource = ip; + this.wildcards = (wildcards & ~OFPFW_NW_SRC_MASK) + | (mask << OFPFW_NW_SRC_SHIFT); + } + } + + protected static String ipToString(int ip) { + return Integer.toString(U8.f((byte) ((ip & 0xff000000) >> 24))) + + "." + Integer.toString((ip & 0x00ff0000) >> 16) + "." + + Integer.toString((ip & 0x0000ff00) >> 8) + "." + + Integer.toString(ip & 0x000000ff); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java new file mode 100644 index 00000000..16a813f1 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java @@ -0,0 +1,106 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.LinkedList; +import java.util.List; + +/** + * Extra info for how to treat OFMatch as a JavaBean + * + * For some (inane!) reason, using chained setters in OFMatch breaks a lot of the JavaBean defaults. + * + * We don't really use OFMatch as a java bean, but there are a lot of nice XML utils that work for + * free if OFMatch follows the java bean paradigm. + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ + +public class OFMatchBeanInfo extends SimpleBeanInfo { + + @Override + public PropertyDescriptor[] getPropertyDescriptors() { + List descs = new LinkedList(); + Field[] fields = OFMatch.class.getDeclaredFields(); + String name; + for (int i=0; i< fields.length; i++) { + int mod = fields[i].getModifiers(); + if(Modifier.isFinal(mod) || // don't expose static or final fields + Modifier.isStatic(mod)) + continue; + + name = fields[i].getName(); + Class type = fields[i].getType(); + + try { + descs.add(new PropertyDescriptor(name, + name2getter(OFMatch.class, name), + name2setter(OFMatch.class, name, type))); + } catch (IntrospectionException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + return descs.toArray(new PropertyDescriptor[0]); + } + + + private Method name2setter(Class c, String name, Class type) { + String mName = "set" + toLeadingCaps(name); + Method m = null; + try { + m = c.getMethod(mName, new Class[]{ type}); + } catch (SecurityException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return m; + } + + private Method name2getter(Class c, String name) { + String mName= "get" + toLeadingCaps(name); + Method m = null; + try { + m = c.getMethod(mName, new Class[]{}); + } catch (SecurityException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return m; + } + + private String toLeadingCaps(String s) { + char[] array = s.toCharArray(); + array[0] = Character.toUpperCase(array[0]); + return String.valueOf(array, 0, array.length); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java new file mode 100644 index 00000000..0caf9ff1 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java @@ -0,0 +1,55 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package org.openflow.protocol; + +import org.openflow.util.HexString; + +public class OFMatchWithSwDpid { + protected OFMatch ofMatch; + protected long switchDataPathId; + + public OFMatchWithSwDpid() { + this.ofMatch = new OFMatch(); + this.switchDataPathId = 0; + } + + public OFMatchWithSwDpid(OFMatch ofm, long swDpid) { + this.ofMatch = ofm.clone(); + this.switchDataPathId = swDpid; + } + public OFMatch getOfMatch() { + return ofMatch; + } + + public void setOfMatch(OFMatch ofMatch) { + this.ofMatch = ofMatch.clone(); + } + + public long getSwitchDataPathId() { + return this.switchDataPathId; + } + + public OFMatchWithSwDpid setSwitchDataPathId(long dpid) { + this.switchDataPathId = dpid; + return this; + } + + @Override + public String toString() { + return "OFMatchWithSwDpid [" + HexString.toHexString(switchDataPathId) + ofMatch + "]"; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessage.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessage.java new file mode 100644 index 00000000..767b7d18 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessage.java @@ -0,0 +1,324 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.ConcurrentHashMap; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.HexString; +import org.openflow.util.U16; +import org.openflow.util.U32; +import org.openflow.util.U8; + +/** + * The base class for all OpenFlow protocol messages. This class contains the + * equivalent of the ofp_header which is present in all OpenFlow messages. + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Feb 3, 2010 + * @author Rob Sherwood (rob.sherwood@stanford.edu) - Feb 3, 2010 + */ +public class OFMessage { + public static final int MAXIMUM_LENGTH = (1 << 16) - 1; + public static byte OFP_VERSION = 0x01; + public static int MINIMUM_LENGTH = 8; + + protected byte version; + protected OFType type; + protected short length; + protected int xid; + + private ConcurrentHashMap storage; + + public OFMessage() { + storage = null; + this.version = OFP_VERSION; + } + + protected synchronized ConcurrentHashMap getMessageStore() { + if (storage == null) { + storage = new ConcurrentHashMap();; + } + return storage; + } + + /** + * Get the length of this message + * + * @return + */ + public short getLength() { + return length; + } + + /** + * Get the length of this message, unsigned + * + * @return + */ + public int getLengthU() { + return U16.f(length); + } + + /** + * Set the length of this message + * + * @param length + */ + public OFMessage setLength(short length) { + this.length = length; + return this; + } + + /** + * Set the length of this message, unsigned + * + * @param length + */ + public OFMessage setLengthU(int length) { + this.length = U16.t(length); + return this; + } + + /** + * Get the type of this message + * + * @return + */ + public OFType getType() { + return type; + } + + /** + * Set the type of this message + * + * @param type + */ + public void setType(OFType type) { + this.type = type; + } + + /** + * Get the OpenFlow version of this message + * + * @return + */ + public byte getVersion() { + return version; + } + + /** + * Set the OpenFlow version of this message + * + * @param version + */ + public void setVersion(byte version) { + this.version = version; + } + + /** + * Get the transaction id of this message + * + * @return + */ + public int getXid() { + return xid; + } + + /** + * Set the transaction id of this message + * + * @param xid + */ + public void setXid(int xid) { + this.xid = xid; + } + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data) { + this.version = data.readByte(); + this.type = OFType.valueOf(data.readByte()); + this.length = data.readShort(); + this.xid = data.readInt(); + } + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeByte(version); + data.writeByte(type.getTypeValue()); + data.writeShort(length); + data.writeInt(xid); + } + + /** + * Returns a summary of the message + * @return "ofmsg=v=$version;t=$type:l=$len:xid=$xid" + */ + @Override + public String toString() { + return "ofmsg" + + ":v=" + U8.f(this.getVersion()) + + ";t=" + this.getType() + + ";l=" + this.getLengthU() + + ";x=" + U32.f(this.getXid()); + } + + @Override + public int hashCode() { + final int prime = 97; + int result = 1; + result = prime * result + length; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + version; + result = prime * result + xid; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFMessage)) { + return false; + } + OFMessage other = (OFMessage) obj; + if (length != other.length) { + return false; + } + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + if (version != other.version) { + return false; + } + if (xid != other.xid) { + return false; + } + return true; + } + + /* + public static String getDataAsString(IOFSwitch sw, OFMessage msg) { + + + Ethernet eth; + StringBuffer sb = new StringBuffer(""); + + DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + Date date = new Date(); + + sb.append(dateFormat.format(date)); + sb.append(" "); + + switch (msg.getType()) { + case PACKET_IN: + OFPacketIn pktIn = (OFPacketIn) msg; + sb.append("packet_in [ "); + sb.append(sw.getStringId()); + sb.append(" -> Controller"); + sb.append(" ]"); + + sb.append("\ntotal length: "); + sb.append(pktIn.getTotalLength()); + sb.append("\nin_port: "); + sb.append(pktIn.getInPort()); + sb.append("\ndata_length: "); + sb.append(pktIn.getTotalLength() - OFPacketIn.MINIMUM_LENGTH); + sb.append("\nbuffer: "); + sb.append(pktIn.getBufferId()); + + break; + + case PACKET_OUT: + OFPacketOut pktOut = (OFPacketOut) msg; + sb.append("packet_out [ "); + sb.append("Controller -> "); + sb.append(HexString.toHexString(sw.getId())); + sb.append(" ]"); + + sb.append("\nin_port: "); + sb.append(pktOut.getInPort()); + sb.append("\nactions_len: "); + sb.append(pktOut.getActionsLength()); + if (pktOut.getActions() != null) { + sb.append("\nactions: "); + sb.append(pktOut.getActions().toString()); + } + break; + + case FLOW_MOD: + OFFlowMod fm = (OFFlowMod) msg; + sb.append("flow_mod [ "); + sb.append("Controller -> "); + sb.append(HexString.toHexString(sw.getId())); + sb.append(" ]"); + + + sb.append("\nADD: cookie: "); + sb.append(fm.getCookie()); + sb.append(" idle: "); + sb.append(fm.getIdleTimeout()); + sb.append(" hard: "); + sb.append(fm.getHardTimeout()); + sb.append(" pri: "); + sb.append(fm.getPriority()); + sb.append(" buf: "); + sb.append(fm.getBufferId()); + sb.append(" flg: "); + sb.append(fm.getFlags()); + if (fm.getActions() != null) { + sb.append("\nactions: "); + sb.append(fm.getActions().toString()); + } + break; + + default: + sb.append("[Unknown Packet]"); + } + + sb.append("\n\n"); + return sb.toString(); + + } + */ + + + // Check if this is really required + /* + public static byte[] getData(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { + return OFMessage.getDataAsString(sw, msg, cntx).getBytes(); + } + */ +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessageContextStore.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessageContextStore.java new file mode 100644 index 00000000..b60aa1cd --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFMessageContextStore.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.protocol.OFMessage; + +public class OFMessageContextStore { + protected OFMessage msg; + String namespace; + + public OFMessageContextStore(OFMessage msg, String namespace) { + this.msg = msg; + this.namespace = namespace; + } + + @SuppressWarnings("unchecked") + public V get(String key) { + return (V)msg.getMessageStore().get(namespace + "|" + key); + } + + public void put(String key, V value) { + msg.getMessageStore().put(namespace + "|" + key, value); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketIn.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketIn.java new file mode 100644 index 00000000..c37c9184 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketIn.java @@ -0,0 +1,211 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; +import org.openflow.util.U32; +import org.openflow.util.U8; + +/** + * Represents an ofp_packet_in + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Feb 8, 2010 + */ +public class OFPacketIn extends OFMessage { + public static short MINIMUM_LENGTH = 18; + + public enum OFPacketInReason { + NO_MATCH, ACTION + } + + protected int bufferId; + protected short totalLength; + protected short inPort; + protected OFPacketInReason reason; + protected byte[] packetData; + + public OFPacketIn() { + super(); + this.type = OFType.PACKET_IN; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * Get buffer_id + * @return + */ + public int getBufferId() { + return this.bufferId; + } + + /** + * Set buffer_id + * @param bufferId + */ + public OFPacketIn setBufferId(int bufferId) { + this.bufferId = bufferId; + return this; + } + + /** + * Returns the packet data + * @return + */ + public byte[] getPacketData() { + return this.packetData; + } + + /** + * Sets the packet data, and updates the length of this message + * @param packetData + */ + public OFPacketIn setPacketData(byte[] packetData) { + this.packetData = packetData; + this.length = U16.t(OFPacketIn.MINIMUM_LENGTH + packetData.length); + return this; + } + + /** + * Get in_port + * @return + */ + public short getInPort() { + return this.inPort; + } + + /** + * Set in_port + * @param inPort + */ + public OFPacketIn setInPort(short inPort) { + this.inPort = inPort; + return this; + } + + /** + * Get reason + * @return + */ + public OFPacketInReason getReason() { + return this.reason; + } + + /** + * Set reason + * @param reason + */ + public OFPacketIn setReason(OFPacketInReason reason) { + this.reason = reason; + return this; + } + + /** + * Get total_len + * @return + */ + public short getTotalLength() { + return this.totalLength; + } + + /** + * Set total_len + * @param totalLength + */ + public OFPacketIn setTotalLength(short totalLength) { + this.totalLength = totalLength; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.bufferId = data.readInt(); + this.totalLength = data.readShort(); + this.inPort = data.readShort(); + this.reason = OFPacketInReason.values()[U8.f(data.readByte())]; + data.readByte(); // pad + this.packetData = new byte[getLengthU() - MINIMUM_LENGTH]; + data.readBytes(this.packetData); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(bufferId); + data.writeShort(totalLength); + data.writeShort(inPort); + data.writeByte((byte) reason.ordinal()); + data.writeByte((byte) 0x0); // pad + data.writeBytes(this.packetData); + } + + @Override + public int hashCode() { + final int prime = 283; + int result = super.hashCode(); + result = prime * result + bufferId; + result = prime * result + inPort; + result = prime * result + Arrays.hashCode(packetData); + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + result = prime * result + totalLength; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPacketIn)) { + return false; + } + OFPacketIn other = (OFPacketIn) obj; + if (bufferId != other.bufferId) { + return false; + } + if (inPort != other.inPort) { + return false; + } + if (!Arrays.equals(packetData, other.packetData)) { + return false; + } + if (reason == null) { + if (other.reason != null) { + return false; + } + } else if (!reason.equals(other.reason)) { + return false; + } + if (totalLength != other.totalLength) { + return false; + } + return true; + } + + public String toString() { + String myStr = super.toString(); + return "packetIn" + + ":bufferId=" + U32.f(this.bufferId) + myStr; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketOut.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketOut.java new file mode 100644 index 00000000..ef4aa61f --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketOut.java @@ -0,0 +1,260 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.Arrays; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.factory.OFActionFactory; +import org.openflow.protocol.factory.OFActionFactoryAware; +import org.openflow.util.HexString; +import org.openflow.util.U16; + +/** + * Represents an ofp_packet_out message + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 12, 2010 + */ +public class OFPacketOut extends OFMessage implements OFActionFactoryAware { + public static int MINIMUM_LENGTH = 16; + public static int BUFFER_ID_NONE = 0xffffffff; + + protected OFActionFactory actionFactory; + protected int bufferId; + protected short inPort; + protected short actionsLength; + protected List actions; + protected byte[] packetData; + + public OFPacketOut() { + super(); + this.type = OFType.PACKET_OUT; + this.length = U16.t(MINIMUM_LENGTH); + this.bufferId = BUFFER_ID_NONE; + } + + /** + * Get buffer_id + * @return + */ + public int getBufferId() { + return this.bufferId; + } + + /** + * Set buffer_id + * @param bufferId + */ + public OFPacketOut setBufferId(int bufferId) { + if (packetData != null && packetData.length > 0 && bufferId != BUFFER_ID_NONE) { + throw new IllegalArgumentException( + "PacketOut should not have both bufferId and packetData set"); + } + this.bufferId = bufferId; + return this; + } + + /** + * Returns the packet data + * @return + */ + public byte[] getPacketData() { + return this.packetData; + } + + /** + * Sets the packet data + * @param packetData + */ + public OFPacketOut setPacketData(byte[] packetData) { + if (packetData != null && packetData.length > 0 && bufferId != BUFFER_ID_NONE) { + throw new IllegalArgumentException( + "PacketOut should not have both bufferId and packetData set"); + } + this.packetData = packetData; + return this; + } + + /** + * Get in_port + * @return + */ + public short getInPort() { + return this.inPort; + } + + /** + * Set in_port + * @param inPort + */ + public OFPacketOut setInPort(short inPort) { + this.inPort = inPort; + return this; + } + + /** + * Set in_port. Convenience method using OFPort enum. + * @param inPort + */ + public OFPacketOut setInPort(OFPort inPort) { + this.inPort = inPort.getValue(); + return this; + } + + /** + * Get actions_len + * @return + */ + public short getActionsLength() { + return this.actionsLength; + } + + /** + * Get actions_len, unsigned + * @return + */ + public int getActionsLengthU() { + return U16.f(this.actionsLength); + } + + /** + * Set actions_len + * @param actionsLength + */ + public OFPacketOut setActionsLength(short actionsLength) { + this.actionsLength = actionsLength; + return this; + } + + /** + * Returns the actions contained in this message + * @return a list of ordered OFAction objects + */ + public List getActions() { + return this.actions; + } + + /** + * Sets the list of actions on this message + * @param actions a list of ordered OFAction objects + */ + public OFPacketOut setActions(List actions) { + this.actions = actions; + return this; + } + + @Override + public void setActionFactory(OFActionFactory actionFactory) { + this.actionFactory = actionFactory; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.bufferId = data.readInt(); + this.inPort = data.readShort(); + this.actionsLength = data.readShort(); + if ( this.actionFactory == null) + throw new RuntimeException("ActionFactory not set"); + this.actions = this.actionFactory.parseActions(data, getActionsLengthU()); + this.packetData = new byte[getLengthU() - MINIMUM_LENGTH - getActionsLengthU()]; + data.readBytes(this.packetData); + validate(); + } + + @Override + public void writeTo(ChannelBuffer data) { + validate(); + super.writeTo(data); + data.writeInt(bufferId); + data.writeShort(inPort); + data.writeShort(actionsLength); + for (OFAction action : actions) { + action.writeTo(data); + } + if (this.packetData != null) + data.writeBytes(this.packetData); + } + + /** validate the invariants of this OFMessage hold */ + public void validate() { + if (!((bufferId != BUFFER_ID_NONE) ^ (packetData != null && packetData.length > 0))) { + throw new IllegalStateException( + "OFPacketOut must have exactly one of (bufferId, packetData) set (not one, not both)"); + } + } + + @Override + public int hashCode() { + final int prime = 293; + int result = super.hashCode(); + result = prime * result + ((actions == null) ? 0 : actions.hashCode()); + result = prime * result + actionsLength; + result = prime * result + bufferId; + result = prime * result + inPort; + result = prime * result + Arrays.hashCode(packetData); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPacketOut)) { + return false; + } + OFPacketOut other = (OFPacketOut) obj; + if (actions == null) { + if (other.actions != null) { + return false; + } + } else if (!actions.equals(other.actions)) { + return false; + } + if (actionsLength != other.actionsLength) { + return false; + } + if (bufferId != other.bufferId) { + return false; + } + if (inPort != other.inPort) { + return false; + } + if (!Arrays.equals(packetData, other.packetData)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "OFPacketOut [actionFactory=" + actionFactory + ", actions=" + + actions + ", actionsLength=" + actionsLength + ", bufferId=0x" + + Integer.toHexString(bufferId) + ", inPort=" + inPort + ", packetData=" + + HexString.toHexString(packetData) + "]"; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketQueue.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketQueue.java new file mode 100644 index 00000000..e8de1af5 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPacketQueue.java @@ -0,0 +1,142 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents ofp_packet_queue + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFPacketQueue { + public static int MINIMUM_LENGTH = 8; + + protected int queueId; + protected short length; + protected List properties = new ArrayList(); + + public OFPacketQueue() { + this.queueId = -1; + this.length = U16.t(MINIMUM_LENGTH); + } + + public OFPacketQueue(int queueId) { + this.queueId = queueId; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the queueId + */ + public long getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + /** + * @return the queue's properties + */ + public List getProperties() { + return properties; + } + + /** + * @param properties the properties to set + */ + public void setProperties(List properties) { + this.properties = properties; + + this.length = U16.t(MINIMUM_LENGTH); + for (OFQueueProp prop : properties) { + this.length += prop.getLength(); + } + } + + /** + * @return the length + */ + public short getLength() { + return length; + } + + public void readFrom(ChannelBuffer data) { + this.queueId = data.readInt(); + this.length = data.readShort(); + data.readShort(); // pad + + int availLength = (this.length - MINIMUM_LENGTH); + this.properties.clear(); + + while (availLength > 0) { + OFQueueProp prop = new OFQueueProp(); + prop.readFrom(data); + properties.add(prop); + availLength -= prop.getLength(); + } + } + + public void writeTo(ChannelBuffer data) { + data.writeInt(queueId); + data.writeShort(length); + data.writeShort(0); // pad + + for (OFQueueProp prop : properties) { + prop.writeTo(data); + } + } + + @Override + public int hashCode() { + final int prime = 359; + int result = super.hashCode(); + result = prime * result + queueId; + result = prime * result + length; + result = prime * result + properties.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPacketQueue)) { + return false; + } + OFPacketQueue other = (OFPacketQueue) obj; + if (queueId != other.queueId) { + return false; + } + if (! properties.equals(other.properties)) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPhysicalPort.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPhysicalPort.java new file mode 100644 index 00000000..da0da615 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPhysicalPort.java @@ -0,0 +1,465 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents ofp_phy_port + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 25, 2010 + */ +public class OFPhysicalPort { + public static int MINIMUM_LENGTH = 48; + public static int OFP_ETH_ALEN = 6; + + public enum OFPortConfig { + OFPPC_PORT_DOWN (1 << 0) { + public String toString() { + return "port-down (0x1)"; + } + }, + OFPPC_NO_STP (1 << 1) { + public String toString() { + return "no-stp (0x2)"; + } + }, + OFPPC_NO_RECV (1 << 2) { + public String toString() { + return "no-recv (0x4)"; + } + }, + OFPPC_NO_RECV_STP (1 << 3) { + public String toString() { + return "no-recv-stp (0x8)"; + } + }, + OFPPC_NO_FLOOD (1 << 4) { + public String toString() { + return "no-flood (0x10)"; + } + }, + OFPPC_NO_FWD (1 << 5) { + public String toString() { + return "no-fwd (0x20)"; + } + }, + OFPPC_NO_PACKET_IN (1 << 6) { + public String toString() { + return "no-pkt-in (0x40)"; + } + }; + + protected int value; + + private OFPortConfig(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + } + + public enum OFPortState { + OFPPS_LINK_DOWN (1 << 0) { + public String toString() { + return "link-down (0x1)"; + } + }, + OFPPS_STP_LISTEN (0 << 8) { + public String toString() { + return "listen (0x0)"; + } + }, + OFPPS_STP_LEARN (1 << 8) { + public String toString() { + return "learn-no-relay (0x100)"; + } + }, + OFPPS_STP_FORWARD (2 << 8) { + public String toString() { + return "forward (0x200)"; + } + }, + OFPPS_STP_BLOCK (3 << 8) { + public String toString() { + return "block-broadcast (0x300)"; + } + }, + OFPPS_STP_MASK (3 << 8) { + public String toString() { + return "block-broadcast (0x300)"; + } + }; + + protected int value; + + private OFPortState(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + } + + public enum OFPortFeatures { + OFPPF_10MB_HD (1 << 0) { + public String toString() { + return "10mb-hd (0x1)"; + } + }, + OFPPF_10MB_FD (1 << 1) { + public String toString() { + return "10mb-fd (0x2)"; + } + }, + OFPPF_100MB_HD (1 << 2) { + public String toString() { + return "100mb-hd (0x4)"; + } + }, + OFPPF_100MB_FD (1 << 3) { + public String toString() { + return "100mb-fd (0x8)"; + } + }, + OFPPF_1GB_HD (1 << 4) { + public String toString() { + return "1gb-hd (0x10)"; + } + }, + OFPPF_1GB_FD (1 << 5) { + public String toString() { + return "1gb-fd (0x20)"; + } + }, + OFPPF_10GB_FD (1 << 6) { + public String toString() { + return "10gb-fd (0x40)"; + } + }, + OFPPF_COPPER (1 << 7) { + public String toString() { + return "copper (0x80)"; + } + }, + OFPPF_FIBER (1 << 8) { + public String toString() { + return "fiber (0x100)"; + } + }, + OFPPF_AUTONEG (1 << 9) { + public String toString() { + return "autoneg (0x200)"; + } + }, + OFPPF_PAUSE (1 << 10) { + public String toString() { + return "pause (0x400)"; + } + }, + OFPPF_PAUSE_ASYM (1 << 11) { + public String toString() { + return "pause-asym (0x800)"; + } + }; + + protected int value; + + private OFPortFeatures(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + } + + protected short portNumber; + protected byte[] hardwareAddress; + protected String name; + protected int config; + protected int state; + protected int currentFeatures; + protected int advertisedFeatures; + protected int supportedFeatures; + protected int peerFeatures; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the hardwareAddress + */ + public byte[] getHardwareAddress() { + return hardwareAddress; + } + + /** + * @param hardwareAddress the hardwareAddress to set + */ + public void setHardwareAddress(byte[] hardwareAddress) { + if (hardwareAddress.length != OFP_ETH_ALEN) + throw new RuntimeException("Hardware address must have length " + + OFP_ETH_ALEN); + this.hardwareAddress = hardwareAddress; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the config + */ + public int getConfig() { + return config; + } + + /** + * @param config the config to set + */ + public void setConfig(int config) { + this.config = config; + } + + /** + * @return the state + */ + public int getState() { + return state; + } + + /** + * @param state the state to set + */ + public void setState(int state) { + this.state = state; + } + + /** + * @return the currentFeatures + */ + public int getCurrentFeatures() { + return currentFeatures; + } + + /** + * @param currentFeatures the currentFeatures to set + */ + public void setCurrentFeatures(int currentFeatures) { + this.currentFeatures = currentFeatures; + } + + /** + * @return the advertisedFeatures + */ + public int getAdvertisedFeatures() { + return advertisedFeatures; + } + + /** + * @param advertisedFeatures the advertisedFeatures to set + */ + public void setAdvertisedFeatures(int advertisedFeatures) { + this.advertisedFeatures = advertisedFeatures; + } + + /** + * @return the supportedFeatures + */ + public int getSupportedFeatures() { + return supportedFeatures; + } + + /** + * @param supportedFeatures the supportedFeatures to set + */ + public void setSupportedFeatures(int supportedFeatures) { + this.supportedFeatures = supportedFeatures; + } + + /** + * @return the peerFeatures + */ + public int getPeerFeatures() { + return peerFeatures; + } + + /** + * @param peerFeatures the peerFeatures to set + */ + public void setPeerFeatures(int peerFeatures) { + this.peerFeatures = peerFeatures; + } + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + if (this.hardwareAddress == null) + this.hardwareAddress = new byte[OFP_ETH_ALEN]; + data.readBytes(this.hardwareAddress); + byte[] name = new byte[16]; + data.readBytes(name); + // find the first index of 0 + int index = 0; + for (byte b : name) { + if (0 == b) + break; + ++index; + } + this.name = new String(Arrays.copyOf(name, index), + Charset.forName("ascii")); + this.config = data.readInt(); + this.state = data.readInt(); + this.currentFeatures = data.readInt(); + this.advertisedFeatures = data.readInt(); + this.supportedFeatures = data.readInt(); + this.peerFeatures = data.readInt(); + } + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeBytes(hardwareAddress); + try { + byte[] name = this.name.getBytes("ASCII"); + if (name.length < 16) { + data.writeBytes(name); + for (int i = name.length; i < 16; ++i) { + data.writeByte((byte) 0); + } + } else { + data.writeBytes(name, 0, 15); + data.writeByte((byte) 0); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + data.writeInt(this.config); + data.writeInt(this.state); + data.writeInt(this.currentFeatures); + data.writeInt(this.advertisedFeatures); + data.writeInt(this.supportedFeatures); + data.writeInt(this.peerFeatures); + } + + @Override + public int hashCode() { + final int prime = 307; + int result = 1; + result = prime * result + advertisedFeatures; + result = prime * result + config; + result = prime * result + currentFeatures; + result = prime * result + Arrays.hashCode(hardwareAddress); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + peerFeatures; + result = prime * result + portNumber; + result = prime * result + state; + result = prime * result + supportedFeatures; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFPhysicalPort)) { + return false; + } + OFPhysicalPort other = (OFPhysicalPort) obj; + if (advertisedFeatures != other.advertisedFeatures) { + return false; + } + if (config != other.config) { + return false; + } + if (currentFeatures != other.currentFeatures) { + return false; + } + if (!Arrays.equals(hardwareAddress, other.hardwareAddress)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (peerFeatures != other.peerFeatures) { + return false; + } + if (portNumber != other.portNumber) { + return false; + } + if (state != other.state) { + return false; + } + if (supportedFeatures != other.supportedFeatures) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPort.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPort.java new file mode 100644 index 00000000..93301bcb --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPort.java @@ -0,0 +1,43 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +public enum OFPort { + OFPP_MAX ((short)0xff00), + OFPP_IN_PORT ((short)0xfff8), + OFPP_TABLE ((short)0xfff9), + OFPP_NORMAL ((short)0xfffa), + OFPP_FLOOD ((short)0xfffb), + OFPP_ALL ((short)0xfffc), + OFPP_CONTROLLER ((short)0xfffd), + OFPP_LOCAL ((short)0xfffe), + OFPP_NONE ((short)0xffff); + + protected short value; + + private OFPort(short value) { + this.value = value; + } + + /** + * @return the value + */ + public short getValue() { + return value; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortMod.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortMod.java new file mode 100644 index 00000000..876e8567 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortMod.java @@ -0,0 +1,182 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_port_mod message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortMod extends OFMessage { + public static int MINIMUM_LENGTH = 32; + + protected short portNumber; + protected byte[] hardwareAddress; + protected int config; + protected int mask; + protected int advertise; + + public OFPortMod() { + super(); + this.type = OFType.PORT_MOD; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the hardwareAddress + */ + public byte[] getHardwareAddress() { + return hardwareAddress; + } + + /** + * @param hardwareAddress the hardwareAddress to set + */ + public void setHardwareAddress(byte[] hardwareAddress) { + if (hardwareAddress.length != OFPhysicalPort.OFP_ETH_ALEN) + throw new RuntimeException("Hardware address must have length " + + OFPhysicalPort.OFP_ETH_ALEN); + this.hardwareAddress = hardwareAddress; + } + + /** + * @return the config + */ + public int getConfig() { + return config; + } + + /** + * @param config the config to set + */ + public void setConfig(int config) { + this.config = config; + } + + /** + * @return the mask + */ + public int getMask() { + return mask; + } + + /** + * @param mask the mask to set + */ + public void setMask(int mask) { + this.mask = mask; + } + + /** + * @return the advertise + */ + public int getAdvertise() { + return advertise; + } + + /** + * @param advertise the advertise to set + */ + public void setAdvertise(int advertise) { + this.advertise = advertise; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.portNumber = data.readShort(); + if (this.hardwareAddress == null) + this.hardwareAddress = new byte[OFPhysicalPort.OFP_ETH_ALEN]; + data.readBytes(this.hardwareAddress); + this.config = data.readInt(); + this.mask = data.readInt(); + this.advertise = data.readInt(); + data.readInt(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeBytes(this.hardwareAddress); + data.writeInt(this.config); + data.writeInt(this.mask); + data.writeInt(this.advertise); + data.writeInt(0); // pad + } + + @Override + public int hashCode() { + final int prime = 311; + int result = super.hashCode(); + result = prime * result + advertise; + result = prime * result + config; + result = prime * result + Arrays.hashCode(hardwareAddress); + result = prime * result + mask; + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPortMod)) { + return false; + } + OFPortMod other = (OFPortMod) obj; + if (advertise != other.advertise) { + return false; + } + if (config != other.config) { + return false; + } + if (!Arrays.equals(hardwareAddress, other.hardwareAddress)) { + return false; + } + if (mask != other.mask) { + return false; + } + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortStatus.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortStatus.java new file mode 100644 index 00000000..8bde6e78 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFPortStatus.java @@ -0,0 +1,126 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_port_status message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortStatus extends OFMessage { + public static int MINIMUM_LENGTH = 64; + + public enum OFPortReason { + OFPPR_ADD, + OFPPR_DELETE, + OFPPR_MODIFY + } + + protected byte reason; + protected OFPhysicalPort desc; + + /** + * @return the reason + */ + public byte getReason() { + return reason; + } + + /** + * @param reason the reason to set + */ + public void setReason(byte reason) { + this.reason = reason; + } + + /** + * @return the desc + */ + public OFPhysicalPort getDesc() { + return desc; + } + + /** + * @param desc the desc to set + */ + public void setDesc(OFPhysicalPort desc) { + this.desc = desc; + } + + public OFPortStatus() { + super(); + this.type = OFType.PORT_STATUS; + this.length = U16.t(MINIMUM_LENGTH); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.reason = data.readByte(); + data.readerIndex(data.readerIndex() + 7); // skip 7 bytes of padding + if (this.desc == null) + this.desc = new OFPhysicalPort(); + this.desc.readFrom(data); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(this.reason); + for (int i = 0; i < 7; ++i) + data.writeByte((byte) 0); + this.desc.writeTo(data); + } + + @Override + public int hashCode() { + final int prime = 313; + int result = super.hashCode(); + result = prime * result + ((desc == null) ? 0 : desc.hashCode()); + result = prime * result + reason; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPortStatus)) { + return false; + } + OFPortStatus other = (OFPortStatus) obj; + if (desc == null) { + if (other.desc != null) { + return false; + } + } else if (!desc.equals(other.desc)) { + return false; + } + if (reason != other.reason) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java new file mode 100644 index 00000000..62be90d5 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java @@ -0,0 +1,125 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_queue_get_config_request message + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueGetConfigReply extends OFMessage { + public static int MINIMUM_LENGTH = 16; + + protected short portNumber; + protected List queues = new ArrayList(); + + public OFQueueGetConfigReply() { + super(); + this.type = OFType.QUEUE_GET_CONFIG_REPLY; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the port's queues + */ + public List getQueues() { + return queues; + } + + /** + * @param queues the queues to set + */ + public void setQueues(List queues) { + this.queues.clear(); + this.queues.addAll(queues); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.portNumber = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + + int availLength = (this.length - MINIMUM_LENGTH); + this.queues.clear(); + + while (availLength > 0) { + OFPacketQueue queue = new OFPacketQueue(); + queue.readFrom(data); + queues.add(queue); + availLength -= queue.getLength(); + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeInt(0); // pad + data.writeShort(0); // pad + + for (OFPacketQueue queue : queues) { + queue.writeTo(data); + } + } + + @Override + public int hashCode() { + final int prime = 349; + int result = super.hashCode(); + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFQueueGetConfigReply)) { + return false; + } + OFQueueGetConfigReply other = (OFQueueGetConfigReply) obj; + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java new file mode 100644 index 00000000..cbb4a373 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java @@ -0,0 +1,95 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_queue_get_config_request message + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueGetConfigRequest extends OFMessage { + public static int MINIMUM_LENGTH = 12; + + protected short portNumber; + + public OFQueueGetConfigRequest(short portNumber) { + super(); + this.type = OFType.QUEUE_GET_CONFIG_REQUEST; + this.length = U16.t(MINIMUM_LENGTH); + this.portNumber = portNumber; + } + + public OFQueueGetConfigRequest() { + this((short) 0); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.portNumber = data.readShort(); + data.readShort(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeShort(0); // pad + } + + @Override + public int hashCode() { + final int prime = 347; + int result = super.hashCode(); + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFQueueGetConfigRequest)) { + return false; + } + OFQueueGetConfigRequest other = (OFQueueGetConfigRequest) obj; + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueProp.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueProp.java new file mode 100644 index 00000000..2e12224b --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFQueueProp.java @@ -0,0 +1,175 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +public class OFQueueProp { + private int NONE_MINIMUM_LENGTH = 8; + private int RATE_MINIMUM_LENGTH = 16; + + public enum OFQueuePropType { + OFPQT_NONE (0), + OFPQT_MIN_RATE (1), + OFPQT_MAX_RATE (2); + + protected int value; + + private OFQueuePropType(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + + public static OFQueuePropType fromShort(short x) { + switch (x) { + case 0: + return OFPQT_NONE; + case 1: + return OFPQT_MIN_RATE; + case 2: + return OFPQT_MAX_RATE; + } + return null; + } + } + + protected OFQueuePropType type; + protected short length; + protected short rate = -1; // not valid if type == OFPQT_NONE + + public OFQueueProp() { + this.type = OFQueuePropType.OFPQT_NONE; + this.length = U16.t(NONE_MINIMUM_LENGTH); + } + + /** + * @return the type + */ + public OFQueuePropType getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(OFQueuePropType type) { + this.type = type; + + switch (type) { + case OFPQT_NONE: + this.length = U16.t(NONE_MINIMUM_LENGTH); + break; + case OFPQT_MIN_RATE: + this.length = U16.t(RATE_MINIMUM_LENGTH); + break; + case OFPQT_MAX_RATE: + this.length = U16.t(RATE_MINIMUM_LENGTH); + break; + } + } + + /** + * @return the rate + */ + public short getRate() { + return rate; + } + + /** + * @param rate the rate to set + */ + public void setRate(short rate) { + this.rate = rate; + } + + /** + * @return the length + */ + public short getLength() { + return length; + } + + public void readFrom(ChannelBuffer data) { + this.type = OFQueuePropType.fromShort(data.readShort()); + this.length = data.readShort(); + data.readInt(); // pad + + if (this.type == OFQueuePropType.OFPQT_MIN_RATE || + this.type == OFQueuePropType.OFPQT_MAX_RATE) { + assert(this.length == RATE_MINIMUM_LENGTH); + + this.rate = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + } else { + assert(this.length == NONE_MINIMUM_LENGTH); + } + } + + public void writeTo(ChannelBuffer data) { + data.writeShort(this.type.getValue()); + data.writeShort(this.length); + data.writeInt(0); // pad + + if (this.type == OFQueuePropType.OFPQT_MIN_RATE || + this.type == OFQueuePropType.OFPQT_MAX_RATE) { + data.writeShort(this.rate); + data.writeInt(0); // pad + data.writeShort(0); // pad + } + } + + @Override + public int hashCode() { + final int prime = 353; + int result = super.hashCode(); + result = prime * result + type.getValue(); + result = prime * result + rate; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFQueueProp)) { + return false; + } + OFQueueProp other = (OFQueueProp) obj; + if (type != other.type) { + return false; + } + if (type == OFQueuePropType.OFPQT_MIN_RATE || + type == OFQueuePropType.OFPQT_MAX_RATE) { + if (rate != other.rate) { + return false; + } + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSetConfig.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSetConfig.java new file mode 100644 index 00000000..4b235647 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSetConfig.java @@ -0,0 +1,29 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +/** + * Represents an OFPT_SET_CONFIG type message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFSetConfig extends OFSwitchConfig { + public OFSetConfig() { + super(); + this.type = OFType.SET_CONFIG; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java new file mode 100644 index 00000000..e5a9c01e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java @@ -0,0 +1,180 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.factory.OFStatisticsFactory; +import org.openflow.protocol.factory.OFStatisticsFactoryAware; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; + + +/** + * Base class for statistics requests/replies + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 27, 2010 + */ +public abstract class OFStatisticsMessageBase extends OFMessage implements + OFStatisticsFactoryAware { + public static int MINIMUM_LENGTH = 12; + + protected OFStatisticsFactory statisticsFactory; + protected OFStatisticsType statisticType; + protected short flags; + + // TODO: this should be List, to + // allow for type safe assignments of lists of specific message + protected List statistics; + + /** + * @return the statisticType + */ + public OFStatisticsType getStatisticType() { + return statisticType; + } + + /** + * @param statisticType the statisticType to set + */ + public void setStatisticType(OFStatisticsType statisticType) { + this.statisticType = statisticType; + } + + /** + * @return the flags + */ + public short getFlags() { + return flags; + } + + /** + * @param flags the flags to set + */ + public void setFlags(short flags) { + this.flags = flags; + } + + /** + * @return the statistics + */ + public List getStatistics() { + return statistics; + } + + /** + * return the first statistics request in the list of statistics, for + * statistics messages that expect exactly one message in their body (e.g., + * flow stats request, port statsrequest) + * + * @return the first and only element in the list of statistics + * @throw IllegalArgumentException if the list does not contain exactly one + * element + */ + public OFStatistics getFirstStatistics() { + if (statistics == null ) { + throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" is null"); + } + if (statistics.size() != 1) { + throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" contains "+statistics.size() +" statreq/reply messages in its body (should be 1)"); + } + + return statistics.get(0); + } + + /** + * @param statistics the statistics to set + */ + public void setStatistics(List statistics) { + this.statistics = statistics; + } + + @Override + public void setStatisticsFactory(OFStatisticsFactory statisticsFactory) { + this.statisticsFactory = statisticsFactory; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.statisticType = OFStatisticsType.valueOf(data.readShort(), this + .getType()); + this.flags = data.readShort(); + if (this.statisticsFactory == null) + throw new RuntimeException("OFStatisticsFactory not set"); + this.statistics = statisticsFactory.parseStatistics(this.getType(), + this.statisticType, data, super.getLengthU() - MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.statisticType.getTypeValue()); + data.writeShort(this.flags); + if (this.statistics != null) { + for (OFStatistics statistic : this.statistics) { + statistic.writeTo(data); + } + } + } + + @Override + public int hashCode() { + final int prime = 317; + int result = super.hashCode(); + result = prime * result + flags; + result = prime * result + + ((statisticType == null) ? 0 : statisticType.hashCode()); + result = prime * result + + ((statistics == null) ? 0 : statistics.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFStatisticsMessageBase)) { + return false; + } + OFStatisticsMessageBase other = (OFStatisticsMessageBase) obj; + if (flags != other.flags) { + return false; + } + if (statisticType == null) { + if (other.statisticType != null) { + return false; + } + } else if (!statisticType.equals(other.statisticType)) { + return false; + } + if (statistics == null) { + if (other.statistics != null) { + return false; + } + } else if (!statistics.equals(other.statistics)) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsReply.java new file mode 100644 index 00000000..ddc72674 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsReply.java @@ -0,0 +1,46 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an ofp_stats_reply message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFStatisticsReply extends OFStatisticsMessageBase { + public enum OFStatisticsReplyFlags { + REPLY_MORE (1 << 0); + + protected short type; + + OFStatisticsReplyFlags(int type) { + this.type = (short) type; + } + + public short getTypeValue() { + return type; + } + } + + public OFStatisticsReply() { + super(); + this.type = OFType.STATS_REPLY; + this.length = U16.t(OFStatisticsMessageBase.MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsRequest.java new file mode 100644 index 00000000..d1d8010e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFStatisticsRequest.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an ofp_stats_request message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFStatisticsRequest extends OFStatisticsMessageBase { + public OFStatisticsRequest() { + super(); + this.type = OFType.STATS_REQUEST; + this.length = U16.t(OFStatisticsMessageBase.MINIMUM_LENGTH); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSwitchConfig.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSwitchConfig.java new file mode 100644 index 00000000..e04e3fa6 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFSwitchConfig.java @@ -0,0 +1,118 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Base class representing ofp_switch_config based messages + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public abstract class OFSwitchConfig extends OFMessage { + public static int MINIMUM_LENGTH = 12; + + public enum OFConfigFlags { + OFPC_FRAG_NORMAL, + OFPC_FRAG_DROP, + OFPC_FRAG_REASM, + OFPC_FRAG_MASK + } + + protected short flags; + protected short missSendLength; + + public OFSwitchConfig() { + super(); + super.setLengthU(MINIMUM_LENGTH); + } + + /** + * @return the flags + */ + public short getFlags() { + return flags; + } + + /** + * @param flags the flags to set + */ + public OFSwitchConfig setFlags(short flags) { + this.flags = flags; + return this; + } + + /** + * @return the missSendLength + */ + public short getMissSendLength() { + return missSendLength; + } + + /** + * @param missSendLength the missSendLength to set + */ + public OFSwitchConfig setMissSendLength(short missSendLength) { + this.missSendLength = missSendLength; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.flags = data.readShort(); + this.missSendLength = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.flags); + data.writeShort(this.missSendLength); + } + + @Override + public int hashCode() { + final int prime = 331; + int result = super.hashCode(); + result = prime * result + flags; + result = prime * result + missSendLength; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFSwitchConfig)) { + return false; + } + OFSwitchConfig other = (OFSwitchConfig) obj; + if (flags != other.flags) { + return false; + } + if (missSendLength != other.missSendLength) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFType.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFType.java new file mode 100644 index 00000000..f1c81e28 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFType.java @@ -0,0 +1,249 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.lang.reflect.Constructor; + +/** + * List of OpenFlow types and mappings to wire protocol value and derived + * classes + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public enum OFType { + HELLO (0, OFHello.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFHello(); + }}), + ERROR (1, OFError.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFError(); + }}), + ECHO_REQUEST (2, OFEchoRequest.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFEchoRequest(); + }}), + ECHO_REPLY (3, OFEchoReply.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFEchoReply(); + }}), + VENDOR (4, OFVendor.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFVendor(); + }}), + FEATURES_REQUEST (5, OFFeaturesRequest.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFFeaturesRequest(); + }}), + FEATURES_REPLY (6, OFFeaturesReply.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFFeaturesReply(); + }}), + GET_CONFIG_REQUEST (7, OFGetConfigRequest.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFGetConfigRequest(); + }}), + GET_CONFIG_REPLY (8, OFGetConfigReply.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFGetConfigReply(); + }}), + SET_CONFIG (9, OFSetConfig.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFSetConfig(); + }}), + PACKET_IN (10, OFPacketIn.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFPacketIn(); + }}), + FLOW_REMOVED (11, OFFlowRemoved.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFFlowRemoved(); + }}), + PORT_STATUS (12, OFPortStatus.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFPortStatus(); + }}), + PACKET_OUT (13, OFPacketOut.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFPacketOut(); + }}), + FLOW_MOD (14, OFFlowMod.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFFlowMod(); + }}), + PORT_MOD (15, OFPortMod.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFPortMod(); + }}), + STATS_REQUEST (16, OFStatisticsRequest.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFStatisticsRequest(); + }}), + STATS_REPLY (17, OFStatisticsReply.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFStatisticsReply(); + }}), + BARRIER_REQUEST (18, OFBarrierRequest.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFBarrierRequest(); + }}), + BARRIER_REPLY (19, OFBarrierReply.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFBarrierReply(); + }}), + QUEUE_GET_CONFIG_REQUEST (20, OFQueueGetConfigRequest.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFQueueGetConfigRequest(); + }}), + QUEUE_GET_CONFIG_REPLY (21, OFQueueGetConfigReply.class, new Instantiable() { + @Override + public OFMessage instantiate() { + return new OFQueueGetConfigReply(); + }}); + + static OFType[] mapping; + + protected Class clazz; + protected Constructor constructor; + protected Instantiable instantiable; + protected byte type; + + /** + * Store some information about the OpenFlow type, including wire protocol + * type number, length, and derived class + * + * @param type Wire protocol number associated with this OFType + * @param clazz The Java class corresponding to this type of OpenFlow + * message + * @param instantiator An Instantiator implementation that creates an + * instance of the specified OFMessage + */ + OFType(int type, Class clazz, Instantiable instantiator) { + this.type = (byte) type; + this.clazz = clazz; + this.instantiable = instantiator; + try { + this.constructor = clazz.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + clazz, e); + } + OFType.addMapping(this.type, this); + } + + /** + * Adds a mapping from type value to OFType enum + * + * @param i OpenFlow wire protocol type + * @param t type + */ + static public void addMapping(byte i, OFType t) { + if (mapping == null) + mapping = new OFType[32]; + OFType.mapping[i] = t; + } + + /** + * Remove a mapping from type value to OFType enum + * + * @param i OpenFlow wire protocol type + */ + static public void removeMapping(byte i) { + OFType.mapping[i] = null; + } + + /** + * Given a wire protocol OpenFlow type number, return the OFType associated + * with it + * + * @param i wire protocol number + * @return OFType enum type + */ + + static public OFType valueOf(Byte i) { + return OFType.mapping[i]; + } + + /** + * @return Returns the wire protocol value corresponding to this OFType + */ + public byte getTypeValue() { + return this.type; + } + + /** + * @return return the OFMessage subclass corresponding to this OFType + */ + public Class toClass() { + return clazz; + } + + /** + * Returns the no-argument Constructor of the implementation class for + * this OFType + * @return the constructor + */ + public Constructor getConstructor() { + return constructor; + } + + /** + * Returns a new instance of the OFMessage represented by this OFType + * @return the new object + */ + public OFMessage newInstance() { + return instantiable.instantiate(); + } + + /** + * @return the instantiable + */ + public Instantiable getInstantiable() { + return instantiable; + } + + /** + * @param instantiable the instantiable to set + */ + public void setInstantiable(Instantiable instantiable) { + this.instantiable = instantiable; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFVendor.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFVendor.java new file mode 100644 index 00000000..8ecb862b --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/OFVendor.java @@ -0,0 +1,131 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; +import org.openflow.protocol.factory.OFVendorDataFactory; +import org.openflow.protocol.factory.OFVendorDataFactoryAware; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Represents ofp_vendor_header + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFVendor extends OFMessage implements OFVendorDataFactoryAware { + public static int MINIMUM_LENGTH = 12; + + protected int vendor; + protected OFVendorData vendorData; + protected OFVendorDataFactory vendorDataFactory; + + public OFVendor() { + super(); + this.type = OFType.VENDOR; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the vendor + */ + public int getVendor() { + return vendor; + } + + /** + * @param vendor the vendor to set + */ + public void setVendor(int vendor) { + this.vendor = vendor; + } + + /** + * @return the data + */ + public OFVendorData getVendorData() { + return vendorData; + } + + /** + * @param data the data to set + */ + public void setVendorData(OFVendorData vendorData) { + this.vendorData = vendorData; + } + + @Override + public void setVendorDataFactory(OFVendorDataFactory vendorDataFactory) { + this.vendorDataFactory = vendorDataFactory; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.vendor = data.readInt(); + if (vendorDataFactory == null) + throw new RuntimeException("OFVendorDataFactory not set"); + + this.vendorData = vendorDataFactory.parseVendorData(vendor, + data, super.getLengthU() - MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + if (vendorData != null) + vendorData.writeTo(data); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 337; + int result = super.hashCode(); + result = prime * result + vendor; + if (vendorData != null) + result = prime * result + vendorData.hashCode(); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + OFVendor other = (OFVendor) obj; + if (vendor != other.vendor) + return false; + if (vendorData == null) { + if (other.vendorData != null) { + return false; + } + } else if (!vendorData.equals(other.vendorData)) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/Wildcards.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/Wildcards.java new file mode 100644 index 00000000..fbda8586 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/Wildcards.java @@ -0,0 +1,527 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package org.openflow.protocol; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; + + +/** + * a more user friendly representation of the wildcards bits in an OpenFlow + * match. The Wildcards object is + *

    + *
  • immutable (i.e., threadsafe)
  • + *
  • instance managed (don't instantiate it yourself), instead call "of"
  • + *
      + *

      + * You can construct a Wildcard object from either its integer representation + *

      + * + * Wildcard.of(0x3820e0); + * + *

      + * Or start with either an empty or full wildcard, and select/unselect foo. + *

      + * + * Wildcard w = Wildcards.NONE + * .set(Flag.DL_SRC, Flag. DL_DST, Flag.DL_VLAN_PCP) + * .setNwDstMask(8) + * .setNwSrcMask(8); + * + *

      + * Remember: Wildcards objects are immutable. set... operations have + * NO EFFECT on the current wildcard object. You HAVE to use the returned + * changed object. + *

      + * + * @author Andreas Wundsam + */ +public class Wildcards { + + public final static Wildcards FULL = new Wildcards(OFMatch.OFPFW_ALL_SANITIZED); + private static final int FULL_INT = FULL.getInt(); + + public final static Wildcards EXACT = new Wildcards(0); + + // floodlight common case: matches on inport + l2 + public final static int INT_INPORT_L2_MATCH = 0x3820e0; + public final static Wildcards INPORT_L2_MATCH = new Wildcards( + INT_INPORT_L2_MATCH); + + /** + * enum type for the binary flags that can be set in the wildcards field of + * an OFMatch. Replaces the unwieldy c-ish int constants in OFMatch. + */ + public static enum Flag { + IN_PORT(OFMatch.OFPFW_IN_PORT), /* Switch input port. */ + DL_VLAN(OFMatch.OFPFW_DL_VLAN), /* VLAN id. */ + DL_SRC(OFMatch.OFPFW_DL_SRC), /* Ethernet source address. */ + DL_DST(OFMatch.OFPFW_DL_DST), /* Ethernet destination addr */ + DL_TYPE(OFMatch.OFPFW_DL_TYPE), /* Ethernet frame type. */ + NW_PROTO(OFMatch.OFPFW_NW_PROTO), /* IP protocol. */ + TP_SRC(OFMatch.OFPFW_TP_SRC), /* TCP/UDP source port. */ + TP_DST(OFMatch.OFPFW_TP_DST), /* TCP/UDP destination port. */ + DL_VLAN_PCP(OFMatch.OFPFW_DL_VLAN_PCP), /* VLAN priority. */ + NW_SRC(-1) { /* + * virtual NW_SRC flag => translates to the strange 6 bits + * in the header + */ + @Override + boolean isBolean() { + return false; + } + + @Override + int getInt(int flags) { + return ((flags & OFMatch.OFPFW_NW_SRC_MASK) >> OFMatch.OFPFW_NW_SRC_SHIFT); + } + + @Override + int setInt(int flags, int srcMask) { + return (flags & ~OFMatch.OFPFW_NW_SRC_MASK) | (srcMask << OFMatch.OFPFW_NW_SRC_SHIFT); + } + + @Override + int wildcard(int flags) { + return flags & ~OFMatch.OFPFW_NW_SRC_MASK; + } + + @Override + int matchOn(int flags) { + return flags | OFMatch.OFPFW_NW_SRC_ALL; + } + + @Override + boolean isPartiallyOn(int flags) { + int intValue = getInt(flags); + return intValue > 0 && intValue < 32; + } + + @Override + boolean isFullyOn(int flags) { + return getInt(flags) >= 32; + } + + }, + NW_DST(-1) { /* + * virtual NW_SRC flag => translates to the strange 6 bits + * in the header + */ + @Override + boolean isBolean() { + return false; + } + + @Override + int getInt(int flags) { + return ((flags & OFMatch.OFPFW_NW_DST_MASK) >> OFMatch.OFPFW_NW_DST_SHIFT); + } + + @Override + int setInt(int flags, int srcMask) { + return (flags & ~OFMatch.OFPFW_NW_DST_MASK) | (srcMask << OFMatch.OFPFW_NW_DST_SHIFT); + } + + @Override + int wildcard(int flags) { + return flags & ~OFMatch.OFPFW_NW_DST_MASK; + } + + @Override + int matchOn(int flags) { + return flags | OFMatch.OFPFW_NW_DST_ALL; + } + + @Override + boolean isFullyOn(int flags) { + return getInt(flags) >= 32; + } + }, + NW_TOS(OFMatch.OFPFW_NW_TOS); /* IP ToS (DSCP field, 6 bits). */ + + final int bitPosition; + + Flag(int bitPosition) { + this.bitPosition = bitPosition; + } + + /** + * @return a modified OF-1.0 flags field with this flag cleared (match + * on this field) + */ + int matchOn(int flags) { + return flags & ~this.bitPosition; + } + + /** + * @return a modified OF-1.0 flags field with this flag set (wildcard + * this field) + */ + int wildcard(int flags) { + return flags | this.bitPosition; + } + + /** + * @return true iff this is a true boolean flag that can either be off + * or on.True in OF-1.0 for all fields except NW_SRC and NW_DST + */ + boolean isBolean() { + return false; + } + + /** + * @return true iff this wildcard field is currently 'partially on'. + * Always false for true Boolean Flags. Can be true in OF-1.0 + * for NW_SRC, NW_DST. + */ + boolean isPartiallyOn(int flags) { + return false; + } + + /** + * @return true iff this wildcard field currently fully on (fully + * wildcarded). Equivalent to the boolean flag being set in the + * bitmask for bit flags, and to the wildcarded bit length set + * to >=32 for NW_SRC and NW_DST + * @param flags + * @return + */ + boolean isFullyOn(int flags) { + return (flags & this.bitPosition) != 0; + } + + /** + * set the integer representation of this flag. only for NW_SRC and + * NW_DST + */ + int setInt(int flags, int srcMask) { + throw new UnsupportedOperationException(); + } + + /** + * set the integer representation of this flag. only for NW_SRC and + * NW_DST + */ + int getInt(int flags) { + throw new UnsupportedOperationException(); + } + + + } + + private final int flags; + + /** private constructor. use Wildcard.of() instead */ + private Wildcards(int flags) { + this.flags = flags; + } + + /** + * return a wildcard object matching the given int flags. May reuse / cache + * frequently used wildcard instances. Don't rely on it though (use equals + * not ==). + * + * @param flags + * @return + */ + public static Wildcards of(int paramFlags) { + int flags = sanitizeInt(paramFlags); + switch(flags) { + case 0x0000: + return EXACT; + case OFMatch.OFPFW_ALL_SANITIZED: + return FULL; + case INT_INPORT_L2_MATCH: + return INPORT_L2_MATCH; + default: + return new Wildcards(flags); + } + } + + /** convience method return a wildcard for exactly one set flag */ + public static Wildcards of(Wildcards.Flag setFlag) { + return Wildcards.of(setFlag.wildcard(0)); + } + + /** convience method return a wildcard for exactly two set flags */ + public static Wildcards of(Wildcards.Flag setFlag, Wildcards.Flag setFlag2) { + return Wildcards.of(setFlag.wildcard(setFlag2.wildcard(0))); + } + + /** convience method return a wildcard for an arbitrary number of set flags */ + public static Wildcards of(Wildcards.Flag... setFlags) { + int flags = 0; + for (Wildcards.Flag flag : setFlags) + flags = flag.wildcard(0); + return Wildcards.of(flags); + } + + /** convience method return a wildcards for ofmatches that match on one flag */ + public static Wildcards ofMatches(Wildcards.Flag setFlag) { + return Wildcards.of(setFlag.matchOn(FULL_INT)); + } + + /** + * convience method return a wildcard for for an ofmatch that match on two + * flags + */ + public static Wildcards ofMatches(Wildcards.Flag setFlag, Wildcards.Flag setFlag2) { + return Wildcards.of(setFlag.matchOn(setFlag2.matchOn(FULL_INT))); + } + + /** + * convience method return a wildcard for an ofmatch that amtch on an + * arbitrary number of set flags + */ + public static Wildcards ofMatches(Wildcards.Flag... setFlags) { + int flags = FULL_INT; + for (Wildcards.Flag flag : setFlags) + flags = flag.matchOn(flags); + return Wildcards.of(flags); + } + + /** + * return a Wildcards object that has the given flags set + *

      + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + */ + public Wildcards wildcard(Wildcards.Flag flag) { + int flags = flag.wildcard(this.flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that has the given flags set + *

      + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + */ + public Wildcards wildcard(Wildcards.Flag flag, Wildcards.Flag flag2) { + int flags = flag.wildcard(flag2.wildcard(this.flags)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that has the given flags wildcarded + *

      + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + */ + public Wildcards wildcard(Wildcards.Flag... setFlags) { + int flags = this.flags; + for (Wildcards.Flag flag : setFlags) + flags = flag.wildcard(flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that matches on exactly the given flag + *

      + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + */ + public Wildcards matchOn(Wildcards.Flag flag) { + int flags = flag.matchOn(this.flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that matches on exactly the given flags + *

      + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + */ + public Wildcards matchOn(Wildcards.Flag flag, Wildcards.Flag flag2) { + int flags = flag.matchOn(flag2.matchOn(this.flags)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that matches on exactly the given flags + *

      + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + */ + public Wildcards matchOn(Wildcards.Flag... setFlags) { + int flags = this.flags; + for (Wildcards.Flag flag : setFlags) + flags = flag.matchOn(flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return the nw src mask in normal CIDR style, e.g., 8 means x.x.x.x/8 + * means 8 bits wildcarded + */ + public int getNwSrcMask() { + return Math.max(0, 32 - Flag.NW_SRC.getInt(flags)); + } + + /** + * return the nw dst mask in normal CIDR style, e.g., 8 means x.x.x.x/8 + * means 8 bits wildcarded + */ + public int getNwDstMask() { + return Math.max(0, 32 - Flag.NW_DST.getInt(flags)); + } + + /** + * return a Wildcard object that has the given nwSrcCidrMask set. + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + * + * @param srcCidrMask + * source mask to set in normal CIDR notation, i.e., 8 + * means x.x.x.x/8 + * @return a modified object + */ + public Wildcards withNwSrcMask(int srcCidrMask) { + int flags = Flag.NW_SRC.setInt(this.flags, Math.max(0, 32 - srcCidrMask)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcard object that has the given nwDstCidrMask set. + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + * + * @param dstCidrMask + * dest mask to set in normal CIDR notation, i.e., 8 means + * x.x.x.x/8 + * @return a modified object + */ + public Wildcards withNwDstMask(int dstCidrMask) { + int flags = Flag.NW_DST.setInt(this.flags, Math.max(0, 32 - dstCidrMask)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcard object that is inverted to this wildcard object. + * NOTE: NOT a mutator function. 'this' wildcard object stays + * unmodified. + * @return a modified object + */ + public Wildcards inverted() { + return Wildcards.of(flags ^ OFMatch.OFPFW_ALL_SANITIZED); + } + + public boolean isWildcarded(Flag flag) { + return flag.isFullyOn(flags); + } + + /** + * return all wildcard flags that are fully wildcarded as an EnumSet. Do not + * modify. Note: some flags (like NW_SRC and NW_DST) that are partially + * wildcarded are not returned in this set. + * + * @return the EnumSet of wildcards + */ + public EnumSet getWildcardedFlags() { + EnumSet res = EnumSet.noneOf(Wildcards.Flag.class); + for (Wildcards.Flag flag : Flag.values()) { + if (flag.isFullyOn(flags)) { + res.add(flag); + } + } + return res; + } + + /** return the OpenFlow 'wire' integer representation of these wildcards */ + public int getInt() { + return flags; + } + + /** + * return the OpenFlow 'wire' integer representation of these wildcards. + * Sanitize nw_src and nw_dst to be max. 32 (values > 32 are technically + * possible, but don't make semantic sense) + */ + public static int sanitizeInt(int flags) { + if (((flags & OFMatch.OFPFW_NW_SRC_MASK) >> OFMatch.OFPFW_NW_SRC_SHIFT) > 32) { + flags = (flags & ~OFMatch.OFPFW_NW_SRC_MASK) | OFMatch.OFPFW_NW_SRC_ALL; + } + if (((flags & OFMatch.OFPFW_NW_DST_MASK) >> OFMatch.OFPFW_NW_DST_SHIFT) > 32) { + flags = (flags & ~OFMatch.OFPFW_NW_DST_MASK) | OFMatch.OFPFW_NW_DST_ALL; + } + return flags; + } + + /** + * is this a wildcard set that has all flags set + and full (/0) nw_src and + * nw_dst wildcarding ? + */ + public boolean isFull() { + return flags == OFMatch.OFPFW_ALL || flags == OFMatch.OFPFW_ALL_SANITIZED; + } + + /** is this a wildcard of an exact match */ + public boolean isExact() { + return flags == 0; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + flags; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Wildcards other = (Wildcards) obj; + if (flags != other.flags) + return false; + return true; + } + + + +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFAction.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFAction.java new file mode 100644 index 00000000..57b5dc1e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFAction.java @@ -0,0 +1,173 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * The base class for all OpenFlow Actions. + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFAction implements Cloneable { + /** + * Note the true minimum length for this header is 8 including a pad to 64 + * bit alignment, however as this base class is used for demuxing an + * incoming Action, it is only necessary to read the first 4 bytes. All + * Actions extending this class are responsible for reading/writing the + * first 8 bytes, including the pad if necessary. + */ + public static int MINIMUM_LENGTH = 4; + public static int OFFSET_LENGTH = 2; + public static int OFFSET_TYPE = 0; + + protected OFActionType type; + protected short length; + + /** + * Get the length of this message + * + * @return + */ + public short getLength() { + return length; + } + + /** + * Get the length of this message, unsigned + * + * @return + */ + public int getLengthU() { + return U16.f(length); + } + + /** + * Set the length of this message + * + * @param length + */ + public OFAction setLength(short length) { + this.length = length; + return this; + } + + /** + * Get the type of this message + * + * @return OFActionType enum + */ + public OFActionType getType() { + return this.type; + } + + /** + * Set the type of this message + * + * @param type + */ + public void setType(OFActionType type) { + this.type = type; + } + + /** + * Returns a summary of the message + * @return "ofmsg=v=$version;t=$type:l=$len:xid=$xid" + */ + public String toString() { + return "ofaction" + + ";t=" + this.getType() + + ";l=" + this.getLength(); + } + + /** + * Given the output from toString(), + * create a new OFAction + * @param val + * @return + */ + public static OFAction fromString(String val) { + String tokens[] = val.split(";"); + if (!tokens[0].equals("ofaction")) + throw new IllegalArgumentException("expected 'ofaction' but got '" + + tokens[0] + "'"); + String type_tokens[] = tokens[1].split("="); + String len_tokens[] = tokens[2].split("="); + OFAction action = new OFAction(); + action.setLength(Short.valueOf(len_tokens[1])); + action.setType(OFActionType.valueOf(type_tokens[1])); + return action; + } + + public void readFrom(ChannelBuffer data) { + this.type = OFActionType.valueOf(data.readShort()); + this.length = data.readShort(); + // Note missing PAD, see MINIMUM_LENGTH comment for details + } + + public void writeTo(ChannelBuffer data) { + data.writeShort(type.getTypeValue()); + data.writeShort(length); + // Note missing PAD, see MINIMUM_LENGTH comment for details + } + + @Override + public int hashCode() { + final int prime = 347; + int result = 1; + result = prime * result + length; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFAction)) { + return false; + } + OFAction other = (OFAction) obj; + if (length != other.length) { + return false; + } + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public OFAction clone() throws CloneNotSupportedException { + return (OFAction) super.clone(); + } + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java new file mode 100644 index 00000000..a6eae63e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java @@ -0,0 +1,96 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + +import java.util.Arrays; + + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFPhysicalPort; + +/** + * Represents an ofp_action_dl_addr + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public abstract class OFActionDataLayer extends OFAction { + public static int MINIMUM_LENGTH = 16; + + protected byte[] dataLayerAddress; + + /** + * @return the dataLayerAddress + */ + public byte[] getDataLayerAddress() { + return dataLayerAddress; + } + + /** + * @param dataLayerAddress the dataLayerAddress to set + */ + public void setDataLayerAddress(byte[] dataLayerAddress) { + this.dataLayerAddress = dataLayerAddress; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + if (this.dataLayerAddress == null) + this.dataLayerAddress = new byte[OFPhysicalPort.OFP_ETH_ALEN]; + data.readBytes(this.dataLayerAddress); + data.readInt(); + data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeBytes(this.dataLayerAddress); + data.writeInt(0); + data.writeShort((short) 0); + } + + @Override + public int hashCode() { + final int prime = 347; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(dataLayerAddress); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionDataLayer)) { + return false; + } + OFActionDataLayer other = (OFActionDataLayer) obj; + if (!Arrays.equals(dataLayerAddress, other.dataLayerAddress)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java new file mode 100644 index 00000000..48b8d0f8 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionDataLayerDestination extends OFActionDataLayer { + public OFActionDataLayerDestination() { + super(); + super.setType(OFActionType.SET_DL_DST); + super.setLength((short) OFActionDataLayer.MINIMUM_LENGTH); + } + + public OFActionDataLayerDestination(byte[] address) { + this(); + this.dataLayerAddress = address; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java new file mode 100644 index 00000000..e04561ce --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionDataLayerSource extends OFActionDataLayer { + public OFActionDataLayerSource() { + super(); + super.setType(OFActionType.SET_DL_SRC); + super.setLength((short) OFActionDataLayer.MINIMUM_LENGTH); + } + + public OFActionDataLayerSource(byte[] address) { + this(); + this.dataLayerAddress = address; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java new file mode 100644 index 00000000..0ec2fa33 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java @@ -0,0 +1,124 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_enqueue + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionEnqueue extends OFAction { + public static int MINIMUM_LENGTH = 16; + + protected short port; + protected int queueId; + + public OFActionEnqueue() { + super.setType(OFActionType.OPAQUE_ENQUEUE); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionEnqueue(short port, int queueId) { + this(); + this.port = port; + this.queueId = queueId; + } + + /** + * Get the output port + * @return + */ + public short getPort() { + return this.port; + } + + /** + * Set the output port + * @param port + */ + public void setPort(short port) { + this.port = port; + } + + /** + * @return the queueId + */ + public int getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.port = data.readShort(); + data.readShort(); + data.readInt(); + this.queueId = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.port); + data.writeShort((short) 0); + data.writeInt(0); + data.writeInt(this.queueId); + } + + @Override + public int hashCode() { + final int prime = 349; + int result = super.hashCode(); + result = prime * result + port; + result = prime * result + queueId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionEnqueue)) { + return false; + } + OFActionEnqueue other = (OFActionEnqueue) obj; + if (port != other.port) { + return false; + } + if (queueId != other.queueId) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java new file mode 100644 index 00000000..dc65ae91 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java @@ -0,0 +1,86 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_nw_addr + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public abstract class OFActionNetworkLayerAddress extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected int networkAddress; + + /** + * @return the networkAddress + */ + public int getNetworkAddress() { + return networkAddress; + } + + /** + * @param networkAddress the networkAddress to set + */ + public void setNetworkAddress(int networkAddress) { + this.networkAddress = networkAddress; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.networkAddress = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.networkAddress); + } + + @Override + public int hashCode() { + final int prime = 353; + int result = super.hashCode(); + result = prime * result + networkAddress; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionNetworkLayerAddress)) { + return false; + } + OFActionNetworkLayerAddress other = (OFActionNetworkLayerAddress) obj; + if (networkAddress != other.networkAddress) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java new file mode 100644 index 00000000..13c14ff0 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionNetworkLayerDestination extends OFActionNetworkLayerAddress { + public OFActionNetworkLayerDestination() { + super(); + super.setType(OFActionType.SET_NW_DST); + super.setLength((short) OFActionNetworkLayerAddress.MINIMUM_LENGTH); + } + + public OFActionNetworkLayerDestination(int ip) { + this(); + this.networkAddress = ip; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java new file mode 100644 index 00000000..ef1d005e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionNetworkLayerSource extends OFActionNetworkLayerAddress { + public OFActionNetworkLayerSource() { + super(); + super.setType(OFActionType.SET_NW_SRC); + super.setLength((short) OFActionNetworkLayerAddress.MINIMUM_LENGTH); + } + + public OFActionNetworkLayerSource(int ip) { + this(); + this.networkAddress = ip; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java new file mode 100644 index 00000000..0d381802 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java @@ -0,0 +1,101 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_enqueue + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionNetworkTypeOfService extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected byte networkTypeOfService; + + public OFActionNetworkTypeOfService() { + super.setType(OFActionType.SET_NW_TOS); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionNetworkTypeOfService(byte tos) { + this(); + this.networkTypeOfService = tos; + } + + + /** + * @return the networkTypeOfService + */ + public byte getNetworkTypeOfService() { + return networkTypeOfService; + } + + /** + * @param networkTypeOfService the networkTypeOfService to set + */ + public void setNetworkTypeOfService(byte networkTypeOfService) { + this.networkTypeOfService = networkTypeOfService; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.networkTypeOfService = data.readByte(); + data.readShort(); + data.readByte(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(this.networkTypeOfService); + data.writeShort((short) 0); + data.writeByte((byte) 0); + } + + @Override + public int hashCode() { + final int prime = 359; + int result = super.hashCode(); + result = prime * result + networkTypeOfService; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionNetworkTypeOfService)) { + return false; + } + OFActionNetworkTypeOfService other = (OFActionNetworkTypeOfService) obj; + if (networkTypeOfService != other.networkTypeOfService) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionOutput.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionOutput.java new file mode 100644 index 00000000..b9521d09 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionOutput.java @@ -0,0 +1,158 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ +public class OFActionOutput extends OFAction implements Cloneable { + public static int MINIMUM_LENGTH = 8; + + protected short port; + protected short maxLength; + + public OFActionOutput() { + super.setType(OFActionType.OUTPUT); + super.setLength((short) MINIMUM_LENGTH); + } + + /** + * Create an Output Action sending packets out the specified + * OpenFlow port. + * + * This is the most common creation pattern for OFActions. + * + * @param port + */ + + public OFActionOutput(short port) { + this(port, (short) 65535); + } + + /** + * Create an Output Action specifying both the port AND + * the snaplen of the packet to send out that port. + * The length field is only meaningful when port == OFPort.OFPP_CONTROLLER + * @param port + * @param maxLength The maximum number of bytes of the packet to send. + * Most hardware only supports this value for OFPP_CONTROLLER + */ + + public OFActionOutput(short port, short maxLength) { + super(); + super.setType(OFActionType.OUTPUT); + super.setLength((short) MINIMUM_LENGTH); + this.port = port; + this.maxLength = maxLength; + } + + /** + * Get the output port + * @return + */ + public short getPort() { + return this.port; + } + + /** + * Set the output port + * @param port + */ + public OFActionOutput setPort(short port) { + this.port = port; + return this; + } + + /** + * Get the max length to send to the controller + * @return + */ + public short getMaxLength() { + return this.maxLength; + } + + /** + * Set the max length to send to the controller + * @param maxLength + */ + public OFActionOutput setMaxLength(short maxLength) { + this.maxLength = maxLength; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.port = data.readShort(); + this.maxLength = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(port); + data.writeShort(maxLength); + } + + @Override + public int hashCode() { + final int prime = 367; + int result = super.hashCode(); + result = prime * result + maxLength; + result = prime * result + port; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionOutput)) { + return false; + } + OFActionOutput other = (OFActionOutput) obj; + if (maxLength != other.maxLength) { + return false; + } + if (port != other.port) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "OFActionOutput [maxLength=" + maxLength + ", port=" + U16.f(port) + + ", length=" + length + ", type=" + type + "]"; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java new file mode 100644 index 00000000..7d6b849a --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java @@ -0,0 +1,53 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + + +/** + * Represents an ofp_action_strip_vlan + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionStripVirtualLan extends OFAction { + public static int MINIMUM_LENGTH = 8; + + public OFActionStripVirtualLan() { + super(); + super.setType(OFActionType.STRIP_VLAN); + super.setLength((short) MINIMUM_LENGTH); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + // PAD + data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + // PAD + data.writeInt(0); + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java new file mode 100644 index 00000000..0bc09c95 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_tp_port + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public abstract class OFActionTransportLayer extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected short transportPort; + + /** + * @return the transportPort + */ + public short getTransportPort() { + return transportPort; + } + + /** + * @param transportPort the transportPort to set + */ + public void setTransportPort(short transportPort) { + this.transportPort = transportPort; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.transportPort = data.readShort(); + data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.transportPort); + data.writeShort((short) 0); + } + + @Override + public int hashCode() { + final int prime = 373; + int result = super.hashCode(); + result = prime * result + transportPort; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionTransportLayer)) { + return false; + } + OFActionTransportLayer other = (OFActionTransportLayer) obj; + if (transportPort != other.transportPort) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java new file mode 100644 index 00000000..7e7b0f1f --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionTransportLayerDestination extends OFActionTransportLayer { + public OFActionTransportLayerDestination() { + super(); + super.setType(OFActionType.SET_TP_DST); + super.setLength((short) OFActionTransportLayer.MINIMUM_LENGTH); + } + + public OFActionTransportLayerDestination(short port) { + this(); + this.transportPort = port; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java new file mode 100644 index 00000000..385aa53c --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionTransportLayerSource extends OFActionTransportLayer { + public OFActionTransportLayerSource() { + super(); + super.setType(OFActionType.SET_TP_SRC); + super.setLength((short) OFActionTransportLayer.MINIMUM_LENGTH); + } + + public OFActionTransportLayerSource(short port) { + this(); + this.transportPort = port; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionType.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionType.java new file mode 100644 index 00000000..18229170 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionType.java @@ -0,0 +1,203 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * + */ +package org.openflow.protocol.action; + +import java.lang.reflect.Constructor; + +import org.openflow.protocol.Instantiable; + +/** + * List of OpenFlow Action types and mappings to wire protocol value and + * derived classes + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public enum OFActionType { + OUTPUT (0, OFActionOutput.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionOutput(); + }}), + SET_VLAN_ID (1, OFActionVirtualLanIdentifier.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionVirtualLanIdentifier(); + }}), + SET_VLAN_PCP (2, OFActionVirtualLanPriorityCodePoint.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionVirtualLanPriorityCodePoint(); + }}), + STRIP_VLAN (3, OFActionStripVirtualLan.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionStripVirtualLan(); + }}), + SET_DL_SRC (4, OFActionDataLayerSource.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionDataLayerSource(); + }}), + SET_DL_DST (5, OFActionDataLayerDestination.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionDataLayerDestination(); + }}), + SET_NW_SRC (6, OFActionNetworkLayerSource.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionNetworkLayerSource(); + }}), + SET_NW_DST (7, OFActionNetworkLayerDestination.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionNetworkLayerDestination(); + }}), + SET_NW_TOS (8, OFActionNetworkTypeOfService.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionNetworkTypeOfService(); + }}), + SET_TP_SRC (9, OFActionTransportLayerSource.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionTransportLayerSource(); + }}), + SET_TP_DST (10, OFActionTransportLayerDestination.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionTransportLayerDestination(); + }}), + OPAQUE_ENQUEUE (11, OFActionEnqueue.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionEnqueue(); + }}), + VENDOR (0xffff, OFActionVendor.class, new Instantiable() { + @Override + public OFAction instantiate() { + return new OFActionVendorGeneric(); + }}); + + protected static OFActionType[] mapping; + + protected Class clazz; + protected Constructor constructor; + protected Instantiable instantiable; + protected int minLen; + protected short type; + + /** + * Store some information about the OpenFlow Action type, including wire + * protocol type number, length, and derrived class + * + * @param type Wire protocol number associated with this OFType + * @param clazz The Java class corresponding to this type of OpenFlow Action + * @param instantiable the instantiable for the OFAction this type represents + */ + OFActionType(int type, Class clazz, Instantiable instantiable) { + this.type = (short) type; + this.clazz = clazz; + this.instantiable = instantiable; + try { + this.constructor = clazz.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + clazz, e); + } + OFActionType.addMapping(this.type, this); + } + + /** + * Adds a mapping from type value to OFActionType enum + * + * @param i OpenFlow wire protocol Action type value + * @param t type + */ + static public void addMapping(short i, OFActionType t) { + if (mapping == null) + mapping = new OFActionType[16]; + // bring higher mappings down to the edge of our array + if (i < 0) + i = (short) (16 + i); + OFActionType.mapping[i] = t; + } + + /** + * Given a wire protocol OpenFlow type number, return the OFType associated + * with it + * + * @param i wire protocol number + * @return OFType enum type + */ + + static public OFActionType valueOf(short i) { + if (i < 0) + i = (short) (16+i); + return OFActionType.mapping[i]; + } + + /** + * @return Returns the wire protocol value corresponding to this + * OFActionType + */ + public short getTypeValue() { + return this.type; + } + + /** + * @return return the OFAction subclass corresponding to this OFActionType + */ + public Class toClass() { + return clazz; + } + + /** + * Returns the no-argument Constructor of the implementation class for + * this OFActionType + * @return the constructor + */ + public Constructor getConstructor() { + return constructor; + } + + /** + * Returns a new instance of the OFAction represented by this OFActionType + * @return the new object + */ + public OFAction newInstance() { + return instantiable.instantiate(); + } + + /** + * @return the instantiable + */ + public Instantiable getInstantiable() { + return instantiable; + } + + /** + * @param instantiable the instantiable to set + */ + public void setInstantiable(Instantiable instantiable) { + this.instantiable = instantiable; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendor.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendor.java new file mode 100644 index 00000000..5860ef11 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendor.java @@ -0,0 +1,94 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public abstract class OFActionVendor extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected int vendor; + + public OFActionVendor() { + super(); + super.setType(OFActionType.VENDOR); + super.setLength((short) MINIMUM_LENGTH); + } + + /** + * @return the vendor + */ + public int getVendor() { + return vendor; + } + + /** + * @param vendor the vendor to set + */ + public void setVendor(int vendor) { + this.vendor = vendor; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.vendor = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + vendor; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVendor)) { + return false; + } + OFActionVendor other = (OFActionVendor) obj; + if (vendor != other.vendor) { + return false; + } + return true; + } + + @Override + public String toString() { + return super.toString() + "; vendor=" + vendor; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java new file mode 100644 index 00000000..4f7859f5 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java @@ -0,0 +1,96 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** A generic / unparsed vendor action. This action is returned by + * BasicFactory.readFromWire if no more specific OFVendorActionFactory + * is registered. + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Andreas Wundsam + */ +public class OFActionVendorGeneric extends OFActionVendor { + public static int MINIMUM_LENGTH = 8; + + private final static byte[] EMPTY_ARRAY = new byte[0]; + + protected byte[] vendorData; + + public OFActionVendorGeneric() { + super(); + } + + public byte[] getVendorData() { + return vendorData; + } + + public void setVendorData(byte[] vendorData) { + this.vendorData = vendorData; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + + int vendorDataLength = this.getLength() - MINIMUM_LENGTH; + if (vendorDataLength > 0) { + vendorData = new byte[vendorDataLength]; + data.readBytes(vendorData); + } else { + vendorData = EMPTY_ARRAY; + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + data.writeBytes(vendorData); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(vendorData); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVendorGeneric)) { + return false; + } + OFActionVendorGeneric other = (OFActionVendorGeneric) obj; + if (!Arrays.equals(vendorData, other.vendorData)) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java new file mode 100644 index 00000000..5bd0e0bd --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java @@ -0,0 +1,98 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_vlan_vid + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionVirtualLanIdentifier extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected short virtualLanIdentifier; + + public OFActionVirtualLanIdentifier() { + super.setType(OFActionType.SET_VLAN_ID); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionVirtualLanIdentifier(short vlanId) { + this(); + this.virtualLanIdentifier = vlanId; + } + + /** + * @return the virtualLanIdentifier + */ + public short getVirtualLanIdentifier() { + return virtualLanIdentifier; + } + + /** + * @param virtualLanIdentifier the virtualLanIdentifier to set + */ + public void setVirtualLanIdentifier(short virtualLanIdentifier) { + this.virtualLanIdentifier = virtualLanIdentifier; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.virtualLanIdentifier = data.readShort(); + data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.virtualLanIdentifier); + data.writeShort((short) 0); + } + + @Override + public int hashCode() { + final int prime = 383; + int result = super.hashCode(); + result = prime * result + virtualLanIdentifier; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVirtualLanIdentifier)) { + return false; + } + OFActionVirtualLanIdentifier other = (OFActionVirtualLanIdentifier) obj; + if (virtualLanIdentifier != other.virtualLanIdentifier) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java new file mode 100644 index 00000000..9202df33 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java @@ -0,0 +1,100 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_vlan_pcp + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionVirtualLanPriorityCodePoint extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected byte virtualLanPriorityCodePoint; + + public OFActionVirtualLanPriorityCodePoint() { + super.setType(OFActionType.SET_VLAN_PCP); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionVirtualLanPriorityCodePoint(byte priority) { + this(); + this.virtualLanPriorityCodePoint = priority; + } + + /** + * @return the virtualLanPriorityCodePoint + */ + public byte getVirtualLanPriorityCodePoint() { + return virtualLanPriorityCodePoint; + } + + /** + * @param virtualLanPriorityCodePoint the virtualLanPriorityCodePoint to set + */ + public void setVirtualLanPriorityCodePoint(byte virtualLanPriorityCodePoint) { + this.virtualLanPriorityCodePoint = virtualLanPriorityCodePoint; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.virtualLanPriorityCodePoint = data.readByte(); + data.readShort(); // pad + data.readByte(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(this.virtualLanPriorityCodePoint); + data.writeShort((short) 0); + data.writeByte((byte) 0); + } + + @Override + public int hashCode() { + final int prime = 389; + int result = super.hashCode(); + result = prime * result + virtualLanPriorityCodePoint; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVirtualLanPriorityCodePoint)) { + return false; + } + OFActionVirtualLanPriorityCodePoint other = (OFActionVirtualLanPriorityCodePoint) obj; + if (virtualLanPriorityCodePoint != other.virtualLanPriorityCodePoint) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/BasicFactory.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/BasicFactory.java new file mode 100644 index 00000000..b61aa72e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/BasicFactory.java @@ -0,0 +1,344 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionType; +import org.openflow.protocol.action.OFActionVendor; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.protocol.statistics.OFVendorStatistics; +import org.openflow.protocol.vendor.OFByteArrayVendorData; +import org.openflow.protocol.vendor.OFVendorData; +import org.openflow.protocol.vendor.OFVendorDataType; +import org.openflow.protocol.vendor.OFVendorId; + + +/** + * A basic OpenFlow factory that supports naive creation of both Messages and + * Actions. + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ +public class BasicFactory implements OFMessageFactory, OFActionFactory, + OFStatisticsFactory, OFVendorDataFactory { + + private final OFVendorActionRegistry vendorActionRegistry; + + public BasicFactory() { + vendorActionRegistry = OFVendorActionRegistry.getInstance(); + } + + /** + * create and return a new instance of a message for OFType t. Also injects + * factories for those message types that implement the *FactoryAware + * interfaces. + * + * @return a newly created instance that may be modified / used freely by + * the caller + */ + @Override + public OFMessage getMessage(OFType t) { + OFMessage message = t.newInstance(); + injectFactories(message); + return message; + } + + @Override + public List parseMessage(ChannelBuffer data) throws MessageParseException { + List msglist = new ArrayList(); + OFMessage msg = null; + + while (data.readableBytes() >= OFMessage.MINIMUM_LENGTH) { + data.markReaderIndex(); + msg = this.parseMessageOne(data); + if (msg == null) { + data.resetReaderIndex(); + break; + } + else { + msglist.add(msg); + } + } + + if (msglist.size() == 0) { + return null; + } + return msglist; + + } + + public OFMessage parseMessageOne(ChannelBuffer data) throws MessageParseException { + try { + OFMessage demux = new OFMessage(); + OFMessage ofm = null; + + if (data.readableBytes() < OFMessage.MINIMUM_LENGTH) + return ofm; + + data.markReaderIndex(); + demux.readFrom(data); + data.resetReaderIndex(); + + if (demux.getLengthU() > data.readableBytes()) + return ofm; + + ofm = getMessage(demux.getType()); + if (ofm == null) + return null; + + injectFactories(ofm); + ofm.readFrom(data); + if (OFMessage.class.equals(ofm.getClass())) { + // advance the position for un-implemented messages + data.readerIndex(data.readerIndex()+(ofm.getLengthU() - + OFMessage.MINIMUM_LENGTH)); + } + + return ofm; + } catch (Exception e) { + /* Write the offending data along with the error message */ + data.resetReaderIndex(); + String msg = + "Message Parse Error for packet:" + dumpBuffer(data) + + "\nException: " + e.toString(); + data.resetReaderIndex(); + + throw new MessageParseException(msg, e); + } + } + + + + + private void injectFactories(OFMessage ofm) { + if (ofm instanceof OFActionFactoryAware) { + ((OFActionFactoryAware)ofm).setActionFactory(this); + } + if (ofm instanceof OFMessageFactoryAware) { + ((OFMessageFactoryAware)ofm).setMessageFactory(this); + } + if (ofm instanceof OFStatisticsFactoryAware) { + ((OFStatisticsFactoryAware)ofm).setStatisticsFactory(this); + } + if (ofm instanceof OFVendorDataFactoryAware) { + ((OFVendorDataFactoryAware)ofm).setVendorDataFactory(this); + } + } + + @Override + public OFAction getAction(OFActionType t) { + return t.newInstance(); + } + + @Override + public List parseActions(ChannelBuffer data, int length) { + return parseActions(data, length, 0); + } + + @Override + public List parseActions(ChannelBuffer data, int length, int limit) { + List results = new ArrayList(); + OFAction demux = new OFAction(); + OFAction ofa; + int end = data.readerIndex() + length; + + while (limit == 0 || results.size() <= limit) { + if ((data.readableBytes() < OFAction.MINIMUM_LENGTH || + (data.readerIndex() + OFAction.MINIMUM_LENGTH) > end)) + return results; + + data.markReaderIndex(); + demux.readFrom(data); + data.resetReaderIndex(); + + if ((demux.getLengthU() > data.readableBytes() || + (data.readerIndex() + demux.getLengthU()) > end)) + return results; + + ofa = parseActionOne(demux.getType(), data); + results.add(ofa); + } + + return results; + } + + private OFAction parseActionOne(OFActionType type, ChannelBuffer data) { + OFAction ofa; + data.markReaderIndex(); + ofa = getAction(type); + ofa.readFrom(data); + + if(type == OFActionType.VENDOR) { + OFActionVendor vendorAction = (OFActionVendor) ofa; + + OFVendorActionFactory vendorActionFactory = vendorActionRegistry.get(vendorAction.getVendor()); + + if(vendorActionFactory != null) { + // if we have a specific vendorActionFactory for this vendor id, + // delegate to it for vendor-specific reparsing of the message + data.resetReaderIndex(); + OFActionVendor newAction = vendorActionFactory.readFrom(data); + if(newAction != null) + ofa = newAction; + } + } + + if (OFAction.class.equals(ofa.getClass())) { + // advance the position for un-implemented messages + data.readerIndex(data.readerIndex()+(ofa.getLengthU() - + OFAction.MINIMUM_LENGTH)); + } + return ofa; + } + + @Override + public OFActionFactory getActionFactory() { + return this; + } + + @Override + public OFStatistics getStatistics(OFType t, OFStatisticsType st) { + return st.newInstance(t); + } + + @Override + public List parseStatistics(OFType t, OFStatisticsType st, + ChannelBuffer data, int length) { + return parseStatistics(t, st, data, length, 0); + } + + /** + * @param t + * OFMessage type: should be one of stats_request or stats_reply + * @param st + * statistics type of this message, e.g., DESC, TABLE + * @param data + * buffer to read from + * @param length + * length of statistics + * @param limit + * number of statistics to grab; 0 == all + * + * @return list of statistics + */ + + @Override + public List parseStatistics(OFType t, OFStatisticsType st, + ChannelBuffer data, int length, int limit) { + List results = new ArrayList(); + OFStatistics statistics = getStatistics(t, st); + + int start = data.readerIndex(); + int count = 0; + + while (limit == 0 || results.size() <= limit) { + // TODO Create a separate MUX/DEMUX path for vendor stats + if (statistics instanceof OFVendorStatistics) + ((OFVendorStatistics)statistics).setLength(length); + + /** + * can't use data.remaining() here, b/c there could be other data + * buffered past this message + */ + if ((length - count) >= statistics.getLength()) { + if (statistics instanceof OFActionFactoryAware) + ((OFActionFactoryAware)statistics).setActionFactory(this); + statistics.readFrom(data); + results.add(statistics); + count += statistics.getLength(); + statistics = getStatistics(t, st); + } else { + if (count < length) { + /** + * Nasty case: partial/incomplete statistic found even + * though we have a full message. Found when NOX sent + * agg_stats request with wrong agg statistics length (52 + * instead of 56) + * + * just throw the rest away, or we will break framing + */ + data.readerIndex(start + length); + } + return results; + } + } + return results; // empty; no statistics at all + } + + + @Override + public OFVendorData getVendorData(OFVendorId vendorId, + OFVendorDataType vendorDataType) { + if (vendorDataType == null) + return null; + + return vendorDataType.newInstance(); + } + + /** + * Attempts to parse and return the OFVendorData contained in the given + * ChannelBuffer, beginning right after the vendor id. + * @param vendor the vendor id that was parsed from the OFVendor message. + * @param data the ChannelBuffer from which to parse the vendor data + * @param length the length to the end of the enclosing message. + * @return an OFVendorData instance + */ + @Override + public OFVendorData parseVendorData(int vendor, ChannelBuffer data, + int length) { + OFVendorDataType vendorDataType = null; + OFVendorId vendorId = OFVendorId.lookupVendorId(vendor); + if (vendorId != null) { + data.markReaderIndex(); + vendorDataType = vendorId.parseVendorDataType(data, length); + data.resetReaderIndex(); + } + + OFVendorData vendorData = getVendorData(vendorId, vendorDataType); + if (vendorData == null) + vendorData = new OFByteArrayVendorData(); + + vendorData.readFrom(data, length); + + return vendorData; + } + + public static String dumpBuffer(ChannelBuffer data) { + // NOTE: Reads all the bytes in buffer from current read offset. + // Set/Reset ReaderIndex if you want to read from a different location + int len = data.readableBytes(); + StringBuffer sb = new StringBuffer(); + for (int i=0 ; i parseActions(ChannelBuffer data, int length); + + /** + * Attempts to parse and return all OFActions contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param data the ChannelBuffer to parse for OpenFlow actions + * @param length the number of Bytes to examine for OpenFlow actions + * @param limit the maximum number of messages to return, 0 means no limit + * @return a list of OFAction instances + */ + public List parseActions(ChannelBuffer data, int length, int limit); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java new file mode 100644 index 00000000..a97a95c0 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +/** + * Objects implementing this interface are expected to be instantiated with an + * instance of an OFActionFactory + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFActionFactoryAware { + /** + * Sets the OFActionFactory + * @param actionFactory + */ + public void setActionFactory(OFActionFactory actionFactory); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java new file mode 100644 index 00000000..8bb70454 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java @@ -0,0 +1,55 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; + + +/** + * The interface to factories used for retrieving OFMessage instances. All + * methods are expected to be thread-safe. + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFMessageFactory { + /** + * Retrieves an OFMessage instance corresponding to the specified OFType + * @param t the type of the OFMessage to be retrieved + * @return an OFMessage instance + */ + public OFMessage getMessage(OFType t); + + /** + * Attempts to parse and return a OFMessages contained in the given + * ChannelBuffer, beginning at the ChannelBuffer's position, and ending at the + * after the first parsed message + * @param data the ChannelBuffer to parse for an OpenFlow message + * @return a list of OFMessage instances + * @throws MessageParseException + */ + public List parseMessage(ChannelBuffer data) throws MessageParseException; + + /** + * Retrieves an OFActionFactory + * @return an OFActionFactory + */ + public OFActionFactory getActionFactory(); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java new file mode 100644 index 00000000..adb1421e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +/** + * + */ +package org.openflow.protocol.factory; + +/** + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ +public interface OFMessageFactoryAware { + + /** + * Sets the message factory for this object + * + * @param factory + */ + void setMessageFactory(OFMessageFactory factory); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java new file mode 100644 index 00000000..32eb3cbf --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java @@ -0,0 +1,72 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFType; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; + + +/** + * The interface to factories used for retrieving OFStatistics instances. All + * methods are expected to be thread-safe. + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFStatisticsFactory { + /** + * Retrieves an OFStatistics instance corresponding to the specified + * OFStatisticsType + * @param t the type of the containing OFMessage, only accepts statistics + * request or reply + * @param st the type of the OFStatistics to be retrieved + * @return an OFStatistics instance + */ + public OFStatistics getStatistics(OFType t, OFStatisticsType st); + + /** + * Attempts to parse and return all OFStatistics contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param t the type of the containing OFMessage, only accepts statistics + * request or reply + * @param st the type of the OFStatistics to be retrieved + * @param data the ChannelBuffer to parse for OpenFlow Statistics + * @param length the number of Bytes to examine for OpenFlow Statistics + * @return a list of OFStatistics instances + */ + public List parseStatistics(OFType t, + OFStatisticsType st, ChannelBuffer data, int length); + + /** + * Attempts to parse and return all OFStatistics contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param t the type of the containing OFMessage, only accepts statistics + * request or reply + * @param st the type of the OFStatistics to be retrieved + * @param data the ChannelBuffer to parse for OpenFlow Statistics + * @param length the number of Bytes to examine for OpenFlow Statistics + * @param limit the maximum number of messages to return, 0 means no limit + * @return a list of OFStatistics instances + */ + public List parseStatistics(OFType t, + OFStatisticsType st, ChannelBuffer data, int length, int limit); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java new file mode 100644 index 00000000..52ab09a9 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +/** + * Objects implementing this interface are expected to be instantiated with an + * instance of an OFStatisticsFactory + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFStatisticsFactoryAware { + /** + * Sets the OFStatisticsFactory + * @param statisticsFactory + */ + public void setStatisticsFactory(OFStatisticsFactory statisticsFactory); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java new file mode 100644 index 00000000..eb89810b --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java @@ -0,0 +1,43 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package org.openflow.protocol.factory; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; + +/** Interface contract for an actionfactory that creates vendor-specific actions. + * VendorActionFactories are registered with the BasicFactory for a specific + * vendor id. + *

      + * Note: Implementations are expected to be thread-safe. + * + * @author Andreas Wundsam + */ +public interface OFVendorActionFactory { + + /** parse the data from the wire, create and return a vendor-specific action. + * + * @param data contains a serialized vendor action at the current readerPosition. + * The full message is guaranteed to be available in the buffer. + * + * @return upon success returns a newly allocated vendor-specific + * action instance, and advances the readerPosition in data for the + * entire length. Upon failure, returns null and leaves the readerPosition + * in data unmodified. + */ + OFActionVendor readFrom(ChannelBuffer data); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java new file mode 100644 index 00000000..1f556812 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java @@ -0,0 +1,50 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package org.openflow.protocol.factory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** Singleton registry object that holds a mapping from vendor ids to vendor-specific + * mapping factories. Threadsafe. + * + * @author Andreas Wundsam + */ +public class OFVendorActionRegistry { + private static class InstanceHolder { + private final static OFVendorActionRegistry instance = new OFVendorActionRegistry(); + } + + public static OFVendorActionRegistry getInstance() { + return InstanceHolder.instance; + } + private final Map vendorActionFactories; + + public OFVendorActionRegistry() { + vendorActionFactories = new ConcurrentHashMap(); + } + + public OFVendorActionFactory register(int vendorId, OFVendorActionFactory factory) { + return vendorActionFactories.put(vendorId, factory); + } + + public OFVendorActionFactory get(int vendorId) { + return vendorActionFactories.get(vendorId); + } + + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java new file mode 100644 index 00000000..d754a4a3 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java @@ -0,0 +1,69 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; +import org.openflow.protocol.vendor.OFVendorDataType; +import org.openflow.protocol.vendor.OFVendorId; + +/** + * The interface to factories used for parsing/creating OFVendorData instances. + * All methods are expected to be thread-safe. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public interface OFVendorDataFactory { + /** + * Retrieves an OFVendorData instance corresponding to the specified + * OFVendorId and OFVendorDataType. There are 3 possible cases for + * how this will be called: + * + * 1) If the vendor id in the OFVendor message is an unknown value, + * then this method is called with both vendorId and vendorDataType + * set to null. In this case typically the factory method should + * return an instance of OFGenericVendorData that just contains + * the raw byte array of the vendor data. + * + * 2) If the vendor id is known but no vendor data type has been + * registered for the data in the message, then vendorId is set to + * the appropriate OFVendorId instance and OFVendorDataType is set + * to null. This would typically be handled the same way as #1 + * + * 3) If both the vendor id and and vendor data type are known, then + * typically you'd just call the method in OFVendorDataType to + * instantiate the appropriate subclass of OFVendorData. + * + * @param vendorId the vendorId of the containing OFVendor message + * @param vendorDataType the type of the OFVendorData to be retrieved + * @return an OFVendorData instance + */ + public OFVendorData getVendorData(OFVendorId vendorId, + OFVendorDataType vendorDataType); + + /** + * Attempts to parse and return the OFVendorData contained in the given + * ChannelBuffer, beginning right after the vendor id. + * @param vendorId the vendor id that was parsed from the OFVendor message. + * @param data the ChannelBuffer from which to parse the vendor data + * @param length the length to the end of the enclosing message. + * @return an OFVendorData instance + */ + public OFVendorData parseVendorData(int vendorId, ChannelBuffer data, + int length); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java new file mode 100644 index 00000000..23614b0d --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java @@ -0,0 +1,28 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.factory; + +/** + * Classes implementing this interface are expected to be instantiated with an + * instance of an OFVendorDataFactory + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public interface OFVendorDataFactoryAware { + public void setVendorDataFactory(OFVendorDataFactory vendorDataFactory); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java new file mode 100644 index 00000000..b5a486c3 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java @@ -0,0 +1,128 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_aggregate_stats_reply structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFAggregateStatisticsReply implements OFStatistics { + protected long packetCount; + protected long byteCount; + protected int flowCount; + + /** + * @return the packetCount + */ + public long getPacketCount() { + return packetCount; + } + + /** + * @param packetCount the packetCount to set + */ + public void setPacketCount(long packetCount) { + this.packetCount = packetCount; + } + + /** + * @return the byteCount + */ + public long getByteCount() { + return byteCount; + } + + /** + * @param byteCount the byteCount to set + */ + public void setByteCount(long byteCount) { + this.byteCount = byteCount; + } + + /** + * @return the flowCount + */ + public int getFlowCount() { + return flowCount; + } + + /** + * @param flowCount the flowCount to set + */ + public void setFlowCount(int flowCount) { + this.flowCount = flowCount; + } + + @Override + public int getLength() { + return 24; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.packetCount = data.readLong(); + this.byteCount = data.readLong(); + this.flowCount = data.readInt(); + data.readInt(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeLong(this.packetCount); + data.writeLong(this.byteCount); + data.writeInt(this.flowCount); + data.writeInt(0); // pad + } + + @Override + public int hashCode() { + final int prime = 397; + int result = 1; + result = prime * result + (int) (byteCount ^ (byteCount >>> 32)); + result = prime * result + flowCount; + result = prime * result + (int) (packetCount ^ (packetCount >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFAggregateStatisticsReply)) { + return false; + } + OFAggregateStatisticsReply other = (OFAggregateStatisticsReply) obj; + if (byteCount != other.byteCount) { + return false; + } + if (flowCount != other.flowCount) { + return false; + } + if (packetCount != other.packetCount) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java new file mode 100644 index 00000000..f41a4f1d --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java @@ -0,0 +1,135 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMatch; + +/** + * Represents an ofp_aggregate_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFAggregateStatisticsRequest implements OFStatistics { + protected OFMatch match; + protected byte tableId; + protected short outPort; + + /** + * @return the match + */ + public OFMatch getMatch() { + return match; + } + + /** + * @param match the match to set + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the outPort + */ + public short getOutPort() { + return outPort; + } + + /** + * @param outPort the outPort to set + */ + public void setOutPort(short outPort) { + this.outPort = outPort; + } + + @Override + public int getLength() { + return 44; + } + + @Override + public void readFrom(ChannelBuffer data) { + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.tableId = data.readByte(); + data.readByte(); // pad + this.outPort = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + this.match.writeTo(data); + data.writeByte(this.tableId); + data.writeByte((byte) 0); + data.writeShort(this.outPort); + } + + @Override + public int hashCode() { + final int prime = 401; + int result = 1; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + outPort; + result = prime * result + tableId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFAggregateStatisticsRequest)) { + return false; + } + OFAggregateStatisticsRequest other = (OFAggregateStatisticsRequest) obj; + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (outPort != other.outPort) { + return false; + } + if (tableId != other.tableId) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java new file mode 100644 index 00000000..86ad782d --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java @@ -0,0 +1,225 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.StringByteSerializer; + +/** + * Represents an ofp_desc_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFDescriptionStatistics implements OFStatistics { + public static int DESCRIPTION_STRING_LENGTH = 256; + public static int SERIAL_NUMBER_LENGTH = 32; + + protected String manufacturerDescription; + protected String hardwareDescription; + protected String softwareDescription; + protected String serialNumber; + protected String datapathDescription; + + /** + * @return the manufacturerDescription + */ + public String getManufacturerDescription() { + return manufacturerDescription; + } + + /** + * @param manufacturerDescription the manufacturerDescription to set + */ + public void setManufacturerDescription(String manufacturerDescription) { + this.manufacturerDescription = manufacturerDescription; + } + + /** + * @return the hardwareDescription + */ + public String getHardwareDescription() { + return hardwareDescription; + } + + /** + * @param hardwareDescription the hardwareDescription to set + */ + public void setHardwareDescription(String hardwareDescription) { + this.hardwareDescription = hardwareDescription; + } + + /** + * @return the softwareDescription + */ + public String getSoftwareDescription() { + return softwareDescription; + } + + /** + * @param softwareDescription the softwareDescription to set + */ + public void setSoftwareDescription(String softwareDescription) { + this.softwareDescription = softwareDescription; + } + + /** + * @return the serialNumber + */ + public String getSerialNumber() { + return serialNumber; + } + + /** + * @param serialNumber the serialNumber to set + */ + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + /** + * @return the datapathDescription + */ + public String getDatapathDescription() { + return datapathDescription; + } + + /** + * @param datapathDescription the datapathDescription to set + */ + public void setDatapathDescription(String datapathDescription) { + this.datapathDescription = datapathDescription; + } + + @Override + public int getLength() { + return 1056; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.manufacturerDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + this.hardwareDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + this.softwareDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + this.serialNumber = StringByteSerializer.readFrom(data, + SERIAL_NUMBER_LENGTH); + this.datapathDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.manufacturerDescription); + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.hardwareDescription); + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.softwareDescription); + StringByteSerializer.writeTo(data, SERIAL_NUMBER_LENGTH, + this.serialNumber); + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.datapathDescription); + } + + @Override + public int hashCode() { + final int prime = 409; + int result = 1; + result = prime + * result + + ((datapathDescription == null) ? 0 : datapathDescription + .hashCode()); + result = prime + * result + + ((hardwareDescription == null) ? 0 : hardwareDescription + .hashCode()); + result = prime + * result + + ((manufacturerDescription == null) ? 0 + : manufacturerDescription.hashCode()); + result = prime * result + + ((serialNumber == null) ? 0 : serialNumber.hashCode()); + result = prime + * result + + ((softwareDescription == null) ? 0 : softwareDescription + .hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFDescriptionStatistics)) { + return false; + } + OFDescriptionStatistics other = (OFDescriptionStatistics) obj; + if (datapathDescription == null) { + if (other.datapathDescription != null) { + return false; + } + } else if (!datapathDescription.equals(other.datapathDescription)) { + return false; + } + if (hardwareDescription == null) { + if (other.hardwareDescription != null) { + return false; + } + } else if (!hardwareDescription.equals(other.hardwareDescription)) { + return false; + } + if (manufacturerDescription == null) { + if (other.manufacturerDescription != null) { + return false; + } + } else if (!manufacturerDescription + .equals(other.manufacturerDescription)) { + return false; + } + if (serialNumber == null) { + if (other.serialNumber != null) { + return false; + } + } else if (!serialNumber.equals(other.serialNumber)) { + return false; + } + if (softwareDescription == null) { + if (other.softwareDescription != null) { + return false; + } + } else if (!softwareDescription.equals(other.softwareDescription)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Switch Desc - Vendor: " + manufacturerDescription + + " Model: " + hardwareDescription + + " Make: " + datapathDescription + + " Version: " + softwareDescription + + " S/N: " + serialNumber; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java new file mode 100644 index 00000000..0570779b --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java @@ -0,0 +1,357 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.factory.OFActionFactory; +import org.openflow.protocol.factory.OFActionFactoryAware; +import org.openflow.util.U16; + +/** + * Represents an ofp_flow_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFFlowStatisticsReply implements OFStatistics, OFActionFactoryAware { + public static int MINIMUM_LENGTH = 88; + + protected OFActionFactory actionFactory; + protected short length = (short) MINIMUM_LENGTH; + protected byte tableId; + protected OFMatch match; + protected int durationSeconds; + protected int durationNanoseconds; + protected short priority; + protected short idleTimeout; + protected short hardTimeout; + protected long cookie; + protected long packetCount; + protected long byteCount; + protected List actions; + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the match + */ + public OFMatch getMatch() { + return match; + } + + /** + * @param match the match to set + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * @return the durationSeconds + */ + public int getDurationSeconds() { + return durationSeconds; + } + + /** + * @param durationSeconds the durationSeconds to set + */ + public void setDurationSeconds(int durationSeconds) { + this.durationSeconds = durationSeconds; + } + + /** + * @return the durationNanoseconds + */ + public int getDurationNanoseconds() { + return durationNanoseconds; + } + + /** + * @param durationNanoseconds the durationNanoseconds to set + */ + public void setDurationNanoseconds(int durationNanoseconds) { + this.durationNanoseconds = durationNanoseconds; + } + + /** + * @return the priority + */ + public short getPriority() { + return priority; + } + + /** + * @param priority the priority to set + */ + public void setPriority(short priority) { + this.priority = priority; + } + + /** + * @return the idleTimeout + */ + public short getIdleTimeout() { + return idleTimeout; + } + + /** + * @param idleTimeout the idleTimeout to set + */ + public void setIdleTimeout(short idleTimeout) { + this.idleTimeout = idleTimeout; + } + + /** + * @return the hardTimeout + */ + public short getHardTimeout() { + return hardTimeout; + } + + /** + * @param hardTimeout the hardTimeout to set + */ + public void setHardTimeout(short hardTimeout) { + this.hardTimeout = hardTimeout; + } + + /** + * @return the cookie + */ + public long getCookie() { + return cookie; + } + + /** + * @param cookie the cookie to set + */ + public void setCookie(long cookie) { + this.cookie = cookie; + } + + /** + * @return the packetCount + */ + public long getPacketCount() { + return packetCount; + } + + /** + * @param packetCount the packetCount to set + */ + public void setPacketCount(long packetCount) { + this.packetCount = packetCount; + } + + /** + * @return the byteCount + */ + public long getByteCount() { + return byteCount; + } + + /** + * @param byteCount the byteCount to set + */ + public void setByteCount(long byteCount) { + this.byteCount = byteCount; + } + + /** + * @param length the length to set + */ + public void setLength(short length) { + this.length = length; + } + + @Override + public int getLength() { + return U16.f(length); + } + + /** + * @param actionFactory the actionFactory to set + */ + @Override + public void setActionFactory(OFActionFactory actionFactory) { + this.actionFactory = actionFactory; + } + + /** + * @return the actions + */ + public List getActions() { + return actions; + } + + /** + * @param actions the actions to set + */ + public void setActions(List actions) { + this.actions = actions; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.length = data.readShort(); + this.tableId = data.readByte(); + data.readByte(); // pad + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.durationSeconds = data.readInt(); + this.durationNanoseconds = data.readInt(); + this.priority = data.readShort(); + this.idleTimeout = data.readShort(); + this.hardTimeout = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + this.cookie = data.readLong(); + this.packetCount = data.readLong(); + this.byteCount = data.readLong(); + if (this.actionFactory == null) + throw new RuntimeException("OFActionFactory not set"); + this.actions = this.actionFactory.parseActions(data, getLength() - + MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.length); + data.writeByte(this.tableId); + data.writeByte((byte) 0); + this.match.writeTo(data); + data.writeInt(this.durationSeconds); + data.writeInt(this.durationNanoseconds); + data.writeShort(this.priority); + data.writeShort(this.idleTimeout); + data.writeShort(this.hardTimeout); + data.writeInt(0); // pad + data.writeShort((short)0); // pad + data.writeLong(this.cookie); + data.writeLong(this.packetCount); + data.writeLong(this.byteCount); + if (actions != null) { + for (OFAction action : actions) { + action.writeTo(data); + } + } + } + + @Override + public String toString() { + String str = "match=" + this.match; + str += " tableId=" + this.tableId; + str += " durationSeconds=" + this.durationSeconds; + str += " durationNanoseconds=" + this.durationNanoseconds; + str += " priority=" + this.priority; + str += " idleTimeout=" + this.idleTimeout; + str += " hardTimeout=" + this.hardTimeout; + str += " cookie=" + this.cookie; + str += " packetCount=" + this.packetCount; + str += " byteCount=" + this.byteCount; + str += " action=" + this.actions; + + return str; + } + + @Override + public int hashCode() { + final int prime = 419; + int result = 1; + result = prime * result + (int) (byteCount ^ (byteCount >>> 32)); + result = prime * result + (int) (cookie ^ (cookie >>> 32)); + result = prime * result + durationNanoseconds; + result = prime * result + durationSeconds; + result = prime * result + hardTimeout; + result = prime * result + idleTimeout; + result = prime * result + length; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + (int) (packetCount ^ (packetCount >>> 32)); + result = prime * result + priority; + result = prime * result + tableId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFFlowStatisticsReply)) { + return false; + } + OFFlowStatisticsReply other = (OFFlowStatisticsReply) obj; + if (byteCount != other.byteCount) { + return false; + } + if (cookie != other.cookie) { + return false; + } + if (durationNanoseconds != other.durationNanoseconds) { + return false; + } + if (durationSeconds != other.durationSeconds) { + return false; + } + if (hardTimeout != other.hardTimeout) { + return false; + } + if (idleTimeout != other.idleTimeout) { + return false; + } + if (length != other.length) { + return false; + } + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (packetCount != other.packetCount) { + return false; + } + if (priority != other.priority) { + return false; + } + if (tableId != other.tableId) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java new file mode 100644 index 00000000..b21de0c7 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java @@ -0,0 +1,135 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMatch; + +/** + * Represents an ofp_flow_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFFlowStatisticsRequest implements OFStatistics { + protected OFMatch match; + protected byte tableId; + protected short outPort; + + /** + * @return the match + */ + public OFMatch getMatch() { + return match; + } + + /** + * @param match the match to set + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the outPort + */ + public short getOutPort() { + return outPort; + } + + /** + * @param outPort the outPort to set + */ + public void setOutPort(short outPort) { + this.outPort = outPort; + } + + @Override + public int getLength() { + return 44; + } + + @Override + public void readFrom(ChannelBuffer data) { + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.tableId = data.readByte(); + data.readByte(); // pad + this.outPort = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + this.match.writeTo(data); + data.writeByte(this.tableId); + data.writeByte((byte) 0); + data.writeShort(this.outPort); + } + + @Override + public int hashCode() { + final int prime = 421; + int result = 1; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + outPort; + result = prime * result + tableId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFFlowStatisticsRequest)) { + return false; + } + OFFlowStatisticsRequest other = (OFFlowStatisticsRequest) obj; + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (outPort != other.outPort) { + return false; + } + if (tableId != other.tableId) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java new file mode 100644 index 00000000..02b97b33 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java @@ -0,0 +1,351 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_port_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortStatisticsReply implements OFStatistics { + protected short portNumber; + protected long receivePackets; + protected long transmitPackets; + protected long receiveBytes; + protected long transmitBytes; + protected long receiveDropped; + protected long transmitDropped; + protected long receiveErrors; + protected long transmitErrors; + protected long receiveFrameErrors; + protected long receiveOverrunErrors; + protected long receiveCRCErrors; + protected long collisions; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the receivePackets + */ + public long getreceivePackets() { + return receivePackets; + } + + /** + * @param receivePackets the receivePackets to set + */ + public void setreceivePackets(long receivePackets) { + this.receivePackets = receivePackets; + } + + /** + * @return the transmitPackets + */ + public long getTransmitPackets() { + return transmitPackets; + } + + /** + * @param transmitPackets the transmitPackets to set + */ + public void setTransmitPackets(long transmitPackets) { + this.transmitPackets = transmitPackets; + } + + /** + * @return the receiveBytes + */ + public long getReceiveBytes() { + return receiveBytes; + } + + /** + * @param receiveBytes the receiveBytes to set + */ + public void setReceiveBytes(long receiveBytes) { + this.receiveBytes = receiveBytes; + } + + /** + * @return the transmitBytes + */ + public long getTransmitBytes() { + return transmitBytes; + } + + /** + * @param transmitBytes the transmitBytes to set + */ + public void setTransmitBytes(long transmitBytes) { + this.transmitBytes = transmitBytes; + } + + /** + * @return the receiveDropped + */ + public long getReceiveDropped() { + return receiveDropped; + } + + /** + * @param receiveDropped the receiveDropped to set + */ + public void setReceiveDropped(long receiveDropped) { + this.receiveDropped = receiveDropped; + } + + /** + * @return the transmitDropped + */ + public long getTransmitDropped() { + return transmitDropped; + } + + /** + * @param transmitDropped the transmitDropped to set + */ + public void setTransmitDropped(long transmitDropped) { + this.transmitDropped = transmitDropped; + } + + /** + * @return the receiveErrors + */ + public long getreceiveErrors() { + return receiveErrors; + } + + /** + * @param receiveErrors the receiveErrors to set + */ + public void setreceiveErrors(long receiveErrors) { + this.receiveErrors = receiveErrors; + } + + /** + * @return the transmitErrors + */ + public long getTransmitErrors() { + return transmitErrors; + } + + /** + * @param transmitErrors the transmitErrors to set + */ + public void setTransmitErrors(long transmitErrors) { + this.transmitErrors = transmitErrors; + } + + /** + * @return the receiveFrameErrors + */ + public long getReceiveFrameErrors() { + return receiveFrameErrors; + } + + /** + * @param receiveFrameErrors the receiveFrameErrors to set + */ + public void setReceiveFrameErrors(long receiveFrameErrors) { + this.receiveFrameErrors = receiveFrameErrors; + } + + /** + * @return the receiveOverrunErrors + */ + public long getReceiveOverrunErrors() { + return receiveOverrunErrors; + } + + /** + * @param receiveOverrunErrors the receiveOverrunErrors to set + */ + public void setReceiveOverrunErrors(long receiveOverrunErrors) { + this.receiveOverrunErrors = receiveOverrunErrors; + } + + /** + * @return the receiveCRCErrors + */ + public long getReceiveCRCErrors() { + return receiveCRCErrors; + } + + /** + * @param receiveCRCErrors the receiveCRCErrors to set + */ + public void setReceiveCRCErrors(long receiveCRCErrors) { + this.receiveCRCErrors = receiveCRCErrors; + } + + /** + * @return the collisions + */ + public long getCollisions() { + return collisions; + } + + /** + * @param collisions the collisions to set + */ + public void setCollisions(long collisions) { + this.collisions = collisions; + } + + @Override + public int getLength() { + return 104; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + data.readInt(); // pad + this.receivePackets = data.readLong(); + this.transmitPackets = data.readLong(); + this.receiveBytes = data.readLong(); + this.transmitBytes = data.readLong(); + this.receiveDropped = data.readLong(); + this.transmitDropped = data.readLong(); + this.receiveErrors = data.readLong(); + this.transmitErrors = data.readLong(); + this.receiveFrameErrors = data.readLong(); + this.receiveOverrunErrors = data.readLong(); + this.receiveCRCErrors = data.readLong(); + this.collisions = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(0); // pad + data.writeLong(this.receivePackets); + data.writeLong(this.transmitPackets); + data.writeLong(this.receiveBytes); + data.writeLong(this.transmitBytes); + data.writeLong(this.receiveDropped); + data.writeLong(this.transmitDropped); + data.writeLong(this.receiveErrors); + data.writeLong(this.transmitErrors); + data.writeLong(this.receiveFrameErrors); + data.writeLong(this.receiveOverrunErrors); + data.writeLong(this.receiveCRCErrors); + data.writeLong(this.collisions); + } + + @Override + public int hashCode() { + final int prime = 431; + int result = 1; + result = prime * result + (int) (collisions ^ (collisions >>> 32)); + result = prime * result + portNumber; + result = prime * result + + (int) (receivePackets ^ (receivePackets >>> 32)); + result = prime * result + (int) (receiveBytes ^ (receiveBytes >>> 32)); + result = prime * result + + (int) (receiveCRCErrors ^ (receiveCRCErrors >>> 32)); + result = prime * result + + (int) (receiveDropped ^ (receiveDropped >>> 32)); + result = prime * result + + (int) (receiveFrameErrors ^ (receiveFrameErrors >>> 32)); + result = prime * result + + (int) (receiveOverrunErrors ^ (receiveOverrunErrors >>> 32)); + result = prime * result + + (int) (receiveErrors ^ (receiveErrors >>> 32)); + result = prime * result + + (int) (transmitBytes ^ (transmitBytes >>> 32)); + result = prime * result + + (int) (transmitDropped ^ (transmitDropped >>> 32)); + result = prime * result + + (int) (transmitErrors ^ (transmitErrors >>> 32)); + result = prime * result + + (int) (transmitPackets ^ (transmitPackets >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFPortStatisticsReply)) { + return false; + } + OFPortStatisticsReply other = (OFPortStatisticsReply) obj; + if (collisions != other.collisions) { + return false; + } + if (portNumber != other.portNumber) { + return false; + } + if (receivePackets != other.receivePackets) { + return false; + } + if (receiveBytes != other.receiveBytes) { + return false; + } + if (receiveCRCErrors != other.receiveCRCErrors) { + return false; + } + if (receiveDropped != other.receiveDropped) { + return false; + } + if (receiveFrameErrors != other.receiveFrameErrors) { + return false; + } + if (receiveOverrunErrors != other.receiveOverrunErrors) { + return false; + } + if (receiveErrors != other.receiveErrors) { + return false; + } + if (transmitBytes != other.transmitBytes) { + return false; + } + if (transmitDropped != other.transmitDropped) { + return false; + } + if (transmitErrors != other.transmitErrors) { + return false; + } + if (transmitPackets != other.transmitPackets) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java new file mode 100644 index 00000000..c07a895a --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_port_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortStatisticsRequest implements OFStatistics { + protected short portNumber; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + data.readInt(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(0); // pad + } + + @Override + public int hashCode() { + final int prime = 433; + int result = 1; + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFPortStatisticsRequest)) { + return false; + } + OFPortStatisticsRequest other = (OFPortStatisticsRequest) obj; + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java new file mode 100644 index 00000000..7d0238a3 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java @@ -0,0 +1,173 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_queue_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFQueueStatisticsReply implements OFStatistics { + protected short portNumber; + protected int queueId; + protected long transmitBytes; + protected long transmitPackets; + protected long transmitErrors; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the queueId + */ + public int getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + /** + * @return the transmitBytes + */ + public long getTransmitBytes() { + return transmitBytes; + } + + /** + * @param transmitBytes the transmitBytes to set + */ + public void setTransmitBytes(long transmitBytes) { + this.transmitBytes = transmitBytes; + } + + /** + * @return the transmitPackets + */ + public long getTransmitPackets() { + return transmitPackets; + } + + /** + * @param transmitPackets the transmitPackets to set + */ + public void setTransmitPackets(long transmitPackets) { + this.transmitPackets = transmitPackets; + } + + /** + * @return the transmitErrors + */ + public long getTransmitErrors() { + return transmitErrors; + } + + /** + * @param transmitErrors the transmitErrors to set + */ + public void setTransmitErrors(long transmitErrors) { + this.transmitErrors = transmitErrors; + } + + @Override + public int getLength() { + return 32; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + this.queueId = data.readInt(); + this.transmitBytes = data.readLong(); + this.transmitPackets = data.readLong(); + this.transmitErrors = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(this.queueId); + data.writeLong(this.transmitBytes); + data.writeLong(this.transmitPackets); + data.writeLong(this.transmitErrors); + } + + @Override + public int hashCode() { + final int prime = 439; + int result = 1; + result = prime * result + portNumber; + result = prime * result + queueId; + result = prime * result + + (int) (transmitBytes ^ (transmitBytes >>> 32)); + result = prime * result + + (int) (transmitErrors ^ (transmitErrors >>> 32)); + result = prime * result + + (int) (transmitPackets ^ (transmitPackets >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFQueueStatisticsReply)) { + return false; + } + OFQueueStatisticsReply other = (OFQueueStatisticsReply) obj; + if (portNumber != other.portNumber) { + return false; + } + if (queueId != other.queueId) { + return false; + } + if (transmitBytes != other.transmitBytes) { + return false; + } + if (transmitErrors != other.transmitErrors) { + return false; + } + if (transmitPackets != other.transmitPackets) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java new file mode 100644 index 00000000..33314539 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java @@ -0,0 +1,107 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_queue_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFQueueStatisticsRequest implements OFStatistics { + protected short portNumber; + protected int queueId; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the queueId + */ + public int getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + this.queueId = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(this.queueId); + } + + @Override + public int hashCode() { + final int prime = 443; + int result = 1; + result = prime * result + portNumber; + result = prime * result + queueId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFQueueStatisticsRequest)) { + return false; + } + OFQueueStatisticsRequest other = (OFQueueStatisticsRequest) obj; + if (portNumber != other.portNumber) { + return false; + } + if (queueId != other.queueId) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatistics.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatistics.java new file mode 100644 index 00000000..5e8f4dd3 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatistics.java @@ -0,0 +1,45 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * The base class for all OpenFlow statistics. + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public interface OFStatistics { + /** + * Returns the wire length of this message in bytes + * @return the length + */ + public int getLength(); + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data); + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java new file mode 100644 index 00000000..f2b9e301 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java @@ -0,0 +1,317 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + +import java.lang.reflect.Constructor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.OFType; + +public enum OFStatisticsType { + DESC (0, OFDescriptionStatistics.class, OFDescriptionStatistics.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFDescriptionStatistics(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFDescriptionStatistics(); + } + }), + FLOW (1, OFFlowStatisticsRequest.class, OFFlowStatisticsReply.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFFlowStatisticsRequest(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFFlowStatisticsReply(); + } + }), + AGGREGATE (2, OFAggregateStatisticsRequest.class, OFAggregateStatisticsReply.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFAggregateStatisticsRequest(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFAggregateStatisticsReply(); + } + }), + TABLE (3, OFTableStatistics.class, OFTableStatistics.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFTableStatistics(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFTableStatistics(); + } + }), + PORT (4, OFPortStatisticsRequest.class, OFPortStatisticsReply.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFPortStatisticsRequest(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFPortStatisticsReply(); + } + }), + QUEUE (5, OFQueueStatisticsRequest.class, OFQueueStatisticsReply.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFQueueStatisticsRequest(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFQueueStatisticsReply(); + } + }), + VENDOR (0xffff, OFVendorStatistics.class, OFVendorStatistics.class, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFVendorStatistics(); + } + }, + new Instantiable() { + @Override + public OFStatistics instantiate() { + return new OFVendorStatistics(); + } + }); + + static OFStatisticsType[] requestMapping; + static OFStatisticsType[] replyMapping; + + protected Class requestClass; + protected Constructor requestConstructor; + protected Instantiable requestInstantiable; + protected Class replyClass; + protected Constructor replyConstructor; + protected Instantiable replyInstantiable; + protected short type; + + /** + * Store some information about the OpenFlow Statistic type, including wire + * protocol type number, and derived class + * + * @param type Wire protocol number associated with this OFStatisticsType + * @param requestClass The Statistics Java class to return when the + * containing OFType is STATS_REQUEST + * @param replyClass The Statistics Java class to return when the + * containing OFType is STATS_REPLY + */ + OFStatisticsType(int type, Class requestClass, + Class replyClass, + Instantiable requestInstantiable, + Instantiable replyInstantiable) { + this.type = (short) type; + this.requestClass = requestClass; + try { + this.requestConstructor = requestClass.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + requestClass, e); + } + + this.replyClass = replyClass; + try { + this.replyConstructor = replyClass.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + replyClass, e); + } + this.requestInstantiable = requestInstantiable; + this.replyInstantiable = replyInstantiable; + OFStatisticsType.addMapping(this.type, OFType.STATS_REQUEST, this); + OFStatisticsType.addMapping(this.type, OFType.STATS_REPLY, this); + } + + /** + * Adds a mapping from type value to OFStatisticsType enum + * + * @param i OpenFlow wire protocol type + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + * @param st type + */ + static public void addMapping(short i, OFType t, OFStatisticsType st) { + if (i < 0) + i = (short) (16+i); + if (t == OFType.STATS_REQUEST) { + if (requestMapping == null) + requestMapping = new OFStatisticsType[16]; + OFStatisticsType.requestMapping[i] = st; + } else if (t == OFType.STATS_REPLY){ + if (replyMapping == null) + replyMapping = new OFStatisticsType[16]; + OFStatisticsType.replyMapping[i] = st; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * Remove a mapping from type value to OFStatisticsType enum + * + * @param i OpenFlow wire protocol type + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + */ + static public void removeMapping(short i, OFType t) { + if (i < 0) + i = (short) (16+i); + if (t == OFType.STATS_REQUEST) { + requestMapping[i] = null; + } else if (t == OFType.STATS_REPLY){ + replyMapping[i] = null; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * Given a wire protocol OpenFlow type number, return the OFStatisticsType + * associated with it + * + * @param i wire protocol number + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + * @return OFStatisticsType enum type + */ + static public OFStatisticsType valueOf(short i, OFType t) { + if (i < 0) + i = (short) (16+i); + if (t == OFType.STATS_REQUEST) { + return requestMapping[i]; + } else if (t == OFType.STATS_REPLY){ + return replyMapping[i]; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * @return Returns the wire protocol value corresponding to this + * OFStatisticsType + */ + public short getTypeValue() { + return this.type; + } + + /** + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + * @return return the OFMessage subclass corresponding to this + * OFStatisticsType + */ + public Class toClass(OFType t) { + if (t == OFType.STATS_REQUEST) { + return requestClass; + } else if (t == OFType.STATS_REPLY){ + return replyClass; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * Returns the no-argument Constructor of the implementation class for + * this OFStatisticsType, either request or reply based on the supplied + * OFType + * + * @param t + * @return + */ + public Constructor getConstructor(OFType t) { + if (t == OFType.STATS_REQUEST) { + return requestConstructor; + } else if (t == OFType.STATS_REPLY) { + return replyConstructor; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * @return the requestInstantiable + */ + public Instantiable getRequestInstantiable() { + return requestInstantiable; + } + + /** + * @param requestInstantiable the requestInstantiable to set + */ + public void setRequestInstantiable( + Instantiable requestInstantiable) { + this.requestInstantiable = requestInstantiable; + } + + /** + * @return the replyInstantiable + */ + public Instantiable getReplyInstantiable() { + return replyInstantiable; + } + + /** + * @param replyInstantiable the replyInstantiable to set + */ + public void setReplyInstantiable(Instantiable replyInstantiable) { + this.replyInstantiable = replyInstantiable; + } + + /** + * Returns a new instance of the implementation class for + * this OFStatisticsType, either request or reply based on the supplied + * OFType + * + * @param t + * @return + */ + public OFStatistics newInstance(OFType t) { + if (t == OFType.STATS_REQUEST) { + return requestInstantiable.instantiate(); + } else if (t == OFType.STATS_REPLY) { + return replyInstantiable.instantiate(); + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java new file mode 100644 index 00000000..9e6d34e1 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java @@ -0,0 +1,223 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.StringByteSerializer; + +/** + * Represents an ofp_table_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFTableStatistics implements OFStatistics { + public static int MAX_TABLE_NAME_LEN = 32; + + protected byte tableId; + protected String name; + protected int wildcards; + protected int maximumEntries; + protected int activeCount; + protected long lookupCount; + protected long matchedCount; + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the wildcards + */ + public int getWildcards() { + return wildcards; + } + + /** + * @param wildcards the wildcards to set + */ + public void setWildcards(int wildcards) { + this.wildcards = wildcards; + } + + /** + * @return the maximumEntries + */ + public int getMaximumEntries() { + return maximumEntries; + } + + /** + * @param maximumEntries the maximumEntries to set + */ + public void setMaximumEntries(int maximumEntries) { + this.maximumEntries = maximumEntries; + } + + /** + * @return the activeCount + */ + public int getActiveCount() { + return activeCount; + } + + /** + * @param activeCount the activeCount to set + */ + public void setActiveCount(int activeCount) { + this.activeCount = activeCount; + } + + /** + * @return the lookupCount + */ + public long getLookupCount() { + return lookupCount; + } + + /** + * @param lookupCount the lookupCount to set + */ + public void setLookupCount(long lookupCount) { + this.lookupCount = lookupCount; + } + + /** + * @return the matchedCount + */ + public long getMatchedCount() { + return matchedCount; + } + + /** + * @param matchedCount the matchedCount to set + */ + public void setMatchedCount(long matchedCount) { + this.matchedCount = matchedCount; + } + + @Override + public int getLength() { + return 64; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.tableId = data.readByte(); + data.readByte(); // pad + data.readByte(); // pad + data.readByte(); // pad + this.name = StringByteSerializer.readFrom(data, MAX_TABLE_NAME_LEN); + this.wildcards = data.readInt(); + this.maximumEntries = data.readInt(); + this.activeCount = data.readInt(); + this.lookupCount = data.readLong(); + this.matchedCount = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeByte(this.tableId); + data.writeByte((byte) 0); // pad + data.writeByte((byte) 0); // pad + data.writeByte((byte) 0); // pad + StringByteSerializer.writeTo(data, MAX_TABLE_NAME_LEN, this.name); + data.writeInt(this.wildcards); + data.writeInt(this.maximumEntries); + data.writeInt(this.activeCount); + data.writeLong(this.lookupCount); + data.writeLong(this.matchedCount); + } + + @Override + public int hashCode() { + final int prime = 449; + int result = 1; + result = prime * result + activeCount; + result = prime * result + (int) (lookupCount ^ (lookupCount >>> 32)); + result = prime * result + (int) (matchedCount ^ (matchedCount >>> 32)); + result = prime * result + maximumEntries; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + tableId; + result = prime * result + wildcards; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFTableStatistics)) { + return false; + } + OFTableStatistics other = (OFTableStatistics) obj; + if (activeCount != other.activeCount) { + return false; + } + if (lookupCount != other.lookupCount) { + return false; + } + if (matchedCount != other.matchedCount) { + return false; + } + if (maximumEntries != other.maximumEntries) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (tableId != other.tableId) { + return false; + } + if (wildcards != other.wildcards) { + return false; + } + return true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java new file mode 100644 index 00000000..0257a6aa --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java @@ -0,0 +1,83 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * The base class for vendor implemented statistics + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFVendorStatistics implements OFStatistics { + protected int vendor; + protected byte[] body; + + // non-message fields + protected int length = 0; + + @Override + public void readFrom(ChannelBuffer data) { + this.vendor = data.readInt(); + if (body == null) + body = new byte[length - 4]; + data.readBytes(body); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeInt(this.vendor); + if (body != null) + data.writeBytes(body); + } + + @Override + public int hashCode() { + final int prime = 457; + int result = 1; + result = prime * result + vendor; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFVendorStatistics)) { + return false; + } + OFVendorStatistics other = (OFVendorStatistics) obj; + if (vendor != other.vendor) { + return false; + } + return true; + } + + @Override + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java new file mode 100644 index 00000000..1f0e14b2 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java @@ -0,0 +1,71 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.vendor; + +import org.openflow.protocol.Instantiable; + +/** + * Subclass of OFVendorDataType that works with any vendor data format that + * begins with a integral value to indicate the format of the remaining data. + * It maps from the per-vendor-id integral data type code to the object + * used to instantiate the class associated with that vendor data type. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFBasicVendorDataType extends OFVendorDataType { + + /** + * The data type value at the beginning of the vendor data. + */ + protected long type; + + /** + * Construct an empty (i.e. no specified data type value) vendor data type. + */ + public OFBasicVendorDataType() { + super(); + this.type = 0; + } + + /** + * Store some information about the vendor data type, including wire protocol + * type number, derived class and instantiator. + * + * @param type Wire protocol number associated with this vendor data type + * @param instantiator An Instantiator implementation that + * creates an instance of an appropriate subclass of OFVendorData. + */ + public OFBasicVendorDataType(long type, Instantiable instantiator) { + super(instantiator); + this.type = type; + } + + /** + * @return Returns the wire protocol value corresponding to this OFVendorDataType + */ + public long getTypeValue() { + return this.type; + } + + /** + * @param type the wire protocol value for this data type + */ + public void setTypeValue(long type) { + this.type = type; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java new file mode 100644 index 00000000..5f789dc3 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java @@ -0,0 +1,162 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.vendor; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.Instantiable; + +/** + * Basic subclass of OFVendorId that works with any vendor data format where + * the data begins with an integral data type value. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFBasicVendorId extends OFVendorId { + + /** + * The size of the data type value at the beginning of all vendor + * data associated with this vendor id. The data type size must be + * either 1, 2, 4 or 8. + */ + protected int dataTypeSize; + + /** + * Map of the vendor data types that have been registered for this + * vendor id. + */ + protected Map dataTypeMap = + new HashMap(); + + /** + * Construct an OFVendorId that where the vendor data begins + * with a data type value whose size is dataTypeSize. + * @param id the id of the vendor, typically the OUI of a vendor + * prefixed with 0. + * @param dataTypeSize the size of the integral data type value + * at the beginning of the vendor data. The value must be the + * size of an integeral data type (i.e. either 1,2,4 or 8). + */ + public OFBasicVendorId(int id, int dataTypeSize) { + super(id); + assert (dataTypeSize == 1) || (dataTypeSize == 2) || + (dataTypeSize == 4) || (dataTypeSize == 8); + this.dataTypeSize = dataTypeSize; + } + + /** + * Get the size of the data type value at the beginning of the vendor + * data. OFBasicVendorId assumes that this value is common across all of + * the vendor data formats associated with a given vendor id. + * @return + */ + public int getDataTypeSize() { + return dataTypeSize; + } + + /** + * Register a vendor data type with this vendor id. + * @param vendorDataType + */ + public void registerVendorDataType(OFBasicVendorDataType vendorDataType) { + dataTypeMap.put(vendorDataType.getTypeValue(), vendorDataType); + } + + /** + * Lookup the OFVendorDataType instance that has been registered with + * this vendor id. + * + * @param vendorDataType the integer code that was parsed from the + * @return + */ + public OFVendorDataType lookupVendorDataType(int vendorDataType) { + return dataTypeMap.get(Long.valueOf(vendorDataType)); + } + + /** + * This function parses enough of the data from the buffer to be able + * to determine the appropriate OFVendorDataType for the data. It is meant + * to be a reasonably generic implementation that will work for most + * formats of vendor extensions. If the vendor data doesn't fit the + * assumptions listed below, then this method will need to be overridden + * to implement custom parsing. + * + * This implementation assumes that the vendor data begins with a data + * type code that is used to distinguish different formats of vendor + * data associated with a particular vendor ID. + * The exact format of the data is vendor-defined, so we don't know how + * how big the code is (or really even if there is a code). This code + * assumes that the common case will be that the data does include + * an initial type code (i.e. so that the vendor can have multiple + * message/data types) and that the size is either 1, 2 or 4 bytes. + * The size of the initial type code is configured by the subclass of + * OFVendorId. + * + * @param data the channel buffer containing the vendor data. + * @param length the length to the end of the enclosing message + * @return the OFVendorDataType that can be used to instantiate the + * appropriate subclass of OFVendorData. + */ + public OFVendorDataType parseVendorDataType(ChannelBuffer data, int length) { + OFVendorDataType vendorDataType = null; + + // Parse out the type code from the vendor data. + long dataTypeValue = 0; + if ((length == 0) || (length >= dataTypeSize)) { + switch (dataTypeSize) { + case 1: + dataTypeValue = data.readByte(); + break; + case 2: + dataTypeValue = data.readShort(); + break; + case 4: + dataTypeValue = data.readInt(); + break; + case 8: + dataTypeValue = data.readLong(); + break; + default: + // This would be indicative of a coding error where the + // dataTypeSize was specified incorrectly. This should have been + // caught in the constructor for OFVendorId. + assert false; + } + + vendorDataType = dataTypeMap.get(dataTypeValue); + } + + // If we weren't able to parse/map the data to a known OFVendorDataType, + // then map it to a generic vendor data type. + if (vendorDataType == null) { + vendorDataType = new OFBasicVendorDataType(dataTypeValue, + new Instantiable() { + @Override + public OFVendorData instantiate() { + return new OFByteArrayVendorData(); + } + } + ); + } + + return vendorDataType; + } + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java new file mode 100644 index 00000000..08fa0031 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java @@ -0,0 +1,94 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Basic implementation of OFVendorData that just treats the data as a + * byte array. This is used if there's an OFVendor message where there's + * no registered OFVendorId or no specific OFVendorDataType that can be + * determined from the data. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFByteArrayVendorData implements OFVendorData { + + protected byte[] bytes; + + /** + * Construct vendor data with an empty byte array. + */ + public OFByteArrayVendorData() { + } + + /** + * Construct vendor data with the specified byte array. + * @param bytes + */ + public OFByteArrayVendorData(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Get the associated byte array for this vendor data. + * @return the byte array containing the raw vendor data. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * Set the byte array for the vendor data. + * @param bytes the raw byte array containing the vendor data. + */ + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Get the length of the vendor data. In this case it's just then length + * of the underlying byte array. + * @return the length of the vendor data + */ + @Override + public int getLength() { + return (bytes != null) ? bytes.length : 0; + } + + /** + * Read the vendor data from the ChannelBuffer into the byte array. + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + bytes = new byte[length]; + data.readBytes(bytes); + } + + /** + * Write the vendor data bytes to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + if (bytes != null) + data.writeBytes(bytes); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorData.java new file mode 100644 index 00000000..6dfb4e6c --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorData.java @@ -0,0 +1,44 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * The base class for all vendor data. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public interface OFVendorData { + /** + * @return length of the data + */ + public int getLength(); + + /** + * Read the vendor data from the specified ChannelBuffer + * @param data + */ + public void readFrom(ChannelBuffer data, int length); + + /** + * Write the vendor data to the specified ChannelBuffer + * @param data + */ + public void writeTo(ChannelBuffer data); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java new file mode 100644 index 00000000..ecae4823 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java @@ -0,0 +1,79 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.vendor; + +import org.openflow.protocol.Instantiable; + +/** + * Class that represents a specific vendor data type format in an + * OFVendor message. Typically the vendor data will begin with an integer + * code that determines the format of the rest of the data, but this + * class does not assume that. It's basically just a holder for an + * instantiator of the appropriate subclass of OFVendorData. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFVendorDataType { + + /** + * Object that instantiates the subclass of OFVendorData + * associated with this data type. + */ + protected Instantiable instantiable; + + /** + * Construct an empty vendor data type. + */ + public OFVendorDataType() { + super(); + } + + /** + * Construct a vendor data type with the specified instantiable. + * @param instantiable object that creates the subclass of OFVendorData + * associated with this data type. + */ + public OFVendorDataType(Instantiable instantiable) { + this.instantiable = instantiable; + } + + /** + * Returns a new instance of a subclass of OFVendorData associated with + * this OFVendorDataType. + * + * @return the new object + */ + public OFVendorData newInstance() { + return instantiable.instantiate(); + } + + /** + * @return the instantiable + */ + public Instantiable getInstantiable() { + return instantiable; + } + + /** + * @param instantiable the instantiable to set + */ + public void setInstantiable(Instantiable instantiable) { + this.instantiable = instantiable; + } + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorId.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorId.java new file mode 100644 index 00000000..f0af8a76 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/vendor/OFVendorId.java @@ -0,0 +1,85 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.vendor; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Base class for the vendor ID corresponding to vendor extensions from a + * given vendor. It is responsible for knowing how to parse out some sort of + * data type value from the vendor data in an OFVendor message so that we can + * dispatch to the different subclasses of OFVendorData corresponding to the + * different formats of data for the vendor extensions. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public abstract class OFVendorId { + static Map mapping = new HashMap(); + + /** + * The vendor id value, typically the OUI of the vendor prefixed with 0. + */ + protected int id; + + /** + * Register a new vendor id. + * @param vendorId the vendor id to register + */ + public static void registerVendorId(OFVendorId vendorId) { + mapping.put(vendorId.getId(), vendorId); + } + + /** + * Lookup the OFVendorId instance corresponding to the given id value. + * @param id the integer vendor id value + * @return the corresponding OFVendorId that's been registered for the + * given value, or null if there id has not been registered. + */ + public static OFVendorId lookupVendorId(int id) { + return mapping.get(id); + } + + /** + * Create an OFVendorId with the give vendor id value + * @param id + */ + public OFVendorId(int id) { + this.id = id; + } + + /** + * @return the vendor id value + */ + public int getId() { + return id; + } + + /** + * This function parses enough of the data from the channel buffer to be + * able to determine the appropriate OFVendorDataType for the data. + * + * @param data the channel buffer containing the vendor data. + * @param length the length to the end of the enclosing message + * @return the OFVendorDataType that can be used to instantiate the + * appropriate subclass of OFVendorData. + */ + public abstract OFVendorDataType parseVendorDataType(ChannelBuffer data, int length); +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/HexString.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/HexString.java new file mode 100644 index 00000000..b5628242 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/HexString.java @@ -0,0 +1,93 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.math.BigInteger; + +public class HexString { + /** + * Convert a string of bytes to a ':' separated hex string + * @param bytes + * @return "0f:ca:fe:de:ad:be:ef" + */ + public static String toHexString(byte[] bytes) { + if (bytes == null) return ""; + int i; + String ret = ""; + String tmp; + for(i=0; i< bytes.length; i++) { + if(i> 0) + ret += ":"; + tmp = Integer.toHexString(U8.f(bytes[i])); + if (tmp.length() == 1) + ret += "0"; + ret += tmp; + } + return ret; + } + + public static String toHexString(long val, int padTo) { + char arr[] = Long.toHexString(val).toCharArray(); + String ret = ""; + // prepend the right number of leading zeros + int i = 0; + for (; i < (padTo * 2 - arr.length); i++) { + ret += "0"; + if ((i % 2) != 0) + ret += ":"; + } + for (int j = 0; j < arr.length; j++) { + ret += arr[j]; + if ((((i + j) % 2) != 0) && (j < (arr.length - 1))) + ret += ":"; + } + return ret; + } + + public static String toHexString(long val) { + return toHexString(val, 8); + } + + + /** + * Convert a string of hex values into a string of bytes + * @param values "0f:ca:fe:de:ad:be:ef" + * @return [15, 5 ,2, 5, 17] + * @throws NumberFormatException If the string can not be parsed + */ + public static byte[] fromHexString(String values) throws NumberFormatException { + String[] octets = values.split(":"); + byte[] ret = new byte[octets.length]; + + for(int i = 0; i < octets.length; i++) { + if (octets[i].length() > 2) + throw new NumberFormatException("Invalid octet length"); + ret[i] = Integer.valueOf(octets[i], 16).byteValue(); + } + return ret; + } + + public static long toLong(String values) throws NumberFormatException { + // Long.parseLong() can't handle HexStrings with MSB set. Sigh. + BigInteger bi = new BigInteger(values.replaceAll(":", ""),16); + if (bi.bitLength() > 64) + throw new NumberFormatException("Input string too big to fit in long: " + values); + return bi.longValue(); + } + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/IProducer.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/IProducer.java new file mode 100644 index 00000000..52ae79a5 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/IProducer.java @@ -0,0 +1,9 @@ +package org.openflow.util; + +public interface IProducer { + + public void registerConsumer(Class iface, Object anObj); + + public void deregisterConsumer(Class iface, Object anObj); + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/LRULinkedHashMap.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/LRULinkedHashMap.java new file mode 100644 index 00000000..7f05381c --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/LRULinkedHashMap.java @@ -0,0 +1,42 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.util.LinkedHashMap; + +public class LRULinkedHashMap extends LinkedHashMap { + private static final long serialVersionUID = -2964986094089626647L; + protected int maximumCapacity; + + public LRULinkedHashMap(int initialCapacity, int maximumCapacity) { + super(initialCapacity, 0.75f, true); + this.maximumCapacity = maximumCapacity; + } + + public LRULinkedHashMap(int maximumCapacity) { + super(16, 0.75f, true); + this.maximumCapacity = maximumCapacity; + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + if (this.size() > maximumCapacity) + return true; + return false; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/ProducerConsumer.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/ProducerConsumer.java new file mode 100644 index 00000000..f2244ef3 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/ProducerConsumer.java @@ -0,0 +1,223 @@ +package org.openflow.util; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +/** + * The following implement a producer/consumer design pattern in which both + * producers and consumers explicitly employ a centralized registration + * mechanism, and java Interfaces are used as contracts.
      + */ +public class ProducerConsumer { + + /* + * Class variables + */ + protected static ProducerConsumer singleton; + + /* + * Default constructor + */ + protected ProducerConsumer() { + producerMap = new Hashtable, Set>(); + } + + /* + * Instance variables + */ + + // Interface/IProducer map + protected Map, Set> producerMap; + + /* + * Protected methods + */ + + protected void _registerConsumer(Object consumer, Class[] interfaces, + Set> iSet, + Set> iUniqueSet) { + // *...Process all interfaces...*/ + for (Class iface : interfaces) { + + // *...Protect against repeated interfaces...*/ + if (!iUniqueSet.contains(iface)) { + iUniqueSet.add(iface); + + Set producers = producerMap.get(iface); + + if (producers != null) { + for (IProducer producer : producers) + producer.registerConsumer(iface, consumer); + iSet.add(iface); + } + + // *...Recurse...*/ + _registerConsumer(consumer, iface.getInterfaces(), iSet, + iUniqueSet); + } + } + } + + protected void _registerConsumer(Object consumer, Class clazz, + Set> iSet, + Set> iUniqueSet) { + if (clazz != null) { + // *...Process all interfaces...*/ + _registerConsumer(consumer, clazz.getInterfaces(), iSet, + iUniqueSet); + + // *...Recurse the class hierarchy...*/ + _registerConsumer(consumer, clazz.getSuperclass(), iSet, + iUniqueSet); + } + } + + protected int _deregisterConsumer(Object consumer, + Class[] interfaces, + Set> iUniqueSet) { + int count = 0; + + // *...Process all interfaces...*/ + for (Class iface : interfaces) { + + // *...Protect against repeated interfaces...*/ + if (!iUniqueSet.contains(iface)) { + iUniqueSet.add(iface); + + Set producers = producerMap.get(iface); + + if (producers != null) { + for (IProducer producer : producers) + producer.deregisterConsumer(iface, consumer); + + count++; + } + + // *...Recurse...*/ + count += _deregisterConsumer(consumer, + iface.getInterfaces(), + iUniqueSet); + } + } + + return count; + } + + protected int _deregisterConsumer(Object consumer, Class clazz, + Set> iUniqueSet) { + int count = 0; + + if (clazz != null) { + // *...Process all interfaces...*/ + count += _deregisterConsumer(consumer, clazz.getInterfaces(), + iUniqueSet); + + // *...Recurse the class hierarchy...*/ + count += _deregisterConsumer(consumer, clazz.getSuperclass(), + iUniqueSet); + } + + return count; + } + + /* + * Singleton API + */ + + /** + * @return singleton ProducerConsumer + */ + public static synchronized ProducerConsumer getSingleton() { + if (singleton == null) singleton = new ProducerConsumer(); + + return singleton; + } + + /* + * Producer APIs + */ + + /** + * Producer registration + * + * @param producer + * object that implements IProducer + * @param iface + * interface supported by the producer + * @return whether there was a previously registered producer, or true if + * one or more the arguments were invalid + */ + public boolean registerProducer(IProducer producer, Class iface) { + if (producer != null && iface != null && iface.isInterface()) { + Set producers = producerMap.get(iface); + + if (producers == null) { + producers = new HashSet(); + producerMap.put(iface, producers); + } + + return producers.add(producer); + } else + return true; + } + + /** + * Producer deregistration + * + * @param producer + * object that implements IProducer + * @param iface + * interface supported by the producer + * @return whether the interface/producer pair was removed, or false if one + * or more the arguments were invalid + */ + public boolean deregisterProducer(IProducer producer, Class iface) { + if (producer != null && iface != null && iface.isInterface()) { + Set producers = producerMap.get(iface); + + if (producers != null) return producers.remove(producer); + } + + return false; + } + + /* + * Consumer APIs + */ + + /** + * Consumer registration + * + * @param consumer + * object that implements producer-specific interfaces + * @return set of supported interfaces + */ + public Set> registerConsumer(Object consumer) { + Set> iSet = new HashSet>(); + + if (consumer != null) + _registerConsumer(consumer, + consumer.getClass(), iSet, + new HashSet>()); + + return iSet; + } + + /** + * Consumer deregistration + * + * @param consumer + * object to deregister + * @return number of unregistered interfaces + */ + public int deregisterConsumer(Object consumer) { + if (consumer != null) + return _deregisterConsumer(consumer, consumer.getClass(), + new HashSet>()); + else + return 0; + } + +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/StringByteSerializer.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/StringByteSerializer.java new file mode 100644 index 00000000..9287fd20 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/StringByteSerializer.java @@ -0,0 +1,58 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class StringByteSerializer { + public static String readFrom(ChannelBuffer data, int length) { + byte[] stringBytes = new byte[length]; + data.readBytes(stringBytes); + // find the first index of 0 + int index = 0; + for (byte b : stringBytes) { + if (0 == b) + break; + ++index; + } + return new String(Arrays.copyOf(stringBytes, index), + Charset.forName("ascii")); + } + + public static void writeTo(ChannelBuffer data, int length, String value) { + try { + byte[] name = value.getBytes("ASCII"); + if (name.length < length) { + data.writeBytes(name); + for (int i = name.length; i < length; ++i) { + data.writeByte((byte) 0); + } + } else { + data.writeBytes(name, 0, length-1); + data.writeByte((byte) 0); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/U16.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/U16.java new file mode 100644 index 00000000..0d8917da --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/U16.java @@ -0,0 +1,28 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +public class U16 { + public static int f(short i) { + return (int)i & 0xffff; + } + + public static short t(int l) { + return (short) l; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/U32.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/U32.java new file mode 100644 index 00000000..3aab400e --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/U32.java @@ -0,0 +1,28 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +public class U32 { + public static long f(int i) { + return (long)i & 0xffffffffL; + } + + public static int t(long l) { + return (int) l; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/U64.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/U64.java new file mode 100644 index 00000000..c6ae0f7b --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/U64.java @@ -0,0 +1,30 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.math.BigInteger; + +public class U64 { + public static BigInteger f(long i) { + return new BigInteger(Long.toBinaryString(i), 2); + } + + public static long t(BigInteger l) { + return l.longValue(); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/U8.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/U8.java new file mode 100644 index 00000000..0b575ad2 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/U8.java @@ -0,0 +1,28 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +public class U8 { + public static short f(byte i) { + return (short) ((short)i & 0xff); + } + + public static byte t(short l) { + return (byte) l; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/util/Unsigned.java b/third-party/openflowj_netty/src/main/java/org/openflow/util/Unsigned.java new file mode 100644 index 00000000..0754a3f7 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/util/Unsigned.java @@ -0,0 +1,212 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.math.BigInteger; +import java.nio.ByteBuffer; + +/***** + * A util library class for dealing with the lack of unsigned datatypes in Java + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * @author David Erickson (daviderickson@cs.stanford.edu) + */ + +public class Unsigned { + /** + * Get an unsigned byte from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the byte from + * @return an unsigned byte contained in a short + */ + public static short getUnsignedByte(ByteBuffer bb) { + return ((short) (bb.get() & (short) 0xff)); + } + + /** + * Get an unsigned byte from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the byte from + * @param offset the offset to get the byte from + * @return an unsigned byte contained in a short + */ + public static short getUnsignedByte(ByteBuffer bb, int offset) { + return ((short) (bb.get(offset) & (short) 0xff)); + } + + /** + * Put an unsigned byte into the specified ByteBuffer at the current + * position + * + * @param bb ByteBuffer to put the byte into + * @param v the short containing the unsigned byte + */ + public static void putUnsignedByte(ByteBuffer bb, short v) { + bb.put((byte) (v & 0xff)); + } + + /** + * Put an unsigned byte into the specified ByteBuffer at the specified + * offset + * + * @param bb ByteBuffer to put the byte into + * @param v the short containing the unsigned byte + * @param offset the offset to insert the unsigned byte at + */ + public static void putUnsignedByte(ByteBuffer bb, short v, int offset) { + bb.put(offset, (byte) (v & 0xff)); + } + + /** + * Get an unsigned short from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the byte from + * @return an unsigned short contained in a int + */ + public static int getUnsignedShort(ByteBuffer bb) { + return (bb.getShort() & 0xffff); + } + + /** + * Get an unsigned short from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the short from + * @param offset the offset to get the short from + * @return an unsigned short contained in a int + */ + public static int getUnsignedShort(ByteBuffer bb, int offset) { + return (bb.getShort(offset) & 0xffff); + } + + /** + * Put an unsigned short into the specified ByteBuffer at the current + * position + * + * @param bb ByteBuffer to put the short into + * @param v the int containing the unsigned short + */ + public static void putUnsignedShort(ByteBuffer bb, int v) { + bb.putShort((short) (v & 0xffff)); + } + + /** + * Put an unsigned short into the specified ByteBuffer at the specified + * offset + * + * @param bb ByteBuffer to put the short into + * @param v the int containing the unsigned short + * @param offset the offset to insert the unsigned short at + */ + public static void putUnsignedShort(ByteBuffer bb, int v, int offset) { + bb.putShort(offset, (short) (v & 0xffff)); + } + + /** + * Get an unsigned int from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the int from + * @return an unsigned int contained in a long + */ + public static long getUnsignedInt(ByteBuffer bb) { + return ((long) bb.getInt() & 0xffffffffL); + } + + /** + * Get an unsigned int from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the int from + * @param offset the offset to get the int from + * @return an unsigned int contained in a long + */ + public static long getUnsignedInt(ByteBuffer bb, int offset) { + return ((long) bb.getInt(offset) & 0xffffffffL); + } + + /** + * Put an unsigned int into the specified ByteBuffer at the current position + * + * @param bb ByteBuffer to put the int into + * @param v the long containing the unsigned int + */ + public static void putUnsignedInt(ByteBuffer bb, long v) { + bb.putInt((int) (v & 0xffffffffL)); + } + + /** + * Put an unsigned int into the specified ByteBuffer at the specified offset + * + * @param bb ByteBuffer to put the int into + * @param v the long containing the unsigned int + * @param offset the offset to insert the unsigned int at + */ + public static void putUnsignedInt(ByteBuffer bb, long v, int offset) { + bb.putInt(offset, (int) (v & 0xffffffffL)); + } + + /** + * Get an unsigned long from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the long from + * @return an unsigned long contained in a BigInteger + */ + public static BigInteger getUnsignedLong(ByteBuffer bb) { + byte[] v = new byte[8]; + for (int i = 0; i < 8; ++i) { + v[i] = bb.get(i); + } + return new BigInteger(1, v); + } + + /** + * Get an unsigned long from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the long from + * @param offset the offset to get the long from + * @return an unsigned long contained in a BigInteger + */ + public static BigInteger getUnsignedLong(ByteBuffer bb, int offset) { + byte[] v = new byte[8]; + for (int i = 0; i < 8; ++i) { + v[i] = bb.get(offset+i); + } + return new BigInteger(1, v); + } + + /** + * Put an unsigned long into the specified ByteBuffer at the current + * position + * + * @param bb ByteBuffer to put the long into + * @param v the BigInteger containing the unsigned long + */ + public static void putUnsignedLong(ByteBuffer bb, BigInteger v) { + bb.putLong(v.longValue()); + } + + /** + * Put an unsigned long into the specified ByteBuffer at the specified + * offset + * + * @param bb ByteBuffer to put the long into + * @param v the BigInteger containing the unsigned long + * @param offset the offset to insert the unsigned long at + */ + public static void putUnsignedLong(ByteBuffer bb, BigInteger v, int offset) { + bb.putLong(offset, v.longValue()); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java new file mode 100644 index 00000000..687d5449 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java @@ -0,0 +1,98 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.nicira; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Base class for vendor data corresponding to a Nicira vendor extension. + * Nicira vendor data always starts with a 4-byte integer data type value. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFNiciraVendorData implements OFVendorData { + + public static final int NX_VENDOR_ID = 0x00002320; + /** + * The value of the integer data type at the beginning of the vendor data + */ + protected int dataType; + + /** + * Construct empty (i.e. unspecified data type) Nicira vendor data. + */ + public OFNiciraVendorData() { + } + + /** + * Contruct Nicira vendor data with the specified data type + * @param dataType the data type value at the beginning of the vendor data. + */ + public OFNiciraVendorData(int dataType) { + this.dataType = dataType; + } + + /** + * Get the data type value at the beginning of the vendor data + * @return the integer data type value + */ + public int getDataType() { + return dataType; + } + + /** + * Set the data type value + * @param dataType the integer data type value at the beginning of the + * vendor data. + */ + public void setDataType(int dataType) { + this.dataType = dataType; + } + + /** + * Get the length of the vendor data. This implementation will normally + * be the superclass for another class that will override this to return + * the overall vendor data length. This implementation just returns the + * length of the part that includes the 4-byte integer data type value + * at the beginning of the vendor data. + */ + @Override + public int getLength() { + return 4; + } + + /** + * Read the vendor data from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + dataType = data.readInt(); + } + + /** + * Write the vendor data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + data.writeInt(dataType); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java new file mode 100644 index 00000000..98f88b24 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java @@ -0,0 +1,30 @@ +package org.openflow.vendor.nicira; + +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorId; + +public class OFNiciraVendorExtensions { + private static boolean initialized = false; + + public static synchronized void initialize() { + if (initialized) + return; + + // Configure openflowj to be able to parse the role request/reply + // vendor messages. + OFBasicVendorId niciraVendorId = + new OFBasicVendorId(OFNiciraVendorData.NX_VENDOR_ID, 4); + OFVendorId.registerVendorId(niciraVendorId); + OFBasicVendorDataType roleRequestVendorData = + new OFBasicVendorDataType(OFRoleRequestVendorData.NXT_ROLE_REQUEST, + OFRoleRequestVendorData.getInstantiable()); + niciraVendorId.registerVendorDataType(roleRequestVendorData); + OFBasicVendorDataType roleReplyVendorData = + new OFBasicVendorDataType(OFRoleReplyVendorData.NXT_ROLE_REPLY, + OFRoleReplyVendorData.getInstantiable()); + niciraVendorId.registerVendorDataType(roleReplyVendorData); + + initialized = true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java new file mode 100644 index 00000000..fa28c71f --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java @@ -0,0 +1,66 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.nicira; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Subclass of OFVendorData representing the vendor data associated with + * a role reply vendor extension. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFRoleReplyVendorData extends OFRoleVendorData { + + protected static Instantiable instantiable = + new Instantiable() { + public OFVendorData instantiate() { + return new OFRoleReplyVendorData(); + } + }; + + /** + * @return a subclass of Instantiable that instantiates + * an instance of OFRoleReplyVendorData. + */ + public static Instantiable getInstantiable() { + return instantiable; + } + + /** + * The data type value for a role reply + */ + public static final int NXT_ROLE_REPLY = 11; + + /** + * Construct a role reply vendor data with an unspecified role value. + */ + public OFRoleReplyVendorData() { + super(NXT_ROLE_REPLY); + } + + /** + * Construct a role reply vendor data with the specified role value. + * @param role the role value for the role reply. Should be one of + * NX_ROLE_OTHER, NX_ROLE_MASTER or NX_ROLE_SLAVE. + */ + public OFRoleReplyVendorData(int role) { + super(NXT_ROLE_REPLY, role); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java new file mode 100644 index 00000000..e7dbe71b --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java @@ -0,0 +1,66 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.nicira; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Subclass of OFVendorData representing the vendor data associated with + * a role request vendor extension. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFRoleRequestVendorData extends OFRoleVendorData { + + protected static Instantiable instantiable = + new Instantiable() { + public OFVendorData instantiate() { + return new OFRoleRequestVendorData(); + } + }; + + /** + * @return a subclass of Instantiable that instantiates + * an instance of OFRoleRequestVendorData. + */ + public static Instantiable getInstantiable() { + return instantiable; + } + + /** + * The data type value for a role request + */ + public static final int NXT_ROLE_REQUEST = 10; + + /** + * Construct a role request vendor data with an unspecified role value. + */ + public OFRoleRequestVendorData() { + super(NXT_ROLE_REQUEST); + } + + /** + * Construct a role request vendor data with the specified role value. + * @param role the role value for the role request. Should be one of + * NX_ROLE_OTHER, NX_ROLE_MASTER or NX_ROLE_SLAVE. + */ + public OFRoleRequestVendorData(int role) { + super(NXT_ROLE_REQUEST, role); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java new file mode 100644 index 00000000..e7c8bf21 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java @@ -0,0 +1,113 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.nicira; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Class that represents the vendor data in the role request + * extension implemented by Open vSwitch to support high availability. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFRoleVendorData extends OFNiciraVendorData { + + /** + * Role value indicating that the controller is in the OTHER role. + */ + public static final int NX_ROLE_OTHER = 0; + + /** + * Role value indicating that the controller is in the MASTER role. + */ + public static final int NX_ROLE_MASTER = 1; + + /** + * Role value indicating that the controller is in the SLAVE role. + */ + public static final int NX_ROLE_SLAVE = 2; + + protected int role; + + /** + * Construct an uninitialized OFRoleVendorData + */ + public OFRoleVendorData() { + super(); + } + + /** + * Construct an OFRoleVendorData with the specified data type + * (i.e. either request or reply) and an unspecified role. + * @param dataType + */ + public OFRoleVendorData(int dataType) { + super(dataType); + } + + /** + * Construct an OFRoleVendorData with the specified data type + * (i.e. either request or reply) and role (i.e. one of of + * master, slave, or other). + * @param dataType either role request or role reply data type + */ + public OFRoleVendorData(int dataType, int role) { + super(dataType); + this.role = role; + } + /** + * @return the role value of the role vendor data + */ + public int getRole() { + return role; + } + + /** + * @param role the role value of the role vendor data + */ + public void setRole(int role) { + this.role = role; + } + + /** + * @return the total length of the role vendor data + */ + @Override + public int getLength() { + return super.getLength() + 4; + } + + /** + * Read the role vendor data from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + role = data.readInt(); + } + + /** + * Write the role vendor data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(role); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java new file mode 100644 index 00000000..9be821f2 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java @@ -0,0 +1,98 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.openflow; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Base class for vendor data corresponding to extensions to OpenFlow 1.0. + * Based on org.openflow.vendor.nicira + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFOpenFlowVendorData implements OFVendorData { + + public static final int OF_VENDOR_ID = 0x000026e1; + + /** + * The value of the integer data type at the beginning of the vendor data + */ + protected int dataType; + + /** + * Construct empty (i.e. unspecified data type) OpenFlow vendor data. + */ + public OFOpenFlowVendorData() { + } + + /** + * Construct OpenFlow vendor data with the specified data type + * @param dataType the data type value at the beginning of the vendor data. + */ + public OFOpenFlowVendorData(int dataType) { + this.dataType = dataType; + } + + /** + * Get the data type value at the beginning of the vendor data + * @return the integer data type value + */ + public int getDataType() { + return dataType; + } + + /** + * Set the data type value + * @param dataType the integer data type value at the beginning of the + * vendor data. + */ + public void setDataType(int dataType) { + this.dataType = dataType; + } + + /** + * Get the length of the vendor data. This implementation will normally + * be the superclass for another class that will override this to return + * the overall vendor data length. This implementation just returns the + * length of the part that includes the 4-byte integer data type value + * at the beginning of the vendor data. + */ + @Override + public int getLength() { + return 4; + } + + /** + * Read the vendor data from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + dataType = data.readInt(); + } + + /** + * Write the vendor data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + data.writeInt(dataType); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java new file mode 100644 index 00000000..3fa10298 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java @@ -0,0 +1,47 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.openflow; + +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorId; + +public class OFOpenFlowVendorExtensions { + private static boolean initialized = false; + + public static synchronized void initialize() { + if (initialized) + return; + + // Configure openflowj to be able to parse the OpenFlow extensions. + OFBasicVendorId openflowVendorId = + new OFBasicVendorId(OFOpenFlowVendorData.OF_VENDOR_ID, 4); + OFVendorId.registerVendorId(openflowVendorId); + + OFBasicVendorDataType queueModifyVendorData = + new OFBasicVendorDataType(OFQueueModifyVendorData.OFP_EXT_QUEUE_MODIFY, + OFQueueModifyVendorData.getInstantiable()); + openflowVendorId.registerVendorDataType(queueModifyVendorData); + + OFBasicVendorDataType queueDeleteVendorData = + new OFBasicVendorDataType(OFQueueDeleteVendorData.OFP_EXT_QUEUE_DELETE, + OFQueueModifyVendorData.getInstantiable()); + openflowVendorId.registerVendorDataType(queueDeleteVendorData); + + initialized = true; + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java new file mode 100644 index 00000000..4fc52bac --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java @@ -0,0 +1,52 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.openflow; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Class that represents the vendor data in the queue delete request + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueDeleteVendorData extends OFQueueVendorData { + + protected static Instantiable instantiable = + new Instantiable() { + public OFVendorData instantiate() { + return new OFQueueDeleteVendorData(); + } + }; + + /** + * @return a subclass of Instantiable that instantiates + * an instance of OFQueueDeleteVendorData. + */ + public static Instantiable getInstantiable() { + return instantiable; + } + + /** + * The data type value for a queue delete request + */ + public static final int OFP_EXT_QUEUE_DELETE = 1; + + public OFQueueDeleteVendorData() { + super(OFP_EXT_QUEUE_DELETE); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java new file mode 100644 index 00000000..0d2f31b5 --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java @@ -0,0 +1,52 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.openflow; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Class that represents the vendor data in the queue modify request + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueModifyVendorData extends OFQueueVendorData { + + protected static Instantiable instantiable = + new Instantiable() { + public OFVendorData instantiate() { + return new OFQueueModifyVendorData(); + } + }; + + /** + * @return a subclass of Instantiable that instantiates + * an instance of OFQueueModifyVendorData. + */ + public static Instantiable getInstantiable() { + return instantiable; + } + + /** + * The data type value for a queue modify request + */ + public static final int OFP_EXT_QUEUE_MODIFY = 0; + + public OFQueueModifyVendorData() { + super(OFP_EXT_QUEUE_MODIFY); + } +} diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java new file mode 100644 index 00000000..eeae9aac --- /dev/null +++ b/third-party/openflowj_netty/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java @@ -0,0 +1,119 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.vendor.openflow; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFPacketQueue; + +/** + * Class that represents the vendor data in a queue modify or delete request + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueVendorData extends OFOpenFlowVendorData { + public static int MINIMUM_LENGTH = 8; + + protected short portNumber; + protected List queues = new ArrayList(); + + public OFQueueVendorData(int dataType) { + super(dataType); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param port the port on which the queue is + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + + /** + * @return the queues + */ + public List getQueues() { + return queues; + } + + /** + * @param queues the queues to modify or delete + */ + public void setQueues(List queues) { + this.queues = queues; + } + + /** + * @return the total length of the queue modify or delete msg + */ + @Override + public int getLength() { + int queuesLength = 0; + + for (OFPacketQueue queue : queues) { + queuesLength += queue.getLength(); + } + + return super.getLength() + MINIMUM_LENGTH + queuesLength; + } + + /** + * Read the queue message data from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + portNumber = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + + int availLength = (length - MINIMUM_LENGTH); + this.queues.clear(); + + while (availLength > 0) { + OFPacketQueue queue = new OFPacketQueue(); + queue.readFrom(data); + queues.add(queue); + availLength -= queue.getLength(); + } + } + + /** + * Write the queue message data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeInt(0); // pad + data.writeShort(0); // pad + + for (OFPacketQueue queue : queues) { + queue.writeTo(data); + } + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/BasicFactoryTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/BasicFactoryTest.java new file mode 100644 index 00000000..312fcd3f --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/BasicFactoryTest.java @@ -0,0 +1,134 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import static org.junit.Assert.assertArrayEquals; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.action.MockVendorAction; +import org.openflow.protocol.action.MockVendorActionFactory; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionVendorGeneric; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFVendorActionRegistry; +import org.openflow.util.U16; + +public class BasicFactoryTest extends TestCase { + + public void testCreateAndParse() throws MessageParseException { + BasicFactory factory = new BasicFactory(); + OFMessage m = factory.getMessage(OFType.HELLO); + m.setVersion((byte) 1); + m.setType(OFType.ECHO_REQUEST); + m.setLength(U16.t(8)); + m.setXid(0xdeadbeef); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + ChannelBuffer bb2 = ChannelBuffers.dynamicBuffer(); + m.writeTo(bb); + bb2.writeBytes(bb, bb.readableBytes()-1); + TestCase.assertNull(factory.parseMessage(bb2)); + bb2.writeByte(bb.readByte()); + List message = factory.parseMessage(bb2); + TestCase.assertNotNull(message); + TestCase.assertEquals(message.size(), 1); + TestCase.assertTrue(message.get(0).getType() == OFType.ECHO_REQUEST); + } + + public void testInvalidMsgParse() throws MessageParseException { + BasicFactory factory = new BasicFactory(); + OFMessage m = factory.getMessage(OFType.HELLO); + m.setVersion((byte) 1); + m.setType(OFType.ECHO_REQUEST); + m.setLength(U16.t(16)); + m.setXid(0xdeadbeef); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + m.writeTo(bb); + List message = factory.parseMessage(bb); + TestCase.assertNull(message); + } + + public void testCurrouptedMsgParse() throws MessageParseException { + BasicFactory factory = new BasicFactory(); + OFMessage m = factory.getMessage(OFType.HELLO); + m.setVersion((byte) 1); + m.setType(OFType.ERROR); + m.setLength(U16.t(8)); + m.setXid(0xdeadbeef); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + m.writeTo(bb); + try { + factory.parseMessage(bb); + } + catch(Exception e) { + TestCase.assertEquals(MessageParseException.class, e.getClass()); + } + } + + public void testCustomVendorAction() throws MessageParseException { + BasicFactory factory = new BasicFactory(); + OFVendorActionRegistry.getInstance().register( + MockVendorAction.VENDOR_ID, new MockVendorActionFactory()); + + + byte[] deadBeefMessage = { + (byte) 0xff, (byte) 0xff, // action vendor + 0x00, 0x10, // length + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte)0xef, // deadbeaf + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 // pad + }; + + ChannelBuffer buf = ChannelBuffers.copiedBuffer(deadBeefMessage); + + List actions = factory.parseActions(buf,deadBeefMessage.length); + assertEquals(1, actions.size()); + OFAction ofAction = actions.get(0); + assertTrue("Action should be MockVendorAction, but is "+ofAction.getClass(), ofAction instanceof MockVendorAction); + assertArrayEquals( new byte[] { 1,2,3,4,5,6,7,8}, ((MockVendorAction)ofAction).getMockData()); + + + } + + public void testGenericVendorAction() throws MessageParseException { + byte[] nonDeadBeefMessage = { + (byte) 0xff, (byte) 0xff, // action vendor + 0x00, 0x10, // length + (byte) 0x7e, (byte) 0xe7, (byte) 0xbe, (byte)0xef, // deadbeaf + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 // pad + }; + + BasicFactory factory = new BasicFactory(); + OFVendorActionRegistry.getInstance().register( + MockVendorAction.VENDOR_ID, new MockVendorActionFactory()); + + ChannelBuffer buf = ChannelBuffers.copiedBuffer(nonDeadBeefMessage); + + List actions = factory.parseActions(buf,nonDeadBeefMessage.length); + assertEquals(1, actions.size()); + OFAction ofAction = actions.get(0); + assertTrue("Action should be OFActionVendorGeneric, but is "+ofAction.getClass(), ofAction instanceof OFActionVendorGeneric); + } + +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFActionTypeTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFActionTypeTest.java new file mode 100644 index 00000000..ed8386cd --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFActionTypeTest.java @@ -0,0 +1,37 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import org.junit.Test; +import org.openflow.protocol.action.OFActionType; + +import junit.framework.TestCase; + + +public class OFActionTypeTest extends TestCase { + @Test + public void testMapping() throws Exception { + TestCase.assertEquals(OFActionType.OUTPUT, + OFActionType.valueOf((short) 0)); + TestCase.assertEquals(OFActionType.OPAQUE_ENQUEUE, + OFActionType.valueOf((short) 11)); + TestCase.assertEquals(OFActionType.VENDOR, + OFActionType.valueOf((short) 0xffff)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java new file mode 100644 index 00000000..7e447bb0 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFBarrierReplyTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFBarrierReply msg = (OFBarrierReply) messageFactory + .getMessage(OFType.BARRIER_REPLY); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.BARRIER_REPLY, msg.getType()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java new file mode 100644 index 00000000..3aa9cb4a --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFBarrierRequestTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFBarrierRequest msg = (OFBarrierRequest) messageFactory + .getMessage(OFType.BARRIER_REQUEST); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.BARRIER_REQUEST, msg.getType()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFErrorTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFErrorTest.java new file mode 100644 index 00000000..45d52576 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFErrorTest.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.OFError.OFErrorType; +import org.openflow.protocol.OFError.OFHelloFailedCode; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.util.OFTestCase; + +public class OFErrorTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFError msg = (OFError) messageFactory.getMessage(OFType.ERROR); + msg.setMessageFactory(messageFactory); + msg.setErrorType((short) OFErrorType.OFPET_HELLO_FAILED.getValue()); + msg.setErrorCode((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE + .ordinal()); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals((short) OFErrorType.OFPET_HELLO_FAILED.getValue(), + msg.getErrorType()); + TestCase.assertEquals((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE + .ordinal(), msg.getErrorType()); + TestCase.assertNull(msg.getOffendingMsg()); + + msg.setOffendingMsg(new OFHello()); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals((short) OFErrorType.OFPET_HELLO_FAILED.getValue(), + msg.getErrorType()); + TestCase.assertEquals((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE + .ordinal(), msg.getErrorType()); + TestCase.assertNotNull(msg.getOffendingMsg()); + TestCase.assertEquals(OFHello.MINIMUM_LENGTH, + msg.getOffendingMsg().length); + } + + public void testGarbageAtEnd() throws MessageParseException { + // This is a OFError msg (12 bytes), that encaps a OFVendor msg (24 + // bytes) + // AND some zeros at the end (40 bytes) for a total of 76 bytes + // THIS is what an NEC sends in reply to Nox's VENDOR request + byte[] oferrorRaw = { 0x01, 0x01, 0x00, 0x4c, 0x00, 0x00, 0x10, + (byte) 0xcc, 0x00, 0x01, 0x00, 0x01, 0x01, 0x04, 0x00, 0x18, + 0x00, 0x00, 0x10, (byte) 0xcc, 0x00, 0x00, 0x23, 0x20, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 }; + OFMessageFactory factory = new BasicFactory(); + ChannelBuffer oferrorBuf = + ChannelBuffers.wrappedBuffer(oferrorRaw); + List msg = factory.parseMessage(oferrorBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertEquals(76, msg.get(0).getLengthU()); + ChannelBuffer out = ChannelBuffers.dynamicBuffer(); + msg.get(0).writeTo(out); + TestCase.assertEquals(76, out.readableBytes()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java new file mode 100644 index 00000000..62e491a9 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java @@ -0,0 +1,62 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + + +public class OFFeaturesReplyTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFFeaturesReply ofr = (OFFeaturesReply) messageFactory + .getMessage(OFType.FEATURES_REPLY); + List ports = new ArrayList(); + OFPhysicalPort port = new OFPhysicalPort(); + port.setHardwareAddress(new byte[6]); + port.setName("eth0"); + ports.add(port); + ofr.setPorts(ports); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + ofr.writeTo(bb); + ofr.readFrom(bb); + TestCase.assertEquals(1, ofr.getPorts().size()); + TestCase.assertEquals("eth0", ofr.getPorts().get(0).getName()); + + // test a 15 character name + ofr.getPorts().get(0).setName("012345678901234"); + bb.clear(); + ofr.writeTo(bb); + ofr.readFrom(bb); + TestCase.assertEquals("012345678901234", ofr.getPorts().get(0).getName()); + + // test a 16 character name getting truncated + ofr.getPorts().get(0).setName("0123456789012345"); + bb.clear(); + ofr.writeTo(bb); + ofr.readFrom(bb); + TestCase.assertEquals("012345678901234", ofr.getPorts().get(0).getName()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java new file mode 100644 index 00000000..11746e72 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java @@ -0,0 +1,43 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.OFFlowRemoved.OFFlowRemovedReason; +import org.openflow.util.OFTestCase; + +public class OFFlowRemovedTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFFlowRemoved msg = (OFFlowRemoved) messageFactory + .getMessage(OFType.FLOW_REMOVED); + msg.setMatch(new OFMatch()); + byte[] hwAddr = new byte[6]; + msg.getMatch().setDataLayerDestination(hwAddr); + msg.getMatch().setDataLayerSource(hwAddr); + msg.setReason(OFFlowRemovedReason.OFPRR_DELETE); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.FLOW_REMOVED, msg.getType()); + TestCase.assertEquals(OFFlowRemovedReason.OFPRR_DELETE, msg.getReason()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java new file mode 100644 index 00000000..c1f1f671 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java @@ -0,0 +1,38 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFGetConfigReplyTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFSetConfig msg = (OFSetConfig) messageFactory + .getMessage(OFType.SET_CONFIG); + msg.setFlags((short) 1); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.SET_CONFIG, msg.getType()); + TestCase.assertEquals((short)1, msg.getFlags()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java new file mode 100644 index 00000000..94d9036e --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFGetConfigRequestTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFGetConfigRequest msg = (OFGetConfigRequest) messageFactory + .getMessage(OFType.GET_CONFIG_REQUEST); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.GET_CONFIG_REQUEST, msg.getType()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMatchTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMatchTest.java new file mode 100644 index 00000000..fd7863ac --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMatchTest.java @@ -0,0 +1,92 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +public class OFMatchTest extends TestCase { + public void testFromString() { + OFMatch correct = new OFMatch(); + OFMatch tester = new OFMatch(); + + // Various combinations of "all"/"any" + tester.fromString("OFMatch[]"); + // correct is already wildcarded + TestCase.assertEquals(correct, tester); + tester.fromString("all"); + TestCase.assertEquals(correct, tester); + tester.fromString("ANY"); + TestCase.assertEquals(correct, tester); + tester.fromString(""); + TestCase.assertEquals(correct, tester); + tester.fromString("[]"); + TestCase.assertEquals(correct, tester); + + // ip_src + correct.setWildcards(~OFMatch.OFPFW_NW_SRC_MASK); + correct.setNetworkSource(0x01010203); + tester.fromString("nw_src=1.1.2.3"); + TestCase.assertEquals(correct.getNetworkSourceMaskLen(), tester + .getNetworkSourceMaskLen()); + TestCase.assertEquals(correct, tester); + tester.fromString("IP_sRc=1.1.2.3"); + TestCase.assertEquals(correct.getNetworkSourceMaskLen(), tester + .getNetworkSourceMaskLen()); + TestCase.assertEquals(correct, tester); + + // 0xVlan + correct = new OFMatch(); + correct.setDataLayerVirtualLan((short)65535); + correct.setWildcards(~OFMatch.OFPFW_DL_VLAN); + tester = new OFMatch(); + tester.fromString("dl_vlan=0xffff"); + TestCase.assertEquals(correct, tester); + } + + public void testToString() { + OFMatch match = new OFMatch(); + match.fromString("nw_dst=3.4.5.6/8"); + TestCase.assertEquals(8, match.getNetworkDestinationMaskLen()); + String correct = "OFMatch[nw_dst=3.0.0.0/8]"; + String tester = match.toString(); + + TestCase.assertEquals(correct, tester); + tester = "OFMatch[dl_type=35020]"; + correct = "OFMatch[dl_type=0x88cc]"; + match = new OFMatch(); + match.fromString(tester); + TestCase.assertEquals(correct, match.toString()); + OFMatch match2 = new OFMatch(); + match2.fromString(correct); + TestCase.assertEquals(match, match2); + } + + public void testClone() { + OFMatch match1 = new OFMatch(); + OFMatch match2 = match1.clone(); + TestCase.assertEquals(match1, match2); + match2.setNetworkProtocol((byte) 4); + match2.setWildcards(match2.getWildcards() & ~OFMatch.OFPFW_NW_PROTO); + TestCase.assertNotSame(match1, match2); + } + + public void testIpToString() { + String test = OFMatch.ipToString(-1); + TestCase.assertEquals("255.255.255.255", test); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java new file mode 100644 index 00000000..60a9e732 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +public class OFMessageContextStoreTest extends TestCase { + public void testStoreAndGet() { + OFMessage msg = new OFMessage(); + OFMessageContextStore store = new OFMessageContextStore(msg, this.getName()); + String key = "mykey"; + String value = "myvalue"; + store.put(key, value); + TestCase.assertEquals(value, store.get(key)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPacketOutTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPacketOutTest.java new file mode 100644 index 00000000..55b54552 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPacketOutTest.java @@ -0,0 +1,45 @@ +package org.openflow.protocol; + +import org.junit.Test; + +public class OFPacketOutTest { + + @Test(expected = IllegalArgumentException.class) + public void testBothBufferIdAndPayloadSet() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(12); + packetOut.setPacketData(new byte[] { 1, 2, 3 }); + } + + @Test + public void testOnlyBufferIdSet() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(12); + packetOut.setPacketData(null); + packetOut.setPacketData(new byte[] {}); + packetOut.validate(); + } + + @Test(expected = IllegalStateException.class) + public void testNeitherBufferIdNorPayloadSet() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE); + packetOut.setPacketData(null); + packetOut.validate(); + } + + @Test(expected = IllegalStateException.class) + public void testNeitherBufferIdNorPayloadSet2() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE); + packetOut.setPacketData(new byte[] {}); + packetOut.validate(); + } + + @Test(expected = IllegalStateException.class) + public void testNeitherBufferIdNorPayloadSet3() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.validate(); + } + +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortConfigTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortConfigTest.java new file mode 100644 index 00000000..9b115ebc --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortConfigTest.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFPortConfigTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFPortMod msg = (OFPortMod) messageFactory + .getMessage(OFType.PORT_MOD); + msg.setHardwareAddress(new byte[6]); + msg.portNumber = 1; + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.PORT_MOD, msg.getType()); + TestCase.assertEquals(1, msg.getPortNumber()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortStatusTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortStatusTest.java new file mode 100644 index 00000000..4fab64e0 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFPortStatusTest.java @@ -0,0 +1,44 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.OFPortStatus.OFPortReason; +import org.openflow.util.OFTestCase; + +public class OFPortStatusTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFPortStatus msg = (OFPortStatus) messageFactory + .getMessage(OFType.PORT_STATUS); + msg.setDesc(new OFPhysicalPort()); + msg.getDesc().setHardwareAddress(new byte[6]); + msg.getDesc().setName("eth0"); + msg.setReason((byte) OFPortReason.OFPPR_ADD.ordinal()); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.PORT_STATUS, msg.getType()); + TestCase.assertEquals((byte) OFPortReason.OFPPR_ADD.ordinal(), msg + .getReason()); + TestCase.assertNotNull(msg.getDesc()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFSetConfigTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFSetConfigTest.java new file mode 100644 index 00000000..2a9e86f7 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFSetConfigTest.java @@ -0,0 +1,38 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFSetConfigTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFGetConfigReply msg = (OFGetConfigReply) messageFactory + .getMessage(OFType.GET_CONFIG_REPLY); + msg.setFlags((short) 1); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.GET_CONFIG_REPLY, msg.getType()); + TestCase.assertEquals((short)1, msg.getFlags()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java new file mode 100644 index 00000000..50bac8f7 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java @@ -0,0 +1,77 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.util.OFTestCase; + +public class OFStatisticsReplyTest extends OFTestCase { + public void testOFFlowStatisticsReply() throws Exception { + byte[] packet = new byte[] { 0x01, 0x11, 0x01, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, (byte) 0xff, + (byte) 0xff, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, (byte) 0xa6, + (byte) 0xa6, 0x00, (byte) 0xff, (byte) 0xff, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + (byte) 0xc4, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x08, 0x06, + 0x00, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x2f, (byte) 0xfa, 0x40, (byte) 0xff, (byte) 0xff, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, (byte) 0xff, (byte) 0xff, 0x00, 0x62, 0x08, + 0x00, 0x00, 0x01, 0x62, 0x37, 0x0a, 0x00, 0x00, 0x02, 0x0a, + 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, (byte) 0xc5, 0x2a, (byte) 0x80, (byte) 0xff, + (byte) 0xff, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xc4, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x00 }; + + OFMessageFactory factory = new BasicFactory(); + ChannelBuffer packetBuf = ChannelBuffers.wrappedBuffer(packet); + List msg = factory.parseMessage(packetBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertTrue(msg.get(0) instanceof OFStatisticsReply); + OFStatisticsReply sr = (OFStatisticsReply) msg.get(0); + TestCase.assertEquals(OFStatisticsType.FLOW, sr.getStatisticType()); + TestCase.assertEquals(3, sr.getStatistics().size()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java new file mode 100644 index 00000000..74af6f41 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java @@ -0,0 +1,79 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.protocol.statistics.OFFlowStatisticsRequest; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.protocol.statistics.OFVendorStatistics; +import org.openflow.util.OFTestCase; + +public class OFStatisticsRequestTest extends OFTestCase { + public void testOFFlowStatisticsRequest() throws Exception { + byte[] packet = new byte[] { 0x01, 0x10, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x01, 0x00, 0x00, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + (byte) 0xff, 0x00, (byte) 0xff, (byte) 0xff }; + + OFMessageFactory factory = new BasicFactory(); + ChannelBuffer packetBuf = ChannelBuffers.wrappedBuffer(packet); + List msg = factory.parseMessage(packetBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertTrue(msg.get(0) instanceof OFStatisticsRequest); + OFStatisticsRequest sr = (OFStatisticsRequest) msg.get(0); + TestCase.assertEquals(OFStatisticsType.FLOW, sr.getStatisticType()); + TestCase.assertEquals(1, sr.getStatistics().size()); + TestCase.assertTrue(sr.getStatistics().get(0) instanceof OFFlowStatisticsRequest); + } + + public void testOFStatisticsRequestVendor() throws Exception { + byte[] packet = new byte[] { 0x01, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x63, (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x20, + (byte) 0xe0, 0x00, 0x11, 0x00, 0x0c, 0x29, (byte) 0xc5, + (byte) 0x95, 0x57, 0x02, 0x25, 0x5c, (byte) 0xca, 0x00, 0x02, + (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x50, 0x04, + 0x00, 0x00, 0x00, 0x00, (byte) 0xff, 0x00, 0x00, 0x00, + (byte) 0xff, (byte) 0xff, 0x4e, 0x20 }; + + OFMessageFactory factory = new BasicFactory(); + ChannelBuffer packetBuf = ChannelBuffers.wrappedBuffer(packet); + List msg = factory.parseMessage(packetBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertTrue(msg.get(0) instanceof OFStatisticsRequest); + OFStatisticsRequest sr = (OFStatisticsRequest) msg.get(0); + TestCase.assertEquals(OFStatisticsType.VENDOR, sr.getStatisticType()); + TestCase.assertEquals(1, sr.getStatistics().size()); + TestCase.assertTrue(sr.getStatistics().get(0) instanceof OFVendorStatistics); + TestCase.assertEquals(68, ((OFVendorStatistics)sr.getStatistics().get(0)).getLength()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java new file mode 100644 index 00000000..d44ef7f8 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java @@ -0,0 +1,37 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import junit.framework.TestCase; + +import org.junit.Test; +import org.openflow.protocol.statistics.OFStatisticsType; + + +public class OFStatisticsTypeTest extends TestCase { + @Test + public void testMapping() throws Exception { + TestCase.assertEquals(OFStatisticsType.DESC, + OFStatisticsType.valueOf((short) 0, OFType.STATS_REQUEST)); + TestCase.assertEquals(OFStatisticsType.QUEUE, + OFStatisticsType.valueOf((short) 5, OFType.STATS_REQUEST)); + TestCase.assertEquals(OFStatisticsType.VENDOR, + OFStatisticsType.valueOf((short) 0xffff, OFType.STATS_REQUEST)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFTypeTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFTypeTest.java new file mode 100644 index 00000000..c6bf0a34 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFTypeTest.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + + +import junit.framework.TestCase; + +import org.junit.Test; + + +public class OFTypeTest extends TestCase { + + public void testOFTypeCreate() throws Exception { + OFType foo = OFType.HELLO; + Class c = foo.toClass(); + TestCase.assertEquals(c, OFHello.class); + } + + @Test + public void testMapping() throws Exception { + TestCase.assertEquals(OFType.HELLO, OFType.valueOf((byte) 0)); + TestCase.assertEquals(OFType.BARRIER_REPLY, OFType.valueOf((byte) 19)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFVendorTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFVendorTest.java new file mode 100644 index 00000000..b85a915a --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/OFVendorTest.java @@ -0,0 +1,215 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol; + +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.vendor.OFByteArrayVendorData; +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorData; +import org.openflow.protocol.vendor.OFVendorId; +import org.openflow.util.OFTestCase; + +public class OFVendorTest extends OFTestCase { + + public static int ACME_VENDOR_ID = 0x00112233; + + static class AcmeVendorData implements OFVendorData { + protected int dataType; + + public int getLength() { + return 4; + } + + public void readFrom(ChannelBuffer data, int length) { + dataType = data.readInt(); + } + + public void writeTo(ChannelBuffer data) { + data.writeInt(dataType); + } + } + + static class AcmeVendorData1 extends AcmeVendorData { + public short flags; + public short value; + + public static int DATA_TYPE = 1; + + public AcmeVendorData1() { + } + + public AcmeVendorData1(short flags, short value) { + this.dataType = DATA_TYPE; + this.flags = flags; + this.value = value; + } + + public short getFlags() { + return flags; + } + + public short getValue() { + return value; + } + + public int getLength() { + return 8; + } + + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + flags = data.readShort(); + value = data.readShort(); + + } + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(flags); + data.writeShort(value); + } + + public static Instantiable getInstantiable() { + return new Instantiable() { + public OFVendorData instantiate() { + return new AcmeVendorData1(); + } + }; + } + } + + static class AcmeVendorData2 extends AcmeVendorData { + public int type; + public int subtype; + + public static int DATA_TYPE = 2; + + public AcmeVendorData2() { + } + + public AcmeVendorData2(int type, int subtype) { + this.dataType = DATA_TYPE; + this.type = type; + this.subtype = subtype; + } + + public int getType() { + return type; + } + + public int getSubtype() { + return subtype; + } + + public int getLength() { + return 12; + } + + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + type = data.readShort(); + subtype = data.readShort(); + + } + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(type); + data.writeShort(subtype); + } + + public static Instantiable getInstantiable() { + return new Instantiable() { + public OFVendorData instantiate() { + return new AcmeVendorData2(); + } + }; + } + } + + { + OFBasicVendorId acmeVendorId = new OFBasicVendorId(ACME_VENDOR_ID, 4); + OFVendorId.registerVendorId(acmeVendorId); + OFBasicVendorDataType acmeVendorData1 = new OFBasicVendorDataType( + AcmeVendorData1.DATA_TYPE, AcmeVendorData1.getInstantiable()); + acmeVendorId.registerVendorDataType(acmeVendorData1); + OFBasicVendorDataType acmeVendorData2 = new OFBasicVendorDataType( + AcmeVendorData2.DATA_TYPE, AcmeVendorData2.getInstantiable()); + acmeVendorId.registerVendorDataType(acmeVendorData2); + } + + private OFVendor makeVendorMessage(int vendor) { + OFVendor msg = (OFVendor) messageFactory.getMessage(OFType.VENDOR); + msg.setVendorDataFactory(new BasicFactory()); + msg.setVendor(vendor); + return msg; + } + + public void testWriteRead() throws Exception { + OFVendor msg = makeVendorMessage(1); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(1, msg.getVendor()); + } + + public void testVendorData() throws Exception { + OFVendor msg = makeVendorMessage(ACME_VENDOR_ID); + OFVendorData vendorData = new AcmeVendorData1((short)11, (short)22); + msg.setVendorData(vendorData); + msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength()); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + assertEquals(ACME_VENDOR_ID, msg.getVendor()); + AcmeVendorData1 vendorData1 = (AcmeVendorData1) msg.getVendorData(); + assertEquals(11, vendorData1.getFlags()); + assertEquals(22, vendorData1.getValue()); + + vendorData = new AcmeVendorData2(33, 44); + msg.setVendorData(vendorData); + msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength()); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + assertEquals(ACME_VENDOR_ID, msg.getVendor()); + AcmeVendorData2 vendorData2 = (AcmeVendorData2) msg.getVendorData(); + assertEquals(33, vendorData2.getType()); + assertEquals(44, vendorData2.getSubtype()); + + final int DUMMY_VENDOR_ID = 55; + msg.setVendor(DUMMY_VENDOR_ID); + byte[] genericVendorDataBytes = new byte[] {0x55, 0x66}; + vendorData = new OFByteArrayVendorData(genericVendorDataBytes); + msg.setVendorData(vendorData); + msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength()); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + assertEquals(DUMMY_VENDOR_ID, msg.getVendor()); + OFByteArrayVendorData genericVendorData = (OFByteArrayVendorData) msg.getVendorData(); + assertTrue(Arrays.equals(genericVendorDataBytes, genericVendorData.getBytes())); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/WildcardsTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/WildcardsTest.java new file mode 100644 index 00000000..5bf8d123 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/WildcardsTest.java @@ -0,0 +1,162 @@ +package org.openflow.protocol; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.EnumSet; + +import org.junit.Test; +import org.openflow.protocol.Wildcards.Flag; + +public class WildcardsTest { + + @Test + public void testBasic() { + int[] intMasks = { 0, 0x3820e0, OFMatch.OFPFW_ALL_SANITIZED }; + for (int i : intMasks) { + Wildcards w = Wildcards.of(i); + assertEquals(i, w.getInt()); + } + } + + @Test + public void testAllSanitize() { + Wildcards w = Wildcards.of(OFMatch.OFPFW_ALL); + assertEquals(OFMatch.OFPFW_ALL_SANITIZED, w.getInt()); + assertTrue(w.isFull()); + assertFalse(w.isExact()); + } + + @Test + public void testAll() { + Wildcards all = Wildcards.FULL; + assertTrue(all.isFull()); + assertFalse(all.isExact()); + assertEquals(0, all.getNwDstMask()); + assertEquals(0, all.getNwSrcMask()); + + // unsetting flags from NONE is a no-op + Wildcards stillAll = all.wildcard(Flag.IN_PORT); + assertTrue(stillAll.isFull()); + assertEquals(all, stillAll); + + // so is setting a >= 32 netmask + + stillAll = all.withNwSrcMask(0); + assertTrue(stillAll.isFull()); + assertEquals(all, stillAll); + + stillAll = all.withNwDstMask(0); + assertTrue(stillAll.isFull()); + assertEquals(all, stillAll); + } + + @Test + public void testNone() { + Wildcards none = Wildcards.EXACT; + assertTrue(none.isExact()); + assertEquals(32, none.getNwDstMask()); + assertEquals(32, none.getNwSrcMask()); + + // unsetting flags from NONE is a no-op + Wildcards stillNone = none.matchOn(Flag.IN_PORT); + assertTrue(stillNone.isExact()); + assertEquals(none, stillNone); + + // so is setting a >= 32 netmask + stillNone = none.withNwSrcMask(32); + assertTrue(stillNone.isExact()); + assertEquals(none, stillNone); + + stillNone = none.withNwDstMask(32); + assertTrue(stillNone.isExact()); + assertEquals(none, stillNone); + } + + @Test + public void testSetOneFlag() { + Wildcards none = Wildcards.EXACT; + assertTrue(none.isExact()); + assertFalse(none.isWildcarded(Flag.DL_SRC)); + Wildcards one = none.wildcard(Flag.DL_SRC); + assertFalse(one.isExact()); + assertTrue(one.isWildcarded(Flag.DL_SRC)); + assertEquals(OFMatch.OFPFW_DL_SRC, one.getInt()); + assertEquals(EnumSet.of(Flag.DL_SRC), one.getWildcardedFlags()); + } + + @Test + public void testSetTwoFlags() { + Wildcards none = Wildcards.EXACT; + + // set two flags + Wildcards two = none.wildcard(Flag.DL_SRC, Flag.DL_DST); + assertFalse(two.isExact()); + assertTrue(two.isWildcarded(Flag.DL_SRC)); + assertTrue(two.isWildcarded(Flag.DL_DST)); + assertEquals(OFMatch.OFPFW_DL_SRC | OFMatch.OFPFW_DL_DST, two.getInt()); + assertEquals(EnumSet.of(Flag.DL_SRC, Flag.DL_DST), two.getWildcardedFlags()); + + // unset dl_dst + Wildcards gone = two.matchOn(Flag.DL_DST); + assertFalse(gone.isExact()); + assertTrue(gone.isWildcarded(Flag.DL_SRC)); + assertFalse(gone.isWildcarded(Flag.DL_DST)); + assertEquals(OFMatch.OFPFW_DL_SRC, gone.getInt()); + assertEquals(EnumSet.of(Flag.DL_SRC), gone.getWildcardedFlags()); + } + + @Test + public void testSetNwSrc() { + Wildcards none = Wildcards.EXACT; + assertEquals(32, none.getNwSrcMask()); + + // unsetting flags from NONE is a no-op + Wildcards nwSet = none.withNwSrcMask(8); + assertFalse(nwSet.isExact()); + assertEquals(EnumSet.noneOf(Flag.class), nwSet.getWildcardedFlags()); + assertEquals(8, nwSet.getNwSrcMask()); + assertEquals((32 - 8) << OFMatch.OFPFW_NW_SRC_SHIFT, nwSet.getInt()); + } + + @Test + public void testSetNwDst() { + Wildcards none = Wildcards.EXACT; + assertEquals(32, none.getNwDstMask()); + + // unsetting flags from NONE is a no-op + Wildcards nwSet = none.withNwDstMask(8); + assertFalse(nwSet.isExact()); + assertEquals(EnumSet.noneOf(Flag.class), nwSet.getWildcardedFlags()); + assertEquals(8, nwSet.getNwDstMask()); + assertEquals((32 - 8) << OFMatch.OFPFW_NW_DST_SHIFT, nwSet.getInt()); + } + + @Test + public void testToString() { + String s = Wildcards.FULL.toString(); + assertNotNull(s); + assertTrue(s.length() > 0); + } + + @Test + public void testInvert() { + assertEquals(Wildcards.FULL, Wildcards.EXACT.inverted()); + + Wildcards some = Wildcards.of(Flag.DL_VLAN, Flag.DL_VLAN_PCP); + Wildcards inv = some.inverted(); + + for(Flag f : Flag.values()) { + boolean shouldBeSet = (f == Flag.DL_VLAN || f == Flag.DL_VLAN_PCP); + + assertEquals("Flag " + f + " " + + (shouldBeSet ? "should be set " : "should not be set"), + shouldBeSet, some.isWildcarded(f)); + assertEquals(!(f == Flag.DL_VLAN || f == Flag.DL_VLAN_PCP), inv.isWildcarded(f)); + } + assertEquals(0, inv.getNwDstMask()); + assertEquals(0, inv.getNwSrcMask()); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorAction.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorAction.java new file mode 100644 index 00000000..49b69fb8 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorAction.java @@ -0,0 +1,41 @@ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; + + +public class MockVendorAction extends OFActionVendor { + public static final int VENDOR_ID = 0xdeadbeef; + + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private byte[] mockData; + + public byte[] getMockData() { + return mockData; + } + + public void setMockData(byte[] mockData) { + this.mockData = mockData; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + + int dataLength = getLength() - MINIMUM_LENGTH; + if(dataLength > 0) { + mockData = new byte[dataLength]; + data.readBytes(mockData); + } else { + mockData = EMPTY_BYTE_ARRAY; + } + + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeBytes(mockData); + } + + +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java new file mode 100644 index 00000000..bbc254ca --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java @@ -0,0 +1,15 @@ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.factory.OFVendorActionFactory; + +public class MockVendorActionFactory implements OFVendorActionFactory { + + @Override + public OFActionVendor readFrom(ChannelBuffer data) { + MockVendorAction action = new MockVendorAction(); + action.readFrom(data); + return action; + } + +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java new file mode 100644 index 00000000..31ad675d --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java @@ -0,0 +1,17 @@ +package org.openflow.protocol.action; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openflow.protocol.factory.OFVendorActionRegistry; + +public class OFVendorActionRegistryTest { + + @Test + public void test() { + MockVendorActionFactory factory = new MockVendorActionFactory(); + OFVendorActionRegistry.getInstance().register(MockVendorAction.VENDOR_ID, factory); + assertEquals(factory, OFVendorActionRegistry.getInstance().get(MockVendorAction.VENDOR_ID)); + } + +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/HexStringTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/HexStringTest.java new file mode 100644 index 00000000..a8f8ba4b --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/HexStringTest.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import org.junit.Test; + +import junit.framework.TestCase; + +/** + * Does hexstring conversion work? + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ + +public class HexStringTest extends TestCase { + + @Test + public void testMarshalling() throws Exception { + String dpidStr = "00:00:00:23:20:2d:16:71"; + long dpid = HexString.toLong(dpidStr); + String testStr = HexString.toHexString(dpid); + TestCase.assertEquals(dpidStr, testStr); + } + + @Test + public void testToLong() { + String dpidStr = "3e:1f:01:fc:72:8c:63:31"; + long valid = 0x3e1f01fc728c6331L; + long testLong = HexString.toLong(dpidStr); + TestCase.assertEquals(valid, testLong); + } + + @Test + public void testToLongMSB() { + String dpidStr = "ca:7c:5e:d1:64:7a:95:9b"; + long valid = -3856102927509056101L; + long testLong = HexString.toLong(dpidStr); + TestCase.assertEquals(valid, testLong); + } + + @Test + public void testToLongError() { + String dpidStr = "09:08:07:06:05:04:03:02:01"; + try { + HexString.toLong(dpidStr); + fail("HexString.toLong() should have thrown a NumberFormatException"); + } + catch (NumberFormatException expected) { + // do nothing + } + } + + @Test + public void testToStringBytes() { + byte[] dpid = { 0, 0, 0, 0, 0, 0, 0, -1 }; + String valid = "00:00:00:00:00:00:00:ff"; + String testString = HexString.toHexString(dpid); + TestCase.assertEquals(valid, testString); + } + + @Test + public void testFromHexStringError() { + String invalidStr = "00:00:00:00:00:00:ffff"; + try { + HexString.fromHexString(invalidStr); + fail("HexString.fromHexString() should have thrown a NumberFormatException"); + } + catch (NumberFormatException expected) { + // do nothing + } + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/OFTestCase.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/OFTestCase.java new file mode 100644 index 00000000..5132c2c7 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/OFTestCase.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; + +import junit.framework.TestCase; + +public class OFTestCase extends TestCase { + public OFMessageFactory messageFactory; + + @Override + protected void setUp() throws Exception { + super.setUp(); + messageFactory = new BasicFactory(); + } + + public void test() throws Exception { + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/U16Test.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/U16Test.java new file mode 100644 index 00000000..ba87e7b1 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/U16Test.java @@ -0,0 +1,33 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import junit.framework.TestCase; + +public class U16Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of a short + * @throws Exception + */ + public void test() throws Exception { + int val = 0xffff; + TestCase.assertEquals((short)-1, U16.t(val)); + TestCase.assertEquals((short)32767, U16.t(0x7fff)); + TestCase.assertEquals(val, U16.f((short)-1)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/U32Test.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/U32Test.java new file mode 100644 index 00000000..223c1034 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/U32Test.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import junit.framework.TestCase; + +public class U32Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of an int + * @throws Exception + */ + public void test() throws Exception { + long val = 0xffffffffL; + TestCase.assertEquals(-1, U32.t(val)); + TestCase.assertEquals(val, U32.f(-1)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/U64Test.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/U64Test.java new file mode 100644 index 00000000..0a97e302 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/U64Test.java @@ -0,0 +1,34 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.math.BigInteger; + +import junit.framework.TestCase; + +public class U64Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of a long + * @throws Exception + */ + public void test() throws Exception { + BigInteger val = new BigInteger("ffffffffffffffff", 16); + TestCase.assertEquals(-1, U64.t(val)); + TestCase.assertEquals(val, U64.f(-1)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/U8Test.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/U8Test.java new file mode 100644 index 00000000..2c06c4db --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/U8Test.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import junit.framework.TestCase; + +public class U8Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of a byte + * @throws Exception + */ + public void test() throws Exception { + short val = 0xff; + TestCase.assertEquals(-1, U8.t(val)); + TestCase.assertEquals(val, U8.f((byte)-1)); + } +} diff --git a/third-party/openflowj_netty/src/test/java/org/openflow/util/UnsignedTest.java b/third-party/openflowj_netty/src/test/java/org/openflow/util/UnsignedTest.java new file mode 100644 index 00000000..7cdf7391 --- /dev/null +++ b/third-party/openflowj_netty/src/test/java/org/openflow/util/UnsignedTest.java @@ -0,0 +1,83 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.util; + +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import junit.framework.TestCase; + +public class UnsignedTest extends TestCase { + public static String ULONG_MAX = "18446744073709551615"; + + /** + * Tests that we correctly extract an unsigned long into a BigInteger + * @throws Exception + */ + public void testGetUnsignedLong() throws Exception { + ByteBuffer bb = ByteBuffer.allocate(8); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.position(0); + bb.limit(8); + BigInteger bi = Unsigned.getUnsignedLong(bb); + BigInteger uLongMax = new BigInteger(ULONG_MAX); + for (int i = 0; i < uLongMax.bitCount(); ++i) { + TestCase.assertTrue("Bit: " + i + " should be: " + uLongMax.testBit(i), + uLongMax.testBit(i) == bi.testBit(i)); + } + TestCase.assertEquals(ULONG_MAX, bi.toString()); + + bb = ByteBuffer.allocate(10); + bb.put((byte)0x00); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.put((byte)0x00); + bb.position(0); + bb.limit(10); + bi = Unsigned.getUnsignedLong(bb, 1); + uLongMax = new BigInteger(ULONG_MAX); + for (int i = 0; i < uLongMax.bitCount(); ++i) { + TestCase.assertTrue("Bit: " + i + " should be: " + uLongMax.testBit(i), + uLongMax.testBit(i) == bi.testBit(i)); + } + TestCase.assertEquals(ULONG_MAX, bi.toString()); + } + + /** + * Tests that we correctly put an unsigned long into a ByteBuffer + * @throws Exception + */ + public void testPutUnsignedLong() throws Exception { + ByteBuffer bb = ByteBuffer.allocate(8); + BigInteger uLongMax = new BigInteger(ULONG_MAX); + Unsigned.putUnsignedLong(bb, uLongMax); + for (int i = 0; i < 8; ++i) { + TestCase.assertTrue("Byte: " + i + " should be 0xff, was: " + bb.get(i), + (bb.get(i) & (short)0xff) == 0xff); + } + + bb = ByteBuffer.allocate(10); + Unsigned.putUnsignedLong(bb, uLongMax, 1); + int offset = 1; + for (int i = 0; i < 8; ++i) { + TestCase.assertTrue("Byte: " + i + " should be 0xff, was: " + + bb.get(offset+i), (bb.get(offset+i) & (short)0xff) == 0xff); + } + } +} -- 2.36.6