f6bf649cb2f526ea228591e9244e959d0084dac8
[onosfw.git] /
1 /*
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  */
16 package org.onosproject.segmentrouting;
17
18 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate;
21 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.apache.felix.scr.annotations.Service;
24 import org.onlab.packet.Ethernet;
25 import org.onlab.packet.MacAddress;
26 import org.onlab.packet.VlanId;
27 import org.onlab.packet.IPv4;
28 import org.onlab.packet.Ip4Address;
29 import org.onlab.packet.Ip4Prefix;
30 import org.onlab.packet.IpAddress;
31 import org.onlab.packet.IpPrefix;
32 import org.onlab.util.KryoNamespace;
33 import org.onosproject.core.ApplicationId;
34 import org.onosproject.core.CoreService;
35 import org.onosproject.event.Event;
36 import org.onosproject.net.ConnectPoint;
37 import org.onosproject.net.PortNumber;
38 import org.onosproject.net.config.ConfigFactory;
39 import org.onosproject.net.config.NetworkConfigEvent;
40 import org.onosproject.net.config.NetworkConfigRegistry;
41 import org.onosproject.net.config.NetworkConfigListener;
42 import org.onosproject.net.config.basics.SubjectFactories;
43 import org.onosproject.net.flow.DefaultTrafficSelector;
44 import org.onosproject.net.flow.DefaultTrafficTreatment;
45 import org.onosproject.net.flow.TrafficSelector;
46 import org.onosproject.net.flow.TrafficTreatment;
47 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
48 import org.onosproject.net.flowobjective.ForwardingObjective;
49 import org.onosproject.net.flowobjective.Objective;
50 import org.onosproject.net.flowobjective.ObjectiveContext;
51 import org.onosproject.net.flowobjective.ObjectiveError;
52 import org.onosproject.net.host.HostEvent;
53 import org.onosproject.net.host.HostListener;
54 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
55 import org.onosproject.segmentrouting.config.DeviceConfiguration;
56 import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
57 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
58 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
59 import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
60 import org.onosproject.mastership.MastershipService;
61 import org.onosproject.net.Device;
62 import org.onosproject.net.DeviceId;
63 import org.onosproject.net.Link;
64 import org.onosproject.net.Port;
65 import org.onosproject.net.device.DeviceEvent;
66 import org.onosproject.net.device.DeviceListener;
67 import org.onosproject.net.device.DeviceService;
68 import org.onosproject.net.flowobjective.FlowObjectiveService;
69 import org.onosproject.net.host.HostService;
70 import org.onosproject.net.intent.IntentService;
71 import org.onosproject.net.link.LinkEvent;
72 import org.onosproject.net.link.LinkListener;
73 import org.onosproject.net.link.LinkService;
74 import org.onosproject.net.packet.InboundPacket;
75 import org.onosproject.net.packet.PacketContext;
76 import org.onosproject.net.packet.PacketProcessor;
77 import org.onosproject.net.packet.PacketService;
78 import org.onosproject.net.topology.TopologyService;
79 import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey;
80 import org.onosproject.store.service.EventuallyConsistentMap;
81 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
82 import org.onosproject.store.service.StorageService;
83 import org.onosproject.store.service.WallClockTimestamp;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
86
87 import java.net.URI;
88 import java.util.Collections;
89 import java.util.HashSet;
90 import java.util.List;
91 import java.util.Map;
92 import java.util.Set;
93 import java.util.concurrent.ConcurrentHashMap;
94 import java.util.concurrent.ConcurrentLinkedQueue;
95 import java.util.concurrent.Executors;
96 import java.util.concurrent.ScheduledExecutorService;
97 import java.util.concurrent.ScheduledFuture;
98 import java.util.concurrent.TimeUnit;
99
100 @SuppressWarnings("ALL")
101 @Service
102 @Component(immediate = true)
103 public class SegmentRoutingManager implements SegmentRoutingService {
104
105     private static Logger log = LoggerFactory
106             .getLogger(SegmentRoutingManager.class);
107
108     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109     protected CoreService coreService;
110
111     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112     protected TopologyService topologyService;
113
114     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115     protected PacketService packetService;
116
117     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118     protected IntentService intentService;
119
120     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121     protected HostService hostService;
122
123     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124     protected DeviceService deviceService;
125
126     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127     protected FlowObjectiveService flowObjectiveService;
128
129     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130     protected LinkService linkService;
131
132     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133     protected MastershipService mastershipService;
134
135     protected ArpHandler arpHandler = null;
136     protected IcmpHandler icmpHandler = null;
137     protected IpHandler ipHandler = null;
138     protected RoutingRulePopulator routingRulePopulator = null;
139     protected ApplicationId appId;
140     protected DeviceConfiguration deviceConfiguration = null;
141
142     private DefaultRoutingHandler defaultRoutingHandler = null;
143     private TunnelHandler tunnelHandler = null;
144     private PolicyHandler policyHandler = null;
145     private InternalPacketProcessor processor = null;
146     private InternalLinkListener linkListener = null;
147     private InternalDeviceListener deviceListener = null;
148     private InternalEventHandler eventHandler = new InternalEventHandler();
149
150     private ScheduledExecutorService executorService = Executors
151             .newScheduledThreadPool(1);
152
153     private static ScheduledFuture<?> eventHandlerFuture = null;
154     private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
155     private Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
156             new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
157     // Per device next objective ID store with (device id + neighbor set) as key
158     private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
159             nsNextObjStore = null;
160     private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
161             subnetNextObjStore = null;
162     private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
163     private EventuallyConsistentMap<String, Policy> policyStore = null;
164     // Per device, per-subnet assigned-vlans store, with (device id + subnet
165     // IPv4 prefix) as key
166     private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
167         subnetVidStore = null;
168
169     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
170     protected StorageService storageService;
171
172     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
173     protected NetworkConfigRegistry cfgService;
174
175     private final InternalConfigListener cfgListener =
176             new InternalConfigListener(this);
177
178     private final ConfigFactory cfgFactory =
179             new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY,
180                               SegmentRoutingConfig.class,
181                               "segmentrouting") {
182                 @Override
183                 public SegmentRoutingConfig createConfig() {
184                     return new SegmentRoutingConfig();
185                 }
186             };
187
188     private final HostListener hostListener = new InternalHostListener();
189
190     private Object threadSchedulerLock = new Object();
191     private static int numOfEventsQueued = 0;
192     private static int numOfEventsExecuted = 0;
193     private static int numOfHandlerExecution = 0;
194     private static int numOfHandlerScheduled = 0;
195
196     private KryoNamespace.Builder kryoBuilder = null;
197
198     private static final short ASSIGNED_VLAN_START = 4093;
199     public static final short ASSIGNED_VLAN_NO_SUBNET = 4094;
200
201     @Activate
202     protected void activate() {
203         appId = coreService
204                 .registerApplication("org.onosproject.segmentrouting");
205
206         kryoBuilder = new KryoNamespace.Builder()
207             .register(NeighborSetNextObjectiveStoreKey.class,
208                     SubnetNextObjectiveStoreKey.class,
209                     SubnetAssignedVidStoreKey.class,
210                     NeighborSet.class,
211                     DeviceId.class,
212                     URI.class,
213                     WallClockTimestamp.class,
214                     org.onosproject.cluster.NodeId.class,
215                     HashSet.class,
216                     Tunnel.class,
217                     DefaultTunnel.class,
218                     Policy.class,
219                     TunnelPolicy.class,
220                     Policy.Type.class,
221                     VlanId.class,
222                     Ip4Address.class,
223                     Ip4Prefix.class,
224                     IpAddress.Version.class,
225                     ConnectPoint.class
226             );
227
228         log.debug("Creating EC map nsnextobjectivestore");
229         EventuallyConsistentMapBuilder<NeighborSetNextObjectiveStoreKey, Integer>
230                 nsNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
231
232         nsNextObjStore = nsNextObjMapBuilder
233                 .withName("nsnextobjectivestore")
234                 .withSerializer(kryoBuilder)
235                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
236                 .build();
237         log.trace("Current size {}", nsNextObjStore.size());
238
239         log.debug("Creating EC map subnetnextobjectivestore");
240         EventuallyConsistentMapBuilder<SubnetNextObjectiveStoreKey, Integer>
241                 subnetNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
242
243         subnetNextObjStore = subnetNextObjMapBuilder
244                 .withName("subnetnextobjectivestore")
245                 .withSerializer(kryoBuilder)
246                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
247                 .build();
248
249         EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
250                 storageService.eventuallyConsistentMapBuilder();
251
252         tunnelStore = tunnelMapBuilder
253                 .withName("tunnelstore")
254                 .withSerializer(kryoBuilder)
255                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
256                 .build();
257
258         EventuallyConsistentMapBuilder<String, Policy> policyMapBuilder =
259                 storageService.eventuallyConsistentMapBuilder();
260
261         policyStore = policyMapBuilder
262                 .withName("policystore")
263                 .withSerializer(kryoBuilder)
264                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
265                 .build();
266
267         EventuallyConsistentMapBuilder<SubnetAssignedVidStoreKey, VlanId>
268             subnetVidStoreMapBuilder = storageService.eventuallyConsistentMapBuilder();
269
270         subnetVidStore = subnetVidStoreMapBuilder
271                 .withName("subnetvidstore")
272                 .withSerializer(kryoBuilder)
273                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
274                 .build();
275
276         cfgService.addListener(cfgListener);
277         cfgService.registerConfigFactory(cfgFactory);
278
279         hostService.addListener(hostListener);
280
281         processor = new InternalPacketProcessor();
282         linkListener = new InternalLinkListener();
283         deviceListener = new InternalDeviceListener();
284
285         packetService.addProcessor(processor, PacketProcessor.director(2));
286         linkService.addListener(linkListener);
287         deviceService.addListener(deviceListener);
288
289         cfgListener.configureNetwork();
290
291         log.info("Started");
292     }
293
294     @Deactivate
295     protected void deactivate() {
296         cfgService.removeListener(cfgListener);
297         cfgService.unregisterConfigFactory(cfgFactory);
298
299         packetService.removeProcessor(processor);
300         linkService.removeListener(linkListener);
301         deviceService.removeListener(deviceListener);
302         processor = null;
303         linkListener = null;
304         deviceService = null;
305
306         groupHandlerMap.clear();
307
308         log.info("Stopped");
309     }
310
311
312     @Override
313     public List<Tunnel> getTunnels() {
314         return tunnelHandler.getTunnels();
315     }
316
317     @Override
318     public TunnelHandler.Result createTunnel(Tunnel tunnel) {
319         return tunnelHandler.createTunnel(tunnel);
320     }
321
322     @Override
323     public TunnelHandler.Result removeTunnel(Tunnel tunnel) {
324         for (Policy policy: policyHandler.getPolicies()) {
325             if (policy.type() == Policy.Type.TUNNEL_FLOW) {
326                 TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
327                 if (tunnelPolicy.tunnelId().equals(tunnel.id())) {
328                     log.warn("Cannot remove the tunnel used by a policy");
329                     return TunnelHandler.Result.TUNNEL_IN_USE;
330                 }
331             }
332         }
333         return tunnelHandler.removeTunnel(tunnel);
334     }
335
336     @Override
337     public PolicyHandler.Result removePolicy(Policy policy) {
338         return policyHandler.removePolicy(policy);
339     }
340
341     @Override
342     public PolicyHandler.Result createPolicy(Policy policy) {
343         return policyHandler.createPolicy(policy);
344     }
345
346     @Override
347     public List<Policy> getPolicies() {
348         return policyHandler.getPolicies();
349     }
350
351     /**
352      * Returns the tunnel object with the tunnel ID.
353      *
354      * @param tunnelId Tunnel ID
355      * @return Tunnel reference
356      */
357     public Tunnel getTunnel(String tunnelId) {
358         return tunnelHandler.getTunnel(tunnelId);
359     }
360
361     /**
362      * Returns the vlan-id assigned to the subnet configured for a device.
363      * If no vlan-id has been assigned, a new one is assigned out of a pool of ids,
364      * if and only if this controller instance is the master for the device.
365      * <p>
366      * USAGE: The assigned vlans are meant to be applied to untagged packets on those
367      * switches/pipelines that need this functionality. These vids are meant
368      * to be used internally within a switch, and thus need to be unique only
369      * on a switch level. Note that packets never go out on the wire with these
370      * vlans. Currently, vlan ids are assigned from value 4093 down.
371      * Vlan id 4094 expected to be used for all ports that are not assigned subnets.
372      * Vlan id 4095 is reserved and unused. Only a single vlan id is assigned
373      * per subnet.
374      * XXX This method should avoid any vlans configured on the ports, but
375      *     currently the app works only on untagged packets and as a result
376      *     ignores any vlan configuration.
377      *
378      * @param deviceId switch dpid
379      * @param subnet IPv4 prefix for which assigned vlan is desired
380      * @return VlanId assigned for the subnet on the device, or
381      *         null if no vlan assignment was found and this instance is not
382      *         the master for the device.
383      */
384     public VlanId getSubnetAssignedVlanId(DeviceId deviceId, Ip4Prefix subnet) {
385         VlanId assignedVid = subnetVidStore.get(new SubnetAssignedVidStoreKey(
386                                                         deviceId, subnet));
387         if (assignedVid != null) {
388             log.debug("Query for subnet:{} on device:{} returned assigned-vlan "
389                     + "{}", subnet, deviceId, assignedVid);
390             return assignedVid;
391         }
392         //check mastership for the right to assign a vlan
393         if (!mastershipService.isLocalMaster(deviceId)) {
394             log.warn("This controller instance is not the master for device {}. "
395                     + "Cannot assign vlan-id for subnet {}", deviceId, subnet);
396             return null;
397         }
398         // vlan assignment is expensive but done only once
399         Set<Ip4Prefix> configuredSubnets = deviceConfiguration.getSubnets(deviceId);
400         Set<Short> assignedVlans = new HashSet<>();
401         Set<Ip4Prefix> unassignedSubnets = new HashSet<>();
402         for (Ip4Prefix sub : configuredSubnets) {
403             VlanId v = subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId,
404                                                                         sub));
405             if (v != null) {
406                 assignedVlans.add(v.toShort());
407             } else {
408                 unassignedSubnets.add(sub);
409             }
410         }
411         short nextAssignedVlan = ASSIGNED_VLAN_START;
412         if (!assignedVlans.isEmpty()) {
413             nextAssignedVlan = (short) (Collections.min(assignedVlans) - 1);
414         }
415         for (Ip4Prefix unsub : unassignedSubnets) {
416             subnetVidStore.put(new SubnetAssignedVidStoreKey(deviceId, unsub),
417                                VlanId.vlanId(nextAssignedVlan--));
418             log.info("Assigned vlan: {} to subnet: {} on device: {}",
419                       nextAssignedVlan + 1, unsub, deviceId);
420         }
421
422         return subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId, subnet));
423     }
424
425     /**
426      * Returns the next objective ID for the given NeighborSet.
427      * If the nextObjective does not exist, a new one is created and
428      * it's id is returned.
429      * TODO move the side-effect creation of a Next Objective into a new method
430      *
431      * @param deviceId Device ID
432      * @param ns NegighborSet
433      * @param meta metadata passed into the creation of a Next Objective
434      * @return next objective ID or -1 if an error was encountered during the
435      *         creation of the nextObjective
436      */
437     public int getNextObjectiveId(DeviceId deviceId, NeighborSet ns,
438                                   TrafficSelector meta) {
439         if (groupHandlerMap.get(deviceId) != null) {
440             log.trace("getNextObjectiveId query in device {}", deviceId);
441             return groupHandlerMap
442                     .get(deviceId).getNextObjectiveId(ns, meta);
443         } else {
444             log.warn("getNextObjectiveId query in device {} not found", deviceId);
445             return -1;
446         }
447     }
448
449     /**
450      * Returns the next objective ID for the Subnet given. If the nextObjectiveID does not exist,
451      * a new one is created and returned.
452      *
453      * @param deviceId Device ID
454      * @param prefix Subnet
455      * @return next objective ID
456      */
457     public int getSubnetNextObjectiveId(DeviceId deviceId, IpPrefix prefix) {
458         if (groupHandlerMap.get(deviceId) != null) {
459             log.trace("getSubnetNextObjectiveId query in device {}", deviceId);
460             return groupHandlerMap
461                     .get(deviceId).getSubnetNextObjectiveId(prefix);
462         } else {
463             log.warn("getSubnetNextObjectiveId query in device {} not found", deviceId);
464             return -1;
465         }
466     }
467
468     private class InternalPacketProcessor implements PacketProcessor {
469         @Override
470         public void process(PacketContext context) {
471
472             if (context.isHandled()) {
473                 return;
474             }
475
476             InboundPacket pkt = context.inPacket();
477             Ethernet ethernet = pkt.parsed();
478
479             if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
480                 arpHandler.processPacketIn(pkt);
481             } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
482                 IPv4 ipPacket = (IPv4) ethernet.getPayload();
483                 ipHandler.addToPacketBuffer(ipPacket);
484                 if (ipPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
485                     icmpHandler.processPacketIn(pkt);
486                 } else {
487                     ipHandler.processPacketIn(pkt);
488                 }
489             }
490         }
491     }
492
493     private class InternalLinkListener implements LinkListener {
494         @Override
495         public void event(LinkEvent event) {
496             if (event.type() == LinkEvent.Type.LINK_ADDED
497                     || event.type() == LinkEvent.Type.LINK_REMOVED) {
498                 log.debug("Event {} received from Link Service", event.type());
499                 scheduleEventHandlerIfNotScheduled(event);
500             }
501         }
502     }
503
504     private class InternalDeviceListener implements DeviceListener {
505         @Override
506         public void event(DeviceEvent event) {
507             switch (event.type()) {
508             case DEVICE_ADDED:
509             case PORT_REMOVED:
510             case DEVICE_UPDATED:
511             case DEVICE_AVAILABILITY_CHANGED:
512                 log.debug("Event {} received from Device Service", event.type());
513                 scheduleEventHandlerIfNotScheduled(event);
514                 break;
515             default:
516             }
517         }
518     }
519
520     private void scheduleEventHandlerIfNotScheduled(Event event) {
521         synchronized (threadSchedulerLock) {
522             eventQueue.add(event);
523             numOfEventsQueued++;
524
525             if ((numOfHandlerScheduled - numOfHandlerExecution) == 0) {
526                 //No pending scheduled event handling threads. So start a new one.
527                 eventHandlerFuture = executorService
528                         .schedule(eventHandler, 100, TimeUnit.MILLISECONDS);
529                 numOfHandlerScheduled++;
530             }
531             log.trace("numOfEventsQueued {}, numOfEventHanlderScheduled {}",
532                       numOfEventsQueued,
533                       numOfHandlerScheduled);
534         }
535     }
536
537     private class InternalEventHandler implements Runnable {
538         @Override
539         public void run() {
540             try {
541                 while (true) {
542                     Event event = null;
543                     synchronized (threadSchedulerLock) {
544                         if (!eventQueue.isEmpty()) {
545                             event = eventQueue.poll();
546                             numOfEventsExecuted++;
547                         } else {
548                             numOfHandlerExecution++;
549                             log.debug("numOfHandlerExecution {} numOfEventsExecuted {}",
550                                       numOfHandlerExecution, numOfEventsExecuted);
551                             break;
552                         }
553                     }
554                     if (event.type() == LinkEvent.Type.LINK_ADDED) {
555                         processLinkAdded((Link) event.subject());
556                     } else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
557                         processLinkRemoved((Link) event.subject());
558                     } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED ||
559                             event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
560                             event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
561                         if (deviceService.isAvailable(((Device) event.subject()).id())) {
562                             log.info("Processing device event {} for available device {}",
563                                      event.type(), ((Device) event.subject()).id());
564                             processDeviceAdded((Device) event.subject());
565                         }
566                     } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
567                         processPortRemoved((Device) event.subject(),
568                                            ((DeviceEvent) event).port());
569                     } else {
570                         log.warn("Unhandled event type: {}", event.type());
571                     }
572                 }
573             } catch (Exception e) {
574                 log.error("SegmentRouting event handler "
575                         + "thread thrown an exception: {}", e);
576             }
577         }
578     }
579
580     private void processLinkAdded(Link link) {
581         log.debug("A new link {} was added", link.toString());
582         if (!deviceConfiguration.isConfigured(link.src().deviceId())) {
583             log.warn("Source device of this link is not configured.");
584             return;
585         }
586         //Irrespective whether the local is a MASTER or not for this device,
587         //create group handler instance and push default TTP flow rules.
588         //Because in a multi-instance setup, instances can initiate
589         //groups for any devices. Also the default TTP rules are needed
590         //to be pushed before inserting any IP table entries for any device
591         DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src()
592                 .deviceId());
593         if (groupHandler != null) {
594             groupHandler.linkUp(link, mastershipService.isLocalMaster(
595                                            link.src().deviceId()));
596         } else {
597             Device device = deviceService.getDevice(link.src().deviceId());
598             if (device != null) {
599                 log.warn("processLinkAdded: Link Added "
600                         + "Notification without Device Added "
601                         + "event, still handling it");
602                 processDeviceAdded(device);
603                 groupHandler = groupHandlerMap.get(link.src()
604                                                    .deviceId());
605                 groupHandler.linkUp(link, mastershipService.isLocalMaster(device.id()));
606             }
607         }
608
609         log.trace("Starting optimized route population process");
610         defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(null);
611         //log.trace("processLinkAdded: re-starting route population process");
612         //defaultRoutingHandler.startPopulationProcess();
613     }
614
615     private void processLinkRemoved(Link link) {
616         log.debug("A link {} was removed", link.toString());
617         DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src().deviceId());
618         if (groupHandler != null) {
619             groupHandler.portDown(link.src().port());
620         }
621         log.trace("Starting optimized route population process");
622         defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
623         //log.trace("processLinkRemoved: re-starting route population process");
624         //defaultRoutingHandler.startPopulationProcess();
625     }
626
627     private void processDeviceAdded(Device device) {
628         log.debug("A new device with ID {} was added", device.id());
629         if (deviceConfiguration == null || !deviceConfiguration.isConfigured(device.id())) {
630             log.warn("Device configuration uploading. Device {} will be "
631                     + "processed after config completes.", device.id());
632             return;
633         }
634         // Irrespective of whether the local is a MASTER or not for this device,
635         // we need to create a SR-group-handler instance. This is because in a
636         // multi-instance setup, any instance can initiate forwarding/next-objectives
637         // for any switch (even if this instance is a SLAVE or not even connected
638         // to the switch). To handle this, a default-group-handler instance is necessary
639         // per switch.
640         if (groupHandlerMap.get(device.id()) == null) {
641             DefaultGroupHandler groupHandler;
642             try {
643                 groupHandler = DefaultGroupHandler.
644                         createGroupHandler(device.id(),
645                                            appId,
646                                            deviceConfiguration,
647                                            linkService,
648                                            flowObjectiveService,
649                                            nsNextObjStore,
650                                            subnetNextObjStore);
651             } catch (DeviceConfigNotFoundException e) {
652                 log.warn(e.getMessage() + " Aborting processDeviceAdded.");
653                 return;
654             }
655             groupHandlerMap.put(device.id(), groupHandler);
656             // Also, in some cases, drivers may need extra
657             // information to process rules (eg. Router IP/MAC); and so, we send
658             // port addressing rules to the driver as well irrespective of whether
659             // this instance is the master or not.
660             defaultRoutingHandler.populatePortAddressingRules(device.id());
661         }
662         if (mastershipService.isLocalMaster(device.id())) {
663             DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
664             groupHandler.createGroupsFromSubnetConfig();
665             routingRulePopulator.populateSubnetBroadcastRule(device.id());
666         }
667     }
668
669     private void processPortRemoved(Device device, Port port) {
670         log.debug("Port {} was removed", port.toString());
671         DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
672         if (groupHandler != null) {
673             groupHandler.portDown(port.number());
674         }
675     }
676
677     private class InternalConfigListener implements NetworkConfigListener {
678         SegmentRoutingManager segmentRoutingManager;
679
680         public InternalConfigListener(SegmentRoutingManager srMgr) {
681             this.segmentRoutingManager = srMgr;
682         }
683
684         public void configureNetwork() {
685             deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
686
687             arpHandler = new ArpHandler(segmentRoutingManager);
688             icmpHandler = new IcmpHandler(segmentRoutingManager);
689             ipHandler = new IpHandler(segmentRoutingManager);
690             routingRulePopulator = new RoutingRulePopulator(segmentRoutingManager);
691             defaultRoutingHandler = new DefaultRoutingHandler(segmentRoutingManager);
692
693             tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
694                                               groupHandlerMap, tunnelStore);
695             policyHandler = new PolicyHandler(appId, deviceConfiguration,
696                                               flowObjectiveService,
697                                               tunnelHandler, policyStore);
698
699             for (Device device : deviceService.getDevices()) {
700                 // Irrespective of whether the local is a MASTER or not for this device,
701                 // we need to create a SR-group-handler instance. This is because in a
702                 // multi-instance setup, any instance can initiate forwarding/next-objectives
703                 // for any switch (even if this instance is a SLAVE or not even connected
704                 // to the switch). To handle this, a default-group-handler instance is necessary
705                 // per switch.
706                 if (groupHandlerMap.get(device.id()) == null) {
707                     DefaultGroupHandler groupHandler;
708                     try {
709                         groupHandler = DefaultGroupHandler.
710                                 createGroupHandler(device.id(),
711                                                    appId,
712                                                    deviceConfiguration,
713                                                    linkService,
714                                                    flowObjectiveService,
715                                                    nsNextObjStore,
716                                                    subnetNextObjStore);
717                     } catch (DeviceConfigNotFoundException e) {
718                         log.warn(e.getMessage() + " Aborting configureNetwork.");
719                         return;
720                     }
721                     groupHandlerMap.put(device.id(), groupHandler);
722
723                     // Also, in some cases, drivers may need extra
724                     // information to process rules (eg. Router IP/MAC); and so, we send
725                     // port addressing rules to the driver as well, irrespective of whether
726                     // this instance is the master or not.
727                     defaultRoutingHandler.populatePortAddressingRules(device.id());
728                 }
729                 if (mastershipService.isLocalMaster(device.id())) {
730                     DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
731                     groupHandler.createGroupsFromSubnetConfig();
732                     routingRulePopulator.populateSubnetBroadcastRule(device.id());
733                 }
734             }
735
736             defaultRoutingHandler.startPopulationProcess();
737         }
738
739         @Override
740         public void event(NetworkConfigEvent event) {
741             if (event.configClass().equals(SegmentRoutingConfig.class)) {
742                 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
743                     log.info("Network configuration added.");
744                     configureNetwork();
745                 }
746                 if (event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) {
747                     log.info("Network configuration updated.");
748                     // TODO support dynamic configuration
749                 }
750             }
751         }
752     }
753
754     private class InternalHostListener implements HostListener {
755         private ForwardingObjective.Builder getForwardingObjectiveBuilder(
756                 MacAddress mac, VlanId vlanId, PortNumber port) {
757             TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
758             sbuilder.matchEthDst(mac);
759             sbuilder.matchVlanId(vlanId);
760
761             TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
762             // TODO Move popVlan from flow action to group action
763             tbuilder.immediate().popVlan();
764             tbuilder.immediate().setOutput(port);
765
766             return DefaultForwardingObjective.builder()
767                     .withFlag(ForwardingObjective.Flag.SPECIFIC)
768                     .withSelector(sbuilder.build())
769                     .withTreatment(tbuilder.build())
770                     .withPriority(100)
771                     .fromApp(appId)
772                     .makePermanent();
773         }
774
775         private void processHostAddedEvent(HostEvent event) {
776             MacAddress mac = event.subject().mac();
777             VlanId vlanId = event.subject().vlan();
778             DeviceId deviceId = event.subject().location().deviceId();
779             PortNumber port = event.subject().location().port();
780             Set<IpAddress> ips = event.subject().ipAddresses();
781             log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
782
783             // TODO Move bridging table population to a separate class
784             // Populate bridging table entry
785             ForwardingObjective.Builder fob =
786                     getForwardingObjectiveBuilder(mac, vlanId, port);
787             flowObjectiveService.forward(deviceId, fob.add(
788                     new BridgingTableObjectiveContext(mac, vlanId)
789             ));
790
791             // Populate IP table entry
792             ips.forEach(ip -> {
793                 if (ip.isIp4()) {
794                     routingRulePopulator.populateIpRuleForHost(
795                             deviceId, ip.getIp4Address(), mac, port);
796                 }
797             });
798         }
799
800         private void processHostRemoveEvent(HostEvent event) {
801             MacAddress mac = event.subject().mac();
802             VlanId vlanId = event.subject().vlan();
803             DeviceId deviceId = event.subject().location().deviceId();
804             PortNumber port = event.subject().location().port();
805             Set<IpAddress> ips = event.subject().ipAddresses();
806             log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
807
808             // Revoke bridging table entry
809             ForwardingObjective.Builder fob =
810                     getForwardingObjectiveBuilder(mac, vlanId, port);
811             flowObjectiveService.forward(deviceId, fob.remove(
812                     new BridgingTableObjectiveContext(mac, vlanId)
813             ));
814
815             // Revoke IP table entry
816             ips.forEach(ip -> {
817                 if (ip.isIp4()) {
818                     routingRulePopulator.revokeIpRuleForHost(
819                             deviceId, ip.getIp4Address(), mac, port);
820                 }
821             });
822         }
823
824         private void processHostMovedEvent(HostEvent event) {
825             MacAddress mac = event.subject().mac();
826             VlanId vlanId = event.subject().vlan();
827             DeviceId prevDeviceId = event.prevSubject().location().deviceId();
828             PortNumber prevPort = event.prevSubject().location().port();
829             Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
830             DeviceId newDeviceId = event.subject().location().deviceId();
831             PortNumber newPort = event.subject().location().port();
832             Set<IpAddress> newIps = event.subject().ipAddresses();
833             log.debug("Host {}/{} is moved from {}:{} to {}:{}",
834                     mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
835
836             // Revoke previous bridging table entry
837             ForwardingObjective.Builder prevFob =
838                     getForwardingObjectiveBuilder(mac, vlanId, prevPort);
839             flowObjectiveService.forward(prevDeviceId, prevFob.remove(
840                     new BridgingTableObjectiveContext(mac, vlanId)
841             ));
842
843             // Revoke previous IP table entry
844             prevIps.forEach(ip -> {
845                 if (ip.isIp4()) {
846                     routingRulePopulator.revokeIpRuleForHost(
847                             prevDeviceId, ip.getIp4Address(), mac, prevPort);
848                 }
849             });
850
851             // Populate new bridging table entry
852             ForwardingObjective.Builder newFob =
853                     getForwardingObjectiveBuilder(mac, vlanId, prevPort);
854             flowObjectiveService.forward(newDeviceId, newFob.add(
855                     new BridgingTableObjectiveContext(mac, vlanId)
856             ));
857
858             // Populate new IP table entry
859             newIps.forEach(ip -> {
860                 if (ip.isIp4()) {
861                     routingRulePopulator.populateIpRuleForHost(
862                             newDeviceId, ip.getIp4Address(), mac, newPort);
863                 }
864             });
865         }
866
867         private void processHostUpdatedEvent(HostEvent event) {
868             MacAddress mac = event.subject().mac();
869             VlanId vlanId = event.subject().vlan();
870             DeviceId prevDeviceId = event.prevSubject().location().deviceId();
871             PortNumber prevPort = event.prevSubject().location().port();
872             Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
873             DeviceId newDeviceId = event.subject().location().deviceId();
874             PortNumber newPort = event.subject().location().port();
875             Set<IpAddress> newIps = event.subject().ipAddresses();
876             log.debug("Host {}/{} is updated", mac, vlanId);
877
878             // Revoke previous IP table entry
879             prevIps.forEach(ip -> {
880                 if (ip.isIp4()) {
881                     routingRulePopulator.revokeIpRuleForHost(
882                             prevDeviceId, ip.getIp4Address(), mac, prevPort);
883                 }
884             });
885
886             // Populate new IP table entry
887             newIps.forEach(ip -> {
888                 if (ip.isIp4()) {
889                     routingRulePopulator.populateIpRuleForHost(
890                             newDeviceId, ip.getIp4Address(), mac, newPort);
891                 }
892             });
893         }
894
895         @Override
896         public void event(HostEvent event) {
897             // Do not proceed without mastership
898             DeviceId deviceId = event.subject().location().deviceId();
899             if (!mastershipService.isLocalMaster(deviceId)) {
900                 return;
901             }
902
903             switch (event.type()) {
904                 case HOST_ADDED:
905                     processHostAddedEvent(event);
906                     break;
907                 case HOST_MOVED:
908                     processHostMovedEvent(event);
909                     break;
910                 case HOST_REMOVED:
911                     processHostRemoveEvent(event);
912                     break;
913                 case HOST_UPDATED:
914                     processHostUpdatedEvent(event);
915                     break;
916                 default:
917                     log.warn("Unsupported host event type: {}", event.type());
918                     break;
919             }
920         }
921     }
922
923     private static class BridgingTableObjectiveContext implements ObjectiveContext {
924         final MacAddress mac;
925         final VlanId vlanId;
926
927         BridgingTableObjectiveContext(MacAddress mac, VlanId vlanId) {
928             this.mac = mac;
929             this.vlanId = vlanId;
930         }
931
932         @Override
933         public void onSuccess(Objective objective) {
934             if (objective.op() == Objective.Operation.ADD) {
935                 log.debug("Successfully populate bridging table entry for {}/{}",
936                         mac, vlanId);
937             } else {
938                 log.debug("Successfully revoke bridging table entry for {}/{}",
939                         mac, vlanId);
940             }
941         }
942
943         @Override
944         public void onError(Objective objective, ObjectiveError error) {
945             if (objective.op() == Objective.Operation.ADD) {
946                 log.debug("Fail to populate bridging table entry for {}/{}. {}",
947                         mac, vlanId, error);
948             } else {
949                 log.debug("Fail to revoke bridging table entry for {}/{}. {}",
950                          mac, vlanId, error);
951             }
952         }
953     }
954 }