3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.samples.simpleforwarding.internal;
12 import java.net.InetAddress;
13 import java.util.ArrayList;
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.LinkedList;
19 import java.util.List;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.Timer;
25 import java.util.TimerTask;
27 import org.opendaylight.controller.clustering.services.CacheConfigException;
28 import org.opendaylight.controller.clustering.services.CacheExistException;
29 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
30 import org.opendaylight.controller.clustering.services.IClusterServices;
31 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
32 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
33 import org.opendaylight.controller.hosttracker.IfIptoHost;
34 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
35 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
36 import org.opendaylight.controller.sal.action.Action;
37 import org.opendaylight.controller.sal.action.Output;
38 import org.opendaylight.controller.sal.action.PopVlan;
39 import org.opendaylight.controller.sal.action.SetDlDst;
40 import org.opendaylight.controller.sal.action.SetVlanId;
41 import org.opendaylight.controller.sal.core.Edge;
42 import org.opendaylight.controller.sal.core.Node;
43 import org.opendaylight.controller.sal.core.NodeConnector;
44 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
45 import org.opendaylight.controller.sal.core.Path;
46 import org.opendaylight.controller.sal.core.Property;
47 import org.opendaylight.controller.sal.core.State;
48 import org.opendaylight.controller.sal.core.UpdateType;
49 import org.opendaylight.controller.sal.flowprogrammer.Flow;
50 import org.opendaylight.controller.sal.match.Match;
51 import org.opendaylight.controller.sal.match.MatchType;
52 import org.opendaylight.controller.sal.packet.Ethernet;
53 import org.opendaylight.controller.sal.packet.IDataPacketService;
54 import org.opendaylight.controller.sal.packet.IListenDataPacket;
55 import org.opendaylight.controller.sal.packet.IPv4;
56 import org.opendaylight.controller.sal.packet.Packet;
57 import org.opendaylight.controller.sal.packet.PacketResult;
58 import org.opendaylight.controller.sal.packet.RawPacket;
59 import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
60 import org.opendaylight.controller.sal.routing.IRouting;
61 import org.opendaylight.controller.sal.utils.EtherTypes;
62 import org.opendaylight.controller.sal.utils.NetUtils;
63 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
64 import org.opendaylight.controller.sal.utils.Status;
65 import org.opendaylight.controller.samples.simpleforwarding.HostNodePair;
66 import org.opendaylight.controller.switchmanager.IInventoryListener;
67 import org.opendaylight.controller.switchmanager.ISwitchManager;
68 import org.opendaylight.controller.topologymanager.ITopologyManager;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
73 * This class implements basic L3 forwarding within the managed devices.
74 * Forwarding is only done within configured subnets.</br>
76 * The basic flow is that the module listens for new hosts from the
77 * {@link org.opendaylight.controller.hosttracker.IfIptoHost HostTracker}
78 * service and on discovering a new host it first calls
79 * <tt>preparePerHostRules()</tt> to create a set of new rules that must be
80 * installed in the network. This is done by repeatedly calling
81 * <tt>updatePerHostRuleInSW()</tt> for each switch in the network. Then it
82 * installs those rules using <tt>installPerHostRules()</tt>.
84 public class SimpleForwardingImpl implements IfNewHostNotify,
85 IListenRoutingUpdates, IInventoryListener, IListenDataPacket {
86 private static Logger log = LoggerFactory.getLogger(SimpleForwardingImpl.class);
87 private static short DEFAULT_IPSWITCH_PRIORITY = 1;
88 static final String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
89 private IfIptoHost hostTracker;
90 private IForwardingRulesManager frm;
91 private ITopologyManager topologyManager;
92 private IRouting routing;
95 * The set of all forwarding rules: (host) -> (switch -> flowmod). Note that
96 * the host includes an attachment point and that while the switch appears
97 * to be a switch's port, in actuality it is a special port which just
98 * represents the switch.
100 private ConcurrentMap<HostNodePair, HashMap<NodeConnector, FlowEntry>> rulesDB;
101 private Map<Node, List<FlowEntry>> tobePrunedPos = new HashMap<Node, List<FlowEntry>>();
102 private IClusterContainerServices clusterContainerService = null;
103 private ISwitchManager switchManager;
104 private IDataPacketService dataPacketService;
107 * Ip packets that are punted may not have their destination known by hostTracker at the time it
108 * is presented to SimpleForwardingImpl. Instead of dropping the packet, we will keep it around
109 * for a 'little' while, to accommodate any transients. See bug 590 for more details.
111 private class PendingPacketData {
112 private final static byte MAX_AGE = 2;
114 public final IPv4 pkt;
115 public final NodeConnector incomingNodeConnector;
118 public PendingPacketData(IPv4 pkt, NodeConnector incomingNodeConnector) {
120 this.incomingNodeConnector = incomingNodeConnector;
123 boolean bumpAgeAndCheckIfTooOld() { return ++age > MAX_AGE; }
125 private static final int MAX_PENDING_PACKET_DESTINATIONS = 64;
126 private ConcurrentMap<InetAddress, PendingPacketData> pendingPacketDestinations;
127 private Timer pendingPacketsAgerTimer;
129 private class PendingPacketsAgerTimerHandler extends TimerTask {
132 if (pendingPacketDestinations == null) {
136 Iterator<ConcurrentMap.Entry<InetAddress, PendingPacketData>> iterator =
137 pendingPacketDestinations.entrySet().iterator();
138 while (iterator.hasNext()) {
139 ConcurrentHashMap.Entry<InetAddress, PendingPacketData> entry = iterator.next();
140 InetAddress dIP = entry.getKey();
141 PendingPacketData pendingPacketData = entry.getValue();
143 if (pendingPacketData.bumpAgeAndCheckIfTooOld()) {
144 iterator.remove(); // safe to remove while iterating...
145 log.debug("Pending packet for {} has been aged out", dIP);
147 /** Replace the entry for a key only if currently mapped to some value.
148 * This will protect the concurrent map against a race where this thread
149 * would be re-adding an entry that just got taken out.
151 pendingPacketDestinations.replace(dIP, pendingPacketData);
154 } catch (IllegalStateException e) {
155 log.debug("IllegalStateException Received by PendingPacketsAgerTimerHandler from: {}",
162 * Add punted packet to pendingPackets
164 private void addToPendingPackets(InetAddress dIP, IPv4 pkt, NodeConnector incomingNodeConnector) {
165 if (pendingPacketDestinations.size() >= MAX_PENDING_PACKET_DESTINATIONS) {
166 log.info("Will not pend packet for {}: Too many destinations", dIP);
170 /** TODO: The current implementation allows for up to 1 pending packet per InetAddress.
171 * This limitation is done for sake of simplicity. A potential enhancement could be to use a
172 * ConcurrentMultiMap instead of ConcurrentMap.
174 if (pendingPacketDestinations.containsKey(dIP)) {
175 log.trace("Will not pend packet for {}: Already have a packet pending", dIP);
179 PendingPacketData pendingPacketData = new PendingPacketData(pkt, incomingNodeConnector);
180 pendingPacketDestinations.put(dIP, pendingPacketData);
181 log.debug("Pending packet for {}", dIP);
185 * Send punted packet to given destination. This is invoked when there is a certain level of
186 * hope that the destination is known by hostTracker.
188 private void sendPendingPacket(InetAddress dIP) {
189 pendingPacketDestinations.get(dIP);
190 PendingPacketData pendingPacketData = pendingPacketDestinations.get(dIP);
191 if (pendingPacketData != null) {
192 handlePuntedIPPacket(pendingPacketData.pkt, pendingPacketData.incomingNodeConnector, false);
193 log.trace("Packet for {} is no longer pending", dIP);
194 pendingPacketDestinations.remove(dIP);
199 * Return codes from the programming of the perHost rules in HW
201 public enum RulesProgrammingReturnCode {
202 SUCCESS, FAILED_FEW_SWITCHES, FAILED_ALL_SWITCHES, FAILED_WRONG_PARAMS
204 public void setDataPacketService(IDataPacketService s) {
205 log.debug("Setting dataPacketService");
206 this.dataPacketService = s;
209 public void unsetDataPacketService(IDataPacketService s) {
210 if (this.dataPacketService == s) {
211 this.dataPacketService = null;
215 public void setRouting(IRouting routing) {
216 log.debug("Setting routing");
217 this.routing = routing;
220 public void unsetRouting(IRouting routing) {
221 if (this.routing == routing) {
226 public void setTopologyManager(ITopologyManager topologyManager) {
227 log.debug("Setting topologyManager");
228 this.topologyManager = topologyManager;
231 public void unsetTopologyManager(ITopologyManager topologyManager) {
232 if (this.topologyManager == topologyManager) {
233 this.topologyManager = null;
237 public void setHostTracker(IfIptoHost hostTracker) {
238 log.debug("Setting HostTracker");
239 this.hostTracker = hostTracker;
242 public void setForwardingRulesManager(
243 IForwardingRulesManager forwardingRulesManager) {
244 log.debug("Setting ForwardingRulesManager");
245 this.frm = forwardingRulesManager;
248 public void unsetHostTracker(IfIptoHost hostTracker) {
249 if (this.hostTracker == hostTracker) {
250 this.hostTracker = null;
254 public void unsetForwardingRulesManager(
255 IForwardingRulesManager forwardingRulesManager) {
256 if (this.frm == forwardingRulesManager) {
262 * Function called when the bundle gets activated
265 public void startUp() {
268 nonClusterObjectCreate();
271 public void nonClusterObjectCreate() {
272 pendingPacketDestinations = new ConcurrentHashMap<InetAddress, PendingPacketData>();
274 /* Pending Packets Ager Timer to go off every 6 seconds to implement pending packet aging */
275 pendingPacketsAgerTimer = new Timer();
276 pendingPacketsAgerTimer.schedule(new PendingPacketsAgerTimerHandler(), 6000, 6000);
280 * Function called when the bundle gets stopped
283 public void shutDown() {
284 log.debug("Destroy all the host Rules given we are shutting down");
285 uninstallPerHostRules();
289 private void allocateCaches() {
290 if (this.clusterContainerService == null) {
291 log.trace("un-initialized clusterContainerService, can't create cache");
296 clusterContainerService.createCache(FORWARDING_RULES_CACHE_NAME,
297 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
298 } catch (CacheExistException cee) {
299 log.error("\nCache already exists - destroy and recreate if needed");
300 } catch (CacheConfigException cce) {
301 log.error("\nCache configuration invalid - check cache mode");
305 @SuppressWarnings({ "unchecked" })
306 private void retrieveCaches() {
307 if (this.clusterContainerService == null) {
308 log.trace("un-initialized clusterContainerService, can't retrieve cache");
312 rulesDB = (ConcurrentMap<HostNodePair, HashMap<NodeConnector, FlowEntry>>) clusterContainerService
313 .getCache(FORWARDING_RULES_CACHE_NAME);
314 if (rulesDB == null) {
315 log.error("\nFailed to get rulesDB handle");
319 private void destroyCaches() {
320 if (this.clusterContainerService == null) {
321 log.trace("un-initialized clusterContainerService, can't destroy cache");
325 clusterContainerService.destroyCache(FORWARDING_RULES_CACHE_NAME);
329 * Populates <tt>rulesDB</tt> with rules specifying how to reach
330 * <tt>host</tt> from <tt>currNode</tt> assuming that:
332 * <li><tt>host</tt> is attached to <tt>rootNode</tt>
333 * <li><tt>link</tt> is the next part of the path to reach <tt>rootNode</tt>
334 * from <tt>currNode</tt>
335 * <li><tt>rulesDB.get(key)</tt> represents the list of rules stored about
336 * <tt>host</tt> at <tt>currNode</tt>
340 * The host to be reached.
342 * The current node being processed.
344 * The node to be reached. Really, the switch which host is
347 * The link to follow from curNode to get to rootNode
349 * The key to store computed rules at in the rulesDB. For now,
350 * this is a {@link HostNodePair} of host and currNode.
352 private void updatePerHostRuleInSW(HostNodeConnector host, Node currNode,
353 Node rootNode, Edge link, HostNodePair key) {
355 // only the link parameter is optional
356 if (host == null || key == null || currNode == null || rootNode == null) {
360 Set<NodeConnector> ports = new HashSet<NodeConnector>();
361 // add a special port of type ALL and port 0 to represent the node
362 // without specifying a port on that node
363 ports.add(NodeConnectorCreator.createNodeConnector(
364 NodeConnectorIDType.ALL, NodeConnector.SPECIALNODECONNECTORID,
367 HashMap<NodeConnector, FlowEntry> pos = this.rulesDB.get(key);
369 pos = new HashMap<NodeConnector, FlowEntry>();
372 for (NodeConnector inPort : ports) {
373 // skip the port connected to the target host
374 if (currNode.equals(rootNode)
375 && (host.getnodeConnector().equals(inPort))) {
379 // remove the current rule, if any
380 FlowEntry removed_po = pos.remove(inPort);
381 Match match = new Match();
382 List<Action> actions = new ArrayList<Action>();
384 // IP destination based forwarding on /32 entries only!
385 match.setField(MatchType.DL_TYPE, EtherTypes.IPv4.shortValue());
386 match.setField(MatchType.NW_DST, host.getNetworkAddress());
388 /* Action for the policy is to forward to a port except on the
389 * switch where the host sits, which is to rewrite also the MAC
390 * and to forward on the Host port */
391 NodeConnector outPort = null;
393 if (currNode.equals(rootNode)) {
394 /* If we're at the root node, then rewrite the DL addr and
395 * possibly pop the VLAN tag. This allows for MAC rewriting
396 * in the core of the network assuming we can uniquely ID
397 * packets based on IP address. */
399 outPort = host.getnodeConnector();
400 if (inPort.equals(outPort)) {
401 // TODO: isn't this code skipped already by the above continue?
402 // skip the host port
405 actions.add(new SetDlDst(host.getDataLayerAddressBytes()));
407 if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
408 // Container mode: at the destination switch, we need to strip out the tag (VLAN)
409 actions.add(new PopVlan());
412 // currNode is NOT the rootNode, find the next hop and create a rule
414 outPort = link.getTailNodeConnector();
415 if (inPort.equals(outPort)) {
416 // skip the outgoing port
420 // If outPort is network link, add VLAN tag
421 if (topologyManager.isInternal(outPort)) {
422 log.debug("outPort {}/{} is internal uplink port",
425 log.debug("outPort {}/{} is host facing port",
429 if ((!inPort.getType().equals(NodeConnectorIDType.ALL))
430 && (topologyManager.isInternal(outPort))) {
431 Node nextNode = link.getHeadNodeConnector()
433 // TODO: Replace this with SAL equivalent
434 //short tag = container.getTag((Long)nextNode.getNodeID());
437 log.debug("adding SET_VLAN {} for traffic " +
438 "leaving {}/{} toward switch {}",
439 new Object[] { tag, currNode, outPort,
441 actions.add(new SetVlanId(tag));
443 log.debug("No tag assigned to switch {}", nextNode);
448 if (outPort != null) {
449 actions.add(new Output(outPort));
451 if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
452 // include input port in the flow match field
453 match.setField(MatchType.IN_PORT, inPort);
455 if (topologyManager.isInternal(inPort)) {
456 log.debug("inPort {}/{} is internal uplink port", currNode,
459 log.debug("inPort {}/{} is host facing port", currNode,
463 // for incoming network link; if the VLAN tag is defined, include it for incoming flow matching
464 if (topologyManager.isInternal(inPort)) {
465 // TODO: Replace this with SAL equivalent
466 //short tag = container.getTag((Long)currNode.getNodeID());
469 log.debug("adding MATCH VLAN {} for traffic entering" +
471 new Object[] {tag, currNode, inPort});
472 match.setField(MatchType.DL_VLAN, tag);
474 log.debug("No tag assigned to switch {}", currNode);
478 // Make sure the priority for IP switch entries is
479 // set to a level just above default drop entries
480 Flow flow = new Flow(match, actions);
481 flow.setIdleTimeout((short) 0);
482 flow.setHardTimeout((short) 0);
483 flow.setPriority(DEFAULT_IPSWITCH_PRIORITY);
485 String policyName = host.getNetworkAddress().getHostAddress()
487 String flowName = "["
488 + (!inPort.getType().equals(NodeConnectorIDType.ALL) ?
489 (inPort.getID()).toString()
491 + host.getNetworkAddress().getHostAddress() + "/32 on N "
493 FlowEntry po = new FlowEntry(policyName, flowName, flow, currNode);
495 /* Now save the rule in the DB rule, so on updates from topology we
498 this.rulesDB.put(key, pos);
499 if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
500 log.debug("Adding Match(inPort = {} , DIP = {})" +
501 " Action(outPort= {}) to node {}",
502 new Object[] { inPort,
503 host.getNetworkAddress().getHostAddress(),
505 if ((removed_po != null)
506 && (!po.getFlow().getMatch().equals(
507 removed_po.getFlow().getMatch()))) {
508 log.debug("Old Flow match: {}, New Flow match: {}",
509 removed_po.getFlow().getMatch(), po.getFlow()
511 addTobePrunedPolicy(currNode, removed_po, po);
515 log.debug("Adding policyMatch(DIP = {}) Action(outPort= {}) " +
516 "to node {}", new Object[] {
517 host.getNetworkAddress().getHostAddress(), outPort,
524 * Calculate the per-Host rules to be installed in the rulesDB,
525 * and that will later on be installed in HW, this routine will
526 * implicitly calculate the shortest path tree among the switch
527 * to which the host is attached and all the other switches in the
528 * network and will automatically create all the rules that allow
529 * a /32 destination IP based forwarding, as in traditional IP
532 * @param host Host for which we are going to prepare the rules in the rulesDB
534 * @return A set of switches touched by the calculation
536 private Set<Node> preparePerHostRules(HostNodeConnector host) {
541 //TODO: race condition! unset* functions can make these null.
542 if (this.routing == null) {
545 if (this.switchManager == null) {
548 if (this.rulesDB == null) {
552 Node rootNode = host.getnodeconnectorNode();
553 Set<Node> nodes = this.switchManager.getNodes();
554 Set<Node> switchesToProgram = new HashSet<Node>();
556 HashMap<NodeConnector, FlowEntry> pos;
559 // for all nodes in the system
560 for (Node node : nodes) {
561 if (node.equals(rootNode)) {
562 // We skip it because for the node with host attached
563 // we will process in every case even if there are no
568 Path res = this.routing.getRoute(node, rootNode);
569 if ((res == null) || ((links = res.getEdges()) == null)) {
570 // No route from node to rootNode can be found, back out any
571 // existing forwarding rules if they exist.
572 log.debug("NO Route/Path between SW[{}] --> SW[{}] cleaning " +
573 "potentially existing entries", node, rootNode);
574 key = new HostNodePair(host, node);
575 pos = this.rulesDB.get(key);
577 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
580 // uninstall any existing rules we put in the
581 // ForwardingRulesManager
582 this.frm.uninstallFlowEntry(po);
585 this.rulesDB.remove(key);
590 log.debug("Route between SW[{}] --> SW[{}]", node, rootNode);
591 Node currNode = node;
592 key = new HostNodePair(host, currNode);
594 // for each link in the route from here to there
595 for (Edge link : links) {
597 log.error("Could not retrieve the Link");
598 // TODO: should we keep going?
602 log.debug(link.toString());
604 // Index all the switches to be programmed
605 updatePerHostRuleInSW(host, currNode, rootNode, link, key);
606 if ((this.rulesDB.get(key)) != null) {
607 /* Calling updatePerHostRuleInSW() doesn't guarantee that
608 * rules will be added in currNode (e.g, there is only one
609 * link from currNode to rootNode This check makes sure that
610 * there are some rules in the rulesDB for the given key
611 * prior to adding switch to switchesToProgram
613 switchesToProgram.add(currNode);
615 currNode = link.getHeadNodeConnector().getNode();
616 key = new HostNodePair(host, currNode);
620 // This rule will be added no matter if any topology is built
621 // or no, it serve as a way to handle the case of a node with
622 // multiple hosts attached to it but not yet connected to the
624 switchesToProgram.add(rootNode);
625 updatePerHostRuleInSW(host, rootNode, rootNode, null,
626 new HostNodePair(host, rootNode));
628 // log.debug("Getting out at the end!");
629 return switchesToProgram;
633 * Calculate the per-Host rules to be installed in the rulesDB
634 * from a specific switch when a host facing port comes up.
635 * These rules will later on be installed in HW. This routine
636 * will implicitly calculate the shortest path from the switch
637 * where the port has come up to the switch where host is ,
638 * attached and will automatically create all the rules that allow
639 * a /32 destination IP based forwarding, as in traditional IP
642 * @param host Host for which we are going to prepare the rules in the rulesDB
643 * @param swId Switch ID where the port has come up
645 * @return A set of switches touched by the calculation
647 private Set<Node> preparePerHostPerSwitchRules(HostNodeConnector host,
648 Node node, NodeConnector swport) {
649 if ((host == null) || (node == null)) {
652 if (this.routing == null) {
655 if (this.switchManager == null) {
658 if (this.rulesDB == null) {
662 Node rootNode = host.getnodeconnectorNode();
663 Set<Node> switchesToProgram = new HashSet<Node>();
665 Map<NodeConnector, FlowEntry> pos;
669 Path res = this.routing.getRoute(node, rootNode);
670 if ((res == null) || ((links = res.getEdges()) == null)) {
671 // the routing service doesn't know how to get there from here
672 log.debug("NO Route/Path between SW[{}] --> SW[{}] cleaning " +
673 "potentially existing entries", node, rootNode);
674 key = new HostNodePair(host, node);
675 pos = this.rulesDB.get(key);
677 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
680 //Uninstall the policy
681 this.frm.uninstallFlowEntry(po);
684 this.rulesDB.remove(key);
689 log.debug("Route between SW[{}] --> SW[{}]", node, rootNode);
691 Node currNode = node;
692 key = new HostNodePair(host, currNode);
694 for (curr = 0; curr < links.size(); curr++) {
695 link = links.get(curr);
697 log.error("Could not retrieve the Link");
701 log.debug("Link [{}/{}] --> [{}/{}]", new Object[] {
702 currNode, link.getHeadNodeConnector(),
703 link.getHeadNodeConnector().getNode(),
704 link.getTailNodeConnector()});
706 // Index all the switches to be programmed
707 switchesToProgram.add(currNode);
708 updatePerHostRuleInSW(host, currNode, rootNode, link, key);
709 break; // come out of the loop for port up case, interested only in programming one switch
712 // This rule will be added no matter if any topology is built
713 // or no, it serve as a way to handle the case of a node with
714 // multiple hosts attached to it but not yet connected to the
716 // switchesToProgram.add(rootNode);
717 //updatePerHostRuleInSW(host, rootNode,
719 // new HostNodePair(host, rootNode),ports);
721 // log.debug("Getting out at the end!");
722 return switchesToProgram;
726 * Routine that fetch the per-Host rules from the rulesDB and
727 * install in HW, the one having the same match rules will be
728 * overwritten silently.
730 * @param host host for which we want to install in HW the per-Host rules
731 * @param switchesToProgram list of switches to be programmed in
732 * HW, usually are them all, but better to be explicit, that list
733 * may change with time based on new switch addition/removal
735 * @return a return code that convey the programming status of the HW
737 private RulesProgrammingReturnCode installPerHostRules(
738 HostNodeConnector host, Set<Node> switchesToProgram) {
739 RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
740 if (host == null || switchesToProgram == null) {
741 return RulesProgrammingReturnCode.FAILED_WRONG_PARAMS;
743 Map<NodeConnector, FlowEntry> pos;
745 // Now program every single switch
746 log.debug("Inside installPerHostRules");
747 for (Node swId : switchesToProgram) {
748 HostNodePair key = new HostNodePair(host, swId);
749 pos = this.rulesDB.get(key);
753 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
756 // Populate the Policy field now
757 Status poStatus = this.frm.modifyOrAddFlowEntry(po);
758 if (!poStatus.isSuccess()) {
759 log.error("Failed to install policy: "
760 + po.getGroupName() + " ("
761 + poStatus.getDescription() + ")");
763 retCode = RulesProgrammingReturnCode.FAILED_FEW_SWITCHES;
764 // Remove the entry from the DB, it was not installed!
765 this.rulesDB.remove(key);
767 log.debug("Successfully installed policy "
768 + po.toString() + " on switch " + swId);
771 log.error("Cannot find a policy for SW:({}) Host: ({})",
773 /* // Now dump every single rule */
774 /* for (HostNodePair dumpkey : this.rulesDB.keySet()) { */
775 /* po = this.rulesDB.get(dumpkey); */
776 /* log.debug("Dumping entry H{" + dumpkey.getHost() + "} S{" + dumpkey.getSwitchId() + "} = {" + (po == null ? "null policy" : po)); */
781 log.debug("Leaving installPerHostRules");
786 * Cleanup all the host rules for a given host
788 * @param host Host for which the host rules need to be cleaned
789 * up, the host could be null in that case it match all the hosts
791 * @return a return code that convey the programming status of the HW
793 private RulesProgrammingReturnCode uninstallPerHostRules(HostNodeConnector host) {
794 RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
795 Map<NodeConnector, FlowEntry> pos;
797 // Now program every single switch
798 for (HostNodePair key : this.rulesDB.keySet()) {
799 if (host == null || key.getHost().equals(host)) {
800 pos = this.rulesDB.get(key);
801 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
804 // Uninstall the policy
805 this.frm.uninstallFlowEntry(po);
808 this.rulesDB.remove(key);
815 * Cleanup all the host rules for a given node, triggered when the
816 * switch disconnects, so there is no reason for Hw cleanup
817 * because it's disconnected anyhow
818 * TBD - Revisit above stmt in light of CSCus88743
819 * @param targetNode Node for which we want to do cleanup
822 private void uninstallPerNodeRules(Node targetNode) {
823 //RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
824 Map<NodeConnector, FlowEntry> pos;
827 // Now program every single switch
828 for (HostNodePair key : this.rulesDB.keySet()) {
829 Node node = key.getNode();
830 if (targetNode == null || node.equals(targetNode)) {
831 log.debug("Work on {} host {}", node, key.getHost());
832 pos = this.rulesDB.get(key);
833 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
836 // Uninstall the policy
837 this.frm.uninstallFlowEntry(po);
840 log.debug("Remove {}", key);
841 this.rulesDB.remove(key);
847 * Cleanup all the host rules currently present in the rulesDB
849 * @return a return code that convey the programming status of the HW
851 private RulesProgrammingReturnCode uninstallPerHostRules() {
852 return uninstallPerHostRules(null);
856 public void recalculateDone() {
857 if (this.hostTracker == null) {
858 //Not yet ready to process all the updates
859 //TODO: we should make sure that this call is executed eventually
862 Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
863 for (HostNodeConnector host : allHosts) {
864 Set<Node> switches = preparePerHostRules(host);
865 if (switches != null) {
866 // This will refresh existing rules, by overwriting
868 installPerHostRules(host, switches);
869 pruneExcessRules(switches);
874 void addTobePrunedPolicy(Node swId, FlowEntry po, FlowEntry new_po) {
875 List<FlowEntry> pl = tobePrunedPos.get(swId);
877 pl = new LinkedList<FlowEntry>();
878 tobePrunedPos.put(swId, pl);
881 log.debug("Adding Pruned Policy for SwId: {}", swId);
882 log.debug("Old Policy: {}", po);
883 log.debug("New Policy: {}", new_po);
886 private void pruneExcessRules(Set<Node> switches) {
887 for (Node swId : switches) {
888 List<FlowEntry> pl = tobePrunedPos.get(swId);
890 log.debug("Policies for Switch: {} in the list to be deleted: {}", swId, pl);
891 Iterator<FlowEntry> plIter = pl.iterator();
892 //for (Policy po: pl) {
893 while (plIter.hasNext()) {
894 FlowEntry po = plIter.next();
895 log.error("Removing Policy, Switch: {} Policy: {}", swId, po);
896 this.frm.uninstallFlowEntry(po);
900 // tobePrunedPos.remove(swId);
905 * A Host facing port has come up in a container. Add rules on the switch where this
906 * port has come up for all the known hosts to the controller.
907 * @param swId switch id of the port where port came up
908 * @param swPort port which came up
910 private void updateRulesforHIFup(Node node, NodeConnector swPort) {
911 if (this.hostTracker == null) {
912 //Not yet ready to process all the updates
915 log.debug("Host Facing Port in a container came up, install the rules for all hosts from this port !");
916 Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
917 for (HostNodeConnector host : allHosts) {
918 if (node.equals(host.getnodeconnectorNode())) {
920 * This host resides behind the same switch and port for which a port up
921 * message is received. Ideally this should not happen, but if it does,
922 * don't program any rules for this host
926 Set<Node> switches = preparePerHostPerSwitchRules(host, node,
928 if (switches != null) {
929 // This will refresh existing rules, by overwriting
931 installPerHostRules(host, switches);
938 public void notifyHTClient(HostNodeConnector host) {
942 Set<Node> switches = preparePerHostRules(host);
943 if (switches != null) {
944 installPerHostRules(host, switches);
946 // Green light for sending pending packet to this host. Safe to call if there are none.
947 sendPendingPacket(host.getNetworkAddress());
952 public void notifyHTClientHostRemoved(HostNodeConnector host) {
956 uninstallPerHostRules(host);
960 public void notifyNode(Node node, UpdateType type,
961 Map<String, Property> propMap) {
968 log.debug("Node {} gone, doing a cleanup", node);
969 uninstallPerNodeRules(node);
977 public void notifyNodeConnector(NodeConnector nodeConnector,
978 UpdateType type, Map<String, Property> propMap) {
979 if (nodeConnector == null) {
991 State state = (State) propMap.get(State.StatePropName);
992 if ((state != null) && (state.getValue() == State.EDGE_UP)) {
1001 handleNodeConnectorStatusUp(nodeConnector);
1003 handleNodeConnectorStatusDown(nodeConnector);
1007 private void handleNodeConnectorStatusUp(NodeConnector nodeConnector) {
1008 if (topologyManager == null) {
1009 log.debug("topologyManager is not set yet");
1013 if (topologyManager.isInternal(nodeConnector)) {
1014 log.debug("{} is not a host facing link", nodeConnector);
1018 log.debug("{} is up", nodeConnector);
1019 updateRulesforHIFup(nodeConnector.getNode(), nodeConnector);
1022 private void handleNodeConnectorStatusDown(NodeConnector nodeConnector) {
1023 log.debug("{} is down", nodeConnector);
1026 void setClusterContainerService(IClusterContainerServices s) {
1027 log.debug("Cluster Service set");
1028 this.clusterContainerService = s;
1031 void unsetClusterContainerService(IClusterContainerServices s) {
1032 if (this.clusterContainerService == s) {
1033 log.debug("Cluster Service removed!");
1034 this.clusterContainerService = null;
1039 * Function called by the dependency manager when all the required
1040 * dependencies are satisfied
1048 * Function called by the dependency manager when at least one
1049 * dependency become unsatisfied or when the component is shutting
1050 * down because for example bundle is being stopped.
1057 * Function called by dependency manager after "init ()" is called
1058 * and after the services provided by the class are registered in
1059 * the service registry
1066 * Function called by the dependency manager before the services
1067 * exported by the component are unregistered, this will be
1068 * followed by a "destroy ()" calls
1072 pendingPacketsAgerTimer.cancel();
1073 pendingPacketDestinations.clear();
1076 public void setSwitchManager(ISwitchManager switchManager) {
1077 this.switchManager = switchManager;
1080 public void unsetSwitchManager(ISwitchManager switchManager) {
1081 if (this.switchManager == switchManager) {
1082 this.switchManager = null;
1087 public PacketResult receiveDataPacket(RawPacket inPkt) {
1088 if (inPkt == null) {
1089 return PacketResult.IGNORED;
1091 log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
1092 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
1093 if (formattedPak instanceof Ethernet) {
1094 Object nextPak = formattedPak.getPayload();
1095 if (nextPak instanceof IPv4) {
1096 log.trace("Handle punted IP packet: {}", formattedPak);
1097 handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector(), true);
1100 return PacketResult.IGNORED;
1104 private void handlePuntedIPPacket(IPv4 pkt, NodeConnector incomingNodeConnector, boolean allowAddPending) {
1105 InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
1106 if (dIP == null || hostTracker == null) {
1107 log.debug("Invalid param(s) in handlePuntedIPPacket.. DestIP: {}. hostTracker: {}", dIP, hostTracker);
1110 HostNodeConnector destHost = hostTracker.hostFind(dIP);
1112 * In cases when incoming and outgoing connectors are in the same node, there is no need
1113 * to verify that there is a route. Because of that, we will only need routing.getRoute()
1114 * if we know that src and dst nodes are different.
1116 if (destHost != null
1117 && (incomingNodeConnector.getNode().equals(destHost.getnodeconnectorNode()) ||
1119 routing.getRoute(incomingNodeConnector.getNode(), destHost.getnodeconnectorNode()) != null)) {
1121 log.trace("Host {} is at {}", dIP, destHost.getnodeConnector());
1123 // If SimpleForwarding is aware of this host, it will try to install
1124 // a path. Forward packet until it's done.
1125 if (dataPacketService != null) {
1128 * if we know where the host is and there's a path from where this
1129 * packet was punted to where the host is, then attempt best effort delivery to the host
1131 NodeConnector nc = destHost.getnodeConnector();
1132 log.trace("Forwarding punted IP received at {} to {}", incomingNodeConnector, nc);
1133 // re-encode the Ethernet packet (the parent of the IPv4 packet)
1134 RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
1135 rp.setOutgoingNodeConnector(nc);
1136 this.dataPacketService.transmitDataPacket(rp);
1138 } else if (allowAddPending) {
1139 // If we made it here, let's hang on to the punted packet, with hopes that its destination
1140 // will become available soon.
1141 addToPendingPackets(dIP, pkt, incomingNodeConnector);
1143 log.warn("Dropping punted IP packet received at {} to Host {}", incomingNodeConnector, dIP);