Committed by
Gerrit Code Review
ONOS-686, 687, 1344 : The first commit for the Segment Routing application
- ICMP/ARP/IP handlers are implemented as a part of the application for now - Default routing and link add/failure/recovery are also supprted - Temporary NetworkConfigHandler, which is hardcoded to support only 6 router FISH topology, is used for test - Some fixes on GroupHanlder app to support transit routers - Supports multi-instance (tested with two instances) Change-Id: Idfa67903e59e1c4cac4da430f89cd4c50e821420
Showing
19 changed files
with
2512 additions
and
448 deletions
... | @@ -152,11 +152,13 @@ public class DefaultGroupHandler { | ... | @@ -152,11 +152,13 @@ public class DefaultGroupHandler { |
152 | * @param newLink new neighbor link | 152 | * @param newLink new neighbor link |
153 | */ | 153 | */ |
154 | public void linkUp(Link newLink) { | 154 | public void linkUp(Link newLink) { |
155 | + | ||
155 | if (newLink.type() != Link.Type.DIRECT) { | 156 | if (newLink.type() != Link.Type.DIRECT) { |
156 | log.warn("linkUp: unknown link type"); | 157 | log.warn("linkUp: unknown link type"); |
157 | return; | 158 | return; |
158 | } | 159 | } |
159 | 160 | ||
161 | + | ||
160 | if (!newLink.src().deviceId().equals(deviceId)) { | 162 | if (!newLink.src().deviceId().equals(deviceId)) { |
161 | log.warn("linkUp: deviceId{} doesn't match with link src{}", | 163 | log.warn("linkUp: deviceId{} doesn't match with link src{}", |
162 | deviceId, | 164 | deviceId, |
... | @@ -307,13 +309,11 @@ public class DefaultGroupHandler { | ... | @@ -307,13 +309,11 @@ public class DefaultGroupHandler { |
307 | 309 | ||
308 | List<Integer> nsSegmentIds = new ArrayList<Integer>(); | 310 | List<Integer> nsSegmentIds = new ArrayList<Integer>(); |
309 | 311 | ||
310 | - // Add one entry for "no label" (-1) to the list if | 312 | + // Always pair up with no edge label |
311 | - // dpid list has not more than one node/neighbor as | 313 | + //If (neighbors.size() == 1) { |
312 | - // there will never be a case a packet going to more than one | 314 | + nsSegmentIds.add(-1); |
313 | - // neighbor without a label at an edge router | 315 | + //} |
314 | - if (neighbors.size() == 1) { | 316 | + |
315 | - nsSegmentIds.add(-1); | ||
316 | - } | ||
317 | // Filter out SegmentIds matching with the | 317 | // Filter out SegmentIds matching with the |
318 | // nodes in the combo | 318 | // nodes in the combo |
319 | for (Integer sId : allSegmentIds) { | 319 | for (Integer sId : allSegmentIds) { |
... | @@ -405,7 +405,8 @@ public class DefaultGroupHandler { | ... | @@ -405,7 +405,8 @@ public class DefaultGroupHandler { |
405 | } | 405 | } |
406 | } | 406 | } |
407 | 407 | ||
408 | - protected GroupKey getGroupKey(Object obj) { | 408 | + public GroupKey getGroupKey(Object obj) { |
409 | return new DefaultGroupKey(kryo.build().serialize(obj)); | 409 | return new DefaultGroupKey(kryo.build().serialize(obj)); |
410 | } | 410 | } |
411 | + | ||
411 | } | 412 | } | ... | ... |
... | @@ -90,6 +90,7 @@ public class DefaultGroupHandlerApp { | ... | @@ -90,6 +90,7 @@ public class DefaultGroupHandlerApp { |
90 | public void activate() { | 90 | public void activate() { |
91 | appId = coreService.registerApplication("org.onosproject.defaultgrouphandler"); | 91 | appId = coreService.registerApplication("org.onosproject.defaultgrouphandler"); |
92 | log.info("DefaultGroupHandlerApp Activating"); | 92 | log.info("DefaultGroupHandlerApp Activating"); |
93 | + | ||
93 | deviceService.addListener(deviceListener); | 94 | deviceService.addListener(deviceListener); |
94 | linkService.addListener(linkListener); | 95 | linkService.addListener(linkListener); |
95 | for (Device device: deviceService.getDevices()) { | 96 | for (Device device: deviceService.getDevices()) { |
... | @@ -111,6 +112,7 @@ public class DefaultGroupHandlerApp { | ... | @@ -111,6 +112,7 @@ public class DefaultGroupHandlerApp { |
111 | device.id()); | 112 | device.id()); |
112 | } | 113 | } |
113 | } | 114 | } |
115 | + | ||
114 | log.info("Activated"); | 116 | log.info("Activated"); |
115 | } | 117 | } |
116 | 118 | ... | ... |
... | @@ -138,7 +138,8 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { | ... | @@ -138,7 +138,8 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { |
138 | Set<DeviceId> updatedNeighbors) { | 138 | Set<DeviceId> updatedNeighbors) { |
139 | Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors); | 139 | Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors); |
140 | 140 | ||
141 | - Set<DeviceId> tmp = updatedNeighbors; | 141 | + Set<DeviceId> tmp = new HashSet<DeviceId>(); |
142 | + tmp.addAll(updatedNeighbors); | ||
142 | tmp.remove(impactedNeighbor); | 143 | tmp.remove(impactedNeighbor); |
143 | Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp); | 144 | Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp); |
144 | 145 | ... | ... |
... | @@ -45,6 +45,8 @@ | ... | @@ -45,6 +45,8 @@ |
45 | <module>reactive-routing</module> | 45 | <module>reactive-routing</module> |
46 | <module>bgprouter</module> | 46 | <module>bgprouter</module> |
47 | <module>test</module> | 47 | <module>test</module> |
48 | + <module>grouphandler</module> | ||
49 | + <module>segmentrouting</module> | ||
48 | </modules> | 50 | </modules> |
49 | 51 | ||
50 | <properties> | 52 | <properties> | ... | ... |
apps/segmentrouting/pom.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2014 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
17 | + <modelVersion>4.0.0</modelVersion> | ||
18 | + <parent> | ||
19 | + <artifactId>onos-apps</artifactId> | ||
20 | + <groupId>org.onosproject</groupId> | ||
21 | + <version>1.2.0-SNAPSHOT</version> | ||
22 | + </parent> | ||
23 | + | ||
24 | + <artifactId>onos-app-segmentrouting</artifactId> | ||
25 | + <packaging>bundle</packaging> | ||
26 | + | ||
27 | + <description>ONOS OSGi bundle archetype</description> | ||
28 | + <url>http://onosproject.org</url> | ||
29 | + | ||
30 | + <dependencies> | ||
31 | + <dependency> | ||
32 | + <groupId>org.onosproject</groupId> | ||
33 | + <artifactId>onos-app-grouphandler</artifactId> | ||
34 | + <version>1.2.0-SNAPSHOT</version> | ||
35 | + </dependency> | ||
36 | + </dependencies> | ||
37 | + | ||
38 | +</project> |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import org.onlab.packet.ARP; | ||
19 | +import org.onlab.packet.Ethernet; | ||
20 | +import org.onlab.packet.Ip4Address; | ||
21 | +import org.onlab.packet.IpAddress; | ||
22 | +import org.onlab.packet.IpPrefix; | ||
23 | +import org.onlab.packet.MacAddress; | ||
24 | +import org.onosproject.net.ConnectPoint; | ||
25 | +import org.onosproject.net.DeviceId; | ||
26 | +import org.onosproject.net.Host; | ||
27 | +import org.onosproject.net.Port; | ||
28 | +import org.onosproject.net.PortNumber; | ||
29 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
30 | +import org.onosproject.net.flow.TrafficTreatment; | ||
31 | +import org.onosproject.net.packet.DefaultOutboundPacket; | ||
32 | +import org.onosproject.net.packet.InboundPacket; | ||
33 | +import org.onosproject.net.HostId; | ||
34 | +import org.onosproject.net.packet.OutboundPacket; | ||
35 | +import org.slf4j.Logger; | ||
36 | +import org.slf4j.LoggerFactory; | ||
37 | + | ||
38 | +import java.nio.ByteBuffer; | ||
39 | + | ||
40 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
41 | + | ||
42 | +public class ArpHandler { | ||
43 | + | ||
44 | + private static Logger log = LoggerFactory.getLogger(ArpHandler.class); | ||
45 | + | ||
46 | + private SegmentRoutingManager srManager; | ||
47 | + private NetworkConfigHandler config; | ||
48 | + | ||
49 | + /** | ||
50 | + * Creates an ArpHandler object. | ||
51 | + * | ||
52 | + * @param srManager SegmentRoutingManager object | ||
53 | + */ | ||
54 | + public ArpHandler(SegmentRoutingManager srManager) { | ||
55 | + this.srManager = srManager; | ||
56 | + this.config = checkNotNull(srManager.networkConfigHandler); | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * Processes incoming ARP packets. | ||
61 | + * If it is an ARP request to router itself or known hosts, | ||
62 | + * then it sends ARP response. | ||
63 | + * If it is an ARP request to unknown hosts in its own subnet, | ||
64 | + * then it flood the ARP request to the ports. | ||
65 | + * If it is an ARP response, then set a flow rule for the host | ||
66 | + * and forward any IP packets to the host in the packet buffer to the host. | ||
67 | + * | ||
68 | + * @param pkt incoming packet | ||
69 | + */ | ||
70 | + public void processPacketIn(InboundPacket pkt) { | ||
71 | + | ||
72 | + Ethernet ethernet = pkt.parsed(); | ||
73 | + ARP arp = (ARP) ethernet.getPayload(); | ||
74 | + | ||
75 | + ConnectPoint connectPoint = pkt.receivedFrom(); | ||
76 | + PortNumber inPort = connectPoint.port(); | ||
77 | + DeviceId deviceId = connectPoint.deviceId(); | ||
78 | + byte[] senderMacAddressByte = arp.getSenderHardwareAddress(); | ||
79 | + Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress()); | ||
80 | + | ||
81 | + srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress. | ||
82 | + valueOf(senderMacAddressByte), inPort); | ||
83 | + | ||
84 | + if (arp.getOpCode() == ARP.OP_REQUEST) { | ||
85 | + handleArpRequest(deviceId, connectPoint, ethernet); | ||
86 | + } else { | ||
87 | + srManager.ipHandler.forwardPackets(deviceId, hostIpAddress); | ||
88 | + } | ||
89 | + } | ||
90 | + | ||
91 | + private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) { | ||
92 | + | ||
93 | + ARP arpRequest = (ARP) payload.getPayload(); | ||
94 | + HostId targetHostId = HostId.hostId(MacAddress.valueOf( | ||
95 | + arpRequest.getTargetHardwareAddress())); | ||
96 | + | ||
97 | + // ARP request for router | ||
98 | + if (isArpReqForRouter(deviceId, arpRequest)) { | ||
99 | + Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()); | ||
100 | + sendArpResponse(arpRequest, config.getRouterMac(targetAddress)); | ||
101 | + | ||
102 | + // ARP request for known hosts | ||
103 | + } else if (srManager.hostService.getHost(targetHostId) != null) { | ||
104 | + MacAddress targetMac = srManager.hostService.getHost(targetHostId).mac(); | ||
105 | + sendArpResponse(arpRequest, targetMac); | ||
106 | + | ||
107 | + // ARP request for unknown host in the subnet | ||
108 | + } else if (isArpReqForSubnet(deviceId, arpRequest)) { | ||
109 | + flood(payload, inPort); | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + | ||
114 | + private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) { | ||
115 | + Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId); | ||
116 | + if (gatewayIpAddress != null) { | ||
117 | + Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest | ||
118 | + .getTargetProtocolAddress()); | ||
119 | + if (gatewayIpAddress.equals(targetProtocolAddress)) { | ||
120 | + return true; | ||
121 | + } | ||
122 | + } | ||
123 | + return false; | ||
124 | + } | ||
125 | + | ||
126 | + private boolean isArpReqForSubnet(DeviceId deviceId, ARP arpRequest) { | ||
127 | + String subnetInfo = config.getSubnetInfo(deviceId); | ||
128 | + if (subnetInfo != null) { | ||
129 | + IpPrefix prefix = IpPrefix.valueOf(subnetInfo); | ||
130 | + if (prefix.contains(Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()))) { | ||
131 | + return true; | ||
132 | + } | ||
133 | + } | ||
134 | + | ||
135 | + return false; | ||
136 | + } | ||
137 | + | ||
138 | + /** | ||
139 | + * Sends an APR request for the target IP address to all ports except in-port. | ||
140 | + * | ||
141 | + * @param deviceId Switch device ID | ||
142 | + * @param targetAddress target IP address for ARP | ||
143 | + * @param inPort in-port | ||
144 | + */ | ||
145 | + public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) { | ||
146 | + | ||
147 | + byte[] senderMacAddress = config.getRouterMacAddress(deviceId).toBytes(); | ||
148 | + byte[] senderIpAddress = config.getRouterIpAddress(deviceId) | ||
149 | + .getIp4Prefix().address().toOctets(); | ||
150 | + | ||
151 | + ARP arpRequest = new ARP(); | ||
152 | + arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET) | ||
153 | + .setProtocolType(ARP.PROTO_TYPE_IP) | ||
154 | + .setHardwareAddressLength( | ||
155 | + (byte) Ethernet.DATALAYER_ADDRESS_LENGTH) | ||
156 | + .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH) | ||
157 | + .setOpCode(ARP.OP_REQUEST) | ||
158 | + .setSenderHardwareAddress(senderMacAddress) | ||
159 | + .setTargetHardwareAddress(MacAddress.ZERO.toBytes()) | ||
160 | + .setSenderProtocolAddress(senderIpAddress) | ||
161 | + .setTargetProtocolAddress(targetAddress.toOctets()); | ||
162 | + | ||
163 | + Ethernet eth = new Ethernet(); | ||
164 | + eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes()) | ||
165 | + .setSourceMACAddress(senderMacAddress) | ||
166 | + .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest); | ||
167 | + | ||
168 | + flood(eth, inPort); | ||
169 | + } | ||
170 | + | ||
171 | + private void sendArpResponse(ARP arpRequest, MacAddress targetMac) { | ||
172 | + | ||
173 | + ARP arpReply = new ARP(); | ||
174 | + arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET) | ||
175 | + .setProtocolType(ARP.PROTO_TYPE_IP) | ||
176 | + .setHardwareAddressLength( | ||
177 | + (byte) Ethernet.DATALAYER_ADDRESS_LENGTH) | ||
178 | + .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH) | ||
179 | + .setOpCode(ARP.OP_REPLY) | ||
180 | + .setSenderHardwareAddress(targetMac.toBytes()) | ||
181 | + .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress()) | ||
182 | + .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress()) | ||
183 | + .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress()); | ||
184 | + | ||
185 | + Ethernet eth = new Ethernet(); | ||
186 | + eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress()) | ||
187 | + .setSourceMACAddress(targetMac.toBytes()) | ||
188 | + .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply); | ||
189 | + | ||
190 | + | ||
191 | + HostId dstId = HostId.hostId(MacAddress.valueOf( | ||
192 | + arpReply.getTargetHardwareAddress())); | ||
193 | + Host dst = srManager.hostService.getHost(dstId); | ||
194 | + if (dst == null) { | ||
195 | + log.warn("Cannot send ARP response to unknown device"); | ||
196 | + return; | ||
197 | + } | ||
198 | + | ||
199 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder(). | ||
200 | + setOutput(dst.location().port()).build(); | ||
201 | + OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(), | ||
202 | + treatment, ByteBuffer.wrap(eth.serialize())); | ||
203 | + | ||
204 | + srManager.packetService.emit(packet); | ||
205 | + } | ||
206 | + | ||
207 | + private void flood(Ethernet request, ConnectPoint inPort) { | ||
208 | + TrafficTreatment.Builder builder; | ||
209 | + ByteBuffer buf = ByteBuffer.wrap(request.serialize()); | ||
210 | + | ||
211 | + for (Port port: srManager.deviceService.getPorts(inPort.deviceId())) { | ||
212 | + if (!port.number().equals(inPort.port()) && | ||
213 | + port.number().toLong() > 0) { | ||
214 | + builder = DefaultTrafficTreatment.builder(); | ||
215 | + builder.setOutput(port.number()); | ||
216 | + srManager.packetService.emit(new DefaultOutboundPacket(inPort.deviceId(), | ||
217 | + builder.build(), buf)); | ||
218 | + } | ||
219 | + } | ||
220 | + } | ||
221 | + | ||
222 | +} |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
0 → 100644
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import org.onlab.packet.IpPrefix; | ||
19 | +import org.onosproject.net.Device; | ||
20 | +import org.onosproject.net.DeviceId; | ||
21 | +import org.onosproject.net.MastershipRole; | ||
22 | +import org.onosproject.net.flow.FlowRule; | ||
23 | +import org.slf4j.Logger; | ||
24 | +import org.slf4j.LoggerFactory; | ||
25 | + | ||
26 | +import java.util.ArrayList; | ||
27 | +import java.util.HashMap; | ||
28 | +import java.util.HashSet; | ||
29 | +import java.util.Set; | ||
30 | + | ||
31 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
32 | + | ||
33 | +public class DefaultRoutingHandler { | ||
34 | + | ||
35 | + private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class); | ||
36 | + | ||
37 | + private SegmentRoutingManager srManager; | ||
38 | + private RoutingRulePopulator rulePopulator; | ||
39 | + private NetworkConfigHandler config; | ||
40 | + private Status populationStatus; | ||
41 | + | ||
42 | + /** | ||
43 | + * Represents the default routing population status. | ||
44 | + */ | ||
45 | + public enum Status { | ||
46 | + // population process is not started yet. | ||
47 | + IDLE, | ||
48 | + | ||
49 | + // population process started. | ||
50 | + STARTED, | ||
51 | + | ||
52 | + // population process was aborted due to errors, mostly for groups not found. | ||
53 | + ABORTED, | ||
54 | + | ||
55 | + // population process was finished successfully. | ||
56 | + SUCCEEDED | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * Creates a DefaultRoutingHandler object. | ||
61 | + * | ||
62 | + * @param srManager SegmentRoutingManager object | ||
63 | + */ | ||
64 | + public DefaultRoutingHandler(SegmentRoutingManager srManager) { | ||
65 | + this.srManager = srManager; | ||
66 | + this.rulePopulator = checkNotNull(srManager.routingRulePopulator); | ||
67 | + this.config = checkNotNull(srManager.networkConfigHandler); | ||
68 | + this.populationStatus = Status.IDLE; | ||
69 | + } | ||
70 | + | ||
71 | + /** | ||
72 | + * Populates all routing rules to all connected routers, including default | ||
73 | + * routing rules, adjacency rules, and policy rules if any. | ||
74 | + * | ||
75 | + * @return true if it succeeds in populating all rules, otherwise false | ||
76 | + */ | ||
77 | + public boolean populateAllRoutingRules() { | ||
78 | + | ||
79 | + populationStatus = Status.STARTED; | ||
80 | + log.info("Starts to populate routing rules"); | ||
81 | + | ||
82 | + for (Device sw : srManager.deviceService.getDevices()) { | ||
83 | + if (srManager.mastershipService. | ||
84 | + getLocalRole(sw.id()) != MastershipRole.MASTER) { | ||
85 | + continue; | ||
86 | + } | ||
87 | + | ||
88 | + ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager); | ||
89 | + if (!populateEcmpRoutingRules(sw, ecmpSPG)) { | ||
90 | + populationStatus = Status.ABORTED; | ||
91 | + log.debug("Abort routing rule population"); | ||
92 | + return false; | ||
93 | + } | ||
94 | + | ||
95 | + // TODO: Set adjacency routing rule for all switches | ||
96 | + } | ||
97 | + | ||
98 | + populationStatus = Status.SUCCEEDED; | ||
99 | + log.info("Completes routing rule population"); | ||
100 | + return true; | ||
101 | + } | ||
102 | + | ||
103 | + private boolean populateEcmpRoutingRules(Device sw, | ||
104 | + ECMPShortestPathGraph ecmpSPG) { | ||
105 | + | ||
106 | + HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = | ||
107 | + ecmpSPG.getAllLearnedSwitchesAndVia(); | ||
108 | + for (Integer itrIdx : switchVia.keySet()) { | ||
109 | + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = | ||
110 | + switchVia.get(itrIdx); | ||
111 | + for (DeviceId targetSw : swViaMap.keySet()) { | ||
112 | + DeviceId destSw = sw.id(); | ||
113 | + Set<DeviceId> nextHops = new HashSet<>(); | ||
114 | + | ||
115 | + for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) { | ||
116 | + if (via.isEmpty()) { | ||
117 | + nextHops.add(destSw); | ||
118 | + } else { | ||
119 | + nextHops.add(via.get(0)); | ||
120 | + } | ||
121 | + } | ||
122 | + if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) { | ||
123 | + return false; | ||
124 | + } | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + return true; | ||
129 | + } | ||
130 | + | ||
131 | + private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw, | ||
132 | + Set<DeviceId> nextHops) { | ||
133 | + boolean result; | ||
134 | + | ||
135 | + if (nextHops.isEmpty()) { | ||
136 | + nextHops.add(destSw); | ||
137 | + } | ||
138 | + | ||
139 | + // If both target switch and dest switch are edge routers, then set IP rule | ||
140 | + // for both subnet and router IP. | ||
141 | + if (config.isEdgeRouter(targetSw) && config.isEdgeRouter(destSw)) { | ||
142 | + String subnets = config.getSubnetInfo(destSw); | ||
143 | + result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets, destSw, nextHops); | ||
144 | + if (!result) { | ||
145 | + return false; | ||
146 | + } | ||
147 | + | ||
148 | + IpPrefix routerIp = config.getRouterIpAddress(destSw); | ||
149 | + result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops); | ||
150 | + if (!result) { | ||
151 | + return false; | ||
152 | + } | ||
153 | + | ||
154 | + // If the target switch is an edge router, then set IP rules for the router IP. | ||
155 | + } else if (config.isEdgeRouter(targetSw)) { | ||
156 | + IpPrefix routerIp = config.getRouterIpAddress(destSw); | ||
157 | + result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops); | ||
158 | + if (!result) { | ||
159 | + return false; | ||
160 | + } | ||
161 | + | ||
162 | + // If the target switch is an transit router, then set MPLS rules only. | ||
163 | + } else if (config.isTransitRouter(targetSw)) { | ||
164 | + result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops); | ||
165 | + if (!result) { | ||
166 | + return false; | ||
167 | + } | ||
168 | + } else { | ||
169 | + log.warn("The switch {} is neither an edge router nor a transit router.", targetSw); | ||
170 | + return false; | ||
171 | + } | ||
172 | + | ||
173 | + return true; | ||
174 | + } | ||
175 | + | ||
176 | + /** | ||
177 | + * Populates table miss entries for all tables, and pipeline rules for | ||
178 | + * VLAN and TACM tables. | ||
179 | + * | ||
180 | + * @param deviceId Switch ID to set the rules | ||
181 | + */ | ||
182 | + public void populateTtpRules(DeviceId deviceId) { | ||
183 | + | ||
184 | + rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.VLAN, | ||
185 | + true, false, false, FlowRule.Type.DEFAULT); | ||
186 | + rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ETHER, | ||
187 | + true, false, false, FlowRule.Type.DEFAULT); | ||
188 | + rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.IP, | ||
189 | + false, true, true, FlowRule.Type.ACL); | ||
190 | + rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.MPLS, | ||
191 | + false, true, true, FlowRule.Type.ACL); | ||
192 | + rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ACL, | ||
193 | + false, false, false, FlowRule.Type.DEFAULT); | ||
194 | + | ||
195 | + rulePopulator.populateTableVlan(deviceId); | ||
196 | + rulePopulator.populateTableTMac(deviceId); | ||
197 | + } | ||
198 | + | ||
199 | + /** | ||
200 | + * Start the flow rule population process if it was never started. | ||
201 | + * The process finishes successfully when all flow rules are set and | ||
202 | + * stops with ABORTED status when any groups required for flows is not | ||
203 | + * set yet. | ||
204 | + */ | ||
205 | + public void startPopulationProcess() { | ||
206 | + synchronized (populationStatus) { | ||
207 | + if (populationStatus == Status.IDLE || | ||
208 | + populationStatus == Status.SUCCEEDED) { | ||
209 | + populationStatus = Status.STARTED; | ||
210 | + populateAllRoutingRules(); | ||
211 | + } | ||
212 | + } | ||
213 | + } | ||
214 | + | ||
215 | + /** | ||
216 | + * Resume the flow rule population process if it was aborted for any reason. | ||
217 | + * Mostly the process is aborted when the groups required are not set yet. | ||
218 | + */ | ||
219 | + public void resumePopulationProcess() { | ||
220 | + synchronized (populationStatus) { | ||
221 | + if (populationStatus == Status.ABORTED) { | ||
222 | + populationStatus = Status.STARTED; | ||
223 | + // TODO: we need to restart from the point aborted instead of restarting. | ||
224 | + populateAllRoutingRules(); | ||
225 | + } | ||
226 | + } | ||
227 | + } | ||
228 | +} |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
0 → 100644
1 | +package org.onosproject.segmentrouting; | ||
2 | + | ||
3 | +import org.onlab.packet.Ip4Address; | ||
4 | +import org.onlab.packet.MacAddress; | ||
5 | +import org.onosproject.grouphandler.DeviceProperties; | ||
6 | +import org.onosproject.net.DeviceId; | ||
7 | +import org.slf4j.Logger; | ||
8 | +import org.slf4j.LoggerFactory; | ||
9 | + | ||
10 | +import java.util.Arrays; | ||
11 | +import java.util.HashMap; | ||
12 | +import java.util.List; | ||
13 | +import java.util.Map; | ||
14 | + | ||
15 | +public class DeviceConfiguration implements DeviceProperties { | ||
16 | + | ||
17 | + private static final Logger log = LoggerFactory | ||
18 | + .getLogger(DeviceConfiguration.class); | ||
19 | + private final List<Integer> allSegmentIds = | ||
20 | + Arrays.asList(101, 102, 103, 104, 105, 106); | ||
21 | + private HashMap<DeviceId, Integer> deviceSegmentIdMap = | ||
22 | + new HashMap<DeviceId, Integer>() { | ||
23 | + { | ||
24 | + put(DeviceId.deviceId("of:0000000000000001"), 101); | ||
25 | + put(DeviceId.deviceId("of:0000000000000002"), 102); | ||
26 | + put(DeviceId.deviceId("of:0000000000000003"), 103); | ||
27 | + put(DeviceId.deviceId("of:0000000000000004"), 104); | ||
28 | + put(DeviceId.deviceId("of:0000000000000005"), 105); | ||
29 | + put(DeviceId.deviceId("of:0000000000000006"), 106); | ||
30 | + } | ||
31 | + }; | ||
32 | + private final HashMap<DeviceId, MacAddress> deviceMacMap = | ||
33 | + new HashMap<DeviceId, MacAddress>() { | ||
34 | + { | ||
35 | + put(DeviceId.deviceId("of:0000000000000001"), | ||
36 | + MacAddress.valueOf("00:00:00:00:00:01")); | ||
37 | + put(DeviceId.deviceId("of:0000000000000002"), | ||
38 | + MacAddress.valueOf("00:00:00:00:00:02")); | ||
39 | + put(DeviceId.deviceId("of:0000000000000003"), | ||
40 | + MacAddress.valueOf("00:00:00:00:00:03")); | ||
41 | + put(DeviceId.deviceId("of:0000000000000004"), | ||
42 | + MacAddress.valueOf("00:00:00:00:00:04")); | ||
43 | + put(DeviceId.deviceId("of:0000000000000005"), | ||
44 | + MacAddress.valueOf("00:00:00:00:00:05")); | ||
45 | + put(DeviceId.deviceId("of:0000000000000006"), | ||
46 | + MacAddress.valueOf("00:00:00:00:00:06")); | ||
47 | + } | ||
48 | + }; | ||
49 | + | ||
50 | + private final HashMap<DeviceId, Ip4Address> deviceIpMap = | ||
51 | + new HashMap<DeviceId, Ip4Address>() { | ||
52 | + { | ||
53 | + put(DeviceId.deviceId("of:0000000000000001"), | ||
54 | + Ip4Address.valueOf("192.168.0.1")); | ||
55 | + put(DeviceId.deviceId("of:0000000000000002"), | ||
56 | + Ip4Address.valueOf("192.168.0.2")); | ||
57 | + put(DeviceId.deviceId("of:0000000000000003"), | ||
58 | + Ip4Address.valueOf("192.168.0.3")); | ||
59 | + put(DeviceId.deviceId("of:0000000000000004"), | ||
60 | + Ip4Address.valueOf("192.168.0.4")); | ||
61 | + put(DeviceId.deviceId("of:0000000000000005"), | ||
62 | + Ip4Address.valueOf("192.168.0.5")); | ||
63 | + put(DeviceId.deviceId("of:0000000000000006"), | ||
64 | + Ip4Address.valueOf("192.168.0.6")); | ||
65 | + } | ||
66 | + }; | ||
67 | + | ||
68 | + @Override | ||
69 | + public int getSegmentId(DeviceId deviceId) { | ||
70 | + if (deviceSegmentIdMap.get(deviceId) != null) { | ||
71 | + log.debug("getSegmentId for device{} is {}", | ||
72 | + deviceId, | ||
73 | + deviceSegmentIdMap.get(deviceId)); | ||
74 | + return deviceSegmentIdMap.get(deviceId); | ||
75 | + } else { | ||
76 | + throw new IllegalStateException(); | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + | ||
81 | + @Override | ||
82 | + public MacAddress getDeviceMac(DeviceId deviceId) { | ||
83 | + if (deviceMacMap.get(deviceId) != null) { | ||
84 | + log.debug("getDeviceMac for device{} is {}", | ||
85 | + deviceId, | ||
86 | + deviceMacMap.get(deviceId)); | ||
87 | + return deviceMacMap.get(deviceId); | ||
88 | + } else { | ||
89 | + throw new IllegalStateException(); | ||
90 | + } | ||
91 | + } | ||
92 | + | ||
93 | + | ||
94 | + @Override | ||
95 | + public boolean isEdgeDevice(DeviceId deviceId) { | ||
96 | + if (deviceId.equals(DeviceId.deviceId("of:0000000000000001")) | ||
97 | + || deviceId.equals(DeviceId.deviceId("of:0000000000000006"))) { | ||
98 | + return true; | ||
99 | + } | ||
100 | + | ||
101 | + return false; | ||
102 | + } | ||
103 | + | ||
104 | + @Override | ||
105 | + public List<Integer> getAllDeviceSegmentIds() { | ||
106 | + return allSegmentIds; | ||
107 | + } | ||
108 | + | ||
109 | + | ||
110 | + /** | ||
111 | + * Returns Segment ID for the router with the MAC address given. | ||
112 | + * | ||
113 | + * @param targetMac Mac address for the router | ||
114 | + * @return Segment ID for the router with the MAC address | ||
115 | + */ | ||
116 | + public int getSegmentId(MacAddress targetMac) { | ||
117 | + for (Map.Entry<DeviceId, MacAddress> entry: deviceMacMap.entrySet()) { | ||
118 | + if (entry.getValue().equals(targetMac)) { | ||
119 | + return deviceSegmentIdMap.get(entry.getKey()); | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + return -1; | ||
124 | + } | ||
125 | + | ||
126 | + /** | ||
127 | + * Returns Segment ID for the router withe IP address given. | ||
128 | + * | ||
129 | + * @param targetAddress IP address of the router | ||
130 | + * @return Segment ID for the router with the IP address | ||
131 | + */ | ||
132 | + public int getSegmentId(Ip4Address targetAddress) { | ||
133 | + for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) { | ||
134 | + if (entry.getValue().equals(targetAddress)) { | ||
135 | + return deviceSegmentIdMap.get(entry.getKey()); | ||
136 | + } | ||
137 | + } | ||
138 | + | ||
139 | + return -1; | ||
140 | + } | ||
141 | + | ||
142 | + /** | ||
143 | + * Returns Router IP address for the router with the device ID given. | ||
144 | + * | ||
145 | + * @param deviceId device ID of the router | ||
146 | + * @return IP address of the router | ||
147 | + */ | ||
148 | + public Ip4Address getRouterIp(DeviceId deviceId) { | ||
149 | + if (deviceIpMap.get(deviceId) != null) { | ||
150 | + log.debug("getDeviceIp for device{} is {}", | ||
151 | + deviceId, | ||
152 | + deviceIpMap.get(deviceId)); | ||
153 | + return deviceIpMap.get(deviceId); | ||
154 | + } else { | ||
155 | + throw new IllegalStateException(); | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | + /** | ||
160 | + * Returns the Device ID of the router with the Segment ID given. | ||
161 | + * | ||
162 | + * @param sid Segment ID of the router | ||
163 | + * @return Device ID of the router | ||
164 | + */ | ||
165 | + public DeviceId getDeviceId(int sid) { | ||
166 | + for (Map.Entry<DeviceId, Integer> entry: deviceSegmentIdMap.entrySet()) { | ||
167 | + if (entry.getValue() == sid) { | ||
168 | + return entry.getKey(); | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + return null; | ||
173 | + } | ||
174 | + | ||
175 | + /** | ||
176 | + * Returns the Device ID of the router with the IP address given. | ||
177 | + * | ||
178 | + * @param ipAddress IP address of the router | ||
179 | + * @return Device ID of the router | ||
180 | + */ | ||
181 | + public DeviceId getDeviceId(Ip4Address ipAddress) { | ||
182 | + for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) { | ||
183 | + if (entry.getValue().equals(ipAddress)) { | ||
184 | + return entry.getKey(); | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + return null; | ||
189 | + } | ||
190 | +} |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
0 → 100644
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import org.onosproject.net.DefaultLink; | ||
19 | +import org.onosproject.net.DefaultPath; | ||
20 | +import org.onosproject.net.Device; | ||
21 | +import org.onosproject.net.DeviceId; | ||
22 | +import org.onosproject.net.Link; | ||
23 | +import org.onosproject.net.Path; | ||
24 | +import org.onosproject.net.provider.ProviderId; | ||
25 | +import org.slf4j.Logger; | ||
26 | +import org.slf4j.LoggerFactory; | ||
27 | +import java.util.ArrayList; | ||
28 | +import java.util.HashMap; | ||
29 | +import java.util.LinkedList; | ||
30 | +import java.util.List; | ||
31 | + | ||
32 | +/** | ||
33 | + * This class creates bandwidth constrained breadth first tree and returns paths | ||
34 | + * from root Device to leaf Devicees which satisfies the bandwidth condition. If | ||
35 | + * bandwidth parameter is not specified, the normal breadth first tree will be | ||
36 | + * calculated. The paths are snapshot paths at the point of the class | ||
37 | + * instantiation. | ||
38 | + */ | ||
39 | +public class ECMPShortestPathGraph { | ||
40 | + LinkedList<DeviceId> deviceQueue = new LinkedList<>(); | ||
41 | + LinkedList<Integer> distanceQueue = new LinkedList<>(); | ||
42 | + HashMap<DeviceId, Integer> deviceSearched = new HashMap<>(); | ||
43 | + HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>(); | ||
44 | + HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>(); | ||
45 | + HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>(); | ||
46 | + DeviceId rootDevice; | ||
47 | + private SegmentRoutingManager srManager; | ||
48 | + private static final Logger log = LoggerFactory | ||
49 | + .getLogger(ECMPShortestPathGraph.class); | ||
50 | + | ||
51 | + /** | ||
52 | + * Constructor. | ||
53 | + * | ||
54 | + * @param rootDevice root of the BFS tree | ||
55 | + * @param linkListToAvoid link list to avoid | ||
56 | + * @param deviceIdListToAvoid device list to avoid | ||
57 | + */ | ||
58 | + public ECMPShortestPathGraph(DeviceId rootDevice, List<String> deviceIdListToAvoid, | ||
59 | + List<Link> linkListToAvoid) { | ||
60 | + this.rootDevice = rootDevice; | ||
61 | + calcECMPShortestPathGraph(deviceIdListToAvoid, linkListToAvoid); | ||
62 | + } | ||
63 | + | ||
64 | + /** | ||
65 | + * Constructor. | ||
66 | + * | ||
67 | + * @param rootDevice root of the BFS tree | ||
68 | + * @param srManager SegmentRoutingManager object | ||
69 | + */ | ||
70 | + public ECMPShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) { | ||
71 | + this.rootDevice = rootDevice; | ||
72 | + this.srManager = srManager; | ||
73 | + calcECMPShortestPathGraph(); | ||
74 | + } | ||
75 | + | ||
76 | + /** | ||
77 | + * Calculates the BFS tree using any provided constraints and Intents. | ||
78 | + */ | ||
79 | + private void calcECMPShortestPathGraph() { | ||
80 | + deviceQueue.add(rootDevice); | ||
81 | + int currDistance = 0; | ||
82 | + distanceQueue.add(currDistance); | ||
83 | + deviceSearched.put(rootDevice, currDistance); | ||
84 | + while (!deviceQueue.isEmpty()) { | ||
85 | + DeviceId sw = deviceQueue.poll(); | ||
86 | + DeviceId prevSw = null; | ||
87 | + currDistance = distanceQueue.poll(); | ||
88 | + | ||
89 | + for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) { | ||
90 | + DeviceId reachedDevice = link.dst().deviceId(); | ||
91 | + if ((prevSw != null) | ||
92 | + && (prevSw.equals(reachedDevice))) { | ||
93 | + /* Ignore LAG links between the same set of Devicees */ | ||
94 | + continue; | ||
95 | + } else { | ||
96 | + prevSw = reachedDevice; | ||
97 | + } | ||
98 | + | ||
99 | + Integer distance = deviceSearched.get(reachedDevice); | ||
100 | + if ((distance != null) && (distance.intValue() < (currDistance + 1))) { | ||
101 | + continue; | ||
102 | + } | ||
103 | + if (distance == null) { | ||
104 | + /* First time visiting this Device node */ | ||
105 | + deviceQueue.add(reachedDevice); | ||
106 | + distanceQueue.add(currDistance + 1); | ||
107 | + deviceSearched.put(reachedDevice, currDistance + 1); | ||
108 | + | ||
109 | + ArrayList<DeviceId> distanceSwArray = distanceDeviceMap | ||
110 | + .get(currDistance + 1); | ||
111 | + if (distanceSwArray == null) { | ||
112 | + distanceSwArray = new ArrayList<DeviceId>(); | ||
113 | + distanceSwArray.add(reachedDevice); | ||
114 | + distanceDeviceMap.put(currDistance + 1, distanceSwArray); | ||
115 | + } else { | ||
116 | + distanceSwArray.add(reachedDevice); | ||
117 | + } | ||
118 | + } | ||
119 | + | ||
120 | + ArrayList<Link> upstreamLinkArray = | ||
121 | + upstreamLinks.get(reachedDevice); | ||
122 | + if (upstreamLinkArray == null) { | ||
123 | + upstreamLinkArray = new ArrayList<Link>(); | ||
124 | + upstreamLinkArray.add(copyDefaultLink(link)); | ||
125 | + //upstreamLinkArray.add(link); | ||
126 | + upstreamLinks.put(reachedDevice, upstreamLinkArray); | ||
127 | + } else { | ||
128 | + /* ECMP links */ | ||
129 | + upstreamLinkArray.add(copyDefaultLink(link)); | ||
130 | + } | ||
131 | + } | ||
132 | + } | ||
133 | + } | ||
134 | + | ||
135 | + /** | ||
136 | + * Calculates the BFS tree using any provided constraints and Intents. | ||
137 | + */ | ||
138 | + private void calcECMPShortestPathGraph(List<String> deviceIdListToAvoid, List<Link> linksToAvoid) { | ||
139 | + deviceQueue.add(rootDevice); | ||
140 | + int currDistance = 0; | ||
141 | + distanceQueue.add(currDistance); | ||
142 | + deviceSearched.put(rootDevice, currDistance); | ||
143 | + boolean foundLinkToAvoid = false; | ||
144 | + while (!deviceQueue.isEmpty()) { | ||
145 | + DeviceId sw = deviceQueue.poll(); | ||
146 | + DeviceId prevSw = null; | ||
147 | + currDistance = distanceQueue.poll(); | ||
148 | + for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) { | ||
149 | + for (Link linkToAvoid: linksToAvoid) { | ||
150 | + // TODO: equls should work | ||
151 | + //if (link.equals(linkToAvoid)) { | ||
152 | + if (linkContains(link, linksToAvoid)) { | ||
153 | + foundLinkToAvoid = true; | ||
154 | + break; | ||
155 | + } | ||
156 | + } | ||
157 | + if (foundLinkToAvoid) { | ||
158 | + foundLinkToAvoid = false; | ||
159 | + continue; | ||
160 | + } | ||
161 | + DeviceId reachedDevice = link.dst().deviceId(); | ||
162 | + if (deviceIdListToAvoid.contains(reachedDevice.toString())) { | ||
163 | + continue; | ||
164 | + } | ||
165 | + if ((prevSw != null) | ||
166 | + && (prevSw.equals(reachedDevice))) { | ||
167 | + /* Ignore LAG links between the same set of Devicees */ | ||
168 | + continue; | ||
169 | + } else { | ||
170 | + prevSw = reachedDevice; | ||
171 | + } | ||
172 | + | ||
173 | + Integer distance = deviceSearched.get(reachedDevice); | ||
174 | + if ((distance != null) && (distance.intValue() < (currDistance + 1))) { | ||
175 | + continue; | ||
176 | + } | ||
177 | + if (distance == null) { | ||
178 | + /* First time visiting this Device node */ | ||
179 | + deviceQueue.add(reachedDevice); | ||
180 | + distanceQueue.add(currDistance + 1); | ||
181 | + deviceSearched.put(reachedDevice, currDistance + 1); | ||
182 | + | ||
183 | + ArrayList<DeviceId> distanceSwArray = distanceDeviceMap | ||
184 | + .get(currDistance + 1); | ||
185 | + if (distanceSwArray == null) { | ||
186 | + distanceSwArray = new ArrayList<DeviceId>(); | ||
187 | + distanceSwArray.add(reachedDevice); | ||
188 | + distanceDeviceMap.put(currDistance + 1, distanceSwArray); | ||
189 | + } else { | ||
190 | + distanceSwArray.add(reachedDevice); | ||
191 | + } | ||
192 | + } | ||
193 | + | ||
194 | + ArrayList<Link> upstreamLinkArray = | ||
195 | + upstreamLinks.get(reachedDevice); | ||
196 | + if (upstreamLinkArray == null) { | ||
197 | + upstreamLinkArray = new ArrayList<Link>(); | ||
198 | + upstreamLinkArray.add(copyDefaultLink(link)); | ||
199 | + upstreamLinks.put(reachedDevice, upstreamLinkArray); | ||
200 | + } else { | ||
201 | + /* ECMP links */ | ||
202 | + upstreamLinkArray.add(copyDefaultLink(link)); | ||
203 | + } | ||
204 | + } | ||
205 | + } | ||
206 | + } | ||
207 | + | ||
208 | + | ||
209 | + private boolean linkContains(Link link, List<Link> links) { | ||
210 | + | ||
211 | + DeviceId srcDevice1 = link.src().deviceId(); | ||
212 | + DeviceId dstDevice1 = link.dst().deviceId(); | ||
213 | + long srcPort1 = link.src().port().toLong(); | ||
214 | + long dstPort1 = link.dst().port().toLong(); | ||
215 | + | ||
216 | + for (Link link2: links) { | ||
217 | + DeviceId srcDevice2 = link2.src().deviceId(); | ||
218 | + DeviceId dstDevice2 = link2.dst().deviceId(); | ||
219 | + long srcPort2 = link2.src().port().toLong(); | ||
220 | + long dstPort2 = link2.dst().port().toLong(); | ||
221 | + | ||
222 | + if (srcDevice1.toString().equals(srcDevice2.toString()) | ||
223 | + && dstDevice1.toString().equals(dstDevice2.toString()) | ||
224 | + && srcPort1 == srcPort2 && dstPort1 == dstPort2) { | ||
225 | + return true; | ||
226 | + } | ||
227 | + } | ||
228 | + | ||
229 | + return false; | ||
230 | + } | ||
231 | + | ||
232 | + private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) { | ||
233 | + DeviceId rootDeviceDeviceId = rootDevice; | ||
234 | + for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) { | ||
235 | + /* Deep clone the path object */ | ||
236 | + Path sofarPath; | ||
237 | + ArrayList<Link> sofarLinks = new ArrayList<Link>(); | ||
238 | + if (path != null && !path.links().isEmpty()) { | ||
239 | + sofarLinks.addAll(path.links()); | ||
240 | + } | ||
241 | + sofarLinks.add(upstreamLink); | ||
242 | + sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, 0); | ||
243 | + if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) { | ||
244 | + paths.add(sofarPath); | ||
245 | + return; | ||
246 | + } else { | ||
247 | + getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths); | ||
248 | + } | ||
249 | + } | ||
250 | + } | ||
251 | + | ||
252 | + /** | ||
253 | + * Return root Device for the graph. | ||
254 | + * | ||
255 | + * @return root Device | ||
256 | + */ | ||
257 | + public DeviceId getRootDevice() { | ||
258 | + return rootDevice; | ||
259 | + } | ||
260 | + | ||
261 | + /** | ||
262 | + * Return the computed ECMP paths from the root Device to a given Device in | ||
263 | + * the network. | ||
264 | + * | ||
265 | + * @param targetDevice the target Device | ||
266 | + * @return the list of ECMP Paths from the root Device to the target Device | ||
267 | + */ | ||
268 | + public ArrayList<Path> getECMPPaths(DeviceId targetDevice) { | ||
269 | + ArrayList<Path> pathArray = paths.get(targetDevice); | ||
270 | + if (pathArray == null && deviceSearched.containsKey( | ||
271 | + targetDevice)) { | ||
272 | + pathArray = new ArrayList<>(); | ||
273 | + DeviceId sw = targetDevice; | ||
274 | + getDFSPaths(sw, null, pathArray); | ||
275 | + paths.put(targetDevice, pathArray); | ||
276 | + } | ||
277 | + return pathArray; | ||
278 | + } | ||
279 | + | ||
280 | + /** | ||
281 | + * Return the complete info of the computed ECMP paths for each Device | ||
282 | + * learned in multiple iterations from the root Device. | ||
283 | + * | ||
284 | + * @return the hash table of Devicees learned in multiple Dijkstra | ||
285 | + * iterations and corresponding ECMP paths to it from the root | ||
286 | + * Device | ||
287 | + */ | ||
288 | + public HashMap<Integer, HashMap<DeviceId, | ||
289 | + ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() { | ||
290 | + | ||
291 | + HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new | ||
292 | + HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>>(); | ||
293 | + | ||
294 | + for (Integer itrIndx : distanceDeviceMap.keySet()) { | ||
295 | + HashMap<DeviceId, ArrayList<Path>> swMap = new | ||
296 | + HashMap<DeviceId, ArrayList<Path>>(); | ||
297 | + for (DeviceId sw : distanceDeviceMap.get(itrIndx)) { | ||
298 | + swMap.put(sw, getECMPPaths(sw)); | ||
299 | + } | ||
300 | + pathGraph.put(itrIndx, swMap); | ||
301 | + } | ||
302 | + | ||
303 | + return pathGraph; | ||
304 | + } | ||
305 | + | ||
306 | + /** | ||
307 | + * Return the complete info of the computed ECMP paths for each Device | ||
308 | + * learned in multiple iterations from the root Device. | ||
309 | + * | ||
310 | + * @return the hash table of Devicees learned in multiple Dijkstra | ||
311 | + * iterations and corresponding ECMP paths in terms of Devicees to | ||
312 | + * be traversed to it from the root Device | ||
313 | + */ | ||
314 | + public HashMap<Integer, HashMap<DeviceId, | ||
315 | + ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() { | ||
316 | + | ||
317 | + HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap = | ||
318 | + new HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>>(); | ||
319 | + | ||
320 | + for (Integer itrIndx : distanceDeviceMap.keySet()) { | ||
321 | + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap = | ||
322 | + new HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>(); | ||
323 | + | ||
324 | + for (DeviceId sw : distanceDeviceMap.get(itrIndx)) { | ||
325 | + ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>(); | ||
326 | + for (Path path : getECMPPaths(sw)) { | ||
327 | + ArrayList<DeviceId> swVia = new ArrayList<>(); | ||
328 | + for (Link link : path.links()) { | ||
329 | + if (link.src().deviceId().equals(rootDevice)) { | ||
330 | + /* No need to add the root Device again in | ||
331 | + * the Via list | ||
332 | + */ | ||
333 | + continue; | ||
334 | + } | ||
335 | + swVia.add(link.src().deviceId()); | ||
336 | + } | ||
337 | + swViaArray.add(swVia); | ||
338 | + } | ||
339 | + swMap.put(sw, swViaArray); | ||
340 | + } | ||
341 | + deviceViaMap.put(itrIndx, swMap); | ||
342 | + } | ||
343 | + return deviceViaMap; | ||
344 | + } | ||
345 | + | ||
346 | + | ||
347 | + private Link copyDefaultLink(Link link) { | ||
348 | + DefaultLink src = (DefaultLink) link; | ||
349 | + DefaultLink defaultLink = new DefaultLink(src.providerId(), src.src(), | ||
350 | + src.dst(), src.type(), src.annotations()); | ||
351 | + | ||
352 | + return defaultLink; | ||
353 | + } | ||
354 | + | ||
355 | + @Override | ||
356 | + public String toString() { | ||
357 | + StringBuilder sBuilder = new StringBuilder(); | ||
358 | + for (Device device: srManager.deviceService.getDevices()) { | ||
359 | + if (device.id() != rootDevice) { | ||
360 | + sBuilder.append("Paths from" + rootDevice + " to " + device.id() + "\r\n"); | ||
361 | + ArrayList<Path> paths = getECMPPaths(device.id()); | ||
362 | + if (paths != null) { | ||
363 | + for (Path path : paths) { | ||
364 | + for (Link link : path.links()) { | ||
365 | + sBuilder.append(" : " + link.src() + " -> " + link.dst()); | ||
366 | + } | ||
367 | + } | ||
368 | + } | ||
369 | + } | ||
370 | + } | ||
371 | + return sBuilder.toString(); | ||
372 | + } | ||
373 | +} | ||
374 | + |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import java.nio.ByteBuffer; | ||
19 | + | ||
20 | +import org.onlab.packet.Ethernet; | ||
21 | +import org.onlab.packet.ICMP; | ||
22 | +import org.onlab.packet.IPv4; | ||
23 | +import org.onlab.packet.Ip4Address; | ||
24 | +import org.onlab.packet.IpPrefix; | ||
25 | +import org.onlab.packet.MPLS; | ||
26 | +import org.onosproject.net.ConnectPoint; | ||
27 | +import org.onosproject.net.DeviceId; | ||
28 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
29 | +import org.onosproject.net.flow.TrafficTreatment; | ||
30 | +import org.onosproject.net.packet.DefaultOutboundPacket; | ||
31 | +import org.onosproject.net.packet.InboundPacket; | ||
32 | +import org.onosproject.net.packet.OutboundPacket; | ||
33 | +import org.slf4j.Logger; | ||
34 | +import org.slf4j.LoggerFactory; | ||
35 | + | ||
36 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
37 | + | ||
38 | +public class IcmpHandler { | ||
39 | + | ||
40 | + private static Logger log = LoggerFactory.getLogger(IcmpHandler.class); | ||
41 | + private SegmentRoutingManager srManager; | ||
42 | + private NetworkConfigHandler config; | ||
43 | + | ||
44 | + /** | ||
45 | + * Creates an IcmpHandler object. | ||
46 | + * | ||
47 | + * @param srManager SegmentRoutingManager object | ||
48 | + */ | ||
49 | + public IcmpHandler(SegmentRoutingManager srManager) { | ||
50 | + this.srManager = srManager; | ||
51 | + this.config = checkNotNull(srManager.networkConfigHandler); | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * Process incoming ICMP packet. | ||
56 | + * If it is an ICMP request to router or known host, then sends an ICMP response. | ||
57 | + * If it is an ICMP packet to known host and forward the packet to the host. | ||
58 | + * If it is an ICMP packet to unknown host in a subnet, then sends an ARP request | ||
59 | + * to the subnet. | ||
60 | + * | ||
61 | + * @param pkt | ||
62 | + */ | ||
63 | + public void processPacketIn(InboundPacket pkt) { | ||
64 | + | ||
65 | + Ethernet ethernet = pkt.parsed(); | ||
66 | + IPv4 ipv4 = (IPv4) ethernet.getPayload(); | ||
67 | + | ||
68 | + ConnectPoint connectPoint = pkt.receivedFrom(); | ||
69 | + DeviceId deviceId = connectPoint.deviceId(); | ||
70 | + Ip4Address destinationAddress = | ||
71 | + Ip4Address.valueOf(ipv4.getDestinationAddress()); | ||
72 | + Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId); | ||
73 | + IpPrefix routerIpPrefix = config.getRouterIpAddress(deviceId); | ||
74 | + Ip4Address routerIpAddress = routerIpPrefix.getIp4Prefix().address(); | ||
75 | + | ||
76 | + // ICMP to the router IP or gateway IP | ||
77 | + if (((ICMP) ipv4.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST && | ||
78 | + (destinationAddress.equals(routerIpAddress) || | ||
79 | + gatewayIpAddress.equals(destinationAddress))) { | ||
80 | + sendICMPResponse(ethernet, connectPoint); | ||
81 | + // TODO: do we need to set the flow rule again ?? | ||
82 | + | ||
83 | + // ICMP for any known host | ||
84 | + } else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) { | ||
85 | + srManager.ipHandler.forwardPackets(deviceId, destinationAddress); | ||
86 | + | ||
87 | + // ICMP for an unknown host in the subnet of the router | ||
88 | + } else if (config.inSameSubnet(deviceId, destinationAddress)) { | ||
89 | + srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint); | ||
90 | + | ||
91 | + // ICMP for an unknown host | ||
92 | + } else { | ||
93 | + log.debug("ICMP request for unknown host {} ", destinationAddress); | ||
94 | + // Do nothing | ||
95 | + } | ||
96 | + } | ||
97 | + | ||
98 | + private void sendICMPResponse(Ethernet icmpRequest, ConnectPoint outport) { | ||
99 | + | ||
100 | + Ethernet icmpReplyEth = new Ethernet(); | ||
101 | + | ||
102 | + IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload(); | ||
103 | + IPv4 icmpReplyIpv4 = new IPv4(); | ||
104 | + | ||
105 | + int destAddress = icmpRequestIpv4.getDestinationAddress(); | ||
106 | + icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress()); | ||
107 | + icmpReplyIpv4.setSourceAddress(destAddress); | ||
108 | + icmpReplyIpv4.setTtl((byte) 64); | ||
109 | + icmpReplyIpv4.setChecksum((short) 0); | ||
110 | + | ||
111 | + ICMP icmpReply = (ICMP) icmpRequestIpv4.getPayload().clone(); | ||
112 | + icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY); | ||
113 | + icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY); | ||
114 | + icmpReply.setChecksum((short) 0); | ||
115 | + | ||
116 | + icmpReplyIpv4.setPayload(icmpReply); | ||
117 | + | ||
118 | + icmpReplyEth.setPayload(icmpReplyIpv4); | ||
119 | + icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4); | ||
120 | + icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress()); | ||
121 | + icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress()); | ||
122 | + icmpReplyEth.setVlanID(icmpRequest.getVlanID()); | ||
123 | + | ||
124 | + Ip4Address destIpAddress = Ip4Address.valueOf(icmpReplyIpv4.getDestinationAddress()); | ||
125 | + Ip4Address destRouterAddress = config.getDestinationRouterAddress(destIpAddress); | ||
126 | + int sid = config.getMplsId(destRouterAddress); | ||
127 | + if (sid < 0) { | ||
128 | + log.warn("Cannot find the Segment ID for {}", destAddress); | ||
129 | + return; | ||
130 | + } | ||
131 | + | ||
132 | + sendPacketOut(outport, icmpReplyEth, sid); | ||
133 | + | ||
134 | + } | ||
135 | + | ||
136 | + private void sendPacketOut(ConnectPoint outport, Ethernet payload, int sid) { | ||
137 | + | ||
138 | + IPv4 ipPacket = (IPv4) payload.getPayload(); | ||
139 | + Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress()); | ||
140 | + | ||
141 | + if (sid == -1 || config.getMplsId(payload.getDestinationMAC()) == sid || | ||
142 | + config.inSameSubnet(outport.deviceId(), destIpAddress)) { | ||
143 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder(). | ||
144 | + setOutput(outport.port()).build(); | ||
145 | + OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(), | ||
146 | + treatment, ByteBuffer.wrap(payload.serialize())); | ||
147 | + srManager.packetService.emit(packet); | ||
148 | + } else { | ||
149 | + log.warn("Send a MPLS packet as a ICMP response"); | ||
150 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
151 | + .setOutput(outport.port()) | ||
152 | + .build(); | ||
153 | + | ||
154 | + payload.setEtherType(Ethernet.MPLS_UNICAST); | ||
155 | + MPLS mplsPkt = new MPLS(); | ||
156 | + mplsPkt.setLabel(sid); | ||
157 | + mplsPkt.setTtl(((IPv4) payload.getPayload()).getTtl()); | ||
158 | + mplsPkt.setPayload(payload.getPayload()); | ||
159 | + payload.setPayload(mplsPkt); | ||
160 | + | ||
161 | + OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(), | ||
162 | + treatment, ByteBuffer.wrap(payload.serialize())); | ||
163 | + | ||
164 | + srManager.packetService.emit(packet); | ||
165 | + } | ||
166 | + } | ||
167 | +} |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import org.onlab.packet.Ethernet; | ||
19 | +import org.onlab.packet.IPv4; | ||
20 | +import org.onlab.packet.Ip4Address; | ||
21 | +import org.onosproject.net.ConnectPoint; | ||
22 | +import org.onosproject.net.DeviceId; | ||
23 | +import org.onosproject.net.Host; | ||
24 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
25 | +import org.onosproject.net.flow.TrafficTreatment; | ||
26 | +import org.onosproject.net.packet.DefaultOutboundPacket; | ||
27 | +import org.onosproject.net.packet.InboundPacket; | ||
28 | +import org.onosproject.net.packet.OutboundPacket; | ||
29 | +import org.slf4j.Logger; | ||
30 | +import org.slf4j.LoggerFactory; | ||
31 | + | ||
32 | +import java.nio.ByteBuffer; | ||
33 | +import java.util.concurrent.ConcurrentHashMap; | ||
34 | +import java.util.concurrent.ConcurrentLinkedQueue; | ||
35 | + | ||
36 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
37 | + | ||
38 | +public class IpHandler { | ||
39 | + | ||
40 | + private static Logger log = LoggerFactory.getLogger(IpHandler.class); | ||
41 | + private SegmentRoutingManager srManager; | ||
42 | + private NetworkConfigHandler config; | ||
43 | + private ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>> ipPacketQueue; | ||
44 | + | ||
45 | + /** | ||
46 | + * Creates an IpHandler object. | ||
47 | + * | ||
48 | + * @param srManager SegmentRoutingManager object | ||
49 | + */ | ||
50 | + public IpHandler(SegmentRoutingManager srManager) { | ||
51 | + this.srManager = srManager; | ||
52 | + this.config = checkNotNull(srManager.networkConfigHandler); | ||
53 | + ipPacketQueue = new ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>>(); | ||
54 | + } | ||
55 | + | ||
56 | + /** | ||
57 | + * Processes incoming IP packets. | ||
58 | + * | ||
59 | + * If it is an IP packet for known host, then forward it to the host. | ||
60 | + * If it is an IP packet for unknown host in subnet, then send an ARP request | ||
61 | + * to the subnet. | ||
62 | + * | ||
63 | + * @param pkt incoming packet | ||
64 | + */ | ||
65 | + public void processPacketIn(InboundPacket pkt) { | ||
66 | + Ethernet ethernet = pkt.parsed(); | ||
67 | + IPv4 ipv4 = (IPv4) ethernet.getPayload(); | ||
68 | + | ||
69 | + ConnectPoint connectPoint = pkt.receivedFrom(); | ||
70 | + DeviceId deviceId = connectPoint.deviceId(); | ||
71 | + Ip4Address destinationAddress = | ||
72 | + Ip4Address.valueOf(ipv4.getDestinationAddress()); | ||
73 | + | ||
74 | + // IP packet for know hosts | ||
75 | + if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) { | ||
76 | + forwardPackets(deviceId, destinationAddress); | ||
77 | + | ||
78 | + // IP packet for unknown host in the subnet of the router | ||
79 | + } else if (config.inSameSubnet(deviceId, destinationAddress)) { | ||
80 | + srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint); | ||
81 | + | ||
82 | + // IP packets for unknown host | ||
83 | + } else { | ||
84 | + log.debug("ICMP request for unknown host {} which is not in the subnet", | ||
85 | + destinationAddress); | ||
86 | + // Do nothing | ||
87 | + } | ||
88 | + } | ||
89 | + | ||
90 | + /** | ||
91 | + * Adds the IP packet to a buffer. | ||
92 | + * The packets are forwarded to corresponding destination when the destination | ||
93 | + * MAC address is known via ARP response. | ||
94 | + * | ||
95 | + * @param ipPacket IP packet to add to the buffer | ||
96 | + */ | ||
97 | + public void addToPacketBuffer(IPv4 ipPacket) { | ||
98 | + | ||
99 | + // Better not buffer TPC packets due to out-of-order packet transfer | ||
100 | + if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) { | ||
101 | + return; | ||
102 | + } | ||
103 | + | ||
104 | + Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress()); | ||
105 | + | ||
106 | + if (ipPacketQueue.get(destIpAddress) == null) { | ||
107 | + ConcurrentLinkedQueue<IPv4> queue = new ConcurrentLinkedQueue<IPv4>(); | ||
108 | + queue.add(ipPacket); | ||
109 | + ipPacketQueue.put(destIpAddress, queue); | ||
110 | + } else { | ||
111 | + ipPacketQueue.get(destIpAddress).add(ipPacket); | ||
112 | + } | ||
113 | + } | ||
114 | + | ||
115 | + /** | ||
116 | + * Forwards IP packets in the buffer to the destination IP address. | ||
117 | + * It is called when the controller finds the destination MAC address | ||
118 | + * via ARP responsees. | ||
119 | + * | ||
120 | + * @param deviceId switch device ID | ||
121 | + * @param destIpAddress destination IP address | ||
122 | + */ | ||
123 | + public void forwardPackets(DeviceId deviceId, Ip4Address destIpAddress) { | ||
124 | + for (IPv4 ipPacket : ipPacketQueue.get(destIpAddress)) { | ||
125 | + Ip4Address destAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress()); | ||
126 | + if (ipPacket != null && config.inSameSubnet(deviceId, destAddress)) { | ||
127 | + ipPacket.setTtl((byte) (ipPacket.getTtl() - 1)); | ||
128 | + ipPacket.setChecksum((short) 0); | ||
129 | + for (Host dest: srManager.hostService.getHostsByIp(destIpAddress)) { | ||
130 | + Ethernet eth = new Ethernet(); | ||
131 | + eth.setDestinationMACAddress(dest.mac()); | ||
132 | + eth.setSourceMACAddress(config.getRouterMacAddress( | ||
133 | + deviceId)); | ||
134 | + eth.setEtherType(Ethernet.TYPE_IPV4); | ||
135 | + eth.setPayload(ipPacket); | ||
136 | + | ||
137 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder(). | ||
138 | + setOutput(dest.location().port()).build(); | ||
139 | + OutboundPacket packet = new DefaultOutboundPacket(deviceId, | ||
140 | + treatment, ByteBuffer.wrap(eth.serialize())); | ||
141 | + srManager.packetService.emit(packet); | ||
142 | + } | ||
143 | + } | ||
144 | + } | ||
145 | + } | ||
146 | +} |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
0 → 100644
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import com.google.common.collect.Lists; | ||
19 | +import org.onlab.packet.Ip4Address; | ||
20 | +import org.onlab.packet.Ip4Prefix; | ||
21 | +import org.onlab.packet.IpPrefix; | ||
22 | +import org.onlab.packet.MacAddress; | ||
23 | +import org.onosproject.net.DeviceId; | ||
24 | +import org.onosproject.net.Link; | ||
25 | +import org.onosproject.net.PortNumber; | ||
26 | +import org.slf4j.Logger; | ||
27 | +import org.slf4j.LoggerFactory; | ||
28 | + | ||
29 | +import java.net.URI; | ||
30 | +import java.util.List; | ||
31 | +import java.util.Set; | ||
32 | + | ||
33 | +/** | ||
34 | + * This class is temporary class and used only for test. | ||
35 | + * It will be replaced with "real" Network Config Manager. | ||
36 | + */ | ||
37 | + | ||
38 | +public class NetworkConfigHandler { | ||
39 | + | ||
40 | + private static Logger log = LoggerFactory.getLogger(NetworkConfigHandler.class); | ||
41 | + private SegmentRoutingManager srManager; | ||
42 | + private DeviceConfiguration deviceConfig = new DeviceConfiguration(); | ||
43 | + | ||
44 | + public NetworkConfigHandler(SegmentRoutingManager srManager) { | ||
45 | + this.srManager = srManager; | ||
46 | + } | ||
47 | + | ||
48 | + public Ip4Address getGatewayIpAddress(DeviceId deviceId) { | ||
49 | + | ||
50 | + if (deviceId.uri().equals(URI.create("of:0000000000000001"))) { | ||
51 | + return Ip4Address.valueOf("10.0.1.128"); | ||
52 | + } else if (deviceId.uri().equals(URI.create("of:0000000000000006"))) { | ||
53 | + return Ip4Address.valueOf("7.7.7.128"); | ||
54 | + } | ||
55 | + | ||
56 | + log.warn("No gateway Ip address was found for {}", deviceId); | ||
57 | + return Ip4Address.valueOf("0.0.0.0"); | ||
58 | + } | ||
59 | + | ||
60 | + public IpPrefix getRouterIpAddress(DeviceId deviceId) { | ||
61 | + | ||
62 | + return IpPrefix.valueOf(deviceConfig.getRouterIp(deviceId), 32); | ||
63 | + } | ||
64 | + | ||
65 | + public MacAddress getRouterMacAddress(DeviceId deviceId) { | ||
66 | + return deviceConfig.getDeviceMac(deviceId); | ||
67 | + } | ||
68 | + | ||
69 | + public boolean inSameSubnet(DeviceId deviceId, Ip4Address destIp) { | ||
70 | + | ||
71 | + String subnetInfo = getSubnetInfo(deviceId); | ||
72 | + if (subnetInfo == null) { | ||
73 | + return false; | ||
74 | + } | ||
75 | + | ||
76 | + IpPrefix prefix = IpPrefix.valueOf(subnetInfo); | ||
77 | + if (prefix.contains(destIp)) { | ||
78 | + return true; | ||
79 | + } | ||
80 | + | ||
81 | + return false; | ||
82 | + } | ||
83 | + | ||
84 | + public boolean inSameSubnet(Ip4Address address, int sid) { | ||
85 | + DeviceId deviceId = deviceConfig.getDeviceId(sid); | ||
86 | + if (deviceId == null) { | ||
87 | + log.warn("Cannot find a device for SID {}", sid); | ||
88 | + return false; | ||
89 | + } | ||
90 | + | ||
91 | + String subnetInfo = getSubnetInfo(deviceId); | ||
92 | + if (subnetInfo == null) { | ||
93 | + log.warn("Cannot find the subnet info for {}", deviceId); | ||
94 | + return false; | ||
95 | + } | ||
96 | + | ||
97 | + Ip4Prefix subnet = Ip4Prefix.valueOf(subnetInfo); | ||
98 | + if (subnet.contains(address)) { | ||
99 | + return true; | ||
100 | + } | ||
101 | + | ||
102 | + return false; | ||
103 | + | ||
104 | + } | ||
105 | + | ||
106 | + public String getSubnetInfo(DeviceId deviceId) { | ||
107 | + // TODO : supports multiple subnet | ||
108 | + if (deviceId.uri().equals(URI.create("of:0000000000000001"))) { | ||
109 | + return "10.0.1.1/24"; | ||
110 | + } else if (deviceId.uri().equals(URI.create("of:0000000000000006"))) { | ||
111 | + return "7.7.7.7/24"; | ||
112 | + } else { | ||
113 | + log.error("Switch {} is not an edge router", deviceId); | ||
114 | + return null; | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + public int getMplsId(DeviceId deviceId) { | ||
119 | + return deviceConfig.getSegmentId(deviceId); | ||
120 | + } | ||
121 | + | ||
122 | + public int getMplsId(MacAddress mac) { | ||
123 | + return deviceConfig.getSegmentId(mac); | ||
124 | + } | ||
125 | + | ||
126 | + public int getMplsId(Ip4Address address) { | ||
127 | + return deviceConfig.getSegmentId(address); | ||
128 | + } | ||
129 | + | ||
130 | + public boolean isEcmpNotSupportedInTransit(DeviceId deviceId) { | ||
131 | + return false; | ||
132 | + } | ||
133 | + | ||
134 | + public boolean isTransitRouter(DeviceId deviceId) { | ||
135 | + return true; | ||
136 | + } | ||
137 | + | ||
138 | + | ||
139 | + public boolean isEdgeRouter(DeviceId deviceId) { | ||
140 | + if (deviceId.uri().equals(URI.create("of:0000000000000001")) | ||
141 | + || deviceId.uri().equals(URI.create("of:0000000000000006"))) { | ||
142 | + return true; | ||
143 | + } | ||
144 | + | ||
145 | + return false; | ||
146 | + } | ||
147 | + | ||
148 | + private List<PortNumber> getPortsToNeighbors(DeviceId deviceId, List<DeviceId> fwdSws) { | ||
149 | + | ||
150 | + List<PortNumber> portNumbers = Lists.newArrayList(); | ||
151 | + | ||
152 | + Set<Link> links = srManager.linkService.getDeviceEgressLinks(deviceId); | ||
153 | + for (Link link: links) { | ||
154 | + for (DeviceId swId: fwdSws) { | ||
155 | + if (link.dst().deviceId().equals(swId)) { | ||
156 | + portNumbers.add(link.src().port()); | ||
157 | + break; | ||
158 | + } | ||
159 | + } | ||
160 | + } | ||
161 | + | ||
162 | + return portNumbers; | ||
163 | + } | ||
164 | + | ||
165 | + public List<PortNumber> getPortsToDevice(DeviceId deviceId) { | ||
166 | + List<PortNumber> portNumbers = Lists.newArrayList(); | ||
167 | + | ||
168 | + Set<Link> links = srManager.linkService.getDeviceEgressLinks(deviceId); | ||
169 | + for (Link link: links) { | ||
170 | + if (link.dst().deviceId().equals(deviceId)) { | ||
171 | + portNumbers.add(link.src().port()); | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + return portNumbers; | ||
176 | + } | ||
177 | + | ||
178 | + | ||
179 | + public Ip4Address getDestinationRouterAddress(Ip4Address destIpAddress) { | ||
180 | + // TODO: need to check the subnet info | ||
181 | + if (destIpAddress.toString().equals("10.0.1.1")) { | ||
182 | + return Ip4Address.valueOf("192.168.0.1"); | ||
183 | + } else if (destIpAddress.toString().equals("7.7.7.7")) { | ||
184 | + return Ip4Address.valueOf("192.168.0.6"); | ||
185 | + } else { | ||
186 | + log.warn("No router was found for {}", destIpAddress); | ||
187 | + return null; | ||
188 | + } | ||
189 | + | ||
190 | + } | ||
191 | + | ||
192 | + public DeviceId getDeviceId(Ip4Address ip4Address) { | ||
193 | + return deviceConfig.getDeviceId(ip4Address); | ||
194 | + } | ||
195 | + | ||
196 | + public MacAddress getRouterMac(Ip4Address targetAddress) { | ||
197 | + if (targetAddress.toString().equals("10.0.1.128")) { | ||
198 | + return MacAddress.valueOf("00:00:00:00:00:01"); | ||
199 | + } else if (targetAddress.toString().equals("7.7.7.128")) { | ||
200 | + return MacAddress.valueOf("00:00:00:00:00:06"); | ||
201 | + } else { | ||
202 | + log.warn("Cannot find a router for {}", targetAddress); | ||
203 | + return null; | ||
204 | + } | ||
205 | + } | ||
206 | +} |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
0 → 100644
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import org.onlab.packet.Ethernet; | ||
19 | +import org.onlab.packet.Ip4Address; | ||
20 | +import org.onlab.packet.IpPrefix; | ||
21 | +import org.onlab.packet.MacAddress; | ||
22 | + | ||
23 | +import org.onlab.packet.MplsLabel; | ||
24 | +import org.onosproject.grouphandler.NeighborSet; | ||
25 | +import org.onosproject.net.DeviceId; | ||
26 | +import org.onosproject.net.Link; | ||
27 | +import org.onosproject.net.PortNumber; | ||
28 | +import org.onosproject.net.flow.DefaultFlowRule; | ||
29 | +import org.onosproject.net.flow.DefaultTrafficSelector; | ||
30 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
31 | +import org.onosproject.net.flow.FlowRule; | ||
32 | +import org.onosproject.net.flow.TrafficSelector; | ||
33 | +import org.onosproject.net.flow.TrafficTreatment; | ||
34 | +import org.onosproject.net.group.DefaultGroupKey; | ||
35 | +import org.onosproject.net.group.Group; | ||
36 | +import org.slf4j.Logger; | ||
37 | +import org.slf4j.LoggerFactory; | ||
38 | + | ||
39 | +import java.util.ArrayList; | ||
40 | +import java.util.Collection; | ||
41 | +import java.util.List; | ||
42 | +import java.util.Set; | ||
43 | + | ||
44 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
45 | + | ||
46 | +public class RoutingRulePopulator { | ||
47 | + | ||
48 | + private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class); | ||
49 | + | ||
50 | + private SegmentRoutingManager srManager; | ||
51 | + private NetworkConfigHandler config; | ||
52 | + | ||
53 | + /** | ||
54 | + * Creates a RoutingRulePopulator object. | ||
55 | + * | ||
56 | + * @param srManager | ||
57 | + */ | ||
58 | + public RoutingRulePopulator(SegmentRoutingManager srManager) { | ||
59 | + this.srManager = srManager; | ||
60 | + this.config = checkNotNull(srManager.networkConfigHandler); | ||
61 | + } | ||
62 | + | ||
63 | + /** | ||
64 | + * Populates IP flow rules for specific hosts directly connected to the switch. | ||
65 | + * | ||
66 | + * @param deviceId switch ID to set the rules | ||
67 | + * @param hostIp host IP address | ||
68 | + * @param hostMac host MAC address | ||
69 | + * @param outPort port where the host is connected | ||
70 | + */ | ||
71 | + public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp, | ||
72 | + MacAddress hostMac, PortNumber outPort) { | ||
73 | + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | ||
74 | + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); | ||
75 | + | ||
76 | + sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 32)); | ||
77 | + sbuilder.matchEthType(Ethernet.TYPE_IPV4); | ||
78 | + | ||
79 | + tbuilder.setEthDst(hostMac) | ||
80 | + .setEthSrc(config.getRouterMacAddress(deviceId)) | ||
81 | + .setOutput(outPort); | ||
82 | + | ||
83 | + TrafficTreatment treatment = tbuilder.build(); | ||
84 | + TrafficSelector selector = sbuilder.build(); | ||
85 | + | ||
86 | + FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100, | ||
87 | + srManager.appId, 600, false, FlowRule.Type.IP); | ||
88 | + | ||
89 | + srManager.flowRuleService.applyFlowRules(f); | ||
90 | + log.debug("Flow rule {} is set to switch {}", f, deviceId); | ||
91 | + } | ||
92 | + | ||
93 | + /** | ||
94 | + * Populates IP flow rules for the subnets of the destination router. | ||
95 | + * | ||
96 | + * @param deviceId switch ID to set the rules | ||
97 | + * @param subnetInfo subnet information | ||
98 | + * @param destSw destination switch ID | ||
99 | + * @param nextHops next hop switch ID list | ||
100 | + * @return true if all rules are set successfully, false otherwise | ||
101 | + */ | ||
102 | + public boolean populateIpRuleForSubnet(DeviceId deviceId, String subnetInfo, | ||
103 | + DeviceId destSw, Set<DeviceId> nextHops) { | ||
104 | + | ||
105 | + List<IpPrefix> subnets = extractSubnet(subnetInfo); | ||
106 | + for (IpPrefix subnet: subnets) { | ||
107 | + if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) { | ||
108 | + return false; | ||
109 | + } | ||
110 | + } | ||
111 | + | ||
112 | + return true; | ||
113 | + } | ||
114 | + | ||
115 | + /** | ||
116 | + * Populates IP flow rules for the router IP address. | ||
117 | + * | ||
118 | + * @param deviceId device ID to set the rules | ||
119 | + * @param ipPrefix the IP address of the destination router | ||
120 | + * @param destSw device ID of the destination router | ||
121 | + * @param nextHops next hop switch ID list | ||
122 | + * @return true if all rules are set successfully, false otherwise | ||
123 | + */ | ||
124 | + public boolean populateIpRuleForRouter(DeviceId deviceId, IpPrefix ipPrefix, | ||
125 | + DeviceId destSw, Set<DeviceId> nextHops) { | ||
126 | + | ||
127 | + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | ||
128 | + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); | ||
129 | + | ||
130 | + sbuilder.matchIPDst(ipPrefix); | ||
131 | + sbuilder.matchEthType(Ethernet.TYPE_IPV4); | ||
132 | + | ||
133 | + NeighborSet ns = null; | ||
134 | + | ||
135 | + //If the next hop is the same as the final destination, then MPLS label is not set. | ||
136 | + if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) { | ||
137 | + tbuilder.decNwTtl(); | ||
138 | + ns = new NeighborSet(nextHops); | ||
139 | + } else { | ||
140 | + tbuilder.copyTtlOut(); | ||
141 | + ns = new NeighborSet(nextHops, config.getMplsId(destSw)); | ||
142 | + } | ||
143 | + | ||
144 | + DefaultGroupKey groupKey = (DefaultGroupKey) srManager.getGroupKey(ns); | ||
145 | + if (groupKey == null) { | ||
146 | + log.warn("Group key is not found for ns {}", ns); | ||
147 | + return false; | ||
148 | + } | ||
149 | + Group group = srManager.groupService.getGroup(deviceId, groupKey); | ||
150 | + if (group != null) { | ||
151 | + tbuilder.group(group.id()); | ||
152 | + } else { | ||
153 | + log.warn("No group found for NeighborSet {} from {} to {}", | ||
154 | + ns, deviceId, destSw); | ||
155 | + return false; | ||
156 | + } | ||
157 | + | ||
158 | + TrafficTreatment treatment = tbuilder.build(); | ||
159 | + TrafficSelector selector = sbuilder.build(); | ||
160 | + | ||
161 | + FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100, | ||
162 | + srManager.appId, 600, false, FlowRule.Type.IP); | ||
163 | + | ||
164 | + srManager.flowRuleService.applyFlowRules(f); | ||
165 | + log.debug("IP flow rule {} is set to switch {}", f, deviceId); | ||
166 | + | ||
167 | + return true; | ||
168 | + } | ||
169 | + | ||
170 | + | ||
171 | + /** | ||
172 | + * Populates MPLS flow rules to all transit routers. | ||
173 | + * | ||
174 | + * @param deviceId device ID of the switch to set the rules | ||
175 | + * @param destSwId destination switch device ID | ||
176 | + * @param nextHops next hops switch ID list | ||
177 | + * @return true if all rules are set successfully, false otherwise | ||
178 | + */ | ||
179 | + public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId, Set<DeviceId> nextHops) { | ||
180 | + | ||
181 | + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | ||
182 | + Collection<TrafficTreatment> treatments = new ArrayList<>(); | ||
183 | + | ||
184 | + // TODO Handle the case of Bos == false | ||
185 | + sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getMplsId(destSwId))); | ||
186 | + sbuilder.matchEthType(Ethernet.MPLS_UNICAST); | ||
187 | + | ||
188 | + //If the next hop is the destination router, do PHP | ||
189 | + if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) { | ||
190 | + TrafficTreatment treatmentBos = | ||
191 | + getMplsTreatment(deviceId, destSwId, nextHops, true, true); | ||
192 | + TrafficTreatment treatment = | ||
193 | + getMplsTreatment(deviceId, destSwId, nextHops, true, false); | ||
194 | + if (treatmentBos != null) { | ||
195 | + treatments.add(treatmentBos); | ||
196 | + } else { | ||
197 | + log.warn("Failed to set MPLS rules."); | ||
198 | + return false; | ||
199 | + } | ||
200 | + } else { | ||
201 | + TrafficTreatment treatmentBos = | ||
202 | + getMplsTreatment(deviceId, destSwId, nextHops, false, true); | ||
203 | + TrafficTreatment treatment = | ||
204 | + getMplsTreatment(deviceId, destSwId, nextHops, false, false); | ||
205 | + | ||
206 | + if (treatmentBos != null) { | ||
207 | + treatments.add(treatmentBos); | ||
208 | + } else { | ||
209 | + log.warn("Failed to set MPLS rules."); | ||
210 | + return false; | ||
211 | + } | ||
212 | + } | ||
213 | + | ||
214 | + TrafficSelector selector = sbuilder.build(); | ||
215 | + for (TrafficTreatment treatment: treatments) { | ||
216 | + FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100, | ||
217 | + srManager.appId, 600, false, FlowRule.Type.MPLS); | ||
218 | + srManager.flowRuleService.applyFlowRules(f); | ||
219 | + log.debug("MPLS rule {} is set to {}", f, deviceId); | ||
220 | + } | ||
221 | + | ||
222 | + return true; | ||
223 | + } | ||
224 | + | ||
225 | + | ||
226 | + private TrafficTreatment getMplsTreatment(DeviceId deviceId, DeviceId destSw, | ||
227 | + Set<DeviceId> nextHops, | ||
228 | + boolean phpRequired, boolean isBos) { | ||
229 | + | ||
230 | + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); | ||
231 | + | ||
232 | + if (phpRequired) { | ||
233 | + tbuilder.copyTtlIn(); | ||
234 | + if (isBos) { | ||
235 | + tbuilder.popMpls(Ethernet.TYPE_IPV4) | ||
236 | + .decNwTtl(); | ||
237 | + } else { | ||
238 | + tbuilder.popMpls(Ethernet.MPLS_UNICAST) | ||
239 | + .decMplsTtl(); | ||
240 | + } | ||
241 | + } else { | ||
242 | + tbuilder.decMplsTtl(); | ||
243 | + } | ||
244 | + | ||
245 | + if (config.isEcmpNotSupportedInTransit(deviceId) | ||
246 | + && config.isTransitRouter(deviceId)) { | ||
247 | + Link link = selectOneLink(deviceId, nextHops); | ||
248 | + if (link == null) { | ||
249 | + log.warn("No link from {} to {}", deviceId, nextHops); | ||
250 | + return null; | ||
251 | + } | ||
252 | + tbuilder.setEthSrc(config.getRouterMacAddress(deviceId)) | ||
253 | + .setEthDst(config.getRouterMacAddress(link.dst().deviceId())) | ||
254 | + .setOutput(link.src().port()); | ||
255 | + } else { | ||
256 | + NeighborSet ns = new NeighborSet(nextHops); | ||
257 | + DefaultGroupKey groupKey = (DefaultGroupKey) srManager.getGroupKey(ns); | ||
258 | + if (groupKey == null) { | ||
259 | + log.warn("Group key is not found for ns {}", ns); | ||
260 | + return null; | ||
261 | + } | ||
262 | + Group group = srManager.groupService.getGroup(deviceId, groupKey); | ||
263 | + if (group != null) { | ||
264 | + tbuilder.group(group.id()); | ||
265 | + } else { | ||
266 | + log.warn("No group found for ns {} key {} in {}", ns, | ||
267 | + srManager.getGroupKey(ns), deviceId); | ||
268 | + return null; | ||
269 | + } | ||
270 | + } | ||
271 | + | ||
272 | + return tbuilder.build(); | ||
273 | + } | ||
274 | + | ||
275 | + /** | ||
276 | + * Populates VLAN flows rules. | ||
277 | + * All packets are forwarded to TMAC table. | ||
278 | + * | ||
279 | + * @param deviceId switch ID to set the rules | ||
280 | + */ | ||
281 | + public void populateTableVlan(DeviceId deviceId) { | ||
282 | + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | ||
283 | + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); | ||
284 | + | ||
285 | + tbuilder.transition(FlowRule.Type.ETHER); | ||
286 | + | ||
287 | + TrafficTreatment treatment = tbuilder.build(); | ||
288 | + TrafficSelector selector = sbuilder.build(); | ||
289 | + | ||
290 | + FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100, | ||
291 | + srManager.appId, 600, false, FlowRule.Type.VLAN); | ||
292 | + | ||
293 | + srManager.flowRuleService.applyFlowRules(f); | ||
294 | + | ||
295 | + log.debug("Vlan flow rule {} is set to switch {}", f, deviceId); | ||
296 | + } | ||
297 | + | ||
298 | + /** | ||
299 | + * Populates TMAC table rules. | ||
300 | + * IP packets are forwarded to IP table. | ||
301 | + * MPLS packets are forwarded to MPLS table. | ||
302 | + * | ||
303 | + * @param deviceId switch ID to set the rules | ||
304 | + */ | ||
305 | + public void populateTableTMac(DeviceId deviceId) { | ||
306 | + | ||
307 | + // flow rule for IP packets | ||
308 | + TrafficSelector selectorIp = DefaultTrafficSelector.builder() | ||
309 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
310 | + .matchEthDst(config.getRouterMacAddress(deviceId)) | ||
311 | + .build(); | ||
312 | + TrafficTreatment treatmentIp = DefaultTrafficTreatment.builder() | ||
313 | + .transition(FlowRule.Type.IP) | ||
314 | + .build(); | ||
315 | + | ||
316 | + FlowRule flowIp = new DefaultFlowRule(deviceId, selectorIp, treatmentIp, 100, | ||
317 | + srManager.appId, 600, false, FlowRule.Type.ETHER); | ||
318 | + | ||
319 | + srManager.flowRuleService.applyFlowRules(flowIp); | ||
320 | + | ||
321 | + // flow rule for MPLS packets | ||
322 | + TrafficSelector selectorMpls = DefaultTrafficSelector.builder() | ||
323 | + .matchEthType(Ethernet.MPLS_UNICAST) | ||
324 | + .matchEthDst(config.getRouterMacAddress(deviceId)) | ||
325 | + .build(); | ||
326 | + TrafficTreatment treatmentMpls = DefaultTrafficTreatment.builder() | ||
327 | + .transition(FlowRule.Type.MPLS) | ||
328 | + .build(); | ||
329 | + | ||
330 | + FlowRule flowMpls = new DefaultFlowRule(deviceId, selectorMpls, treatmentMpls, 100, | ||
331 | + srManager.appId, 600, false, FlowRule.Type.ETHER); | ||
332 | + | ||
333 | + srManager.flowRuleService.applyFlowRules(flowMpls); | ||
334 | + | ||
335 | + } | ||
336 | + | ||
337 | + /** | ||
338 | + * Populates a table miss entry. | ||
339 | + * | ||
340 | + * @param deviceId switch ID to set rules | ||
341 | + * @param tableToAdd table to set the rules | ||
342 | + * @param toControllerNow flag to send packets to controller immediately | ||
343 | + * @param toControllerWrite flag to send packets to controller at the end of pipeline | ||
344 | + * @param toTable flag to send packets to a specific table | ||
345 | + * @param tableToSend table type to send packets when the toTable flag is set | ||
346 | + */ | ||
347 | + public void populateTableMissEntry(DeviceId deviceId, FlowRule.Type tableToAdd, boolean toControllerNow, | ||
348 | + boolean toControllerWrite, | ||
349 | + boolean toTable, FlowRule.Type tableToSend) { | ||
350 | + // TODO: Change arguments to EnumSet | ||
351 | + TrafficSelector selector = DefaultTrafficSelector.builder() | ||
352 | + .build(); | ||
353 | + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); | ||
354 | + | ||
355 | + if (toControllerNow) { | ||
356 | + tBuilder.setOutput(PortNumber.CONTROLLER); | ||
357 | + } | ||
358 | + | ||
359 | + if (toControllerWrite) { | ||
360 | + tBuilder.deferred().setOutput(PortNumber.CONTROLLER); | ||
361 | + } | ||
362 | + | ||
363 | + if (toTable) { | ||
364 | + tBuilder.transition(tableToSend); | ||
365 | + } | ||
366 | + | ||
367 | + FlowRule flow = new DefaultFlowRule(deviceId, selector, tBuilder.build(), 0, | ||
368 | + srManager.appId, 600, false, tableToAdd); | ||
369 | + | ||
370 | + srManager.flowRuleService.applyFlowRules(flow); | ||
371 | + | ||
372 | + } | ||
373 | + | ||
374 | + | ||
375 | + private List<IpPrefix> extractSubnet(String subnetInfo) { | ||
376 | + List<IpPrefix> subnetIpPrefixes = new ArrayList<>(); | ||
377 | + | ||
378 | + // TODO: refactoring required depending on the format of the subnet info | ||
379 | + IpPrefix prefix = IpPrefix.valueOf(subnetInfo); | ||
380 | + if (prefix == null) { | ||
381 | + log.error("Wrong ip prefix type {}", subnetInfo); | ||
382 | + } else { | ||
383 | + subnetIpPrefixes.add(prefix); | ||
384 | + } | ||
385 | + | ||
386 | + return subnetIpPrefixes; | ||
387 | + } | ||
388 | + | ||
389 | + private Link selectOneLink(DeviceId srcId, Set<DeviceId> destIds) { | ||
390 | + | ||
391 | + Set<Link> links = srManager.linkService.getDeviceEgressLinks(srcId); | ||
392 | + DeviceId destId = (DeviceId) destIds.toArray()[0]; | ||
393 | + for (Link link: links) { | ||
394 | + if (link.dst().deviceId().equals(destId)) { | ||
395 | + return link; | ||
396 | + } | ||
397 | + } | ||
398 | + | ||
399 | + return null; | ||
400 | + } | ||
401 | + | ||
402 | +} |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
0 → 100644
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.segmentrouting; | ||
17 | + | ||
18 | +import org.apache.felix.scr.annotations.Activate; | ||
19 | +import org.apache.felix.scr.annotations.Component; | ||
20 | +import org.apache.felix.scr.annotations.Deactivate; | ||
21 | +import org.apache.felix.scr.annotations.Reference; | ||
22 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
23 | +import org.onlab.packet.Ethernet; | ||
24 | +import org.onlab.packet.IPv4; | ||
25 | +import org.onosproject.core.ApplicationId; | ||
26 | +import org.onosproject.core.CoreService; | ||
27 | +import org.onosproject.event.Event; | ||
28 | +import org.onosproject.grouphandler.DefaultGroupHandler; | ||
29 | +import org.onosproject.grouphandler.NeighborSet; | ||
30 | +import org.onosproject.mastership.MastershipService; | ||
31 | +import org.onosproject.net.Device; | ||
32 | +import org.onosproject.net.DeviceId; | ||
33 | +import org.onosproject.net.Link; | ||
34 | +import org.onosproject.net.MastershipRole; | ||
35 | +import org.onosproject.net.Port; | ||
36 | +import org.onosproject.net.device.DeviceEvent; | ||
37 | +import org.onosproject.net.device.DeviceListener; | ||
38 | +import org.onosproject.net.device.DeviceService; | ||
39 | +import org.onosproject.net.flow.FlowRuleService; | ||
40 | +import org.onosproject.net.group.Group; | ||
41 | +import org.onosproject.net.group.GroupEvent; | ||
42 | +import org.onosproject.net.group.GroupKey; | ||
43 | +import org.onosproject.net.group.GroupListener; | ||
44 | +import org.onosproject.net.group.GroupService; | ||
45 | +import org.onosproject.net.host.HostService; | ||
46 | +import org.onosproject.net.intent.IntentService; | ||
47 | +import org.onosproject.net.link.LinkEvent; | ||
48 | +import org.onosproject.net.link.LinkListener; | ||
49 | +import org.onosproject.net.link.LinkService; | ||
50 | +import org.onosproject.net.packet.InboundPacket; | ||
51 | +import org.onosproject.net.packet.PacketContext; | ||
52 | +import org.onosproject.net.packet.PacketProcessor; | ||
53 | +import org.onosproject.net.packet.PacketService; | ||
54 | +import org.onosproject.net.topology.TopologyService; | ||
55 | +import org.slf4j.Logger; | ||
56 | +import org.slf4j.LoggerFactory; | ||
57 | + | ||
58 | +import java.util.Map; | ||
59 | +import java.util.concurrent.ConcurrentHashMap; | ||
60 | +import java.util.concurrent.ConcurrentLinkedQueue; | ||
61 | +import java.util.concurrent.Executors; | ||
62 | +import java.util.concurrent.ScheduledExecutorService; | ||
63 | +import java.util.concurrent.ScheduledFuture; | ||
64 | +import java.util.concurrent.TimeUnit; | ||
65 | + | ||
66 | +@SuppressWarnings("ALL") | ||
67 | +@Component(immediate = true) | ||
68 | +public class SegmentRoutingManager { | ||
69 | + | ||
70 | + private static Logger log = LoggerFactory.getLogger(SegmentRoutingManager.class); | ||
71 | + | ||
72 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
73 | + protected CoreService coreService; | ||
74 | + | ||
75 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
76 | + protected TopologyService topologyService; | ||
77 | + | ||
78 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
79 | + protected PacketService packetService; | ||
80 | + | ||
81 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
82 | + protected IntentService intentService; | ||
83 | + | ||
84 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
85 | + protected HostService hostService; | ||
86 | + | ||
87 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
88 | + protected DeviceService deviceService; | ||
89 | + | ||
90 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
91 | + protected FlowRuleService flowRuleService; | ||
92 | + | ||
93 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
94 | + protected LinkService linkService; | ||
95 | + | ||
96 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
97 | + protected GroupService groupService; | ||
98 | + | ||
99 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
100 | + protected MastershipService mastershipService; | ||
101 | + | ||
102 | + protected NetworkConfigHandler networkConfigHandler = new NetworkConfigHandler(this); | ||
103 | + protected ArpHandler arpHandler = new ArpHandler(this); | ||
104 | + protected IcmpHandler icmpHandler = new IcmpHandler(this); | ||
105 | + protected IpHandler ipHandler = new IpHandler(this); | ||
106 | + protected RoutingRulePopulator routingRulePopulator = new RoutingRulePopulator(this); | ||
107 | + protected ApplicationId appId; | ||
108 | + | ||
109 | + private DefaultRoutingHandler defaultRoutingHandler = new DefaultRoutingHandler(this); | ||
110 | + private DeviceConfiguration deviceConfiguration = new DeviceConfiguration(); | ||
111 | + private InternalPacketProcessor processor = new InternalPacketProcessor(); | ||
112 | + private InternalEventHandler eventHandler = new InternalEventHandler(); | ||
113 | + | ||
114 | + private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); | ||
115 | + | ||
116 | + private static ScheduledFuture<?> eventHandlerFuture = null; | ||
117 | + private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>(); | ||
118 | + private Map<DeviceId, DefaultGroupHandler> groupHandlerMap | ||
119 | + = new ConcurrentHashMap<DeviceId, DefaultGroupHandler>(); | ||
120 | + | ||
121 | + private static int numOfEvents = 0; | ||
122 | + private static int numOfHandlerExecution = 0; | ||
123 | + private static int numOfHandlerScheduled = 0; | ||
124 | + | ||
125 | + @Activate | ||
126 | + protected void activate() { | ||
127 | + appId = coreService.registerApplication("org.onosproject.segmentrouting"); | ||
128 | + packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2); | ||
129 | + linkService.addListener(new InternalLinkListener()); | ||
130 | + groupService.addListener(new InternalGroupListener()); | ||
131 | + deviceService.addListener(new InternalDeviceListener()); | ||
132 | + | ||
133 | + for (Device device: deviceService.getDevices()) { | ||
134 | + if (mastershipService. | ||
135 | + getLocalRole(device.id()) == MastershipRole.MASTER) { | ||
136 | + DefaultGroupHandler groupHandler = | ||
137 | + DefaultGroupHandler.createGroupHandler(device.id(), | ||
138 | + appId, deviceConfiguration, linkService, groupService); | ||
139 | + groupHandler.createGroups(); | ||
140 | + groupHandlerMap.put(device.id(), groupHandler); | ||
141 | + log.debug("Initiating default group handling for {}", device.id()); | ||
142 | + | ||
143 | + defaultRoutingHandler.startPopulationProcess(); | ||
144 | + } else { | ||
145 | + log.debug("Activate: Local role {} " | ||
146 | + + "is not MASTER for device {}", | ||
147 | + mastershipService. | ||
148 | + getLocalRole(device.id()), | ||
149 | + device.id()); | ||
150 | + } | ||
151 | + } | ||
152 | + | ||
153 | + log.info("Started"); | ||
154 | + } | ||
155 | + | ||
156 | + @Deactivate | ||
157 | + protected void deactivate() { | ||
158 | + packetService.removeProcessor(processor); | ||
159 | + processor = null; | ||
160 | + log.info("Stopped"); | ||
161 | + } | ||
162 | + | ||
163 | + /** | ||
164 | + * Returns the GrouopKey object for the device and the NighborSet given. | ||
165 | + * | ||
166 | + * @param ns NeightborSet object for the GroupKey | ||
167 | + * @return GroupKey object for the NeighborSet | ||
168 | + */ | ||
169 | + public GroupKey getGroupKey(NeighborSet ns) { | ||
170 | + | ||
171 | + for (DefaultGroupHandler groupHandler: groupHandlerMap.values()) { | ||
172 | + return groupHandler.getGroupKey(ns); | ||
173 | + } | ||
174 | + | ||
175 | + return null; | ||
176 | + } | ||
177 | + | ||
178 | + private class InternalPacketProcessor implements PacketProcessor { | ||
179 | + | ||
180 | + @Override | ||
181 | + public void process(PacketContext context) { | ||
182 | + | ||
183 | + if (context.isHandled()) { | ||
184 | + return; | ||
185 | + } | ||
186 | + | ||
187 | + InboundPacket pkt = context.inPacket(); | ||
188 | + Ethernet ethernet = pkt.parsed(); | ||
189 | + | ||
190 | + if (ethernet.getEtherType() == Ethernet.TYPE_ARP) { | ||
191 | + arpHandler.processPacketIn(pkt); | ||
192 | + } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) { | ||
193 | + IPv4 ipPacket = (IPv4) ethernet.getPayload(); | ||
194 | + ipHandler.addToPacketBuffer(ipPacket); | ||
195 | + if (ipPacket.getProtocol() == IPv4.PROTOCOL_ICMP) { | ||
196 | + icmpHandler.processPacketIn(pkt); | ||
197 | + } else { | ||
198 | + ipHandler.processPacketIn(pkt); | ||
199 | + } | ||
200 | + } | ||
201 | + } | ||
202 | + } | ||
203 | + | ||
204 | + private class InternalLinkListener implements LinkListener { | ||
205 | + @Override | ||
206 | + public void event(LinkEvent event) { | ||
207 | + if (event.type() == LinkEvent.Type.LINK_ADDED || | ||
208 | + event.type() == LinkEvent.Type.LINK_REMOVED) { | ||
209 | + scheduleEventHandlerIfNotScheduled(event); | ||
210 | + } | ||
211 | + } | ||
212 | + } | ||
213 | + | ||
214 | + private class InternalDeviceListener implements DeviceListener { | ||
215 | + | ||
216 | + @Override | ||
217 | + public void event(DeviceEvent event) { | ||
218 | + if (mastershipService. | ||
219 | + getLocalRole(event.subject().id()) != MastershipRole.MASTER) { | ||
220 | + log.debug("Local role {} is not MASTER for device {}", | ||
221 | + mastershipService. | ||
222 | + getLocalRole(event.subject().id()), | ||
223 | + event.subject().id()); | ||
224 | + return; | ||
225 | + } | ||
226 | + | ||
227 | + switch (event.type()) { | ||
228 | + case DEVICE_ADDED: | ||
229 | + case PORT_REMOVED: | ||
230 | + scheduleEventHandlerIfNotScheduled(event); | ||
231 | + break; | ||
232 | + default: | ||
233 | + } | ||
234 | + } | ||
235 | + } | ||
236 | + | ||
237 | + private class InternalGroupListener implements GroupListener { | ||
238 | + | ||
239 | + @Override | ||
240 | + public void event(GroupEvent event) { | ||
241 | + switch (event.type()) { | ||
242 | + case GROUP_ADDED: | ||
243 | + scheduleEventHandlerIfNotScheduled(event); | ||
244 | + break; | ||
245 | + case GROUP_ADD_REQUESTED: | ||
246 | + log.info("Group add requested"); | ||
247 | + break; | ||
248 | + case GROUP_UPDATED: | ||
249 | + break; | ||
250 | + default: | ||
251 | + log.warn("Unhandled group event type: {}", event.type()); | ||
252 | + } | ||
253 | + } | ||
254 | + } | ||
255 | + | ||
256 | + private void scheduleEventHandlerIfNotScheduled(Event event) { | ||
257 | + | ||
258 | + eventQueue.add(event); | ||
259 | + numOfEvents++; | ||
260 | + if (eventHandlerFuture == null || | ||
261 | + eventHandlerFuture.isDone()) { | ||
262 | + eventHandlerFuture = executorService.schedule(eventHandler, | ||
263 | + 100, TimeUnit.MILLISECONDS); | ||
264 | + numOfHandlerScheduled++; | ||
265 | + } | ||
266 | + | ||
267 | + log.trace("numOfEvents {}, numOfEventHanlderScheduled {}", numOfEvents, | ||
268 | + numOfHandlerScheduled); | ||
269 | + | ||
270 | + } | ||
271 | + | ||
272 | + private class InternalEventHandler implements Runnable { | ||
273 | + | ||
274 | + public void run() { | ||
275 | + numOfHandlerExecution++; | ||
276 | + while (!eventQueue.isEmpty()) { | ||
277 | + Event event = eventQueue.poll(); | ||
278 | + if (event.type() == LinkEvent.Type.LINK_ADDED) { | ||
279 | + processLinkAdded((Link) event.subject()); | ||
280 | + } else if (event.type() == LinkEvent.Type.LINK_REMOVED) { | ||
281 | + processLinkRemoved((Link) event.subject()); | ||
282 | + } else if (event.type() == GroupEvent.Type.GROUP_ADDED) { | ||
283 | + processGroupAdded((Group) event.subject()); | ||
284 | + } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED) { | ||
285 | + processDeviceAdded((Device) event.subject()); | ||
286 | + } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) { | ||
287 | + processPortRemoved((Device) event.subject(), | ||
288 | + ((DeviceEvent) event).port()); | ||
289 | + } else { | ||
290 | + log.warn("Unhandled event type: {}", event.type()); | ||
291 | + } | ||
292 | + } | ||
293 | + log.debug("numOfHandlerExecution {} numOfEventHanlderScheduled {} numOfEvents {}", | ||
294 | + numOfHandlerExecution, numOfHandlerScheduled, numOfEvents); | ||
295 | + } | ||
296 | + } | ||
297 | + | ||
298 | + | ||
299 | + | ||
300 | + private void processLinkAdded(Link link) { | ||
301 | + log.debug("A new link {} was added", link.toString()); | ||
302 | + | ||
303 | + if (mastershipService. | ||
304 | + getLocalRole(link.src().deviceId()) == MastershipRole.MASTER) { | ||
305 | + DefaultGroupHandler groupHandler = | ||
306 | + groupHandlerMap.get(link.src().deviceId()); | ||
307 | + if (groupHandler != null) { | ||
308 | + groupHandler.linkUp(link); | ||
309 | + } | ||
310 | + } | ||
311 | + defaultRoutingHandler.startPopulationProcess(); | ||
312 | + } | ||
313 | + | ||
314 | + private void processLinkRemoved(Link link) { | ||
315 | + log.debug("A link {} was removed", link.toString()); | ||
316 | + defaultRoutingHandler.startPopulationProcess(); | ||
317 | + } | ||
318 | + | ||
319 | + | ||
320 | + private void processGroupAdded(Group group) { | ||
321 | + log.debug("A new group with ID {} was added", group.id()); | ||
322 | + defaultRoutingHandler.resumePopulationProcess(); | ||
323 | + } | ||
324 | + | ||
325 | + private void processDeviceAdded(Device device) { | ||
326 | + log.debug("A new device with ID {} was added", device.id()); | ||
327 | + defaultRoutingHandler.populateTtpRules(device.id()); | ||
328 | + DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler( | ||
329 | + device.id(), appId, new DeviceConfiguration(), linkService, groupService); | ||
330 | + dgh.createGroups(); | ||
331 | + groupHandlerMap.put(device.id(), dgh); | ||
332 | + } | ||
333 | + | ||
334 | + private void processPortRemoved(Device device, Port port) { | ||
335 | + log.debug("Port {} was removed", port.toString()); | ||
336 | + DefaultGroupHandler groupHandler = | ||
337 | + groupHandlerMap.get(device.id()); | ||
338 | + if (groupHandler != null) { | ||
339 | + groupHandler.portDown(port.number()); | ||
340 | + } | ||
341 | + } | ||
342 | +} |
... | @@ -345,7 +345,8 @@ public final class KryoNamespaces { | ... | @@ -345,7 +345,8 @@ public final class KryoNamespaces { |
345 | MplsLabelResourceAllocation.class, | 345 | MplsLabelResourceAllocation.class, |
346 | MplsLabelResourceRequest.class, | 346 | MplsLabelResourceRequest.class, |
347 | MplsLabel.class, | 347 | MplsLabel.class, |
348 | - org.onlab.packet.MplsLabel.class | 348 | + org.onlab.packet.MplsLabel.class, |
349 | + org.onlab.packet.MPLS.class | ||
349 | ) | 350 | ) |
350 | 351 | ||
351 | .build(); | 352 | .build(); | ... | ... |
... | @@ -285,4 +285,18 @@ | ... | @@ -285,4 +285,18 @@ |
285 | <bundle>mvn:org.onosproject/onos-app-calendar/@ONOS-VERSION</bundle> | 285 | <bundle>mvn:org.onosproject/onos-app-calendar/@ONOS-VERSION</bundle> |
286 | </feature> | 286 | </feature> |
287 | 287 | ||
288 | + <feature name="onos-app-grouphandler" version="@FEATURE-VERSION" | ||
289 | + description="Group Handler Sample App"> | ||
290 | + <feature>onos-api</feature> | ||
291 | + <bundle>mvn:org.onosproject/onos-app-grouphandler/@ONOS-VERSION</bundle> | ||
292 | + </feature> | ||
293 | + | ||
294 | + <feature name="onos-app-segmentrouting" version="@FEATURE-VERSION" | ||
295 | + description="Segment routing application"> | ||
296 | + <feature>onos-api</feature> | ||
297 | + <bundle>mvn:org.onosproject/onos-app-segmentrouting/@ONOS-VERSION</bundle> | ||
298 | + <bundle>mvn:org.onosproject/onos-app-grouphandler/@ONOS-VERSION</bundle> | ||
299 | + </feature> | ||
300 | + | ||
301 | + | ||
288 | </features> | 302 | </features> | ... | ... |
... | @@ -70,7 +70,7 @@ public final class DriverManager implements OpenFlowSwitchDriverFactory { | ... | @@ -70,7 +70,7 @@ public final class DriverManager implements OpenFlowSwitchDriverFactory { |
70 | if (vendor.startsWith("Stanford University, Ericsson Research and CPqD Research") | 70 | if (vendor.startsWith("Stanford University, Ericsson Research and CPqD Research") |
71 | && | 71 | && |
72 | hw.startsWith("OpenFlow 1.3 Reference Userspace Switch")) { | 72 | hw.startsWith("OpenFlow 1.3 Reference Userspace Switch")) { |
73 | - return new OFSwitchImplCPqD13(dpid, desc); | 73 | + return new OFSwitchImplSpringOpenTTP(dpid, desc); |
74 | } | 74 | } |
75 | 75 | ||
76 | if (hw.startsWith("Open vSwitch")) { | 76 | if (hw.startsWith("Open vSwitch")) { | ... | ... |
... | @@ -15,43 +15,21 @@ | ... | @@ -15,43 +15,21 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.openflow.drivers; | 16 | package org.onosproject.openflow.drivers; |
17 | 17 | ||
18 | +import com.google.common.collect.Lists; | ||
18 | import org.onosproject.openflow.controller.Dpid; | 19 | import org.onosproject.openflow.controller.Dpid; |
19 | -import org.onosproject.openflow.controller.RoleState; | ||
20 | -import org.onosproject.openflow.controller.OpenFlowSwitch.TableType; | ||
21 | import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch; | 20 | import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch; |
22 | import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted; | 21 | import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted; |
23 | import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted; | 22 | import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted; |
24 | import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; | 23 | import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; |
25 | -import org.projectfloodlight.openflow.protocol.OFAsyncGetReply; | ||
26 | -import org.projectfloodlight.openflow.protocol.OFBarrierRequest; | ||
27 | -import org.projectfloodlight.openflow.protocol.OFErrorMsg; | ||
28 | -import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; | ||
29 | -import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply; | ||
30 | -import org.projectfloodlight.openflow.protocol.OFMatchV3; | ||
31 | -import org.projectfloodlight.openflow.protocol.OFOxmList; | ||
32 | -import org.projectfloodlight.openflow.protocol.OFPortDesc; | ||
33 | -import org.projectfloodlight.openflow.protocol.OFStatsReply; | ||
34 | import org.projectfloodlight.openflow.protocol.OFFlowMod; | 24 | import org.projectfloodlight.openflow.protocol.OFFlowMod; |
35 | import org.projectfloodlight.openflow.protocol.OFDescStatsReply; | 25 | import org.projectfloodlight.openflow.protocol.OFDescStatsReply; |
36 | import org.projectfloodlight.openflow.protocol.OFFactory; | 26 | import org.projectfloodlight.openflow.protocol.OFFactory; |
37 | import org.projectfloodlight.openflow.protocol.OFMessage; | 27 | import org.projectfloodlight.openflow.protocol.OFMessage; |
38 | import org.projectfloodlight.openflow.protocol.OFType; | 28 | import org.projectfloodlight.openflow.protocol.OFType; |
39 | -import org.projectfloodlight.openflow.protocol.action.OFAction; | ||
40 | import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; | 29 | import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; |
41 | -import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType; | 30 | +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable; |
42 | -import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort; | ||
43 | -import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid; | ||
44 | -import org.projectfloodlight.openflow.types.EthType; | ||
45 | -import org.projectfloodlight.openflow.types.MacAddress; | ||
46 | -import org.projectfloodlight.openflow.types.OFBufferId; | ||
47 | -import org.projectfloodlight.openflow.types.OFPort; | ||
48 | -import org.projectfloodlight.openflow.types.OFVlanVidMatch; | ||
49 | import org.projectfloodlight.openflow.types.TableId; | 31 | import org.projectfloodlight.openflow.types.TableId; |
50 | -import org.projectfloodlight.openflow.types.U32; | ||
51 | -import org.projectfloodlight.openflow.util.HexString; | ||
52 | 32 | ||
53 | -import java.io.IOException; | ||
54 | -import java.util.ArrayList; | ||
55 | import java.util.Collections; | 33 | import java.util.Collections; |
56 | import java.util.List; | 34 | import java.util.List; |
57 | import java.util.concurrent.atomic.AtomicBoolean; | 35 | import java.util.concurrent.atomic.AtomicBoolean; |
... | @@ -63,8 +41,6 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -63,8 +41,6 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
63 | private final AtomicBoolean driverHandshakeComplete; | 41 | private final AtomicBoolean driverHandshakeComplete; |
64 | private AtomicBoolean haltStateMachine; | 42 | private AtomicBoolean haltStateMachine; |
65 | 43 | ||
66 | - private DriverState driverState; | ||
67 | - | ||
68 | /* Default table ID - compatible with CpqD switch */ | 44 | /* Default table ID - compatible with CpqD switch */ |
69 | private static final int TABLE_VLAN = 0; | 45 | private static final int TABLE_VLAN = 0; |
70 | private static final int TABLE_TMAC = 1; | 46 | private static final int TABLE_TMAC = 1; |
... | @@ -72,14 +48,6 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -72,14 +48,6 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
72 | private static final int TABLE_MPLS = 3; | 48 | private static final int TABLE_MPLS = 3; |
73 | private static final int TABLE_ACL = 5; | 49 | private static final int TABLE_ACL = 5; |
74 | 50 | ||
75 | - private static final long TEST_FLOW_REMOVED_MASK = 0xf; | ||
76 | - private static final long TEST_PACKET_IN_MASK = 0x7; | ||
77 | - private static final long TEST_PORT_STATUS_MASK = 0x7; | ||
78 | - | ||
79 | - private static final int OFPCML_NO_BUFFER = 0xffff; | ||
80 | - | ||
81 | - private long barrierXidToWaitFor = -1; | ||
82 | - | ||
83 | /* Set the default values. These variables will get | 51 | /* Set the default values. These variables will get |
84 | * overwritten based on the switch vendor type | 52 | * overwritten based on the switch vendor type |
85 | */ | 53 | */ |
... | @@ -89,17 +57,10 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -89,17 +57,10 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
89 | protected int mplsTableId = TABLE_MPLS; | 57 | protected int mplsTableId = TABLE_MPLS; |
90 | protected int aclTableId = TABLE_ACL; | 58 | protected int aclTableId = TABLE_ACL; |
91 | 59 | ||
92 | - /* priority values for OF message */ | ||
93 | - private static final short MAX_PRIORITY = (short) 0xffff; | ||
94 | - private static final short PRIORITY_MULTIPLIER = (short) 2046; | ||
95 | - private static final short MIN_PRIORITY = 0x0; | ||
96 | - | ||
97 | - | ||
98 | protected OFSwitchImplSpringOpenTTP(Dpid dpid, OFDescStatsReply desc) { | 60 | protected OFSwitchImplSpringOpenTTP(Dpid dpid, OFDescStatsReply desc) { |
99 | super(dpid); | 61 | super(dpid); |
100 | driverHandshakeComplete = new AtomicBoolean(false); | 62 | driverHandshakeComplete = new AtomicBoolean(false); |
101 | haltStateMachine = new AtomicBoolean(false); | 63 | haltStateMachine = new AtomicBoolean(false); |
102 | - driverState = DriverState.INIT; | ||
103 | setSwitchDescription(desc); | 64 | setSwitchDescription(desc); |
104 | } | 65 | } |
105 | 66 | ||
... | @@ -117,6 +78,7 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -117,6 +78,7 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
117 | return null; | 78 | return null; |
118 | } | 79 | } |
119 | 80 | ||
81 | + | ||
120 | @Override | 82 | @Override |
121 | public void startDriverHandshake() { | 83 | public void startDriverHandshake() { |
122 | log.debug("Starting driver handshake for sw {}", getStringId()); | 84 | log.debug("Starting driver handshake for sw {}", getStringId()); |
... | @@ -126,12 +88,9 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -126,12 +88,9 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
126 | startDriverHandshakeCalled = true; | 88 | startDriverHandshakeCalled = true; |
127 | factory = this.factory(); | 89 | factory = this.factory(); |
128 | 90 | ||
129 | - try { | 91 | + driverHandshakeComplete.set(true); |
130 | - nextDriverState(); | 92 | + log.debug("Driver handshake is complete"); |
131 | - } catch (IOException e) { | 93 | + |
132 | - log.error("Error {} during driver handshake for sw {}", e.getCause(), | ||
133 | - getStringId()); | ||
134 | - } | ||
135 | } | 94 | } |
136 | 95 | ||
137 | @Override | 96 | @Override |
... | @@ -142,6 +101,8 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -142,6 +101,8 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
142 | return driverHandshakeComplete.get(); | 101 | return driverHandshakeComplete.get(); |
143 | } | 102 | } |
144 | 103 | ||
104 | + | ||
105 | + | ||
145 | @Override | 106 | @Override |
146 | public void processDriverHandshakeMessage(OFMessage m) { | 107 | public void processDriverHandshakeMessage(OFMessage m) { |
147 | if (!startDriverHandshakeCalled) { | 108 | if (!startDriverHandshakeCalled) { |
... | @@ -150,13 +111,9 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -150,13 +111,9 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
150 | if (driverHandshakeComplete.get()) { | 111 | if (driverHandshakeComplete.get()) { |
151 | throw new SwitchDriverSubHandshakeCompleted(m); | 112 | throw new SwitchDriverSubHandshakeCompleted(m); |
152 | } | 113 | } |
153 | - try { | ||
154 | - processOFMessage(m); | ||
155 | - } catch (IOException e) { | ||
156 | - log.error("Error generated when processing OFMessage {}", e.getCause()); | ||
157 | - } | ||
158 | } | 114 | } |
159 | 115 | ||
116 | + | ||
160 | @Override | 117 | @Override |
161 | public void write(OFMessage msg) { | 118 | public void write(OFMessage msg) { |
162 | this.channel.write(Collections.singletonList(msg)); | 119 | this.channel.write(Collections.singletonList(msg)); |
... | @@ -168,384 +125,51 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -168,384 +125,51 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
168 | } | 125 | } |
169 | 126 | ||
170 | @Override | 127 | @Override |
171 | - public void transformAndSendMsg(OFMessage m, TableType tableType) { | 128 | + public void transformAndSendMsg(OFMessage msg, TableType type) { |
172 | - | 129 | + if (msg.getType() == OFType.FLOW_MOD) { |
173 | - if (m.getType() == OFType.FLOW_MOD) { | 130 | + OFFlowMod flowMod = (OFFlowMod) msg; |
174 | - OFFlowMod flowMod = (OFFlowMod) m; | ||
175 | OFFlowMod.Builder builder = flowMod.createBuilder(); | 131 | OFFlowMod.Builder builder = flowMod.createBuilder(); |
176 | - builder.setTableId(getTableId(tableType)); | 132 | + List<OFInstruction> instructions = flowMod.getInstructions(); |
177 | - OFFlowMod newFlowMod = builder.build(); | 133 | + List<OFInstruction> newInstructions = Lists.newArrayList(); |
178 | - if (role == RoleState.MASTER) { | 134 | + for (OFInstruction i : instructions) { |
179 | - this.write(newFlowMod); | 135 | + if (i instanceof OFInstructionGotoTable) { |
180 | - } | 136 | + OFInstructionGotoTable gotoTable = (OFInstructionGotoTable) i; |
181 | - } else { | 137 | + TableType tid = TableType.values()[gotoTable.getTableId().getValue()]; |
182 | - if (role == RoleState.MASTER) { | 138 | + newInstructions.add( |
183 | - this.write(m); | 139 | + gotoTable.createBuilder() |
184 | - } | 140 | + .setTableId(getTableId(tid)).build()); |
185 | - } | 141 | + } else { |
186 | - } | 142 | + newInstructions.add(i); |
187 | - | ||
188 | - /* | ||
189 | - * Driver handshake state machine | ||
190 | - */ | ||
191 | - | ||
192 | - enum DriverState { | ||
193 | - INIT, | ||
194 | - SET_TABLE_MISS_ENTRIES, | ||
195 | - SET_TABLE_VLAN_TMAC, | ||
196 | - AUDIT_GROUPS, | ||
197 | - SET_GROUPS, | ||
198 | - VERIFY_GROUPS, | ||
199 | - SET_ADJACENCY_LABELS, | ||
200 | - EXIT | ||
201 | - } | ||
202 | - | ||
203 | - protected void nextDriverState() throws IOException { | ||
204 | - DriverState currentState = driverState; | ||
205 | - if (haltStateMachine.get()) { | ||
206 | - return; | ||
207 | - } | ||
208 | - switch (currentState) { | ||
209 | - case INIT: | ||
210 | - driverState = DriverState.SET_TABLE_MISS_ENTRIES; | ||
211 | - setTableMissEntries(); | ||
212 | - sendHandshakeBarrier(); | ||
213 | - break; | ||
214 | - case SET_TABLE_MISS_ENTRIES: | ||
215 | - driverState = DriverState.SET_TABLE_VLAN_TMAC; | ||
216 | - /* TODO: read network configuration | ||
217 | - boolean isConfigured = getNetworkConfig(); | ||
218 | - if (!isConfigured) { | ||
219 | - return; // this will result in a handshake timeout | ||
220 | } | 143 | } |
221 | - */ | 144 | + } |
222 | - populateTableVlan(); | 145 | + builder.setTableId(getTableId(type)); |
223 | - populateTableTMac(); | 146 | + builder.setInstructions(newInstructions); |
224 | - sendHandshakeBarrier(); | 147 | + OFMessage msgnew = builder.build(); |
225 | - break; | 148 | + channel.write(Collections.singletonList(msgnew)); |
226 | - case SET_TABLE_VLAN_TMAC: | 149 | + log.trace("Installed {}", msgnew); |
227 | - driverState = DriverState.EXIT; | ||
228 | - driverHandshakeComplete.set(true); | ||
229 | - log.debug("Driver handshake is complete"); | ||
230 | - break; | ||
231 | - case EXIT: | ||
232 | - default: | ||
233 | - driverState = DriverState.EXIT; | ||
234 | - log.error("Driver handshake has exited for sw: {}", getStringId()); | ||
235 | - } | ||
236 | - } | ||
237 | - | ||
238 | - private void processStatsReply(OFStatsReply sr) { | ||
239 | - switch (sr.getStatsType()) { | ||
240 | - case AGGREGATE: | ||
241 | - break; | ||
242 | - case DESC: | ||
243 | - break; | ||
244 | - case EXPERIMENTER: | ||
245 | - break; | ||
246 | - case FLOW: | ||
247 | - break; | ||
248 | - case GROUP_DESC: | ||
249 | - processGroupDesc((OFGroupDescStatsReply) sr); | ||
250 | - break; | ||
251 | - case GROUP_FEATURES: | ||
252 | - processGroupFeatures((OFGroupFeaturesStatsReply) sr); | ||
253 | - break; | ||
254 | - case METER_CONFIG: | ||
255 | - break; | ||
256 | - case METER_FEATURES: | ||
257 | - break; | ||
258 | - case PORT_DESC: | ||
259 | - break; | ||
260 | - case TABLE_FEATURES: | ||
261 | - break; | ||
262 | - default: | ||
263 | - break; | ||
264 | - | ||
265 | - } | ||
266 | - } | ||
267 | - | ||
268 | - private void processOFMessage(OFMessage m) throws IOException { | ||
269 | - switch (m.getType()) { | ||
270 | - case BARRIER_REPLY: | ||
271 | - processBarrierReply(m); | ||
272 | - break; | ||
273 | - | ||
274 | - case ERROR: | ||
275 | - processErrorMessage(m); | ||
276 | - break; | ||
277 | - | ||
278 | - case GET_ASYNC_REPLY: | ||
279 | - OFAsyncGetReply asrep = (OFAsyncGetReply) m; | ||
280 | - decodeAsyncGetReply(asrep); | ||
281 | - break; | ||
282 | - | ||
283 | - case PACKET_IN: | ||
284 | - // not ready to handle packet-ins | ||
285 | - break; | ||
286 | - | ||
287 | - case QUEUE_GET_CONFIG_REPLY: | ||
288 | - // not doing queue config yet | ||
289 | - break; | ||
290 | - | ||
291 | - case STATS_REPLY: | ||
292 | - processStatsReply((OFStatsReply) m); | ||
293 | - break; | ||
294 | - | ||
295 | - case ROLE_REPLY: // channelHandler should handle this | ||
296 | - case PORT_STATUS: // channelHandler should handle this | ||
297 | - case FEATURES_REPLY: // don't care | ||
298 | - case FLOW_REMOVED: // don't care | ||
299 | - default: | ||
300 | - log.debug("Received message {} during switch-driver subhandshake " | ||
301 | - + "from switch {} ... Ignoring message", m, getStringId()); | ||
302 | - } | ||
303 | - } | ||
304 | 150 | ||
305 | - private void processBarrierReply(OFMessage m) throws IOException { | ||
306 | - if (m.getXid() == barrierXidToWaitFor) { | ||
307 | - // Driver state-machine progresses to the next state. | ||
308 | - // If Barrier messages is not received, then eventually | ||
309 | - // the ChannelHandler state machine will timeout, and the switch | ||
310 | - // will be disconnected. | ||
311 | - nextDriverState(); | ||
312 | } else { | 151 | } else { |
313 | - log.error("Received incorrect barrier-message xid {} (expected: {}) in " | 152 | + channel.write(Collections.singletonList(msg)); |
314 | - + "switch-driver state {} for switch {}", m, barrierXidToWaitFor, | ||
315 | - driverState, getStringId()); | ||
316 | } | 153 | } |
317 | } | 154 | } |
318 | 155 | ||
319 | - private void processErrorMessage(OFMessage m) { | 156 | + @Override |
320 | - log.error("Switch {} Error {} in DriverState", getStringId(), | 157 | + public TableType getTableType(TableId tid) { |
321 | - (OFErrorMsg) m, driverState); | 158 | + switch (tid.getValue()) { |
322 | - } | 159 | + case TABLE_IPV4_UNICAST: |
323 | - | 160 | + return TableType.IP; |
324 | - private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) { | 161 | + case TABLE_MPLS: |
325 | - log.info("Sw: {} Group Features {}", getStringId(), gfsr); | 162 | + return TableType.MPLS; |
326 | - } | 163 | + case TABLE_ACL: |
327 | - | 164 | + return TableType.ACL; |
328 | - private void processGroupDesc(OFGroupDescStatsReply gdsr) { | 165 | + case TABLE_VLAN: |
329 | - log.info("Sw: {} Group Desc {}", getStringId(), gdsr); | 166 | + return TableType.VLAN; |
330 | - } | 167 | + case TABLE_TMAC: |
331 | - | 168 | + return TableType.ETHER; |
332 | - /* | 169 | + default: |
333 | - * Utility functions | 170 | + log.error("Table type for Table id {} is not supported in the driver", tid); |
334 | - */ | 171 | + return TableType.NONE; |
335 | - | ||
336 | - private void decodeAsyncGetReply(OFAsyncGetReply rep) { | ||
337 | - long frm = rep.getFlowRemovedMaskEqualMaster(); | ||
338 | - //long frs = rep.getFlowRemovedMaskSlave(); | ||
339 | - long pim = rep.getPacketInMaskEqualMaster(); | ||
340 | - //long pis = rep.getPacketInMaskSlave(); | ||
341 | - long psm = rep.getPortStatusMaskEqualMaster(); | ||
342 | - //long pss = rep.getPortStatusMaskSlave(); | ||
343 | - | ||
344 | - if (role == RoleState.MASTER || role == RoleState.EQUAL) { // should separate | ||
345 | - log.info("FRM:{}", HexString.toHexString((frm & TEST_FLOW_REMOVED_MASK))); | ||
346 | - log.info("PIM:{}", HexString.toHexString((pim & TEST_PACKET_IN_MASK))); | ||
347 | - log.info("PSM:{}", HexString.toHexString((psm & TEST_PORT_STATUS_MASK))); | ||
348 | - } | ||
349 | - } | ||
350 | - | ||
351 | - protected void setTableMissEntries() throws IOException { | ||
352 | - // set all table-miss-entries | ||
353 | - populateTableMissEntry(vlanTableId, true, false, false, -1); | ||
354 | - populateTableMissEntry(tmacTableId, true, false, false, -1); | ||
355 | - populateTableMissEntry(ipv4UnicastTableId, false, true, true, | ||
356 | - aclTableId); | ||
357 | - populateTableMissEntry(mplsTableId, false, true, true, | ||
358 | - aclTableId); | ||
359 | - populateTableMissEntry(aclTableId, false, false, false, -1); | ||
360 | - log.debug("TableMissEntries are set"); | ||
361 | - } | ||
362 | - | ||
363 | - /** | ||
364 | - * Adds a table-miss-entry to a pipeline table. | ||
365 | - * <p> | ||
366 | - * The table-miss-entry can be added with 'write-actions' or | ||
367 | - * 'apply-actions'. It can also add a 'goto-table' instruction. By default | ||
368 | - * if none of the booleans in the call are set, then the table-miss entry is | ||
369 | - * added with no instructions, which means that if a packet hits the | ||
370 | - * table-miss-entry, pipeline execution will stop, and the action set | ||
371 | - * associated with the packet will be executed. | ||
372 | - * | ||
373 | - * @param tableToAdd the table to where the table-miss-entry will be added | ||
374 | - * @param toControllerNow as an APPLY_ACTION instruction | ||
375 | - * @param toControllerWrite as a WRITE_ACTION instruction | ||
376 | - * @param toTable as a GOTO_TABLE instruction | ||
377 | - * @param tableToSend the table to send as per the GOTO_TABLE instruction it | ||
378 | - * needs to be set if 'toTable' is true. Ignored of 'toTable' is | ||
379 | - * false. | ||
380 | - */ | ||
381 | - protected void populateTableMissEntry(int tableToAdd, boolean toControllerNow, | ||
382 | - boolean toControllerWrite, | ||
383 | - boolean toTable, int tableToSend) { | ||
384 | - OFOxmList oxmList = OFOxmList.EMPTY; | ||
385 | - OFMatchV3 match = factory.buildMatchV3() | ||
386 | - .setOxmList(oxmList) | ||
387 | - .build(); | ||
388 | - OFAction outc = factory.actions() | ||
389 | - .buildOutput() | ||
390 | - .setPort(OFPort.CONTROLLER) | ||
391 | - .setMaxLen(OFPCML_NO_BUFFER) | ||
392 | - .build(); | ||
393 | - List<OFInstruction> instructions = new ArrayList<OFInstruction>(); | ||
394 | - if (toControllerNow) { | ||
395 | - // table-miss instruction to send to controller immediately | ||
396 | - OFInstruction instr = factory.instructions() | ||
397 | - .buildApplyActions() | ||
398 | - .setActions(Collections.singletonList(outc)) | ||
399 | - .build(); | ||
400 | - instructions.add(instr); | ||
401 | - } | ||
402 | - | ||
403 | - if (toControllerWrite) { | ||
404 | - // table-miss instruction to write-action to send to controller | ||
405 | - // this will be executed whenever the action-set gets executed | ||
406 | - OFInstruction instr = factory.instructions() | ||
407 | - .buildWriteActions() | ||
408 | - .setActions(Collections.singletonList(outc)) | ||
409 | - .build(); | ||
410 | - instructions.add(instr); | ||
411 | - } | ||
412 | - | ||
413 | - if (toTable) { | ||
414 | - // table-miss instruction to goto-table x | ||
415 | - OFInstruction instr = factory.instructions() | ||
416 | - .gotoTable(TableId.of(tableToSend)); | ||
417 | - instructions.add(instr); | ||
418 | - } | ||
419 | - | ||
420 | - if (!toControllerNow && !toControllerWrite && !toTable) { | ||
421 | - // table-miss has no instruction - at which point action-set will be | ||
422 | - // executed - if there is an action to output/group in the action | ||
423 | - // set | ||
424 | - // the packet will be sent there, otherwise it will be dropped. | ||
425 | - instructions = Collections.<OFInstruction>emptyList(); | ||
426 | - } | ||
427 | - | ||
428 | - OFMessage tableMissEntry = factory.buildFlowAdd() | ||
429 | - .setTableId(TableId.of(tableToAdd)) | ||
430 | - .setMatch(match) // match everything | ||
431 | - .setInstructions(instructions) | ||
432 | - .setPriority(MIN_PRIORITY) | ||
433 | - .setBufferId(OFBufferId.NO_BUFFER) | ||
434 | - .setIdleTimeout(0) | ||
435 | - .setHardTimeout(0) | ||
436 | - .setXid(getNextTransactionId()) | ||
437 | - .build(); | ||
438 | - write(tableMissEntry); | ||
439 | - } | ||
440 | - | ||
441 | - private void populateTableVlan() throws IOException { | ||
442 | - List<OFMessage> msglist = new ArrayList<OFMessage>(); | ||
443 | - for (OFPortDesc p : getPorts()) { | ||
444 | - int pnum = p.getPortNo().getPortNumber(); | ||
445 | - if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) { | ||
446 | - OFOxmInPort oxp = factory.oxms().inPort(p.getPortNo()); | ||
447 | - OFOxmVlanVid oxv = factory.oxms() | ||
448 | - .vlanVid(OFVlanVidMatch.UNTAGGED); | ||
449 | - OFOxmList oxmList = OFOxmList.of(oxp, oxv); | ||
450 | - OFMatchV3 match = factory.buildMatchV3() | ||
451 | - .setOxmList(oxmList).build(); | ||
452 | - | ||
453 | - // TODO: match on vlan-tagged packets for vlans configured on | ||
454 | - // subnet ports and strip-vlan | ||
455 | - | ||
456 | - OFInstruction gotoTbl = factory.instructions().buildGotoTable() | ||
457 | - .setTableId(TableId.of(tmacTableId)).build(); | ||
458 | - List<OFInstruction> instructions = new ArrayList<OFInstruction>(); | ||
459 | - instructions.add(gotoTbl); | ||
460 | - OFMessage flowEntry = factory.buildFlowAdd() | ||
461 | - .setTableId(TableId.of(vlanTableId)) | ||
462 | - .setMatch(match) | ||
463 | - .setInstructions(instructions) | ||
464 | - .setPriority(1000) // does not matter - all rules | ||
465 | - // exclusive | ||
466 | - .setBufferId(OFBufferId.NO_BUFFER) | ||
467 | - .setIdleTimeout(0) | ||
468 | - .setHardTimeout(0) | ||
469 | - .setXid(getNextTransactionId()) | ||
470 | - .build(); | ||
471 | - msglist.add(flowEntry); | ||
472 | - } | ||
473 | } | 172 | } |
474 | - write(msglist); | ||
475 | - log.debug("Adding {} port/vlan-rules in sw {}", msglist.size(), getStringId()); | ||
476 | - } | ||
477 | - | ||
478 | - private void populateTableTMac() throws IOException { | ||
479 | - // match for router-mac and ip-packets | ||
480 | - OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4); | ||
481 | - | ||
482 | - /* TODO: need to read network config and need to allow only | ||
483 | - the packets with DMAC as the correspondent router MAC address | ||
484 | - Until network configuration is implemented, all packets are allowed | ||
485 | - | ||
486 | - OFOxmEthDst dmac = factory.oxms().ethDst(getRouterMacAddr()); | ||
487 | - OFOxmList oxmListIp = OFOxmList.of(dmac, oxe); | ||
488 | - OFMatchV3 matchIp = factory.buildMatchV3() | ||
489 | - .setOxmList(oxmListIp).build(); | ||
490 | - */ | ||
491 | - OFOxmList oxmList = OFOxmList.EMPTY; | ||
492 | - OFMatchV3 matchIp = factory.buildMatchV3() | ||
493 | - .setOxmList(oxmList) | ||
494 | - .build(); | ||
495 | - | ||
496 | - OFInstruction gotoTblIp = factory.instructions().buildGotoTable() | ||
497 | - .setTableId(TableId.of(ipv4UnicastTableId)).build(); | ||
498 | - List<OFInstruction> instructionsIp = Collections.singletonList(gotoTblIp); | ||
499 | - OFMessage ipEntry = factory.buildFlowAdd() | ||
500 | - .setTableId(TableId.of(tmacTableId)) | ||
501 | - .setMatch(matchIp) | ||
502 | - .setInstructions(instructionsIp) | ||
503 | - .setPriority(1000) // strict priority required lower than | ||
504 | - // multicastMac | ||
505 | - .setBufferId(OFBufferId.NO_BUFFER) | ||
506 | - .setIdleTimeout(0) | ||
507 | - .setHardTimeout(0) | ||
508 | - .setXid(getNextTransactionId()) | ||
509 | - .build(); | ||
510 | - | ||
511 | - // match for router-mac and mpls packets | ||
512 | - OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST); | ||
513 | - /* TODO: need to read network config and need to allow only | ||
514 | - the packets with DMAC as the correspondent router MAC address | ||
515 | - OFOxmList oxmListMpls = OFOxmList.of(dmac, oxmpls); | ||
516 | - OFMatchV3 matchMpls = factory.buildMatchV3() | ||
517 | - .setOxmList(oxmListMpls).build(); | ||
518 | - */ | ||
519 | - OFOxmList oxmListMpls = OFOxmList.EMPTY; | ||
520 | - OFMatchV3 matchMpls = factory.buildMatchV3() | ||
521 | - .setOxmList(oxmList) | ||
522 | - .build(); | ||
523 | - | ||
524 | - OFInstruction gotoTblMpls = factory.instructions().buildGotoTable() | ||
525 | - .setTableId(TableId.of(mplsTableId)).build(); | ||
526 | - List<OFInstruction> instructionsMpls = Collections.singletonList(gotoTblMpls); | ||
527 | - OFMessage mplsEntry = factory.buildFlowAdd() | ||
528 | - .setTableId(TableId.of(tmacTableId)) | ||
529 | - .setMatch(matchMpls) | ||
530 | - .setInstructions(instructionsMpls) | ||
531 | - .setPriority(1001) // strict priority required lower than | ||
532 | - // multicastMac | ||
533 | - .setBufferId(OFBufferId.NO_BUFFER) | ||
534 | - .setIdleTimeout(0) | ||
535 | - .setHardTimeout(0) | ||
536 | - .setXid(getNextTransactionId()) | ||
537 | - .build(); | ||
538 | - | ||
539 | - log.debug("Adding termination-mac-rules in sw {}", getStringId()); | ||
540 | - List<OFMessage> msglist = new ArrayList<OFMessage>(2); | ||
541 | - msglist.add(ipEntry); | ||
542 | - msglist.add(mplsEntry); | ||
543 | - write(msglist); | ||
544 | - } | ||
545 | - | ||
546 | - private MacAddress getRouterMacAddr() { | ||
547 | - // TODO: need to read network config : RouterIp | ||
548 | - return MacAddress.of("00:00:00:00:00:00"); | ||
549 | } | 173 | } |
550 | 174 | ||
551 | private TableId getTableId(TableType tableType) { | 175 | private TableId getTableId(TableType tableType) { |
... | @@ -556,6 +180,10 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -556,6 +180,10 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
556 | return TableId.of(mplsTableId); | 180 | return TableId.of(mplsTableId); |
557 | case ACL: | 181 | case ACL: |
558 | return TableId.of(aclTableId); | 182 | return TableId.of(aclTableId); |
183 | + case VLAN: | ||
184 | + return TableId.of(vlanTableId); | ||
185 | + case ETHER: | ||
186 | + return TableId.of(tmacTableId); | ||
559 | default: { | 187 | default: { |
560 | log.error("Table type {} is not supported in the driver", tableType); | 188 | log.error("Table type {} is not supported in the driver", tableType); |
561 | return TableId.NONE; | 189 | return TableId.NONE; |
... | @@ -563,19 +191,4 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { | ... | @@ -563,19 +191,4 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch { |
563 | } | 191 | } |
564 | } | 192 | } |
565 | 193 | ||
566 | - private void sendHandshakeBarrier() throws IOException { | ||
567 | - long xid = getNextTransactionId(); | ||
568 | - barrierXidToWaitFor = xid; | ||
569 | - OFBarrierRequest br = factory() | ||
570 | - .buildBarrierRequest() | ||
571 | - .setXid(xid) | ||
572 | - .build(); | ||
573 | - write(br); | ||
574 | - } | ||
575 | - | ||
576 | - @Override | ||
577 | - public TableType getTableType(TableId tid) { | ||
578 | - return TableType.NONE; // XXX this needs to be fixed | ||
579 | - } | ||
580 | - | ||
581 | } | 194 | } | ... | ... |
1 | +package org.onlab.packet; | ||
2 | + | ||
3 | +import java.nio.ByteBuffer; | ||
4 | +import java.util.HashMap; | ||
5 | +import java.util.Map; | ||
6 | + | ||
7 | +public class MPLS extends BasePacket { | ||
8 | + public static final int ADDRESS_LENGTH = 4; | ||
9 | + public static final byte PROTOCOL_IPV4 = 0x1; | ||
10 | + public static final byte PROTOCOL_MPLS = 0x6; | ||
11 | + public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP; | ||
12 | + | ||
13 | + static { | ||
14 | + PROTOCOL_CLASS_MAP = new HashMap<Byte, Class<? extends IPacket>>(); | ||
15 | + PROTOCOL_CLASS_MAP.put(PROTOCOL_IPV4, IPv4.class); | ||
16 | + PROTOCOL_CLASS_MAP.put(PROTOCOL_MPLS, MPLS.class); | ||
17 | + } | ||
18 | + | ||
19 | + protected int label; //20bits | ||
20 | + protected byte bos; //1bit | ||
21 | + protected byte ttl; //8bits | ||
22 | + protected byte protocol; | ||
23 | + | ||
24 | + /** | ||
25 | + * Default constructor that sets the version to 4. | ||
26 | + */ | ||
27 | + public MPLS() { | ||
28 | + super(); | ||
29 | + this.bos = 1; | ||
30 | + this.protocol = PROTOCOL_IPV4; | ||
31 | + } | ||
32 | + | ||
33 | + @Override | ||
34 | + public byte[] serialize() { | ||
35 | + byte[] payloadData = null; | ||
36 | + if (payload != null) { | ||
37 | + payload.setParent(this); | ||
38 | + payloadData = payload.serialize(); | ||
39 | + } | ||
40 | + | ||
41 | + byte[] data = new byte[(4 + ((payloadData != null) ? payloadData.length : 0)) ]; | ||
42 | + ByteBuffer bb = ByteBuffer.wrap(data); | ||
43 | + | ||
44 | + bb.putInt(((this.label & 0x000fffff) << 12) | ((this.bos & 0x1) << 8 | (this.ttl & 0xff))); | ||
45 | + if (payloadData != null) { | ||
46 | + bb.put(payloadData); | ||
47 | + } | ||
48 | + | ||
49 | + return data; | ||
50 | + } | ||
51 | + | ||
52 | + @Override | ||
53 | + public IPacket deserialize(byte[] data, int offset, int length) { | ||
54 | + ByteBuffer bb = ByteBuffer.wrap(data, offset, length); | ||
55 | + | ||
56 | + int mplsheader = bb.getInt(); | ||
57 | + this.label = ((mplsheader & 0xfffff000) >> 12); | ||
58 | + this.bos = (byte) ((mplsheader & 0x00000100) >> 8); | ||
59 | + this.bos = (byte) (mplsheader & 0x000000ff); | ||
60 | + this.protocol = (this.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS; | ||
61 | + | ||
62 | + IPacket payload; | ||
63 | + if (IPv4.PROTOCOL_CLASS_MAP.containsKey(this.protocol)) { | ||
64 | + Class<? extends IPacket> clazz = IPv4.PROTOCOL_CLASS_MAP.get(this.protocol); | ||
65 | + try { | ||
66 | + payload = clazz.newInstance(); | ||
67 | + } catch (Exception e) { | ||
68 | + throw new RuntimeException("Error parsing payload for MPLS packet", e); | ||
69 | + } | ||
70 | + } else { | ||
71 | + payload = new Data(); | ||
72 | + } | ||
73 | + this.payload = payload.deserialize(data, bb.position(), bb.limit() - bb.position()); | ||
74 | + this.payload.setParent(this); | ||
75 | + | ||
76 | + return this; | ||
77 | + } | ||
78 | + | ||
79 | + /** | ||
80 | + * Returns the MPLS label. | ||
81 | + * | ||
82 | + * @return MPLS label | ||
83 | + */ | ||
84 | + public int getLabel() { | ||
85 | + return label; | ||
86 | + } | ||
87 | + | ||
88 | + /** | ||
89 | + * Sets the MPLS label. | ||
90 | + * | ||
91 | + * @param label | ||
92 | + */ | ||
93 | + public void setLabel(int label) { | ||
94 | + this.label = label; | ||
95 | + } | ||
96 | + | ||
97 | + /** | ||
98 | + * Returns the MPLS TTL of the packet. | ||
99 | + * | ||
100 | + * @return MPLS TTL of the packet | ||
101 | + */ | ||
102 | + public byte getTtl() { | ||
103 | + return ttl; | ||
104 | + } | ||
105 | + | ||
106 | + /** | ||
107 | + * Sets the MPLS TTL of the packet. | ||
108 | + * | ||
109 | + * @param ttl MPLS TTL | ||
110 | + */ | ||
111 | + public void setTtl(byte ttl) { | ||
112 | + this.ttl = ttl; | ||
113 | + } | ||
114 | + | ||
115 | +} |
-
Please register or login to post a comment