package org.freertr.ip; import java.util.List; import org.freertr.addr.addrIP; import org.freertr.addr.addrIPv4; import org.freertr.addr.addrIPv6; import org.freertr.pack.packHolder; import org.freertr.pack.packLdp; import org.freertr.pack.packLdpMp; import org.freertr.rtr.rtrLdpNeigh; import org.freertr.tab.tabGen; import org.freertr.tab.tabLabel; import org.freertr.tab.tabLabelEntry; import org.freertr.tab.tabRouteEntry; import org.freertr.util.bits; import org.freertr.util.counter; /** * stores one multipoint lsp * * @author matecsaba */ public class ipFwdMpmp implements Comparable { /** * multipoint to multipoint */ public boolean mp2mp; /** * local endpoint */ public boolean local; /** * root node address */ public addrIP root; /** * uplink neighbor */ public addrIP uplnk; /** * opaque value */ public byte[] opaque; /** * self root */ public boolean selfRoot; /** * vrf of uplink */ public ipFwd vrfUpl; /** * receiver vrf */ public ipFwd vrfRx; /** * neighbors */ public final tabGen neighs = new tabGen(); /** * counter */ public counter cntr = new counter(); /** * create new instance * * @param mp true=multipoint2multipoint, false=point2multipoint * @param rot root address * @param opq opaque value */ public ipFwdMpmp(boolean mp, addrIP rot, byte[] opq) { mp2mp = mp; root = rot.copyBytes(); opaque = new byte[opq.length]; bits.byteCopy(opq, 0, opaque, 0, opaque.length); } /** * copy bytes * * @return copy of record */ public ipFwdMpmp copyBytes() { ipFwdMpmp n = new ipFwdMpmp(mp2mp, root, opaque); n.local = local; n.selfRoot = selfRoot; n.vrfUpl = vrfUpl; n.vrfRx = vrfRx; if (uplnk != null) { n.uplnk.copyBytes(); } for (int i = 0; i < neighs.size(); i++) { ipFwdMpNe ntry = neighs.get(i); if (ntry == null) { continue; } n.neighs.add(ntry.copyBytes()); } return n; } /** * compare this entry * * @param o other * @return false if equals, true if differs */ public boolean differs(ipFwdMpmp o) { if (o == null) { return true; } if (mp2mp != o.mp2mp) { return true; } if (local != o.local) { return true; } if (selfRoot != o.selfRoot) { return true; } if (root.compareTo(o.root) != 0) { return true; } if (opaque.length != o.opaque.length) { return true; } if (bits.byteComp(opaque, 0, o.opaque, 0, opaque.length) != 0) { return true; } if (uplnk == null) { if (o.uplnk != null) { return true; } } else { if (o.uplnk == null) { return true; } if (uplnk.compareTo(o.uplnk) != 0) { return true; } } if (neighs.size() != o.neighs.size()) { return true; } for (int i = 0; i < neighs.size(); i++) { if (neighs.get(i).differs(o.neighs.get(i))) { return true; } } return false; } public String toString() { return mp2mp + " " + root + " " + bits.byteDump(opaque, 0, -1); } /** * create for tunnel * * @param mp true=multipoint2multipoint, false=point2multipoint * @param rot root address * @param id tunnel id * @return instance */ public static ipFwdMpmp create4tunnel(boolean mp, addrIP rot, int id) { byte[] op = new byte[7]; op[0] = 1; // generic lsp id bits.msbPutW(op, 1, 4); bits.msbPutD(op, 3, id); return new ipFwdMpmp(mp, rot, op); } /** * create for multicast group * * @param mp true=multipoint2multipoint, false=point2multipoint * @param rot root address * @param mcst multicast group * @return instance */ public static ipFwdMpmp create4multicast(boolean mp, addrIP rot, ipFwdMcast mcst) { byte[] op; if (mcst.group.isIPv4()) { op = new byte[11]; op[0] = 3; // ipv4 transit bits.msbPutW(op, 1, 8); mcst.source.toIPv4().toBuffer(op, 3); mcst.group.toIPv4().toBuffer(op, 7); } else { op = new byte[35]; op[0] = 4; // ipv6 transit bits.msbPutW(op, 1, 32); mcst.source.toIPv6().toBuffer(op, 3); mcst.group.toIPv6().toBuffer(op, 19); } return new ipFwdMpmp(mp, rot, op); } /** * create for multicast vpn group * * @param mp true=multipoint2multipoint, false=point2multipoint * @param rot root address * @param rd route distinguisher * @param mcst multicast group * @return instance */ public static ipFwdMpmp create4vpnMcast(boolean mp, addrIP rot, long rd, ipFwdMcast mcst) { byte[] op; if (mcst.group.isIPv4()) { op = new byte[19]; op[0] = (byte) 250; // vpnv4 transit bits.msbPutW(op, 1, 16); mcst.source.toIPv4().toBuffer(op, 3); mcst.group.toIPv4().toBuffer(op, 7); bits.msbPutQ(op, 11, rd); } else { op = new byte[43]; op[0] = (byte) 251; // vpnv6 transit bits.msbPutW(op, 1, 40); mcst.source.toIPv6().toBuffer(op, 3); mcst.group.toIPv6().toBuffer(op, 19); bits.msbPutQ(op, 35, rd); } return new ipFwdMpmp(mp, rot, op); } /** * create for recursive * * @param mp true=multipoint2multipoint, false=point2multipoint * @param rot root address * @param orig original lsp * @return instance */ public static ipFwdMpmp create4recursive(boolean mp, addrIP rot, packLdpMp orig) { packHolder pck = new packHolder(true, true); orig.createFEC(pck); pck.merge2end(); pck.putByte(0, 7); // recursive pck.msbPutW(1, pck.dataSize()); // size pck.putSkip(3); pck.merge2beg(); return new ipFwdMpmp(mp, rot, pck.getCopy()); } /** * create for vpn recursive * * @param mp true=multipoint2multipoint, false=point2multipoint * @param rot root address * @param rd route distinguisher * @param orig original lsp * @return instance */ public static ipFwdMpmp create4vpnRecursive(boolean mp, addrIP rot, long rd, packLdpMp orig) { packHolder pck = new packHolder(true, true); orig.createFEC(pck); pck.merge2end(); pck.putByte(0, 8); // vpn recursive pck.msbPutW(1, pck.dataSize() + 8); // size pck.msbPutQ(3, rd); pck.putSkip(11); pck.merge2beg(); return new ipFwdMpmp(mp, rot, pck.getCopy()); } /** * decode for multicast group * * @param ntry entry to decode * @return multicast group, null if error happened */ public static ipFwdMcast decode4multicast(ipFwdMpmp ntry) { if (ntry.mp2mp) { return null; } if (ntry.opaque.length < 3) { return null; } int siz = bits.msbGetW(ntry.opaque, 1); // size if (siz > (ntry.opaque.length - 3)) { return null; } addrIP src = new addrIP(); addrIP grp = new addrIP(); long rd = 0; switch (ntry.opaque[0] & 0xff) { case 3: // ipv4 transit if (siz < 8) { return null; } addrIPv4 a4 = new addrIPv4(); a4.fromBuf(ntry.opaque, 3); src.fromIPv4addr(a4); a4.fromBuf(ntry.opaque, 7); grp.fromIPv4addr(a4); break; case 4: // ipv6 transit if (siz < 32) { return null; } addrIPv6 a6 = new addrIPv6(); a6.fromBuf(ntry.opaque, 3); src.fromIPv6addr(a6); a6.fromBuf(ntry.opaque, 19); grp.fromIPv6addr(a6); break; case 250: // vpnv4 transit if (siz < 16) { return null; } a4 = new addrIPv4(); a4.fromBuf(ntry.opaque, 3); src.fromIPv4addr(a4); a4.fromBuf(ntry.opaque, 7); grp.fromIPv4addr(a4); rd = bits.msbGetQ(ntry.opaque, 11); break; case 251: // vpnv6 transit if (siz < 40) { return null; } a6 = new addrIPv6(); a6.fromBuf(ntry.opaque, 3); src.fromIPv6addr(a6); a6.fromBuf(ntry.opaque, 19); grp.fromIPv6addr(a6); rd = bits.msbGetQ(ntry.opaque, 35); break; default: return null; } ipFwdMcast d = new ipFwdMcast(grp, src); d.rd = rd; return d; } /** * list peers * * @return list */ public String listPeers() { String a = ""; if (local) { a = " local"; } for (int i = 0; i < neighs.size(); i++) { ipFwdMpNe ntry = neighs.get(i); a += " " + ntry.labelL + "/" + ntry.addr + "/" + ntry.labelR; } return a.trim(); } /** * dump this entry * * @param peer just one peer * @return string */ public String dump(addrIP peer) { String a = ""; String b = ""; if (peer == null) { a = "|" + listPeers(); } else { b = "|n/a"; ipFwdMpNe ntry = new ipFwdMpNe(peer); ntry = neighs.find(ntry); if (ntry != null) { b = "|" + ntry.labelR; } } return (mp2mp ? "mp2mp" : "p2mp") + "|" + selfRoot + " " + local + "|" + root + b + "|" + bits.byteDump(opaque, 0, -1) + "|" + uplnk + a; } public int compareTo(ipFwdMpmp o) { if (mp2mp != o.mp2mp) { if (mp2mp) { return -1; } else { return +1; } } int i = root.compareTo(o.root); if (i != 0) { return i; } if (opaque.length < o.opaque.length) { return -1; } if (opaque.length > o.opaque.length) { return +1; } return bits.byteComp(opaque, 0, o.opaque, 0, opaque.length); } /** * stop labels */ public void stopLabels() { for (int i = 0; i < neighs.size(); i++) { ipFwdMpNe ntry = neighs.get(i); if (ntry == null) { continue; } if (ntry.labelL == null) { continue; } tabLabel.release(ntry.labelL, tabLabelEntry.owner.mp2mp); } } /** * add one peer * * @param adr address * @param ifc interface * @param label remote label * @param alloc allocate local label */ public void addPeer(addrIP adr, ipFwdIface ifc, int label, boolean alloc) { ipFwdMpNe ntry = new ipFwdMpNe(adr); ntry.iface = ifc; ipFwdMpNe old = neighs.add(ntry); if (old != null) { ntry = old; } if (alloc && (ntry.labelL == null)) { ntry.labelL = tabLabel.allocate(tabLabelEntry.owner.mp2mp); ntry.labelL.clrDupMpls(tabLabelEntry.owner.mp2mp); } ntry.labelR = label; } /** * delete one peer * * @param adr address * @return true if nothing done */ public boolean delPeer(addrIP adr) { ipFwdMpNe ntry = new ipFwdMpNe(adr); ntry = neighs.del(ntry); if (ntry == null) { return true; } if (uplnk != null) { if (adr.compareTo(uplnk) == 0) { uplnk = null; } } for (int i = 0; i < neighs.size(); i++) { ipFwdMpNe curr = neighs.get(i); if (curr == null) { continue; } if (curr.labelL == null) { continue; } curr.labelL.delDupMpls(tabLabelEntry.owner.mp2mp, ntry.addr); } if (ntry.labelL == null) { return false; } tabLabel.release(ntry.labelL, tabLabelEntry.owner.mp2mp); return false; } private int getTyp() { if (mp2mp) { return packLdp.fecTmp2mpDn; } else { return packLdp.fecTp2mp; } } /** * get fec * * @return fec */ public packLdpMp getFec() { packLdpMp res = new packLdpMp(); res.typ = getTyp(); res.root = root.copyBytes(); res.opaque = new byte[opaque.length]; bits.byteCopy(opaque, 0, res.opaque, 0, opaque.length); return res; } /** * get reverse fec * * @param adr address to use * @param typ type to reverse * @return reverse fec */ public packLdpMp getReverse(addrIP adr, int typ) { ipFwdMpNe ntry = new ipFwdMpNe(adr); ntry = neighs.find(ntry); if (ntry == null) { return null; } if (ntry.labelL == null) { return null; } packLdpMp res = getFec(); res.typ = packLdpMp.getReverse(typ); res.label = ntry.labelL.label; return res; } private addrIP getRootNeigh(ipFwd fwd) { tabRouteEntry rou = fwd.actualU.route(root); if (rou == null) { return null; } if (rou.best.iface == null) { return null; } ipFwdIface ifc = (ipFwdIface) rou.best.iface; selfRoot = false; switch (rou.best.rouTyp) { case local: selfRoot = true; return null; case conn: if (ifc.lower.checkMyAddress(root)) { selfRoot = true; return null; } return root; case remote: case defpref: case automesh: return root; default: return rou.best.nextHop; } } private void doDel(ipFwd fwd, addrIP adr) { rtrLdpNeigh nei = fwd.ldpNeighFind(null, adr, false); delPeer(adr); if (nei == null) { return; } packLdpMp fec = getFec(); nei.sendLabelWdrw(fec); nei.pmpAdvert.del(fec); } private void doRootNeigh(ipFwd fwd) { addrIP adr = getRootNeigh(fwd); if (adr == null) { if (uplnk == null) { return; } doDel(fwd, uplnk); uplnk = null; return; } if (uplnk != null) { if (uplnk.compareTo(adr) == 0) { return; } doDel(fwd, uplnk); uplnk = null; } rtrLdpNeigh nei = fwd.ldpNeighFind(null, adr, false); if (nei == null) { return; } if (nei.conn == null) { return; } addPeer(adr, nei.ifc, -1, true); packLdpMp fec = getReverse(adr, 0); if (fec == null) { return; } uplnk = adr; fec.typ = getTyp(); nei.pmpAdvert.add(fec); nei.sendLabelMap(fec); } /** * update forwarding state * * @param fwd forwarder */ public void updateState(ipFwd fwd) { for (int i = neighs.size() - 1; i >= 0; i--) { ipFwdMpNe ntry = neighs.get(i); if (ntry == null) { continue; } if (fwd.ldpNeighFind(null, ntry.addr, false) != null) { continue; } delPeer(ntry.addr); } if (local) { doRootNeigh(fwd); } else { if (neighs.size() < 1) { return; } doRootNeigh(fwd); int i = neighs.size(); if (uplnk != null) { i--; } if (selfRoot) { i++; } if (i < 1) { if (uplnk == null) { return; } doDel(fwd, uplnk); uplnk = null; return; } } boolean nedLoc = false; if (mp2mp) { nedLoc = local; } else { nedLoc = local && (!selfRoot); } boolean ned = false; for (int o = 0; o < neighs.size(); o++) { ipFwdMpNe curr = neighs.get(o); if (curr == null) { continue; } if (curr.labelL == null) { continue; } if (nedLoc) { if (vrfRx == null) { curr.labelL.setFwdCommon(tabLabelEntry.owner.mp2mp, fwd); } else { curr.labelL.setFwdCommon(tabLabelEntry.owner.mp2mp, vrfRx); } ned = true; } for (int i = 0; i < neighs.size(); i++) { if (i == o) { continue; } ipFwdMpNe ntry = neighs.get(i); if (ntry == null) { continue; } if (ntry.labelR < 0) { continue; } List labs = tabLabel.int2labels(ntry.labelR); curr.labelL.addDupMpls(tabLabelEntry.owner.mp2mp, fwd, ntry.iface, ntry.addr, labs); ned = true; } if (ned) { continue; } curr.labelL.clrDupMpls(tabLabelEntry.owner.mp2mp); } } /** * send local packet * * @param fwdCor forwarder * @param orig packet to send */ public void sendPack(ipFwd fwdCor, packHolder orig) { cntr.tx(orig); if ((!mp2mp) && (!selfRoot)) { return; } if (vrfUpl != null) { fwdCor = vrfUpl; } for (int i = 0; i < neighs.size(); i++) { ipFwdMpNe ntry = neighs.get(i); if (ntry == null) { continue; } if (ntry.labelR < 0) { continue; } packHolder pck = orig.copyBytes(true, true); pck.MPLSlabel = ntry.labelR; ipMpls.createMPLSheader(pck); fwdCor.mplsTxPack(ntry.addr, pck, false); } } }