/** * @copyright 2013 Computer Science Department, Recursive InterNetworking Architecture (RINA) laboratory, Boston University. * All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation * for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all * copies and that both the copyright notice and this permission notice appear in supporting documentation. * The RINA laboratory of the Computer Science Department at Boston University makes no * representations about the suitability of this software for any purpose. */ package rina.ipc.ae; import java.util.LinkedHashMap; import java.util.LinkedList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import rina.irm.impl.IRMImpl; import rina.irm.util.HandleEntry; import rina.message.CDAP; import rina.message.CDAP.CDAPMessage; import rina.object.gpb.ApplicationProcessNamingInfoMessage_t.applicationProcessNamingInfo_t; import rina.object.gpb.DirectoryForwardingTableEntry_t.directoryForwardingTableEntrySet_t; import rina.object.gpb.DirectoryForwardingTableEntry_t.directoryForwardingTableEntry_t; import rina.object.gpb.EnrollmentInformation_t.enrollmentInformation_t; import rina.object.gpb.IDDMessage_t.iddMessage_t; import rina.object.gpb.IDDMessage_t.opCode_t; import rina.object.gpb.EnrollmentMessage_t; import rina.object.gpb.Member_t.member_t; import rina.object.gpb.Member_t.members_t; import rina.object.gpb.Neighbour_t.neighbor_t; import rina.object.gpb.Neighbour_t.neighbors_t; import rina.object.gpb.UnderlyingDIFs_t.underlyingDIFs_t; import rina.object.internal.ApplicationProcessNamingInfo; import rina.object.internal.DirectoryForwardingTable; import rina.object.internal.DirectoryForwardingTableEntry; import rina.object.internal.ForwardingTable; import rina.object.internal.IDDRecord; import rina.object.internal.Neighbor; import rina.object.internal.Neighbors; import rina.rib.impl.RIBImpl; import rina.routing.RoutingDaemon; import rina.util.FlowInfoQueue; import application.ae.ApplicationEntity; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; /** * Management AE of IPC process * This Management AE is only used to handle authentication * @author Yuefeng Wang. Computer Science Department, Boston University * */ public class ManagementAE extends ApplicationEntity { private Log log = LogFactory.getLog(this.getClass()); private IRMImpl irm = null; private RoutingDaemon routingDaemon = null; //stores all connections to this AE private LinkedHashMap<Integer, HandleEntry> handleEntries = null; private int authenticatorHandleID = -1; private boolean enrolled = false; //this is a RINA DIF internal address //it will be assigned by the authenticator after joining the DIF private int rinaAddr = -1; private String DIFName = null; private Neighbors neighbors = null; private ForwardingTable forwardingTable = null; private DirectoryForwardingTable directoryForwardingTable = null; private LinkedHashMap<String, FlowInfoQueue> callbackApplicaitonFlowInfoQueues = null; public ManagementAE(String ApName, String ApInstance, String AeInstance, RIBImpl rib, IRMImpl irm) { super(ApName, ApInstance, "Management", AeInstance, rib); this.rib.addAttribute("managementAeMsgQueue", this.msgQueue); this.DIFName = (String)this.rib.getAttribute("difName"); this.enrolled = (Boolean) this.rib.getAttribute("enrolledState"); this.log.debug("this.enrolled " + this.enrolled); this.irm = irm; this.irm.setMae(this); this.handleEntries = new LinkedHashMap<Integer, HandleEntry> (); this.neighbors = (Neighbors)this.rib.getAttribute("neighbors"); this.directoryForwardingTable = (DirectoryForwardingTable) this.rib.getAttribute("directoryForwardingTable"); this.callbackApplicaitonFlowInfoQueues = new LinkedHashMap<String, FlowInfoQueue>(); this.rib.addAttribute("callbackApplicaitonFlowInfoQueues", this.callbackApplicaitonFlowInfoQueues); this.forwardingTable = (ForwardingTable)this.rib.getAttribute("forwardingTable"); if(this.enrolled == false) { try // the reason to have this try/catch is in rib such info may not exist, which throw exceptions { String authenticatorApName = null; String authenticatorApInstance = null; if(this.rib.getAttribute("authenticatorApName") == null) { this.log.debug("authenticator info is unknow about the DIF " + this.DIFName + ", querry IDD"); IDDRecord difRecord = this.queryIDD(this.DIFName); ApplicationProcessNamingInfo authenticatorApInfo = difRecord.getAuthenticatorNameInfoList().get(0); authenticatorApName = authenticatorApInfo.getApName(); authenticatorApInstance = authenticatorApInfo.getApInstance(); this.rib.addAttribute("authenticatorApName", authenticatorApName ); this.rib.addAttribute("authenticatorApInstance", authenticatorApInstance); }else { this.log.debug("authenticator info is known about the DIF " + this.DIFName); authenticatorApName = this.rib.getAttribute("authenticatorApName").toString(); authenticatorApInstance = this.rib.getAttribute("authenticatorApInstance").toString(); } this.log.debug("authenticatorApName:" + authenticatorApName + ", authenticatorApInstance:" +authenticatorApInstance); this.enrollment(authenticatorApName, authenticatorApInstance, null, null); }catch(Exception e) { this.log.info(" Enrollment fails, and this IPC remains isolated."); } }else // this if an authenticator from the beginning { this.rinaAddr = (Integer)this.rib.getAttribute("rinaAddr"); LinkedList<String> underlyingDIFs = (LinkedList<String>) this.rib.getAttribute("underlyingDIFs"); this.rib.addMember(this.rinaAddr, this.apName, this.apInstance, underlyingDIFs);// apName + apInstance identifies an IPC process // from there, this ipc is also an authenticator, which can enroll new IPC into the DIF. } //start the routing Daemon this.routingDaemon = new RoutingDaemon(this.rib, this.irm); this.rib.addAttribute("routingDaemon", this.routingDaemon); } public void enrollment(String dstApName, String dstApInstance, String dstAeName, String dstAeInstance) { //by default management AE is responsible for authentication if(dstAeName == null) { dstAeName = "Management"; } //by default management AE Instance "1" if(dstAeInstance == null) { dstAeInstance = "1"; } this.log.info("Enrollment procedure started, and authenticator info: " + dstApName + "/" + dstApInstance + "/"+ dstAeName + "/" + dstAeInstance); this.authenticatorHandleID = this.irm.allocateFlow(this.apName, this.apInstance, this.aeName, this.aeInstance, dstApName, dstApInstance, dstAeName, dstAeInstance); this.log.info("authenticatorHandleID is " + this.authenticatorHandleID); CDAP.CDAPMessage M_CONNECT = null; // AUTH_NONE; // AUTH_PASSWD; // AUTH_SSHRSA; // AUTH_SSHDSA; if(this.rib.getAttribute("authenPolicy").toString().equals("AUTH_PASSWD")) { this.log.debug("authenPolicy : AUTH_PASSWD"); CDAP.authValue_t.Builder authValue = CDAP.authValue_t.newBuilder(); String userName = (String)this.rib.getAttribute("userName"); String passWord = (String)this.rib.getAttribute("passWord"); //this might be a list //TODO LinkedList<String> underlyingDIFs = (LinkedList<String>)this.rib.getAttribute("underlyingDIFs"); this.log.debug("Enrollment info - userName/passWord/underlyingDIFs: " + userName + "/" + passWord + "/" + underlyingDIFs); authValue.setAuthName(userName); authValue.setAuthPassword(passWord); underlyingDIFs_t.Builder underlyingDIFs_tosend = underlyingDIFs_t.newBuilder(); for(int i=0; i< underlyingDIFs.size(); i++ ) { underlyingDIFs_tosend.addUnderlyingDIFs(underlyingDIFs.get(i)); } authValue.setAuthOther(underlyingDIFs_tosend.buildPartial().toByteString()); M_CONNECT = rina.message.CDAPMessageGenerator.generateM_CONNECT ( CDAP.authTypes_t.AUTH_PASSWD, authValue.buildPartial(), dstAeInstance,//destAEInst dstAeName,//destAEName dstApInstance,//destApInst dstApName,//destApName 110, this.aeInstance,//srcAEInst this.aeName,//srcAEName this.apInstance,//srcApInst this.apName//srcApName ); }else if(this.rib.getAttribute("authenPolicy").toString().equals("AUTH_NONE")) { this.log.debug("authenPolicy: AUTH_NONE"); M_CONNECT = rina.message.CDAPMessageGenerator.generateM_CONNECT ( dstAeInstance,//destAEInst dstAeName,//destAEName dstApInstance,//destApInst dstApName,//destApName 110, this.aeInstance,//srcAEInst this.aeName,//srcAEName this.apInstance,//srcApInst this.apName//srcApName ); }else { //Implement your own authentication policy } try { this.irm.send(this.authenticatorHandleID, M_CONNECT.toByteArray()); this.log.info( "M_CONNECT sent to authenticator."); } catch (Exception e) { this.log.error(e.getMessage()); } while(!enrolled) { byte[] msg = this.irm.receive(this.authenticatorHandleID); CDAP.CDAPMessage cdapMessage = null; try { cdapMessage = CDAP.CDAPMessage.parseFrom(msg); } catch (Exception e) { this.log.error(e.getMessage()); } // this.log.info("CDAPMessage received, and opcode is " + cdapMessage.getOpCode()); this.processEnrollmentCDAPMessage(cdapMessage); } this.enrolled = true; this.rib.addAttribute("enrolledState", this.enrolled); HandleEntry he = this.handleEntries.get(this.authenticatorHandleID); //after the enrollment is done, a handler is created to handle the communication with the authenticator new ManagementAEHandler(this.authenticatorHandleID, he, this.rib, this.irm); // from there, this ipc is also an authenticator, which can enroll new IPC into the DIF. this.start(); } private void processEnrollmentCDAPMessage(CDAPMessage cdapMessage) { switch(cdapMessage.getOpCode()){ case M_CREATE: this.handleEnrollment_M_CREATE(cdapMessage); break; case M_START: this.handleEnrollment_M_START(cdapMessage); break; case M_START_R: this.handleEnrollment_M_START_R(cdapMessage); break; case M_STOP: this.handleEnrollment_M_STOP(cdapMessage); break; case M_CONNECT_R: this.handleEnrollment_M_CONNECT_R(cdapMessage); break; default: break; } } // M_START_R during Enrollment private void handleEnrollment_M_START_R(CDAPMessage cdapMessage) { if(cdapMessage.getObjClass().equals("address")) { this.rinaAddr = (int) cdapMessage.getObjValue().getInt64Val(); this.rib.addAttribute("rinaAddr", this.rinaAddr); this.log.info("RINA address received from authenticator: " + this.rinaAddr); } } private void handle_M_READ(CDAPMessage cdapMessage) { if(cdapMessage.getObjClass().equals("watchdog timer")) { CDAP.CDAPMessage M_READ_R = rina.message.CDAPMessageGenerator.generateM_READ_R ( cdapMessage.getObjClass(), cdapMessage.getObjName(), cdapMessage.getInvokeID() ); try { this.irm.send(this.authenticatorHandleID, M_READ_R.toByteArray()); this.log.debug("M_READ_R(watchdog timer) sent " ); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } // M_START during Enrollment private void handleEnrollment_M_START(CDAPMessage cdapMessage) { if (cdapMessage.getObjClass().equals("operationstatus")) { this.enrolled = true; this.rib.addAttribute("enrolledState", true); this.log.debug("Enrollment procedure finished." ); //put its undelrying DIF info of itself in the rib //this will be used when enroll new member to tell its who is its neighbor //LinkedList<String> underlyingDIFs = (LinkedList<String>) this.rib.getAttribute("underlyingDIFs"); //this.rib.addUnderlyingDIFsInfo(this.rinaAddr, underlyingDIFs); } } // M_CONNECT_R during Enrollment private void handleEnrollment_M_CONNECT_R(CDAPMessage cdapMessage) { CDAP.objVal_t.Builder obj = CDAP.objVal_t.newBuilder(); enrollmentInformation_t ei_msg = EnrollmentMessage_t.generate(this.rinaAddr,"STOPPED"); obj.setByteval(ei_msg.toByteString()); CDAP.CDAPMessage M_START_Enroll = rina.message.CDAPMessageGenerator.generateM_START ( "enrollment information", "/daf/management/enrollment", obj.buildPartial(), 21 ); try { this.irm.send(this.authenticatorHandleID, M_START_Enroll.toByteArray()); this.log.info("M_START(enrollment information) sent. " ); } catch (Exception e) { // TODO Auto-generated catch block this.log.error(e.getMessage()); //e.printStackTrace(); } } // M_STOP during Enrollment private void handleEnrollment_M_STOP(CDAPMessage cdapMessage) { if(cdapMessage.getObjClass().equals("enrollment information")) { CDAP.CDAPMessage M_STOP_R = rina.message.CDAPMessageGenerator.generateM_STOP_R ( 0, "enrollment information", cdapMessage.getInvokeID() ); try { this.irm.send(this.authenticatorHandleID, M_STOP_R.toByteArray()); this.log.info("M_STOP_R(enrollment information) sent. " ); } catch (Exception e1) { // TODO Auto-generated catch block this.log.error(e1.getMessage()); //e1.printStackTrace(); } } } // M_CREATE during Enrollment // this is different with he M_CREATE hander in the ManagementAEhandler.java // here only for enrollment, so don't have to broadcast info it received to its neighbor when receiving new info such as // new DIF member, new Application registration //NOTE: if M_CREATE here is modified, check if in ManagementAEhandler.java 's handle M_CREATE anything needs also changing private void handleEnrollment_M_CREATE(CDAPMessage cdapMessage) { this.log.info("M_CREATE ("+ cdapMessage.getObjClass() + ") received." ); if(cdapMessage.getObjClass().equals("directoryforwardingtableentry set")) { directoryForwardingTableEntrySet_t sets = null; try { sets = directoryForwardingTableEntrySet_t.parseFrom(cdapMessage.getObjValue().getByteval()); } catch (InvalidProtocolBufferException e) { // TODO Auto-generated catch block e.printStackTrace(); } int num = sets.getDirectoryForwardingTableEntryCount(); this.log.info(num + " directoryforwardingtable entry received." ); for(int i =0 ;i < num; i++) { directoryForwardingTableEntry_t entry = sets.getDirectoryForwardingTableEntry(i); applicationProcessNamingInfo_t apInfo_t = entry.getApplicationName(); String apName = apInfo_t.getApplicationProcessName(); String apInstance = apInfo_t.getApplicationProcessInstance(); ApplicationProcessNamingInfo apInfo = null; if(apInstance == "") //apIntance may not be used for application { apInfo = new ApplicationProcessNamingInfo(apName); }else //if application is an IPC, then apInstance is used for sure { apInfo = new ApplicationProcessNamingInfo(apName, apInstance); } DirectoryForwardingTableEntry directoryForwardingTableEntry = new DirectoryForwardingTableEntry(apInfo, entry.getIpcProcessAddress(), entry.getTimestamp()); this.directoryForwardingTable.addEntry(directoryForwardingTableEntry); this.log.debug("directoryForwardingTableEntry added info (apName/apInstance/ipcaddr/timestamp): " + apName + "/" + apInstance + "/" + entry.getIpcProcessAddress() + "/" + entry.getTimestamp()); } }else if (cdapMessage.getObjClass().equals("neighbor set")) { //NOTE: this is different from M_CRATE handle in MAEHander.java //after the enrollment, routing deamon will be started, where ipc will sub to all its direct neighbor neighbors_t neighbors_obj = null; try { neighbors_obj = neighbors_t.parseFrom(cdapMessage.getObjValue().getByteval()); } catch (InvalidProtocolBufferException e) { // TODO Auto-generated catch block e.printStackTrace(); } int num = neighbors_obj.getNeighborCount(); this.log.info(num + " neihgbor entry received." ); for(int i =0 ;i < num; i++) { neighbor_t neighbor_obj = neighbors_obj.getNeighbor(i); String apName = neighbor_obj.getApplicationProcessName(); String apInstance = neighbor_obj.getApplicationProcessInstance(); long addr = neighbor_obj.getAddress(); Neighbor neighbor = new Neighbor(apName,apInstance,addr); this.neighbors.addNeighbor(neighbor); //add direct neighbor to its forwarding table //this is bootstrap this.forwardingTable.addNextHop((int)neighbor.getAddr(), (int) neighbor.getAddr()); this.log.debug("neighbor entry added info (apName/apInstance/addr): " + apName + "/" + apInstance + "/" + addr); } }else if(cdapMessage.getObjClass().equals("qoscube set")) { }else if(cdapMessage.getObjClass().equals("datatransercons")) { }else if(cdapMessage.getObjClass().equals("member set")) { //NOTE: this is different from M_CRATE handle in MAEHander.java //after the enrollment, routing deamon will be started, where ipc will sub to all its direct neighbor members_t sets = null; try { sets = members_t.parseFrom(cdapMessage.getObjValue().getByteval()); } catch (InvalidProtocolBufferException e) { // TODO Auto-generated catch block e.printStackTrace(); } int num = sets.getMemberCount(); this.log.debug( num + " DIF member entry recevied ." ); for(int i =0 ;i < num; i++) { member_t entry = sets.getMember(i); String IPCApName = entry.getApplicationProcessName(); String IPCApInstance = entry.getApplicationProcessInstance(); int addr = (int) entry.getAddress(); LinkedList<String> underlyingDIFs = new LinkedList<String> (); for(int count = 0 ; count < entry.getUnderlyingDIFsCount(); count++) { underlyingDIFs.add(entry.getUnderlyingDIFs(count)); } this.rib.addMember(addr, IPCApName, IPCApInstance,underlyingDIFs ); this.log.debug("The rib updates its information(member list) about the IPC: " + IPCApName + "/" + IPCApInstance + "/" + addr); } } } public synchronized void addNewHandle(int handleID ,HandleEntry he) { this.handleEntries.put(handleID, he); //start a thread there to handle new flow with the handle new ManagementAEHandler(handleID, he, this.rib, this.irm); } public synchronized void addAuthenticatorHandle(int handleID ,HandleEntry he) { this.handleEntries.put(handleID, he); } // this ipc first stores this in its RIB forwardingDirectory, // then send to all its neighbors public void registerApplication(ApplicationProcessNamingInfo apInfo, FlowInfoQueue flowInfoQueue) { this.log.info("registerApplication() is called"); DirectoryForwardingTableEntry entry = new DirectoryForwardingTableEntry(apInfo, this.rinaAddr,System.currentTimeMillis()); //put into its own RIB this.directoryForwardingTable.addEntry(entry); //this is the interface IPC uses to talk to apps on top of it String key = apInfo.getApName() + apInfo.getApInstance() + apInfo.getAeName() + apInfo.getAeInstance(); this.callbackApplicaitonFlowInfoQueues.put(key, flowInfoQueue); this.log.debug("Application flowInfoQueue added with key " + key); CDAP.objVal_t.Builder objM_CREATE_directoryforwardingtableentries = CDAP.objVal_t.newBuilder(); directoryForwardingTableEntrySet_t.Builder directoryForwardingTableEntrySet = directoryForwardingTableEntrySet_t.newBuilder(); directoryForwardingTableEntrySet.addDirectoryForwardingTableEntry(entry.convert()); objM_CREATE_directoryforwardingtableentries.setByteval(ByteString.copyFrom(directoryForwardingTableEntrySet.buildPartial().toByteArray())); LinkedList<Neighbor> neighborList = this.neighbors.getNeighborList(); for(int j = 0; j< neighborList.size(); j++ ) { Neighbor neighbor = neighborList.get(j); String dstipcName = neighbor.getApName(); String dstipcInstance = neighbor.getApInstance(); neighbor.print(); int handle = this.irm.allocateFlow(this.apName, this.apInstance, "Management", "1", dstipcName, dstipcInstance, "Management", "1"); this.log.debug("handle (to management ae where IPCInfo:" + dstipcName + "/" + dstipcInstance + ") is " + handle); CDAP.CDAPMessage M_CREATE = rina.message.CDAPMessageGenerator.generateM_CREATE ( "directoryforwardingtableentry set", "/dif/management/flowallocator/directoryforwardingtableentries", objM_CREATE_directoryforwardingtableentries.buildPartial(), 56 ); try { //send it to every neighbor this.irm.send(handle, M_CREATE.toByteArray()); this.log.debug("M_CREATE(directoryforwardingtableentry set) containing one directory forwarding table entry sent due to application registration" ); } catch (Exception e1) { this.log.error(e1.getMessage()); //e1.printStackTrace(); } } } public IDDRecord queryIDD(String DIFName) { this.log.debug("query IDD about DIF:" + DIFName); IDDRecord ans = null; iddMessage_t.Builder reqMsg = iddMessage_t.newBuilder(); reqMsg.setOpCode(opCode_t.Request); reqMsg.setDifName(DIFName); ans = this.irm.queryIDD(reqMsg.buildPartial()); return ans; } public IDDRecord queryIDD(ApplicationProcessNamingInfo apInfo) { IDDRecord ans = null; iddMessage_t.Builder reqMsg = iddMessage_t.newBuilder(); reqMsg.setOpCode(opCode_t.Request); reqMsg.setApplicationNameInfo(apInfo.convert()); ans = this.irm.queryIDD(reqMsg.buildPartial()); return ans; } public void registerToIDD(iddMessage_t iddRegMsg) { this.irm.registerToIDD(iddRegMsg); } }