org.opendaylight.atrium.routingservice.impl.RibManager.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.atrium.routingservice.impl.RibManager.java

Source

/*
 * Copyright (c) 2016 Wipro Ltd. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.atrium.routingservice.impl;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.opendaylight.atrium.routingservice.api.RouteEntry.createBinaryString;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import org.opendaylight.atrium.hostservice.api.Host;
import org.opendaylight.atrium.hostservice.api.HostEvent;
import org.opendaylight.atrium.hostservice.api.HostListener;
import org.opendaylight.atrium.hostservice.api.HostService;
import org.opendaylight.atrium.routingservice.api.AtriumFibEntry;
import org.opendaylight.atrium.routingservice.api.AtriumFibUpdate;
import org.opendaylight.atrium.routingservice.api.FibListener;
import org.opendaylight.atrium.routingservice.api.RouteEntry;
import org.opendaylight.atrium.routingservice.api.RoutingService;
import org.opendaylight.atrium.routingservice.api.RouteUpdate;
import org.opendaylight.atrium.routingservice.config.api.RoutingConfigService;
import org.opendaylight.atrium.util.AtriumIpAddress;
import org.opendaylight.atrium.util.AtriumIpPrefix;
import org.opendaylight.atrium.util.AtriumMacAddress;
import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.protocol.bgp.rib.RibReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.routes.Ipv4Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.routes.ipv4.routes.Ipv4Route;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.AsPath;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.Segments;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.CNextHop;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.c.next.hop.Ipv4NextHopCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hostservice.api.rev150725.address.node.connector.ConnectorAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.c.next.hop.ipv4.next.hop._case.Ipv4NextHop;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hostservice.api.rev150725.HostId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hostservice.api.rev150725.HostNode;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;

/**
 * Collects the RIB routes through bgpcep application, requires configuration as
 * given in atrium-bgp-config.xml in utils. Generates FIB entry and pass it on
 * to FIBListeners (for example BGPRouter application which translates them to
 * FlowObjectives)
 *
 * @param <T>
 */
