commit 2947f0c6b7596263aad92d31b2800f4ecd875f98 Author: Yi-Ting Shih Date: Wed Oct 8 19:44:08 2025 +0800 Feat: done diff --git a/lab3_111550013.zip b/lab3_111550013.zip new file mode 100644 index 0000000..96b0bd5 Binary files /dev/null and b/lab3_111550013.zip differ diff --git a/lab3_111550013/ProxyArp/pom.xml b/lab3_111550013/ProxyArp/pom.xml new file mode 100644 index 0000000..39ebe03 --- /dev/null +++ b/lab3_111550013/ProxyArp/pom.xml @@ -0,0 +1,92 @@ + + + + 4.0.0 + + + org.onosproject + onos-dependencies + 2.7.0 + + + nycu.winlab + ProxyArp + 1.0-SNAPSHOT + bundle + + ONOS OSGi bundle archetype + http://onosproject.org + + + nycu.winlab.ProxyArp + Proxy Arp App + Winlab, NCTU + default + http://onosproject.org + ONOS OSGi bundle archetype. + + + + + org.onosproject + onos-api + ${onos.version} + provided + + + + org.onosproject + onlab-osgi + ${onos.version} + provided + + + + org.onosproject + onlab-misc + ${onos.version} + provided + + + + org.onosproject + onos-api + ${onos.version} + test + tests + + + + + + + org.onosproject + onos-maven-plugin + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + true + + + + + + diff --git a/lab3_111550013/ProxyArp/src/main/java/nycu/winlab/ProxyArp/AppComponent.java b/lab3_111550013/ProxyArp/src/main/java/nycu/winlab/ProxyArp/AppComponent.java new file mode 100644 index 0000000..8807ca6 --- /dev/null +++ b/lab3_111550013/ProxyArp/src/main/java/nycu/winlab/ProxyArp/AppComponent.java @@ -0,0 +1,171 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nycu.winlab; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; + +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; + +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.PacketPriority; + +import org.onosproject.net.edge.EdgePortService; +import org.onosproject.net.ConnectPoint; + +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; + +import org.onlab.packet.Ethernet; +import org.onlab.packet.ARP; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import java.nio.ByteBuffer; + +/** + * Skeletal ONOS application component. + */ +@Component(immediate = true) +public class AppComponent { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + private CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + private PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + private EdgePortService edgePortService; + + private ApplicationId appId; + private final PacketProcessor processor = new ProxyArpProcessor(); + private final Map arpTable = new ConcurrentHashMap<>(); + private final Map macTable = new ConcurrentHashMap<>(); + + private TrafficSelector arpSelector; + + @Activate + protected void activate() { + arpSelector = DefaultTrafficSelector.builder(). + matchEthType(Ethernet.TYPE_ARP). + build(); + + appId = coreService.registerApplication("nycu.winlab.ProxyArp"); + packetService.addProcessor(processor, PacketProcessor.director(3)); + packetService.requestPackets(arpSelector, PacketPriority.REACTIVE, appId); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + packetService.cancelPackets(arpSelector, PacketPriority.REACTIVE, appId); + packetService.removeProcessor(processor); + log.info("Stopped"); + } + + private class ProxyArpProcessor implements PacketProcessor { + private void sendPacket(Ethernet ethPacket, ConnectPoint outPort) { + TrafficTreatment treatment = DefaultTrafficTreatment.builder(). + setOutput(outPort.port()). + build(); + + OutboundPacket outPacket = new DefaultOutboundPacket( + outPort.deviceId(), + treatment, + ByteBuffer.wrap(ethPacket.serialize()) + ); + + packetService.emit(outPacket); + } + + @Override + public void process(PacketContext context) { + if (context.isHandled()) { + return; + } + + InboundPacket packet = context.inPacket(); + Ethernet ethPacket = packet.parsed(); + + if (ethPacket == null) { + return; + } + + ARP arpPacket = (ARP) ethPacket.getPayload(); + + if (arpPacket.getProtocolType() != ARP.PROTO_TYPE_IP) { + return; + } + + ConnectPoint inPort = packet.receivedFrom(); + MacAddress srcMac = ethPacket.getSourceMAC(); + Ip4Address srcIp = Ip4Address.valueOf(arpPacket. + getSenderProtocolAddress()); + Ip4Address dstIp = Ip4Address.valueOf(arpPacket. + getTargetProtocolAddress()); + + macTable.putIfAbsent(srcMac, inPort); + arpTable.putIfAbsent(srcIp, srcMac); + + if (arpPacket.getOpCode() == ARP.OP_REQUEST) { + MacAddress dstMac = arpTable.get(dstIp); + if (dstMac == null) { + for (ConnectPoint cp: edgePortService.getEdgePoints()) { + if (!cp.equals(inPort)) { + sendPacket(ethPacket, cp); + } + } + log.info("TABLE MISS. Send request to edge ports"); + } else { + Ethernet arpReply = ARP.buildArpReply(dstIp, dstMac, ethPacket); + sendPacket(arpReply, inPort); + log.info("TABLE HIT. Requested MAC = {}", dstMac); + } + } + if (arpPacket.getOpCode() == ARP.OP_REPLY) { + arpTable.put(srcIp, srcMac); + macTable.put(srcMac, inPort); + + MacAddress dstMac = arpTable.get(dstIp); + ConnectPoint outPort = macTable.get(dstMac); + + if (outPort != null) { + sendPacket(ethPacket, outPort); + } + } + } + } +} diff --git a/lab3_111550013/ProxyArp/src/main/java/nycu/winlab/ProxyArp/package-info.java b/lab3_111550013/ProxyArp/src/main/java/nycu/winlab/ProxyArp/package-info.java new file mode 100644 index 0000000..ac0ccd0 --- /dev/null +++ b/lab3_111550013/ProxyArp/src/main/java/nycu/winlab/ProxyArp/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ONOS application archetype. + */ +package nycu.winlab.ProxyArp; diff --git a/lab3_111550013/ProxyArp/src/test/java/nycu/winlab/proxyarp/AppComponentTest.java b/lab3_111550013/ProxyArp/src/test/java/nycu/winlab/proxyarp/AppComponentTest.java new file mode 100644 index 0000000..e67cc2e --- /dev/null +++ b/lab3_111550013/ProxyArp/src/test/java/nycu/winlab/proxyarp/AppComponentTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nycu.winlab; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cfg.ComponentConfigAdapter; + +/** + * Set of tests of the ONOS application component. + */ +public class AppComponentTest { + + private AppComponent component; + + @Before + public void setUp() { + component = new AppComponent(); + component.activate(); + } + + @After + public void tearDown() { + component.deactivate(); + } + + @Test + public void basics() { + + } + +} diff --git a/lab3_111550013/bridge-app/pom.xml b/lab3_111550013/bridge-app/pom.xml new file mode 100644 index 0000000..5d82dd2 --- /dev/null +++ b/lab3_111550013/bridge-app/pom.xml @@ -0,0 +1,93 @@ + + + + 4.0.0 + + + org.onosproject + onos-dependencies + 2.7.0 + + + nycu.winlab + bridge-app + 1.0-SNAPSHOT + bundle + + ONOS OSGi bundle archetype + http://onosproject.org + + + nctu.winlab.bridge + Learning Bridge App + Winlab, NCTU + default + http://onosproject.org + ONOS OSGi bundle archetype. + + + + + org.onosproject + onos-api + ${onos.version} + provided + + + + org.onosproject + onlab-osgi + ${onos.version} + provided + + + + org.onosproject + onlab-misc + ${onos.version} + provided + + + + org.onosproject + onos-api + ${onos.version} + test + tests + + + + + + + org.onosproject + onos-maven-plugin + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + diff --git a/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/AppComponent.java b/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/AppComponent.java new file mode 100644 index 0000000..e296bcf --- /dev/null +++ b/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/AppComponent.java @@ -0,0 +1,205 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nycu.winlab.bridge; + +import org.onosproject.cfg.ComponentConfigService; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; + +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; + +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.flowobjective.ForwardingObjective; + +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketService; + +import org.onlab.packet.Ethernet; +import org.onlab.packet.MacAddress; + +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Skeletal ONOS application component. + */ +@Component(immediate = true) +public class AppComponent { + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** Some configurable property. */ + private String someProperty; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected ComponentConfigService cfgService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected FlowRuleService flowRuleService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected FlowObjectiveService flowObjectiveService; + + private static final int FLOWRULE_PRIORITY = 30; + private static final int FLOWRULE_TIMEOUT = 30; + + private ApplicationId appId; + private final PacketProcessor processor = new BridgePacketProcessor(); + private final Map> macTables = + new ConcurrentHashMap<>(); + + private TrafficSelector ipv4Selector; + private TrafficSelector arpSelector; + + @Activate + protected void activate() { + appId = coreService.registerApplication("nycu.winlab.bridge"); + packetService.addProcessor(processor, PacketProcessor.director(2)); + + ipv4Selector = DefaultTrafficSelector.builder(). + matchEthType(Ethernet.TYPE_IPV4). + build(); + + arpSelector = DefaultTrafficSelector.builder(). + matchEthType(Ethernet.TYPE_ARP). + build(); + + packetService.requestPackets(ipv4Selector, PacketPriority.REACTIVE, appId); + packetService.requestPackets(arpSelector, PacketPriority.REACTIVE, appId); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + cfgService.unregisterProperties(getClass(), false); + + packetService.cancelPackets(ipv4Selector, PacketPriority.REACTIVE, appId); + packetService.cancelPackets(arpSelector, PacketPriority.REACTIVE, appId); + + packetService.removeProcessor(processor); + flowRuleService.removeFlowRulesById(appId); + macTables.clear(); + log.info("Stopped"); + } + + private class BridgePacketProcessor implements PacketProcessor { + private void installFlowRule( + DeviceId deviceId, + MacAddress srcMac, + MacAddress dstMac, + PortNumber outPort + ) { + TrafficSelector selector = DefaultTrafficSelector.builder(). + matchEthSrc(srcMac). + matchEthDst(dstMac). + build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder(). + setOutput(outPort). + build(); + + ForwardingObjective forwardingObjective = + DefaultForwardingObjective.builder(). + withSelector(selector). + withTreatment(treatment). + withPriority(FLOWRULE_PRIORITY). + withFlag(ForwardingObjective.Flag.VERSATILE). + makeTemporary(FLOWRULE_TIMEOUT). + fromApp(appId). + add(); + + flowObjectiveService.forward(deviceId, forwardingObjective); + } + + @Override + public void process(PacketContext context) { + if (context.isHandled()) { + return; + } + + InboundPacket packet = context.inPacket(); + Ethernet ethPacket = packet.parsed(); + + if (ethPacket == null) { + return; + } + + ConnectPoint cp = packet.receivedFrom(); + DeviceId deviceId = cp.deviceId(); + PortNumber inPort = cp.port(); + MacAddress srcMac = ethPacket.getSourceMAC(); + MacAddress dstMac = ethPacket.getDestinationMAC(); + + macTables.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>()); + Map macTable = macTables.get(cp.deviceId()); + + if (!macTable.containsKey(srcMac)) { + macTable.put(srcMac, inPort); + log.info("Add an entry to the port table of `{}`. " + + "MAC address: `{}` => Port: `{}`.", + deviceId, srcMac, inPort); + } + + PortNumber outPort = macTable.get(dstMac); + + if (outPort == null) { + context.treatmentBuilder().setOutput(PortNumber.FLOOD); + context.send(); + + log.info("MAC address `{}` is missed on `{}`. Flood the packet.", + dstMac, deviceId); + } else { + if (!outPort.equals(inPort)) { + installFlowRule(deviceId, srcMac, dstMac, outPort); + context.treatmentBuilder().setOutput(outPort); + context.send(); + + log.info("MAC address `{}` is matched on `{}`. Install a flow rule.", + dstMac, deviceId); + } else { + context.block(); + } + } + } + + } +} diff --git a/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/SomeInterface.java b/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/SomeInterface.java new file mode 100644 index 0000000..94a3b63 --- /dev/null +++ b/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/SomeInterface.java @@ -0,0 +1,22 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nycu.winlab.bridge; + +public interface SomeInterface { + + void someMethod(); + +} \ No newline at end of file diff --git a/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/package-info.java b/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/package-info.java new file mode 100644 index 0000000..ab96195 --- /dev/null +++ b/lab3_111550013/bridge-app/src/main/java/nycu/winlab/bridge/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ONOS application archetype. + */ +package nycu.winlab.bridge; \ No newline at end of file diff --git a/lab3_111550013/bridge-app/src/test/java/nycu/winlab/bridge/AppComponentTest.java b/lab3_111550013/bridge-app/src/test/java/nycu/winlab/bridge/AppComponentTest.java new file mode 100644 index 0000000..3d47cd4 --- /dev/null +++ b/lab3_111550013/bridge-app/src/test/java/nycu/winlab/bridge/AppComponentTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2025-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nycu.winlab.bridge; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cfg.ComponentConfigAdapter; + +/** + * Set of tests of the ONOS application component. + */ +public class AppComponentTest { + + private AppComponent component; + + @Before + public void setUp() { + component = new AppComponent(); + component.cfgService = new ComponentConfigAdapter(); + component.activate(); + } + + @After + public void tearDown() { + component.deactivate(); + } + + @Test + public void basics() { + + } + +}