blob: cd6bfeff722d46c2a207b8c9742d124dd5a83d65 [file] [log] [blame]
sangho11c30ac2015-01-22 14:30:55 -08001/*
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 */
16package org.onosproject.openflow.drivers;
17
18import org.onosproject.openflow.controller.Dpid;
sangho87af8112015-01-29 12:53:08 -080019import org.onosproject.openflow.controller.RoleState;
sangho11c30ac2015-01-22 14:30:55 -080020import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
sangho87af8112015-01-29 12:53:08 -080021import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
22import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
23import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
24import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
25import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
26import org.projectfloodlight.openflow.protocol.OFErrorMsg;
27import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
28import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply;
29import org.projectfloodlight.openflow.protocol.OFMatchV3;
30import org.projectfloodlight.openflow.protocol.OFOxmList;
31import org.projectfloodlight.openflow.protocol.OFPortDesc;
32import org.projectfloodlight.openflow.protocol.OFStatsReply;
33import org.projectfloodlight.openflow.protocol.OFFlowMod;
34import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
35import org.projectfloodlight.openflow.protocol.OFFactory;
sangho11c30ac2015-01-22 14:30:55 -080036import org.projectfloodlight.openflow.protocol.OFMessage;
sangho87af8112015-01-29 12:53:08 -080037import org.projectfloodlight.openflow.protocol.OFType;
38import org.projectfloodlight.openflow.protocol.action.OFAction;
39import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
40import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
41import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
42import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
43import org.projectfloodlight.openflow.types.EthType;
44import org.projectfloodlight.openflow.types.MacAddress;
45import org.projectfloodlight.openflow.types.OFBufferId;
46import org.projectfloodlight.openflow.types.OFPort;
47import org.projectfloodlight.openflow.types.OFVlanVidMatch;
48import org.projectfloodlight.openflow.types.TableId;
49import org.projectfloodlight.openflow.types.U32;
50import org.projectfloodlight.openflow.util.HexString;
sangho11c30ac2015-01-22 14:30:55 -080051
sangho87af8112015-01-29 12:53:08 -080052import java.io.IOException;
53import java.util.ArrayList;
54import java.util.Collections;
sangho11c30ac2015-01-22 14:30:55 -080055import java.util.List;
sangho87af8112015-01-29 12:53:08 -080056import java.util.concurrent.atomic.AtomicBoolean;
sangho11c30ac2015-01-22 14:30:55 -080057
sangho11c30ac2015-01-22 14:30:55 -080058public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
59
sangho87af8112015-01-29 12:53:08 -080060 private OFFactory factory;
61
62 private final AtomicBoolean driverHandshakeComplete;
63 private AtomicBoolean haltStateMachine;
64
65 private DriverState driverState;
66
67 /* Default table ID - compatible with CpqD switch */
68 private static final int TABLE_VLAN = 0;
69 private static final int TABLE_TMAC = 1;
70 private static final int TABLE_IPV4_UNICAST = 2;
71 private static final int TABLE_MPLS = 3;
72 private static final int TABLE_ACL = 5;
73
74 private static final long TEST_FLOW_REMOVED_MASK = 0xf;
75 private static final long TEST_PACKET_IN_MASK = 0x7;
76 private static final long TEST_PORT_STATUS_MASK = 0x7;
77
78 private static final int OFPCML_NO_BUFFER = 0xffff;
79
80 private long barrierXidToWaitFor = -1;
81
82 /* Set the default values. These variables will get
83 * overwritten based on the switch vendor type
84 */
85 protected int vlanTableId = TABLE_VLAN;
86 protected int tmacTableId = TABLE_TMAC;
87 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
88 protected int mplsTableId = TABLE_MPLS;
89 protected int aclTableId = TABLE_ACL;
90
91 /* priority values for OF message */
92 private static final short MAX_PRIORITY = (short) 0xffff;
93 private static final short PRIORITY_MULTIPLIER = (short) 2046;
94 private static final short MIN_PRIORITY = 0x0;
95
96
97 protected OFSwitchImplSpringOpenTTP(Dpid dpid, OFDescStatsReply desc) {
98 super(dpid);
99 driverHandshakeComplete = new AtomicBoolean(false);
100 haltStateMachine = new AtomicBoolean(false);
101 driverState = DriverState.INIT;
102 setSwitchDescription(desc);
sangho11c30ac2015-01-22 14:30:55 -0800103 }
104
sangho11c30ac2015-01-22 14:30:55 -0800105
106 @Override
sangho87af8112015-01-29 12:53:08 -0800107 public String toString() {
108 return "OFSwitchImplSpringOpenTTP [" + ((channel != null)
109 ? channel.getRemoteAddress() : "?")
110 + " DPID[" + ((this.getStringId() != null) ?
111 this.getStringId() : "?") + "]]";
sangho11c30ac2015-01-22 14:30:55 -0800112 }
113
114 @Override
115 public Boolean supportNxRole() {
116 return null;
117 }
118
119 @Override
120 public void startDriverHandshake() {
sangho87af8112015-01-29 12:53:08 -0800121 log.debug("Starting driver handshake for sw {}", getStringId());
122 if (startDriverHandshakeCalled) {
123 throw new SwitchDriverSubHandshakeAlreadyStarted();
124 }
125 startDriverHandshakeCalled = true;
126 factory = this.factory();
sangho11c30ac2015-01-22 14:30:55 -0800127
sangho87af8112015-01-29 12:53:08 -0800128 try {
129 nextDriverState();
130 } catch (IOException e) {
131 log.error("Error {} during driver handshake for sw {}", e.getCause(),
132 getStringId());
133 }
sangho11c30ac2015-01-22 14:30:55 -0800134 }
135
136 @Override
137 public boolean isDriverHandshakeComplete() {
sangho87af8112015-01-29 12:53:08 -0800138 if (!startDriverHandshakeCalled) {
139 throw new SwitchDriverSubHandshakeNotStarted();
140 }
141 return driverHandshakeComplete.get();
sangho11c30ac2015-01-22 14:30:55 -0800142 }
143
144 @Override
145 public void processDriverHandshakeMessage(OFMessage m) {
sangho87af8112015-01-29 12:53:08 -0800146 if (!startDriverHandshakeCalled) {
147 throw new SwitchDriverSubHandshakeNotStarted();
148 }
149 if (driverHandshakeComplete.get()) {
150 throw new SwitchDriverSubHandshakeCompleted(m);
151 }
152 try {
153 processOFMessage(m);
154 } catch (IOException e) {
155 log.error("Error generated when processing OFMessage {}", e.getCause());
156 }
sangho11c30ac2015-01-22 14:30:55 -0800157 }
158
sangho87af8112015-01-29 12:53:08 -0800159 @Override
160 public void write(OFMessage msg) {
161 this.channel.write(Collections.singletonList(msg));
162 }
163
164 @Override
165 public void write(List<OFMessage> msgs) {
166 this.channel.write(msgs);
167 }
168
169 @Override
170 public void sendMsg(OFMessage m, TableType tableType) {
171
172 if (m.getType() == OFType.FLOW_MOD) {
173 OFFlowMod flowMod = (OFFlowMod) m;
174 OFFlowMod.Builder builder = flowMod.createBuilder();
175 builder.setTableId(getTableId(tableType));
176 OFFlowMod newFlowMod = builder.build();
177 if (role == RoleState.MASTER) {
178 this.write(newFlowMod);
179 }
180 } else {
181 if (role == RoleState.MASTER) {
182 this.write(m);
183 }
184 }
185 }
186
187 /*
188 * Driver handshake state machine
189 */
190
191 enum DriverState {
192 INIT,
193 SET_TABLE_MISS_ENTRIES,
194 SET_TABLE_VLAN_TMAC,
195 AUDIT_GROUPS,
196 SET_GROUPS,
197 VERIFY_GROUPS,
198 SET_ADJACENCY_LABELS,
199 EXIT
200 }
201
202 protected void nextDriverState() throws IOException {
203 DriverState currentState = driverState;
204 if (haltStateMachine.get()) {
205 return;
206 }
207 switch (currentState) {
208 case INIT:
209 driverState = DriverState.SET_TABLE_MISS_ENTRIES;
210 setTableMissEntries();
211 sendHandshakeBarrier();
212 break;
213 case SET_TABLE_MISS_ENTRIES:
214 driverState = DriverState.SET_TABLE_VLAN_TMAC;
215 /* TODO: read network configuration
216 boolean isConfigured = getNetworkConfig();
217 if (!isConfigured) {
218 return; // this will result in a handshake timeout
219 }
220 */
221 populateTableVlan();
222 populateTableTMac();
223 sendHandshakeBarrier();
224 break;
225 case SET_TABLE_VLAN_TMAC:
226 driverState = DriverState.EXIT;
227 driverHandshakeComplete.set(true);
228 log.debug("Driver handshake is complete");
229 break;
230 case EXIT:
231 default:
232 driverState = DriverState.EXIT;
233 log.error("Driver handshake has exited for sw: {}", getStringId());
234 }
235 }
236
237 private void processStatsReply(OFStatsReply sr) {
238 switch (sr.getStatsType()) {
239 case AGGREGATE:
240 break;
241 case DESC:
242 break;
243 case EXPERIMENTER:
244 break;
245 case FLOW:
246 break;
247 case GROUP_DESC:
248 processGroupDesc((OFGroupDescStatsReply) sr);
249 break;
250 case GROUP_FEATURES:
251 processGroupFeatures((OFGroupFeaturesStatsReply) sr);
252 break;
253 case METER_CONFIG:
254 break;
255 case METER_FEATURES:
256 break;
257 case PORT_DESC:
258 break;
259 case TABLE_FEATURES:
260 break;
261 default:
262 break;
263
264 }
265 }
266
267 private void processOFMessage(OFMessage m) throws IOException {
268 switch (m.getType()) {
269 case BARRIER_REPLY:
270 processBarrierReply(m);
271 break;
272
273 case ERROR:
274 processErrorMessage(m);
275 break;
276
277 case GET_ASYNC_REPLY:
278 OFAsyncGetReply asrep = (OFAsyncGetReply) m;
279 decodeAsyncGetReply(asrep);
280 break;
281
282 case PACKET_IN:
283 // not ready to handle packet-ins
284 break;
285
286 case QUEUE_GET_CONFIG_REPLY:
287 // not doing queue config yet
288 break;
289
290 case STATS_REPLY:
291 processStatsReply((OFStatsReply) m);
292 break;
293
294 case ROLE_REPLY: // channelHandler should handle this
295 case PORT_STATUS: // channelHandler should handle this
296 case FEATURES_REPLY: // don't care
297 case FLOW_REMOVED: // don't care
298 default:
299 log.debug("Received message {} during switch-driver subhandshake "
300 + "from switch {} ... Ignoring message", m, getStringId());
301 }
302 }
303
304 private void processBarrierReply(OFMessage m) throws IOException {
305 if (m.getXid() == barrierXidToWaitFor) {
306 // Driver state-machine progresses to the next state.
307 // If Barrier messages is not received, then eventually
308 // the ChannelHandler state machine will timeout, and the switch
309 // will be disconnected.
310 nextDriverState();
311 } else {
312 log.error("Received incorrect barrier-message xid {} (expected: {}) in "
313 + "switch-driver state {} for switch {}", m, barrierXidToWaitFor,
314 driverState, getStringId());
315 }
316 }
317
318 private void processErrorMessage(OFMessage m) {
319 log.error("Switch {} Error {} in DriverState", getStringId(),
320 (OFErrorMsg) m, driverState);
321 }
322
323 private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) {
324 log.info("Sw: {} Group Features {}", getStringId(), gfsr);
325 }
326
327 private void processGroupDesc(OFGroupDescStatsReply gdsr) {
328 log.info("Sw: {} Group Desc {}", getStringId(), gdsr);
329 }
330
331 /*
332 * Utility functions
333 */
334
335 private void decodeAsyncGetReply(OFAsyncGetReply rep) {
336 long frm = rep.getFlowRemovedMaskEqualMaster();
337 //long frs = rep.getFlowRemovedMaskSlave();
338 long pim = rep.getPacketInMaskEqualMaster();
339 //long pis = rep.getPacketInMaskSlave();
340 long psm = rep.getPortStatusMaskEqualMaster();
341 //long pss = rep.getPortStatusMaskSlave();
342
343 if (role == RoleState.MASTER || role == RoleState.EQUAL) { // should separate
344 log.info("FRM:{}", HexString.toHexString((frm & TEST_FLOW_REMOVED_MASK)));
345 log.info("PIM:{}", HexString.toHexString((pim & TEST_PACKET_IN_MASK)));
346 log.info("PSM:{}", HexString.toHexString((psm & TEST_PORT_STATUS_MASK)));
347 }
348 }
349
350 protected void setTableMissEntries() throws IOException {
351 // set all table-miss-entries
352 populateTableMissEntry(vlanTableId, true, false, false, -1);
353 populateTableMissEntry(tmacTableId, true, false, false, -1);
354 populateTableMissEntry(ipv4UnicastTableId, false, true, true,
355 aclTableId);
356 populateTableMissEntry(mplsTableId, false, true, true,
357 aclTableId);
358 populateTableMissEntry(aclTableId, false, false, false, -1);
359 log.debug("TableMissEntries are set");
360 }
361
362 /**
363 * Adds a table-miss-entry to a pipeline table.
364 * <p>
365 * The table-miss-entry can be added with 'write-actions' or
366 * 'apply-actions'. It can also add a 'goto-table' instruction. By default
367 * if none of the booleans in the call are set, then the table-miss entry is
368 * added with no instructions, which means that if a packet hits the
369 * table-miss-entry, pipeline execution will stop, and the action set
370 * associated with the packet will be executed.
371 *
372 * @param tableToAdd the table to where the table-miss-entry will be added
373 * @param toControllerNow as an APPLY_ACTION instruction
374 * @param toControllerWrite as a WRITE_ACTION instruction
375 * @param toTable as a GOTO_TABLE instruction
376 * @param tableToSend the table to send as per the GOTO_TABLE instruction it
377 * needs to be set if 'toTable' is true. Ignored of 'toTable' is
378 * false.
sangho87af8112015-01-29 12:53:08 -0800379 */
380 protected void populateTableMissEntry(int tableToAdd, boolean toControllerNow,
381 boolean toControllerWrite,
Ray Milkey51365f32015-02-04 11:24:22 -0800382 boolean toTable, int tableToSend) {
sangho87af8112015-01-29 12:53:08 -0800383 OFOxmList oxmList = OFOxmList.EMPTY;
384 OFMatchV3 match = factory.buildMatchV3()
385 .setOxmList(oxmList)
386 .build();
387 OFAction outc = factory.actions()
388 .buildOutput()
389 .setPort(OFPort.CONTROLLER)
390 .setMaxLen(OFPCML_NO_BUFFER)
391 .build();
392 List<OFInstruction> instructions = new ArrayList<OFInstruction>();
393 if (toControllerNow) {
394 // table-miss instruction to send to controller immediately
395 OFInstruction instr = factory.instructions()
396 .buildApplyActions()
397 .setActions(Collections.singletonList(outc))
398 .build();
399 instructions.add(instr);
400 }
401
402 if (toControllerWrite) {
403 // table-miss instruction to write-action to send to controller
404 // this will be executed whenever the action-set gets executed
405 OFInstruction instr = factory.instructions()
406 .buildWriteActions()
407 .setActions(Collections.singletonList(outc))
408 .build();
409 instructions.add(instr);
410 }
411
412 if (toTable) {
413 // table-miss instruction to goto-table x
414 OFInstruction instr = factory.instructions()
415 .gotoTable(TableId.of(tableToSend));
416 instructions.add(instr);
417 }
418
419 if (!toControllerNow && !toControllerWrite && !toTable) {
420 // table-miss has no instruction - at which point action-set will be
421 // executed - if there is an action to output/group in the action
422 // set
423 // the packet will be sent there, otherwise it will be dropped.
Ray Milkey8dc82082015-02-20 16:22:38 -0800424 instructions = Collections.<OFInstruction>emptyList();
sangho87af8112015-01-29 12:53:08 -0800425 }
426
427 OFMessage tableMissEntry = factory.buildFlowAdd()
428 .setTableId(TableId.of(tableToAdd))
429 .setMatch(match) // match everything
430 .setInstructions(instructions)
431 .setPriority(MIN_PRIORITY)
432 .setBufferId(OFBufferId.NO_BUFFER)
433 .setIdleTimeout(0)
434 .setHardTimeout(0)
435 .setXid(getNextTransactionId())
436 .build();
437 write(tableMissEntry);
438 }
439
440 private void populateTableVlan() throws IOException {
441 List<OFMessage> msglist = new ArrayList<OFMessage>();
442 for (OFPortDesc p : getPorts()) {
443 int pnum = p.getPortNo().getPortNumber();
444 if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) {
445 OFOxmInPort oxp = factory.oxms().inPort(p.getPortNo());
446 OFOxmVlanVid oxv = factory.oxms()
447 .vlanVid(OFVlanVidMatch.UNTAGGED);
448 OFOxmList oxmList = OFOxmList.of(oxp, oxv);
449 OFMatchV3 match = factory.buildMatchV3()
450 .setOxmList(oxmList).build();
451
452 // TODO: match on vlan-tagged packets for vlans configured on
453 // subnet ports and strip-vlan
454
455 OFInstruction gotoTbl = factory.instructions().buildGotoTable()
456 .setTableId(TableId.of(tmacTableId)).build();
457 List<OFInstruction> instructions = new ArrayList<OFInstruction>();
458 instructions.add(gotoTbl);
459 OFMessage flowEntry = factory.buildFlowAdd()
460 .setTableId(TableId.of(vlanTableId))
461 .setMatch(match)
462 .setInstructions(instructions)
463 .setPriority(1000) // does not matter - all rules
464 // exclusive
465 .setBufferId(OFBufferId.NO_BUFFER)
466 .setIdleTimeout(0)
467 .setHardTimeout(0)
468 .setXid(getNextTransactionId())
469 .build();
470 msglist.add(flowEntry);
471 }
472 }
473 write(msglist);
474 log.debug("Adding {} port/vlan-rules in sw {}", msglist.size(), getStringId());
475 }
476
477 private void populateTableTMac() throws IOException {
478 // match for router-mac and ip-packets
479 OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4);
480
481 /* TODO: need to read network config and need to allow only
482 the packets with DMAC as the correspondent router MAC address
483 Until network configuration is implemented, all packets are allowed
484
485 OFOxmEthDst dmac = factory.oxms().ethDst(getRouterMacAddr());
486 OFOxmList oxmListIp = OFOxmList.of(dmac, oxe);
487 OFMatchV3 matchIp = factory.buildMatchV3()
488 .setOxmList(oxmListIp).build();
489 */
490 OFOxmList oxmList = OFOxmList.EMPTY;
491 OFMatchV3 matchIp = factory.buildMatchV3()
492 .setOxmList(oxmList)
493 .build();
494
495 OFInstruction gotoTblIp = factory.instructions().buildGotoTable()
496 .setTableId(TableId.of(ipv4UnicastTableId)).build();
497 List<OFInstruction> instructionsIp = Collections.singletonList(gotoTblIp);
498 OFMessage ipEntry = factory.buildFlowAdd()
499 .setTableId(TableId.of(tmacTableId))
500 .setMatch(matchIp)
501 .setInstructions(instructionsIp)
502 .setPriority(1000) // strict priority required lower than
503 // multicastMac
504 .setBufferId(OFBufferId.NO_BUFFER)
505 .setIdleTimeout(0)
506 .setHardTimeout(0)
507 .setXid(getNextTransactionId())
508 .build();
509
510 // match for router-mac and mpls packets
511 OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST);
512 /* TODO: need to read network config and need to allow only
513 the packets with DMAC as the correspondent router MAC address
514 OFOxmList oxmListMpls = OFOxmList.of(dmac, oxmpls);
515 OFMatchV3 matchMpls = factory.buildMatchV3()
516 .setOxmList(oxmListMpls).build();
517 */
518 OFOxmList oxmListMpls = OFOxmList.EMPTY;
519 OFMatchV3 matchMpls = factory.buildMatchV3()
520 .setOxmList(oxmList)
521 .build();
522
523 OFInstruction gotoTblMpls = factory.instructions().buildGotoTable()
524 .setTableId(TableId.of(mplsTableId)).build();
525 List<OFInstruction> instructionsMpls = Collections.singletonList(gotoTblMpls);
526 OFMessage mplsEntry = factory.buildFlowAdd()
527 .setTableId(TableId.of(tmacTableId))
528 .setMatch(matchMpls)
529 .setInstructions(instructionsMpls)
530 .setPriority(1001) // strict priority required lower than
531 // multicastMac
532 .setBufferId(OFBufferId.NO_BUFFER)
533 .setIdleTimeout(0)
534 .setHardTimeout(0)
535 .setXid(getNextTransactionId())
536 .build();
537
538 log.debug("Adding termination-mac-rules in sw {}", getStringId());
539 List<OFMessage> msglist = new ArrayList<OFMessage>(2);
540 msglist.add(ipEntry);
541 msglist.add(mplsEntry);
542 write(msglist);
543 }
544
545 private MacAddress getRouterMacAddr() {
546 // TODO: need to read network config : RouterIp
547 return MacAddress.of("00:00:00:00:00:00");
548 }
549
550 private TableId getTableId(TableType tableType) {
551 switch (tableType) {
552 case IP:
553 return TableId.of(ipv4UnicastTableId);
554 case MPLS:
555 return TableId.of(mplsTableId);
556 case ACL:
557 return TableId.of(aclTableId);
558 default: {
559 log.error("Table type {} is not supported in the driver", tableType);
560 return TableId.NONE;
561 }
562 }
563 }
564
565 private void sendHandshakeBarrier() throws IOException {
566 long xid = getNextTransactionId();
567 barrierXidToWaitFor = xid;
568 OFBarrierRequest br = factory()
569 .buildBarrierRequest()
570 .setXid(xid)
571 .build();
572 write(br);
573 }
Ray Milkey51365f32015-02-04 11:24:22 -0800574}