blob: 3a615a565bcfeea6ce5197ac3398e2673b300ead [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Foundation
*
* 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 org.onlab.packet.IpAddress;
import org.onosproject.lisp.ctl.LispRouter;
import org.onosproject.lisp.ctl.LispRouterFactory;
import org.onosproject.lisp.ctl.impl.util.LispMapUtil;
import org.onosproject.lisp.msg.authentication.LispAuthenticationConfig;
import org.onosproject.lisp.msg.protocols.DefaultLispInfoReply.DefaultInfoReplyBuilder;
import org.onosproject.lisp.msg.protocols.DefaultLispInfoRequest.DefaultInfoRequestBuilder;
import org.onosproject.lisp.msg.protocols.DefaultLispMapNotify.DefaultNotifyBuilder;
import org.onosproject.lisp.msg.protocols.DefaultLispMapRegister.DefaultRegisterBuilder;
import org.onosproject.lisp.msg.protocols.DefaultLispMapRequest.DefaultRequestBuilder;
import org.onosproject.lisp.msg.protocols.LispEidRecord;
import org.onosproject.lisp.msg.protocols.LispInfoReply;
import org.onosproject.lisp.msg.protocols.LispInfoReply.InfoReplyBuilder;
import org.onosproject.lisp.msg.protocols.LispInfoRequest;
import org.onosproject.lisp.msg.protocols.LispInfoRequest.InfoRequestBuilder;
import org.onosproject.lisp.msg.protocols.LispMapNotify;
import org.onosproject.lisp.msg.protocols.LispMapNotify.NotifyBuilder;
import org.onosproject.lisp.msg.protocols.LispMapRecord;
import org.onosproject.lisp.msg.protocols.LispMapRegister;
import org.onosproject.lisp.msg.protocols.LispMapRegister.RegisterBuilder;
import org.onosproject.lisp.msg.protocols.LispMapRequest;
import org.onosproject.lisp.msg.protocols.LispMapRequest.RequestBuilder;
import org.onosproject.lisp.msg.protocols.LispMessage;
import org.onosproject.lisp.msg.types.LispAfiAddress;
import org.onosproject.lisp.msg.types.LispIpv4Address;
import org.onosproject.lisp.msg.types.LispIpv6Address;
import org.onosproject.lisp.msg.types.lcaf.LispNatLcafAddress.NatAddressBuilder;
import org.onosproject.lisp.msg.types.LispNoAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.onlab.packet.IpAddress.valueOf;
import static org.onosproject.lisp.msg.authentication.LispAuthenticationKeyEnum.valueOf;
/**
* LISP map server class.
* Handles map-register message and acknowledges with map-notify message.
*/
public final class LispMapServer {
private static final Logger log = LoggerFactory.getLogger(LispMapServer.class);
private static final int MAP_NOTIFY_PORT = 4342;
private static final int INFO_REPLY_PORT = 4342;
private static final String INVALID_AUTHENTICATION_DATA_MSG =
"Unmatched authentication data of {}.";
private static final String FAILED_TO_FORMULATE_NAT_MSG =
"Fails during formulate NAT address.";
private boolean enableSmr = false;
private LispMappingDatabase mapDb = LispExpireMapDatabase.getInstance();
private LispAuthenticationConfig authConfig = LispAuthenticationConfig.getInstance();
// non-instantiable (except for our Singleton)
private LispMapServer() {
}
static LispMapServer getInstance() {
return SingletonHelper.INSTANCE;
}
/**
* Enable LISP Map server sends SMR(Solicit Map Request) message.
*
* @param enable whether enable or disable sending SMR
*/
public void enableSmr(boolean enable) {
this.enableSmr = enable;
}
/**
* Handles map-register message and replies with map-notify message.
*
* @param message map-register message
* @return map-notify message
*/
LispMapNotify processMapRegister(LispMessage message) {
LispMapRegister register = (LispMapRegister) message;
if (!checkMapRegisterAuthData(register)) {
log.warn(INVALID_AUTHENTICATION_DATA_MSG, "Map-Register");
return null;
}
register.getMapRecords().forEach(mapRecord -> {
LispEidRecord eidRecord =
new LispEidRecord(mapRecord.getMaskLength(),
mapRecord.getEidPrefixAfi());
LispMapRecord oldMapRecord = mapDb.getMapRecordByEidRecord(eidRecord,
register.isProxyMapReply());
if (oldMapRecord == null) {
mapDb.putMapRecord(eidRecord, mapRecord, register.isProxyMapReply());
} else {
if (oldMapRecord.getMapVersionNumber() <= mapRecord.getMapVersionNumber()) {
mapDb.putMapRecord(eidRecord, mapRecord, register.isProxyMapReply());
if (enableSmr) {
sendSmrMessage(eidRecord);
}
}
}
});
// we only acknowledge back to ETR when want-map-notify bit is set to true
// otherwise, we do not acknowledge back to ETR
if (register.isWantMapNotify()) {
NotifyBuilder notifyBuilder = new DefaultNotifyBuilder();
notifyBuilder.withKeyId(authConfig.lispAuthKeyId());
notifyBuilder.withAuthDataLength(valueOf(authConfig.lispAuthKeyId()).getHashLength());
notifyBuilder.withAuthKey(authConfig.lispAuthKey());
notifyBuilder.withNonce(register.getNonce());
notifyBuilder.withMapRecords(register.getMapRecords());
LispMapNotify notify = notifyBuilder.build();
InetSocketAddress address =
new InetSocketAddress(register.getSender().getAddress(), MAP_NOTIFY_PORT);
notify.configSender(address);
return notify;
}
return null;
}
/**
* Handles info-request message and replies with info-reply message.
*
* @param message info-request message
* @return info-reply message
*/
LispInfoReply processInfoRequest(LispMessage message) {
LispInfoRequest request = (LispInfoRequest) message;
if (!checkInfoRequestAuthData(request)) {
log.warn(INVALID_AUTHENTICATION_DATA_MSG, "Info-Request");
return null;
}
NatAddressBuilder natBuilder = new NatAddressBuilder();
try {
LispAfiAddress msAddress =
new LispIpv4Address(valueOf(InetAddress.getLocalHost()));
natBuilder.withMsRlocAddress(msAddress);
natBuilder.withMsUdpPortNumber((short) INFO_REPLY_PORT);
// try to extract global ETR RLOC address from info-request
IpAddress globalRlocIp = valueOf(request.getSender().getAddress());
LispAfiAddress globalRlocAddress;
if (globalRlocIp.isIp4()) {
globalRlocAddress = new LispIpv4Address(globalRlocIp);
} else {
globalRlocAddress = new LispIpv6Address(globalRlocIp);
}
natBuilder.withGlobalEtrRlocAddress(globalRlocAddress);
natBuilder.withEtrUdpPortNumber((short) request.getSender().getPort());
natBuilder.withPrivateEtrRlocAddress(new LispNoAddress());
// TODO: need to specify RTR addresses
} catch (UnknownHostException e) {
log.warn(FAILED_TO_FORMULATE_NAT_MSG, e);
}
InfoReplyBuilder replyBuilder = new DefaultInfoReplyBuilder();
replyBuilder.withKeyId(request.getKeyId());
replyBuilder.withAuthDataLength(valueOf(authConfig.lispAuthKeyId()).getHashLength());
replyBuilder.withAuthKey(authConfig.lispAuthKey());
replyBuilder.withNonce(request.getNonce());
replyBuilder.withEidPrefix(request.getPrefix());
replyBuilder.withMaskLength(request.getMaskLength());
replyBuilder.withTtl(request.getTtl());
replyBuilder.withNatLcafAddress(natBuilder.build());
replyBuilder.withIsInfoReply(true);
LispInfoReply reply = replyBuilder.build();
reply.configSender(request.getSender());
return reply;
}
/**
* Checks the integrity of the received map-register message by calculating
* authentication data from received map-register message.
*
* @param register map-register message
* @return evaluation result
*/
private boolean checkMapRegisterAuthData(LispMapRegister register) {
RegisterBuilder registerBuilder = new DefaultRegisterBuilder();
registerBuilder.withKeyId(register.getKeyId());
registerBuilder.withAuthKey(authConfig.lispAuthKey());
registerBuilder.withNonce(register.getNonce());
registerBuilder.withIsProxyMapReply(register.isProxyMapReply());
registerBuilder.withIsWantMapNotify(register.isWantMapNotify());
registerBuilder.withMapRecords(register.getMapRecords());
LispMapRegister authRegister = registerBuilder.build();
return Arrays.equals(authRegister.getAuthData(), register.getAuthData());
}
/**
* Checks the integrity of the received info-request message by calculating
* authentication data from received info-request message.
*
* @param request info-request message
* @return evaluation result
*/
private boolean checkInfoRequestAuthData(LispInfoRequest request) {
InfoRequestBuilder requestBuilder = new DefaultInfoRequestBuilder();
requestBuilder.withKeyId(request.getKeyId());
requestBuilder.withAuthKey(authConfig.lispAuthKey());
requestBuilder.withNonce(request.getNonce());
requestBuilder.withTtl(request.getTtl());
requestBuilder.withEidPrefix(request.getPrefix());
requestBuilder.withIsInfoReply(request.isInfoReply());
requestBuilder.withMaskLength(request.getMaskLength());
LispInfoRequest authRequest = requestBuilder.build();
return Arrays.equals(authRequest.getAuthData(), request.getAuthData());
}
/**
* Sends SMR (Solicit Map Request) to their subscribers.
*
* @param eidRecord the updated EID
*/
private void sendSmrMessage(LispEidRecord eidRecord) {
RequestBuilder builder = new DefaultRequestBuilder();
LispAfiAddress msAddress = null;
try {
msAddress = new LispIpv4Address(IpAddress.valueOf(InetAddress.getLocalHost()));
} catch (UnknownHostException e) {
log.warn("Source EID is not found, {}", e.getMessage());
}
LispMapRequest msg = builder.withIsSmr(true)
.withIsSmrInvoked(true)
.withIsProbe(false)
.withIsPitr(false)
.withIsAuthoritative(false)
.withIsMapDataPresent(false)
.withSourceEid(msAddress)
.withEidRecords(ImmutableList.of(eidRecord))
.build();
LispRouterFactory routerFactory = LispRouterFactory.getInstance();
Collection<LispRouter> routers = routerFactory.getRouters();
routers.forEach(router -> {
if (isInEidRecordRange(eidRecord, router.getEidRecords())) {
router.sendMessage(msg);
}
});
}
private boolean isInEidRecordRange(LispEidRecord originalRecord, List<LispEidRecord> records) {
for (LispEidRecord record : records) {
return LispMapUtil.isInRange(record, originalRecord) ||
LispMapUtil.isInRange(originalRecord, record);
}
return false;
}
/**
* 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 LispMapServer INSTANCE = new LispMapServer();
private SingletonHelper() {
throw new IllegalAccessError(ILLEGAL_ACCESS_MSG);
}
}
}