public class RibManager<T extends Route> implements BindingAwareProvider, AutoCloseable, DataTreeChangeListener<T>,
        TransactionChainListener, RoutingService {

    private static final Logger LOG = LoggerFactory.getLogger(RibManager.class);

    boolean closed = false;

    // Reference to the localRIB in bgpcep
    private RibReference localRibRef = null;

    // Constants used in IID
    static final Class<? extends AddressFamily> AFI = Ipv4AddressFamily.class;
    static final Class<? extends SubsequentAddressFamily> SAFI = UnicastSubsequentAddressFamily.class;
    static final TablesKey KEY = new TablesKey(AFI, SAFI);

    // Single threaded Executor which processes route updates from BGP Session
    private ExecutorService bgpUpdatesExecutor;

    /** The rib table4. */
    private InvertedRadixTree<RouteEntry> ribTable4;

    // Used for creating the transaction 
    private final BindingTransactionChain chain;

    // Stores all incoming route updates in a queue.
    private final BlockingQueue<DataTreeModification<T>> routeUpdatesQueue = new LinkedBlockingQueue<>();

    // Listener for FIB updates
    private FibListener fibListener;

    // DataTreeChangeService for registering DataTreeChange Events
    DataTreeChangeService dataTreeChangeService;

    // Host tracking utility used to track nexthop hosts
    private HostService hostService;

    // The IPv4 address to MAC address mapping
    private final Map<AtriumIpAddress, AtriumMacAddress> ip2Mac = new ConcurrentHashMap<>();

    // Next-hop IP address to route entry mapping for next hops pending MAC
    // resolution
    private SetMultimap<AtriumIpAddress, RouteEntry> routesWaitingOnArp;

    // RoutingConfig Service to check if the IP Prefix is local
    RoutingConfigService routingConfigService;

    /**
     * Constructor for RibManager
     * 
     * @param dataBroker
     * @param ribReference
     * @param hostService
     * @param routingConfigService
     */
    public RibManager(final DataBroker dataBroker, final RibReference ribReference, final HostService hostService,
            final RoutingConfigService routingConfigService) {
        localRibRef = ribReference;
        this.chain = dataBroker.createTransactionChain(this);
        this.hostService = hostService;
        this.routingConfigService = routingConfigService;
        dataTreeChangeService = (DataTreeChangeService) dataBroker;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.opendaylight.controller.sal.binding.api.BindingAwareProvider#
     * onSessionInitiated
     * (org.opendaylight.controller.sal.binding.api.BindingAwareBroker
     * .ProviderContext)
     */
    @Override
    public void onSessionInitiated(ProviderContext session) {
        LOG.info("Router Session Initiated");
        routesWaitingOnArp = Multimaps.synchronizedSetMultimap(HashMultimap.<AtriumIpAddress, RouteEntry>create());
        ribTable4 = new ConcurrentInvertedRadixTree<>(new DefaultByteArrayNodeFactory());
        bgpUpdatesExecutor = Executors
                .newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("atrium-bgp-updates-%d").build());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.opendaylight.atrium.routingservice.api.RoutingService#start()
     */
    public final void start() {

        bgpUpdatesExecutor.execute(new Runnable() {
            @Override
            public void run() {
                doUpdatesThread();
            }
        });

        // Starting host listener
        hostService.start();

        hostService.addListener(new InternalHostListener());

        final InstanceIdentifier<Tables> tablesId = this.localRibRef.getInstanceIdentifier().child(LocRib.class)
                .child(Tables.class, new TablesKey(AFI, SAFI));
        final DataTreeIdentifier<T> id = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
                getRouteWildcard(tablesId));
        dataTreeChangeService.registerDataTreeChangeListener(id, this);

        LOG.info("Rib Manager Started");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.opendaylight.atrium.routingservice.api.RoutingService#stop()
     */
    @Override
    public void stop() {
        // TODO Auto-generated method stub
        this.closed = true;
        // Stop host service
        hostService.stop();

        // Stop the thread(s)
        bgpUpdatesExecutor.shutdownNow();
        synchronized (this) {
            // Cleanup all local state
            ribTable4 = new ConcurrentInvertedRadixTree<>(new DefaultByteArrayNodeFactory());
            routeUpdatesQueue.clear();
            routesWaitingOnArp.clear();
            ip2Mac.clear();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.AutoCloseable#close()
     */
    @Override
    public void close() throws Exception {
        LOG.info("RoutingserviceProvider Closed");
        stop();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.opendaylight.atrium.routingservice.api.RoutingService#getRoutes4()
     */
    @Override
    public Collection<RouteEntry> getRoutes4() {
        Iterator<KeyValuePair<RouteEntry>> it = ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();

        List<RouteEntry> routes = new LinkedList<>();

        while (it.hasNext()) {
            KeyValuePair<RouteEntry> entry = it.next();
            routes.add(entry.getValue());
        }
        return routes;
    }

    /**
     * Used for constructing the RIB IID
     * 
     * @param tablesId
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected InstanceIdentifier<T> getRouteWildcard(final InstanceIdentifier<Tables> tablesId) {
        return tablesId.child((Class) Ipv4Routes.class).child(Ipv4Route.class);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener#
     * onDataTreeChanged(java.util.Collection)
     */
    @Override
    public synchronized void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
        // TODO Auto-generated method stub
        if (this.closed) {
            LOG.trace("Transaction chain was already closed, skipping update.");
            return;
        }

        final ReadOnlyTransaction trans = this.chain.newReadOnlyTransaction();
        LOG.debug("Received data change {} event with transaction {}", changes, trans.getIdentifier());

        for (final DataTreeModification<T> change : changes) {
            try {
                routeChanged(change, trans);
            } catch (final RuntimeException e) {
                LOG.warn("Data change {} was not completely propagated to listener {}, aborting", change, this, e);
                // trans.cancel();
                return;
            }
        }

    }

    /**
     * Processes adding a route entry.
     * <p>
     * The route entry is added to the radix tree. If there was an existing next
     * hop for this prefix, but the next hop was different, then the old route
     * entry is deleted.
     *
     * </p>
     * <p>
     * NOTE: Currently, we don't handle routes if the next hop is within the SDN
     * domain.
     * </p>
     *
     * @param routeEntry
     *            the route entry to add
     * @param withdrawPrefixes
     *            the collection of accumulated prefixes whose intents will be
     *            withdrawn
     * @return the corresponding FIB entry change, or null
     */
    private AtriumFibEntry processRouteAdd(RouteEntry routeEntry, Collection<AtriumIpPrefix> withdrawPrefixes) {
        LOG.info("Processing route add: {}", routeEntry);

        // Find the old next-hop if we are updating an old route entry
        AtriumIpAddress oldNextHop = null;
        RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
        if (oldRouteEntry != null) {
            oldNextHop = oldRouteEntry.nextHop();
        }

        // Add the new route to the RIB
        addRibRoute(routeEntry);

        if (oldNextHop != null) {
            if (oldNextHop.equals(routeEntry.nextHop())) {
                return null; // No change
            }
            //
            // Update an existing nexthop for the prefix.
            // We need to remove the old flows for this prefix from the
            // switches before the new flows are added.
            //
            withdrawPrefixes.add(oldRouteEntry.prefix());
        }

        if (isIpPrefixLocal(routeEntry.prefix())) {
            // Route originated by local SDN domain
            // We don't handle these here, reactive routing APP will handle
            // these
            LOG.debug("Own route {} to {}", routeEntry.prefix(), routeEntry.nextHop());
            return null;
        }

        AtriumMacAddress nextHopMacAddress = null;

        // Find the MAC address of next hop router for this route entry.
        // If the MAC address can not be found in ARP cache, then this prefix
        // will be put in routesWaitingOnArp queue.

        LOG.info("Sending request to host service for MAC resolution : {}", routeEntry.nextHop());
        // Monitor the IP address for updates of the MAC address
        hostService.startMonitoringIp(routeEntry.nextHop());

        LOG.info("Checking the ip2Mac table for : {}", routeEntry.nextHop());
        // Check if we know the MAC address of the next hop MacAddress
        nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());

        if (nextHopMacAddress == null) {
            Host host = hostService.getHost(new HostId(routeEntry.nextHop().toString()));
            if (host != null) {
                HostNode hostNode = host.getHostNode();
                if (hostNode != null) {
                    List<ConnectorAddress> addresses = hostNode.getConnectorAddress();
                    org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress mac = addresses
                            .get(0).getMac();
                    checkNotNull(mac);
                    nextHopMacAddress = AtriumMacAddress.valueOf(mac.getValue());
                }
            }
            if (nextHopMacAddress != null) {
                ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
            }
        }
        if (nextHopMacAddress == null) {
            LOG.info("nextHopMacAddress not found in ip2Mac : {}", routeEntry.nextHop());
            routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
            return null;
        }

        LOG.info("Creating FIB entry : " + routeEntry.prefix() + "," + routeEntry.nextHop() + ","
                + nextHopMacAddress);
        return new AtriumFibEntry(routeEntry.prefix(), routeEntry.nextHop(), nextHopMacAddress);
    }

    /*
     * Processes the deletion of a route entry. <p> The prefix for the routing
     * entry is removed from radix tree. If the operation is successful, the
     * prefix is added to the collection of prefixes whose intents that will be
     * withdrawn. </p>
     *
     * @param routeEntry the route entry to delete
     *
     * @param withdrawPrefixes the collection of accumulated prefixes whose
     * intents will be withdrawn
     */
    private void processRouteDelete(RouteEntry routeEntry, Collection<AtriumIpPrefix> withdrawPrefixes) {
        LOG.debug("Processing route delete: {}", routeEntry);
        boolean isRemoved = removeRibRoute(routeEntry.prefix());

        if (isRemoved) {
            withdrawPrefixes.add(routeEntry.prefix());
        }

        routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
    }

    /**
     * Removes a route for a prefix from the RIB. The prefix can be either IPv4
     * or IPv6.
     *
     * @param prefix
     *            the prefix to use
     * @return true if the route was found and removed, otherwise false
     */
    boolean removeRibRoute(AtriumIpPrefix prefix) {
        if (prefix.isIp4()) {
            // IPv4
            return ribTable4.remove(createBinaryString(prefix));
        }
        return false;
    }

    /**
     * Processes route updates.
     *
     * @param routeUpdate
     *            the route updates to process
     */
    public synchronized void processRouteUpdates(DataTreeModification<T> routeUpdate) {

        Collection<AtriumIpPrefix> withdrawPrefixes = new LinkedList<>();
        Collection<AtriumFibUpdate> fibUpdates = new LinkedList<>();
        Collection<AtriumFibUpdate> fibWithdraws = new LinkedList<>();

        LOG.info("Processing route update: {}", routeUpdate);
        final DataObjectModification<T> root = routeUpdate.getRootNode();

        switch (root.getModificationType()) {
        case SUBTREE_MODIFIED:
        case WRITE:
            LOG.debug("WRITE/SUBTREEMODIFIED: Updated Data for {} is - {}",
                    routeUpdate.getRootPath().getRootIdentifier(), root.getDataAfter());

            Ipv4Route ipv4RouteAfter = (Ipv4Route) root.getDataAfter();
            AtriumIpAddress nextHopAfter = getNextHopFromIpv4Route(ipv4RouteAfter);
            AtriumIpPrefix ipPrefixAfter = getIpPrefixFromIpv4Route(ipv4RouteAfter);
            RouteEntry routeEntryAfter = new RouteEntry(ipPrefixAfter, nextHopAfter);

            AtriumFibEntry fib = processRouteAdd(routeEntryAfter, withdrawPrefixes);

            if (fib != null) {
                fibUpdates.add(new AtriumFibUpdate(AtriumFibUpdate.Type.UPDATE, fib));
            }
            break;
        case DELETE:
            LOG.debug("DELETE: Data before for {} is {}", routeUpdate.getRootPath().getRootIdentifier(),
                    root.getDataBefore());
            Ipv4Route ipv4RouteUpdate = (Ipv4Route) root.getDataBefore();
            AtriumIpAddress nextHopIp = getNextHopFromIpv4Route(ipv4RouteUpdate);
            AtriumIpPrefix ipPrefix = getIpPrefixFromIpv4Route(ipv4RouteUpdate);
            if (nextHopIp != null && ipPrefix != null) {
                processRouteDelete(new RouteEntry(ipPrefix, nextHopIp), withdrawPrefixes);
            } else {
                LOG.warn("Issue with deleted route attributes");
            }

            break;
        default:
            LOG.error("Unknown update Type: {}", root.getModificationType());
            break;
        }

        withdrawPrefixes.forEach(p -> fibWithdraws
                .add(new AtriumFibUpdate(AtriumFibUpdate.Type.DELETE, new AtriumFibEntry(p, null, null))));

        if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
            // Send FIB Notification
            fibListener.update(fibUpdates, fibWithdraws);

            // TODO: Send a notification through md_sal or update fib in
            // data store
        }
    }

    /**
     * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
     * IPv6.
     *
     * @param prefix
     *            the prefix to use
     * @return the route if found, otherwise null
     */
    RouteEntry findRibRoute(AtriumIpPrefix prefix) {
        String binaryString = createBinaryString(prefix);
        if (prefix.isIp4()) {
            // IPv4
            return ribTable4.getValueForExactKey(binaryString);
        }
        return null;
    }

    /**
     * Checks if is ip prefix local.
     *
     * @param prefix
     *            the prefix
     * @return true, if is ip prefix local
     */
    // TODO: copy the following method from RoutingConfigurationService
    private boolean isIpPrefixLocal(AtriumIpPrefix prefix) {
        return routingConfigService.isIpPrefixLocal(prefix);
    }

    /**
     * Adds a route to the RIB. The route can be either IPv4 or IPv6.
     *
     * @param routeEntry
     *            the route entry to use
     */
    void addRibRoute(RouteEntry routeEntry) {
        if (routeEntry.isIp4()) {
            // IPv4
            ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry);
        }
    }

    /**
     * Signals the Router that the MAC to IP mapping has potentially been
     * updated. This has the effect of updating the MAC address for any
     * installed prefixes if it has changed, as well as installing any pending
     * prefixes that were waiting for MAC resolution.
     *
     * @param ipAddress
     *            the IP address that an event was received for
     * @param macAddress
     *            the most recently known MAC address for the IP address
     */
    private void updateMac(AtriumIpAddress ipAddress, AtriumMacAddress macAddress) {
        LOG.debug("Received updated MAC info: {} => {}", ipAddress, macAddress);

        // We synchronize on "this" to prevent changes to the Radix tree
        // while we're pushing intents. If the tree changes, the
        // tree and the intents could get out of sync.
        //
        synchronized (this) {
            Collection<AtriumFibUpdate> submitFibEntries = new LinkedList<>();

            Set<RouteEntry> routesToPush = routesWaitingOnArp.removeAll(ipAddress);

            for (RouteEntry routeEntry : routesToPush) {
                // These will always be adds
                RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
                if (foundRouteEntry != null && foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
                    // We only push FIB updates if the prefix is still in the
                    // radix tree and the next hop is the same as our entry.
                    // The prefix could have been removed while we were waiting
                    // for the ARP, or the next hop could have changed.
                    submitFibEntries.add(new AtriumFibUpdate(AtriumFibUpdate.Type.UPDATE,
                            new AtriumFibEntry(routeEntry.prefix(), ipAddress, macAddress)));
                } else {
                    LOG.debug("{} has been revoked before the MAC was resolved", routeEntry);
                }
            }

            if (!submitFibEntries.isEmpty()) {

                fibListener.update(submitFibEntries, Collections.emptyList());

                // TODO: Send a notification through md_sal or update fib in
                // data store
            }

            ip2Mac.put(ipAddress, macAddress);
        }
    }

    /**
     * The listener interface for receiving internalHost events. The class that
     * is interested in processing a internalHost event implements this
     * interface, and the object created with that class is registered with a
     * component using the component's <code>addInternalHostListener
     * <code> method. When the internalHost event occurs, that object's
     * appropriate method is invoked.
     *
     * @see InternalHostEvent
     */
    class InternalHostListener implements HostListener {

        /*
         * (non-Javadoc)
         *
         * @see
         * org.opendaylight.atrium.hostservice.api.HostListener#hostEventUpdate
         * (org.opendaylight.atrium.hostservice.api.HostEvent)
         */
        @Override
        public void hostEventUpdate(HostEvent event) {
            checkNotNull(event.getHost());
            LOG.info("Receved MAC/IP mapping for host ");
            switch (event.getType()) {
            case HOST_ADDED: {
                Host host = event.getHost();
                List<ConnectorAddress> addresses = host.getHostNode().getConnectorAddress();
                for (ConnectorAddress address : addresses) {
                    AtriumMacAddress mac = AtriumMacAddress.valueOf(address.getMac().toString());
                    Ipv4Address ipv4Address = address.getIp().getIpv4Address();
                    Ipv6Address ipv6Address = address.getIp().getIpv6Address();
                    if (ipv4Address != null) {
                        updateMac(AtriumIpAddress.valueOf(ipv4Address.getValue()), mac);
                    }
                    if (ipv6Address != null) {
                        updateMac(AtriumIpAddress.valueOf(ipv6Address.getValue()), mac);
                    }
                }
                break;
            }
            case HOST_REMOVED: {
                Host host = event.getHost();
                List<ConnectorAddress> addresses = host.getHostNode().getConnectorAddress();
                for (ConnectorAddress address : addresses) {
                    Ipv4Address ipv4Address = address.getIp().getIpv4Address();
                    Ipv6Address ipv6Address = address.getIp().getIpv6Address();
                    if (ipv4Address != null) {
                        AtriumIpAddress ip = AtriumIpAddress.valueOf(ipv4Address.getValue());
                        ip2Mac.remove(ip);
                    }
                    if (ipv6Address != null) {
                        AtriumIpAddress ip = AtriumIpAddress.valueOf(ipv6Address.getValue());
                        ip2Mac.remove(ip);
                    }
                }
                break;
            }
            default:
                break;
            }
        }

    }

    /**
     * Thread for handling route updates.
     */
    private void doUpdatesThread() {
        boolean interrupted = false;
        try {
            while (!interrupted) {
                try {
                    DataTreeModification<T> routeUpdates = routeUpdatesQueue.take();
                    processRouteUpdates(routeUpdates);
                } catch (InterruptedException e) {
                    LOG.error("Interrupted while taking from updates queue", e);
                    interrupted = true;
                } catch (Exception e) {
                    LOG.error("exception", e);
                }
            }
        } finally {
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * Handles the route update event and initiates processing of routes. 
     * @param change
     * @param trans
     */
    private void routeChanged(final DataTreeModification<T> change, final ReadOnlyTransaction trans) {
        // removeObject(trans, change.getRootPath().getRootIdentifier(),
        // root.getDataBefore());
        try {
            routeUpdatesQueue.put(change);
        } catch (InterruptedException e) {
            LOG.error("Interrupted while putting on routeUpdatesQueue", e);
            Thread.currentThread().interrupt();
        }

    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.opendaylight.atrium.routingservice.api.RoutingService#addFibListener
     * (org.opendaylight.atrium.routingservice.api.FibListener)
     */
    @Override
    public void addFibListener(FibListener fibListener) {
        // TODO Auto-generated method stub
        this.fibListener = fibListener;
    }

    @Override
    public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,
            Throwable cause) {
        // TODO Auto-generated method stub
        LOG.error("RibManager for {} failed in transaction {}", localRibRef.getInstanceIdentifier(),
                transaction != null ? transaction.getIdentifier() : null, cause);
    }

    @Override
    public void onTransactionChainSuccessful(TransactionChain<?, ?> arg0) {
        LOG.info("Topology builder for {} shut down", localRibRef.getInstanceIdentifier());

    }

    private Long getASNumberFromASPath(AsPath asPath) {
        if (asPath == null) {
            return Long.valueOf(0);
        }
        List<Segments> segments = asPath.getSegments();
        for (Segments segment : segments) {
            List<AsNumber> asNumbers = segment.getAsSequence();
            for (AsNumber asNumber : asNumbers) {
                if (asNumber != null) {
                    return asNumber.getValue();
                }
            }
        }
        return null;
    }

    private Ipv4Address getNextHopFromCNextHop(Ipv4NextHopCase ipv4CNextHop) {
        if (ipv4CNextHop == null) {
            return null;
        }
        Ipv4NextHop ipv4NextHop = ipv4CNextHop.getIpv4NextHop();
        if (ipv4NextHop != null) {
            return ipv4NextHop.getGlobal();
        } else {
            return null;
        }

    }

    private AtriumIpAddress getNextHopFromIpv4Route(Ipv4Route route) {
        if (route == null) {
            return null;
        }
        Ipv4NextHopCase nhc = (Ipv4NextHopCase) route.getAttributes().getCNextHop();
        Ipv4Address ipv4Address = getNextHopFromCNextHop(nhc);
        if (ipv4Address != null) {
            return AtriumIpAddress.valueOf(ipv4Address.getValue());
        }
        return null;

    }

    private AtriumIpPrefix getIpPrefixFromIpv4Route(Ipv4Route ipv4Route) {
        if (ipv4Route == null) {
            return null;
        }
        Ipv4Prefix prefix = ipv4Route.getPrefix();
        if (prefix != null) {
            return AtriumIpPrefix.valueOf(prefix.getValue());
        }
        return null;
    }

}