/* * Copyright 2016-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.lisp.ctl.impl; import com.google.common.collect.Maps; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.util.Tools; import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.CoreService; import org.onosproject.lisp.ctl.LispController; import org.onosproject.lisp.ctl.LispMessageListener; import org.onosproject.lisp.ctl.LispRouter; import org.onosproject.lisp.ctl.LispRouterAgent; import org.onosproject.lisp.ctl.LispRouterFactory; import org.onosproject.lisp.ctl.LispRouterId; import org.onosproject.lisp.ctl.LispRouterListener; import org.onosproject.lisp.msg.authentication.LispAuthenticationConfig; import org.onosproject.lisp.msg.protocols.LispInfoReply; import org.onosproject.lisp.msg.protocols.LispInfoRequest; import org.onosproject.lisp.msg.protocols.LispMessage; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import java.util.Dictionary; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import static java.util.concurrent.Executors.newFixedThreadPool; import static java.util.stream.Collectors.toConcurrentMap; import static org.onlab.util.Tools.get; import static org.onlab.util.Tools.getIntegerProperty; import static org.onlab.util.Tools.groupedThreads; import static org.slf4j.LoggerFactory.getLogger; /** * LISP controller initiation class. */ @Component(immediate = true) @Service public class LispControllerImpl implements LispController { private static final String APP_ID = "org.onosproject.lisp-base"; private static final Logger log = getLogger(LispControllerImpl.class); private static final String DEFAULT_LISP_AUTH_KEY = "onos"; private static final short DEFAULT_LISP_AUTH_KEY_ID = 1; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ComponentConfigService cfgService; @Property(name = "lispAuthKey", value = DEFAULT_LISP_AUTH_KEY, label = "Authentication key which is used to calculate authentication " + "data for LISP control message; default value is onos") private String lispAuthKey = DEFAULT_LISP_AUTH_KEY; @Property(name = "lispAuthKeyId", intValue = DEFAULT_LISP_AUTH_KEY_ID, label = "Authentication key id which denotes the authentication method " + "that ONOS uses to calculate the authentication data; " + "1 denotes HMAC SHA1 encryption, 2 denotes HMAC SHA256 encryption; " + "default value is 1") private int lispAuthKeyId = DEFAULT_LISP_AUTH_KEY_ID; ExecutorService executorMessages = newFixedThreadPool(4, groupedThreads("onos/lisp", "event-stats-%d", log)); protected LispRouterAgent agent = new DefaultLispRouterAgent(); ConcurrentMap<LispRouterId, LispRouter> connectedRouters = Maps.newConcurrentMap(); final LispAuthenticationConfig authConfig = LispAuthenticationConfig.getInstance(); LispControllerBootstrap bootstrap = new LispControllerBootstrap(); private Set<LispRouterListener> lispRouterListeners = new CopyOnWriteArraySet<>(); private Set<LispMessageListener> lispMessageListeners = new CopyOnWriteArraySet<>(); private LispRouterFactory routerFactory = LispRouterFactory.getInstance(); @Activate public void activate(ComponentContext context) { coreService.registerApplication(APP_ID, this::cleanup); cfgService.registerProperties(getClass()); initAuthConfig(context.getProperties()); routerFactory.setAgent(agent); bootstrap.start(); log.info("Started"); } /** * Shutdowns all listening channel and all LISP channels. * Clean information about routers before deactivating. */ private void cleanup() { bootstrap.stop(); routerFactory.cleanAgent(); connectedRouters.values().forEach(LispRouter::disconnectRouter); connectedRouters.clear(); } @Deactivate public void deactivate() { cleanup(); cfgService.unregisterProperties(getClass(), false); log.info("Stopped"); } @Modified public void modified(ComponentContext context) { readComponentConfiguration(context); bootstrap.stop(); bootstrap.start(); } /** * Initializes authentication key and authentication method. * * @param properties a set of properties that contained in component context */ private void initAuthConfig(Dictionary<?, ?> properties) { authConfig.updateLispAuthKey(get(properties, "lispAuthKey")); authConfig.updateLispAuthKeyId(getIntegerProperty(properties, "lispAuthKeyId")); } /** * Extracts properties from the component configuration context. * * @param context the component context */ private void readComponentConfiguration(ComponentContext context) { Dictionary<?, ?> properties = context.getProperties(); String lispAuthKeyStr = Tools.get(properties, "lispAuthKey"); lispAuthKey = lispAuthKeyStr != null ? lispAuthKeyStr : DEFAULT_LISP_AUTH_KEY; authConfig.updateLispAuthKey(lispAuthKey); log.info("Configured. LISP authentication key is {}", lispAuthKey); Integer lispAuthMethodInt = Tools.getIntegerProperty(properties, "lispAuthKeyId"); if (lispAuthMethodInt == null) { lispAuthKeyId = DEFAULT_LISP_AUTH_KEY_ID; log.info("LISP authentication method is not configured, default value is {}", lispAuthKeyId); } else { lispAuthKeyId = lispAuthMethodInt; log.info("Configured. LISP authentication method is configured to {}", lispAuthKeyId); } authConfig.updateLispAuthKeyId(lispAuthKeyId); } @Override public Iterable<LispRouter> getRouters() { return connectedRouters.values(); } @Override public Iterable<LispRouter> getSubscribedRouters() { return connectedRouters.entrySet() .stream() .filter(e -> e.getValue().isSubscribed()) .collect(toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)).values(); } @Override public LispRouter connectRouter(LispRouterId routerId) { if (connectedRouters.containsKey(routerId)) { log.debug("LISP router {} is already existing", routerId); return connectedRouters.get(routerId); } else { // TODO: currently we do not consider to add LISP router from netcfg log.warn("Adding router from netcfg is not supported currently"); return null; } } @Override public void disconnectRouter(LispRouterId routerId, boolean remove) { if (!connectedRouters.containsKey(routerId)) { log.warn("LISP router {} is not existing", routerId); } else { connectedRouters.get(routerId).disconnectRouter(); if (remove) { agent.removeConnectedRouter(routerId); } } } @Override public LispRouter getRouter(LispRouterId routerId) { return connectedRouters.get(routerId); } @Override public void addRouterListener(LispRouterListener listener) { if (!lispRouterListeners.contains(listener)) { lispRouterListeners.add(listener); } } @Override public void removeRouterListener(LispRouterListener listener) { lispRouterListeners.remove(listener); } @Override public void addMessageListener(LispMessageListener listener) { if (!lispMessageListeners.contains(listener)) { lispMessageListeners.add(listener); } } @Override public void removeMessageListener(LispMessageListener listener) { lispMessageListeners.remove(listener); } /** * Implementation of a LISP agent which is responsible for keeping track of * connected LISP routers and the state in which they are in. */ public final class DefaultLispRouterAgent implements LispRouterAgent { private final Logger log = getLogger(DefaultLispRouterAgent.class); /** * Prevents object instantiation from external class. */ private DefaultLispRouterAgent() { } @Override public boolean addConnectedRouter(LispRouterId routerId, LispRouter router) { if (connectedRouters.get(routerId) != null) { log.warn("Trying to add connectedRouter but found a previous " + "value for routerId: {}", routerId); return false; } else { log.info("Added router {}", routerId); connectedRouters.put(routerId, router); for (LispRouterListener listener : lispRouterListeners) { listener.routerAdded(routerId); } return true; } } @Override public void removeConnectedRouter(LispRouterId routerId) { if (connectedRouters.get(routerId) == null) { log.error("Trying to remove router {} from connectedRouter " + "list but no element was found", routerId); } else { log.info("Removed router {}", routerId); connectedRouters.remove(routerId); for (LispRouterListener listener : lispRouterListeners) { listener.routerRemoved(routerId); } } } @Override public void processUpstreamMessage(LispRouterId routerId, LispMessage message) { switch (message.getType()) { case LISP_MAP_REGISTER: case LISP_MAP_REQUEST: executorMessages.execute( new LispIncomingMessageHandler(routerId, message)); break; case LISP_INFO: if (message instanceof LispInfoRequest) { executorMessages.execute( new LispIncomingMessageHandler(routerId, message)); } else { log.warn("Not incoming LISP control message"); } break; default: log.warn("Not incoming LISP control message"); break; } } @Override public void processDownstreamMessage(LispRouterId routerId, LispMessage message) { switch (message.getType()) { case LISP_MAP_NOTIFY: case LISP_MAP_REPLY: executorMessages.execute( new LispOutgoingMessageHandler(routerId, message)); break; case LISP_INFO: if (message instanceof LispInfoReply) { executorMessages.execute( new LispOutgoingMessageHandler(routerId, message)); } else { log.warn("Not outgoing LISP control message"); } break; default: log.warn("Not outgoing LISP control message"); break; } } } /** * LISP message handler. */ protected class LispMessageHandler implements Runnable { private final LispRouterId routerId; private final LispMessage message; private final boolean isIncoming; LispMessageHandler(LispRouterId routerId, LispMessage message, boolean isIncoming) { this.routerId = routerId; this.message = message; this.isIncoming = isIncoming; } @Override public void run() { for (LispMessageListener listener : lispMessageListeners) { if (isIncoming) { listener.handleIncomingMessage(routerId, message); } else { listener.handleOutgoingMessage(routerId, message); } } } } /** * LISP incoming message handler. */ protected final class LispIncomingMessageHandler extends LispMessageHandler implements Runnable { LispIncomingMessageHandler(LispRouterId routerId, LispMessage message) { super(routerId, message, true); } } /** * LISP outgoing message handler. */ protected final class LispOutgoingMessageHandler extends LispMessageHandler implements Runnable { LispOutgoingMessageHandler(LispRouterId routerId, LispMessage message) { super(routerId, message, false); } } }