blob: d52654f8a3f7d3ce2fa56d2f83a2315bc04b89c3 [file] [log] [blame]
Hyunsun Moon90163ba2016-10-12 13:35:14 -07001/*
2 * Copyright 2017-present 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 */
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;
24import org.onosproject.net.flow.FlowRule;
25import org.onosproject.net.packet.InboundPacket;
26import org.onosproject.ofagent.api.OFSwitch;
27import org.onosproject.ofagent.api.OFSwitchCapabilities;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040028import org.onosproject.ofagent.api.OFSwitchService;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040029import org.projectfloodlight.openflow.protocol.OFBarrierReply;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070030import org.projectfloodlight.openflow.protocol.OFControllerRole;
Daniel Parkbe6b6732016-11-11 15:52:19 +090031import org.projectfloodlight.openflow.protocol.OFEchoReply;
32import org.projectfloodlight.openflow.protocol.OFEchoRequest;
33import org.projectfloodlight.openflow.protocol.OFFactories;
34import org.projectfloodlight.openflow.protocol.OFFactory;
35import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040036import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
Daniel Parkbe6b6732016-11-11 15:52:19 +090037import org.projectfloodlight.openflow.protocol.OFHello;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070038import org.projectfloodlight.openflow.protocol.OFMessage;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040039import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040040import org.projectfloodlight.openflow.protocol.OFPortDesc;
41import org.projectfloodlight.openflow.protocol.OFPortReason;
42import org.projectfloodlight.openflow.protocol.OFPortStatus;
Claudine Chiu7c6d51c2017-06-15 23:13:51 -040043import org.projectfloodlight.openflow.protocol.OFRoleReply;
44import org.projectfloodlight.openflow.protocol.OFRoleRequest;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040045import org.projectfloodlight.openflow.protocol.OFSetConfig;
46import org.projectfloodlight.openflow.protocol.OFStatsReply;
47import org.projectfloodlight.openflow.protocol.OFStatsRequest;
48import org.projectfloodlight.openflow.protocol.OFType;
Daniel Parkbe6b6732016-11-11 15:52:19 +090049import org.projectfloodlight.openflow.protocol.OFVersion;
50import org.projectfloodlight.openflow.types.DatapathId;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040051import org.projectfloodlight.openflow.types.OFPort;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040052import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070054
Claudine Chiu785ef2d2017-07-04 13:13:28 -040055import java.util.ArrayList;
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090056import java.util.Collections;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040057import java.util.List;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070058import java.util.Set;
59import java.util.concurrent.ConcurrentHashMap;
60
61import static com.google.common.base.Preconditions.checkArgument;
62import static com.google.common.base.Preconditions.checkNotNull;
Claudine Chiue2d5acc2017-06-08 22:49:21 -040063import static org.projectfloodlight.openflow.protocol.OFControllerRole.ROLE_EQUAL;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070064
65/**
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090066 * Implementation of the default OpenFlow switch.
Hyunsun Moon90163ba2016-10-12 13:35:14 -070067 */
68public final class DefaultOFSwitch implements OFSwitch {
69
70 private static final String ERR_CH_DUPLICATE = "Channel already exists: ";
71 private static final String ERR_CH_NOT_FOUND = "Channel not found: ";
Daniel Parkbe6b6732016-11-11 15:52:19 +090072 private static final long NUM_BUFFERS = 1024;
73 private static final short NUM_TABLES = 3;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070074
Claudine Chiue2d5acc2017-06-08 22:49:21 -040075 private final Logger log;
76
Claudine Chiu785ef2d2017-07-04 13:13:28 -040077 private final OFSwitchService ofSwitchService;
78
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090079 private final DatapathId dpId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070080 private final OFSwitchCapabilities capabilities;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040081 private final NetworkId networkId;
82 private final DeviceId deviceId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070083
Claudine Chiue2d5acc2017-06-08 22:49:21 -040084 // miss_send_len field (in OFSetConfig and OFGetConfig messages) indicates the max
85 // bytes of a packet that the switch sends to the controller
86 private int missSendLen = 0xffff;
87
Hyunsun Moon90163ba2016-10-12 13:35:14 -070088 private final ConcurrentHashMap<Channel, OFControllerRole> controllerRoleMap
89 = new ConcurrentHashMap<>();
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090090 private static final OFFactory FACTORY = OFFactories.getFactory(OFVersion.OF_13);
Hyunsun Moon90163ba2016-10-12 13:35:14 -070091
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090092 private int handshakeTransactionIds = -1;
Daniel Parkbe6b6732016-11-11 15:52:19 +090093
Claudine Chiu785ef2d2017-07-04 13:13:28 -040094 private DefaultOFSwitch(DatapathId dpid, OFSwitchCapabilities capabilities,
95 NetworkId networkId, DeviceId deviceId,
96 OFSwitchService ofSwitchService) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +090097 this.dpId = dpid;
Hyunsun Moon90163ba2016-10-12 13:35:14 -070098 this.capabilities = capabilities;
Claudine Chiu785ef2d2017-07-04 13:13:28 -040099 this.networkId = networkId;
100 this.deviceId = deviceId;
101 this.ofSwitchService = ofSwitchService;
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400102 log = LoggerFactory.getLogger(getClass().getName() + " : " + dpid);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700103 }
104
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400105 public static DefaultOFSwitch of(DatapathId dpid, OFSwitchCapabilities capabilities,
106 NetworkId networkId, DeviceId deviceId,
107 ServiceDirectory serviceDirectory) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900108 checkNotNull(dpid, "DPID cannot be null");
109 checkNotNull(capabilities, "OF capabilities cannot be null");
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400110 return new DefaultOFSwitch(dpid, capabilities, networkId, deviceId,
111 serviceDirectory.get(OFSwitchService.class));
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900112 }
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700113
114 @Override
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900115 public DatapathId dpid() {
116 return this.dpId;
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700117 }
118
119 @Override
120 public OFSwitchCapabilities capabilities() {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900121 return this.capabilities;
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700122 }
123
124 @Override
125 public void addControllerChannel(Channel channel) {
126 controllerRoleMap.compute(channel, (ch, existing) -> {
127 final String error = ERR_CH_DUPLICATE + channel.remoteAddress();
128 checkArgument(existing == null, error);
129 return ROLE_EQUAL;
130 });
131 }
132
133 @Override
134 public void deleteControllerChannel(Channel channel) {
135 if (controllerRoleMap.remove(channel) == null) {
136 final String error = ERR_CH_NOT_FOUND + channel.remoteAddress();
137 throw new IllegalStateException(error);
138 }
139 }
140
141 @Override
142 public void setRole(Channel channel, OFControllerRole role) {
143 controllerRoleMap.compute(channel, (ch, existing) -> {
144 final String error = ERR_CH_NOT_FOUND + channel.remoteAddress();
145 checkNotNull(existing, error);
146 return role;
147 });
148 }
149
150 @Override
151 public OFControllerRole role(Channel channel) {
152 OFControllerRole role = controllerRoleMap.get(channel);
153 if (role == null) {
154 final String error = ERR_CH_NOT_FOUND + channel.remoteAddress();
155 throw new IllegalStateException(error);
156 }
157 return role;
158 }
159
160 @Override
161 public Set<Channel> controllerChannels() {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900162 return ImmutableSet.copyOf(controllerRoleMap.keySet());
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700163 }
164
165 @Override
166 public void processPortAdded(Port port) {
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400167 sendPortStatus(port, OFPortReason.ADD);
168 }
169
170 @Override
171 public void processPortRemoved(Port port) {
172 sendPortStatus(port, OFPortReason.DELETE);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700173 }
174
175 @Override
176 public void processPortDown(Port port) {
177 // TODO generate PORT_STATUS message and send it to the controller
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400178 log.debug("Functionality not yet supported for {}", port);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700179 }
180
181 @Override
182 public void processPortUp(Port port) {
183 // TODO generate PORT_STATUS message and send it to the controller
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400184 log.debug("Functionality not yet supported for {}", port);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700185 }
186
187 @Override
188 public void processFlowRemoved(FlowRule flowRule) {
189 // TODO generate FLOW_REMOVED message and send it to the controller
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400190 log.debug("Functionality not yet supported for {}", flowRule);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700191 }
192
193 @Override
194 public void processPacketIn(InboundPacket packet) {
195 // TODO generate PACKET_IN message and send it to the controller
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400196 log.debug("Functionality not yet supported for {}", packet);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700197 }
198
199 @Override
200 public void processControllerCommand(Channel channel, OFMessage msg) {
201 // TODO process controller command
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400202 log.debug("Functionality not yet supported for {}", msg);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700203 }
204
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400205 private void sendPortStatus(Port port, OFPortReason ofPortReason) {
206 Set<Channel> channels = controllerChannels();
207 if (channels.isEmpty()) {
208 log.trace("No channels present. Port status will not be sent.");
209 return;
210 }
211 OFPortDesc ofPortDesc = portDesc(port);
212 OFPortStatus ofPortStatus = FACTORY.buildPortStatus()
213 .setDesc(ofPortDesc)
214 .setReason(ofPortReason)
215 .build();
216 log.trace("Sending port status {}", ofPortStatus);
217 channels.forEach(channel -> {
218 channel.writeAndFlush(Collections.singletonList(ofPortStatus));
219 });
220 }
221
222 private OFPortDesc portDesc(Port port) {
223 OFPort ofPort = OFPort.of((int) port.number().toLong());
224 // TODO handle port state and other port attributes
225 OFPortDesc ofPortDesc = FACTORY.buildPortDesc()
226 .setPortNo(ofPort)
227 .build();
228 return ofPortDesc;
229 }
230
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700231 @Override
232 public void processStatsRequest(Channel channel, OFMessage msg) {
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400233 if (msg.getType() != OFType.STATS_REQUEST) {
234 log.warn("Ignoring message of type {}.", msg.getType());
235 return;
236 }
237
238 OFStatsRequest ofStatsRequest = (OFStatsRequest) msg;
239 OFStatsReply ofStatsReply = null;
240 switch (ofStatsRequest.getStatsType()) {
241 case PORT_DESC:
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400242 List<OFPortDesc> portDescs = new ArrayList<>();
243 Set<Port> ports = ofSwitchService.ports(networkId, deviceId);
244 ports.forEach(port -> {
245 OFPortDesc ofPortDesc = portDesc(port);
246 portDescs.add(ofPortDesc);
247 });
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400248 ofStatsReply = FACTORY.buildPortDescStatsReply()
249 .setXid(msg.getXid())
Claudine Chiu785ef2d2017-07-04 13:13:28 -0400250 .setEntries(portDescs)
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400251 //TODO add details
252 .build();
253 break;
254 case METER_FEATURES:
255 OFMeterFeatures ofMeterFeatures = FACTORY.buildMeterFeatures()
256 .build();
257 ofStatsReply = FACTORY.buildMeterFeaturesStatsReply()
258 .setXid(msg.getXid())
259 .setFeatures(ofMeterFeatures)
260 //TODO add details
261 .build();
262 break;
263 case DESC:
264 ofStatsReply = FACTORY.buildDescStatsReply()
265 .setXid(msg.getXid())
266 .build();
267 break;
268 default:
269 log.debug("Functionality not yet supported for type {} statsType{} msg {}",
270 msg.getType(), ofStatsRequest.getStatsType(), msg);
271 break;
272 }
273
274 if (ofStatsReply != null) {
275 log.trace("request {}; reply {}", msg, ofStatsReply);
276 channel.writeAndFlush(Collections.singletonList(ofStatsReply));
277 }
278
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700279 }
280
281 @Override
282 public void processRoleRequest(Channel channel, OFMessage msg) {
Claudine Chiu7c6d51c2017-06-15 23:13:51 -0400283 OFRoleRequest ofRoleRequest = (OFRoleRequest) msg;
284 OFControllerRole oldRole = role(channel);
285 OFControllerRole newRole = ofRoleRequest.getRole();
286 if (oldRole.equals(newRole)) {
287 log.trace("No change needed to existing role {}", oldRole);
288 } else {
289 log.trace("Changing role from {} to {}", oldRole, newRole);
290 setRole(channel, newRole);
291 }
292 OFRoleReply ofRoleReply = FACTORY.buildRoleReply()
293 .setRole(role(channel))
294 .setXid(msg.getXid())
295 .build();
296 channel.writeAndFlush(Collections.singletonList(ofRoleReply));
297 log.trace("request {}; reply {}", msg, ofRoleReply);
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700298 }
299
300 @Override
301 public void processFeaturesRequest(Channel channel, OFMessage msg) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900302 OFFeaturesReply ofFeaturesReply = FACTORY.buildFeaturesReply()
303 .setDatapathId(dpId)
Daniel Parkbe6b6732016-11-11 15:52:19 +0900304 .setNBuffers(NUM_BUFFERS)
305 .setNTables(NUM_TABLES)
306 .setCapabilities(capabilities.ofSwitchCapabilities())
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900307 .setXid(msg.getXid())
308 .build();
309 channel.writeAndFlush(Collections.singletonList(ofFeaturesReply));
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700310 }
311
312 @Override
313 public void processLldp(Channel channel, OFMessage msg) {
314 // TODO process lldp
315 }
Daniel Parkbe6b6732016-11-11 15:52:19 +0900316
317 @Override
318 public void sendOfHello(Channel channel) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900319 OFHello ofHello = FACTORY.buildHello()
320 .setXid(this.handshakeTransactionIds--)
321 .build();
322 channel.writeAndFlush(Collections.singletonList(ofHello));
Daniel Parkbe6b6732016-11-11 15:52:19 +0900323 }
324
325 @Override
326 public void processEchoRequest(Channel channel, OFMessage msg) {
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900327 OFEchoReply ofEchoReply = FACTORY.buildEchoReply()
Daniel Parkbe6b6732016-11-11 15:52:19 +0900328 .setXid(msg.getXid())
Hyunsun Moonf4ba44f2017-03-14 03:25:52 +0900329 .setData(((OFEchoRequest) msg).getData())
330 .build();
331 channel.writeAndFlush(Collections.singletonList(ofEchoReply));
Daniel Parkbe6b6732016-11-11 15:52:19 +0900332 }
Claudine Chiue2d5acc2017-06-08 22:49:21 -0400333
334 @Override
335 public void processGetConfigRequest(Channel channel, OFMessage msg) {
336 OFGetConfigReply ofGetConfigReply = FACTORY.buildGetConfigReply()
337 .setXid(msg.getXid())
338 .setMissSendLen(missSendLen)
339 .build();
340 log.trace("request {}; reply {}", msg, ofGetConfigReply);
341 channel.writeAndFlush(Collections.singletonList(ofGetConfigReply));
342 }
343
344 @Override
345 public void processSetConfigMessage(Channel channel, OFMessage msg) {
346 OFSetConfig ofSetConfig = (OFSetConfig) msg;
347 if (missSendLen != ofSetConfig.getMissSendLen()) {
348 log.trace("Changing missSendLen from {} to {}.",
349 missSendLen, ofSetConfig.getMissSendLen());
350 missSendLen = ofSetConfig.getMissSendLen();
351 }
352
353 // SetConfig message is not acknowledged
354 }
355
356 @Override
357 public void processBarrierRequest(Channel channel, OFMessage msg) {
358 // TODO check previous state requests have been handled before issuing BarrierReply
359 OFBarrierReply ofBarrierReply = FACTORY.buildBarrierReply()
360 .setXid(msg.getXid())
361 .build();
362 log.trace("request {}; reply {}", msg, ofBarrierReply);
363 channel.writeAndFlush(Collections.singletonList(ofBarrierReply));
364 }
Hyunsun Moon90163ba2016-10-12 13:35:14 -0700365}