package org.freertr.ip; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.freertr.addr.addrIP; import org.freertr.addr.addrIPv6; import org.freertr.addr.addrMac; import org.freertr.addr.addrPrefix; import org.freertr.ifc.ifcDn; import org.freertr.ifc.ifcNull; import org.freertr.ifc.ifcUp; import org.freertr.pack.packHolder; import org.freertr.tab.tabGen; import org.freertr.tab.tabRateLimit; 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; import org.freertr.enc.encTlv; /** * implements neighbor (rfc2461) cache * * @author matecsaba */ public class ipIfc6nei implements ifcUp { /** * neighbor cache dynamic */ public boolean neiCacheDynamic = true; /** * neighbor cache timeout */ public int neiCacheTimeout = ipIfcLoop.defaultCacheTime; /** * neighbor cache retry */ public int neiCacheRetry = ipIfcLoop.defaultRetryTime; /** * neighbor query rate */ public tabRateLimit neiQueryRate; private addrMac hwaddr = new addrMac(); private addrIPv6 ipaddr = new addrIPv6(); private addrIPv6 lladdr = new addrIPv6(); private addrPrefix network = new addrPrefix( new addrIPv6(), 0); private tabGen cache = new tabGen(); private ifcDn lower = new ifcNull(); private Timer timer; private ipIfc6 upper; private counter cntr = new counter(); private long currTim = bits.getTime(); private ipCor6 ipc = new ipCor6(); private ipIcmp6 icc = new ipIcmp6(); private encTlv tlv = ipIcmp6.getTLVreader(); /** * get counter * * @return counter */ public counter getCounter() { return cntr; } /** * create nei handler * * @param parent parent of me */ public ipIfc6nei(ipIfc6 parent) { upper = parent; } /** * set ip network * * @param addr address * @param mask mask */ public void setIPv6addr(addrIPv6 addr, int mask) { resetTimer(false); cache.clear(); lladdr = upper.getLinkLocalAddr().toIPv6(); ipaddr = addr.copyBytes(); network = new addrPrefix(addr, mask); resetTimer(true); } /** * set ll address * * @param adr address */ public void setLinkLocalAddr(addrIP adr) { lladdr = adr.toIPv6(); } private synchronized void resetTimer(boolean needRun) { try { timer.cancel(); } catch (Exception e) { } timer = null; if (!needRun) { return; } timer = new Timer(); ipIfc6neiTimer task = new ipIfc6neiTimer(this); timer.schedule(task, 500, 60000); } /** * set parent * * @param parent parent */ public void setParent(ifcDn parent) { lower = parent; hwaddr = (addrMac) lower.getHwAddr(); lladdr = upper.getLinkLocalAddr().toIPv6(); } /** * set state * * @param stat state */ public void setState(state.states stat) { cntr.stateChange(stat); resetTimer(state.toUsable(stat) == state.states.up); } /** * close interface */ public void closeUp() { resetTimer(false); } /** * is proxy nei enabled * * @return not enabled */ public boolean getProxyArp() { return false; } /** * got icmp packet * * @param pck packet parsed * @return false if replied successfully, true if error happened */ public boolean gotIcmpPack(packHolder pck) { if (debugger.ipIfc6neiTraf) { logger.debug("rx op=" + icc.icmp2string(pck.ICMPtc) + " " + pck.IPsrc + " -> " + pck.IPtrg); } int siz; int ned = -1; addrIPv6 adrI = pck.IPsrc.toIPv6(); switch (pck.ICMPtc) { case ipIcmp6.icmpRtrSol: siz = 8; ned = 1; // source address break; case ipIcmp6.icmpRtrAdv: siz = 16; ned = 1; // source address break; case ipIcmp6.icmpNeiSol: siz = 24; ned = 1; // source address break; case ipIcmp6.icmpNeiAdv: siz = 24; ned = 2; // target address pck.getAddr(adrI, 8); break; default: return true; } if (adrI.compareTo(ipaddr) == 0) { cntr.drop(pck, counter.reasons.badSrcAddr); logger.info("ipv6 address conflict at " + lower); return true; } if (adrI.compareTo(lladdr) == 0) { cntr.drop(pck, counter.reasons.badSrcAddr); logger.info("linklocal address conflict at " + lower); return true; } pck.getSkip(siz); for (;;) { if (tlv.getBytes(pck)) { break; } if ((tlv.valTyp != ned)) { continue; } if (tlv.valSiz < addrMac.size) { continue; } ipIfc6neiEntry ntry = new ipIfc6neiEntry(); addrMac adrM = new addrMac(); adrM.fromBuf(tlv.valDat, 0); ntry.ip = adrI.copyBytes(); ntry.mac = adrM; ntry.time = currTim; if (pck.ICMPtc == ipIcmp6.icmpRtrAdv) { ntry.router = true; } if (neiCacheDynamic) { addEntry(ntry); continue; } if (!adrI.isLinkLocal()) { continue; } addEntry(ntry); } return false; } private void addEntry(ipIfc6neiEntry ntry) { if (debugger.ipIfc6neiEvnt) { logger.debug("add " + ntry); } cache.put(ntry); upper.upper.tableChanger(); } private void delEntry(ipIfc6neiEntry ntry) { if (debugger.ipIfc6neiEvnt) { logger.debug("del " + ntry); } cache.del(ntry); upper.upper.tableChanger(); } /** * received packet * * @param pck packet */ public void recvPack(packHolder pck) { cntr.rx(pck); if (pck.msbGetW(0) != ipIfc6.type) {// ethertype return; } pck.getSkip(2); if (ipc.parseIPheader(pck, true)) { return; } if (pck.IPprt != ipIcmp6.protoNum) { return; } pck.getSkip(pck.IPsiz); if (icc.parseICMPheader(pck)) { return; } gotIcmpPack(pck); } private void putHeader(packHolder pck, addrMac adr) { pck.putStart(); pck.ETHtrg.setAddr(adr); pck.ETHsrc.setAddr(hwaddr); } /** * put dst,src mac address to packet * * @param pck packet to update * @param adr next hop ip address * @return true not found, false if updated successfully */ public boolean readMACheader(packHolder pck, addrIPv6 adr) { ipIfc6neiEntry ntry = new ipIfc6neiEntry(); ntry.ip = adr; ntry = cache.find(ntry); if (ntry != null) { ntry.time = currTim; putHeader(pck, ntry.mac); return false; } if (adr.isMulticast()) { if (upper.ifcHdr.mcastAsBcast) { putHeader(pck, addrMac.getBroadcast()); return false; } if (!upper.ifcHdr.mcastAsUcast) { putHeader(pck, adr.conv2multiMac()); return false; } ntry = cache.get(0); if (ntry == null) { putHeader(pck, adr.conv2multiMac()); return false; } putHeader(pck, ntry.mac); return false; } if (!adr.isLinkLocal()) { if (adr.compareTo(network.network) < 0) { putHeader(pck, addrMac.getBroadcast()); return false; } if (adr.compareTo(network.broadcast) > 0) { putHeader(pck, addrMac.getBroadcast()); return false; } if (!network.matches(adr)) { cntr.drop(pck, counter.reasons.badTrgAddr); return true; } } cntr.drop(pck, counter.reasons.notInTab); if (neiQueryRate != null) { if (neiQueryRate.check(1)) { return true; } } icc.createNeighSol(hwaddr, pck, adr, lladdr); pck.msbPutW(0, ipIfc6.type); // ethertype pck.putSkip(2); pck.merge2beg(); if (upper.ifcHdr.mcastAsBcast) { putHeader(pck, addrMac.getBroadcast()); } else { putHeader(pck, pck.IPtrg.toIPv6().conv2multiMac()); } lower.sendPack(pck); return true; } /** * do neighbor cache cleanup round */ public void doCachePurge() { if (debugger.ipIfc6neiEvnt) { logger.debug("purge"); } currTim = bits.getTime(); ipIfc6neiEntry ntry = new ipIfc6neiEntry(); packHolder pck = new packHolder(true, true); for (int i = cache.size() - 1; i >= 0; i--) { ntry = cache.get(i); if (ntry == null) { continue; } if (ntry.stat) { continue; } if ((currTim - ntry.time) > neiCacheRetry) { icc.createNeighSol(hwaddr, pck, ntry.ip, lladdr); pck.msbPutW(0, ipIfc6.type); // ethertype pck.putSkip(2); pck.merge2beg(); putHeader(pck, ntry.mac); lower.sendPack(pck); } if ((currTim - ntry.time) < neiCacheTimeout) { continue; } delEntry(ntry); } } /** * get mac address * * @param adr ip address * @return mac address, null if not found */ public addrMac getMACaddr(addrIPv6 adr) { ipIfc6neiEntry ntry = new ipIfc6neiEntry(); ntry.ip = adr; ntry = cache.find(ntry); if (ntry == null) { return null; } return ntry.mac; } /** * get mac and ip address * * @param seq sequence to read * @param adr ip address * @param mac mac address * @return false on success, true on error */ public boolean getMACaddr(int seq, addrIP adr, addrMac mac) { ipIfc6neiEntry ntry = cache.get(seq); if (ntry == null) { return true; } adr.fromIPv6addr(ntry.ip); mac.setAddr(ntry.mac); return false; } /** * get static mac and ip addresses * * @param lst list to append * @param beg beginning */ public void getMACaddr(List lst, String beg) { for (int i = 0; i < cache.size(); i++) { ipIfc6neiEntry ntry = cache.get(i); if (ntry == null) { continue; } if (!ntry.stat) { continue; } lst.add(beg + ntry.ip + " " + ntry.mac); } } /** * update packet header * * @param mod mode * @param mac address to set * @param adr address to set */ public void updateMACheader(int mod, addrMac mac, addrIPv6 adr) { ipIfc6neiEntry ntry = new ipIfc6neiEntry(); ntry.ip = adr.copyBytes(); ntry.mac = mac.copyBytes(); ntry.time = currTim; switch (mod) { case 0: addEntry(ntry); break; case 1: ntry.stat = true; addEntry(ntry); break; case 2: ntry.stat = true; delEntry(ntry); break; } } /** * get list of neighbors * * @return list of entries */ public userFormat getShCache() { userFormat lst = new userFormat("|", "mac|address|time|static|router"); for (int i = 0; i < cache.size(); i++) { ipIfc6neiEntry ntry = cache.get(i); if (ntry == null) { continue; } lst.add(ntry.mac + "|" + ntry.ip + "|" + bits.timePast(ntry.time) + "|" + ntry.stat + "|" + ntry.router); } return lst; } /** * get local mac address * * @return mac address */ public addrMac getLocalMac() { return hwaddr.copyBytes(); } } class ipIfc6neiTimer extends TimerTask { ipIfc6nei parent; public ipIfc6neiTimer(ipIfc6nei prnt) { parent = prnt; } public void run() { try { parent.doCachePurge(); } catch (Exception e) { logger.traceback(e); } } } class ipIfc6neiEntry implements Comparable { public addrMac mac; public addrIPv6 ip; public long time; public boolean stat; public boolean router; public String toString() { return mac + " " + ip + " " + bits.timePast(time); } public int compareTo(ipIfc6neiEntry o) { return ip.compareTo(o.ip); } }