initial commit
diff --git a/src/main/java/org.onosproject.xran/controller/Controller.java b/src/main/java/org.onosproject.xran/controller/Controller.java
new file mode 100644
index 0000000..b5b7f7d
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/Controller.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015-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.xran.controller;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.sctp.SctpChannel;
+import io.netty.channel.sctp.nio.NioSctpServerChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by dimitris on 7/27/17.
+ */
+public class Controller {
+ protected static final Logger log = LoggerFactory.getLogger(Controller.class);
+ protected XranDeviceAgent deviceAgent;
+ protected XranHostAgent hostAgent;
+ protected XranPacketProcessor packetAgent;
+ private EventLoopGroup bossGroup;
+ private EventLoopGroup workerGroup;
+ private ChannelFuture channel;
+ private int port = 8007;
+ private boolean isRunning = false;
+
+ public void run() {
+ final Controller ctrl = this;
+ try {
+ ServerBootstrap b = createServerBootStrap();
+ b.childHandler(new ChannelInitializer<SctpChannel>() {
+ @Override
+ public void initChannel(SctpChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ //new LoggingHandler(LogLevel.INFO),
+ new XranChannelHandler(ctrl)
+ );
+ }
+ });
+ channel = b.bind(this.port).sync();
+ } catch (Exception e) {
+ log.warn(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private ServerBootstrap createServerBootStrap() {
+ bossGroup = new NioEventLoopGroup(1);
+ workerGroup = new NioEventLoopGroup();
+
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioSctpServerChannel.class)
+ .handler(new LoggingHandler(LogLevel.INFO));
+ return b;
+ }
+
+ public void start(XranDeviceAgent deviceAgent, XranHostAgent hostAgent, XranControllerImpl.InternalXranPacketAgent packetAgent, int port) {
+ if (isRunning && this.port != port) {
+ stop();
+ this.deviceAgent = deviceAgent;
+ this.hostAgent = hostAgent;
+ this.packetAgent = packetAgent;
+ this.port = port;
+ run();
+ } else if (!isRunning) {
+ this.deviceAgent = deviceAgent;
+ this.hostAgent = hostAgent;
+ this.packetAgent = packetAgent;
+ this.port = port;
+ run();
+ isRunning = true;
+ }
+ }
+
+
+ public void stop() {
+ if (isRunning) {
+ channel.channel().close();
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ isRunning = false;
+ }
+ }
+}
diff --git a/src/main/java/org.onosproject.xran/controller/XranChannelHandler.java b/src/main/java/org.onosproject.xran/controller/XranChannelHandler.java
new file mode 100644
index 0000000..15c1e0b
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/XranChannelHandler.java
@@ -0,0 +1,135 @@
+/*
+ * 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.xran.controller;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler.Sharable;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.sctp.SctpMessage;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.onosproject.net.DeviceId;
+import org.onosproject.xran.codecs.pdu.XrancPdu;
+import org.openmuc.jasn1.ber.BerByteArrayOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.DatatypeConverter;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+
+import static org.onosproject.net.DeviceId.deviceId;
+
+/**
+ * Created by dimitris on 7/20/17.
+ */
+
+@Sharable
+public class XranChannelHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(XranChannelHandler.class);
+
+ private final Controller controller;
+
+ XranChannelHandler(Controller controller) {
+ this.controller = controller;
+ }
+
+ public static SctpMessage getSctpMessage(XrancPdu pdu) throws IOException {
+ BerByteArrayOutputStream os = new BerByteArrayOutputStream(4096);
+
+ pdu.encode(os);
+
+ log.debug("Sending message: {}", pdu);
+ final ByteBuf buf = Unpooled.buffer(os.getArray().length);
+ for (int i = 0; i < buf.capacity(); i++) {
+ buf.writeByte(os.getArray()[i]);
+ }
+ return new SctpMessage(0, 0, buf);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws IOException, URISyntaxException {
+ final SocketAddress address = ctx.channel().remoteAddress();
+ if (!(address instanceof InetSocketAddress)) {
+ log.warn("Invalid client connection. Xran Cell is indentifed based on IP");
+ ctx.close();
+ return;
+ }
+
+ final InetSocketAddress inetAddress = (InetSocketAddress) address;
+ final String host = inetAddress.getHostString();
+ log.info("New client connected to the Server: {}", host);
+
+ log.info("Adding device...");
+
+ controller.deviceAgent.addConnectedCell(host, ctx);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
+ SctpMessage sctpMessage = (SctpMessage) msg;
+ ByteBuf byteBuf = sctpMessage.content();
+
+ byte[] bytes = new byte[byteBuf.readableBytes()];
+ byteBuf.readBytes(bytes);
+
+ XrancPdu recv_pdu = new XrancPdu();
+
+ InputStream inputStream = new ByteArrayInputStream(bytes);
+
+ recv_pdu.decode(inputStream);
+
+ controller.packetAgent.handlePacket(recv_pdu, ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+ log.info("Client dropped the connection");
+
+ final SocketAddress address = ctx.channel().remoteAddress();
+ if (!(address instanceof InetSocketAddress)) {
+ log.warn("Invalid client connection. Xran Cell is indentifed based on IP");
+ ctx.close();
+ return;
+ }
+
+ final InetSocketAddress inetAddress = (InetSocketAddress) address;
+ final String host = inetAddress.getHostString();
+
+ controller.deviceAgent.removeConnectedCell(host);
+
+ ctx.close();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ // Close the connection when an exception is raised.
+ log.warn("exceptionCaught: {}", ExceptionUtils.getStackTrace(cause));
+ cause.printStackTrace();
+ ctx.close();
+ }
+}
+
diff --git a/src/main/java/org.onosproject.xran/controller/XranController.java b/src/main/java/org.onosproject.xran/controller/XranController.java
new file mode 100644
index 0000000..19b7537
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/XranController.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015-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.xran.controller;
+
+import org.onosproject.xran.providers.XranDeviceListener;
+import org.onosproject.xran.providers.XranHostListener;
+
+/**
+ * Created by dimitris on 7/27/17.
+ */
+public interface XranController {
+
+ void addListener(XranDeviceListener listener);
+
+ void addListener(XranHostListener listener);
+
+ void removeListener(XranDeviceListener listener);
+
+ void removeListener(XranHostListener listener);
+}
diff --git a/src/main/java/org.onosproject.xran/controller/XranControllerImpl.java b/src/main/java/org.onosproject.xran/controller/XranControllerImpl.java
new file mode 100644
index 0000000..36b4c07
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/XranControllerImpl.java
@@ -0,0 +1,787 @@
+/*
+ * Copyright 2015-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.xran.controller;
+
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.sctp.SctpMessage;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.felix.scr.annotations.*;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.config.*;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.xran.XranStore;
+import org.onosproject.xran.codecs.api.*;
+import org.onosproject.xran.codecs.pdu.*;
+import org.onosproject.xran.entities.RnibCell;
+import org.onosproject.xran.entities.RnibLink;
+import org.onosproject.xran.entities.RnibUe;
+import org.onosproject.xran.identifiers.LinkId;
+import org.onosproject.xran.impl.XranConfig;
+import org.onosproject.xran.providers.XranDeviceListener;
+import org.onosproject.xran.providers.XranHostListener;
+import org.onosproject.xran.samplemessages.*;
+import org.onosproject.xran.wrapper.CellMap;
+import org.onosproject.xran.wrapper.LinkMap;
+import org.onosproject.xran.wrapper.UeMap;
+import org.openmuc.jasn1.ber.types.BerInteger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.stream.Collectors;
+
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.xran.entities.RnibCell.decodeDeviceId;
+import static org.onosproject.xran.entities.RnibCell.uri;
+import static org.onosproject.xran.entities.RnibUe.UeState;
+import static org.onosproject.xran.entities.RnibUe.hostIdtoMME;
+
+/**
+ * Created by dimitris on 7/20/17.
+ */
+@Component(immediate = true)
+@Service
+public class XranControllerImpl implements XranController {
+ private static final String XRAN_APP_ID = "org.onosproject.xran";
+ private static final Class<XranConfig> CONFIG_CLASS = XranConfig.class;
+
+ private static final Logger log =
+ LoggerFactory.getLogger(XranControllerImpl.class);
+ /* CONFIG */
+ private final InternalNetworkConfigListener configListener =
+ new InternalNetworkConfigListener();
+ /* VARIABLES */
+ private final Controller controller = new Controller();
+ private XranConfig xranConfig;
+ private ApplicationId appId;
+ /* Services */
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private NetworkConfigRegistry registry;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private NetworkConfigService configService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private CoreService coreService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private XranStore xranStore;
+ private ConfigFactory<ApplicationId, XranConfig> xranConfigFactory =
+ new ConfigFactory<ApplicationId, XranConfig>(
+ SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "xran") {
+ @Override
+ public XranConfig createConfig() {
+ return new XranConfig();
+ }
+ };
+ /* WRAPPERS */
+ private CellMap cellMap;
+ private UeMap ueMap;
+ private LinkMap linkMap;
+ /* MAPS */
+ private ConcurrentMap<String, ECGI> legitCells = new ConcurrentHashMap<>();
+ /* AGENTS */
+ private InternalXranDeviceAgent deviceAgent = new InternalXranDeviceAgent();
+ private InternalXranHostAgent hostAgent = new InternalXranHostAgent();
+ private InternalXranPacketAgent packetAgent = new InternalXranPacketAgent();
+ /* LISTENERS */
+ private Set<XranDeviceListener> xranDeviceListeners = new CopyOnWriteArraySet<>();
+ private Set<XranHostListener> xranHostListeners = new CopyOnWriteArraySet<>();
+ private InternalDeviceListener device_listener = new InternalDeviceListener();
+ private InternalHostListener host_listener = new InternalHostListener();
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication(XRAN_APP_ID);
+
+ configService.addListener(configListener);
+ registry.registerConfigFactory(xranConfigFactory);
+ deviceService.addListener(device_listener);
+ hostService.addListener(host_listener);
+
+ cellMap = new CellMap(xranStore);
+ ueMap = new UeMap(xranStore);
+ linkMap = new LinkMap(xranStore);
+
+ xranStore.setController(this);
+
+ log.info("XRAN Controller Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ controller.stop();
+
+ deviceService.removeListener(device_listener);
+ hostService.removeListener(host_listener);
+
+ legitCells.clear();
+
+ configService.removeListener(configListener);
+ registry.unregisterConfigFactory(xranConfigFactory);
+
+ log.info("XRAN Controller Stopped");
+ }
+
+ @Override
+ public void addListener(XranDeviceListener listener) {
+ xranDeviceListeners.add(listener);
+ }
+
+ @Override
+ public void addListener(XranHostListener listener) {
+ xranHostListeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(XranDeviceListener listener) {
+ xranDeviceListeners.remove(listener);
+ }
+
+ @Override
+ public void removeListener(XranHostListener listener) {
+ xranHostListeners.remove(listener);
+ }
+
+ private void restartTimer(RnibUe ue) {
+ Timer timer = new Timer();
+ ue.setTimer(timer);
+ log.info("Starting UE timer...");
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ if (ue.getUeState() == UeState.IDLE) {
+ hostAgent.removeConnectedHost(ue);
+ log.info("UE is removed after 10 seconds of IDLE");
+ } else {
+ log.info("UE not removed cause its ACTIVE");
+ }
+ }
+ }, 10000);
+ }
+
+ private void restartTimer(RnibLink link) {
+ Timer timer = new Timer();
+ link.setTimer(timer);
+ log.info("Starting Link timer...");
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ LinkId linkId = link.getLinkId();
+ xranStore.removeLink(linkId);
+ log.info("Link is removed after not receiving Meas Reports for 10 seconds");
+ }
+ }, 10000);
+
+ }
+
+ class InternalDeviceListener implements DeviceListener {
+
+ @Override
+ public void event(DeviceEvent event) {
+ log.info("Device Event {}", event);
+ switch (event.type()) {
+ case DEVICE_ADDED: {
+ try {
+ ECGI ecgi = decodeDeviceId(event.subject().id());
+ RnibCell cell = cellMap.get(ecgi);
+ if (cell != null) {
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(
+ new TimerTask() {
+ @Override
+ public void run() {
+ CellConfigReport conf = cell.getConf();
+ if (conf == null) {
+ try {
+ ChannelHandlerContext ctx = cellMap.getCtx(ecgi);
+ SctpMessage msg = XranChannelHandler.getSctpMessage(ConfigEncoderDecoder.constructPacket(ecgi));
+ ctx.writeAndFlush(msg);
+ } catch (IOException e) {
+ log.error(ExceptionUtils.getFullStackTrace(e));
+ e.printStackTrace();
+ }
+ } else {
+ // FIXME: maybe remove this map.
+ cellMap.putPciArfcn(cell);
+ try {
+ ChannelHandlerContext ctx = cellMap.
+ getCtx(ecgi);
+ XrancPdu xrancPdu = L2MeasConf.constructPacket(ecgi, xranConfig.getL2MeasInterval());
+ cell.setMeasConfig(xrancPdu.getBody().getL2MeasConfig());
+ SctpMessage sctpMessage = XranChannelHandler.
+ getSctpMessage(xrancPdu);
+ ctx.writeAndFlush(sctpMessage);
+ } catch (IOException e) {
+ log.error(ExceptionUtils.getFullStackTrace(e));
+ e.printStackTrace();
+ }
+ timer.cancel();
+ timer.purge();
+ }
+ }
+ },
+ 0,
+ xranConfig.getConfigRequestInterval() * 1000
+ );
+ }
+ } catch (IOException e) {
+ log.error(ExceptionUtils.getFullStackTrace(e));
+ e.printStackTrace();
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ class InternalHostListener implements HostListener {
+
+ @Override
+ public void event(HostEvent event) {
+ log.info("Host Event {}", event);
+ switch (event.type()) {
+ case HOST_ADDED:
+ case HOST_MOVED: {
+ RnibUe ue = ueMap.get(hostIdtoMME(event.subject().id()));
+ if (ue != null) {
+ ECGI ecgi_primary = linkMap.getPrimaryCell(ue);
+ RnibCell primary = cellMap.get(ecgi_primary);
+ ue.setMeasConfig(null);
+ if (primary != null) {
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(
+ new TimerTask() {
+ @Override
+ public void run() {
+ if (ue.getCapability() == null) {
+ try {
+ ChannelHandlerContext ctx = cellMap.getCtx(primary.getEcgi());
+ SctpMessage msg = XranChannelHandler.
+ getSctpMessage(UECapabilityEnq.constructPacket(
+ primary.getEcgi(),
+ ue.getRanId()));
+ ctx.writeAndFlush(msg);
+ } catch (IOException e) {
+ log.warn(ExceptionUtils.getFullStackTrace(e));
+ e.printStackTrace();
+ }
+ } else {
+ if (ue.getMeasConfig() == null) {
+ try {
+ ChannelHandlerContext ctx = cellMap.getCtx(primary.getEcgi());
+ RXSigMeasConfig.MeasCells measCells = new RXSigMeasConfig.MeasCells();
+ xranStore.getCellNodes().forEach(cell -> {
+ CellConfigReport cellReport = cell.getConf();
+ if (cellReport != null) {
+ PCIARFCN pciarfcn = new PCIARFCN();
+ pciarfcn.setPci(cellReport.getPci());
+ pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
+ measCells.setPCIARFCN(pciarfcn);
+ }
+ });
+ XrancPdu xrancPdu = SignalMeasConfig.constructPacket(
+ primary.getEcgi(),
+ ue.getRanId(),
+ measCells,
+ xranConfig.getRxSignalInterval()
+ );
+ ue.setMeasConfig(xrancPdu.getBody().getRXSigMeasConfig());
+ SctpMessage msg = XranChannelHandler.getSctpMessage(xrancPdu);
+ ctx.writeAndFlush(msg);
+ } catch (IOException e) {
+ log.warn(ExceptionUtils.getFullStackTrace(e));
+ e.printStackTrace();
+ }
+ }
+ timer.cancel();
+ timer.purge();
+ }
+ }
+ },
+ 0,
+ xranConfig.getConfigRequestInterval() * 1000
+ );
+ }
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ public class InternalXranDeviceAgent implements XranDeviceAgent {
+
+ private final Logger log = LoggerFactory.getLogger(InternalXranDeviceAgent.class);
+
+ @Override
+ public boolean addConnectedCell(String host, ChannelHandlerContext ctx) {
+ ECGI ecgi = legitCells.get(host);
+
+ if (ecgi == null) {
+ log.error("Device is not a legit source; ignoring...");
+ } else {
+ log.info("Device exists in configuration; registering...");
+ RnibCell storeCell = cellMap.get(ecgi);
+ if (storeCell == null) {
+ storeCell = new RnibCell();
+ storeCell.setEcgi(ecgi);
+ cellMap.put(storeCell, ctx);
+
+ for (XranDeviceListener l : xranDeviceListeners) {
+ l.deviceAdded(storeCell);
+ }
+ return true;
+ } else {
+ log.error("Device already registered; ignoring...");
+ }
+ }
+ ctx.close();
+ return false;
+ }
+
+ @Override
+ public boolean removeConnectedCell(String host) {
+ ECGI ecgi = legitCells.get(host);
+ List<RnibLink> linksByECGI = xranStore.getLinksByECGI(ecgi);
+
+ linksByECGI.forEach(rnibLink -> xranStore.removeLink(rnibLink.getLinkId()));
+
+ if (cellMap.remove(ecgi)) {
+ for (XranDeviceListener l : xranDeviceListeners) {
+ l.deviceRemoved(deviceId(uri(ecgi)));
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public class InternalXranHostAgent implements XranHostAgent {
+
+ @Override
+ public boolean addConnectedHost(RnibUe ue, RnibCell cell, ChannelHandlerContext ctx) {
+
+ if (ueMap.get(ue.getMmeS1apId()) != null) {
+ linkMap.putPrimaryLink(cell, ue);
+
+ Set<ECGI> ecgiSet = xranStore.getLinksByUeId(ue.getMmeS1apId().longValue())
+ .stream()
+ .map(l -> l.getLinkId().getSource())
+ .collect(Collectors.toSet());
+
+ for (XranHostListener l : xranHostListeners) {
+ l.hostAdded(ue, ecgiSet);
+ }
+ return true;
+ } else {
+ ueMap.put(ue);
+ linkMap.putPrimaryLink(cell, ue);
+
+ Set<ECGI> ecgiSet = Sets.newConcurrentHashSet();
+ ecgiSet.add(cell.getEcgi());
+ for (XranHostListener l : xranHostListeners) {
+ l.hostAdded(ue, ecgiSet);
+ }
+ return true;
+ }
+
+ }
+
+ @Override
+ public boolean removeConnectedHost(RnibUe ue) {
+ List<RnibLink> links = xranStore.getLinksByUeId(ue.getMmeS1apId().longValue());
+ links.forEach(rnibLink -> xranStore.removeLink(rnibLink.getLinkId()));
+ if (ueMap.remove(ue.getMmeS1apId())) {
+ for (XranHostListener l : xranHostListeners) {
+ l.hostRemoved(ue.getHostId());
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public class InternalXranPacketAgent implements XranPacketProcessor {
+ @Override
+ public void handlePacket(XrancPdu recv_pdu, ChannelHandlerContext ctx) throws IOException {
+ XrancPdu send_pdu;
+
+ int apiID = recv_pdu.getHdr().getApiId().intValue();
+ log.debug("Received message: {}", recv_pdu);
+ switch (apiID) {
+ case 1: {
+ // Decode Cell config report.
+ CellConfigReport report = recv_pdu.getBody().getCellConfigReport();
+
+ ECGI ecgi = report.getEcgi();
+
+ RnibCell cell = xranStore.getCell(ecgi);
+ cell.setConf(report);
+
+ break;
+ }
+ case 2: {
+ // Decode UE Admission Request.
+ UEAdmissionRequest ueAdmissionRequest = recv_pdu.getBody().getUEAdmissionRequest();
+
+ ECGI ecgi = ueAdmissionRequest.getEcgi();
+ if (xranStore.getCell(ecgi) != null) {
+ CRNTI crnti = ueAdmissionRequest.getCrnti();
+ send_pdu = UEAdmEncoderDecoder.constructPacket(ecgi, crnti);
+ ctx.writeAndFlush(XranChannelHandler.getSctpMessage(send_pdu));
+ } else {
+ log.warn("Could not find ECGI in registered cells: {}", ecgi);
+ }
+ break;
+ }
+ case 4: {
+ // Decode UE Admission Status.
+ UEAdmissionStatus ueAdmissionStatus = recv_pdu.getBody().getUEAdmissionStatus();
+
+ RnibUe ue = ueMap.get(ueAdmissionStatus.getCrnti());
+ if (ue != null) {
+ if (ueAdmissionStatus.getAdmEstStatus().value.intValue() == 0) {
+ ue.setUeState(UeState.ACTIVE);
+ } else {
+ ue.setUeState(UeState.IDLE);
+ }
+ }
+ break;
+ }
+ case 5: {
+ // Decode UE Admission Context Update.
+ UEAttachComplete ueAttachComplete = recv_pdu.getBody().getUEAttachComplete();
+
+ RnibCell cell = xranStore.getCell(ueAttachComplete.getEcgi());
+
+ RnibUe ue = ueMap.get(ueAttachComplete.getMMEUES1APID());
+ if (ueMap.get(ueAttachComplete.getMMEUES1APID()) == null) {
+ ue = new RnibUe();
+ }
+
+ ue.setMmeS1apId(ueAttachComplete.getMMEUES1APID());
+ ue.setEnbS1apId(ueAttachComplete.getENBUES1APID());
+ ue.setRanId(ueAttachComplete.getCrnti());
+
+ hostAgent.addConnectedHost(ue, cell, ctx);
+ break;
+ }
+ case 6: {
+ // Decode UE Reconfig_Ind.
+ UEReconfigInd ueReconfigInd = recv_pdu.getBody().getUEReconfigInd();
+ RnibUe ue = ueMap.get(ueReconfigInd.getCrntiOld());
+
+ if (ue != null) {
+ ue.setRanId(ueReconfigInd.getCrntiNew());
+ } else {
+ log.warn("Could not find UE with this CRNTI: {}", ueReconfigInd.getCrntiOld());
+ }
+ break;
+ }
+ case 7: {
+ // If xRANc wants to deactivate UE, we pass UEReleaseInd from xRANc to eNB.
+ // Decode UE Release_Ind.
+ UEReleaseInd ueReleaseInd = recv_pdu.getBody().getUEReleaseInd();
+ RnibUe ue = ueMap.get(ueReleaseInd.getCrnti());
+ if (ue != null) {
+ ue.setUeState(UeState.IDLE);
+ restartTimer(ue);
+ }
+ break;
+ }
+ case 8: {
+ // Decode Bearer Adm Request
+ BearerAdmissionRequest bearerAdmissionRequest = recv_pdu.getBody().getBearerAdmissionRequest();
+
+ ECGI ecgi = bearerAdmissionRequest.getEcgi();
+ CRNTI crnti = bearerAdmissionRequest.getCrnti();
+ ERABParams erabParams = bearerAdmissionRequest.getErabParams();
+ RnibLink link = linkMap.get(ecgi, crnti);
+ if (link != null) {
+ link.setBearerParameters(erabParams);
+ } else {
+ log.warn("Could not find link between {}-{}", ecgi, crnti);
+ }
+
+ BerInteger numErabs = bearerAdmissionRequest.getNumErabs();
+
+ send_pdu = BearerEncoderDecoder.constructPacket(ecgi, crnti, erabParams, numErabs);
+ // Encode and send Bearer Admission Response - API ID 9
+ ctx.writeAndFlush(XranChannelHandler.getSctpMessage(send_pdu));
+ break;
+ }
+ case 10: {
+ //Decode Bearer Admission Status
+ BearerAdmissionStatus bearerAdmissionStatus = recv_pdu.getBody().getBearerAdmissionStatus();
+
+// ECGI ecgi = bearerAdmissionStatus.getEcgi();
+// CRNTI crnti = bearerAdmissionStatus.getCrnti();
+//
+// RnibLink link = linkMap.get(ecgi, crnti);
+
+ break;
+ }
+ case 11: {
+ //Decode Bearer Release Ind
+ BearerReleaseInd bearerReleaseInd = recv_pdu.getBody().getBearerReleaseInd();
+
+ ECGI ecgi = bearerReleaseInd.getEcgi();
+ CRNTI crnti = bearerReleaseInd.getCrnti();
+ RnibLink link = linkMap.get(ecgi, crnti);
+
+ List<ERABID> erabidsRelease = bearerReleaseInd.getErabIds().getERABID();
+ List<ERABParamsItem> erabParamsItem = link.getBearerParameters().getERABParamsItem();
+
+ List<ERABParamsItem> unreleased = erabParamsItem
+ .stream()
+ .filter(item -> {
+ Optional<ERABID> any = erabidsRelease.stream().filter(id -> id.equals(item.getId())).findAny();
+ return !any.isPresent();
+ }).collect(Collectors.toList());
+
+ link.getBearerParameters().setERABParamsItem(new ArrayList<>(unreleased));
+
+ break;
+ }
+ case 12: {
+ // Don't know what will invoke sending UE CAPABILITY ENQUIRY
+ // Encode and send UE CAPABILITY ENQUIRY
+ UECapabilityEnquiry ueCapabilityEnquiry = recv_pdu.getBody().getUECapabilityEnquiry();
+ XrancPdu xrancPdu = UECapabilityEnq.constructPacket(ueCapabilityEnquiry.getEcgi(), ueCapabilityEnquiry.getCrnti());
+ ctx.writeAndFlush(XranChannelHandler.getSctpMessage(xrancPdu));
+ break;
+ }
+ case 13: {
+ // Decode UE Capability Info
+ UECapabilityInfo capabilityInfo = recv_pdu.getBody().getUECapabilityInfo();
+
+ RnibUe ue = ueMap.get(capabilityInfo.getCrnti());
+ if (ue != null) {
+ ue.setCapability(capabilityInfo);
+ } else {
+ log.warn("Could not find UE with this CRNTI: {}", capabilityInfo.getCrnti());
+ }
+ break;
+
+ //14, 15, 16 are handoff
+ }
+ case 18: {
+ // Decode RX Sig Meas Report.
+ RXSigMeasReport rxSigMeasReport = recv_pdu.getBody().getRXSigMeasReport();
+ List<RXSigReport> rxSigReportList = rxSigMeasReport.getCellMeasReports().getRXSigReport();
+
+ if (!rxSigReportList.isEmpty()) {
+ rxSigReportList.forEach(rxSigReport -> {
+ RnibCell cell = cellMap.get(rxSigReport.getPciArfcn());
+ if (cell != null) {
+ ECGI ecgi = cell.getEcgi();
+ RnibLink link = linkMap.get(ecgi, rxSigMeasReport.getCrnti());
+ if (link == null) {
+ log.warn("Could not find link between: {}-{} | Creating non-serving link..", ecgi, rxSigMeasReport.getCrnti());
+ link = linkMap.putNonServingLink(cell, rxSigMeasReport.getCrnti());
+
+ if (link != null) {
+ restartTimer(link);
+ }
+ }
+
+ if (link != null) {
+ RSRQRange rsrq = rxSigReport.getRsrq();
+ RSRPRange rsrp = rxSigReport.getRsrp();
+
+ RnibLink.LinkQuality quality = link.getQuality();
+ quality.setRsrp(rsrp.value.intValue() - 140);
+ quality.setRsrq((rsrq.value.intValue() * 0.5) - 19.5);
+ }
+ } else {
+ log.warn("Could not find cell with PCI-ARFCN: {}", rxSigReport.getPciArfcn());
+ }
+ });
+ }
+ break;
+ }
+ case 20: {
+ RadioMeasReportPerUE radioMeasReportPerUE = recv_pdu.getBody().getRadioMeasReportPerUE();
+
+ List<RadioRepPerServCell> servCells = radioMeasReportPerUE.getRadioReportServCells().getRadioRepPerServCell();
+
+ servCells.forEach(servCell -> {
+ RnibCell cell = cellMap.get(servCell.getPciArfcn());
+ if (cell != null) {
+ RnibLink link = linkMap.get(cell.getEcgi(), radioMeasReportPerUE.getCrnti());
+ if (link != null) {
+ RadioRepPerServCell.CqiHist cqiHist = servCell.getCqiHist();
+ RnibLink.LinkQuality quality = link.getQuality();
+ quality.setCqiHist(cqiHist);
+
+ final double[] values = {0, 0, 0};
+ int i = 1;
+ cqiHist.getBerInteger().forEach(value -> {
+ values[0] = Math.max(values[0], value.intValue());
+ values[1] += i * value.intValue();
+ values[2] += value.intValue();
+ });
+
+ quality.setCqiMode(values[0]);
+ quality.setCqiMean(values[1] / values[2]);
+
+ } else {
+ log.warn("Could not find link between: {}-{}", cell.getEcgi(), radioMeasReportPerUE.getCrnti());
+ }
+ } else {
+ log.warn("Could not find cell with PCI-ARFCN: {}", servCell.getPciArfcn());
+ }
+ });
+
+ }
+ case 21: {
+ RadioMeasReportPerCell radioMeasReportPerCell = recv_pdu.getBody().getRadioMeasReportPerCell();
+ break;
+ }
+ case 22: {
+ SchedMeasReportPerUE schedMeasReportPerUE = recv_pdu.getBody().getSchedMeasReportPerUE();
+
+ List<SchedMeasRepPerServCell> servCells = schedMeasReportPerUE.getSchedReportServCells().getSchedMeasRepPerServCell();
+
+ servCells.forEach(servCell -> {
+ RnibCell cell = cellMap.get(servCell.getPciArfcn());
+ if (cell != null) {
+ RnibLink link = linkMap.get(cell.getEcgi(), schedMeasReportPerUE.getCrnti());
+ if (link != null) {
+ link.getQuality().setMcs_dl(servCell.getMcsDl());
+ link.getQuality().setMcs_ul(servCell.getMcsUl());
+
+ link.getResourceUsage().setDl(servCell.getPrbUsage().getPrbUsageDl());
+ link.getResourceUsage().setUl(servCell.getPrbUsage().getPrbUsageUl());
+ } else {
+ log.warn("Could not find link between: {}-{}", cell.getEcgi(), schedMeasReportPerUE.getCrnti());
+ }
+ } else {
+ log.warn("Could not find cell with PCI-ARFCN: {}", servCell.getPciArfcn());
+ }
+ });
+ break;
+ }
+ case 23: {
+ SchedMeasReportPerCell schedMeasReportPerCell = recv_pdu.getBody().getSchedMeasReportPerCell();
+
+ RnibCell cell = cellMap.get(schedMeasReportPerCell.getEcgi());
+ if (cell != null) {
+ cell.setPrimaryPrbUsage(schedMeasReportPerCell.getPrbUsagePcell());
+ cell.setSecondaryPrbUsage(schedMeasReportPerCell.getPrbUsageScell());
+ cell.setQci(schedMeasReportPerCell.getQciVals());
+ } else {
+ log.warn("Could not find cell with ECGI: {}", schedMeasReportPerCell.getEcgi());
+ }
+ break;
+ }
+ case 24: {
+ PDCPMeasReportPerUe pdcpMeasReportPerUe = recv_pdu.getBody().getPDCPMeasReportPerUe();
+
+ RnibLink link = linkMap.get(pdcpMeasReportPerUe.getEcgi(), pdcpMeasReportPerUe.getCrnti());
+ if (link != null) {
+ link.getPdcpThroughput().setDl(pdcpMeasReportPerUe.getThroughputDl());
+ link.getPdcpThroughput().setUl(pdcpMeasReportPerUe.getThroughputUl());
+ link.getPdcpPackDelay().setDl(pdcpMeasReportPerUe.getPktDelayDl());
+ link.getPdcpPackDelay().setUl(pdcpMeasReportPerUe.getPktDelayUl());
+ } else {
+ log.warn("Could not find link between: {}-{}", pdcpMeasReportPerUe.getEcgi(), pdcpMeasReportPerUe.getCrnti());
+ }
+ break;
+ }
+ case 34: {
+ TrafficSplitConfig trafficSplitConfig = recv_pdu.getBody().getTrafficSplitConfig();
+
+ List<TrafficSplitPercentage> splitPercentages = trafficSplitConfig.getTrafficSplitPercent().getTrafficSplitPercentage();
+
+ splitPercentages.forEach(trafficSplitPercentage -> {
+ RnibCell cell = cellMap.get(trafficSplitPercentage.getEcgi());
+ if (cell != null) {
+ RnibLink link = linkMap.get(cell.getEcgi(), trafficSplitConfig.getCrnti());
+ if (link != null) {
+ link.setTrafficPercent(trafficSplitPercentage);
+ } else {
+ log.warn("Could not find link between: {}-{}", cell.getEcgi(), trafficSplitConfig.getCrnti());
+ }
+ } else {
+ log.warn("Could not find cell with ECGI: {}", trafficSplitConfig.getEcgi());
+ }
+ });
+ }
+ default: {
+ log.warn("Wrong API ID");
+ }
+ }
+
+ }
+ }
+
+ class InternalNetworkConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ switch (event.type()) {
+ case CONFIG_REGISTERED:
+ break;
+ case CONFIG_UNREGISTERED:
+ break;
+ case CONFIG_ADDED:
+ case CONFIG_UPDATED:
+ if (event.configClass() == CONFIG_CLASS) {
+ handleConfigEvent(event.config());
+ }
+ break;
+ case CONFIG_REMOVED:
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void handleConfigEvent(Optional<Config> config) {
+ if (!config.isPresent()) {
+ return;
+ }
+
+ xranConfig = (XranConfig) config.get();
+
+ legitCells.putAll(xranConfig.activeCellSet());
+
+ controller.start(deviceAgent, hostAgent, packetAgent, xranConfig.getXrancPort());
+ }
+ }
+}
diff --git a/src/main/java/org.onosproject.xran/controller/XranDeviceAgent.java b/src/main/java/org.onosproject.xran/controller/XranDeviceAgent.java
new file mode 100644
index 0000000..b81a628
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/XranDeviceAgent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015-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.xran.controller;
+
+import io.netty.channel.ChannelHandlerContext;
+
+/**
+ * Created by dimitris on 7/27/17.
+ */
+public interface XranDeviceAgent {
+
+ boolean addConnectedCell(String host, ChannelHandlerContext ctx);
+
+ boolean removeConnectedCell(String host);
+}
diff --git a/src/main/java/org.onosproject.xran/controller/XranHostAgent.java b/src/main/java/org.onosproject.xran/controller/XranHostAgent.java
new file mode 100644
index 0000000..07ab26e
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/XranHostAgent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015-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.xran.controller;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.onosproject.xran.entities.RnibCell;
+import org.onosproject.xran.entities.RnibUe;
+
+/**
+ * Created by dimitris on 7/28/17.
+ */
+public interface XranHostAgent {
+ boolean addConnectedHost(RnibUe ue, RnibCell cell, ChannelHandlerContext ctx);
+
+ boolean removeConnectedHost(RnibUe ue);
+}
diff --git a/src/main/java/org.onosproject.xran/controller/XranPacketProcessor.java b/src/main/java/org.onosproject.xran/controller/XranPacketProcessor.java
new file mode 100644
index 0000000..c2e1bbc
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/XranPacketProcessor.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015-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.xran.controller;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.onosproject.net.DeviceId;
+import org.onosproject.xran.codecs.pdu.XrancPdu;
+
+import java.io.IOException;
+
+public interface XranPacketProcessor {
+ void handlePacket(XrancPdu pdu, ChannelHandlerContext ctx) throws IOException;
+}
diff --git a/src/main/java/org.onosproject.xran/controller/package-info.java b/src/main/java/org.onosproject.xran/controller/package-info.java
new file mode 100644
index 0000000..7f4660f
--- /dev/null
+++ b/src/main/java/org.onosproject.xran/controller/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Created by dimitris on 7/21/17.
+ */
+package org.onosproject.xran.controller;
\ No newline at end of file