| /* |
| * 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.ImmutableList; |
| import com.google.common.collect.Lists; |
| import org.onosproject.lisp.msg.protocols.DefaultLispEncapsulatedControl.DefaultEcmBuilder; |
| import org.onosproject.lisp.msg.protocols.DefaultLispMapRecord.DefaultMapRecordBuilder; |
| import org.onosproject.lisp.msg.protocols.DefaultLispMapReply.DefaultReplyBuilder; |
| import org.onosproject.lisp.msg.protocols.LispEidRecord; |
| import org.onosproject.lisp.msg.protocols.LispEncapsulatedControl; |
| import org.onosproject.lisp.msg.protocols.LispEncapsulatedControl.EcmBuilder; |
| import org.onosproject.lisp.msg.protocols.LispLocator; |
| import org.onosproject.lisp.msg.protocols.LispMapRecord; |
| import org.onosproject.lisp.msg.protocols.LispMapRecord.MapRecordBuilder; |
| import org.onosproject.lisp.msg.protocols.LispMapReply.ReplyBuilder; |
| import org.onosproject.lisp.msg.protocols.LispMapReplyAction; |
| import org.onosproject.lisp.msg.protocols.LispMapRequest; |
| import org.onosproject.lisp.msg.protocols.LispMapReply; |
| import org.onosproject.lisp.msg.protocols.LispMessage; |
| import org.onosproject.lisp.msg.types.LispAfiAddress; |
| import org.onosproject.lisp.msg.types.LispIpAddress; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.net.InetSocketAddress; |
| import java.util.List; |
| |
| /** |
| * LISP map resolver class. |
| * Handles map-request message and acknowledges with map-reply message. |
| */ |
| public final class LispMapResolver { |
| |
| private static final Logger log = LoggerFactory.getLogger(LispMapResolver.class); |
| |
| private static final int ECM_DST_PORT = 4342; |
| private static final int NEGATIVE_REPLY_DST_PORT = 4342; |
| private static final int MAP_REPLY_RECORD_TTL = 15; |
| private static final short MAP_VERSION_NUMBER = 0; |
| private static final String NO_ITR_RLOCS_MSG = |
| "No ITR RLOC is found, cannot respond to ITR."; |
| private static final String NO_ETR_RLOCS_MSG = |
| "No ETR RLOC is found, cannot relay to ETR."; |
| private static final String NO_MAP_INFO_MSG = "Map information is not found."; |
| |
| private LispMappingDatabase mapDb = LispMappingDatabase.getInstance(); |
| |
| // non-instantiable (except for our Singleton) |
| private LispMapResolver() { |
| } |
| |
| static LispMapResolver getInstance() { |
| return SingletonHelper.INSTANCE; |
| } |
| |
| /** |
| * Handles encapsulated control message and replies with map-reply message. |
| * |
| * @param message encapsulated control message |
| * @return map-reply message |
| */ |
| List<LispMessage> processMapRequest(LispMessage message) { |
| |
| LispEncapsulatedControl ecm = (LispEncapsulatedControl) message; |
| LispMapRequest request = (LispMapRequest) ecm.getControlMessage(); |
| |
| List<LispMapRecord> mapReplyRecords = |
| mapDb.getMapRecordByEidRecords(request.getEids(), true); |
| |
| List<LispMapRecord> mapRequestRecords = |
| mapDb.getMapRecordByEidRecords(request.getEids(), false); |
| |
| if (mapReplyRecords.size() + mapRequestRecords.size() == 0) { |
| |
| List<LispMessage> mapReplies = Lists.newArrayList(); |
| |
| // build natively-forward map reply messages based on map-request from ITR |
| ReplyBuilder replyBuilder = initMapReplyBuilder(request); |
| replyBuilder.withMapRecords(getNegativeMapRecords(request.getEids())); |
| LispMessage mapReply = replyBuilder.build(); |
| mapReply.configSender(new InetSocketAddress(ecm.getSender().getAddress(), |
| NEGATIVE_REPLY_DST_PORT)); |
| mapReplies.add(mapReply); |
| |
| log.warn(NO_MAP_INFO_MSG); |
| |
| } else { |
| |
| if (!mapReplyRecords.isEmpty()) { |
| |
| List<LispMessage> mapReplies = Lists.newArrayList(); |
| |
| // build map-reply message based on map-request from ITR |
| ReplyBuilder replyBuilder = initMapReplyBuilder(request); |
| replyBuilder.withMapRecords(mapReplyRecords); |
| |
| List<InetSocketAddress> addresses = |
| getItrAddresses(request.getItrRlocs(), |
| ecm.innerUdp().getSourcePort()); |
| |
| addresses.forEach(address -> { |
| if (address != null) { |
| LispMapReply reply = replyBuilder.build(); |
| reply.configSender(address); |
| mapReplies.add(reply); |
| } else { |
| log.warn(NO_ITR_RLOCS_MSG); |
| } |
| }); |
| |
| return mapReplies; |
| } |
| |
| if (!mapRequestRecords.isEmpty()) { |
| |
| List<LispMessage> ecms = Lists.newArrayList(); |
| |
| // re-encapsulate encapsulated control message from ITR |
| List<InetSocketAddress> addresses = |
| getEtrAddresses(mapRequestRecords, ECM_DST_PORT); |
| |
| addresses.forEach(address -> { |
| if (address != null) { |
| LispEncapsulatedControl reencapEcm = cloneEcm(ecm); |
| reencapEcm.configSender(address); |
| ecms.add(reencapEcm); |
| } else { |
| log.warn(NO_ETR_RLOCS_MSG); |
| } |
| }); |
| |
| return ecms; |
| } |
| } |
| return ImmutableList.of(); |
| } |
| |
| /** |
| * Initializes MapReply builder without specifying map records. |
| * |
| * @param request received map request from ITR |
| * @return initialized MapReply builder |
| */ |
| private ReplyBuilder initMapReplyBuilder(LispMapRequest request) { |
| ReplyBuilder replyBuilder = new DefaultReplyBuilder(); |
| replyBuilder.withNonce(request.getNonce()); |
| replyBuilder.withIsEtr(false); |
| replyBuilder.withIsSecurity(false); |
| replyBuilder.withIsProbe(request.isProbe()); |
| |
| return replyBuilder; |
| } |
| |
| /** |
| * Clones ECM from original ECM. |
| * |
| * @param ecm original ECM |
| * @return cloned ECM |
| */ |
| private LispEncapsulatedControl cloneEcm(LispEncapsulatedControl ecm) { |
| EcmBuilder ecmBuilder = new DefaultEcmBuilder(); |
| ecmBuilder.innerLispMessage(ecm.getControlMessage()); |
| ecmBuilder.isSecurity(ecm.isSecurity()); |
| ecmBuilder.innerIpHeader(ecm.innerIpHeader()); |
| ecmBuilder.innerUdpHeader(ecm.innerUdp()); |
| |
| return ecmBuilder.build(); |
| } |
| |
| /** |
| * Obtains a collection of map records with natively-forward action. |
| * |
| * @param eids endpoint identifier records |
| * @return a collection of map records with natively-forward action |
| */ |
| private List<LispMapRecord> getNegativeMapRecords(List<LispEidRecord> eids) { |
| List<LispMapRecord> mapRecords = Lists.newArrayList(); |
| |
| MapRecordBuilder recordBuilder = new DefaultMapRecordBuilder(); |
| recordBuilder.withRecordTtl(MAP_REPLY_RECORD_TTL); |
| recordBuilder.withLocators(Lists.newArrayList()); |
| recordBuilder.withIsAuthoritative(false); |
| recordBuilder.withMapVersionNumber(MAP_VERSION_NUMBER); |
| recordBuilder.withAction(LispMapReplyAction.NativelyForward); |
| |
| eids.forEach(eid -> { |
| recordBuilder.withEidPrefixAfi(eid.getPrefix()); |
| recordBuilder.withMaskLength(eid.getMaskLength()); |
| mapRecords.add(recordBuilder.build()); |
| }); |
| |
| return mapRecords; |
| } |
| |
| /** |
| * Obtains a collection of valid ITR addresses with a port number specified. |
| * These addresses will be used to acknowledge map-reply to ITR. |
| * |
| * @param itrRlocs a collection of ITR RLOCs |
| * @param port port number |
| * @return a collection of valid ITR addresses with a port number specified |
| */ |
| private List<InetSocketAddress> getItrAddresses(List<LispAfiAddress> itrRlocs, |
| int port) { |
| List<InetSocketAddress> addresses = Lists.newArrayList(); |
| for (LispAfiAddress itrRloc : itrRlocs) { |
| addresses.add(new InetSocketAddress(((LispIpAddress) |
| itrRloc).getAddress().toInetAddress(), port)); |
| } |
| return addresses; |
| } |
| |
| /** |
| * Obtains a collection of valid ETR addresses with a port number specified. |
| * These addresses will be used to relay map-request to ETR. |
| * |
| * @param mapRecords a collection of map records |
| * @param port port number |
| * @return a collection of valid ETR addresses with a port number specified |
| */ |
| private List<InetSocketAddress> getEtrAddresses(List<LispMapRecord> mapRecords, |
| int port) { |
| List<InetSocketAddress> addresses = Lists.newArrayList(); |
| for (LispMapRecord mapRecord : mapRecords) { |
| |
| // we only select the first locator record in all cases... |
| LispLocator locatorRecord = mapRecord.getLocators().get(0); |
| if (locatorRecord != null) { |
| addresses.add(new InetSocketAddress(((LispIpAddress) |
| locatorRecord.getLocatorAfi()).getAddress() |
| .toInetAddress(), port)); |
| } |
| } |
| return addresses; |
| } |
| |
| /** |
| * Prevents object instantiation from external. |
| */ |
| private static final class SingletonHelper { |
| private static final String ILLEGAL_ACCESS_MSG = "Should not instantiate this class."; |
| private static final LispMapResolver INSTANCE = new LispMapResolver(); |
| |
| private SingletonHelper() { |
| throw new IllegalAccessError(ILLEGAL_ACCESS_MSG); |
| } |
| } |
| } |