blob: 8efcf1cc778f958e466ce48f4a6ba3442f47129f [file] [log] [blame]
Hyunsun Moon90163ba2016-10-12 13:35:14 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moon90163ba2016-10-12 13:35:14 -07003 *
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 */
16package org.onosproject.ofagent.impl;
17
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090018import com.google.common.collect.ImmutableSet;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070019import io.netty.channel.Channel;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040020import org.onlab.osgi.ServiceDirectory;
21import org.onosproject.incubator.net.virtual.NetworkId;
22import org.onosproject.net.DeviceId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070023import org.onosproject.net.Port;
Claudine Chiu2729ffd2017-07-31 21:38:27 -040024import org.onosproject.net.device.PortStatistics;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070025import org.onosproject.net.flow.FlowRule;
26import org.onosproject.net.packet.InboundPacket;
27import org.onosproject.ofagent.api.OFSwitch;
28import org.onosproject.ofagent.api.OFSwitchCapabilities;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040029import org.onosproject.ofagent.api.OFSwitchService;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040030import org.projectfloodlight.openflow.protocol.OFBarrierReply;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070031import org.projectfloodlight.openflow.protocol.OFControllerRole;
Daniel Parkbe6b6732016-11-11 15:52:19 +090032import org.projectfloodlight.openflow.protocol.OFEchoReply;
33import org.projectfloodlight.openflow.protocol.OFEchoRequest;
34import org.projectfloodlight.openflow.protocol.OFFactories;
35import org.projectfloodlight.openflow.protocol.OFFactory;
36import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040037import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
Daniel Parkbe6b6732016-11-11 15:52:19 +090038import org.projectfloodlight.openflow.protocol.OFHello;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070039import org.projectfloodlight.openflow.protocol.OFMessage;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040040import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040041import org.projectfloodlight.openflow.protocol.OFPortDesc;
42import org.projectfloodlight.openflow.protocol.OFPortReason;
Claudine Chiu2729ffd2017-07-31 21:38:27 -040043import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
44import org.projectfloodlight.openflow.protocol.OFPortStatsRequest;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040045import org.projectfloodlight.openflow.protocol.OFPortStatus;
Claudine Chiu7c6d51c2017-06-15 23:13:51 -040046import org.projectfloodlight.openflow.protocol.OFRoleReply;
47import org.projectfloodlight.openflow.protocol.OFRoleRequest;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040048import org.projectfloodlight.openflow.protocol.OFSetConfig;
49import org.projectfloodlight.openflow.protocol.OFStatsReply;
50import org.projectfloodlight.openflow.protocol.OFStatsRequest;
51import org.projectfloodlight.openflow.protocol.OFType;
Daniel Parkbe6b6732016-11-11 15:52:19 +090052import org.projectfloodlight.openflow.protocol.OFVersion;
53import org.projectfloodlight.openflow.types.DatapathId;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040054import org.projectfloodlight.openflow.types.OFPort;
Claudine Chiu2729ffd2017-07-31 21:38:27 -040055import org.projectfloodlight.openflow.types.U64;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070058
Claudine Chiu785ef2d2017-07-04 13:13:28 -040059import java.util.ArrayList;
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090060import java.util.Collections;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040061import java.util.List;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070062import java.util.Set;
63import java.util.concurrent.ConcurrentHashMap;
64
65import static com.google.common.base.Preconditions.checkArgument;
66import static com.google.common.base.Preconditions.checkNotNull;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040067import static org.projectfloodlight.openflow.protocol.OFControllerRole.ROLE_EQUAL;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070068
69/**
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090070 * Implementation of the default OpenFlow switch.
Hyunsun Moon90163ba2016-10-12 13:35:14 -070071 */
72public final class DefaultOFSwitch implements OFSwitch {
73
74 private static final String ERR_CH_DUPLICATE = "Channel already exists: ";
75 private static final String ERR_CH_NOT_FOUND = "Channel not found: ";
Daniel Parkbe6b6732016-11-11 15:52:19 +090076 private static final long NUM_BUFFERS = 1024;
77 private static final short NUM_TABLES = 3;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070078
Claudine Chiue2d5acc2017-06-08 22:49:21 -040079 private final Logger log;
80
Claudine Chiu785ef2d2017-07-04 13:13:28 -040081 private final OFSwitchService ofSwitchService;
82
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090083 private final DatapathId dpId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070084 private final OFSwitchCapabilities capabilities;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040085 private final NetworkId networkId;
86 private final DeviceId deviceId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070087
Claudine Chiue2d5acc2017-06-08 22:49:21 -040088 // miss_send_len field (in OFSetConfig and OFGetConfig messages) indicates the max
89 // bytes of a packet that the switch sends to the controller
90 private int missSendLen = 0xffff;
91
Hyunsun Moon90163ba2016-10-12 13:35:14 -070092 private final ConcurrentHashMap<Channel, OFControllerRole> controllerRoleMap
93 = new ConcurrentHashMap<>();
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090094 private static final OFFactory FACTORY = OFFactories.getFactory(OFVersion.OF_13);
Hyunsun Moon90163ba2016-10-12 13:35:14 -070095
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090096 private int handshakeTransactionIds = -1;
Daniel Parkbe6b6732016-11-11 15:52:19 +090097
Claudine Chiu785ef2d2017-07-04 13:13:28 -040098 private DefaultOFSwitch(DatapathId dpid, OFSwitchCapabilities capabilities,
99 NetworkId networkId, DeviceId deviceId,
100 OFSwitchService ofSwitchService) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900101 this.dpId = dpid;
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700102 this.capabilities = capabilities;
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400103 this.networkId = networkId;
104 this.deviceId = deviceId;
105 this.ofSwitchService = ofSwitchService;
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400106 log = LoggerFactory.getLogger(getClass().getName() + " : " + dpid);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700107 }
108
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400109 public static DefaultOFSwitch of(DatapathId dpid, OFSwitchCapabilities capabilities,
110 NetworkId networkId, DeviceId deviceId,
111 ServiceDirectory serviceDirectory) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900112 checkNotNull(dpid, "DPID cannot be null");
113 checkNotNull(capabilities, "OF capabilities cannot be null");
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400114 return new DefaultOFSwitch(dpid, capabilities, networkId, deviceId,
115 serviceDirectory.get(OFSwitchService.class));
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900116 }
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700117
118 @Override
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900119 public DatapathId dpid() {
120 return this.dpId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700121 }
122
123 @Override
124 public OFSwitchCapabilities capabilities() {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900125 return this.capabilities;
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700126 }
127
128 @Override
129 public void addControllerChannel(Channel channel) {
130 controllerRoleMap.compute(channel, (ch, existing) -> {
131 final String error = ERR_CH_DUPLICATE + channel.remoteAddress();
132 checkArgument(existing == null, error);
133 return ROLE_EQUAL;
134 });
135 }
136
137 @Override
138 public void deleteControllerChannel(Channel channel) {
139 if (controllerRoleMap.remove(channel) == null) {
140 final String error = ERR_CH_NOT_FOUND + channel.remoteAddress();
141 throw new IllegalStateException(error);
142 }
143 }
144
145 @Override
146 public void setRole(Channel channel, OFControllerRole role) {
147 controllerRoleMap.compute(channel, (ch, existing) -> {
148 final String error = ERR_CH_NOT_FOUND + channel.remoteAddress();
149 checkNotNull(existing, error);
150 return role;
151 });
152 }
153
154 @Override
155 public OFControllerRole role(Channel channel) {
156 OFControllerRole role = controllerRoleMap.get(channel);
157 if (role == null) {
158 final String error = ERR_CH_NOT_FOUND + channel.remoteAddress();
159 throw new IllegalStateException(error);
160 }
161 return role;
162 }
163
164 @Override
165 public Set<Channel> controllerChannels() {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900166 return ImmutableSet.copyOf(controllerRoleMap.keySet());
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700167 }
168
169 @Override
170 public void processPortAdded(Port port) {
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400171 sendPortStatus(port, OFPortReason.ADD);
172 }
173
174 @Override
175 public void processPortRemoved(Port port) {
176 sendPortStatus(port, OFPortReason.DELETE);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700177 }
178
179 @Override
180 public void processPortDown(Port port) {
181 // TODO generate PORT_STATUS message and send it to the controller
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400182 log.debug("processPortDown: Functionality not yet supported for {}", port);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700183 }
184
185 @Override
186 public void processPortUp(Port port) {
187 // TODO generate PORT_STATUS message and send it to the controller
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400188 log.debug("processPortUp: Functionality not yet supported for {}", port);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700189 }
190
191 @Override
192 public void processFlowRemoved(FlowRule flowRule) {
193 // TODO generate FLOW_REMOVED message and send it to the controller
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400194 log.debug("processFlowRemoved: Functionality not yet supported for {}", flowRule);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700195 }
196
197 @Override
198 public void processPacketIn(InboundPacket packet) {
199 // TODO generate PACKET_IN message and send it to the controller
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400200 log.debug("processPacketIn: Functionality not yet supported for {}", packet);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700201 }
202
203 @Override
204 public void processControllerCommand(Channel channel, OFMessage msg) {
205 // TODO process controller command
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400206 log.debug("processControllerCommand: Functionality not yet supported for {}", msg);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700207 }
208
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400209 private void sendPortStatus(Port port, OFPortReason ofPortReason) {
210 Set<Channel> channels = controllerChannels();
211 if (channels.isEmpty()) {
212 log.trace("No channels present. Port status will not be sent.");
213 return;
214 }
215 OFPortDesc ofPortDesc = portDesc(port);
216 OFPortStatus ofPortStatus = FACTORY.buildPortStatus()
217 .setDesc(ofPortDesc)
218 .setReason(ofPortReason)
219 .build();
220 log.trace("Sending port status {}", ofPortStatus);
221 channels.forEach(channel -> {
222 channel.writeAndFlush(Collections.singletonList(ofPortStatus));
223 });
224 }
225
226 private OFPortDesc portDesc(Port port) {
227 OFPort ofPort = OFPort.of((int) port.number().toLong());
228 // TODO handle port state and other port attributes
229 OFPortDesc ofPortDesc = FACTORY.buildPortDesc()
230 .setPortNo(ofPort)
231 .build();
232 return ofPortDesc;
233 }
234
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400235 private OFPortStatsEntry portStatsEntry(PortStatistics portStatistic) {
236 OFPortStatsEntry ofPortStatsEntry = FACTORY.buildPortStatsEntry()
237 .setPortNo(OFPort.of(portStatistic.port()))
238 .setTxBytes(U64.of(portStatistic.bytesSent()))
239 .setTxPackets(U64.of(portStatistic.packetsSent()))
240 .setTxDropped(U64.of(portStatistic.packetsTxDropped()))
241 .setTxErrors(U64.of(portStatistic.packetsTxErrors()))
242 .setRxBytes(U64.of(portStatistic.bytesReceived()))
243 .setRxPackets(U64.of(portStatistic.packetsReceived()))
244 .setRxDropped(U64.of(portStatistic.packetsRxDropped()))
245 .setRxErrors(U64.of(portStatistic.packetsRxErrors()))
246 .setDurationSec(portStatistic.durationSec())
247 .setDurationNsec(portStatistic.durationNano())
248 .build();
249 return ofPortStatsEntry;
250 }
251
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700252 @Override
253 public void processStatsRequest(Channel channel, OFMessage msg) {
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400254 if (msg.getType() != OFType.STATS_REQUEST) {
255 log.warn("Ignoring message of type {}.", msg.getType());
256 return;
257 }
258
259 OFStatsRequest ofStatsRequest = (OFStatsRequest) msg;
260 OFStatsReply ofStatsReply = null;
261 switch (ofStatsRequest.getStatsType()) {
262 case PORT_DESC:
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400263 List<OFPortDesc> portDescs = new ArrayList<>();
264 Set<Port> ports = ofSwitchService.ports(networkId, deviceId);
265 ports.forEach(port -> {
266 OFPortDesc ofPortDesc = portDesc(port);
267 portDescs.add(ofPortDesc);
268 });
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400269 ofStatsReply = FACTORY.buildPortDescStatsReply()
270 .setXid(msg.getXid())
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400271 .setEntries(portDescs)
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400272 //TODO add details
273 .build();
274 break;
Claudine Chiu2729ffd2017-07-31 21:38:27 -0400275 case PORT:
276 OFPortStatsRequest portStatsRequest = (OFPortStatsRequest) msg;
277 OFPort ofPort = portStatsRequest.getPortNo();
278 List<OFPortStatsEntry> portStatsEntries = new ArrayList<>();
279 List<PortStatistics> portStatistics =
280 ofSwitchService.getPortStatistics(networkId, deviceId);
281 if (ofPort.equals(OFPort.ANY)) {
282 portStatistics.forEach(portStatistic -> {
283 OFPortStatsEntry ofPortStatsEntry = portStatsEntry(portStatistic);
284 portStatsEntries.add(ofPortStatsEntry);
285 });
286 }
287 ofStatsReply = FACTORY.buildPortStatsReply()
288 .setEntries(portStatsEntries)
289 .setXid(msg.getXid())
290 .build();
291 break;
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400292 case METER_FEATURES:
293 OFMeterFeatures ofMeterFeatures = FACTORY.buildMeterFeatures()
294 .build();
295 ofStatsReply = FACTORY.buildMeterFeaturesStatsReply()
296 .setXid(msg.getXid())
297 .setFeatures(ofMeterFeatures)
298 //TODO add details
299 .build();
300 break;
301 case DESC:
302 ofStatsReply = FACTORY.buildDescStatsReply()
303 .setXid(msg.getXid())
304 .build();
305 break;
306 default:
307 log.debug("Functionality not yet supported for type {} statsType{} msg {}",
308 msg.getType(), ofStatsRequest.getStatsType(), msg);
309 break;
310 }
311
312 if (ofStatsReply != null) {
313 log.trace("request {}; reply {}", msg, ofStatsReply);
314 channel.writeAndFlush(Collections.singletonList(ofStatsReply));
315 }
316
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700317 }
318
319 @Override
320 public void processRoleRequest(Channel channel, OFMessage msg) {
Claudine Chiu7c6d51c2017-06-15 23:13:51 -0400321 OFRoleRequest ofRoleRequest = (OFRoleRequest) msg;
322 OFControllerRole oldRole = role(channel);
323 OFControllerRole newRole = ofRoleRequest.getRole();
324 if (oldRole.equals(newRole)) {
325 log.trace("No change needed to existing role {}", oldRole);
326 } else {
327 log.trace("Changing role from {} to {}", oldRole, newRole);
328 setRole(channel, newRole);
329 }
330 OFRoleReply ofRoleReply = FACTORY.buildRoleReply()
331 .setRole(role(channel))
332 .setXid(msg.getXid())
333 .build();
334 channel.writeAndFlush(Collections.singletonList(ofRoleReply));
335 log.trace("request {}; reply {}", msg, ofRoleReply);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700336 }
337
338 @Override
339 public void processFeaturesRequest(Channel channel, OFMessage msg) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900340 OFFeaturesReply ofFeaturesReply = FACTORY.buildFeaturesReply()
341 .setDatapathId(dpId)
Daniel Parkbe6b6732016-11-11 15:52:19 +0900342 .setNBuffers(NUM_BUFFERS)
343 .setNTables(NUM_TABLES)
344 .setCapabilities(capabilities.ofSwitchCapabilities())
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900345 .setXid(msg.getXid())
346 .build();
347 channel.writeAndFlush(Collections.singletonList(ofFeaturesReply));
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700348 }
349
350 @Override
351 public void processLldp(Channel channel, OFMessage msg) {
352 // TODO process lldp
353 }
Daniel Parkbe6b6732016-11-11 15:52:19 +0900354
355 @Override
356 public void sendOfHello(Channel channel) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900357 OFHello ofHello = FACTORY.buildHello()
358 .setXid(this.handshakeTransactionIds--)
359 .build();
360 channel.writeAndFlush(Collections.singletonList(ofHello));
Daniel Parkbe6b6732016-11-11 15:52:19 +0900361 }
362
363 @Override
364 public void processEchoRequest(Channel channel, OFMessage msg) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900365 OFEchoReply ofEchoReply = FACTORY.buildEchoReply()
Daniel Parkbe6b6732016-11-11 15:52:19 +0900366 .setXid(msg.getXid())
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900367 .setData(((OFEchoRequest) msg).getData())
368 .build();
369 channel.writeAndFlush(Collections.singletonList(ofEchoReply));
Daniel Parkbe6b6732016-11-11 15:52:19 +0900370 }
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400371
372 @Override
373 public void processGetConfigRequest(Channel channel, OFMessage msg) {
374 OFGetConfigReply ofGetConfigReply = FACTORY.buildGetConfigReply()
375 .setXid(msg.getXid())
376 .setMissSendLen(missSendLen)
377 .build();
378 log.trace("request {}; reply {}", msg, ofGetConfigReply);
379 channel.writeAndFlush(Collections.singletonList(ofGetConfigReply));
380 }
381
382 @Override
383 public void processSetConfigMessage(Channel channel, OFMessage msg) {
384 OFSetConfig ofSetConfig = (OFSetConfig) msg;
385 if (missSendLen != ofSetConfig.getMissSendLen()) {
386 log.trace("Changing missSendLen from {} to {}.",
387 missSendLen, ofSetConfig.getMissSendLen());
388 missSendLen = ofSetConfig.getMissSendLen();
389 }
390
391 // SetConfig message is not acknowledged
392 }
393
394 @Override
395 public void processBarrierRequest(Channel channel, OFMessage msg) {
396 // TODO check previous state requests have been handled before issuing BarrierReply
397 OFBarrierReply ofBarrierReply = FACTORY.buildBarrierReply()
398 .setXid(msg.getXid())
399 .build();
400 log.trace("request {}; reply {}", msg, ofBarrierReply);
401 channel.writeAndFlush(Collections.singletonList(ofBarrierReply));
402 }
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700403}