aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-10-09 18:32:44 -0700
committerAshlee Young <ashlee@onosfw.com>2015-10-09 18:32:44 -0700
commit6a07d2d622eaa06953f3353e39c080984076e8de (patch)
treebfb50a2090fce186c2cc545a400c969bf2ea702b /framework/src/onos/apps
parente6d71622143ff9b2421a1abbe8434b954b5b1099 (diff)
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
Diffstat (limited to 'framework/src/onos/apps')
-rw-r--r--framework/src/onos/apps/aaa/pom.xml25
-rw-r--r--framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java592
-rw-r--r--framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java239
-rw-r--r--framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java351
-rw-r--r--framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java28
-rw-r--r--framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java27
-rw-r--r--framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java469
-rw-r--r--framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java132
-rw-r--r--framework/src/onos/apps/acl/pom.xml30
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java290
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java56
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java146
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java191
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java85
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java338
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java251
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java20
-rw-r--r--framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java20
-rw-r--r--framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml2
-rw-r--r--framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java142
-rw-r--r--framework/src/onos/apps/bgprouter/pom.xml1
-rw-r--r--framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java1
-rw-r--r--framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java2
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java178
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java22
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java144
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java36
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java40
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java166
-rw-r--r--framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java25
-rw-r--r--framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java2
-rw-r--r--framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java162
-rw-r--r--framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java4
-rw-r--r--framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java10
-rw-r--r--framework/src/onos/apps/flowanalyzer/pom.xml32
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java33
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java213
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml23
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java28
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java120
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java103
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java183
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java21
-rw-r--r--framework/src/onos/apps/igmp/pom.xml117
-rw-r--r--framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java155
-rw-r--r--framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java39
-rw-r--r--framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java39
-rw-r--r--framework/src/onos/apps/mfwd/pom.xml142
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java45
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java72
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java62
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java5
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java211
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java68
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java237
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java133
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java190
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java431
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java110
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java48
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java338
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java4
-rw-r--r--framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java149
-rw-r--r--framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml30
-rw-r--r--framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml44
-rw-r--r--framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java16
-rw-r--r--framework/src/onos/apps/pim/pom.xml122
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java47
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java4
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java153
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java340
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java395
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java97
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java53
-rw-r--r--framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java20
-rw-r--r--framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml24
-rw-r--r--framework/src/onos/apps/pom.xml5
-rw-r--r--framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java26
-rw-r--r--framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java35
-rw-r--r--framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java395
-rw-r--r--framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java200
-rw-r--r--framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java56
-rw-r--r--framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java10
-rw-r--r--framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java51
-rw-r--r--framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java97
-rw-r--r--framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java39
-rw-r--r--framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java104
-rw-r--r--framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java4
-rw-r--r--framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java17
-rw-r--r--framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java166
-rw-r--r--framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java24
-rw-r--r--framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java192
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java901
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java81
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java74
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java18
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java216
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java2
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java460
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java81
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java417
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java7
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java105
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java2
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java113
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java128
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java2
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java2
-rw-r--r--framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java3
-rw-r--r--framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java4
-rw-r--r--framework/src/onos/apps/vtn/app/app.xml25
-rw-r--r--framework/src/onos/apps/vtn/app/features.xml27
-rw-r--r--framework/src/onos/apps/vtn/app/pom.xml44
-rw-r--r--framework/src/onos/apps/vtn/pom.xml46
-rw-r--r--framework/src/onos/apps/vtn/vtnmgr/pom.xml53
-rw-r--r--framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java68
-rw-r--r--framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java672
-rw-r--r--framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/pom.xml56
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java38
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java94
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java72
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java81
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java79
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java183
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java160
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java229
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java93
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java39
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java78
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java77
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java77
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java129
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java76
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java77
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java130
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java76
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java156
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java70
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java97
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java60
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java45
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java99
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java118
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java61
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java46
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java118
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java134
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java94
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java45
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java135
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java72
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java183
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java80
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java167
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java72
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java100
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java208
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java40
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java40
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java40
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java40
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java39
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java53
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java47
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java57
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml56
-rw-r--r--framework/src/onos/apps/vtn/vtnweb/pom.xml87
-rw-r--r--framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java379
-rw-r--r--framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java373
-rw-r--r--framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java412
-rw-r--r--framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java20
-rw-r--r--framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml45
186 files changed, 17358 insertions, 2549 deletions
diff --git a/framework/src/onos/apps/aaa/pom.xml b/framework/src/onos/apps/aaa/pom.xml
index 78fd4a62..b03930a9 100644
--- a/framework/src/onos/apps/aaa/pom.xml
+++ b/framework/src/onos/apps/aaa/pom.xml
@@ -44,36 +44,41 @@
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onlab-junit</artifactId>
- <scope>test</scope>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-api</artifactId>
+ <artifactId>onos-app-xos-integration</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
+ <dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onlab-osgi</artifactId>
- <version>${project.version}</version>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-app-xos-integration</artifactId>
+ <artifactId>onos-api</artifactId>
<version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
</dependency>
</dependencies>
- <build>
+ <build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
index 7e3de885..479ec7ed 100644
--- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
@@ -15,13 +15,14 @@
*/
package org.onosproject.aaa;
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.Set;
+
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Modified;
-import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.DeserializationException;
@@ -38,14 +39,16 @@ import org.onlab.packet.RADIUSAttribute;
import org.onlab.packet.TpPort;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
@@ -58,28 +61,21 @@ import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.xosintegration.VoltTenantService;
-import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
+import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
import static org.onosproject.net.packet.PacketPriority.CONTROL;
import static org.slf4j.LoggerFactory.getLogger;
-
/**
* AAA application for ONOS.
*/
@Component(immediate = true)
public class AAA {
+
+ // for verbose output
+ private final Logger log = getLogger(getClass());
+
// a list of our dependencies :
// to register with ONOS as an application - described next
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -96,8 +92,28 @@ public class AAA {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected VoltTenantService voltTenantService;
- // for verbose output
- private final Logger log = getLogger(getClass());
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry netCfgService;
+
+ // Parsed RADIUS server addresses
+ protected InetAddress radiusIpAddress;
+ protected String radiusMacAddress;
+
+ // NAS IP address
+ protected InetAddress nasIpAddress;
+ protected String nasMacAddress;
+
+ // RADIUS server secret
+ protected String radiusSecret;
+
+ // ID of RADIUS switch
+ protected String radiusSwitch;
+
+ // RADIUS port number
+ protected long radiusPort;
+
+ // RADIUS server TCP port number
+ protected short radiusServerPort;
// our application-specific event handler
private ReactivePacketProcessor processor = new ReactivePacketProcessor();
@@ -105,124 +121,80 @@ public class AAA {
// our unique identifier
private ApplicationId appId;
- // Map of state machines. Each state machine is represented by an
- // unique identifier on the switch: dpid + port number
- Map stateMachineMap = null;
-
- // RADIUS server IP address
- private static final String DEFAULT_RADIUS_IP = "192.168.1.10";
- // NAS IP address
- private static final String DEFAULT_NAS_IP = "192.168.1.11";
- // RADIUS uplink port
- private static final int DEFAULT_RADIUS_UPLINK = 2;
- // RADIUS server shared secret
- private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
- // RADIUS MAC address
- private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
- // NAS MAC address
- private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
- // Radius Switch Id
- private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
- // Radius Port Number
- private static final String DEFAULT_RADIUS_PORT = "129";
-
- @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
- label = "RADIUS IP Address")
- private String radiusIpAddress = DEFAULT_RADIUS_IP;
-
- @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
- label = "NAS IP Address")
- private String nasIpAddress = DEFAULT_NAS_IP;
-
- @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
- label = "RADIUS MAC Address")
- private String radiusMacAddress = RADIUS_MAC_ADDRESS;
-
- @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
- label = "NAS MAC Address")
- private String nasMacAddress = NAS_MAC_ADDRESS;
-
- @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
- label = "RADIUS shared secret")
- private String radiusSecret = DEFAULT_RADIUS_SECRET;
-
- @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
- label = "Radius switch")
- private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
-
- @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
- label = "Radius port")
- private String radiusPort = DEFAULT_RADIUS_PORT;
-
- // Parsed RADIUS server IP address
- protected InetAddress parsedRadiusIpAddress;
-
- // Parsed NAS IP address
- protected InetAddress parsedNasIpAddress;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ComponentConfigService cfgService;
-
- @Modified
- public void modified(ComponentContext context) {
- Dictionary<?, ?> properties = context.getProperties();
-
- String s = Tools.get(properties, "radiusIpAddress");
- try {
- parsedRadiusIpAddress = InetAddress.getByName(s);
- radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
- } catch (UnknownHostException e) {
- log.error("Invalid RADIUS IP address specification: {}", s);
- }
- try {
- s = Tools.get(properties, "nasIpAddress");
- parsedNasIpAddress = InetAddress.getByName(s);
- nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
- } catch (UnknownHostException e) {
- log.error("Invalid NAS IP address specification: {}", s);
- }
+ // Configuration properties factory
+ private final ConfigFactory factory =
+ new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY,
+ AAAConfig.class,
+ "AAA") {
+ @Override
+ public AAAConfig createConfig() {
+ return new AAAConfig();
+ }
+ };
- s = Tools.get(properties, "radiusMacAddress");
- radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
+ // Listener for config changes
+ private final InternalConfigListener cfgListener = new InternalConfigListener();
- s = Tools.get(properties, "nasMacAddress");
- nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
+ /**
+ * Builds an EAPOL packet based on the given parameters.
+ *
+ * @param dstMac destination MAC address
+ * @param srcMac source MAC address
+ * @param vlan vlan identifier
+ * @param eapolType EAPOL type
+ * @param eap EAP payload
+ * @return Ethernet frame
+ */
+ private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
+ short vlan, byte eapolType, EAP eap) {
- s = Tools.get(properties, "radiusSecret");
- radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(dstMac.toBytes());
+ eth.setSourceMACAddress(srcMac.toBytes());
+ eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+ if (vlan != Ethernet.VLAN_UNTAGGED) {
+ eth.setVlanID(vlan);
+ }
+ //eapol header
+ EAPOL eapol = new EAPOL();
+ eapol.setEapolType(eapolType);
+ eapol.setPacketLength(eap.getLength());
- s = Tools.get(properties, "radiusSwitchId");
- radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
+ //eap part
+ eapol.setPayload(eap);
- s = Tools.get(properties, "radiusPortNumber");
- radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
+ eth.setPayload(eapol);
+ eth.setPad(true);
+ return eth;
}
@Activate
- public void activate(ComponentContext context) {
- cfgService.registerProperties(getClass());
- modified(context);
+ public void activate() {
+ netCfgService.addListener(cfgListener);
+ netCfgService.registerConfigFactory(factory);
+
// "org.onosproject.aaa" is the FQDN of our app
appId = coreService.registerApplication("org.onosproject.aaa");
+
+ cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AAAConfig.class));
+
// register our event handler
packetService.addProcessor(processor, PacketProcessor.director(2));
requestIntercepts();
- // Instantiate the map of the state machines
- stateMachineMap = Collections.synchronizedMap(Maps.newHashMap());
- hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
+ StateMachine.initializeMaps();
+ hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
}
@Deactivate
public void deactivate() {
- cfgService.unregisterProperties(getClass(), false);
-
appId = coreService.registerApplication("org.onosproject.aaa");
withdrawIntercepts();
// de-register and null our handler
packetService.removeProcessor(processor);
processor = null;
+ StateMachine.destroyMaps();
}
/**
@@ -237,8 +209,8 @@ public class AAA {
TrafficSelector radSelector = DefaultTrafficSelector.builder()
.matchEthType(EthType.EtherType.IPV4.ethType().toShort())
.matchIPProtocol(IPv4.PROTOCOL_UDP)
- .matchUdpDst(TpPort.tpPort(1812))
- .matchUdpSrc(TpPort.tpPort(1812))
+ .matchUdpDst(TpPort.tpPort(radiusServerPort))
+ .matchUdpSrc(TpPort.tpPort(radiusServerPort))
.build();
packetService.requestPackets(radSelector, CONTROL, appId);
}
@@ -254,45 +226,12 @@ public class AAA {
TrafficSelector radSelector = DefaultTrafficSelector.builder()
.matchEthType(EthType.EtherType.IPV4.ethType().toShort())
.matchIPProtocol(IPv4.PROTOCOL_UDP)
- .matchUdpDst(TpPort.tpPort(1812))
- .matchUdpSrc(TpPort.tpPort(1812))
+ .matchUdpDst(TpPort.tpPort(radiusServerPort))
+ .matchUdpSrc(TpPort.tpPort(radiusServerPort))
.build();
packetService.cancelPackets(radSelector, CONTROL, appId);
}
- /**
- * Builds an EAPOL packet based on the given parameters.
- *
- * @param dstMac destination MAC address
- * @param srcMac source MAC address
- * @param vlan vlan identifier
- * @param eapolType EAPOL type
- * @param eap EAP payload
- * @return Ethernet frame
- */
- private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
- short vlan, byte eapolType, EAP eap) {
-
- Ethernet eth = new Ethernet();
- eth.setDestinationMACAddress(dstMac.toBytes());
- eth.setSourceMACAddress(srcMac.toBytes());
- eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
- if (vlan != Ethernet.VLAN_UNTAGGED) {
- eth.setVlanID(vlan);
- }
- //eapol header
- EAPOL eapol = new EAPOL();
- eapol.setEapolType(eapolType);
- eapol.setPacketLength(eap.getLength());
-
- //eap part
- eapol.setPayload(eap);
-
- eth.setPayload(eapol);
- eth.setPad(true);
- return eth;
- }
-
// our handler defined as a private inner class
/**
@@ -308,42 +247,66 @@ public class AAA {
if (ethPkt == null) {
return;
}
- // identify if incoming packet comes from supplicant (EAP) or RADIUS
- switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
- case EAPOL:
- handleSupplicantPacket(context.inPacket());
- break;
- case IPV4:
- IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
- Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
- Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
- if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
- // TODO: check for port as well when it's configurable
- UDP udpPacket = (UDP) ipv4Packet.getPayload();
-
- byte[] datagram = udpPacket.getPayload().serialize();
- RADIUS radiusPacket;
- try {
+ try {
+ // identify if incoming packet comes from supplicant (EAP) or RADIUS
+ switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
+ case EAPOL:
+ handleSupplicantPacket(context.inPacket());
+ break;
+ case IPV4:
+ IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
+ Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
+ Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress);
+ if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
+ // TODO: check for port as well when it's configurable
+ UDP udpPacket = (UDP) ipv4Packet.getPayload();
+
+ byte[] datagram = udpPacket.getPayload().serialize();
+ RADIUS radiusPacket;
radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
- } catch (DeserializationException e) {
- log.warn("Unable to deserialize RADIUS packet:", e);
- return;
+ handleRadiusPacket(radiusPacket);
}
- handleRadiusPacket(radiusPacket);
- }
- break;
- default:
- return;
+
+ break;
+ default:
+ log.trace("Skipping Ethernet packet type {}",
+ EthType.EtherType.lookup(ethPkt.getEtherType()));
+ }
+ } catch (DeserializationException | StateMachineException e) {
+ log.warn("Unable to process RADIUS packet:", e);
}
}
+ /**
+ * Creates and initializes common fields of a RADIUS packet.
+ *
+ * @param identifier RADIUS identifier
+ * @param eapPacket EAP packet
+ * @return RADIUS packet
+ */
+ private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) {
+ RADIUS radiusPayload =
+ new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
+ eapPacket.getIdentifier());
+ radiusPayload.setIdentifier(identifier);
+ radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
+ eapPacket.getData());
+
+ radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
+ AAA.this.nasIpAddress.getAddress());
+
+ radiusPayload.encapsulateMessage(eapPacket);
+ radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+
+ return radiusPayload;
+ }
/**
* Handles PAE packets (supplicant).
*
* @param inPacket Ethernet packet coming from the supplicant
*/
- private void handleSupplicantPacket(InboundPacket inPacket) {
+ private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
Ethernet ethPkt = inPacket.parsed();
// Where does it come from?
MacAddress srcMAC = ethPkt.getSourceMAC();
@@ -351,114 +314,83 @@ public class AAA {
DeviceId deviceId = inPacket.receivedFrom().deviceId();
PortNumber portNumber = inPacket.receivedFrom().port();
String sessionId = deviceId.toString() + portNumber.toString();
- StateMachine stateMachine = getStateMachine(sessionId);
+ StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
+ if (stateMachine == null) {
+ stateMachine = new StateMachine(sessionId, voltTenantService);
+ }
+
EAPOL eapol = (EAPOL) ethPkt.getPayload();
switch (eapol.getEapolType()) {
case EAPOL.EAPOL_START:
- try {
- stateMachine.start();
- stateMachine.supplicantConnectpoint = inPacket.receivedFrom();
-
- //send an EAP Request/Identify to the supplicant
- EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null);
- Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
- ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
- eapPayload);
- stateMachine.supplicantAddress = srcMAC;
- stateMachine.vlanId = ethPkt.getVlanID();
-
- this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
- } catch (StateMachineException e) {
- e.printStackTrace();
- }
+ stateMachine.start();
+ stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
+
+ //send an EAP Request/Identify to the supplicant
+ EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
+ Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
+ ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
+ eapPayload);
+ stateMachine.setSupplicantAddress(srcMAC);
+ stateMachine.setVlanId(ethPkt.getVlanID());
+
+ this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
break;
case EAPOL.EAPOL_PACKET:
- //check if this is a Response/Identify or a Response/TLS
+ RADIUS radiusPayload;
+ // check if this is a Response/Identify or a Response/TLS
EAP eapPacket = (EAP) eapol.getPayload();
byte dataType = eapPacket.getDataType();
switch (dataType) {
+
case EAP.ATTR_IDENTITY:
- try {
- //request id access to RADIUS
- RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
- eapPacket.getIdentifier());
- radiusPayload.setIdentifier(stateMachine.getIdentifier());
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
- eapPacket.getData());
- stateMachine.setUsername(eapPacket.getData());
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
- AAA.this.parsedNasIpAddress.getAddress());
-
- radiusPayload.encapsulateMessage(eapPacket);
-
- // set Request Authenticator in StateMachine
- stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
- radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
- sendRadiusMessage(radiusPayload);
+ // request id access to RADIUS
+ stateMachine.setUsername(eapPacket.getData());
- //change the state to "PENDING"
- stateMachine.requestAccess();
- } catch (StateMachineException e) {
- e.printStackTrace();
- }
+ radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
+
+ // set Request Authenticator in StateMachine
+ stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
+ sendRadiusMessage(radiusPayload);
+
+ // change the state to "PENDING"
+ stateMachine.requestAccess();
break;
case EAP.ATTR_MD5:
- //verify if the EAP identifier corresponds to the challenge identifier from the client state
- //machine.
- if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) {
+ // verify if the EAP identifier corresponds to the
+ // challenge identifier from the client state
+ // machine.
+ if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
//send the RADIUS challenge response
- RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
- eapPacket.getIdentifier());
- radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier());
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
- stateMachine.getUsername());
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
- AAA.this.parsedNasIpAddress.getAddress());
-
- radiusPayload.encapsulateMessage(eapPacket);
+ radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket);
radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
- stateMachine.getChallengeState());
- radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+ stateMachine.challengeState());
sendRadiusMessage(radiusPayload);
}
break;
case EAP.ATTR_TLS:
- try {
- //request id access to RADIUS
- RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
- eapPacket.getIdentifier());
- radiusPayload.setIdentifier(stateMachine.getIdentifier());
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
- stateMachine.getUsername());
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
- AAA.this.parsedNasIpAddress.getAddress());
-
- radiusPayload.encapsulateMessage(eapPacket);
+ // request id access to RADIUS
+ radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
- radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
- stateMachine.getChallengeState());
- stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
+ radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
+ stateMachine.challengeState());
+ stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
- radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+ sendRadiusMessage(radiusPayload);
+ // TODO: this gets called on every fragment, should only be called at TLS-Start
+ stateMachine.requestAccess();
- sendRadiusMessage(radiusPayload);
- // TODO: this gets called on every fragment, should only be called at TLS-Start
- stateMachine.requestAccess();
- } catch (StateMachineException e) {
- e.printStackTrace();
- }
break;
default:
return;
}
break;
default:
- return;
+ log.trace("Skipping EAPOL message {}", eapol.getEapolType());
}
}
@@ -467,99 +399,46 @@ public class AAA {
*
* @param radiusPacket RADIUS packet coming from the RADIUS server.
*/
- private void handleRadiusPacket(RADIUS radiusPacket) {
- StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier());
+ private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
+ StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
if (stateMachine == null) {
log.error("Invalid session identifier, exiting...");
return;
}
- EAP eapPayload = new EAP();
- Ethernet eth = null;
+ EAP eapPayload;
+ Ethernet eth;
switch (radiusPacket.getCode()) {
case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
eapPayload = radiusPacket.decapsulateMessage();
stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
- eth = buildEapolResponse(stateMachine.supplicantAddress,
- MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
+ eth = buildEapolResponse(stateMachine.supplicantAddress(),
+ MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
eapPayload);
- this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
+ this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
break;
case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
- try {
- //send an EAPOL - Success to the supplicant.
- byte[] eapMessage =
- radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
- eapPayload = new EAP();
- eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
- eth = buildEapolResponse(stateMachine.supplicantAddress,
- MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
- eapPayload);
- this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
-
- stateMachine.authorizeAccess();
- } catch (StateMachineException e) {
- e.printStackTrace();
- }
+ //send an EAPOL - Success to the supplicant.
+ byte[] eapMessage =
+ radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
+ eapPayload = new EAP();
+ eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
+ eth = buildEapolResponse(stateMachine.supplicantAddress(),
+ MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
+ eapPayload);
+ this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+
+ stateMachine.authorizeAccess();
break;
case RADIUS.RADIUS_CODE_ACCESS_REJECT:
- try {
- stateMachine.denyAccess();
- } catch (StateMachineException e) {
- e.printStackTrace();
- }
+ stateMachine.denyAccess();
break;
default:
log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
}
}
- private StateMachine getStateMachineById(byte identifier) {
- StateMachine stateMachine = null;
- Set stateMachineSet = stateMachineMap.entrySet();
-
- synchronized (stateMachineMap) {
- Iterator itr = stateMachineSet.iterator();
- while (itr.hasNext()) {
- Map.Entry entry = (Map.Entry) itr.next();
- stateMachine = (StateMachine) entry.getValue();
- if (identifier == stateMachine.getIdentifier()) {
- //the state machine has already been created for this session session
- stateMachine = (StateMachine) entry.getValue();
- break;
- }
- }
- }
-
- return stateMachine;
- }
-
- private StateMachine getStateMachine(String sessionId) {
- StateMachine stateMachine = null;
- Set stateMachineSet = stateMachineMap.entrySet();
-
- synchronized (stateMachineMap) {
- Iterator itr = stateMachineSet.iterator();
- while (itr.hasNext()) {
-
- Map.Entry entry = (Map.Entry) itr.next();
- if (sessionId.equals(entry.getKey())) {
- //the state machine has already been created for this session session
- stateMachine = (StateMachine) entry.getValue();
- break;
- }
- }
- }
-
- if (stateMachine == null) {
- stateMachine = new StateMachine(sessionId, voltTenantService);
- stateMachineMap.put(sessionId, stateMachine);
- }
-
- return stateMachine;
- }
-
private void sendRadiusMessage(RADIUS radiusMessage) {
Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
@@ -576,12 +455,12 @@ public class AAA {
IPv4 ip4Packet = new IPv4();
Ethernet ethPkt = new Ethernet();
radiusMessage.setParent(udp);
- udp.setDestinationPort((short) 1812);
- udp.setSourcePort((short) 1812); // TODO: make this configurable
+ udp.setDestinationPort(radiusServerPort);
+ udp.setSourcePort(radiusServerPort);
udp.setPayload(radiusMessage);
udp.setParent(ip4Packet);
- ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
- ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
+ ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress());
+ ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress());
ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
ip4Packet.setPayload(udp);
ip4Packet.setParent(ethPkt);
@@ -591,7 +470,7 @@ public class AAA {
ethPkt.setPayload(ip4Packet);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
+ .setOutput(PortNumber.portNumber(radiusPort)).build();
OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
treatment, ByteBuffer.wrap(ethPkt.serialize()));
packetService.emit(packet);
@@ -613,4 +492,59 @@ public class AAA {
}
+ private class InternalConfigListener implements NetworkConfigListener {
+
+ /**
+ * Reconfigures the DHCP Server according to the configuration parameters passed.
+ *
+ * @param cfg configuration object
+ */
+ private void reconfigureNetwork(AAAConfig cfg) {
+ AAAConfig newCfg;
+ if (cfg == null) {
+ newCfg = new AAAConfig();
+ } else {
+ newCfg = cfg;
+ }
+ if (newCfg.nasIp() != null) {
+ nasIpAddress = newCfg.nasIp();
+ }
+ if (newCfg.radiusIp() != null) {
+ radiusIpAddress = newCfg.radiusIp();
+ }
+ if (newCfg.radiusMac() != null) {
+ radiusMacAddress = newCfg.radiusMac();
+ }
+ if (newCfg.nasMac() != null) {
+ nasMacAddress = newCfg.nasMac();
+ }
+ if (newCfg.radiusSecret() != null) {
+ radiusSecret = newCfg.radiusSecret();
+ }
+ if (newCfg.radiusSwitch() != null) {
+ radiusSwitch = newCfg.radiusSwitch();
+ }
+ if (newCfg.radiusPort() != -1) {
+ radiusPort = newCfg.radiusPort();
+ }
+ if (newCfg.radiusServerUDPPort() != -1) {
+ radiusServerPort = newCfg.radiusServerUDPPort();
+ }
+ }
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(AAAConfig.class)) {
+
+ AAAConfig cfg = netCfgService.getConfig(appId, AAAConfig.class);
+ reconfigureNetwork(cfg);
+ log.info("Reconfigured");
+ }
+ }
+ }
+
+
}
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java
new file mode 100644
index 00000000..c18d2bf2
--- /dev/null
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.aaa;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.BasicElementConfig;
+
+/**
+ * Network config for the AAA app.
+ */
+public class AAAConfig extends Config<ApplicationId> {
+
+ private static final String RADIUS_IP = "radiusIp";
+ private static final String RADIUS_SERVER_PORT = "1812";
+ private static final String RADIUS_MAC = "radiusMac";
+ private static final String NAS_IP = "nasIp";
+ private static final String NAS_MAC = "nasMac";
+ private static final String RADIUS_SECRET = "radiusSecret";
+ private static final String RADIUS_SWITCH = "radiusSwitch";
+ private static final String RADIUS_PORT = "radiusPort";
+
+ // RADIUS server IP address
+ protected static final String DEFAULT_RADIUS_IP = "192.168.1.10";
+
+ // RADIUS MAC address
+ protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10";
+
+ // NAS IP address
+ protected static final String DEFAULT_NAS_IP = "192.168.1.11";
+
+ // NAS MAC address
+ protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01";
+
+ // RADIUS server shared secret
+ protected static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
+
+ // Radius Switch Id
+ protected static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
+
+ // Radius Port Number
+ protected static final String DEFAULT_RADIUS_PORT = "129";
+
+ // Radius Server UDP Port Number
+ protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812";
+
+ /**
+ * Gets the value of a string property, protecting for an empty
+ * JSON object.
+ *
+ * @param name name of the property
+ * @param defaultValue default value if none has been specified
+ * @return String value if one os found, default value otherwise
+ */
+ private String getStringProperty(String name, String defaultValue) {
+ if (object == null) {
+ return defaultValue;
+ }
+ return get(name, defaultValue);
+ }
+
+ /**
+ * Returns the NAS ip.
+ *
+ * @return ip address or null if not set
+ */
+ public InetAddress nasIp() {
+ try {
+ return InetAddress.getByName(getStringProperty(NAS_IP, DEFAULT_NAS_IP));
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the NAS ip.
+ *
+ * @param ip new ip address; null to clear
+ * @return self
+ */
+ public BasicElementConfig nasIp(String ip) {
+ return (BasicElementConfig) setOrClear(NAS_IP, ip);
+ }
+
+ /**
+ * Returns the RADIUS server ip.
+ *
+ * @return ip address or null if not set
+ */
+ public InetAddress radiusIp() {
+ try {
+ return InetAddress.getByName(getStringProperty(RADIUS_IP, DEFAULT_RADIUS_IP));
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the RADIUS server ip.
+ *
+ * @param ip new ip address; null to clear
+ * @return self
+ */
+ public BasicElementConfig radiusIp(String ip) {
+ return (BasicElementConfig) setOrClear(RADIUS_IP, ip);
+ }
+
+ /**
+ * Returns the RADIUS MAC address.
+ *
+ * @return mac address or null if not set
+ */
+ public String radiusMac() {
+ return getStringProperty(RADIUS_MAC, DEFAULT_RADIUS_MAC);
+ }
+
+ /**
+ * Sets the RADIUS MAC address.
+ *
+ * @param mac new MAC address; null to clear
+ * @return self
+ */
+ public BasicElementConfig radiusMac(String mac) {
+ return (BasicElementConfig) setOrClear(RADIUS_MAC, mac);
+ }
+
+ /**
+ * Returns the RADIUS MAC address.
+ *
+ * @return mac address or null if not set
+ */
+ public String nasMac() {
+ return getStringProperty(NAS_MAC, DEFAULT_NAS_MAC);
+ }
+
+ /**
+ * Sets the RADIUS MAC address.
+ *
+ * @param mac new MAC address; null to clear
+ * @return self
+ */
+ public BasicElementConfig nasMac(String mac) {
+ return (BasicElementConfig) setOrClear(NAS_MAC, mac);
+ }
+
+ /**
+ * Returns the RADIUS secret.
+ *
+ * @return radius secret or null if not set
+ */
+ public String radiusSecret() {
+ return getStringProperty(RADIUS_SECRET, DEFAULT_RADIUS_SECRET);
+ }
+
+ /**
+ * Sets the RADIUS secret.
+ *
+ * @param secret new MAC address; null to clear
+ * @return self
+ */
+ public BasicElementConfig radiusSecret(String secret) {
+ return (BasicElementConfig) setOrClear(RADIUS_SECRET, secret);
+ }
+
+ /**
+ * Returns the ID of the RADIUS switch.
+ *
+ * @return radius switch ID or null if not set
+ */
+ public String radiusSwitch() {
+ return getStringProperty(RADIUS_SWITCH, DEFAULT_RADIUS_SWITCH);
+ }
+
+ /**
+ * Sets the ID of the RADIUS switch.
+ *
+ * @param switchId new RADIUS switch ID; null to clear
+ * @return self
+ */
+ public BasicElementConfig radiusSwitch(String switchId) {
+ return (BasicElementConfig) setOrClear(RADIUS_SWITCH, switchId);
+ }
+
+ /**
+ * Returns the RADIUS port.
+ *
+ * @return radius port or null if not set
+ */
+ public long radiusPort() {
+ return Integer.parseInt(getStringProperty(RADIUS_PORT, DEFAULT_RADIUS_PORT));
+ }
+
+ /**
+ * Sets the RADIUS port.
+ *
+ * @param port new RADIUS port; null to clear
+ * @return self
+ */
+ public BasicElementConfig radiusPort(long port) {
+ return (BasicElementConfig) setOrClear(RADIUS_PORT, port);
+ }
+
+ /**
+ * Returns the RADIUS server UDP port.
+ *
+ * @return radius server UDP port.
+ */
+ public short radiusServerUDPPort() {
+ return Short.parseShort(getStringProperty(RADIUS_SERVER_PORT,
+ DEFAULT_RADIUS_SERVER_PORT));
+ }
+
+ /**
+ * Sets the RADIUS port.
+ *
+ * @param port new RADIUS UDP port; -1 to clear
+ * @return self
+ */
+ public BasicElementConfig radiusServerUDPPort(short port) {
+ return (BasicElementConfig) setOrClear(RADIUS_SERVER_PORT, (long) port);
+ }
+
+}
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java
index 60959ada..84f69241 100644
--- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java
@@ -18,13 +18,16 @@
package org.onosproject.aaa;
+import java.util.BitSet;
+import java.util.Map;
+
import org.onlab.packet.MacAddress;
import org.onosproject.net.ConnectPoint;
import org.onosproject.xosintegration.VoltTenant;
import org.onosproject.xosintegration.VoltTenantService;
import org.slf4j.Logger;
-import java.util.BitSet;
+import com.google.common.collect.Maps;
import static org.slf4j.LoggerFactory.getLogger;
@@ -58,9 +61,9 @@ class StateMachine {
private byte[] requestAuthenticator;
// Supplicant connectivity info
- protected ConnectPoint supplicantConnectpoint;
- protected MacAddress supplicantAddress;
- protected short vlanId;
+ private ConnectPoint supplicantConnectpoint;
+ private MacAddress supplicantAddress;
+ private short vlanId;
private String sessionId = null;
@@ -109,8 +112,28 @@ class StateMachine {
private int currentState = STATE_IDLE;
+ // Maps of state machines. Each state machine is represented by an
+ // unique identifier on the switch: dpid + port number
+ private static Map<String, StateMachine> sessionIdMap;
+ private static Map<Integer, StateMachine> identifierMap;
- /**
+ public static void initializeMaps() {
+ sessionIdMap = Maps.newConcurrentMap();
+ identifierMap = Maps.newConcurrentMap();
+ }
+
+ public static void destroyMaps() {
+ sessionIdMap = null;
+ identifierMap = null;
+ }
+
+ public static StateMachine lookupStateMachineById(byte identifier) {
+ return identifierMap.get((int) identifier);
+ }
+
+ public static StateMachine lookupStateMachineBySessionId(String sessionId) {
+ return sessionIdMap.get(sessionId);
+ } /**
* State Machine Constructor.
*
* @param sessionId session Id represented by the switch dpid + port number
@@ -120,15 +143,69 @@ class StateMachine {
log.info("Creating a new state machine for {}", sessionId);
this.sessionId = sessionId;
this.voltService = voltService;
+ sessionIdMap.put(sessionId, this);
+ }
+
+ /**
+ * Gets the connect point for the supplicant side.
+ *
+ * @return supplicant connect point
+ */
+ public ConnectPoint supplicantConnectpoint() {
+ return supplicantConnectpoint;
+ }
+
+ /**
+ * Sets the supplicant side connect point.
+ *
+ * @param supplicantConnectpoint supplicant select point.
+ */
+ public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
+ this.supplicantConnectpoint = supplicantConnectpoint;
+ }
+
+ /**
+ * Gets the MAC address of the supplicant.
+ *
+ * @return supplicant MAC address
+ */
+ public MacAddress supplicantAddress() {
+ return supplicantAddress;
+ }
+
+ /**
+ * Sets the supplicant MAC address.
+ *
+ * @param supplicantAddress new supplicant MAC address
+ */
+ public void setSupplicantAddress(MacAddress supplicantAddress) {
+ this.supplicantAddress = supplicantAddress;
+ }
+
+ /**
+ * Gets the client's Vlan ID.
+ *
+ * @return client vlan ID
+ */
+ public short vlanId() {
+ return vlanId;
+ }
+ /**
+ * Sets the client's vlan ID.
+ *
+ * @param vlanId new client vlan ID
+ */
+ public void setVlanId(short vlanId) {
+ this.vlanId = vlanId;
}
/**
- * Get the client id that is requesting for access.
+ * Gets the client id that is requesting for access.
*
* @return The client id.
*/
- public String getSessionId() {
+ public String sessionId() {
return this.sessionId;
}
@@ -137,7 +214,7 @@ class StateMachine {
*/
private void createIdentifier() throws StateMachineException {
log.debug("Creating Identifier.");
- int index = -1;
+ int index;
try {
//find the first available spot for identifier assignment
@@ -178,11 +255,11 @@ class StateMachine {
}
/**
- * Get the challenge EAP identifier set by the RADIUS.
+ * Gets the challenge EAP identifier set by the RADIUS.
*
* @return The challenge EAP identifier.
*/
- protected byte getChallengeIdentifier() {
+ protected byte challengeIdentifier() {
return this.challengeIdentifier;
}
@@ -198,11 +275,11 @@ class StateMachine {
}
/**
- * Get the challenge state set by the RADIUS.
+ * Gets the challenge state set by the RADIUS.
*
* @return The challenge state.
*/
- protected byte[] getChallengeState() {
+ protected byte[] challengeState() {
return this.challengeState;
}
@@ -217,16 +294,16 @@ class StateMachine {
/**
- * Get the username.
+ * Gets the username.
*
* @return The requestAuthenticator.
*/
- protected byte[] getReqeustAuthenticator() {
+ protected byte[] requestAuthenticator() {
return this.requestAuthenticator;
}
/**
- * Set the username.
+ * Sets the authenticator.
*
* @param authenticator The username sent to the RADIUS upon access request.
*/
@@ -236,11 +313,11 @@ class StateMachine {
/**
- * Get the username.
+ * Gets the username.
*
* @return The username.
*/
- protected byte[] getUsername() {
+ protected byte[] username() {
return this.username;
}
@@ -249,7 +326,7 @@ class StateMachine {
*
* @return The state machine identifier.
*/
- public byte getIdentifier() {
+ public byte identifier() {
return (byte) this.identifier;
}
@@ -267,7 +344,7 @@ class StateMachine {
/**
* Move to the next state.
*
- * @param msg
+ * @param msg message
*/
private void next(int msg) {
currentState = transition[currentState][msg];
@@ -280,14 +357,11 @@ class StateMachine {
* @throws StateMachineException if authentication protocol is violated
*/
public void start() throws StateMachineException {
- try {
- states[currentState].start();
- //move to the next state
- next(TRANSITION_START);
- createIdentifier();
- } catch (StateMachineInvalidTransitionException e) {
- e.printStackTrace();
- }
+ states[currentState].start();
+ //move to the next state
+ next(TRANSITION_START);
+ createIdentifier();
+ identifierMap.put(identifier, this);
}
/**
@@ -297,13 +371,9 @@ class StateMachine {
* @throws StateMachineException if authentication protocol is violated
*/
public void requestAccess() throws StateMachineException {
- try {
- states[currentState].requestAccess();
- //move to the next state
- next(TRANSITION_REQUEST_ACCESS);
- } catch (StateMachineInvalidTransitionException e) {
- e.printStackTrace();
- }
+ states[currentState].requestAccess();
+ //move to the next state
+ next(TRANSITION_REQUEST_ACCESS);
}
/**
@@ -313,27 +383,22 @@ class StateMachine {
* @throws StateMachineException if authentication protocol is violated
*/
public void authorizeAccess() throws StateMachineException {
- try {
- states[currentState].radiusAccepted();
- //move to the next state
- next(TRANSITION_AUTHORIZE_ACCESS);
-
- if (voltService != null) {
- voltService.addTenant(
- VoltTenant.builder()
- .withHumanReadableName("VCPE-" + this.identifier)
- .withId(this.identifier)
- .withProviderService(1)
- .withServiceSpecificId(String.valueOf(this.identifier))
- .withPort(this.supplicantConnectpoint)
- .withVlanId(String.valueOf(this.vlanId)).build());
- }
-
- deleteIdentifier();
- } catch (StateMachineInvalidTransitionException e) {
- e.printStackTrace();
+ states[currentState].radiusAccepted();
+ //move to the next state
+ next(TRANSITION_AUTHORIZE_ACCESS);
+
+ if (voltService != null) {
+ voltService.addTenant(
+ VoltTenant.builder()
+ .withHumanReadableName("VCPE-" + this.identifier)
+ .withId(this.identifier)
+ .withProviderService(1)
+ .withServiceSpecificId(String.valueOf(this.identifier))
+ .withPort(this.supplicantConnectpoint)
+ .withVlanId(String.valueOf(this.vlanId)).build());
}
+ deleteIdentifier();
}
/**
@@ -343,14 +408,10 @@ class StateMachine {
* @throws StateMachineException if authentication protocol is violated
*/
public void denyAccess() throws StateMachineException {
- try {
- states[currentState].radiusDenied();
- //move to the next state
- next(TRANSITION_DENY_ACCESS);
- deleteIdentifier();
- } catch (StateMachineInvalidTransitionException e) {
- e.printStackTrace();
- }
+ states[currentState].radiusDenied();
+ //move to the next state
+ next(TRANSITION_DENY_ACCESS);
+ deleteIdentifier();
}
/**
@@ -360,141 +421,117 @@ class StateMachine {
* @throws StateMachineException if authentication protocol is violated
*/
public void logoff() throws StateMachineException {
- try {
- states[currentState].logoff();
- //move to the next state
- next(TRANSITION_LOGOFF);
- } catch (StateMachineInvalidTransitionException e) {
- e.printStackTrace();
- }
+ states[currentState].logoff();
+ //move to the next state
+ next(TRANSITION_LOGOFF);
}
/**
- * Get the current state.
+ * Gets the current state.
*
* @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
* STATE_UNAUTHORIZED.
*/
- public int getState() {
+ public int state() {
return currentState;
}
-
+ @Override
public String toString() {
return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
("state: " + this.currentState);
}
-}
-// FIXME: A source file should contain no more than one top-level entity!
+ abstract class State {
+ private final Logger log = getLogger(getClass());
-abstract class State {
- private final Logger log = getLogger(getClass());
-
- private String name = "State";
+ private String name = "State";
- public void start() throws StateMachineInvalidTransitionException {
- log.warn("START transition from this state is not allowed.");
- }
+ public void start() throws StateMachineInvalidTransitionException {
+ log.warn("START transition from this state is not allowed.");
+ }
- public void requestAccess() throws StateMachineInvalidTransitionException {
- log.warn("REQUEST ACCESS transition from this state is not allowed.");
- }
+ public void requestAccess() throws StateMachineInvalidTransitionException {
+ log.warn("REQUEST ACCESS transition from this state is not allowed.");
+ }
- public void radiusAccepted() throws StateMachineInvalidTransitionException {
- log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
- }
+ public void radiusAccepted() throws StateMachineInvalidTransitionException {
+ log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
+ }
- public void radiusDenied() throws StateMachineInvalidTransitionException {
- log.warn("DENY ACCESS transition from this state is not allowed.");
- }
+ public void radiusDenied() throws StateMachineInvalidTransitionException {
+ log.warn("DENY ACCESS transition from this state is not allowed.");
+ }
- public void logoff() throws StateMachineInvalidTransitionException {
- log.warn("LOGOFF transition from this state is not allowed.");
+ public void logoff() throws StateMachineInvalidTransitionException {
+ log.warn("LOGOFF transition from this state is not allowed.");
+ }
}
-}
-/**
- * Idle state: supplicant is logged of from the network.
- */
-class Idle extends State {
- private final Logger log = getLogger(getClass());
- private String name = "IDLE_STATE";
+ /**
+ * Idle state: supplicant is logged of from the network.
+ */
+ class Idle extends State {
+ private final Logger log = getLogger(getClass());
+ private String name = "IDLE_STATE";
- public void start() {
- log.info("Moving from IDLE state to STARTED state.");
+ public void start() {
+ log.info("Moving from IDLE state to STARTED state.");
+ }
}
-}
-/**
- * Started state: supplicant has entered the network and informed the authenticator.
- */
-class Started extends State {
- private final Logger log = getLogger(getClass());
- private String name = "STARTED_STATE";
+ /**
+ * Started state: supplicant has entered the network and informed the authenticator.
+ */
+ class Started extends State {
+ private final Logger log = getLogger(getClass());
+ private String name = "STARTED_STATE";
- public void requestAccess() {
- log.info("Moving from STARTED state to PENDING state.");
+ public void requestAccess() {
+ log.info("Moving from STARTED state to PENDING state.");
+ }
}
-}
-/**
- * Pending state: supplicant has been identified by the authenticator but has not access yet.
- */
-class Pending extends State {
- private final Logger log = getLogger(getClass());
- private String name = "PENDING_STATE";
+ /**
+ * Pending state: supplicant has been identified by the authenticator but has not access yet.
+ */
+ class Pending extends State {
+ private final Logger log = getLogger(getClass());
+ private String name = "PENDING_STATE";
- public void radiusAccepted() {
- log.info("Moving from PENDING state to AUTHORIZED state.");
- }
+ public void radiusAccepted() {
+ log.info("Moving from PENDING state to AUTHORIZED state.");
+ }
- public void radiusDenied() {
- log.info("Moving from PENDING state to UNAUTHORIZED state.");
+ public void radiusDenied() {
+ log.info("Moving from PENDING state to UNAUTHORIZED state.");
+ }
}
-}
-/**
- * Authorized state: supplicant port has been accepted, access is granted.
- */
-class Authorized extends State {
- private final Logger log = getLogger(getClass());
- private String name = "AUTHORIZED_STATE";
+ /**
+ * Authorized state: supplicant port has been accepted, access is granted.
+ */
+ class Authorized extends State {
+ private final Logger log = getLogger(getClass());
+ private String name = "AUTHORIZED_STATE";
- public void logoff() {
+ public void logoff() {
- log.info("Moving from AUTHORIZED state to IDLE state.");
+ log.info("Moving from AUTHORIZED state to IDLE state.");
+ }
}
-}
-/**
- * Unauthorized state: supplicant port has been rejected, access is denied.
- */
-class Unauthorized extends State {
- private final Logger log = getLogger(getClass());
- private String name = "UNAUTHORIZED_STATE";
+ /**
+ * Unauthorized state: supplicant port has been rejected, access is denied.
+ */
+ class Unauthorized extends State {
+ private final Logger log = getLogger(getClass());
+ private String name = "UNAUTHORIZED_STATE";
- public void logoff() {
- log.info("Moving from UNAUTHORIZED state to IDLE state.");
+ public void logoff() {
+ log.info("Moving from UNAUTHORIZED state to IDLE state.");
+ }
}
-}
-/**
- * Exception for the State Machine.
- */
-class StateMachineException extends Exception {
- public StateMachineException(String message) {
- super(message);
-
- }
-}
-
-/**
- * Exception raised when the transition from one state to another is invalid.
- */
-class StateMachineInvalidTransitionException extends StateMachineException {
- public StateMachineInvalidTransitionException(String message) {
- super(message);
- }
}
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java
new file mode 100644
index 00000000..d4a4da77
--- /dev/null
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015 AT&T Foundry
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.onosproject.aaa;
+
+/**
+ * Exception for the State Machine.
+ */
+class StateMachineException extends Exception {
+ public StateMachineException(String message) {
+ super(message);
+
+ }
+}
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java
new file mode 100644
index 00000000..9f41a34f
--- /dev/null
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright 2015 AT&T Foundry
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.onosproject.aaa;
+
+/**
+ * Exception raised when the transition from one state to another is invalid.
+ */
+class StateMachineInvalidTransitionException extends StateMachineException {
+ public StateMachineInvalidTransitionException(String message) {
+ super(message);
+ }
+}
diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
index 1b581ab1..75a60336 100644
--- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
+++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
@@ -15,29 +15,494 @@
*/
package org.onosproject.aaa;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.EAP;
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.RADIUS;
+import org.onlab.packet.RADIUSAttribute;
+import org.onlab.packet.UDP;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableSet;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.onosproject.net.NetTestTools.connectPoint;
/**
* Set of tests of the ONOS application component.
*/
public class AAATest {
+ MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
+ MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
+
+ PacketProcessor packetProcessor;
private AAA aaa;
+ List<Ethernet> savedPackets = new LinkedList<>();
+
+ /**
+ * Saves the given packet onto the saved packets list.
+ *
+ * @param eth packet to save
+ */
+ private void savePacket(Ethernet eth) {
+ savedPackets.add(eth);
+ }
+
+ /**
+ * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
+ */
+ private class MockPacketService extends PacketServiceAdapter {
+
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ packetProcessor = processor;
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ try {
+ Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
+ 0, packet.data().array().length);
+ savePacket(eth);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Mocks the DefaultPacketContext.
+ */
+ private final class TestPacketContext extends DefaultPacketContext {
+
+ private TestPacketContext(long time, InboundPacket inPkt,
+ OutboundPacket outPkt, boolean block) {
+ super(time, inPkt, outPkt, block);
+ }
+
+ @Override
+ public void send() {
+ // We don't send anything out.
+ }
+ }
+
+ /**
+ * Mocks a host to allow locating the Radius server.
+ */
+ private static final class MockHost implements Host {
+ @Override
+ public HostId id() {
+ return null;
+ }
+
+ @Override
+ public MacAddress mac() {
+ return null;
+ }
+
+ @Override
+ public VlanId vlan() {
+ return VlanId.vlanId(VlanId.UNTAGGED);
+ }
+
+ @Override
+ public Set<IpAddress> ipAddresses() {
+ return null;
+ }
+
+ @Override
+ public HostLocation location() {
+ return null;
+ }
+
+ @Override
+ public Annotations annotations() {
+ return null;
+ }
+
+ @Override
+ public ProviderId providerId() {
+ return null;
+ }
+ }
+
+ /**
+ * Mocks the Host service.
+ */
+ private static final class MockHostService extends HostServiceAdapter {
+ @Override
+ public Set<Host> getHostsByIp(IpAddress ip) {
+ return ImmutableSet.of(new MockHost());
+ }
+ }
+
+ /**
+ * Mocks the network config registry.
+ */
+ @SuppressWarnings("unchecked")
+ private static final class TestNetworkConfigRegistry
+ extends NetworkConfigRegistryAdapter {
+ @Override
+ public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+ return (C) new AAAConfig();
+ }
+ }
+
+ /**
+ * Sends an Ethernet packet to the process method of the Packet Processor.
+ *
+ * @param reply Ethernet packet
+ */
+ private void sendPacket(Ethernet reply) {
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
+ InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
+ reply,
+ byteBuffer);
+
+ PacketContext context = new TestPacketContext(127L, inPacket, null, false);
+ packetProcessor.process(context);
+ }
+
+ /**
+ * Constructs an Ethernet packet containing a EAPOL_START Payload.
+ *
+ * @return Ethernet packet
+ */
+ private Ethernet constructSupplicantStartPacket() {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(clientMac.toBytes());
+ eth.setSourceMACAddress(serverMac.toBytes());
+ eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+ eth.setVlanID((short) 2);
+
+ EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null);
+
+ //eapol header
+ EAPOL eapol = new EAPOL();
+ eapol.setEapolType(EAPOL.EAPOL_START);
+ eapol.setPacketLength(eap.getLength());
+
+ //eap part
+ eapol.setPayload(eap);
+
+ eth.setPayload(eapol);
+ eth.setPad(true);
+ return eth;
+ }
+
+ /**
+ * Constructs an Ethernet packet containing identification payload.
+ *
+ * @return Ethernet packet
+ */
+ private Ethernet constructSupplicantIdentifyPacket(byte type) {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(clientMac.toBytes());
+ eth.setSourceMACAddress(serverMac.toBytes());
+ eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+ eth.setVlanID((short) 2);
+
+ String username = "user";
+ EAP eap = new EAP(EAP.REQUEST, (byte) 1, type,
+ username.getBytes(Charsets.US_ASCII));
+ eap.setIdentifier((byte) 1);
+
+ // eapol header
+ EAPOL eapol = new EAPOL();
+ eapol.setEapolType(EAPOL.EAPOL_PACKET);
+ eapol.setPacketLength(eap.getLength());
+
+ // eap part
+ eapol.setPayload(eap);
+
+ eth.setPayload(eapol);
+ eth.setPad(true);
+ return eth;
+ }
+
+ /**
+ * Constructs an Ethernet packet containing a RADIUS challenge
+ * packet.
+ *
+ * @param challengeCode code to use in challenge packet
+ * @param challengeType type to use in challenge packet
+ * @return Ethernet packet
+ */
+ private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(clientMac.toBytes());
+ eth.setSourceMACAddress(serverMac.toBytes());
+ eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort());
+ eth.setVlanID((short) 2);
+
+ IPv4 ipv4 = new IPv4();
+ ipv4.setProtocol(IPv4.PROTOCOL_UDP);
+ ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress());
+
+ String challenge = "1234";
+
+ EAP eap = new EAP(challengeType, (byte) 1, challengeType,
+ challenge.getBytes(Charsets.US_ASCII));
+ eap.setIdentifier((byte) 1);
+
+ RADIUS radius = new RADIUS();
+ radius.setCode(challengeCode);
+
+ radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
+ challenge.getBytes(Charsets.US_ASCII));
+ radius.setPayload(eap);
+ radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+ eap.serialize());
+
+ UDP udp = new UDP();
+ udp.setPayload(radius);
+ ipv4.setPayload(udp);
+
+ eth.setPayload(ipv4);
+ eth.setPad(true);
+ return eth;
+ }
+
+ /**
+ * Sets up the services required by the AAA application.
+ */
@Before
public void setUp() {
-
+ aaa = new AAA();
+ aaa.netCfgService = new TestNetworkConfigRegistry();
+ aaa.coreService = new CoreServiceAdapter();
+ aaa.packetService = new MockPacketService();
+ aaa.hostService = new MockHostService();
+ aaa.activate();
}
+ /**
+ * Tears down the AAA application.
+ */
@After
public void tearDown() {
+ aaa.deactivate();
+ }
+
+ /**
+ * Extracts the RADIUS packet from a packet sent by the supplicant.
+ *
+ * @param supplicantPacket packet sent by the supplicant
+ * @return RADIUS packet
+ * @throws DeserializationException if deserialization of the packet contents
+ * fails.
+ */
+ private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket)
+ throws DeserializationException {
+ assertThat(supplicantPacket, notNullValue());
+ assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED));
+ assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress));
+ assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress));
+
+ assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class));
+ IPv4 ipv4 = (IPv4) supplicantPacket.getPayload();
+ assertThat(ipv4, notNullValue());
+ assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(),
+ is(aaa.nasIpAddress.getHostAddress()));
+ assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(),
+ is(aaa.radiusIpAddress.getHostAddress()));
+
+ assertThat(ipv4.getPayload(), instanceOf(UDP.class));
+ UDP udp = (UDP) ipv4.getPayload();
+ assertThat(udp, notNullValue());
+
+ assertThat(udp.getPayload(), instanceOf(Data.class));
+ Data data = (Data) udp.getPayload();
+ RADIUS radius = RADIUS.deserializer()
+ .deserialize(data.getData(), 0, data.getData().length);
+ assertThat(radius, notNullValue());
+ return radius;
}
+ /**
+ * Checks the contents of a RADIUS packet being sent to the RADIUS server.
+ *
+ * @param radiusPacket packet to check
+ * @param code expected code
+ */
+ private void checkRadiusPacket(Ethernet radiusPacket, byte code) {
+ assertThat(radiusPacket.getVlanID(), is((short) 2));
+
+ // TODO: These address values seem wrong, but are produced by the current AAA implementation
+ assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L)));
+ assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
+
+ assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
+ EAPOL eapol = (EAPOL) radiusPacket.getPayload();
+ assertThat(eapol, notNullValue());
+
+ assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
+ assertThat(eapol.getPayload(), instanceOf(EAP.class));
+ EAP eap = (EAP) eapol.getPayload();
+ assertThat(eap, notNullValue());
+ assertThat(eap.getCode(), is(code));
+ }
+
+ /**
+ * Fetches the sent packet at the given index. The requested packet
+ * must be the last packet on the list.
+ *
+ * @param index index into sent packets array
+ * @return packet
+ */
+ private Ethernet fetchPacket(int index) {
+ assertThat(savedPackets.size(), is(index + 1));
+ Ethernet eth = savedPackets.get(index);
+ assertThat(eth, notNullValue());
+ return eth;
+ }
+
+ /**
+ * Tests the authentication path through the AAA application.
+ *
+ * @throws DeserializationException if packed deserialization fails.
+ */
@Test
- public void basics() {
+ public void testAuthentication() throws DeserializationException {
+
+ // Our session id will be the device ID ("of:1") with the port ("1") concatenated
+ String sessionId = "of:11";
+
+ // (1) Supplicant start up
+
+ Ethernet startPacket = constructSupplicantStartPacket();
+ sendPacket(startPacket);
+
+ Ethernet responsePacket = fetchPacket(0);
+ checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY);
+
+ // (2) Supplicant identify
+
+ Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY);
+ sendPacket(identifyPacket);
+ Ethernet radiusIdentifyPacket = fetchPacket(1);
+
+ RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket);
+ assertThat(radiusAccessRequest, notNullValue());
+ assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+ assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
+ is("user"));
+
+ IpAddress nasIp =
+ IpAddress.valueOf(IpAddress.Version.INET,
+ radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
+ .getValue());
+ assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress()));
+
+ // State machine should have been created by now
+
+ StateMachine stateMachine =
+ StateMachine.lookupStateMachineBySessionId(sessionId);
+ assertThat(stateMachine, notNullValue());
+ assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
+
+ // (3) RADIUS MD5 challenge
+
+ Ethernet radiusCodeAccessChallengePacket =
+ constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
+ sendPacket(radiusCodeAccessChallengePacket);
+
+ Ethernet radiusChallengeMD5Packet = fetchPacket(2);
+ checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5);
+
+ // (4) Supplicant MD5 response
+
+ Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5);
+ sendPacket(md5RadiusPacket);
+ Ethernet supplicantMD5ResponsePacket = fetchPacket(3);
+ RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket);
+ assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1));
+ assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+
+ // State machine should be in pending state
+
+ assertThat(stateMachine, notNullValue());
+ assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
+
+ // (5) RADIUS TLS Challenge
+
+ Ethernet radiusCodeAccessChallengeTLSPacket =
+ constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS);
+ sendPacket(radiusCodeAccessChallengeTLSPacket);
+
+ Ethernet radiusChallengeTLSPacket = fetchPacket(4);
+ checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS);
+
+ // (6) Supplicant TLS response
+
+ Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS);
+ sendPacket(tlsRadiusPacket);
+ Ethernet supplicantTLSResponsePacket = fetchPacket(5);
+ RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket);
+ assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0));
+ assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+
+ // (7) RADIUS Success
+
+ Ethernet successPacket =
+ constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
+ sendPacket(successPacket);
+ Ethernet supplicantSuccessPacket = fetchPacket(6);
+
+ checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS);
+
+ // State machine should be in authorized state
+
+ assertThat(stateMachine, notNullValue());
+ assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
}
+ /**
+ * Tests the default configuration.
+ */
+ @Test
+ public void testConfig() {
+ assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP));
+ assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC));
+ assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP));
+ assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC));
+ }
}
diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
index 2fe44ab9..04837e85 100644
--- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
+++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
@@ -21,6 +21,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import static org.junit.Assert.*;
public class StateMachineTest {
@@ -30,6 +31,7 @@ public class StateMachineTest {
public void setUp() {
System.out.println("Set Up.");
StateMachine.bitSet.clear();
+ StateMachine.initializeMaps();
stateMachine = new StateMachine("session0", null);
}
@@ -37,6 +39,7 @@ public class StateMachineTest {
public void tearDown() {
System.out.println("Tear Down.");
StateMachine.bitSet.clear();
+ StateMachine.destroyMaps();
stateMachine = null;
}
@@ -46,19 +49,19 @@ public class StateMachineTest {
*/
public void basic() throws StateMachineException {
System.out.println("======= BASIC =======.");
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
}
@Test
@@ -68,19 +71,19 @@ public class StateMachineTest {
public void testIdleState() throws StateMachineException {
System.out.println("======= IDLE STATE TEST =======.");
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
stateMachine.denyAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
}
@Test
@@ -92,19 +95,19 @@ public class StateMachineTest {
stateMachine.start();
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
stateMachine.denyAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
}
@Test
@@ -118,19 +121,19 @@ public class StateMachineTest {
stateMachine.requestAccess();
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
stateMachine.denyAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
}
@Test
@@ -144,19 +147,19 @@ public class StateMachineTest {
stateMachine.requestAccess();
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
stateMachine.denyAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
}
@Test
@@ -170,19 +173,19 @@ public class StateMachineTest {
stateMachine.authorizeAccess();
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
stateMachine.denyAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
}
@Test
@@ -196,27 +199,27 @@ public class StateMachineTest {
stateMachine.denyAccess();
stateMachine.start();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
stateMachine.requestAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
stateMachine.authorizeAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
stateMachine.denyAccess();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
stateMachine.logoff();
- Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+ Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
}
@Test
public void testIdentifierAvailability() throws StateMachineException {
System.out.println("======= IDENTIFIER TEST =======.");
- byte identifier = stateMachine.getIdentifier();
- System.out.println("State: " + stateMachine.getState());
+ byte identifier = stateMachine.identifier();
+ System.out.println("State: " + stateMachine.state());
System.out.println("Identifier: " + Byte.toUnsignedInt(identifier));
Assert.assertEquals(-1, identifier);
stateMachine.start();
@@ -230,7 +233,7 @@ public class StateMachineTest {
for (int i = 1; i <= 255; i++) {
StateMachine sm = new StateMachine("session" + i, null);
sm.start();
- byte id = sm.getIdentifier();
+ byte id = sm.identifier();
Assert.assertEquals(i, Byte.toUnsignedInt(id));
if (i == 3) {
sm3 = sm;
@@ -244,27 +247,72 @@ public class StateMachineTest {
//simulate the state machine for a specific session and logoff so we can free up a spot for an identifier
//let's choose identifier 247 then we free up 3
+ Assert.assertNotNull(sm247);
sm247.requestAccess();
sm247.authorizeAccess();
sm247.logoff();
- sm247 = null;
+ Assert.assertNotNull(sm3);
sm3.requestAccess();
sm3.authorizeAccess();
sm3.logoff();
- sm3 = null;
StateMachine otherSM3 = new StateMachine("session3b", null);
otherSM3.start();
otherSM3.requestAccess();
- byte id3 = otherSM3.getIdentifier();
+ byte id3 = otherSM3.identifier();
Assert.assertEquals(3, Byte.toUnsignedInt(id3));
StateMachine otherSM247 = new StateMachine("session247b", null);
otherSM247.start();
otherSM247.requestAccess();
- byte id247 = otherSM247.getIdentifier();
+ byte id247 = otherSM247.identifier();
Assert.assertEquals(247, Byte.toUnsignedInt(id247));
+ }
+ @Test
+ public void testSessionIdLookups() {
+ String sessionId1 = "session1";
+ String sessionId2 = "session2";
+ String sessionId3 = "session3";
+
+ StateMachine machine1ShouldBeNull =
+ StateMachine.lookupStateMachineBySessionId(sessionId1);
+ assertNull(machine1ShouldBeNull);
+ StateMachine machine2ShouldBeNull =
+ StateMachine.lookupStateMachineBySessionId(sessionId2);
+ assertNull(machine2ShouldBeNull);
+
+ StateMachine stateMachine1 = new StateMachine(sessionId1, null);
+ StateMachine stateMachine2 = new StateMachine(sessionId2, null);
+
+ assertEquals(stateMachine1,
+ StateMachine.lookupStateMachineBySessionId(sessionId1));
+ assertEquals(stateMachine2,
+ StateMachine.lookupStateMachineBySessionId(sessionId2));
+ assertNull(StateMachine.lookupStateMachineBySessionId(sessionId3));
+ }
+
+ @Test
+ public void testIdentifierLookups() throws StateMachineException {
+ String sessionId1 = "session1";
+ String sessionId2 = "session2";
+
+ StateMachine machine1ShouldBeNull =
+ StateMachine.lookupStateMachineById((byte) 1);
+ assertNull(machine1ShouldBeNull);
+ StateMachine machine2ShouldBeNull =
+ StateMachine.lookupStateMachineById((byte) 2);
+ assertNull(machine2ShouldBeNull);
+
+ StateMachine stateMachine1 = new StateMachine(sessionId1, null);
+ stateMachine1.start();
+ StateMachine stateMachine2 = new StateMachine(sessionId2, null);
+ stateMachine2.start();
+
+ assertEquals(stateMachine1,
+ StateMachine.lookupStateMachineById(stateMachine1.identifier()));
+ assertEquals(stateMachine2,
+ StateMachine.lookupStateMachineById(stateMachine2.identifier()));
}
}
diff --git a/framework/src/onos/apps/acl/pom.xml b/framework/src/onos/apps/acl/pom.xml
index 54dee432..454ac7e6 100644
--- a/framework/src/onos/apps/acl/pom.xml
+++ b/framework/src/onos/apps/acl/pom.xml
@@ -18,7 +18,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -36,10 +38,10 @@
<url>http://onosproject.org</url>
<properties>
- <onos.version>1.4.0-SNAPSHOT</onos.version>
<onos.app.name>org.onosproject.acl</onos.app.name>
<onos.app.origin>DLUT</onos.app.origin>
- <web.context>/onos/acl</web.context>
+
+ <web.context>/onos/v1/acl</web.context>
<api.version>1.0.0</api.version>
<api.title>ONOS ACL Application REST API</api.title>
<api.description>
@@ -64,19 +66,34 @@
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-junit</artifactId>
- <version>${onos.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-rest</artifactId>
- <version>${onos.version}</version>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-rest</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
- <version>${onos.version}</version>
+ <version>${project.version}</version>
</dependency>
<dependency>
@@ -100,7 +117,6 @@
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-misc</artifactId>
- <version>${onos.version}</version>
</dependency>
</dependencies>
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
new file mode 100644
index 00000000..8c91da4c
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Prefix;
+import org.onosproject.core.IdGenerator;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * ACL rule class.
+ */
+public final class AclRule {
+
+ private final RuleId id;
+
+ private final Ip4Prefix srcIp;
+ private final Ip4Prefix dstIp;
+ private final byte ipProto;
+ private final short dstTpPort;
+ private final Action action;
+
+ private static IdGenerator idGenerator;
+
+ /**
+ * Enum type for ACL rule's action.
+ */
+ public enum Action {
+ DENY, ALLOW
+ }
+
+ /**
+ * Constructor for serializer.
+ */
+ private AclRule() {
+ this.id = null;
+ this.srcIp = null;
+ this.dstIp = null;
+ this.ipProto = 0;
+ this.dstTpPort = 0;
+ this.action = null;
+ }
+
+ /**
+ * Create a new ACL rule.
+ *
+ * @param srcIp source IP address
+ * @param dstIp destination IP address
+ * @param ipProto IP protocol
+ * @param dstTpPort destination transport layer port
+ * @param action ACL rule's action
+ */
+ private AclRule(Ip4Prefix srcIp, Ip4Prefix dstIp, byte ipProto,
+ short dstTpPort, Action action) {
+ checkState(idGenerator != null, "Id generator is not bound.");
+ this.id = RuleId.valueOf(idGenerator.getNewId());
+ this.srcIp = srcIp;
+ this.dstIp = dstIp;
+ this.ipProto = ipProto;
+ this.dstTpPort = dstTpPort;
+ this.action = action;
+ }
+
+ /**
+ * Check if the first CIDR address is in (or the same as) the second CIDR address.
+ */
+ private boolean checkCIDRinCIDR(Ip4Prefix cidrAddr1, Ip4Prefix cidrAddr2) {
+ if (cidrAddr2 == null) {
+ return true;
+ } else if (cidrAddr1 == null) {
+ return false;
+ }
+ if (cidrAddr1.prefixLength() < cidrAddr2.prefixLength()) {
+ return false;
+ }
+ int offset = 32 - cidrAddr2.prefixLength();
+
+ int cidr1Prefix = cidrAddr1.address().toInt();
+ int cidr2Prefix = cidrAddr2.address().toInt();
+ cidr1Prefix = cidr1Prefix >> offset;
+ cidr2Prefix = cidr2Prefix >> offset;
+ cidr1Prefix = cidr1Prefix << offset;
+ cidr2Prefix = cidr2Prefix << offset;
+
+ return (cidr1Prefix == cidr2Prefix);
+ }
+
+ /**
+ * Check if this ACL rule match the given ACL rule.
+ *
+ * @param r ACL rule to check against
+ * @return true if this ACL rule matches the given ACL ruleule.
+ */
+ public boolean checkMatch(AclRule r) {
+ return (this.dstTpPort == r.dstTpPort || r.dstTpPort == 0)
+ && (this.ipProto == r.ipProto || r.ipProto == 0)
+ && (checkCIDRinCIDR(this.srcIp(), r.srcIp()))
+ && (checkCIDRinCIDR(this.dstIp(), r.dstIp()));
+ }
+
+ /**
+ * Returns a new ACL rule builder.
+ *
+ * @return ACL rule builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of an ACL rule.
+ */
+ public static final class Builder {
+
+ private Ip4Prefix srcIp = null;
+ private Ip4Prefix dstIp = null;
+ private byte ipProto = 0;
+ private short dstTpPort = 0;
+ private Action action = Action.DENY;
+
+ private Builder() {
+ // Hide constructor
+ }
+
+ /**
+ * Sets the source IP address for the ACL rule that will be built.
+ *
+ * @param srcIp source IP address to use for built ACL rule
+ * @return this builder
+ */
+ public Builder srcIp(Ip4Prefix srcIp) {
+ this.srcIp = srcIp;
+ return this;
+ }
+
+ /**
+ * Sets the destination IP address for the ACL rule that will be built.
+ *
+ * @param dstIp destination IP address to use for built ACL rule
+ * @return this builder
+ */
+ public Builder dstIp(Ip4Prefix dstIp) {
+ this.dstIp = dstIp;
+ return this;
+ }
+
+ /**
+ * Sets the IP protocol for the ACL rule that will be built.
+ *
+ * @param ipProto IP protocol to use for built ACL rule
+ * @return this builder
+ */
+ public Builder ipProto(byte ipProto) {
+ this.ipProto = ipProto;
+ return this;
+ }
+
+ /**
+ * Sets the destination transport layer port for the ACL rule that will be built.
+ *
+ * @param dstTpPort destination transport layer port to use for built ACL rule
+ * @return this builder
+ */
+ public Builder dstTpPort(short dstTpPort) {
+ if ((ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP)) {
+ this.dstTpPort = dstTpPort;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the action for the ACL rule that will be built.
+ *
+ * @param action action to use for built ACL rule
+ * @return this builder
+ */
+ public Builder action(Action action) {
+ this.action = action;
+ return this;
+ }
+
+ /**
+ * Builds an ACL rule from the accumulated parameters.
+ *
+ * @return ACL rule instance
+ */
+ public AclRule build() {
+ checkState(srcIp != null && dstIp != null, "Either srcIp or dstIp must be assigned.");
+ checkState(ipProto == 0 || ipProto == IPv4.PROTOCOL_ICMP
+ || ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP,
+ "ipProto must be assigned to TCP, UDP, or ICMP.");
+ return new AclRule(srcIp, dstIp, ipProto, dstTpPort, action);
+ }
+
+ }
+
+ /**
+ * Binds an id generator for unique ACL rule id generation.
+ * <p>
+ * Note: A generator cannot be bound if there is already a generator bound.
+ *
+ * @param newIdGenerator id generator
+ */
+ public static void bindIdGenerator(IdGenerator newIdGenerator) {
+ checkState(idGenerator == null, "Id generator is already bound.");
+ idGenerator = checkNotNull(newIdGenerator);
+ }
+
+ public RuleId id() {
+ return id;
+ }
+
+ public Ip4Prefix srcIp() {
+ return srcIp;
+ }
+
+ public Ip4Prefix dstIp() {
+ return this.dstIp;
+ }
+
+ public byte ipProto() {
+ return ipProto;
+ }
+
+ public short dstTpPort() {
+ return dstTpPort;
+ }
+
+ public Action action() {
+ return action;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(action, id.fingerprint(), ipProto, srcIp, dstIp, dstTpPort);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof AclRule) {
+ AclRule that = (AclRule) obj;
+ return Objects.equals(id, that.id) &&
+ Objects.equals(srcIp, that.srcIp) &&
+ Objects.equals(dstIp, that.dstIp) &&
+ Objects.equals(ipProto, that.ipProto) &&
+ Objects.equals(dstTpPort, that.dstTpPort) &&
+ Objects.equals(action, that.action);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .omitNullValues()
+ .add("id", id)
+ .add("srcIp", srcIp)
+ .add("dstIp", dstIp)
+ .add("ipProto", ipProto)
+ .add("dstTpPort", dstTpPort)
+ .add("action", action)
+ .toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java
new file mode 100644
index 00000000..487a6761
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import java.util.List;
+
+/**
+ * Service interface exported by ACL application.
+ */
+public interface AclService {
+
+ /**
+ * Gets a list containing all ACL rules.
+ *
+ * @return a list containing all ACL rules
+ */
+ List<AclRule> getAclRules();
+
+ /**
+ * Adds a new ACL rule.
+ *
+ * @param rule ACL rule
+ * @return true if successfully added, otherwise false
+ */
+ boolean addAclRule(AclRule rule);
+
+ /**
+ * Removes an exsiting ACL rule by rule id.
+ *
+ * @param ruleId ACL rule identifier
+ */
+ void removeAclRule(RuleId ruleId);
+
+ /**
+ * Clears ACL and resets all.
+ */
+ void clearAcl();
+
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java
new file mode 100644
index 00000000..ff9e25f6
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.store.Store;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Service interface exported by ACL distributed store.
+ */
+public interface AclStore extends Store {
+
+ /**
+ * Gets a list containing all ACL rules.
+ *
+ * @return a list containing all ACL rules
+ */
+ List<AclRule> getAclRules();
+
+ /**
+ * Adds a new ACL rule.
+ *
+ * @param rule new ACL rule
+ */
+ void addAclRule(AclRule rule);
+
+ /**
+ * Gets an existing ACL rule.
+ *
+ * @param ruleId ACL rule id
+ * @return ACL rule with the given id
+ */
+ AclRule getAclRule(RuleId ruleId);
+
+ /**
+ * Removes an existing ACL rule by rule id.
+ *
+ * @param ruleId ACL rule id
+ */
+ void removeAclRule(RuleId ruleId);
+
+ /**
+ * Clears ACL and reset all.
+ */
+ void clearAcl();
+
+ /**
+ * Gets the current priority for new ACL flow rule by device id.
+ *
+ * @param deviceId device id
+ * @return new ACL flow rule's priority in the given device
+ */
+ int getPriorityByDevice(DeviceId deviceId);
+
+ /**
+ * Gets a set containing all ACL flow rules belonging to a given ACL rule.
+ *
+ * @param ruleId ACL rule id
+ * @return a set containing all ACL flow rules belonging to the given ACL rule
+ */
+ Set<FlowRule> getFlowByRule(RuleId ruleId);
+
+ /**
+ * Adds a new mapping from ACL rule to ACL flow rule.
+ *
+ * @param ruleId ACL rule id
+ * @param flowRule ACL flow rule
+ */
+ void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule);
+
+ /**
+ * Removes an existing mapping from ACL rule to ACL flow rule.
+ *
+ * @param ruleId ACL rule id
+ */
+ void removeRuleToFlowMapping(RuleId ruleId);
+
+ /**
+ * Gets a list containing all allowing ACL rules matching a given denying ACL rule.
+ *
+ * @param denyingRuleId denying ACL rule id
+ * @return a list containing all allowing ACL rules matching the given denying ACL rule
+ */
+ List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId);
+
+ /**
+ * Adds a new mapping from denying ACL rule to allowing ACL rule.
+ *
+ * @param denyingRuleId denying ACL rule id
+ * @param allowingRuleId allowing ACL rule id
+ */
+ void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId);
+
+ /**
+ * Removes an exsiting mapping from denying ACL rule to allowing ACL rule.
+ *
+ * @param denyingRuleId denying ACL rule id
+ */
+ void removeDenyToAllowMapping(RuleId denyingRuleId);
+
+ /**
+ * Checks if an existing ACL rule already works in a given device.
+ *
+ * @param ruleId ACL rule id
+ * @param deviceId devide id
+ * @return true if the given ACL rule works in the given device
+ */
+ boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId);
+
+ /**
+ * Adds a new mapping from ACL rule to device.
+ *
+ * @param ruleId ACL rule id
+ * @param deviceId device id
+ */
+ void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId);
+
+ /**
+ * Removes an existing mapping from ACL rule to device.
+ *
+ * @param ruleId ACL rule id
+ */
+ void removeRuleToDeviceMapping(RuleId ruleId);
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java
new file mode 100644
index 00000000..e792efba
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Prefix;
+import org.onosproject.rest.AbstractWebResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+/**
+ * Manage ACL rules.
+ */
+@Path("rules")
+public class AclWebResource extends AbstractWebResource {
+
+ /**
+ * Get all ACL rules.
+ * Returns array of all ACL rules.
+ *
+ * @return 200 OK
+ */
+ @GET
+ public Response queryAclRule() {
+ List<AclRule> rules = get(AclService.class).getAclRules();
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode root = mapper.createObjectNode();
+ ArrayNode arrayNode = mapper.createArrayNode();
+ for (AclRule rule : rules) {
+ ObjectNode node = mapper.createObjectNode();
+ node.put("id", rule.id().toString());
+ if (rule.srcIp() != null) {
+ node.put("srcIp", rule.srcIp().toString());
+ }
+ if (rule.dstIp() != null) {
+ node.put("dstIp", rule.dstIp().toString());
+ }
+ if (rule.ipProto() != 0) {
+ switch (rule.ipProto()) {
+ case IPv4.PROTOCOL_ICMP:
+ node.put("ipProto", "ICMP");
+ break;
+ case IPv4.PROTOCOL_TCP:
+ node.put("ipProto", "TCP");
+ break;
+ case IPv4.PROTOCOL_UDP:
+ node.put("ipProto", "UDP");
+ break;
+ default:
+ break;
+ }
+ }
+ if (rule.dstTpPort() != 0) {
+ node.put("dstTpPort", rule.dstTpPort());
+ }
+ node.put("action", rule.action().toString());
+ arrayNode.add(node);
+ }
+ root.set("aclRules", arrayNode);
+ return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build();
+ }
+
+ /**
+ * Add a new ACL rule.
+ *
+ * @param stream JSON data describing the rule
+ * @return 200 OK
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response addAclRule(InputStream stream) throws URISyntaxException {
+ AclRule newRule = jsonToRule(stream);
+ return get(AclService.class).addAclRule(newRule) ?
+ Response.created(new URI(newRule.id().toString())).build() :
+ Response.serverError().build();
+ }
+
+ /**
+ * Remove ACL rule.
+ *
+ * @param id ACL rule id (in hex string format)
+ * @return 200 OK
+ */
+ @DELETE
+ @Path("{id}")
+ public Response removeAclRule(@PathParam("id") String id) {
+ RuleId ruleId = new RuleId(Long.parseLong(id.substring(2), 16));
+ get(AclService.class).removeAclRule(ruleId);
+ return Response.ok().build();
+ }
+
+ /**
+ * Remove all ACL rules.
+ *
+ * @return 200 OK
+ */
+ @DELETE
+ public Response clearACL() {
+ get(AclService.class).clearAcl();
+ return Response.ok().build();
+ }
+
+ /**
+ * Turns a JSON string into an ACL rule instance.
+ */
+ private AclRule jsonToRule(InputStream stream) {
+ JsonNode node;
+ try {
+ node = mapper().readTree(stream);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Unable to parse ACL request", e);
+ }
+
+ AclRule.Builder rule = AclRule.builder();
+
+ String s = node.path("srcIp").asText(null);
+ if (s != null) {
+ rule.srcIp(Ip4Prefix.valueOf(s));
+ }
+
+ s = node.path("dstIp").asText(null);
+ if (s != null) {
+ rule.dstIp(Ip4Prefix.valueOf(s));
+ }
+
+ s = node.path("ipProto").asText(null);
+ if (s != null) {
+ if ("TCP".equalsIgnoreCase(s)) {
+ rule.ipProto(IPv4.PROTOCOL_TCP);
+ } else if ("UDP".equalsIgnoreCase(s)) {
+ rule.ipProto(IPv4.PROTOCOL_UDP);
+ } else if ("ICMP".equalsIgnoreCase(s)) {
+ rule.ipProto(IPv4.PROTOCOL_ICMP);
+ } else {
+ throw new IllegalArgumentException("ipProto must be assigned to TCP, UDP, or ICMP");
+ }
+ }
+
+ int port = node.path("dstTpPort").asInt(0);
+ if (port > 0) {
+ rule.dstTpPort((short) port);
+ }
+
+ s = node.path("action").asText(null);
+ if (s != null) {
+ if ("allow".equalsIgnoreCase(s)) {
+ rule.action(AclRule.Action.ALLOW);
+ } else if ("deny".equalsIgnoreCase(s)) {
+ rule.action(AclRule.Action.DENY);
+ } else {
+ throw new IllegalArgumentException("action must be ALLOW or DENY");
+ }
+ }
+
+ return rule.build();
+ }
+
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java
new file mode 100644
index 00000000..468dab5c
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li and Heng Qi
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+/**
+ * ACL rule identifier suitable as an external key.
+ * <p>This class is immutable.</p>
+ */
+public final class RuleId {
+ private final long value;
+
+ /**
+ * Creates an ACL rule identifier from the specified long value.
+ *
+ * @param value long value
+ * @return ACL rule identifier
+ */
+ public static RuleId valueOf(long value) {
+ return new RuleId(value);
+ }
+
+ /**
+ * Constructor for serializer.
+ */
+ RuleId() {
+ this.value = 0;
+ }
+
+ /**
+ * Constructs the ID corresponding to a given long value.
+ *
+ * @param value the underlying value of this ID
+ */
+ RuleId(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the backing value.
+ *
+ * @return the value
+ */
+ public long fingerprint() {
+ return value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(value);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof RuleId)) {
+ return false;
+ }
+ RuleId that = (RuleId) obj;
+ return this.value == that.value;
+ }
+
+ @Override
+ public String toString() {
+ return "0x" + Long.toHexString(value);
+ }
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
new file mode 100644
index 00000000..f5c0c204
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl.impl;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.acl.AclRule;
+import org.onosproject.acl.AclService;
+import org.onosproject.acl.AclStore;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.acl.RuleId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the ACL service.
+ */
+@Component(immediate = true)
+@Service
+public class AclManager implements AclService {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected AclStore aclStore;
+
+ private final Logger log = getLogger(getClass());
+ private ApplicationId appId;
+ private final HostListener hostListener = new InternalHostListener();
+ private IdGenerator idGenerator;
+
+ /**
+ * Checks if the given IP address is in the given CIDR address.
+ */
+ private boolean checkIpInCIDR(Ip4Address ip, Ip4Prefix cidr) {
+ int offset = 32 - cidr.prefixLength();
+ int cidrPrefix = cidr.address().toInt();
+ int ipIntValue = ip.toInt();
+ cidrPrefix = cidrPrefix >> offset;
+ ipIntValue = ipIntValue >> offset;
+ cidrPrefix = cidrPrefix << offset;
+ ipIntValue = ipIntValue << offset;
+
+ return (cidrPrefix == ipIntValue);
+ }
+
+ private class InternalHostListener implements HostListener {
+
+ /**
+ * Generate new ACL flow rules for new host following the given ACL rule.
+ */
+ private void processHostAddedEvent(HostEvent event, AclRule rule) {
+ DeviceId deviceId = event.subject().location().deviceId();
+ for (IpAddress address : event.subject().ipAddresses()) {
+ if ((rule.srcIp() != null) ?
+ (checkIpInCIDR(address.getIp4Address(), rule.srcIp())) :
+ (checkIpInCIDR(address.getIp4Address(), rule.dstIp()))) {
+ if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
+ List<RuleId> allowingRuleList = aclStore
+ .getAllowingRuleByDenyingRule(rule.id());
+ if (allowingRuleList != null) {
+ for (RuleId allowingRuleId : allowingRuleList) {
+ generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
+ }
+ }
+ generateACLFlow(rule, deviceId);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void event(HostEvent event) {
+ // if a new host appears and an existing rule denies
+ // its traffic, a new ACL flow rule is generated.
+ if (event.type() == HostEvent.Type.HOST_ADDED) {
+ DeviceId deviceId = event.subject().location().deviceId();
+ if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
+ for (AclRule rule : aclStore.getAclRules()) {
+ if (rule.action() != AclRule.Action.ALLOW) {
+ processHostAddedEvent(event, rule);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onos.acl");
+ hostService.addListener(hostListener);
+ idGenerator = coreService.getIdGenerator("acl-ids");
+ AclRule.bindIdGenerator(idGenerator);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ hostService.removeListener(hostListener);
+ flowRuleService.removeFlowRulesById(appId);
+ aclStore.clearAcl();
+ log.info("Stopped");
+ }
+
+ @Override
+ public List<AclRule> getAclRules() {
+ return aclStore.getAclRules();
+ }
+
+ /**
+ * Checks if the new ACL rule matches an existing rule.
+ * If existing allowing rules matches the new denying rule, store the mappings.
+ *
+ * @return true if the new ACL rule matches an existing rule, false otherwise
+ */
+ private boolean matchCheck(AclRule newRule) {
+ for (AclRule existingRule : aclStore.getAclRules()) {
+ if (newRule.checkMatch(existingRule)) {
+ return true;
+ }
+
+ if (existingRule.action() == AclRule.Action.ALLOW
+ && newRule.action() == AclRule.Action.DENY) {
+ if (existingRule.checkMatch(newRule)) {
+ aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addAclRule(AclRule rule) {
+ if (matchCheck(rule)) {
+ return false;
+ }
+ aclStore.addAclRule(rule);
+ log.info("ACL rule(id:{}) is added.", rule.id());
+ if (rule.action() != AclRule.Action.ALLOW) {
+ enforceRuleAdding(rule);
+ }
+ return true;
+ }
+
+ /**
+ * Gets a set containing all devices connecting with the hosts
+ * whose IP address is in the given CIDR IP address.
+ */
+ private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
+ Set<DeviceId> deviceIdSet = new HashSet<>();
+ final Iterable<Host> hosts = hostService.getHosts();
+
+ if (cidrAddr.prefixLength() != 32) {
+ for (Host h : hosts) {
+ for (IpAddress a : h.ipAddresses()) {
+ if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
+ deviceIdSet.add(h.location().deviceId());
+ }
+ }
+ }
+ } else {
+ for (Host h : hosts) {
+ for (IpAddress a : h.ipAddresses()) {
+ if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
+ deviceIdSet.add(h.location().deviceId());
+ return deviceIdSet;
+ }
+ }
+ }
+ }
+ return deviceIdSet;
+ }
+
+ /**
+ * Enforces denying ACL rule by ACL flow rules.
+ */
+ private void enforceRuleAdding(AclRule rule) {
+ Set<DeviceId> dpidSet;
+ if (rule.srcIp() != null) {
+ dpidSet = getDeviceIdSet(rule.srcIp());
+ } else {
+ dpidSet = getDeviceIdSet(rule.dstIp());
+ }
+
+ for (DeviceId deviceId : dpidSet) {
+ List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
+ if (allowingRuleList != null) {
+ for (RuleId allowingRuleId : allowingRuleList) {
+ generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
+ }
+ }
+ generateACLFlow(rule, deviceId);
+ }
+ }
+
+ /**
+ * Generates ACL flow rule according to ACL rule
+ * and install it into related device.
+ */
+ private void generateACLFlow(AclRule rule, DeviceId deviceId) {
+ if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
+ return;
+ }
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
+
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
+ if (rule.srcIp() != null) {
+ selectorBuilder.matchIPSrc(rule.srcIp());
+ if (rule.dstIp() != null) {
+ selectorBuilder.matchIPDst(rule.dstIp());
+ }
+ } else {
+ selectorBuilder.matchIPDst(rule.dstIp());
+ }
+ if (rule.ipProto() != 0) {
+ selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
+ }
+ if (rule.dstTpPort() != 0) {
+ switch (rule.ipProto()) {
+ case IPv4.PROTOCOL_TCP:
+ selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
+ break;
+ case IPv4.PROTOCOL_UDP:
+ selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
+ break;
+ default:
+ break;
+ }
+ }
+ if (rule.action() == AclRule.Action.ALLOW) {
+ treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
+ }
+ flowEntry.forDevice(deviceId);
+ flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
+ flowEntry.withSelector(selectorBuilder.build());
+ flowEntry.withTreatment(treatment.build());
+ flowEntry.fromApp(appId);
+ flowEntry.makePermanent();
+ // install flow rule
+ flowRuleService.applyFlowRules(flowEntry.build());
+ log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
+ aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
+ aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
+ }
+
+ @Override
+ public void removeAclRule(RuleId ruleId) {
+ aclStore.removeAclRule(ruleId);
+ log.info("ACL rule(id:{}) is removed.", ruleId);
+ enforceRuleRemoving(ruleId);
+ }
+
+ /**
+ * Enforces removing an existing ACL rule.
+ */
+ private void enforceRuleRemoving(RuleId ruleId) {
+ Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
+ if (flowSet != null) {
+ for (FlowRule flowRule : flowSet) {
+ flowRuleService.removeFlowRules(flowRule);
+ log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
+ }
+ }
+ aclStore.removeRuleToFlowMapping(ruleId);
+ aclStore.removeRuleToDeviceMapping(ruleId);
+ aclStore.removeDenyToAllowMapping(ruleId);
+ }
+
+ @Override
+ public void clearAcl() {
+ aclStore.clearAcl();
+ flowRuleService.removeFlowRulesById(appId);
+ log.info("ACL is cleared.");
+ }
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java
new file mode 100644
index 00000000..a5fcfcc7
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl.impl;
+
+import com.google.common.collect.Collections2;
+import org.onosproject.acl.AclRule;
+import org.onosproject.acl.AclStore;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.acl.RuleId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the ACL store service.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedAclStore extends AbstractStore implements AclStore {
+
+ private final Logger log = getLogger(getClass());
+ private final int defaultFlowMaxPriority = 30000;
+
+ private ConsistentMap<RuleId, AclRule> ruleSet;
+ private ConsistentMap<DeviceId, Integer> deviceToPriority;
+ private ConsistentMap<RuleId, Set<DeviceId>> ruleToDevice;
+ private ConsistentMap<RuleId, Set<FlowRule>> ruleToFlow;
+ private ConsistentMap<RuleId, List<RuleId>> denyRuleToAllowRule;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Activate
+ public void activate() {
+ ApplicationId appId = coreService.getAppId("org.onosproject.acl");
+
+ KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(AclRule.class)
+ .register(AclRule.Action.class)
+ .register(RuleId.class);
+
+ ruleSet = storageService.<RuleId, AclRule>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("acl-rule-set")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ deviceToPriority = storageService.<DeviceId, Integer>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("device-to-priority")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ ruleToFlow = storageService.<RuleId, Set<FlowRule>>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("rule-to-flow")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ denyRuleToAllowRule = storageService.<RuleId, List<RuleId>>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("deny-to-allow")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ ruleToDevice = storageService.<RuleId, Set<DeviceId>>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("rule-to-device")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactive() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public List<AclRule> getAclRules() {
+ List<AclRule> aclRules = new ArrayList<>();
+ aclRules.addAll(Collections2.transform(ruleSet.values(), Versioned::value));
+ return aclRules;
+ }
+
+ @Override
+ public void addAclRule(AclRule rule) {
+ ruleSet.putIfAbsent(rule.id(), rule);
+ }
+
+ @Override
+ public AclRule getAclRule(RuleId ruleId) {
+ Versioned<AclRule> rule = ruleSet.get(ruleId);
+ if (rule != null) {
+ return rule.value();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void removeAclRule(RuleId ruleId) {
+ ruleSet.remove(ruleId);
+ }
+
+ @Override
+ public void clearAcl() {
+ ruleSet.clear();
+ deviceToPriority.clear();
+ ruleToFlow.clear();
+ denyRuleToAllowRule.clear();
+ ruleToDevice.clear();
+ }
+
+ @Override
+ public int getPriorityByDevice(DeviceId deviceId) {
+ return deviceToPriority.compute(deviceId,
+ (id, priority) -> (priority == null) ? defaultFlowMaxPriority : (priority - 1))
+ .value();
+ }
+
+ @Override
+ public Set<FlowRule> getFlowByRule(RuleId ruleId) {
+ Versioned<Set<FlowRule>> flowRuleSet = ruleToFlow.get(ruleId);
+ if (flowRuleSet != null) {
+ return flowRuleSet.value();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule) {
+ ruleToFlow.computeIf(ruleId,
+ flowRuleSet -> (flowRuleSet == null || !flowRuleSet.contains(flowRule)),
+ (id, flowRuleSet) -> {
+ Set<FlowRule> newSet = new HashSet<>();
+ if (flowRuleSet != null) {
+ newSet.addAll(flowRuleSet);
+ }
+ newSet.add(flowRule);
+ return newSet;
+ });
+ }
+
+ @Override
+ public void removeRuleToFlowMapping(RuleId ruleId) {
+ ruleToFlow.remove(ruleId);
+ }
+
+ @Override
+ public List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId) {
+ Versioned<List<RuleId>> allowRuleIdSet = denyRuleToAllowRule.get(denyingRuleId);
+ if (allowRuleIdSet != null) {
+ return allowRuleIdSet.value();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId) {
+ denyRuleToAllowRule.computeIf(denyingRuleId,
+ ruleIdList -> (ruleIdList == null || !ruleIdList.contains(allowingRuleId)),
+ (id, ruleIdList) -> {
+ ArrayList<RuleId> newList = new ArrayList<>();
+ if (ruleIdList != null) {
+ newList.addAll(ruleIdList);
+ }
+ newList.add(allowingRuleId);
+ return newList;
+ });
+ }
+
+ @Override
+ public void removeDenyToAllowMapping(RuleId denyingRuleId) {
+ denyRuleToAllowRule.remove(denyingRuleId);
+ }
+
+ @Override
+ public boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId) {
+ return ruleToDevice.containsKey(ruleId) && ruleToDevice.get(ruleId).value().contains(deviceId);
+ }
+
+ @Override
+ public void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId) {
+ ruleToDevice.computeIf(ruleId,
+ deviceIdSet -> (deviceIdSet == null || !deviceIdSet.contains(deviceId)),
+ (id, deviceIdSet) -> {
+ Set<DeviceId> newSet = new HashSet<>();
+ if (deviceIdSet != null) {
+ newSet.addAll(deviceIdSet);
+ }
+ newSet.add(deviceId);
+ return newSet;
+ });
+ }
+
+ @Override
+ public void removeRuleToDeviceMapping(RuleId ruleId) {
+ ruleToDevice.remove(ruleId);
+ }
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java
new file mode 100644
index 00000000..9da9b3b7
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ACL application implementation.
+ */
+package org.onosproject.acl.impl;
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java
new file mode 100644
index 00000000..67f755c6
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ACL application.
+ */
+package org.onosproject.acl;
diff --git a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml
index 2c2d5cf3..fc188b7f 100644
--- a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml
+++ b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml
@@ -33,7 +33,7 @@
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.classnames</param-name>
- <param-value>org.onos.acl.AclWebResource</param-value>
+ <param-value>org.onosproject.acl.AclWebResource</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
diff --git a/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java
new file mode 100644
index 00000000..c554db6e
--- /dev/null
+++ b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li and Heng Qi
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.acl;
+
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.rest.ResourceTest;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test class for ACL application REST resource.
+ */
+public class AclWebResourceTest extends ResourceTest {
+
+ final AclService mockAclService = createMock(AclService.class);
+ final AclStore mockAclStore = createMock(AclStore.class);
+ final List<AclRule> rules = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ expect(mockAclService.getAclRules()).andReturn(rules).anyTimes();
+ ServiceDirectory testDirectory = new TestServiceDirectory().add(AclService.class, mockAclService)
+ .add(AclStore.class, mockAclStore);
+ BaseResource.setServiceDirectory(testDirectory);
+
+ IdGenerator idGenerator = new MockIdGenerator();
+ AclRule.bindIdGenerator(idGenerator);
+ }
+
+ @After
+ public void tearDown() {
+ verify(mockAclService);
+ }
+
+ /**
+ * Mock id generator for testing.
+ */
+ private class MockIdGenerator implements IdGenerator {
+ private AtomicLong nextId = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return nextId.getAndIncrement();
+ }
+ }
+
+ @Override
+ public AppDescriptor configure() {
+ return new WebAppDescriptor.Builder("org.onosproject.acl").build();
+ }
+
+ @Test
+ @Ignore("FIXME: This needs to get reworked")
+ public void addRule() throws IOException {
+ WebResource.Builder rs = resource().path("rules").header("Content-type", "application/json");
+ String response;
+ String json;
+
+ replay(mockAclService);
+
+ // input a invalid JSON string that contains neither nw_src and nw_dst
+ json = "{\"ipProto\":\"TCP\",\"dstTpPort\":\"80\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("Failed! Either srcIp or dstIp must be assigned."));
+
+ // input a invalid JSON string that doesn't contain CIDR mask bits
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("Malformed IPv4 prefix string: 10.0.0.1. " +
+ "Address must take form \"x.x.x.x/y\""));
+
+ // input a invalid JSON string that contains a invalid IP address
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.256/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("Invalid IP address string: 10.0.0.256"));
+
+ // input a invalid JSON string that contains a invalid IP address
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.01/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("Invalid IP address string: 10.0.01"));
+
+ // input a invalid JSON string that contains a invalid CIDR mask bits
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/a\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("Failed! For input string: \"a\""));
+
+ // input a invalid JSON string that contains a invalid CIDR mask bits
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/33\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("Invalid prefix length 33. The value must be in the interval [0, 32]"));
+
+ // input a invalid JSON string that contains a invalid ipProto value
+ json = "{\"ipProto\":\"ARP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("ipProto must be assigned to TCP, UDP, or ICMP."));
+
+ // input a invalid JSON string that contains a invalid dstTpPort value
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"a\",\"action\":\"DENY\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("dstTpPort must be assigned to a numerical value."));
+
+ // input a invalid JSON string that contains a invalid action value
+ json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"PERMIT\"}";
+ response = rs.post(String.class, json);
+ assertThat(response, containsString("action must be assigned to ALLOW or DENY."));
+ }
+}
diff --git a/framework/src/onos/apps/bgprouter/pom.xml b/framework/src/onos/apps/bgprouter/pom.xml
index decdf5c2..6503ee79 100644
--- a/framework/src/onos/apps/bgprouter/pom.xml
+++ b/framework/src/onos/apps/bgprouter/pom.xml
@@ -24,7 +24,6 @@
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
-
<artifactId>onos-app-bgprouter</artifactId>
<packaging>bundle</packaging>
diff --git a/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java b/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
index 88265350..6130a2e2 100644
--- a/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
+++ b/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
@@ -101,6 +101,7 @@ public class IcmpHandler {
icmpReplyIpv4.setChecksum((short) 0);
ICMP icmpReply = new ICMP();
+ icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload());
icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
icmpReply.setChecksum((short) 0);
diff --git a/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java b/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java
index 1523e9c2..fa916865 100644
--- a/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java
+++ b/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java
@@ -86,7 +86,7 @@ public class CordFabricManager implements FabricService {
private short radiusPort = 1812;
- private short ofPort = 6633;
+ private short ofPort = 6653;
private DeviceId fabricDeviceId = DeviceId.deviceId("of:5e3e486e73000187");
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
index 072254de..cb8acab2 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -21,24 +21,11 @@ import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.TpPort;
import org.onlab.util.KryoNamespace;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.LeadershipEvent;
-import org.onosproject.cluster.LeadershipEventListener;
-import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
@@ -57,11 +44,15 @@ import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.cordvtn.OvsdbNode.State;
import static org.onosproject.cordvtn.OvsdbNode.State.INIT;
+import static org.onosproject.cordvtn.OvsdbNode.State.DISCONNECT;
+import static org.onosproject.net.Device.Type.SWITCH;
import static org.slf4j.LoggerFactory.getLogger;
/**
- * CORD VTN Application that provisions overlay virtual tenant networks.
+ * Provides initial setup or cleanup for provisioning virtual tenant networks
+ * on ovsdb, integration bridge and vm when they are added or deleted.
*/
@Component(immediate = true)
@Service
@@ -69,6 +60,11 @@ public class CordVtn implements CordVtnService {
protected final Logger log = getLogger(getClass());
+ private static final int NUM_THREADS = 1;
+ private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(OvsdbNode.class);
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -79,112 +75,81 @@ public class CordVtn implements CordVtnService {
protected LogicalClockService clockService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClusterService clusterService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected LeadershipService leadershipService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigService configService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigRegistry configRegistry;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected MastershipService mastershipService;
-
- private static final int DEFAULT_NUM_THREADS = 1;
- private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
- .register(KryoNamespaces.API)
- .register(OvsdbNode.class);
+ private final ExecutorService eventExecutor = Executors
+ .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
- private final ExecutorService eventExecutor = Executors.newFixedThreadPool(
- DEFAULT_NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
-
- private final LeadershipEventListener leadershipListener = new InternalLeadershipListener();
private final DeviceListener deviceListener = new InternalDeviceListener();
private final HostListener hostListener = new InternalHostListener();
- private final NodeHandler nodeHandler = new NodeHandler();
+
+ private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
private final BridgeHandler bridgeHandler = new BridgeHandler();
- private final VirtualMachineHandler vmHandler = new VirtualMachineHandler();
-
- private final ConfigFactory configFactory =
- new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
- @Override
- public CordVtnConfig createConfig() {
- return new CordVtnConfig();
- }
- };
-
- private ApplicationId appId;
- private NodeId local;
+ private final VmHandler vmHandler = new VmHandler();
+
private EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore;
- private NodeConnectionManager nodeConnectionManager;
@Activate
protected void activate() {
- appId = coreService.registerApplication("org.onosproject.cordvtn");
-
- local = clusterService.getLocalNode().id();
+ coreService.registerApplication("org.onosproject.cordvtn");
nodeStore = storageService.<DeviceId, OvsdbNode>eventuallyConsistentMapBuilder()
.withName("cordvtn-nodestore")
.withSerializer(NODE_SERIALIZER)
.withTimestampProvider((k, v) -> clockService.getTimestamp())
.build();
- configRegistry.registerConfigFactory(configFactory);
deviceService.addListener(deviceListener);
hostService.addListener(hostListener);
- leadershipService.addListener(leadershipListener);
- leadershipService.runForLeadership(appId.name());
- nodeConnectionManager = new NodeConnectionManager(appId, local, nodeStore,
- mastershipService, leadershipService);
- nodeConnectionManager.start();
+
log.info("Started");
}
@Deactivate
protected void deactivate() {
- nodeConnectionManager.stop();
- leadershipService.removeListener(leadershipListener);
- leadershipService.withdraw(appId.name());
deviceService.removeListener(deviceListener);
hostService.removeListener(hostListener);
+
eventExecutor.shutdown();
nodeStore.destroy();
- configRegistry.unregisterConfigFactory(configFactory);
+
log.info("Stopped");
}
@Override
- public void addNode(String hostname, IpAddress ip, TpPort port) {
- DefaultOvsdbNode node = new DefaultOvsdbNode(hostname, ip, port, DeviceId.NONE, INIT);
-
- if (nodeStore.containsKey(node.deviceId())) {
- log.warn("Node {} with ovsdb-server {}:{} already exists", hostname, ip, port);
+ public void addNode(OvsdbNode ovsdbNode) {
+ if (nodeStore.containsKey(ovsdbNode.deviceId())) {
+ log.warn("Node {} already exists", ovsdbNode.host());
return;
}
- nodeStore.put(node.deviceId(), node);
- log.info("New node {} with ovsdb-server {}:{} has been added", hostname, ip, port);
+ nodeStore.put(ovsdbNode.deviceId(), ovsdbNode);
+ if (ovsdbNode.state() != INIT) {
+ updateNode(ovsdbNode, INIT);
+ }
}
@Override
- public void deleteNode(IpAddress ip, TpPort port) {
- DeviceId deviceId = DeviceId.deviceId("ovsdb:" + ip + ":" + port);
- OvsdbNode node = nodeStore.get(deviceId);
+ public void deleteNode(OvsdbNode ovsdbNode) {
+ if (!nodeStore.containsKey(ovsdbNode.deviceId())) {
+ log.warn("Node {} does not exist", ovsdbNode.host());
+ return;
+ }
+ updateNode(ovsdbNode, DISCONNECT);
+ }
- if (node == null) {
- log.warn("Node with ovsdb-server on {}:{} does not exist", ip, port);
+ @Override
+ public void updateNode(OvsdbNode ovsdbNode, State state) {
+ if (!nodeStore.containsKey(ovsdbNode.deviceId())) {
+ log.warn("Node {} does not exist", ovsdbNode.host());
return;
}
- nodeConnectionManager.disconnectNode(node);
- nodeStore.remove(node.deviceId());
+ DefaultOvsdbNode updatedNode = new DefaultOvsdbNode(ovsdbNode.host(),
+ ovsdbNode.ip(),
+ ovsdbNode.port(),
+ state);
+ nodeStore.put(ovsdbNode.deviceId(), updatedNode);
}
@Override
@@ -193,58 +158,33 @@ public class CordVtn implements CordVtnService {
}
@Override
+ public OvsdbNode getNode(DeviceId deviceId) {
+ return nodeStore.get(deviceId);
+ }
+
+ @Override
public List<OvsdbNode> getNodes() {
return nodeStore.values()
.stream()
.collect(Collectors.toList());
}
- private void initialSetup() {
- // Read ovsdb nodes from network config
- CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
- if (config == null) {
- log.warn("No configuration found");
- return;
- }
- config.ovsdbNodes().forEach(
- node -> addNode(node.hostname(), node.ip(), node.port()));
- }
-
- private synchronized void processLeadershipChange(NodeId leader) {
- // Only the leader performs the initial setup
- if (leader == null || !leader.equals(local)) {
- return;
- }
- initialSetup();
- }
-
- private class InternalLeadershipListener implements LeadershipEventListener {
-
- @Override
- public void event(LeadershipEvent event) {
- if (event.subject().topic().equals(appId.name())) {
- processLeadershipChange(event.subject().leader());
- }
- }
- }
-
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
Device device = event.subject();
- ConnectionHandler handler =
- (device.type() == Device.Type.CONTROLLER ? nodeHandler : bridgeHandler);
+ ConnectionHandler handler = (device.type() == SWITCH ? bridgeHandler : ovsdbHandler);
switch (event.type()) {
- case DEVICE_ADDED:
- eventExecutor.submit(() -> handler.connected(device));
- break;
- case DEVICE_AVAILABILITY_CHANGED:
- eventExecutor.submit(() -> handler.disconnected(device));
- break;
- default:
- break;
+ case DEVICE_ADDED:
+ eventExecutor.submit(() -> handler.connected(device));
+ break;
+ case DEVICE_AVAILABILITY_CHANGED:
+ eventExecutor.submit(() -> handler.disconnected(device));
+ break;
+ default:
+ break;
}
}
}
@@ -268,7 +208,7 @@ public class CordVtn implements CordVtnService {
}
}
- private class NodeHandler implements ConnectionHandler<Device> {
+ private class OvsdbHandler implements ConnectionHandler<Device> {
@Override
public void connected(Device device) {
@@ -296,7 +236,7 @@ public class CordVtn implements CordVtnService {
}
}
- private class VirtualMachineHandler implements ConnectionHandler<Host> {
+ private class VmHandler implements ConnectionHandler<Host> {
@Override
public void connected(Host host) {
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
index c2c37aba..fdaf752a 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
@@ -27,12 +27,12 @@ import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
- * Configuration object for CORD VTN service.
+ * Configuration object for CordVtn service.
*/
public class CordVtnConfig extends Config<ApplicationId> {
public static final String OVSDB_NODES = "ovsdbNodes";
- public static final String HOSTNAME = "hostname";
+ public static final String HOST = "host";
public static final String IP = "ip";
public static final String PORT = "port";
@@ -49,7 +49,7 @@ public class CordVtnConfig extends Config<ApplicationId> {
return null;
}
nodes.forEach(jsonNode -> ovsdbNodes.add(new OvsdbNodeConfig(
- jsonNode.path(HOSTNAME).asText(),
+ jsonNode.path(HOST).asText(),
IpAddress.valueOf(jsonNode.path(IP).asText()),
TpPort.tpPort(jsonNode.path(PORT).asInt()))));
@@ -57,27 +57,27 @@ public class CordVtnConfig extends Config<ApplicationId> {
}
/**
- * Configuration for an OVSDB node.
+ * Configuration for an ovsdb node.
*/
public static class OvsdbNodeConfig {
- private final String hostname;
+ private final String host;
private final IpAddress ip;
private final TpPort port;
- public OvsdbNodeConfig(String hostname, IpAddress ip, TpPort port) {
- this.hostname = checkNotNull(hostname);
+ public OvsdbNodeConfig(String host, IpAddress ip, TpPort port) {
+ this.host = checkNotNull(host);
this.ip = checkNotNull(ip);
this.port = checkNotNull(port);
}
/**
- * Returns hostname of the node.
+ * Returns host information of the node.
*
- * @return hostname
+ * @return host
*/
- public String hostname() {
- return this.hostname;
+ public String host() {
+ return this.host;
}
/**
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
new file mode 100644
index 00000000..043b3760
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cordvtn;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.slf4j.Logger;
+
+import static org.onosproject.cordvtn.OvsdbNode.State.INIT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Reads node information from the network config file and handles the config
+ * update events.
+ * Only a leader controller performs the node addition or deletion.
+ */
+@Component(immediate = true)
+public class CordVtnConfigManager {
+
+ protected final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry configRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CordVtnService cordVtnService;
+
+ private final ConfigFactory configFactory =
+ new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
+ @Override
+ public CordVtnConfig createConfig() {
+ return new CordVtnConfig();
+ }
+ };
+
+ private final LeadershipEventListener leadershipListener = new InternalLeadershipListener();
+ private final NetworkConfigListener configListener = new InternalConfigListener();
+
+ private NodeId local;
+ private ApplicationId appId;
+
+ @Activate
+ protected void active() {
+ local = clusterService.getLocalNode().id();
+ appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
+
+ configService.addListener(configListener);
+ configRegistry.registerConfigFactory(configFactory);
+
+ leadershipService.addListener(leadershipListener);
+ leadershipService.runForLeadership(CordVtnService.CORDVTN_APP_ID);
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ leadershipService.removeListener(leadershipListener);
+ leadershipService.withdraw(appId.name());
+
+ configRegistry.unregisterConfigFactory(configFactory);
+ configService.removeListener(configListener);
+ }
+
+ private void readConfiguration() {
+ CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+
+ if (config == null) {
+ log.warn("No configuration found");
+ return;
+ }
+
+ config.ovsdbNodes().forEach(node -> {
+ DefaultOvsdbNode ovsdbNode =
+ new DefaultOvsdbNode(node.host(), node.ip(), node.port(), INIT);
+ cordVtnService.addNode(ovsdbNode);
+ log.info("Add new node {}", node.host());
+ });
+ }
+
+ private synchronized void processLeadershipChange(NodeId leader) {
+ if (leader == null || !leader.equals(local)) {
+ return;
+ }
+ readConfiguration();
+ }
+
+ private class InternalLeadershipListener implements LeadershipEventListener {
+
+ @Override
+ public void event(LeadershipEvent event) {
+ if (event.subject().topic().equals(appId.name())) {
+ processLeadershipChange(event.subject().leader());
+ }
+ }
+ }
+
+ private class InternalConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ // TODO handle update event
+ }
+ }
+}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
index d26a10aa..1f75dceb 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
@@ -15,8 +15,8 @@
*/
package org.onosproject.cordvtn;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.TpPort;
+import org.onosproject.cordvtn.OvsdbNode.State;
+import org.onosproject.net.DeviceId;
import java.util.List;
@@ -24,22 +24,30 @@ import java.util.List;
* Service for provisioning overlay virtual networks on compute nodes.
*/
public interface CordVtnService {
+
+ String CORDVTN_APP_ID = "org.onosproject.cordvtn";
/**
* Adds a new node to the service.
*
- * @param hostname hostname of the node
- * @param ip ip address to access the ovsdb server running on the node
- * @param port port number to access the ovsdb server running on the node
+ * @param ovsdbNode ovsdb node
+ */
+ void addNode(OvsdbNode ovsdbNode);
+
+ /**
+ * Deletes a node from the service.
+ *
+ * @param ovsdbNode ovsdb node
*/
- void addNode(String hostname, IpAddress ip, TpPort port);
+ void deleteNode(OvsdbNode ovsdbNode);
/**
- * Deletes the node from the service.
+ * Updates ovsdb node.
+ * It only used for updating node's connection state.
*
- * @param ip ip address to access the ovsdb server running on the node
- * @param port port number to access the ovsdb server running on the node
+ * @param ovsdbNode ovsdb node
+ * @param state ovsdb connection state
*/
- void deleteNode(IpAddress ip, TpPort port);
+ void updateNode(OvsdbNode ovsdbNode, State state);
/**
* Returns the number of the nodes known to the service.
@@ -49,6 +57,14 @@ public interface CordVtnService {
int getNodeCount();
/**
+ * Returns OvsdbNode with given device id.
+ *
+ * @param deviceId device id
+ * @return ovsdb node
+ */
+ OvsdbNode getNode(DeviceId deviceId);
+
+ /**
* Returns all nodes known to the service.
*
* @return list of nodes
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java
index b8cdbe94..ce8b0f1d 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java
@@ -15,6 +15,7 @@
*/
package org.onosproject.cordvtn;
+import com.google.common.base.MoreObjects;
import org.onlab.packet.IpAddress;
import org.onlab.packet.TpPort;
import org.onosproject.net.DeviceId;
@@ -26,21 +27,15 @@ import java.util.Objects;
*/
public class DefaultOvsdbNode implements OvsdbNode {
- private final String hostname;
+ private final String host;
private final IpAddress ip;
private final TpPort port;
- private final DeviceId deviceId;
- private final DeviceId bridgeId;
private final State state;
- public DefaultOvsdbNode(String hostname, IpAddress ip, TpPort port,
- DeviceId bridgeId, State state) {
- this.hostname = hostname;
+ public DefaultOvsdbNode(String host, IpAddress ip, TpPort port, State state) {
+ this.host = host;
this.ip = ip;
this.port = port;
- this.deviceId = DeviceId.deviceId(
- "ovsdb:" + ip.toString() + ":" + port.toString());
- this.bridgeId = bridgeId;
this.state = state;
}
@@ -55,8 +50,8 @@ public class DefaultOvsdbNode implements OvsdbNode {
}
@Override
- public String hostname() {
- return this.hostname;
+ public String host() {
+ return this.host;
}
@Override
@@ -66,12 +61,12 @@ public class DefaultOvsdbNode implements OvsdbNode {
@Override
public DeviceId deviceId() {
- return this.deviceId;
+ return DeviceId.deviceId("ovsdb:" + this.ip.toString() + ":" + this.port.toString());
}
@Override
- public DeviceId bridgeId() {
- return this.bridgeId;
+ public DeviceId intBrId() {
+ return DeviceId.deviceId("of:" + this.host);
}
@Override
@@ -82,8 +77,9 @@ public class DefaultOvsdbNode implements OvsdbNode {
if (o instanceof DefaultOvsdbNode) {
DefaultOvsdbNode that = (DefaultOvsdbNode) o;
- // We compare the ip and port only.
- if (this.ip.equals(that.ip) && this.port.equals(that.port)) {
+ if (this.host.equals(that.host) &&
+ this.ip.equals(that.ip) &&
+ this.port.equals(that.port)) {
return true;
}
}
@@ -92,6 +88,16 @@ public class DefaultOvsdbNode implements OvsdbNode {
@Override
public int hashCode() {
- return Objects.hash(ip, port);
+ return Objects.hash(host, ip, port);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("host", host)
+ .add("ip", ip)
+ .add("port", port)
+ .add("state", state)
+ .toString();
}
}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java
index 0b7029ef..ebba4cd5 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java
@@ -15,12 +15,19 @@
*/
package org.onosproject.cordvtn;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.LeadershipService;
import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
import org.slf4j.Logger;
import java.util.concurrent.Executors;
@@ -28,118 +35,131 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.cordvtn.OvsdbNode.State.CONNECTED;
+import static org.onosproject.cordvtn.OvsdbNode.State.DISCONNECTED;
+import static org.onosproject.cordvtn.OvsdbNode.State.READY;
import static org.slf4j.LoggerFactory.getLogger;
/**
- * Node connection manager.
+ * Provides the connection state management of all nodes registered to the service
+ * so that the nodes keep connected unless it is requested to be deleted.
*/
+@Component(immediate = true)
public class NodeConnectionManager {
protected final Logger log = getLogger(getClass());
- private final ApplicationId appId;
- private final NodeId localId;
- private final EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore;
- private final MastershipService mastershipService;
- private final LeadershipService leadershipService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ CordVtnService cordVtnService;
private static final int DELAY_SEC = 5;
- private ScheduledExecutorService connectionExecutor;
-
- /**
- * Creates a new NodeConnectionManager.
- *
- * @param localId local id
- * @param nodeStore node store
- * @param mastershipService mastership service
- */
- public NodeConnectionManager(ApplicationId appId, NodeId localId,
- EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore,
- MastershipService mastershipService,
- LeadershipService leadershipService) {
- this.appId = appId;
- this.localId = localId;
- this.nodeStore = nodeStore;
- this.mastershipService = mastershipService;
- this.leadershipService = leadershipService;
- }
- /**
- * Starts the node connection manager.
- */
- public void start() {
- connectionExecutor = Executors.newSingleThreadScheduledExecutor(
- groupedThreads("onos/cordvtn", "connection-executor"));
- connectionExecutor.scheduleWithFixedDelay(() -> nodeStore.values()
+ private final DeviceListener deviceListener = new InternalDeviceListener();
+ private final ScheduledExecutorService connectionExecutor = Executors
+ .newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "connection-manager"));
+
+ private NodeId localId;
+
+ @Activate
+ protected void activate() {
+ localId = clusterService.getLocalNode().id();
+ deviceService.addListener(deviceListener);
+
+ connectionExecutor.scheduleWithFixedDelay(() -> cordVtnService.getNodes()
.stream()
.filter(node -> localId.equals(getMaster(node)))
- .forEach(node -> connectNode(node)), 0, DELAY_SEC, TimeUnit.SECONDS);
+ .forEach(node -> {
+ connect(node);
+ disconnect(node);
+ }), 0, DELAY_SEC, TimeUnit.SECONDS);
}
- /**
- * Stops the node connection manager.
- */
+ @Deactivate
public void stop() {
connectionExecutor.shutdown();
+ deviceService.removeListener(deviceListener);
}
- /**
- * Adds a new node to the system.
- *
- * @param ovsdbNode ovsdb node
- */
- public void connectNode(OvsdbNode ovsdbNode) {
+ public void connect(OvsdbNode ovsdbNode) {
switch (ovsdbNode.state()) {
case INIT:
case DISCONNECTED:
- // TODO: set the node to passive mode
+ setPassiveMode(ovsdbNode);
case READY:
- // TODO: initiate connection
- break;
- case CONNECTED:
+ setupConnection(ovsdbNode);
break;
default:
+ break;
}
}
- /**
- * Deletes the ovsdb node.
- *
- * @param ovsdbNode ovsdb node
- */
- public void disconnectNode(OvsdbNode ovsdbNode) {
+ public void disconnect(OvsdbNode ovsdbNode) {
switch (ovsdbNode.state()) {
- case CONNECTED:
+ case DISCONNECT:
// TODO: disconnect
break;
- case INIT:
- case READY:
- case DISCONNECTED:
- break;
default:
+ break;
+ }
+ }
+
+ private class InternalDeviceListener implements DeviceListener {
+
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ if (device.type() != Device.Type.CONTROLLER) {
+ return;
+ }
+
+ DefaultOvsdbNode node;
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ node = (DefaultOvsdbNode) cordVtnService.getNode(device.id());
+ if (node != null) {
+ cordVtnService.updateNode(node, CONNECTED);
+ }
+ break;
+ case DEVICE_AVAILABILITY_CHANGED:
+ node = (DefaultOvsdbNode) cordVtnService.getNode(device.id());
+ if (node != null) {
+ cordVtnService.updateNode(node, DISCONNECTED);
+ }
+ break;
+ default:
+ break;
+ }
}
}
private NodeId getMaster(OvsdbNode ovsdbNode) {
- // Return the master of the bridge(switch) if it exist or
- // return the current leader
- if (ovsdbNode.bridgeId() == DeviceId.NONE) {
- return leadershipService.getLeader(this.appId.name());
- } else {
- return mastershipService.getMasterFor(ovsdbNode.bridgeId());
+ NodeId master = mastershipService.getMasterFor(ovsdbNode.intBrId());
+
+ // master is null if there's no such device
+ if (master == null) {
+ master = leadershipService.getLeader(CordVtnService.CORDVTN_APP_ID);
}
+ return master;
}
private void setPassiveMode(OvsdbNode ovsdbNode) {
// TODO: need ovsdb client implementation first
// TODO: set the remove ovsdb server passive mode
- // TODO: set the node state READY if it succeed
- }
-
- private void connect(OvsdbNode ovsdbNode) {
- // TODO: need ovsdb client implementation first
+ cordVtnService.updateNode(ovsdbNode, READY);
}
- private void disconnect(OvsdbNode ovsdbNode) {
- // TODO: need ovsdb client implementation first
+ private void setupConnection(OvsdbNode ovsdbNode) {
+ // TODO initiate connection
}
}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
index bb2a0b7d..296bd439 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
@@ -24,51 +24,52 @@ import org.onosproject.net.DeviceId;
*/
public interface OvsdbNode {
/**
- * State of the ovsdb node.
+ * Ovsdb connection state.
*/
enum State {
- INIT, READY, CONNECTED, DISCONNECTED
+ INIT, READY, CONNECTED, DISCONNECT, DISCONNECTED
}
/**
- * Returns the IP address of ovsdb server.
+ * Returns the IP address of the ovsdb server.
*
* @return ip address
*/
IpAddress ip();
/**
- * Returns the port number of ovsdb server.
+ * Returns the port number of the ovsdb server.
*
* @return port number
*/
TpPort port();
/**
- * Returns the hostname of the node.
+ * Returns the host information of the ovsdb server.
+ * It could be hostname or ip address.
*
- * @return hostname
+ * @return host
*/
- String hostname();
+ String host();
/**
- * Returns the state of the node.
+ * Returns the connection state of the ovsdb server.
*
- * @return state of the node
+ * @return connection state
*/
State state();
/**
- * Returns the device ID of the node.
+ * Returns the device id of the ovsdb server.
*
* @return device id
*/
DeviceId deviceId();
/**
- * Returns the device ID of the bridge associated with this node.
+ * Returns the device id of the integration bridge associated with the node.
*
* @return device id
*/
- DeviceId bridgeId();
+ DeviceId intBrId();
}
diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java
index c9fade9e..5615af1a 100644
--- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java
+++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java
@@ -65,7 +65,7 @@ public interface DhcpStore {
*
* @param hostId the host ID for which the mapping needs to be changed
*/
- void releaseIP(HostId hostId);
+ Ip4Address releaseIP(HostId hostId);
/**
* Returns a collection of all the MacAddress to IPAddress mapping assigned to the hosts.
diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
index 345d5ad0..96d94a2b 100644
--- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
+++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -154,6 +154,8 @@ public class DhcpManager implements DhcpService {
private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
+ private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
+
protected Timeout timeout;
protected static int timerDelay = 2;
@@ -290,12 +292,18 @@ public class DhcpManager implements DhcpService {
DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
DHCP dhcpReply = new DHCP();
dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
-
- dhcpReply.setYourIPAddress(ipOffered.toInt());
- dhcpReply.setServerIPAddress(myIP.toInt());
-
- dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+ dhcpReply.setFlags(dhcpPacket.getFlags());
+ dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
+ dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+
+ if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
+ dhcpReply.setYourIPAddress(ipOffered.toInt());
+ dhcpReply.setServerIPAddress(myIP.toInt());
+ if (dhcpPacket.getGatewayIPAddress() == 0) {
+ ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
+ }
+ }
dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
dhcpReply.setHardwareAddressLength((byte) 6);
@@ -317,54 +325,57 @@ public class DhcpManager implements DhcpService {
option.setData(myIP.toOctets());
optionList.add(option);
- // IP Address Lease Time.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
- option.setLength((byte) 4);
- option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
- optionList.add(option);
-
- // IP Address Renewal Time.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
- option.setLength((byte) 4);
- option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
- optionList.add(option);
-
- // IP Address Rebinding Time.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
- option.setLength((byte) 4);
- option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
- optionList.add(option);
-
- // Subnet Mask.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
- option.setLength((byte) 4);
- option.setData(subnetMask.toOctets());
- optionList.add(option);
-
- // Broadcast Address.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
- option.setLength((byte) 4);
- option.setData(broadcastAddress.toOctets());
- optionList.add(option);
-
- // Router Address.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
- option.setLength((byte) 4);
- option.setData(routerAddress.toOctets());
- optionList.add(option);
-
- // DNS Server Address.
- option = new DHCPOption();
- option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
- option.setLength((byte) 4);
- option.setData(domainServer.toOctets());
- optionList.add(option);
+ if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
+
+ // IP Address Lease Time.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
+ option.setLength((byte) 4);
+ option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
+ optionList.add(option);
+
+ // IP Address Renewal Time.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
+ option.setLength((byte) 4);
+ option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
+ optionList.add(option);
+
+ // IP Address Rebinding Time.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
+ option.setLength((byte) 4);
+ option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
+ optionList.add(option);
+
+ // Subnet Mask.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
+ option.setLength((byte) 4);
+ option.setData(subnetMask.toOctets());
+ optionList.add(option);
+
+ // Broadcast Address.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
+ option.setLength((byte) 4);
+ option.setData(broadcastAddress.toOctets());
+ optionList.add(option);
+
+ // Router Address.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
+ option.setLength((byte) 4);
+ option.setData(routerAddress.toOctets());
+ optionList.add(option);
+
+ // DNS Server Address.
+ option = new DHCPOption();
+ option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
+ option.setLength((byte) 4);
+ option.setData(domainServer.toOctets());
+ optionList.add(option);
+ }
// End Option.
option = new DHCPOption();
@@ -447,41 +458,51 @@ public class DhcpManager implements DhcpService {
} else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
- outgoingPacketType = DHCPPacketType.DHCPACK;
-
if (flagIfServerIP && flagIfRequestedIP) {
// SELECTING state
- if (myIP.equals(serverIP) &&
- dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
+ if (myIP.equals(serverIP)) {
- Ethernet ethReply = buildReply(packet, requestedIP,
- (byte) outgoingPacketType.getValue());
+ if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
+ outgoingPacketType = DHCPPacketType.DHCPACK;
+ discoverHost(context, requestedIP);
+ } else {
+ outgoingPacketType = DHCPPacketType.DHCPNAK;
+ }
+ Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
sendReply(context, ethReply);
- discoverHost(context, requestedIP);
}
} else if (flagIfRequestedIP) {
// INIT-REBOOT state
if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
- Ethernet ethReply = buildReply(packet, requestedIP,
- (byte) outgoingPacketType.getValue());
+ outgoingPacketType = DHCPPacketType.DHCPACK;
+ Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
sendReply(context, ethReply);
discoverHost(context, requestedIP);
}
+
} else {
// RENEWING and REBINDING state
int ciaadr = dhcpPayload.getClientIPAddress();
if (ciaadr != 0) {
Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime)) {
- Ethernet ethReply = buildReply(packet, clientIaddr,
- (byte) outgoingPacketType.getValue());
- sendReply(context, ethReply);
+ outgoingPacketType = DHCPPacketType.DHCPACK;
discoverHost(context, clientIaddr);
+ } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 &&
+ ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) {
+ outgoingPacketType = DHCPPacketType.DHCPNAK;
+ } else {
+ return;
}
+ Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue());
+ sendReply(context, ethReply);
}
}
} else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) {
- dhcpStore.releaseIP(hostId);
+ Ip4Address ip4Address = dhcpStore.releaseIP(hostId);
+ if (ip4Address != null) {
+ hostProviderService.removeIpFromHost(hostId, ip4Address);
+ }
}
}
}
@@ -666,9 +687,10 @@ public class DhcpManager implements DhcpService {
if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
(ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
- dhcpStore.releaseIP(entry.getKey());
- // TODO remove only the IP from the host entry when the API is in place.
- hostProviderService.hostVanished(entry.getKey());
+ Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
+ if (ip4Address != null) {
+ hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
+ }
}
}
timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
index dbdadb34..63f69d40 100644
--- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
+++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
@@ -212,7 +212,7 @@ public class DistributedDhcpStore implements DhcpStore {
}
@Override
- public void releaseIP(HostId hostId) {
+ public Ip4Address releaseIP(HostId hostId) {
if (allocationMap.containsKey(hostId)) {
IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
.assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
@@ -222,7 +222,9 @@ public class DistributedDhcpStore implements DhcpStore {
if (ipWithinRange(freeIP)) {
freeIPPool.add(freeIP);
}
+ return freeIP;
}
+ return null;
}
@Override
diff --git a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
index 3ea3b1b8..fd4701c6 100644
--- a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
+++ b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
@@ -25,6 +25,7 @@ import org.onlab.packet.DHCPPacketType;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.UDP;
import org.onosproject.core.CoreServiceAdapter;
@@ -234,7 +235,8 @@ public class DhcpManagerTest {
public void setDefaultTimeoutForPurge(int timeInSeconds) {
}
- public void releaseIP(HostId hostId) {
+ public Ip4Address releaseIP(HostId hostId) {
+ return null;
}
public Map<HostId, IpAssignment> listAssignedMapping() {
@@ -331,12 +333,18 @@ public class DhcpManagerTest {
@Override
public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
+
}
@Override
public void hostVanished(HostId hostId) {
}
+ @Override
+ public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
+
+ }
+
}
/**
diff --git a/framework/src/onos/apps/flowanalyzer/pom.xml b/framework/src/onos/apps/flowanalyzer/pom.xml
index f5dfcf2b..b0920412 100644
--- a/framework/src/onos/apps/flowanalyzer/pom.xml
+++ b/framework/src/onos/apps/flowanalyzer/pom.xml
@@ -40,6 +40,38 @@
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+
</dependencies>
</project>
diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java
new file mode 100644
index 00000000..2c61949b
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.flowanalyzer;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+
+/**
+ * Analyzes flows for cycles and black holes.
+ */
+@Command(scope = "onos", name = "flow-analysis",
+ description = "Analyzes flows for cycles and black holes")
+public class FlowAnalysisCommand extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ FlowAnalyzer service = get(FlowAnalyzer.class);
+ print(service.analyze());
+ }
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
index 5d99d746..6aaaaee8 100644
--- a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
+++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
@@ -21,12 +21,31 @@ import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.host.HostService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.net.link.LinkService;
+import org.onosproject.net.Link;
+import org.onosproject.net.topology.TopologyVertex;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
+import java.util.HashSet;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -42,11 +61,10 @@ public class FlowAnalyzer {
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected LinkService linkService;
+ protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
+ protected LinkService linkService;
@Activate
public void activate(ComponentContext context) {
@@ -58,12 +76,193 @@ public class FlowAnalyzer {
log.info("Stopped");
}
+ TopologyGraph graph;
+ Map<FlowEntry, String> label = new HashMap<>();
+ Set<FlowEntry> ignoredFlows = new HashSet<>();
/**
- * ...
+ * Analyzes and prints out a report on the status of every flow entry inside
+ * the network. The possible states are: Cleared (implying that the entry leads to
+ * a host), Cycle (implying that it is part of cycle), and Black Hole (implying
+ * that the entry does not lead to a single host).
*/
- public void analyze() {
- // TODO: implement this
+ public String analyze() {
+ graph = topologyService.getGraph(topologyService.currentTopology());
+ for (TopologyVertex v: graph.getVertexes()) {
+ DeviceId srcDevice = v.deviceId();
+ Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice);
+ for (FlowEntry flow: flowTable) {
+ dfs(flow);
+ }
+ }
+
+ //analyze the cycles to look for "critical flows" that can be removed
+ //to break the cycle
+ Set<FlowEntry> critpts = new HashSet<>();
+ for (FlowEntry flow: label.keySet()) {
+ if ("Cycle".equals(label.get(flow))) {
+ Map<FlowEntry, String> labelSaved = label;
+ label = new HashMap<FlowEntry, String>();
+ ignoredFlows.add(flow);
+ for (TopologyVertex v: graph.getVertexes()) {
+ DeviceId srcDevice = v.deviceId();
+ Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice);
+ for (FlowEntry flow1: flowTable) {
+ dfs(flow1);
+ }
+ }
+
+ boolean replacable = true;
+ for (FlowEntry flow2: label.keySet()) {
+ if ("Cleared".equals(labelSaved.get(flow2)) && !("Cleared".equals(label.get(flow2)))) {
+ replacable = false;
+ }
+ }
+ if (replacable) {
+ critpts.add(flow);
+ }
+ label = labelSaved;
+ }
+ }
+
+ for (FlowEntry flow: critpts) {
+ label.put(flow, "Cycle Critical Point");
+ }
+
+ String s = "\n";
+ for (FlowEntry flow: label.keySet()) {
+ s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n");
+ s += ("Analysis: " + label.get(flow) + "!\n\n");
+ }
+ s += ("Analyzed " + label.keySet().size() + " flows.");
+ //log.info(s);
+ return s;
+ }
+
+ public Map<FlowEntry, String> calcLabels() {
+ analyze();
+ return label;
+ }
+ public String analysisOutput() {
+ analyze();
+ String s = "\n";
+ for (FlowEntry flow: label.keySet()) {
+ s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n");
+ s += ("Analysis: " + label.get(flow) + "!\n\n");
+ }
+ return s;
+ }
+
+ private boolean dfs(FlowEntry flow) {
+ if (ignoredFlows.contains(flow)) {
+ return false;
+ }
+ if ("Cycle".equals(label.get(flow)) ||
+ "Black Hole".equals(label.get(flow)) ||
+ "Cleared".equals(label.get(flow)) ||
+ "NA".equals(label.get(flow)) ||
+ "Cycle Critical Point".equals(label.get(flow))) {
+
+ // This flow has already been analyzed and there is no need to analyze it further
+ return !"Black Hole".equals(label.get(flow));
+ }
+
+ if ("Visiting".equals(label.get(flow))) {
+ //you've detected a cycle because you reached the same entry again during your dfs
+ //let it continue so you can label the whole cycle
+ label.put(flow, "Cycle");
+ } else {
+ //otherwise, mark off the current flow entry as currently being visited
+ label.put(flow, "Visiting");
+ }
+
+ boolean pointsToLiveEntry = false;
+
+ List<Instruction> instructions = flow.treatment().allInstructions();
+ for (Instruction i: instructions) {
+ if (i instanceof Instructions.OutputInstruction) {
+ pointsToLiveEntry |= analyzeInstruction(i, flow);
+ }
+ if ("NA".equals(label.get(flow))) {
+ return pointsToLiveEntry;
+ }
+ }
+
+ if (!pointsToLiveEntry) {
+ //this entry does not point to any "live" entries thus must be a black hole
+ label.put(flow, "Black Hole");
+ } else if ("Visiting".equals(label.get(flow))) {
+ //the flow is not in a cycle or in a black hole
+ label.put(flow, "Cleared");
+ }
+ return pointsToLiveEntry;
}
+ private boolean analyzeInstruction(Instruction i, FlowEntry flow) {
+ boolean pointsToLiveEntry = false;
+ Instructions.OutputInstruction output = (Instructions.OutputInstruction) i;
+ PortNumber port = output.port();
+ PortNumber outPort = null;
+
+ DeviceId egress = null;
+ boolean hasHost = false;
+
+ ConnectPoint portPt = new ConnectPoint(flow.deviceId(), port);
+ for (Link l: linkService.getEgressLinks(portPt)) {
+ if (l.dst().elementId() instanceof DeviceId) {
+ egress = l.dst().deviceId();
+ outPort = l.dst().port();
+ } else if (l.dst().elementId() instanceof HostId) {
+ //the port leads to a host: therefore it is not a dead link
+ pointsToLiveEntry = true;
+ hasHost = true;
+ }
+ }
+ if (!topologyService.isInfrastructure(topologyService.currentTopology(), portPt) && egress == null) {
+ pointsToLiveEntry = true;
+ hasHost = true;
+ }
+ if (hasHost) {
+ return pointsToLiveEntry;
+ }
+ if (egress == null) {
+ //the port that the flow instructions tells you to send the packet
+ //to doesn't exist or is a controller port
+ label.put(flow, "NA");
+ return pointsToLiveEntry;
+ }
+
+ Iterable<FlowEntry> dstFlowTable = flowRuleService.getFlowEntries(egress);
+
+ Set<Criterion> flowCriteria = flow.selector().criteria();
+
+ //filter the criteria in order to remove port dependency
+ Set<Criterion> filteredCriteria = new HashSet<>();
+ for (Criterion criterion : flowCriteria) {
+ if (!(criterion instanceof PortCriterion)) {
+ filteredCriteria.add(criterion);
+ }
+ }
+
+ //ensure that the in port is equal to the port that it is coming in from
+ filteredCriteria.add(Criteria.matchInPort(outPort));
+
+ for (FlowEntry entry: dstFlowTable) {
+ if (ignoredFlows.contains(entry)) {
+ continue;
+ }
+ if (filteredCriteria.containsAll(entry.selector().criteria())) {
+ dfs(entry);
+
+ if (!"Black Hole".equals(label.get(entry))) {
+ //this entry is "live" i.e not a black hole
+ pointsToLiveEntry = true;
+ }
+ }
+ }
+ return pointsToLiveEntry;
+ }
+ public String flowEntryRepresentation(FlowEntry flow) {
+ return "Device: " + flow.deviceId() + ", " + flow.selector().criteria() + ", " + flow.treatment().immediate();
+ }
}
diff --git a/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..93cb27ee
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.flowanalyzer.FlowAnalysisCommand"/>
+ </command>
+
+ </command-bundle>
+</blueprint>
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java
new file mode 100644
index 00000000..4ea3aa48
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java
@@ -0,0 +1,28 @@
+package org.onosproject.flowanalyzer;
+
+import org.onlab.graph.MutableAdjacencyListsGraph;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.topology.TopologyGraph;
+import org.onosproject.net.topology.TopologyVertex;
+
+import java.util.Set;
+
+/**
+ * Default implementation of an immutable topology graph based on a generic
+ * implementation of adjacency lists graph.
+ */
+public class DefaultMutableTopologyGraph
+ extends MutableAdjacencyListsGraph<TopologyVertex, TopologyEdge>
+ implements TopologyGraph {
+
+ /**
+ * Creates a topology graph comprising of the specified vertexes and edges.
+ *
+ * @param vertexes set of graph vertexes
+ * @param edges set of graph edges
+ */
+ public DefaultMutableTopologyGraph(Set<TopologyVertex> vertexes, Set<TopologyEdge> edges) {
+ super(vertexes, edges);
+ }
+
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java
new file mode 100644
index 00000000..faa2f5f9
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.flowanalyzer;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleExtPayLoad;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.topology.TopologyService;
+
+import java.util.Arrays;
+import java.util.TreeSet;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Created by nikcheerla on 7/20/15.
+ */
+public class FlowAnalyzerTest {
+
+ FlowRuleService flowRuleService = new MockFlowRuleService();
+ TopologyService topologyService;
+ MockLinkService linkService = new MockLinkService();
+
+ @Test
+ @Ignore("This needs to be reworked to be more robust")
+ public void basic() {
+ flowRuleService = new MockFlowRuleService();
+ flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 90));
+ flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 100));
+ flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 150));
+ flowRuleService.applyFlowRules(genFlow("ATL-002", 80, 70));
+ flowRuleService.applyFlowRules(genFlow("ATL-003", 120, 130));
+ flowRuleService.applyFlowRules(genFlow("ATL-004", 50));
+ flowRuleService.applyFlowRules(genFlow("ATL-005", 140, 10));
+
+ linkService.addLink("H00:00:00:00:00:0660", 160, "ATL-005", 140);
+ linkService.addLink("ATL-005", 10, "ATL-004", 40);
+ linkService.addLink("ATL-004", 50, "ATL-002", 80);
+ linkService.addLink("ATL-002", 70, "ATL-001", 110);
+ linkService.addLink("ATL-001", 150, "H00:00:00:00:00:0770", 170);
+ linkService.addLink("ATL-001", 90, "ATL-004", 30);
+ linkService.addLink("ATL-001", 100, "ATL-003", 120);
+ linkService.addLink("ATL-003", 130, "ATL-005", 20);
+
+ topologyService = new MockTopologyService(linkService.createdGraph);
+
+ FlowAnalyzer flowAnalyzer = new FlowAnalyzer();
+ flowAnalyzer.flowRuleService = flowRuleService;
+ flowAnalyzer.linkService = linkService;
+ flowAnalyzer.topologyService = topologyService;
+
+ String labels = flowAnalyzer.analysisOutput();
+ String correctOutput = "Flow Rule: Device: atl-005, [IN_PORT{port=140}], [OUTPUT{port=10}]\n" +
+ "Analysis: Cleared!\n" +
+ "\n" +
+ "Flow Rule: Device: atl-003, [IN_PORT{port=120}], [OUTPUT{port=130}]\n" +
+ "Analysis: Black Hole!\n" +
+ "\n" +
+ "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=90}]\n" +
+ "Analysis: Cycle Critical Point!\n" +
+ "\n" +
+ "Flow Rule: Device: atl-004, [], [OUTPUT{port=50}]\n" +
+ "Analysis: Cycle!\n" +
+ "\n" +
+ "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=150}]\n" +
+ "Analysis: Cleared!\n" +
+ "\n" +
+ "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=100}]\n" +
+ "Analysis: Black Hole!\n" +
+ "\n" +
+ "Flow Rule: Device: atl-002, [IN_PORT{port=80}], [OUTPUT{port=70}]\n" +
+ "Analysis: Cycle!\n";
+ assertEquals("Wrong labels", new TreeSet(Arrays.asList(labels.replaceAll("\\s+", "").split("!"))),
+ new TreeSet(Arrays.asList(correctOutput.replaceAll("\\s+", "").split("!"))));
+ }
+
+ public FlowRule genFlow(String d, long inPort, long outPort) {
+ DeviceId device = DeviceId.deviceId(d);
+ TrafficSelector ts = DefaultTrafficSelector.builder().matchInPort(PortNumber.portNumber(inPort)).build();
+ TrafficTreatment tt = DefaultTrafficTreatment.builder()
+ .add(Instructions.createOutput(PortNumber.portNumber(outPort))).build();
+ return new DefaultFlowRule(device, ts, tt, 1, new DefaultApplicationId(5000, "of"),
+ 50000, true, FlowRuleExtPayLoad.flowRuleExtPayLoad(new byte[5]));
+ }
+ public FlowRule genFlow(String d, long outPort) {
+ DeviceId device = DeviceId.deviceId(d);
+ TrafficSelector ts = DefaultTrafficSelector.builder().build();
+ TrafficTreatment tt = DefaultTrafficTreatment.builder()
+ .add(Instructions.createOutput(PortNumber.portNumber(outPort))).build();
+ return new DefaultFlowRule(device, ts, tt, 1, new DefaultApplicationId(5000, "of"),
+ 50000, true, FlowRuleExtPayLoad.flowRuleExtPayLoad(new byte[5]));
+ }
+
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java
new file mode 100644
index 00000000..40bb0043
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java
@@ -0,0 +1,103 @@
+package org.onosproject.flowanalyzer;
+
+import com.google.common.collect.Sets;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+/**
+ * Created by nikcheerla on 7/20/15.
+ */
+
+public class MockFlowRuleService extends FlowRuleServiceAdapter {
+
+ final Set<FlowRule> flows = Sets.newHashSet();
+ boolean success;
+
+ int errorFlow = -1;
+ public void setErrorFlow(int errorFlow) {
+ this.errorFlow = errorFlow;
+ }
+
+ public void setFuture(boolean success) {
+ this.success = success;
+ }
+
+ @Override
+ public void apply(FlowRuleOperations ops) {
+ AtomicBoolean thisSuccess = new AtomicBoolean(success);
+ ops.stages().forEach(stage -> stage.forEach(flow -> {
+ if (errorFlow == flow.rule().id().value()) {
+ thisSuccess.set(false);
+ } else {
+ switch (flow.type()) {
+ case ADD:
+ case MODIFY: //TODO is this the right behavior for modify?
+ flows.add(flow.rule());
+ break;
+ case REMOVE:
+ flows.remove(flow.rule());
+ break;
+ default:
+ break;
+ }
+ }
+ }));
+ if (thisSuccess.get()) {
+ ops.callback().onSuccess(ops);
+ } else {
+ ops.callback().onError(ops);
+ }
+ }
+
+ @Override
+ public int getFlowRuleCount() {
+ return flows.size();
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ return flows.stream()
+ .filter(flow -> flow.deviceId().equals(deviceId))
+ .map(DefaultFlowEntry::new)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public void applyFlowRules(FlowRule... flowRules) {
+ for (FlowRule flow : flowRules) {
+ flows.add(flow);
+ }
+ }
+
+ @Override
+ public void removeFlowRules(FlowRule... flowRules) {
+ for (FlowRule flow : flowRules) {
+ flows.remove(flow);
+ }
+ }
+
+ @Override
+ public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
+ return flows.stream()
+ .filter(flow -> flow.appId() == id.id())
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) {
+ return flows.stream()
+ .filter(flow -> flow.appId() == appId.id() && flow.groupId().id() == groupId)
+ .collect(Collectors.toList());
+ }
+}
+
+
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java
new file mode 100644
index 00000000..2171c6f8
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java
@@ -0,0 +1,183 @@
+package org.onosproject.flowanalyzer;
+
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.link.LinkServiceAdapter;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.TopologyVertex;
+
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashSet;
+
+import static org.onosproject.net.Link.State.ACTIVE;
+
+
+/**
+ * Created by nikcheerla on 7/21/15.
+ */
+public class MockLinkService extends LinkServiceAdapter {
+ DefaultMutableTopologyGraph createdGraph = new DefaultMutableTopologyGraph(new HashSet<>(), new HashSet<>());
+ List<Link> links = new ArrayList<>();
+
+ @Override
+ public int getLinkCount() {
+ return links.size();
+ }
+
+ @Override
+ public Iterable<Link> getLinks() {
+ return links;
+ }
+
+ @Override
+ public Set<Link> getDeviceLinks(DeviceId deviceId) {
+ Set<Link> egress = getDeviceEgressLinks(deviceId);
+ egress.addAll(getDeviceIngressLinks(deviceId));
+ return egress;
+ }
+
+ @Override
+ public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
+ Set<Link> setL = new HashSet<>();
+ for (Link l: links) {
+ if (l.src().elementId() instanceof DeviceId && l.src().deviceId().equals(deviceId)) {
+ setL.add(l);
+ }
+ }
+ return setL;
+ }
+
+ @Override
+ public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
+ Set<Link> setL = new HashSet<>();
+ for (Link l: links) {
+ if (l.dst().elementId() instanceof DeviceId && l.dst().deviceId().equals(deviceId)) {
+ setL.add(l);
+ }
+ }
+ return setL;
+ }
+
+
+ @Override
+ public Set<Link> getEgressLinks(ConnectPoint pt) {
+ Set<Link> setL = new HashSet<>();
+ for (Link l: links) {
+ if (l.src().equals(pt)) {
+ setL.add(l);
+ }
+ }
+ return setL;
+ }
+
+ @Override
+ public Set<Link> getIngressLinks(ConnectPoint pt) {
+ Set<Link> setL = new HashSet<>();
+ for (Link l: links) {
+ if (l.dst().equals(pt)) {
+ setL.add(l);
+ }
+ }
+ return setL;
+ }
+
+ @Override
+ public Set<Link> getLinks(ConnectPoint pt) {
+ Set<Link> setL = new HashSet<>();
+ for (Link l: links) {
+ if (l.src().equals(pt) || l.dst().equals(pt)) {
+ setL.add(l);
+ }
+ }
+ return setL;
+ }
+
+ public void addLink(String device, long port, String device2, long port2) {
+ ElementId d1;
+ if (device.charAt(0) == 'H') {
+ device = device.substring(1, device.length());
+ d1 = HostId.hostId(device);
+ } else {
+ d1 = DeviceId.deviceId(device);
+ }
+
+ ElementId d2;
+ if (device2.charAt(0) == 'H') {
+ d2 = HostId.hostId(device2.substring(1, device2.length()));
+ } else {
+ d2 = DeviceId.deviceId(device2);
+ }
+
+ ConnectPoint src = new ConnectPoint(d1, PortNumber.portNumber(port));
+ ConnectPoint dst = new ConnectPoint(d2, PortNumber.portNumber(port2));
+ Link curLink;
+ curLink = new Link() {
+ @Override
+ public ConnectPoint src() {
+ return src;
+ }
+
+ @Override
+ public ConnectPoint dst() {
+ return dst;
+ }
+
+ @Override
+ public boolean isDurable() {
+ return true;
+ }
+
+ @Override
+ public Annotations annotations() {
+ return null;
+ }
+
+ @Override
+ public Type type() {
+ return null;
+ }
+
+ @Override
+ public ProviderId providerId() {
+ return null;
+ }
+
+ @Override
+ public State state() {
+ return ACTIVE;
+ }
+ };
+ links.add(curLink);
+ if (d1 instanceof DeviceId && d2 instanceof DeviceId) {
+ TopologyVertex v1 = () -> (DeviceId) d1, v2 = () -> (DeviceId) d2;
+ createdGraph.addVertex(v1);
+ createdGraph.addVertex(v2);
+ createdGraph.addEdge(new TopologyEdge() {
+ @Override
+ public Link link() {
+ return curLink;
+ }
+
+ @Override
+ public TopologyVertex src() {
+ return v1;
+ }
+
+ @Override
+ public TopologyVertex dst() {
+ return v2;
+ }
+ });
+ }
+ }
+
+
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java
new file mode 100644
index 00000000..0d25c977
--- /dev/null
+++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java
@@ -0,0 +1,21 @@
+package org.onosproject.flowanalyzer;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyGraph;
+import org.onosproject.net.topology.TopologyServiceAdapter;
+
+
+/**
+ * Created by nikcheerla on 7/20/15.
+ */
+public class MockTopologyService extends TopologyServiceAdapter {
+ TopologyGraph cur;
+
+ public MockTopologyService(TopologyGraph g) {
+ cur = g;
+ }
+
+ @Override
+ public TopologyGraph getGraph(Topology topology) {
+ return cur;
+ }
+}
diff --git a/framework/src/onos/apps/igmp/pom.xml b/framework/src/onos/apps/igmp/pom.xml
new file mode 100644
index 00000000..7980d2c0
--- /dev/null
+++ b/framework/src/onos/apps/igmp/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-igmp</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Internet Group Message Protocol</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.igmp</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- This is needed by ComponentContext, used for tunable configuration -->
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.8</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>
+ ${project.groupId}.${project.artifactId}
+ </Bundle-SymbolicName>
+ <Import-Package>
+ org.slf4j,
+ org.osgi.framework,
+ org.apache.commons.lang.math.*,
+ com.google.common.*,
+ org.onlab.packet.*,
+ org.onlab.rest.*,
+ org.onosproject.*,
+ org.onosproject.mfwd.impl.*;
+ org.onlab.util.*,
+ org.jboss.netty.util.*
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java
new file mode 100644
index 00000000..ae539c62
--- /dev/null
+++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.igmp.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IGMP;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * Internet Group Management Protocol.
+ */
+@Component(immediate = true)
+public class IGMPComponent {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private IGMPPacketProcessor processor = new IGMPPacketProcessor();
+ private static ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.igmp");
+
+ packetService.addProcessor(processor, PacketProcessor.director(1));
+
+ // Build a traffic selector for all multicast traffic
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPProtocol(IPv4.PROTOCOL_IGMP);
+ packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ packetService.removeProcessor(processor);
+ processor = null;
+ log.info("Stopped");
+ }
+
+ /**
+ * Packet processor responsible for handling IGMP packets.
+ */
+ private class IGMPPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ // Stop processing if the packet has been handled, since we
+ // can't do any more to it.
+ if (context.isHandled()) {
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethPkt = pkt.parsed();
+ if (ethPkt == null) {
+ return;
+ }
+
+ /*
+ * IPv6 MLD packets are handled by ICMP6. We'll only deal
+ * with IPv4.
+ */
+ if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
+ return;
+ }
+
+ IPv4 ip = (IPv4) ethPkt.getPayload();
+ IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+ IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+ log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() +
+ "\tingress port: " + context.inPacket().receivedFrom().toString());
+
+ if (ip.getProtocol() != IPv4.PROTOCOL_IGMP) {
+ log.error("IGMP Picked up a non IGMP packet.");
+ return;
+ }
+
+ IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4");
+ if (!mcast.contains(gaddr)) {
+ log.error("IGMP Picked up a non multicast packet.");
+ return;
+ }
+
+ if (mcast.contains(saddr)) {
+ log.error("IGMP Picked up a packet with a multicast source address.");
+ return;
+ }
+ IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+ IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+ IGMP igmp = (IGMP) ip.getPayload();
+ switch (igmp.getIgmpType()) {
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ IGMPProcessMembership.processMembership(igmp, pkt.receivedFrom());
+ break;
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
+ IGMPProcessQuery.processQuery(igmp, pkt.receivedFrom());
+ break;
+
+ case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
+ case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
+ case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
+ log.debug("IGMP version 1 & 2 message types are not currently supported. Message type: " +
+ igmp.getIgmpType());
+ break;
+
+ default:
+ log.debug("Unkown IGMP message type: " + igmp.getIgmpType());
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java
new file mode 100644
index 00000000..3d7d6033
--- /dev/null
+++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.igmp.impl;
+
+import org.onlab.packet.IGMP;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Process an IGMP Membership Report.
+ */
+public final class IGMPProcessMembership {
+
+ // Hide the default constructor.
+ private IGMPProcessMembership() {
+ }
+
+ /**
+ * Process the IGMP Membership report.
+ *
+ * @param igmp the deserialized IGMP message.
+ * @param receivedFrom the ConnectPoint this message came from.
+ */
+ public static void processMembership(IGMP igmp, ConnectPoint receivedFrom) {
+ }
+
+}
diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java
new file mode 100644
index 00000000..eb256796
--- /dev/null
+++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.igmp.impl;
+
+import org.onlab.packet.IGMP;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Process IGMP Query messages.
+ */
+public final class IGMPProcessQuery {
+
+ // Hide the default constructor.
+ private IGMPProcessQuery() {
+ }
+
+ /**
+ * Process the IGMP Membership Query message.
+ *
+ * @param igmp The deserialzed IGMP message
+ * @param receivedFrom the ConnectPoint this message came from.
+ */
+ public static void processQuery(IGMP igmp, ConnectPoint receivedFrom) {
+ }
+
+}
diff --git a/framework/src/onos/apps/mfwd/pom.xml b/framework/src/onos/apps/mfwd/pom.xml
new file mode 100644
index 00000000..835de836
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-mfwd</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Multicast forwarding application</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.mfwd</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty</artifactId>
+ <version>3.9.0.Final</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <_wab>src/main/webapp/</_wab>
+ <Bundle-SymbolicName>
+ ${project.groupId}.${project.artifactId}
+ </Bundle-SymbolicName>
+ <Import-Package>
+ org.slf4j,
+ org.osgi.framework,
+ javax.ws.rs,javax.ws.rs.core,
+ com.sun.jersey.api.core,
+ com.sun.jersey.spi.container.servlet,
+ com.sun.jersey.server.impl.container.servlet,
+ com.fasterxml.jackson.databind,
+ com.fasterxml.jackson.databind.node,
+ org.apache.commons.lang.math.*,
+ org.apache.karaf.shell.commands,
+ org.apache.karaf.shell.console,
+ com.google.common.*,
+ org.onlab.packet.*,
+ org.onlab.rest.*,
+ org.onosproject.*,
+ org.onlab.util.*,
+ org.jboss.netty.util.*
+ </Import-Package>
+ <Web-ContextPath>${web.context}</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
+
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java
new file mode 100644
index 00000000..ae5d9e93
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.mfwd.impl.McastRouteTable;
+
+/**
+ * Deletes a multicast route.
+ */
+@Command(scope = "onos", name = "mcast-delete",
+ description = "Delete a multicast route flow")
+public class McastDeleteCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "sAddr",
+ description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",
+ required = true, multiValued = false)
+ String sAddr = null;
+
+ @Argument(index = 1, name = "gAddr",
+ description = "IP Address of the multicast group",
+ required = true, multiValued = false)
+ String gAddr = null;
+
+ @Override
+ protected void execute() {
+ McastRouteTable mrib = McastRouteTable.getInstance();
+ mrib.removeRoute(sAddr, gAddr);
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java
new file mode 100644
index 00000000..7260fde5
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+
+import org.onosproject.mfwd.impl.McastConnectPoint;
+import org.onosproject.mfwd.impl.McastRouteBase;
+import org.onosproject.mfwd.impl.McastRouteTable;
+
+/**
+ * Installs a source, multicast group flow.
+ */
+@Command(scope = "onos", name = "mcast-join",
+ description = "Installs a source, multicast group flow")
+public class McastJoinCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "sAddr",
+ description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",
+ required = true, multiValued = false)
+ String sAddr = null;
+
+ @Argument(index = 1, name = "gAddr",
+ description = "IP Address of the multicast group",
+ required = true, multiValued = false)
+ String gAddr = null;
+
+ @Argument(index = 2, name = "ingressPort",
+ description = "Ingress port and Egress ports",
+ required = false, multiValued = false)
+ String ingressPort = null;
+
+ @Argument(index = 3, name = "ports",
+ description = "Ingress port and Egress ports",
+ required = false, multiValued = true)
+ String[] ports = null;
+
+ @Override
+ protected void execute() {
+ McastRouteTable mrib = McastRouteTable.getInstance();
+ McastRouteBase mr = mrib.addRoute(sAddr, gAddr);
+
+ // Port format "of:0000000000000023/4"
+ if (ingressPort != null) {
+ String inCP = ingressPort;
+ log.debug("Ingress port provided: " + inCP);
+ mr.addIngressPoint(inCP);
+ }
+
+ for (int i = 0; i < ports.length; i++) {
+ String egCP = ports[i];
+ log.debug("Egress port provided: " + egCP);
+ mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);
+ }
+ print("Added the mcast route");
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java
new file mode 100644
index 00000000..7fa3a13a
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.cli;
+
+import org.apache.karaf.shell.commands.Command;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.mfwd.impl.McastRouteTable;
+import org.onosproject.mfwd.impl.MRibCodec;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Displays the source, multicast group flows entries.
+ */
+@Command(scope = "onos", name = "mcast-show", description = "Displays the source, multicast group flows")
+public class McastShowCommand extends AbstractShellCommand {
+
+ private final Logger log = getLogger(getClass());
+ private static final String MCAST_GROUP = "mcastgroup";
+
+ @Override
+ protected void execute() {
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ if (outputJson()) {
+ print("%s", json(mrt));
+ } else {
+ printMrib4(mrt);
+ }
+ }
+
+ public JsonNode json(McastRouteTable mrt) {
+ ObjectNode pushContent = new MRibCodec().encode(mrt , this);
+ return pushContent;
+ }
+
+ /**
+ * Displays multicast route table entries.
+ *
+ * @param mrt Mutlicast Route Table
+ */
+ protected void printMrib4(McastRouteTable mrt) {
+ print(mrt.printMcastRouteTable());
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java
new file mode 100644
index 00000000..7b5ed39a
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Sample Multicast forwarding framework using intents.
+ */
+package org.onosproject.mfwd.cli;
+
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java
new file mode 100644
index 00000000..c4f18527
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+
+import org.onlab.packet.IpPrefix;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Encode and Decode the Multicast Route Table in JSON for CLI and REST commands.
+ */
+public class MRibCodec extends JsonCodec<McastRouteTable> {
+
+ private final Logger log = getLogger(getClass());
+ private static final String SOURCE_ADDRESS = "sourceAddress";
+ private static final String GROUP_ADDRESS = "groupAddress";
+ private static final String INGRESS_POINT = "ingressPoint";
+ private static final String EGRESS_POINT = "egressPoint";
+ private static final String MCASTCONNECTPOINT = "McastConnectPoint";
+ private static final String ELEMENTID = "elementId";
+ private static final String PORTNUMBER = "portNumber";
+ private static final String MCAST_GROUP = "mcastGroup";
+
+ /**
+ * Encode the MRIB into json format.
+ *
+ * @param mcastRouteTable McastRouteTable
+ * @param context CodecContext
+ * @return result ObjectNode
+ */
+ @Override
+ public ObjectNode encode(McastRouteTable mcastRouteTable, CodecContext context) {
+
+ final JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
+ final ObjectNode macastRouteTabNode = nodeFactory.objectNode();
+ ArrayNode mcastGroupNode = context.mapper().createArrayNode();
+ Optional<McastRouteTable> mcastRouteTabOpt = Optional.ofNullable(mcastRouteTable);
+
+ //checking whether the McastRouteTable is present.
+ if (mcastRouteTabOpt.isPresent()) {
+ Map<IpPrefix, McastRouteGroup> mrib4 = mcastRouteTabOpt.get().getMrib4();
+ Optional<Map<IpPrefix, McastRouteGroup>> mrib4Opt = Optional.ofNullable(mrib4);
+
+ //checking whether the mrib4 is present.
+ if (mrib4Opt.isPresent()) {
+
+ for (McastRouteGroup mg : mrib4Opt.get().values()) {
+ Collection<McastRouteSource> mcastRoute = mg.getSources().values();
+ Optional<Collection<McastRouteSource>> mcastRouteOpt = Optional.ofNullable(mcastRoute);
+
+ //checking whether the McastRouteSource List is present.
+ if (mcastRouteOpt.isPresent()) {
+ for (McastRouteSource mcastRouteSource : mcastRouteOpt.get()) {
+ mcastGroupNode.add(createMcastGroupNode(mcastRouteSource, context));
+ }
+ macastRouteTabNode.put(MCAST_GROUP, mcastGroupNode);
+ }
+ }
+ }
+ }
+ return macastRouteTabNode;
+ }
+ /**
+ * Method for creating the McastGroup object node.
+ *
+ * @param mcastRouteSource McastRouteSource
+ */
+ private ObjectNode createMcastGroupNode(McastRouteSource mcastRouteSource, CodecContext context) {
+
+ final ObjectNode mcastGroupNode = context.mapper().createObjectNode();
+ final ObjectNode ingressNode = context.mapper().createObjectNode();
+ final ObjectNode egressNode = context.mapper().createObjectNode();
+ final ArrayNode jsonLabelIds = context.mapper().createArrayNode();
+ final String sAddr = mcastRouteSource.getSaddr().toString();
+ final String gAddr = mcastRouteSource.getGaddr().toString();
+
+ Optional<String> saddrOpt = Optional.ofNullable(sAddr);
+ Optional<String> gaddrOpt = Optional.ofNullable(gAddr);
+
+ //checking source address and group address are present.
+ if (saddrOpt.isPresent() && gaddrOpt.isPresent()) {
+ mcastGroupNode.put(SOURCE_ADDRESS, saddrOpt.get().toString());
+ mcastGroupNode.put(GROUP_ADDRESS, gaddrOpt.get().toString());
+ McastConnectPoint mcastIngCP = mcastRouteSource.getIngressPoint();
+ Optional<McastConnectPoint> mcastIngCPOpt = Optional.ofNullable(mcastIngCP);
+
+ //checking whether the ingress connection point is present.
+ if (mcastIngCPOpt.isPresent()) {
+ ingressNode.put(MCASTCONNECTPOINT, mcastConnectPoint(mcastIngCPOpt.get(), context));
+ }
+
+ mcastGroupNode.put(INGRESS_POINT , ingressNode);
+ Set<McastConnectPoint> mcastEgCPSet = mcastRouteSource.getEgressPoints();
+ Optional<Set<McastConnectPoint>> mcastEgCPOpt = Optional.ofNullable(mcastEgCPSet);
+
+ //checking whether the egress connection points are present.
+ if (mcastEgCPOpt.isPresent()) {
+ for (final McastConnectPoint mcastConnectPoint : mcastEgCPOpt.get()) {
+ jsonLabelIds.add(mcastConnectPoint(mcastConnectPoint, context));
+ }
+ }
+
+ egressNode.put(MCASTCONNECTPOINT , jsonLabelIds);
+ mcastGroupNode.put(EGRESS_POINT , egressNode);
+ }
+ return mcastGroupNode;
+ }
+
+ /**
+ * Method for creating the McastConnectPoint object node.
+ *
+ * @param mcastConnectPoint McastConnectPoint
+ * @param context CodecContext
+ * @return mcastCpNode ObjectNode
+ */
+ private ObjectNode mcastConnectPoint(McastConnectPoint mcastConnectPoint, CodecContext context) {
+ final ObjectNode mcastCpNode = context.mapper().createObjectNode();
+ mcastCpNode.put(ELEMENTID , mcastConnectPoint.getConnectPoint().elementId().toString());
+ mcastCpNode.put(PORTNUMBER , mcastConnectPoint.getConnectPoint().port().toLong());
+ return mcastCpNode;
+ }
+
+ /**
+ * Decode json format and insert into the flow table.
+ *
+ * @param json ObjectNode
+ * @param context CodecContext
+ * @return mr McastRouteBase
+ */
+ @Override
+ public McastRouteTable decode(ObjectNode json, CodecContext context) {
+
+ String macAddr = null;
+ String portNo = null;
+ String sAddr = json.path(SOURCE_ADDRESS).asText();
+ String gAddr = json.path(GROUP_ADDRESS).asText();
+ JsonNode inPntObjNode = (JsonNode) json.path(INGRESS_POINT);
+ JsonNode egPntArrNode = (JsonNode) json.path(EGRESS_POINT);
+
+ log.debug("sAddr :" + sAddr + " gAddr :" + gAddr + " inPntObjNode :" + inPntObjNode);
+ log.debug("egPntArrNode :" + egPntArrNode.toString());
+
+ McastRouteTable mrib = McastRouteTable.getInstance();
+ McastRouteBase mr = mrib.addRoute(sAddr, gAddr);
+ Optional<JsonNode> inPntOpt = Optional.ofNullable(inPntObjNode);
+
+ if (inPntOpt.isPresent()) {
+
+ JsonNode inMcastCP = inPntOpt.get().path(MCASTCONNECTPOINT);
+ Optional<JsonNode> inCpOpt = Optional.ofNullable(inMcastCP);
+
+ if (inCpOpt.isPresent()) {
+ macAddr = inCpOpt.get().path(ELEMENTID).asText();
+ portNo = inCpOpt.get().path(PORTNUMBER).asText();
+ mr.addIngressPoint(macAddr + "/" + Long.parseLong(portNo));
+ }
+ }
+
+ Optional<JsonNode> egPntOpt = Optional.ofNullable(egPntArrNode);
+
+ if (egPntOpt.isPresent()) {
+ JsonNode egMcastCP = egPntOpt.get().path(MCASTCONNECTPOINT);
+ Optional<JsonNode> egMcCpOpt = Optional.ofNullable(egMcastCP);
+
+ if (egMcCpOpt.isPresent()) {
+ Iterator<JsonNode> egCpIt = egMcCpOpt.get().elements();
+
+ while (egCpIt.hasNext()) {
+
+ JsonNode egMcastCPObj = egCpIt.next();
+ Optional<JsonNode> egMcCpObOpt = Optional.ofNullable(egMcastCPObj);
+ if (egMcCpObOpt.isPresent()) {
+ macAddr = egMcCpObOpt.get().path(ELEMENTID).asText();
+ portNo = egMcCpObOpt.get().path(PORTNUMBER).asText();
+ log.debug("macAddr egPort : " + macAddr + " portNo egPort :" + portNo);
+ mr.addEgressPoint(macAddr + "/" + Long.parseLong(portNo), McastConnectPoint.JoinSource.STATIC);
+ }
+ }
+ }
+ }
+ return mrib;
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java
new file mode 100644
index 00000000..e2a6ff0d
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.onosproject.net.ConnectPoint;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Mulitcast ConnectPoint adds a variable to track the usage
+ * of these multicast endpoints.
+ */
+public class McastConnectPoint {
+
+ private ConnectPoint connectPoint;
+
+ public enum JoinSource {
+ STATIC, IGMP, PIM;
+ }
+
+ public EnumSet<JoinSource> interest = EnumSet.noneOf(JoinSource.class);
+
+ public McastConnectPoint(ConnectPoint cp) {
+ this.connectPoint = cp;
+ }
+
+ public McastConnectPoint(ConnectPoint cp, JoinSource src) {
+ this.connectPoint = cp;
+ interest.add(src);
+ }
+
+ public McastConnectPoint(String connectPoint, JoinSource src) {
+ ConnectPoint cp = ConnectPoint.deviceConnectPoint(connectPoint);
+ this.connectPoint = cp;
+ this.interest.add(src);
+ }
+
+ /**
+ * Get the connect point.
+ *
+ * @return connectPoint
+ */
+ public ConnectPoint getConnectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Get the sources of interest for this egressPoint.
+ *
+ * @return interest flags
+ */
+ public Set<JoinSource> getInterest() {
+ return interest;
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java
new file mode 100644
index 00000000..f5bd1e01
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IPv4;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * WORK-IN-PROGRESS: The multicast forwarding application using intent framework.
+ */
+@Component(immediate = true)
+public class McastForwarding {
+
+ private final Logger log = getLogger(getClass());
+ private final IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4");
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private ReactivePacketProcessor processor = new ReactivePacketProcessor();
+ private McastRouteTable mrib;
+ private static ApplicationId appId;
+
+ /**
+ * Active MulticastForwardingIntent.
+ */
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.mfwd");
+
+ packetService.addProcessor(processor, PacketProcessor.director(2));
+
+ // Build a traffic selector for all multicast traffic
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPDst(mcast);
+ packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+ mrib = McastRouteTable.getInstance();
+ log.info("Started");
+ }
+
+ /**
+ * Deactivate Multicast Forwarding Intent.
+ */
+ @Deactivate
+ public void deactivate() {
+ packetService.removeProcessor(processor);
+ processor = null;
+ log.info("Stopped");
+ }
+
+ /**
+ * Get the application ID, used by the McastIntentManager.
+ *
+ * @return the application ID
+ */
+ public static ApplicationId getAppId() {
+ return appId;
+ }
+
+ /**
+ * Packet processor responsible for forwarding packets along their paths.
+ */
+ private class ReactivePacketProcessor implements PacketProcessor {
+
+ /**
+ * Process incoming packets.
+ *
+ * @param context packet processing context
+ */
+ @Override
+ public void process(PacketContext context) {
+ // Stop processing if the packet has been handled, since we
+ // can't do any more to it.
+ if (context.isHandled()) {
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethPkt = pkt.parsed();
+
+ if (ethPkt == null) {
+ return;
+ }
+
+ if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4 &&
+ ethPkt.getEtherType() != Ethernet.TYPE_IPV6) {
+ return;
+ }
+
+ if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
+ // Ignore ipv6 at the moment.
+ return;
+ }
+
+ IPv4 ip = (IPv4) ethPkt.getPayload();
+ IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+ IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+
+ log.debug("Packet ({}, {}) has been punted\n" +
+ "\tingress port: {}\n",
+ saddr.toString(),
+ gaddr.toString(),
+ context.inPacket().receivedFrom().toString());
+
+ if (!mcast.contains(gaddr)) {
+ // Yikes, this is a bad group address
+ return;
+ }
+
+ if (mcast.contains(saddr)) {
+ // Yikes, the source address is multicast
+ return;
+ }
+
+ IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+ IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+ /*
+ * Do a best match lookup on the (s, g) of the packet. If an entry does
+ * not exist create one and store it's incoming connect point.
+ *
+ * The connect point is deviceId / portId that the packet entered
+ * the SDN network. This differs from traditional mcast where the
+ * ingress port would be a specific device.
+ */
+ McastRoute entry = mrib.findBestMatch(spfx, gpfx);
+ if (entry == null || entry.getSaddr().equals(IPv4.fromIPv4Address(0))) {
+
+ /*
+ * Create an entry that we can fast drop.
+ */
+ entry = mrib.addRoute(spfx, gpfx);
+ entry.addIngressPoint(context.inPacket().receivedFrom());
+ }
+
+ /*
+ * TODO: If we do not have an ingress or any egress connect points we
+ * should set up a fast drop entry.
+ */
+ if (entry.getIngressPoint() == null) {
+ return;
+ }
+
+ if (entry.getEgressPoints().isEmpty()) {
+ return;
+ }
+
+ /*
+ * This is odd, we should not have received a punted packet if an
+ * intent was installed unless the intent was not installed
+ * correctly. However, we are seeing packets get punted after
+ * the intent has been installed.
+ *
+ * Therefore we are going to forward the packets even if they
+ * should have already been forwarded by the intent fabric.
+ */
+ if (entry.getIntentKey() != null) {
+ return;
+ }
+
+ entry.setIntent();
+ McastIntentManager im = McastIntentManager.getInstance();
+ im.setIntent(entry);
+
+ entry.incrementPuntCount();
+
+ // Send the pack out each of the egress devices & port
+ forwardPacketToDst(context, entry);
+ }
+ }
+
+ /**
+ * Forward the packet to it's multicast destinations.
+ *
+ * @param context The packet context
+ * @param entry The multicast route entry matching this packet
+ */
+ private void forwardPacketToDst(PacketContext context, McastRoute entry) {
+
+ // Send the pack out each of the respective egress ports
+ for (ConnectPoint egress : entry.getEgressConnectPoints()) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(egress.port()).build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(
+ egress.deviceId(),
+ treatment,
+ context.inPacket().unparsed());
+
+ packetService.emit(packet);
+ }
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java
new file mode 100644
index 00000000..90f65c94
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+
+@Component(immediate = true)
+@Service(value = org.onosproject.mfwd.impl.McastIntentManager.class)
+public class McastIntentManager {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ private static McastIntentManager instance;
+
+ public McastIntentManager() {
+ instance = this;
+ }
+
+ /**
+ * Active this component.
+ */
+ @Activate
+ public void activate() { }
+
+ /**
+ * Deactivate this component.
+ */
+ @Deactivate
+ public void deactivate() {
+ withdrawAllIntents();
+ }
+
+ /**
+ * Get instance of this intentManager.
+ *
+ * @return the instance of this intent manager.
+ */
+ public static McastIntentManager getInstance() {
+ if (instance == null) {
+ instance = new McastIntentManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Install the PointToMultipoint forwarding intent.
+ *
+ * @param mroute multicast route entry
+ * @return the intent that has been set or null otherwise
+ */
+ public SinglePointToMultiPointIntent setIntent(McastRoute mroute) {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+ if (mroute.getIngressPoint() == null ||
+ mroute.getEgressPoints().isEmpty()) {
+ return null;
+ }
+
+ /*
+ * Match the group AND source addresses. We will also check ether type to
+ * determine if we are doing ipv4 or ipv6.
+ *
+ * If we really wanted to be pendantic we could put in a
+ * condition to make sure the ethernet MAC address was also
+ * mcast.
+ */
+ selector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(mroute.getGaddr())
+ .matchIPSrc(mroute.getSaddr());
+
+ SinglePointToMultiPointIntent intent =
+ SinglePointToMultiPointIntent.builder()
+ .appId(McastForwarding.getAppId())
+ .selector(selector.build())
+ .treatment(treatment)
+ .ingressPoint(mroute.getIngressPoint().getConnectPoint())
+ .egressPoints(mroute.getEgressConnectPoints()).
+ build();
+
+ intentService.submit(intent);
+ return intent;
+ }
+
+ /**
+ * Withdraw the intent represented by this route.
+ *
+ * @param mroute the mcast route whose intent we want to remove
+ */
+ public void withdrawIntent(McastRouteBase mroute) {
+ Intent intent = intentService.getIntent(mroute.getIntentKey());
+ intentService.withdraw(intent);
+ }
+
+ /**
+ * Withdraw all intents.
+ *
+ * This will be called from the deactivate method so we don't leave
+ * a mess behind us after we leave.
+ */
+ public void withdrawAllIntents() {
+ for (Intent intent : intentService.getIntents()) {
+ intentService.withdraw(intent);
+ }
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java
new file mode 100644
index 00000000..12b7e6d4
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+
+import java.util.Set;
+
+/**
+ * This McastRouteBase interface is implemented by the McastRouteBase class which
+ * in turn acts as the base class for both the McastRouteGroup and McastRouteSource.
+ */
+interface McastRoute {
+
+ /**
+ * Gets the group addresses.
+ *
+ * @return group address
+ */
+ public IpPrefix getGaddr();
+
+ /**
+ * Gets the source address.
+ *
+ * @return the source address
+ */
+ public IpPrefix getSaddr();
+
+ /**
+ * Determines if this is an IPv4 multicast route.
+ *
+ * @return true if it is an IPv4 route
+ */
+ public boolean isIp4();
+
+ /**
+ * Determines if this is an IPv6 multicast route.
+ *
+ * @return true if it is an IPv6 route
+ */
+ public boolean isIp6();
+
+ /**
+ * Add the ingress ConnectPoint.
+ *
+ * @param cpstr string representing a ConnectPoint
+ * @return whether ingress has been added, only add if ingressPoint is null
+ */
+ public boolean addIngressPoint(String cpstr);
+
+ /**
+ * Add the ingress ConnectPoint.
+ *
+ * @param cp the ConnectPoint of incoming traffic.
+ * @return whether ingress has been added, only add if ingressPoint is null
+ */
+ public boolean addIngressPoint(ConnectPoint cp);
+
+ /**
+ * Get the ingress connect point.
+ *
+ * @return the ingress connect point
+ */
+ public McastConnectPoint getIngressPoint();
+
+ /**
+ * Add an egress connect point.
+ *
+ * @param cp the egress McastConnectPoint to be added
+ * @return return the McastConnectPoint
+ */
+ public McastConnectPoint addEgressPoint(ConnectPoint cp);
+
+ /**
+ * Add an egress connect point.
+ *
+ * @param connectPoint deviceId/portNum
+ * @return return the McastConnectPoint
+ */
+ public McastConnectPoint addEgressPoint(String connectPoint);
+
+ /**
+ * Add an egress connect point.
+ *
+ * @param cp the egress McastConnectPoint to be added
+ * @param interest the protocol that has shown interest in this route
+ * @return return the McastConnectPoint
+ */
+ public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest);
+
+ /**
+ * Add an egress connect point.
+ *
+ * @param connectPoint deviceId/portNum
+ * @param interest the protocol that has shown interest in this route
+ * @return return the McastConnectPoint
+ */
+ public McastConnectPoint addEgressPoint(String connectPoint, McastConnectPoint.JoinSource interest);
+
+ /**
+ * Get the egress connect points.
+ *
+ * @return a set of egress connect points
+ */
+ public Set<McastConnectPoint> getEgressPoints();
+
+ /**
+ * Get the egress connect points.
+ *
+ * @return a set of egress connect points
+ */
+ public Set<ConnectPoint> getEgressConnectPoints();
+
+ /**
+ * Find the egress connect point if it exists.
+ *
+ * @param cp ConnectPoint to search for
+ * @return the connect point when found, null otherwise.
+ */
+ public McastConnectPoint findEgressConnectPoint(ConnectPoint cp);
+
+ /**
+ * remove Interest from a McastConnectPoint.
+ *
+ * @param mcp connect point.
+ * @param interest the protocol interested in this multicast stream
+ * @return whether or not interest was removed
+ */
+ public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest);
+
+ /**
+ * Increment the punt count.
+ */
+ public void incrementPuntCount();
+
+ /**
+ * Get the punt count.
+ *
+ * @return the punt count
+ */
+ public int getPuntCount();
+
+ /**
+ * Have the McastIntentManager create an intent, attempt to
+ * install the intent and then save the key.
+ */
+ public void setIntent();
+
+ /**
+ * Set the Intent key.
+ *
+ * @param intent intent
+ */
+ public void setIntent(SinglePointToMultiPointIntent intent);
+
+ /**
+ * Withdraw the intent if it has been installed.
+ */
+ public void withdrawIntent();
+
+ /**
+ * Get the intent key.
+ *
+ * @return the intentKey
+ */
+ public Key getIntentKey();
+
+ /**
+ * Pretty print the the route.
+ *
+ * @return a pretty string
+ */
+ public String toString();
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
new file mode 100644
index 00000000..730acfa7
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.commons.collections.set.ListOrderedSet;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.Key;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * McastRouteBase base class for McastRouteGroup and McastRouteSource.
+ */
+public class McastRouteBase implements McastRoute {
+ protected final IpPrefix gaddr;
+ protected final IpPrefix saddr;
+
+ protected McastConnectPoint ingressPoint;
+ protected Set<McastConnectPoint> egressPoints;
+
+ protected boolean isGroup = false;
+
+ protected boolean dirty = false;
+
+ /**
+ * How may times has this packet been punted.
+ */
+ private int puntCount = 0;
+
+ /**
+ * If the intentKey is null that means no intent has
+ * been installed.
+ */
+ protected Key intentKey = null;
+
+ /**
+ * Create a multicast route. This is the parent class for both the Group
+ * and the source.
+ *
+ * @param saddr source address
+ * @param gaddr multicast group address
+ */
+ public McastRouteBase(String saddr, String gaddr) {
+ this.gaddr = IpPrefix.valueOf(checkNotNull(gaddr));
+ if (saddr == null || saddr.equals("*")) {
+ this.saddr = IpPrefix.valueOf(0, 0);
+ } else {
+ this.saddr = IpPrefix.valueOf(checkNotNull(gaddr));
+ }
+ this.init();
+ }
+
+ /**
+ * Create a multicast group table entry.
+ * @param gaddr multicast group address
+ */
+ public McastRouteBase(String gaddr) {
+ this("*", gaddr);
+ }
+
+ /**
+ * Set the source and group address value of a (*, G) group.
+ *
+ * @param gpfx the group prefix address
+ */
+ public McastRouteBase(IpPrefix gpfx) {
+ this(IpPrefix.valueOf(0, 0), gpfx);
+ }
+
+ /**
+ * Create a multicast route constructor.
+ *
+ * @param saddr source address
+ * @param gaddr group address
+ */
+ public McastRouteBase(IpPrefix saddr, IpPrefix gaddr) {
+ this.saddr = checkNotNull(saddr);
+ this.gaddr = checkNotNull(gaddr);
+
+ this.init();
+ }
+
+ private void init() {
+ this.isGroup = (this.saddr.prefixLength() == 0);
+ this.ingressPoint = null;
+ this.egressPoints = new HashSet();
+ }
+
+ /**
+ * Get the multicast group address.
+ *
+ * @return the multicast group address
+ */
+ @Override
+ public IpPrefix getGaddr() {
+ return gaddr;
+ }
+
+ /**
+ * Get the multicast source address.
+ *
+ * @return the multicast source address
+ */
+ @Override
+ public IpPrefix getSaddr() {
+ return saddr;
+ }
+
+ /**
+ * Is this an IPv4 multicast route.
+ *
+ * @return true if it is an IPv4 route
+ */
+ @Override
+ public boolean isIp4() {
+ return gaddr.isIp4();
+ }
+
+ /**
+ * Is this an IPv6 multicast route.
+ *
+ * @return true if it is an IPv6 route
+ */
+ @Override
+ public boolean isIp6() {
+ return gaddr.isIp6();
+ }
+
+ /**
+ * Is this a multicast group route?
+ *
+ * @return true if it is a multicast group route.
+ */
+ public boolean isGroup() {
+ return isGroup;
+ }
+
+ /**
+ * @return true if this is (S, G) false if it (*, G).
+ */
+ public boolean isSource() {
+ return (!isGroup);
+ }
+
+ /**
+ * Get the dirty state.
+ *
+ * @return whether this route is dirty or not.
+ */
+ public boolean getDirty() {
+ return this.dirty;
+ }
+
+ /**
+ * Set the dirty state to indicate that something changed.
+ * This may require an update to the flow tables (intents).
+ *
+ * @param dirty set the dirty bit
+ */
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ }
+
+ /**
+ * Add an ingress point to this route.
+ *
+ * @param ingress incoming connect point
+ * @return whether ingress has been added, only add if ingressPoint is null
+ */
+ public boolean addIngressPoint(ConnectPoint ingress) {
+
+ // Do NOT add the ingressPoint if it is not null.
+ if (this.ingressPoint != null) {
+ // TODO: Log an warning.
+ return false;
+ }
+ this.ingressPoint = new McastConnectPoint(checkNotNull(ingress));
+ setDirty(true);
+ return true;
+ }
+
+ /**
+ * Add or modify the ingress connect point.
+ *
+ * @param connectPoint string switch device Id
+ * @return whether ingress has been added, only add if ingressPoint is null
+ */
+ public boolean addIngressPoint(String connectPoint) {
+
+ if (this.ingressPoint != null) {
+ // TODO: log a warning.
+ return false;
+ }
+ ConnectPoint cp = ConnectPoint.deviceConnectPoint(checkNotNull(connectPoint));
+ return this.addIngressPoint(cp);
+ }
+
+ /**
+ * Get the ingress McastConnectPoint.
+ *
+ * @return the ingress McastConnectPoint
+ */
+ public McastConnectPoint getIngressPoint() {
+ return this.ingressPoint;
+ }
+
+ /**
+ * Add an egress McastConnectPoint.
+ *
+ * @param cp egress connect point
+ * @return return the McastConnectPoint
+ */
+ public McastConnectPoint addEgressPoint(ConnectPoint cp) {
+ McastConnectPoint mcp = this.findEgressConnectPoint(cp);
+ if (mcp == null) {
+ mcp = new McastConnectPoint(checkNotNull(cp));
+ egressPoints.add(mcp);
+ setDirty(true);
+ }
+ return mcp;
+ }
+
+ /**
+ * Add an egress connect point from a string.
+ *
+ * @param connectPoint string representing a connect point
+ * @return the MulticastConnectPoint
+ */
+ public McastConnectPoint addEgressPoint(String connectPoint) {
+ checkNotNull(connectPoint);
+ return this.addEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint));
+ }
+
+ /**
+ * Add an egress McastConnectPoint.
+ *
+ * @param cp the egress connect point
+ * @param interest the source of interest for mcast traffic
+ */
+ public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest) {
+ checkNotNull(cp);
+ checkNotNull(interest);
+ McastConnectPoint mcp = this.addEgressPoint(cp);
+ if (mcp != null) {
+ mcp.interest.add(interest);
+ setDirty(true);
+ }
+ return mcp;
+ }
+
+ /**
+ * Add an egress McastConnectPoint.
+ *
+ * @param cpstr deviceId/port of the connect point
+ */
+ public McastConnectPoint addEgressPoint(String cpstr, McastConnectPoint.JoinSource interest) {
+ checkNotNull(cpstr);
+ checkNotNull(interest);
+ return this.addEgressPoint(ConnectPoint.deviceConnectPoint(cpstr), interest);
+ }
+
+ /**
+ * Get egress connect points for the route.
+ *
+ * @return Set of egress connect points
+ */
+ public Set<McastConnectPoint> getEgressPoints() {
+ return egressPoints;
+ }
+
+ /**
+ * Get egress McastConnectPoints points as ConnectPoints for intent system.
+ *
+ * @return Set of egress ConnectPoints
+ */
+ public Set<ConnectPoint> getEgressConnectPoints() {
+ Set<ConnectPoint> cps = new ListOrderedSet();
+
+ for (McastConnectPoint mcp : egressPoints) {
+ cps.add(mcp.getConnectPoint());
+ }
+ return cps;
+ }
+
+ /**
+ * Find the Multicast Connect Point that contains the ConnectPoint.
+ *
+ * @param cp the regular ConnectPoint to match
+ * @return the McastConnectPoint that contains cp or null if not found.
+ */
+ public McastConnectPoint findEgressConnectPoint(ConnectPoint cp) {
+ for (McastConnectPoint mcp : this.egressPoints) {
+ if (mcp.getConnectPoint().equals(cp)) {
+ return mcp;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Remove specified interest from the given ConnectPoint.
+ *
+ * @param mcp connect point.
+ * @param interest the protocol interested in this multicast stream
+ * @return true if removed, false otherwise
+ */
+ public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest) {
+ checkNotNull(mcp);
+ if (mcp.interest.contains(interest)) {
+ mcp.interest.remove(interest);
+ setDirty(true);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the number of times the packet has been punted.
+ *
+ * @return the punt count
+ */
+ @Override
+ public int getPuntCount() {
+ return puntCount;
+ }
+
+ /**
+ * Increment the punt count.
+ *
+ * TODO: we need to handle wrapping.
+ */
+ @Override
+ public void incrementPuntCount() {
+ puntCount++;
+ }
+
+ /**
+ * Have the McastIntentManager create and set the intent, then save the intent key.
+ *
+ * If we already have an intent, we will first withdraw the existing intent and
+ * replace it with a new one. This will support the case where the ingress connectPoint
+ * or group of egress connectPoints change.
+ */
+ @Override
+ public void setIntent() {
+ if (this.intentKey != null) {
+ this.withdrawIntent();
+ }
+ McastIntentManager im = McastIntentManager.getInstance();
+ SinglePointToMultiPointIntent intent = im.setIntent(this);
+ this.intentKey = intent.key();
+ }
+
+ /**
+ * Set the Intent key.
+ *
+ * @param intent the multicast intent
+ */
+ @Override
+ public void setIntent(SinglePointToMultiPointIntent intent) {
+ intentKey = intent.key();
+ }
+
+ /**
+ * Get the intent key represented by this route.
+ *
+ * @return intentKey
+ */
+ @Override
+ public Key getIntentKey() {
+ return this.intentKey;
+ }
+
+
+ /**
+ * Withdraw the intent and set the key to null.
+ */
+ @Override
+ public void withdrawIntent() {
+ if (intentKey == null) {
+ // nothing to withdraw
+ return;
+ }
+ McastIntentManager im = McastIntentManager.getInstance();
+ im.withdrawIntent(this);
+ this.intentKey = null;
+ }
+
+ /**
+ * Pretty Print this Multicast Route. Works for McastRouteSource and McastRouteGroup.
+ *
+ * @return pretty string of the multicast route
+ */
+ @Override
+ public String toString() {
+ String out = String.format("(%s, %s)\n\t",
+ saddr.toString(), gaddr.toString());
+
+ out += "intent: ";
+ out += (intentKey == null) ? "not installed" : this.intentKey.toString();
+ out += "\n\tingress: ";
+ out += (ingressPoint == null) ? "NULL" : ingressPoint.toString();
+ out += "\n\tegress: {\n";
+ if (egressPoints != null && !egressPoints.isEmpty()) {
+ for (McastConnectPoint eg : egressPoints) {
+ out += "\t\t" + eg.getConnectPoint().toString() + "\n";
+ }
+ }
+ out += ("\t}\n");
+ out += ("\tpunted: " + this.getPuntCount() + "\n");
+ return out;
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java
new file mode 100644
index 00000000..4a58e1b1
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.HashMap;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * The McastRouteGroup extends the McastRouteBase class and serves two purposes:
+ * first it represents a (*, G) multicast route entry. Second it serves
+ * as a container for all (S, G) multicast route entries that belong
+ * to the same group address.
+ */
+public class McastRouteGroup extends McastRouteBase {
+ private HashMap<IpPrefix, McastRouteSource> sources;
+
+ /**
+ * Class constructor.
+ *
+ * @param gaddr - String representation of group address.
+ */
+ public McastRouteGroup(String gaddr) {
+ super(checkNotNull(gaddr));
+ this.init();
+ }
+
+ /**
+ * Create a multicast group.
+ *
+ * @param gpfx - Group address
+ */
+ public McastRouteGroup(IpPrefix gpfx) {
+ super(checkNotNull(gpfx));
+ this.init();
+ }
+
+ /**
+ * Common initialization used by constructors.
+ */
+ private void init() {
+ this.sources = new HashMap();
+ super.isGroup = true;
+ }
+
+ /**
+ * Find a specific multicast source address for this group.
+ *
+ * @param saddr the source address
+ * @return the multicast source route or null if it does not exist
+ */
+ public McastRouteSource findSource(IpPrefix saddr) {
+ return this.sources.get(checkNotNull(saddr));
+ }
+
+ /**
+ * Return the entire set of multicast sources for this group.
+ *
+ * @return the set of multicast sources
+ */
+ public HashMap<IpPrefix, McastRouteSource> getSources() {
+ return this.sources;
+ }
+
+ /**
+ * Add a new McastRouteSource to this group.
+ *
+ * @param src the multicast source
+ */
+ public void addSource(McastRouteSource src) {
+ checkNotNull(src);
+ this.sources.put(src.getSaddr(), src);
+ }
+
+ /**
+ * Remove the source with this specific IpPrefix from this group entry.
+ *
+ * @param spfx IP Prefix of the source to be removed
+ * @return the source route that was just removed
+ */
+ public McastRouteSource removeSource(IpPrefix spfx) {
+ McastRouteSource src = this.sources.remove(spfx);
+ src.withdrawIntent();
+ return src;
+ }
+
+ /**
+ * Remove all sources from this.
+ */
+ public void removeSources() {
+ for (McastRouteSource src : this.sources.values()) {
+ src.withdrawIntent();
+ this.sources.remove(src.getSaddr());
+ }
+ }
+
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java
new file mode 100644
index 00000000..68edc2e0
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * This class represents and specific multicast senders source address. Objects from
+ * this class will belong to the sources collection of the multicast group.
+ */
+public class McastRouteSource extends McastRouteBase {
+
+ // A reference to our parent group
+ private McastRouteGroup group;
+
+ /**
+ * Create a multicast source with IpPrefixes.
+ *
+ * @param source the source address
+ * @param group the group address
+ */
+ public McastRouteSource(IpPrefix source, IpPrefix group) {
+ super(checkNotNull(source), checkNotNull(group));
+ }
+
+ /**
+ * Set our parent multicast group.
+ *
+ * @param group the group this source belongs to
+ */
+ public void setGroup(McastRouteGroup group) {
+ this.group = group;
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
new file mode 100644
index 00000000..5a07bec7
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpPrefix;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * The Mcast Route Table holds all multicast state for the controller.
+ *
+ * State for IPv4 and IPv6 are maintained. The tables are sets of McastRouteGroup
+ * structures that represent (*, G) state with a series of egress ConnectPoints.
+ * Each (*, G) may also have a set of (S, G) that may have there own set of
+ * ingress and egress ConnectPoints.
+ *
+ * TODO: perhaps should probably create two separate singleton for IPv4 and IPv6 respectively.
+ */
+@Service(value = org.onosproject.mfwd.impl.McastRouteTable.class)
+public final class McastRouteTable {
+
+ /*
+ * Create a map of the McastGroups indexed by the multicast group prefix.
+ * We may choose to change the map data structure in to some form a radix trie
+ * depending on the type of real world usage we see.
+ */
+ private final Map<IpPrefix, McastRouteGroup> mrib4;
+ private final Map<IpPrefix, McastRouteGroup> mrib6;
+ private static McastRouteTable instance = null;
+
+ private Boolean ipv6Enabled = false;
+
+ /**
+ * Create the two v4 & v6 tables.
+ */
+ private McastRouteTable() {
+ mrib4 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
+ if (ipv6Enabled) {
+ mrib6 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
+ } else {
+ mrib6 = null;
+ }
+ }
+
+ /**
+ * Get the single instance of this multicast group address.
+ *
+ * @return the multicast route table
+ */
+ public static McastRouteTable getInstance() {
+ if (instance == null) {
+ instance = new McastRouteTable();
+ }
+ return instance;
+ }
+
+ /**
+ * Get the IPv4 MRIB.
+ *
+ * @return the IPv4 MRIB
+ */
+ public Map<IpPrefix, McastRouteGroup> getMrib4() {
+ return mrib4;
+ }
+
+ /**
+ * Get the IPv6 MRIB.
+ *
+ * @return Return the set of prefix keyed McastGroups
+ */
+ public Map<IpPrefix, McastRouteGroup> getMrib6() {
+ return mrib6;
+ }
+
+ /**
+ * Save the McastRouteGroup in the address family appropriate mrib.
+ *
+ * @param group The McastRouteGroup to save
+ */
+ private void storeGroup(McastRouteGroup group) {
+ if (group.isIp4()) {
+ mrib4.put(group.getGaddr(), group);
+ } else if (group.isIp6() && ipv6Enabled) {
+ mrib6.put(group.getGaddr(), group);
+ }
+ }
+
+ /**
+ * Remove the group.
+ *
+ * @param group the group to be removed
+ */
+ private void removeGroup(McastRouteGroup group) {
+ IpPrefix gpfx = group.getGaddr();
+ if (gpfx.isIp4()) {
+ mrib4.remove(gpfx);
+ } else if (gpfx.isIp6() && ipv6Enabled) {
+ mrib6.remove(gpfx);
+ }
+ }
+
+ /**
+ * Add a multicast route to the MRIB. This function will.
+ *
+ * @param saddr source address * or x.x.x.x or x.x.x.x/y
+ * @param gaddr group address x.x.x.x or x.x.x.x/y
+ * @return the multicast route
+ */
+ public McastRouteBase addRoute(String saddr, String gaddr) {
+ IpPrefix gpfx = IpPrefix.valueOf(gaddr);
+ IpPrefix spfx = IpPrefix.valueOf(0, 0);
+ if (saddr != null && !saddr.equals("*")) {
+ spfx = IpPrefix.valueOf(saddr);
+ }
+ return addRoute(spfx, gpfx);
+ }
+
+ /**
+ * Add a multicast route to the MRIB. This function will store either
+ * (S, G) or (*, G) in the mrib if an entry does not already exist. If
+ * an entry does exist it is returned to the caller.
+ *
+ * Every (S, G) is stored as part of it's parent group entry which also represents
+ * (*, G) routes. In the case of a (S, G) we will also create the (*, G) entry if needed
+ * then save the (S, G) to the (*, G).
+ *
+ * @param spfx the source prefix
+ * @param gpfx the group prefix
+ * @return the resulting McastRouteSource or McastRouteGroup accordingly.
+ */
+ public McastRouteBase addRoute(IpPrefix spfx, IpPrefix gpfx) {
+
+ /**
+ * If a group route (*, g) does not exist we will need to make so we
+ * can start attaching our sources to the group entry.
+ */
+ McastRouteGroup group = findMcastGroup(gpfx);
+ if (group == null) {
+ group = new McastRouteGroup(gpfx);
+
+ // Save it for later
+ if (gpfx.isIp4()) {
+ this.mrib4.put(gpfx, group);
+ } else if (gpfx.isIp6() && ipv6Enabled) {
+ this.mrib6.put(gpfx, group);
+ }
+ }
+
+ /**
+ * If the source prefix length is 0 then we have our (*, g) entry, we can
+ * just return now.
+ */
+ if (spfx.prefixLength() == 0) {
+ return group;
+ }
+
+ // See if the source already exists. If so just return it.
+ McastRouteSource source = group.findSource(spfx);
+ if (source != null) {
+ return source;
+ }
+
+ /**
+ * We have the group but no source. We need to create the source then add it
+ * to the group.
+ */
+ source = new McastRouteSource(spfx, gpfx);
+
+ // Have the source save it's parent
+ source.setGroup(group);
+
+ // Save this source as part of this group
+ group.addSource(source);
+
+ return source;
+ }
+
+ /**
+ * Delete a multicast route from the MRIB.
+ *
+ * @param saddr source address * or x.x.x.x or x.x.x.x/y
+ * @param gaddr group address x.x.x.x or x.x.x.x/y
+ */
+ public void removeRoute(String saddr, String gaddr) {
+ IpPrefix gpfx = IpPrefix.valueOf(gaddr);
+ IpPrefix spfx = IpPrefix.valueOf(0, 0);
+ if (saddr != null && !saddr.equals("*")) {
+ spfx = IpPrefix.valueOf(saddr);
+ }
+ removeRoute(spfx, gpfx);
+ }
+
+ /**
+ * Remove a multicast route.
+ *
+ * @param spfx the source prefix
+ * @param gpfx the group prefix
+ */
+ public void removeRoute(IpPrefix spfx, IpPrefix gpfx) {
+
+ /**
+ * If a group route (*, g) does not exist we will need to make so we
+ * can start attaching our sources to the group entry.
+ */
+ McastRouteGroup group = findMcastGroup(gpfx);
+ if (group == null) {
+ // The group does not exist, we can't remove it.
+ return;
+ }
+
+ /**
+ * If the source prefix length is 0 then we have a (*, g) entry, which
+ * means we will remove this group and all of it's sources. We will
+ * also withdraw it's intent if need be.
+ */
+ if (spfx.prefixLength() > 0) {
+ group.removeSource(spfx);
+
+ /*
+ * Now a little house keeping. If this group has no more sources
+ * nor egress connectPoints git rid of it.
+ */
+ if (group.getSources().size() == 0 &&
+ group.getEgressPoints().size() == 0) {
+ removeGroup(group);
+ }
+
+ } else {
+ // Group remove has been explicitly requested.
+ group.removeSources();
+ group.withdrawIntent();
+ removeGroup(group);
+ }
+ }
+
+ /**
+ * Find the specific multicast group entry.
+ *
+ * @param group the group address
+ * @return McastRouteGroup the multicast (*, G) group route
+ */
+ public McastRouteGroup findMcastGroup(IpPrefix group) {
+ McastRouteGroup g = null;
+ if (group.isIp4()) {
+ g = mrib4.get(group);
+ } else if (group.isIp6() && ipv6Enabled) {
+ g = mrib6.get(group);
+ }
+ return g;
+ }
+
+ /**
+ * Find the multicast (S, G) entry if it exists.
+ *
+ * @param saddr the source address
+ * @param gaddr the group address
+ * @return The multicast source route entry if it exists, null if it does not.
+ */
+ public McastRouteSource findMcastSource(IpPrefix saddr, IpPrefix gaddr) {
+ McastRouteGroup grp = findMcastGroup(checkNotNull(gaddr));
+ if (grp == null) {
+ return null;
+ }
+ return grp.findSource(saddr);
+ }
+
+ /**
+ * This will first look up a Group entry. If no group entry was found null will
+ * be returned. If the group entry has been found we will then look up the (s, g) entry.
+ * If the (s, g) entry has been found, that will be returned. If no (s, g) was found
+ * the (*, g) group entry will be returned.
+ *
+ * @param saddr the source address
+ * @param gaddr the group address
+ * @return return the best matching McastRouteSource or McastRouteGroup
+ */
+ public McastRoute findBestMatch(IpPrefix saddr, IpPrefix gaddr) {
+ McastRouteGroup grp = this.findMcastGroup(checkNotNull(gaddr));
+ if (grp == null) {
+ return null;
+ }
+
+ // Found a group now look for a source
+ McastRouteSource src = grp.findSource(checkNotNull(saddr));
+ if (src == null) {
+ return grp;
+ }
+
+ return src;
+ }
+
+ /**
+ * Print out the multicast route table in it's entirety.
+ *
+ * TODO: Eventually we will have to implement paging and how to handle large tables.
+ * @return String
+ */
+ public String printMcastRouteTable() {
+ String out = this.toString() + "\n";
+
+ for (McastRouteGroup grp : mrib4.values()) {
+ out += grp.toString() + "\n";
+ for (McastRouteSource src : grp.getSources().values()) {
+ out += src.toString() + "\n";
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Print out a summary of groups in the MRIB.
+ *
+ * @return String
+ */
+ public String toString() {
+ String out = "Mcast Route Table: ";
+ out += mrib4.size() + " IPv4 Multicast Groups\n";
+ if (ipv6Enabled) {
+ out += mrib6.size() + " IPv6 Multicast Groups\n";
+ }
+ return out;
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java
new file mode 100644
index 00000000..eaef5fcf
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Sample Multicast forwarding framework using intents.
+ */
+package org.onosproject.mfwd.impl;
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java
new file mode 100644
index 00000000..608e0447
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.rest;
+
+import java.io.IOException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onosproject.mfwd.impl.McastConnectPoint;
+import org.onosproject.mfwd.impl.McastRouteTable;
+import org.onosproject.mfwd.impl.McastRouteBase;
+import org.onosproject.mfwd.impl.MRibCodec;
+import org.onosproject.rest.AbstractWebResource;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Rest API for Multicast Forwarding.
+ */
+@Path("mcast")
+public class McastResource extends AbstractWebResource {
+
+ private final Logger log = getLogger(getClass());
+ private static final String SOURCE_ADDRESS = "sourceAddress";
+ private static final String GROUP_ADDRESS = "groupAddress";
+ private static final String INGRESS_POINT = "ingressPoint";
+ private static final String EGRESS_POINT = "egressPoint";
+ private static final String MCAST_GROUP = "mcastGroup";
+
+ /**
+ * Retrieve the multicast route table.
+ *
+ * @return the multicast route table.
+ * @throws IOException if an error occurs
+ */
+ @Path("show")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response showAll() throws IOException {
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ ObjectNode pushContent = new MRibCodec().encode(mrt , this);
+ return ok(pushContent.toString()).build();
+ }
+
+ /**
+ * Static join a multicast flow.
+ *
+ * @param sAddr source address to join
+ * @param gAddr group address to join
+ * @param ports ingress and egress ConnectPoints to join
+ * @return the Result of the join
+ * @throws IOException if something failed with the join command
+ */
+ @Path("/join")
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response join(@QueryParam("src") String sAddr,
+ @QueryParam("grp") String gAddr,
+ @DefaultValue("") @QueryParam("ports") String ports)
+ throws IOException {
+
+ ObjectMapper mapper = new ObjectMapper();
+ log.debug("Source IP Address: " + sAddr);
+ log.debug("Destination IP Address: " + gAddr);
+ log.debug("Ingress and Egress ports: " + ports);
+
+ String output = "Insertion Faild";
+ if (sAddr != null && gAddr != null && ports != null) {
+
+ String[] portArr = ports.split(",");
+ log.debug("Port Array Length: " + portArr.length);
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ McastRouteBase mr = mrt.addRoute(sAddr, gAddr);
+
+ // Port format "of:0000000000000023/4"
+ log.debug("checking inside outer if: " + portArr.length);
+
+ if (mr != null && portArr != null && portArr.length > 0) {
+
+ String inCP = portArr[0];
+ log.debug("Ingress port provided: " + inCP);
+ mr.addIngressPoint(inCP);
+
+ for (int i = 1; i < portArr.length; i++) {
+ String egCP = portArr[i];
+ log.debug("Egress port provided: " + egCP);
+ mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);
+ }
+ mrt.printMcastRouteTable();
+ output = "Successfully Inserted";
+ }
+ } else {
+ output = "Please Insert the rest uri correctly";
+ }
+ return Response.ok(output).build();
+ }
+
+ /**
+ * Delete multicast state.
+ *
+ * @param src address to be deleted
+ * @param grp address to be deleted
+ * @return status of delete if successful
+ */
+ @Path("/delete")
+ @DELETE
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response removeMcastFlow(@QueryParam("src") String src,
+ @QueryParam("grp") String grp) {
+
+ String resp = "Failed to delete";
+ log.info("Source IP Address to delete: " + src);
+ log.info("Destination IP Address to delete: " + grp);
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ if (src != null && grp != null) {
+ mrt.removeRoute(src, grp);
+ resp = "Deleted flow for src " + src + " and grp " + grp;
+ }
+
+ return Response.ok(resp).build();
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..966cb4f2
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.mfwd.cli.McastJoinCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.mfwd.cli.McastDeleteCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.mfwd.cli.McastShowCommand"/>
+ </command>
+ </command-bundle>
+
+</blueprint>
diff --git a/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..c4c4f459
--- /dev/null
+++ b/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~ Copyright 2014 Open Networking Laboratory
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>ONOS APP MFWD</display-name>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
+ <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
+ </init-param>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.classnames</param-name>
+ <param-value>
+ org.onosproject.mfwd.rest.McastResource
+ </param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+</web-app>
diff --git a/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java b/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
index 85b5de27..8466b95e 100644
--- a/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
+++ b/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
@@ -45,7 +45,8 @@ import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.OpticalCircuitIntent;
import org.onosproject.net.intent.OpticalConnectivityIntent;
import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.net.resource.device.DeviceResourceService;
+import org.onosproject.net.newresource.ResourceService;
+import org.onosproject.net.resource.device.IntentSetMultimap;
import org.onosproject.net.resource.link.LinkResourceAllocations;
import org.onosproject.net.resource.link.LinkResourceService;
import org.onosproject.net.topology.LinkWeight;
@@ -97,11 +98,14 @@ public class OpticalPathProvisioner {
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceResourceService deviceResourceService;
+ protected IntentSetMultimap intentSetMultimap;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkResourceService linkResourceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ResourceService resourceService;
+
private ApplicationId appId;
private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
@@ -292,7 +296,6 @@ public class OpticalPathProvisioner {
.bidirectional(true)
.build();
intents.add(circuitIntent);
- continue;
} else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
// Create lightpath
// FIXME: hardcoded ODU signal type
@@ -304,7 +307,6 @@ public class OpticalPathProvisioner {
.bidirectional(true)
.build();
intents.add(opticalIntent);
- continue;
} else {
log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
return Collections.emptyList();
@@ -377,13 +379,13 @@ public class OpticalPathProvisioner {
private void releaseResources(Intent intent) {
LinkResourceAllocations lra = linkResourceService.getAllocations(intent.id());
if (intent instanceof OpticalConnectivityIntent) {
- deviceResourceService.releasePorts(intent.id());
+ resourceService.release(intent.id());
if (lra != null) {
linkResourceService.releaseResources(lra);
}
} else if (intent instanceof OpticalCircuitIntent) {
- deviceResourceService.releasePorts(intent.id());
- deviceResourceService.releaseMapping(intent.id());
+ resourceService.release(intent.id());
+ intentSetMultimap.releaseMapping(intent.id());
if (lra != null) {
linkResourceService.releaseResources(lra);
}
diff --git a/framework/src/onos/apps/pim/pom.xml b/framework/src/onos/apps/pim/pom.xml
new file mode 100644
index 00000000..83a366b6
--- /dev/null
+++ b/framework/src/onos/apps/pim/pom.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-pim</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Protocol Independent Multicast Emulation</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.pim</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- This is needed by ComponentContext, used for tunable configuration -->
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.8</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>
+ ${project.groupId}.${project.artifactId}
+ </Bundle-SymbolicName>
+ <Import-Package>
+ org.slf4j,
+ org.osgi.framework,
+ org.apache.commons.lang.math.*,
+ com.google.common.*,
+ org.apache.karaf.shell.commands,
+ org.apache.karaf.shell.console,
+ org.onlab.packet.*,
+ org.onlab.rest.*,
+ org.onosproject.*,
+ org.onosproject.mfwd.impl.*;
+ org.onlab.util.*,
+ org.jboss.netty.util.*
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
new file mode 100644
index 00000000..0ef7e389
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.pim.impl.PIMNeighbors;
+import org.onosproject.pim.impl.PIMNeighborsCodec;
+
+import java.util.HashMap;
+
+@Command(scope = "onos", name = "pim-neighbors", description = "Displays the pim neighbors")
+public class PIMShowCommand extends AbstractShellCommand {
+
+ // prints either the json or cli version of the hash map connect point
+ // neighbors from the PIMNeighbors class.
+ @Override
+ protected void execute() {
+ // grab connect point neighbors hash map to send in to json encoder.
+ HashMap<ConnectPoint, PIMNeighbors> pimNbrs = PIMNeighbors.getConnectPointNeighbors();
+ if (outputJson()) {
+ print("%s", json(pimNbrs));
+ } else {
+ print(PIMNeighbors.printPimNeighbors());
+ }
+ }
+
+ private JsonNode json(HashMap<ConnectPoint, PIMNeighbors> pimNbrs) {
+ return new PIMNeighborsCodec().encode(pimNbrs, this);
+ }
+
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java
new file mode 100644
index 00000000..954dacbb
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * PIM Multicast forwarding framework using intents.
+ */
+package org.onosproject.pim.cli; \ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
new file mode 100644
index 00000000..bd5e1486
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.PIM;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * Protocol Independent Multicast Emulation.
+ */
+@Component(immediate = true)
+public class PIMComponent {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private PIMPacketProcessor processor = new PIMPacketProcessor();
+ private static ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.pim");
+
+ packetService.addProcessor(processor, PacketProcessor.director(1));
+
+ // Build a traffic selector for all multicast traffic
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPProtocol(IPv4.PROTOCOL_PIM);
+ packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ packetService.removeProcessor(processor);
+ processor = null;
+ log.info("Stopped");
+ }
+
+ /**
+ * Packet processor responsible for handling IGMP packets.
+ */
+ private class PIMPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ // Stop processing if the packet has been handled, since we
+ // can't do any more to it.
+ if (context.isHandled()) {
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ if (pkt == null) {
+ return;
+ }
+
+ Ethernet ethPkt = pkt.parsed();
+ if (ethPkt == null) {
+ return;
+ }
+
+ /*
+ * IPv6 MLD packets are handled by ICMP6. We'll only deal
+ * with IPv4.
+ */
+ if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
+ return;
+ }
+
+ IPv4 ip = (IPv4) ethPkt.getPayload();
+ IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+ IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+ log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() +
+ "\tingress port: " + context.inPacket().receivedFrom().toString());
+
+ if (ip.getProtocol() != IPv4.PROTOCOL_PIM) {
+ log.debug("PIM Picked up a non PIM packet: IP protocol: " + ip.getProtocol());
+ return;
+ }
+
+ // TODO: check incoming to be PIM.PIM_ADDRESS or "Our" address.
+ IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+ IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+ PIM pim = (PIM) ip.getPayload();
+ switch (pim.getPimMsgType()) {
+
+ case PIM.TYPE_HELLO:
+ PIMNeighbors.processHello(ethPkt, context.inPacket().receivedFrom());
+ break;
+
+ case PIM.TYPE_JOIN_PRUNE_REQUEST:
+ // Create the function
+ break;
+
+ case PIM.TYPE_ASSERT:
+ case PIM.TYPE_BOOTSTRAP:
+ case PIM.TYPE_CANDIDATE_RP_ADV:
+ case PIM.TYPE_GRAFT:
+ case PIM.TYPE_GRAFT_ACK:
+ case PIM.TYPE_REGISTER:
+ case PIM.TYPE_REGISTER_STOP:
+ log.debug("Unsupported PIM message type: " + pim.getPimMsgType());
+ break;
+
+ default:
+ log.debug("Unkown PIM message type: " + pim.getPimMsgType());
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
new file mode 100644
index 00000000..1a96138f
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in reliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.pim.PIMHello;
+import org.onlab.packet.pim.PIMHelloOption;
+import org.onosproject.net.ConnectPoint;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * PIMNeighbor represents all the PIM routers that have sent us
+ * hello messages, or that possibly have been statically configured.
+ */
+public class PIMNeighbor {
+ private final Logger log = getLogger(getClass());
+
+ // The primary address of this PIM neighbor
+ private IpAddress primaryAddr;
+
+ // The MacAddress of this neighbor
+ private MacAddress macAddress;
+
+ // The ConnectPoint this PIM neighbor is connected to.
+ private ConnectPoint connectPoint;
+
+ // Is this neighbor us?
+ private boolean isThisUs = false;
+
+ // The option values this neighbor has sent us.
+ private int priority = 0;
+ private int genId = 0;
+ private short holdtime = 0;
+
+ // Is this pim neighbor the DR?
+ private boolean isDr = false;
+
+ // Timeout for this neighbor
+ private volatile Timeout timeout;
+
+ private boolean reelect = false;
+
+ // A back pointer the neighbors list this neighbor belongs to.
+ private PIMNeighbors neighbors;
+
+ /**
+ * Construct this neighbor from the address and connect point.
+ *
+ * @param ipaddr IP Address of neighbor
+ * @param macaddr MAC Address of the neighbor
+ * @param cp The ConnectPoint of this neighbor
+ */
+ public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, ConnectPoint cp) {
+ this.macAddress = macaddr;
+ this.primaryAddr = ipaddr;
+ this.connectPoint = cp;
+ this.resetTimeout();
+ }
+
+ /**
+ * Get the primary address of this neighbor.
+ *
+ * @return the primary IP address.
+ */
+ public IpAddress getPrimaryAddr() {
+ return primaryAddr;
+ }
+
+ /**
+ * Set the primary address of this neighbor.
+ *
+ * @param primaryAddr the address we'll use when sending hello messages
+ */
+ public void setPrimaryAddr(IpAddress primaryAddr) {
+ this.primaryAddr = primaryAddr;
+ }
+
+ /**
+ * Get the priority this neighbor has advertised to us.
+ *
+ * @return the priority
+ */
+ public int getPriority() {
+ return priority;
+ }
+
+ /**
+ * Set the priority for this neighbor.
+ *
+ * @param priority This neighbors priority.
+ */
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Get the generation ID.
+ *
+ * @return the generation ID.
+ */
+ public int getGenId() {
+ return genId;
+ }
+
+ /**
+ * Set the generation ID.
+ *
+ * @param genId the generation ID.
+ */
+ public void setGenId(int genId) {
+ this.genId = genId;
+ }
+
+ /**
+ * Get the holdtime for this neighbor.
+ *
+ * @return the holdtime
+ */
+ public short getHoldtime() {
+ return holdtime;
+ }
+
+ /**
+ * Set the holdtime for this neighbor.
+ *
+ * @param holdtime the holdtime.
+ */
+ public void setholdtime(short holdtime) {
+ this.holdtime = holdtime;
+ }
+
+ /**
+ * Is this neighbor the designated router on this connect point?
+ *
+ * @return true if so, false if not.
+ */
+ public boolean isDr() {
+ return isDr;
+ }
+
+ /**
+ * Set this router as the designated router on this connect point.
+ *
+ * @param isDr True is this neighbor is the DR false otherwise
+ */
+ public void setIsDr(boolean isDr) {
+ this.isDr = isDr;
+ }
+
+ /**
+ * The ConnectPoint this neighbor is connected to.
+ *
+ * @return the ConnectPoint
+ */
+ public ConnectPoint getConnectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Set the ConnectPoint this router is connected to.
+ *
+ * @param connectPoint the ConnectPoint this router is connected to.
+ */
+ public void setConnectPoint(ConnectPoint connectPoint) {
+ this.connectPoint = connectPoint;
+ }
+
+ /**
+ * Set a back pointer to the neighbors list this neighbor is a member of.
+ *
+ * @param neighbors the neighbor list this neighbor belongs to
+ */
+ public void setNeighbors(PIMNeighbors neighbors) {
+ this.neighbors = neighbors;
+ }
+
+ /**
+ * We have received a fresh hello from a neighbor, now we need to process it.
+ * Depending on the values received in the the hello options may force a
+ * re-election process.
+ *
+ * We will also refresh the timeout for this neighbor.
+ *
+ * @param hello copy of the hello we'll be able to extract options from.
+ */
+ public void refresh(PIMHello hello) {
+ checkNotNull(hello);
+
+ for (PIMHelloOption opt : hello.getOptions().values()) {
+
+ int len = opt.getOptLength();
+ byte [] value = new byte[len];
+ ByteBuffer bb = ByteBuffer.wrap(value);
+
+ switch (opt.getOptType()) {
+ case PIMHelloOption.OPT_GENID:
+ int newid = bb.getInt();
+ if (this.genId != newid) {
+ // TODO: we have a newly rebooted neighbor. Send them our joins.
+ this.genId = newid;
+ }
+ break;
+
+ case PIMHelloOption.OPT_PRIORITY:
+ int newpri = bb.getInt();
+ if (this.priority != newpri) {
+
+ // The priorities have changed. We may need to re-elect a new DR?
+ if (this.isDr || this.neighbors.getDesignatedRouter().getPriority() < priority) {
+ reelect = true;
+ }
+ this.priority = newpri;
+ }
+ break;
+
+ case PIMHelloOption.OPT_HOLDTIME:
+ short holdtime = bb.getShort();
+ if (this.holdtime != holdtime) {
+ this.holdtime = holdtime;
+ if (holdtime == 0) {
+ // We have a neighbor going down. We can remove all joins
+ // we have learned from them.
+ // TODO: What else do we need to do when a neighbor goes down?
+
+ log.debug("PIM Neighbor has timed out: {}", this.primaryAddr.toString());
+ return;
+ }
+ }
+ break;
+
+ case PIMHelloOption.OPT_PRUNEDELAY:
+ case PIMHelloOption.OPT_ADDRLIST:
+ // TODO: implement prune delay and addr list. Fall through for now.
+
+ default:
+ log.debug("PIM Hello option type: {} not yet supported or unknown.", opt.getOptType());
+ break;
+ }
+ }
+
+ if (reelect) {
+ this.neighbors.electDR(this);
+ }
+
+ // Reset the next timeout timer
+ this.resetTimeout();
+ }
+
+ /* --------------------------------------- Timer functions -------------------------- */
+
+ /**
+ * Restart the timeout task for this neighbor.
+ */
+ private void resetTimeout() {
+
+ if (this.holdtime == 0) {
+
+ // Prepare to die.
+ log.debug("shutting down timer for nbr {}", this.primaryAddr.toString());
+ if (this.timeout != null) {
+ this.timeout.cancel();
+ this.timeout = null;
+ }
+ return;
+ }
+
+ // Cancel the existing timeout and start a fresh new one.
+ if (this.timeout != null) {
+ this.timeout.cancel();
+ }
+
+ this.timeout = PIMTimer.getTimer().newTimeout(new NeighborTimeoutTask(this), holdtime, TimeUnit.SECONDS);
+ }
+
+ /**
+ * The task to run when a neighbor timeout expires.
+ */
+ private final class NeighborTimeoutTask implements TimerTask {
+ PIMNeighbor nbr;
+
+ NeighborTimeoutTask(PIMNeighbor nbr) {
+ this.nbr = nbr;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+
+ // TODO: log.debug;
+ PIMNeighbors neighbors = nbr.neighbors;
+ neighbors.removeNeighbor(nbr.getPrimaryAddr());
+ }
+ }
+
+ /**
+ * Stop the timeout timer.
+ *
+ * This happens when we remove the neighbor.
+ */
+ private final void stopTimeout() {
+ this.timeout.cancel();
+ this.timeout = null;
+ }
+
+ @Override
+ public String toString() {
+ String out = "";
+ if (this.isDr) {
+ out += "*NBR:";
+ } else {
+ out += "NBR:";
+ }
+ out += "\tIP: " + this.primaryAddr.toString();
+ out += "\tPr: " + String.valueOf(this.priority);
+ out += "\tHoldTime: " + String.valueOf(this.holdtime);
+ out += "\tGenID: " + String.valueOf(this.genId) + "\n";
+ return out;
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java
new file mode 100644
index 00000000..cad90768
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java
@@ -0,0 +1,395 @@
+
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.PIM;
+import org.onlab.packet.pim.PIMHello;
+import org.onosproject.net.ConnectPoint;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PIMNeighbors is a collection of all neighbors we have received
+ * PIM hello messages from. The main structure is a HashMap indexed
+ * by ConnectPoint with another HashMap indexed on the PIM neighbors
+ * IPAddress, it contains all PIM neighbors attached on that ConnectPoint.
+ */
+public final class PIMNeighbors {
+
+ private static Logger log = LoggerFactory.getLogger("PIMNeighbors");
+
+ /**
+ * This is the global container for all PIM neighbors indexed by ConnectPoints.
+ *
+ * NOTE: We'll have a problem if the same neighbor can show up on two interfaces
+ * but that should never happen.
+ */
+ private static HashMap<ConnectPoint, PIMNeighbors> connectPointNeighbors = new HashMap<>();
+
+ // The connect point these neighbors are connected to.
+ private ConnectPoint connectPoint;
+
+ // Pointer to the current designated router on this ConnectPoint.
+ private PIMNeighbor designatedRouter;
+
+ // The list of neighbors we have learned on this ConnectPoint.
+ private HashMap<IpAddress, PIMNeighbor> neighbors = new HashMap<>();
+
+ /*
+ * TODO: turn ourIpAddress, ourPriority and OurHoldTime into config options.
+ */
+ // The IP address we are using to source our PIM hello messages on this connect Point.
+ private IpAddress ourIpAddress;
+
+ // The priority we use on this ConnectPoint.
+ private int ourPriority = 1;
+
+ // The holdtime we are sending out.
+ private int ourHoldtime = 105;
+
+ // Then generation ID we are sending out. 0 means we need to generate a new random ID
+ private int ourGenid = 0;
+
+ // Hello Timer for sending hello messages per ConnectPoint with neighbors.
+ private volatile Timeout helloTimer;
+
+ // The period of which we will be sending out PIM hello messages.
+ private final int defaultPimHelloInterval = 30; // seconds
+
+ /**
+ * Create PIMNeighbors object per ConnectPoint.
+ *
+ * @param cp the ConnectPoint.
+ * @return PIMNeighbors structure
+ */
+ public static PIMNeighbors getConnectPointNeighbors(ConnectPoint cp) {
+ return connectPointNeighbors.get(cp);
+ }
+
+ /**
+ * Process incoming hello message, we will need the Macaddress and IP address of the sender.
+ *
+ * @param ethPkt the ethernet header
+ * @param receivedFrom the connect point we recieved this message from
+ */
+ public static void processHello(Ethernet ethPkt, ConnectPoint receivedFrom) {
+ checkNotNull(ethPkt);
+ checkNotNull(ethPkt);
+
+ MacAddress srcmac = ethPkt.getSourceMAC();
+ IPv4 ip = (IPv4) ethPkt.getPayload();
+ Ip4Address srcip = Ip4Address.valueOf(ip.getSourceAddress());
+
+ PIM pim = (PIM) ip.getPayload();
+ checkNotNull(pim);
+
+ PIMHello hello = (PIMHello) pim.getPayload();
+ checkNotNull(hello);
+
+ PIMNeighbor nbr = PIMNeighbors.findOrCreate(srcip, srcmac, receivedFrom);
+ if (nbr == null) {
+ log.error("Could not create a neighbor for: {1}", srcip.toString());
+ return;
+ }
+
+ nbr.setConnectPoint(receivedFrom);
+ nbr.refresh(hello);
+ }
+
+ /**
+ * Create a PIM Neighbor.
+ *
+ * @param cp The ConnectPoint this neighbor was found on
+ */
+ public PIMNeighbors(ConnectPoint cp) {
+ this.connectPoint = cp;
+
+ // TODO: use network config to assign address.
+ this.ourIpAddress = IpAddress.valueOf("10.2.2.2");
+ this.addIpAddress(this.ourIpAddress);
+ }
+
+ /**
+ * Create a PIM neighbor.
+ *
+ * @param cp the ConnectPoint this neighbor was found on
+ * @param ourIp the IP address of this neighbor
+ */
+ public PIMNeighbors(ConnectPoint cp, IpAddress ourIp) {
+ this.connectPoint = cp;
+ this.addIpAddress(ourIp);
+ }
+
+ /**
+ * Start the hello timer when we have been given an IP address.
+ *
+ * @param ourIp our IP address.
+ */
+ public void addIpAddress(IpAddress ourIp) {
+ this.startHelloTimer();
+
+ // Kick off the first pim hello packet
+ this.sendHelloPacket();
+ }
+
+ /**
+ * Getter for our IP address.
+ *
+ * @return our IP address.
+ */
+ public IpAddress getOurIpAddress() {
+ return this.ourIpAddress;
+ }
+
+ /**
+ * Get our priority.
+ *
+ * @return our priority.
+ */
+ public int getOurPriority() {
+ return this.ourPriority;
+ }
+
+ /**
+ * Get the neighbor list for this specific connectPoint.
+ *
+ * @return PIM neighbors on this ConnectPoint
+ */
+ public HashMap<IpAddress, PIMNeighbor> getOurNeighborsList() {
+ return this.neighbors;
+ }
+
+ /**
+ * Get the designated router on this connection.
+ *
+ * @return the PIMNeighbor representing the DR
+ */
+ public PIMNeighbor getDesignatedRouter() {
+ return designatedRouter;
+ }
+
+ /**
+ * Are we the DR on this CP?
+ *
+ * @return true if we are, false if not
+ */
+ public boolean weAreTheDr() {
+ return (designatedRouter != null &&
+ designatedRouter.getPrimaryAddr().equals(ourIpAddress));
+ }
+
+ /**
+ * Find the neighbor with the given IP address on this CP.
+ *
+ * @param ipaddr the IP address of the neighbor we are interested in
+ * @return the pim neighbor if it exists
+ */
+ public PIMNeighbor findNeighbor(IpAddress ipaddr) {
+ PIMNeighbor nbr = neighbors.get(ipaddr);
+ return nbr;
+ }
+
+ /**
+ * Add a new PIM neighbor to this list.
+ *
+ * @param nbr the neighbor to be added.
+ */
+ public void addNeighbor(PIMNeighbor nbr) {
+ if (neighbors.containsKey(nbr.getPrimaryAddr())) {
+
+ // TODO: Hmmm, how should this be handled?
+ log.debug("We are adding a neighbor that already exists: {}", nbr.toString());
+ neighbors.remove(nbr.getPrimaryAddr(), nbr);
+ }
+ nbr.setNeighbors(this);
+ neighbors.put(nbr.getPrimaryAddr(), nbr);
+ }
+
+ /**
+ * Remove the neighbor from our neighbor list.
+ *
+ * @param ipaddr the IP address of the neighbor to remove
+ */
+ public void removeNeighbor(IpAddress ipaddr) {
+
+ boolean reelect = (designatedRouter == null || designatedRouter.getPrimaryAddr().equals(ipaddr));
+ if (neighbors.containsKey(ipaddr)) {
+ neighbors.remove(ipaddr);
+ }
+ this.electDR();
+ }
+
+ /**
+ * Remove the given neighbor from the neighbor list.
+ *
+ * @param nbr the nbr to be removed.
+ */
+ public void removeNeighbor(PIMNeighbor nbr) {
+
+ boolean reelect = (designatedRouter == null || nbr.isDr());
+ neighbors.remove(nbr.getPrimaryAddr(), nbr);
+ this.electDR();
+ }
+
+ /**
+ * Elect a new DR on this ConnectPoint.
+ *
+ * @return the PIM Neighbor that wins
+ */
+ public PIMNeighbor electDR() {
+
+ for (PIMNeighbor nbr : this.neighbors.values()) {
+ if (this.designatedRouter == null) {
+ this.designatedRouter = nbr;
+ continue;
+ }
+
+ if (nbr.getPriority() > this.designatedRouter.getPriority()) {
+ this.designatedRouter = nbr;
+ continue;
+ }
+
+ // We could sort in ascending order
+ if (this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) {
+ this.designatedRouter = nbr;
+ continue;
+ }
+ }
+
+ return this.designatedRouter;
+ }
+
+ /**
+ * Elect a new DR given the new neighbor.
+ *
+ * @param nbr the new neighbor to use in DR election.
+ * @return the PIM Neighbor that wins DR election
+ */
+ public PIMNeighbor electDR(PIMNeighbor nbr) {
+
+ // Make sure I have
+ if (this.designatedRouter == null ||
+ this.designatedRouter.getPriority() < nbr.getPriority() ||
+ this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) {
+ this.designatedRouter = nbr;
+ }
+ return this.designatedRouter;
+ }
+
+ /**
+ * Find or create a pim neighbor with a given ip address and connect point.
+ *
+ * @param ipaddr of the pim neighbor
+ * @param mac The mac address of our sending neighbor
+ * @param cp the connect point the neighbor was learned from
+ * @return an existing or new PIM neighbor
+ */
+ public static PIMNeighbor findOrCreate(IpAddress ipaddr, MacAddress mac, ConnectPoint cp) {
+ PIMNeighbors neighbors = connectPointNeighbors.get(cp);
+ if (neighbors == null) {
+ neighbors = new PIMNeighbors(cp);
+ connectPointNeighbors.put(cp, neighbors);
+ }
+
+ PIMNeighbor nbr = neighbors.findNeighbor(ipaddr);
+ if (nbr == null) {
+ nbr = new PIMNeighbor(ipaddr, mac, cp);
+ neighbors.addNeighbor(nbr);
+ neighbors.electDR(nbr);
+ }
+ return nbr;
+ }
+
+ // Returns the connect point neighbors hash map
+ public static HashMap<ConnectPoint, PIMNeighbors> getConnectPointNeighbors() {
+ return connectPointNeighbors;
+ }
+
+ /* ---------------------------------- PIM Hello Timer ----------------------------------- */
+
+ /**
+ * Start a new hello timer for this ConnectPoint.
+ */
+ private void startHelloTimer() {
+ this.helloTimer = PIMTimer.getTimer().newTimeout(
+ new HelloTimer(this),
+ this.defaultPimHelloInterval,
+ TimeUnit.SECONDS);
+
+ log.trace("Started Hello Timer: " + this.ourIpAddress.toString());
+ }
+
+ /**
+ * This inner class handles transmitting a PIM hello message on this ConnectPoint.
+ */
+ private final class HelloTimer implements TimerTask {
+ PIMNeighbors neighbors;
+
+ HelloTimer(PIMNeighbors neighbors) {
+ this.neighbors = neighbors;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+
+ // Send off a hello packet
+ sendHelloPacket();
+
+ // restart the hello timer
+ neighbors.startHelloTimer();
+ }
+ }
+
+ private void sendHelloPacket() {
+ PIMHello hello = new PIMHello();
+
+ // TODO: we will need to implement the network config service to assign ip addresses & options
+ /*
+ hello.createDefaultOptions();
+
+ Ethernet eth = hello.createPIMHello(this.ourIpAddress);
+ hello.sendPacket(this.connectPoint);
+ */
+ }
+
+ /**
+ * prints the connectPointNeighbors list with each neighbor list.
+ *
+ * @return string of neighbors.
+ */
+ public static String printPimNeighbors() {
+ String out = "PIM Neighbors Table: \n";
+
+ for (PIMNeighbors pn: connectPointNeighbors.values()) {
+
+ out += "CP:\n " + pn.toString();
+ for (PIMNeighbor nbr : pn.neighbors.values()) {
+ out += "\t" + nbr.toString();
+ }
+ }
+ return out;
+ }
+
+ @Override
+ public String toString() {
+ String out = "PIM Neighbors: ";
+ if (this.ourIpAddress != null) {
+ out += "IP: " + this.ourIpAddress.toString();
+ } else {
+ out += "IP: *Null*";
+ }
+ out += "\tPR: " + String.valueOf(this.ourPriority) + "\n";
+ return out;
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java
new file mode 100644
index 00000000..ee62eb79
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.HashMap;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PIM neighbors Codec.
+ */
+public class PIMNeighborsCodec extends JsonCodec<HashMap<ConnectPoint, PIMNeighbors>> {
+ // JSON field names
+ //Return Name
+ private static final String CPNBRLIST = "connect_point_list";
+
+ // PIM Neightbors Fields
+ private static final String IP = "ip";
+ private static final String PRIORITY = "priority";
+ private static final String NBRLIST = "neighbor_list";
+
+ // PIM neighbor Files
+ private static final String DR = "designated";
+ private static final String NBR_IP = "ip";
+ private static final String PR = "priority";
+ private static final String HOLDTIME = "hold_time";
+
+ /**
+ * Encode the PIM Neighbors.
+ *
+ * @param cpn ConnectPoint neighbors
+ * @param context encoding context
+ *
+ * @return Encoded neighbors used by CLI and REST
+ */
+ @Override
+ public ObjectNode encode(HashMap<ConnectPoint, PIMNeighbors> cpn, CodecContext context) {
+ checkNotNull(cpn, "Pim Neighbors cannot be null");
+
+ ObjectNode pimNbrJsonCodec = context.mapper().createObjectNode();
+ ArrayNode cpnList = context.mapper().createArrayNode();
+
+ for (PIMNeighbors pn: cpn.values()) {
+ // get the PimNeighbors Obj, contains Neighbors list
+ // create the json object for a single Entry in the Neighbors list
+ ObjectNode cp = context.mapper().createObjectNode();
+ cp.put(IP, pn.getOurIpAddress().toString());
+ cp.put(PRIORITY, String.valueOf(pn.getOurPriority()));
+
+ // create the array for the neighbors list
+ ArrayNode nbrsList = context.mapper().createArrayNode();
+ for (PIMNeighbor nbr : pn.getOurNeighborsList().values()) {
+ nbrsList.add(neighbor(nbr, context));
+ }
+ // adds pim neighbor to list
+ cp.set(NBRLIST, nbrsList);
+ // adds to arraynode which will represent the connect point neighbors hash map.
+ cpnList.add(cp);
+ }
+ pimNbrJsonCodec.set(CPNBRLIST, cpnList);
+ return pimNbrJsonCodec;
+ }
+
+ /**
+ * Encode a single PIM Neighbor.
+ *
+ * @param nbr the neighbor to be encoded
+ * @param context encoding context
+ * @return the encoded neighbor
+ */
+ private ObjectNode neighbor(PIMNeighbor nbr, CodecContext context) {
+ return context.mapper().createObjectNode()
+ .put(DR, Boolean.toString(nbr.isDr()))
+ .put(NBR_IP, nbr.getPrimaryAddr().toString())
+ .put(PR, String.valueOf(nbr.getPriority()))
+ .put(HOLDTIME, String.valueOf(nbr.getHoldtime()));
+ }
+}
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
new file mode 100644
index 00000000..c131a53b
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+
+/**
+ * PIM Timer used for PIM Neighbors.
+ */
+public final class PIMTimer {
+
+ private static volatile HashedWheelTimer timer;
+
+ // Ban public construction
+ private PIMTimer() {
+ }
+
+ /**
+ * Returns the singleton hashed-wheel timer.
+ *
+ * @return hashed-wheel timer
+ */
+ public static HashedWheelTimer getTimer() {
+ if (PIMTimer.timer == null) {
+ initTimer();
+ }
+ return PIMTimer.timer;
+ }
+
+ // Start the PIM timer.
+ private static synchronized void initTimer() {
+ if (PIMTimer.timer == null) {
+
+ // Create and start a new hashed wheel timer, if it does not exist.
+ HashedWheelTimer hwTimer = new HashedWheelTimer();
+ hwTimer.start();
+ PIMTimer.timer = hwTimer;
+ }
+ }
+}
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java
new file mode 100644
index 00000000..29d1ce4e
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * PIM Emulation speak hello messages and listen to Join/Prunes.
+ */
+package org.onosproject.pim.impl;
diff --git a/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml
new file mode 100644
index 00000000..c30e3792
--- /dev/null
+++ b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.pim.cli.PIMShowCommand"/>
+ </command>
+ </command-bundle>
+
+</blueprint>
diff --git a/framework/src/onos/apps/pom.xml b/framework/src/onos/apps/pom.xml
index 334c690b..b955130a 100644
--- a/framework/src/onos/apps/pom.xml
+++ b/framework/src/onos/apps/pom.xml
@@ -53,11 +53,12 @@
<module>olt</module>
<module>cip</module>
<module>flowanalyzer</module>
- <module>vtnrsc</module>
<module>vtn</module>
- <module>vtnweb</module>
<module>dhcp</module>
<module>cordvtn</module>
+ <module>mfwd</module>
+ <module>igmp</module>
+ <module>pim</module>
</modules>
<properties>
diff --git a/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java b/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
index 2eb96df2..2eb1d5ec 100644
--- a/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
+++ b/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
@@ -23,6 +23,8 @@ import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@@ -214,27 +216,35 @@ public class ProxyArp {
if (context.isHandled()) {
return;
}
- // If IPv6 NDP is disabled, don't handle IPv6 frames.
+
InboundPacket pkt = context.inPacket();
Ethernet ethPkt = pkt.parsed();
if (ethPkt == null) {
return;
}
- if (!ipv6NeighborDiscovery && (ethPkt.getEtherType() == TYPE_IPV6)) {
- return;
+
+ if (ethPkt.getEtherType() == TYPE_ARP) {
+ //handle the arp packet.
+ proxyArpService.handlePacket(context);
+ } else if (ipv6NeighborDiscovery && ethPkt.getEtherType() == TYPE_IPV6) {
+ IPv6 ipv6Pkt = (IPv6) ethPkt.getPayload();
+ if (ipv6Pkt.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
+ ICMP6 icmp6Pkt = (ICMP6) ipv6Pkt.getPayload();
+ if (icmp6Pkt.getIcmpType() == NEIGHBOR_SOLICITATION ||
+ icmp6Pkt.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
+ // handle ICMPv6 solicitations and advertisements
+ proxyArpService.handlePacket(context);
+ }
+ }
}
+ // FIXME why were we listening to IPv4 frames at all?
// Do not ARP for multicast packets. Let mfwd handle them.
if (ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
if (ethPkt.getDestinationMAC().isMulticast()) {
return;
}
}
-
- //handle the arp packet.
- proxyArpService.handlePacket(context);
}
}
}
-
-
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java
new file mode 100644
index 00000000..01f4f700
--- /dev/null
+++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.reactive.routing;
+
+/**
+ * Specifies the type of an IP address or an IP prefix location.
+ */
+enum LocationType {
+ /**
+ * The location of an IP address or an IP prefix is in local SDN network.
+ */
+ LOCAL,
+ /**
+ * The location of an IP address or an IP prefix is outside local SDN network.
+ */
+ INTERNET,
+ /**
+ * There is no route for this IP address or IP prefix.
+ */
+ NO_ROUTE
+}
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java
new file mode 100644
index 00000000..8e86056e
--- /dev/null
+++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.reactive.routing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.IntentRequestListener;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.config.RoutingConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * FIB component for reactive routing intents.
+ */
+public class ReactiveRoutingFib implements IntentRequestListener {
+
+ private static final int PRIORITY_OFFSET = 100;
+ private static final int PRIORITY_MULTIPLIER = 5;
+ protected static final ImmutableList<Constraint> CONSTRAINTS
+ = ImmutableList.of(new PartialFailureConstraint());
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final ApplicationId appId;
+ private final HostService hostService;
+ private final RoutingConfigurationService configService;
+ private final InterfaceService interfaceService;
+ private final IntentSynchronizationService intentSynchronizer;
+
+ private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+ /**
+ * Class constructor.
+ *
+ * @param appId application ID to use to generate intents
+ * @param hostService host service
+ * @param configService routing configuration service
+ * @param interfaceService interface service
+ * @param intentSynchronizer intent synchronization service
+ */
+ public ReactiveRoutingFib(ApplicationId appId, HostService hostService,
+ RoutingConfigurationService configService,
+ InterfaceService interfaceService,
+ IntentSynchronizationService intentSynchronizer) {
+ this.appId = appId;
+ this.hostService = hostService;
+ this.configService = configService;
+ this.interfaceService = interfaceService;
+ this.intentSynchronizer = intentSynchronizer;
+
+ routeIntents = Maps.newConcurrentMap();
+ }
+
+ @Override
+ public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
+ checkNotNull(hostIpAddress);
+ Set<ConnectPoint> ingressPoints =
+ configService.getBgpPeerConnectPoints();
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+ if (hostIpAddress.isIp4()) {
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ } else {
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ }
+
+ // Match the destination IP prefix at the first hop
+ IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
+ selector.matchIPDst(ipPrefix);
+
+ // Rewrite the destination MAC address
+ MacAddress hostMac = null;
+ ConnectPoint egressPoint = null;
+ for (Host host : hostService.getHostsByIp(hostIpAddress)) {
+ if (host.mac() != null) {
+ hostMac = host.mac();
+ egressPoint = host.location();
+ break;
+ }
+ }
+ if (hostMac == null) {
+ hostService.startMonitoringIp(hostIpAddress);
+ return;
+ }
+
+ TrafficTreatment.Builder treatment =
+ DefaultTrafficTreatment.builder().setEthDst(hostMac);
+ Key key = Key.of(ipPrefix.toString(), appId);
+ int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
+ + PRIORITY_OFFSET;
+ MultiPointToSinglePointIntent intent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(egressPoint)
+ .priority(priority)
+ .constraints(CONSTRAINTS)
+ .build();
+
+ log.trace("Generates ConnectivityInternetToHost intent {}", intent);
+ submitReactiveIntent(ipPrefix, intent);
+ }
+
+ @Override
+ public void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix,
+ IpAddress nextHopIpAddress) {
+ // Find the attachment point (egress interface) of the next hop
+ Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
+ if (egressInterface == null) {
+ log.warn("No outgoing interface found for {}",
+ nextHopIpAddress);
+ return;
+ }
+
+ Set<Host> hosts = hostService.getHostsByIp(nextHopIpAddress);
+ if (hosts.isEmpty()) {
+ log.warn("No host found for next hop IP address");
+ return;
+ }
+ MacAddress nextHopMacAddress = null;
+ for (Host host : hosts) {
+ nextHopMacAddress = host.mac();
+ break;
+ }
+
+ hosts = hostService.getHostsByIp(hostIp);
+ if (hosts.isEmpty()) {
+ log.warn("No host found for host IP address");
+ return;
+ }
+ Host host = hosts.stream().findFirst().get();
+ ConnectPoint ingressPoint = host.location();
+
+ // Generate the intent itself
+ ConnectPoint egressPort = egressInterface.connectPoint();
+ log.debug("Generating intent for prefix {}, next hop mac {}",
+ prefix, nextHopMacAddress);
+
+ // Match the destination IP prefix at the first hop
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ if (prefix.isIp4()) {
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPDst(prefix);
+ } else {
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ selector.matchIPv6Dst(prefix);
+ }
+
+ // Rewrite the destination MAC address
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(nextHopMacAddress);
+ if (!egressInterface.vlan().equals(VlanId.NONE)) {
+ treatment.setVlanId(egressInterface.vlan());
+ // If we set VLAN ID, we have to make sure a VLAN tag exists.
+ // TODO support no VLAN -> VLAN routing
+ selector.matchVlanId(VlanId.ANY);
+ }
+
+ int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
+ Key key = Key.of(prefix.toString() + "-reactive", appId);
+ MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .ingressPoints(Collections.singleton(ingressPoint))
+ .egressPoint(egressPort)
+ .priority(priority)
+ .constraints(CONSTRAINTS)
+ .build();
+
+ submitReactiveIntent(prefix, intent);
+ }
+
+ @Override
+ public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
+ IpAddress srcIpAddress,
+ MacAddress srcMacAddress,
+ ConnectPoint srcConnectPoint) {
+ checkNotNull(dstIpAddress);
+ checkNotNull(srcIpAddress);
+ checkNotNull(srcMacAddress);
+ checkNotNull(srcConnectPoint);
+
+ IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
+ IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
+ ConnectPoint dstConnectPoint = null;
+ MacAddress dstMacAddress = null;
+
+ for (Host host : hostService.getHostsByIp(dstIpAddress)) {
+ if (host.mac() != null) {
+ dstMacAddress = host.mac();
+ dstConnectPoint = host.location();
+ break;
+ }
+ }
+ if (dstMacAddress == null) {
+ hostService.startMonitoringIp(dstIpAddress);
+ return;
+ }
+
+ //
+ // Handle intent from source host to destination host
+ //
+ MultiPointToSinglePointIntent srcToDstIntent =
+ hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
+ dstMacAddress, srcConnectPoint);
+ submitReactiveIntent(dstIpPrefix, srcToDstIntent);
+
+ //
+ // Handle intent from destination host to source host
+ //
+
+ // Since we proactively handle the intent from destination host to
+ // source host, we should check whether there is an exiting intent
+ // first.
+ if (mp2pIntentExists(srcIpPrefix)) {
+ updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
+ return;
+ } else {
+ // There is no existing intent, create a new one.
+ MultiPointToSinglePointIntent dstToSrcIntent =
+ hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
+ srcMacAddress, dstConnectPoint);
+ submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
+ }
+ }
+
+ /**
+ * Generates MultiPointToSinglePointIntent for both source host and
+ * destination host located in local SDN network.
+ *
+ * @param dstIpAddress the destination IP address
+ * @param dstConnectPoint the destination host connect point
+ * @param dstMacAddress the MAC address of destination host
+ * @param srcConnectPoint the connect point where packet-in from
+ * @return the generated MultiPointToSinglePointIntent
+ */
+ private MultiPointToSinglePointIntent hostToHostIntentGenerator(
+ IpAddress dstIpAddress,
+ ConnectPoint dstConnectPoint,
+ MacAddress dstMacAddress,
+ ConnectPoint srcConnectPoint) {
+ checkNotNull(dstIpAddress);
+ checkNotNull(dstConnectPoint);
+ checkNotNull(dstMacAddress);
+ checkNotNull(srcConnectPoint);
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(srcConnectPoint);
+ IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ if (dstIpAddress.isIp4()) {
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPDst(dstIpPrefix);
+ } else {
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ selector.matchIPv6Dst(dstIpPrefix);
+ }
+
+ // Rewrite the destination MAC address
+ TrafficTreatment.Builder treatment =
+ DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
+
+ Key key = Key.of(dstIpPrefix.toString(), appId);
+ int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
+ + PRIORITY_OFFSET;
+ MultiPointToSinglePointIntent intent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(dstConnectPoint)
+ .priority(priority)
+ .constraints(CONSTRAINTS)
+ .build();
+
+ log.trace("Generates ConnectivityHostToHost = {} ", intent);
+ return intent;
+ }
+
+ @Override
+ public void updateExistingMp2pIntent(IpPrefix ipPrefix,
+ ConnectPoint ingressConnectPoint) {
+ checkNotNull(ipPrefix);
+ checkNotNull(ingressConnectPoint);
+
+ MultiPointToSinglePointIntent existingIntent =
+ getExistingMp2pIntent(ipPrefix);
+ if (existingIntent != null) {
+ Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
+ // Add host connect point into ingressPoints of the existing intent
+ if (ingressPoints.add(ingressConnectPoint)) {
+ MultiPointToSinglePointIntent updatedMp2pIntent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(existingIntent.key())
+ .selector(existingIntent.selector())
+ .treatment(existingIntent.treatment())
+ .ingressPoints(ingressPoints)
+ .egressPoint(existingIntent.egressPoint())
+ .priority(existingIntent.priority())
+ .constraints(CONSTRAINTS)
+ .build();
+
+ log.trace("Update an existing MultiPointToSinglePointIntent "
+ + "to new intent = {} ", updatedMp2pIntent);
+ submitReactiveIntent(ipPrefix, updatedMp2pIntent);
+ }
+ // If adding ingressConnectPoint to ingressPoints failed, it
+ // because between the time interval from checking existing intent
+ // to generating new intent, onos updated this intent due to other
+ // packet-in and the new intent also includes the
+ // ingressConnectPoint. This will not affect reactive routing.
+ }
+ }
+
+ /**
+ * Submits a reactive intent to the intent synchronizer.
+ *
+ * @param ipPrefix IP prefix of the intent
+ * @param intent intent to submit
+ */
+ void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
+ routeIntents.put(ipPrefix, intent);
+
+ intentSynchronizer.submit(intent);
+ }
+
+ /**
+ * Gets the existing MultiPointToSinglePointIntent from memory for a given
+ * IP prefix.
+ *
+ * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
+ * @return the MultiPointToSinglePointIntent if found, otherwise null
+ */
+ private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix ipPrefix) {
+ checkNotNull(ipPrefix);
+ return routeIntents.get(ipPrefix);
+ }
+
+ @Override
+ public boolean mp2pIntentExists(IpPrefix ipPrefix) {
+ checkNotNull(ipPrefix);
+ return routeIntents.get(ipPrefix) != null;
+ }
+}
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
index ad78a1ce..96aa06ee 100644
--- a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
+++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
@@ -25,27 +25,39 @@ import org.onlab.packet.EthType;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
+import org.onosproject.routing.IntentRequestListener;
+import org.onosproject.routing.RouteEntry;
import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.SdnIpService;
import org.onosproject.routing.config.RoutingConfigurationService;
import org.slf4j.Logger;
import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.Set;
+import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.packet.Ethernet.TYPE_ARP;
import static org.onlab.packet.Ethernet.TYPE_IPV4;
import static org.onosproject.net.packet.PacketPriority.REACTIVE;
@@ -74,16 +86,32 @@ public class SdnIpReactiveRouting {
protected RoutingService routingService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected SdnIpService sdnIpService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RoutingConfigurationService config;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
private ApplicationId appId;
+ private IntentRequestListener intentRequestListener;
+
private ReactiveRoutingProcessor processor =
new ReactiveRoutingProcessor();
@Activate
public void activate() {
appId = coreService.registerApplication(APP_NAME);
+
+ intentRequestListener = new ReactiveRoutingFib(appId, hostService,
+ config, interfaceService,
+ sdnIpService.getIntentSynchronizationService());
+
packetService.addProcessor(processor, PacketProcessor.director(2));
requestIntercepts();
log.info("SDN-IP Reactive Routing Started");
@@ -168,12 +196,11 @@ public class SdnIpReactiveRouting {
IpAddress srcIp =
IpAddress.valueOf(ipv4Packet.getSourceAddress());
MacAddress srcMac = ethPkt.getSourceMAC();
- routingService.packetReactiveProcessor(dstIp, srcIp,
- srcConnectPoint, srcMac);
+ packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac);
// TODO emit packet first or packetReactiveProcessor first
ConnectPoint egressConnectPoint = null;
- egressConnectPoint = routingService.getEgressConnectPoint(dstIp);
+ egressConnectPoint = getEgressConnectPoint(dstIp);
if (egressConnectPoint != null) {
forwardPacketToDst(context, egressConnectPoint);
}
@@ -185,6 +212,173 @@ public class SdnIpReactiveRouting {
}
/**
+ * Routes packet reactively.
+ *
+ * @param dstIpAddress the destination IP address of a packet
+ * @param srcIpAddress the source IP address of a packet
+ * @param srcConnectPoint the connect point where a packet comes from
+ * @param srcMacAddress the source MAC address of a packet
+ */
+ private void packetReactiveProcessor(IpAddress dstIpAddress,
+ IpAddress srcIpAddress,
+ ConnectPoint srcConnectPoint,
+ MacAddress srcMacAddress) {
+ checkNotNull(dstIpAddress);
+ checkNotNull(srcIpAddress);
+ checkNotNull(srcConnectPoint);
+ checkNotNull(srcMacAddress);
+
+ //
+ // Step1: Try to update the existing intent first if it exists.
+ //
+ IpPrefix ipPrefix = null;
+ RouteEntry routeEntry = null;
+ if (config.isIpAddressLocal(dstIpAddress)) {
+ if (dstIpAddress.isIp4()) {
+ ipPrefix = IpPrefix.valueOf(dstIpAddress,
+ Ip4Address.BIT_LENGTH);
+ } else {
+ ipPrefix = IpPrefix.valueOf(dstIpAddress,
+ Ip6Address.BIT_LENGTH);
+ }
+ } else {
+ // Get IP prefix from BGP route table
+ routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
+ if (routeEntry != null) {
+ ipPrefix = routeEntry.prefix();
+ }
+ }
+ if (ipPrefix != null
+ && intentRequestListener.mp2pIntentExists(ipPrefix)) {
+ intentRequestListener.updateExistingMp2pIntent(ipPrefix,
+ srcConnectPoint);
+ return;
+ }
+
+ //
+ // Step2: There is no existing intent for the destination IP address.
+ // Check whether it is necessary to create a new one. If necessary then
+ // create a new one.
+ //
+ TrafficType trafficType =
+ trafficTypeClassifier(srcConnectPoint, dstIpAddress);
+
+ switch (trafficType) {
+ case HOST_TO_INTERNET:
+ // If the destination IP address is outside the local SDN network.
+ // The Step 1 has already handled it. We do not need to do anything here.
+ intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
+ ipPrefix, routeEntry.nextHop());
+ break;
+ case INTERNET_TO_HOST:
+ intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
+ break;
+ case HOST_TO_HOST:
+ intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
+ srcIpAddress, srcMacAddress, srcConnectPoint);
+ break;
+ case INTERNET_TO_INTERNET:
+ log.trace("This is transit traffic, "
+ + "the intent should be preinstalled already");
+ break;
+ case DROP:
+ // TODO here should setUpDropPacketIntent(...);
+ // We need a new type of intent here.
+ break;
+ case UNKNOWN:
+ log.trace("This is unknown traffic, so we do nothing");
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Classifies the traffic and return the traffic type.
+ *
+ * @param srcConnectPoint the connect point where the packet comes from
+ * @param dstIp the destination IP address in packet
+ * @return the traffic type which this packet belongs to
+ */
+ private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
+ IpAddress dstIp) {
+ LocationType dstIpLocationType = getLocationType(dstIp);
+ Optional<Interface> srcInterface =
+ interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
+ Set<ConnectPoint> ingressPoints = config.getBgpPeerConnectPoints();
+
+ switch (dstIpLocationType) {
+ case INTERNET:
+ if (srcInterface.isPresent() &&
+ (!ingressPoints.contains(srcConnectPoint))) {
+ return TrafficType.HOST_TO_INTERNET;
+ } else {
+ return TrafficType.INTERNET_TO_INTERNET;
+ }
+ case LOCAL:
+ if (srcInterface.isPresent() &&
+ (!ingressPoints.contains(srcConnectPoint))) {
+ return TrafficType.HOST_TO_HOST;
+ } else {
+ // TODO Currently we only consider local public prefixes.
+ // In the future, we will consider the local private prefixes.
+ // If dstIpLocationType is a local private, we should return
+ // TrafficType.DROP.
+ return TrafficType.INTERNET_TO_HOST;
+ }
+ case NO_ROUTE:
+ return TrafficType.DROP;
+ default:
+ return TrafficType.UNKNOWN;
+ }
+ }
+
+ /**
+ * Evaluates the location of an IP address and returns the location type.
+ *
+ * @param ipAddress the IP address to evaluate
+ * @return the IP address location type
+ */
+ private LocationType getLocationType(IpAddress ipAddress) {
+ if (config.isIpAddressLocal(ipAddress)) {
+ return LocationType.LOCAL;
+ } else if (routingService.getLongestMatchableRouteEntry(ipAddress) != null) {
+ return LocationType.INTERNET;
+ } else {
+ return LocationType.NO_ROUTE;
+ }
+ }
+
+ public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
+ LocationType type = getLocationType(dstIpAddress);
+ if (type == LocationType.LOCAL) {
+ Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
+ if (!hosts.isEmpty()) {
+ return hosts.iterator().next().location();
+ } else {
+ hostService.startMonitoringIp(dstIpAddress);
+ return null;
+ }
+ } else if (type == LocationType.INTERNET) {
+ IpAddress nextHopIpAddress = null;
+ RouteEntry routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
+ if (routeEntry != null) {
+ nextHopIpAddress = routeEntry.nextHop();
+ Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
+ if (it != null) {
+ return it.connectPoint();
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Emits the specified packet onto the network.
*
* @param context the packet context
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java
new file mode 100644
index 00000000..134126b3
--- /dev/null
+++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.reactive.routing;
+
+/**
+ * Specifies the type of traffic.
+ * <p>
+ * We classify traffic by the first packet of each traffic.
+ * </p>
+ */
+enum TrafficType {
+ /**
+ * Traffic from a host located in local SDN network wants to
+ * communicate with destination host located in Internet (outside
+ * local SDN network).
+ */
+ HOST_TO_INTERNET,
+ /**
+ * Traffic from Internet wants to communicate with a host located
+ * in local SDN network.
+ */
+ INTERNET_TO_HOST,
+ /**
+ * Both the source host and destination host of a traffic are in
+ * local SDN network.
+ */
+ HOST_TO_HOST,
+ /**
+ * Traffic from Internet wants to traverse local SDN network.
+ */
+ INTERNET_TO_INTERNET,
+ /**
+ * Any traffic wants to communicate with a destination which has
+ * no route, or traffic from Internet wants to access a local private
+ * IP address.
+ */
+ DROP,
+ /**
+ * Traffic does not belong to the types above.
+ */
+ UNKNOWN
+}
diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java
index 2d1bb311..1069ec5a 100644
--- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java
+++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java
@@ -47,6 +47,16 @@ public interface IntentRequestListener {
ConnectPoint srcConnectPoint);
/**
+ * Sets up connectivity for packet from a local host to the Internet.
+ *
+ * @param hostIp IP address of the local host
+ * @param prefix external IP prefix that the host is talking to
+ * @param nextHopIpAddress IP address of the next hop router for the prefix
+ */
+ void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix,
+ IpAddress nextHopIpAddress);
+
+ /**
* Adds one new ingress connect point into ingress points of an existing
* intent and resubmits the new intent.
* <p>
diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java
new file mode 100644
index 00000000..dc6a838d
--- /dev/null
+++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.routing;
+
+import org.onosproject.net.intent.Intent;
+
+/**
+ * Submits and withdraws intents to the IntentService from a single point in
+ * the cluster at any one time. The provided intents will be synchronized with
+ * the IntentService on leadership change.
+ */
+public interface IntentSynchronizationService {
+
+ /**
+ * Submits and intent to the synchronizer.
+ * <p>
+ * The intent will be submitted directly to the IntentService if this node
+ * is the leader, otherwise it will be stored in the synchronizer for
+ * synchronization if this node becomes the leader.
+ * </p>
+ *
+ * @param intent intent to submit
+ */
+ void submit(Intent intent);
+
+ /**
+ * Withdraws an intent from the synchronizer.
+ * <p>
+ * The intent will be withdrawn directly from the IntentService if this node
+ * is the leader. The intent will be removed from the synchronizer's
+ * in-memory storage.
+ * </p>
+ *
+ * @param intent intent to withdraw
+ */
+ void withdraw(Intent intent);
+}
diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
index 8b7040e2..7399ed75 100644
--- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
+++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
@@ -16,8 +16,6 @@
package org.onosproject.routing;
import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.ConnectPoint;
import org.onosproject.routing.config.BgpConfig;
import java.util.Collection;
@@ -32,63 +30,6 @@ public interface RoutingService {
Class<BgpConfig> CONFIG_CLASS = BgpConfig.class;
/**
- * Specifies the type of an IP address or an IP prefix location.
- */
- enum LocationType {
- /**
- * The location of an IP address or an IP prefix is in local SDN network.
- */
- LOCAL,
- /**
- * The location of an IP address or an IP prefix is outside local SDN network.
- */
- INTERNET,
- /**
- * There is no route for this IP address or IP prefix.
- */
- NO_ROUTE
- }
-
- /**
- * Specifies the type of traffic.
- * <p>
- * We classify traffic by the first packet of each traffic.
- * </p>
- */
- enum TrafficType {
- /**
- * Traffic from a host located in local SDN network wants to
- * communicate with destination host located in Internet (outside
- * local SDN network).
- */
- HOST_TO_INTERNET,
- /**
- * Traffic from Internet wants to communicate with a host located
- * in local SDN network.
- */
- INTERNET_TO_HOST,
- /**
- * Both the source host and destination host of a traffic are in
- * local SDN network.
- */
- HOST_TO_HOST,
- /**
- * Traffic from Internet wants to traverse local SDN network.
- */
- INTERNET_TO_INTERNET,
- /**
- * Any traffic wants to communicate with a destination which has
- * no route, or traffic from Internet wants to access a local private
- * IP address.
- */
- DROP,
- /**
- * Traffic does not belong to the types above.
- */
- UNKNOWN
- }
-
- /**
* Starts the routing service.
*/
void start();
@@ -101,15 +42,6 @@ public interface RoutingService {
void addFibListener(FibListener fibListener);
/**
- * Adds intent creation and submission listener.
- *
- * @param intentRequestListener listener to send intent creation and
- * submission request to
- */
- void addIntentRequestListener(IntentRequestListener
- intentRequestListener);
-
- /**
* Stops the routing service.
*/
void stop();
@@ -129,14 +61,6 @@ public interface RoutingService {
Collection<RouteEntry> getRoutes6();
/**
- * Evaluates the location of an IP address and returns the location type.
- *
- * @param ipAddress the IP address to evaluate
- * @return the IP address location type
- */
- LocationType getLocationType(IpAddress ipAddress);
-
- /**
* Finds out the route entry which has the longest matchable IP prefix.
*
* @param ipAddress IP address used to find out longest matchable IP prefix
@@ -145,25 +69,4 @@ public interface RoutingService {
*/
RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress);
- /**
- * Finds out the egress connect point where to emit the first packet
- * based on destination IP address.
- *
- * @param dstIpAddress the destination IP address
- * @return the egress connect point if found, otherwise null
- */
- ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress);
-
- /**
- * Routes packet reactively.
- *
- * @param dstIpAddress the destination IP address of a packet
- * @param srcIpAddress the source IP address of a packet
- * @param srcConnectPoint the connect point where a packet comes from
- * @param srcMacAddress the source MAC address of a packet
- */
- void packetReactiveProcessor(IpAddress dstIpAddress,
- IpAddress srcIpAddress,
- ConnectPoint srcConnectPoint,
- MacAddress srcMacAddress);
}
diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java
new file mode 100644
index 00000000..0945336c
--- /dev/null
+++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routing;
+
+/**
+ * Service interface exported by SDN-IP.
+ */
+public interface SdnIpService {
+
+ /**
+ * Changes whether this SDN-IP instance is the primary or not based on the
+ * boolean parameter.
+ *
+ * @param isPrimary true if the instance is primary, false if it is not
+ */
+ void modifyPrimary(boolean isPrimary);
+
+ /**
+ * Gets the intent synchronization service.
+ *
+ * @return intent synchronization service
+ */
+ // TODO fix service resolution in SDN-IP
+ IntentSynchronizationService getIntentSynchronizationService();
+
+}
diff --git a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java
index c47d2768..b89eb2d1 100644
--- a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java
+++ b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java
@@ -19,10 +19,15 @@ import org.hamcrest.Matchers;
import org.junit.Test;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Unit tests for the RouteEntry class.
@@ -35,17 +40,22 @@ public class RouteEntryTest {
public void testConstructor() {
Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24");
Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8");
-
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.toString(),
is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
+
+ Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop6 = Ip6Address.valueOf("2000::1");
+ RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+ assertThat(routeEntry6.toString(),
+ is("RouteEntry{prefix=1000::/64, nextHop=2000::1}"));
}
/**
* Tests invalid class constructor for null IPv4 prefix.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullPrefix() {
+ public void testInvalidConstructorNullIpv4Prefix() {
Ip4Prefix prefix = null;
Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8");
@@ -53,10 +63,21 @@ public class RouteEntryTest {
}
/**
+ * Tests invalid class constructor for null IPv6 prefix.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidConstructorNullIpv6Prefix() {
+ Ip6Prefix prefix = null;
+ Ip6Address nextHop = Ip6Address.valueOf("2000::1");
+
+ new RouteEntry(prefix, nextHop);
+ }
+
+ /**
* Tests invalid class constructor for null IPv4 next-hop.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullNextHop() {
+ public void testInvalidConstructorNullIpv4NextHop() {
Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24");
Ip4Address nextHop = null;
@@ -64,16 +85,32 @@ public class RouteEntryTest {
}
/**
+ * Tests invalid class constructor for null IPv6 next-hop.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidConstructorNullIpv6NextHop() {
+ Ip6Prefix prefix = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop = null;
+
+ new RouteEntry(prefix, nextHop);
+ }
+
+ /**
* Tests getting the fields of a route entry.
*/
@Test
public void testGetFields() {
Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24");
Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8");
-
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.prefix(), is(prefix));
assertThat(routeEntry.nextHop(), is(nextHop));
+
+ Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop6 = Ip6Address.valueOf("2000::1");
+ RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+ assertThat(routeEntry6.prefix(), is(prefix6));
+ assertThat(routeEntry6.nextHop(), is(nextHop6));
}
/**
@@ -105,6 +142,33 @@ public class RouteEntryTest {
prefix = Ip4Prefix.valueOf("255.255.255.255/32");
assertThat(RouteEntry.createBinaryString(prefix),
is("11111111111111111111111111111111"));
+
+ Ip6Prefix prefix6;
+ Pattern pattern;
+ Matcher matcher;
+
+ prefix6 = Ip6Prefix.valueOf("::/0");
+ assertThat(RouteEntry.createBinaryString(prefix6), is(""));
+
+ prefix6 = Ip6Prefix.valueOf("2000::1000/112");
+ pattern = Pattern.compile("00100{108}");
+ matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+ assertTrue(matcher.matches());
+
+ prefix6 = Ip6Prefix.valueOf("2000::1000/116");
+ pattern = Pattern.compile("00100{108}0001");
+ matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+ assertTrue(matcher.matches());
+
+ prefix6 = Ip6Prefix.valueOf("2000::2000/116");
+ pattern = Pattern.compile("00100{108}0010");
+ matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+ assertTrue(matcher.matches());
+
+ prefix6 = Ip6Prefix.valueOf("2000::1234/128");
+ pattern = Pattern.compile("00100{108}0001001000110100");
+ matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+ assertTrue(matcher.matches());
}
/**
@@ -121,6 +185,16 @@ public class RouteEntryTest {
RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
assertThat(routeEntry1, is(routeEntry2));
+
+ Ip6Prefix prefix3 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop3 = Ip6Address.valueOf("2000::2");
+ RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
+
+ Ip6Prefix prefix4 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop4 = Ip6Address.valueOf("2000::2");
+ RouteEntry routeEntry4 = new RouteEntry(prefix4, nextHop4);
+
+ assertThat(routeEntry3, is(routeEntry4));
}
/**
@@ -142,6 +216,21 @@ public class RouteEntryTest {
assertThat(routeEntry1, Matchers.is(not(routeEntry2)));
assertThat(routeEntry1, Matchers.is(not(routeEntry3)));
+
+ Ip6Prefix prefix4 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop4 = Ip6Address.valueOf("2000::1");
+ RouteEntry routeEntry4 = new RouteEntry(prefix4, nextHop4);
+
+ Ip6Prefix prefix5 = Ip6Prefix.valueOf("1000::/65");
+ Ip6Address nextHop5 = Ip6Address.valueOf("2000::1");
+ RouteEntry routeEntry5 = new RouteEntry(prefix5, nextHop5);
+
+ Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop6 = Ip6Address.valueOf("2000::2");
+ RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+
+ assertThat(routeEntry4, Matchers.is(not(routeEntry5)));
+ assertThat(routeEntry4, Matchers.is(not(routeEntry6)));
}
/**
@@ -155,5 +244,12 @@ public class RouteEntryTest {
assertThat(routeEntry.toString(),
is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
+
+ Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+ Ip6Address nextHop6 = Ip6Address.valueOf("2000::1");
+ RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+
+ assertThat(routeEntry6.toString(),
+ is("RouteEntry{prefix=1000::/64, nextHop=2000::1}"));
}
}
diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
index 45206903..c58bc1b9 100644
--- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
+++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
@@ -32,8 +32,8 @@ import java.util.List;
public class Configuration {
// We call the BGP routers in our SDN network the BGP speakers, and call
// the BGP routers outside our SDN network the BGP peers.
- private List<BgpSpeaker> bgpSpeakers;
- private List<BgpPeer> peers;
+ private List<BgpSpeaker> bgpSpeakers = Collections.emptyList();
+ private List<BgpPeer> peers = Collections.emptyList();
private MacAddress virtualGatewayMacAddress;
// All IP prefixes from the configuration are local
diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
index 0a6f9d4c..19c3f70b 100644
--- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
+++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
@@ -195,13 +195,16 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService {
}
BgpConfig bgpConfig = configService.getConfig(routerAppId, BgpConfig.class);
-
- return bgpConfig.bgpSpeakers().stream()
- .flatMap(speaker -> speaker.peers().stream())
- .map(peer -> interfaceService.getMatchingInterface(peer))
- .filter(intf -> intf != null)
- .map(intf -> intf.connectPoint())
- .collect(Collectors.toSet());
+ if (bgpConfig == null) {
+ return Collections.emptySet();
+ } else {
+ return bgpConfig.bgpSpeakers().stream()
+ .flatMap(speaker -> speaker.peers().stream())
+ .map(peer -> interfaceService.getMatchingInterface(peer))
+ .filter(intf -> intf != null)
+ .map(intf -> intf.connectPoint())
+ .collect(Collectors.toSet());
+ }
}
@Override
diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
index 6700d530..75d789ab 100644
--- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
+++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
@@ -35,9 +35,7 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onosproject.core.CoreService;
-import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
@@ -46,7 +44,6 @@ import org.onosproject.routing.BgpService;
import org.onosproject.routing.FibEntry;
import org.onosproject.routing.FibListener;
import org.onosproject.routing.FibUpdate;
-import org.onosproject.routing.IntentRequestListener;
import org.onosproject.routing.RouteEntry;
import org.onosproject.routing.RouteListener;
import org.onosproject.routing.RouteUpdate;
@@ -61,7 +58,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
@@ -100,7 +96,6 @@ public class Router implements RoutingService {
private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
private FibListener fibComponent;
- private IntentRequestListener intentRequestListener;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -145,12 +140,6 @@ public class Router implements RoutingService {
@Override
public void addFibListener(FibListener fibListener) {
this.fibComponent = checkNotNull(fibListener);
-
- }
-
- @Override
- public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
- this.intentRequestListener = checkNotNull(intentRequestListener);
}
@Override
@@ -287,12 +276,10 @@ public class Router implements RoutingService {
void addRibRoute(RouteEntry routeEntry) {
if (routeEntry.isIp4()) {
// IPv4
- ribTable4.put(createBinaryString(routeEntry.prefix()),
- routeEntry);
+ ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry);
} else {
// IPv6
- ribTable6.put(createBinaryString(routeEntry.prefix()),
- routeEntry);
+ ribTable6.put(createBinaryString(routeEntry.prefix()), routeEntry);
}
}
@@ -553,17 +540,6 @@ public class Router implements RoutingService {
}
@Override
- public LocationType getLocationType(IpAddress ipAddress) {
- if (routingConfigurationService.isIpAddressLocal(ipAddress)) {
- return LocationType.LOCAL;
- } else if (getLongestMatchableRouteEntry(ipAddress) != null) {
- return LocationType.INTERNET;
- } else {
- return LocationType.NO_ROUTE;
- }
- }
-
- @Override
public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
RouteEntry routeEntry = null;
Iterable<RouteEntry> routeEntries;
@@ -587,142 +563,4 @@ public class Router implements RoutingService {
return routeEntry;
}
- @Override
- public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
- LocationType type = getLocationType(dstIpAddress);
- if (type == LocationType.LOCAL) {
- Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
- if (!hosts.isEmpty()) {
- return hosts.iterator().next().location();
- } else {
- hostService.startMonitoringIp(dstIpAddress);
- return null;
- }
- } else if (type == LocationType.INTERNET) {
- IpAddress nextHopIpAddress = null;
- RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
- if (routeEntry != null) {
- nextHopIpAddress = routeEntry.nextHop();
- Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
- if (it != null) {
- return it.connectPoint();
- } else {
- return null;
- }
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- @Override
- public void packetReactiveProcessor(IpAddress dstIpAddress,
- IpAddress srcIpAddress,
- ConnectPoint srcConnectPoint,
- MacAddress srcMacAddress) {
- checkNotNull(dstIpAddress);
- checkNotNull(srcIpAddress);
- checkNotNull(srcConnectPoint);
- checkNotNull(srcMacAddress);
-
- //
- // Step1: Try to update the existing intent first if it exists.
- //
- IpPrefix ipPrefix = null;
- if (routingConfigurationService.isIpAddressLocal(dstIpAddress)) {
- if (dstIpAddress.isIp4()) {
- ipPrefix = IpPrefix.valueOf(dstIpAddress,
- Ip4Address.BIT_LENGTH);
- } else {
- ipPrefix = IpPrefix.valueOf(dstIpAddress,
- Ip6Address.BIT_LENGTH);
- }
- } else {
- // Get IP prefix from BGP route table
- RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
- if (routeEntry != null) {
- ipPrefix = routeEntry.prefix();
- }
- }
- if (ipPrefix != null
- && intentRequestListener.mp2pIntentExists(ipPrefix)) {
- intentRequestListener.updateExistingMp2pIntent(ipPrefix,
- srcConnectPoint);
- return;
- }
-
- //
- // Step2: There is no existing intent for the destination IP address.
- // Check whether it is necessary to create a new one. If necessary then
- // create a new one.
- //
- TrafficType trafficType =
- trafficTypeClassifier(srcConnectPoint, dstIpAddress);
-
- switch (trafficType) {
- case HOST_TO_INTERNET:
- // If the destination IP address is outside the local SDN network.
- // The Step 1 has already handled it. We do not need to do anything here.
- break;
- case INTERNET_TO_HOST:
- intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
- break;
- case HOST_TO_HOST:
- intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
- srcIpAddress, srcMacAddress, srcConnectPoint);
- break;
- case INTERNET_TO_INTERNET:
- log.trace("This is transit traffic, "
- + "the intent should be preinstalled already");
- break;
- case DROP:
- // TODO here should setUpDropPaccketIntent(...);
- // We need a new type of intent here.
- break;
- case UNKNOWN:
- log.trace("This is unknown traffic, so we do nothing");
- break;
- default:
- break;
- }
- }
-
- /**
- * Classifies the traffic and return the traffic type.
- *
- * @param srcConnectPoint the connect point where the packet comes from
- * @param dstIp the destination IP address in packet
- * @return the traffic type which this packet belongs to
- */
- private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
- IpAddress dstIp) {
- LocationType dstIpLocationType = getLocationType(dstIp);
- Optional<Interface> srcInterface =
- interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
-
- switch (dstIpLocationType) {
- case INTERNET:
- if (!srcInterface.isPresent()) {
- return TrafficType.HOST_TO_INTERNET;
- } else {
- return TrafficType.INTERNET_TO_INTERNET;
- }
- case LOCAL:
- if (!srcInterface.isPresent()) {
- return TrafficType.HOST_TO_HOST;
- } else {
- // TODO Currently we only consider local public prefixes.
- // In the future, we will consider the local private prefixes.
- // If dstIpLocationType is a local private, we should return
- // TrafficType.DROP.
- return TrafficType.INTERNET_TO_HOST;
- }
- case NO_ROUTE:
- return TrafficType.DROP;
- default:
- return TrafficType.UNKNOWN;
- }
- }
}
diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java
index 29526fff..3c868202 100644
--- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java
+++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java
@@ -18,10 +18,7 @@ package org.onosproject.routing.impl;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.ConnectPoint;
import org.onosproject.routing.FibListener;
-import org.onosproject.routing.IntentRequestListener;
import org.onosproject.routing.RouteEntry;
import org.onosproject.routing.RoutingService;
import org.onosproject.routing.StaticRoutingService;
@@ -49,11 +46,6 @@ public class StaticRouter implements RoutingService, StaticRoutingService {
}
@Override
- public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
-
- }
-
- @Override
public void stop() {
}
@@ -69,27 +61,11 @@ public class StaticRouter implements RoutingService, StaticRoutingService {
}
@Override
- public LocationType getLocationType(IpAddress ipAddress) {
- return null;
- }
-
- @Override
public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
return null;
}
@Override
- public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
- return null;
- }
-
- @Override
- public void packetReactiveProcessor(IpAddress dstIpAddress, IpAddress srcIpAddress,
- ConnectPoint srcConnectPoint, MacAddress srcMacAddress) {
-
- }
-
- @Override
public FibListener getFibListener() {
return fibListener;
}
diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
index 45bc309f..c73e18cb 100644
--- a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
+++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
@@ -22,6 +22,8 @@ import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
@@ -81,6 +83,13 @@ public class RouterTest {
DeviceId.deviceId("of:0000000000000004"),
PortNumber.portNumber(1));
+ private static final ConnectPoint SW5_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000005"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW6_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000006"),
+ PortNumber.portNumber(1));
private Router router;
@Before
@@ -132,7 +141,6 @@ public class RouterTest {
hostService.startMonitoringIp(host1Address);
expectLastCall().anyTimes();
-
IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
@@ -148,7 +156,7 @@ public class RouterTest {
IpAddress host3Address = IpAddress.valueOf("192.168.40.1");
Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1),
- new HostLocation(SW4_ETH1, 1),
+ new HostLocation(SW3_ETH1, 1),
Sets.newHashSet(host3Address));
expect(hostService.getHostsByIp(host3Address))
@@ -156,6 +164,41 @@ public class RouterTest {
hostService.startMonitoringIp(host3Address);
expectLastCall().anyTimes();
+ IpAddress host4Address = IpAddress.valueOf("1000::1");
+ Host host4 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+ MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE,
+ new HostLocation(SW4_ETH1, 1),
+ Sets.newHashSet(host4Address));
+
+ expect(hostService.getHostsByIp(host4Address))
+ .andReturn(Sets.newHashSet(host4)).anyTimes();
+ hostService.startMonitoringIp(host4Address);
+ expectLastCall().anyTimes();
+
+ IpAddress host5Address = IpAddress.valueOf("2000::1");
+ Host host5 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+ MacAddress.valueOf("00:00:00:00:00:05"), VlanId.NONE,
+ new HostLocation(SW5_ETH1, 1),
+ Sets.newHashSet(host5Address));
+
+ expect(hostService.getHostsByIp(host5Address))
+ .andReturn(Sets.newHashSet(host5)).anyTimes();
+ hostService.startMonitoringIp(host5Address);
+ expectLastCall().anyTimes();
+
+ // Next hop on a VLAN
+ IpAddress host6Address = IpAddress.valueOf("3000::1");
+ Host host6 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+ MacAddress.valueOf("00:00:00:00:00:06"), VlanId.vlanId((short) 1),
+ new HostLocation(SW6_ETH1, 1),
+ Sets.newHashSet(host6Address));
+
+ expect(hostService.getHostsByIp(host6Address))
+ .andReturn(Sets.newHashSet(host6)).anyTimes();
+ hostService.startMonitoringIp(host6Address);
+ expectLastCall().anyTimes();
+
+
// Called during shutdown
hostService.removeListener(anyObject(HostListener.class));
@@ -163,10 +206,10 @@ public class RouterTest {
}
/**
- * Tests adding a route entry.
+ * Tests adding a IPv4 route entry.
*/
@Test
- public void testRouteAdd() {
+ public void testIpv4RouteAdd() {
// Construct a route entry
IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1");
@@ -175,7 +218,34 @@ public class RouterTest {
// Expected FIB entry
FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
- MacAddress.valueOf("00:00:00:00:00:01"));
+ MacAddress.valueOf("00:00:00:00:00:01"));
+
+ fibListener.update(Collections.singletonList(new FibUpdate(
+ FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
+
+ replay(fibListener);
+
+ router.processRouteUpdates(Collections.singletonList(
+ new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
+
+ verify(fibListener);
+ }
+
+
+ /**
+ * Tests adding a IPv6 route entry.
+ */
+ @Test
+ public void testIpv6RouteAdd() {
+ // Construct a route entry
+ IpPrefix prefix = Ip6Prefix.valueOf("4000::/64");
+ IpAddress nextHopIp = Ip6Address.valueOf("1000::1");
+
+ RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
+
+ // Expected FIB entry
+ FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
+ MacAddress.valueOf("00:00:00:00:00:04"));
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
@@ -188,13 +258,14 @@ public class RouterTest {
verify(fibListener);
}
+
/**
- * Tests updating a route entry.
+ * Tests updating a IPv4 route entry.
*/
@Test
public void testRouteUpdate() {
// Firstly add a route
- testRouteAdd();
+ testIpv4RouteAdd();
// Route entry with updated next hop for the original prefix
RouteEntry routeEntryUpdate = new RouteEntry(
@@ -230,12 +301,53 @@ public class RouterTest {
}
/**
- * Tests deleting a route entry.
+ * Tests updating a IPv6 route entry.
*/
@Test
- public void testRouteDelete() {
+ public void testIpv6RouteUpdate() {
// Firstly add a route
- testRouteAdd();
+ testIpv6RouteAdd();
+
+ // Route entry with updated next hop for the original prefix
+ RouteEntry routeEntryUpdate = new RouteEntry(
+ Ip6Prefix.valueOf("4000::/64"),
+ Ip6Address.valueOf("2000::1"));
+
+ // The old FIB entry will be withdrawn
+ FibEntry withdrawFibEntry = new FibEntry(
+ Ip6Prefix.valueOf("4000::/64"), null, null);
+
+ // A new FIB entry will be added
+ FibEntry updateFibEntry = new FibEntry(
+ Ip6Prefix.valueOf("4000::/64"),
+ Ip6Address.valueOf("2000::1"),
+ MacAddress.valueOf("00:00:00:00:00:05"));
+
+ reset(fibListener);
+ fibListener.update(Collections.singletonList(new FibUpdate(
+ FibUpdate.Type.UPDATE, updateFibEntry)),
+ Collections.singletonList(new FibUpdate(
+ FibUpdate.Type.DELETE, withdrawFibEntry)));
+ replay(fibListener);
+
+ reset(routingConfigurationService);
+ expect(routingConfigurationService.isIpPrefixLocal(
+ anyObject(IpPrefix.class))).andReturn(false);
+ replay(routingConfigurationService);
+
+ router.processRouteUpdates(Collections.singletonList(new RouteUpdate(
+ RouteUpdate.Type.UPDATE, routeEntryUpdate)));
+
+ verify(fibListener);
+ }
+
+ /**
+ * Tests deleting a IPv4 route entry.
+ */
+ @Test
+ public void testIpv4RouteDelete() {
+ // Firstly add a route
+ testIpv4RouteAdd();
RouteEntry deleteRouteEntry = new RouteEntry(
Ip4Prefix.valueOf("1.1.1.0/24"),
@@ -257,10 +369,37 @@ public class RouterTest {
}
/**
- * Tests adding a route whose next hop is the local BGP speaker.
+ * Tests deleting a IPv6 route entry.
*/
@Test
- public void testLocalRouteAdd() {
+ public void testIpv6RouteDelete() {
+ // Firstly add a route
+ testIpv6RouteAdd();
+
+ RouteEntry deleteRouteEntry = new RouteEntry(
+ Ip6Prefix.valueOf("4000::/64"),
+ Ip6Address.valueOf("1000::1"));
+
+ FibEntry deleteFibEntry = new FibEntry(
+ Ip6Prefix.valueOf("4000::/64"), null, null);
+
+ reset(fibListener);
+ fibListener.update(Collections.emptyList(), Collections.singletonList(
+ new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry)));
+
+ replay(fibListener);
+
+ router.processRouteUpdates(Collections.singletonList(
+ new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry)));
+
+ verify(fibListener);
+ }
+
+ /**
+ * Tests adding a IPv4 route whose next hop is the local BGP speaker.
+ */
+ @Test
+ public void testIpv4LocalRouteAdd() {
// Construct a route entry, the next hop is the local BGP speaker
RouteEntry routeEntry = new RouteEntry(
Ip4Prefix.valueOf("1.1.1.0/24"),
@@ -284,4 +423,33 @@ public class RouterTest {
assertTrue(router.getRoutes4().contains(routeEntry));
verify(fibListener);
}
+
+ /**
+ * Tests adding a IPv6 route whose next hop is the local BGP speaker.
+ */
+ @Test
+ public void testIpv6LocalRouteAdd() {
+ // Construct a route entry, the next hop is the local BGP speaker
+ RouteEntry routeEntry = new RouteEntry(
+ Ip6Prefix.valueOf("4000::/64"),
+ Ip6Address.valueOf("::"));
+
+ // No methods on the FIB listener should be called
+ replay(fibListener);
+
+ reset(routingConfigurationService);
+ expect(routingConfigurationService.isIpPrefixLocal(
+ anyObject(IpPrefix.class))).andReturn(true);
+ replay(routingConfigurationService);
+
+ // Call the processRouteUpdates() method in Router class
+ RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
+ routeEntry);
+ router.processRouteUpdates(Collections.singletonList(routeUpdate));
+
+ // Verify
+ assertEquals(1, router.getRoutes6().size());
+ assertTrue(router.getRoutes6().contains(routeEntry));
+ verify(fibListener);
+ }
}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
index d8d8f45d..eaabed33 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
@@ -15,118 +15,79 @@
*/
package org.onosproject.sdnip;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
-import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.net.intent.constraint.PartialFailureConstraint;
-import org.onosproject.routing.FibListener;
-import org.onosproject.routing.FibUpdate;
-import org.onosproject.routing.IntentRequestListener;
-import org.onosproject.routing.config.RoutingConfigurationService;
+import org.onosproject.routing.IntentSynchronizationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Semaphore;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
/**
* Synchronizes intents between the in-memory intent store and the
* IntentService.
*/
-public class IntentSynchronizer implements FibListener, IntentRequestListener {
- private static final int PRIORITY_OFFSET = 100;
- private static final int PRIORITY_MULTIPLIER = 5;
- protected static final ImmutableList<Constraint> CONSTRAINTS
- = ImmutableList.of(new PartialFailureConstraint());
+public class IntentSynchronizer implements IntentSynchronizationService {
private static final Logger log =
LoggerFactory.getLogger(IntentSynchronizer.class);
private final ApplicationId appId;
private final IntentService intentService;
- private final HostService hostService;
- private final InterfaceService interfaceService;
- private final Map<IntentKey, PointToPointIntent> peerIntents;
- private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+ private final Map<Key, Intent> intents;
//
// State to deal with SDN-IP Leader election and pushing Intents
//
private final ExecutorService bgpIntentsSynchronizerExecutor;
- private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
private volatile boolean isElectedLeader = false;
private volatile boolean isActivatedLeader = false;
- private final RoutingConfigurationService configService;
+ /**
+ * Class constructor.
+ *
+ * @param appId the Application ID
+ * @param intentService the intent service
+ */
+ IntentSynchronizer(ApplicationId appId, IntentService intentService) {
+ this(appId, intentService,
+ newSingleThreadExecutor(groupedThreads("onos/sdnip", "sync")));
+ }
/**
* Class constructor.
*
* @param appId the Application ID
* @param intentService the intent service
- * @param hostService the host service
- * @param configService the SDN-IP configuration service
- * @param interfaceService the interface service
+ * @param executorService executor service for synchronization thread
*/
IntentSynchronizer(ApplicationId appId, IntentService intentService,
- HostService hostService,
- RoutingConfigurationService configService,
- InterfaceService interfaceService) {
+ ExecutorService executorService) {
this.appId = appId;
this.intentService = intentService;
- this.hostService = hostService;
- this.interfaceService = interfaceService;
- peerIntents = new ConcurrentHashMap<>();
- routeIntents = new ConcurrentHashMap<>();
- this.configService = configService;
+ intents = new ConcurrentHashMap<>();
- bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setNameFormat("sdnip-intents-synchronizer-%d").build());
+ bgpIntentsSynchronizerExecutor = executorService;
}
/**
* Starts the synchronizer.
*/
public void start() {
- bgpIntentsSynchronizerExecutor.execute(this::doIntentSynchronizationThread);
+
}
/**
@@ -187,794 +148,118 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener {
}
}
- /**
- * Signals the synchronizer that the SDN-IP leadership has changed.
- *
- * @param isLeader true if this instance is now the leader, otherwise false
- */
- public void leaderChanged(boolean isLeader) {
- log.debug("SDN-IP Leader changed: {}", isLeader);
-
- if (!isLeader) {
- this.isElectedLeader = false;
- this.isActivatedLeader = false;
- return; // Nothing to do
- }
- this.isActivatedLeader = false;
- this.isElectedLeader = true;
-
- //
- // Tell the Intents Synchronizer thread to start the synchronization
- //
- intentsSynchronizerSemaphore.release();
- }
-
- /**
- * Gets the route intents.
- *
- * @return the route intents
- */
- public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
- List<MultiPointToSinglePointIntent> result = new LinkedList<>();
-
- for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
- routeIntents.entrySet()) {
- result.add(entry.getValue());
- }
- return result;
- }
-
- /**
- * Thread for Intent Synchronization.
- */
- private void doIntentSynchronizationThread() {
- boolean interrupted = false;
- try {
- while (!interrupted) {
- try {
- intentsSynchronizerSemaphore.acquire();
- //
- // Drain all permits, because a single synchronization is
- // sufficient.
- //
- intentsSynchronizerSemaphore.drainPermits();
- } catch (InterruptedException e) {
- interrupted = true;
- break;
- }
- synchronizeIntents();
- }
- } finally {
- if (interrupted) {
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Submits a collection of point-to-point intents.
- *
- * @param intents the intents to submit
- */
- void submitPeerIntents(Collection<PointToPointIntent> intents) {
+ @Override
+ public void submit(Intent intent) {
synchronized (this) {
- // Store the intents in memory
- for (PointToPointIntent intent : intents) {
- peerIntents.put(new IntentKey(intent), intent);
- }
-
- // Push the intents
+ intents.put(intent.key(), intent);
if (isElectedLeader && isActivatedLeader) {
- log.debug("SDN-IP Submitting all Peer Intents...");
- for (Intent intent : intents) {
- log.trace("SDN-IP Submitting intents: {}", intent);
- intentService.submit(intent);
- }
+ log.trace("SDN-IP Submitting intent: {}", intent);
+ intentService.submit(intent);
}
}
}
- /**
- * Submits a MultiPointToSinglePointIntent for reactive routing.
- *
- * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
- * @param intent the intent to submit
- */
- void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
+ @Override
+ public void withdraw(Intent intent) {
synchronized (this) {
- // Store the intent in memory
- routeIntents.put(ipPrefix, intent);
-
- // Push the intent
+ intents.remove(intent.key(), intent);
if (isElectedLeader && isActivatedLeader) {
- log.trace("SDN-IP submitting reactive routing intent: {}", intent);
- intentService.submit(intent);
+ log.trace("SDN-IP Withdrawing intent: {}", intent);
+ intentService.withdraw(intent);
}
}
}
/**
- * Generates a route intent for a prefix, the next hop IP address, and
- * the next hop MAC address.
- * <p/>
- * This method will find the egress interface for the intent.
- * Intent will match dst IP prefix and rewrite dst MAC address at all other
- * border switches, then forward packets according to dst MAC address.
+ * Signals the synchronizer that the SDN-IP leadership has changed.
*
- * @param prefix IP prefix of the route to add
- * @param nextHopIpAddress IP address of the next hop
- * @param nextHopMacAddress MAC address of the next hop
- * @return the generated intent, or null if no intent should be submitted
+ * @param isLeader true if this instance is now the leader, otherwise false
*/
- private MultiPointToSinglePointIntent generateRouteIntent(
- IpPrefix prefix,
- IpAddress nextHopIpAddress,
- MacAddress nextHopMacAddress) {
-
- // Find the attachment point (egress interface) of the next hop
- Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
- if (egressInterface == null) {
- log.warn("No outgoing interface found for {}",
- nextHopIpAddress);
- return null;
- }
-
- //
- // Generate the intent itself
- //
- Set<ConnectPoint> ingressPorts = new HashSet<>();
- ConnectPoint egressPort = egressInterface.connectPoint();
- log.debug("Generating intent for prefix {}, next hop mac {}",
- prefix, nextHopMacAddress);
-
- for (Interface intf : interfaceService.getInterfaces()) {
- // TODO this should be only peering interfaces
- if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
- ConnectPoint srcPort = intf.connectPoint();
- ingressPorts.add(srcPort);
- }
- }
-
- // Match the destination IP prefix at the first hop
- TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
- if (prefix.isIp4()) {
- selector.matchEthType(Ethernet.TYPE_IPV4);
- selector.matchIPDst(prefix);
- } else {
- selector.matchEthType(Ethernet.TYPE_IPV6);
- selector.matchIPv6Dst(prefix);
- }
-
- // Rewrite the destination MAC address
- TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
- .setEthDst(nextHopMacAddress);
- if (!egressInterface.vlan().equals(VlanId.NONE)) {
- treatment.setVlanId(egressInterface.vlan());
- // If we set VLAN ID, we have to make sure a VLAN tag exists.
- // TODO support no VLAN -> VLAN routing
- selector.matchVlanId(VlanId.ANY);
- }
-
- int priority =
- prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
- Key key = Key.of(prefix.toString(), appId);
- return MultiPointToSinglePointIntent.builder()
- .appId(appId)
- .key(key)
- .selector(selector.build())
- .treatment(treatment.build())
- .ingressPoints(ingressPorts)
- .egressPoint(egressPort)
- .priority(priority)
- .constraints(CONSTRAINTS)
- .build();
- }
-
- @Override
- public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
- checkNotNull(hostIpAddress);
- Set<ConnectPoint> ingressPoints =
- configService.getBgpPeerConnectPoints();
-
- TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-
- if (hostIpAddress.isIp4()) {
- selector.matchEthType(Ethernet.TYPE_IPV4);
- } else {
- selector.matchEthType(Ethernet.TYPE_IPV6);
- }
+ public void leaderChanged(boolean isLeader) {
+ log.debug("SDN-IP Leader changed: {}", isLeader);
- // Match the destination IP prefix at the first hop
- IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
- selector.matchIPDst(ipPrefix);
-
- // Rewrite the destination MAC address
- MacAddress hostMac = null;
- ConnectPoint egressPoint = null;
- for (Host host : hostService.getHostsByIp(hostIpAddress)) {
- if (host.mac() != null) {
- hostMac = host.mac();
- egressPoint = host.location();
- break;
- }
- }
- if (hostMac == null) {
- hostService.startMonitoringIp(hostIpAddress);
- return;
+ if (!isLeader) {
+ this.isElectedLeader = false;
+ this.isActivatedLeader = false;
+ return; // Nothing to do
}
+ this.isActivatedLeader = false;
+ this.isElectedLeader = true;
- TrafficTreatment.Builder treatment =
- DefaultTrafficTreatment.builder().setEthDst(hostMac);
- Key key = Key.of(ipPrefix.toString(), appId);
- int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
- + PRIORITY_OFFSET;
- MultiPointToSinglePointIntent intent =
- MultiPointToSinglePointIntent.builder()
- .appId(appId)
- .key(key)
- .selector(selector.build())
- .treatment(treatment.build())
- .ingressPoints(ingressPoints)
- .egressPoint(egressPoint)
- .priority(priority)
- .constraints(CONSTRAINTS)
- .build();
-
- log.trace("Generates ConnectivityInternetToHost intent {}", intent);
- submitReactiveIntent(ipPrefix, intent);
- }
-
-
- @Override
- public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
- //
- // NOTE: Semantically, we MUST withdraw existing intents before
- // submitting new intents.
- //
- synchronized (this) {
- MultiPointToSinglePointIntent intent;
-
- log.debug("SDN-IP submitting intents = {} withdrawing = {}",
- updates.size(), withdraws.size());
-
- //
- // Prepare the Intent batch operations for the intents to withdraw
- //
- for (FibUpdate withdraw : withdraws) {
- checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
- "FibUpdate with wrong type in withdraws list");
-
- IpPrefix prefix = withdraw.entry().prefix();
- intent = routeIntents.remove(prefix);
- if (intent == null) {
- log.trace("SDN-IP No intent in routeIntents to delete " +
- "for prefix: {}", prefix);
- continue;
- }
- if (isElectedLeader && isActivatedLeader) {
- log.trace("SDN-IP Withdrawing intent: {}", intent);
- intentService.withdraw(intent);
- }
- }
-
- //
- // Prepare the Intent batch operations for the intents to submit
- //
- for (FibUpdate update : updates) {
- checkArgument(update.type() == FibUpdate.Type.UPDATE,
- "FibUpdate with wrong type in updates list");
-
- IpPrefix prefix = update.entry().prefix();
- intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
- update.entry().nextHopMac());
-
- if (intent == null) {
- // This preserves the old semantics - if an intent can't be
- // generated, we don't do anything with that prefix. But
- // perhaps we should withdraw the old intent anyway?
- continue;
- }
-
- MultiPointToSinglePointIntent oldIntent =
- routeIntents.put(prefix, intent);
- if (isElectedLeader && isActivatedLeader) {
- if (oldIntent != null) {
- log.trace("SDN-IP Withdrawing old intent: {}",
- oldIntent);
- intentService.withdraw(oldIntent);
- }
- log.trace("SDN-IP Submitting intent: {}", intent);
- intentService.submit(intent);
- }
- }
- }
+ // Run the synchronization method off-thread
+ bgpIntentsSynchronizerExecutor.execute(this::synchronizeIntents);
}
- /**
- * Synchronize the in-memory Intents with the Intents in the Intent
- * framework.
- */
- void synchronizeIntents() {
- synchronized (this) {
-
- Map<IntentKey, Intent> localIntents = new HashMap<>();
- Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
- Collection<Intent> storeInMemoryIntents = new LinkedList<>();
- Collection<Intent> addIntents = new LinkedList<>();
- Collection<Intent> deleteIntents = new LinkedList<>();
-
- if (!isElectedLeader) {
- return; // Nothing to do: not the leader anymore
- }
- log.debug("SDN-IP synchronizing all intents...");
-
- // Prepare the local intents
- for (Intent intent : routeIntents.values()) {
- localIntents.put(new IntentKey(intent), intent);
- }
- for (Intent intent : peerIntents.values()) {
- localIntents.put(new IntentKey(intent), intent);
+ private void synchronizeIntents() {
+ Map<Key, Intent> serviceIntents = new HashMap<>();
+ intentService.getIntents().forEach(i -> {
+ if (i.appId().equals(appId)) {
+ serviceIntents.put(i.key(), i);
}
+ });
- // Fetch all intents for this application
- for (Intent intent : intentService.getIntents()) {
- if (!intent.appId().equals(appId)) {
- continue;
- }
- fetchedIntents.put(new IntentKey(intent), intent);
- }
- if (log.isDebugEnabled()) {
- for (Intent intent: fetchedIntents.values()) {
- log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
- intent);
- }
- }
-
- computeIntentsDelta(localIntents, fetchedIntents,
- storeInMemoryIntents, addIntents,
- deleteIntents);
+ List<Intent> intentsToAdd = new LinkedList<>();
+ List<Intent> intentsToRemove = new LinkedList<>();
- //
- // Perform the actions:
- // 1. Store in memory fetched intents that are same. Can be done
- // even if we are not the leader anymore
- // 2. Delete intents: check if the leader before the operation
- // 3. Add intents: check if the leader before the operation
- //
- for (Intent intent : storeInMemoryIntents) {
- // Store the intent in memory based on its type
- if (intent instanceof MultiPointToSinglePointIntent) {
- MultiPointToSinglePointIntent mp2pIntent =
- (MultiPointToSinglePointIntent) intent;
- // Find the IP prefix
- Criterion c =
- mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
- if (c == null) {
- // Try IPv6
- c =
- mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
- }
- if (c != null && c instanceof IPCriterion) {
- IPCriterion ipCriterion = (IPCriterion) c;
- IpPrefix ipPrefix = ipCriterion.ip();
- if (ipPrefix == null) {
- continue;
- }
- log.trace("SDN-IP Intent Synchronizer: updating " +
- "in-memory Route Intent for prefix {}",
- ipPrefix);
- routeIntents.put(ipPrefix, mp2pIntent);
- } else {
- log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
- mp2pIntent.id());
- }
- continue;
- }
- if (intent instanceof PointToPointIntent) {
- PointToPointIntent p2pIntent = (PointToPointIntent) intent;
- log.trace("SDN-IP Intent Synchronizer: updating " +
- "in-memory Peer Intent {}", p2pIntent);
- peerIntents.put(new IntentKey(intent), p2pIntent);
- continue;
- }
- }
-
- // Withdraw Intents
- for (Intent intent : deleteIntents) {
- intentService.withdraw(intent);
- log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
- intent);
- }
- if (!isElectedLeader) {
- log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
- "not elected leader anymore");
- isActivatedLeader = false;
- return;
- }
-
- // Add Intents
- for (Intent intent : addIntents) {
- intentService.submit(intent);
- log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
- intent);
- }
- if (!isElectedLeader) {
- log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
- "not elected leader anymore");
- isActivatedLeader = false;
- return;
- }
-
- if (isElectedLeader) {
- isActivatedLeader = true; // Allow push of Intents
+ for (Intent localIntent : intents.values()) {
+ Intent serviceIntent = serviceIntents.remove(localIntent.key());
+ if (serviceIntent == null) {
+ intentsToAdd.add(localIntent);
} else {
- isActivatedLeader = false;
- }
- log.debug("SDN-IP intent synchronization completed");
- }
- }
-
- /**
- * Computes the delta in two sets of Intents: local in-memory Intents,
- * and intents fetched from the Intent framework.
- *
- * @param localIntents the local in-memory Intents
- * @param fetchedIntents the Intents fetched from the Intent framework
- * @param storeInMemoryIntents the Intents that should be stored in memory.
- * Note: This Collection must be allocated by the caller, and it will
- * be populated by this method.
- * @param addIntents the Intents that should be added to the Intent
- * framework. Note: This Collection must be allocated by the caller, and
- * it will be populated by this method.
- * @param deleteIntents the Intents that should be deleted from the Intent
- * framework. Note: This Collection must be allocated by the caller, and
- * it will be populated by this method.
- */
- private void computeIntentsDelta(
- final Map<IntentKey, Intent> localIntents,
- final Map<IntentKey, Intent> fetchedIntents,
- Collection<Intent> storeInMemoryIntents,
- Collection<Intent> addIntents,
- Collection<Intent> deleteIntents) {
-
- //
- // Compute the deltas between the LOCAL in-memory Intents and the
- // FETCHED Intents:
- // - If an Intent is in both the LOCAL and FETCHED sets:
- // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
- // the LOCAL Intent should be added/installed; otherwise the
- // FETCHED intent should be stored in the local memory
- // (i.e., override the LOCAL Intent) to preserve the original
- // Intent ID.
- // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
- // Intent should be added/installed.
- // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
- // Intent should be deleted/withdrawn.
- //
- for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
- IntentKey intentKey = entry.getKey();
- Intent localIntent = entry.getValue();
- Intent fetchedIntent = fetchedIntents.get(intentKey);
-
- if (fetchedIntent == null) {
- //
- // No FETCHED Intent found: push the LOCAL Intent.
- //
- addIntents.add(localIntent);
- continue;
- }
-
- IntentState state =
- intentService.getIntentState(fetchedIntent.key());
- if (state == null ||
- state == IntentState.WITHDRAWING ||
- state == IntentState.WITHDRAWN) {
- // The intent has been withdrawn but according to our route
- // table it should be installed. We'll reinstall it.
- addIntents.add(localIntent);
- continue;
+ IntentState state = intentService.getIntentState(serviceIntent.key());
+ if (!IntentUtils.equals(serviceIntent, localIntent) || state == null ||
+ state == IntentState.WITHDRAW_REQ ||
+ state == IntentState.WITHDRAWING ||
+ state == IntentState.WITHDRAWN) {
+ intentsToAdd.add(localIntent);
+ }
}
- storeInMemoryIntents.add(fetchedIntent);
}
- for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
- IntentKey intentKey = entry.getKey();
- Intent fetchedIntent = entry.getValue();
- Intent localIntent = localIntents.get(intentKey);
-
- if (localIntent != null) {
- continue;
+ for (Intent serviceIntent : serviceIntents.values()) {
+ IntentState state = intentService.getIntentState(serviceIntent.key());
+ if (state != null && state != IntentState.WITHDRAW_REQ
+ && state != IntentState.WITHDRAWING
+ && state != IntentState.WITHDRAWN) {
+ intentsToRemove.add(serviceIntent);
}
-
- IntentState state =
- intentService.getIntentState(fetchedIntent.key());
- if (state == null ||
- state == IntentState.WITHDRAWING ||
- state == IntentState.WITHDRAWN) {
- // Nothing to do. The intent has been already withdrawn.
- continue;
- }
- //
- // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
- //
- deleteIntents.add(fetchedIntent);
- }
- }
-
- /**
- * Helper class that can be used to compute the key for an Intent by
- * by excluding the Intent ID.
- */
- static final class IntentKey {
- private final Intent intent;
-
- /**
- * Constructor.
- *
- * @param intent the intent to use
- */
- IntentKey(Intent intent) {
- checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
- (intent instanceof PointToPointIntent),
- "Intent type not recognized", intent);
- this.intent = intent;
}
- /**
- * Compares two Multi-Point to Single-Point Intents whether they
- * represent same logical intention.
- *
- * @param intent1 the first Intent to compare
- * @param intent2 the second Intent to compare
- * @return true if both Intents represent same logical intention,
- * otherwise false
- */
- static boolean equalIntents(MultiPointToSinglePointIntent intent1,
- MultiPointToSinglePointIntent intent2) {
- return Objects.equals(intent1.appId(), intent2.appId()) &&
- Objects.equals(intent1.selector(), intent2.selector()) &&
- Objects.equals(intent1.treatment(), intent2.treatment()) &&
- Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
- Objects.equals(intent1.egressPoint(), intent2.egressPoint());
- }
+ log.debug("SDN-IP Intent Synchronizer: submitting {}, withdrawing {}",
+ intentsToAdd.size(), intentsToRemove.size());
- /**
- * Compares two Point-to-Point Intents whether they represent
- * same logical intention.
- *
- * @param intent1 the first Intent to compare
- * @param intent2 the second Intent to compare
- * @return true if both Intents represent same logical intention,
- * otherwise false
- */
- static boolean equalIntents(PointToPointIntent intent1,
- PointToPointIntent intent2) {
- return Objects.equals(intent1.appId(), intent2.appId()) &&
- Objects.equals(intent1.selector(), intent2.selector()) &&
- Objects.equals(intent1.treatment(), intent2.treatment()) &&
- Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
- Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+ // Withdraw Intents
+ for (Intent intent : intentsToRemove) {
+ intentService.withdraw(intent);
+ log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
+ intent);
}
-
- @Override
- public int hashCode() {
- if (intent instanceof PointToPointIntent) {
- PointToPointIntent p2pIntent = (PointToPointIntent) intent;
- return Objects.hash(p2pIntent.appId(),
- p2pIntent.resources(),
- p2pIntent.selector(),
- p2pIntent.treatment(),
- p2pIntent.constraints(),
- p2pIntent.ingressPoint(),
- p2pIntent.egressPoint());
- }
- if (intent instanceof MultiPointToSinglePointIntent) {
- MultiPointToSinglePointIntent m2pIntent =
- (MultiPointToSinglePointIntent) intent;
- return Objects.hash(m2pIntent.appId(),
- m2pIntent.resources(),
- m2pIntent.selector(),
- m2pIntent.treatment(),
- m2pIntent.constraints(),
- m2pIntent.ingressPoints(),
- m2pIntent.egressPoint());
- }
- checkArgument(false, "Intent type not recognized", intent);
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if ((obj == null) || (!(obj instanceof IntentKey))) {
- return false;
- }
- IntentKey other = (IntentKey) obj;
-
- if (this.intent instanceof PointToPointIntent) {
- if (!(other.intent instanceof PointToPointIntent)) {
- return false;
- }
- return equalIntents((PointToPointIntent) this.intent,
- (PointToPointIntent) other.intent);
- }
- if (this.intent instanceof MultiPointToSinglePointIntent) {
- if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
- return false;
- }
- return equalIntents(
- (MultiPointToSinglePointIntent) this.intent,
- (MultiPointToSinglePointIntent) other.intent);
- }
- checkArgument(false, "Intent type not recognized", intent);
- return false;
+ if (!isElectedLeader) {
+ log.debug("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
+ "not elected leader anymore");
+ isActivatedLeader = false;
+ return;
}
- }
- @Override
- public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
- IpAddress srcIpAddress,
- MacAddress srcMacAddress,
- ConnectPoint srcConnectPoint) {
- checkNotNull(dstIpAddress);
- checkNotNull(srcIpAddress);
- checkNotNull(srcMacAddress);
- checkNotNull(srcConnectPoint);
-
- IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
- IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
- ConnectPoint dstConnectPoint = null;
- MacAddress dstMacAddress = null;
-
- for (Host host : hostService.getHostsByIp(dstIpAddress)) {
- if (host.mac() != null) {
- dstMacAddress = host.mac();
- dstConnectPoint = host.location();
- break;
- }
+ // Add Intents
+ for (Intent intent : intentsToAdd) {
+ intentService.submit(intent);
+ log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
+ intent);
}
- if (dstMacAddress == null) {
- hostService.startMonitoringIp(dstIpAddress);
+ if (!isElectedLeader) {
+ log.debug("SDN-IP Intent Synchronizer: cannot submit intents: " +
+ "not elected leader anymore");
+ isActivatedLeader = false;
return;
}
- //
- // Handle intent from source host to destination host
- //
- MultiPointToSinglePointIntent srcToDstIntent =
- hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
- dstMacAddress, srcConnectPoint);
- submitReactiveIntent(dstIpPrefix, srcToDstIntent);
-
- //
- // Handle intent from destination host to source host
- //
-
- // Since we proactively handle the intent from destination host to
- // source host, we should check whether there is an exiting intent
- // first.
- if (mp2pIntentExists(srcIpPrefix)) {
- updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
- return;
+ if (isElectedLeader) {
+ isActivatedLeader = true; // Allow push of Intents
} else {
- // There is no existing intent, create a new one.
- MultiPointToSinglePointIntent dstToSrcIntent =
- hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
- srcMacAddress, dstConnectPoint);
- submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
+ isActivatedLeader = false;
}
+ log.debug("SDN-IP intent synchronization completed");
}
- /**
- * Generates MultiPointToSinglePointIntent for both source host and
- * destination host located in local SDN network.
- *
- * @param dstIpAddress the destination IP address
- * @param dstConnectPoint the destination host connect point
- * @param dstMacAddress the MAC address of destination host
- * @param srcConnectPoint the connect point where packet-in from
- * @return the generated MultiPointToSinglePointIntent
- */
- private MultiPointToSinglePointIntent hostToHostIntentGenerator(
- IpAddress dstIpAddress,
- ConnectPoint dstConnectPoint,
- MacAddress dstMacAddress,
- ConnectPoint srcConnectPoint) {
- checkNotNull(dstIpAddress);
- checkNotNull(dstConnectPoint);
- checkNotNull(dstMacAddress);
- checkNotNull(srcConnectPoint);
-
- Set<ConnectPoint> ingressPoints = new HashSet<>();
- ingressPoints.add(srcConnectPoint);
- IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
-
- TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
- if (dstIpAddress.isIp4()) {
- selector.matchEthType(Ethernet.TYPE_IPV4);
- selector.matchIPDst(dstIpPrefix);
- } else {
- selector.matchEthType(Ethernet.TYPE_IPV6);
- selector.matchIPv6Dst(dstIpPrefix);
- }
-
- // Rewrite the destination MAC address
- TrafficTreatment.Builder treatment =
- DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
-
- Key key = Key.of(dstIpPrefix.toString(), appId);
- int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
- + PRIORITY_OFFSET;
- MultiPointToSinglePointIntent intent =
- MultiPointToSinglePointIntent.builder()
- .appId(appId)
- .key(key)
- .selector(selector.build())
- .treatment(treatment.build())
- .ingressPoints(ingressPoints)
- .egressPoint(dstConnectPoint)
- .priority(priority)
- .constraints(CONSTRAINTS)
- .build();
-
- log.trace("Generates ConnectivityHostToHost = {} ", intent);
- return intent;
- }
-
- @Override
- public void updateExistingMp2pIntent(IpPrefix ipPrefix,
- ConnectPoint ingressConnectPoint) {
- checkNotNull(ipPrefix);
- checkNotNull(ingressConnectPoint);
-
- MultiPointToSinglePointIntent existingIntent =
- getExistingMp2pIntent(ipPrefix);
- if (existingIntent != null) {
- Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
- // Add host connect point into ingressPoints of the existing intent
- if (ingressPoints.add(ingressConnectPoint)) {
- MultiPointToSinglePointIntent updatedMp2pIntent =
- MultiPointToSinglePointIntent.builder()
- .appId(appId)
- .key(existingIntent.key())
- .selector(existingIntent.selector())
- .treatment(existingIntent.treatment())
- .ingressPoints(ingressPoints)
- .egressPoint(existingIntent.egressPoint())
- .priority(existingIntent.priority())
- .constraints(CONSTRAINTS)
- .build();
-
- log.trace("Update an existing MultiPointToSinglePointIntent "
- + "to new intent = {} ", updatedMp2pIntent);
- submitReactiveIntent(ipPrefix, updatedMp2pIntent);
- }
- // If adding ingressConnectPoint to ingressPoints failed, it
- // because between the time interval from checking existing intent
- // to generating new intent, onos updated this intent due to other
- // packet-in and the new intent also includes the
- // ingressConnectPoint. This will not affect reactive routing.
- }
- }
-
- @Override
- public boolean mp2pIntentExists(IpPrefix ipPrefix) {
- checkNotNull(ipPrefix);
- return routeIntents.get(ipPrefix) != null;
- }
-
- /**
- * Gets the existing MultiPointToSinglePointIntent from memory for a given
- * IP prefix.
- *
- * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
- * @return the MultiPointToSinglePointIntent if found, otherwise null
- */
- private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
- ipPrefix) {
- checkNotNull(ipPrefix);
- return routeIntents.get(ipPrefix);
- }
}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java
new file mode 100644
index 00000000..8e2a3df3
--- /dev/null
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Utilities for dealing with intents.
+ */
+public final class IntentUtils {
+
+ private static final Logger log = LoggerFactory.getLogger(IntentUtils.class);
+
+ private IntentUtils() {
+
+ }
+
+ /**
+ * Checks if two intents represent the same value.
+ *
+ * <p>({@link Intent#equals(Object)} only checks ID equality)</p>
+ *
+ * <p>Both intents must be of the same type.</p>
+ *
+ * @param one first intent
+ * @param two second intent
+ * @return true if the two intents represent the same value, otherwise false
+ */
+ public static boolean equals(Intent one, Intent two) {
+ checkArgument(one.getClass() == two.getClass(),
+ "Intents are not the same type");
+
+ if (!(Objects.equals(one.appId(), two.appId()) &&
+ Objects.equals(one.key(), two.key()))) {
+ return false;
+ }
+
+ if (one instanceof MultiPointToSinglePointIntent) {
+ MultiPointToSinglePointIntent intent1 = (MultiPointToSinglePointIntent) one;
+ MultiPointToSinglePointIntent intent2 = (MultiPointToSinglePointIntent) two;
+
+ return Objects.equals(intent1.selector(), intent2.selector()) &&
+ Objects.equals(intent1.treatment(), intent2.treatment()) &&
+ Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
+ Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+ } else if (one instanceof PointToPointIntent) {
+ PointToPointIntent intent1 = (PointToPointIntent) one;
+ PointToPointIntent intent2 = (PointToPointIntent) two;
+
+ return Objects.equals(intent1.selector(), intent2.selector()) &&
+ Objects.equals(intent1.treatment(), intent2.treatment()) &&
+ Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
+ Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+ } else {
+ log.error("Unimplemented intent type");
+ return false;
+ }
+ }
+}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
index 459db2b7..b2ce0f8a 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
@@ -15,6 +15,8 @@
*/
package org.onosproject.sdnip;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
@@ -22,16 +24,18 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.TpPort;
import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.routing.RoutingService;
import org.onosproject.routing.config.BgpConfig;
import org.slf4j.Logger;
@@ -49,18 +53,26 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class PeerConnectivityManager {
private static final int PRIORITY_OFFSET = 1000;
+ private static final String SUFFIX_DST = "dst";
+ private static final String SUFFIX_SRC = "src";
+ private static final String SUFFIX_ICMP = "icmp";
+
private static final Logger log = LoggerFactory.getLogger(
PeerConnectivityManager.class);
private static final short BGP_PORT = 179;
- private final IntentSynchronizer intentSynchronizer;
+ private final IntentSynchronizationService intentSynchronizer;
private final NetworkConfigService configService;
private final InterfaceService interfaceService;
private final ApplicationId appId;
private final ApplicationId routerAppId;
+ // Just putting something random here for now. Figure out exactly what
+ // indexes we need when we start making use of them.
+ private final Multimap<BgpConfig.BgpSpeakerConfig, PointToPointIntent> peerIntents;
+
/**
* Creates a new PeerConnectivityManager.
*
@@ -71,7 +83,7 @@ public class PeerConnectivityManager {
* @param routerAppId application ID
*/
public PeerConnectivityManager(ApplicationId appId,
- IntentSynchronizer intentSynchronizer,
+ IntentSynchronizationService intentSynchronizer,
NetworkConfigService configService,
ApplicationId routerAppId,
InterfaceService interfaceService) {
@@ -80,6 +92,8 @@ public class PeerConnectivityManager {
this.configService = configService;
this.routerAppId = routerAppId;
this.interfaceService = interfaceService;
+
+ peerIntents = HashMultimap.create();
}
/**
@@ -100,8 +114,6 @@ public class PeerConnectivityManager {
* BGP speakers and external BGP peers.
*/
private void setUpConnectivity() {
- List<PointToPointIntent> intents = new ArrayList<>();
-
BgpConfig config = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
if (config == null) {
@@ -113,11 +125,12 @@ public class PeerConnectivityManager {
log.debug("Start to set up BGP paths for BGP speaker: {}",
bgpSpeaker);
- intents.addAll(buildSpeakerIntents(bgpSpeaker));
- }
+ buildSpeakerIntents(bgpSpeaker).forEach(i -> {
+ peerIntents.put(bgpSpeaker, i);
+ intentSynchronizer.submit(i);
+ });
- // Submit all the intents.
- intentSynchronizer.submitPeerIntents(intents);
+ }
}
private Collection<PointToPointIntent> buildSpeakerIntents(BgpConfig.BgpSpeakerConfig speaker) {
@@ -167,8 +180,8 @@ public class PeerConnectivityManager {
List<PointToPointIntent> intents = new ArrayList<>();
TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
-
TrafficSelector selector;
+ Key key;
byte tcpProtocol;
byte icmpProtocol;
@@ -188,8 +201,11 @@ public class PeerConnectivityManager {
null,
BGP_PORT);
+ key = buildKey(ipOne, ipTwo, SUFFIX_DST);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portOne)
@@ -204,8 +220,11 @@ public class PeerConnectivityManager {
BGP_PORT,
null);
+ key = buildKey(ipOne, ipTwo, SUFFIX_SRC);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portOne)
@@ -220,8 +239,11 @@ public class PeerConnectivityManager {
null,
BGP_PORT);
+ key = buildKey(ipTwo, ipOne, SUFFIX_DST);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portTwo)
@@ -236,8 +258,11 @@ public class PeerConnectivityManager {
BGP_PORT,
null);
+ key = buildKey(ipTwo, ipOne, SUFFIX_SRC);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portTwo)
@@ -252,8 +277,11 @@ public class PeerConnectivityManager {
null,
null);
+ key = buildKey(ipOne, ipTwo, SUFFIX_ICMP);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portOne)
@@ -268,8 +296,11 @@ public class PeerConnectivityManager {
null,
null);
+ key = buildKey(ipTwo, ipOne, SUFFIX_ICMP);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portTwo)
@@ -316,4 +347,27 @@ public class PeerConnectivityManager {
return builder.build();
}
+ /**
+ * Builds an intent Key for a point-to-point intent based off the source
+ * and destination IP address, as well as a suffix String to distinguish
+ * between different types of intents between the same source and
+ * destination.
+ *
+ * @param srcIp source IP address
+ * @param dstIp destination IP address
+ * @param suffix suffix string
+ * @return
+ */
+ private Key buildKey(IpAddress srcIp, IpAddress dstIp, String suffix) {
+ String keyString = new StringBuilder()
+ .append(srcIp.toString())
+ .append("-")
+ .append(dstIp.toString())
+ .append("-")
+ .append(suffix)
+ .toString();
+
+ return Key.of(keyString, appId);
+ }
+
}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
index 3d1fe65c..1b3eda9d 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -32,7 +32,9 @@ import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
+import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.SdnIpService;
import org.onosproject.routing.config.RoutingConfigurationService;
import org.slf4j.Logger;
@@ -79,6 +81,7 @@ public class SdnIp implements SdnIpService {
private IntentSynchronizer intentSynchronizer;
private PeerConnectivityManager peerConnectivity;
+ private SdnIpFib fib;
private LeadershipEventListener leadershipEventListener =
new InnerLeadershipEventListener();
@@ -93,10 +96,7 @@ public class SdnIp implements SdnIpService {
localControllerNode = clusterService.getLocalNode();
- intentSynchronizer = new IntentSynchronizer(appId, intentService,
- hostService,
- config,
- interfaceService);
+ intentSynchronizer = new IntentSynchronizer(appId, intentService);
intentSynchronizer.start();
peerConnectivity = new PeerConnectivityManager(appId,
@@ -106,8 +106,9 @@ public class SdnIp implements SdnIpService {
interfaceService);
peerConnectivity.start();
- routingService.addFibListener(intentSynchronizer);
- routingService.addIntentRequestListener(intentSynchronizer);
+ fib = new SdnIpFib(appId, interfaceService, intentSynchronizer);
+
+ routingService.addFibListener(fib);
routingService.start();
leadershipService.addListener(leadershipEventListener);
@@ -131,6 +132,11 @@ public class SdnIp implements SdnIpService {
intentSynchronizer.leaderChanged(isPrimary);
}
+ @Override
+ public IntentSynchronizationService getIntentSynchronizationService() {
+ return intentSynchronizer;
+ }
+
/**
* Converts DPIDs of the form xx:xx:xx:xx:xx:xx:xx to OpenFlow provider
* device URIs.
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
new file mode 100644
index 00000000..c0001bdc
--- /dev/null
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import com.google.common.collect.ImmutableList;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.FibListener;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * FIB component of SDN-IP.
+ */
+public class SdnIpFib implements FibListener {
+ private Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final int PRIORITY_OFFSET = 100;
+ private static final int PRIORITY_MULTIPLIER = 5;
+ protected static final ImmutableList<Constraint> CONSTRAINTS
+ = ImmutableList.of(new PartialFailureConstraint());
+
+ private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+ private final ApplicationId appId;
+ private final InterfaceService interfaceService;
+ private final IntentSynchronizationService intentSynchronizer;
+
+ /**
+ * Class constructor.
+ *
+ * @param appId application ID to use when generating intents
+ * @param interfaceService interface service
+ * @param intentSynchronizer intent synchronizer
+ */
+ public SdnIpFib(ApplicationId appId, InterfaceService interfaceService,
+ IntentSynchronizationService intentSynchronizer) {
+ routeIntents = new ConcurrentHashMap<>();
+
+ this.appId = appId;
+ this.interfaceService = interfaceService;
+ this.intentSynchronizer = intentSynchronizer;
+ }
+
+
+ @Override
+ public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
+ int submitCount = 0, withdrawCount = 0;
+ //
+ // NOTE: Semantically, we MUST withdraw existing intents before
+ // submitting new intents.
+ //
+ synchronized (this) {
+ MultiPointToSinglePointIntent intent;
+
+ //
+ // Prepare the Intent batch operations for the intents to withdraw
+ //
+ for (FibUpdate withdraw : withdraws) {
+ checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
+ "FibUpdate with wrong type in withdraws list");
+
+ IpPrefix prefix = withdraw.entry().prefix();
+ intent = routeIntents.remove(prefix);
+ if (intent == null) {
+ log.trace("SDN-IP No intent in routeIntents to delete " +
+ "for prefix: {}", prefix);
+ continue;
+ }
+ intentSynchronizer.withdraw(intent);
+ withdrawCount++;
+ }
+
+ //
+ // Prepare the Intent batch operations for the intents to submit
+ //
+ for (FibUpdate update : updates) {
+ checkArgument(update.type() == FibUpdate.Type.UPDATE,
+ "FibUpdate with wrong type in updates list");
+
+ IpPrefix prefix = update.entry().prefix();
+ intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
+ update.entry().nextHopMac());
+
+ if (intent == null) {
+ // This preserves the old semantics - if an intent can't be
+ // generated, we don't do anything with that prefix. But
+ // perhaps we should withdraw the old intent anyway?
+ continue;
+ }
+
+ routeIntents.put(prefix, intent);
+ intentSynchronizer.submit(intent);
+ submitCount++;
+ }
+
+ log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
+ updates.size(), withdrawCount, withdraws.size());
+ }
+ }
+
+ /**
+ * Generates a route intent for a prefix, the next hop IP address, and
+ * the next hop MAC address.
+ * <p/>
+ * This method will find the egress interface for the intent.
+ * Intent will match dst IP prefix and rewrite dst MAC address at all other
+ * border switches, then forward packets according to dst MAC address.
+ *
+ * @param prefix IP prefix of the route to add
+ * @param nextHopIpAddress IP address of the next hop
+ * @param nextHopMacAddress MAC address of the next hop
+ * @return the generated intent, or null if no intent should be submitted
+ */
+ private MultiPointToSinglePointIntent generateRouteIntent(
+ IpPrefix prefix,
+ IpAddress nextHopIpAddress,
+ MacAddress nextHopMacAddress) {
+
+ // Find the attachment point (egress interface) of the next hop
+ Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
+ if (egressInterface == null) {
+ log.warn("No outgoing interface found for {}",
+ nextHopIpAddress);
+ return null;
+ }
+
+ // Generate the intent itself
+ Set<ConnectPoint> ingressPorts = new HashSet<>();
+ ConnectPoint egressPort = egressInterface.connectPoint();
+ log.debug("Generating intent for prefix {}, next hop mac {}",
+ prefix, nextHopMacAddress);
+
+ for (Interface intf : interfaceService.getInterfaces()) {
+ // TODO this should be only peering interfaces
+ if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
+ ConnectPoint srcPort = intf.connectPoint();
+ ingressPorts.add(srcPort);
+ }
+ }
+
+ // Match the destination IP prefix at the first hop
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ if (prefix.isIp4()) {
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ selector.matchIPDst(prefix);
+ } else {
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ selector.matchIPv6Dst(prefix);
+ }
+
+ // Rewrite the destination MAC address
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(nextHopMacAddress);
+ if (!egressInterface.vlan().equals(VlanId.NONE)) {
+ treatment.setVlanId(egressInterface.vlan());
+ // If we set VLAN ID, we have to make sure a VLAN tag exists.
+ // TODO support no VLAN -> VLAN routing
+ selector.matchVlanId(VlanId.ANY);
+ }
+
+ int priority =
+ prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
+ Key key = Key.of(prefix.toString(), appId);
+ return MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .ingressPoints(ingressPorts)
+ .egressPoint(egressPort)
+ .priority(priority)
+ .constraints(CONSTRAINTS)
+ .build();
+ }
+
+}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
index 72cc112e..7a17cfe0 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
@@ -18,7 +18,7 @@ package org.onosproject.sdnip.cli;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.sdnip.SdnIpService;
+import org.onosproject.routing.SdnIpService;
/**
* Command to change whether this SDNIP instance is primary or not.
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index fc5782e4..6dc3ce10 100644
--- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -16,6 +16,7 @@
package org.onosproject.sdnip;
import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
import org.junit.Test;
import org.onlab.junit.TestUtils;
@@ -27,10 +28,9 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
@@ -43,20 +43,13 @@ import org.onosproject.net.intent.AbstractIntentTest;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.routing.FibEntry;
-import org.onosproject.routing.FibUpdate;
import org.onosproject.routing.RouteEntry;
-import org.onosproject.routing.config.BgpPeer;
-import org.onosproject.routing.config.RoutingConfigurationService;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
@@ -64,11 +57,8 @@ import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
/**
* This class tests the intent synchronization function in the
@@ -76,10 +66,7 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
*/
public class IntentSyncTest extends AbstractIntentTest {
- private RoutingConfigurationService routingConfig;
- private InterfaceService interfaceService;
private IntentService intentService;
- private NetworkConfigService configService;
private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
@@ -100,65 +87,18 @@ public class IntentSyncTest extends AbstractIntentTest {
private IntentSynchronizer intentSynchronizer;
private final Set<Interface> interfaces = Sets.newHashSet();
- private static final ApplicationId APPID = new ApplicationId() {
- @Override
- public short id() {
- return 1;
- }
-
- @Override
- public String name() {
- return "SDNIP";
- }
- };
+ private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
@Before
public void setUp() throws Exception {
super.setUp();
- routingConfig = createMock(RoutingConfigurationService.class);
- interfaceService = createMock(InterfaceService.class);
- configService = createMock(NetworkConfigService.class);
-
- // These will set expectations on routingConfig
setUpInterfaceService();
- setUpBgpPeers();
-
- replay(routingConfig);
- replay(interfaceService);
intentService = createMock(IntentService.class);
intentSynchronizer = new IntentSynchronizer(APPID, intentService,
- null, routingConfig,
- interfaceService);
- }
-
- /**
- * Sets up BGP peers in external networks.
- */
- private void setUpBgpPeers() {
-
- Map<IpAddress, BgpPeer> peers = new HashMap<>();
-
- String peerSw1Eth1 = "192.168.10.1";
- peers.put(IpAddress.valueOf(peerSw1Eth1),
- new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
-
- // Two BGP peers are connected to switch 2 port 1.
- String peer1Sw2Eth1 = "192.168.20.1";
- peers.put(IpAddress.valueOf(peer1Sw2Eth1),
- new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
-
- String peer2Sw2Eth1 = "192.168.20.2";
- peers.put(IpAddress.valueOf(peer2Sw2Eth1),
- new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
-
- String peer1Sw4Eth1 = "192.168.40.1";
- peers.put(IpAddress.valueOf(peer1Sw4Eth1),
- new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
-
- expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
+ MoreExecutors.newDirectExecutorService());
}
/**
@@ -200,267 +140,13 @@ public class IntentSyncTest extends AbstractIntentTest {
MacAddress.valueOf("00:00:00:00:00:04"),
VlanId.vlanId((short) 1));
- expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn(
- Collections.singleton(sw4Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1")))
- .andReturn(sw4Eth1).anyTimes();
-
interfaces.add(sw4Eth1);
-
- expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn(
- Collections.singleton(sw1Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1")))
- .andReturn(sw1Eth1).anyTimes();
- expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn(
- Collections.singleton(sw2Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1")))
- .andReturn(sw2Eth1).anyTimes();
- expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn(
- Collections.singleton(sw3Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1")))
- .andReturn(sw3Eth1).anyTimes();
- expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
- }
-
- /**
- * Tests adding a FIB entry to the IntentSynchronizer.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is submitted to the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibAdd() throws TestUtilsException {
- FibEntry fibEntry = new FibEntry(
- Ip4Prefix.valueOf("1.1.1.0/24"),
- Ip4Address.valueOf("192.168.10.1"),
- MacAddress.valueOf("00:00:00:00:00:01"));
-
- // Construct a MultiPointToSinglePointIntent intent
- TrafficSelector.Builder selectorBuilder =
- DefaultTrafficSelector.builder();
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
- fibEntry.prefix());
-
- TrafficTreatment.Builder treatmentBuilder =
- DefaultTrafficTreatment.builder();
- treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
-
- Set<ConnectPoint> ingressPoints = new HashSet<>();
- ingressPoints.add(SW2_ETH1);
- ingressPoints.add(SW3_ETH1);
- ingressPoints.add(SW4_ETH1);
-
- MultiPointToSinglePointIntent intent =
- MultiPointToSinglePointIntent.builder()
- .appId(APPID)
- .selector(selectorBuilder.build())
- .treatment(treatmentBuilder.build())
- .ingressPoints(ingressPoints)
- .egressPoint(SW1_ETH1)
- .constraints(IntentSynchronizer.CONSTRAINTS)
- .build();
-
- // Setup the expected intents
- intentService.submit(eqExceptId(intent));
- replay(intentService);
-
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
- fibEntry);
- intentSynchronizer.update(Collections.singleton(fibUpdate),
- Collections.emptyList());
-
- assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
- Intent firstIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
- IntentKey firstIntentKey = new IntentKey(firstIntent);
- IntentKey intentKey = new IntentKey(intent);
- assertTrue(firstIntentKey.equals(intentKey));
- verify(intentService);
- }
-
- /**
- * Tests adding a FIB entry with to a next hop in a VLAN.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is submitted to the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibAddWithVlan() throws TestUtilsException {
- FibEntry fibEntry = new FibEntry(
- Ip4Prefix.valueOf("3.3.3.0/24"),
- Ip4Address.valueOf("192.168.40.1"),
- MacAddress.valueOf("00:00:00:00:00:04"));
-
- // Construct a MultiPointToSinglePointIntent intent
- TrafficSelector.Builder selectorBuilder =
- DefaultTrafficSelector.builder();
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(fibEntry.prefix())
- .matchVlanId(VlanId.ANY);
-
- TrafficTreatment.Builder treatmentBuilder =
- DefaultTrafficTreatment.builder();
- treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
- .setVlanId(VlanId.vlanId((short) 1));
-
- Set<ConnectPoint> ingressPoints = new HashSet<>();
- ingressPoints.add(SW1_ETH1);
- ingressPoints.add(SW2_ETH1);
- ingressPoints.add(SW3_ETH1);
-
- MultiPointToSinglePointIntent intent =
- MultiPointToSinglePointIntent.builder()
- .appId(APPID)
- .selector(selectorBuilder.build())
- .treatment(treatmentBuilder.build())
- .ingressPoints(ingressPoints)
- .egressPoint(SW4_ETH1)
- .constraints(IntentSynchronizer.CONSTRAINTS)
- .build();
-
- // Setup the expected intents
- intentService.submit(eqExceptId(intent));
-
- replay(intentService);
-
- // Run the test
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
-
- intentSynchronizer.update(Collections.singleton(fibUpdate),
- Collections.emptyList());
-
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
- Intent firstIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
- IntentKey firstIntentKey = new IntentKey(firstIntent);
- IntentKey intentKey = new IntentKey(intent);
- assertTrue(firstIntentKey.equals(intentKey));
- verify(intentService);
- }
-
- /**
- * Tests updating a FIB entry.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is submitted to the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibUpdate() throws TestUtilsException {
- // Firstly add a route
- testFibAdd();
-
- Intent addedIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
-
- // Start to construct a new route entry and new intent
- FibEntry fibEntryUpdate = new FibEntry(
- Ip4Prefix.valueOf("1.1.1.0/24"),
- Ip4Address.valueOf("192.168.20.1"),
- MacAddress.valueOf("00:00:00:00:00:02"));
-
- // Construct a new MultiPointToSinglePointIntent intent
- TrafficSelector.Builder selectorBuilderNew =
- DefaultTrafficSelector.builder();
- selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
- fibEntryUpdate.prefix());
-
- TrafficTreatment.Builder treatmentBuilderNew =
- DefaultTrafficTreatment.builder();
- treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
-
-
- Set<ConnectPoint> ingressPointsNew = new HashSet<>();
- ingressPointsNew.add(SW1_ETH1);
- ingressPointsNew.add(SW3_ETH1);
- ingressPointsNew.add(SW4_ETH1);
-
- MultiPointToSinglePointIntent intentNew =
- MultiPointToSinglePointIntent.builder()
- .appId(APPID)
- .selector(selectorBuilderNew.build())
- .treatment(treatmentBuilderNew.build())
- .ingressPoints(ingressPointsNew)
- .egressPoint(SW2_ETH1)
- .constraints(IntentSynchronizer.CONSTRAINTS)
- .build();
-
- // Set up test expectation
- reset(intentService);
- // Setup the expected intents
- intentService.withdraw(eqExceptId(addedIntent));
- intentService.submit(eqExceptId(intentNew));
- replay(intentService);
-
- // Call the update() method in IntentSynchronizer class
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
- fibEntryUpdate);
- intentSynchronizer.update(Collections.singletonList(fibUpdate),
- Collections.emptyList());
-
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
- Intent firstIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
- IntentKey firstIntentKey = new IntentKey(firstIntent);
- IntentKey intentNewKey = new IntentKey(intentNew);
- assertTrue(firstIntentKey.equals(intentNewKey));
- verify(intentService);
}
/**
- * Tests deleting a FIB entry.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is withdrawn from the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibDelete() throws TestUtilsException {
- // Firstly add a route
- testFibAdd();
-
- Intent addedIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
-
- // Construct the existing route entry
- FibEntry fibEntry = new FibEntry(
- Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
-
- // Set up expectation
- reset(intentService);
- // Setup the expected intents
- intentService.withdraw(eqExceptId(addedIntent));
- replay(intentService);
-
- // Call the update() method in IntentSynchronizer class
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
- intentSynchronizer.update(Collections.emptyList(),
- Collections.singletonList(fibUpdate));
-
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
- verify(intentService);
- }
-
- /**
- * This method tests the behavior of intent Synchronizer.
+ * Tests the synchronization behavior of intent synchronizer. We set up
+ * a discrepancy between the intent service state and the intent
+ * synchronizer's state and ensure that this is reconciled correctly.
*
* @throws TestUtilsException
*/
@@ -529,27 +215,13 @@ public class IntentSyncTest extends AbstractIntentTest {
// Compose a intent, which is equal to intent5 but the id is different.
MultiPointToSinglePointIntent intent5New =
staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01");
- assertThat(IntentSynchronizer.IntentKey.equalIntents(
- intent5, intent5New),
- is(true));
+ assertThat(IntentUtils.equals(intent5, intent5New), is(true));
assertFalse(intent5.equals(intent5New));
MultiPointToSinglePointIntent intent6 = intentBuilder(
routeEntry6.prefix(), "00:00:00:00:00:01", SW1_ETH1);
- // Set up the routeIntents field in IntentSynchronizer class
- ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
- routeIntents = new ConcurrentHashMap<>();
- routeIntents.put(routeEntry1.prefix(), intent1);
- routeIntents.put(routeEntry3.prefix(), intent3);
- routeIntents.put(routeEntry4Update.prefix(), intent4Update);
- routeIntents.put(routeEntry5.prefix(), intent5New);
- routeIntents.put(routeEntry6.prefix(), intent6);
- routeIntents.put(routeEntry7.prefix(), intent7);
- TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
-
// Set up expectation
- reset(intentService);
Set<Intent> intents = new HashSet<>();
intents.add(intent1);
expect(intentService.getIntentState(intent1.key()))
@@ -568,9 +240,9 @@ public class IntentSyncTest extends AbstractIntentTest {
.andReturn(IntentState.WITHDRAWING).anyTimes();
expect(intentService.getIntents()).andReturn(intents).anyTimes();
+ // These are the operations that should be done to the intentService
+ // during synchronization
intentService.withdraw(intent2);
- intentService.withdraw(intent4);
-
intentService.submit(intent3);
intentService.submit(intent4Update);
intentService.submit(intent6);
@@ -578,16 +250,101 @@ public class IntentSyncTest extends AbstractIntentTest {
replay(intentService);
// Start the test
+
+ // Simulate some input from the clients. The intent synchronizer has not
+ // gained the global leadership yet, but it will remember this input for
+ // when it does.
+ intentSynchronizer.submit(intent1);
+ intentSynchronizer.submit(intent2);
+ intentSynchronizer.withdraw(intent2);
+ intentSynchronizer.submit(intent3);
+ intentSynchronizer.submit(intent4);
+ intentSynchronizer.submit(intent4Update);
+ intentSynchronizer.submit(intent5);
+ intentSynchronizer.submit(intent6);
+ intentSynchronizer.submit(intent7);
+
+ // Give the leadership to the intent synchronizer. It will now attempt
+ // to synchronize the intents in the store with the intents it has
+ // recorded based on the earlier user input.
+ intentSynchronizer.leaderChanged(true);
+
+ verify(intentService);
+ }
+
+ /**
+ * Tests the behavior of the submit API, both when the synchronizer has
+ * leadership and when it does not.
+ */
+ @Test
+ public void testSubmit() {
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+ Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1);
+
+ // Set up expectations
+ intentService.submit(intent);
+ expect(intentService.getIntents()).andReturn(Collections.emptyList())
+ .anyTimes();
+ replay(intentService);
+
+ // Give the intent synchronizer leadership so it will submit intents
+ // to the intent service
+ intentSynchronizer.leaderChanged(true);
+
+ // Test the submit
+ intentSynchronizer.submit(intent);
+
+ verify(intentService);
+
+ // Now we'll remove leadership from the intent synchronizer and verify
+ // that it does not submit any intents to the intent service when we
+ // call the submit API
+ reset(intentService);
+ replay(intentService);
+
+ intentSynchronizer.leaderChanged(false);
+
+ intentSynchronizer.submit(intent);
+
+ verify(intentService);
+ }
+
+ /**
+ * Tests the behavior of the withdraw API, both when the synchronizer has
+ * leadership and when it does not.
+ */
+ @Test
+ public void testWithdraw() {
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+ Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1);
+
+ // Submit an intent first so we can withdraw it later
+ intentService.submit(intent);
+ intentService.withdraw(intent);
+ expect(intentService.getIntents()).andReturn(Collections.emptyList())
+ .anyTimes();
+ replay(intentService);
+
+ // Give the intent synchronizer leadership so it will submit intents
+ // to the intent service
intentSynchronizer.leaderChanged(true);
- intentSynchronizer.synchronizeIntents();
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 6);
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent1));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent3));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent4Update));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent5));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent6));
+ // Test the submit then withdraw
+ intentSynchronizer.submit(intent);
+ intentSynchronizer.withdraw(intent);
+
+ verify(intentService);
+
+ // Now we'll remove leadership from the intent synchronizer and verify
+ // that it does not withdraw any intents to the intent service when we
+ // call the withdraw API
+ reset(intentService);
+ replay(intentService);
+
+ intentSynchronizer.leaderChanged(false);
+
+ intentSynchronizer.submit(intent);
+ intentSynchronizer.withdraw(intent);
verify(intentService);
}
@@ -607,10 +364,10 @@ public class IntentSyncTest extends AbstractIntentTest {
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
if (ipPrefix.isIp4()) {
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV4); // IPv4
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
selectorBuilder.matchIPDst(ipPrefix);
} else {
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV6); // IPv6
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
selectorBuilder.matchIPv6Dst(ipPrefix);
}
@@ -628,11 +385,12 @@ public class IntentSyncTest extends AbstractIntentTest {
MultiPointToSinglePointIntent intent =
MultiPointToSinglePointIntent.builder()
.appId(APPID)
+ .key(Key.of(ipPrefix.toString(), APPID))
.selector(selectorBuilder.build())
.treatment(treatmentBuilder.build())
.ingressPoints(ingressPoints)
.egressPoint(egressPoint)
- .constraints(IntentSynchronizer.CONSTRAINTS)
+ .constraints(SdnIpFib.CONSTRAINTS)
.build();
return intent;
}
@@ -646,7 +404,7 @@ public class IntentSyncTest extends AbstractIntentTest {
* @return the newly constructed MultiPointToSinglePointIntent
* @throws TestUtilsException
*/
- private MultiPointToSinglePointIntent staticIntentBuilder(
+ private MultiPointToSinglePointIntent staticIntentBuilder(
MultiPointToSinglePointIntent intent, RouteEntry routeEntry,
String nextHopMacAddress) throws TestUtilsException {
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
index d89c3c2b..c4b2daad 100644
--- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
@@ -19,7 +19,6 @@ import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.onlab.junit.TestUtils;
import org.onlab.junit.TestUtils.TestUtilsException;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
@@ -28,13 +27,14 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
@@ -42,8 +42,9 @@ import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intent.AbstractIntentTest;
import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.routing.config.BgpConfig;
import org.onosproject.routing.config.BgpPeer;
import org.onosproject.routing.config.BgpSpeaker;
@@ -71,26 +72,15 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
*/
public class PeerConnectivityManagerTest extends AbstractIntentTest {
- private static final ApplicationId APPID = new ApplicationId() {
- @Override
- public short id() {
- return 0;
- }
-
- @Override
- public String name() {
- return "foo";
- }
- };
+ private static final ApplicationId APPID = TestApplicationId.create("foo");
private static final ApplicationId CONFIG_APP_ID = APPID;
private PeerConnectivityManager peerConnectivityManager;
- private IntentSynchronizer intentSynchronizer;
+ private IntentSynchronizationService intentSynchronizer;
private RoutingConfigurationService routingConfig;
private InterfaceService interfaceService;
private NetworkConfigService networkConfigService;
- private IntentService intentService;
private Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers;
private Map<String, Interface> interfaces;
@@ -98,8 +88,6 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
private BgpConfig bgpConfig;
- private Map<String, Interface> configuredInterfaces;
- private Map<IpAddress, BgpPeer> configuredPeers;
private List<PointToPointIntent> intentList;
private final String dpid1 = "00:00:00:00:00:00:00:01";
@@ -136,7 +124,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
// These will set expectations on routingConfig and interfaceService
bgpSpeakers = setUpBgpSpeakers();
interfaces = Collections.unmodifiableMap(setUpInterfaces());
- peers = Collections.unmodifiableMap(setUpPeers());
+ peers = setUpPeers();
initPeerConnectivity();
intentList = setUpIntentList();
@@ -169,11 +157,11 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
* Sets up logical interfaces, which emulate the configured interfaces
* in SDN-IP application.
*
- * @return configured interfaces as a MAP from Interface name to Interface
+ * @return configured interfaces as a map from interface name to Interface
*/
private Map<String, Interface> setUpInterfaces() {
- configuredInterfaces = new HashMap<>();
+ Map<String, Interface> configuredInterfaces = new HashMap<>();
String interfaceSw1Eth1 = "s1-eth1";
InterfaceIpAddress ia1 =
@@ -242,7 +230,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
*/
private Map<IpAddress, BgpPeer> setUpPeers() {
- configuredPeers = new HashMap<>();
+ Map<IpAddress, BgpPeer> configuredPeers = new HashMap<>();
String peerSw1Eth1 = "192.168.10.1";
configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
@@ -266,14 +254,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
* @return point to point intent list
*/
private List<PointToPointIntent> setUpIntentList() {
-
intentList = new ArrayList<>();
setUpBgpIntents();
setUpIcmpIntents();
return intentList;
-
}
/**
@@ -306,8 +292,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
builder.matchTcpDst(TpPort.tpPort(dstTcpPort));
}
+ Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0]
+ + "-" + ((srcTcpPort == null) ? "dst" : "src"), APPID);
+
PointToPointIntent intent = PointToPointIntent.builder()
.appId(APPID)
+ .key(key)
.selector(builder.build())
.treatment(noTreatment)
.ingressPoint(srcConnectPoint)
@@ -392,8 +382,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
.matchIPDst(IpPrefix.valueOf(dstPrefix))
.build();
+ Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0]
+ + "-" + "icmp", APPID);
+
PointToPointIntent intent = PointToPointIntent.builder()
.appId(APPID)
+ .key(key)
.selector(selector)
.treatment(noTreatment)
.ingressPoint(srcConnectPoint)
@@ -434,19 +428,14 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
expect(bgpConfig.bgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
replay(bgpConfig);
- expect(networkConfigService.getConfig(APPID, BgpConfig.class)).andReturn(bgpConfig).anyTimes();
+ expect(networkConfigService.getConfig(APPID, BgpConfig.class))
+ .andReturn(bgpConfig).anyTimes();
replay(networkConfigService);
replay(routingConfig);
replay(interfaceService);
- intentService = createMock(IntentService.class);
- replay(intentService);
-
- intentSynchronizer = new IntentSynchronizer(APPID, intentService,
- null, routingConfig,
- interfaceService);
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+ intentSynchronizer = createMock(IntentSynchronizationService.class);
+ replay(intentSynchronizer);
peerConnectivityManager =
new PeerConnectivityManager(APPID, intentSynchronizer,
@@ -464,20 +453,18 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
*/
@Test
public void testConnectionSetup() {
-
- reset(intentService);
+ reset(intentSynchronizer);
// Setup the expected intents
for (Intent intent : intentList) {
- intentService.submit(eqExceptId(intent));
+ intentSynchronizer.submit(eqExceptId(intent));
}
- replay(intentService);
+ replay(intentSynchronizer);
// Running the interface to be tested.
peerConnectivityManager.start();
- verify(intentService);
-
+ verify(intentSynchronizer);
}
/**
@@ -488,7 +475,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
reset(interfaceService);
expect(interfaceService.getInterfaces()).andReturn(
- Sets.<Interface>newHashSet()).anyTimes();
+ Sets.newHashSet()).anyTimes();
expect(interfaceService.getInterfacesByPort(s2Eth1))
.andReturn(Collections.emptySet()).anyTimes();
expect(interfaceService.getInterfacesByPort(s1Eth1))
@@ -508,10 +495,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
replay(interfaceService);
- reset(intentService);
- replay(intentService);
+ reset(intentSynchronizer);
+ replay(intentSynchronizer);
peerConnectivityManager.start();
- verify(intentService);
+ verify(intentSynchronizer);
}
/**
@@ -527,10 +514,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
replay(routingConfig);
- reset(intentService);
- replay(intentService);
+ reset(intentSynchronizer);
+ replay(intentSynchronizer);
peerConnectivityManager.start();
- verify(intentService);
+ verify(intentSynchronizer);
}
/**
@@ -540,7 +527,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
@Test
public void testNoPeerInterface() {
String peerSw100Eth1 = "192.168.200.1";
- configuredPeers.put(IpAddress.valueOf(peerSw100Eth1),
+ peers.put(IpAddress.valueOf(peerSw100Eth1),
new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1));
testConnectionSetup();
}
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
new file mode 100644
index 00000000..5466d520
--- /dev/null
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intent.AbstractIntentTest;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.routing.FibEntry;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.config.BgpPeer;
+import org.onosproject.routing.config.RoutingConfigurationService;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
+
+/**
+ * Unit tests for SdnIpFib.
+ */
+public class SdnIpFibTest extends AbstractIntentTest {
+
+ private RoutingConfigurationService routingConfig;
+ private InterfaceService interfaceService;
+
+ private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000001"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000002"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000003"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000004"),
+ PortNumber.portNumber(1));
+
+ private SdnIpFib sdnipFib;
+ private IntentSynchronizationService intentSynchronizer;
+ private final Set<Interface> interfaces = Sets.newHashSet();
+
+ private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ routingConfig = createMock(RoutingConfigurationService.class);
+ interfaceService = createMock(InterfaceService.class);
+
+ // These will set expectations on routingConfig and interfaceService
+ setUpInterfaceService();
+ setUpBgpPeers();
+
+ replay(routingConfig);
+ replay(interfaceService);
+
+ intentSynchronizer = createMock(IntentSynchronizationService.class);
+
+ sdnipFib = new SdnIpFib(APPID, interfaceService, intentSynchronizer);
+ }
+
+ /**
+ * Sets up BGP peers in external networks.
+ */
+ private void setUpBgpPeers() {
+
+ Map<IpAddress, BgpPeer> peers = new HashMap<>();
+
+ String peerSw1Eth1 = "192.168.10.1";
+ peers.put(IpAddress.valueOf(peerSw1Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
+
+ // Two BGP peers are connected to switch 2 port 1.
+ String peer1Sw2Eth1 = "192.168.20.1";
+ peers.put(IpAddress.valueOf(peer1Sw2Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
+
+ String peer2Sw2Eth1 = "192.168.20.2";
+ peers.put(IpAddress.valueOf(peer2Sw2Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
+
+ String peer1Sw4Eth1 = "192.168.40.1";
+ peers.put(IpAddress.valueOf(peer1Sw4Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
+
+ expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
+ }
+
+ /**
+ * Sets up InterfaceService.
+ */
+ private void setUpInterfaceService() {
+ Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet();
+ interfaceIpAddresses1.add(new InterfaceIpAddress(
+ IpAddress.valueOf("192.168.10.101"),
+ IpPrefix.valueOf("192.168.10.0/24")));
+ Interface sw1Eth1 = new Interface(SW1_ETH1,
+ interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
+ VlanId.NONE);
+ interfaces.add(sw1Eth1);
+
+ Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
+ interfaceIpAddresses2.add(
+ new InterfaceIpAddress(IpAddress.valueOf("192.168.20.101"),
+ IpPrefix.valueOf("192.168.20.0/24")));
+ Interface sw2Eth1 = new Interface(SW2_ETH1,
+ interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
+ VlanId.NONE);
+ interfaces.add(sw2Eth1);
+
+ Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
+ interfaceIpAddresses3.add(
+ new InterfaceIpAddress(IpAddress.valueOf("192.168.30.101"),
+ IpPrefix.valueOf("192.168.30.0/24")));
+ Interface sw3Eth1 = new Interface(SW3_ETH1,
+ interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
+ VlanId.NONE);
+ interfaces.add(sw3Eth1);
+
+ InterfaceIpAddress interfaceIpAddress4 =
+ new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"),
+ IpPrefix.valueOf("192.168.40.0/24"));
+ Interface sw4Eth1 = new Interface(SW4_ETH1,
+ Sets.newHashSet(interfaceIpAddress4),
+ MacAddress.valueOf("00:00:00:00:00:04"),
+ VlanId.vlanId((short) 1));
+
+ expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn(
+ Collections.singleton(sw4Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1")))
+ .andReturn(sw4Eth1).anyTimes();
+
+ interfaces.add(sw4Eth1);
+
+ expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn(
+ Collections.singleton(sw1Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1")))
+ .andReturn(sw1Eth1).anyTimes();
+ expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn(
+ Collections.singleton(sw2Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1")))
+ .andReturn(sw2Eth1).anyTimes();
+ expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn(
+ Collections.singleton(sw3Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1")))
+ .andReturn(sw3Eth1).anyTimes();
+ expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
+ }
+
+ /**
+ * Tests adding a FIB entry to the IntentSynchronizer.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is submitted to the IntentService.
+ */
+ @Test
+ public void testFibAdd() {
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+ FibEntry fibEntry = new FibEntry(prefix,
+ Ip4Address.valueOf("192.168.10.1"),
+ MacAddress.valueOf("00:00:00:00:00:01"));
+
+ // Construct a MultiPointToSinglePointIntent intent
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder();
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+ fibEntry.prefix());
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(SW2_ETH1);
+ ingressPoints.add(SW3_ETH1);
+ ingressPoints.add(SW4_ETH1);
+
+ MultiPointToSinglePointIntent intent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilder.build())
+ .treatment(treatmentBuilder.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(SW1_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Setup the expected intents
+ intentSynchronizer.submit(eqExceptId(intent));
+ replay(intentSynchronizer);
+
+ // Send in the UPDATE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+ sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+
+ verify(intentSynchronizer);
+ }
+
+ /**
+ * Tests adding a FIB entry with to a next hop in a VLAN.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is submitted to the IntentService.
+ */
+ @Test
+ public void testFibAddWithVlan() {
+ IpPrefix prefix = Ip4Prefix.valueOf("3.3.3.0/24");
+ FibEntry fibEntry = new FibEntry(prefix,
+ Ip4Address.valueOf("192.168.40.1"),
+ MacAddress.valueOf("00:00:00:00:00:04"));
+
+ // Construct a MultiPointToSinglePointIntent intent
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder();
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(fibEntry.prefix())
+ .matchVlanId(VlanId.ANY);
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
+ .setVlanId(VlanId.vlanId((short) 1));
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(SW1_ETH1);
+ ingressPoints.add(SW2_ETH1);
+ ingressPoints.add(SW3_ETH1);
+
+ MultiPointToSinglePointIntent intent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilder.build())
+ .treatment(treatmentBuilder.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(SW4_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Setup the expected intents
+ intentSynchronizer.submit(eqExceptId(intent));
+
+ replay(intentSynchronizer);
+
+ // Send in the UPDATE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+ sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+
+ verify(intentSynchronizer);
+ }
+
+ /**
+ * Tests updating a FIB entry.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is submitted to the IntentService.
+ */
+ @Test
+ public void testFibUpdate() {
+ // Firstly add a route
+ testFibAdd();
+
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+
+ // Start to construct a new route entry and new intent
+ FibEntry fibEntryUpdate = new FibEntry(prefix,
+ Ip4Address.valueOf("192.168.20.1"),
+ MacAddress.valueOf("00:00:00:00:00:02"));
+
+ // Construct a new MultiPointToSinglePointIntent intent
+ TrafficSelector.Builder selectorBuilderNew =
+ DefaultTrafficSelector.builder();
+ selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+ fibEntryUpdate.prefix());
+
+ TrafficTreatment.Builder treatmentBuilderNew =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
+
+ Set<ConnectPoint> ingressPointsNew = new HashSet<>();
+ ingressPointsNew.add(SW1_ETH1);
+ ingressPointsNew.add(SW3_ETH1);
+ ingressPointsNew.add(SW4_ETH1);
+
+ MultiPointToSinglePointIntent intentNew =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilderNew.build())
+ .treatment(treatmentBuilderNew.build())
+ .ingressPoints(ingressPointsNew)
+ .egressPoint(SW2_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Set up test expectation
+ reset(intentSynchronizer);
+
+ // Setup the expected intents
+ intentSynchronizer.submit(eqExceptId(intentNew));
+ replay(intentSynchronizer);
+
+ // Send in the UPDATE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
+ fibEntryUpdate);
+ sdnipFib.update(Collections.singletonList(fibUpdate),
+ Collections.emptyList());
+
+ verify(intentSynchronizer);
+ }
+
+ /**
+ * Tests deleting a FIB entry.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is withdrawn from the IntentService.
+ */
+ @Test
+ public void testFibDelete() {
+ // Firstly add a route
+ testFibAdd();
+
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+
+ // Construct the existing route entry
+ FibEntry fibEntry = new FibEntry(prefix, null, null);
+
+ // Construct the existing MultiPointToSinglePoint intent
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder();
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+ fibEntry.prefix());
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(SW2_ETH1);
+ ingressPoints.add(SW3_ETH1);
+ ingressPoints.add(SW4_ETH1);
+
+ MultiPointToSinglePointIntent addedIntent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilder.build())
+ .treatment(treatmentBuilder.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(SW1_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Set up expectation
+ reset(intentSynchronizer);
+ // Setup the expected intents
+ intentSynchronizer.withdraw(eqExceptId(addedIntent));
+ replay(intentSynchronizer);
+
+ // Send in the DELETE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
+ sdnipFib.update(Collections.emptyList(), Collections.singletonList(fibUpdate));
+
+ verify(intentSynchronizer);
+ }
+}
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
index 69b18aa9..7f825e81 100644
--- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
@@ -17,7 +17,6 @@ package org.onosproject.sdnip;
import org.easymock.IArgumentMatcher;
import org.onosproject.net.intent.Intent;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
import static org.easymock.EasyMock.reportMatcher;
@@ -53,8 +52,6 @@ public final class TestIntentServiceHelper {
* the solution is to use an EasyMock matcher that verifies that all the
* value properties of the provided intent match the expected values, but
* ignores the intent ID when testing equality.
- *
- * FIXME this currently does not take key into account
*/
private static final class IdAgnosticIntentMatcher implements
IArgumentMatcher {
@@ -86,9 +83,7 @@ public final class TestIntentServiceHelper {
Intent providedIntent = (Intent) object;
providedString = providedIntent.toString();
- IntentKey thisIntentKey = new IntentKey(intent);
- IntentKey providedIntentKey = new IntentKey(providedIntent);
- return thisIntentKey.equals(providedIntentKey);
+ return IntentUtils.equals(intent, providedIntent);
}
}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
index ef9d444a..8fdf81a2 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,24 +18,26 @@ package org.onosproject.segmentrouting;
import com.google.common.collect.Lists;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
+import org.onosproject.incubator.net.config.basics.ConfigException;
+import org.onosproject.incubator.net.config.basics.InterfaceConfig;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
+import org.onosproject.segmentrouting.config.SegmentRoutingConfig.AdjacencySid;
import org.onosproject.segmentrouting.grouphandler.DeviceProperties;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
-import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
-import org.onosproject.segmentrouting.config.NetworkConfigManager;
-import org.onosproject.segmentrouting.config.SegmentRouterConfig;
-import org.onosproject.segmentrouting.config.SegmentRouterConfig.Subnet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Segment Routing configuration component that reads the
@@ -50,7 +52,6 @@ public class DeviceConfiguration implements DeviceProperties {
.getLogger(DeviceConfiguration.class);
private final List<Integer> allSegmentIds = new ArrayList<>();
private final HashMap<DeviceId, SegmentRouterInfo> deviceConfigMap = new HashMap<>();
- private final NetworkConfigManager configService;
private class SegmentRouterInfo {
int nodeSid;
@@ -60,48 +61,60 @@ public class DeviceConfiguration implements DeviceProperties {
boolean isEdge;
HashMap<PortNumber, Ip4Address> gatewayIps;
HashMap<PortNumber, Ip4Prefix> subnets;
- List<SegmentRouterConfig.AdjacencySid> adjacencySids;
+ List<AdjacencySid> adjacencySids;
}
/**
* Constructor. Reads all the configuration for all devices of type
* Segment Router and organizes into various maps for easier access.
- *
- * @param configService handle to network configuration manager
- * component from where the relevant configuration is retrieved.
*/
- public DeviceConfiguration(NetworkConfigManager configService) {
- this.configService = checkNotNull(configService);
- List<SwitchConfig> allSwitchCfg =
- this.configService.getConfiguredAllowedSwitches();
- for (SwitchConfig cfg : allSwitchCfg) {
- if (!(cfg instanceof SegmentRouterConfig)) {
- continue;
- }
+ public DeviceConfiguration(NetworkConfigRegistry cfgService) {
+ // Read config from device subject, excluding gatewayIps and subnets.
+ Set<DeviceId> deviceSubjects =
+ cfgService.getSubjects(DeviceId.class, SegmentRoutingConfig.class);
+ deviceSubjects.forEach(subject -> {
+ SegmentRoutingConfig config =
+ cfgService.getConfig(subject, SegmentRoutingConfig.class);
SegmentRouterInfo info = new SegmentRouterInfo();
- info.nodeSid = ((SegmentRouterConfig) cfg).getNodeSid();
- info.deviceId = ((SegmentRouterConfig) cfg).getDpid();
- info.mac = MacAddress.valueOf(((
- SegmentRouterConfig) cfg).getRouterMac());
- String routerIp = ((SegmentRouterConfig) cfg).getRouterIp();
- Ip4Prefix prefix = checkNotNull(IpPrefix.valueOf(routerIp).getIp4Prefix());
- info.ip = prefix.address();
- info.isEdge = ((SegmentRouterConfig) cfg).isEdgeRouter();
- info.subnets = new HashMap<>();
+ info.deviceId = subject;
+ info.nodeSid = config.getSid();
+ info.ip = config.getIp();
+ info.mac = config.getMac();
+ info.isEdge = config.isEdgeRouter();
+ info.adjacencySids = config.getAdjacencySids();
info.gatewayIps = new HashMap<>();
- for (Subnet s: ((SegmentRouterConfig) cfg).getSubnets()) {
- info.subnets.put(PortNumber.portNumber(s.getPortNo()),
- Ip4Prefix.valueOf(s.getSubnetIp()));
- String gatewayIp = s.getSubnetIp().
- substring(0, s.getSubnetIp().indexOf('/'));
- info.gatewayIps.put(PortNumber.portNumber(s.getPortNo()),
- Ip4Address.valueOf(gatewayIp));
- }
- info.adjacencySids = ((SegmentRouterConfig) cfg).getAdjacencySids();
+ info.subnets = new HashMap<>();
+
this.deviceConfigMap.put(info.deviceId, info);
this.allSegmentIds.add(info.nodeSid);
-
- }
+ });
+
+ // Read gatewayIps and subnets from port subject.
+ Set<ConnectPoint> portSubjects =
+ cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
+ portSubjects.forEach(subject -> {
+ InterfaceConfig config =
+ cfgService.getConfig(subject, InterfaceConfig.class);
+ Set<Interface> networkInterfaces;
+ try {
+ networkInterfaces = config.getInterfaces();
+ } catch (ConfigException e) {
+ log.error("Error loading port configuration");
+ return;
+ }
+ networkInterfaces.forEach(networkInterface -> {
+ DeviceId dpid = networkInterface.connectPoint().deviceId();
+ PortNumber port = networkInterface.connectPoint().port();
+ SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
+
+ Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
+ interfaceAddresses.forEach(interfaceAddress -> {
+ info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+ info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
+ });
+ });
+
+ });
}
/**
@@ -379,8 +392,8 @@ public class DeviceConfiguration implements DeviceProperties {
*/
public List<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) {
if (deviceConfigMap.get(deviceId) != null) {
- for (SegmentRouterConfig.AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) {
- if (asid.getAdjSid() == sid) {
+ for (AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) {
+ if (asid.getAsid() == sid) {
return asid.getPorts();
}
}
@@ -402,9 +415,9 @@ public class DeviceConfiguration implements DeviceProperties {
if (deviceConfigMap.get(deviceId).adjacencySids.isEmpty()) {
return false;
} else {
- for (SegmentRouterConfig.AdjacencySid asid:
+ for (AdjacencySid asid:
deviceConfigMap.get(deviceId).adjacencySids) {
- if (asid.getAdjSid() == sid) {
+ if (asid.getAsid() == sid) {
return true;
}
}
@@ -414,4 +427,4 @@ public class DeviceConfiguration implements DeviceProperties {
return false;
}
-}
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index 0f8fa59d..f65f03e0 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -111,10 +111,10 @@ public class IcmpHandler {
icmpReplyIpv4.setChecksum((short) 0);
ICMP icmpReply = new ICMP();
+ icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload());
icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
icmpReply.setChecksum((short) 0);
-
icmpReplyIpv4.setPayload(icmpReply);
icmpReplyEth.setPayload(icmpReplyIpv4);
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 874faabf..05663129 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -27,6 +27,12 @@ import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.event.Event;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
import org.onosproject.segmentrouting.grouphandler.NeighborSet;
import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
@@ -50,7 +56,6 @@ import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.net.topology.TopologyService;
-import org.onosproject.segmentrouting.config.NetworkConfigManager;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
import org.onosproject.store.service.StorageService;
@@ -133,7 +138,21 @@ public class SegmentRoutingManager implements SegmentRoutingService {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
- private NetworkConfigManager networkConfigService = new NetworkConfigManager();;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry cfgService;
+
+ private final InternalConfigListener cfgListener =
+ new InternalConfigListener(this);
+
+ private final ConfigFactory cfgFactory =
+ new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY,
+ SegmentRoutingConfig.class,
+ "segmentrouting") {
+ @Override
+ public SegmentRoutingConfig createConfig() {
+ return new SegmentRoutingConfig();
+ }
+ };
private Object threadSchedulerLock = new Object();
private static int numOfEventsQueued = 0;
@@ -192,44 +211,17 @@ public class SegmentRoutingManager implements SegmentRoutingService {
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
- networkConfigService.init();
- deviceConfiguration = new DeviceConfiguration(networkConfigService);
- arpHandler = new ArpHandler(this);
- icmpHandler = new IcmpHandler(this);
- ipHandler = new IpHandler(this);
- routingRulePopulator = new RoutingRulePopulator(this);
- defaultRoutingHandler = new DefaultRoutingHandler(this);
- tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
- groupHandlerMap, tunnelStore);
- policyHandler = new PolicyHandler(appId, deviceConfiguration,
- flowObjectiveService, tunnelHandler, policyStore);
-
- packetService.addProcessor(processor, PacketProcessor.director(2));
- linkService.addListener(new InternalLinkListener());
- deviceService.addListener(new InternalDeviceListener());
-
- for (Device device : deviceService.getDevices()) {
- //Irrespective whether the local is a MASTER or not for this device,
- //create group handler instance and push default TTP flow rules.
- //Because in a multi-instance setup, instances can initiate
- //groups for any devices. Also the default TTP rules are needed
- //to be pushed before inserting any IP table entries for any device
- DefaultGroupHandler groupHandler = DefaultGroupHandler
- .createGroupHandler(device.id(), appId,
- deviceConfiguration, linkService,
- flowObjectiveService,
- nsNextObjStore);
- groupHandlerMap.put(device.id(), groupHandler);
- defaultRoutingHandler.populateTtpRules(device.id());
- }
+ cfgService.addListener(cfgListener);
+ cfgService.registerConfigFactory(cfgFactory);
- defaultRoutingHandler.startPopulationProcess();
log.info("Started");
-
}
@Deactivate
protected void deactivate() {
+ cfgService.removeListener(cfgListener);
+ cfgService.unregisterConfigFactory(cfgFactory);
+
packetService.removeProcessor(processor);
processor = null;
log.info("Stopped");
@@ -512,6 +504,59 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
}
+ private class InternalConfigListener implements NetworkConfigListener {
+ SegmentRoutingManager segmentRoutingManager;
+
+ public InternalConfigListener(SegmentRoutingManager srMgr) {
+ this.segmentRoutingManager = srMgr;
+ }
+ public void configureNetwork() {
+ deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
+
+ arpHandler = new ArpHandler(segmentRoutingManager);
+ icmpHandler = new IcmpHandler(segmentRoutingManager);
+ ipHandler = new IpHandler(segmentRoutingManager);
+ routingRulePopulator = new RoutingRulePopulator(segmentRoutingManager);
+ defaultRoutingHandler = new DefaultRoutingHandler(segmentRoutingManager);
+
+ tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
+ groupHandlerMap, tunnelStore);
+ policyHandler = new PolicyHandler(appId, deviceConfiguration,
+ flowObjectiveService,
+ tunnelHandler, policyStore);
+
+ packetService.addProcessor(processor, PacketProcessor.director(2));
+ linkService.addListener(new InternalLinkListener());
+ deviceService.addListener(new InternalDeviceListener());
+
+ for (Device device : deviceService.getDevices()) {
+ //Irrespective whether the local is a MASTER or not for this device,
+ //create group handler instance and push default TTP flow rules.
+ //Because in a multi-instance setup, instances can initiate
+ //groups for any devices. Also the default TTP rules are needed
+ //to be pushed before inserting any IP table entries for any device
+ DefaultGroupHandler groupHandler = DefaultGroupHandler
+ .createGroupHandler(device.id(), appId,
+ deviceConfiguration, linkService,
+ flowObjectiveService,
+ nsNextObjStore);
+ groupHandlerMap.put(device.id(), groupHandler);
+ defaultRoutingHandler.populateTtpRules(device.id());
+ }
+ defaultRoutingHandler.startPopulationProcess();
+ }
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(SegmentRoutingConfig.class)) {
+ log.info("Network configuration change detected. (Re)Configuring...");
+ configureNetwork();
+ return;
+ }
+ }
+ }
}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
new file mode 100644
index 00000000..6dc3f0db
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.BasicElementConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Configuration object for Segment Routing Application.
+ */
+public class SegmentRoutingConfig extends Config<DeviceId> {
+ private static final String NAME = "name";
+ private static final String IP = "routerIp";
+ private static final String MAC = "routerMac";
+ private static final String SID = "nodeSid";
+ private static final String EDGE = "isEdgeRouter";
+ private static final String ADJSID = "adjacencySids";
+
+ public Optional<String> getName() {
+ String name = get(NAME, null);
+ return name != null ? Optional.of(name) : Optional.empty();
+ }
+
+ public BasicElementConfig setName(String name) {
+ return (BasicElementConfig) setOrClear(NAME, name);
+ }
+
+ public Ip4Address getIp() {
+ String ip = get(IP, null);
+ return ip != null ? Ip4Address.valueOf(ip) : null;
+ }
+
+ public BasicElementConfig setIp(String ip) {
+ return (BasicElementConfig) setOrClear(IP, ip);
+ }
+
+ public MacAddress getMac() {
+ String mac = get(MAC, null);
+ return mac != null ? MacAddress.valueOf(mac) : null;
+ }
+
+ public BasicElementConfig setMac(String mac) {
+ return (BasicElementConfig) setOrClear(MAC, mac);
+ }
+
+ public int getSid() {
+ return get(SID, -1);
+ }
+
+ public BasicElementConfig setSid(int sid) {
+ return (BasicElementConfig) setOrClear(SID, sid);
+ }
+
+ public boolean isEdgeRouter() {
+ return get(EDGE, false);
+ }
+
+ public BasicElementConfig setEdgeRouter(boolean isEdgeRouter) {
+ return (BasicElementConfig) setOrClear(EDGE, isEdgeRouter);
+ }
+
+ public List<AdjacencySid> getAdjacencySids() {
+ ArrayList<AdjacencySid> adjacencySids = new ArrayList<>();
+
+ if (!object.has(ADJSID)) {
+ return adjacencySids;
+ }
+
+ ArrayNode adjacencySidNodes = (ArrayNode) object.path(ADJSID);
+ adjacencySidNodes.forEach(adjacencySidNode -> {
+ int asid = adjacencySidNode.path(AdjacencySid.ASID).asInt();
+
+ ArrayList<Integer> ports = new ArrayList<Integer>();
+ ArrayNode portsNodes = (ArrayNode) adjacencySidNode.path(AdjacencySid.PORTS);
+ portsNodes.forEach(portNode -> {
+ ports.add(portNode.asInt());
+ });
+
+ AdjacencySid adjacencySid = new AdjacencySid(asid, ports);
+ adjacencySids.add(adjacencySid);
+ });
+
+ return adjacencySids;
+ }
+
+ public class AdjacencySid {
+ private static final String ASID = "adjSid";
+ private static final String PORTS = "ports";
+
+ int asid;
+ List<Integer> ports;
+
+ public AdjacencySid(int asid, List<Integer> ports) {
+ this.asid = asid;
+ this.ports = ports;
+ }
+
+ public int getAsid() {
+ return asid;
+ }
+
+ public List<Integer> getPorts() {
+ return ports;
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java
index fdae9c9e..95f7e244 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
index 816ca7bc..497f5256 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java b/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java
index 12c8140a..d93ad78f 100644
--- a/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java
+++ b/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java
@@ -92,7 +92,8 @@ public class CounterTestIncrementCommand extends AbstractShellCommand {
} catch (InterruptedException e) {
return;
} catch (ExecutionException | TimeoutException e) {
- e.printStackTrace();
+ print("Error executing command");
+ log.error("Error executing command counter-test-increment", e);
}
}
}
diff --git a/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java b/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java
index de9e9f21..ad3236e5 100644
--- a/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java
+++ b/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java
@@ -179,8 +179,8 @@ public class IntentPerfInstaller {
workers = Executors.newFixedThreadPool(DEFAULT_NUM_WORKERS, groupedThreads("onos/intent-perf", "worker-%d"));
// disable flow backups for testing
- configService.setProperty("org.onosproject.store.flow.impl.DistributedFlowRuleStore",
- "backupEnabled", "false");
+ configService.setProperty("org.onosproject.store.flow.impl.NewDistributedFlowRuleStore",
+ "backupEnabled", "true");
// TODO: replace with shared executor
messageHandlingExecutor = Executors.newSingleThreadExecutor(
diff --git a/framework/src/onos/apps/vtn/app/app.xml b/framework/src/onos/apps/vtn/app/app.xml
new file mode 100644
index 00000000..a0efd7f4
--- /dev/null
+++ b/framework/src/onos/apps/vtn/app/app.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<app name="org.onosproject.vtn" origin="ON.Lab" version="${project.version}"
+ featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+ features="${project.artifactId}">
+ <description>${project.description}</description>
+
+ <artifact>mvn:${project.groupId}/onos-app-vtn-mgr/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-app-vtn-web/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-app-vtn-rsc/${project.version}</artifact>
+</app>
diff --git a/framework/src/onos/apps/vtn/app/features.xml b/framework/src/onos/apps/vtn/app/features.xml
new file mode 100644
index 00000000..c82b41d5
--- /dev/null
+++ b/framework/src/onos/apps/vtn/app/features.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+ <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
+ <feature name="${project.artifactId}" version="${project.version}"
+ description="${project.description}">
+ <feature>onos-api</feature>
+ <feature>onos-drivers</feature>
+ <bundle>mvn:${project.groupId}/onos-app-vtn-mgr/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-app-vtn-web/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-app-vtn-rsc/${project.version}</bundle>
+ </feature>
+</features>
diff --git a/framework/src/onos/apps/vtn/app/pom.xml b/framework/src/onos/apps/vtn/app/pom.xml
new file mode 100644
index 00000000..4ed66172
--- /dev/null
+++ b/framework/src/onos/apps/vtn/app/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ~ Copyright 2014 Open Networking Laboratory ~ ~ Licensed under the Apache
+ License, Version 2.0 (the "License"); ~ you may not use this file except
+ in compliance with the License. ~ You may obtain a copy of the License at
+ ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable
+ law or agreed to in writing, software ~ distributed under the License is
+ distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. ~ See the License for the specific language
+ governing permissions and ~ limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-vtn-onosfw</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS framework applications</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn-rsc</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn-web</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn-mgr</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/apps/vtn/pom.xml b/framework/src/onos/apps/vtn/pom.xml
index fb8fcb13..c2cfe2be 100644
--- a/framework/src/onos/apps/vtn/pom.xml
+++ b/framework/src/onos/apps/vtn/pom.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2015 Open Networking Laboratory
+ ~ Copyright 2014 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<project
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
- xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
+
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
@@ -27,32 +27,14 @@
</parent>
<artifactId>onos-app-vtn</artifactId>
- <packaging>bundle</packaging>
+ <packaging>pom</packaging>
+ <description>ONOS framework applications</description>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <onos.app.name>org.onosproject.vtn</onos.app.name>
- </properties>
- <dependencies>
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>jsr311-api</artifactId>
- <version>1.1.1</version>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-incubator-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-core-serializers</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-app-vtnrsc</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
+ <modules>
+ <module>vtnrsc</module>
+ <module>vtnmgr</module>
+ <module>vtnweb</module>
+ <module>app</module>
+ </modules>
</project>
diff --git a/framework/src/onos/apps/vtn/vtnmgr/pom.xml b/framework/src/onos/apps/vtn/vtnmgr/pom.xml
new file mode 100644
index 00000000..03e66708
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnmgr/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-vtn-mgr</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>jsr311-api</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-incubator-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-serializers</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn-rsc</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java
new file mode 100644
index 00000000..a20f852b
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtn;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.Host;
+
+/**
+ * VTN application that applies configuration and flows to the device.
+ */
+public interface VTNService {
+
+ /**
+ * Creates a vxlan tunnel and creates the ovs when a ovs controller node is detected.
+ *
+ * @param device controller-type device
+ */
+ void onServerDetected(Device device);
+
+ /**
+ * Drops a vxlan tunnel and drops the ovs when a ovs controller node is vanished.
+ *
+ * @param device controller-type device
+ */
+ void onServerVanished(Device device);
+
+ /**
+ * Applies default forwarding flows when a ovs is detected.
+ *
+ * @param device switch-type device
+ */
+ void onOvsDetected(Device device);
+
+ /**
+ * Remove default forwarding flows when a ovs is vanished.
+ *
+ * @param device switch-type device
+ */
+ void onOvsVanished(Device device);
+
+ /**
+ * Applies multicast flows and tunnel flows when a VM is detected.
+ *
+ * @param host a VM
+ */
+ void onHostDetected(Host host);
+
+ /**
+ * Remove multicast flows and tunnel flows when a VM is vanished.
+ *
+ * @param host a VM
+ */
+ void onHostVanished(Host host);
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java
new file mode 100644
index 00000000..090ef0f1
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtn.impl;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.DefaultTunnelDescription;
+import org.onosproject.net.behaviour.IpTunnelEndPoint;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.behaviour.TunnelConfig;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoint;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vtn.VTNService;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Provides implementation of VTNService.
+ */
+@Component(immediate = true)
+@Service
+public class VTNManager implements VTNService {
+ private final Logger log = getLogger(getClass());
+
+ private static final String APP_ID = "org.onosproject.app.vtn";
+ private ScheduledExecutorService backgroundService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TenantNetworkService tenantNetworkService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VirtualPortService virtualPortService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveStore flowObjectiveStore;
+ protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+ private EventuallyConsistentMap<HostId, SegmentationId> binding;
+ private ApplicationId appId;
+ private HostListener hostListener = new InnerHostListener();
+ private DeviceListener deviceListener = new InnerDeviceListener();
+ private static final String IFACEID = "ifaceid";
+ private static final String PORT_HEAD = "vxlan";
+ private static final String DEFAULT_BRIDGE_NAME = "br-int";
+ private static final String CONTROLLER_IP_KEY = "ipaddress";
+ private static final int DEFAULT_MAC_PRIORITY = 0x0000;
+ private static final int MAC_PRIORITY = 0xffff;
+ private static final int DEFAULT_PORT_PRIORITY = 0x0000;
+ private static final int PORT_PRIORITY = 0xffff;
+ private static final String SWITCH_CHANNEL_ID = "channelId";
+ private static final String DRIVER_NAME = "onosfw";
+
+ @Activate
+ public void activate() {
+ KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API);
+ appId = coreService.registerApplication(APP_ID);
+ deviceService.addListener(deviceListener);
+ hostService.addListener(hostListener);
+ backgroundService = newSingleThreadScheduledExecutor(groupedThreads("onos-apps/vtn",
+ "manager-background"));
+ binding = storageService
+ .<HostId, SegmentationId>eventuallyConsistentMapBuilder()
+ .withName("all_tunnel").withSerializer(serializer)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ backgroundService.shutdown();
+ binding.destroy();
+ log.info("Stopped");
+ }
+
+ @Override
+ public void onServerDetected(Device device) {
+ Iterable<Device> devices = deviceService.getAvailableDevices();
+ DriverHandler handler = driverService.createHandler(device.id());
+ BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
+ bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME));
+ String ipAddress = device.annotations().value(CONTROLLER_IP_KEY);
+ IpAddress ip = IpAddress.valueOf(ipAddress);
+ Sets.newHashSet(devices).stream()
+ .filter(d -> Device.Type.CONTROLLER == d.type())
+ .filter(d -> !device.id().equals(d.id())).forEach(d -> {
+ String ipAddress1 = d.annotations()
+ .value(CONTROLLER_IP_KEY);
+ IpAddress ip1 = IpAddress.valueOf(ipAddress1);
+ applyTunnelConfig(ip, ip1, handler);
+ DriverHandler handler1 = driverService
+ .createHandler(d.id());
+ applyTunnelConfig(ip1, ip, handler1);
+
+ });
+ }
+
+ @Override
+ public void onServerVanished(Device device) {
+ Iterable<Device> devices = deviceService.getAvailableDevices();
+ String ipAddress = device.annotations().value(CONTROLLER_IP_KEY);
+ IpAddress dst = IpAddress.valueOf(ipAddress);
+ Sets.newHashSet(devices).stream()
+ .filter(d -> d.type() == Device.Type.CONTROLLER)
+ .filter(d -> !device.id().equals(d.id())).forEach(d -> {
+ String ipAddress1 = d.annotations()
+ .value(CONTROLLER_IP_KEY);
+ DriverHandler handler = driverService.createHandler(d.id());
+ IpAddress src = IpAddress.valueOf(ipAddress1);
+ removeTunnelConfig(src, dst, handler);
+ });
+ }
+
+ private void applyTunnelConfig(IpAddress src, IpAddress dst,
+ DriverHandler handler) {
+ TunnelEndPoint tunnelAsSrc = IpTunnelEndPoint.ipTunnelPoint(src);
+ TunnelEndPoint tunnelAsDst = IpTunnelEndPoint.ipTunnelPoint(dst);
+ TunnelDescription tunnel = new DefaultTunnelDescription(
+ tunnelAsSrc,
+ tunnelAsDst,
+ TunnelDescription.Type.VXLAN,
+ null);
+ TunnelConfig config = handler.behaviour(TunnelConfig.class);
+ config.createTunnel(tunnel);
+ }
+
+ private void removeTunnelConfig(IpAddress src, IpAddress dst,
+ DriverHandler handler) {
+ TunnelEndPoint tunnelAsSrc = IpTunnelEndPoint.ipTunnelPoint(src);
+ TunnelEndPoint tunnelAsDst = IpTunnelEndPoint.ipTunnelPoint(dst);
+ TunnelDescription tunnel = new DefaultTunnelDescription(
+ tunnelAsSrc,
+ tunnelAsDst,
+ TunnelDescription.Type.VXLAN,
+ null);
+ TunnelConfig config = handler.behaviour(TunnelConfig.class);
+ config.removeTunnel(tunnel);
+ }
+
+ @Override
+ public void onOvsDetected(Device device) {
+ programMacDefaultRules(device.id(), appId, Objective.Operation.ADD);
+ programPortDefaultRules(device.id(), appId, Objective.Operation.ADD);
+ }
+
+ @Override
+ public void onOvsVanished(Device device) {
+ programMacDefaultRules(device.id(), appId, Objective.Operation.REMOVE);
+ programPortDefaultRules(device.id(), appId, Objective.Operation.REMOVE);
+ }
+
+ @Override
+ public void onHostDetected(Host host) {
+ String ifaceId = host.annotations().value(IFACEID);
+ DeviceId deviceId = host.location().deviceId();
+ String currentControllerIp = getControllerIpOfSwitch(deviceId);
+ Iterable<Device> devices = deviceService.getAvailableDevices();
+ VirtualPortId portId = VirtualPortId.portId(ifaceId);
+ VirtualPort port = virtualPortService.getPort(portId);
+ TenantNetwork network = tenantNetworkService
+ .getNetwork(port.networkId());
+ String tunnelName = "vxlan-" + currentControllerIp;
+ binding.put(host.id(), network.segmentationId());
+ List<Port> allPorts = deviceService.getPorts(deviceId);
+ PortNumber inPort = host.location().port();
+ List<PortNumber> localVmPorts = getLocalPorts(deviceId, ifaceId);
+ List<PortNumber> localTunnelPorts = new ArrayList<>();
+ Sets.newHashSet(allPorts.iterator()).stream()
+ .filter(p -> !p.number().equals(PortNumber.LOCAL)).forEach(p -> {
+ if (p.annotations().value("portName").startsWith(PORT_HEAD)) {
+ localTunnelPorts.add(p.number());
+ }
+ });
+
+ localVmPorts.forEach(lp -> programLocalBcastRules(deviceId, network.segmentationId(), lp, localVmPorts,
+ localTunnelPorts, appId, Objective.Operation.ADD));
+ programLocalOut(deviceId, network.segmentationId(), inPort, host.mac(),
+ appId, Objective.Operation.ADD);
+ localTunnelPorts
+ .forEach(tp -> programTunnelFloodOut(deviceId,
+ network.segmentationId(),
+ tp, localVmPorts,
+ appId,
+ Objective.Operation.ADD));
+ Sets.newHashSet(devices).stream()
+ .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> {
+ DriverHandler handler = driverService.createHandler(d.id());
+ BridgeConfig bridgeConfig = handler
+ .behaviour(BridgeConfig.class);
+ Collection<BridgeDescription> bridgeDescriptions = bridgeConfig
+ .getBridges();
+
+ Iterator<BridgeDescription> it = bridgeDescriptions
+ .iterator();
+ if (it.hasNext()) {
+ BridgeDescription sw = it.next();
+ Set<PortNumber> ports = bridgeConfig.getPortNumbers();
+ ports.stream()
+ .filter(p -> p.name()
+ .equalsIgnoreCase(tunnelName))
+ .forEach(p -> programTunnelOut(sw.deviceId(),
+ network.segmentationId(), p,
+ host.mac(), appId,
+ Objective.Operation.ADD));
+ }
+ });
+ programLocalIn(deviceId, network.segmentationId(), inPort, host.mac(),
+ appId, Objective.Operation.ADD);
+ localTunnelPorts
+ .forEach(tp -> programTunnelIn(deviceId,
+ network.segmentationId(),
+ tp, inPort, host.mac(),
+ appId, Objective.Operation.ADD));
+
+ }
+
+ @Override
+ public void onHostVanished(Host host) {
+ String ifaceId = host.annotations().value(IFACEID);
+ SegmentationId segId = binding.remove(host.id());
+ DeviceId deviceId = host.location().deviceId();
+ String currentControllerIp = getControllerIpOfSwitch(deviceId);
+ Iterable<Device> devices = deviceService.getAvailableDevices();
+
+ String tunnelName = "vxlan-" + currentControllerIp;
+ List<Port> allPorts = deviceService.getPorts(deviceId);
+ PortNumber inPort = host.location().port();
+
+ List<PortNumber> localTunnelPorts = new ArrayList<>();
+ Sets.newHashSet(allPorts.iterator()).stream()
+ .filter(p -> !p.number().equals(PortNumber.LOCAL)).forEach(p -> {
+ if (p.annotations().value("portName").startsWith(PORT_HEAD)) {
+ localTunnelPorts.add(p.number());
+ }
+ });
+
+ List<PortNumber> localVmPorts = getLocalPorts(deviceId, ifaceId);
+ localVmPorts.add(inPort);
+ localVmPorts.forEach(lp -> programLocalBcastRules(deviceId, segId, lp, localVmPorts,
+ localTunnelPorts, appId, Objective.Operation.REMOVE));
+ programLocalOut(deviceId, segId, inPort, host.mac(),
+ appId, Objective.Operation.REMOVE);
+ localTunnelPorts
+ .forEach(tp -> programTunnelFloodOut(deviceId,
+ segId,
+ tp, localVmPorts,
+ appId,
+ Objective.Operation.REMOVE));
+ Sets.newHashSet(devices).stream()
+ .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> {
+ DriverHandler handler = driverService.createHandler(d.id());
+ BridgeConfig bridgeConfig = handler
+ .behaviour(BridgeConfig.class);
+ Collection<BridgeDescription> bridgeDescriptions = bridgeConfig
+ .getBridges();
+
+ Iterator<BridgeDescription> it = bridgeDescriptions
+ .iterator();
+ if (it.hasNext()) {
+ BridgeDescription sw = it.next();
+ Set<PortNumber> ports = bridgeConfig.getPortNumbers();
+ ports.stream()
+ .filter(p -> p.name()
+ .equalsIgnoreCase(tunnelName))
+ .forEach(p -> programTunnelOut(sw.deviceId(),
+ segId, p,
+ host.mac(), appId,
+ Objective.Operation.REMOVE));
+ }
+ });
+ programLocalIn(deviceId, segId, inPort, host.mac(),
+ appId, Objective.Operation.REMOVE);
+ localTunnelPorts
+ .forEach(tp -> programTunnelIn(deviceId,
+ segId,
+ tp, inPort, host.mac(),
+ appId, Objective.Operation.REMOVE));
+ }
+
+ private class InnerDeviceListener implements DeviceListener {
+
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ if (Device.Type.CONTROLLER == device.type()
+ && DeviceEvent.Type.DEVICE_ADDED == event.type()) {
+ backgroundService.execute(() -> onServerDetected(device));
+ } else if (Device.Type.CONTROLLER == device.type()
+ && DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED == event
+ .type()) {
+ backgroundService.execute(() -> onServerVanished(device));
+ } else if (Device.Type.SWITCH == device.type()
+ && DeviceEvent.Type.DEVICE_ADDED == event.type()) {
+ backgroundService.execute(() -> onOvsDetected(device));
+ } else if (Device.Type.SWITCH == device.type()
+ && DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED == event
+ .type()) {
+ backgroundService.execute(() -> onOvsVanished(device));
+ } else {
+ log.info("Do nothing for this device type");
+ }
+ }
+
+ }
+
+ private class InnerHostListener implements HostListener {
+
+ @Override
+ public void event(HostEvent event) {
+ Host host = event.subject();
+ if (HostEvent.Type.HOST_ADDED == event.type()) {
+ backgroundService.execute(() -> onHostDetected(host));
+ } else if (HostEvent.Type.HOST_REMOVED == event.type()) {
+ backgroundService.execute(() -> onHostVanished(host));
+ } else if (HostEvent.Type.HOST_UPDATED == event.type()) {
+ backgroundService.execute(() -> {
+ onHostVanished(host);
+ onHostDetected(host);
+ });
+ }
+ }
+
+ }
+
+ // Used to forward the flows to the local VM.
+ private void programLocalOut(DeviceId dpid, SegmentationId segmentationId,
+ PortNumber outPort, MacAddress sourceMac,
+ ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchTunnelId(Long.parseLong(segmentationId.toString()))
+ .matchEthDst(sourceMac).build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(outPort).build();
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).withFlag(Flag.SPECIFIC)
+ .withPriority(MAC_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(dpid, objective.add());
+ } else {
+ flowServiceForward(dpid, objective.remove());
+ }
+
+ }
+
+ // Used to forward the flows into the VXLAN tunnel.
+ private void programTunnelOut(DeviceId dpid, SegmentationId segmentationId,
+ PortNumber tunnelOutPort, MacAddress dstMac,
+ ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthDst(dstMac).add(Criteria.matchTunnelId(Long
+ .parseLong(segmentationId.toString())))
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+
+ .setOutput(tunnelOutPort).build();
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).withFlag(Flag.SPECIFIC)
+ .withPriority(MAC_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(dpid, objective.add());
+ } else {
+ flowServiceForward(dpid, objective.remove());
+ }
+
+ }
+
+ // Used to forward multicast flows to remote VMs of the same tenant via
+ // VXLAN tunnel.
+ private void programTunnelFloodOut(DeviceId deviceId,
+ SegmentationId segmentationId,
+ PortNumber ofPortOut,
+ List<PortNumber> localVmPorts,
+ ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(ofPortOut)
+
+ .add(Criteria.matchTunnelId(Long.parseLong(segmentationId
+ .toString()))).matchEthDst(MacAddress.BROADCAST)
+ .build();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+
+ for (PortNumber outPort : localVmPorts) {
+ treatment.setOutput(outPort);
+ }
+
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment.build())
+ .withSelector(selector).fromApp(appId).makePermanent()
+ .withFlag(Flag.SPECIFIC).withPriority(MAC_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(deviceId, objective.add());
+ } else {
+ flowServiceForward(deviceId, objective.remove());
+ }
+ }
+
+ // Applies default flows to mac table.
+ private void programMacDefaultRules(DeviceId dpid, ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().drop()
+ .build();
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
+ .withPriority(DEFAULT_MAC_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(dpid, objective.add());
+ } else {
+ flowServiceForward(dpid, objective.remove());
+ }
+ }
+
+ // Used to forward the flows to the local VMs with the same tenant.
+ private void programLocalBcastRules(DeviceId deviceId,
+ SegmentationId segmentationId,
+ PortNumber inPort,
+ List<PortNumber> localVmPorts,
+ List<PortNumber> localTunnelPorts,
+ ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(inPort).matchEthDst(MacAddress.BROADCAST)
+ .add(Criteria.matchTunnelId(Long
+ .parseLong(segmentationId.toString())))
+ .build();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ for (PortNumber outPort : localVmPorts) {
+ if (inPort != outPort) {
+ treatment.setOutput(outPort);
+ }
+ }
+ for (PortNumber outport : localTunnelPorts) {
+ treatment.setOutput(outport);
+ }
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment.build())
+ .withSelector(selector).fromApp(appId).makePermanent()
+ .withFlag(Flag.SPECIFIC).withPriority(MAC_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(deviceId, objective.add());
+ } else {
+ flowServiceForward(deviceId, objective.remove());
+ }
+ }
+
+ // Used to apply local entry flow.
+ private void programLocalIn(DeviceId dpid, SegmentationId segmentationId,
+ PortNumber inPort, MacAddress srcMac,
+ ApplicationId appid, Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(inPort).matchEthSrc(srcMac).build();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ treatment.add(Instructions.modTunnelId(Long.parseLong(segmentationId
+ .toString())));
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment.build())
+ .withSelector(selector).fromApp(appId).makePermanent()
+ .withFlag(Flag.SPECIFIC).withPriority(PORT_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(dpid, objective.add());
+ } else {
+ flowServiceForward(dpid, objective.remove());
+ }
+ }
+
+ // Used to forward the flows from the egress tunnel to the VM.
+ private void programTunnelIn(DeviceId dpid, SegmentationId segmentationId,
+ PortNumber tunnelInPort, PortNumber outPort,
+ MacAddress sourceMac, ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(tunnelInPort).add(Criteria.matchTunnelId(Long
+ .parseLong(segmentationId.toString())))
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
+ .withPriority(PORT_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(dpid, objective.add());
+ } else {
+ flowServiceForward(dpid, objective.remove());
+ }
+ }
+
+ // Applies the default flows to port table.
+ private void programPortDefaultRules(DeviceId dpid, ApplicationId appid,
+ Objective.Operation type) {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
+ .withPriority(DEFAULT_PORT_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowServiceForward(dpid, objective.add());
+ } else {
+ flowServiceForward(dpid, objective.remove());
+ }
+ }
+
+ // Used to get channelId from the device annotations.
+ private String getControllerIpOfSwitch(DeviceId deviceId) {
+ Device device = deviceService.getDevice(deviceId);
+ String url = device.annotations().value(SWITCH_CHANNEL_ID);
+ return url.substring(0, url.lastIndexOf(":"));
+ }
+
+ private Iterable<String> getIfaceIds(String ifaceId) {
+ VirtualPortId portId = VirtualPortId.portId(ifaceId);
+ VirtualPort port = virtualPortService.getPort(portId);
+ if (port == null) {
+ return Collections.emptyList();
+ }
+
+ TenantNetwork network = tenantNetworkService
+ .getNetwork(port.networkId());
+ if (network == null) {
+ return Collections.emptyList();
+ }
+
+ Collection<VirtualPort> ports = virtualPortService
+ .getPorts(network.id());
+ return ports.stream().map(p -> p.portId().portId())
+ .collect(Collectors.toSet());
+ }
+
+ private List<PortNumber> getLocalPorts(DeviceId deviceId, String ifaceId) {
+ DriverHandler handler = driverService
+ .createHandler(getController(deviceId));
+ BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
+ Iterable<String> ifaceIds = getIfaceIds(ifaceId);
+ return bridgeConfig.getLocalPorts(ifaceIds);
+ }
+
+ private DeviceId getController(DeviceId deviceId) {
+ Iterable<Device> devices = deviceService.getAvailableDevices();
+ for (Device device : devices) {
+ if (device.type() == Device.Type.CONTROLLER && device.id()
+ .toString().contains(getControllerIpOfSwitch(deviceId))) {
+ return device.id();
+ }
+ }
+ log.info("Can not find controller for device : {}", deviceId);
+ return null;
+ }
+
+ //Used to apply flowRule
+ private void flowServiceForward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+ Driver driver = driverService.getDriver(DRIVER_NAME);
+ Pipeliner pipeLiner = driver.createBehaviour(new DefaultDriverData(driver, deviceId), Pipeliner.class);
+ if (pipeLiner != null) {
+ final PipelinerContext context = new InnerPipelineContext();
+ pipeLiner.init(deviceId, context);
+ pipeLiner.forward(forwardingObjective);
+ }
+ }
+
+ // Processing context for initializing pipeline driver behaviours.
+ private class InnerPipelineContext implements PipelinerContext {
+ @Override
+ public ServiceDirectory directory() {
+ return serviceDirectory;
+ }
+
+ @Override
+ public FlowObjectiveStore store() {
+ return flowObjectiveStore;
+ }
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java
new file mode 100644
index 00000000..f18dbf8a
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN application that applies configuration and flows to the device.
+ */
+package org.onosproject.vtn.impl;
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java
new file mode 100644
index 00000000..371466c3
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN application that applies configuration and flows to the device.
+ */
+package org.onosproject.vtn;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/pom.xml b/framework/src/onos/apps/vtn/vtnrsc/pom.xml
new file mode 100644
index 00000000..8696295c
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+
+ <artifactId>onos-app-vtn-rsc</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-serializers</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java
new file mode 100644
index 00000000..3d6ba8e8
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * The continuous IP address range between the start address and the end address for the allocation pools.
+ */
+public interface AllocationPool {
+
+ /**
+ * The start address for the allocation pool.
+ *
+ * @return startIp
+ */
+ IpAddress startIp();
+
+ /**
+ * The end address for the allocation pool.
+ *
+ * @return endIp
+ */
+ IpAddress endIp();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java
new file mode 100644
index 00000000..4e1028d8
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+
+/**
+ * Immutable representation of a allowed address pair.
+ */
+public final class AllowedAddressPair {
+ private final IpAddress ip;
+ private final MacAddress mac;
+ // Public construction is prohibited
+ private AllowedAddressPair(IpAddress ip, MacAddress mac) {
+ checkNotNull(ip, "IpAddress cannot be null");
+ checkNotNull(mac, "MacAddress cannot be null");
+ this.ip = ip;
+ this.mac = mac;
+ }
+ /**
+ * Returns the AllowedAddressPair ip address.
+ *
+ * @return ip address
+ */
+ public IpAddress ip() {
+ return ip;
+ }
+
+ /**
+ * Returns the AllowedAddressPair MAC address.
+ *
+ * @return MAC address
+ */
+ public MacAddress mac() {
+ return mac;
+ }
+
+
+ /**
+ * Creates a allowedAddressPair using the supplied ipAddress &amp;
+ * macAddress.
+ *
+ * @param ip IP address
+ * @param mac MAC address
+ * @return AllowedAddressPair
+ */
+ public static AllowedAddressPair allowedAddressPair(IpAddress ip,
+ MacAddress mac) {
+ return new AllowedAddressPair(ip, mac);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ip, mac);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof AllowedAddressPair) {
+ final AllowedAddressPair that = (AllowedAddressPair) obj;
+ return Objects.equals(this.ip, that.ip)
+ && Objects.equals(this.mac, that.mac);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("ip", ip).add("mac", mac).toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java
new file mode 100644
index 00000000..c715d08a
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+public final class BindingHostId {
+ private final String bindingHostId;
+
+ // Public construction is prohibited
+ private BindingHostId(String bindingHostId) {
+ checkNotNull(bindingHostId, "BindingHosttId cannot be null");
+ this.bindingHostId = bindingHostId;
+ }
+
+ /**
+ * Creates a BindingHostId identifier.
+ *
+ * @param bindingHostId the bindingHostId identifier
+ * @return the bindingHostId identifier
+ */
+ public static BindingHostId bindingHostId(String bindingHostId) {
+ return new BindingHostId(bindingHostId);
+ }
+
+ /**
+ * Returns the bindingHostId identifier.
+ *
+ * @return the bindingHostId identifier
+ */
+ public String bindingHostId() {
+ return bindingHostId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(bindingHostId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof BindingHostId) {
+ final BindingHostId that = (BindingHostId) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.bindingHostId, that.bindingHostId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return bindingHostId;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java
new file mode 100644
index 00000000..8a480194
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * The continuous IP address range between the start address and the end address
+ * for the allocation pools.
+ */
+public final class DefaultAllocationPool implements AllocationPool {
+
+ private final IpAddress startIp;
+ private final IpAddress endIp;
+
+ /**
+ * Creates an AllocationPool by using the start IP address and the end IP
+ * address.
+ *
+ * @param startIp the start IP address of the allocation pool
+ * @param endIp the end IP address of the allocation pool
+ */
+ public DefaultAllocationPool(IpAddress startIp, IpAddress endIp) {
+ checkNotNull(startIp, "StartIp cannot be null");
+ checkNotNull(endIp, "EndIp cannot be null");
+ this.startIp = startIp;
+ this.endIp = endIp;
+ }
+
+ @Override
+ public IpAddress startIp() {
+ return startIp;
+ }
+
+ @Override
+ public IpAddress endIp() {
+ return endIp;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(startIp, endIp);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultAllocationPool) {
+ final DefaultAllocationPool other = (DefaultAllocationPool) obj;
+ return Objects.equals(this.startIp, other.startIp)
+ && Objects.equals(this.endIp, other.endIp);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("startIp", startIp).add("endIp", endIp)
+ .toString();
+ }
+}
+
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java
new file mode 100644
index 00000000..8679100d
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Host route dictionaries for the subnet.
+ */
+public final class DefaultHostRoute implements HostRoute {
+
+ private final IpAddress nexthop;
+ private final IpPrefix destination;
+
+ /**
+ *
+ * Creates a DefaultHostRoute by using the next hop and the destination.
+ *
+ * @param nexthop of the DefaultHostRoute
+ * @param destination of the DefaultHostRoute
+ */
+ public DefaultHostRoute(IpAddress nexthop, IpPrefix destination) {
+ this.nexthop = nexthop;
+ this.destination = destination;
+ }
+
+ @Override
+ public IpAddress nexthop() {
+ return nexthop;
+ }
+
+ @Override
+ public IpPrefix destination() {
+ return destination;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("nexthop", nexthop)
+ .add("destination", destination).toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(nexthop, destination);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultHostRoute) {
+ final DefaultHostRoute other = (DefaultHostRoute) obj;
+ return Objects.equals(this.nexthop, other.nexthop)
+ && Objects.equals(this.destination, other.destination);
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java
new file mode 100644
index 00000000..6049b558
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+import java.util.Set;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Default implementation of Subnet interface .
+ */
+public final class DefaultSubnet implements Subnet {
+ private final SubnetId id;
+ private final String subnetName;
+ private final TenantNetworkId networkId;
+ private final TenantId tenantId;
+ private final Version ipVersion;
+ private final IpPrefix cidr;
+ private final IpAddress gatewayIp;
+ private final boolean dhcpEnabled;
+ private final boolean shared;
+ private final Mode ipV6AddressMode;
+ private final Mode ipV6RaMode;
+ private final Set<HostRoute> hostRoutes;
+ private final Set<AllocationPool> allocationPools;
+
+ /**
+ * Creates a subnet object.
+ *
+ * @param id subnet identifier
+ * @param subnetName the name of subnet
+ * @param networkId network identifier
+ * @param tenantId tenant identifier
+ * @param ipVersion Version of ipv4 or ipv6
+ * @param cidr the cidr
+ * @param gatewayIp gateway ip
+ * @param dhcpEnabled dhcp enabled or not
+ * @param shared indicates whether this network is shared across all
+ * tenants, By default, only administrative user can change this
+ * value
+ * @param hostRoutes a collection of host routes
+ * @param ipV6AddressMode ipV6AddressMode
+ * @param ipV6RaMode ipV6RaMode
+ * @param allocationPoolsIt a collection of allocationPools
+ */
+ public DefaultSubnet(SubnetId id, String subnetName,
+ TenantNetworkId networkId, TenantId tenantId,
+ Version ipVersion, IpPrefix cidr, IpAddress gatewayIp,
+ boolean dhcpEnabled, boolean shared,
+ Set<HostRoute> hostRoutes, Mode ipV6AddressMode,
+ Mode ipV6RaMode,
+ Set<AllocationPool> allocationPoolsIt) {
+ this.id = id;
+ this.subnetName = subnetName;
+ this.networkId = networkId;
+ this.tenantId = tenantId;
+ this.ipVersion = ipVersion;
+ this.cidr = cidr;
+ this.gatewayIp = gatewayIp;
+ this.dhcpEnabled = dhcpEnabled;
+ this.shared = shared;
+ this.ipV6AddressMode = ipV6AddressMode;
+ this.ipV6RaMode = ipV6RaMode;
+ this.hostRoutes = hostRoutes;
+ this.allocationPools = allocationPoolsIt;
+ }
+
+ @Override
+ public SubnetId id() {
+ return id;
+ }
+
+ @Override
+ public String subnetName() {
+ return subnetName;
+ }
+
+ @Override
+ public TenantNetworkId networkId() {
+ return networkId;
+ }
+
+ @Override
+ public TenantId tenantId() {
+ return tenantId;
+ }
+
+ @Override
+ public Version ipVersion() {
+ return ipVersion;
+ }
+
+ @Override
+ public IpPrefix cidr() {
+ return cidr;
+ }
+
+ @Override
+ public IpAddress gatewayIp() {
+ return gatewayIp;
+ }
+
+ @Override
+ public boolean dhcpEnabled() {
+ return dhcpEnabled;
+ }
+
+ @Override
+ public boolean shared() {
+ return shared;
+ }
+
+ @Override
+ public Iterable<HostRoute> hostRoutes() {
+ return hostRoutes;
+ }
+
+ @Override
+ public Mode ipV6AddressMode() {
+ return ipV6AddressMode;
+ }
+
+ @Override
+ public Mode ipV6RaMode() {
+ return ipV6RaMode;
+ }
+
+ @Override
+ public Iterable<AllocationPool> allocationPools() {
+ return allocationPools;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, subnetName, ipVersion, cidr, gatewayIp,
+ dhcpEnabled, shared, tenantId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultSubnet) {
+ final DefaultSubnet that = (DefaultSubnet) obj;
+ return Objects.equals(this.id, that.id)
+ && Objects.equals(this.subnetName, that.subnetName)
+ && Objects.equals(this.ipVersion, that.ipVersion)
+ && Objects.equals(this.cidr, that.cidr)
+ && Objects.equals(this.shared, that.shared)
+ && Objects.equals(this.gatewayIp, that.gatewayIp)
+ && Objects.equals(this.dhcpEnabled, that.dhcpEnabled);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("id", id).add("subnetName", subnetName)
+ .add("ipVersion", ipVersion).add("cidr", cidr)
+ .add("shared", shared).add("gatewayIp", gatewayIp)
+ .add("dhcpEnabled", dhcpEnabled).toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java
new file mode 100644
index 00000000..8c941ea2
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+
+/**
+ * Default implementation of TenantNetwork interface.
+ */
+public final class DefaultTenantNetwork implements TenantNetwork {
+ private final TenantNetworkId id;
+ private final String name;
+ private final boolean adminStateUp;
+ private final State state;
+ private final boolean shared;
+ private final Type type;
+ private final TenantId tenantId;
+ private final boolean routerExternal;
+ private final PhysicalNetwork physicalNetwork;
+ private final SegmentationId segmentationId;
+
+ /**
+ * Creates a neutronNetwork element attributed to the specified provider.
+ *
+ * @param id network identifier
+ * @param name the network name
+ * @param adminStateUp administrative state of the network
+ * @param state the network state
+ * @param shared indicates whether this network is shared across all
+ * tenants, By default, only administrative user can change this
+ * value
+ * @param tenantId tenant identifier
+ * @param routerExternal network routerExternal
+ * @param type the network type
+ * @param physicalNetwork physicalNetwork identifier
+ * @param segmentationId segmentation identifier
+ */
+ public DefaultTenantNetwork(TenantNetworkId id, String name,
+ boolean adminStateUp, State state,
+ boolean shared, TenantId tenantId,
+ boolean routerExternal, Type type,
+ PhysicalNetwork physicalNetwork,
+ SegmentationId segmentationId) {
+ this.id = id;
+ this.name = name;
+ this.adminStateUp = adminStateUp;
+ this.state = state;
+ this.shared = shared;
+ this.type = type;
+ this.tenantId = tenantId;
+ this.routerExternal = routerExternal;
+ this.physicalNetwork = physicalNetwork;
+ this.segmentationId = segmentationId;
+ }
+
+ @Override
+ public TenantNetworkId id() {
+ return id;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public boolean adminStateUp() {
+ return adminStateUp;
+ }
+
+ @Override
+ public State state() {
+ return state;
+ }
+
+ @Override
+ public boolean shared() {
+ return shared;
+ }
+
+ @Override
+ public TenantId tenantId() {
+ return tenantId;
+ }
+
+ @Override
+ public boolean routerExternal() {
+ return routerExternal;
+ }
+
+ @Override
+ public Type type() {
+ return type;
+ }
+
+ @Override
+ public PhysicalNetwork physicalNetwork() {
+ return physicalNetwork;
+ }
+
+ @Override
+ public SegmentationId segmentationId() {
+ return segmentationId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name, adminStateUp, state, shared, tenantId,
+ routerExternal, type, physicalNetwork,
+ segmentationId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultTenantNetwork) {
+ final DefaultTenantNetwork that = (DefaultTenantNetwork) obj;
+ return Objects.equals(this.id, that.id)
+ && Objects.equals(this.name, that.name)
+ && Objects.equals(this.adminStateUp, that.adminStateUp)
+ && Objects.equals(this.state, that.state)
+ && Objects.equals(this.shared, that.shared)
+ && Objects.equals(this.tenantId, that.tenantId)
+ && Objects.equals(this.routerExternal, that.routerExternal)
+ && Objects.equals(this.type, that.type)
+ && Objects.equals(this.physicalNetwork,
+ that.physicalNetwork)
+ && Objects.equals(this.segmentationId, that.segmentationId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("id", id).add("name", name)
+ .add("adminStateUp", adminStateUp).add("state", state)
+ .add("shared", shared).add("tenantId", tenantId)
+ .add("routeExternal", routerExternal).add("type", type)
+ .add("physicalNetwork", physicalNetwork)
+ .add("segmentationId", segmentationId).toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java
new file mode 100644
index 00000000..9ee85da7
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Default implementation of VirtualPort interface .
+ */
+public final class DefaultVirtualPort implements VirtualPort {
+ private final VirtualPortId id;
+ private final TenantNetworkId networkId;
+ private final Boolean adminStateUp;
+ private final String name;
+ private final State state;
+ private final MacAddress macAddress;
+ private final TenantId tenantId;
+ private final String deviceOwner;
+ private final DeviceId deviceId;
+ private final Set<FixedIp> fixedIps;
+ private final BindingHostId bindingHostId;
+ private final String bindingVnicType;
+ private final String bindingVifType;
+ private final String bindingVifDetails;
+ private final Set<AllowedAddressPair> allowedAddressPairs;
+ private final Set<SecurityGroup> securityGroups;
+
+ /**
+ * Creates a VirtualPort object.
+ *
+ * @param id the virtual port identifier
+ * @param networkId the network identifier
+ * @param adminStateUp adminStateup true or false
+ * @param strMap the map of properties of virtual port
+ * @param state virtual port state
+ * @param macAddress the MAC address
+ * @param tenantId the tenant identifier
+ * @param deviceId the device identifier
+ * @param fixedIps set of fixed IP
+ * @param bindingHostId the binding host identifier
+ * @param allowedAddressPairs the collection of allowdeAddressPairs
+ * @param securityGroups the collection of securityGroups
+ */
+ public DefaultVirtualPort(VirtualPortId id,
+ TenantNetworkId networkId,
+ Boolean adminStateUp,
+ Map<String, String> strMap,
+ State state,
+ MacAddress macAddress,
+ TenantId tenantId,
+ DeviceId deviceId,
+ Set<FixedIp> fixedIps,
+ BindingHostId bindingHostId,
+ Set<AllowedAddressPair> allowedAddressPairs,
+ Set<SecurityGroup> securityGroups) {
+ this.id = id;
+ this.networkId = networkId;
+ this.adminStateUp = adminStateUp;
+ this.name = strMap.get("name");
+ this.state = state;
+ this.macAddress = macAddress;
+ this.tenantId = tenantId;
+ this.deviceOwner = strMap.get("deviceOwner");
+ this.deviceId = deviceId;
+ this.fixedIps = fixedIps;
+ this.bindingHostId = bindingHostId;
+ this.bindingVnicType = strMap.get("bindingVnicType");
+ this.bindingVifType = strMap.get("bindingVifType");
+ this.bindingVifDetails = strMap.get("bindingVifDetails");
+ this.allowedAddressPairs = allowedAddressPairs;
+ this.securityGroups = securityGroups;
+ }
+
+ @Override
+ public VirtualPortId portId() {
+ return id;
+ }
+
+ @Override
+ public TenantNetworkId networkId() {
+ return networkId;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public boolean adminStateUp() {
+ return adminStateUp;
+ }
+
+ @Override
+ public State state() {
+ return state;
+ }
+
+ @Override
+ public MacAddress macAddress() {
+ return macAddress;
+ }
+
+ @Override
+ public TenantId tenantId() {
+ return tenantId;
+ }
+
+ @Override
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ @Override
+ public String deviceOwner() {
+ return deviceOwner;
+ }
+
+ @Override
+ public Collection<AllowedAddressPair> allowedAddressPairs() {
+ return allowedAddressPairs;
+ }
+
+ @Override
+ public Set<FixedIp> fixedIps() {
+ return fixedIps;
+ }
+
+ @Override
+ public BindingHostId bindingHostId() {
+ return bindingHostId;
+ }
+
+ @Override
+ public String bindingVnicType() {
+ return bindingVifType;
+ }
+
+ @Override
+ public String bindingVifType() {
+ return bindingVifType;
+ }
+
+ @Override
+ public String bindingVifDetails() {
+ return bindingVifDetails;
+ }
+
+ @Override
+ public Collection<SecurityGroup> securityGroups() {
+ return securityGroups;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, networkId, adminStateUp, name, state,
+ macAddress, tenantId, deviceId, deviceOwner,
+ allowedAddressPairs, fixedIps, bindingHostId,
+ bindingVnicType, bindingVifType, bindingVifDetails,
+ securityGroups);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultVirtualPort) {
+ final DefaultVirtualPort that = (DefaultVirtualPort) obj;
+ return Objects.equals(this.id, that.id)
+ && Objects.equals(this.networkId, that.networkId)
+ && Objects.equals(this.adminStateUp, that.adminStateUp)
+ && Objects.equals(this.state, that.state)
+ && Objects.equals(this.name, that.name)
+ && Objects.equals(this.tenantId, that.tenantId)
+ && Objects.equals(this.macAddress, that.macAddress)
+ && Objects.equals(this.deviceId, that.deviceId)
+ && Objects.equals(this.deviceOwner, that.deviceOwner)
+ && Objects.equals(this.allowedAddressPairs,
+ that.allowedAddressPairs)
+ && Objects.equals(this.fixedIps, that.fixedIps)
+ && Objects.equals(this.bindingHostId, that.bindingHostId)
+ && Objects.equals(this.bindingVifDetails,
+ that.bindingVifDetails)
+ && Objects.equals(this.bindingVifType, that.bindingVifType)
+ && Objects.equals(this.bindingVnicType,
+ that.bindingVnicType)
+ && Objects.equals(this.securityGroups, that.securityGroups);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("id", id).add("network_id", networkId)
+ .add("adminStateUp", adminStateUp).add("state", state)
+ .add("name", name).add("state", state)
+ .add("macAddress", macAddress).add("tenantId", tenantId)
+ .add("deviced", deviceId).add("deviceOwner", deviceOwner)
+ .add("allowedAddressPairs", allowedAddressPairs)
+ .add("fixedIp", fixedIps).add("bindingHostId", bindingHostId)
+ .add("bindingVnicType", bindingVnicType)
+ .add("bindingVifDetails", bindingVifDetails)
+ .add("bindingVifType", bindingVifType)
+ .add("securityGroups", securityGroups).toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java
new file mode 100644
index 00000000..c6569a7b
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * Immutable representation of a IP address for the port, Include the IP address
+ * and subnet identity.
+ */
+public final class FixedIp {
+ private final SubnetId subnetId;
+ private final IpAddress ip;
+ // Public construction is prohibited
+ private FixedIp(SubnetId subnetId, IpAddress ip) {
+ checkNotNull(subnetId, "SubnetId cannot be null");
+ checkNotNull(ip, "IpAddress cannot be null");
+ this.subnetId = subnetId;
+ this.ip = ip;
+ }
+
+ /**
+ * Returns the FixedIp subnet identifier.
+ *
+ * @return subnet identifier
+ */
+ public SubnetId subnetId() {
+ return subnetId;
+ }
+
+ /**
+ * Returns the FixedIp IP address.
+ *
+ * @return IP address
+ */
+ public IpAddress ip() {
+ return ip;
+ }
+
+ /**
+ * Creates a fixed ip using the supplied fixedIp.
+ *
+ * @param subnetId subnet identity
+ * @param ip IP address
+ * @return FixedIp
+ */
+ public static FixedIp fixedIp(SubnetId subnetId, IpAddress ip) {
+ return new FixedIp(subnetId, ip);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(subnetId, ip);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof FixedIp) {
+ final FixedIp that = (FixedIp) obj;
+ return Objects.equals(this.subnetId, that.subnetId)
+ && Objects.equals(this.ip, that.ip);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("subnetId", subnetId).add("ip", ip)
+ .toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java
new file mode 100644
index 00000000..b18cb950
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Host route dictionaries for the subnet.
+ */
+public interface HostRoute {
+
+ /**
+ * Returns the next hop address.
+ *
+ * @return next hop address
+ */
+ IpAddress nexthop();
+
+ /**
+ * Returns the destination address.
+ *
+ * @return destination address
+ */
+ IpPrefix destination();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java
new file mode 100644
index 00000000..e96e666a
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a physical network identity.
+ */
+public final class PhysicalNetwork {
+
+ private final String physicalNetwork;
+
+ // Public construction is prohibited
+ private PhysicalNetwork(String physicalNetwork) {
+ checkNotNull(physicalNetwork, "PhysicalNetwork cannot be null");
+ this.physicalNetwork = physicalNetwork;
+ }
+
+ /**
+ * Creates a PhysicalNetwork object.
+ *
+ * @param physicalNetwork physical network
+ * @return physical network
+ */
+ public static PhysicalNetwork physicalNetwork(String physicalNetwork) {
+ return new PhysicalNetwork(physicalNetwork);
+ }
+
+ /**
+ * Returns a physicalNetwork.
+ *
+ * @return physical network
+ */
+ public String physicalNetwork() {
+ return physicalNetwork;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(physicalNetwork);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof PhysicalNetwork) {
+ final PhysicalNetwork that = (PhysicalNetwork) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.physicalNetwork,
+ that.physicalNetwork);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return physicalNetwork;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java
new file mode 100644
index 00000000..9ec1dc63
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a security group.
+ */
+public final class SecurityGroup {
+ private final String securityGroup;
+
+ /**
+ * Returns the securityGroup.
+ *
+ * @return securityGroup
+ */
+ public String securityGroup() {
+ return securityGroup;
+ }
+ // Public construction is prohibited
+ private SecurityGroup(String securityGroup) {
+ checkNotNull(securityGroup, "SecurityGroup cannot be null");
+ this.securityGroup = securityGroup;
+ }
+
+ /**
+ * Creates a securityGroup using the supplied securityGroup.
+ *
+ * @param securityGroup security group
+ * @return securityGroup
+ */
+ public static SecurityGroup securityGroup(String securityGroup) {
+ return new SecurityGroup(securityGroup);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(securityGroup);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof SecurityGroup) {
+ final SecurityGroup that = (SecurityGroup) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.securityGroup, that.securityGroup);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("securityGroup", securityGroup)
+ .toString();
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java
new file mode 100644
index 00000000..a076265f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a Segmentation identifier.
+ */
+public final class SegmentationId {
+
+ private final String segmentationId;
+
+ // Public construction is prohibited
+ private SegmentationId(String segmentationId) {
+ checkNotNull(segmentationId, "SegmentationId cannot be null");
+ this.segmentationId = segmentationId;
+ }
+
+ /**
+ * Creates a SegmentationId object.
+ *
+ * @param segmentationId segmentation identifier
+ * @return SegmentationId
+ */
+ public static SegmentationId segmentationId(String segmentationId) {
+ return new SegmentationId(segmentationId);
+ }
+
+ /**
+ * Returns the segmentation identifier.
+ *
+ * @return segmentationId
+ */
+ public String segmentationId() {
+ return segmentationId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(segmentationId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof SegmentationId) {
+ final SegmentationId that = (SegmentationId) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.segmentationId, that.segmentationId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return segmentationId;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java
new file mode 100644
index 00000000..f563a78f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Representation of a subnet.
+ */
+public interface Subnet {
+
+ /**
+ * Coarse classification of the type of the ipV6Mode.
+ */
+ enum Mode {
+ DHCPV6_STATEFUL, DHCPV6_STATELESS, SLAAC
+ }
+
+ /**
+ * Returns the subnet identifier.
+ *
+ * @return identifier
+ */
+ SubnetId id();
+
+ /**
+ * Returns the name of the subnet.
+ *
+ * @return subnetName
+ */
+ String subnetName();
+
+ /**
+ * Returns the network identifier.
+ *
+ * @return the network identifier
+ */
+ TenantNetworkId networkId();
+
+ /**
+ * Returns tenant identifier.
+ *
+ * @return the tenant identifier
+ */
+ TenantId tenantId();
+
+ /**
+ * Returns the IP version, which is 4 or 6.
+ *
+ * @return ipVersion
+ */
+ Version ipVersion();
+
+ /**
+ * Returns the cidr.
+ *
+ * @return cidr
+ */
+ IpPrefix cidr();
+
+ /**
+ * Returns the gateway IP address.
+ *
+ * @return gatewayIp
+ */
+ IpAddress gatewayIp();
+
+ /**
+ * Returns true if DHCP is enabled and return false if DHCP is disabled.
+ *
+ * @return true or false
+ */
+ boolean dhcpEnabled();
+
+ /**
+ * Indicates whether this tenantNetwork is shared across all tenants. By
+ * default, only administrative user can change this value.
+ *
+ * @return true or false
+ */
+ boolean shared();
+
+ /**
+ * Returns a collection of hostRoutes.
+ *
+ * @return a collection of hostRoutes
+ */
+ Iterable<HostRoute> hostRoutes();
+
+ /**
+ * Returns the ipV6AddressMode. A valid value is dhcpv6-stateful,
+ * dhcpv6-stateless, or slaac.
+ *
+ * @return ipV6AddressMode whose value is dhcpv6-stateful, dhcpv6-stateless
+ * or slaac
+ */
+ Mode ipV6AddressMode();
+
+ /**
+ * Returns the ipV6RaMode.A valid value is dhcpv6-stateful,
+ * dhcpv6-stateless, or slaac.
+ *
+ * @return ipV6RaMode whose value is dhcpv6-stateful, dhcpv6-stateless or
+ * slaac
+ */
+ Mode ipV6RaMode();
+
+ /**
+ * Returns a collection of allocation_pools.
+ *
+ * @return a collection of allocationPools
+ */
+ Iterable<AllocationPool> allocationPools();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java
new file mode 100644
index 00000000..4bcc3329
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+/**
+ * Immutable representation of a subnet identifier.
+ */
+public final class SubnetId {
+
+ private final String subnetId;
+
+ // Public construction is prohibited
+ private SubnetId(String subnetId) {
+ checkNotNull(subnetId, "SubnetId cannot be null");
+ this.subnetId = subnetId;
+ }
+
+ /**
+ * Creates a Subnet identifier.
+ *
+ * @param subnetId the subnet identifier
+ * @return the subnet identifier
+ */
+ public static SubnetId subnetId(String subnetId) {
+ return new SubnetId(subnetId);
+ }
+
+ /**
+ * Returns the subnet identifier.
+ *
+ * @return the subnet identifier
+ */
+ public String subnetId() {
+ return subnetId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(subnetId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof SubnetId) {
+ final SubnetId that = (SubnetId) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.subnetId, that.subnetId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return subnetId;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java
new file mode 100644
index 00000000..c4d99e49
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a tenant identifier.
+ */
+public final class TenantId {
+
+ private final String tenantId;
+
+ // Public construction is prohibited
+ private TenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ /**
+ * Creates a network id using the tenantid.
+ *
+ * @param tenantid network String
+ * @return TenantId
+ */
+ public static TenantId tenantId(String tenantid) {
+ checkNotNull(tenantid, "Tenantid can not be null");
+ return new TenantId(tenantid);
+ }
+
+ /**
+ * Returns the tenant identifier.
+ *
+ * @return the tenant identifier
+ */
+ public String tenantId() {
+ return tenantId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tenantId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof TenantId) {
+ final TenantId that = (TenantId) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.tenantId, that.tenantId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return tenantId;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java
new file mode 100644
index 00000000..256352f4
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+/**
+ * Representation of the tenantNetwork.
+ */
+public interface TenantNetwork {
+
+ /**
+ * Coarse classification of the state of the tenantNetwork.
+ */
+ enum State {
+ /**
+ * Signifies that a tenantNetwork is currently active.This state means
+ * that this network is available.
+ */
+ ACTIVE,
+ /**
+ * Signifies that a tenantNetwork is currently built.
+ */
+ BUILD,
+ /**
+ * Signifies that a tenantNetwork is currently unavailable.
+ */
+ DOWN,
+ /**
+ * Signifies that a tenantNetwork is currently error.
+ */
+ ERROR
+ }
+
+ /**
+ * Coarse classification of the type of the tenantNetwork.
+ */
+ enum Type {
+ /**
+ * Signifies that a tenantNetwork is local.
+ */
+ LOCAL
+ }
+
+ /**
+ * Returns the tenantNetwork identifier.
+ *
+ * @return tenantNetwork identifier
+ */
+ TenantNetworkId id();
+
+ /**
+ * Returns the tenantNetwork name.
+ *
+ * @return tenantNetwork name
+ */
+ String name();
+
+ /**
+ * Returns the administrative state of the tenantNetwork,which is up(true)
+ * or down(false).
+ *
+ * @return true or false
+ */
+ boolean adminStateUp();
+
+ /**
+ * Returns the tenantNetwork state.
+ *
+ * @return tenant network state
+ */
+ State state();
+
+ /**
+ * Indicates whether this tenantNetwork is shared across all tenants. By
+ * default,only administrative user can change this value.
+ *
+ * @return true or false
+ */
+ boolean shared();
+
+ /**
+ * Returns the UUID of the tenant that will own the tenantNetwork. This
+ * tenant can be different from the tenant that makes the create
+ * tenantNetwork request.
+ *
+ * @return the tenant identifier
+ */
+ TenantId tenantId();
+
+ /**
+ * Returns the routerExternal.Indicates whether this network is externally
+ * accessible.
+ *
+ * @return true or false
+ */
+ boolean routerExternal();
+
+ /**
+ * Returns the tenantNetwork Type.
+ *
+ * @return tenantNetwork Type
+ */
+ Type type();
+
+ /**
+ * Returns the tenantNetwork physical network.
+ *
+ * @return tenantNetwork physical network
+ */
+ PhysicalNetwork physicalNetwork();
+
+ /**
+ * Returns the tenantNetwork segmentation id.
+ *
+ * @return tenantNetwork segmentation id
+ */
+ SegmentationId segmentationId();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java
new file mode 100644
index 00000000..fbb9e480
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a tenantNetwork identity.
+ */
+public final class TenantNetworkId {
+
+ private final String networkId;
+
+ // Public construction is prohibited
+ private TenantNetworkId(String networkId) {
+ this.networkId = networkId;
+ }
+
+ /**
+ * Creates a TenantNetwork identifier.
+ *
+ * @param networkId tenantNetwork identify string
+ * @return the attached tenantNetwork identifier
+ */
+ public static TenantNetworkId networkId(String networkId) {
+ checkNotNull(networkId, "Networkid cannot be null");
+ return new TenantNetworkId(networkId);
+ }
+
+ /**
+ * Returns tenantNetwork identifier.
+ *
+ * @return the tenantNetwork identifier
+ */
+ public String networkId() {
+ return networkId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof TenantNetworkId) {
+ final TenantNetworkId that = (TenantNetworkId) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.networkId, that.networkId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return networkId;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java
new file mode 100644
index 00000000..d2d7c146
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Representation of the VirtualPort.
+ */
+public interface VirtualPort {
+ /**
+ * Coarse classification of the type of the virtual port.
+ */
+ enum State {
+ /**
+ * Signifies that a virtualPort is currently active,This state mean that
+ * this virtualPort is available.
+ */
+ ACTIVE,
+ /**
+ * Signifies that a virtualPort is currently unavailable.
+ */
+ DOWN;
+ }
+
+ /**
+ * Returns the virtualPort identifier.
+ *
+ * @return virtualPort identifier
+ */
+ VirtualPortId portId();
+
+ /**
+ * Returns the network identifier.
+ *
+ * @return tenantNetwork identifier
+ */
+ TenantNetworkId networkId();
+
+ /**
+ * Returns the symbolic name for the virtualPort.
+ *
+ * @return virtualPort name
+ */
+ String name();
+
+ /**
+ * Returns the administrative status of the port,which is up(true) or
+ * down(false).
+ *
+ * @return true if the administrative status of the port is up
+ */
+ boolean adminStateUp();
+
+ /**
+ * Returns the state.
+ *
+ * @return state
+ */
+ State state();
+
+ /**
+ * Returns the MAC address.
+ *
+ * @return MAC Address
+ */
+ MacAddress macAddress();
+
+ /**
+ * Returns the port tenantId.
+ *
+ * @return port tenantId
+ */
+ TenantId tenantId();
+
+ /**
+ * Returns the device identifier.
+ *
+ * @return deviceId
+ */
+ DeviceId deviceId();
+
+ /**
+ * Returns the identifier of the entity that uses this port.
+ *
+ * @return deviceOwner
+ */
+ String deviceOwner();
+
+ /**
+ * Returns the virtualPort allowedAddressPairs.
+ *
+ * @return virtualPort allowedAddressPairs
+ */
+ Collection<AllowedAddressPair> allowedAddressPairs();
+
+ /**
+ * Returns set of IP addresses for the port, include the IP addresses and subnet
+ * identity.
+ *
+ * @return FixedIps Set of fixedIp
+ */
+ Set<FixedIp> fixedIps();
+
+ /**
+ * Returns the virtualPort bindinghostId.
+ *
+ * @return virtualPort bindinghostId
+ */
+ BindingHostId bindingHostId();
+
+ /**
+ * Returns the virtualPort bindingVnicType.
+ *
+ * @return virtualPort bindingVnicType
+ */
+ String bindingVnicType();
+
+ /**
+ * Returns the virtualPort bindingVifType.
+ *
+ * @return virtualPort bindingVifType
+ */
+ String bindingVifType();
+
+ /**
+ * Returns the virtualPort bindingvifDetail.
+ *
+ * @return virtualPort bindingvifDetail
+ */
+ String bindingVifDetails();
+
+ /**
+ * Returns the security groups.
+ *
+ * @return port security groups
+ */
+ Iterable<SecurityGroup> securityGroups();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java
new file mode 100644
index 00000000..3038bdff
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+/**
+ * Immutable representation of a virtual port identifier.
+ */
+public final class VirtualPortId {
+ private final String portId;
+ // Public construction is prohibited
+ private VirtualPortId(String virtualPortId) {
+ checkNotNull(virtualPortId, "VirtualPortId cannot be null");
+ this.portId = virtualPortId;
+ }
+
+ public String portId() {
+ return portId;
+ }
+
+ /**
+ * Creates a virtualPort id using the supplied portId.
+ *
+ * @param portId virtualport identifier
+ * @return VirtualPortId
+ */
+ public static VirtualPortId portId(String portId) {
+ return new VirtualPortId(portId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(portId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof VirtualPortId) {
+ final VirtualPortId that = (VirtualPortId) obj;
+ return this.getClass() == that.getClass()
+ && Objects.equals(this.portId, that.portId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return portId;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java
new file mode 100644
index 00000000..bcfdacfa
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for creating a TenantNetwork.
+ */
+@Command(scope = "onos", name = "tenantnetwork-create",
+ description = "Supports for creating a TenantNetwork")
+public class TenantNetworkCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "TenantNetwork network id", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Argument(index = 1, name = "tenantID", description = "The tenant id of TenantNetwork",
+ required = true, multiValued = false)
+ String tenantID = null;
+
+ @Argument(index = 2, name = "type", description = "The type of TenantNetwork", required = true,
+ multiValued = false)
+ String type = null;
+
+ @Argument(index = 3, name = "segmentationID", description = "The segmentation id of TenantNetwork",
+ required = true, multiValued = false)
+ String segmentationID = "";
+
+ @Option(name = "-n", aliases = "--name", description = "TenantNetwork name", required = false,
+ multiValued = false)
+ String name = null;
+
+ @Option(name = "-a", aliases = "--adminStateUp", description = "TenantNetwork adminStateUp is true or false",
+ required = false, multiValued = false)
+ boolean adminStateUp = false;
+
+ @Option(name = "-s", aliases = "--state", description = "The state of TenantNetwork",
+ required = false, multiValued = false)
+ String state = null;
+
+ @Option(name = "-d", aliases = "--shared", description = "TenantNetwork is shared or not",
+ required = false, multiValued = false)
+ boolean shared = false;
+
+ @Option(name = "-r", aliases = "--routerExternal",
+ description = "TenantNetwork is routerExternal or not", required = false,
+ multiValued = false)
+ boolean routerExternal = false;
+
+ @Option(name = "-p", aliases = "--physicalNetwork", description = "The physical network of Tenant",
+ required = false, multiValued = false)
+ String physicalNetwork = "";
+
+ @Override
+ protected void execute() {
+ TenantNetworkService service = get(TenantNetworkService.class);
+ TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name,
+ adminStateUp,
+ TenantNetwork.State.valueOf(state),
+ shared, TenantId.tenantId(tenantID),
+ routerExternal,
+ TenantNetwork.Type.valueOf(type),
+ PhysicalNetwork.physicalNetwork(physicalNetwork),
+ SegmentationId.segmentationId(segmentationID));
+
+ Set<TenantNetwork> networksSet = Sets.newHashSet(network);
+ service.createNetworks(networksSet);
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java
new file mode 100644
index 00000000..47ea83c2
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+/**
+ * Supports for querying TenantNetworks by network id.
+ */
+@Command(scope = "onos", name = "tenantnetworks", description = "Supports for querying"
+ + "tenantNetworks by networkid")
+public class TenantNetworkQueryCommand extends AbstractShellCommand {
+
+ @Option(name = "-i", aliases = "--id", description = "TenantNetwork id", required = false,
+ multiValued = false)
+ String id = null;
+
+ private static final String FMT = "networkId=%s, networkName=%s, segmentationId=%s,"
+ + "tenantId=%s, type=%s, adminStateUp=%s";
+
+ @Override
+ protected void execute() {
+ TenantNetworkService service = get(TenantNetworkService.class);
+ if (id != null) {
+ TenantNetwork network = service.getNetwork(TenantNetworkId.networkId(id));
+ printNetwork(network);
+ } else {
+ Iterable<TenantNetwork> networks = service.getNetworks();
+ for (TenantNetwork network : networks) {
+ printNetwork(network);
+ }
+ }
+ }
+
+ private void printNetwork(TenantNetwork network) {
+ if (network == null) {
+ return;
+ }
+ print(FMT, network.id(), network.name(), network.segmentationId(),
+ network.tenantId(), network.type(), network.adminStateUp());
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java
new file mode 100644
index 00000000..0ea22853
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for removing a TenantNetwork by network id.
+ */
+@Command(scope = "onos", name = "tenantnetwork-remove", description = "Supports for removing"
+ + " a tenantNetwork by tenantNetworkid")
+public class TenantNetworkRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "TenantNetwork neutronNetwork Id",
+ required = true, multiValued = false)
+ String id = null;
+
+ @Override
+ protected void execute() {
+ TenantNetworkService service = get(TenantNetworkService.class);
+ Set<TenantNetworkId> networkIds = Sets.newHashSet(TenantNetworkId.networkId(id));
+ service.removeNetworks(networkIds);
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java
new file mode 100644
index 00000000..2a738f72
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for updating a TenantNetwork.
+ */
+@Command(scope = "onos", name = "tenantnetwork-update",
+ description = "Supports for updating a TenantNetwork")
+public class TenantNetworkUpdateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "TenantNetwork network id", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Argument(index = 1, name = "tenantID", description = "The tenant id of TenantNetwork",
+ required = true, multiValued = false)
+ String tenantID = null;
+
+ @Argument(index = 2, name = "type", description = "The type of TenantNetwork", required = true,
+ multiValued = false)
+ String type = null;
+
+ @Argument(index = 3, name = "segmentationID", description = "The segmentation id of TenantNetwork",
+ required = true, multiValued = false)
+ String segmentationID = "";
+
+ @Option(name = "-n", aliases = "--name", description = "TenantNetwork name", required = false,
+ multiValued = false)
+ String name = null;
+
+ @Option(name = "-a", aliases = "--adminStateUp", description = "TenantNetwork adminStateUp is true or false",
+ required = false, multiValued = false)
+ boolean adminStateUp = false;
+
+ @Option(name = "-s", aliases = "--state", description = "The state of TenantNetwork",
+ required = false, multiValued = false)
+ String state = null;
+
+ @Option(name = "-d", aliases = "--shared", description = "TenantNetwork is shared or not",
+ required = false, multiValued = false)
+ boolean shared = false;
+
+ @Option(name = "-r", aliases = "--routerExternal",
+ description = "TenantNetwork is routerExternal or not", required = false,
+ multiValued = false)
+ boolean routerExternal = false;
+
+ @Option(name = "-p", aliases = "--physicalNetwork", description = "The physical network of Tenant",
+ required = false, multiValued = false)
+ String physicalNetwork = "";
+
+ @Override
+ protected void execute() {
+ TenantNetworkService service = get(TenantNetworkService.class);
+ TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name,
+ adminStateUp,
+ TenantNetwork.State.valueOf(state),
+ shared, TenantId.tenantId(tenantID),
+ routerExternal,
+ TenantNetwork.Type.valueOf(type),
+ PhysicalNetwork.physicalNetwork(physicalNetwork),
+ SegmentationId.segmentationId(segmentationID));
+
+ Set<TenantNetwork> networksSet = Sets.newHashSet();
+ networksSet.add(network);
+ service.updateNetworks(networksSet);
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java
new file mode 100644
index 00000000..1622c800
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Command line interface for tenant networks.
+ */
+package org.onosproject.vtnrsc.cli.network;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java
new file mode 100644
index 00000000..56236408
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.Subnet.Mode;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for creating a subnet.
+ */
+@Command(scope = "onos", name = "subnet-create", description = "Supports for creating a subnet")
+public class SubnetCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Subnet Id", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Argument(index = 1, name = "subnetName", description = "Subnet String name", required = true,
+ multiValued = false)
+ String subnetName = null;
+
+ @Argument(index = 2, name = "networkId", description = "Subnet Network Id", required = true,
+ multiValued = false)
+ String networkId = null;
+
+ @Argument(index = 3, name = "tenantId", description = "Subnet Tenant Id", required = true,
+ multiValued = false)
+ String tenantId = null;
+
+ @Option(name = "-i", aliases = "--ipVersion", description = "Subnet Version ipVersion",
+ required = false, multiValued = false)
+ Version ipVersion = null;
+
+ @Option(name = "-c", aliases = "--cidr", description = "Subnet IpPrefix cidr",
+ required = false, multiValued = false)
+ String cidr = "0.0.0.0/0";
+
+ @Option(name = "-g", aliases = "--gatewayIp", description = "Subnet IpAddress gatewayIp",
+ required = false, multiValued = false)
+ String gatewayIp = "0.0.0.0";
+
+ @Option(name = "-d", aliases = "--dhcpEnabled", description = "Subnet boolean dhcpEnabled",
+ required = false, multiValued = false)
+ boolean dhcpEnabled = false;
+
+ @Option(name = "-s", aliases = "--shared", description = "Subnet boolean shared",
+ required = false, multiValued = false)
+ boolean shared = false;
+
+ @Option(name = "-m", aliases = "--ipV6AddressMode",
+ description = "Subnet Mode ipV6AddressMode", required = false, multiValued = false)
+ String ipV6AddressMode = null;
+
+ @Option(name = "-r", aliases = "--ipV6RaMode", description = "Subnet Mode ipV6RaMode",
+ required = false, multiValued = false)
+ String ipV6RaMode = null;
+
+ @Option(name = "-h", aliases = "--hostRoutes", description = "Subnet jsonnode hostRoutes",
+ required = false, multiValued = false)
+ Set<HostRoute> hostRoutes = Sets.newHashSet();
+
+ @Option(name = "-a", aliases = "--allocationPools",
+ description = "Subnet jsonnode allocationPools", required = false, multiValued = false)
+ Set<AllocationPool> allocationPools = Sets.newHashSet();
+
+ @Override
+ protected void execute() {
+ SubnetService service = get(SubnetService.class);
+ if (id == null || networkId == null || tenantId == null) {
+ print(null, "id,networkId,tenantId can not be null");
+ return;
+ }
+ Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName,
+ TenantNetworkId.networkId(networkId),
+ TenantId.tenantId(tenantId), ipVersion,
+ cidr == null ? null : IpPrefix.valueOf(cidr),
+ gatewayIp == null ? null : IpAddress.valueOf(gatewayIp),
+ dhcpEnabled, shared, hostRoutes,
+ ipV6AddressMode == null ? null : Mode.valueOf(ipV6AddressMode),
+ ipV6RaMode == null ? null : Mode.valueOf(ipV6RaMode),
+ allocationPools);
+
+ Set<Subnet> subnetsSet = Sets.newHashSet(subnet);
+ service.createSubnets(subnetsSet);
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java
new file mode 100644
index 00000000..f5a94f0f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+/**
+ * Supports for querying a subnet.
+ */
+@Command(scope = "onos", name = "subnets", description = "Supports for querying a subnet")
+public class SubnetQueryCommand extends AbstractShellCommand {
+
+ @Option(name = "-i", aliases = "--id", description = "Subnet id", required = false,
+ multiValued = false)
+ String id = null;
+
+ private static final String FMT = "subnetId=%s, networkId=%s, subnetName=%s,"
+ + "tenantId=%s, cidr=%s, dhcpEnabled=%s, gatewayIp=%s," + "ipVersion=%s";
+
+ @Override
+ protected void execute() {
+ SubnetService service = get(SubnetService.class);
+ if (id != null) {
+ Subnet subnet = service.getSubnet(SubnetId.subnetId(id));
+ printSubnet(subnet);
+ } else {
+ Iterable<Subnet> subnets = service.getSubnets();
+ if (subnets == null) {
+ return;
+ }
+ for (Subnet subnet : subnets) {
+ printSubnet(subnet);
+ }
+ }
+ }
+
+ private void printSubnet(Subnet subnet) {
+ print(FMT, subnet.id(), subnet.networkId(), subnet.subnetName(),
+ subnet.tenantId(), subnet.cidr(), subnet.dhcpEnabled(), subnet
+ .gatewayIp(), subnet.ipVersion());
+
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java
new file mode 100644
index 00000000..241af87e
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for removing a subnet.
+ */
+@Command(scope = "onos", name = "subnet-remove", description = "Supports for removing a subnet")
+public class SubnetRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Subnet SubnetId Id", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Override
+ protected void execute() {
+ SubnetService service = get(SubnetService.class);
+ Set<SubnetId> subnetsSet = Sets.newHashSet();
+ subnetsSet.add(SubnetId.subnetId(id));
+ service.removeSubnets(subnetsSet);
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java
new file mode 100644
index 00000000..b0578a1e
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.Subnet.Mode;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for updating a subnet.
+ */
+@Command(scope = "onos", name = "subnet-update", description = "Supports for updating a subnet")
+public class SubnetUpdateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Subnet Id", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Argument(index = 1, name = "subnetName", description = "Subnet String name", required = true,
+ multiValued = false)
+ String subnetName = null;
+
+ @Argument(index = 2, name = "networkId", description = "Subnet Network Id", required = true,
+ multiValued = false)
+ String networkId = null;
+
+ @Argument(index = 3, name = "tenantId", description = "Subnet Tenant Id", required = true,
+ multiValued = false)
+ String tenantId = null;
+
+ @Option(name = "-i", aliases = "--ipVersion", description = "Subnet Version ipVersion",
+ required = false, multiValued = false)
+ Version ipVersion = null;
+
+ @Option(name = "-c", aliases = "--cidr", description = "Subnet IpPrefix cidr", required = false,
+ multiValued = false)
+ String cidr = "0.0.0.0/0";
+
+ @Option(name = "-g", aliases = "--gatewayIp", description = "Subnet IpAddress gatewayIp",
+ required = false, multiValued = false)
+ String gatewayIp = "0.0.0.0";
+
+ @Option(name = "-d", aliases = "--dhcpEnabled", description = "Subnet boolean dhcpEnabled",
+ required = false, multiValued = false)
+ boolean dhcpEnabled = false;
+
+ @Option(name = "-s", aliases = "--shared", description = "Subnet boolean shared", required = false,
+ multiValued = false)
+ boolean shared = false;
+
+ @Option(name = "-m", aliases = "--ipV6AddressMode", description = "Subnet Mode ipV6AddressMode",
+ required = false, multiValued = false)
+ String ipV6AddressMode = null;
+
+ @Option(name = "-r", aliases = "--ipV6RaMode", description = "Subnet Mode ipV6RaMode",
+ required = false, multiValued = false)
+ String ipV6RaMode = null;
+
+ @Option(name = "-h", aliases = "--hostRoutes", description = "Subnet jsonnode hostRoutes",
+ required = false, multiValued = false)
+ Set<HostRoute> hostRoutes = Sets.newHashSet();
+
+ @Option(name = "-a", aliases = "--allocationPools",
+ description = "Subnet jsonnode allocationPools", required = false, multiValued = false)
+ Set<AllocationPool> allocationPools = Sets.newHashSet();
+
+ @Override
+ protected void execute() {
+ SubnetService service = get(SubnetService.class);
+ if (id == null || networkId == null || tenantId == null) {
+ print(null, "id,networkId,tenantId can not be null");
+ return;
+ }
+ Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName,
+ TenantNetworkId.networkId(networkId),
+ TenantId.tenantId(tenantId), ipVersion,
+ cidr == null ? null : IpPrefix.valueOf(cidr),
+ gatewayIp == null ? null : IpAddress.valueOf(gatewayIp),
+ dhcpEnabled, shared, hostRoutes,
+ ipV6AddressMode == null ? null : Mode.valueOf(ipV6AddressMode),
+ ipV6RaMode == null ? null : Mode.valueOf(ipV6RaMode),
+ allocationPools);
+ Set<Subnet> subnetsSet = Sets.newHashSet();
+ subnetsSet.add(subnet);
+ service.updateSubnets(subnetsSet);
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java
new file mode 100644
index 00000000..b3a2ff51
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Command line interface for subnets.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java
new file mode 100644
index 00000000..4c555e33
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for creating a virtualPort.
+ */
+@Command(scope = "onos", name = "virtualport-create",
+ description = "Supports for creating a virtualPort.")
+public class VirtualPortCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "virtualPort id.", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Argument(index = 1, name = "networkId", description = "network id.", required = true,
+ multiValued = false)
+ String networkId = null;
+
+ @Argument(index = 2, name = "name", description = "virtualPort name.", required = true,
+ multiValued = false)
+ String name = null;
+
+ @Argument(index = 3, name = "tenantId", description = "tenant id.", required = true,
+ multiValued = false)
+ String tenantId = null;
+
+ @Argument(index = 4, name = "deviceId", description = "device id.", required = true,
+ multiValued = false)
+ String deviceId = null;
+
+ @Option(name = "-a", aliases = "--adminStateUp",
+ description = "administrative status of the virtualPort which is true or false.",
+ required = false, multiValued = false)
+ Boolean adminStateUp = false;
+
+ @Option(name = "-s", aliases = "--state", description = "virtualPort state.", required = false,
+ multiValued = false)
+ String state = null;
+
+ @Option(name = "-m", aliases = "--macAddress", description = "MAC address.", required = false,
+ multiValued = false)
+ String macAddress = "";
+
+ @Option(name = "-d", aliases = "--deviceOwner", description = "ID of the entity that uses this "
+ + "virtualPort.", required = false, multiValued = false)
+ String deviceOwner = null;
+
+ @Option(name = "-f", aliases = "--fixedIp",
+ description = "The IP address for the port,include the IP address "
+ + "and subnet identity.", required = false, multiValued = false)
+ FixedIp fixedIp = null;
+
+ @Option(name = "-i", aliases = "--bindingHostId", description = "virtualPort bindingHostId.",
+ required = false, multiValued = false)
+ String bindingHostId = null;
+
+ @Option(name = "-t", aliases = "--bindingvnicType", description = "virtualPort bindingvnicType.",
+ required = false, multiValued = false)
+ String bindingvnicType = null;
+
+ @Option(name = "-v", aliases = "--bindingvifType", description = "virtualPort bindingvifType.",
+ required = false, multiValued = false)
+ String bindingvifType = null;
+
+ @Option(name = "-b", aliases = "--bindingvnicDetails",
+ description = "virtualPort bindingvnicDetails.", required = false, multiValued = false)
+ String bindingvnicDetails = null;
+
+ @Option(name = "-l", aliases = "--allowedAddress", description = "virtual allowedAddressPair.",
+ required = false, multiValued = false)
+ Set<AllowedAddressPair> allowedAddressPairs = Sets.newHashSet();
+
+ @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.",
+ required = false, multiValued = false)
+ Set<SecurityGroup> securityGroups = Sets.newHashSet();
+
+ @Override
+ protected void execute() {
+ Map<String, String> strMap = Maps.newHashMap();
+ strMap.putIfAbsent("name", name);
+ strMap.putIfAbsent("deviceOwner", deviceOwner);
+ strMap.putIfAbsent("bindingvnicType", bindingvnicType);
+ strMap.putIfAbsent("bindingvifType", bindingvifType);
+ strMap.putIfAbsent("bindingvnicDetails", bindingvnicDetails);
+ VirtualPortService service = get(VirtualPortService.class);
+ VirtualPort virtualPort = new DefaultVirtualPort(VirtualPortId.portId(id),
+ TenantNetworkId.networkId(networkId),
+ false, strMap, VirtualPort.State.ACTIVE,
+ MacAddress.valueOf(macAddress),
+ TenantId.tenantId(tenantId),
+ DeviceId.deviceId(deviceId), Sets.newHashSet(fixedIp),
+ BindingHostId.bindingHostId(bindingHostId),
+ allowedAddressPairs, securityGroups);
+ Set<VirtualPort> virtualPorts = Sets.newHashSet(virtualPort);
+ service.createPorts(virtualPorts);
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java
new file mode 100644
index 00000000..47126d1b
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Collection;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+/**
+ * Supports for querying virtualPorts.
+ */
+@Command(scope = "onos", name = "virtualports", description = "Supports for querying virtualPorts.")
+public class VirtualPortQueryCommand extends AbstractShellCommand {
+
+ @Option(name = "-v", aliases = "--vPortId", description = "virtualPort ID.", required = false,
+ multiValued = false)
+ String vPortId;
+
+ @Option(name = "-n", aliases = "--networkId", description = "network ID.", required = false,
+ multiValued = false)
+ String networkId;
+
+ @Option(name = "-d", aliases = "--deviceId", description = "device ID.", required = false,
+ multiValued = false)
+ String deviceId;
+
+ @Option(name = "-t", aliases = "--tenantId", description = "tenant ID.", required = false,
+ multiValued = false)
+ String tenantId;
+
+ private static final String FMT = "virtualPortId=%s, networkId=%s, name=%s,"
+ + " tenantId=%s, deviceId=%s, adminStateUp=%s, state=%s,"
+ + " macAddress=%s, deviceOwner=%s, fixedIp=%s, bindingHostId=%s,"
+ + " bindingvnicType=%s, bindingvifType=%s, bindingvnicDetails=%s,"
+ + " allowedAddress=%s, securityGroups=%s";
+
+ @Override
+ protected void execute() {
+ VirtualPortService service = get(VirtualPortService.class);
+ if (vPortId != null && networkId == null && deviceId == null && tenantId == null) {
+ VirtualPort port = service.getPort(VirtualPortId.portId(vPortId));
+ printPort(port);
+ } else if (vPortId == null && networkId != null && deviceId == null && tenantId == null) {
+ Collection<VirtualPort> ports = service.getPorts(TenantNetworkId.networkId(networkId));
+ printPorts(ports);
+ } else if (vPortId == null && networkId == null && deviceId != null && tenantId == null) {
+ Collection<VirtualPort> ports = service.getPorts(DeviceId.deviceId(deviceId));
+ printPorts(ports);
+ } else if (vPortId == null && networkId == null && deviceId == null && tenantId != null) {
+ Collection<VirtualPort> ports = service.getPorts(DeviceId.deviceId(tenantId));
+ printPorts(ports);
+ } else if (vPortId == null && networkId == null && deviceId == null && tenantId == null) {
+ Collection<VirtualPort> ports = service.getPorts();
+ printPorts(ports);
+ } else {
+ print("cannot input more than one parameter");
+ }
+
+ }
+
+ private void printPorts(Collection<VirtualPort> ports) {
+ for (VirtualPort port : ports) {
+ printPort(port);
+ }
+ }
+
+ private void printPort(VirtualPort port) {
+ print(FMT, port.portId(), port.networkId(), port.name(), port.tenantId(), port.deviceId(),
+ port.adminStateUp(), port.state(), port.macAddress(), port.deviceOwner(), port
+ .fixedIps(), port.bindingHostId(), port.bindingVnicType(),
+ port.bindingVifType(), port.bindingVifDetails(), port.allowedAddressPairs(),
+ port.securityGroups());
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java
new file mode 100644
index 00000000..1a3cb4f0
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for removing a virtualPort.
+ */
+@Command(scope = "onos", name = "virtualport-remove",
+ description = "Supports for removing a virtualPort.")
+public class VirtualPortRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "virtualPort id.", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Override
+ protected void execute() {
+ VirtualPortService service = get(VirtualPortService.class);
+ Set<VirtualPortId> virtualPorts = Sets.newHashSet(VirtualPortId.portId(id));
+ service.removePorts(virtualPorts);
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java
new file mode 100644
index 00000000..6df4b23c
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for updating a virtualPort.
+ */
+@Command(scope = "onos", name = "virtualport-update",
+ description = "Supports for updating a virtualPort.")
+public class VirtualPortUpdateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "virtualPort id.", required = true,
+ multiValued = false)
+ String id = null;
+
+ @Argument(index = 1, name = "networkId", description = "network id.", required = true,
+ multiValued = false)
+ String networkId = null;
+
+ @Argument(index = 2, name = "name", description = "virtualPort name.", required = true,
+ multiValued = false)
+ String name = null;
+
+ @Argument(index = 3, name = "tenantId", description = "tenant id.", required = true,
+ multiValued = false)
+ String tenantId = null;
+
+ @Argument(index = 4, name = "deviceId", description = "device id.", required = true,
+ multiValued = false)
+ String deviceId = null;
+
+ @Option(name = "-a", aliases = "--adminStateUp",
+ description = "administrative status of the virtualPort which is true or false.",
+ required = false, multiValued = false)
+ Boolean adminStateUp = false;
+
+ @Option(name = "-s", aliases = "--state", description = "virtualPort state.", required = false,
+ multiValued = false)
+ String state = null;
+
+ @Option(name = "-m", aliases = "--macAddress", description = "MAC address.", required = false,
+ multiValued = false)
+ String macAddress = "";
+
+ @Option(name = "-d", aliases = "--deviceOwner",
+ description = "ID of the entity that uses this " + "virtualPort.", required = false,
+ multiValued = false)
+ String deviceOwner = null;
+
+ @Option(name = "-f", aliases = "--fixedIp",
+ description = "The IP address for the port,include the IP address "
+ + "and subnet identity.", required = false, multiValued = false)
+ FixedIp fixedIp = null;
+
+ @Option(name = "-i", aliases = "--bindingHostId", description = "virtualPort bindingHostId.",
+ required = false, multiValued = false)
+ String bindingHostId = "";
+
+ @Option(name = "-t", aliases = "--bindingvnicType",
+ description = "virtualPort bindingvnicType.", required = false, multiValued = false)
+ String bindingvnicType = null;
+
+ @Option(name = "-v", aliases = "--bindingvifType", description = "virtualPort bindingvifType.",
+ required = false, multiValued = false)
+ String bindingvifType = null;
+
+ @Option(name = "-b", aliases = "--bindingvnicDetails",
+ description = "virtualPort bindingvnicDetails.", required = false, multiValued = false)
+ String bindingvnicDetails = null;
+
+ @Option(name = "-l", aliases = "--allowedAddress", description = "virtual allowedAddressPair.",
+ required = false, multiValued = false)
+ Set<AllowedAddressPair> allowedAddressPairs = Sets.newHashSet();
+
+ @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.",
+ required = false, multiValued = false)
+ Set<SecurityGroup> securityGroups = Sets.newHashSet();
+
+ @Override
+ protected void execute() {
+ VirtualPortService service = get(VirtualPortService.class);
+ Map<String, String> strMap = Maps.newHashMap();
+ strMap.putIfAbsent("name", name);
+ strMap.putIfAbsent("deviceOwner", deviceOwner);
+ strMap.putIfAbsent("bindingvnicType", bindingvnicType);
+ strMap.putIfAbsent("bindingvifType", bindingvifType);
+ strMap.putIfAbsent("bindingvnicDetails", bindingvnicDetails);
+ VirtualPort virtualPort = new DefaultVirtualPort(VirtualPortId.portId(id),
+ TenantNetworkId.networkId(networkId),
+ false, strMap, VirtualPort.State.ACTIVE,
+ MacAddress.valueOf(macAddress),
+ TenantId.tenantId(tenantId),
+ DeviceId.deviceId(deviceId), Sets.newHashSet(fixedIp),
+ BindingHostId.bindingHostId(bindingHostId),
+ allowedAddressPairs, securityGroups);
+ Set<VirtualPort> virtualPorts = Sets.newHashSet(virtualPort);
+ service.updatePorts(virtualPorts);
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java
new file mode 100644
index 00000000..fac214a1
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Command line interface for virtual ports.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java
new file mode 100644
index 00000000..b245fb14
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN resources that used by virtual tenant network.
+ */
+package org.onosproject.vtnrsc;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java
new file mode 100644
index 00000000..82eb9611
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.subnet;
+
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+public interface SubnetService {
+ /**
+ * Returns the subnet with the specified identifier.
+ *
+ * @param subnetId subnet identifier
+ * @return true or false
+ */
+ boolean exists(SubnetId subnetId);
+ /**
+ * Returns a collection of the currently known subnets.
+ *
+ * @return iterable collection of subnets
+ */
+ Iterable<Subnet> getSubnets();
+
+ /**
+ * Returns the subnet with the specified identifier.
+ *
+ * @param subnetId subnet identifier
+ * @return subnet or null if one with the given identifier is not known
+ */
+ Subnet getSubnet(SubnetId subnetId);
+ /**
+ * Creates new subnets.
+ *
+ * @param subnets the iterable collection of subnets
+ * @return true if the identifier subnet has been created right
+ */
+ boolean createSubnets(Iterable<Subnet> subnets);
+
+ /**
+ * Updates existing subnets.
+ *
+ * @param subnets the iterable collection of subnets
+ * @return true if all subnets were updated successfully
+ */
+ boolean updateSubnets(Iterable<Subnet> subnets);
+
+ /**
+ * Administratively removes the specified subnets from the store.
+ *
+ * @param subnetIds the iterable collection of subnets identifier
+ * @return true if remove identifier subnets successfully
+ */
+ boolean removeSubnets(Iterable<SubnetId> subnetIds);
+
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java
new file mode 100644
index 00000000..890beb29
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.subnet.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultAllocationPool;
+import org.onosproject.vtnrsc.DefaultHostRoute;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of the Subnet service.
+ */
+@Component(immediate = true)
+@Service
+public class SubnetManager implements SubnetService {
+
+ private static final String SUBNET_ID_NULL = "Subnet ID cannot be null";
+ private static final String SUBNET_NOT_NULL = "Subnet cannot be null";
+ private static final String SUBNET = "vtn-subnet-store";
+ private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+
+
+ private final Logger log = getLogger(getClass());
+
+ protected Map<SubnetId, Subnet> subnetStore;
+ protected ApplicationId appId;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TenantNetworkService tenantNetworkService;
+
+ @Activate
+ public void activate() {
+
+ appId = coreService.registerApplication(VTNRSC_APP);
+
+ subnetStore = storageService.<SubnetId, Subnet>consistentMapBuilder()
+ .withName(SUBNET)
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+ Subnet.class,
+ SubnetId.class,
+ TenantNetworkId.class,
+ TenantId.class,
+ HostRoute.class,
+ DefaultHostRoute.class,
+ Subnet.Mode.class,
+ AllocationPool.class,
+ DefaultAllocationPool.class,
+ DefaultSubnet.class,
+ IpAddress.Version.class))
+ .build().asJavaMap();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public Iterable<Subnet> getSubnets() {
+ return Collections.unmodifiableCollection(subnetStore.values());
+ }
+
+ @Override
+ public Subnet getSubnet(SubnetId subnetId) {
+ checkNotNull(subnetId, SUBNET_ID_NULL);
+ return subnetStore.get(subnetId);
+ }
+
+ @Override
+ public boolean exists(SubnetId subnetId) {
+ checkNotNull(subnetId, SUBNET_ID_NULL);
+ return subnetStore.containsKey(subnetId);
+ }
+
+ @Override
+ public boolean createSubnets(Iterable<Subnet> subnets) {
+ checkNotNull(subnets, SUBNET_NOT_NULL);
+ for (Subnet subnet : subnets) {
+ if (!tenantNetworkService.exists(subnet.networkId())) {
+ log.debug("The network identifier that the subnet {} belong to is not exist",
+ subnet.networkId().toString(), subnet.id().toString());
+ return false;
+ }
+ subnetStore.put(subnet.id(), subnet);
+ if (!subnetStore.containsKey(subnet.id())) {
+ log.debug("The identified subnet whose identifier is {} create failed",
+ subnet.id().toString());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean updateSubnets(Iterable<Subnet> subnets) {
+ checkNotNull(subnets, SUBNET_NOT_NULL);
+ if (subnets != null) {
+ for (Subnet subnet : subnets) {
+ if (!subnetStore.containsKey(subnet.id())) {
+ log.debug("The subnet is not exist whose identifier is {}",
+ subnet.id().toString());
+ return false;
+ }
+
+ subnetStore.put(subnet.id(), subnet);
+
+ if (!subnet.equals(subnetStore.get(subnet.id()))) {
+ log.debug("The subnet is updated failed whose identifier is {}",
+ subnet.id().toString());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean removeSubnets(Iterable<SubnetId> subnetIds) {
+ checkNotNull(subnetIds, SUBNET_ID_NULL);
+ if (subnetIds != null) {
+ for (SubnetId subnetId : subnetIds) {
+ subnetStore.remove(subnetId);
+ if (subnetStore.containsKey(subnetId)) {
+ log.debug("The subnet created is failed whose identifier is {}",
+ subnetId.toString());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java
new file mode 100644
index 00000000..79040d8d
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Provides implementation of the Subnet service.
+ */
+package org.onosproject.vtnrsc.subnet.impl;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java
new file mode 100644
index 00000000..7b2bdb90
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+package org.onosproject.vtnrsc.subnet;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java
new file mode 100644
index 00000000..e246cc4e
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.tenantnetwork;
+
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+
+/**
+ * Service for interacting with the inventory of tenantNetwork.
+ */
+public interface TenantNetworkService {
+
+ /**
+ * Returns if the tenantNetwork is existed.
+ *
+ * @param networkId tenantNetwork identifier
+ * @return true or false if one with the given identifier exists.
+ */
+ boolean exists(TenantNetworkId networkId);
+
+ /**
+ * Returns the number of tenantNetwork known to the system.
+ *
+ * @return number of tenantNetwork.
+ */
+ int getNetworkCount();
+
+ /**
+ * Returns an iterable collection of the currently known tenantNetwork.
+ *
+ * @return collection of tenantNetwork.
+ */
+ Iterable<TenantNetwork> getNetworks();
+
+ /**
+ * Returns the tenantNetwork with the identifier.
+ *
+ * @param networkId TenantNetwork identifier
+ * @return TenantNetwork or null if one with the given identifier is not
+ * known.
+ */
+ TenantNetwork getNetwork(TenantNetworkId networkId);
+
+ /**
+ * Creates tenantNetworks by networks.
+ *
+ * @param networks the collection of tenantNetworks
+ * @return true if all given identifiers created successfully.
+ */
+ boolean createNetworks(Iterable<TenantNetwork> networks);
+
+ /**
+ * Updates tenantNetworks by tenantNetworks.
+ *
+ * @param networks the collection of tenantNetworks
+ * @return true if all given identifiers updated successfully.
+ */
+ boolean updateNetworks(Iterable<TenantNetwork> networks);
+
+ /**
+ * Deletes tenantNetwork by tenantNetworkIds.
+ *
+ * @param networksIds the collection of tenantNetworkIds
+ * @return true if the specified tenantNetworks deleted successfully.
+ */
+ boolean removeNetworks(Iterable<TenantNetworkId> networksIds);
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java
new file mode 100644
index 00000000..0dfc99e2
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.tenantnetwork.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of the tenantNetworkService.
+ */
+@Component(immediate = true)
+@Service
+public class TenantNetworkManager implements TenantNetworkService {
+
+ private static final String NETWORK_ID_NULL = "Network ID cannot be null";
+ private static final String NETWORK_NOT_NULL = "Network ID cannot be null";
+ private static final String TENANTNETWORK = "vtn-tenant-network-store";
+ private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+
+ protected Map<TenantNetworkId, TenantNetwork> networkIdAsKeyStore;
+ protected ApplicationId appId;
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+
+ @Activate
+ public void activate() {
+
+ appId = coreService.registerApplication(VTNRSC_APP);
+
+ networkIdAsKeyStore = storageService.<TenantNetworkId, TenantNetwork>consistentMapBuilder()
+ .withName(TENANTNETWORK)
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+ TenantNetworkId.class,
+ DefaultTenantNetwork.class,
+ TenantNetwork.State.class,
+ TenantId.class,
+ TenantNetwork.Type.class,
+ PhysicalNetwork.class,
+ SegmentationId.class))
+ .build().asJavaMap();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public boolean exists(TenantNetworkId networkId) {
+ checkNotNull(networkId, NETWORK_ID_NULL);
+ return networkIdAsKeyStore.containsKey(networkId);
+ }
+
+ @Override
+ public int getNetworkCount() {
+ return networkIdAsKeyStore.size();
+ }
+
+ @Override
+ public Iterable<TenantNetwork> getNetworks() {
+ return Collections.unmodifiableCollection(networkIdAsKeyStore.values());
+ }
+
+ @Override
+ public TenantNetwork getNetwork(TenantNetworkId networkId) {
+ checkNotNull(networkId, NETWORK_ID_NULL);
+ return networkIdAsKeyStore.get(networkId);
+ }
+
+ @Override
+ public boolean createNetworks(Iterable<TenantNetwork> networks) {
+ checkNotNull(networks, NETWORK_NOT_NULL);
+ for (TenantNetwork network : networks) {
+ networkIdAsKeyStore.put(network.id(), network);
+ if (!networkIdAsKeyStore.containsKey(network.id())) {
+ log.debug("The tenantNetwork is created failed which identifier was {}", network.id()
+ .toString());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean updateNetworks(Iterable<TenantNetwork> networks) {
+ checkNotNull(networks, NETWORK_NOT_NULL);
+ for (TenantNetwork network : networks) {
+ if (!networkIdAsKeyStore.containsKey(network.id())) {
+ log.debug("The tenantNetwork is not exist whose identifier was {} ",
+ network.id().toString());
+ return false;
+ }
+
+ networkIdAsKeyStore.put(network.id(), network);
+
+ if (!network.equals(networkIdAsKeyStore.get(network.id()))) {
+ log.debug("The tenantNetwork is updated failed whose identifier was {} ",
+ network.id().toString());
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ @Override
+ public boolean removeNetworks(Iterable<TenantNetworkId> networkIds) {
+ checkNotNull(networkIds, NETWORK_NOT_NULL);
+ for (TenantNetworkId networkId : networkIds) {
+ networkIdAsKeyStore.remove(networkId);
+ if (networkIdAsKeyStore.containsKey(networkId)) {
+ log.debug("The tenantNetwork is removed failed whose identifier was {}",
+ networkId.toString());
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java
new file mode 100644
index 00000000..f381fda6
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of service for interacting with the inventory of tenant networks.
+ */
+package org.onosproject.vtnrsc.tenantnetwork.impl;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java
new file mode 100644
index 00000000..1489c973
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of tenant networks.
+ */
+package org.onosproject.vtnrsc.tenantnetwork;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java
new file mode 100644
index 00000000..6f3cf653
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.tunnel;
+
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+public interface TunnelConfigService {
+ /**
+ * Returns the subnet with the specified identifier.
+ *
+ * @param subnetId subnet identifier
+ * @return true or false
+ */
+ boolean exists(SubnetId subnetId);
+ /**
+ * Returns a collection of the currently known subnets.
+ *
+ * @return iterable collection of subnets
+ */
+ Iterable<Subnet> getSubnets();
+
+ /**
+ * Returns the subnet with the specified identifier.
+ *
+ * @param subnetId subnet identifier
+ * @return subnet or null if one with the given identifier is not known
+ */
+ Subnet getSubnet(SubnetId subnetId);
+ /**
+ * Creates new subnets.
+ *
+ * @param subnets the iterable collection of subnets
+ * @return true if the identifier subnet has been created right
+ */
+ boolean createSubnets(Iterable<Subnet> subnets);
+
+ /**
+ * Updates existing subnets.
+ *
+ * @param subnets the iterable collection of subnets
+ * @return true if all subnets were updated successfully
+ */
+ boolean updateSubnets(Iterable<Subnet> subnets);
+
+ /**
+ * Administratively removes the specified subnets from the store.
+ *
+ * @param subnetIds the iterable collection of subnets identifier
+ * @return true if remove identifier subnets successfully
+ */
+ boolean removeSubnets(Iterable<SubnetId> subnetIds);
+
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java
new file mode 100644
index 00000000..3a84e6e3
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+package org.onosproject.vtnrsc.tunnel;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java
new file mode 100644
index 00000000..05ebccf9
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.virtualport;
+
+import java.util.Collection;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+
+/**
+ * Service for interacting with the inventory of virtualPort.
+ */
+public interface VirtualPortService {
+ /**
+ * Returns if the virtualPort is existed.
+ *
+ * @param virtualPortId virtualPort identifier
+ * @return true or false if one with the given identifier is not existed.
+ */
+ boolean exists(VirtualPortId virtualPortId);
+
+ /**
+ * Returns the virtualPort with the identifier.
+ *
+ * @param virtualPortId virtualPort ID
+ * @return VirtualPort or null if one with the given ID is not know.
+ */
+ VirtualPort getPort(VirtualPortId virtualPortId);
+
+ /**
+ * Returns the collection of the currently known virtualPort.
+ * @return collection of VirtualPort.
+ */
+ Collection<VirtualPort> getPorts();
+
+ /**
+ * Returns the collection of the virtualPorts associated with the networkId.
+ *
+ * @param networkId the network identifer
+ * @return collection of virtualPort.
+ */
+ Collection<VirtualPort> getPorts(TenantNetworkId networkId);
+
+ /**
+ * Returns the collection of the virtualPorts associated with the tenantId.
+ *
+ * @param tenantId the tenant identifier
+ * @return collection of virtualPorts.
+ */
+ Collection<VirtualPort> getPorts(TenantId tenantId);
+
+ /**
+ * Returns the collection of the virtualPorts associated with the deviceId.
+ *
+ * @param deviceId the device identifier
+ * @return collection of virtualPort.
+ */
+ Collection<VirtualPort> getPorts(DeviceId deviceId);
+
+ /**
+ * Creates virtualPorts by virtualPorts.
+ *
+ * @param virtualPorts the iterable collection of virtualPorts
+ * @return true if all given identifiers created successfully.
+ */
+ boolean createPorts(Iterable<VirtualPort> virtualPorts);
+
+ /**
+ * Updates virtualPorts by virtualPorts.
+ *
+ * @param virtualPorts the iterable collection of virtualPorts
+ * @return true if all given identifiers updated successfully.
+ */
+ boolean updatePorts(Iterable<VirtualPort> virtualPorts);
+
+ /**
+ * Deletes virtualPortIds by virtualPortIds.
+ *
+ * @param virtualPortIds the iterable collection of virtualPort identifiers
+ * @return true or false if one with the given identifier to delete is
+ * successfully.
+ */
+ boolean removePorts(Iterable<VirtualPortId> virtualPortIds);
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java
new file mode 100644
index 00000000..926809c9
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.virtualport.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides implementation of the VirtualPort APIs.
+ */
+@Component(immediate = true)
+@Service
+public class VirtualPortManager implements VirtualPortService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String VIRTUALPORT = "vtn-virtual-port";
+ private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+
+ private static final String VIRTUALPORT_ID_NULL = "VirtualPort ID cannot be null";
+ private static final String VIRTUALPORT_NOT_NULL = "VirtualPort cannot be null";
+ private static final String TENANTID_NOT_NULL = "TenantId cannot be null";
+ private static final String NETWORKID_NOT_NULL = "NetworkId cannot be null";
+ private static final String DEVICEID_NOT_NULL = "DeviceId cannot be null";
+
+ protected Map<VirtualPortId, VirtualPort> vPortStore;
+ protected ApplicationId appId;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TenantNetworkService networkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Activate
+ public void activate() {
+
+ appId = coreService.registerApplication(VTNRSC_APP);
+
+ vPortStore = storageService.<VirtualPortId, VirtualPort>consistentMapBuilder()
+ .withName(VIRTUALPORT)
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+ VirtualPortId.class,
+ TenantNetworkId.class,
+ VirtualPort.State.class,
+ TenantId.class,
+ AllowedAddressPair.class,
+ FixedIp.class,
+ BindingHostId.class,
+ SecurityGroup.class,
+ SubnetId.class,
+ IpAddress.class,
+ DefaultVirtualPort.class))
+ .build().asJavaMap();
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ vPortStore.clear();
+ log.info("Stoppped");
+ }
+
+ @Override
+ public boolean exists(VirtualPortId vPortId) {
+ checkNotNull(vPortId, VIRTUALPORT_ID_NULL);
+ return vPortStore.containsKey(vPortId);
+ }
+
+ @Override
+ public VirtualPort getPort(VirtualPortId vPortId) {
+ checkNotNull(vPortId, VIRTUALPORT_ID_NULL);
+ return vPortStore.get(vPortId);
+ }
+
+ @Override
+ public Collection<VirtualPort> getPorts() {
+ return Collections.unmodifiableCollection(vPortStore.values());
+ }
+
+ @Override
+ public Collection<VirtualPort> getPorts(TenantNetworkId networkId) {
+ checkNotNull(networkId, NETWORKID_NOT_NULL);
+ return vPortStore.values().stream().filter(d -> d.networkId().equals(networkId))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Collection<VirtualPort> getPorts(TenantId tenantId) {
+ checkNotNull(tenantId, TENANTID_NOT_NULL);
+ return vPortStore.values().stream().filter(d -> d.tenantId().equals(tenantId))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Collection<VirtualPort> getPorts(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICEID_NOT_NULL);
+ return vPortStore.values().stream().filter(d -> d.deviceId().equals(deviceId))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean createPorts(Iterable<VirtualPort> vPorts) {
+ checkNotNull(vPorts, VIRTUALPORT_NOT_NULL);
+ for (VirtualPort vPort : vPorts) {
+ log.debug("vPortId is {} ", vPort.portId().toString());
+ vPortStore.put(vPort.portId(), vPort);
+ if (!vPortStore.containsKey(vPort.portId())) {
+ log.debug("The virtualPort is created failed whose identifier is {} ",
+ vPort.portId().toString());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean updatePorts(Iterable<VirtualPort> vPorts) {
+ checkNotNull(vPorts, VIRTUALPORT_NOT_NULL);
+ if (vPorts != null) {
+ for (VirtualPort vPort : vPorts) {
+ vPortStore.put(vPort.portId(), vPort);
+ if (!vPortStore.containsKey(vPort.portId())) {
+ log.debug("The virtualPort is not exist whose identifier is {}",
+ vPort.portId().toString());
+ return false;
+ }
+
+ vPortStore.put(vPort.portId(), vPort);
+
+ if (!vPort.equals(vPortStore.get(vPort.portId()))) {
+ log.debug("The virtualPort is updated failed whose identifier is {}",
+ vPort.portId().toString());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean removePorts(Iterable<VirtualPortId> vPortIds) {
+ checkNotNull(vPortIds, VIRTUALPORT_ID_NULL);
+ if (vPortIds != null) {
+ for (VirtualPortId vPortId : vPortIds) {
+ vPortStore.remove(vPortId);
+ if (vPortStore.containsKey(vPortId)) {
+ log.debug("The virtualPort is removed failed whose identifier is {}",
+ vPortId.toString());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java
new file mode 100644
index 00000000..24eb0d3f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of service for interacting with the inventory of virtual ports.
+ */
+package org.onosproject.vtnrsc.virtualport.impl;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java
new file mode 100644
index 00000000..06a01a04
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of virtual ports.
+ */
+package org.onosproject.vtnrsc.virtualport;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java
new file mode 100644
index 00000000..57c97c1c
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.AllocationPool;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Subnet AllocationPool codec.
+ */
+public final class AllocationPoolsCodec extends JsonCodec<AllocationPool> {
+
+ @Override
+ public ObjectNode encode(AllocationPool alocPool, CodecContext context) {
+ checkNotNull(alocPool, "AllocationPools cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("start", alocPool.startIp().toString())
+ .put("end", alocPool.endIp().toString());
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java
new file mode 100644
index 00000000..7960808f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * VirtualPort AllowedAddressPair codec.
+ */
+public final class AllowedAddressPairCodec extends JsonCodec<AllowedAddressPair> {
+
+ @Override
+ public ObjectNode encode(AllowedAddressPair alocAddPair, CodecContext context) {
+ checkNotNull(alocAddPair, "AllowedAddressPair cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("ip_address", alocAddPair.ip().toString())
+ .put("mac_address", alocAddPair.mac().toString());
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java
new file mode 100644
index 00000000..96c9bb4e
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.FixedIp;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * VirtualPort FixedIp codec.
+ */
+public final class FixedIpCodec extends JsonCodec<FixedIp> {
+
+ @Override
+ public ObjectNode encode(FixedIp fixIp, CodecContext context) {
+ checkNotNull(fixIp, "FixedIp cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("subnet_id", fixIp.subnetId().toString())
+ .put("ip_address", fixIp.ip().toString());
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java
new file mode 100644
index 00000000..69ca6b3f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.HostRoute;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Subnet HostRoute codec.
+ */
+public final class HostRoutesCodec extends JsonCodec<HostRoute> {
+
+ @Override
+ public ObjectNode encode(HostRoute hostRoute, CodecContext context) {
+ checkNotNull(hostRoute, "HostRoute cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("nexthop", hostRoute.nexthop().toString())
+ .put("destination", hostRoute.destination().toString());
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java
new file mode 100644
index 00000000..c2ded196
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.SecurityGroup;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Virtualport SecurityGroup codec.
+ */
+public final class SecurityGroupCodec extends JsonCodec<SecurityGroup> {
+
+ @Override
+ public ObjectNode encode(SecurityGroup securGroup, CodecContext context) {
+ checkNotNull(securGroup, "SecurityGroup cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("security_group", securGroup.securityGroup());
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java
new file mode 100644
index 00000000..122b75a9
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.Subnet;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Subnet JSON codec.
+ */
+public final class SubnetCodec extends JsonCodec<Subnet> {
+ @Override
+ public ObjectNode encode(Subnet subnet, CodecContext context) {
+ checkNotNull(subnet, "Subnet cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("id", subnet.id().toString())
+ .put("gateway_ip", subnet.gatewayIp().toString())
+ .put("network_id", subnet.networkId().toString())
+ .put("name", subnet.subnetName())
+ .put("ip_version", subnet.ipVersion().toString())
+ .put("cidr", subnet.cidr().toString())
+ .put("shared", subnet.shared())
+ .put("enabled_dchp", subnet.dhcpEnabled())
+ .put("tenant_id", subnet.tenantId().toString())
+ .put("ipv6_address_mode", subnet.ipV6AddressMode() == null ? null
+ : subnet.ipV6AddressMode().toString())
+ .put("ipv6_ra_mode", subnet.ipV6RaMode() == null ? null
+ : subnet.ipV6RaMode().toString());
+ result.set("allocation_pools", new AllocationPoolsCodec().encode(subnet
+ .allocationPools(), context));
+ result.set("host_routes",
+ new HostRoutesCodec().encode(subnet.hostRoutes(), context));
+ return result;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java
new file mode 100644
index 00000000..48ba3b97
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.TenantNetwork;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * TenantNetwork JSON codec.
+ */
+public final class TenantNetworkCodec extends JsonCodec<TenantNetwork> {
+
+ @Override
+ public ObjectNode encode(TenantNetwork network, CodecContext context) {
+ checkNotNull(network, "Network cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("id", network.id().toString())
+ .put("name", network.name())
+ .put("admin_state_up", network.adminStateUp())
+ .put("status", "" + network.state())
+ .put("shared", network.shared())
+ .put("tenant_id", network.tenantId().toString())
+ .put("router:external", network.routerExternal())
+ .put("provider:network_type", "" + network.type())
+ .put("provider:physical_network", network.physicalNetwork().toString())
+ .put("provider:segmentation_id", network.segmentationId().toString());
+ return result;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java
new file mode 100644
index 00000000..e57d56bc
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.VirtualPort;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * VirtualPort JSON codec.
+ */
+public final class VirtualPortCodec extends JsonCodec<VirtualPort> {
+ @Override
+ public ObjectNode encode(VirtualPort vPort, CodecContext context) {
+ checkNotNull(vPort, "VPort cannot be null");
+ ObjectNode result = context
+ .mapper()
+ .createObjectNode()
+ .put("id", vPort.portId().toString())
+ .put("network_id", vPort.networkId().toString())
+ .put("admin_state_up", vPort.adminStateUp())
+ .put("name", vPort.name())
+ .put("status", vPort.state().toString())
+ .put("mac_address", vPort.macAddress().toString())
+ .put("tenant_id", vPort.tenantId().toString())
+ .put("device_id", vPort.deviceId().toString())
+ .put("device_owner", vPort.deviceOwner())
+ .put("binding:vnic_type", vPort.bindingVnicType())
+ .put("binding:Vif_type", vPort.bindingVifType())
+ .put("binding:host_id", vPort.bindingHostId().toString())
+ .put("binding:vif_details", vPort.bindingVifDetails());
+ result.set("allowed_address_pairs", new AllowedAddressPairCodec().encode(
+ vPort.allowedAddressPairs(), context));
+ result.set("fixed_ips", new FixedIpCodec().encode(
+ vPort.fixedIps(), context));
+ result.set("security_groups", new SecurityGroupCodec().encode(
+ vPort.securityGroups(), context));
+ return result;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java
new file mode 100644
index 00000000..34636a9f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Codecs for virtual tenant objects.
+ */
+package org.onosproject.vtnrsc.web;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..c6a9c81b
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,56 @@
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkCreateCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkQueryCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkRemoveCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkUpdateCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.subnet.SubnetCreateCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.subnet.SubnetQueryCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.subnet.SubnetRemoveCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.subnet.SubnetUpdateCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortCreateCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortQueryCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortRemoveCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortUpdateCommand"/>
+ </command>
+ </command-bundle>
+</blueprint>
diff --git a/framework/src/onos/apps/vtn/vtnweb/pom.xml b/framework/src/onos/apps/vtn/vtnweb/pom.xml
new file mode 100644
index 00000000..bcb71d9f
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnweb/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+
+ <artifactId>onos-app-vtn-web</artifactId>
+ <packaging>bundle</packaging>
+ <properties>
+ <web.context>/onos/vtn</web.context>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>jsr311-api</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-vtn-rsc</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <_wab>src/main/webapp/</_wab>
+ <Bundle-SymbolicName>
+ ${project.groupId}.${project.artifactId}
+ </Bundle-SymbolicName>
+ <Import-Package>
+ org.slf4j,
+ org.osgi.framework,
+ javax.ws.rs,
+ javax.ws.rs.core,
+ com.sun.jersey.api.core,
+ com.sun.jersey.spi.container.servlet,
+ com.sun.jersey.server.impl.container.servlet,
+ com.fasterxml.jackson.databind,
+ com.fasterxml.jackson.databind.node,
+ com.fasterxml.jackson.core,
+ org.apache.karaf.shell.commands,
+ org.apache.commons.lang.math.*,
+ com.google.common.*,
+ org.onlab.packet.*,
+ org.onlab.rest.*,
+ org.onosproject.*,
+ org.onlab.util.*,
+ org.jboss.netty.util.*
+ </Import-Package>
+ <Web-ContextPath>${web.context}</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project> \ No newline at end of file
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java
new file mode 100644
index 00000000..deb9ca37
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultAllocationPool;
+import org.onosproject.vtnrsc.DefaultHostRoute;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.Subnet.Mode;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+import org.onosproject.vtnrsc.web.SubnetCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+@Path("subnets")
+public class SubnetWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(SubnetWebResource.class);
+ public static final String SUBNET_NOT_CREATE = "Subnets is failed to create!";
+ public static final String SUBNET_NOT_FOUND = "Subnets is not found";
+ public static final String JSON_NOT_NULL = "JsonNode can not be null";
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response listSubnets() {
+ Iterable<Subnet> subnets = get(SubnetService.class).getSubnets();
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("subnets", new SubnetCodec().encode(subnets, this));
+ return ok(result.toString()).build();
+ }
+
+ @GET
+ @Path("{subnetUUID}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getSubnet(@PathParam("subnetUUID") String id) {
+
+ if (!get(SubnetService.class).exists(SubnetId.subnetId(id))) {
+ return Response.status(NOT_FOUND)
+ .entity(SUBNET_NOT_FOUND).build();
+ }
+ Subnet sub = nullIsNotFound(get(SubnetService.class)
+ .getSubnet(SubnetId.subnetId(id)),
+ SUBNET_NOT_FOUND);
+
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("subnet", new SubnetCodec().encode(sub, this));
+ return ok(result.toString()).build();
+ }
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createSubnet(final InputStream input) {
+
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode subnode = mapper.readTree(input);
+ Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode);
+ Boolean result = nullIsNotFound((get(SubnetService.class)
+ .createSubnets(subnets)),
+ SUBNET_NOT_CREATE);
+
+ if (!result) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(SUBNET_NOT_CREATE).build();
+ }
+ return Response.status(202).entity(result.toString()).build();
+ } catch (Exception e) {
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @PUT
+ @Path("{subnetUUID}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateSubnet(@PathParam("id") String id,
+ final InputStream input) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode subnode = mapper.readTree(input);
+ Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode);
+ Boolean result = nullIsNotFound(get(SubnetService.class)
+ .updateSubnets(subnets), SUBNET_NOT_FOUND);
+ if (!result) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(SUBNET_NOT_FOUND).build();
+ }
+ return Response.status(203).entity(result.toString()).build();
+ } catch (Exception e) {
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @Path("{subnetUUID}")
+ @DELETE
+ public Response deleteSingleSubnet(@PathParam("subnetUUID") String id)
+ throws IOException {
+ try {
+ SubnetId subId = SubnetId.subnetId(id);
+ Set<SubnetId> subIds = new HashSet<>();
+ subIds.add(subId);
+ get(SubnetService.class).removeSubnets(subIds);
+ return Response.status(201).entity("SUCCESS").build();
+ } catch (Exception e) {
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ private Iterable<Subnet> createOrUpdateByInputStream(JsonNode subnode) {
+ checkNotNull(subnode, JSON_NOT_NULL);
+ Iterable<Subnet> subnets = null;
+ JsonNode subnetNodes = subnode.get("subnets");
+ if (subnetNodes == null) {
+ subnetNodes = subnode.get("subnet");
+ }
+ log.debug("subnetNodes is {}", subnetNodes.toString());
+ if (subnetNodes.isArray()) {
+ subnets = changeJsonToSubs(subnetNodes);
+ } else {
+ subnets = changeJsonToSub(subnetNodes);
+ }
+ return subnets;
+ }
+
+ /**
+ * Returns a collection of subnets from subnetNodes.
+ *
+ * @param subnetNodes the subnet json node
+ * @return subnets a collection of subnets
+ */
+ public Iterable<Subnet> changeJsonToSubs(JsonNode subnetNodes) {
+ checkNotNull(subnetNodes, JSON_NOT_NULL);
+ Map<SubnetId, Subnet> subMap = new HashMap<>();
+ for (JsonNode subnetNode : subnetNodes) {
+ if (!subnetNode.hasNonNull("id")) {
+ return null;
+ }
+ SubnetId id = SubnetId.subnetId(subnetNode.get("id").asText());
+ String subnetName = subnetNode.get("name").asText();
+ TenantId tenantId = TenantId
+ .tenantId(subnetNode.get("tenant_id").asText());
+ TenantNetworkId networkId = TenantNetworkId
+ .networkId(subnetNode.get("network_id").asText());
+ String version = subnetNode.get("ip_version").asText();
+ Version ipVersion;
+ switch (version) {
+ case "4":
+ ipVersion = Version.INET;
+ break;
+ case "6":
+ ipVersion = Version.INET;
+ break;
+ default:
+ throw new IllegalArgumentException("ipVersion should be 4 or 6.");
+ }
+ IpPrefix cidr = IpPrefix.valueOf(subnetNode.get("cidr").asText());
+ IpAddress gatewayIp = IpAddress
+ .valueOf(subnetNode.get("gateway_ip").asText());
+ Boolean dhcpEnabled = subnetNode.get("enable_dhcp").asBoolean();
+ Boolean shared = subnetNode.get("shared").asBoolean();
+ JsonNode hostRoutes = subnetNode.get("host_routes");
+ Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes);
+ JsonNode allocationPools = subnetNode.get("allocation_pools");
+ Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools);
+ Mode ipV6AddressMode = Mode
+ .valueOf(subnetNode.get("ipv6_address_mode").asText());
+ Mode ipV6RaMode = Mode
+ .valueOf(subnetNode.get("ipv6_ra_mode").asText());
+ Subnet subnet = new DefaultSubnet(id, subnetName, networkId,
+ tenantId, ipVersion, cidr,
+ gatewayIp, dhcpEnabled, shared,
+ Sets.newHashSet(hostRoutesIt), ipV6AddressMode,
+ ipV6RaMode, Sets.newHashSet(allocationPoolsIt));
+ subMap.put(id, subnet);
+ }
+ return Collections.unmodifiableCollection(subMap.values());
+ }
+
+ /**
+ * Returns a collection of subnets from subnetNodes.
+ *
+ * @param subnetNodes the subnet json node
+ * @return subnets a collection of subnets
+ */
+ public Iterable<Subnet> changeJsonToSub(JsonNode subnetNodes) {
+ checkNotNull(subnetNodes, JSON_NOT_NULL);
+ checkArgument(subnetNodes.get("enable_dhcp").isBoolean(), "enable_dhcp should be boolean");
+ checkArgument(subnetNodes.get("shared").isBoolean(), "shared should be boolean");
+ Map<SubnetId, Subnet> subMap = new HashMap<>();
+ if (!subnetNodes.hasNonNull("id")) {
+ return null;
+ }
+ SubnetId id = SubnetId.subnetId(subnetNodes.get("id").asText());
+ String subnetName = subnetNodes.get("name").asText();
+ TenantId tenantId = TenantId
+ .tenantId(subnetNodes.get("tenant_id").asText());
+ TenantNetworkId networkId = TenantNetworkId
+ .networkId(subnetNodes.get("network_id").asText());
+ String version = subnetNodes.get("ip_version").asText();
+ Version ipVersion;
+ switch (version) {
+ case "4":
+ ipVersion = Version.INET;
+ break;
+ case "6":
+ ipVersion = Version.INET;
+ break;
+ default:
+ throw new IllegalArgumentException("ipVersion should be 4 or 6.");
+ }
+
+ IpPrefix cidr = IpPrefix.valueOf(subnetNodes.get("cidr").asText());
+ IpAddress gatewayIp = IpAddress
+ .valueOf(subnetNodes.get("gateway_ip").asText());
+ Boolean dhcpEnabled = subnetNodes.get("enable_dhcp").asBoolean();
+ Boolean shared = subnetNodes.get("shared").asBoolean();
+ JsonNode hostRoutes = subnetNodes.get("host_routes");
+ Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes);
+ JsonNode allocationPools = subnetNodes.get("allocation_pools");
+ Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools);
+
+ Mode ipV6AddressMode = getMode(subnetNodes.get("ipv6_address_mode")
+ .asText());
+ Mode ipV6RaMode = getMode(subnetNodes.get("ipv6_ra_mode").asText());
+
+ Subnet subnet = new DefaultSubnet(id, subnetName, networkId, tenantId,
+ ipVersion, cidr, gatewayIp,
+ dhcpEnabled, shared, Sets.newHashSet(hostRoutesIt),
+ ipV6AddressMode, ipV6RaMode,
+ Sets.newHashSet(allocationPoolsIt));
+ subMap.put(id, subnet);
+ return Collections.unmodifiableCollection(subMap.values());
+ }
+
+ /**
+ * Gets ipv6_address_mode or ipv6_ra_mode type.
+ *
+ * @param mode the String value in JsonNode
+ * @return ipV6Mode Mode of the ipV6Mode
+ */
+ private Mode getMode(String mode) {
+ Mode ipV6Mode;
+ if (mode == null) {
+ return null;
+ }
+ switch (mode) {
+ case "dhcpv6-stateful":
+ ipV6Mode = Mode.DHCPV6_STATEFUL;
+ break;
+ case "dhcpv6-stateless":
+ ipV6Mode = Mode.DHCPV6_STATELESS;
+ break;
+ case "slaac":
+ ipV6Mode = Mode.SLAAC;
+ break;
+ default:
+ ipV6Mode = null;
+ }
+ return ipV6Mode;
+ }
+
+ /**
+ * Changes JsonNode alocPools to a collection of the alocPools.
+ *
+ * @param allocationPools the allocationPools JsonNode
+ * @return a collection of allocationPools
+ */
+ public Iterable<AllocationPool> jsonNodeToAllocationPools(JsonNode allocationPools) {
+ checkNotNull(allocationPools, JSON_NOT_NULL);
+ ConcurrentMap<Integer, AllocationPool> alocplMaps = Maps
+ .newConcurrentMap();
+ Integer i = 0;
+ for (JsonNode node : allocationPools) {
+ IpAddress startIp = IpAddress.valueOf(node.get("start").asText());
+ IpAddress endIp = IpAddress.valueOf(node.get("end").asText());
+ AllocationPool alocPls = new DefaultAllocationPool(startIp, endIp);
+ alocplMaps.putIfAbsent(i, alocPls);
+ i++;
+ }
+ return Collections.unmodifiableCollection(alocplMaps.values());
+ }
+
+ /**
+ * Changes hostRoutes JsonNode to a collection of the hostRoutes.
+ *
+ * @param hostRoutes the hostRoutes json node
+ * @return a collection of hostRoutes
+ */
+ public Iterable<HostRoute> jsonNodeToHostRoutes(JsonNode hostRoutes) {
+ checkNotNull(hostRoutes, JSON_NOT_NULL);
+ ConcurrentMap<Integer, HostRoute> hostRouteMaps = Maps
+ .newConcurrentMap();
+ Integer i = 0;
+ for (JsonNode node : hostRoutes) {
+ IpAddress nexthop = IpAddress.valueOf(node.get("nexthop").asText());
+ IpPrefix destination = IpPrefix.valueOf(node.get("destination")
+ .asText());
+ HostRoute hostRoute = new DefaultHostRoute(nexthop, destination);
+ hostRouteMaps.putIfAbsent(i, hostRoute);
+ i++;
+ }
+ return Collections.unmodifiableCollection(hostRouteMaps.values());
+ }
+
+ /**
+ * Returns the specified item if that items is null; otherwise throws not
+ * found exception.
+ *
+ * @param item item to check
+ * @param <T> item type
+ * @param message not found message
+ * @return item if not null
+ * @throws org.onlab.util.ItemNotFoundException if item is null
+ */
+ protected <T> T nullIsNotFound(T item, String message) {
+ if (item == null) {
+ throw new ItemNotFoundException(message);
+ }
+ return item;
+ }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java
new file mode 100644
index 00000000..0b877822
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.OK;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.TenantNetwork.State;
+import org.onosproject.vtnrsc.TenantNetwork.Type;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.web.TenantNetworkCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+
+/**
+ * REST resource for interacting with the inventory of networks.
+ */
+@Path("networks")
+public class TenantNetworkWebResource extends AbstractWebResource {
+ public static final String NETWORK_NOT_FOUND = "Network is not found";
+ public static final String NETWORK_ID_EXIST = "Network id is existed";
+ public static final String NETWORK_ID_NOT_EXIST = "Network id is not existed";
+ public static final String CREATE_NETWORK = "create network";
+ public static final String UPDATE_NETWORK = "update network";
+ public static final String DELETE_NETWORK = "delete network";
+ public static final String JSON_NOT_NULL = "JsonNode can not be null";
+
+ protected static final Logger log = LoggerFactory
+ .getLogger(TenantNetworkWebResource.class);
+ private final ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps
+ .newConcurrentMap();
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response getNetworks(@QueryParam("id") String queryId,
+ @QueryParam("name") String queryName,
+ @QueryParam("admin_state_up") String queryadminStateUp,
+ @QueryParam("status") String querystate,
+ @QueryParam("shared") String queryshared,
+ @QueryParam("tenant_id") String querytenantId,
+ @QueryParam("router:external") String routerExternal,
+ @QueryParam("provider:network_type") String type,
+ @QueryParam("provider:physical_network") String physicalNetwork,
+ @QueryParam("provider:segmentation_id") String segmentationId) {
+ Iterable<TenantNetwork> networks = get(TenantNetworkService.class)
+ .getNetworks();
+ Iterator<TenantNetwork> networkors = networks.iterator();
+ while (networkors.hasNext()) {
+ TenantNetwork network = networkors.next();
+ if ((queryId == null || queryId.equals(network.id().toString()))
+ && (queryName == null || queryName.equals(network.name()))
+ && (queryadminStateUp == null || queryadminStateUp
+ .equals(network.adminStateUp()))
+ && (querystate == null || querystate.equals(network.state()
+ .toString()))
+ && (queryshared == null || queryshared.equals(network
+ .shared()))
+ && (querytenantId == null || querytenantId.equals(network
+ .tenantId().toString()))
+ && (routerExternal == null || routerExternal.equals(network
+ .routerExternal()))
+ && (type == null || type.equals(network.type()))
+ && (physicalNetwork == null || physicalNetwork
+ .equals(network.physicalNetwork()))
+ && (segmentationId == null || segmentationId.equals(network
+ .segmentationId()))) {
+ networksMap.putIfAbsent(network.id(), network);
+ }
+ }
+ networks = Collections.unmodifiableCollection(networksMap.values());
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("networks", new TenantNetworkCodec().encode(networks, this));
+
+ return ok(result.toString()).build();
+ }
+
+ private State isState(String state) {
+ if (state.equals("ACTIVE")) {
+ return TenantNetwork.State.ACTIVE;
+ } else if (state.equals("BUILD")) {
+ return TenantNetwork.State.BUILD;
+ } else if (state.equals("DOWN")) {
+ return TenantNetwork.State.DOWN;
+ } else if (state.equals("ERROR")) {
+ return TenantNetwork.State.ERROR;
+ } else {
+ return null;
+ }
+ }
+
+ private Type isType(String type) {
+ if (type.equals("LOCAL")) {
+ return TenantNetwork.Type.LOCAL;
+ } else {
+ return null;
+ }
+ }
+
+ @GET
+ @Path("{id}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response getNetwork(@PathParam("id") String id) {
+
+ if (!get(TenantNetworkService.class).exists(TenantNetworkId
+ .networkId(id))) {
+ return Response.status(NOT_FOUND)
+ .entity(NETWORK_NOT_FOUND).build();
+ }
+ TenantNetwork network = nullIsNotFound(get(TenantNetworkService.class)
+ .getNetwork(TenantNetworkId.networkId(id)), NETWORK_NOT_FOUND);
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("network", new TenantNetworkCodec().encode(network, this));
+
+ return ok(result.toString()).build();
+
+ }
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createNetworks(InputStream input) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ JsonNode nodes = null;
+ Iterable<TenantNetwork> networks = null;
+ if (cfg.get("network") != null) {
+ nodes = cfg.get("network");
+ if (nodes.isArray()) {
+ networks = changeJson2objs(nodes);
+ } else {
+ networks = changeJson2obj(CREATE_NETWORK, null, nodes);
+ }
+ } else if (cfg.get("networks") != null) {
+ nodes = cfg.get("networks");
+ networks = changeJson2objs(nodes);
+ }
+ Boolean issuccess = nullIsNotFound((get(TenantNetworkService.class)
+ .createNetworks(networks)),
+ NETWORK_NOT_FOUND);
+
+ if (!issuccess) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(NETWORK_ID_EXIST).build();
+ }
+ return Response.status(OK).entity(issuccess.toString()).build();
+ } catch (Exception e) {
+ log.error("Creates tenantNetwork exception {}.", e.toString());
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @PUT
+ @Path("{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateNetworks(@PathParam("id") String id, InputStream input) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ JsonNode nodes = null;
+ Iterable<TenantNetwork> networks = null;
+ if (cfg.get("network") != null) {
+ nodes = cfg.get("network");
+ if (nodes.isArray()) {
+ networks = changeJson2objs(nodes);
+ } else {
+ networks = changeJson2obj(UPDATE_NETWORK,
+ TenantNetworkId.networkId(id),
+ nodes);
+ }
+ } else if (cfg.get("networks") != null) {
+ nodes = cfg.get("networks");
+ networks = changeJson2objs(nodes);
+ }
+ Boolean issuccess = nullIsNotFound((get(TenantNetworkService.class)
+ .updateNetworks(networks)),
+ NETWORK_NOT_FOUND);
+ if (!issuccess) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(NETWORK_ID_NOT_EXIST).build();
+ }
+ return Response.status(OK).entity(issuccess.toString()).build();
+ } catch (Exception e) {
+ log.error("Updates tenantNetwork failed because of exception {}.",
+ e.toString());
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @DELETE
+ @Path("{id}")
+ public Response deleteNetworks(@PathParam("id") String id) {
+ log.debug("Deletes network by identifier {}.", id);
+ Set<TenantNetworkId> networkSet = new HashSet<>();
+ networkSet.add(TenantNetworkId.networkId(id));
+ Boolean issuccess = nullIsNotFound(get(TenantNetworkService.class)
+ .removeNetworks(networkSet), NETWORK_NOT_FOUND);
+ if (!issuccess) {
+ log.debug("Network identifier {} is not existed", id);
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(NETWORK_ID_NOT_EXIST).build();
+ }
+ return Response.status(OK).entity(issuccess.toString()).build();
+ }
+
+ /**
+ * Returns a collection of tenantNetworks.
+ *
+ * @param flag the flag
+ * @param networkId network identifier
+ * @param node the network json node
+ * @return a collection of tenantNetworks
+ */
+ public Iterable<TenantNetwork> changeJson2obj(String flag,
+ TenantNetworkId networkId,
+ JsonNode node) {
+ checkNotNull(node, JSON_NOT_NULL);
+ TenantNetwork network = null;
+ ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps
+ .newConcurrentMap();
+ if (node != null) {
+ checkArgument(node.get("admin_state_up").isBoolean(), "admin_state_up should be boolean");
+ checkArgument(node.get("shared").isBoolean(), "shared should be boolean");
+ checkArgument(node.get("router:external").isBoolean(), "router:external should be boolean");
+ String name = node.get("name").asText();
+ boolean adminStateUp = node.get("admin_state_up").asBoolean();
+ String state = node.get("status").asText();
+ boolean shared = node.get("shared").asBoolean();
+ String tenantId = node.get("tenant_id").asText();
+ boolean routerExternal = node.get("router:external").asBoolean();
+ String type = node.get("provider:network_type").asText();
+ String physicalNetwork = node.get("provider:physical_network")
+ .asText();
+ String segmentationId = node.get("provider:segmentation_id")
+ .asText();
+ TenantNetworkId id = null;
+ if (flag == CREATE_NETWORK) {
+ id = TenantNetworkId.networkId(node.get("id").asText());
+ } else if (flag == UPDATE_NETWORK) {
+ id = networkId;
+ }
+ network = new DefaultTenantNetwork(
+ id,
+ name,
+ adminStateUp,
+ isState(state),
+ shared,
+ TenantId.tenantId(tenantId),
+ routerExternal,
+ isType(type),
+ PhysicalNetwork
+ .physicalNetwork(physicalNetwork),
+ SegmentationId
+ .segmentationId(segmentationId));
+ networksMap.putIfAbsent(id, network);
+ }
+ return Collections.unmodifiableCollection(networksMap.values());
+ }
+
+ /**
+ * Returns a collection of tenantNetworks.
+ *
+ * @param nodes the network jsonnodes
+ * @return a collection of tenantNetworks
+ */
+ public Iterable<TenantNetwork> changeJson2objs(JsonNode nodes) {
+ checkNotNull(nodes, JSON_NOT_NULL);
+ TenantNetwork network = null;
+ ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps
+ .newConcurrentMap();
+ if (nodes != null) {
+ for (JsonNode node : nodes) {
+ String id = node.get("id").asText();
+ String name = node.get("name").asText();
+ boolean adminStateUp = node.get("admin_state_up").asBoolean();
+ String state = node.get("status").asText();
+ boolean shared = node.get("shared").asBoolean();
+ String tenantId = node.get("tenant_id").asText();
+ boolean routerExternal = node.get("router:external")
+ .asBoolean();
+ String type = node.get("provider:network_type").asText();
+ String physicalNetwork = node.get("provider:physical_network")
+ .asText();
+ String segmentationId = node.get("provider:segmentation_id")
+ .asText();
+ network = new DefaultTenantNetwork(
+ TenantNetworkId
+ .networkId(id),
+ name,
+ adminStateUp,
+ isState(state),
+ shared,
+ TenantId.tenantId(tenantId),
+ routerExternal,
+ isType(type),
+ PhysicalNetwork
+ .physicalNetwork(physicalNetwork),
+ SegmentationId
+ .segmentationId(segmentationId));
+ networksMap.putIfAbsent(TenantNetworkId.networkId(id), network);
+ }
+ }
+ return Collections.unmodifiableCollection(networksMap.values());
+ }
+
+ /**
+ * Returns the specified item if that items is null; otherwise throws not
+ * found exception.
+ *
+ * @param item item to check
+ * @param <T> item type
+ * @param message not found message
+ * @return item if not null
+ * @throws org.onlab.util.ItemNotFoundException if item is null
+ */
+ protected <T> T nullIsNotFound(T item, String message) {
+ if (item == null) {
+ throw new ItemNotFoundException(message);
+ }
+ return item;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java
new file mode 100644
index 00000000..03d3a653
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.OK;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPort.State;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.onosproject.vtnrsc.web.VirtualPortCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * REST resource for interacting with the inventory of infrastructure
+ * virtualPort.
+ */
+@Path("ports")
+public class VirtualPortWebResource extends AbstractWebResource {
+ public static final String VPORT_NOT_FOUND = "VirtualPort is not found";
+ public static final String VPORT_ID_EXIST = "VirtualPort id is exist";
+ public static final String VPORT_ID_NOT_EXIST = "VirtualPort id is not exist";
+ public static final String JSON_NOT_NULL = "JsonNode can not be null";
+ protected static final Logger log = LoggerFactory
+ .getLogger(VirtualPortService.class);
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response getPorts() {
+ Iterable<VirtualPort> virtualPorts = get(VirtualPortService.class)
+ .getPorts();
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("ports", new VirtualPortCodec().encode(virtualPorts, this));
+ return ok(result.toString()).build();
+ }
+
+ @GET
+ @Path("{id}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response getportsById(@PathParam("id") String id) {
+
+ if (!get(VirtualPortService.class).exists(VirtualPortId.portId(id))) {
+ return Response.status(NOT_FOUND)
+ .entity(VPORT_NOT_FOUND).build();
+ }
+ VirtualPort virtualPort = nullIsNotFound(get(VirtualPortService.class)
+ .getPort(VirtualPortId.portId(id)), VPORT_NOT_FOUND);
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("port", new VirtualPortCodec().encode(virtualPort, this));
+ return ok(result.toString()).build();
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createPorts(InputStream input) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ Iterable<VirtualPort> vPorts = createOrUpdateByInputStream(cfg);
+ Boolean issuccess = nullIsNotFound(get(VirtualPortService.class)
+ .createPorts(vPorts), VPORT_NOT_FOUND);
+ if (!issuccess) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(VPORT_ID_NOT_EXIST).build();
+ }
+ return Response.status(OK).entity(issuccess.toString()).build();
+ } catch (Exception e) {
+ log.error("Creates VirtualPort failed because of exception {}",
+ e.toString());
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @Path("{portUUID}")
+ @DELETE
+ public Response deletePorts(@PathParam("portUUID") String id) {
+ Set<VirtualPortId> vPortIds = new HashSet<>();
+ try {
+ if (id != null) {
+ vPortIds.add(VirtualPortId.portId(id));
+ }
+ Boolean issuccess = nullIsNotFound(get(VirtualPortService.class)
+ .removePorts(vPortIds), VPORT_NOT_FOUND);
+ if (!issuccess) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(VPORT_ID_NOT_EXIST).build();
+ }
+ return Response.status(OK).entity(issuccess.toString()).build();
+ } catch (Exception e) {
+ log.error("Deletes VirtualPort failed because of exception {}",
+ e.toString());
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response updatePorts(@PathParam("id") String id, InputStream input) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ Iterable<VirtualPort> vPorts = createOrUpdateByInputStream(cfg);
+ Boolean issuccess = nullIsNotFound(get(VirtualPortService.class)
+ .updatePorts(vPorts), VPORT_NOT_FOUND);
+ if (!issuccess) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(VPORT_ID_NOT_EXIST).build();
+ }
+ return Response.status(OK).entity(issuccess.toString()).build();
+ } catch (Exception e) {
+ log.error("Updates failed because of exception {}", e.toString());
+ return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ /**
+ * Returns a Object of the currently known infrastructure virtualPort.
+ *
+ * @param vPortNode the virtualPort json node
+ * @return a collection of virtualPorts
+ */
+ public Iterable<VirtualPort> createOrUpdateByInputStream(JsonNode vPortNode) {
+ checkNotNull(vPortNode, JSON_NOT_NULL);
+ JsonNode vPortNodes = vPortNode.get("ports");
+ if (vPortNodes == null) {
+ vPortNodes = vPortNode.get("port");
+ }
+ if (vPortNodes.isArray()) {
+ return changeJsonToPorts(vPortNodes);
+ } else {
+ return changeJsonToPort(vPortNodes);
+ }
+ }
+
+ /**
+ * Returns the iterable collection of virtualports from subnetNodes.
+ *
+ * @param vPortNodes the virtualPort json node
+ * @return virtualPorts a collection of virtualPorts
+ */
+ public Iterable<VirtualPort> changeJsonToPorts(JsonNode vPortNodes) {
+ checkNotNull(vPortNodes, JSON_NOT_NULL);
+ Map<VirtualPortId, VirtualPort> portMap = new HashMap<>();
+ Map<String, String> strMap = new HashMap<>();
+ for (JsonNode vPortnode : vPortNodes) {
+ VirtualPortId id = VirtualPortId.portId(vPortnode.get("id")
+ .asText());
+ String name = vPortnode.get("name").asText();
+ TenantId tenantId = TenantId.tenantId(vPortnode.get("tenant_id")
+ .asText());
+ TenantNetworkId networkId = TenantNetworkId.networkId(vPortnode
+ .get("network_id").asText());
+ checkArgument(vPortnode.get("admin_state_up").isBoolean(), "admin_state_up should be boolean");
+ Boolean adminStateUp = vPortnode.get("admin_state_up").asBoolean();
+ String state = vPortnode.get("status").asText();
+ MacAddress macAddress = MacAddress.valueOf(vPortnode
+ .get("mac_address").asText());
+ DeviceId deviceId = DeviceId.deviceId(vPortnode.get("device_id")
+ .asText());
+ String deviceOwner = vPortnode.get("device_owner").asText();
+ JsonNode fixedIpNodes = vPortNodes.get("fixed_ips");
+ Set<FixedIp> fixedIps = new HashSet<>();
+ for (JsonNode fixedIpNode : fixedIpNodes) {
+ FixedIp fixedIp = jsonNodeToFixedIps(fixedIpNode);
+ fixedIps.add(fixedIp);
+ }
+
+ BindingHostId bindingHostId = BindingHostId
+ .bindingHostId(vPortnode.get("binding:host_id").asText());
+ String bindingVnicType = vPortnode.get("binding:vnic_type")
+ .asText();
+ String bindingVifType = vPortnode.get("binding:vif_type").asText();
+ String bindingVifDetails = vPortnode.get("binding:vif_details")
+ .asText();
+ JsonNode allowedAddressPairJsonNode = vPortnode
+ .get("allowed_address_pairs");
+ Collection<AllowedAddressPair> allowedAddressPairs =
+ jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode);
+ JsonNode securityGroupNode = vPortnode.get("security_groups");
+ Collection<SecurityGroup> securityGroups = jsonNodeToSecurityGroup(securityGroupNode);
+ strMap.put("name", name);
+ strMap.put("deviceOwner", deviceOwner);
+ strMap.put("bindingVnicType", bindingVnicType);
+ strMap.put("bindingVifType", bindingVifType);
+ strMap.put("bindingVifDetails", bindingVifDetails);
+ VirtualPort vPort = new DefaultVirtualPort(id, networkId,
+ adminStateUp, strMap,
+ isState(state),
+ macAddress, tenantId,
+ deviceId, fixedIps,
+ bindingHostId,
+ Sets.newHashSet(allowedAddressPairs),
+ Sets.newHashSet(securityGroups));
+ portMap.put(id, vPort);
+ }
+ return Collections.unmodifiableCollection(portMap.values());
+ }
+
+ /**
+ * Returns a collection of virtualPorts from subnetNodes.
+ *
+ * @param vPortNodes the virtualPort json node
+ * @return virtualPorts a collection of virtualPorts
+ */
+ public Iterable<VirtualPort> changeJsonToPort(JsonNode vPortNodes) {
+ checkNotNull(vPortNodes, JSON_NOT_NULL);
+ Map<VirtualPortId, VirtualPort> vportMap = new HashMap<>();
+ Map<String, String> strMap = new HashMap<>();
+ VirtualPortId id = VirtualPortId.portId(vPortNodes.get("id").asText());
+ String name = vPortNodes.get("name").asText();
+ TenantId tenantId = TenantId.tenantId(vPortNodes.get("tenant_id")
+ .asText());
+ TenantNetworkId networkId = TenantNetworkId.networkId(vPortNodes
+ .get("network_id").asText());
+ Boolean adminStateUp = vPortNodes.get("admin_state_up").asBoolean();
+ String state = vPortNodes.get("status").asText();
+ MacAddress macAddress = MacAddress.valueOf(vPortNodes
+ .get("mac_address").asText());
+ DeviceId deviceId = DeviceId.deviceId(vPortNodes.get("device_id")
+ .asText());
+ String deviceOwner = vPortNodes.get("device_owner").asText();
+ JsonNode fixedIpNodes = vPortNodes.get("fixed_ips");
+ Set<FixedIp> fixedIps = new HashSet<>();
+ for (JsonNode fixedIpNode : fixedIpNodes) {
+ FixedIp fixedIp = jsonNodeToFixedIps(fixedIpNode);
+ fixedIps.add(fixedIp);
+ }
+
+ BindingHostId bindingHostId = BindingHostId
+ .bindingHostId(vPortNodes.get("binding:host_id").asText());
+ String bindingVnicType = vPortNodes.get("binding:vnic_type").asText();
+ String bindingVifType = vPortNodes.get("binding:vif_type").asText();
+ String bindingVifDetails = vPortNodes.get("binding:vif_details")
+ .asText();
+ JsonNode allowedAddressPairJsonNode = vPortNodes
+ .get("allowed_address_pairs");
+ Collection<AllowedAddressPair> allowedAddressPairs =
+ jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode);
+ JsonNode securityGroupNode = vPortNodes.get("security_groups");
+ Collection<SecurityGroup> securityGroups = jsonNodeToSecurityGroup(securityGroupNode);
+ strMap.put("name", name);
+ strMap.put("deviceOwner", deviceOwner);
+ strMap.put("bindingVnicType", bindingVnicType);
+ strMap.put("bindingVifType", bindingVifType);
+ strMap.put("bindingVifDetails", bindingVifDetails);
+ VirtualPort vPort = new DefaultVirtualPort(id, networkId, adminStateUp,
+ strMap, isState(state),
+ macAddress, tenantId,
+ deviceId, fixedIps,
+ bindingHostId,
+ Sets.newHashSet(allowedAddressPairs),
+ Sets.newHashSet(securityGroups));
+ vportMap.put(id, vPort);
+
+ return Collections.unmodifiableCollection(vportMap.values());
+ }
+
+ /**
+ * Returns a Object of the currently known infrastructure virtualPort.
+ *
+ * @param allowedAddressPairs the allowedAddressPairs json node
+ * @return a collection of allowedAddressPair
+ */
+ public Collection<AllowedAddressPair> jsonNodeToAllowedAddressPair(JsonNode allowedAddressPairs) {
+ checkNotNull(allowedAddressPairs, JSON_NOT_NULL);
+ ConcurrentMap<Integer, AllowedAddressPair> allowMaps = Maps
+ .newConcurrentMap();
+ int i = 0;
+ for (JsonNode node : allowedAddressPairs) {
+ IpAddress ip = IpAddress.valueOf(node.get("ip_address").asText());
+ MacAddress mac = MacAddress.valueOf(node.get("mac_address")
+ .asText());
+ AllowedAddressPair allows = AllowedAddressPair
+ .allowedAddressPair(ip, mac);
+ allowMaps.put(i, allows);
+ i++;
+ }
+ log.debug("The jsonNode of allowedAddressPairallow is {}"
+ + allowedAddressPairs.toString());
+ return Collections.unmodifiableCollection(allowMaps.values());
+ }
+
+ /**
+ * Returns a collection of virtualPorts.
+ *
+ * @param securityGroups the virtualPort jsonnode
+ * @return a collection of securityGroups
+ */
+ public Collection<SecurityGroup> jsonNodeToSecurityGroup(JsonNode securityGroups) {
+ checkNotNull(securityGroups, JSON_NOT_NULL);
+ ConcurrentMap<Integer, SecurityGroup> securMaps = Maps
+ .newConcurrentMap();
+ int i = 0;
+ for (JsonNode node : securityGroups) {
+ SecurityGroup securityGroup = SecurityGroup
+ .securityGroup(node.asText());
+ securMaps.put(i, securityGroup);
+ i++;
+ }
+ return Collections.unmodifiableCollection(securMaps.values());
+ }
+
+ /**
+ * Returns a collection of fixedIps.
+ *
+ * @param fixedIpNode the fixedIp jsonnode
+ * @return a collection of SecurityGroup
+ */
+ public FixedIp jsonNodeToFixedIps(JsonNode fixedIpNode) {
+ SubnetId subnetId = SubnetId.subnetId(fixedIpNode.get("subnet_id")
+ .asText());
+ IpAddress ipAddress = IpAddress.valueOf(fixedIpNode.get("ip_address")
+ .asText());
+ FixedIp fixedIps = FixedIp.fixedIp(subnetId, ipAddress);
+ return fixedIps;
+ }
+
+ /**
+ * Returns VirtualPort State.
+ *
+ * @param state the virtualport state
+ * @return the virtualPort state
+ */
+ private State isState(String state) {
+ if (state.equals("ACTIVE")) {
+ return VirtualPort.State.ACTIVE;
+ } else {
+ return VirtualPort.State.DOWN;
+ }
+
+ }
+
+ /**
+ * Returns the specified item if that items is null; otherwise throws not
+ * found exception.
+ *
+ * @param item item to check
+ * @param <T> item type
+ * @param message not found message
+ * @return item if not null
+ * @throws org.onlab.util.ItemNotFoundException if item is null
+ */
+ protected <T> T nullIsNotFound(T item, String message) {
+ if (item == null) {
+ throw new ItemNotFoundException(message);
+ }
+ return item;
+ }
+}
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java
new file mode 100644
index 00000000..c81fc3d8
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN web that used rest to creat vtn resources.
+ */
+package org.onosproject.vtnweb.resources;
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..4cc12455
--- /dev/null
+++ b/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>VTNRSC REST API v1.0</display-name>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
+ <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
+ </init-param>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.classnames</param-name>
+ <param-value>
+ org.onosproject.vtnweb.resources.TenantNetworkWebResource,
+ org.onosproject.vtnweb.resources.SubnetWebResource,
+ org.onosproject.vtnweb.resources.VirtualPortWebResource
+ </param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+</web-app>