Open Kilda Java Documentation
SwitchManager.java
Go to the documentation of this file.
1 /* Copyright 2017 Telstra Open Source
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package org.openkilda.floodlight.switchmanager;
17 
18 import static java.util.Arrays.asList;
19 import static java.util.Collections.emptyList;
20 import static java.util.Collections.singletonList;
22 import static org.openkilda.messaging.Utils.ETH_TYPE;
23 import static org.projectfloodlight.openflow.protocol.OFVersion.OF_12;
24 import static org.projectfloodlight.openflow.protocol.OFVersion.OF_13;
25 import static org.projectfloodlight.openflow.protocol.OFVersion.OF_15;
26 
42 
43 import com.google.common.collect.ImmutableSet;
44 import com.google.common.util.concurrent.ListenableFuture;
45 import net.floodlightcontroller.core.FloodlightContext;
46 import net.floodlightcontroller.core.IFloodlightProviderService;
47 import net.floodlightcontroller.core.IOFMessageListener;
48 import net.floodlightcontroller.core.IOFSwitch;
49 import net.floodlightcontroller.core.PortChangeType;
50 import net.floodlightcontroller.core.internal.IOFSwitchService;
51 import net.floodlightcontroller.core.module.FloodlightModuleContext;
52 import net.floodlightcontroller.core.module.FloodlightModuleException;
53 import net.floodlightcontroller.core.module.IFloodlightModule;
54 import net.floodlightcontroller.core.module.IFloodlightService;
55 import net.floodlightcontroller.restserver.IRestApiService;
56 import net.floodlightcontroller.util.FlowModUtils;
57 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
58 import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
59 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
60 import org.projectfloodlight.openflow.protocol.OFFactory;
61 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
62 import org.projectfloodlight.openflow.protocol.OFFlowMod;
63 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
64 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
65 import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
66 import org.projectfloodlight.openflow.protocol.OFLegacyMeterBandDrop;
67 import org.projectfloodlight.openflow.protocol.OFLegacyMeterFlags;
68 import org.projectfloodlight.openflow.protocol.OFLegacyMeterMod;
69 import org.projectfloodlight.openflow.protocol.OFLegacyMeterModCommand;
70 import org.projectfloodlight.openflow.protocol.OFMessage;
71 import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
72 import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsRequest;
73 import org.projectfloodlight.openflow.protocol.OFMeterFlags;
74 import org.projectfloodlight.openflow.protocol.OFMeterMod;
75 import org.projectfloodlight.openflow.protocol.OFMeterModCommand;
76 import org.projectfloodlight.openflow.protocol.OFPortConfig;
77 import org.projectfloodlight.openflow.protocol.OFPortDesc;
78 import org.projectfloodlight.openflow.protocol.OFPortMod;
79 import org.projectfloodlight.openflow.protocol.OFType;
80 import org.projectfloodlight.openflow.protocol.action.OFAction;
81 import org.projectfloodlight.openflow.protocol.action.OFActions;
82 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
83 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
84 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionMeter;
85 import org.projectfloodlight.openflow.protocol.match.Match;
86 import org.projectfloodlight.openflow.protocol.match.Match.Builder;
87 import org.projectfloodlight.openflow.protocol.match.MatchField;
88 import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop;
89 import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
90 import org.projectfloodlight.openflow.types.DatapathId;
91 import org.projectfloodlight.openflow.types.EthType;
92 import org.projectfloodlight.openflow.types.MacAddress;
93 import org.projectfloodlight.openflow.types.OFBufferId;
94 import org.projectfloodlight.openflow.types.OFGroup;
95 import org.projectfloodlight.openflow.types.OFPort;
96 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
97 import org.projectfloodlight.openflow.types.U64;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
100 
101 import java.util.ArrayList;
102 import java.util.Arrays;
103 import java.util.Collection;
104 import java.util.HashMap;
105 import java.util.HashSet;
106 import java.util.List;
107 import java.util.Map;
108 import java.util.Optional;
109 import java.util.Set;
110 import java.util.concurrent.ExecutionException;
111 import java.util.concurrent.Future;
112 import java.util.concurrent.TimeUnit;
113 import java.util.concurrent.TimeoutException;
114 import java.util.stream.Collectors;
115 import java.util.stream.Stream;
116 
120 public class SwitchManager implements IFloodlightModule, IFloodlightService, ISwitchManager, IOFMessageListener {
121  private static final Logger logger = LoggerFactory.getLogger(SwitchManager.class);
122 
127  public static final long FLOW_COOKIE_MASK = 0x7FFFFFFFFFFFFFFFL;
128 
129  static final U64 NON_SYSTEM_MASK = U64.of(0x80000000FFFFFFFFL);
130 
131  public static final int VERIFICATION_RULE_PRIORITY = FlowModUtils.PRIORITY_MAX - 1000;
132  public static final int DEFAULT_RULE_PRIORITY = FlowModUtils.PRIORITY_HIGH;
133 
134 
135  // This is invalid VID mask - it cut of highest bit that indicate presence of VLAN tag on package. But valid mask
136  // 0x1FFF lead to rule reject during install attempt on accton based switches.
137  private static short OF10_VLAN_MASK = 0x0FFF;
138 
139  private IFloodlightProviderService floodlightProvider;
140  private IOFSwitchService ofSwitchService;
141  private IRestApiService restApiService;
142  private KafkaMessageProducer kafkaProducer;
143  private ConnectModeRequest.Mode connectMode;
144 
145  private String topoDiscoTopic;
146 
147  // IFloodlightModule Methods
148 
156  private static OFInstructionApplyActions buildInstructionApplyActions(OFFactory ofFactory,
157  List<OFAction> actionList) {
158  return ofFactory.instructions().applyActions(actionList).createBuilder().build();
159  }
160 
168  private static OFAction legacyMeterAction(final OFFactory ofFactory, final long meterId) {
169  return ofFactory.actions().buildNiciraLegacyMeter().setMeterId(meterId).build();
170  }
171 
175  @Override
176  public Collection<Class<? extends IFloodlightService>> getModuleServices() {
177  Collection<Class<? extends IFloodlightService>> services = new ArrayList<>();
178  services.add(ISwitchManager.class);
179  return services;
180  }
181 
185  @Override
186  public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
187  Map<Class<? extends IFloodlightService>, IFloodlightService> map = new HashMap<>();
188  map.put(ISwitchManager.class, this);
189  return map;
190  }
191 
195  @Override
196  public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
197  Collection<Class<? extends IFloodlightService>> services = new ArrayList<>(3);
198  services.add(IFloodlightProviderService.class);
199  services.add(IOFSwitchService.class);
200  services.add(IRestApiService.class);
201  return services;
202  }
203 
207  @Override
208  public void init(FloodlightModuleContext context) throws FloodlightModuleException {
209  floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
210  ofSwitchService = context.getServiceImpl(IOFSwitchService.class);
211  restApiService = context.getServiceImpl(IRestApiService.class);
212  kafkaProducer = context.getServiceImpl(KafkaMessageProducer.class);
213 
214  ConfigurationProvider provider = ConfigurationProvider.of(context, this);
215  KafkaTopicsConfig topicsConfig = provider.getConfiguration(KafkaTopicsConfig.class);
216  topoDiscoTopic = topicsConfig.getTopoDiscoTopic();
217 
218  String connectModeProperty = provider.getConfiguration(SwitchManagerConfig.class).getConnectMode();
219  try {
220  connectMode = ConnectModeRequest.Mode.valueOf(connectModeProperty);
221  } catch (Exception e) {
222  logger.error("CONFIG EXCEPTION: connect-mode could not be set to {}, defaulting to AUTO",
223  connectModeProperty);
224  connectMode = ConnectModeRequest.Mode.AUTO;
225  }
226  // TODO: Ensure Kafka Topics are created..
227  }
228 
232  @Override
233  public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
234  logger.info("Starting " + SwitchEventCollector.class.getCanonicalName());
235  restApiService.addRestletRoutable(new SwitchManagerWebRoutable());
236  floodlightProvider.addOFMessageListener(OFType.ERROR, this);
237  }
238 
242  @Override
244  public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
245  logger.debug("OF_ERROR: {}", msg);
246  // TODO: track xid for flow id
247  if (OFType.ERROR.equals(msg.getType())) {
248  ErrorMessage error = new ErrorMessage(
249  new ErrorData(ErrorType.INTERNAL_ERROR, ((OFErrorMsg) msg).getErrType().toString(), null),
250  System.currentTimeMillis(), CorrelationContext.getId(), Destination.WFM_TRANSACTION);
251  // TODO: Most/all commands are flow related, but not all. 'kilda.flow' might
252  // not be the best place to send a generic error.
253  kafkaProducer.postMessage("kilda.flow", error);
254  }
255  return Command.CONTINUE;
256  }
257 
261  @Override
262  public String getName() {
263  return "KildaSwitchManager";
264  }
265 
266  // ISwitchManager Methods
267 
271  @Override
272  public boolean isCallbackOrderingPrereq(OFType type, String name) {
273  logger.trace("isCallbackOrderingPrereq for {} : {}", type, name);
274  return false;
275  }
276 
280  @Override
281  public boolean isCallbackOrderingPostreq(OFType type, String name) {
282  logger.trace("isCallbackOrderingPostreq for {} : {}", type, name);
283  return false;
284  }
285 
289  @Override
291  if (mode != null) {
292  this.connectMode = mode;
293  }
294  return this.connectMode;
295  }
296 
300  @Override
301  public void installDefaultRules(final DatapathId dpid) throws SwitchOperationException {
302  installDropFlow(dpid);
303  installVerificationRule(dpid, true);
304  installVerificationRule(dpid, false);
305  }
306 
310  @Override
311  public long installIngressFlow(
312  final DatapathId dpid, final String flowId,
313  final Long cookie, final int inputPort, final int outputPort,
314  final int inputVlanId, final int transitVlanId,
315  final OutputVlanType outputVlanType, final long meterId) throws SwitchOperationException {
316  List<OFAction> actionList = new ArrayList<>();
317  IOFSwitch sw = lookupSwitch(dpid);
318  OFFactory ofFactory = sw.getOFFactory();
319 
320 
321  // build meter instruction
322  OFInstructionMeter meter = null;
323  if (meterId != 0L && !OVS_MANUFACTURER.equals(sw.getSwitchDescription().getManufacturerDescription())) {
324  if (ofFactory.getVersion().compareTo(OF_12) <= 0) {
325  actionList.add(legacyMeterAction(ofFactory, meterId));
326  } else if (ofFactory.getVersion().compareTo(OF_15) == 0) {
327  actionList.add(ofFactory.actions().buildMeter().setMeterId(meterId).build());
328  } else /* OF_13, OF_14 */ {
329  meter = ofFactory.instructions().buildMeter().setMeterId(meterId).build();
330  }
331  }
332 
333  // output action based on encap scheme
334  actionList.addAll(inputVlanTypeToOfActionList(ofFactory, transitVlanId, outputVlanType));
335 
336  // transmit packet from outgoing port
337  actionList.add(actionSetOutputPort(ofFactory, outputPort));
338 
339  // build instruction with action list
340  OFInstructionApplyActions actions = buildInstructionApplyActions(ofFactory, actionList);
341 
342  // build match by input port and input vlan id
343  Match match = matchFlow(ofFactory, inputPort, inputVlanId);
344 
345  // build FLOW_MOD command with meter
346  OFFlowMod flowMod = buildFlowMod(ofFactory, match, meter, actions,
348 
349  return pushFlow(sw, "--InstallIngressFlow--", flowMod);
350  }
351 
355  @Override
356  public long installEgressFlow(
357  final DatapathId dpid, String flowId, final Long cookie,
358  final int inputPort, final int outputPort,
359  final int transitVlanId, final int outputVlanId,
360  final OutputVlanType outputVlanType) throws SwitchOperationException {
361  List<OFAction> actionList = new ArrayList<>();
362  IOFSwitch sw = lookupSwitch(dpid);
363  OFFactory ofFactory = sw.getOFFactory();
364 
365  // build match by input port and transit vlan id
366  Match match = matchFlow(ofFactory, inputPort, transitVlanId);
367 
368  // output action based on encap scheme
369  actionList.addAll(outputVlanTypeToOfActionList(ofFactory, outputVlanId, outputVlanType));
370 
371  // transmit packet from outgoing port
372  actionList.add(actionSetOutputPort(ofFactory, outputPort));
373 
374  // build instruction with action list
375  OFInstructionApplyActions actions = buildInstructionApplyActions(ofFactory, actionList);
376 
377  // build FLOW_MOD command, no meter
378  OFFlowMod flowMod = buildFlowMod(ofFactory, match, null, actions,
380 
381  return pushFlow(sw, "--InstallEgressFlow--", flowMod);
382  }
383 
387  @Override
388  public long installTransitFlow(
389  final DatapathId dpid, final String flowId,
390  final Long cookie, final int inputPort, final int outputPort,
391  final int transitVlanId) throws SwitchOperationException {
392  List<OFAction> actionList = new ArrayList<>();
393  IOFSwitch sw = lookupSwitch(dpid);
394  OFFactory ofFactory = sw.getOFFactory();
395 
396  // build match by input port and transit vlan id
397  Match match = matchFlow(ofFactory, inputPort, transitVlanId);
398 
399  // transmit packet from outgoing port
400  actionList.add(actionSetOutputPort(ofFactory, outputPort));
401 
402  // build instruction with action list
403  OFInstructionApplyActions actions = buildInstructionApplyActions(ofFactory, actionList);
404 
405  // build FLOW_MOD command, no meter
406  OFFlowMod flowMod = buildFlowMod(ofFactory, match, null, actions,
408 
409  return pushFlow(sw, flowId, flowMod);
410  }
411 
415  @Override
416  public long installOneSwitchFlow(
417  final DatapathId dpid, final String flowId,
418  final Long cookie, final int inputPort,
419  final int outputPort, final int inputVlanId,
420  final int outputVlanId,
421  final OutputVlanType outputVlanType, final long meterId) throws SwitchOperationException {
422  // TODO: As per other locations, how different is this to IngressFlow? Why separate code path?
423  // As with any set of tests, the more we test the same code path, the better.
424  // Based on brief glance, this looks 90% the same as IngressFlow.
425 
426  List<OFAction> actionList = new ArrayList<>();
427  IOFSwitch sw = lookupSwitch(dpid);
428  OFFactory ofFactory = sw.getOFFactory();
429 
430 
431  // build meter instruction
432  OFInstructionMeter meter = null;
433  if (meterId != 0L && !OVS_MANUFACTURER.equals(sw.getSwitchDescription().getManufacturerDescription())) {
434  if (ofFactory.getVersion().compareTo(OF_12) <= 0) {
435  actionList.add(legacyMeterAction(ofFactory, meterId));
436  } else if (ofFactory.getVersion().compareTo(OF_15) == 0) {
437  actionList.add(ofFactory.actions().buildMeter().setMeterId(meterId).build());
438  } else /* OF_13, OF_14 */ {
439  meter = ofFactory.instructions().buildMeter().setMeterId(meterId).build();
440  }
441  }
442 
443  // output action based on encap scheme
444  actionList.addAll(pushSchemeOutputVlanTypeToOfActionList(ofFactory, outputVlanId, outputVlanType));
445  // transmit packet from outgoing port
446  actionList.add(actionSetOutputPort(ofFactory, outputPort));
447 
448  // build instruction with action list
449  OFInstructionApplyActions actions = buildInstructionApplyActions(ofFactory, actionList);
450 
451  // build match by input port and transit vlan id
452  Match match = matchFlow(ofFactory, inputPort, inputVlanId);
453 
454  // build FLOW_MOD command with meter
455  OFFlowMod flowMod = buildFlowMod(ofFactory, match, meter, actions,
457 
458  pushFlow(sw, flowId, flowMod);
459 
460  return flowMod.getXid();
461  }
462 
466  @Override
467  public List<OFFlowStatsEntry> dumpFlowTable(final DatapathId dpid) {
468  List<OFFlowStatsEntry> entries = new ArrayList<>();
469  IOFSwitch sw = ofSwitchService.getSwitch(dpid);
470  if (sw == null) {
471  throw new IllegalArgumentException(String.format("Switch %s was not found", dpid));
472  }
473 
474  OFFactory ofFactory = sw.getOFFactory();
475  OFFlowStatsRequest flowRequest = ofFactory.buildFlowStatsRequest()
476  .setOutGroup(OFGroup.ANY)
477  .setCookieMask(U64.ZERO)
478  .build();
479 
480  try {
481  Future<List<OFFlowStatsReply>> future = sw.writeStatsRequest(flowRequest);
482  List<OFFlowStatsReply> values = future.get(10, TimeUnit.SECONDS);
483  if (values != null) {
484  entries = values.stream()
485  .map(OFFlowStatsReply::getEntries)
486  .flatMap(List::stream)
487  .collect(Collectors.toList());
488  }
489  } catch (ExecutionException | InterruptedException | TimeoutException e) {
490  logger.error("Could not get flow stats for {}.", dpid, e);
491  }
492 
493  return entries;
494  }
495 
499  @Override
500  public OFMeterConfigStatsReply dumpMeters(final DatapathId dpid) throws SwitchOperationException {
501  OFMeterConfigStatsReply values = null;
502  IOFSwitch sw = lookupSwitch(dpid);
503  if (sw == null) {
504  throw new IllegalArgumentException(String.format("Switch %s was not found", dpid));
505  }
506 
507  OFFactory ofFactory = sw.getOFFactory();
508  if (ofFactory.getVersion().compareTo(OF_13) < 0) {
510  String.format("Dumping of meters is not supported on the requested switch %s.", dpid));
511  }
512 
513  OFMeterConfigStatsRequest meterRequest = ofFactory.buildMeterConfigStatsRequest()
514  .setMeterId(0xffffffff)
515  .build();
516 
517  try {
518  ListenableFuture<OFMeterConfigStatsReply> future = sw.writeRequest(meterRequest);
519  values = future.get(5, TimeUnit.SECONDS);
520  } catch (ExecutionException | InterruptedException | TimeoutException e) {
521  logger.error("Could not get meter config stats for {}.", dpid, e);
522  }
523 
524  return values;
525  }
526 
530  @Override
531  public long installMeter(final DatapathId dpid, final long bandwidth, final long burstSize, final long meterId)
532  throws SwitchOperationException {
533  if (meterId == 0) {
534  logger.info("skip installing meter {} on switch {} width bandwidth {}", meterId, dpid, bandwidth);
535  return 0L;
536  }
537 
538  IOFSwitch sw = lookupSwitch(dpid);
539 
540  if (OVS_MANUFACTURER.equals(sw.getSwitchDescription().getManufacturerDescription())) {
541  logger.info("skip installing meter {} on OVS switch {} width bandwidth {}", meterId, dpid, bandwidth);
542  return 0L;
543  }
544 
545  long meterCommandXid;
546 
547  if (sw.getOFFactory().getVersion().compareTo(OF_12) <= 0) {
548  meterCommandXid = installLegacyMeter(sw, dpid, bandwidth, burstSize, meterId);
549  } else {
550  meterCommandXid = buildAndinstallMeter(sw, dpid, bandwidth, burstSize, meterId);
551  }
552 
553  // All cases when we're installing meters require that we wait until the command is processed and the meter is
554  // installed.
555  sendBarrierRequest(sw);
556 
557  return meterCommandXid;
558  }
559 
560  @Override
561  public Map<DatapathId, IOFSwitch> getAllSwitchMap() {
562  return ofSwitchService.getAllSwitchMap();
563  }
564 
565  // Utility Methods
566 
570  @Override
571  public long deleteMeter(final DatapathId dpid, final long meterId)
572  throws SwitchOperationException {
573  if (meterId == 0) {
574  logger.info("skip deleting meter {} from switch {}", meterId, dpid);
575  return 0L;
576  }
577 
578  IOFSwitch sw = lookupSwitch(dpid);
579  if (OVS_MANUFACTURER.equals(sw.getSwitchDescription().getManufacturerDescription())) {
580  logger.info("skip deleting meter {} from OVS switch {}", meterId, dpid);
581  return 0L;
582  }
583 
584  if (sw.getOFFactory().getVersion().compareTo(OF_12) <= 0) {
585  return deleteLegacyMeter(sw, dpid, meterId);
586  } else {
587  return buildAndDeleteMeter(sw, dpid, meterId);
588  }
589  }
590 
591 
592 
593  @Override
594  public List<Long> deleteAllNonDefaultRules(final DatapathId dpid) throws SwitchOperationException {
595  List<OFFlowStatsEntry> flowStatsBefore = dumpFlowTable(dpid);
596  IOFSwitch sw = lookupSwitch(dpid);
597  OFFactory ofFactory = sw.getOFFactory();
598 
599  Set<Long> removedRules = new HashSet<>();
600 
601  for (OFFlowStatsEntry flowStatsEntry : flowStatsBefore) {
602  long flowCookie = flowStatsEntry.getCookie().getValue();
603  if (flowCookie != DROP_RULE_COOKIE
604  && flowCookie != VERIFICATION_BROADCAST_RULE_COOKIE
605  && flowCookie != VERIFICATION_UNICAST_RULE_COOKIE) {
606  OFFlowDelete flowDelete = ofFactory.buildFlowDelete()
607  .setCookie(U64.of(flowCookie))
608  .setCookieMask(U64.NO_MASK)
609  .build();
610  pushFlow(sw, "--DeleteFlow--", flowDelete);
611 
612  logger.info("Rule with cookie {} is to be removed from switch {}.", flowCookie, dpid);
613 
614  removedRules.add(flowCookie);
615  }
616  }
617 
618  // Wait for OFFlowDelete to be processed.
619  sendBarrierRequest(sw);
620 
621  List<OFFlowStatsEntry> flowStatsAfter = dumpFlowTable(dpid);
622  Set<Long> cookiesAfter = flowStatsAfter.stream()
623  .map(entry -> entry.getCookie().getValue())
624  .collect(Collectors.toSet());
625 
626  flowStatsBefore.stream()
627  .map(entry -> entry.getCookie().getValue())
628  .filter(cookie -> !cookiesAfter.contains(cookie))
629  .filter(cookie -> !removedRules.contains(cookie))
630  .forEach(cookie -> {
631  logger.warn("Rule with cookie {} has been removed although not requested. Switch {}.", cookie,
632  dpid);
633  removedRules.add(cookie);
634  });
635 
636  cookiesAfter.stream()
637  .filter(removedRules::contains)
638  .forEach(cookie -> {
639  logger.warn("Rule with cookie {} was requested to be removed, but it still remains. Switch {}.",
640  cookie, dpid);
641  removedRules.remove(cookie);
642  });
643 
644  return new ArrayList<>(removedRules);
645  }
646 
647 
648  @Override
649  public List<Long> deleteRulesByCriteria(final DatapathId dpid, DeleteRulesCriteria... criteria)
650  throws SwitchOperationException {
651  List<OFFlowStatsEntry> flowStatsBefore = dumpFlowTable(dpid);
652 
653  IOFSwitch sw = lookupSwitch(dpid);
654  OFFactory ofFactory = sw.getOFFactory();
655 
656  for (DeleteRulesCriteria criteriaEntry : criteria) {
657  OFFlowDelete dropFlowDelete = buildFlowDeleteByCriteria(ofFactory, criteriaEntry);
658 
659  logger.info("Rules by criteria {} are to be removed from switch {}.", criteria, dpid);
660 
661  pushFlow(sw, "--DeleteFlow--", dropFlowDelete);
662  }
663 
664  // Wait for OFFlowDelete to be processed.
665  sendBarrierRequest(sw);
666 
667  List<OFFlowStatsEntry> flowStatsAfter = dumpFlowTable(dpid);
668  Set<Long> cookiesAfter = flowStatsAfter.stream()
669  .map(entry -> entry.getCookie().getValue())
670  .collect(Collectors.toSet());
671 
672  return flowStatsBefore.stream()
673  .map(entry -> entry.getCookie().getValue())
674  .filter(cookie -> !cookiesAfter.contains(cookie))
675  .peek(cookie -> logger.info("Rule with cookie {} has been removed from switch {}.", cookie, dpid))
676  .collect(Collectors.toList());
677  }
678 
679  @Override
680  public List<Long> deleteDefaultRules(final DatapathId dpid) throws SwitchOperationException {
681  return deleteRulesWithCookie(dpid, DROP_RULE_COOKIE, VERIFICATION_BROADCAST_RULE_COOKIE,
683  }
684 
688  @Override
689  public void installVerificationRule(final DatapathId dpid, final boolean isBroadcast)
690  throws SwitchOperationException {
691  IOFSwitch sw = lookupSwitch(dpid);
692  OFFactory ofFactory = sw.getOFFactory();
693 
694  // Don't install the unicast for OpenFlow 1.2 doesn't work properly
695  if (!isBroadcast) {
696  if (ofFactory.getVersion().compareTo(OF_12) > 0) {
697  logger.debug("installing unicast verification match for {}", dpid);
698  } else {
699  logger.debug("not installing unicast verification match for {}", dpid);
700  return;
701  }
702  }
703 
704  logger.debug("installing verification rule for {}", dpid);
705 
706  Match match = matchVerification(sw, isBroadcast);
707  ArrayList<OFAction> actionList = new ArrayList<>(2);
708  actionList.add(actionSendToController(sw));
709  actionList.add(actionSetDstMac(sw, dpidToMac(sw)));
710  OFInstructionApplyActions instructionApplyActions = ofFactory.instructions()
711  .applyActions(actionList).createBuilder().build();
712  final long cookie = isBroadcast ? VERIFICATION_BROADCAST_RULE_COOKIE : VERIFICATION_UNICAST_RULE_COOKIE;
713  OFFlowMod flowMod = buildFlowMod(ofFactory, match, null, instructionApplyActions,
715  String flowname = (isBroadcast) ? "Broadcast" : "Unicast";
716  flowname += "--VerificationFlow--" + dpid.toString();
717  pushFlow(sw, flowname, flowMod);
718  }
719 
730  @Override
731  public void installDropFlowCustom(final DatapathId dpid, String dstMac, String dstMask,
732  final long cookie, final int priority) throws SwitchOperationException {
733  IOFSwitch sw = lookupSwitch(dpid);
734  OFFactory ofFactory = sw.getOFFactory();
735 
736  Match match = simpleDstMatch(ofFactory, dstMac, dstMask);
737  OFFlowMod flowMod = buildFlowMod(ofFactory, match, null, null, cookie, priority);
738  String flowName = "--CustomDropRule--" + dpid.toString();
739  pushFlow(sw, flowName, flowMod);
740  }
741 
742 
746  @Override
747  public void installDropFlow(final DatapathId dpid) throws SwitchOperationException {
748  // TODO: leverage installDropFlowCustom
749  IOFSwitch sw = lookupSwitch(dpid);
750  OFFactory ofFactory = sw.getOFFactory();
751 
752  if (ofFactory.getVersion() == OF_12) {
753  logger.debug("Skip installation of drop flow for switch {}", dpid);
754  } else {
755  logger.debug("Installing drop flow for switch {}", dpid);
756  OFFlowMod flowMod = buildFlowMod(ofFactory, null, null, null, DROP_RULE_COOKIE, 1);
757  String flowName = "--DropRule--" + dpid.toString();
758  pushFlow(sw, flowName, flowMod);
759  }
760  }
761 
762  private long buildAndinstallMeter(final IOFSwitch sw, final DatapathId dpid, final long bandwidth,
763  final long burstSize, final long meterId)
764  throws OFInstallException {
765  logger.debug("installing meter {} on switch {} width bandwidth {}", meterId, dpid, bandwidth);
766 
767  Set<OFMeterFlags> flags = new HashSet<>(asList(OFMeterFlags.KBPS, OFMeterFlags.BURST));
768  OFFactory ofFactory = sw.getOFFactory();
769 
770  OFMeterBandDrop.Builder bandBuilder = ofFactory.meterBands()
771  .buildDrop()
772  .setRate(bandwidth)
773  .setBurstSize(burstSize);
774 
775  OFMeterMod.Builder meterModBuilder = ofFactory.buildMeterMod()
776  .setMeterId(meterId)
777  .setCommand(OFMeterModCommand.ADD)
778  .setFlags(flags);
779 
780  if (sw.getOFFactory().getVersion().compareTo(OF_13) > 0) {
781  meterModBuilder.setBands(singletonList(bandBuilder.build()));
782  } else {
783  meterModBuilder.setMeters(singletonList(bandBuilder.build()));
784  }
785 
786  OFMeterMod meterMod = meterModBuilder.build();
787 
788  return pushFlow(sw, "--InstallMeter--", meterMod);
789  }
790 
791  private long installLegacyMeter(
792  final IOFSwitch sw, final DatapathId dpid,
793  final long bandwidth, final long burstSize, final long meterId)
794  throws OFInstallException {
795  logger.debug("installing legacy meter {} on OVS switch {} width bandwidth {}", meterId, dpid, bandwidth);
796 
797  Set<OFLegacyMeterFlags> flags = new HashSet<>(asList(OFLegacyMeterFlags.KBPS, OFLegacyMeterFlags.BURST));
798  OFFactory ofFactory = sw.getOFFactory();
799 
800  OFLegacyMeterBandDrop.Builder bandBuilder = ofFactory.legacyMeterBandDrop(bandwidth, burstSize).createBuilder();
801 
802  OFLegacyMeterMod meterMod = ofFactory.buildLegacyMeterMod()
803  .setMeterId(meterId)
804  .setCommand(OFLegacyMeterModCommand.ADD)
805  .setMeters(singletonList(bandBuilder.build()))
806  .setFlags(flags)
807  .build();
808 
809  return pushFlow(sw, "--InstallMeter", meterMod);
810  }
811 
812  private long buildAndDeleteMeter(IOFSwitch sw, final DatapathId dpid, final long meterId)
813  throws OFInstallException {
814  logger.debug("deleting meter {} from switch {}", meterId, dpid);
815 
816  OFFactory ofFactory = sw.getOFFactory();
817 
818  OFMeterMod.Builder meterDeleteBuilder = ofFactory.buildMeterMod()
819  .setMeterId(meterId)
820  .setCommand(OFMeterModCommand.DELETE);
821 
822  if (sw.getOFFactory().getVersion().compareTo(OF_13) > 0) {
823  meterDeleteBuilder.setBands(emptyList());
824  } else {
825  meterDeleteBuilder.setMeters(emptyList());
826  }
827 
828  OFMeterMod meterDelete = meterDeleteBuilder.build();
829 
830  return pushFlow(sw, "--DeleteMeter--", meterDelete);
831  }
832 
833  private long deleteLegacyMeter(final IOFSwitch sw, final DatapathId dpid, final long meterId)
834  throws OFInstallException {
835  logger.debug("deleting legacy meter {} from switch {}", meterId, dpid);
836 
837  OFFactory ofFactory = sw.getOFFactory();
838 
839  OFLegacyMeterMod meterDelete = ofFactory.buildLegacyMeterMod()
840  .setMeterId(meterId)
841  .setMeters(emptyList())
842  .setCommand(OFLegacyMeterModCommand.DELETE)
843  .build();
844 
845  return pushFlow(sw, "--DeleteMeter--", meterDelete);
846  }
847 
848  private OFFlowDelete buildFlowDeleteByCriteria(OFFactory ofFactory, DeleteRulesCriteria criteria) {
849  OFFlowDelete.Builder builder = ofFactory.buildFlowDelete();
850  if (criteria.getCookie() != null) {
851  builder.setCookie(U64.of(criteria.getCookie()));
852  builder.setCookieMask(U64.NO_MASK);
853  }
854 
855  if (criteria.getInPort() != null) {
856  // Match either In Port or both Port & Vlan criteria.
857  Match match = matchFlow(ofFactory, criteria.getInPort(),
858  Optional.ofNullable(criteria.getInVlan()).orElse(0));
859  builder.setMatch(match);
860 
861  } else if (criteria.getInVlan() != null) {
862  // Match In Vlan criterion if In Port is not specified
863  Match.Builder matchBuilder = ofFactory.buildMatch();
864  matchVlan(ofFactory, matchBuilder, criteria.getInVlan());
865  builder.setMatch(matchBuilder.build());
866  }
867 
868  if (criteria.getPriority() != null) {
869  // Match Priority criterion.
870  builder.setPriority(criteria.getPriority());
871  }
872 
873  if (criteria.getOutPort() != null) {
874  // Match only Out Vlan criterion.
875  builder.setOutPort(OFPort.of(criteria.getOutPort()));
876  }
877 
878  return builder.build();
879  }
880 
881  private OFBarrierReply sendBarrierRequest(IOFSwitch sw) {
882  OFFactory ofFactory = sw.getOFFactory();
883  OFBarrierRequest barrierRequest = ofFactory.buildBarrierRequest().build();
884 
885  OFBarrierReply result = null;
886  try {
887  ListenableFuture<OFBarrierReply> future = sw.writeRequest(barrierRequest);
888  result = future.get(10, TimeUnit.SECONDS);
889  } catch (ExecutionException | InterruptedException | TimeoutException e) {
890  logger.error("Could not get a barrier reply for {}.", sw.getId(), e);
891  }
892  return result;
893  }
894 
895 
896  private List<Long> deleteRulesWithCookie(final DatapathId dpid, Long... cookiesToRemove)
897  throws SwitchOperationException {
898  DeleteRulesCriteria[] criteria = Stream.of(cookiesToRemove)
899  .map(cookie -> DeleteRulesCriteria.builder().cookie(cookie).build())
900  .toArray(DeleteRulesCriteria[]::new);
901 
902  return deleteRulesByCriteria(dpid, criteria);
903  }
904 
915  private Match matchFlow(final OFFactory ofFactory, final int inputPort, final int vlanId) {
916  Match.Builder mb = ofFactory.buildMatch();
917  //
918  // Extra emphasis: vlan of 0 means match on port on not VLAN.
919  //
920  mb.setExact(MatchField.IN_PORT, OFPort.of(inputPort));
921  if (vlanId > 0) {
922  matchVlan(ofFactory, mb, vlanId);
923  }
924 
925  return mb.build();
926  }
927 
928  private void matchVlan(final OFFactory ofFactory, final Match.Builder matchBuilder, final int vlanId) {
929  if (0 <= OF_12.compareTo(ofFactory.getVersion())) {
930  matchBuilder.setMasked(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(vlanId),
931  OFVlanVidMatch.ofRawVid(OF10_VLAN_MASK));
932  } else {
933  matchBuilder.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(vlanId));
934  }
935  }
936 
945  private List<OFAction> replaceSchemeOutputVlanTypeToOfActionList(OFFactory ofFactory, int outputVlanId,
946  OutputVlanType outputVlanType) {
947  List<OFAction> actionList;
948 
949  switch (outputVlanType) {
950  case PUSH:
951  case REPLACE:
952  actionList = singletonList(actionReplaceVlan(ofFactory, outputVlanId));
953  break;
954  case POP:
955  case NONE:
956  actionList = singletonList(actionPopVlan(ofFactory));
957  break;
958  default:
959  actionList = emptyList();
960  logger.error("Unknown OutputVlanType: " + outputVlanType);
961  }
962 
963  return actionList;
964  }
965 
974  private List<OFAction> pushSchemeOutputVlanTypeToOfActionList(OFFactory ofFactory, int outputVlanId,
975  OutputVlanType outputVlanType) {
976  List<OFAction> actionList = new ArrayList<>(2);
977 
978  switch (outputVlanType) {
979  case PUSH: // No VLAN on packet so push a new one
980  actionList.add(actionPushVlan(ofFactory, ETH_TYPE));
981  actionList.add(actionReplaceVlan(ofFactory, outputVlanId));
982  break;
983  case REPLACE: // VLAN on packet but needs to be replaced
984  actionList.add(actionReplaceVlan(ofFactory, outputVlanId));
985  break;
986  case POP: // VLAN on packet, so remove it
987  // TODO: can i do this? pop two vlan's back to back...
988  actionList.add(actionPopVlan(ofFactory));
989  break;
990  case NONE:
991  break;
992  default:
993  logger.error("Unknown OutputVlanType: " + outputVlanType);
994  }
995 
996  return actionList;
997  }
998 
1007  private List<OFAction> outputVlanTypeToOfActionList(OFFactory ofFactory, int outputVlanId,
1008  OutputVlanType outputVlanType) {
1009  return replaceSchemeOutputVlanTypeToOfActionList(ofFactory, outputVlanId, outputVlanType);
1010  }
1011 
1019  private List<OFAction> inputVlanTypeToOfActionList(OFFactory ofFactory, int transitVlanId,
1020  OutputVlanType outputVlanType) {
1021  List<OFAction> actionList = new ArrayList<>(3);
1022  if (OutputVlanType.PUSH.equals(outputVlanType) || OutputVlanType.NONE.equals(outputVlanType)) {
1023  actionList.add(actionPushVlan(ofFactory, ETH_TYPE));
1024  }
1025  actionList.add(actionReplaceVlan(ofFactory, transitVlanId));
1026  return actionList;
1027  }
1028 
1036  private OFAction actionSetOutputPort(final OFFactory ofFactory, final int outputPort) {
1037  OFActions actions = ofFactory.actions();
1038  return actions.buildOutput().setMaxLen(0xFFFFFFFF).setPort(OFPort.of(outputPort)).build();
1039  }
1040 
1048  private OFAction actionReplaceVlan(final OFFactory factory, final int newVlan) {
1049  OFOxms oxms = factory.oxms();
1050  OFActions actions = factory.actions();
1051 
1052  if (OF_12.compareTo(factory.getVersion()) == 0) {
1053  return actions.buildSetField().setField(oxms.buildVlanVid()
1054  .setValue(OFVlanVidMatch.ofRawVid((short) newVlan))
1055  .build()).build();
1056  } else {
1057  return actions.buildSetField().setField(oxms.buildVlanVid()
1058  .setValue(OFVlanVidMatch.ofVlan(newVlan))
1059  .build()).build();
1060  }
1061  }
1062 
1070  private OFAction actionPushVlan(final OFFactory ofFactory, final int etherType) {
1071  OFActions actions = ofFactory.actions();
1072  return actions.buildPushVlan().setEthertype(EthType.of(etherType)).build();
1073  }
1074 
1081  private OFAction actionPopVlan(final OFFactory ofFactory) {
1082  OFActions actions = ofFactory.actions();
1083  return actions.popVlan();
1084  }
1085 
1097  private OFFlowMod buildFlowMod(final OFFactory ofFactory, final Match match, final OFInstructionMeter meter,
1098  final OFInstructionApplyActions actions, final long cookie, final int priority) {
1099  OFFlowMod.Builder fmb = ofFactory.buildFlowAdd();
1100  fmb.setIdleTimeout(FlowModUtils.INFINITE_TIMEOUT);
1101  fmb.setHardTimeout(FlowModUtils.INFINITE_TIMEOUT);
1102  fmb.setBufferId(OFBufferId.NO_BUFFER);
1103  fmb.setCookie(U64.of(cookie));
1104  fmb.setPriority(priority);
1105  List<OFInstruction> instructions = new ArrayList<>(2);
1106 
1107  // If no meter then no bandwidth limit
1108  if (meter != null) {
1109  instructions.add(meter);
1110  }
1111 
1112  // If no instruction then Drops packet
1113  if (actions != null) {
1114  instructions.add(actions);
1115  }
1116 
1117  // If no then match everything
1118  if (match != null) {
1119  fmb.setMatch(match);
1120  }
1121 
1122  return fmb.setInstructions(instructions).build();
1123  }
1124 
1131  private MacAddress dpidToMac(final IOFSwitch sw) {
1132  return MacAddress.of(Arrays.copyOfRange(sw.getId().getBytes(), 2, 8));
1133  }
1134 
1142  private Match matchVerification(final IOFSwitch sw, final boolean isBroadcast) {
1143  MacAddress dstMac = isBroadcast ? MacAddress.of(VERIFICATION_BCAST_PACKET_DST) : dpidToMac(sw);
1144  Builder builder = sw.getOFFactory().buildMatch();
1145  builder.setMasked(MatchField.ETH_DST, dstMac, MacAddress.NO_MASK);
1146  return builder.build();
1147  }
1148 
1149 
1150 
1157  private OFAction actionSendToController(final IOFSwitch sw) {
1158  OFActions actions = sw.getOFFactory().actions();
1159  return actions.buildOutput().setMaxLen(0xFFffFFff).setPort(OFPort.CONTROLLER)
1160  .build();
1161  }
1162 
1170  private OFAction actionSetDstMac(final IOFSwitch sw, final MacAddress macAddress) {
1171  OFOxms oxms = sw.getOFFactory().oxms();
1172  OFActions actions = sw.getOFFactory().actions();
1173  return actions.buildSetField()
1174  .setField(oxms.buildEthDst().setValue(macAddress).build()).build();
1175  }
1176 
1177 
1178 
1188  private Match simpleDstMatch(OFFactory ofFactory, String dstMac, String dstMask) {
1189  Match match = null;
1190  if (dstMac != null && dstMask != null && dstMac.length() > 0 && dstMask.length() > 0) {
1191  Builder builder = ofFactory.buildMatch();
1192  builder.setMasked(MatchField.ETH_DST, MacAddress.of(dstMac), MacAddress.NO_MASK);
1193  match = builder.build();
1194  }
1195  return match;
1196  }
1197 
1198 
1199 
1209  private long pushFlow(final IOFSwitch sw, final String flowId, final OFMessage flowMod) throws OFInstallException {
1210  logger.info("installing {} flow: {}", flowId, flowMod);
1211 
1212  if (! sw.write(flowMod)) {
1213  throw new OFInstallException(sw.getId(), flowMod);
1214  }
1215 
1216  return flowMod.getXid();
1217  }
1218 
1226  private IOFSwitch lookupSwitch(DatapathId dpId) throws SwitchOperationException {
1227  IOFSwitch swInfo = ofSwitchService.getSwitch(dpId);
1228  if (swInfo == null) {
1229  throw new SwitchOperationException(dpId, String.format("Switch %s was not found", dpId));
1230  }
1231  return swInfo;
1232  }
1233 
1234 
1238  private static final class SafeData {
1239  // Any switch rule with a priority less than this will be ignored
1240  static final int PRIORITY_IGNORE_THRESHOLD = 100;
1241  private static final int window = 5;
1242  // Used to filter out rules with low packet counts .. only test rules with more packets than this
1243  private static final int PACKET_COUNT_MIN = 5;
1244 
1245  DatapathId dpid;
1246 
1251  List<Long> timestamps;
1252  Map<Long, List<Long>> ruleByteCounts; // counter per cookie per timestamp
1253  Map<Long, List<Long>> rulePktCounts; // counter per cookie per timestamp
1254  // Stages - 0 = not started; 1 = applied; 2 = okay; 3 = removed (too many errors)
1255  int dropRuleStage;
1256  int broadcastRuleStage;
1257  int unicastRuleStage;
1258 
1259  void consumeData(long timestamp, List<OFFlowStatsEntry> flowEntries) {
1260  timestamps.add(timestamp);
1261 
1262  for (OFFlowStatsEntry flowStatsEntry : flowEntries) {
1263  if (flowStatsEntry.getPriority() <= PRIORITY_IGNORE_THRESHOLD) {
1264  continue;
1265  }
1266 
1267  long flowCookie = flowStatsEntry.getCookie().getValue();
1268  if (!ruleByteCounts.containsKey(flowCookie)) {
1269  ruleByteCounts.put(flowCookie, new ArrayList<>());
1270  rulePktCounts.put(flowCookie, new ArrayList<>());
1271  }
1272  ruleByteCounts.get(flowCookie).add(flowStatsEntry.getByteCount().getValue());
1273  rulePktCounts.get(flowCookie).add(flowStatsEntry.getPacketCount().getValue());
1274  }
1275  }
1276 
1277  // collect 2 windows per stage .. apply rule after first window
1278  boolean shouldApplyRule(int stage) {
1279  return timestamps.size() == ((stage - 1) * 2 + 1) * window;
1280  }
1281 
1282  boolean shouldTestRule(int stage) {
1283  return timestamps.size() == ((stage - 1) * 2 + 2) * window;
1284  }
1285 
1286  // Starting with just the effect on packet count
1287  List<Integer> getRuleEffect(int stage) {
1288  int start = (stage - 1) * 2;
1289  int middle = start + 1;
1290  int end = middle + 1;
1291  int goodCounts = 0;
1292  int badCounts = 0;
1293 
1294  for (List<Long> packets : rulePktCounts.values()) {
1295  long packetsBefore = packets.get(middle) - packets.get(start);
1296  // We shouldn't start at the middle .. since we wouldn't have applied the rule yet.
1297  // So, start at middle+1 .. that is the first data point after applying the rule.
1298  long packetsAfter = packets.get(end) - packets.get(middle + 1);
1299  boolean ruleHadNoEffect = (packetsBefore > PACKET_COUNT_MIN && packetsAfter > 0);
1300  if (ruleHadNoEffect) {
1301  goodCounts++;
1302  } else {
1303  badCounts++;
1304  }
1305  }
1306  return asList(badCounts, goodCounts);
1307  }
1308 
1309  boolean isRuleOkay(List<Integer> ruleEffect) {
1310  // Initial algorithm: if any rule was sending data and then stopped, then applied rule
1311  // is not okay.
1312  // The first array element has the count of "bad_counts" .. ie packet count before rule
1313  // wasn't zero, but was zero after.
1314  int badCounts = ruleEffect.get(0);
1315  return badCounts == 0;
1316  }
1317  }
1318 
1319  private Map<DatapathId, SafeData> safeSwitches = new HashMap<>();
1320  private long lastRun = 0L;
1321 
1325  @Override
1326  public void startSafeMode(final DatapathId dpid) {
1327  // Don't create a new object if one already exists .. ie, don't restart the process of
1328  // installing base rules.
1329  if (!safeSwitches.containsKey(dpid)) {
1330  SafeData safeData = safeSwitches.put(dpid, new SafeData());
1331  safeData.dpid = dpid;
1332  }
1333  }
1334 
1338  @Override
1339  public void stopSafeMode(final DatapathId dpid) {
1340  safeSwitches.remove(dpid);
1341  }
1342 
1343  private static final long tick_length = 1000;
1344  private static final boolean BROADCAST = true;
1345  private static final int DROP_STAGE = 1;
1346  private static final int BROADCAST_STAGE = 2;
1347  private static final int UNICAST_STAGE = 3;
1348  // NB: The logic in safeModeTick relies on these RULE_* numbers. Mostly, it relies on the
1349  // IS_GOOD and NO_GOOD being greater that TESTED. And in reality, TESTED is just the lower
1350  // of IS_GOOD and NO_GOOD.
1351  private static final int RULE_APPLIED = 1;
1352  private static final int RULE_TESTED = 2;
1353  private static final int RULE_IS_GOOD = 2;
1354  private static final int RULE_NO_GOOD = 3;
1355 
1356  @Override
1357  public void safeModeTick() {
1358  // this may be called sporadically, so we'll need to measure the time between calls ..
1359  long time = System.currentTimeMillis();
1360  if (time - lastRun < tick_length) {
1361  return;
1362  }
1363 
1364  lastRun = time;
1365 
1366  Collection<SafeData> values = safeSwitches.values();
1367  for (SafeData safeData : values) {
1368  // Grab switch rule stats .. X pre and post .. X for 0, X for 1 .. make a decision.
1369  try {
1370  safeData.consumeData(time, dumpFlowTable(safeData.dpid));
1371  int datapoints = safeData.timestamps.size();
1372 
1373  if (safeData.dropRuleStage < RULE_TESTED) {
1374 
1375  logger.debug("SAFE MODE: Collected Data during Drop Rule Stage for '{}' ", safeData.dpid);
1376  if (safeData.shouldApplyRule(DROP_STAGE)) {
1377  logger.info("SAFE MODE: APPLY Drop Rule for '{}' ", safeData.dpid);
1378  safeData.dropRuleStage = RULE_APPLIED;
1379  installDropFlow(safeData.dpid);
1380  } else if (safeData.shouldTestRule(DROP_STAGE)) {
1381  List<Integer> ruleEffect = safeData.getRuleEffect(DROP_STAGE);
1382  if (safeData.isRuleOkay(ruleEffect)) {
1383  logger.info("SAFE MODE: Drop Rule is GOOD for '{}' ", safeData.dpid);
1384  safeData.dropRuleStage = RULE_IS_GOOD;
1385  } else {
1386  logger.warn("SAFE MODE: Drop Rule is BAD for '{}'. "
1387  + "Good Packet Count: {}. Bad Packet Count: {} ",
1388  safeData.dpid, ruleEffect.get(0), ruleEffect.get(1));
1389  safeData.dropRuleStage = RULE_NO_GOOD;
1390  deleteRulesWithCookie(safeData.dpid, ISwitchManager.DROP_RULE_COOKIE);
1391  }
1392  }
1393 
1394  } else if (safeData.broadcastRuleStage < RULE_TESTED) {
1395 
1396  logger.debug("SAFE MODE: Collected Data during Broadcast Verification Rule "
1397  + "Stage for '{}' ", safeData.dpid);
1398  if (safeData.shouldApplyRule(BROADCAST_STAGE)) {
1399  logger.info("SAFE MODE: APPLY Broadcast Verification Rule for '{}' ", safeData.dpid);
1400  safeData.broadcastRuleStage = RULE_APPLIED;
1401  installVerificationRule(safeData.dpid, BROADCAST);
1402  } else if (safeData.shouldTestRule(BROADCAST_STAGE)) {
1403  List<Integer> ruleEffect = safeData.getRuleEffect(BROADCAST_STAGE);
1404  if (safeData.isRuleOkay(ruleEffect)) {
1405  logger.info("SAFE MODE: Broadcast Verification Rule is GOOD for '{}' ", safeData.dpid);
1406  safeData.broadcastRuleStage = RULE_IS_GOOD;
1407  } else {
1408  logger.warn("SAFE MODE: Broadcast Verification Rule is BAD for '{}'. "
1409  + "Good Packet Count: {}. Bad Packet Count: {} ",
1410  safeData.dpid, ruleEffect.get(0), ruleEffect.get(1));
1411  safeData.broadcastRuleStage = RULE_NO_GOOD;
1412  deleteRulesWithCookie(safeData.dpid, ISwitchManager.VERIFICATION_BROADCAST_RULE_COOKIE);
1413  }
1414  }
1415  } else if (safeData.unicastRuleStage < RULE_TESTED) {
1416 
1417  // TODO: make this smarter and advance the unicast if unicast not applied.
1418  logger.debug("SAFE MODE: Collected Data during Unicast Verification Rule Stage "
1419  + "for '{}' ", safeData.dpid);
1420  if (safeData.shouldApplyRule(UNICAST_STAGE)) {
1421  logger.info("SAFE MODE: APPLY Unicast Verification Rule for '{}' ", safeData.dpid);
1422  safeData.unicastRuleStage = RULE_APPLIED;
1423  installVerificationRule(safeData.dpid, !BROADCAST);
1424  } else if (safeData.shouldTestRule(UNICAST_STAGE)) {
1425  List<Integer> ruleEffect = safeData.getRuleEffect(UNICAST_STAGE);
1426  if (safeData.isRuleOkay(ruleEffect)) {
1427  logger.info("SAFE MODE: Unicast Verification Rule is GOOD for '{}' ", safeData.dpid);
1428  safeData.unicastRuleStage = RULE_IS_GOOD;
1429  } else {
1430  logger.warn("SAFE MODE: Unicast Verification Rule is BAD for '{}'. "
1431  + "Good Packet Count: {}. Bad Packet Count: {} ",
1432  safeData.dpid, ruleEffect.get(0), ruleEffect.get(1));
1433  safeData.unicastRuleStage = RULE_NO_GOOD;
1434  deleteRulesWithCookie(safeData.dpid, ISwitchManager.VERIFICATION_UNICAST_RULE_COOKIE);
1435  }
1436  }
1437 
1438  } else {
1439  // once done with installing rules, we need to notify kilda that the switch is up
1440  // and that ports up.
1441  logger.info("SAFE MODE: COMPLETED base rules for '{}' ", safeData.dpid);
1442  IOFSwitch sw = lookupSwitch(safeData.dpid);
1443  sendSwitchActivate(sw);
1444  sendPortUpEvents(sw);
1445  // WE ARE DONE!! Remove ourselves from the list.
1446  values.remove(safeData); // will be reflected in safeSwitches
1447  }
1448  } catch (SwitchOperationException e) {
1449  logger.error("Error while switch {} was in safe mode. Removing switch from safe "
1450  + "mode and NOT SENDING ACTIVATION. \nERROR: {}", safeData.dpid, e);
1451  values.remove(safeData);
1452  }
1453  }
1454  }
1455 
1456 
1460  @Override
1461  public void sendSwitchActivate(final IOFSwitch sw) throws SwitchOperationException {
1463  kafkaProducer.postMessage(topoDiscoTopic, message);
1464  }
1465 
1469  @Override
1470  public void sendPortUpEvents(final IOFSwitch sw) throws SwitchOperationException {
1471  if (sw.getEnabledPortNumbers() != null) {
1472  for (OFPort p : sw.getEnabledPortNumbers()) {
1474  kafkaProducer.postMessage(topoDiscoTopic,
1476  PortChangeType.UP));
1477  }
1478  }
1479  }
1480  }
1481 
1482  // TODO(surabujin): this method can/should be moved to the RecordHandler level
1483  @Override
1484  public void configurePort(DatapathId dpId, int portNumber, Boolean portAdminDown) throws SwitchOperationException {
1485  IOFSwitch sw = lookupSwitch(dpId);
1486 
1487  boolean makeChanges = false;
1488  if (portAdminDown != null) {
1489  makeChanges = true;
1490  updatePortStatus(sw, portNumber, portAdminDown);
1491  }
1492 
1493  if (makeChanges) {
1494  sendBarrierRequest(sw);
1495  }
1496  }
1497 
1498  private void updatePortStatus(IOFSwitch sw, int portNumber, boolean isAdminDown) throws SwitchOperationException {
1499  Set<OFPortConfig> config = new HashSet<>(1);
1500  if (isAdminDown) {
1501  config.add(OFPortConfig.PORT_DOWN);
1502  }
1503 
1504  Set<OFPortConfig> portMask = ImmutableSet.of(OFPortConfig.PORT_DOWN);
1505 
1506  final OFFactory ofFactory = sw.getOFFactory();
1507  OFPortMod ofPortMod = ofFactory.buildPortMod()
1508  .setPortNo(OFPort.of(portNumber))
1509  // switch can argue against empty HWAddress (BAD_HW_ADDR) :(
1510  .setHwAddr(getPortHwAddress(sw, portNumber))
1511  .setConfig(config)
1512  .setMask(portMask)
1513  .build();
1514 
1515  if (!sw.write(ofPortMod)) {
1516  throw new SwitchOperationException(sw.getId(),
1517  String.format("Unable to update port configuration: %s", ofPortMod));
1518  }
1519 
1520  logger.debug("Successfully updated port status {}", ofPortMod);
1521  }
1522 
1523  private MacAddress getPortHwAddress(IOFSwitch sw, int portNumber) {
1524  OFPortDesc portDesc = sw.getPort(OFPort.of(portNumber));
1525  return portDesc.getHwAddr();
1526  }
1527 }
Map< Class<? extends IFloodlightService >, IFloodlightService > getServiceImpls()
long installMeter(final DatapathId dpid, final long bandwidth, final long burstSize, final long meterId)
boolean isCallbackOrderingPrereq(OFType type, String name)
Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx)
long installEgressFlow(final DatapathId dpid, String flowId, final Long cookie, final int inputPort, final int outputPort, final int transitVlanId, final int outputVlanId, final OutputVlanType outputVlanType)
void init(FloodlightModuleContext context)
long installIngressFlow(final DatapathId dpid, final String flowId, final Long cookie, final int inputPort, final int outputPort, final int inputVlanId, final int transitVlanId, final OutputVlanType outputVlanType, final long meterId)
static Message buildSwitchMessage(final IOFSwitch sw, final SwitchState eventType)
static final int ETH_TYPE
Definition: Utils.java:85
static Message buildPortMessage(final DatapathId switchId, final OFPort port, final PortChangeType type)
ConnectModeRequest.Mode connectMode(final ConnectModeRequest.Mode mode)
List< Long > deleteRulesByCriteria(final DatapathId dpid, DeleteRulesCriteria... criteria)
name
Definition: setup.py:24
Collection< Class<? extends IFloodlightService > > getModuleServices()
List< Long > deleteDefaultRules(final DatapathId dpid)
List< Long > deleteAllNonDefaultRules(final DatapathId dpid)
boolean isCallbackOrderingPostreq(OFType type, String name)
List< OFFlowStatsEntry > dumpFlowTable(final DatapathId dpid)
long installTransitFlow(final DatapathId dpid, final String flowId, final Long cookie, final int inputPort, final int outputPort, final int transitVlanId)
static ConfigurationProvider of(FloodlightModuleContext moduleContext, IFloodlightModule module)
list result
Definition: plan-d.py:72
void installVerificationRule(final DatapathId dpid, final boolean isBroadcast)
Collection< Class<? extends IFloodlightService > > getModuleDependencies()
OFMeterConfigStatsReply dumpMeters(final DatapathId dpid)
void startUp(FloodlightModuleContext context)
void postMessage(final String topic, final Message message)
def build()
Definition: plan-e.py:73
void configurePort(DatapathId dpId, int portNumber, Boolean portAdminDown)
long installOneSwitchFlow(final DatapathId dpid, final String flowId, final Long cookie, final int inputPort, final int outputPort, final int inputVlanId, final int outputVlanId, final OutputVlanType outputVlanType, final long meterId)
long deleteMeter(final DatapathId dpid, final long meterId)
net
Definition: plan-b.py:46
void installDropFlowCustom(final DatapathId dpid, String dstMac, String dstMask, final long cookie, final int priority)