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