├── .github ├── dependabot.yml └── workflows │ ├── gerrit-verify.yaml │ └── github2gerrit.yaml ├── .gitignore ├── .gitreview ├── addresstracker ├── implementation │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── opendaylight │ │ │ │ └── l2switch │ │ │ │ └── addresstracker │ │ │ │ └── addressobserver │ │ │ │ ├── AddressObservationWriter.java │ │ │ │ ├── AddressObserverUsingArp.java │ │ │ │ ├── AddressObserverUsingIpv4.java │ │ │ │ ├── AddressObserverUsingIpv6.java │ │ │ │ └── AddressTrackerProvider.java │ │ ├── resources │ │ │ └── org │ │ │ │ └── opendaylight │ │ │ │ └── blueprint │ │ │ │ └── address-tracker.xml │ │ └── yang │ │ │ └── address-tracker-config.yang │ │ └── test │ │ └── java │ │ └── org │ │ └── opendaylight │ │ └── l2switch │ │ └── addresstracker │ │ └── addressobserver │ │ ├── AddressObservationWriterTest.java │ │ ├── AddressObserverUsingArpTest.java │ │ ├── AddressObserverUsingIpv4Test.java │ │ └── AddressObserverUsingIpv6Test.java ├── model │ ├── pom.xml │ └── src │ │ └── main │ │ └── yang │ │ └── address-tracker.yang └── pom.xml ├── arphandler ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── opendaylight │ │ │ └── l2switch │ │ │ └── arphandler │ │ │ ├── core │ │ │ ├── ArpHandlerProvider.java │ │ │ ├── ArpPacketHandler.java │ │ │ ├── PacketDispatcher.java │ │ │ └── ProactiveFloodFlowWriter.java │ │ │ ├── flow │ │ │ └── InitialFlowWriter.java │ │ │ └── inventory │ │ │ └── InventoryReader.java │ ├── resources │ │ └── org │ │ │ └── opendaylight │ │ │ └── blueprint │ │ │ └── arp-handler.xml │ └── yang │ │ └── arp-handler-config.yang │ └── test │ └── java │ └── org │ └── opendaylight │ └── l2switch │ └── arphandler │ ├── core │ ├── ArpPacketHandlerTest.java │ ├── PacketDispatcherTest.java │ └── ProactiveFloodFlowWriterTest.java │ ├── flow │ └── InitialFlowWriterTest.java │ └── inventory │ └── InventoryReaderTest.java ├── artifacts └── pom.xml ├── distribution └── karaf │ └── pom.xml ├── features ├── feature-parent │ └── pom.xml ├── features-l2switch │ └── pom.xml ├── odl-l2switch-addresstracker │ └── pom.xml ├── odl-l2switch-all │ └── pom.xml ├── odl-l2switch-arphandler │ └── pom.xml ├── odl-l2switch-hosttracker │ └── pom.xml ├── odl-l2switch-loopremover │ └── pom.xml ├── odl-l2switch-packethandler │ └── pom.xml ├── odl-l2switch-switch-rest │ └── pom.xml ├── odl-l2switch-switch-ui │ └── pom.xml ├── odl-l2switch-switch │ └── pom.xml └── pom.xml ├── hosttracker ├── implementation │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── org │ │ │ └── opendaylight │ │ │ └── l2switch │ │ │ └── hosttracker │ │ │ └── plugin │ │ │ ├── internal │ │ │ ├── ConcurrentClusterAwareHostHashMap.java │ │ │ ├── ConcurrentClusterAwareLinkHashMap.java │ │ │ ├── HostTrackerImpl.java │ │ │ ├── HostTrackerOperation.java │ │ │ ├── OperationProcessor.java │ │ │ └── SimpleAddressObserver.java │ │ │ ├── inventory │ │ │ └── Host.java │ │ │ └── util │ │ │ ├── Compare.java │ │ │ └── Utilities.java │ │ ├── resources │ │ └── org │ │ │ └── opendaylight │ │ │ └── blueprint │ │ │ └── host-tracker.xml │ │ └── yang │ │ └── host-tracker-config.yang ├── model │ ├── pom.xml │ └── src │ │ └── main │ │ └── yang │ │ └── host-tracker-service.yang └── pom.xml ├── l2switch-main ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── opendaylight │ │ │ └── l2switch │ │ │ ├── L2SwitchMainProvider.java │ │ │ ├── flow │ │ │ ├── FlowWriterService.java │ │ │ ├── FlowWriterServiceImpl.java │ │ │ ├── InitialFlowWriter.java │ │ │ └── ReactiveFlowWriter.java │ │ │ ├── inventory │ │ │ └── InventoryReader.java │ │ │ └── util │ │ │ └── InstanceIdentifierUtils.java │ ├── resources │ │ └── org │ │ │ └── opendaylight │ │ │ └── blueprint │ │ │ └── l2switch-impl.xml │ └── yang │ │ └── l2switch-config.yang │ └── test │ ├── java │ └── org │ │ └── opendaylight │ │ └── l2switch │ │ ├── flow │ │ ├── FlowWriterServiceImplTest.java │ │ ├── InitialFlowWriterTest.java │ │ ├── InventoryReaderTest.java │ │ └── ReactiveFlowWriterTest.java │ │ └── util │ │ └── InstanceIdentifierUtilsTest.java │ └── resources │ └── customtopo.py ├── loopremover ├── implementation │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── opendaylight │ │ │ │ └── l2switch │ │ │ │ └── loopremover │ │ │ │ ├── LoopRemoverProvider.java │ │ │ │ ├── flow │ │ │ │ └── InitialFlowWriter.java │ │ │ │ ├── topology │ │ │ │ ├── NetworkGraphImpl.java │ │ │ │ ├── NetworkGraphService.java │ │ │ │ └── TopologyLinkDataChangeHandler.java │ │ │ │ └── util │ │ │ │ └── InstanceIdentifierUtils.java │ │ ├── resources │ │ │ └── org │ │ │ │ └── opendaylight │ │ │ │ └── blueprint │ │ │ │ └── loop-remover.xml │ │ └── yang │ │ │ └── loop-remover-config.yang │ │ └── test │ │ └── java │ │ └── org │ │ └── opendaylight │ │ └── l2switch │ │ └── loopremover │ │ ├── flow │ │ └── InitialFlowWriterTest.java │ │ ├── topology │ │ ├── NetworkGraphImplTest.java │ │ └── TopologyLinkDataChangeHandlerTest.java │ │ └── util │ │ └── InstanceIdentifierUtilsTest.java ├── model │ ├── pom.xml │ └── src │ │ └── main │ │ └── yang │ │ └── stp-status-aware-nodeconnnector.yang └── pom.xml ├── packethandler ├── implementation │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── opendaylight │ │ │ │ └── l2switch │ │ │ │ └── packethandler │ │ │ │ ├── PacketHandlerProvider.java │ │ │ │ └── decoders │ │ │ │ ├── AbstractPacketDecoder.java │ │ │ │ ├── ArpDecoder.java │ │ │ │ ├── EthernetDecoder.java │ │ │ │ ├── IcmpDecoder.java │ │ │ │ ├── Ipv4Decoder.java │ │ │ │ ├── Ipv6Decoder.java │ │ │ │ └── utils │ │ │ │ ├── BitBufferHelper.java │ │ │ │ ├── BufferException.java │ │ │ │ ├── HexEncode.java │ │ │ │ └── NetUtils.java │ │ └── resources │ │ │ └── org │ │ │ └── opendaylight │ │ │ └── blueprint │ │ │ └── packet-handler.xml │ │ └── test │ │ └── java │ │ └── org │ │ └── opendaylight │ │ └── l2switch │ │ └── packethandler │ │ └── decoders │ │ ├── ArpDecoderTest.java │ │ ├── EthernetDecoderTest.java │ │ ├── IcmpDecoderTest.java │ │ ├── Ipv4DecoderTest.java │ │ ├── Ipv6DecoderTest.java │ │ └── utils │ │ ├── BitBufferHelperTest.java │ │ ├── HexEncodeTest.java │ │ └── NetUtilsTest.java ├── model │ ├── pom.xml │ └── src │ │ └── main │ │ └── yang │ │ ├── arp-packet.yang │ │ ├── ethernet-packet.yang │ │ ├── icmp-packet.yang │ │ ├── ipv4-packet.yang │ │ ├── ipv6-packet.yang │ │ └── packet.yang └── pom.xml ├── parent └── pom.xml └── pom.xml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # SPDX-FileCopyrightText: 2025 The Linux Foundation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | # To get started with Dependabot version updates, you'll need to specify which 6 | # package ecosystems to update and where the package manifests are located. 7 | # Please see the documentation for all configuration options: 8 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 9 | 10 | version: 2 11 | updates: 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /.github/workflows/gerrit-verify.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # SPDX-License-Identifier: Apache-2.0 3 | # SPDX-FileCopyrightText: 2024 The Linux Foundation 4 | 5 | name: Gerrit Composed Maven Verify 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | workflow_dispatch: 10 | inputs: 11 | GERRIT_BRANCH: 12 | description: "Branch that change is against" 13 | required: true 14 | type: string 15 | GERRIT_CHANGE_ID: 16 | description: "The ID for the change" 17 | required: true 18 | type: string 19 | GERRIT_CHANGE_NUMBER: 20 | description: "The Gerrit number" 21 | required: true 22 | type: string 23 | GERRIT_CHANGE_URL: 24 | description: "URL to the change" 25 | required: true 26 | type: string 27 | GERRIT_EVENT_TYPE: 28 | description: "Type of Gerrit event" 29 | required: true 30 | type: string 31 | GERRIT_PATCHSET_NUMBER: 32 | description: "The patch number for the change" 33 | required: true 34 | type: string 35 | GERRIT_PATCHSET_REVISION: 36 | description: "The revision sha" 37 | required: true 38 | type: string 39 | GERRIT_PROJECT: 40 | description: "Project in Gerrit" 41 | required: true 42 | type: string 43 | GERRIT_REFSPEC: 44 | description: "Gerrit refspec of change" 45 | required: true 46 | type: string 47 | 48 | concurrency: 49 | # yamllint disable-line rule:line-length 50 | group: compose-maven-verify-${{ github.workflow }}-${{ github.event.inputs.GERRIT_CHANGE_ID || github.run_id }} 51 | cancel-in-progress: true 52 | 53 | jobs: 54 | prepare: 55 | runs-on: ubuntu-latest 56 | steps: 57 | - name: Clear votes 58 | # yamllint disable-line rule:line-length 59 | uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8 60 | with: 61 | host: ${{ vars.GERRIT_SERVER }} 62 | username: ${{ vars.GERRIT_SSH_USER }} 63 | key: ${{ secrets.GERRIT_SSH_PRIVKEY }} 64 | known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }} 65 | gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }} 66 | gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }} 67 | vote-type: clear 68 | comment-only: true 69 | - name: Allow replication 70 | run: sleep 10s 71 | 72 | maven-verify: 73 | needs: prepare 74 | # use compose-jjb-verify from the v0.4 series of releng-reusable-workflows 75 | # yamllint disable-line rule:line-length 76 | uses: lfit/releng-reusable-workflows/.github/workflows/compose-maven-verify.yaml@main 77 | with: 78 | GERRIT_BRANCH: ${{ inputs.GERRIT_BRANCH }} 79 | GERRIT_CHANGE_ID: ${{ inputs.GERRIT_CHANGE_ID }} 80 | GERRIT_CHANGE_NUMBER: ${{ inputs.GERRIT_CHANGE_NUMBER }} 81 | GERRIT_CHANGE_URL: ${{ inputs.GERRIT_CHANGE_URL }} 82 | GERRIT_EVENT_TYPE: ${{ inputs.GERRIT_EVENT_TYPE }} 83 | GERRIT_PATCHSET_NUMBER: ${{ inputs.GERRIT_PATCHSET_NUMBER }} 84 | GERRIT_PATCHSET_REVISION: ${{ inputs.GERRIT_PATCHSET_REVISION }} 85 | GERRIT_PROJECT: ${{ inputs.GERRIT_PROJECT }} 86 | GERRIT_REFSPEC: ${{ inputs.GERRIT_REFSPEC }} 87 | JDK_VERSION: "21" 88 | MVN_VERSION: "3.9.5" 89 | MVN_PROFILES: "docker" 90 | MVN_PHASES: "clean install" 91 | # yamllint disable rule:line-length 92 | MVN_OPTS: >- 93 | -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn 94 | -Dmaven.repo.local=/tmp/r -Dorg.ops4j.pax.url.mvn.localRepository=/tmp/r 95 | -DaltDeploymentRepository=staging::default::file:"${GITHUB_WORKSPACE}"/m2repo 96 | -Dkaraf.keep.unpack 97 | # yamllint enable rule:line-length 98 | ENV_VARS: ${{ toJSON(vars) }} 99 | 100 | vote: 101 | if: ${{ always() }} 102 | # yamllint enable rule:line-length 103 | needs: [prepare, maven-verify] 104 | runs-on: ubuntu-latest 105 | steps: 106 | - name: Get conclusion 107 | # yamllint disable-line rule:line-length 108 | uses: im-open/workflow-conclusion@e4f7c4980600fbe0818173e30931d3550801b992 # v2.2.3 109 | - name: Set vote 110 | # yamllint disable-line rule:line-length 111 | uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8 112 | with: 113 | host: ${{ vars.GERRIT_SERVER }} 114 | username: ${{ vars.GERRIT_SSH_USER }} 115 | key: ${{ secrets.GERRIT_SSH_PRIVKEY }} 116 | known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }} 117 | gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }} 118 | gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }} 119 | vote-type: ${{ env.WORKFLOW_CONCLUSION }} 120 | comment-only: true 121 | -------------------------------------------------------------------------------- /.github/workflows/github2gerrit.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # SPDX-License-Identifier: Apache-2.0 3 | # Copyright 2024 The Linux Foundation 4 | 5 | name: call-github2gerrit-reusable-workflow 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | workflow_dispatch: 10 | pull_request_target: 11 | types: [opened, reopened, edited, synchronize] 12 | branches: 13 | - master 14 | 15 | concurrency: 16 | # yamllint disable-line rule:line-length 17 | group: ${{ github.workflow }}-${{ github.run_id }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | call-in-g2g-workflow: 22 | permissions: 23 | contents: read 24 | pull-requests: write 25 | uses: lfit/github2gerrit/.github/workflows/github2gerrit.yaml@main 26 | with: 27 | GERRIT_KNOWN_HOSTS: ${{ vars.GERRIT_KNOWN_HOSTS }} 28 | GERRIT_SSH_USER_G2G: ${{ vars.GERRIT_SSH_USER_G2G }} 29 | GERRIT_SSH_USER_G2G_EMAIL: ${{ vars.GERRIT_SSH_USER_G2G_EMAIL }} 30 | ORGANIZATION: ${{ vars.ORGANIZATION }} 31 | secrets: 32 | GERRIT_SSH_PRIVKEY_G2G: ${{ secrets.GERRIT_SSH_PRIVKEY_G2G }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.class 4 | *.iml 5 | **/target 6 | **/bin 7 | dist 8 | **/logs 9 | products 10 | repository 11 | workspace 12 | *~ 13 | target 14 | target-ide 15 | .classpath 16 | .project 17 | .settings 18 | xtend-gen 19 | yang-gen-code 20 | yang-gen-config 21 | .externalToolBuilders 22 | maven-eclipse.xml 23 | .DS_Store 24 | .checkstyle 25 | .fbExcludeFilterFile 26 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=git.opendaylight.org 3 | port=29418 4 | project=l2switch.git 5 | defaultbranch=master 6 | -------------------------------------------------------------------------------- /addresstracker/implementation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.l2switch 6 | l2switch-parent 7 | 0.8.0-SNAPSHOT 8 | ../../parent 9 | 10 | 11 | org.opendaylight.l2switch.addresstracker 12 | addresstracker-impl 13 | bundle 14 | 15 | 16 | 17 | org.opendaylight.mdsal 18 | mdsal-binding-api 19 | 20 | 21 | org.opendaylight.openflowplugin.model 22 | model-flow-service 23 | 24 | 25 | org.opendaylight.openflowplugin.model 26 | model-topology 27 | 28 | 29 | org.opendaylight.yangtools 30 | yang-common 31 | 32 | 33 | org.opendaylight.l2switch.packethandler 34 | packethandler-model 35 | 36 | 37 | org.opendaylight.l2switch.addresstracker 38 | addresstracker-model 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.apache.felix 46 | maven-bundle-plugin 47 | true 48 | 49 | 50 | org.opendaylight.yang.gen.v1.urn.opendaylight.packet.address.tracker.impl.rev140528 51 | * 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObserverUsingArp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.addresstracker.addressobserver; 9 | 10 | import org.opendaylight.mdsal.binding.api.NotificationService.Listener; 11 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; 12 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.arp.packet.received.packet.chain.packet.ArpPacket; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacket; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFields; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacket; 18 | 19 | /** 20 | * AddressObserver listens to ARP packets to find addresses (mac, ip) and store 21 | * these address observations for each node-connector. These packets are 22 | * returned to the network after the addresses are learned. 23 | */ 24 | public class AddressObserverUsingArp implements Listener { 25 | 26 | private final AddressObservationWriter addressObservationWriter; 27 | 28 | public AddressObserverUsingArp(AddressObservationWriter addressObservationWriter) { 29 | this.addressObservationWriter = addressObservationWriter; 30 | } 31 | 32 | /** 33 | * The handler function for ARP packets. 34 | * 35 | * @param packetReceived 36 | * The incoming packet. 37 | */ 38 | @Override 39 | public void onNotification(ArpPacketReceived packetReceived) { 40 | if (packetReceived == null || packetReceived.getPacketChain() == null) { 41 | return; 42 | } 43 | 44 | RawPacketFields rawPacket = null; 45 | EthernetPacket ethernetPacket = null; 46 | ArpPacket arpPacket = null; 47 | for (PacketChain packetChain : packetReceived.getPacketChain()) { 48 | if (packetChain.getPacket() instanceof RawPacket) { 49 | rawPacket = ((RawPacket) packetChain.getPacket()).getRawPacketFields(); 50 | } else if (packetChain.getPacket() instanceof EthernetPacket) { 51 | ethernetPacket = (EthernetPacket) packetChain.getPacket(); 52 | } else if (packetChain.getPacket() instanceof ArpPacket) { 53 | arpPacket = (ArpPacket) packetChain.getPacket(); 54 | } 55 | } 56 | if (rawPacket == null || ethernetPacket == null || arpPacket == null) { 57 | return; 58 | } 59 | 60 | addressObservationWriter.addAddress(ethernetPacket.getSourceMac(), 61 | IetfInetUtil.ipAddressFor(arpPacket.getSourceProtocolAddress()), rawPacket.getIngress()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObserverUsingIpv4.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.addresstracker.addressobserver; 9 | 10 | import org.opendaylight.mdsal.binding.api.NotificationService.Listener; 11 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; 12 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacket; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFields; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacket; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv4.rev140528.Ipv4PacketReceived; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv4.rev140528.ipv4.packet.received.packet.chain.packet.Ipv4Packet; 18 | 19 | 20 | /** 21 | * AddressObserver listens to IPv4 packets to find addresses (mac, ip) and store 22 | * these address observations for each node-connector. These packets are 23 | * returned to the network after the addresses are learned. 24 | */ 25 | public class AddressObserverUsingIpv4 implements Listener { 26 | private static final String IPV4_IP_TO_IGNORE = "0.0.0.0"; 27 | 28 | private final AddressObservationWriter addressObservationWriter; 29 | 30 | public AddressObserverUsingIpv4(AddressObservationWriter addressObservationWriter) { 31 | this.addressObservationWriter = addressObservationWriter; 32 | } 33 | 34 | /** 35 | * The handler function for IPv4 packets. 36 | * 37 | * @param packetReceived 38 | * The incoming packet. 39 | */ 40 | @Override 41 | public void onNotification(Ipv4PacketReceived packetReceived) { 42 | if (packetReceived == null || packetReceived.getPacketChain() == null) { 43 | return; 44 | } 45 | 46 | RawPacketFields rawPacket = null; 47 | EthernetPacket ethernetPacket = null; 48 | Ipv4Packet ipv4Packet = null; 49 | for (PacketChain packetChain : packetReceived.getPacketChain()) { 50 | if (packetChain.getPacket() instanceof RawPacket) { 51 | rawPacket = ((RawPacket) packetChain.getPacket()).getRawPacketFields(); 52 | } else if (packetChain.getPacket() instanceof EthernetPacket) { 53 | ethernetPacket = (EthernetPacket) packetChain.getPacket(); 54 | } else if (packetChain.getPacket() instanceof Ipv4Packet) { 55 | ipv4Packet = (Ipv4Packet) packetChain.getPacket(); 56 | } 57 | } 58 | if (rawPacket == null || ethernetPacket == null || ipv4Packet == null) { 59 | return; 60 | } 61 | 62 | if (!IPV4_IP_TO_IGNORE.equals(ipv4Packet.getSourceIpv4().getValue())) { 63 | addressObservationWriter.addAddress(ethernetPacket.getSourceMac(), 64 | IetfInetUtil.ipAddressFor(ipv4Packet.getSourceIpv4().getValue()), rawPacket.getIngress()); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObserverUsingIpv6.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.addresstracker.addressobserver; 9 | 10 | import org.opendaylight.mdsal.binding.api.NotificationService.Listener; 11 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; 12 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacket; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFields; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacket; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv6.rev140528.Ipv6PacketReceived; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv6.rev140528.ipv6.packet.received.packet.chain.packet.Ipv6Packet; 18 | 19 | /** 20 | * AddressObserver listens to IPv6 packets to find addresses (mac, ip) and store 21 | * these address observations for each node-connector. These packets are 22 | * returned to the network after the addresses are learned. 23 | */ 24 | public class AddressObserverUsingIpv6 implements Listener { 25 | private static final String IPV6_IP_TO_IGNORE = "0:0:0:0:0:0:0:0"; 26 | 27 | private final AddressObservationWriter addressObservationWriter; 28 | 29 | public AddressObserverUsingIpv6(AddressObservationWriter addressObservationWriter) { 30 | this.addressObservationWriter = addressObservationWriter; 31 | } 32 | 33 | /** 34 | * The handler function for IPv6 packets. 35 | * 36 | * @param packetReceived 37 | * The incoming packet. 38 | */ 39 | @Override 40 | public void onNotification(Ipv6PacketReceived packetReceived) { 41 | if (packetReceived == null || packetReceived.getPacketChain() == null) { 42 | return; 43 | } 44 | 45 | RawPacketFields rawPacket = null; 46 | EthernetPacket ethernetPacket = null; 47 | Ipv6Packet ipv6Packet = null; 48 | for (PacketChain packetChain : packetReceived.getPacketChain()) { 49 | if (packetChain.getPacket() instanceof RawPacket) { 50 | rawPacket = ((RawPacket) packetChain.getPacket()).getRawPacketFields(); 51 | } else if (packetChain.getPacket() instanceof EthernetPacket) { 52 | ethernetPacket = (EthernetPacket) packetChain.getPacket(); 53 | } else if (packetChain.getPacket() instanceof Ipv6Packet) { 54 | ipv6Packet = (Ipv6Packet) packetChain.getPacket(); 55 | } 56 | } 57 | if (rawPacket == null || ethernetPacket == null || ipv6Packet == null) { 58 | return; 59 | } 60 | 61 | if (!IPV6_IP_TO_IGNORE.equals(ipv6Packet.getSourceIpv6().getValue())) { 62 | addressObservationWriter.addAddress(ethernetPacket.getSourceMac(), 63 | IetfInetUtil.ipAddressFor(ipv6Packet.getSourceIpv6().getValue()), rawPacket.getIngress()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressTrackerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Inocybe and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.addresstracker.addressobserver; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashSet; 12 | import java.util.List; 13 | import java.util.Set; 14 | import org.eclipse.jdt.annotation.NonNull; 15 | import org.opendaylight.mdsal.binding.api.DataBroker; 16 | import org.opendaylight.mdsal.binding.api.NotificationService; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.address.tracker.config.rev160621.AddressTrackerConfig; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv4.rev140528.Ipv4PacketReceived; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv6.rev140528.Ipv6PacketReceived; 21 | import org.opendaylight.yangtools.concepts.Registration; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | public class AddressTrackerProvider { 26 | private static final Logger LOG = LoggerFactory.getLogger(AddressTrackerProvider.class); 27 | private static final String ARP_PACKET_TYPE = "arp"; 28 | private static final String IPV4_PACKET_TYPE = "ipv4"; 29 | private static final String IPV6_PACKET_TYPE = "ipv6"; 30 | 31 | private final List listenerRegistrations = new ArrayList<>(); 32 | private final NotificationService notificationService; 33 | private final DataBroker dataBroker; 34 | private final long timestampUpdateInterval; 35 | private final String observerAddressesFrom; 36 | 37 | public AddressTrackerProvider(final DataBroker dataBroker, final NotificationService notificationService, 38 | final AddressTrackerConfig config) { 39 | this.notificationService = notificationService; 40 | this.dataBroker = dataBroker; 41 | this.timestampUpdateInterval = config.getTimestampUpdateInterval().longValue(); 42 | this.observerAddressesFrom = config.getObserveAddressesFrom(); 43 | } 44 | 45 | public void init() { 46 | // Setup AddressObserver & AddressObservationWriter 47 | AddressObservationWriter addressObservationWriter = new AddressObservationWriter(dataBroker); 48 | addressObservationWriter.setTimestampUpdateInterval(timestampUpdateInterval); 49 | Set packetTypes = processObserveAddressesFrom(observerAddressesFrom); 50 | 51 | if (packetTypes.isEmpty()) { // set default to arp 52 | packetTypes = new HashSet<>(); 53 | packetTypes.add(ARP_PACKET_TYPE); 54 | } 55 | 56 | if (packetTypes.contains(ARP_PACKET_TYPE)) { 57 | // Register AddressObserver for notifications 58 | this.listenerRegistrations.add(notificationService.registerListener(ArpPacketReceived.class, 59 | new AddressObserverUsingArp(addressObservationWriter))); 60 | } 61 | 62 | if (packetTypes.contains(IPV4_PACKET_TYPE)) { 63 | // Register AddressObserver for notifications 64 | this.listenerRegistrations.add(notificationService.registerListener(Ipv4PacketReceived.class, 65 | new AddressObserverUsingIpv4(addressObservationWriter))); 66 | } 67 | if (packetTypes.contains(IPV6_PACKET_TYPE)) { 68 | // Register AddressObserver for notifications 69 | this.listenerRegistrations.add(notificationService.registerListener(Ipv6PacketReceived.class, 70 | new AddressObserverUsingIpv6(addressObservationWriter))); 71 | } 72 | LOG.info("AddressTracker initialized."); 73 | } 74 | 75 | public void close() { 76 | listenerRegistrations.forEach(reg -> reg.close()); 77 | LOG.info("AddressTracker torn down."); 78 | } 79 | 80 | private static @NonNull Set processObserveAddressesFrom(String observeAddressesFrom) { 81 | Set packetTypes = new HashSet<>(); 82 | if (observeAddressesFrom == null || observeAddressesFrom.isEmpty()) { 83 | packetTypes.add(ARP_PACKET_TYPE); 84 | return packetTypes; 85 | } 86 | String[] observeAddressFromSplit = observeAddressesFrom.split(","); 87 | if (observeAddressFromSplit.length == 0) { 88 | packetTypes.add(ARP_PACKET_TYPE); 89 | return packetTypes; 90 | } 91 | for (String packetType : observeAddressFromSplit) { 92 | packetTypes.add(packetType); 93 | } 94 | return packetTypes; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/main/resources/org/opendaylight/blueprint/address-tracker.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 9 | 10 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/main/yang/address-tracker-config.yang: -------------------------------------------------------------------------------- 1 | module address-tracker-config { 2 | 3 | yang-version 1; 4 | namespace "urn:opendaylight:packet:address-tracker-config"; 5 | prefix "address-tracker-config"; 6 | 7 | description 8 | "This module contains the base configuration for address-tracker."; 9 | 10 | revision 2016-06-21 { 11 | description "Initial module draft."; 12 | } 13 | 14 | container address-tracker-config { 15 | leaf timestamp-update-interval { 16 | type uint32; 17 | default 600000; 18 | description "Value is in milliseconds. In case if you are using host-expiry 19 | feature, before modifying timestamp-update-interval please consider 20 | the host-purge-age value in hosttracker module, also consider 21 | default flow idletimeout/hardtimeout values installed by l2switch 22 | in l2switch-main module"; 23 | } 24 | leaf observe-addresses-from { 25 | type string; 26 | default "arp"; 27 | description "All possible values for comma separated values e.g. arp,ipv,ipv6"; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.addresstracker.addressobserver; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.Mockito.mock; 12 | import static org.mockito.Mockito.times; 13 | import static org.mockito.Mockito.verify; 14 | import static org.mockito.Mockito.when; 15 | 16 | import com.google.common.util.concurrent.FluentFuture; 17 | import java.util.Map; 18 | import java.util.Optional; 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | import org.opendaylight.mdsal.binding.api.DataBroker; 22 | import org.opendaylight.mdsal.binding.api.ReadTransaction; 23 | import org.opendaylight.mdsal.binding.api.WriteTransaction; 24 | import org.opendaylight.mdsal.common.api.LogicalDatastoreType; 25 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; 26 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; 27 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.AddressCapableNodeConnector; 29 | import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.Addresses; 30 | import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.AddressesKey; 31 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 32 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 33 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; 34 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 35 | import org.opendaylight.yangtools.util.concurrent.FluentFutures; 36 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 37 | 38 | public class AddressObservationWriterTest { 39 | 40 | private AddressCapableNodeConnector addrCapableNc; 41 | private ReadTransaction readTransaction; 42 | private WriteTransaction writeTransaction; 43 | private DataBroker dataService; 44 | private FluentFuture> checkedFuture; 45 | private NodeConnector nodeConnector; 46 | private Addresses address; 47 | private AddressesKey addrKey; 48 | private MacAddress macAddress; 49 | private IpAddress ipAddress; 50 | private NodeConnectorRef realNcRef; 51 | 52 | @Before 53 | public void init() throws Exception { 54 | macAddress = new MacAddress("ba:43:52:ce:09:f4"); 55 | ipAddress = new IpAddress(new Ipv4Address("10.0.0.1")); 56 | realNcRef = new NodeConnectorRef( 57 | InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); 58 | 59 | readTransaction = mock(ReadTransaction.class); 60 | dataService = mock(DataBroker.class); 61 | nodeConnector = mock(NodeConnector.class); 62 | when(dataService.newReadOnlyTransaction()).thenReturn(readTransaction); 63 | checkedFuture = FluentFutures.immediateFluentFuture(Optional.of(nodeConnector)); 64 | when(readTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) 65 | .thenReturn(checkedFuture); 66 | 67 | addrCapableNc = mock(AddressCapableNodeConnector.class); 68 | when(nodeConnector.augmentation(AddressCapableNodeConnector.class)).thenReturn(addrCapableNc); 69 | 70 | address = mock(Addresses.class); 71 | when(address.getIp()).thenReturn(ipAddress); 72 | when(address.getMac()).thenReturn(macAddress); 73 | when(address.getLastSeen()).thenReturn(1410350400L); 74 | when(address.getFirstSeen()).thenReturn(1410350400L); 75 | addrKey = mock(AddressesKey.class); 76 | when(address.key()).thenReturn(addrKey); 77 | when(addrCapableNc.getAddresses()).thenReturn(Map.of(addrKey, address)); 78 | 79 | writeTransaction = mock(WriteTransaction.class); 80 | when(dataService.newWriteOnlyTransaction()).thenReturn(writeTransaction); 81 | when(writeTransaction.commit()).thenReturn(mock(FluentFuture.class)); 82 | } 83 | 84 | @Test 85 | public void addAddressTest() throws Exception { 86 | AddressObservationWriter addressObservationWriter = new AddressObservationWriter(dataService); 87 | addressObservationWriter.setTimestampUpdateInterval(20L); 88 | addressObservationWriter.addAddress(macAddress, ipAddress, realNcRef); 89 | verify(readTransaction, times(1)).read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); 90 | verify(readTransaction, times(1)).close(); 91 | verify(writeTransaction, times(1)).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), 92 | any(AddressCapableNodeConnector.class)); 93 | verify(writeTransaction, times(1)).commit(); 94 | } 95 | 96 | @Test 97 | public void addAddressNullTest() throws Exception { 98 | 99 | AddressObservationWriter addressObservationWriter = new AddressObservationWriter(dataService); 100 | addressObservationWriter.setTimestampUpdateInterval(20L); 101 | addressObservationWriter.addAddress(macAddress, null, realNcRef); 102 | verify(readTransaction, times(0)).read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); 103 | verify(readTransaction, times(0)).close(); 104 | verify(writeTransaction, times(0)).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), 105 | any(AddressCapableNodeConnector.class)); 106 | verify(writeTransaction, times(0)).commit(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObserverUsingArpTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.addresstracker.addressobserver; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.Mockito.mock; 12 | import static org.mockito.Mockito.times; 13 | import static org.mockito.Mockito.verify; 14 | 15 | import java.util.ArrayList; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; 19 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 22 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceivedBuilder; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.arp.packet.received.packet.chain.packet.ArpPacketBuilder; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChainBuilder; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacketBuilder; 27 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFieldsBuilder; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacketBuilder; 29 | 30 | public class AddressObserverUsingArpTest { 31 | 32 | private AddressObservationWriter addressObservationWriterMock; 33 | 34 | @Before 35 | public void init() { 36 | addressObservationWriterMock = mock(AddressObservationWriter.class); 37 | } 38 | 39 | @Test 40 | public void onArpPacketReceivedTest() throws Exception { 41 | ArrayList packetChainList = new ArrayList<>(); 42 | packetChainList.add(new PacketChainBuilder() 43 | .setPacket(new RawPacketBuilder().setRawPacketFields(new RawPacketFieldsBuilder().build()).build()) 44 | .build()); 45 | packetChainList.add(new PacketChainBuilder() 46 | .setPacket(new EthernetPacketBuilder().setSourceMac(new MacAddress("aa:bb:cc:dd:ee:ff")).build()) 47 | .build()); 48 | packetChainList.add(new PacketChainBuilder() 49 | .setPacket(new ArpPacketBuilder().setSourceProtocolAddress("1.2.3.4").build()).build()); 50 | 51 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 52 | AddressObserverUsingArp addressOberserverArp = new AddressObserverUsingArp(addressObservationWriterMock); 53 | addressOberserverArp.onNotification(arpReceived); 54 | 55 | verify(addressObservationWriterMock, times(1)).addAddress(any(MacAddress.class), any(IpAddress.class), any()); 56 | } 57 | 58 | @Test 59 | public void onArpPacketReceivedNullInputTest1() throws Exception { 60 | 61 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(null).build(); 62 | AddressObserverUsingArp addressOberserverArp = new AddressObserverUsingArp(addressObservationWriterMock); 63 | addressOberserverArp.onNotification(arpReceived); 64 | 65 | verify(addressObservationWriterMock, times(0)).addAddress(any(MacAddress.class), any(IpAddress.class), 66 | any(NodeConnectorRef.class)); 67 | } 68 | 69 | @Test 70 | public void onArpPacketReceivedNullInputTest2() throws Exception { 71 | 72 | ArrayList packetChainList = new ArrayList<>(); 73 | packetChainList.add(new PacketChainBuilder().setPacket(new RawPacketBuilder().build()).build()); 74 | packetChainList.add(new PacketChainBuilder() 75 | .setPacket(new EthernetPacketBuilder().setSourceMac(new MacAddress("aa:bb:cc:dd:ee:ff")).build()) 76 | .build()); 77 | 78 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 79 | AddressObserverUsingArp addressOberserverArp = new AddressObserverUsingArp(addressObservationWriterMock); 80 | addressOberserverArp.onNotification(arpReceived); 81 | 82 | verify(addressObservationWriterMock, times(0)).addAddress(any(MacAddress.class), any(IpAddress.class), 83 | any(NodeConnectorRef.class)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /addresstracker/model/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.l2switch 6 | l2switch-parent 7 | 0.8.0-SNAPSHOT 8 | ../../parent 9 | 10 | org.opendaylight.l2switch.addresstracker 11 | addresstracker-model 12 | bundle 13 | 14 | 15 | 16 | org.opendaylight.openflowplugin.model 17 | model-inventory 18 | 19 | 20 | org.opendaylight.yangtools 21 | yang-common 22 | 23 | 24 | org.opendaylight.mdsal.binding.model.ietf 25 | rfc6991-ietf-yang-types 26 | 27 | 28 | org.opendaylight.l2switch.packethandler 29 | packethandler-model 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.felix 37 | maven-bundle-plugin 38 | true 39 | 40 | 41 | ${project.groupId}.${project.artifactId} 42 | org.opendaylight.yangtools.yang.binding.annotations, * 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /addresstracker/model/src/main/yang/address-tracker.yang: -------------------------------------------------------------------------------- 1 | module address-tracker { 2 | yang-version 1; 3 | namespace "urn:opendaylight:address-tracker"; 4 | prefix address-tracker; 5 | 6 | import yang-ext { 7 | prefix ext; 8 | revision-date "2013-07-09"; 9 | } 10 | import ietf-inet-types { 11 | prefix inet; 12 | revision-date 2013-07-15; 13 | } 14 | import ietf-yang-types { 15 | prefix yang; 16 | revision-date 2013-07-15; 17 | } 18 | import opendaylight-inventory { 19 | prefix inv; 20 | revision-date 2013-08-19; 21 | } 22 | import ethernet-packet { 23 | prefix ethernet; 24 | revision-date 2014-05-28; 25 | } 26 | 27 | description 28 | "Address Tracker Data Model"; 29 | 30 | revision 2014-06-17 { 31 | description 32 | "Address Tracker module draft."; 33 | } 34 | 35 | grouping address-node-connector { 36 | list addresses { 37 | key id; 38 | leaf id { 39 | description "A 64-bit key for this observation. This is opaque and should not be interpreted."; 40 | type uint64; 41 | } 42 | leaf mac { 43 | type yang:mac-address; 44 | description "MAC address"; 45 | } 46 | leaf ip { 47 | type inet:ip-address; 48 | description "IPv4 or IPv6 address"; 49 | } 50 | leaf vlan { 51 | type ethernet:vlan-id; 52 | description "VLAN id"; 53 | } 54 | leaf first-seen { 55 | type int64; 56 | description "Timestamp (number of ms since January 1, 1970, 00:00:00 GMT) of observing this address for the first time"; 57 | } 58 | leaf last-seen { 59 | type int64; 60 | description "The most recent timestamp (tnumber of ms since January 1, 1970, 00:00:00 GMT) of observing this address"; 61 | } 62 | } 63 | } 64 | 65 | augment "/inv:nodes/inv:node/inv:node-connector" { 66 | ext:augment-identifier "address-capable-node-connector"; 67 | uses address-node-connector; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /addresstracker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.opendaylight.l2switch 9 | l2switch-parent 10 | 0.8.0-SNAPSHOT 11 | ../parent 12 | 13 | 14 | addresstracker.aggregator 15 | org.opendaylight.l2switch.addresstracker 16 | pom 17 | 18 | 19 | model 20 | implementation 21 | 22 | 23 | -------------------------------------------------------------------------------- /arphandler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | org.opendaylight.l2switch 7 | l2switch-parent 8 | 0.8.0-SNAPSHOT 9 | ../parent 10 | 11 | 4.0.0 12 | 13 | org.opendaylight.l2switch.arphandler 14 | arphandler-impl 15 | bundle 16 | 17 | 18 | 19 | com.github.spotbugs 20 | spotbugs-annotations 21 | true 22 | 23 | 24 | net.sf.jung 25 | jung-api 26 | 27 | 28 | org.opendaylight.l2switch.packethandler 29 | packethandler-model 30 | 31 | 32 | org.opendaylight.l2switch.addresstracker 33 | addresstracker-model 34 | 35 | 36 | org.opendaylight.l2switch.loopremover 37 | loopremover-model 38 | 39 | 40 | org.opendaylight.mdsal 41 | mdsal-binding-api 42 | 43 | 44 | org.opendaylight.openflowplugin 45 | openflowplugin-api 46 | 47 | 48 | org.opendaylight.openflowplugin.model 49 | model-flow-service 50 | 51 | 52 | org.opendaylight.openflowplugin.model 53 | model-topology 54 | 55 | 56 | org.opendaylight.yangtools 57 | yang-common 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.apache.felix 65 | maven-bundle-plugin 66 | true 67 | 68 | 69 | org.opendaylight.yang.gen.v1.urn.opendaylight.packet.packet.handler.impl.rev140528 70 | * 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ArpHandlerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Inocybe and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.arphandler.core; 9 | 10 | import org.opendaylight.l2switch.arphandler.flow.InitialFlowWriter; 11 | import org.opendaylight.l2switch.arphandler.inventory.InventoryReader; 12 | import org.opendaylight.mdsal.binding.api.DataBroker; 13 | import org.opendaylight.mdsal.binding.api.NotificationService; 14 | import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.handler.config.rev140528.ArpHandlerConfig; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.EthernetPacketReceived; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacket; 20 | import org.opendaylight.yangtools.concepts.Registration; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | public class ArpHandlerProvider { 25 | private static final Logger LOG = LoggerFactory.getLogger(ArpHandlerProvider.class); 26 | private Registration listenerRegistration; 27 | private Registration floodTopoListenerReg; 28 | private Registration floodInvListenerReg; 29 | private Registration topoNodeListenerReg; 30 | 31 | private final NotificationService notificationService; 32 | private final DataBroker dataBroker; 33 | private final RpcConsumerRegistry rpcService; 34 | private final ArpHandlerConfig arpHandlerConfig; 35 | 36 | public ArpHandlerProvider(final DataBroker dataBroker, final NotificationService notificationProviderService, 37 | final RpcConsumerRegistry rpcService, final ArpHandlerConfig config) { 38 | this.notificationService = notificationProviderService; 39 | this.dataBroker = dataBroker; 40 | this.rpcService = rpcService; 41 | this.arpHandlerConfig = config; 42 | } 43 | 44 | public void init() { 45 | if (arpHandlerConfig.getIsProactiveFloodMode()) { 46 | //Setup proactive flow writer, which writes flood flows 47 | LOG.info("ArpHandler is in Proactive Flood Mode"); 48 | ProactiveFloodFlowWriter floodFlowWriter = new ProactiveFloodFlowWriter(dataBroker, 49 | rpcService.getRpc(AddFlow.class)); 50 | floodFlowWriter.setFlowTableId(arpHandlerConfig.getFloodFlowTableId()); 51 | floodFlowWriter.setFlowPriority(arpHandlerConfig.getFloodFlowPriority()); 52 | floodFlowWriter.setFlowIdleTimeout(arpHandlerConfig.getFloodFlowIdleTimeout()); 53 | floodFlowWriter.setFlowHardTimeout(arpHandlerConfig.getFloodFlowHardTimeout()); 54 | floodFlowWriter.setFlowInstallationDelay(arpHandlerConfig.getFloodFlowInstallationDelay().toJava()); 55 | floodTopoListenerReg = floodFlowWriter.registerAsDataChangeListener(); 56 | floodInvListenerReg = notificationService.registerListener(EthernetPacketReceived.class, floodFlowWriter); 57 | } else { 58 | //Write initial flows to send arp to controller 59 | LOG.info("ArpHandler is in Reactive Mode"); 60 | InitialFlowWriter initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); 61 | initialFlowWriter.setFlowTableId(arpHandlerConfig.getArpFlowTableId()); 62 | initialFlowWriter.setFlowPriority(arpHandlerConfig.getArpFlowPriority()); 63 | initialFlowWriter.setFlowIdleTimeout(arpHandlerConfig.getArpFlowIdleTimeout()); 64 | initialFlowWriter.setFlowHardTimeout(arpHandlerConfig.getArpFlowHardTimeout()); 65 | initialFlowWriter.setIsHybridMode(arpHandlerConfig.getIsHybridMode()); 66 | topoNodeListenerReg = initialFlowWriter.registerAsDataChangeListener(dataBroker); 67 | 68 | // Setup InventoryReader 69 | InventoryReader inventoryReader = new InventoryReader(dataBroker); 70 | 71 | // Setup PacketDispatcher 72 | PacketDispatcher packetDispatcher = new PacketDispatcher(inventoryReader, 73 | rpcService.getRpc(TransmitPacket.class)); 74 | 75 | // Setup ArpPacketHandler 76 | ArpPacketHandler arpPacketHandler = new ArpPacketHandler(packetDispatcher); 77 | 78 | // Register ArpPacketHandler 79 | this.listenerRegistration = notificationService.registerListener(ArpPacketReceived.class, arpPacketHandler); 80 | } 81 | LOG.info("ArpHandler initialized."); 82 | } 83 | 84 | public void close() throws Exception { 85 | if (listenerRegistration != null) { 86 | listenerRegistration.close(); 87 | } 88 | if (floodTopoListenerReg != null) { 89 | floodTopoListenerReg.close(); 90 | } 91 | if (floodInvListenerReg != null) { 92 | floodInvListenerReg.close(); 93 | } 94 | if (topoNodeListenerReg != null) { 95 | topoNodeListenerReg.close(); 96 | } 97 | LOG.info("ArpHandler (instance {}) torn down.", this); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ArpPacketHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.arphandler.core; 9 | 10 | import org.opendaylight.mdsal.binding.api.NotificationService.Listener; 11 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 12 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.arp.packet.received.packet.chain.packet.ArpPacket; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacket; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFields; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacket; 17 | 18 | /** 19 | * ArpPacketHandler listens for incoming ARP packets and processes them. 20 | */ 21 | public class ArpPacketHandler implements Listener { 22 | 23 | private final PacketDispatcher packetDispatcher; 24 | 25 | public ArpPacketHandler(PacketDispatcher packetDispatcher) { 26 | this.packetDispatcher = packetDispatcher; 27 | } 28 | 29 | /** 30 | * The handler function for ARP packets. 31 | * 32 | * @param packetReceived 33 | * The incoming packet. 34 | */ 35 | @Override 36 | public void onNotification(ArpPacketReceived packetReceived) { 37 | if (packetReceived == null || packetReceived.getPacketChain() == null) { 38 | return; 39 | } 40 | 41 | RawPacketFields rawPacket = null; 42 | EthernetPacket ethernetPacket = null; 43 | ArpPacket arpPacket = null; 44 | for (PacketChain packetChain : packetReceived.getPacketChain()) { 45 | if (packetChain.getPacket() instanceof RawPacket) { 46 | rawPacket = ((RawPacket) packetChain.getPacket()).getRawPacketFields(); 47 | } else if (packetChain.getPacket() instanceof EthernetPacket) { 48 | ethernetPacket = (EthernetPacket) packetChain.getPacket(); 49 | } else if (packetChain.getPacket() instanceof ArpPacket) { 50 | arpPacket = (ArpPacket) packetChain.getPacket(); 51 | } 52 | } 53 | if (rawPacket == null || ethernetPacket == null || arpPacket == null) { 54 | return; 55 | } 56 | 57 | packetDispatcher.dispatchPacket(packetReceived.getPayload(), rawPacket.getIngress(), 58 | ethernetPacket.getSourceMac(), ethernetPacket.getDestinationMac()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /arphandler/src/main/resources/org/opendaylight/blueprint/arp-handler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 10 | 11 | 12 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /arphandler/src/main/yang/arp-handler-config.yang: -------------------------------------------------------------------------------- 1 | module arp-handler-config { 2 | 3 | yang-version 1; 4 | namespace "urn:opendaylight:packet:arp-handler-config"; 5 | prefix "arp-handler-config"; 6 | 7 | description 8 | "This module contains the base configuration for arphandler-impl 9 | implementation."; 10 | 11 | revision 2014-05-28 { 12 | description "Initial revision."; 13 | } 14 | 15 | container arp-handler-config { 16 | leaf arp-flow-table-id { 17 | type uint8; 18 | default 0; 19 | } 20 | leaf arp-flow-priority { 21 | type uint16; 22 | default 1; 23 | } 24 | leaf arp-flow-hard-timeout { 25 | type uint16; 26 | default 0; 27 | } 28 | leaf arp-flow-idle-timeout { 29 | type uint16; 30 | default 0; 31 | } 32 | leaf flood-flow-table-id { 33 | type uint8; 34 | default 0; 35 | } 36 | leaf flood-flow-priority { 37 | type uint16; 38 | default 2; 39 | } 40 | leaf flood-flow-hard-timeout { 41 | type uint16; 42 | default 0; 43 | } 44 | leaf flood-flow-idle-timeout { 45 | type uint16; 46 | default 0; 47 | } 48 | leaf flood-flow-installation-delay { 49 | type uint32; 50 | default 2000; 51 | } 52 | leaf is-proactive-flood-mode { 53 | type boolean; 54 | default true; 55 | description "Setting the value to false is same as passive discovery mode, 56 | where arp packets are sent to controller. Setting to true 57 | refers to proactive flood mode where flood flows are 58 | automatically written on to each switch."; 59 | } 60 | leaf is-hybrid-mode { 61 | type boolean; 62 | default false; 63 | description "Setting the value to true refers to proactive flood mode where 64 | flood flows are automatically written to each switch."; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/ArpPacketHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.arphandler.core; 9 | 10 | import static org.mockito.Mockito.times; 11 | import static org.mockito.Mockito.verify; 12 | import static org.mockito.Mockito.verifyNoInteractions; 13 | 14 | import java.util.ArrayList; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.mockito.Mock; 18 | import org.mockito.MockitoAnnotations; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceivedBuilder; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.arp.packet.received.packet.chain.packet.ArpPacketBuilder; 22 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChainBuilder; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacketBuilder; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFieldsBuilder; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacketBuilder; 27 | 28 | public class ArpPacketHandlerTest { 29 | 30 | @Mock 31 | private PacketDispatcher packetDispatcher; 32 | private ArpPacketHandler arpPacketHandler; 33 | 34 | @Before 35 | public void initMocks() { 36 | MockitoAnnotations.initMocks(this); 37 | arpPacketHandler = new ArpPacketHandler(packetDispatcher); 38 | } 39 | 40 | @Test 41 | public void onArpPacketReceivedTest() throws Exception { 42 | ArrayList packetChainList = new ArrayList<>(); 43 | packetChainList.add(new PacketChainBuilder() 44 | .setPacket(new RawPacketBuilder().setRawPacketFields(new RawPacketFieldsBuilder().build()).build()) 45 | .build()); 46 | packetChainList.add(new PacketChainBuilder().setPacket(new EthernetPacketBuilder().build()).build()); 47 | packetChainList.add(new PacketChainBuilder().setPacket(new ArpPacketBuilder().build()).build()); 48 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 49 | arpPacketHandler.onNotification(arpReceived); 50 | 51 | verify(packetDispatcher, times(1)).dispatchPacket(null, null, null, null); 52 | } 53 | 54 | @Test 55 | public void onArpPacketReceivedTest_NullInput() throws Exception { 56 | arpPacketHandler.onNotification(null); 57 | verifyNoInteractions(packetDispatcher); 58 | } 59 | 60 | @Test 61 | public void onArpPacketReceivedTest_NullPacketChain() throws Exception { 62 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().build(); 63 | arpPacketHandler.onNotification(arpReceived); 64 | 65 | verifyNoInteractions(packetDispatcher); 66 | } 67 | 68 | @Test 69 | public void onArpPacketReceivedTest_EmptyPacketChain() throws Exception { 70 | ArrayList packetChainList = new ArrayList<>(); 71 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 72 | arpPacketHandler.onNotification(arpReceived); 73 | 74 | verifyNoInteractions(packetDispatcher); 75 | } 76 | 77 | @Test 78 | public void onArpPacketReceivedTest_NoRawPacket() throws Exception { 79 | ArrayList packetChainList = new ArrayList<>(); 80 | packetChainList.add(new PacketChainBuilder().setPacket(new EthernetPacketBuilder().build()).build()); 81 | packetChainList.add(new PacketChainBuilder().setPacket(new ArpPacketBuilder().build()).build()); 82 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 83 | arpPacketHandler.onNotification(arpReceived); 84 | 85 | verifyNoInteractions(packetDispatcher); 86 | } 87 | 88 | @Test 89 | public void onArpPacketReceivedTest_NoEthernetPacket() throws Exception { 90 | ArrayList packetChainList = new ArrayList<>(); 91 | packetChainList.add(new PacketChainBuilder().setPacket(new RawPacketBuilder().build()).build()); 92 | packetChainList.add(new PacketChainBuilder().setPacket(new ArpPacketBuilder().build()).build()); 93 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 94 | arpPacketHandler.onNotification(arpReceived); 95 | 96 | verifyNoInteractions(packetDispatcher); 97 | } 98 | 99 | @Test 100 | public void onArpPacketReceivedTest_NoArpPacket() throws Exception { 101 | ArrayList packetChainList = new ArrayList<>(); 102 | packetChainList.add(new PacketChainBuilder().setPacket(new RawPacketBuilder().build()).build()); 103 | packetChainList.add(new PacketChainBuilder().setPacket(new EthernetPacketBuilder().build()).build()); 104 | ArpPacketReceived arpReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 105 | arpPacketHandler.onNotification(arpReceived); 106 | 107 | verifyNoInteractions(packetDispatcher); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /arphandler/src/test/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.arphandler.flow; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.Mockito.timeout; 12 | import static org.mockito.Mockito.verify; 13 | import static org.mockito.Mockito.when; 14 | 15 | import java.util.List; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.mockito.Mock; 19 | import org.mockito.MockitoAnnotations; 20 | import org.opendaylight.mdsal.binding.api.DataObjectModification; 21 | import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; 22 | import org.opendaylight.mdsal.binding.api.DataTreeModification; 23 | import org.opendaylight.mdsal.common.api.LogicalDatastoreType; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 27 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 29 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; 30 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; 31 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 32 | 33 | public class InitialFlowWriterTest { 34 | @Mock 35 | private AddFlow addFlow; 36 | @Mock 37 | private DataTreeModification mockChange; 38 | @Mock 39 | private DataObjectModification mockModification; 40 | 41 | private InitialFlowWriter initialFlowWriter; 42 | 43 | @Before 44 | public void before() { 45 | MockitoAnnotations.initMocks(this); 46 | initialFlowWriter = new InitialFlowWriter(addFlow); 47 | } 48 | 49 | @Test 50 | public void onDataChange_Valid() throws Exception { 51 | when(mockModification.getDataAfter()).thenReturn(new NodeBuilder().setId(new NodeId("openflow:1")).build()); 52 | when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); 53 | when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, 54 | InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1"))).build())); 55 | when(mockChange.getRootNode()).thenReturn(mockModification); 56 | 57 | initialFlowWriter.onDataTreeChanged(List.of(mockChange)); 58 | verify(addFlow, timeout(500)).invoke(any(AddFlowInput.class)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /distribution/karaf/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.odlparent 6 | karaf4-parent 7 | 13.1.3 8 | 9 | 10 | distribution-karaf 11 | org.opendaylight.l2switch 12 | 0.8.0-SNAPSHOT 13 | pom 14 | 15 | 16 | odl-l2switch-switch 17 | 18 | 19 | 20 | 21 | 22 | org.apache.karaf.features 23 | framework 24 | kar 25 | 26 | 27 | org.opendaylight.l2switch 28 | features-l2switch 29 | ${project.version} 30 | features 31 | xml 32 | runtime 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-deploy-plugin 42 | 43 | true 44 | 45 | 46 | 47 | 48 | 49 | scm:git:ssh://git.opendaylight.org:29418/l2switch.git 50 | scm:git:ssh://git.opendaylight.org:29418/l2switch.git 51 | HEAD 52 | https://git.opendaylight.org/gerrit/gitweb?p=l2switch.git;a=summary 53 | 54 | 55 | -------------------------------------------------------------------------------- /features/feature-parent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.odlparent 14 | single-feature-parent 15 | 13.1.3 16 | 17 | 18 | 19 | org.opendaylight.l2switch 20 | feature-parent 21 | 0.8.0-SNAPSHOT 22 | pom 23 | OpenDaylight :: L2Switch :: Feature Parent 24 | 25 | 26 | 27 | 28 | org.opendaylight.netconf 29 | netconf-artifacts 30 | 6.0.8 31 | import 32 | pom 33 | 34 | 35 | org.opendaylight.l2switch 36 | l2switch-parent 37 | ${project.version} 38 | import 39 | pom 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /features/features-l2switch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.odlparent 14 | feature-repo-parent 15 | 13.1.3 16 | 17 | 18 | 19 | org.opendaylight.l2switch 20 | features-l2switch 21 | 0.8.0-SNAPSHOT 22 | feature 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-all 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | ${project.groupId} 34 | odl-l2switch-switch 35 | ${project.version} 36 | xml 37 | features 38 | 39 | 40 | ${project.groupId} 41 | odl-l2switch-switch-rest 42 | ${project.version} 43 | xml 44 | features 45 | 46 | 55 | 56 | ${project.groupId} 57 | odl-l2switch-hosttracker 58 | ${project.version} 59 | xml 60 | features 61 | 62 | 63 | ${project.groupId} 64 | odl-l2switch-addresstracker 65 | ${project.version} 66 | xml 67 | features 68 | 69 | 70 | ${project.groupId} 71 | odl-l2switch-arphandler 72 | ${project.version} 73 | xml 74 | features 75 | 76 | 77 | ${project.groupId} 78 | odl-l2switch-loopremover 79 | ${project.version} 80 | xml 81 | features 82 | 83 | 84 | ${project.groupId} 85 | odl-l2switch-packethandler 86 | ${project.version} 87 | xml 88 | features 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /features/odl-l2switch-addresstracker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-addresstracker 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: AddressTracker 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-packethandler 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | org.opendaylight.l2switch.addresstracker 34 | addresstracker-impl 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /features/odl-l2switch-all/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.odlparent 14 | single-feature-parent 15 | 13.1.3 16 | 17 | 18 | 19 | org.opendaylight.l2switch 20 | odl-l2switch-all 21 | 0.8.0-SNAPSHOT 22 | feature 23 | 24 | OpenDaylight :: L2Switch :: All 25 | 26 | 27 | 28 | ${project.groupId} 29 | odl-l2switch-switch 30 | ${project.version} 31 | xml 32 | features 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /features/odl-l2switch-arphandler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-arphandler 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: ArpHandler 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-packethandler 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | ${project.groupId} 34 | odl-l2switch-loopremover 35 | ${project.version} 36 | xml 37 | features 38 | 39 | 40 | org.opendaylight.l2switch.arphandler 41 | arphandler-impl 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /features/odl-l2switch-hosttracker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-hosttracker 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: HostTracker 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-addresstracker 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | org.opendaylight.l2switch.hosttracker 34 | hosttracker-impl 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /features/odl-l2switch-loopremover/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-loopremover 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: LoopRemover 23 | 24 | 25 | 26 | org.opendaylight.openflowplugin 27 | odl-openflowplugin-flow-services 28 | xml 29 | features 30 | 31 | 32 | org.opendaylight.l2switch.loopremover 33 | loopremover-impl 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /features/odl-l2switch-packethandler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-packethandler 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: PacketHandler 23 | 24 | 25 | 26 | org.opendaylight.openflowplugin 27 | odl-openflowplugin-southbound 28 | xml 29 | features 30 | 31 | 32 | org.opendaylight.l2switch.packethandler 33 | packethandler-impl 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /features/odl-l2switch-switch-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-switch-rest 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: Switch REST 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-switch 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | org.opendaylight.netconf 34 | odl-restconf 35 | xml 36 | features 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /features/odl-l2switch-switch-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-switch-ui 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: Switch UI 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-switch-rest 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | org.opendaylight.netconf 34 | odl-mdsal-apidocs 35 | xml 36 | features 37 | 38 | 39 | org.opendaylight.dluxapps 40 | odl-dluxapps-topology 41 | xml 42 | features 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /features/odl-l2switch-switch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | org.opendaylight.l2switch 14 | feature-parent 15 | 0.8.0-SNAPSHOT 16 | ../feature-parent 17 | 18 | 19 | odl-l2switch-switch 20 | feature 21 | 22 | OpenDaylight :: L2Switch :: Switch 23 | 24 | 25 | 26 | ${project.groupId} 27 | odl-l2switch-hosttracker 28 | ${project.version} 29 | xml 30 | features 31 | 32 | 33 | ${project.groupId} 34 | odl-l2switch-arphandler 35 | ${project.version} 36 | xml 37 | features 38 | 39 | 40 | org.opendaylight.l2switch.main 41 | main-impl 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /features/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | org.opendaylight.odlparent 13 | odlparent-lite 14 | 13.1.3 15 | 16 | 17 | 18 | org.opendaylight.l2switch 19 | features-l2switch-aggregator 20 | 0.8.0-SNAPSHOT 21 | pom 22 | ${project.artifactId} 23 | 24 | 25 | feature-parent 26 | features-l2switch 27 | odl-l2switch-addresstracker 28 | odl-l2switch-arphandler 29 | odl-l2switch-hosttracker 30 | odl-l2switch-loopremover 31 | odl-l2switch-packethandler 32 | odl-l2switch-switch 33 | odl-l2switch-switch-rest 34 | 37 | odl-l2switch-all 38 | 39 | 40 | -------------------------------------------------------------------------------- /hosttracker/implementation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.l2switch 6 | l2switch-parent 7 | 0.8.0-SNAPSHOT 8 | ../../parent 9 | 10 | hosttracker-impl 11 | org.opendaylight.l2switch.hosttracker 12 | hosttracker-impl 13 | bundle 14 | 15 | 16 | 17 | org.opendaylight.yangtools 18 | yang-common 19 | 20 | 21 | org.opendaylight.mdsal 22 | yang-binding 23 | 24 | 25 | org.opendaylight.mdsal 26 | mdsal-binding-api 27 | 28 | 29 | org.opendaylight.openflowplugin.model 30 | model-inventory 31 | 32 | 33 | org.opendaylight.l2switch.addresstracker 34 | addresstracker-model 35 | 36 | 37 | org.opendaylight.l2switch.hosttracker 38 | hosttracker-model 39 | 40 | 41 | org.opendaylight.mdsal.model 42 | ietf-topology 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.felix 50 | maven-bundle-plugin 51 | true 52 | 53 | 54 | org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.host.tracker.impl.rev140528 55 | * 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareLinkHashMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.hosttracker.plugin.internal; 9 | 10 | import java.util.Collection; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Map.Entry; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | import org.opendaylight.mdsal.common.api.LogicalDatastoreType; 17 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId; 18 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; 19 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | /** 24 | * This will (try to) submit all writes and deletes in to the MD-SAL database. 25 | * The removeLocally and putLocally methods should be used when dataChanges are dealt locally and not update to MD-SAL. 26 | */ 27 | 28 | public class ConcurrentClusterAwareLinkHashMap { 29 | private static final Logger LOG = LoggerFactory.getLogger(ConcurrentClusterAwareLinkHashMap.class); 30 | 31 | private final OperationProcessor opProcessor; 32 | 33 | /** 34 | * The instance identifiers for each Link submitted to MD-SAL. 35 | */ 36 | private final ConcurrentHashMap, LinkId> instanceIDs = new ConcurrentHashMap<>(); 37 | 38 | /** 39 | * The local Links' HashMap. 40 | */ 41 | private final ConcurrentHashMap linkHashMap = new ConcurrentHashMap<>(); 42 | 43 | public ConcurrentClusterAwareLinkHashMap(OperationProcessor opProcessor) { 44 | this.opProcessor = opProcessor; 45 | } 46 | 47 | 48 | /** 49 | * Puts the given value (Link) only in this local HashMap. Ideally used for 50 | * Link data listener events. 51 | * 52 | * @param ii the value's (Link's) InstanceIdentifier<Link> 53 | * @param link the Link to store locally. 54 | * @return the previous value associated with {@code key}, or 55 | * {@code null} if there was no mapping for {@code key} 56 | */ 57 | public synchronized Link putLocally(InstanceIdentifier ii, Link link) { 58 | LOG.trace("Putting locally {}", link.getLinkId()); 59 | this.instanceIDs.put(ii, link.getLinkId()); 60 | return this.linkHashMap.put(link.getLinkId(), link); 61 | } 62 | 63 | /** 64 | * Removes the given links both locally and on MD-SAL database. 65 | * 66 | * @param links 67 | * the links to remove. 68 | */ 69 | public synchronized void removeAll(List links) { 70 | for (final Map.Entry, LinkId> e : this.instanceIDs.entrySet()) { 71 | LOG.debug("Links to remove from local & MD-SAL database: {}", links); 72 | for (Link l : links) { 73 | if (e.getValue().equals(l.getLinkId())) { 74 | this.opProcessor.enqueueOperation(tx -> tx.delete(LogicalDatastoreType.OPERATIONAL, e.getKey())); 75 | this.linkHashMap.remove(e.getValue()); 76 | break; 77 | } 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Returns the Values from this local HashMap. 84 | * 85 | * @return the Values from this local HashMap. 86 | */ 87 | public synchronized Collection values() { 88 | return this.linkHashMap.values(); 89 | } 90 | 91 | /** 92 | * Removes, if exists, the Link with the given InstanceIdentifier<Link> from 93 | * this local HashMap. Ideally used for link data listener events. 94 | * 95 | * @param iiL 96 | * the InstanceIdentifier<Link> of the Link to remove. 97 | * @return the removed Link if exits, null if it doesn't exist. 98 | */ 99 | public synchronized Link removeLocally(InstanceIdentifier iiL) { 100 | LinkId linkId = this.instanceIDs.remove(iiL); 101 | if (linkId != null) { 102 | return this.linkHashMap.remove(linkId); 103 | } 104 | return null; 105 | } 106 | 107 | /** 108 | * Removes, if exists, the Link with the given Key (LinkId) from this local 109 | * HashMap. Ideally used for link data listener events. 110 | * 111 | * @param key 112 | * the key (LinkId) of the Link to remove. 113 | * @return the removed Link if exits, null if it doesn't exist. 114 | */ 115 | public synchronized Link removeLocally(LinkId key) { 116 | Iterator, LinkId>> iterator = this.instanceIDs.entrySet().iterator(); 117 | while (iterator.hasNext()) { 118 | if (iterator.next().getValue().equals(key)) { 119 | iterator.remove(); 120 | break; 121 | } 122 | } 123 | return linkHashMap.remove(key); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/HostTrackerOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Evan Zeller and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.hosttracker.plugin.internal; 9 | 10 | import org.opendaylight.mdsal.binding.api.ReadWriteTransaction; 11 | 12 | interface HostTrackerOperation { 13 | void applyOperation(ReadWriteTransaction tx); 14 | } 15 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/OperationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Evan Zeller and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.hosttracker.plugin.internal; 9 | 10 | import static java.util.Objects.requireNonNull; 11 | 12 | import com.google.common.util.concurrent.FutureCallback; 13 | import com.google.common.util.concurrent.Futures; 14 | import com.google.common.util.concurrent.MoreExecutors; 15 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 16 | import java.util.concurrent.BlockingQueue; 17 | import java.util.concurrent.LinkedBlockingQueue; 18 | import java.util.concurrent.atomic.AtomicReference; 19 | import org.opendaylight.mdsal.binding.api.DataBroker; 20 | import org.opendaylight.mdsal.binding.api.ReadWriteTransaction; 21 | import org.opendaylight.mdsal.binding.api.Transaction; 22 | import org.opendaylight.mdsal.binding.api.TransactionChain; 23 | import org.opendaylight.mdsal.binding.api.TransactionChainListener; 24 | import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | public class OperationProcessor implements AutoCloseable, Runnable, TransactionChainListener { 29 | private static final Logger LOG = LoggerFactory.getLogger(OperationProcessor.class); 30 | private static final int NUM_RETRY_SUBMIT = 2; 31 | private static final int OPS_PER_CHAIN = 256; 32 | private static final int QUEUE_DEPTH = 512; 33 | 34 | private final BlockingQueue queue = new LinkedBlockingQueue<>(QUEUE_DEPTH); 35 | private final DataBroker dataBroker; 36 | private final AtomicReference transactionChain; 37 | 38 | @SuppressFBWarnings("MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR") 39 | OperationProcessor(final DataBroker dataBroker) { 40 | this.dataBroker = requireNonNull(dataBroker); 41 | this.transactionChain = new AtomicReference<>(dataBroker.createTransactionChain(this)); 42 | } 43 | 44 | @Override 45 | public void onTransactionChainFailed(TransactionChain chain, Transaction transaction, Throwable cause) { 46 | chainFailure(); 47 | } 48 | 49 | @Override 50 | public void onTransactionChainSuccessful(TransactionChain chain) { 51 | // no-op 52 | } 53 | 54 | @Override 55 | public void run() { 56 | boolean done = false; 57 | while (!done) { 58 | try { 59 | HostTrackerOperation op = queue.take(); 60 | final TransactionChain txChain = transactionChain.get(); 61 | if (txChain == null) { 62 | break; 63 | } 64 | 65 | ReadWriteTransaction tx = txChain.newReadWriteTransaction(); 66 | int ops = 0; 67 | while (op != null && ops < OPS_PER_CHAIN) { 68 | op.applyOperation(tx); 69 | ops += 1; 70 | op = queue.poll(); 71 | } 72 | 73 | submitTransaction(tx, NUM_RETRY_SUBMIT); 74 | } catch (InterruptedException e) { 75 | done = true; 76 | } 77 | } 78 | clearQueue(); 79 | } 80 | 81 | @Override 82 | public void close() { 83 | final TransactionChain txChain = transactionChain.getAndSet(null); 84 | if (txChain != null) { 85 | txChain.close(); 86 | } 87 | } 88 | 89 | private void chainFailure() { 90 | try { 91 | final TransactionChain prevChain = transactionChain.getAndSet( 92 | dataBroker.createTransactionChain(this)); 93 | if (prevChain != null) { 94 | prevChain.close(); 95 | } 96 | clearQueue(); 97 | } catch (IllegalStateException e) { 98 | LOG.warn("Failed to close chain", e); 99 | } 100 | } 101 | 102 | public void enqueueOperation(HostTrackerOperation op) { 103 | try { 104 | queue.put(op); 105 | } catch (InterruptedException e) { 106 | Thread.currentThread().interrupt(); 107 | } 108 | } 109 | 110 | public void submitTransaction(final ReadWriteTransaction tx, final int tries) { 111 | Futures.addCallback(tx.commit(), new FutureCallback() { 112 | @Override 113 | public void onSuccess(Object obj) { 114 | LOG.trace("tx {} succeeded", tx.getIdentifier()); 115 | } 116 | 117 | @Override 118 | public void onFailure(Throwable failure) { 119 | if (failure instanceof OptimisticLockFailedException) { 120 | if (tries - 1 > 0) { 121 | LOG.warn("tx {} failed, retrying", tx.getIdentifier()); 122 | // do retry 123 | submitTransaction(tx, tries - 1); 124 | } else { 125 | LOG.warn("tx {} failed, out of retries", tx.getIdentifier()); 126 | // out of retries 127 | chainFailure(); 128 | } 129 | } else { 130 | // failed due to another type of 131 | // TransactionCommitFailedException. 132 | LOG.warn("tx {} failed: {}", tx.getIdentifier(), failure.getMessage()); 133 | chainFailure(); 134 | } 135 | } 136 | }, MoreExecutors.directExecutor()); 137 | } 138 | 139 | private void clearQueue() { 140 | while (!queue.isEmpty()) { 141 | queue.poll(); 142 | } 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/Compare.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 André Martins, Colin Dixon, Evan Zeller and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.hosttracker.plugin.util; 9 | 10 | import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.Addresses; 11 | import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.host.AttachmentPointsBuilder; 12 | 13 | public final class Compare { 14 | private Compare() { 15 | } 16 | 17 | /** 18 | * Compare two addresses. This method is different than equals since it only 19 | * compares MAC, VLAN and IP Address. 20 | * 21 | * @param addr1 22 | * first Address to compare. 23 | * @param addr2 24 | * second Address to compare. 25 | * @return true if both have the same MAC, VLAN and IP Address, false 26 | * otherwise. 27 | */ 28 | public static boolean addresses(Addresses addr1, Addresses addr2) { 29 | return (addr1.getMac() == null && addr2.getMac() == null 30 | || addr1.getMac() != null && addr1.getMac().equals(addr2.getMac())) 31 | && (addr1.getVlan() == null && addr2.getVlan() == null 32 | || addr1.getVlan() != null && addr1.getVlan().equals(addr2.getVlan())) 33 | && (addr1.getIp() == null && addr2.getIp() == null 34 | || addr1.getIp() != null && addr1.getIp().equals(addr2.getIp())); 35 | } 36 | 37 | /** 38 | * Compares two AttachmentPointsBuilder. This method is different than 39 | * equals since it only compares the TpId of both AttachmentPointsBuilder. 40 | * 41 | * @param atp1 42 | * first AttachmentPointsBuilder to compare. 43 | * @param atp2 44 | * second AttachmentPointsBuilder to compare. 45 | * @return true if both have the same TpId, false otherwise. 46 | */ 47 | public static boolean attachmentPointsBuilder(AttachmentPointsBuilder atp1, AttachmentPointsBuilder atp2) { 48 | return atp1.getTpId() == null && atp2.getTpId() == null 49 | || atp1.getTpId() != null && atp1.getTpId().equals(atp2.getTpId()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/Utilities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 André Martins, Colin Dixon, Evan Zeller and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.hosttracker.plugin.util; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.host.AttachmentPointsBuilder; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.host.AttachmentPointsKey; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; 15 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId; 16 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; 17 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; 18 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; 19 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId; 20 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder; 21 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder; 22 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; 23 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; 24 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; 25 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder; 26 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey; 27 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; 28 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; 29 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 30 | 31 | public final class Utilities { 32 | private Utilities() { 33 | // Hidden on purpose 34 | } 35 | 36 | public static List createLinks(NodeId srcNId, TpId srcTpId, NodeId dstNId, TpId dstTpId) { 37 | List links = new ArrayList<>(); 38 | LinkBuilder srcdst = new LinkBuilder() 39 | .setSource(new SourceBuilder() 40 | .setSourceNode(srcNId) 41 | .setSourceTp(srcTpId).build()) 42 | .setDestination(new DestinationBuilder() 43 | .setDestNode(dstNId).setDestTp(dstTpId).build()) 44 | .setLinkId(new LinkId(srcTpId.getValue() + "/" + dstTpId.getValue())); 45 | srcdst.withKey(new LinkKey(srcdst.getLinkId())); 46 | LinkBuilder dstsrc = new LinkBuilder() 47 | .setSource(new SourceBuilder().setSourceNode(dstNId).setSourceTp(dstTpId).build()) 48 | .setDestination(new DestinationBuilder().setDestNode(srcNId).setDestTp(srcTpId).build()) 49 | .setLinkId(new LinkId(dstTpId.getValue() + "/" + srcTpId.getValue())); 50 | dstsrc.withKey(new LinkKey(dstsrc.getLinkId())); 51 | links.add(dstsrc.build()); 52 | links.add(srcdst.build()); 53 | return links; 54 | } 55 | 56 | public static InstanceIdentifier buildNodeIID(NodeKey nk, String topologyId) { 57 | return InstanceIdentifier.builder(NetworkTopology.class) 58 | .child(Topology.class, new TopologyKey(new TopologyId(topologyId))) 59 | .child(Node.class, nk).build(); 60 | } 61 | 62 | public static InstanceIdentifier buildLinkIID(LinkKey lk, String topologyId) { 63 | return InstanceIdentifier.builder(NetworkTopology.class) 64 | .child(Topology.class, new TopologyKey(new TopologyId(topologyId))) 65 | .child(Link.class, lk).build(); 66 | } 67 | 68 | public static AttachmentPointsBuilder createAPsfromNodeConnector(NodeConnector nc) { 69 | return createAPsfromTP(new TpId(nc.getId().getValue())); 70 | } 71 | 72 | public static AttachmentPointsBuilder createAPsfromTP(TpId tpId) { 73 | return new AttachmentPointsBuilder().setTpId(tpId).withKey(new AttachmentPointsKey(tpId)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/resources/org/opendaylight/blueprint/host-tracker.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /hosttracker/implementation/src/main/yang/host-tracker-config.yang: -------------------------------------------------------------------------------- 1 | module host-tracker-config { 2 | 3 | yang-version 1; 4 | namespace "urn:opendaylight:l2switch:host-tracker-config"; 5 | prefix "host-tracker-impl"; 6 | 7 | description 8 | "This module contains the base configuration for host-tracker 9 | implementation."; 10 | 11 | revision 2014-05-28 { 12 | description "Initial module draft."; 13 | } 14 | 15 | container host-tracker-config { 16 | leaf topology-id { 17 | type string; 18 | default "flow:1"; 19 | } 20 | leaf host-purge-interval { 21 | type int64; 22 | default 0; 23 | description "The interval in seconds (0 disables host purging) at which to 24 | evaluate whether hosts should be removed in seconds"; 25 | } 26 | leaf host-purge-age { 27 | type int64; 28 | default 600; 29 | description "How long a host must have been unobserved for it to be removed 30 | in seconds. In seconds, It is valid only if host-purge-interval 31 | > 0, Before modifying this configurable variable please consider 32 | default flow idletimeout/hardtimeout values installed by l2switch 33 | in l2switch-main module; Also consider timestamp-update-interval 34 | value in addressTracker module"; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /hosttracker/model/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | hosttracker.aggregator 6 | org.opendaylight.l2switch.hosttracker 7 | 0.8.0-SNAPSHOT 8 | 9 | hosttracker-model 10 | bundle 11 | 12 | 13 | 14 | org.opendaylight.l2switch.addresstracker 15 | addresstracker-model 16 | 17 | 18 | org.opendaylight.openflowplugin.model 19 | model-topology 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.apache.felix 27 | maven-bundle-plugin 28 | true 29 | 30 | 31 | ${project.groupId}.${project.artifactId} 32 | org.opendaylight.yangtools.yang.binding.annotations, * 33 | 34 | 35 | 36 | 37 | 38 | hosttracker-model 39 | 40 | -------------------------------------------------------------------------------- /hosttracker/model/src/main/yang/host-tracker-service.yang: -------------------------------------------------------------------------------- 1 | module host-tracker-service { 2 | namespace "urn:opendaylight:host-tracker"; 3 | prefix host-track; 4 | 5 | import ietf-yang-types {prefix yang;} 6 | import address-tracker {prefix at;} 7 | import opendaylight-inventory {prefix inv;} 8 | import network-topology {prefix "topo"; revision-date "2013-10-21"; } 9 | import yang-ext {prefix ext;} 10 | 11 | revision 2014-06-24 { 12 | description 13 | "draft based on address-tracker 2014-06-17"; 14 | } 15 | 16 | typedef host-id { 17 | type string; 18 | } 19 | 20 | grouping host { 21 | description "List of addresses and attachment points"; 22 | uses at:address-node-connector; 23 | leaf id { 24 | type host-id; 25 | } 26 | list attachment-points { 27 | description "the assumption is that all address can be reached at all attachment points"; 28 | uses topo:tp-attributes; 29 | key tp-id; 30 | leaf corresponding-tp { 31 | type topo:tp-ref; 32 | } 33 | leaf active { 34 | type boolean; 35 | } 36 | } 37 | } 38 | 39 | augment "/topo:network-topology/topo:topology/topo:node" { 40 | ext:augment-identifier "host-node"; 41 | uses host; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /hosttracker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | hosttracker.aggregator 5 | 6 | org.opendaylight.l2switch 7 | l2switch-parent 8 | 0.8.0-SNAPSHOT 9 | ../parent 10 | 11 | hosttracker.aggregator 12 | org.opendaylight.l2switch.hosttracker 13 | pom 14 | 15 | 16 | model 17 | implementation 18 | 19 | 20 | -------------------------------------------------------------------------------- /l2switch-main/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.l2switch 6 | l2switch-parent 7 | 0.8.0-SNAPSHOT 8 | ../parent 9 | 10 | org.opendaylight.l2switch.main 11 | main-impl 12 | bundle 13 | 14 | 15 | 16 | org.opendaylight.mdsal 17 | mdsal-binding-api 18 | 19 | 20 | org.opendaylight.openflowplugin.model 21 | model-flow-service 22 | 23 | 24 | org.opendaylight.openflowplugin.model 25 | model-topology 26 | 27 | 28 | org.opendaylight.yangtools 29 | yang-common 30 | 31 | 32 | org.opendaylight.l2switch.packethandler 33 | packethandler-model 34 | 35 | 36 | org.opendaylight.l2switch.packethandler 37 | packethandler-impl 38 | 39 | 40 | org.opendaylight.l2switch.addresstracker 41 | addresstracker-model 42 | 43 | 44 | org.opendaylight.l2switch.loopremover 45 | loopremover-model 46 | 47 | 48 | org.opendaylight.openflowplugin 49 | openflowplugin-api 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.apache.felix 57 | maven-bundle-plugin 58 | true 59 | 60 | 61 | org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.main.impl.rev140528 62 | * 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /l2switch-main/src/main/java/org/opendaylight/l2switch/L2SwitchMainProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch; 9 | 10 | import org.opendaylight.l2switch.flow.FlowWriterServiceImpl; 11 | import org.opendaylight.l2switch.flow.InitialFlowWriter; 12 | import org.opendaylight.l2switch.flow.ReactiveFlowWriter; 13 | import org.opendaylight.l2switch.inventory.InventoryReader; 14 | import org.opendaylight.mdsal.binding.api.DataBroker; 15 | import org.opendaylight.mdsal.binding.api.NotificationService; 16 | import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.l2switch.config.rev140528.L2switchConfig; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 20 | import org.opendaylight.yangtools.concepts.Registration; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | public class L2SwitchMainProvider { 25 | private static final Logger LOG = LoggerFactory.getLogger(L2SwitchMainProvider.class); 26 | private Registration topoNodeListherReg; 27 | private Registration reactFlowWriterReg; 28 | 29 | private final DataBroker dataService; 30 | private final NotificationService notificationService; 31 | private final RpcConsumerRegistry rpcService; 32 | private final L2switchConfig mainConfig; 33 | 34 | public L2SwitchMainProvider(final DataBroker dataBroker, 35 | final NotificationService notificationPublishService, 36 | final RpcConsumerRegistry rpcService, final L2switchConfig config) { 37 | this.dataService = dataBroker; 38 | this.notificationService = notificationPublishService; 39 | this.rpcService = rpcService; 40 | this.mainConfig = config; 41 | } 42 | 43 | public void init() { 44 | // Setup FlowWrtierService 45 | FlowWriterServiceImpl flowWriterService = new FlowWriterServiceImpl(rpcService.getRpc(AddFlow.class)); 46 | flowWriterService.setFlowTableId(mainConfig.getReactiveFlowTableId()); 47 | flowWriterService.setFlowPriority(mainConfig.getReactiveFlowPriority()); 48 | flowWriterService.setFlowIdleTimeout(mainConfig.getReactiveFlowIdleTimeout()); 49 | flowWriterService.setFlowHardTimeout(mainConfig.getReactiveFlowHardTimeout()); 50 | 51 | // Setup InventoryReader 52 | InventoryReader inventoryReader = new InventoryReader(dataService); 53 | 54 | // Write initial flows 55 | if (mainConfig.getIsInstallDropallFlow()) { 56 | LOG.info("L2Switch will install a dropall flow on each switch"); 57 | InitialFlowWriter initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); 58 | initialFlowWriter.setFlowTableId(mainConfig.getDropallFlowTableId()); 59 | initialFlowWriter.setFlowPriority(mainConfig.getDropallFlowPriority()); 60 | initialFlowWriter.setFlowIdleTimeout(mainConfig.getDropallFlowIdleTimeout()); 61 | initialFlowWriter.setFlowHardTimeout(mainConfig.getDropallFlowHardTimeout()); 62 | topoNodeListherReg = initialFlowWriter.registerAsDataChangeListener(dataService); 63 | } 64 | else { 65 | LOG.info("Dropall flows will not be installed"); 66 | } 67 | 68 | if (mainConfig.getIsLearningOnlyMode()) { 69 | LOG.info("L2Switch is in Learning Only Mode"); 70 | } 71 | else { 72 | // Setup reactive flow writer 73 | LOG.info("L2Switch will react to network traffic and install flows"); 74 | ReactiveFlowWriter reactiveFlowWriter = new ReactiveFlowWriter(inventoryReader, flowWriterService); 75 | reactFlowWriterReg = notificationService.registerListener(ArpPacketReceived.class, reactiveFlowWriter); 76 | } 77 | LOG.info("L2SwitchMain initialized."); 78 | } 79 | 80 | public void close() { 81 | if (reactFlowWriterReg != null) { 82 | reactFlowWriterReg.close(); 83 | } 84 | 85 | if (topoNodeListherReg != null) { 86 | topoNodeListherReg.close(); 87 | } 88 | LOG.info("L2SwitchMain (instance {}) torn down.", this); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /l2switch-main/src/main/java/org/opendaylight/l2switch/flow/FlowWriterService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.flow; 9 | 10 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 11 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 12 | 13 | /** 14 | * Service that adds packet forwarding flows to configuration data store. 15 | */ 16 | public interface FlowWriterService { 17 | /** 18 | * Writes a flow that forwards packets to destPort if destination mac in 19 | * packet is destMac and source Mac in packet is sourceMac. If sourceMac is 20 | * null then flow would not set any source mac, resulting in all packets 21 | * with destMac being forwarded to destPort. 22 | * 23 | * @param sourceMac the source MAC 24 | * @param destMac the destination MAC 25 | * @param destNodeConnectorRef the destination port NodeConnectorRef 26 | */ 27 | void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef); 28 | 29 | /** 30 | * Writes mac-to-mac flow on all ports that are in the path between given 31 | * source and destination ports. It uses path provided by 32 | * org.opendaylight.l2switch.loopremover.topology.NetworkGraphService to 33 | * find a links between given ports. And then writes appropriate flow on each 34 | * port that is covered in that path. 35 | * 36 | * @param sourceMac the source MAC 37 | * @param sourceNodeConnectorRef the the source port NodeConnectorRef 38 | * @param destMac the destination MAC 39 | * @param destNodeConnectorRef the destination port NodeConnectorRef 40 | */ 41 | void addBidirectionalMacToMacFlows(MacAddress sourceMac, NodeConnectorRef sourceNodeConnectorRef, 42 | MacAddress destMac, NodeConnectorRef destNodeConnectorRef); 43 | } 44 | -------------------------------------------------------------------------------- /l2switch-main/src/main/java/org/opendaylight/l2switch/flow/ReactiveFlowWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.flow; 9 | 10 | import org.opendaylight.l2switch.inventory.InventoryReader; 11 | import org.opendaylight.mdsal.binding.api.NotificationService.Listener; 12 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.arp.packet.received.packet.chain.packet.ArpPacket; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacket; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFields; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacket; 21 | 22 | /** 23 | * This class listens to certain type of packets and writes a mac to mac flows. 24 | */ 25 | public class ReactiveFlowWriter implements Listener { 26 | private final InventoryReader inventoryReader; 27 | private final FlowWriterService flowWriterService; 28 | 29 | public ReactiveFlowWriter(InventoryReader inventoryReader, FlowWriterService flowWriterService) { 30 | this.inventoryReader = inventoryReader; 31 | this.flowWriterService = flowWriterService; 32 | } 33 | 34 | /** 35 | * Checks if a MAC should be considered for flow creation. 36 | * 37 | * @param macToCheck 38 | * MacAddress to consider 39 | * @return true if a MacAddess is broadcast or multicast, false if the 40 | * MacAddress is unicast (and thus legible for flow creation). 41 | */ 42 | 43 | private boolean ignoreThisMac(MacAddress macToCheck) { 44 | if (macToCheck == null) { 45 | return true; 46 | } 47 | String[] octets = macToCheck.getValue().split(":"); 48 | short firstByte = Short.parseShort(octets[0], 16); 49 | 50 | /* 51 | * First bit in first byte for unicast and multicast is 1 Unicast and 52 | * multicast are handled by flooding, they are not legible for flow 53 | * creation 54 | */ 55 | 56 | return (firstByte & 1) == 1; 57 | } 58 | 59 | @Override 60 | public void onNotification(ArpPacketReceived packetReceived) { 61 | if (packetReceived == null || packetReceived.getPacketChain() == null) { 62 | return; 63 | } 64 | 65 | RawPacketFields rawPacket = null; 66 | EthernetPacket ethernetPacket = null; 67 | ArpPacket arpPacket = null; 68 | for (PacketChain packetChain : packetReceived.getPacketChain()) { 69 | if (packetChain.getPacket() instanceof RawPacket) { 70 | rawPacket = ((RawPacket) packetChain.getPacket()).getRawPacketFields(); 71 | } else if (packetChain.getPacket() instanceof EthernetPacket) { 72 | ethernetPacket = (EthernetPacket) packetChain.getPacket(); 73 | } else if (packetChain.getPacket() instanceof ArpPacket) { 74 | arpPacket = (ArpPacket) packetChain.getPacket(); 75 | } 76 | } 77 | if (rawPacket == null || ethernetPacket == null || arpPacket == null) { 78 | return; 79 | } 80 | MacAddress destMac = ethernetPacket.getDestinationMac(); 81 | if (!ignoreThisMac(destMac)) { 82 | writeFlows(rawPacket.getIngress(), ethernetPacket.getSourceMac(), ethernetPacket.getDestinationMac()); 83 | } 84 | } 85 | 86 | /** 87 | * Invokes flow writer service to write bidirectional mac-mac flows on a 88 | * switch. 89 | * 90 | * @param ingress 91 | * The NodeConnector where the payload came from. 92 | * @param srcMac 93 | * The source MacAddress of the packet. 94 | * @param destMac 95 | * The destination MacAddress of the packet. 96 | */ 97 | public void writeFlows(NodeConnectorRef ingress, MacAddress srcMac, MacAddress destMac) { 98 | NodeConnectorRef destNodeConnector = inventoryReader 99 | .getNodeConnector(ingress.getValue().firstIdentifierOf(Node.class), destMac); 100 | if (destNodeConnector != null) { 101 | flowWriterService.addBidirectionalMacToMacFlows(srcMac, ingress, destMac, destNodeConnector); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /l2switch-main/src/main/java/org/opendaylight/l2switch/inventory/InventoryReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.inventory; 9 | 10 | import com.google.common.util.concurrent.FluentFuture; 11 | import java.util.Optional; 12 | import java.util.concurrent.ExecutionException; 13 | import org.opendaylight.mdsal.binding.api.DataBroker; 14 | import org.opendaylight.mdsal.binding.api.ReadTransaction; 15 | import org.opendaylight.mdsal.common.api.LogicalDatastoreType; 16 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.AddressCapableNodeConnector; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.Addresses; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 22 | import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; 24 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | /** 29 | * InventoryReader reads the opendaylight-inventory tree in MD-SAL data store. 30 | */ 31 | public class InventoryReader { 32 | 33 | private static final Logger LOG = LoggerFactory.getLogger(InventoryReader.class); 34 | private DataBroker dataService; 35 | 36 | /** 37 | * Construct an InventoryService object with the specified inputs. 38 | * 39 | * @param dataService 40 | * The DataBrokerService associated with the InventoryService. 41 | */ 42 | public InventoryReader(DataBroker dataService) { 43 | this.dataService = dataService; 44 | } 45 | 46 | /** 47 | * Get the NodeConnector on the specified node with the specified MacAddress 48 | * observation. 49 | * 50 | * @param nodeInsId 51 | * InstanceIdentifier for the node on which to search for. 52 | * @param macAddress 53 | * MacAddress to be searched for. 54 | * @return NodeConnectorRef that pertains to the NodeConnector containing 55 | * the MacAddress observation. 56 | */ 57 | public NodeConnectorRef getNodeConnector(InstanceIdentifier nodeInsId, MacAddress macAddress) { 58 | if (nodeInsId == null || macAddress == null) { 59 | return null; 60 | } 61 | 62 | final FluentFuture> readFuture; 63 | try (ReadTransaction readOnlyTransaction = dataService.newReadOnlyTransaction()) { 64 | readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, nodeInsId); 65 | } 66 | 67 | final Optional dataObjectOptional; 68 | try { 69 | dataObjectOptional = readFuture.get(); 70 | } catch (InterruptedException e) { 71 | LOG.error("Failed to read nodes from Operation data store."); 72 | throw new RuntimeException("Failed to read nodes from Operation data store.", e); 73 | } catch (ExecutionException e) { 74 | LOG.error("Failed to read nodes from Operation data store."); 75 | throw new RuntimeException("Failed to read nodes from Operation data store.", e); 76 | } 77 | 78 | if (dataObjectOptional.isEmpty()) { 79 | return null; 80 | } 81 | 82 | final Node node = dataObjectOptional.orElseThrow(); 83 | LOG.debug("Looking address{} in node : {}", macAddress, nodeInsId); 84 | 85 | NodeConnectorRef destNodeConnector = null; 86 | long latest = -1; 87 | for (NodeConnector nc : node.nonnullNodeConnector().values()) { 88 | // Don't look for mac in discarding node connectors 89 | StpStatusAwareNodeConnector saNodeConnector = nc.augmentation(StpStatusAwareNodeConnector.class); 90 | if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) { 91 | continue; 92 | } 93 | LOG.debug("Looking address{} in nodeconnector : {}", macAddress, nc.key()); 94 | AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class); 95 | if (acnc != null) { 96 | for (Addresses add : acnc.nonnullAddresses().values()) { 97 | if (macAddress.equals(add.getMac())) { 98 | final long lastSeen = add.getLastSeen(); 99 | if (lastSeen > latest) { 100 | destNodeConnector = new NodeConnectorRef(nodeInsId.child(NodeConnector.class, nc.key())); 101 | latest = lastSeen; 102 | LOG.debug("Found address{} in nodeconnector : {}", macAddress, nc.key()); 103 | break; 104 | } 105 | } 106 | } 107 | } 108 | } 109 | return destNodeConnector; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /l2switch-main/src/main/java/org/opendaylight/l2switch/util/InstanceIdentifierUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.util; 9 | 10 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; 11 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; 12 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; 13 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 17 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 18 | 19 | /** 20 | * InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers. 21 | */ 22 | public final class InstanceIdentifierUtils { 23 | 24 | private InstanceIdentifierUtils() { 25 | throw new UnsupportedOperationException("Utility class should never be instantiated"); 26 | } 27 | 28 | public static InstanceIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { 29 | return nodeConnectorRef.getValue().firstIdentifierOf(Node.class); 30 | } 31 | 32 | public static InstanceIdentifier generateFlowTableInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, 33 | final TableKey flowTableKey) { 34 | return generateNodeInstanceIdentifier(nodeConnectorRef).builder().augmentation(FlowCapableNode.class) 35 | .child(Table.class, flowTableKey).build(); 36 | } 37 | 38 | public static InstanceIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, 39 | final TableKey flowTableKey, final FlowKey flowKey) { 40 | return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).child(Flow.class, flowKey); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /l2switch-main/src/main/resources/org/opendaylight/blueprint/l2switch-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 9 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /l2switch-main/src/main/yang/l2switch-config.yang: -------------------------------------------------------------------------------- 1 | module l2switch-config { 2 | 3 | yang-version 1; 4 | namespace "urn:opendaylight:l2switch:l2switch-config"; 5 | prefix "l2switch-config"; 6 | 7 | description 8 | "This module contains the base configuration for main implementation."; 9 | 10 | revision 2014-05-28 { 11 | description "Initial module draft."; 12 | } 13 | 14 | container l2switch-config { 15 | leaf is-learning-only-mode { 16 | type boolean; 17 | default false; 18 | } 19 | leaf is-install-dropall-flow { 20 | type boolean; 21 | default true; 22 | } 23 | leaf dropall-flow-table-id { 24 | type uint8; 25 | default 0; 26 | } 27 | leaf dropall-flow-priority { 28 | type uint16; 29 | default 0; 30 | } 31 | leaf dropall-flow-hard-timeout { 32 | type uint16; 33 | default 0; 34 | } 35 | leaf dropall-flow-idle-timeout { 36 | type uint16; 37 | default 0; 38 | } 39 | leaf reactive-flow-table-id { 40 | type uint8; 41 | default 0; 42 | } 43 | leaf reactive-flow-priority { 44 | type uint16; 45 | default 10; 46 | } 47 | leaf reactive-flow-hard-timeout { 48 | type uint16; 49 | default 300; 50 | } 51 | leaf reactive-flow-idle-timeout { 52 | type uint16; 53 | default 600; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /l2switch-main/src/test/java/org/opendaylight/l2switch/flow/FlowWriterServiceImplTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.flow; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.Mockito.times; 12 | import static org.mockito.Mockito.verify; 13 | 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import org.mockito.Mock; 17 | import org.mockito.MockitoAnnotations; 18 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; 22 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; 27 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; 29 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 30 | 31 | public class FlowWriterServiceImplTest { 32 | @Mock 33 | private AddFlow addFlow; 34 | private FlowWriterServiceImpl flowWriterService; 35 | private InstanceIdentifier nodeConnectorInstanceIdentifier; 36 | private NodeConnectorRef nodeConnectorRef; 37 | 38 | @Before 39 | public void initMocks() { 40 | MockitoAnnotations.initMocks(this); 41 | flowWriterService = new FlowWriterServiceImpl(addFlow); 42 | } 43 | 44 | @Test 45 | public void addMacToMacFlowTest() { 46 | nodeConnectorInstanceIdentifier = InstanceIdentifier.builder(Nodes.class) 47 | .child(Node.class, new NodeKey(new NodeId("node-id"))) 48 | .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("nodeconnector-id"))).build(); 49 | nodeConnectorRef = new NodeConnectorRef(nodeConnectorInstanceIdentifier); 50 | 51 | MacAddress sourceMac = new MacAddress("00:00:ac:f0:01:01"); 52 | MacAddress destMac = new MacAddress("00:00:ac:f0:02:02"); 53 | 54 | flowWriterService.addMacToMacFlow(sourceMac, destMac, nodeConnectorRef); 55 | verify(addFlow, times(1)).invoke(any(AddFlowInput.class)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /l2switch-main/src/test/java/org/opendaylight/l2switch/flow/InitialFlowWriterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.flow; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.Mockito.times; 12 | import static org.mockito.Mockito.verify; 13 | import static org.mockito.Mockito.when; 14 | 15 | import java.util.List; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.mockito.Mock; 19 | import org.mockito.MockitoAnnotations; 20 | import org.opendaylight.mdsal.binding.api.DataObjectModification; 21 | import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; 22 | import org.opendaylight.mdsal.binding.api.DataTreeModification; 23 | import org.opendaylight.mdsal.common.api.LogicalDatastoreType; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 27 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 29 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; 30 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; 31 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 32 | 33 | public class InitialFlowWriterTest { 34 | @Mock 35 | private AddFlow addFlow; 36 | @Mock 37 | private DataTreeModification mockChange; 38 | @Mock 39 | private DataObjectModification mockModification; 40 | private InitialFlowWriter initialFlowWriter; 41 | 42 | @Before 43 | public void initMocks() { 44 | MockitoAnnotations.initMocks(this); 45 | initialFlowWriter = new InitialFlowWriter(addFlow); 46 | } 47 | 48 | @Test 49 | public void onDataChange_Valid() throws Exception { 50 | InstanceIdentifier instanceId = InstanceIdentifier.builder(Nodes.class) 51 | .child(Node.class, new NodeKey(new NodeId("openflow:1"))) 52 | .build(); 53 | Node topoNode = new NodeBuilder().setId(new NodeId("openflow:1")).build(); 54 | when(mockModification.getDataAfter()).thenReturn(topoNode); 55 | when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); 56 | when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, 57 | instanceId)); 58 | when(mockChange.getRootNode()).thenReturn(mockModification); 59 | 60 | initialFlowWriter.onDataTreeChanged(List.of(mockChange)); 61 | Thread.sleep(250); 62 | verify(addFlow, times(1)).invoke(any(AddFlowInput.class)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /l2switch-main/src/test/java/org/opendaylight/l2switch/flow/ReactiveFlowWriterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | 9 | package org.opendaylight.l2switch.flow; 10 | 11 | import static org.mockito.ArgumentMatchers.any; 12 | import static org.mockito.Mockito.times; 13 | import static org.mockito.Mockito.verify; 14 | import static org.mockito.Mockito.when; 15 | 16 | import java.util.ArrayList; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | import org.mockito.Mock; 20 | import org.mockito.MockitoAnnotations; 21 | import org.opendaylight.l2switch.inventory.InventoryReader; 22 | import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; 27 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceivedBuilder; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.arp.packet.received.packet.chain.packet.ArpPacketBuilder; 29 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 30 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChainBuilder; 31 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacketBuilder; 32 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFieldsBuilder; 33 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacketBuilder; 34 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 35 | 36 | public class ReactiveFlowWriterTest { 37 | 38 | @Mock 39 | private InventoryReader inventoryReader; 40 | @Mock 41 | private FlowWriterService flowWriterService; 42 | @Mock 43 | private NodeConnectorRef destNodeConnectorRef; 44 | private ReactiveFlowWriter reactiveFlowWriter; 45 | private InstanceIdentifier nodeInstanceIdentifier; 46 | private NodeConnectorRef nodeConnectorRef; 47 | private ArrayList packetChainList; 48 | 49 | 50 | 51 | @Before 52 | public void initMocks() { 53 | 54 | MockitoAnnotations.initMocks(this); 55 | reactiveFlowWriter = new ReactiveFlowWriter(inventoryReader, flowWriterService); 56 | 57 | nodeInstanceIdentifier = InstanceIdentifier.builder(Nodes.class).child(Node.class).build(); 58 | nodeConnectorRef = new NodeConnectorRef(nodeInstanceIdentifier); 59 | packetChainList = new ArrayList<>(); 60 | packetChainList.add(new PacketChainBuilder() 61 | .setPacket(new RawPacketBuilder() 62 | .setRawPacketFields(new RawPacketFieldsBuilder().setIngress(nodeConnectorRef).build()) 63 | .build()) 64 | .build()); 65 | packetChainList.add(new PacketChainBuilder() 66 | .setPacket(new EthernetPacketBuilder().setSourceMac(new MacAddress("00:00:00:00:00:01")).build()) 67 | .build()); 68 | packetChainList.add(new PacketChainBuilder() 69 | .setPacket(new ArpPacketBuilder().setSourceProtocolAddress("10.0.0.1").build()) 70 | .build()); 71 | } 72 | 73 | @Test 74 | public void onArpPacketReceivedTest() { 75 | 76 | ArpPacketReceived arpPacketReceived = new ArpPacketReceivedBuilder().setPacketChain(packetChainList).build(); 77 | reactiveFlowWriter.onNotification(arpPacketReceived); 78 | 79 | } 80 | 81 | @Test 82 | public void writeFlowsTest() { 83 | 84 | when(inventoryReader.getNodeConnector(any(InstanceIdentifier.class), any(MacAddress.class))) 85 | .thenReturn(destNodeConnectorRef); 86 | reactiveFlowWriter.writeFlows(nodeConnectorRef, new MacAddress("00:00:00:00:00:01"), 87 | new MacAddress("00:00:00:00:00:02")); 88 | 89 | verify(inventoryReader, times(1)).getNodeConnector(any(InstanceIdentifier.class), any(MacAddress.class)); 90 | verify(flowWriterService, times(1)).addBidirectionalMacToMacFlows(any(MacAddress.class), 91 | any(NodeConnectorRef.class), any(MacAddress.class), any(NodeConnectorRef.class)); 92 | 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /l2switch-main/src/test/java/org/opendaylight/l2switch/util/InstanceIdentifierUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.util; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | 13 | import org.junit.Test; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 22 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; 27 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 28 | import org.opendaylight.yangtools.yang.common.Uint8; 29 | 30 | public class InstanceIdentifierUtilsTest { 31 | private static final Uint8 NUM_ID_1 = Uint8.ONE; 32 | private static final String STR_ID_1 = "id1"; 33 | 34 | @Test 35 | public void testGenerateNodeInstanceIdentifier() throws Exception { 36 | NodeConnectorRef ncRef = new NodeConnectorRef( 37 | InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); 38 | assertNotNull(InstanceIdentifierUtils.generateNodeInstanceIdentifier(ncRef)); 39 | } 40 | 41 | @Test 42 | public void testGenerateFlowTableInstanceIdentifier() throws Exception { 43 | NodeConnectorRef ncRef = new NodeConnectorRef( 44 | InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); 45 | InstanceIdentifier
tableInsId = InstanceIdentifierUtils.generateFlowTableInstanceIdentifier(ncRef, 46 | new TableKey(NUM_ID_1)); 47 | assertNotNull(tableInsId); 48 | assertEquals(NUM_ID_1, tableInsId.firstKeyOf(Table.class).getId()); 49 | } 50 | 51 | @Test 52 | public void testGenerateFlowInstanceIdentifier() throws Exception { 53 | NodeConnectorRef ncRef = new NodeConnectorRef( 54 | InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId(STR_ID_1))) 55 | .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(STR_ID_1))).build()); 56 | InstanceIdentifier flowInsId = InstanceIdentifierUtils.generateFlowInstanceIdentifier(ncRef, 57 | new TableKey(NUM_ID_1), new FlowKey(new FlowId(STR_ID_1))); 58 | assertNotNull(flowInsId); 59 | assertEquals(NUM_ID_1, flowInsId.firstKeyOf(Table.class).getId()); 60 | assertEquals(STR_ID_1, flowInsId.firstKeyOf(Flow.class).getId().getValue()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /l2switch-main/src/test/resources/customtopo.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | 3 | # usage: sudo mn --controller=remote,ip= --switch=ovsk,protocols=OpenFlow13 --custom --topo ring ... 4 | 5 | from mininet.topo import Topo 6 | 7 | 8 | def add_hosts_to_switch(self, switch, hosts, start_host_suffix): 9 | host_suffix = start_host_suffix 10 | for _ in range(hosts): 11 | host = self.addHost("h%s" % host_suffix) 12 | self.addLink(switch, host) 13 | host_suffix += 1 14 | 15 | 16 | class RingTopo(Topo): 17 | def __init__(self, switches=3, hosts_per_switch=1, **opts): 18 | Topo.__init__(self, **opts) 19 | host_suffix = 1 20 | switch = self.addSwitch('s%s' % 1) 21 | first_switch = switch 22 | for i in range(1, switches): 23 | # add hosts to switch 24 | add_hosts_to_switch(self, switch, hosts_per_switch, host_suffix) 25 | host_suffix += hosts_per_switch 26 | 27 | new_switch = self.addSwitch('s%s' % (i + 1)) 28 | self.addLink(new_switch, switch) 29 | switch = new_switch 30 | 31 | add_hosts_to_switch(self, switch, hosts_per_switch, host_suffix) 32 | self.addLink(switch, first_switch) 33 | 34 | 35 | class MeshTopo(Topo): 36 | def __init__(self, switches=3, hosts_per_switch=1, **opts): 37 | Topo.__init__(self, **opts) 38 | created_switches = [] 39 | host_suffix = 1 40 | for i in range(switches): 41 | new_switch = self.addSwitch('s%s' % (i + 1)) 42 | 43 | # add hosts to new switch 44 | add_hosts_to_switch(self, new_switch, hosts_per_switch, host_suffix) 45 | host_suffix += hosts_per_switch 46 | 47 | for switch in created_switches: 48 | self.addLink(new_switch, switch) 49 | 50 | created_switches.append(new_switch) 51 | 52 | 53 | topos = {'ring': RingTopo, 54 | 'mesh': MeshTopo} 55 | -------------------------------------------------------------------------------- /loopremover/implementation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | org.opendaylight.l2switch 7 | l2switch-parent 8 | 0.8.0-SNAPSHOT 9 | ../../parent 10 | 11 | 4.0.0 12 | 13 | org.opendaylight.l2switch.loopremover 14 | loopremover-impl 15 | 16 | bundle 17 | 18 | 19 | 20 | org.opendaylight.l2switch.loopremover 21 | loopremover-model 22 | 23 | 24 | org.opendaylight.mdsal 25 | mdsal-binding-api 26 | 27 | 28 | org.opendaylight.mdsal.model 29 | ietf-topology 30 | 31 | 32 | org.opendaylight.openflowplugin 33 | openflowplugin-api 34 | 35 | 36 | org.opendaylight.openflowplugin.model 37 | model-flow-service 38 | 39 | 40 | org.opendaylight.openflowplugin.model 41 | model-inventory 42 | 43 | 44 | org.opendaylight.yangtools 45 | yang-common 46 | 47 | 48 | net.sf.jung 49 | jung-api 50 | 51 | 52 | net.sf.jung 53 | jung-algorithms 54 | 55 | 56 | net.sf.jung 57 | jung-graph-impl 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.apache.felix 65 | maven-bundle-plugin 66 | true 67 | 68 | 69 | 70 | org.apache.commons*, 71 | edu.uci.ics.jung.algorithms.blockmodel, 72 | edu.uci.ics.jung.algorithms.cluster, 73 | edu.uci.ics.jung.algorithms.filters, 74 | edu.uci.ics.jung.algorithms.flows, 75 | edu.uci.ics.jung.algorithms.generators, 76 | edu.uci.ics.jung.algorithms.generators.random, 77 | edu.uci.ics.jung.algorithms.layout, 78 | edu.uci.ics.jung.algorithms.layout.util, 79 | edu.uci.ics.jung.algorithms.metrics, 80 | edu.uci.ics.jung.algorithms.scoring, 81 | edu.uci.ics.jung.algorithms.scoring.util, 82 | edu.uci.ics.jung.algorithms.shortestpath, 83 | edu.uci.ics.jung.algorithms.transformation, 84 | edu.uci.ics.jung.algorithms.util, 85 | edu.uci.ics.jung.graph;-split-package:=merge-first, 86 | edu.uci.ics.jung.graph.event, 87 | edu.uci.ics.jung.graph.util;-split-package:=merge-first, 88 | org.opendaylight.yang.gen.v1.urn.opendaylight.packet.loop.remover.impl.rev140528 89 | , 90 | * 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/LoopRemoverProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.loopremover; 9 | 10 | import org.opendaylight.l2switch.loopremover.flow.InitialFlowWriter; 11 | import org.opendaylight.l2switch.loopremover.topology.NetworkGraphImpl; 12 | import org.opendaylight.l2switch.loopremover.topology.TopologyLinkDataChangeHandler; 13 | import org.opendaylight.mdsal.binding.api.DataBroker; 14 | import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.loop.remover.config.rev140528.LoopRemoverConfig; 17 | import org.opendaylight.yangtools.concepts.Registration; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | // FIXME: @Component(service = { }) once we have the required constructor 22 | public final class LoopRemoverProvider implements AutoCloseable { 23 | private static final Logger LOG = LoggerFactory.getLogger(LoopRemoverProvider.class); 24 | 25 | private Registration listenerRegistration; 26 | private Registration topoNodeListnerReg; 27 | 28 | // FIXME: an @Activate constructor which deals with dynamic config 29 | public LoopRemoverProvider(final DataBroker dataBroker, final RpcConsumerRegistry rpcService, 30 | final LoopRemoverConfig config) { 31 | // Write initial flows 32 | if (config.getIsInstallLldpFlow()) { 33 | LOG.info("LoopRemover will install an lldp flow"); 34 | var initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); 35 | initialFlowWriter.setFlowTableId(config.getLldpFlowTableId()); 36 | initialFlowWriter.setFlowPriority(config.getLldpFlowPriority()); 37 | initialFlowWriter.setFlowIdleTimeout(config.getLldpFlowIdleTimeout()); 38 | initialFlowWriter.setFlowHardTimeout(config.getLldpFlowHardTimeout()); 39 | topoNodeListnerReg = initialFlowWriter.registerAsDataChangeListener(dataBroker); 40 | } 41 | 42 | // Register Topology DataChangeListener 43 | var topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBroker, new NetworkGraphImpl()); 44 | topologyLinkDataChangeHandler.setGraphRefreshDelay(config.getGraphRefreshDelay().toJava()); 45 | topologyLinkDataChangeHandler.setTopologyId(config.getTopologyId()); 46 | listenerRegistration = topologyLinkDataChangeHandler.registerAsDataChangeListener(); 47 | LOG.info("LoopRemover initialized."); 48 | } 49 | 50 | // FIXME: @Deactivate 51 | @Override 52 | public void close() { 53 | if (listenerRegistration != null) { 54 | listenerRegistration.close(); 55 | listenerRegistration = null; 56 | } 57 | if (topoNodeListnerReg != null) { 58 | topoNodeListnerReg.close(); 59 | topoNodeListnerReg = null; 60 | } 61 | LOG.info("LoopRemover (instance {}) torn down.", this); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/NetworkGraphService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.loopremover.topology; 9 | 10 | import java.util.List; 11 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; 12 | 13 | /** 14 | * Service that allows to build a network graph using Topology links {@link 15 | * org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link} 16 | * and exposes operation that can be performed on such graph. 17 | */ 18 | public interface NetworkGraphService { 19 | 20 | /** 21 | * Adds links to existing graph or creates new graph with given links if 22 | * graph was not initialized. 23 | * 24 | * @param links the links to add 25 | */ 26 | void addLinks(List links); 27 | 28 | /** 29 | * Removes links from existing graph. 30 | * 31 | * @param links the links to remove 32 | */ 33 | void removeLinks(List links); 34 | 35 | /** 36 | * Returns a path between 2 nodes. Implementation should ideally return 37 | * shortest path. 38 | * 39 | * @param sourceNodeId the source node Id 40 | * @param destinationNodeId the destination node Id 41 | */ 42 | // List getPath(NodeId sourceNodeId, NodeId destinationNodeId); 43 | 44 | /** 45 | * Forms MST(minimum spanning tree) from network graph and returns links 46 | * that are not in MST. 47 | */ 48 | List getLinksInMst(); 49 | 50 | /** 51 | * Returns all the links in current network graph. 52 | */ 53 | List getAllLinks(); 54 | 55 | /** 56 | * Clears the prebuilt graph, in case same service instance is required to 57 | * process a new graph. 58 | */ 59 | void clear(); 60 | } 61 | -------------------------------------------------------------------------------- /loopremover/implementation/src/main/resources/org/opendaylight/blueprint/loop-remover.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 9 | 10 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /loopremover/implementation/src/main/yang/loop-remover-config.yang: -------------------------------------------------------------------------------- 1 | module loop-remover-config { 2 | 3 | yang-version 1; 4 | namespace "urn:opendaylight:packet:loop-remover-config"; 5 | prefix "loop-remover-config"; 6 | 7 | description 8 | "This module contains the base configuration for loop-remover 9 | implementation."; 10 | 11 | revision 2014-05-28 { 12 | description "Initial module draft."; 13 | } 14 | 15 | container loop-remover-config { 16 | leaf is-install-lldp-flow { 17 | type boolean; 18 | default true; 19 | } 20 | leaf lldp-flow-table-id { 21 | type uint8; 22 | default 0; 23 | } 24 | leaf lldp-flow-priority { 25 | type uint16; 26 | default 100; 27 | } 28 | leaf lldp-flow-hard-timeout { 29 | type uint16; 30 | default 0; 31 | } 32 | leaf lldp-flow-idle-timeout { 33 | type uint16; 34 | default 0; 35 | } 36 | leaf graph-refresh-delay { 37 | type uint32; 38 | default 1000; 39 | } 40 | leaf topology-id { 41 | type string; 42 | default "flow:1"; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/flow/InitialFlowWriterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.loopremover.flow; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.Mockito.timeout; 12 | import static org.mockito.Mockito.verify; 13 | import static org.mockito.Mockito.when; 14 | 15 | import java.util.List; 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.Test; 18 | import org.junit.jupiter.api.extension.ExtendWith; 19 | import org.mockito.Mock; 20 | import org.mockito.junit.jupiter.MockitoExtension; 21 | import org.opendaylight.mdsal.binding.api.DataObjectModification; 22 | import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; 23 | import org.opendaylight.mdsal.binding.api.DataTreeModification; 24 | import org.opendaylight.mdsal.common.api.LogicalDatastoreType; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 27 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 28 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 29 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; 30 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 31 | 32 | @ExtendWith(MockitoExtension.class) 33 | class InitialFlowWriterTest { 34 | @Mock 35 | private AddFlow addFlow; 36 | @Mock 37 | private DataTreeModification mockChange; 38 | @Mock 39 | private DataObjectModification mockModification; 40 | private InitialFlowWriter initialFlowWriter; 41 | 42 | @BeforeEach 43 | void beforeEach() { 44 | initialFlowWriter = new InitialFlowWriter(addFlow); 45 | } 46 | 47 | @Test 48 | void onDataChange_Valid() { 49 | var instanceId = InstanceIdentifier.builder(Nodes.class) 50 | .child(Node.class, new NodeKey(new NodeId("openflow:1"))) 51 | .build(); 52 | 53 | when(mockModification.getDataBefore()).thenReturn(null); 54 | when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); 55 | when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, 56 | instanceId)); 57 | when(mockChange.getRootNode()).thenReturn(mockModification); 58 | 59 | initialFlowWriter.onDataTreeChanged(List.of(mockChange)); 60 | verify(addFlow, timeout(250)).invoke(any()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.loopremover.util; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | 13 | import org.junit.Test; 14 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; 15 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; 16 | import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; 17 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; 18 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; 22 | import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; 23 | import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; 24 | import org.opendaylight.yangtools.yang.common.Uint8; 25 | 26 | public class InstanceIdentifierUtilsTest { 27 | 28 | private static final Uint8 NUM_ID_1 = Uint8.ONE; 29 | private static final String STR_ID_1 = "id1"; 30 | private static final String STR_ID_2 = "id2"; 31 | 32 | @Test 33 | public void testCreateNodePath() throws Exception { 34 | InstanceIdentifier insId = InstanceIdentifierUtils.createNodePath(new NodeId(STR_ID_1)); 35 | assertNotNull(insId); 36 | assertNotNull(insId.firstIdentifierOf(Nodes.class)); 37 | assertEquals(STR_ID_1, insId.firstKeyOf(Node.class).getId().getValue()); 38 | } 39 | 40 | @Test 41 | public void testGetNodePath() throws Exception { 42 | InstanceIdentifier ncInsId = InstanceIdentifier.builder(Nodes.class).child(Node.class) 43 | .child(NodeConnector.class).build(); 44 | assertNotNull(InstanceIdentifierUtils.getNodePath(ncInsId)); 45 | } 46 | 47 | @Test 48 | public void testCreateTablePath() throws Exception { 49 | InstanceIdentifier
tableInsId = InstanceIdentifierUtils.createTablePath( 50 | InstanceIdentifier.builder(Nodes.class).child(Node.class).build(), new TableKey(NUM_ID_1)); 51 | assertNotNull(tableInsId); 52 | assertEquals(NUM_ID_1.shortValue(), tableInsId.firstKeyOf(Table.class).getId().shortValue()); 53 | assertNotNull(tableInsId.firstIdentifierOf(FlowCapableNode.class)); 54 | } 55 | 56 | @Test 57 | public void testCreateNodeConnectorIdentifier() throws Exception { 58 | InstanceIdentifier ncInsId = InstanceIdentifierUtils.createNodeConnectorIdentifier(STR_ID_1, 59 | STR_ID_2); 60 | assertNotNull(ncInsId); 61 | assertEquals(STR_ID_1, ncInsId.firstKeyOf(Node.class).getId().getValue()); 62 | assertEquals(STR_ID_2, ncInsId.firstKeyOf(NodeConnector.class).getId().getValue()); 63 | } 64 | 65 | @Test 66 | public void testGenerateNodeInstanceIdentifier() throws Exception { 67 | NodeConnectorRef ncRef = new NodeConnectorRef( 68 | InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); 69 | assertNotNull(InstanceIdentifierUtils.generateNodeInstanceIdentifier(ncRef)); 70 | } 71 | 72 | @Test 73 | public void testGenerateFlowTableInstanceIdentifier() throws Exception { 74 | NodeConnectorRef ncRef = new NodeConnectorRef( 75 | InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); 76 | InstanceIdentifier
tableInsId = InstanceIdentifierUtils.generateFlowTableInstanceIdentifier(ncRef, 77 | new TableKey(NUM_ID_1)); 78 | assertNotNull(tableInsId); 79 | assertEquals(NUM_ID_1, tableInsId.firstKeyOf(Table.class).getId()); 80 | } 81 | 82 | @Test 83 | public void testGenerateTopologyInstanceIdentifier() throws Exception { 84 | InstanceIdentifier topologyInsId = InstanceIdentifierUtils 85 | .generateTopologyInstanceIdentifier(STR_ID_1); 86 | assertNotNull(topologyInsId); 87 | assertEquals(STR_ID_1, topologyInsId.firstKeyOf(Topology.class).getTopologyId().getValue()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /loopremover/model/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.l2switch 6 | l2switch-parent 7 | 0.8.0-SNAPSHOT 8 | ../../parent 9 | 10 | org.opendaylight.l2switch.loopremover 11 | loopremover-model 12 | bundle 13 | 14 | 15 | 16 | org.opendaylight.openflowplugin.model 17 | model-inventory 18 | 19 | 20 | org.opendaylight.yangtools 21 | yang-common 22 | 23 | 24 | org.opendaylight.mdsal.binding.model.ietf 25 | rfc6991-ietf-yang-types 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.felix 33 | maven-bundle-plugin 34 | true 35 | 36 | 37 | ${project.groupId}.${project.artifactId} 38 | org.opendaylight.yangtools.yang.binding.annotations, * 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /loopremover/model/src/main/yang/stp-status-aware-nodeconnnector.yang: -------------------------------------------------------------------------------- 1 | module stp-status-aware-node-connector { 2 | yang-version 1; 3 | namespace "urn:opendaylight:l2switch:loopremover"; 4 | prefix loopremover; 5 | 6 | 7 | import yang-ext { 8 | prefix ext; 9 | revision-date "2013-07-09"; 10 | } 11 | import opendaylight-inventory { 12 | prefix inv; 13 | revision-date 2013-08-19; 14 | } 15 | 16 | description 17 | "Loop remover stp status aware Data Model"; 18 | 19 | revision 2014-07-14 { 20 | description 21 | "l2switch loop remover module draft."; 22 | } 23 | 24 | typedef stp-status { 25 | type enumeration { 26 | enum "forwarding" { 27 | value 1; 28 | description "STP Status Forwarding"; 29 | } 30 | enum "discarding" { 31 | value 2; 32 | description "STP status discarding"; 33 | } 34 | } 35 | } 36 | 37 | grouping stp-status-node-connector { 38 | leaf status { 39 | type stp-status; 40 | } 41 | } 42 | augment "/inv:nodes/inv:node/inv:node-connector" { 43 | ext:augment-identifier "stp-status-aware-node-connector"; 44 | uses stp-status-node-connector; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /loopremover/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.opendaylight.l2switch 9 | l2switch-parent 10 | 0.8.0-SNAPSHOT 11 | ../parent 12 | 13 | 14 | org.opendaylight.l2switch.loopremover 15 | loopremover.aggregator 16 | pom 17 | 18 | 19 | model 20 | implementation 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /packethandler/implementation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.opendaylight.l2switch 7 | l2switch-parent 8 | 0.8.0-SNAPSHOT 9 | ../../parent 10 | 11 | org.opendaylight.l2switch.packethandler 12 | packethandler-impl 13 | bundle 14 | 15 | 16 | 17 | com.github.spotbugs 18 | spotbugs-annotations 19 | true 20 | 21 | 22 | org.opendaylight.l2switch.packethandler 23 | packethandler-model 24 | 25 | 26 | org.opendaylight.mdsal 27 | mdsal-binding-api 28 | 29 | 30 | org.opendaylight.openflowplugin.model 31 | model-flow-service 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.felix 39 | maven-bundle-plugin 40 | true 41 | 42 | 43 | 44 | org.opendaylight.l2switch.packethandler.decoders, 45 | org.opendaylight.l2switch.packethandler.decoders.utils, 46 | 47 | * 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/PacketHandlerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.packethandler; 9 | 10 | import com.google.common.collect.ImmutableSet; 11 | import org.opendaylight.l2switch.packethandler.decoders.AbstractPacketDecoder; 12 | import org.opendaylight.l2switch.packethandler.decoders.ArpDecoder; 13 | import org.opendaylight.l2switch.packethandler.decoders.EthernetDecoder; 14 | import org.opendaylight.l2switch.packethandler.decoders.IcmpDecoder; 15 | import org.opendaylight.l2switch.packethandler.decoders.Ipv4Decoder; 16 | import org.opendaylight.l2switch.packethandler.decoders.Ipv6Decoder; 17 | import org.opendaylight.mdsal.binding.api.NotificationPublishService; 18 | import org.opendaylight.mdsal.binding.api.NotificationService; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | public class PacketHandlerProvider implements AutoCloseable { 23 | private static final Logger LOG = LoggerFactory.getLogger(PacketHandlerProvider.class); 24 | 25 | private final ImmutableSet> decoders; 26 | 27 | public PacketHandlerProvider(final NotificationPublishService notificationPublishService, 28 | final NotificationService notificationService) { 29 | decoders = new ImmutableSet.Builder>() 30 | .add(new EthernetDecoder(notificationPublishService, notificationService)) 31 | .add(new ArpDecoder(notificationPublishService, notificationService)) 32 | .add(new Ipv4Decoder(notificationPublishService, notificationService)) 33 | .add(new Ipv6Decoder(notificationPublishService, notificationService)) 34 | .add(new IcmpDecoder(notificationPublishService, notificationService)) 35 | .build(); 36 | LOG.info("PacketHandler initialized."); 37 | } 38 | 39 | @Override 40 | public void close() { 41 | for (AbstractPacketDecoder decoder : decoders) { 42 | decoder.close(); 43 | } 44 | LOG.info("PacketHandler (instance {}) torn down.", this); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/AbstractPacketDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.packethandler.decoders; 9 | 10 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | import org.opendaylight.mdsal.binding.api.NotificationPublishService; 14 | import org.opendaylight.mdsal.binding.api.NotificationService; 15 | import org.opendaylight.mdsal.binding.api.NotificationService.Listener; 16 | import org.opendaylight.yangtools.concepts.Registration; 17 | import org.opendaylight.yangtools.yang.binding.Notification; 18 | 19 | /** 20 | * A base class for all decoders. Each extended decoder should also implement a notification listener that it can 21 | * consume. 22 | */ 23 | // FIXME: Implement NotificationInterestLister equivalent when it is available again 24 | public abstract class AbstractPacketDecoder 25 | implements AutoCloseable { 26 | 27 | //private final Class

producedPacketNotificationType; 28 | private final NotificationPublishService notificationProviderService; 29 | private final NotificationService notificationService; 30 | 31 | private static final int CPUS = Runtime.getRuntime().availableProcessors(); 32 | private final ExecutorService decodeAndPublishExecutor = Executors.newFixedThreadPool(CPUS); 33 | 34 | protected Registration listenerRegistration; 35 | 36 | /** 37 | * Constructor. 38 | */ 39 | @SuppressFBWarnings("MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR") 40 | public AbstractPacketDecoder(Class

producedPacketNotificationType, 41 | NotificationPublishService notificationProviderService, 42 | NotificationService notificationService) { 43 | // this.producedPacketNotificationType = producedPacketNotificationType; 44 | this.notificationProviderService = notificationProviderService; 45 | this.notificationService = notificationService; 46 | listenerRegistration = this.notificationService.registerListener(getPacketType(), getConsumedListener()); 47 | } 48 | 49 | // /** 50 | // * Keeps track of listeners registered for the notification that a decoder 51 | // * produces. 52 | // */ 53 | // public synchronized void onNotificationSubscribtion(Class clazz) { 54 | // if (clazz != null && clazz.equals(producedPacketNotificationType)) { 55 | // if (listenerRegistration == null) { 56 | // NotificationListener notificationListener = getConsumedNotificationListener(); 57 | // listenerRegistration = notificationProviderService.registerNotificationListener(notificationListener); 58 | // } 59 | // } 60 | // } 61 | 62 | /** 63 | * Every extended decoder should call this method on a receipt of a input 64 | * packet notification. This method would make sure it decodes only when 65 | * necessary and publishes corresponding event on successful decoding. 66 | */ 67 | public void decodeAndPublish(final C consumedPacketNotification) { 68 | decodeAndPublishExecutor.execute(() -> { 69 | P packetNotification = null; 70 | if (consumedPacketNotification != null && canDecode(consumedPacketNotification)) { 71 | packetNotification = decode(consumedPacketNotification); 72 | } 73 | if (packetNotification != null) { 74 | try { 75 | notificationProviderService.putNotification(packetNotification); 76 | } catch (InterruptedException e) { 77 | Thread.currentThread().interrupt(); 78 | throw new IllegalStateException("Interrupted while publishing notification", e); 79 | } 80 | } 81 | }); 82 | } 83 | 84 | /** 85 | * Decodes the payload in given Packet further and returns a extension of 86 | * Packet. e.g. ARP, IPV4, LLDP etc. 87 | */ 88 | public abstract P decode(C consumedPacketNotification); 89 | 90 | public abstract Listener getConsumedListener(); 91 | 92 | public abstract Class getPacketType(); 93 | 94 | public abstract boolean canDecode(C consumedPacketNotification); 95 | 96 | @Override 97 | public void close() { 98 | if (listenerRegistration != null) { 99 | listenerRegistration.close(); 100 | } 101 | decodeAndPublishExecutor.shutdown(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/utils/BufferException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.packethandler.decoders.utils; 9 | 10 | /** 11 | * Describes an exception that is raised during BitBufferHelper operations. 12 | */ 13 | public class BufferException extends Exception { 14 | private static final long serialVersionUID = 1L; 15 | 16 | public BufferException(String message) { 17 | super(message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/utils/HexEncode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.packethandler.decoders.utils; 9 | 10 | import java.math.BigInteger; 11 | 12 | /** 13 | * The class provides methods to convert hex encode strings. 14 | */ 15 | public final class HexEncode { 16 | private HexEncode() { 17 | } 18 | 19 | /** 20 | * This method converts byte array into String format without ":" inserted. 21 | * 22 | * @param bytes 23 | * The byte array to convert to string 24 | * @return The hexadecimal representation of the byte array. If bytes is 25 | * null, "null" string is returned 26 | */ 27 | public static String bytesToHexString(byte[] bytes) { 28 | 29 | if (bytes == null) { 30 | return "null"; 31 | } 32 | 33 | String ret = ""; 34 | StringBuilder sb = new StringBuilder(); 35 | for (int i = 0; i < bytes.length; i++) { 36 | if (i > 0) { 37 | ret += ":"; 38 | } 39 | short u8byte = (short) (bytes[i] & 0xff); 40 | String tmp = Integer.toHexString(u8byte); 41 | if (tmp.length() == 1) { 42 | sb.append("0"); 43 | } 44 | sb.append(tmp); 45 | } 46 | ret = sb.toString(); 47 | return ret; 48 | } 49 | 50 | public static String longToHexString(long val) { 51 | char[] arr = Long.toHexString(val).toCharArray(); 52 | StringBuilder sb = new StringBuilder(); 53 | // prepend the right number of leading zeros 54 | int index = 0; 55 | for (; index < 16 - arr.length; index++) { 56 | sb.append("0"); 57 | if ((index & 0x01) == 1) { 58 | sb.append(":"); 59 | } 60 | } 61 | for (int j = 0; j < arr.length; j++) { 62 | sb.append(arr[j]); 63 | if ((index + j & 0x01) == 1 && j < arr.length - 1) { 64 | sb.append(":"); 65 | } 66 | } 67 | return sb.toString(); 68 | } 69 | 70 | public static byte[] bytesFromHexString(String values) { 71 | String target = ""; 72 | if (values != null) { 73 | target = values; 74 | } 75 | String[] octets = target.split(":"); 76 | 77 | byte[] ret = new byte[octets.length]; 78 | for (int i = 0; i < octets.length; i++) { 79 | ret[i] = Integer.valueOf(octets[i], 16).byteValue(); 80 | } 81 | return ret; 82 | } 83 | 84 | public static long stringToLong(String values) { 85 | return new BigInteger(values.replaceAll(":", ""), 16).longValue(); 86 | } 87 | 88 | /** 89 | * This method converts byte array into HexString format with ":" inserted. 90 | */ 91 | public static String bytesToHexStringFormat(byte[] bytes) { 92 | if (bytes == null) { 93 | return "null"; 94 | } 95 | String ret = ""; 96 | StringBuilder sb = new StringBuilder(); 97 | for (int i = 0; i < bytes.length; i++) { 98 | if (i > 0) { 99 | sb.append(":"); 100 | } 101 | short u8byte = (short) (bytes[i] & 0xff); 102 | String tmp = Integer.toHexString(u8byte); 103 | if (tmp.length() == 1) { 104 | sb.append("0"); 105 | } 106 | sb.append(tmp); 107 | } 108 | ret = sb.toString(); 109 | return ret; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /packethandler/implementation/src/main/resources/org/opendaylight/blueprint/packet-handler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packethandler/implementation/src/test/java/org/opendaylight/l2switch/packethandler/decoders/IcmpDecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.packethandler.decoders; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import org.junit.Test; 16 | import org.mockito.Mockito; 17 | import org.opendaylight.mdsal.binding.api.NotificationPublishService; 18 | import org.opendaylight.mdsal.binding.api.NotificationService; 19 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain; 20 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChainBuilder; 21 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacketBuilder; 22 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacketBuilder; 23 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.icmp.rev140528.IcmpPacketReceived; 24 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.icmp.rev140528.icmp.packet.received.packet.chain.packet.IcmpPacket; 25 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv4.rev140528.Ipv4PacketReceivedBuilder; 26 | import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv4.rev140528.ipv4.packet.received.packet.chain.packet.Ipv4PacketBuilder; 27 | import org.opendaylight.yangtools.yang.common.Uint32; 28 | 29 | public class IcmpDecoderTest { 30 | @Test 31 | public void testDecode() throws Exception { 32 | byte[] ethPayload = { 0x00, 0x0c, (byte) 0xce, 0x13, (byte) 0xb9, (byte) 0xa0, 0x00, 0x22, 0x5f, 0x3f, 33 | (byte) 0x98, (byte) 0x91, 0x08, 0x00, // ethernet 34 | 0x45, 0x00, 0x00, 0x3c, (byte) 0xc6, 0x3e, 0x00, 0x00, (byte) 0x80, 0x01, (byte) 0xf2, (byte) 0xd7, 35 | (byte) 0xc0, (byte) 0xa8, 0x00, 36 | 0x59, (byte) 0xc0, (byte) 0xa8, 0x00, 0x01, // ipv4 37 | 0x08, // Type = 8 (Echo request) 38 | 0x00, // Code = 0 39 | 0x42, // Checksum (+ next byte) 40 | 0x5c, // 41 | 0x02, // Identifier (+ next byte) 42 | 0x00, // 43 | 0x09, // Sequence number (+ next byte) 44 | 0x00, // 45 | 0, 0, 0, 0 // CRC 46 | }; 47 | 48 | NotificationPublishService npServiceMock = Mockito.mock(NotificationPublishService.class); 49 | NotificationService mock2 = Mockito.mock(NotificationService.class); 50 | ArrayList packetChainList = new ArrayList<>(); 51 | packetChainList.add(new PacketChainBuilder().setPacket(new RawPacketBuilder().build()).build()); 52 | packetChainList.add(new PacketChainBuilder().setPacket(new EthernetPacketBuilder().build()).build()); 53 | packetChainList.add(new PacketChainBuilder() 54 | .setPacket(new Ipv4PacketBuilder() 55 | .setPayloadOffset(Uint32.valueOf(34)) 56 | .build()) 57 | .build()); 58 | 59 | IcmpPacketReceived notification = new IcmpDecoder(npServiceMock, mock2) 60 | .decode(new Ipv4PacketReceivedBuilder().setPacketChain(packetChainList).setPayload(ethPayload) 61 | .build()); 62 | 63 | IcmpPacket icmpPacket = (IcmpPacket) notification.getPacketChain().get(3).getPacket(); 64 | assertEquals(8, icmpPacket.getType().intValue()); 65 | assertEquals(0, icmpPacket.getCode().intValue()); 66 | assertEquals(0x425c, icmpPacket.getCrc().intValue()); 67 | assertEquals(512, icmpPacket.getIdentifier().intValue()); 68 | assertEquals(2304, icmpPacket.getSequenceNumber().intValue()); 69 | 70 | assertTrue(Arrays.equals(ethPayload, notification.getPayload())); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packethandler/implementation/src/test/java/org/opendaylight/l2switch/packethandler/decoders/utils/HexEncodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package org.opendaylight.l2switch.packethandler.decoders.utils; 9 | 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | public class HexEncodeTest { 14 | @Test 15 | public void testbytesToHexString() { 16 | byte[] bytes1 = { (byte) 0x01, (byte) 0x02, (byte) 0x03 }; 17 | String str1 = HexEncode.bytesToHexString(bytes1); 18 | Assert.assertTrue(str1.equals("010203")); 19 | 20 | byte[] bytes2 = { (byte) 0x11, (byte) 0x22, (byte) 0x33 }; 21 | String str2 = HexEncode.bytesToHexString(bytes2); 22 | Assert.assertFalse(str2.equals("010203")); 23 | 24 | } 25 | 26 | @Test 27 | public void testLongToHexString() { 28 | long value1 = 12345678L; 29 | String str1 = HexEncode.longToHexString(value1); 30 | Assert.assertTrue(str1.equals("00:00:00:00:00:bc:61:4e")); 31 | 32 | long value2 = 98765432L; 33 | String str2 = HexEncode.longToHexString(value2); 34 | Assert.assertFalse(str2.equals("00:44:33:22:11:bc:61:4e")); 35 | 36 | } 37 | 38 | @Test 39 | public void testBytesFromHexString() { 40 | String byteStr1 = "00:11:22:33:44:55"; 41 | byte[] byteArray1 = new byte[(byteStr1.length() + 1) / 3]; 42 | byteArray1 = HexEncode.bytesFromHexString(byteStr1); 43 | 44 | Assert.assertTrue(byteArray1[0] == (byte) 0x0); 45 | Assert.assertTrue(byteArray1[1] == (byte) 0x11); 46 | Assert.assertTrue(byteArray1[2] == (byte) 0x22); 47 | Assert.assertTrue(byteArray1[3] == (byte) 0x33); 48 | Assert.assertTrue(byteArray1[4] == (byte) 0x44); 49 | Assert.assertTrue(byteArray1[5] == (byte) 0x55); 50 | 51 | String byteStr2 = "00:11:22:33:44:55"; 52 | byte[] byteArray2 = new byte[(byteStr2.length() + 1) / 3]; 53 | byteArray2 = HexEncode.bytesFromHexString(byteStr2); 54 | 55 | Assert.assertFalse(byteArray2[0] == (byte) 0x55); 56 | Assert.assertFalse(byteArray2[1] == (byte) 0x44); 57 | Assert.assertFalse(byteArray2[2] == (byte) 0x33); 58 | Assert.assertFalse(byteArray2[3] == (byte) 0x22); 59 | Assert.assertFalse(byteArray2[4] == (byte) 0x11); 60 | Assert.assertFalse(byteArray2[5] == (byte) 0x0); 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packethandler/model/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.opendaylight.l2switch 6 | l2switch-parent 7 | 0.8.0-SNAPSHOT 8 | ../../parent 9 | 10 | org.opendaylight.l2switch.packethandler 11 | packethandler-model 12 | bundle 13 | 14 | 15 | 16 | org.opendaylight.openflowplugin.model 17 | model-flow-service 18 | 19 | 20 | org.opendaylight.mdsal.binding.model.ietf 21 | rfc6991-ietf-yang-types 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.felix 29 | maven-bundle-plugin 30 | true 31 | 32 | 33 | ${project.groupId}.${project.artifactId} 34 | org.opendaylight.yangtools.yang.binding.annotations, * 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /packethandler/model/src/main/yang/arp-packet.yang: -------------------------------------------------------------------------------- 1 | module arp-packet { 2 | yang-version 1; 3 | namespace "urn:opendaylight:packet:arp"; 4 | prefix arp; 5 | 6 | import ietf-inet-types { 7 | prefix inet; 8 | revision-date 2013-07-15; 9 | } 10 | import base-packet { 11 | prefix bpacket; 12 | revision-date 2014-05-28; 13 | } 14 | import ethernet-packet { 15 | prefix ethernet; 16 | revision-date 2014-05-28; 17 | } 18 | 19 | revision 2014-05-28 { 20 | description 21 | "ARP packet module draft."; 22 | } 23 | 24 | typedef known-hardware-type { 25 | type enumeration { 26 | enum "reserved" { 27 | value 0; 28 | } 29 | enum "ethernet" { 30 | value 1; 31 | } 32 | } 33 | } 34 | 35 | typedef known-operation { 36 | type enumeration { 37 | enum "reserved" { 38 | value 0; 39 | } 40 | enum "request" { 41 | value 1; 42 | } 43 | enum "reply" { 44 | value 2; 45 | } 46 | enum "request reverse" { 47 | value 3; 48 | } 49 | enum "reply reverse" { 50 | value 4; 51 | } 52 | } 53 | } 54 | 55 | 56 | grouping arp-packet-fields { 57 | leaf hardware-type { 58 | type known-hardware-type; 59 | description "Network protocol type"; 60 | } 61 | 62 | leaf protocol-type { 63 | type ethernet:known-ether-type; 64 | description "Higher layer protocol for which the ARP request is intended. This corresponds to EtherType."; 65 | } 66 | 67 | leaf hardware-length { 68 | type uint8; 69 | description "Length (in octets) of a hardware address. Ethernet address size is 6."; 70 | } 71 | 72 | leaf protocol-length { 73 | type uint8; 74 | description "Length (in octets) of addresses used in the higher layer protocol. IPv4 address size is 4."; 75 | } 76 | 77 | leaf operation { 78 | type known-operation; 79 | description "Specifies the operation that the sender is performing: 1 for request, 2 for reply."; 80 | } 81 | 82 | leaf source-hardware-address { 83 | type string; 84 | description "Media address of the sender."; 85 | } 86 | 87 | leaf source-protocol-address { 88 | type string; 89 | description "Internet address of the sender."; 90 | } 91 | 92 | leaf destination-hardware-address { 93 | type string; 94 | description "Media address of the destination/target."; 95 | } 96 | 97 | leaf destination-protocol-address { 98 | type string; 99 | description "Internet address of the destination/target."; 100 | } 101 | 102 | uses bpacket:packet-fields; 103 | } 104 | 105 | notification arp-packet-received { 106 | uses bpacket:packet-chain-grp { 107 | augment "packet-chain/packet" { 108 | case arp-packet { 109 | uses arp-packet-fields; 110 | } 111 | } 112 | } 113 | uses bpacket:packet-payload; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /packethandler/model/src/main/yang/icmp-packet.yang: -------------------------------------------------------------------------------- 1 | module icmp-packet { 2 | yang-version 1; 3 | namespace "urn:opendaylight:packet:icmp"; 4 | prefix icmp; 5 | 6 | import base-packet { prefix bpacket; revision-date 2014-05-28; } 7 | 8 | revision 2014-05-28 { 9 | description 10 | "ICMP packet module draft."; 11 | } 12 | 13 | grouping icmp-packet-fields { 14 | leaf type { 15 | type uint8; 16 | } 17 | 18 | leaf code { 19 | type uint8; 20 | } 21 | 22 | leaf crc { 23 | type uint16; 24 | } 25 | 26 | leaf identifier { 27 | type uint16; 28 | } 29 | 30 | leaf sequence-number { 31 | type uint16; 32 | } 33 | 34 | uses bpacket:packet-fields; 35 | } 36 | 37 | notification icmp-packet-received { 38 | uses bpacket:packet-chain-grp { 39 | augment "packet-chain/packet" { 40 | case icmp-packet { 41 | uses icmp-packet-fields; 42 | } 43 | } 44 | } 45 | uses bpacket:packet-payload; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packethandler/model/src/main/yang/ipv6-packet.yang: -------------------------------------------------------------------------------- 1 | module ipv6-packet { 2 | yang-version 1; 3 | namespace "urn:opendaylight:packet:ipv6"; 4 | prefix ipv6; 5 | 6 | import ietf-inet-types { 7 | prefix inet; 8 | revision-date 2013-07-15; 9 | } 10 | 11 | import base-packet { 12 | prefix bpacket; 13 | revision-date 2014-05-28; 14 | } 15 | 16 | import ipv4-packet { 17 | prefix ipv4; 18 | revision-date 2014-05-28; 19 | } 20 | 21 | revision 2014-05-28 { 22 | description 23 | "IPv6 packet module draft."; 24 | } 25 | 26 | grouping ipv6-packet-fields { 27 | leaf version { 28 | type uint8; 29 | } 30 | 31 | leaf dscp { 32 | type inet:dscp; 33 | description "Differentiated Code Services Point"; 34 | } 35 | 36 | leaf ecn { 37 | type uint8; 38 | description "Explicit Congestion Notification"; 39 | } 40 | 41 | leaf flow-label { 42 | type uint32; 43 | description "Flow label"; 44 | } 45 | 46 | leaf ipv6-length { 47 | type uint16; 48 | description "Payload size including any extension headers, in octets"; 49 | } 50 | 51 | leaf next-header { 52 | type ipv4:known-ip-protocols; 53 | description "Type of the next header or Transport layer protocol used by the payload"; 54 | } 55 | 56 | leaf hop-limit { 57 | type uint8; 58 | description "Hop Limit, the IPv6 version of Time to live"; 59 | } 60 | 61 | leaf source-ipv6 { 62 | type inet:ipv6-address; 63 | } 64 | 65 | leaf destination-ipv6 { 66 | type inet:ipv6-address; 67 | } 68 | 69 | list extension-headers { 70 | leaf next-header { 71 | type ipv4:known-ip-protocols; 72 | description "Type of this extension header"; 73 | } 74 | 75 | leaf length { 76 | type uint16; 77 | description "Length of this extension header, in octets"; 78 | } 79 | 80 | leaf data { 81 | type binary; 82 | description "Data for this extension header"; 83 | } 84 | } 85 | 86 | uses bpacket:packet-fields; 87 | } 88 | 89 | notification ipv6-packet-received { 90 | uses bpacket:packet-chain-grp { 91 | augment "packet-chain/packet" { 92 | case ipv6-packet { 93 | uses ipv6-packet-fields; 94 | } 95 | } 96 | } 97 | uses bpacket:packet-payload; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packethandler/model/src/main/yang/packet.yang: -------------------------------------------------------------------------------- 1 | module base-packet { 2 | yang-version 1; 3 | namespace "urn:opendaylight:packet:basepacket"; 4 | prefix bpacket; 5 | 6 | import ietf-yang-types {prefix yang; revision-date 2013-07-15;} 7 | import opendaylight-inventory {prefix inv;revision-date "2013-08-19";} 8 | import packet-processing {prefix pprocessing; revision-date 2013-07-09;} 9 | import opendaylight-match-types {prefix match-type;revision-date "2013-10-26";} 10 | import opendaylight-table-types {prefix table-type;revision-date "2013-10-26";} 11 | import opendaylight-flow-types {prefix flow-type;revision-date "2013-10-26";} 12 | 13 | 14 | revision 2014-05-28 { 15 | description 16 | "Base packet module draft."; 17 | } 18 | 19 | grouping packet-payload { 20 | leaf payload { 21 | type binary; 22 | } 23 | } 24 | 25 | grouping packet-fields { 26 | leaf payload-offset { 27 | type uint32; 28 | } 29 | leaf payload-length { 30 | type uint32; 31 | } 32 | } 33 | 34 | grouping packet-chain-grp { 35 | list packet-chain { 36 | // Note on the design here: 37 | // 38 | // We are inlining raw view of the packet, as these fields are being repeated 39 | // in other cases. YANG semantics requires that individual case statements within 40 | // a choice do not overlap on child QNames -- i.e. they differ in namespace or 41 | // or localname. Since this structure is defined as a grouping, there is no way 42 | // things like ipv4-packet-received can differentiate on namespace. We therefore 43 | // enclose this case in an explicit case (to keep consistency with users) and a 44 | // further container as the sole element. 45 | // 46 | // This guarantees the contents of packet-fields do not clash across cases, while 47 | // keeping things reasonable (to an extent). 48 | // 49 | // TODO: a much better design would look like this: 50 | // 51 | // container packet { 52 | // uses packet-fields; 53 | // 54 | // choice packet-type { 55 | // case raw-packet { 56 | // // .. the contents of raw-packet-fields .. 57 | // } 58 | // } 59 | // } 60 | // 61 | // which still has a potential problem with naming fields, but at least it is not 62 | // baked into the design. 63 | choice packet { 64 | case raw-packet { 65 | container raw-packet-fields { 66 | leaf ingress { 67 | type inv:node-connector-ref; 68 | } 69 | leaf connection-cookie { 70 | type pprocessing:connection-cookie; 71 | } 72 | leaf flow-cookie { 73 | type flow-type:flow-cookie; 74 | } 75 | leaf table-id { 76 | type table-type:table-id; 77 | } 78 | leaf packet-in-reason { 79 | type identityref { 80 | base pprocessing:packet-in-reason; 81 | } 82 | } 83 | container match { 84 | uses match-type:match; 85 | } 86 | uses packet-fields; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /packethandler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.opendaylight.l2switch 9 | l2switch-parent 10 | 0.8.0-SNAPSHOT 11 | ../parent 12 | 13 | 14 | packethandler.aggregator 15 | org.opendaylight.l2switch.packethandler 16 | pom 17 | 18 | 19 | model 20 | implementation 21 | 22 | 23 | -------------------------------------------------------------------------------- /parent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.opendaylight.mdsal 7 | binding-parent 8 | 12.0.6 9 | 10 | 11 | org.opendaylight.l2switch 12 | l2switch-parent 13 | 0.8.0-SNAPSHOT 14 | pom 15 | 16 | 17 | 18 | 19 | org.opendaylight.controller 20 | controller-artifacts 21 | 8.0.6 22 | import 23 | pom 24 | 25 | 26 | org.opendaylight.openflowplugin 27 | openflowplugin-artifacts 28 | 0.17.3 29 | import 30 | pom 31 | 32 | 33 | org.opendaylight.l2switch 34 | l2switch-artifacts 35 | ${project.version} 36 | import 37 | pom 38 | 39 | 40 | 41 | 42 | net.sf.jung 43 | jung-api 44 | 2.1.1 45 | 46 | 47 | net.sf.jung 48 | jung-algorithms 49 | 2.1.1 50 | 51 | 52 | net.sf.jung 53 | jung-graph-impl 54 | 2.1.1 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | opendaylight-release 63 | ${nexusproxy}/repositories/opendaylight.release/ 64 | 65 | 66 | 67 | opendaylight-snapshot 68 | ${nexusproxy}/repositories/opendaylight.snapshot/ 69 | 70 | 71 | ${project.artifactId}-site 72 | ./ 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-checkstyle-plugin 81 | 82 | checkstyle.violationSeverity=error 83 | 84 | 85 | 86 | com.github.spotbugs 87 | spotbugs-maven-plugin 88 | 89 | true 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.opendaylight.l2switch 5 | l2switch 6 | 0.8.0-SNAPSHOT 7 | pom 8 | l2switch 9 | 10 | 11 | parent 12 | packethandler 13 | loopremover 14 | arphandler 15 | addresstracker 16 | hosttracker 17 | l2switch-main 18 | distribution/karaf 19 | features 20 | artifacts 21 | 22 | 23 | scm:git:ssh://git.opendaylight.org:29418/l2switch.git 24 | scm:git:ssh://git.opendaylight.org:29418/l2switch.git 25 | HEAD 26 | https://wiki.opendaylight.org/view/L2_Switch:Main 27 | 28 | 29 | 30 | 31 | opendaylight-release 32 | ${nexusproxy}/repositories/opendaylight.release/ 33 | 34 | 35 | 36 | opendaylight-snapshot 37 | ${nexusproxy}/repositories/opendaylight.snapshot/ 38 | 39 | 40 | ${project.artifactId}-site 41 | ./ 42 | 43 | 44 | 45 | --------------------------------------------------------------------------------