package org.freertr.rtr; import java.util.ArrayList; import java.util.List; import org.freertr.addr.addrIP; import org.freertr.addr.addrIPv4; import org.freertr.addr.addrIPv6; import org.freertr.addr.addrPrefix; import org.freertr.cfg.cfgAll; import org.freertr.cfg.cfgIfc; import org.freertr.cfg.cfgPlymp; import org.freertr.cfg.cfgRtr; import org.freertr.cfg.cfgVrf; import org.freertr.ip.ipFwd; import org.freertr.ip.ipRtr; import org.freertr.tab.tabGen; import org.freertr.tab.tabIndex; import org.freertr.tab.tabLabel; import org.freertr.tab.tabListing; import org.freertr.tab.tabPlcmapN; import org.freertr.tab.tabQos; import org.freertr.tab.tabRoute; import org.freertr.tab.tabRouteAttr; import org.freertr.tab.tabRouteEntry; import org.freertr.tab.tabRouteUtil; import org.freertr.user.userHelping; import org.freertr.util.bits; import org.freertr.util.cmds; import org.freertr.util.debugger; import org.freertr.util.logger; /** * bgp4 vrf router * * @author matecsaba */ public class rtrBgpVrfRtr extends ipRtr { /** * import distance */ public int distance; /** * flow specification */ public tabListing flowSpec; /** * install flow specification */ public boolean flowInst; /** * import mode */ public int imprtMode; /** * export mode */ public int exprtMode; /** * originating interface */ public cfgIfc iface; /** * mvpn advertisement source */ public cfgIfc mvpn; /** * srv6 advertisement source */ public cfgIfc srv6; /** * default information originate */ public boolean defRou; /** * forwarder override */ public ipFwd setVrfF; /** * vrf afi type updater */ public boolean setVrfT; /** * forwarder to use */ protected final ipFwd fwd; private final rtrBgp parent; private final cfgVrf vrf; private final boolean other; private final boolean ipv4; private tabGen peers = new tabGen(); /** * unregister from ip */ public void unregister2ip() { if (debugger.rtrBgpEvnt) { logger.debug("stop " + vrf); } fwd.routerDel(this); } /** * register to ip */ public void register2ip() { if (debugger.rtrBgpEvnt) { logger.debug("start " + vrf); } fwd.routerAdd(this, parent.rouTyp, parent.rtrNum); } /** * create new instance * * @param p parent to use * @param v vrf to use * @param o other afi */ public rtrBgpVrfRtr(rtrBgp p, cfgVrf v, boolean o) { if (o ^ (p.rouTyp == tabRouteAttr.routeType.bgp4)) { fwd = v.fwd4; ipv4 = true; } else { fwd = v.fwd6; ipv4 = false; } parent = p; other = o; vrf = v; routerVpn = true; distance = -1; imprtMode = 1; exprtMode = 1; } /** * convert to string * * @return string */ public String toString() { return "bgp on " + parent.fwdCore; } /** * create computed table */ public synchronized void routerCreateComputed() { } /** * redistribution changed */ public void routerRedistChanged() { parent.routerRedistChanged(); } /** * others changed */ public void routerOthersChanged() { if (fwd.prefixMode == ipFwd.labelMode.common) { return; } parent.routerRedistChanged(); } private void doExportRoute(int afi, tabRouteEntry ntry, tabRoute trg, List rt) { if (ntry == null) { return; } ntry = ntry.copyBytes(tabRoute.addType.ecmp); ntry.rouDst = fwd.rd; for (int i = 0; i < ntry.alts.size(); i++) { tabRouteAttr attr = ntry.alts.get(i); if (attr.labelLoc == null) { attr.labelLoc = fwd.commonLabel; } attr.extComm = tabRouteUtil.appendLongList(attr.extComm, rt); attr.rouSrc = rtrBgpUtil.peerOriginate; } if (iface != null) { addrIP adr = null; if (ipv4) { if (iface.addr4 != null) { adr = new addrIP(); adr.fromIPv4addr(iface.addr4); } } else { if (iface.addr6 != null) { adr = new addrIP(); adr.fromIPv6addr(iface.addr6); } } if (adr != null) { for (int i = 0; i < ntry.alts.size(); i++) { tabRouteAttr attr = ntry.alts.get(i); attr.nextHop = adr.copyBytes(); } } } tabRouteUtil.generateSrv6pfx(ntry, srv6, ntry.best.labelLoc); if (afi != rtrBgpUtil.sfiEthVpn) { tabRoute.addUpdatedEntry(tabRoute.addType.ecmp, trg, afi, 0, ntry, true, fwd.exportMap, fwd.exportPol, fwd.exportList); return; } for (int i = 0; i < ntry.alts.size(); i++) { tabRouteAttr attr = ntry.alts.get(i); attr.evpnLab = attr.labelLoc.label << 4; } byte[] buf = new byte[addrIP.size]; ntry.prefix.network.toBuffer(buf, 0); ntry.prefix.broadcast.fromBuf(buf, 0); buf = new byte[addrIP.size]; ntry.prefix.wildcard.fromBuf(buf, 0); buf[0] = 5; ntry.prefix.network.fromBuf(buf, 0); afi = rtrBgpUtil.sfiUnicast; tabRoute.addUpdatedEntry(tabRoute.addType.ecmp, trg, afi, 0, ntry, true, fwd.exportMap, fwd.exportPol, fwd.exportList); } /** * merge routes to table * * @param nUni unicast table to update * @param nMlt multicast table to update * @param nFlw flowspec table to update * @param nMvpn mvpn table to update */ public void doAdvertise(tabRoute nUni, tabRoute nMlt, tabRoute nFlw, tabRoute nMvpn) { final List rt = new ArrayList(); for (int i = 0; i < fwd.rtExp.size(); i++) { rt.add(tabRouteUtil.rt2comm(fwd.rtExp.get(i))); } for (int i = 0; i < fwd.clrExp.size(); i++) { rt.add(tabRouteUtil.clr2comm(fwd.clrExp.get(i))); } for (int i = 0; i < fwd.rtImp.size(); i++) { tabRouteEntry ntry = new tabRouteEntry(); ntry.prefix = tabRouteUtil.extcomm2rtfilter(parent.localAs, tabRouteUtil.rt2comm(fwd.rtImp.get(i))); ntry.best.rouSrc = rtrBgpUtil.peerOriginate; parent.newlyRtf.add(tabRoute.addType.always, ntry, false, true); } if (defRou) { tabRouteEntry ntry = new tabRouteEntry(); ntry.prefix = rtrBgpUtil.defaultRoute(other ? parent.afiOuni : parent.afiUni); ntry.best.aggrRtr = new addrIP(); ntry.best.aggrRtr.fromIPv4addr(parent.routerID); ntry.best.aggrAs = parent.localAs; if ((exprtMode & 1) != 0) { doExportRoute(rtrBgpUtil.sfiUnicast, ntry, nUni, rt); } if ((exprtMode & 2) != 0) { doExportRoute(rtrBgpUtil.sfiEthVpn, ntry, parent.newlyEvpn, rt); } ntry = new tabRouteEntry(); ntry.prefix = rtrBgpUtil.defaultRoute(other ? parent.afiOuni : parent.afiUni); ntry.best.aggrRtr = new addrIP(); ntry.best.aggrRtr.fromIPv4addr(parent.routerID); ntry.best.aggrAs = parent.localAs; doExportRoute(rtrBgpUtil.sfiMulticast, ntry, nMlt, rt); } for (int i = 0; i < routerRedistedU.size(); i++) { tabRouteEntry ntry = routerRedistedU.get(i); if ((exprtMode & 1) != 0) { doExportRoute(rtrBgpUtil.sfiUnicast, ntry, nUni, rt); } if ((exprtMode & 2) != 0) { doExportRoute(rtrBgpUtil.sfiEthVpn, ntry, parent.newlyEvpn, rt); } } for (int i = 0; i < routerRedistedM.size(); i++) { doExportRoute(rtrBgpUtil.sfiMulticast, routerRedistedM.get(i), nMlt, rt); } for (int i = 0; i < routerRedistedF.size(); i++) { doExportRoute(rtrBgpUtil.sfiFlwSpc, routerRedistedF.get(i), nFlw, rt); } tabRoute tab = new tabRoute("agg"); routerDoAggregates(parent.afiUni, nUni, tab, fwd.commonLabel, parent.routerID, parent.localAs); for (int i = 0; i < tab.size(); i++) { tabRouteEntry ntry = tab.get(i); if ((exprtMode & 1) != 0) { doExportRoute(rtrBgpUtil.sfiUnicast, ntry, nUni, rt); } if ((exprtMode & 2) != 0) { doExportRoute(rtrBgpUtil.sfiEthVpn, ntry, parent.newlyEvpn, rt); } } tab = new tabRoute("agg"); routerDoAggregates(parent.afiMlt, nMlt, tab, fwd.commonLabel, parent.routerID, parent.localAs); for (int i = 0; i < tab.size(); i++) { doExportRoute(rtrBgpUtil.sfiMulticast, tab.get(i), nMlt, rt); } if (flowSpec != null) { tabRouteEntry ntry = new tabRouteEntry(); ntry.best.extComm = new ArrayList(); ntry.rouDst = fwd.rd; ntry.best.extComm.addAll(rt); rtrBgpFlow.doAdvertise(nFlw, flowSpec, ntry, other ^ (parent.afiUni == rtrBgpUtil.safiIp6uni), parent.localAs); } if (mvpn == null) { return; } tabRouteEntry ntry = new tabRouteEntry(); ntry.prefix = rtrBgpUtil.defaultRoute(parent.afiUni); byte[] buf = new byte[128]; if (ipv4) { addrIPv4 adr = mvpn.addr4; if (adr != null) { adr.toBuffer(buf, 2); } buf[0] = addrIPv4.size; } else { addrIPv6 adr = mvpn.addr6; if (adr != null) { adr.toBuffer(buf, 2); } buf[0] = addrIPv6.size; } buf[0]++; buf[1] = 1; // intra-as pmsi ntry.prefix.network.fromBuf(buf, 0); ntry.prefix.broadcast.fromBuf(buf, 16); ntry.prefix.wildcard.fromBuf(buf, 32); ntry.prefix.mask.fromBuf(buf, 48); ntry.best.extComm = new ArrayList(); ntry.rouDst = fwd.rd; ntry.best.extComm.addAll(rt); ntry.best.rouSrc = rtrBgpUtil.peerOriginate; tabRoute.addUpdatedEntry(tabRoute.addType.better, nMvpn, other ? parent.afiVpoM : parent.afiVpnM, 0, ntry, true, fwd.exportMap, fwd.exportPol, fwd.exportList); } private List getRtList() { final List rt = new ArrayList(); for (int i = 0; i < fwd.rtImp.size(); i++) { rt.add(tabRouteUtil.rt2comm(fwd.rtImp.get(i))); } for (int i = 0; i < fwd.clrImp.size(); i++) { rt.add(tabRouteUtil.clr2comm(fwd.clrImp.get(i))); } return rt; } private addrPrefix evpn2prefix(int afi, addrPrefix prefix) { if (afi != rtrBgpUtil.sfiEthVpn) { return prefix; } if (ipv4 != prefix.broadcast.isIPv4()) { return null; } return tabRouteUtil.convertL3evpn(prefix); } private boolean doImportRoute(int afi, tabRouteEntry ntry, tabRoute trg, List rt) { if (ntry.best.rouSrc == rtrBgpUtil.peerOriginate) { return true; } if (ntry.best.extComm == null) { return true; } boolean needed = false; for (int i = 0; i < rt.size(); i++) { needed |= tabRouteUtil.findLongList(ntry.best.extComm, rt.get(i)) >= 0; if (needed) { break; } } if (!needed) { return true; } ntry = ntry.copyBytes(tabRoute.addType.ecmp); ntry.oldDst = ntry.rouDst; ntry.rouDst = 0; if (afi == rtrBgpUtil.sfiEthVpn) { ntry.prefix = evpn2prefix(rtrBgpUtil.sfiEthVpn, ntry.prefix); if (ntry.prefix == null) { return true; } for (int i = 0; i < ntry.alts.size(); i++) { tabRouteAttr attr = ntry.alts.get(i); attr.labelRem = tabLabel.prependLabel(attr.labelRem, attr.evpnLab >>> 4); } afi = rtrBgpUtil.sfiUnicast; } for (int i = 0; i < ntry.alts.size(); i++) { tabRouteAttr attr = ntry.alts.get(i); if (attr.rouTab == null) { attr.rouTab = parent.fwdCore; } if (attr.segrouPrf != null) { attr.rouTab = parent.vrfCore.fwd6; } if (setVrfF != null) { attr.rouTab = setVrfF; } if (distance > 0) { attr.distance = distance; } } tabRoute.addUpdatedEntry(tabRoute.addType.always, trg, afi, 0, ntry, false, fwd.importMap, fwd.importPol, fwd.importList); if (parent.routerAutoMesh == null) { return false; } peers.add(ntry.best.nextHop); return false; } /** * full import routes from table * * @param cmpU unicast table to read * @param cmpM multicast table to read * @param cmpF flowspec table to read * @return other changes trigger full recomputation */ public boolean doPeersFull(tabRoute cmpU, tabRoute cmpM, tabRoute cmpF) { routerChangedU = null; routerChangedM = null; routerChangedF = null; final List rt = getRtList(); tabRoute tabU = new tabRoute("bgp"); tabRoute tabM = new tabRoute("bgp"); tabRoute tabF = new tabRoute("bgp"); peers = new tabGen(); if ((imprtMode & 1) != 0) { for (int i = 0; i < cmpU.size(); i++) { doImportRoute(rtrBgpUtil.sfiUnicast, cmpU.get(i), tabU, rt); } for (int i = 0; i < cmpM.size(); i++) { doImportRoute(rtrBgpUtil.sfiMulticast, cmpM.get(i), tabM, rt); } } if ((imprtMode & 2) != 0) { for (int i = 0; i < parent.newlyEvpn.size(); i++) { doImportRoute(rtrBgpUtil.sfiEthVpn, parent.newlyEvpn.get(i), tabU, rt); } } for (int i = 0; i < cmpF.size(); i++) { doImportRoute(rtrBgpUtil.sfiFlwSpc, cmpF.get(i), tabF, rt); } if (flowSpec != null) { rtrBgpFlow.doAdvertise(tabF, flowSpec, new tabRouteEntry(), other ^ (parent.afiUni == rtrBgpUtil.safiIp6uni), parent.localAs); } if ((!tabU.differs(tabRoute.addType.alters, routerComputedU)) && (!tabU.differs(tabRoute.addType.alters, routerComputedM)) && (!tabF.differs(tabRoute.addType.alters, routerComputedF))) { return fwd.prefixMode != ipFwd.labelMode.common; } routerComputedU = tabU; routerComputedM = tabM; routerComputedF = tabF; routerComputedI = new tabGen>(); fwd.routerChg(this, true); if (flowInst) { fwd.flowspec = tabQos.convertPolicy(rtrBgpFlow.doDecode(tabF, other ^ (parent.afiUni == rtrBgpUtil.safiIp6uni))); } return fwd.prefixMode != ipFwd.labelMode.common; } private void doUpdateRoute(int afi, tabRouteEntry ntry, tabRoute chg, tabRoute trg, tabRoute cmp, List rt) { addrPrefix pfx = evpn2prefix(afi, ntry.prefix); if (pfx == null) { return; } tabRouteEntry res = trg.find(pfx); if (res == null) { res = cmp.find(ntry); if (res == null) { return; } if (doImportRoute(afi, res, trg, rt)) { return; } if (chg == null) { return; } chg.add(tabRoute.addType.always, pfx, null); return; } if (ntry.rouDst != res.oldDst) { return; } res = cmp.find(ntry); if (res == null) { trg.del(pfx); if (chg == null) { return; } chg.add(tabRoute.addType.always, pfx, null); return; } if (doImportRoute(afi, res, trg, rt)) { trg.del(pfx); } if (chg == null) { return; } chg.add(tabRoute.addType.always, pfx, null); } /** * incremental import routes from table * * @param cmpU unicast table to read * @param cmpM multicast table to read * @param cmpF flowspec table to read * @param chgU unicast table to process * @param chgM multicast table to process * @param chgF flowspec table to process * @param chgE evpn table to process * @return other changes trigger full recomputation */ public boolean doPeersIncr(tabRoute cmpU, tabRoute cmpM, tabRoute cmpF, tabRoute chgU, tabRoute chgM, tabRoute chgF, tabRoute chgE) { if ((chgU == null) || (chgM == null) || (chgF == null) || (chgE == null)) { if (debugger.rtrBgpFull) { logger.debug("changes disappeared"); } parent.needFull.add(1); parent.compute.wakeup(); return true; } final List rt = getRtList(); routerChangedU = new tabRoute("chg"); routerChangedM = new tabRoute("chg"); routerChangedF = new tabRoute("chg"); if ((imprtMode & 1) != 0) { for (int i = 0; i < chgU.size(); i++) { doUpdateRoute(rtrBgpUtil.sfiUnicast, chgU.get(i), routerChangedU, routerComputedU, cmpU, rt); } for (int i = 0; i < chgM.size(); i++) { doUpdateRoute(rtrBgpUtil.sfiMulticast, chgM.get(i), routerChangedM, routerComputedM, cmpM, rt); } } if ((imprtMode & 2) != 0) { for (int i = 0; i < chgE.size(); i++) { doUpdateRoute(rtrBgpUtil.sfiEthVpn, chgE.get(i), routerChangedU, routerComputedU, parent.computedEvpn, rt); } } for (int i = 0; i < chgF.size(); i++) { doUpdateRoute(rtrBgpUtil.sfiFlwSpc, chgF.get(i), routerChangedF, routerComputedF, cmpF, rt); } if ((routerChangedU.size() + routerChangedM.size() + routerChangedF.size()) < 1) { return fwd.prefixMode != ipFwd.labelMode.common; } fwd.routerChg(this, fwd.prefixMode != ipFwd.labelMode.common); if (flowInst && (chgF.size() > 0)) { tabRoute tabF = new tabRoute("bgp"); fwd.flowspec = tabQos.convertPolicy(rtrBgpFlow.doDecode(tabF, other ^ (parent.afiUni == rtrBgpUtil.safiIp6uni))); routerComputedF = tabF; } return fwd.prefixMode != ipFwd.labelMode.common; } /** * get help * * @param l list */ public void routerGetHelp(userHelping l) { } /** * get config * * @param l list * @param beg beginning * @param filter filter */ public void routerGetConfig(List l, String beg, int filter) { } /** * configure * * @param cmd command * @return false if success, true if error */ public boolean routerConfigure(cmds cmd) { return true; } /** * stop work */ public void routerCloseNow() { } /** * get config * * @param l list to append * @param beg1 beginning * @param beg2 beginning */ public void getConfig(List l, String beg1, String beg2) { beg2 += vrf.name + " "; l.add(beg1 + beg2 + "enable"); l.add(beg1 + beg2 + "distance " + distance); l.add(beg1 + beg2 + "import" + mode2string(imprtMode)); l.add(beg1 + beg2 + "export" + mode2string(exprtMode)); cmds.cfgLine(l, !defRou, beg1, beg2 + "default-originate", ""); cmds.cfgLine(l, !flowInst, beg1, beg2 + "flowspec-install", ""); cmds.cfgLine(l, flowSpec == null, beg1, beg2 + "flowspec-advert", "" + flowSpec); if (mvpn != null) { l.add(beg1 + beg2 + "mvpn " + mvpn.name); } if (srv6 != null) { l.add(beg1 + beg2 + "srv6 " + srv6.name); } if (setVrfF != null) { l.add(beg1 + beg2 + "set-vrf " + setVrfF.cfgName + " " + (setVrfT ? "ipv4" : "ipv6")); } if (iface != null) { l.add(beg1 + beg2 + "update-source " + iface.name); } cfgRtr.getShRedist(l, beg1 + beg2, this); l.add(beg1 + cmds.comment); } private final String mode2string(int i) { String a = ""; if ((i & 1) != 0) { a += " l3vpn"; } if ((i & 2) != 0) { a += " evpn"; } return a; } private final int string2mode(boolean negated, cmds cmd) { if (negated) { return 1; } int i = 0; for (;;) { String a = cmd.word(); if (a.length() < 1) { break; } if (a.equals("l3vpn")) { i |= 1; continue; } if (a.equals("evpn")) { i |= 2; continue; } } return i; } /** * configure the vrf * * @param negated negated command effect * @param cmd command parameters * @param s command to execute */ public void doConfig(boolean negated, cmds cmd, String s) { if (s.equals("distance")) { distance = bits.str2num(cmd.word()); parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("mvpn")) { if (negated) { mvpn = null; } else { mvpn = cfgAll.ifcFind(cmd.word(), 0); } parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("import")) { imprtMode = string2mode(negated, cmd); parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("export")) { exprtMode = string2mode(negated, cmd); parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("update-source")) { if (negated) { iface = null; parent.needFull.add(1); parent.compute.wakeup(); return; } cfgIfc res = cfgAll.ifcFind(cmd.word(), 0); if (res == null) { cmd.error("no such interface"); return; } if (res.vrfFor != parent.vrfCore) { cmd.error("in other vrf"); return; } iface = res; parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("srv6")) { if (negated) { srv6 = null; } else { srv6 = cfgAll.ifcFind(cmd.word(), 0); } parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("set-vrf")) { if (negated) { setVrfF = null; setVrfT = false; parent.needFull.add(1); parent.compute.wakeup(); return; } cfgVrf vrf = cfgAll.vrfFind(cmd.word(), false); if (vrf == null) { cmd.error("no such vrf"); return; } s = cmd.word(); if (s.equals("ipv4")) { setVrfF = vrf.fwd4; setVrfT = true; } else { setVrfF = vrf.fwd6; setVrfT = false; } parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("default-originate")) { defRou = !negated; parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("flowspec-install")) { flowInst = !negated; if (negated) { fwd.flowspec = null; } parent.needFull.add(1); parent.compute.wakeup(); return; } if (s.equals("flowspec-advert")) { if (negated) { flowSpec = null; parent.needFull.add(1); parent.compute.wakeup(); return; } cfgPlymp ntry = cfgAll.plmpFind(cmd.word(), false); if (ntry == null) { cmd.error("no such policy map"); return; } flowSpec = ntry.plcmap; parent.needFull.add(1); parent.compute.wakeup(); return; } if (cfgRtr.doCfgRedist(this, fwd, negated, s, cmd)) { cmd.badCmd(); } parent.needFull.add(1); parent.compute.wakeup(); } /** * get neighbor count * * @return count */ public int routerNeighCount() { return 0; } /** * get neighbor list * * @param tab list */ public void routerNeighList(tabRoute tab) { } /** * get interface count * * @return count */ public int routerIfaceCount() { return 0; } /** * maximum recursion depth * * @return allowed number */ public int routerRecursions() { return 1; } /** * get list of link states * * @param tab table to update * @param par parameter * @param asn asn * @param adv advertiser */ public void routerLinkStates(tabRoute tab, int par, int asn, addrIPv4 adv) { } /** * get peer list * * @param tab list to append */ public void getPeerList(tabRoute tab) { for (int i = 0; i < peers.size(); i++) { addrIP adr = peers.get(i); if (adr == null) { continue; } tabRouteEntry ntry = new tabRouteEntry(); ntry.prefix = new addrPrefix(adr, addrIP.size * 8); tabRoute.addUpdatedEntry(tabRoute.addType.better, tab, parent.afiUni, 0, ntry, true, null, null, parent.routerAutoMesh); } } }