package org.freertr.rtr; import org.freertr.addr.addrClns; import org.freertr.addr.addrIP; import org.freertr.addr.addrIsis; import org.freertr.addr.addrMac; import org.freertr.clnt.clntEcho; import org.freertr.clnt.clntPing; import org.freertr.clnt.clntTwamp; import org.freertr.ip.ipMpls; import org.freertr.pack.packHolder; import org.freertr.tab.tabAverage; import org.freertr.tab.tabGen; import org.freertr.tab.tabLabel; import org.freertr.tab.tabLabelEntry; import org.freertr.util.bits; import org.freertr.util.counter; import org.freertr.util.debugger; import org.freertr.util.logger; import org.freertr.util.notifier; import org.freertr.enc.encTlv; import org.freertr.prt.prtIsoip; import org.freertr.sec.secInfoCls; import org.freertr.sec.secInfoWrk; /** * isis neighbor * * @author matecsaba */ public class rtrIsisNeigh implements Runnable, rtrBfdClnt, Comparable { /** * ipinfo result */ public secInfoWrk ipInfoRes; /** * peer mac address */ protected final addrMac ethAddr; /** * system id */ protected addrIsis rtrID; /** * peer interface address */ protected addrIP ifcAddr; /** * peer interface other address */ protected addrIP ofcAddr; /** * time echo sent */ protected long echoTime; /** * calculated echo */ protected tabAverage echoCalc = new tabAverage(1, 1); /** * hold time */ protected int holdTime; /** * dis priority */ protected int rtrPri; /** * peer dis address knowledge */ protected addrIsis peerDisA; /** * peer dis interface knowledge */ protected int peerDisI; /** * peer circuit id */ protected int peerCirc; /** * peer extended circuit id */ protected int peerExtCirc; /** * peer adjacency state */ protected int peerAdjState; /** * peer in other area */ protected boolean foreignArea; /** * uptime */ protected long upTime; /** * notifier */ protected notifier notif = new notifier(); /** * segment routing label */ protected tabLabelEntry segrouLab; /** * segment routing other label */ protected tabLabelEntry segrouOth; /** * the level */ protected final rtrIsisLevel level; private int updPos; private final rtrIsis lower; private final rtrIsisIface iface; private long lastHeard; private boolean need2run = true; private final tabGen advert; private final tabGen request; private final tabGen pending; /** * level2 hello pdu */ public final static int msgTypL1hello = 0x0f; /** * level1 hello pdu */ public final static int msgTypL2hello = 0x10; /** * p2p hello pdu */ public final static int msgTypP2Phello = 0x11; /** * level1 link state pdu */ public final static int msgTypL1lsp = 0x12; /** * level2 link state pdu */ public final static int msgTypL2lsp = 0x14; /** * level1 complete sequence numbers pdu */ public final static int msgTypL1csnp = 0x18; /** * level2 complete sequence numbers pdu */ public final static int msgTypL2csnp = 0x19; /** * level1 partial sequence numbers pdu */ public final static int msgTypL1psnp = 0x1a; /** * level2 partial sequence numbers pdu */ public final static int msgTypL2psnp = 0x1b; /** * down */ public final static int statDown = 2; /** * initing */ public final static int statInit = 1; /** * up */ public final static int statUp = 0; /** * convert message type to string * * @param i message type * @return string */ public static String msgTyp2string(int i) { switch (i) { case msgTypL1hello: return "l1hello"; case msgTypL2hello: return "l2hello"; case msgTypP2Phello: return "p2pHello"; case msgTypL1lsp: return "l1lsp"; case msgTypL2lsp: return "l2lsp"; case msgTypL1csnp: return "l1csnp"; case msgTypL2csnp: return "l2csnp"; case msgTypL1psnp: return "l1psnp"; case msgTypL2psnp: return "l2psnp"; default: return "unknown=" + i; } } /** * convert status to string * * @return string */ public String status2string() { if (!iface.shouldIanswer(level.level, rtrID)) { return "unneeded"; } switch (peerAdjState) { case statDown: return "down"; case statInit: return "init"; case statUp: return "up"; default: return "unknown=" + peerAdjState; } } /** * convert message type to level * * @param i message type * @return level of message */ public static int msgTyp2level(int i) { switch (i) { case msgTypL1csnp: case msgTypL1psnp: case msgTypL1lsp: case msgTypL1hello: return 1; case msgTypL2csnp: case msgTypL2psnp: case msgTypL2lsp: case msgTypL2hello: return 2; case msgTypP2Phello: return 3; default: return 0; } } /** * convert message type to header size * * @param i message type * @return header size */ public static int msgTyp2headSiz(int i) { switch (i) { case msgTypP2Phello: return 20; case msgTypL1hello: case msgTypL2hello: return 27; case msgTypL1lsp: case msgTypL2lsp: return 27; case msgTypL1csnp: case msgTypL2csnp: return 33; case msgTypL1psnp: case msgTypL2psnp: return 17; default: return 0; } } /** * create one instance * * @param parent the isis protocol * @param lev the isis level * @param ifc the isis interface * @param adr address of peer */ public rtrIsisNeigh(rtrIsis parent, rtrIsisLevel lev, rtrIsisIface ifc, addrMac adr) { lower = parent; iface = ifc; level = lev; ethAddr = adr.copyBytes(); rtrID = new addrIsis(); ifcAddr = new addrIP(); ofcAddr = new addrIP(); peerDisA = new addrIsis(); peerAdjState = statDown; lastHeard = bits.getTime(); advert = new tabGen(); request = new tabGen(); pending = new tabGen(); } public int compareTo(rtrIsisNeigh o) { if (level.level < o.level.level) { return -1; } if (level.level > o.level.level) { return +1; } return ethAddr.compareTo(o.ethAddr); } public String toString() { return "isis with l" + level.level + " " + ethAddr; } /** * start this neighbor */ protected void startNow() { if (debugger.rtrIsisEvnt) { logger.debug("starting neighbor l" + level.level + " " + ethAddr); } new Thread(this).start(); upTime = bits.getTime(); } /** * stow this neighbor */ protected void stopNow() { logger.error("neighbor level" + level.level + " " + ifcAddr + " down"); iface.iface.bfdDel(ifcAddr, this); if (iface.oface != null) { iface.oface.bfdDel(ofcAddr, this); } peerAdjState = statDown; tabLabel.release(segrouLab, tabLabelEntry.owner.isisAdj); tabLabel.release(segrouOth, tabLabelEntry.owner.isisAdj); level.schedWork(7); need2run = false; iface.neighs.del(this); notif.wakeup(); iface.doRetrans(); } /** * get peer metric * * @return metric */ public int getMetric() { int met = iface.metric; if (iface.dynamicMetric < 1) { return met; } return echoCalc.getResult(met); } /** * stop work */ public void bfdPeerDown() { stopNow(); } /** * get handshake status * * @return status */ protected int getMyHandshake() { switch (peerAdjState) { case statDown: return statInit; case statInit: case statUp: return statUp; default: return statDown; } } /** * received one packet * * @param pck packet to parse * @param typ type of packet */ public void recvPack(packHolder pck, int typ) { if (debugger.rtrIsisTraf) { logger.debug("got " + msgTyp2string(typ) + " from l" + level.level + " " + ethAddr); } lastHeard = bits.getTime(); switch (typ) { case msgTypL1hello: case msgTypL2hello: recvHelloLan(pck); break; case msgTypP2Phello: recvHelloP2p(pck); break; case msgTypL1lsp: case msgTypL2lsp: recvLsp(typ, pck); break; case msgTypL1csnp: case msgTypL2csnp: recvCsnp(pck); break; case msgTypL1psnp: case msgTypL2psnp: recvPsnp(pck); break; default: logger.info("got invalid packet from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badTyp); break; } } private void recvHelloP2p(packHolder pck) { int i = pck.getByte(0); // circuit type if ((i & level.level) == 0) { logger.info("got invalid level from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badLen); return; } pck.getAddr(rtrID, 1); // system id holdTime = pck.msbGetW(7) * 1000; // hold time i = pck.msbGetW(9) - 8; // pdu length if (pck.dataSize() < i) { logger.info("got truncated from l" + level.level + " " + ethAddr); return; } pck.setDataSize(i); peerCirc = pck.getByte(11); // circuit id pck.getSkip(12); readHelloTlvs(pck, msgTypP2Phello); } private void recvHelloLan(packHolder pck) { int i = pck.getByte(0); // circuit type if ((i & level.level) == 0) { logger.info("got invalid level from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badLen); return; } pck.getAddr(rtrID, 1); // system id holdTime = pck.msbGetW(7) * 1000; // hold time i = pck.msbGetW(9) - 8; // pdu length if (pck.dataSize() < i) { logger.info("got truncated from l" + level.level + " " + ethAddr); return; } pck.setDataSize(i); rtrPri = pck.getByte(11) & 0x7f; // dis priority pck.getAddr(peerDisA, 12); // dis address peerDisI = pck.getByte(18); // dis circuit pck.getSkip(19); readHelloTlvs(pck, msgTypL2hello); } private void readHelloTlvs(packHolder pck, int typ) { int hdrSiz = msgTyp2headSiz(typ) - 8; encTlv tlv = rtrIsis.getTlv(); int oldAdjSt = peerAdjState; peerAdjState = statDown; foreignArea = false; peerExtCirc = 0; ifcAddr = new addrIP(); ofcAddr = new addrIP(); boolean protoSupp = false; boolean otherSupp = false; boolean areaAddr = false; boolean seenOwn = false; byte[] remAuth = null; int authOfs = 0; int packSiz = pck.dataSize(); for (;;) { int ofs = pck.dataSize(); if (tlv.getBytes(pck)) { break; } switch (tlv.valTyp) { case rtrIsisLsp.tlvHandshake: peerAdjState = bits.getByte(tlv.valDat, 0); // adjacency state peerExtCirc = bits.msbGetD(tlv.valDat, 1); // circuit id break; case rtrIsisLsp.tlvProtSupp: int protoId = lower.getNLPIDval(false); int otherId = lower.getNLPIDval(true); for (int i = 0; i < tlv.valSiz; i++) { int currId = bits.getByte(tlv.valDat, i); protoSupp |= currId == protoId; otherSupp |= currId == otherId; } break; case rtrIsisLsp.tlvAuthen: remAuth = tlv.copyBytes(); authOfs = packSiz - ofs; break; case rtrIsisLsp.tlvAreaAddr: for (int i = 0; i < tlv.valSiz;) { addrClns cl = new addrClns(); cl.fromBuf(tlv.valDat, i); i += cl.getAddrLen(); cl.fillUnunsed(); int o = cl.compareTo(lower.areaID); areaAddr |= o == 0; foreignArea |= o != 0; } break; case rtrIsisLsp.tlvIpv4addr: case rtrIsisLsp.tlvIpv6addr: lower.getAddrIface(false, tlv, ifcAddr); lower.getAddrIface(true, tlv, ofcAddr); break; case rtrIsisLsp.tlvLanNeigh: for (int i = 0; i < tlv.valSiz;) { addrMac mc = new addrMac(); mc.fromBuf(tlv.valDat, i); i += addrMac.size; seenOwn |= mc.compareTo(iface.hwaddr) == 0; } break; } } pck.setBytesLeft(packSiz); pck.getSkip(-hdrSiz); if (!iface.netPnt2pnt) { if (seenOwn) { peerAdjState = statUp; } else { peerAdjState = statInit; } } int i = 1; byte[] locAuth = iface.getAuthData(pck, typ, authOfs + hdrSiz); if (locAuth == null) { if (remAuth == null) { i = 0; } } else if (remAuth != null) { if (locAuth.length == remAuth.length) { i = bits.byteComp(locAuth, 0, remAuth, 0, locAuth.length); } } if (i != 0) { peerAdjState = statDown; logger.info("got bad authentication from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badKey); return; } if (!protoSupp) { peerAdjState = statDown; logger.info("got no protocol from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badProto); return; } if (ifcAddr.isFilled(0)) { peerAdjState = statDown; logger.info("got no address from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badAddr); return; } if ((iface.connectedCheck) && (!iface.iface.network.matches(ifcAddr))) { logger.info("got from out of subnet peer " + ifcAddr); return; } if (iface.otherEna) { if (!otherSupp) { peerAdjState = statDown; logger.info("got no other protocol from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badProto); return; } if (ofcAddr.isFilled(0)) { peerAdjState = statDown; logger.info("got no other address from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badAddr); return; } if ((iface.connectedCheck) && (!iface.oface.network.matches(ofcAddr))) { logger.info("got from out of subnet peer " + ofcAddr); return; } } if ((level.level == 1) && (!areaAddr)) { peerAdjState = statDown; logger.info("got bad area from l" + level.level + " " + ethAddr); iface.cntr.drop(pck, counter.reasons.badAddr); return; } if (oldAdjSt == peerAdjState) { return; } if (peerAdjState != statUp) { if (oldAdjSt != statUp) { return; } logger.error("neighbor level" + level.level + " " + ifcAddr + " forgot us"); iface.iface.bfdDel(ifcAddr, this); if (iface.oface != null) { iface.oface.bfdDel(ofcAddr, this); } tabLabel.release(segrouLab, tabLabelEntry.owner.isisAdj); tabLabel.release(segrouOth, tabLabelEntry.owner.isisAdj); peerAdjState = statDown; level.schedWork(7); return; } iface.iface.lower.createETHheader(new packHolder(true, true), ifcAddr, 0); if (iface.otherEna) { iface.oface.lower.createETHheader(new packHolder(true, true), ofcAddr, 0); } if (iface.ipInfoCfg != null) { secInfoCls cls = new secInfoCls(null, null, null, lower.fwdCore, ifcAddr, prtIsoip.proto, iface.iface.addr); ipInfoRes = new secInfoWrk(iface.ipInfoCfg, cls); ipInfoRes.doWork(true); } logger.warn("neighbor level" + level.level + " " + ifcAddr + " up"); advert.clear(); if (lower.segrouLab != null) { segrouLab = tabLabel.allocate(tabLabelEntry.owner.isisAdj); segrouLab.setFwdMpls(tabLabelEntry.owner.isisAdj, lower.fwdCore, iface.iface, ifcAddr, tabLabel.int2labels(ipMpls.labelImp)); if (iface.otherEna) { segrouOth = tabLabel.allocate(tabLabelEntry.owner.isisAdj); segrouOth.setFwdMpls(tabLabelEntry.owner.isisAdj, lower.other.fwd, iface.oface, ofcAddr, tabLabel.int2labels(ipMpls.labelImp)); } } level.schedWork(7); if (iface.bfdTrigger) { iface.iface.bfdAdd(ifcAddr, this, "isis"); if (iface.otherEna) { iface.oface.bfdAdd(ofcAddr, this, "isis"); } } } private tabGen readLspList(packHolder pck) { tabGen l = new tabGen(); encTlv tlv = rtrIsis.getTlv(); for (;;) { if (tlv.getBytes(pck)) { break; } if (tlv.valTyp != rtrIsisLsp.tlvLspEntries) { continue; } packHolder p = new packHolder(true, true); p.putCopy(tlv.valDat, 0, 0, tlv.valSiz); p.putSkip(tlv.valSiz); p.merge2beg(); for (;;) { if (p.dataSize() < 1) { break; } rtrIsisLsp lsp = new rtrIsisLsp(); int i = lsp.readSeq(p, 0); if (i < 0) { break; } p.getSkip(i); if (debugger.rtrIsisTraf) { logger.debug("lsp " + lsp); } l.add(lsp); } } return l; } private void recvLsp(int typ, packHolder pck) { if (peerAdjState != statUp) { return; } rtrIsisLsp lsp = new rtrIsisLsp(); if (lsp.readData(pck, 0) < 0) { logger.info("got bad lsp from l" + level.level + " " + ifcAddr); iface.cntr.drop(pck, counter.reasons.badSum); return; } byte[] buf = level.getAuthen(new packHolder(true, true), 0, 0); if (buf != null) { byte[] got = new byte[0]; encTlv tlv = rtrIsis.getTlv(); packHolder p = new packHolder(true, true); int siz = lsp.writeData(p, 0); p.msbPutW(2, 0); // lifetime p.msbPutW(16, 0); // checksum p.putSkip(siz); p.merge2end(); p.getSkip(rtrIsisLsp.headSize); int pos = rtrIsisLsp.headSize; for (;;) { int ofs = p.dataSize(); if (tlv.getBytes(p)) { break; } if (tlv.valTyp != rtrIsisLsp.tlvAuthen) { continue; } if (tlv.valSiz != buf.length) { continue; } pos = siz - ofs; got = new byte[tlv.valSiz]; bits.byteCopy(tlv.valDat, 0, got, 0, got.length); } p.setBytesLeft(siz); buf = level.getAuthen(p, typ, pos); boolean authed = got.length == buf.length; if (authed) { authed = bits.byteComp(got, 0, buf, 0, buf.length) == 0; } if (!authed) { logger.info("got bad authentication from l" + level.level + " " + ethAddr + " on " + lsp); return; } } if (debugger.rtrIsisTraf) { logger.debug("lsp " + lsp); } request.del(lsp); pending.del(lsp); if (iface.shouldIanswer(level.level, rtrID)) { iface.sendPsnpPack(lsp, level); } rtrIsisLsp old = level.lsps.add(lsp); if (old == null) { advert.put(lsp.copyBytes(false)); if (lower.routerID.compareTo(lsp.srcID) == 0) { level.schedWork(1); } level.schedWork(6); doRetrans(); level.wakeNeighs(); return; } if (!old.otherNewer(lsp)) { if (!lsp.otherNewer(old)) { advert.put(lsp.copyBytes(false)); return; } advert.del(lsp); return; } level.lsps.put(lsp); advert.put(lsp.copyBytes(false)); if (lower.routerID.compareTo(lsp.srcID) == 0) { level.schedWork(1); } level.schedWork(6); doRetrans(); level.wakeNeighs(); } private void recvPsnp(packHolder pck) { if (peerAdjState != statUp) { return; } if (!iface.shouldIanswer(level.level, rtrID)) { return; } int i = pck.msbGetW(0) - 8; // pdu length if (pck.dataSize() < i) { logger.info("got truncated from l" + level.level + " " + ethAddr); return; } pck.setDataSize(i); // pck.getAddr(adr, 2); // source id // int circ = pck.getByte(8); pck.getSkip(9); tabGen l1 = readLspList(pck); tabGen l2 = new tabGen(); for (i = 0; i < l1.size(); i++) { rtrIsisLsp ntry = l1.get(i); ntry = level.lsps.find(ntry); if (ntry == null) { continue; } l2.add(ntry); } doLspList(l1, l2); doRetrans(); } private void recvCsnp(packHolder pck) { if (peerAdjState != statUp) { return; } if (!iface.shouldIanswer(level.level, rtrID)) { return; } int i = pck.msbGetW(0) - 8; // pdu length if (pck.dataSize() < i) { logger.info("got truncated from l" + level.level + " " + ethAddr); return; } pck.setDataSize(i); // pck.getAddr(adr, 2); // source id // int circ = pck.getByte(8); rtrIsisLsp frst = new rtrIsisLsp(); rtrIsisLsp last = new rtrIsisLsp(); frst.readId(pck, 9); // first lsp last.readId(pck, 17); // last lsp pck.getSkip(25); tabGen l1 = readLspList(pck); tabGen l2 = new tabGen(); for (i = 0; i < level.lsps.size(); i++) { rtrIsisLsp ntry = level.lsps.get(i); if (ntry == null) { continue; } if (ntry.compareTo(frst) < 0) { continue; } if (ntry.compareTo(last) > 0) { continue; } l2.add(ntry); } doLspList(l1, l2); doRetrans(); } private void doLspList(tabGen rem, tabGen loc) { for (int i = 0; i < rem.size(); i++) { rtrIsisLsp lsp = rem.get(i); if (lsp == null) { continue; } rtrIsisLsp old = level.lsps.find(lsp); if (old == null) { if (lsp.getTimeRemain(true) < 1) { continue; } request.add(lsp.copyBytes(false)); continue; } if (old.otherNewer(lsp)) { request.add(lsp.copyBytes(false)); } else { request.del(lsp); } } for (int i = 0; i < loc.size(); i++) { rtrIsisLsp lsp = loc.get(i); if (lsp == null) { continue; } rtrIsisLsp old = rem.find(lsp); if (old == null) { if (lsp.getTimeRemain(true) < 1) { continue; } advert.del(lsp); continue; } if (old.otherNewer(lsp)) { advert.del(lsp); } else { advert.add(lsp.copyBytes(false)); } } } /** * do retransmit work */ protected synchronized void doRetrans() { long tim = bits.getTime(); if ((tim - lastHeard) > holdTime) { stopNow(); level.schedWork(7); return; } if (ipInfoRes != null) { if (ipInfoRes.need2drop()) { stopNow(); level.schedWork(7); return; } } if (peerAdjState != statUp) { return; } if ((echoTime + iface.echoTimer) < tim) { echoCalc.updateFrom(iface.echoParam); switch (iface.dynamicMetric) { case 1: clntPing png = new clntPing(); png.meas = echoCalc; png.fwd = lower.fwdCore; png.src = iface.iface; png.trg = ifcAddr; png.doWork(); break; case 2: clntEcho ech = new clntEcho(); ech.meas = echoCalc; ech.udp = lower.udpCore; ech.src = iface.iface; ech.trg = ifcAddr; ech.doWork(); break; case 3: clntTwamp twm = new clntTwamp(); twm.meas = echoCalc; twm.udp = lower.udpCore; twm.src = iface.iface; twm.trg = ifcAddr; twm.doWork(); break; } echoTime = tim - 1; } int i = request.size(); if (i > 0) { i = bits.random(0, i); rtrIsisLsp lsp = request.get(i); if (lsp != null) { if (pending.find(lsp) == null) { pending.add(lsp.copyBytes(false)); lsp.sequence = 0; iface.sendPsnpPack(lsp, level); } } } for (i = advert.size(); i >= 0; i--) { rtrIsisLsp lsp = advert.get(i); if (lsp == null) { continue; } rtrIsisLsp old = level.lsps.find(lsp); if (old == null) { advert.del(lsp); continue; } if (!lsp.otherNewer(old)) { continue; } advert.del(old); } for (i = 0; i < level.lsps.size(); i++) { updPos = (updPos + 1) % level.lsps.size(); rtrIsisLsp lsp = level.lsps.get(updPos); if (lsp == null) { continue; } if (advert.find(lsp) != null) { continue; } if (pending.find(lsp) != null) { continue; } pending.add(lsp.copyBytes(false)); iface.sendLspPack(lsp, level); if (debugger.rtrIsisTraf) { logger.debug("lsp " + lsp); } if (iface.amIdis(level.level)) { advert.add(lsp.copyBytes(false)); } break; } } public void run() { long last = 0; for (;;) { if (!need2run) { break; } notif.sleep(iface.retransTimer); long tim = bits.getTime(); if ((tim - last) >= iface.retransTimer) { pending.clear(); last = tim; } try { doRetrans(); } catch (Exception e) { logger.traceback(e); } } } }