blob: 8ac1e2281dc94cd327d7d5b2632ca8fd21fe072e [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
tom7ef8ff92014-09-17 13:08:06 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
tom7ef8ff92014-09-17 13:08:06 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
tom7ef8ff92014-09-17 13:08:06 -070016
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.driver;
tom7ef8ff92014-09-17 13:08:06 -070018
tom7ef8ff92014-09-17 13:08:06 -070019import org.jboss.netty.channel.Channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080020import org.onlab.packet.IpAddress;
alshabibb452fd72015-04-22 20:46:20 -070021import org.onosproject.net.driver.AbstractHandlerBehaviour;
Brian O'Connorabafb502014-12-02 22:26:20 -080022import org.onosproject.openflow.controller.Dpid;
23import org.onosproject.openflow.controller.RoleState;
tom7ef8ff92014-09-17 13:08:06 -070024import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
25import org.projectfloodlight.openflow.protocol.OFErrorMsg;
26import org.projectfloodlight.openflow.protocol.OFExperimenter;
27import org.projectfloodlight.openflow.protocol.OFFactories;
28import org.projectfloodlight.openflow.protocol.OFFactory;
29import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
30import org.projectfloodlight.openflow.protocol.OFMessage;
alshabibb452fd72015-04-22 20:46:20 -070031import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
tom7ef8ff92014-09-17 13:08:06 -070032import org.projectfloodlight.openflow.protocol.OFPortDesc;
33import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
Thomas Vachuska39274462014-12-02 13:23:50 -080034import org.projectfloodlight.openflow.protocol.OFPortStatus;
tom7ef8ff92014-09-17 13:08:06 -070035import org.projectfloodlight.openflow.protocol.OFRoleReply;
alshabibb452fd72015-04-22 20:46:20 -070036import org.projectfloodlight.openflow.protocol.OFRoleRequest;
tom7ef8ff92014-09-17 13:08:06 -070037import org.projectfloodlight.openflow.protocol.OFVersion;
38import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
40
Thomas Vachuska1c681d72015-05-18 14:58:53 -070041import java.io.IOException;
42import java.net.InetSocketAddress;
43import java.net.SocketAddress;
44import java.util.ArrayList;
45import java.util.Collections;
46import java.util.List;
47import java.util.concurrent.atomic.AtomicInteger;
48import java.util.stream.Collectors;
49
tom7ef8ff92014-09-17 13:08:06 -070050/**
51 * An abstract representation of an OpenFlow switch. Can be extended by others
52 * to serve as a base for their vendor specific representation of a switch.
53 */
alshabibb452fd72015-04-22 20:46:20 -070054public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
55 implements OpenFlowSwitchDriver {
tom7ef8ff92014-09-17 13:08:06 -070056
Yuta HIGUCHIf5416d82014-10-24 21:17:40 -070057 protected final Logger log = LoggerFactory.getLogger(getClass());
tom7ef8ff92014-09-17 13:08:06 -070058
alshabibb452fd72015-04-22 20:46:20 -070059 private Channel channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080060 protected String channelId;
tom7ef8ff92014-09-17 13:08:06 -070061
62 private boolean connected;
63 protected boolean startDriverHandshakeCalled = false;
alshabibb452fd72015-04-22 20:46:20 -070064 private Dpid dpid;
tom7ef8ff92014-09-17 13:08:06 -070065 private OpenFlowAgent agent;
66 private final AtomicInteger xidCounter = new AtomicInteger(0);
67
68 private OFVersion ofVersion;
69
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070070 protected List<OFPortDescStatsReply> ports = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -070071
72 protected boolean tableFull;
73
74 private RoleHandler roleMan;
75
76 protected RoleState role;
77
78 protected OFFeaturesReply features;
79 protected OFDescStatsReply desc;
80
alshabibb452fd72015-04-22 20:46:20 -070081 @Override
82 public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
tom7ef8ff92014-09-17 13:08:06 -070083 this.dpid = dpid;
84 this.desc = desc;
alshabibb452fd72015-04-22 20:46:20 -070085 this.ofVersion = ofv;
tom7ef8ff92014-09-17 13:08:06 -070086 }
87
88 //************************
89 // Channel related
90 //************************
91
92 @Override
93 public final void disconnectSwitch() {
94 this.channel.close();
95 }
96
97 @Override
98 public final void sendMsg(OFMessage m) {
Jonathan Hart9f1600a2015-07-28 13:58:31 -070099 if (role == RoleState.MASTER && channel.isConnected()) {
Marc De Leenheerd24420f2015-05-27 09:40:59 -0700100 channel.write(Collections.singletonList(m));
alshabib339a3d92014-09-26 17:54:32 -0700101 }
tom7ef8ff92014-09-17 13:08:06 -0700102 }
103
104 @Override
105 public final void sendMsg(List<OFMessage> msgs) {
Jonathan Hart9f1600a2015-07-28 13:58:31 -0700106 if (role == RoleState.MASTER && channel.isConnected()) {
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700107 channel.write(msgs);
alshabib339a3d92014-09-26 17:54:32 -0700108 }
tom7ef8ff92014-09-17 13:08:06 -0700109 }
110
111 @Override
alshabibb452fd72015-04-22 20:46:20 -0700112 public final void sendRoleRequest(OFMessage msg) {
113 if (msg instanceof OFRoleRequest ||
114 msg instanceof OFNiciraControllerRoleRequest) {
115 channel.write(Collections.singletonList(msg));
116 return;
117 }
118 throw new IllegalArgumentException("Someone is trying to send " +
119 "a non role request message");
120 }
tom7ef8ff92014-09-17 13:08:06 -0700121
Ayaka Koshibe3c240772015-05-20 16:23:40 -0700122 @Override
alshabiba2df7b2a2015-05-06 13:57:10 -0700123 public final void sendHandshakeMessage(OFMessage message) {
124 if (!this.isDriverHandshakeComplete()) {
125 channel.write(Collections.singletonList(message));
126 }
127 }
128
tom7ef8ff92014-09-17 13:08:06 -0700129 @Override
130 public final boolean isConnected() {
131 return this.connected;
132 }
133
134 @Override
135 public final void setConnected(boolean connected) {
136 this.connected = connected;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700137 }
tom7ef8ff92014-09-17 13:08:06 -0700138
139 @Override
140 public final void setChannel(Channel channel) {
141 this.channel = channel;
Ray Milkeye53f1712015-01-16 09:17:16 -0800142 final SocketAddress address = channel.getRemoteAddress();
143 if (address instanceof InetSocketAddress) {
144 final InetSocketAddress inetAddress = (InetSocketAddress) address;
145 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700146 if (ipAddress.isIp4()) {
Ray Milkeye53f1712015-01-16 09:17:16 -0800147 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
148 } else {
149 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
150 }
151 }
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700152 }
tom7ef8ff92014-09-17 13:08:06 -0700153
Ray Milkeye53f1712015-01-16 09:17:16 -0800154 @Override
155 public String channelId() {
156 return channelId;
157 }
158
tom7ef8ff92014-09-17 13:08:06 -0700159 //************************
160 // Switch features related
161 //************************
162
163 @Override
164 public final long getId() {
165 return this.dpid.value();
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700166 }
tom7ef8ff92014-09-17 13:08:06 -0700167
168 @Override
169 public final String getStringId() {
170 return this.dpid.toString();
171 }
172
173 @Override
174 public final void setOFVersion(OFVersion ofV) {
175 this.ofVersion = ofV;
176 }
177
178 @Override
179 public void setTableFull(boolean full) {
180 this.tableFull = full;
181 }
182
183 @Override
184 public void setFeaturesReply(OFFeaturesReply featuresReply) {
185 this.features = featuresReply;
186 }
187
188 @Override
189 public abstract Boolean supportNxRole();
190
191 //************************
192 // Message handling
193 //************************
194 /**
195 * Handle the message coming from the dataplane.
196 *
197 * @param m the actual message
198 */
199 @Override
200 public final void handleMessage(OFMessage m) {
Thomas Vachuska39274462014-12-02 13:23:50 -0800201 if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
alshabib339a3d92014-09-26 17:54:32 -0700202 this.agent.processMessage(dpid, m);
203 }
tom7ef8ff92014-09-17 13:08:06 -0700204 }
205
206 @Override
207 public RoleState getRole() {
208 return role;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700209 }
tom7ef8ff92014-09-17 13:08:06 -0700210
211 @Override
212 public final boolean connectSwitch() {
213 return this.agent.addConnectedSwitch(dpid, this);
214 }
215
216 @Override
217 public final boolean activateMasterSwitch() {
218 return this.agent.addActivatedMasterSwitch(dpid, this);
219 }
220
221 @Override
222 public final boolean activateEqualSwitch() {
223 return this.agent.addActivatedEqualSwitch(dpid, this);
224 }
225
226 @Override
227 public final void transitionToEqualSwitch() {
228 this.agent.transitionToEqualSwitch(dpid);
229 }
230
231 @Override
232 public final void transitionToMasterSwitch() {
233 this.agent.transitionToMasterSwitch(dpid);
234 }
235
236 @Override
237 public final void removeConnectedSwitch() {
238 this.agent.removeConnectedSwitch(dpid);
239 }
240
241 @Override
242 public OFFactory factory() {
243 return OFFactories.getFactory(ofVersion);
244 }
245
246 @Override
247 public void setPortDescReply(OFPortDescStatsReply portDescReply) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700248 this.ports.add(portDescReply);
249 }
250
251 @Override
252 public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
253 this.ports.addAll(portDescReplies);
tom7ef8ff92014-09-17 13:08:06 -0700254 }
255
256 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700257 public void returnRoleReply(RoleState requested, RoleState response) {
258 this.agent.returnRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700259 }
260
261 @Override
tom7ef8ff92014-09-17 13:08:06 -0700262 public abstract void startDriverHandshake();
263
264 @Override
265 public abstract boolean isDriverHandshakeComplete();
266
267 @Override
268 public abstract void processDriverHandshakeMessage(OFMessage m);
269
alshabib339a3d92014-09-26 17:54:32 -0700270
271 // Role Handling
272
tom7ef8ff92014-09-17 13:08:06 -0700273 @Override
274 public void setRole(RoleState role) {
275 try {
tom7ef8ff92014-09-17 13:08:06 -0700276 if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
Madan Jampanif2af7712015-05-29 18:43:52 -0700277 log.debug("Sending role {} to switch {}", role, getStringId());
alshabib339a3d92014-09-26 17:54:32 -0700278 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
279 this.role = role;
280 }
alshabib7814e9f2014-09-30 11:52:12 -0700281 } else {
282 this.role = role;
tom7ef8ff92014-09-17 13:08:06 -0700283 }
284 } catch (IOException e) {
285 log.error("Unable to write to switch {}.", this.dpid);
286 }
287 }
288
alshabib339a3d92014-09-26 17:54:32 -0700289 @Override
290 public void reassertRole() {
291 if (this.getRole() == RoleState.MASTER) {
292 log.warn("Received permission error from switch {} while " +
293 "being master. Reasserting master role.",
294 this.getStringId());
295 this.setRole(RoleState.MASTER);
296 }
297 }
298
299
tom7ef8ff92014-09-17 13:08:06 -0700300
301 @Override
302 public void handleRole(OFMessage m) throws SwitchStateException {
303 RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
304 RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
305 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
306 if (rri.getRole() == RoleState.MASTER) {
alshabib339a3d92014-09-26 17:54:32 -0700307 this.role = rri.getRole();
tom7ef8ff92014-09-17 13:08:06 -0700308 this.transitionToMasterSwitch();
309 } else if (rri.getRole() == RoleState.EQUAL ||
alshabib339a3d92014-09-26 17:54:32 -0700310 rri.getRole() == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700311 this.transitionToEqualSwitch();
312 }
alshabib339a3d92014-09-26 17:54:32 -0700313 } else {
alshabib4785eec2014-12-04 16:45:45 -0800314 log.warn("Failed to set role for {}", this.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700315 }
316 }
317
318 @Override
319 public void handleNiciraRole(OFMessage m) throws SwitchStateException {
320 RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
321 if (r == null) {
322 // The message wasn't really a Nicira role reply. We just
323 // dispatch it to the OFMessage listeners in this case.
324 this.handleMessage(m);
alshabibdfc7afb2014-10-21 20:13:27 -0700325 return;
tom7ef8ff92014-09-17 13:08:06 -0700326 }
327
328 RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
329 new RoleReplyInfo(r, null, m.getXid()));
330 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
331 if (r == RoleState.MASTER) {
alshabib339a3d92014-09-26 17:54:32 -0700332 this.role = r;
tom7ef8ff92014-09-17 13:08:06 -0700333 this.transitionToMasterSwitch();
334 } else if (r == RoleState.EQUAL ||
335 r == RoleState.SLAVE) {
336 this.transitionToEqualSwitch();
337 }
alshabib339a3d92014-09-26 17:54:32 -0700338 } else {
alshabibdfc7afb2014-10-21 20:13:27 -0700339 this.disconnectSwitch();
tom7ef8ff92014-09-17 13:08:06 -0700340 }
341 }
342
343 @Override
344 public boolean handleRoleError(OFErrorMsg error) {
345 try {
346 return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
347 } catch (SwitchStateException e) {
348 this.disconnectSwitch();
349 }
350 return true;
351 }
352
alshabib339a3d92014-09-26 17:54:32 -0700353
tom7ef8ff92014-09-17 13:08:06 -0700354
355 @Override
356 public final void setAgent(OpenFlowAgent ag) {
357 if (this.agent == null) {
358 this.agent = ag;
359 }
360 }
361
362 @Override
363 public final void setRoleHandler(RoleHandler roleHandler) {
364 if (this.roleMan == null) {
365 this.roleMan = roleHandler;
366 }
367 }
368
369 @Override
370 public void setSwitchDescription(OFDescStatsReply d) {
371 this.desc = d;
372 }
373
374 @Override
375 public int getNextTransactionId() {
376 return this.xidCounter.getAndIncrement();
377 }
378
379 @Override
380 public List<OFPortDesc> getPorts() {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700381 return this.ports.stream()
382 .flatMap((portReply) -> (portReply.getEntries().stream()))
383 .collect(Collectors.toList());
384 //return Collections.unmodifiableList(ports.getEntries());
tom7ef8ff92014-09-17 13:08:06 -0700385 }
386
387 @Override
Ray Milkeyd3edd032015-01-16 11:38:58 -0800388 public String manufacturerDescription() {
tom7ef8ff92014-09-17 13:08:06 -0700389 return this.desc.getMfrDesc();
390 }
391
392
393 @Override
394 public String datapathDescription() {
395 return this.desc.getDpDesc();
396 }
397
398
399 @Override
400 public String hardwareDescription() {
401 return this.desc.getHwDesc();
402 }
403
404 @Override
405 public String softwareDescription() {
406 return this.desc.getSwDesc();
407 }
408
409 @Override
410 public String serialNumber() {
411 return this.desc.getSerialNum();
412 }
413
alshabibb452fd72015-04-22 20:46:20 -0700414
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700415 @Override
416 public boolean isOptical() {
417 return false;
418 }
419
alshabib9af70072015-02-09 14:34:16 -0800420
alshabibb452fd72015-04-22 20:46:20 -0700421 @Override
422 public String toString() {
423 return this.getClass().getName() + " [" + ((channel != null)
424 ? channel.getRemoteAddress() : "?")
425 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
426 }
427
428
alshabib9af70072015-02-09 14:34:16 -0800429
tom7ef8ff92014-09-17 13:08:06 -0700430}