package org.freertr.rtr; import java.util.Timer; import java.util.TimerTask; import org.freertr.addr.addrIP; import org.freertr.addr.addrPrefix; import org.freertr.cfg.cfgAll; import org.freertr.cfg.cfgVrf; import org.freertr.ip.ipFwd; import org.freertr.ip.ipFwdBier; import org.freertr.ip.ipFwdIface; import org.freertr.ip.ipFwdMcast; import org.freertr.ip.ipPrt; import org.freertr.pack.packHolder; import org.freertr.pack.packPim; import org.freertr.pack.packPimGrp; import org.freertr.tab.tabGen; import org.freertr.tab.tabRouteEntry; import org.freertr.user.userFormat; import org.freertr.util.bits; import org.freertr.util.counter; import org.freertr.util.debugger; import org.freertr.util.logger; import org.freertr.util.state; /** * protocol independent multicast (rfc4601) interface * * @author matecsaba */ public class rtrPimIface implements ipPrt { /** * hello interval */ public int helloInterval = 30000; /** * dr priority */ public int drPriority = 1; /** * time to wait between packets */ public int interPackTime = 20; /** * bier id, 0 to disable tunneling */ public int bierTunnel = 0; /** * source of join messages */ public ipFwdIface joinSource = null; /** * allow receive of routes */ public boolean allowRx = true; /** * allow transmit of routes */ public boolean allowTx = true; /** * bfd enabled */ public boolean bfdTrigger; /** * extra groups */ public tabGen extra = new tabGen(); private int generationId; private ipFwd fwdCore; private ipFwdIface iface; private counter cntr = new counter(); private tabGen neighs = new tabGen(); private Timer keepTimer; /** * create new instance * * @param fwd forwarder * @param ifc interface */ public rtrPimIface(ipFwd fwd, ipFwdIface ifc) { fwdCore = fwd; iface = ifc; generationId = bits.randomD(); } /** * setup timer thread * * @param shutdown set true to shut down */ public void restartTimer(boolean shutdown) { try { keepTimer.cancel(); } catch (Exception e) { } keepTimer = null; if (shutdown) { return; } if (helloInterval < 1) { return; } keepTimer = new Timer(); rtrPimIfaceHello task = new rtrPimIfaceHello(this); keepTimer.schedule(task, 500, helloInterval); } /** * unregister from ip */ public void unregister2ip() { if (debugger.rtrPimEvnt) { logger.debug("unregister " + iface); } fwdCore.protoDel(this, iface, null); restartTimer(true); } /** * register to ip */ public void register2ip() { if (debugger.rtrPimEvnt) { logger.debug("register " + iface); } fwdCore.protoAdd(this, iface, null); restartTimer(false); } public String toString() { return "pim"; } /** * get protocol number * * @return number */ public int getProtoNum() { return packPim.proto; } /** * close interface * * @param iface interface */ public void closeUp(ipFwdIface iface) { } /** * get counter * * @return counter */ public counter getCounter() { return cntr; } /** * received packet * * @param rxIfc interface * @param pckBin packet */ public void recvPack(ipFwdIface rxIfc, packHolder pckBin) { if (!allowRx) { return; } cntr.rx(pckBin); packPim pckPim = new packPim(); if (pckPim.parseHeader(pckBin)) { cntr.drop(pckBin, counter.reasons.badHdr); return; } if (pckPim.parsePayload(pckBin)) { cntr.drop(pckBin, counter.reasons.badCod); return; } if (debugger.rtrPimTraf) { logger.debug("rx " + pckPim + " from " + pckBin.IPsrc); } rtrPimNeigh nei = new rtrPimNeigh(this, pckBin.IPsrc); switch (pckPim.typ) { case packPim.typJoin: if (pckPim.upstream.compareTo(iface.addr) != 0) { break; } int tim = pckPim.valHoldTime * 1000; for (int o = 0; o < pckPim.groups.size(); o++) { packPimGrp grp = pckPim.groups.get(o); if (!grp.group.wildcard.isFilled(0)) { continue; } int lab = 0; ipFwd fwd = fwdCore; if (grp.rd != 0) { cfgVrf vrf = cfgAll.findRd(grp.group.network.isIPv4(), grp.rd); if (vrf == null) { continue; } if (bierTunnel < 1) { continue; } fwd = vrf.getFwd(grp.group.network); lab = fwd.commonLabel.label; } for (int i = 0; i < grp.joins.size(); i++) { addrPrefix src = grp.joins.get(i); if (!src.wildcard.isFilled(0)) { continue; } if (bierTunnel > 0) { fwd.mcastAddFloodBier(grp.group.network, src.network, lab, fwdCore, pckBin.IPsrc, bierTunnel, tim); } else { fwd.mcastAddFloodIfc(grp.group.network, src.network, iface, tim); } } for (int i = 0; i < grp.prunes.size(); i++) { addrPrefix src = grp.prunes.get(i); if (!src.wildcard.isFilled(0)) { continue; } if (bierTunnel > 0) { fwd.mcastDelFloodBier(grp.group.network, src.network, lab, fwdCore, pckBin.IPsrc); } else { fwd.mcastDelFloodIfc(grp.group.network, src.network, iface); } } } break; case packPim.typHello: rtrPimNeigh old = neighs.add(nei); if (old != null) { nei = old; } else { nei.upTime = bits.getTime(); logger.warn("neighbor " + nei.peer + " up"); if (bfdTrigger) { iface.bfdAdd(pckBin.IPsrc, nei, "pim"); } } nei.last = bits.getTime(); nei.hold = pckPim.valHoldTime * 1000; nei.pri = pckPim.valDrPri; break; } } /** * alert packet * * @param rxIfc interface * @param pck packet * @return false if success, true if error */ public boolean alertPack(ipFwdIface rxIfc, packHolder pck) { return true; } /** * error packet * * @param err error code * @param rtr address * @param rxIfc interface * @param pck packet */ public void errorPack(counter.reasons err, addrIP rtr, ipFwdIface rxIfc, packHolder pck) { } /** * purge neighbors */ public synchronized void purgeNeighs() { long tim = bits.getTime(); for (int i = neighs.size(); i >= 0; i--) { rtrPimNeigh nei = neighs.get(i); if (nei == null) { continue; } if ((nei.last + nei.hold) > tim) { continue; } neighs.del(nei); logger.error("neighbor " + nei.peer + " down"); iface.bfdDel(nei.peer, nei); } } /** * send hello packet */ public void sendHello() { if (bierTunnel > 0) { return; } if (!allowTx) { return; } packHolder pckBin = new packHolder(true, true); packPim pckPim = new packPim(); pckPim.fillHello(helloInterval, drPriority, generationId, iface.addr); pckPim.createHello(pckBin); if (debugger.rtrPimTraf) { logger.debug("tx " + pckPim); } pckPim.createHeader(pckBin, iface, null); fwdCore.protoPack(iface, null, pckBin); } /** * send one join * * @param grp group to join * @param ups upstream address * @param need 1=join, 0=prune */ public void sendJoin(ipFwdMcast grp, addrIP ups, int need) { if (!allowTx) { return; } if (bierTunnel > 0) { if (ups == null) { ups = grp.source; } tabRouteEntry rou = fwdCore.actualM.route(ups); if (rou == null) { return; } if (rou.best.oldHop != null) { ups = rou.best.oldHop; } } else { if (ups == null) { ups = grp.upstream; } } if (ups == null) { return; } packHolder pckBin = new packHolder(true, true); packPim pckPim = new packPim(); pckPim.fillJoin(ups, grp.rd, grp.group, grp.source, helloInterval * need); pckPim.createJoin(pckBin); if (debugger.rtrPimTraf) { logger.debug("tx " + pckPim + " on " + iface); } if (bierTunnel < 1) { ups = null; } if (joinSource != null) { pckPim.createHeader(pckBin, joinSource, ups); } else { pckPim.createHeader(pckBin, iface, ups); } if (bierTunnel < 1) { fwdCore.protoPack(iface, null, pckBin); return; } if (ups == null) { return; } fwdCore.createIPheader(pckBin); pckBin.ETHtype = iface.lower.getEthtyp(); ipFwdBier clnt = new ipFwdBier(bierTunnel); clnt.addPeer(fwdCore, ups, 0, -1); clnt.updatePeers(); clnt.sendPack(pckBin); } /** * send join messages */ public void sendJoins() { for (int i = 0; i < fwdCore.groups.size(); i++) { ipFwdMcast grp = fwdCore.groups.get(i); if (grp.iface == null) { continue; } if (grp.iface.ifwNum != iface.ifwNum) { continue; } bits.sleep(interPackTime); sendJoin(grp, null, 1); } for (int i = 0; i < extra.size(); i++) { ipFwdMcast grp = extra.get(i); bits.sleep(interPackTime); sendJoin(grp, grp.upstream, 1); } } /** * list neighbors * * @param l list to append */ public void getShNeighs(userFormat l) { for (int i = 0; i < neighs.size(); i++) { rtrPimNeigh nei = neighs.get(i); if (nei == null) { continue; } l.add(iface + "|" + nei.getShNeigh()); } } /** * get number of neighbors * * @return number of neighbors */ public int neighCount() { return neighs.size(); } /** * set state * * @param iface interface * @param stat state */ public void setState(ipFwdIface iface, state.states stat) { } } class rtrPimNeigh implements Comparable, rtrBfdClnt { /** * parent */ public final rtrPimIface lower; /** * address of peer */ public final addrIP peer; /** * last hello */ public long last; /** * hold time */ public int hold; /** * dr priority */ public int pri; /** * uptime */ public long upTime; /** * create instance * * @param parent parent * @param adr address */ public rtrPimNeigh(rtrPimIface parent, addrIP adr) { lower = parent; peer = adr.copyBytes(); } public int compareTo(rtrPimNeigh o) { return peer.compareTo(o.peer); } /** * get neighbors line * * @return string */ public String getShNeigh() { return peer + "|" + pri + "|" + bits.timePast(upTime); } public void bfdPeerDown() { last = 0; lower.purgeNeighs(); } } class rtrPimIfaceHello extends TimerTask { private final rtrPimIface lower; public rtrPimIfaceHello(rtrPimIface parent) { lower = parent; } public void run() { try { lower.purgeNeighs(); lower.sendHello(); lower.sendJoins(); } catch (Exception e) { logger.traceback(e); } } }