blob: 3164381abaf20c78097dfe24fb3828b8ca540f30 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* Copyright 2012, Big Switch Networks, Inc.
3* Originally created by David Erickson, Stanford University
4*
5* Licensed under the Apache License, Version 2.0 (the "License"); you may
6* not use this file except in compliance with the License. You may obtain
7* 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, WITHOUT
13* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14* License for the specific language governing permissions and limitations
15* under the License.
16**/
17
18package net.floodlightcontroller.core.internal;
19
20import java.io.IOException;
21import java.net.SocketAddress;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.Date;
26import java.util.HashMap;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30import java.util.concurrent.ConcurrentHashMap;
31import java.util.concurrent.ConcurrentMap;
32import java.util.concurrent.Future;
33import java.util.concurrent.atomic.AtomicInteger;
34import java.util.concurrent.locks.Lock;
35import java.util.concurrent.locks.ReentrantReadWriteLock;
36
37import net.floodlightcontroller.core.FloodlightContext;
38import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080039import net.floodlightcontroller.core.IFloodlightProviderService.Role;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080040import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041import net.floodlightcontroller.core.IOFSwitch;
42import net.floodlightcontroller.core.annotations.LogMessageDoc;
43import net.floodlightcontroller.core.annotations.LogMessageDocs;
44import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
45import net.floodlightcontroller.threadpool.IThreadPoolService;
46import net.floodlightcontroller.util.TimedCache;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070047import net.onrc.onos.core.main.IOnosRemoteSwitch;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080048
49import org.codehaus.jackson.annotate.JsonIgnore;
50import org.codehaus.jackson.annotate.JsonProperty;
51import org.codehaus.jackson.map.annotate.JsonSerialize;
Pankaj Berde98478422013-09-10 13:53:26 -070052import org.codehaus.jackson.map.ser.ToStringSerializer;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080053import org.jboss.netty.channel.Channel;
54import org.openflow.protocol.OFFeaturesReply;
55import org.openflow.protocol.OFFeaturesRequest;
56import org.openflow.protocol.OFFlowMod;
57import org.openflow.protocol.OFMatch;
58import org.openflow.protocol.OFMessage;
59import org.openflow.protocol.OFPhysicalPort;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080060import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
61import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080062import org.openflow.protocol.OFPort;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080063import org.openflow.protocol.OFStatisticsRequest;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080064import org.openflow.protocol.OFType;
65import org.openflow.protocol.OFVendor;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080066import org.openflow.protocol.statistics.OFDescriptionStatistics;
67import org.openflow.protocol.statistics.OFStatistics;
68import org.openflow.util.HexString;
69import org.openflow.util.U16;
70import org.openflow.vendor.nicira.OFNiciraVendorData;
71import org.openflow.vendor.nicira.OFRoleRequestVendorData;
72import org.openflow.vendor.nicira.OFRoleVendorData;
73import org.slf4j.Logger;
74import org.slf4j.LoggerFactory;
75
Pankaj Berde98478422013-09-10 13:53:26 -070076
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080077/**
78 * This is the internal representation of an openflow switch.
79 */
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -070080public class OFSwitchImpl implements IOFSwitch, IOnosRemoteSwitch {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080081 // TODO: should we really do logging in the class or should we throw
82 // exception that can then be handled by callers?
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070083 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImpl.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080084
85 private static final String HA_CHECK_SWITCH =
86 "Check the health of the indicated switch. If the problem " +
87 "persists or occurs repeatedly, it likely indicates a defect " +
88 "in the switch HA implementation.";
89
90 protected ConcurrentMap<Object, Object> attributes;
91 protected IFloodlightProviderService floodlightProvider;
92 protected IThreadPoolService threadPool;
93 protected Date connectedSince;
94 protected String stringId;
95 protected Channel channel;
96 protected AtomicInteger transactionIdSource;
97 // Lock to protect modification of the port maps. We only need to
98 // synchronize on modifications. For read operations we are fine since
99 // we rely on ConcurrentMaps which works for our use case.
100 private Object portLock;
101 // Map port numbers to the appropriate OFPhysicalPort
102 protected ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber;
103 // Map port names to the appropriate OFPhyiscalPort
104 // XXX: The OF spec doesn't specify if port names need to be unique but
105 // according it's always the case in practice.
106 protected ConcurrentHashMap<String, OFPhysicalPort> portsByName;
107 protected Map<Integer,OFStatisticsFuture> statsFutureMap;
108 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
109 protected Map<Integer,OFFeaturesReplyFuture> featuresFutureMap;
110 protected boolean connected;
111 protected Role role;
112 protected TimedCache<Long> timedCache;
113 protected ReentrantReadWriteLock listenerLock;
114 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
115 /**
116 * When sending a role request message, the role request is added
117 * to this queue. If a role reply is received this queue is checked to
118 * verify that the reply matches the expected reply. We require in order
119 * delivery of replies. That's why we use a Queue.
120 * The RoleChanger uses a timeout to ensure we receive a timely reply.
121 *
122 * Need to synchronize on this instance if a request is sent, received,
123 * checked.
124 */
125 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
126
127 /* Switch features from initial featuresReply */
128 protected int capabilities;
129 protected int buffers;
130 protected int actions;
131 protected byte tables;
132 protected long datapathId;
133
134 public static IOFSwitchFeatures switchFeatures;
135 protected static final ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>> local_msg_buffer =
136 new ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>>() {
137 @Override
138 protected Map<OFSwitchImpl,List<OFMessage>> initialValue() {
139 return new HashMap<OFSwitchImpl,List<OFMessage>>();
140 }
141 };
142
143 // for managing our map sizes
144 protected static final int MAX_MACS_PER_SWITCH = 1000;
145
146 protected static class PendingRoleRequestEntry {
147 protected int xid;
148 protected Role role;
149 // cookie is used to identify the role "generation". roleChanger uses
150 protected long cookie;
151 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
152 this.xid = xid;
153 this.role = role;
154 this.cookie = cookie;
155 }
156 }
157
158 public OFSwitchImpl() {
159 this.stringId = null;
160 this.attributes = new ConcurrentHashMap<Object, Object>();
161 this.connectedSince = new Date();
162 this.transactionIdSource = new AtomicInteger();
163 this.portLock = new Object();
164 this.portsByNumber = new ConcurrentHashMap<Short, OFPhysicalPort>();
165 this.portsByName = new ConcurrentHashMap<String, OFPhysicalPort>();
166 this.connected = true;
167 this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>();
168 this.featuresFutureMap = new ConcurrentHashMap<Integer,OFFeaturesReplyFuture>();
169 this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>();
170 this.role = null;
171 this.timedCache = new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval
172 this.listenerLock = new ReentrantReadWriteLock();
173 this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, Long>();
174 this.pendingRoleRequests = new LinkedList<OFSwitchImpl.PendingRoleRequestEntry>();
175
176 // Defaults properties for an ideal switch
177 this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL);
178 this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true));
179 this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, new Boolean(true));
180 }
181
182
183 @Override
184 public Object getAttribute(String name) {
185 if (this.attributes.containsKey(name)) {
186 return this.attributes.get(name);
187 }
188 return null;
189 }
190
191 @Override
192 public void setAttribute(String name, Object value) {
193 this.attributes.put(name, value);
194 return;
195 }
196
197 @Override
198 public Object removeAttribute(String name) {
199 return this.attributes.remove(name);
200 }
201
202 @Override
203 public boolean hasAttribute(String name) {
204 return this.attributes.containsKey(name);
205 }
206
207 @Override
208 @JsonIgnore
209 public Channel getChannel() {
210 return this.channel;
211 }
212
213 @JsonIgnore
214 public void setChannel(Channel channel) {
215 this.channel = channel;
216 }
217
218 @Override
219 public void write(OFMessage m, FloodlightContext bc) throws IOException {
220 Map<OFSwitchImpl,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
221 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
222 if (msg_buffer == null) {
223 msg_buffer = new ArrayList<OFMessage>();
224 msg_buffer_map.put(this, msg_buffer);
225 }
226
227 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
228 msg_buffer.add(m);
229
230 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
231 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
232 this.write(msg_buffer);
233 msg_buffer.clear();
234 }
235 }
236
237 @Override
238 @LogMessageDoc(level="WARN",
239 message="Sending OF message that modifies switch " +
240 "state while in the slave role: {switch}",
241 explanation="An application has sent a message to a switch " +
242 "that is not valid when the switch is in a slave role",
243 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
244 public void write(List<OFMessage> msglist,
245 FloodlightContext bc) throws IOException {
246 for (OFMessage m : msglist) {
247 if (role == Role.SLAVE) {
248 switch (m.getType()) {
249 case PACKET_OUT:
250 case FLOW_MOD:
251 case PORT_MOD:
252 log.warn("Sending OF message that modifies switch " +
253 "state while in the slave role: {}",
254 m.getType().name());
255 break;
256 default:
257 break;
258 }
259 }
260 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
261 }
262 this.write(msglist);
263 }
264
265 public void write(List<OFMessage> msglist) throws IOException {
266 this.channel.write(msglist);
267 }
268
269 @Override
270 public void disconnectOutputStream() {
271 channel.close();
272 }
HIGUCHI Yuta2995eb52013-06-14 12:50:10 -0700273
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800274 @Override
275 @JsonIgnore
276 public void setFeaturesReply(OFFeaturesReply featuresReply) {
277 synchronized(portLock) {
278 if (stringId == null) {
279 /* ports are updated via port status message, so we
280 * only fill in ports on initial connection.
281 */
282 for (OFPhysicalPort port : featuresReply.getPorts()) {
283 setPort(port);
284 }
285 }
286 this.datapathId = featuresReply.getDatapathId();
287 this.capabilities = featuresReply.getCapabilities();
288 this.buffers = featuresReply.getBuffers();
289 this.actions = featuresReply.getActions();
290 this.tables = featuresReply.getTables();
291 this.stringId = HexString.toHexString(this.datapathId);
292 }
293 }
294
295 @Override
296 @JsonIgnore
297 public Collection<OFPhysicalPort> getEnabledPorts() {
298 List<OFPhysicalPort> result = new ArrayList<OFPhysicalPort>();
299 for (OFPhysicalPort port : portsByNumber.values()) {
300 if (portEnabled(port)) {
301 result.add(port);
302 }
303 }
304 return result;
305 }
306
307 @Override
308 @JsonIgnore
309 public Collection<Short> getEnabledPortNumbers() {
310 List<Short> result = new ArrayList<Short>();
311 for (OFPhysicalPort port : portsByNumber.values()) {
312 if (portEnabled(port)) {
313 result.add(port.getPortNumber());
314 }
315 }
316 return result;
317 }
318
319 @Override
320 public OFPhysicalPort getPort(short portNumber) {
321 return portsByNumber.get(portNumber);
322 }
323
324 @Override
325 public OFPhysicalPort getPort(String portName) {
326 return portsByName.get(portName);
327 }
328
329 @Override
330 @JsonIgnore
331 public void setPort(OFPhysicalPort port) {
332 synchronized(portLock) {
333 portsByNumber.put(port.getPortNumber(), port);
334 portsByName.put(port.getName(), port);
335 }
336 }
337
338 @Override
339 @JsonProperty("ports")
340 public Collection<OFPhysicalPort> getPorts() {
341 return Collections.unmodifiableCollection(portsByNumber.values());
342 }
343
344 @Override
345 public void deletePort(short portNumber) {
346 synchronized(portLock) {
347 portsByName.remove(portsByNumber.get(portNumber).getName());
348 portsByNumber.remove(portNumber);
349 }
350 }
351
352 @Override
353 public void deletePort(String portName) {
354 synchronized(portLock) {
355 portsByNumber.remove(portsByName.get(portName).getPortNumber());
356 portsByName.remove(portName);
357 }
358 }
359
360 @Override
361 public boolean portEnabled(short portNumber) {
362 if (portsByNumber.get(portNumber) == null) return false;
363 return portEnabled(portsByNumber.get(portNumber));
364 }
365
366 @Override
367 public boolean portEnabled(String portName) {
368 if (portsByName.get(portName) == null) return false;
369 return portEnabled(portsByName.get(portName));
370 }
371
372 @Override
373 public boolean portEnabled(OFPhysicalPort port) {
374 if (port == null)
375 return false;
376 if ((port.getConfig() & OFPortConfig.OFPPC_PORT_DOWN.getValue()) > 0)
377 return false;
378 if ((port.getState() & OFPortState.OFPPS_LINK_DOWN.getValue()) > 0)
379 return false;
380 // Port STP state doesn't work with multiple VLANs, so ignore it for now
381 //if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
382 // return false;
383 return true;
384 }
385
386 @Override
387 @JsonSerialize(using=DPIDSerializer.class)
388 @JsonProperty("dpid")
389 public long getId() {
390 if (this.stringId == null)
391 throw new RuntimeException("Features reply has not yet been set");
392 return this.datapathId;
393 }
394
395 @JsonIgnore
396 @Override
397 public String getStringId() {
398 return stringId;
399 }
400
401 /* (non-Javadoc)
402 * @see java.lang.Object#toString()
403 */
404 @Override
405 public String toString() {
HIGUCHI Yuta535f21b2013-06-21 10:02:16 -0700406 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?") + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800407 }
408
409 @Override
410 public ConcurrentMap<Object, Object> getAttributes() {
411 return this.attributes;
412 }
413
414 @Override
415 public Date getConnectedSince() {
416 return connectedSince;
417 }
418
419 @JsonIgnore
420 @Override
421 public int getNextTransactionId() {
422 return this.transactionIdSource.incrementAndGet();
423 }
424
425 @Override
426 public void sendStatsQuery(OFStatisticsRequest request, int xid,
427 IOFMessageListener caller) throws IOException {
428 request.setXid(xid);
429 this.iofMsgListenersMap.put(xid, caller);
430 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
431 msglist.add(request);
432 this.channel.write(msglist);
433 return;
434 }
435
436 @Override
437 public Future<List<OFStatistics>> getStatistics(OFStatisticsRequest request) throws IOException {
438 request.setXid(getNextTransactionId());
439 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this, request.getXid());
440 this.statsFutureMap.put(request.getXid(), future);
441 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
442 msglist.add(request);
443 this.channel.write(msglist);
444 return future;
445 }
446
447 @Override
448 public void deliverStatisticsReply(OFMessage reply) {
449 OFStatisticsFuture future = this.statsFutureMap.get(reply.getXid());
450 if (future != null) {
451 future.deliverFuture(this, reply);
452 // The future will ultimately unregister itself and call
453 // cancelStatisticsReply
454 return;
455 }
456 /* Transaction id was not found in statsFutureMap.check the other map */
457 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
458 if (caller != null) {
459 caller.receive(this, reply, null);
460 }
461 }
462
463 @Override
464 public void cancelStatisticsReply(int transactionId) {
465 if (null == this.statsFutureMap.remove(transactionId)) {
466 this.iofMsgListenersMap.remove(transactionId);
467 }
468 }
469
470 @Override
471 public void cancelAllStatisticsReplies() {
472 /* we don't need to be synchronized here. Even if another thread
473 * modifies the map while we're cleaning up the future will eventuall
474 * timeout */
475 for (OFStatisticsFuture f : statsFutureMap.values()) {
476 f.cancel(true);
477 }
478 statsFutureMap.clear();
479 iofMsgListenersMap.clear();
480 }
481
482
483 /**
484 * @param floodlightProvider the floodlightProvider to set
485 */
486 @JsonIgnore
487 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
488 this.floodlightProvider = floodlightProvider;
489 }
490
491 @JsonIgnore
492 public void setThreadPoolService(IThreadPoolService tp) {
493 this.threadPool = tp;
494 }
495
496 @JsonIgnore
497 @Override
498 public synchronized boolean isConnected() {
499 return connected;
500 }
501
502 @Override
503 @JsonIgnore
504 public synchronized void setConnected(boolean connected) {
505 this.connected = connected;
506 }
507
508 @Override
509 public Role getRole() {
510 return role;
511 }
512
513 @JsonIgnore
514 @Override
515 public boolean isActive() {
516 return (role != Role.SLAVE);
517 }
518
519 @Override
520 @JsonIgnore
521 public void setSwitchProperties(OFDescriptionStatistics description) {
522 if (switchFeatures != null) {
523 switchFeatures.setFromDescription(this, description);
524 }
525 }
526
527 @Override
528 @LogMessageDoc(level="ERROR",
529 message="Failed to clear all flows on switch {switch}",
530 explanation="An I/O error occured while trying to clear " +
531 "flows on the switch.",
532 recommendation=LogMessageDoc.CHECK_SWITCH)
533 public void clearAllFlowMods() {
534 // Delete all pre-existing flows
535 OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
536 OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory()
537 .getMessage(OFType.FLOW_MOD))
538 .setMatch(match)
539 .setCommand(OFFlowMod.OFPFC_DELETE)
540 .setOutPort(OFPort.OFPP_NONE)
541 .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
542 try {
543 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
544 msglist.add(fm);
545 channel.write(msglist);
546 } catch (Exception e) {
547 log.error("Failed to clear all flows on switch " + this, e);
548 }
549 }
550
551 @Override
552 public boolean updateBroadcastCache(Long entry, Short port) {
553 if (timedCache.update(entry)) {
554 Long count = portBroadcastCacheHitMap.putIfAbsent(port, new Long(1));
555 if (count != null) {
556 count++;
557 }
558 return true;
559 } else {
560 return false;
561 }
562 }
563
564 @Override
565 @JsonIgnore
566 public Map<Short, Long> getPortBroadcastHits() {
567 return this.portBroadcastCacheHitMap;
568 }
569
570
571 @Override
572 public void flush() {
573 Map<OFSwitchImpl,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
574 List<OFMessage> msglist = msg_buffer_map.get(this);
575 if ((msglist != null) && (msglist.size() > 0)) {
576 try {
577 this.write(msglist);
578 } catch (IOException e) {
579 // TODO: log exception
580 e.printStackTrace();
581 }
582 msglist.clear();
583 }
584 }
585
586 public static void flush_all() {
587 Map<OFSwitchImpl,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
588 for (OFSwitchImpl sw : msg_buffer_map.keySet()) {
589 sw.flush();
590 }
591 }
592
593 /**
594 * Return a read lock that must be held while calling the listeners for
595 * messages from the switch. Holding the read lock prevents the active
596 * switch list from being modified out from under the listeners.
597 * @return
598 */
599 @JsonIgnore
600 public Lock getListenerReadLock() {
601 return listenerLock.readLock();
602 }
603
604 /**
605 * Return a write lock that must be held when the controllers modifies the
606 * list of active switches. This is to ensure that the active switch list
607 * doesn't change out from under the listeners as they are handling a
608 * message from the switch.
609 * @return
610 */
611 @JsonIgnore
612 public Lock getListenerWriteLock() {
613 return listenerLock.writeLock();
614 }
615
616 /**
617 * Get the IP Address for the switch
618 * @return the inet address
619 */
620 @JsonSerialize(using=ToStringSerializer.class)
621 public SocketAddress getInetAddress() {
622 return channel.getRemoteAddress();
623 }
624
625 /**
626 * Send NX role request message to the switch requesting the specified role.
627 *
628 * This method should ONLY be called by @see RoleChanger.submitRequest().
629 *
630 * After sending the request add it to the queue of pending request. We
631 * use the queue to later verify that we indeed receive the correct reply.
632 * @param sw switch to send the role request message to
633 * @param role role to request
634 * @param cookie an opaque value that will be stored in the pending queue so
635 * RoleChanger can check for timeouts.
636 * @return transaction id of the role request message that was sent
637 */
638 protected int sendNxRoleRequest(Role role, long cookie)
639 throws IOException {
640 synchronized(pendingRoleRequests) {
641 // Convert the role enum to the appropriate integer constant used
642 // in the NX role request message
643 int nxRole = 0;
644 switch (role) {
645 case EQUAL:
646 nxRole = OFRoleVendorData.NX_ROLE_OTHER;
647 break;
648 case MASTER:
649 nxRole = OFRoleVendorData.NX_ROLE_MASTER;
650 break;
651 case SLAVE:
652 nxRole = OFRoleVendorData.NX_ROLE_SLAVE;
653 break;
654 default:
655 log.error("Invalid Role specified for switch {}."
656 + " Disconnecting.", this);
657 // TODO: should throw an error
658 return 0;
659 }
660
661 // Construct the role request message
662 OFVendor roleRequest = (OFVendor)floodlightProvider.
663 getOFMessageFactory().getMessage(OFType.VENDOR);
664 int xid = this.getNextTransactionId();
665 roleRequest.setXid(xid);
666 roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID);
667 OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData();
668 roleRequestData.setRole(nxRole);
669 roleRequest.setVendorData(roleRequestData);
670 roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH +
671 roleRequestData.getLength());
672
673 // Send it to the switch
674 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
675 msglist.add(roleRequest);
676 // FIXME: should this use this.write() in order for messages to
677 // be processed by handleOutgoingMessage()
678 this.channel.write(msglist);
679
680 pendingRoleRequests.add(new PendingRoleRequestEntry(xid, role, cookie));
681 return xid;
682 }
683 }
684
685 /**
686 * Deliver a RoleReply message to this switch. Checks if the reply
687 * message matches the expected reply (head of the pending request queue).
688 * We require in-order delivery of replies. If there's any deviation from
689 * our expectations we disconnect the switch.
690 *
691 * We must not check the received role against the controller's current
692 * role because there's no synchronization but that's fine @see RoleChanger
693 *
694 * Will be called by the OFChannelHandler's receive loop
695 *
696 * @param xid Xid of the reply message
697 * @param role The Role in the the reply message
698 */
699 @LogMessageDocs({
700 @LogMessageDoc(level="ERROR",
701 message="Switch {switch}: received unexpected role reply for " +
702 "Role {role}" +
703 " Disconnecting switch",
704 explanation="The switch sent an unexpected HA role reply",
705 recommendation=HA_CHECK_SWITCH),
706 @LogMessageDoc(level="ERROR",
707 message="Switch {switch}: expected role reply with " +
708 "Xid {xid}, got {xid}. Disconnecting switch",
709 explanation="The switch sent an unexpected HA role reply",
710 recommendation=HA_CHECK_SWITCH),
711 @LogMessageDoc(level="ERROR",
712 message="Switch {switch}: expected role reply with " +
713 "Role {role}, got {role}. Disconnecting switch",
714 explanation="The switch sent an unexpected HA role reply",
715 recommendation=HA_CHECK_SWITCH)
716 })
717 protected void deliverRoleReply(int xid, Role role) {
718 synchronized(pendingRoleRequests) {
719 PendingRoleRequestEntry head = pendingRoleRequests.poll();
720 if (head == null) {
721 // Maybe don't disconnect if the role reply we received is
722 // for the same role we are already in.
723 log.error("Switch {}: received unexpected role reply for Role {}" +
724 " Disconnecting switch", this, role );
725 this.channel.close();
726 }
727 else if (head.xid != xid) {
728 // check xid before role!!
729 log.error("Switch {}: expected role reply with " +
730 "Xid {}, got {}. Disconnecting switch",
731 new Object[] { this, head.xid, xid } );
732 this.channel.close();
733 }
734 else if (head.role != role) {
735 log.error("Switch {}: expected role reply with " +
736 "Role {}, got {}. Disconnecting switch",
737 new Object[] { this, head.role, role } );
738 this.channel.close();
739 }
740 else {
741 log.debug("Received role reply message from {}, setting role to {}",
742 this, role);
743 if (this.role == null && getAttribute(SWITCH_SUPPORTS_NX_ROLE) == null) {
744 // The first role reply we received. Set the attribute
745 // that the switch supports roles
746 setAttribute(SWITCH_SUPPORTS_NX_ROLE, true);
747 }
748 this.role = role;
749 }
750 }
751 }
752
753 /**
754 * Checks whether the given xid matches the xid of the first pending
755 * role request.
756 * @param xid
757 * @return
758 */
759 protected boolean checkFirstPendingRoleRequestXid (int xid) {
760 synchronized(pendingRoleRequests) {
761 PendingRoleRequestEntry head = pendingRoleRequests.peek();
762 if (head == null)
763 return false;
764 else
765 return head.xid == xid;
766 }
767 }
768
769 /**
770 * Checks whether the given request cookie matches the cookie of the first
771 * pending request
772 * @param cookie
773 * @return
774 */
775 protected boolean checkFirstPendingRoleRequestCookie(long cookie) {
776 synchronized(pendingRoleRequests) {
777 PendingRoleRequestEntry head = pendingRoleRequests.peek();
778 if (head == null)
779 return false;
780 else
781 return head.cookie == cookie;
782 }
783 }
784
785 /**
786 * Called if we receive a vendor error message indicating that roles
787 * are not supported by the switch. If the xid matches the first pending
788 * one, we'll mark the switch as not supporting roles and remove the head.
789 * Otherwise we ignore it.
790 * @param xid
791 */
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700792 protected void deliverRoleRequestNotSupported(int xid) {
793 deliverRoleRequestNotSupportedEx(xid);
794 }
795
796 /**
797 * ONOS Extension to deliverRoleRequestNotSupported().
798 * This version return the Roll request made.
799 * @see deliverRoleRequestNotSupported
800 * @param xid
801 * @return Role of attempted RoleRequest.
802 */
803 protected Role deliverRoleRequestNotSupportedEx(int xid) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800804 synchronized(pendingRoleRequests) {
805 PendingRoleRequestEntry head = pendingRoleRequests.poll();
806 this.role = null;
807 if (head!=null && head.xid == xid) {
808 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
Jonathan Harta95c6d92013-03-18 16:12:27 -0700809 return head.role;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800810 }
811 else {
Jonathan Hart9e92c512013-03-20 16:24:44 -0700812 log.debug("Closing {} because a role request error didn't match " +
813 "head of pendingRoleRequests queue", this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800814 this.channel.close();
Jonathan Harta95c6d92013-03-18 16:12:27 -0700815 return null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800816 }
817 }
818 }
819
820 @Override
821 public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
822 throws IOException {
823 OFMessage request = new OFFeaturesRequest();
824 request.setXid(getNextTransactionId());
825 OFFeaturesReplyFuture future =
826 new OFFeaturesReplyFuture(threadPool, this, request.getXid());
827 this.featuresFutureMap.put(request.getXid(), future);
828 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
829 msglist.add(request);
830 this.channel.write(msglist);
831 return future;
832 }
833
834 @Override
835 public void deliverOFFeaturesReply(OFMessage reply) {
836 OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid());
837 if (future != null) {
838 future.deliverFuture(this, reply);
839 // The future will ultimately unregister itself and call
840 // cancelFeaturesReply
841 return;
842 }
843 log.error("Switch {}: received unexpected featureReply", this);
844 }
845
846 @Override
847 public void cancelFeaturesReply(int transactionId) {
848 this.featuresFutureMap.remove(transactionId);
849 }
850
851
852 @Override
853 public int getBuffers() {
854 return buffers;
855 }
856
857
858 @Override
859 public int getActions() {
860 return actions;
861 }
862
863
864 @Override
865 public int getCapabilities() {
866 return capabilities;
867 }
868
869
870 @Override
871 public byte getTables() {
872 return tables;
873 }
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800874
875
876 @Override
877 public void setupRemoteSwitch(Long dpid) {
878 this.datapathId = dpid;
879 this.stringId = HexString.toHexString(this.datapathId);
880 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800881}