package org.freertr.spf; import org.freertr.enc.encTlv; import java.util.ArrayList; import java.util.List; import org.freertr.addr.addrIP; import org.freertr.addr.addrIPv4; import org.freertr.addr.addrPrefix; import org.freertr.addr.addrType; import org.freertr.cfg.cfgAll; import org.freertr.cry.cryHashCrc32; import org.freertr.ip.ipFwd; import org.freertr.ip.ipFwdIface; import org.freertr.ip.ipMpls; import org.freertr.pack.packHolder; import org.freertr.tab.tabGen; import org.freertr.tab.tabIndex; import org.freertr.tab.tabIntMatcher; import org.freertr.tab.tabLabel; import org.freertr.tab.tabLabelBier; import org.freertr.tab.tabLabelBierN; import org.freertr.tab.tabLabelEntry; import org.freertr.tab.tabRoute; import org.freertr.tab.tabRouteAttr; import org.freertr.tab.tabRouteEntry; import org.freertr.tab.tabRouteIface; import org.freertr.user.userFormat; import org.freertr.util.bits; import org.freertr.util.cmds; import org.freertr.util.logger; import org.freertr.util.syncInt; /** * dijkstra's shortest path first * * @param type of nodes * @author matecsaba */ public class spfCalc { /** * beginning of graph */ public final static String graphBeg1 = "sfdp -Tpng > net.png << EOF"; /** * beginning of graph */ public final static String graphBeg2 = "graph net {"; /** * beginning of graph */ public final static String graphBeg3 = "node [fontname=ubuntu,shape=none,labelloc=b,image=\"../misc/router.svg\"] edge [fontname=ubuntu,shape=none]"; /** * ending of graph */ public final static String graphEnd1 = "}"; /** * ending of graph */ public final static String graphEnd2 = "EOF"; private final tabGen> nodes; private final List log = new ArrayList(); private final int count; private final long tim1; private long tim2; private long tim3; private long tim4; private spfNode spfRoot; private spfCalc prev; /** * log size */ public final syncInt logSize; /** * log topology changes: 0x1=(dis)appear, 0x2=connect, 0x4=forward, * 0x8=(un)reachable, 0x10=metric, 0x20=prefix */ public final syncInt topoLog; /** * bidir check */ public final syncInt bidir; /** * consider ecmp */ public final syncInt ecmp; /** * consider hops in ecmp */ public final syncInt hops; /** * construct spf * * @param old old spf */ public spfCalc(spfCalc old) { tim1 = bits.getTime(); nodes = new tabGen>(); if (old == null) { count = 1; logSize = new syncInt(0); topoLog = new syncInt(0); bidir = new syncInt(0); hops = new syncInt(0); ecmp = new syncInt(0); return; } log.addAll(old.log); logSize = old.logSize; topoLog = old.topoLog; bidir = old.bidir; hops = old.hops; ecmp = old.ecmp; count = old.count + 1; if (topoLog.get() > 0) { prev = old; } spfLog ntry = new spfLog(); ntry.when = old.tim1; ntry.tim = (int) (old.tim4 - old.tim1); ntry.unreach = old.listReachablility(false); ntry.topo = old.listTopoHsh(); log.add(ntry); int max = logSize.get(); for (; log.size() > max;) { log.remove(0); } } /** * get log mode * * @return mode */ public String getTopoLogMode() { String a = ""; int mod = topoLog.get(); if ((mod & 0x1) == 0) { a += "noappear"; } if ((mod & 0x2) == 0) { a += "noconnect"; } if ((mod & 0x4) == 0) { a += "noforward"; } if ((mod & 0x8) == 0) { a += "noreachable"; } if ((mod & 0x10) == 0) { a += "nometric"; } if ((mod & 0x20) == 0) { a += "noprefix"; } return a; } /** * get log mode * * @param cmd commands */ public void setTopoLogMode(cmds cmd) { int mod = 0xffff; for (;;) { String a = cmd.word(); if (a.length() < 1) { break; } if (a.equals("noappear")) { mod &= ~0x1; continue; } if (a.equals("noconnect")) { mod &= ~0x2; continue; } if (a.equals("noforward")) { mod &= ~0x4; continue; } if (a.equals("noreachable")) { mod &= ~0x8; continue; } if (a.equals("nometric")) { mod &= ~0x10; continue; } if (a.equals("noprefix")) { mod &= ~0x20; continue; } } topoLog.set(mod); } /** * copy topology * * @return copy */ public spfCalc copyBytes() { spfCalc res = new spfCalc(this); res.bidir.set(bidir.get()); res.hops.set(hops.get()); res.ecmp.set(ecmp.get()); for (int o = 0; o < nodes.size(); o++) { spfNode nod = nodes.get(o); for (int i = 0; i < nod.conn.size(); i++) { spfConn con = nod.conn.get(i); res.addConn(nod.name, con.target.name, con.metric, con.realHop, con.stub, con.ident); } for (int i = 0; i < nod.prfAdd.size(); i++) { res.addPref(nod.name, nod.prfAdd.get(i), false); } for (int i = 0; i < nod.prfFix.size(); i++) { res.addPref(nod.name, nod.prfFix.get(i), true); } for (int i = 0; i < nod.othAdd.size(); i++) { res.addOpref(nod.name, nod.othAdd.get(i), false); } for (int i = 0; i < nod.othFix.size(); i++) { res.addOpref(nod.name, nod.othFix.get(i), true); } res.addStub(nod.name, nod.stub); res.addAlgo(nod.name, nod.algo); res.addIdent(nod.name, nod.ident); res.addSegRouB(nod.name, nod.srBeg); res.addSegRouI(nod.name, nod.srIdx); res.addBierB(nod.name, nod.brBeg); res.addBierI(nod.name, nod.brIdx); res.addBierS(nod.name, nod.brSub); } return res; } /** * add one connection * * @param from source node * @param to target node * @param metric metric * @param realHop true if hop, false if network * @param stub stub adjacency * @param ident link id */ public void addConn(Ta from, Ta to, int metric, boolean realHop, boolean stub, String ident) { if (metric < 0) { metric = 0; } spfNode ntry = new spfNode(to); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } spfConn c = new spfConn(); c.metric = metric; c.target = ntry; c.realHop = realHop; c.stub = stub; c.ident = ident; ntry = new spfNode(from); old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.conn.add(c); } /** * add next hop * * @param met metric of interface * @param nod node to add * @param hop hop to add * @param ifc interface number * @param ohop other hop to add * @param oifc other interface number */ public void addNextHop(int met, Ta nod, addrIP hop, tabRouteIface ifc, addrIP ohop, tabRouteIface oifc) { spfNode ntry = new spfNode(nod); ntry = nodes.find(ntry); if (ntry == null) { return; } if (ntry.uplinks == null) { return; } if (hop != null) { hop = hop.copyBytes(); } if (ohop != null) { ohop = ohop.copyBytes(); } if (met > ntry.nxtMet) { return; } if (met < ntry.nxtMet) { for (int i = 0; i < ntry.uplinks.size(); i++) { spfResult upl = ntry.uplinks.get(i); upl.nxtHop = null; upl.iface = null; upl.othHop = null; upl.oface = null; } ntry.nxtMet = met; } for (int i = 0; i < ntry.uplinks.size(); i++) { spfResult upl = ntry.uplinks.get(i); if (upl.hops > 1) { continue; } if (upl.iface != null) { continue; } upl.nxtHop = hop; upl.iface = ifc; upl.othHop = ohop; upl.oface = oifc; return; } } /** * add prefix * * @param nod node to add * @param rou route * @param fix fixed metric */ public void addPref(Ta nod, tabRouteEntry rou, boolean fix) { spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } if (fix) { ntry.prfFix.add(tabRoute.addType.ecmp, rou, false, false); } else { ntry.prfAdd.add(tabRoute.addType.ecmp, rou, false, false); } } /** * add other prefix * * @param nod node to add * @param rou route * @param fix fixed metric */ public void addOpref(Ta nod, tabRouteEntry rou, boolean fix) { spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } if (fix) { ntry.othFix.add(tabRoute.addType.ecmp, rou, false, false); } else { ntry.othAdd.add(tabRoute.addType.ecmp, rou, false, false); } } /** * add algorithm * * @param nod node to add * @param algo algorithm */ public void addAlgo(Ta nod, List algo) { spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.algo.addAll(algo); } /** * add stub status * * @param nod node to add * @param st stub status */ public void addStub(Ta nod, boolean st) { spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.stub = st; } /** * add ident * * @param nod node to add * @param ident link id */ public void addIdent(Ta nod, String ident) { if (ident == null) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.ident = ident; } /** * add segment routing base * * @param nod node to add * @param beg base label */ public void addSegRouB(Ta nod, int beg) { if (beg < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } if (ntry.srBeg != 0) { return; } ntry.srBeg = beg; } /** * add segment routing index * * @param nod node to add * @param idx node index */ public void addSegRouI(Ta nod, int idx) { if (idx < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.srIdx = idx; } /** * add segment routing index * * @param nod node to add * @param pref prefix to update * @param idx node index * @param src rouSrc */ public void addSegRouI(Ta nod, addrPrefix pref, int idx, int src) { if (idx < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.srIdx = idx; tabRouteEntry rou; rou = ntry.prfFix.find(pref); if (rou != null) { rou.best.segrouIdx = idx; rou.best.rouSrc |= src; } rou = ntry.prfAdd.find(pref); if (rou != null) { rou.best.segrouIdx = idx; rou.best.rouSrc |= src; } rou = ntry.othFix.find(pref); if (rou != null) { rou.best.segrouIdx = idx; rou.best.rouSrc |= src; } rou = ntry.othAdd.find(pref); if (rou != null) { rou.best.segrouIdx = idx; rou.best.rouSrc |= src; } } /** * add bier base * * @param nod node to add * @param beg base label */ public void addBierB(Ta nod, int beg) { if (beg < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } if (ntry.brBeg != 0) { return; } ntry.brBeg = beg; } /** * add bier index * * @param nod node to add * @param idx node index */ public void addBierI(Ta nod, int idx) { if (idx < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.brIdx = idx; ntry.brLst.add(new spfIndex(idx)); } /** * add bier subdomain * * @param nod node to add * @param sub node subdomain */ public void addBierS(Ta nod, int sub) { if (sub < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.brSub = sub; ntry.brLst.add(new spfIndex(sub)); } /** * add bier index * * @param nod node to add * @param pref prefix * @param idx node index * @param hdr header * @param sub subdomain */ public void addBierI(Ta nod, addrPrefix pref, int idx, int hdr, int sub) { if (idx < 1) { return; } spfNode ntry = new spfNode(nod); spfNode old = nodes.add(ntry); if (old != null) { ntry = old; } ntry.brIdx = idx; ntry.brLst.add(new spfIndex(idx)); tabRouteEntry rou; rou = ntry.prfFix.find(pref); if (rou != null) { rou.best.bierIdx = idx; rou.best.bierHdr = hdr; rou.best.bierSub = sub; } rou = ntry.prfAdd.find(pref); if (rou != null) { rou.best.bierIdx = idx; rou.best.bierHdr = hdr; rou.best.bierSub = sub; } rou = ntry.othFix.find(pref); if (rou != null) { rou.best.bierIdx = idx; rou.best.bierHdr = hdr; rou.best.bierSub = sub; } rou = ntry.othAdd.find(pref); if (rou != null) { rou.best.bierIdx = idx; rou.best.bierHdr = hdr; rou.best.bierSub = sub; } } private void diffPrefix(spfNode nod, tabRoute cl, tabRoute ol) { for (int i = 0; i < cl.size(); i++) { tabRouteEntry cr = cl.get(i); tabRouteEntry or = ol.find(cr); if (or == null) { logger.info("prefix " + addrPrefix.ip2str(cr.prefix) + " appeared at " + nod); continue; } if (cr.best.metric != or.best.metric) { logger.info("prefix " + addrPrefix.ip2str(cr.prefix) + " metric changed at " + nod + " from " + or.best.metric + " to " + cr.best.metric); continue; } if (cr.best.tag != or.best.tag) { logger.info("prefix " + addrPrefix.ip2str(cr.prefix) + " tag changed at " + nod + " from " + or.best.tag + " to " + cr.best.tag); continue; } } for (int i = 0; i < ol.size(); i++) { tabRouteEntry or = ol.get(i); tabRouteEntry cr = cl.find(or); if (cr == null) { logger.info("prefix " + addrPrefix.ip2str(or.prefix) + " lost at " + nod); continue; } } } /** * collect exclude list * * @param algo algorithm * @return exclude list */ public tabGen> flexExcl(int algo) { tabGen> res = new tabGen>(); for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.algo.indexOf(algo) >= 0) { continue; } res.add(ntry); } return res; } /** * find shortest path * * @param excl exclude list * @param from starting node * @param to target node, null to every node * @return false on success, true on error */ public boolean doWork(tabGen> excl, Ta from, Ta to) { tim2 = bits.getTime(); if (excl == null) { excl = new tabGen>(); } for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } ntry.uplink = null; ntry.uplinks = null; ntry.result = null; ntry.metric = Integer.MAX_VALUE; ntry.nxtMet = Integer.MAX_VALUE; ntry.visited = false; } spfNode ntry = nodes.find(new spfNode(from)); if (ntry == null) { prev = null; return true; } spfRoot = ntry; tabGen> lst = new tabGen>(); ntry.metric = 0; ntry.visited = true; lst.add(ntry); boolean frst = true; boolean bid = bidir.get() != 0; boolean ecm = ecmp.get() != 0; boolean hps = hops.get() != 0; boolean res = true; for (;;) { if (lst.size() < 1) { break; } ntry = lst.get(0); for (int i = 1; i < lst.size(); i++) { spfNode cur = lst.get(i); if (cur.metric < ntry.metric) { ntry = cur; } } if (to != null) { if (to.compareTo(ntry.name) == 0) { res = false; break; } } lst.del(ntry); if (excl.find(ntry) != null) { continue; } ntry.visited = true; if ((!frst) && ntry.stub) { continue; } for (int i = 0; i < ntry.conn.size(); i++) { spfConn c = ntry.conn.get(i); if (c == null) { continue; } if ((!frst) && c.stub) { continue; } if (bid) { if (c.target.findConn(ntry, -1) == null) { continue; } } int o = ntry.metric + c.metric; if (c.target.metric < o) { continue; } int p; if (frst) { p = 0; } else { p = ntry.uplink.hops; } if (c.realHop) { p++; } spfResult upl = new spfResult(ntry, p); if (c.target.metric != o) { c.target.uplinks = new ArrayList>(); c.target.uplinks.add(upl); c.target.uplink = upl; c.target.metric = o; lst.add(c.target); continue; } if (hps && (upl.hops > c.target.uplink.hops)) { continue; } if (ecm) { c.target.uplinks.add(upl); } if (c.target.uplink.compareTo(upl) < 0) { continue; } if (!ecm) { c.target.uplinks.clear(); c.target.uplinks.add(upl); } if (hps && (upl.hops < c.target.uplink.hops)) { c.target.uplinks.clear(); c.target.uplinks.add(upl); } c.target.uplink = upl; } frst = false; } tim3 = bits.getTime(); if (prev == null) { return res; } int mode = topoLog.get(); for (int o = 0; o < prev.nodes.size(); o++) { spfNode cn = prev.nodes.get(o); if (cn == null) { continue; } spfNode on = nodes.find(cn); if (on == null) { if ((mode & 0x1) != 0) { logger.warn("old node " + cn + " disappeared"); } continue; } } for (int o = 0; o < nodes.size(); o++) { spfNode cn = nodes.get(o); if (cn == null) { continue; } spfNode on = prev.nodes.find(cn); if (on == null) { if ((mode & 0x1) != 0) { logger.warn("new node " + cn + " appeared"); } continue; } for (int i = 0; i < cn.conn.size(); i++) { spfConn cc = cn.conn.get(i); if (cc == null) { continue; } spfConn oc = on.findConn(cc.target, cc.metric); if (oc == null) { if ((mode & 0x2) != 0) { logger.warn("node " + cn + " established connection to " + cc.target); } continue; } if (cc.metric != oc.metric) { if ((mode & 0x10) != 0) { logger.warn("metric changed on node " + cn + " toward " + cc.target + " from " + oc.metric + " to " + cc.metric); } } if (cc.stub && !oc.stub) { if ((mode & 0x4) != 0) { logger.warn("node " + cn + " unwilling to forward to " + cc.target); } } if (!cc.stub && oc.stub) { if ((mode & 0x4) != 0) { logger.warn("node " + cn + " willing to forward to " + cc.target); } } } for (int i = 0; i < on.conn.size(); i++) { spfConn oc = on.conn.get(i); if (oc == null) { continue; } spfConn cc = cn.findConn(oc.target, oc.metric); if (cc == null) { if ((mode & 0x2) != 0) { logger.warn("node " + on + " lost connection to " + oc.target); } continue; } } if (on.visited && !cn.visited) { if ((mode & 0x8) != 0) { logger.warn("node " + cn + " became unreachable"); } } if (!on.visited && cn.visited) { if ((mode & 0x8) != 0) { logger.warn("node " + cn + " became reachable"); } } if ((mode & 0x20) != 0) { diffPrefix(cn, cn.prfAdd, on.prfAdd); diffPrefix(cn, cn.prfFix, on.prfFix); diffPrefix(cn, cn.othAdd, on.othAdd); diffPrefix(cn, cn.othFix, on.othFix); } } prev = null; return res; } /** * find next hops * * @param which node id * @return list of next hops */ protected List> findNextHop(Ta which) { List> res = new ArrayList>(); spfNode old = nodes.find(new spfNode(which)); if (old == null) { return res; } if (old.result != null) { return old.result; } List> ned = new ArrayList>(); ned.add(new spfResult(old, -1)); for (;;) { if (ned.size() < 1) { break; } spfResult cur = ned.remove(0); if (cur.nodeH.uplinks == null) { continue; } for (int i = 0; i < cur.nodeH.uplinks.size(); i++) { spfResult upl = cur.nodeH.uplinks.get(i); int hops = cur.hops; if (hops < 0) { hops = upl.hops; } if (upl.iface == null) { ned.add(new spfResult(upl.nodeH, hops)); continue; } spfResult out = new spfResult(cur.nodeH, hops); out.iface = upl.iface; out.nxtHop = upl.nxtHop; out.oface = upl.oface; out.othHop = upl.othHop; out.srBeg = cur.nodeH.srBeg; out.brBeg = cur.nodeH.brBeg; res.add(out); } } old.result = res; return res; } /** * get metric to node * * @param ntry node to query * @return segment routing peers */ protected tabGen> findSegrouPeers(spfNode ntry) { tabGen> res = new tabGen>(); for (int i = 0; i < ntry.conn.size(); i++) { spfConn con = ntry.conn.get(i); if (con.target.srIdx < 1) { continue; } res.add(new tabIndex(con.target.srIdx, null)); } return res; } /** * get metric to node * * @param which node to query * @return metric to node, negative on error */ public int getMetric(Ta which) { spfNode ntry = nodes.find(new spfNode(which)); if (ntry == null) { return -1; } return ntry.metric; } /** * get segment routing base * * @param which node to query * @return label, -1=not found */ public int getSegRouB(Ta which) { spfNode ntry = nodes.find(new spfNode(which)); if (ntry == null) { return -1; } return ntry.srBeg; } /** * get bier base * * @param which node to query * @return label, -1=not found */ public int getBierB(Ta which) { spfNode ntry = nodes.find(new spfNode(which)); if (ntry == null) { return -1; } return ntry.brBeg; } private void doBier(spfNode ntry) { if (ntry.uplink == null) { return; } for (int o = 0; o < ntry.brLst.size(); o++) { ntry.uplink.nodeH.brLst.add(ntry.brLst.get(o)); } doBier(ntry.uplink.nodeH); } /** * get bier info * * @param fwd forwarder * @param base base * @param bsl bsl * @return calculated bier info */ public tabLabelBier getBierI(ipFwd fwd, int base, int bsl) { tabLabelBier res = new tabLabelBier(base, bsl); for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } doBier(ntry); } for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.uplink == null) { continue; } if (ntry.uplink.iface == null) { continue; } if (ntry.brBeg <= 0) { continue; } tabLabelBierN per = new tabLabelBierN(fwd, ntry.uplink.iface, ntry.uplink.nxtHop, ntry.brBeg, 0); for (int o = 0; o < ntry.brLst.size(); o++) { per.setBit(ntry.brLst.get(o).get() - 1); } res.peers.add(per); } tim4 = bits.getTime(); return res; } /** * list segment routing * * @return list of segment routing */ public String listSegRou() { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.srIdx <= 0) { continue; } s += " " + ntry + "=" + ntry.srIdx; } return s; } /** * list no segment routing * * @return list of no segment routing */ public String listNoSegRou() { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.srIdx > 0) { continue; } s += " " + ntry; } return s; } /** * list bier * * @return list of bier */ public String listBier() { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.brIdx <= 0) { continue; } s += " " + ntry + "=" + ntry.brIdx; } return s; } /** * list no bier * * @return list of no bier */ public String listNoBier() { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.brIdx > 0) { continue; } s += " " + ntry; } return s; } /** * count reachables * * @param state required state * @return number of reachable nodes */ public int countReachablility(boolean state) { int o = 0; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.visited != state) { continue; } o++; } return o; } /** * list reachables * * @param state required state * @return list of reachable nodes */ public String listReachablility(boolean state) { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.visited != state) { continue; } s += " " + ntry; } return s; } /** * list stubs * * @return list of stub nodes */ public String listStubs() { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } if (ntry.conn.size() > 1) { continue; } s += " " + ntry; } return s; } /** * list topology * * @return list of topology */ public int listTopoHsh() { cryHashCrc32 h = new cryHashCrc32(cryHashCrc32.polyCrc32i); h.init(); h.update(listTopoSum().getBytes()); return h.getCrc(); } /** * list topology * * @return list of topology */ public String listTopoSum() { String s = ""; for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } s += " " + ntry + "," + ntry.visited + "," + ntry.algo.size() + "," + ntry.conn.size() + "," + (ntry.prfFix.size() + ntry.prfAdd.size() + ntry.othFix.size() + ntry.othAdd.size()); } return s; } /** * list algorithm * * @return list of algorithm */ public userFormat listAlgorithm() { userFormat res = new userFormat("|", "node|algos"); for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } String a = ""; for (int o = 0; o < ntry.algo.size(); o++) { a += " " + ntry.algo.get(o); } res.add(ntry.name + "|" + a); } return res; } /** * list topology * * @param adr address of node * @return list of topology */ public userFormat listTopology(Ta adr) { userFormat res = new userFormat("|", "category|value|addition"); spfNode ntry = new spfNode(adr); ntry = nodes.find(ntry); if (ntry == null) { return null; } res.add("node|" + ntry.name); res.add("name|" + ntry.ident); res.add("reachable|" + ntry.visited); res.add("stub|" + (ntry.conn.size() <= 1)); res.add("uplink|" + ntry.uplink); if (ntry.uplinks != null) { res.add("uplinks|" + ntry.uplinks.size()); for (int i = 0; i < ntry.uplinks.size(); i++) { spfResult upl = ntry.uplinks.get(i); res.add("uplinknod|" + upl.nodeH); res.add("uplinkhop|" + upl.hops); } } if (ntry.result != null) { res.add("reaches|" + ntry.result.size()); for (int i = 0; i < ntry.result.size(); i++) { spfResult upl = ntry.result.get(i); res.add("reachnod|" + upl.nodeH); res.add("reachhop|" + upl.hops); res.add("reachvia|" + upl.nxtHop); res.add("reachifc|" + upl.iface); res.add("reachothvia|" + upl.othHop); res.add("reachothifc|" + upl.oface); } } res.add("reachmet|" + ntry.metric); res.add("hopmet|" + ntry.nxtMet); res.add("connections|" + ntry.conn.size()); res.add("prefixes|" + ntry.prfFix.size() + " " + ntry.prfAdd.size() + " " + ntry.othFix.size() + " " + ntry.othAdd.size()); res.add("segrout|" + ntry.srIdx + " " + ntry.srBeg); for (int i = 0; i < ntry.algo.size(); i++) { res.add("flexalgo|" + ntry.algo.get(i)); } res.add("bieri|" + ntry.brIdx + " " + ntry.brSub + " " + ntry.brBeg); String a = ""; for (int i = 0; i < ntry.brLst.size(); i++) { spfIndex idx = ntry.brLst.get(i); if (idx == null) { continue; } a += idx + " "; } res.add("biers|" + a); for (int i = 0; i < ntry.conn.size(); i++) { spfConn con = ntry.conn.get(i); if (con == null) { continue; } res.add("neighbor|" + con.target + "|" + con.metric + " " + con.ident); } for (int i = 0; i < ntry.prfFix.size(); i++) { tabRouteEntry rou = ntry.prfFix.get(i); if (rou == null) { continue; } res.add("fixprefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } for (int i = 0; i < ntry.prfAdd.size(); i++) { tabRouteEntry rou = ntry.prfAdd.get(i); if (rou == null) { continue; } res.add("addprefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } for (int i = 0; i < ntry.othFix.size(); i++) { tabRouteEntry rou = ntry.othFix.get(i); if (rou == null) { continue; } res.add("fixprefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } for (int i = 0; i < ntry.othAdd.size(); i++) { tabRouteEntry rou = ntry.othAdd.get(i); if (rou == null) { continue; } res.add("addprefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } return res; } /** * list topology * * @return list of topology */ public userFormat listTopology() { userFormat res = new userFormat("|", "node|category|value|addition"); for (int i = 0; i < nodes.size(); i++) { spfNode ntry = nodes.get(i); if (ntry == null) { continue; } res.add(ntry + "|reach|" + ntry.visited + "|" + ntry.conn.size()); res.add(ntry + "|segrou|" + ntry.srIdx); res.add(ntry + "|bieri|" + ntry.brIdx); res.add(ntry + "|bierd|" + ntry.brSub); for (int o = 0; o < ntry.algo.size(); o++) { res.add(ntry + "|flexalgo|" + ntry.algo.get(o)); } for (int o = 0; o < ntry.conn.size(); o++) { spfConn con = ntry.conn.get(o); if (con == null) { continue; } res.add(ntry + "|neigh|" + con.target + "|" + con.metric); } for (int o = 0; o < ntry.prfFix.size(); o++) { tabRouteEntry rou = ntry.prfFix.get(o); if (rou == null) { continue; } res.add(ntry + "|prefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } for (int o = 0; o < ntry.prfAdd.size(); o++) { tabRouteEntry rou = ntry.prfAdd.get(o); if (rou == null) { continue; } res.add(ntry + "|prefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } for (int o = 0; o < ntry.othFix.size(); o++) { tabRouteEntry rou = ntry.othFix.get(o); if (rou == null) { continue; } res.add(ntry + "|prefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } for (int o = 0; o < ntry.othAdd.size(); o++) { tabRouteEntry rou = ntry.othAdd.get(o); if (rou == null) { continue; } res.add(ntry + "|prefix|" + addrPrefix.ip2str(rou.prefix) + "|" + rou.best.metric); } } return res; } /** * list statistics * * @return list */ public userFormat listStatistics() { userFormat res = new userFormat("|", "category|value"); res.add("reach|" + listReachablility(true)); res.add("reachable|" + countReachablility(true)); res.add("unreach|" + listReachablility(false)); res.add("unreachable|" + countReachablility(false)); res.add("stub|" + listStubs()); res.add("segrou|" + listSegRou()); res.add("nosegrou|" + listNoSegRou()); res.add("bier|" + listBier()); res.add("nobier|" + listNoBier()); res.add("topostr|" + listTopoSum()); res.add("topoid|" + bits.toHexD(listTopoHsh())); res.add("last|" + bits.time2str(cfgAll.timeZoneName, tim1 + cfgAll.timeServerOffset, 3) + " (" + bits.timePast(tim1) + " ago)"); res.add("fill|" + (tim2 - tim1)); res.add("calc|" + (tim3 - tim2)); res.add("table|" + (tim4 - tim3)); res.add("run|" + count); return res; } /** * list statistics * * @return list */ public userFormat listUsages() { userFormat res = new userFormat("|", "when|ago|time|topoid|unreach"); for (int i = log.size() - 1; i >= 0; i--) { res.add("" + log.get(i)); } return res; } /** * list tree * * @return list */ public List listTree() { List res = new ArrayList(); if (spfRoot == null) { return res; } listTree(res, spfRoot, ""); return res; } private void listTree(List res, spfNode ntry, String pref) { List> down = new ArrayList>(); for (int i = 0; i < ntry.conn.size(); i++) { spfConn cur = ntry.conn.get(i); if (cur.target.uplink == null) { continue; } if (ntry.compareTo(cur.target.uplink.nodeH) != 0) { continue; } down.add(cur); } res.add(pref + "`--" + ntry); for (int i = 0; i < down.size(); i++) { spfConn cur = down.get(i); String a = (i + 1) == down.size() ? " " : " |"; listTree(res, cur.target, pref + a); } } /** * list graphviz * * @param msk masks: 1=cli, 2=svg, 4=nets, 8=ints * @return list */ public List listGraphviz(int msk) { List res = new ArrayList(); if ((msk & 0x1) == 0) { res.add(graphBeg1); } res.add(graphBeg2); if ((msk & 0x2) == 0) { res.add(graphBeg3); } for (int o = 0; o < nodes.size(); o++) { spfNode ntry = nodes.get(o); res.add("//" + ntry); for (int i = 0; i < ntry.conn.size(); i++) { spfConn cur = ntry.conn.get(i); String a; if ((msk & 0x8) == 0) { a = ""; } else { a = " [taillabel=\"" + cur.ident + "\"]"; } res.add(" \"" + ntry + "\" -- \"" + cur.target + "\" [weight=" + cur.metric + "]" + a); } if ((msk & 0x4) == 0) { continue; } for (int i = 0; i < ntry.prfAdd.size(); i++) { tabRouteEntry cur = ntry.prfAdd.get(i); res.add(" \"" + ntry + "\" -- \"" + addrPrefix.ip2str(cur.prefix) + "\" [weight=" + cur.best.metric + "]"); } for (int i = 0; i < ntry.prfFix.size(); i++) { tabRouteEntry cur = ntry.prfFix.get(i); res.add(" \"" + ntry + "\" -- \"" + addrPrefix.ip2str(cur.prefix) + "\" [weight=" + cur.best.metric + "]"); } for (int i = 0; i < ntry.othAdd.size(); i++) { tabRouteEntry cur = ntry.othAdd.get(i); res.add(" \"" + ntry + "\" -- \"" + addrPrefix.ip2str(cur.prefix) + "\" [weight=" + cur.best.metric + "]"); } for (int i = 0; i < ntry.othFix.size(); i++) { tabRouteEntry cur = ntry.othFix.get(i); res.add(" \"" + ntry + "\" -- \"" + addrPrefix.ip2str(cur.prefix) + "\" [weight=" + cur.best.metric + "]"); } } res.add(graphEnd1); if ((msk & 0x1) == 0) { res.add(graphEnd2); } return res; } private void listNhIncons(tabGen> lst, spfNode nod, addrPrefix pfx) { spfPrefix ntry = new spfPrefix(pfx); spfPrefix old = lst.add(ntry); if (old != null) { ntry = old; } ntry.nodes.add(nod); } /** * hostnames * * @return text */ public userFormat listHostnames() { userFormat res = new userFormat("|", "router|name"); for (int o = 0; o < nodes.size(); o++) { spfNode ntry = nodes.get(o); res.add(ntry.name + "|" + ntry.ident); } return res; } /** * inconsistent next hops * * @param mtch matcher * @return text */ public userFormat listNhIncons(tabIntMatcher mtch) { tabGen> lst = new tabGen>(); for (int o = 0; o < nodes.size(); o++) { spfNode ntry = nodes.get(o); for (int i = 0; i < ntry.prfFix.size(); i++) { listNhIncons(lst, ntry, ntry.prfFix.get(i).prefix); } for (int i = 0; i < ntry.prfAdd.size(); i++) { listNhIncons(lst, ntry, ntry.prfAdd.get(i).prefix); } for (int i = 0; i < ntry.othFix.size(); i++) { listNhIncons(lst, ntry, ntry.othFix.get(i).prefix); } for (int i = 0; i < ntry.othAdd.size(); i++) { listNhIncons(lst, ntry, ntry.othAdd.get(i).prefix); } } userFormat res = new userFormat("|", "path|nexthops"); for (int i = 0; i < lst.size(); i++) { spfPrefix ntry = lst.get(i); if (!mtch.matches(ntry.nodes.size())) { continue; } res.add("" + ntry); } return res; } /** * inconsistent metrics * * @param mtch matcher * @return text */ public userFormat listMetIncons(tabIntMatcher mtch) { userFormat res = new userFormat("|", "source|target|diff"); for (int o = 0; o < nodes.size(); o++) { spfNode ntry = nodes.get(o); for (int i = 0; i < ntry.conn.size(); i++) { spfConn cn = ntry.conn.get(i); spfConn co = cn.target.findConn(ntry, cn.metric); if (co == null) { res.add(ntry + "|" + cn.target + "|missing"); continue; } int p = cn.metric - co.metric; if (p < 0) { p = -p; } if (p < 1) { continue; } if (!mtch.matches(p)) { continue; } res.add(ntry + "|" + cn.target + "|" + p); } } return res; } /** * get routes * * @param fwdCor forwarding core * @param fwdKey forwarder key * @param segrouLab segment routing labels * @param segrouUsd segment routing usage * @return routes */ public tabRoute getRoutes(ipFwd fwdCor, tabLabelEntry.owner fwdKey, tabLabelEntry[] segrouLab, tabGen> segrouUsd) { tabRoute tab1 = new tabRoute("routes"); for (int o = 0; o < nodes.size(); o++) { spfNode ntry = nodes.get(o); List> hop = findNextHop(ntry.name); if (hop.size() < 1) { continue; } int met = getMetric(ntry.name); int sro = getSegRouB(ntry.name); int bro = getBierB(ntry.name); tabGen> srp = findSegrouPeers(ntry); for (int i = 0; i < ntry.prfAdd.size(); i++) { tabRouteEntry rou = ntry.prfAdd.get(i).copyBytes(tabRoute.addType.notyet); rou.best.srcRtr = ntry.name.copyBytes(); rou.best.segrouOld = sro; rou.best.bierOld = bro; rou.best.metric += met; populateRoute(tab1, fwdCor, ntry, fwdKey, segrouLab, segrouUsd, srp, rou, hop, false); } for (int i = 0; i < ntry.prfFix.size(); i++) { tabRouteEntry rou = ntry.prfFix.get(i).copyBytes(tabRoute.addType.notyet); rou.best.srcRtr = ntry.name.copyBytes(); rou.best.segrouOld = sro; rou.best.bierOld = bro; populateRoute(tab1, fwdCor, ntry, fwdKey, segrouLab, segrouUsd, srp, rou, hop, false); } } tim4 = bits.getTime(); return tab1; } /** * get other routes * * @param fwdCor forwarding core * @param fwdKey forwarder key * @param segrouLab segment routing labels * @param segrouUsd segment routing usage * @return routes */ public tabRoute getOroutes(ipFwd fwdCor, tabLabelEntry.owner fwdKey, tabLabelEntry[] segrouLab, tabGen> segrouUsd) { tabRoute tab1 = new tabRoute("routes"); for (int o = 0; o < nodes.size(); o++) { spfNode ntry = nodes.get(o); List> hop = findNextHop(ntry.name); if (hop.size() < 1) { continue; } int met = getMetric(ntry.name); int sro = getSegRouB(ntry.name); int bro = getBierB(ntry.name); tabGen> srp = findSegrouPeers(ntry); for (int i = 0; i < ntry.othAdd.size(); i++) { tabRouteEntry rou = ntry.othAdd.get(i).copyBytes(tabRoute.addType.notyet); rou.best.srcRtr = ntry.name.copyBytes(); rou.best.segrouOld = sro; rou.best.bierOld = bro; rou.best.metric += met; populateRoute(tab1, fwdCor, ntry, fwdKey, segrouLab, segrouUsd, srp, rou, hop, true); } for (int i = 0; i < ntry.othFix.size(); i++) { tabRouteEntry rou = ntry.othFix.get(i).copyBytes(tabRoute.addType.notyet); rou.best.srcRtr = ntry.name.copyBytes(); rou.best.segrouOld = sro; rou.best.bierOld = bro; populateRoute(tab1, fwdCor, ntry, fwdKey, segrouLab, segrouUsd, srp, rou, hop, true); } } tim4 = bits.getTime(); return tab1; } private void populateRoute(tabRoute tab1, ipFwd fwdCor, spfNode ntry, tabLabelEntry.owner fwdKey, tabLabelEntry[] segrouLab, tabGen> segrouUsd, tabGen> srp, tabRouteEntry rou, List> hop, boolean other) { rou.alts.clear(); boolean srPop = (rou.best.rouSrc & 16) != 0; for (int i = 0; i < hop.size(); i++) { spfResult upl = hop.get(i); tabRouteAttr res = new tabRouteAttr(); rou.best.copyBytes(res, false); if (!other) { if (upl.nxtHop == null) { continue; } res.nextHop = upl.nxtHop.copyBytes(); res.iface = upl.iface; } else { if (upl.othHop == null) { continue; } res.nextHop = upl.othHop.copyBytes(); res.iface = upl.oface; } res.hops = upl.hops; res.segrouBeg = upl.srBeg; res.bierBeg = upl.brBeg; res.segrouIdx = rou.best.segrouIdx; res.segrouOld = rou.best.segrouOld; res.bierIdx = rou.best.bierIdx; res.bierHdr = rou.best.bierHdr; res.bierOld = rou.best.bierOld; if ((segrouUsd == null) || (res.segrouIdx < 1) || (res.segrouBeg < 1)) { res.labelRem = null; rou.addAlt(res); continue; } res.labelRem = tabLabel.int2labels(res.segrouBeg + res.segrouIdx); if (srPop && (res.hops <= 1)) { res.labelRem = tabLabel.int2labels(ipMpls.labelImp); } rou.addAlt(res); } rou.hashBest(); long oldVer = tab1.version; tab1.add(tabRoute.addType.ecmp, rou, false, true); if (oldVer == tab1.version) { return; } if (segrouUsd == null) { return; } if (rou.best.labelRem == null) { return; } if (rou.best.segrouIdx >= segrouLab.length) { return; } segrouLab[rou.best.segrouIdx].setFwdMpls(fwdKey, fwdCor, (ipFwdIface) rou.best.iface, rou.best.nextHop, rou.best.labelRem); tabIndex sri = new tabIndex(rou.best.segrouIdx, rou.prefix); sri.neighs = srp; sri.conned = ntry.nxtMet < Integer.MAX_VALUE; tabIndex.add2table(segrouUsd, sri); } /** * list link states * * @param tab table to populate * @param prt protocol id * @param par parameter * @param asn asn * @param adv advertiser * @param sizN size of node * @param sizM size of metric */ public void listLinkStates(tabRoute tab, int prt, int par, int asn, addrIPv4 adv, int sizN, int sizM) { encTlv tlv = spfLnkst.getTlv(); packHolder pck = new packHolder(true, true); packHolder hlp = new packHolder(true, true); for (int o = 0; o < nodes.size(); o++) { spfNode nod = nodes.get(o); spfLnkst.createHeader(tlv, pck, prt, spfLnkst.nlriTypNode); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, nod, spfLnkst.typNodeLocal); hlp.clear(); if (nod.ident != null) { tlv.putStr(hlp, spfLnkst.typNodeName, nod.ident); } spfLnkst.createEntry(tab, null, tlv, pck, hlp, 0, 0); for (int i = 0; i < nod.conn.size(); i++) { spfConn con = nod.conn.get(i); spfLnkst.createHeader(tlv, pck, prt, spfLnkst.nlriTypLink); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, nod, spfLnkst.typNodeLocal); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, con.target, spfLnkst.typNodeRemote); hlp.clear(); spfLnkst.createEntry(tab, null, tlv, pck, hlp, sizM, con.metric); } for (int i = 0; i < nod.prfFix.size(); i++) { tabRouteEntry rou = nod.prfFix.get(i); spfLnkst.createHeader(tlv, pck, prt, spfLnkst.getPrefixType(rou)); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, nod, spfLnkst.typNodeLocal); spfLnkst.createPrefix(tab, null, tlv, pck, hlp, rou); } for (int i = 0; i < nod.prfAdd.size(); i++) { tabRouteEntry rou = nod.prfAdd.get(i); spfLnkst.createHeader(tlv, pck, prt, spfLnkst.getPrefixType(rou)); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, nod, spfLnkst.typNodeLocal); spfLnkst.createPrefix(tab, null, tlv, pck, hlp, rou); } for (int i = 0; i < nod.othFix.size(); i++) { tabRouteEntry rou = nod.othFix.get(i); spfLnkst.createHeader(tlv, pck, prt, spfLnkst.getPrefixType(rou)); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, nod, spfLnkst.typNodeLocal); spfLnkst.createPrefix(tab, null, tlv, pck, hlp, rou); } for (int i = 0; i < nod.othAdd.size(); i++) { tabRouteEntry rou = nod.othAdd.get(i); spfLnkst.createHeader(tlv, pck, prt, spfLnkst.getPrefixType(rou)); spfLnkst.createNode(tlv, pck, hlp, sizN, asn, adv, par, nod, spfLnkst.typNodeLocal); spfLnkst.createPrefix(tab, null, tlv, pck, hlp, rou); } } } }