forked from KNEMC/KLALB
KLALB V3.4
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
package org.kne.cloud.network.ntp;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.kne.cloud.clock.HighAccuracyClock;
|
||||
import org.kne.cloud.clock.NTPTimestamps;
|
||||
import org.kne.cloud.network.MultipurposeSocketAddress;
|
||||
import org.kne.io.KNEChannels;
|
||||
|
||||
public class NTPv4Protocol implements Closeable, AutoCloseable {
|
||||
private static final boolean showPacket = false;
|
||||
public static final int NTP_DEFAULT_PORT = 123;
|
||||
private static final long DEFAULT_POLL_INTERVAL = 2000000000L;
|
||||
private static final long REQUEST_TIMEOUT = 10000000000L;
|
||||
private static final long EPHEMERAL_TIMEOUT = 60000000000L;
|
||||
|
||||
public static class NTPPeer {
|
||||
private MultipurposeSocketAddress address;
|
||||
private boolean isEphemeral;// ephemeral
|
||||
private volatile long ephemeralUpdateTime = System.nanoTime();
|
||||
private volatile long pollInterval = DEFAULT_POLL_INTERVAL;
|
||||
private volatile long pollUpdateTime = System.nanoTime() - pollInterval;
|
||||
private volatile int requestMode;
|
||||
private volatile long peerPollInterval = pollInterval;
|
||||
|
||||
private boolean checkPollInterval() {
|
||||
long curr = System.nanoTime();
|
||||
if (curr - pollUpdateTime > Math.min(peerPollInterval, pollInterval)) {
|
||||
pollUpdateTime = curr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkEphemeralTimeout() {
|
||||
if (!isEphemeral) {
|
||||
return false;
|
||||
}
|
||||
long curr = System.nanoTime();
|
||||
return curr - ephemeralUpdateTime > EPHEMERAL_TIMEOUT;
|
||||
}
|
||||
|
||||
private void resetEphemeralTimeout() {
|
||||
ephemeralUpdateTime = System.nanoTime();
|
||||
}
|
||||
|
||||
public long getPollInterval() {
|
||||
return pollInterval;
|
||||
}
|
||||
|
||||
public void setPollInterval(long pollInterval) {
|
||||
this.pollInterval = pollInterval;
|
||||
}
|
||||
|
||||
public NTPPeer(MultipurposeSocketAddress address, int requestMode) {
|
||||
this(address, requestMode, false);
|
||||
}
|
||||
|
||||
protected NTPPeer(MultipurposeSocketAddress address, int requestMode, boolean isEphemeral) {
|
||||
super();
|
||||
checkRequestMode(requestMode);
|
||||
this.address = address;
|
||||
this.isEphemeral = isEphemeral;
|
||||
this.requestMode = requestMode;
|
||||
}
|
||||
|
||||
private void checkRequestMode(int requestMode2) {
|
||||
if (requestMode2 != NTPv4Packet.NTP_CLIENT && requestMode2 != NTPv4Packet.NTP_SYMMETRIC_ACTIVE) {
|
||||
throw new IllegalArgumentException(
|
||||
"request mode must be NTPv4Packet.NTP_CLIEN or NTPv4Packet.NTP_SYMMETRIC_ACTIVE");
|
||||
}
|
||||
}
|
||||
|
||||
public NTPPeer(String hostport, int requestMode) {
|
||||
this(new MultipurposeSocketAddress(hostport), requestMode);
|
||||
}
|
||||
|
||||
public MultipurposeSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
private boolean isEphemeral() {
|
||||
return isEphemeral;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NTPPeer [address=" + address + ", isEphemeral=" + isEphemeral + ", ephemeralUpdateTime="
|
||||
+ ephemeralUpdateTime + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
NTPPeer other = (NTPPeer) obj;
|
||||
return Objects.equals(address, other.address);
|
||||
}
|
||||
|
||||
private void setPeerPollInterval(long peerPollInterval) {
|
||||
this.peerPollInterval = peerPollInterval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CheckListItem {
|
||||
private NTPPeer peer;
|
||||
private BigInteger timeStamp;
|
||||
private long createTime = System.nanoTime();
|
||||
|
||||
private CheckListItem(BigInteger timeStamp, NTPPeer peer) {
|
||||
this.timeStamp = timeStamp;
|
||||
this.peer = peer;
|
||||
}
|
||||
|
||||
private boolean checkTimeout() {
|
||||
return System.nanoTime() - createTime > REQUEST_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
private Set<NTPPeer> peers = Collections.synchronizedSet(new HashSet<>());
|
||||
private Set<NTPPeer> ephemeralPeers = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private ReentrantLock checkLock = new ReentrantLock();
|
||||
private List<CheckListItem> checkList = new ArrayList<>();
|
||||
|
||||
private NTPContext context;
|
||||
private DatagramSocket dgs;
|
||||
private MultipurposeSocketAddress bind;
|
||||
|
||||
public NTPv4Protocol(NTPContext context) throws UnknownHostException, IOException {
|
||||
this(context, new MultipurposeSocketAddress("UDP", "::0", NTP_DEFAULT_PORT));
|
||||
}
|
||||
|
||||
public NTPv4Protocol(NTPContext context, MultipurposeSocketAddress bind) throws UnknownHostException, IOException {
|
||||
this.bind = bind;
|
||||
this.context = context;
|
||||
dgs = bind.listenDatagramSocket();
|
||||
context.registerIO(this);
|
||||
Thread tr = new Thread(recv);
|
||||
tr.setName("NTPv4 Receive Thread");
|
||||
tr.start();
|
||||
}
|
||||
|
||||
private void setNTPInformation(NTPv4Packet nv4, NTPPeer peer) {
|
||||
if (peer != null) {
|
||||
long interval = peer.getPollInterval();
|
||||
int pi = (int) log(interval / 1000000000L, 2);
|
||||
nv4.setPollInterval(pi);
|
||||
} else {
|
||||
long interval = DEFAULT_POLL_INTERVAL;
|
||||
int pi = (int) log(interval / 1000000000L, 2);
|
||||
nv4.setPollInterval(pi);
|
||||
}
|
||||
nv4.setVersionNumber(NTPv4Packet.NTP_VERSION_NUMBER);
|
||||
nv4.setLeapIndicator(context.getLeapIndicator());
|
||||
int stratum = context.getStratum();
|
||||
if (stratum == 16) {
|
||||
stratum = 0;
|
||||
}
|
||||
nv4.setStratum(stratum);
|
||||
BigInteger precision = context.getLocalPrecision();
|
||||
byte b = (byte) Math.ceil(log(precision.doubleValue() / (Math.pow(2, 64)), 2));
|
||||
nv4.setPrecision(b);
|
||||
nv4.setRootDelay((int) context.getRootDelay());
|
||||
nv4.setRootDispersion((int) context.getCurrentRootDispersion());
|
||||
nv4.setReferenceTimestamp64(context.getReferenceTimestamp64());
|
||||
nv4.setTransmitTimestamp64(context.getClock().getCurrentTimeNTP64());
|
||||
}
|
||||
|
||||
private void setNTPInformation(NTPv4Packet nv4, NTPv4Packet request, NTPPeer peer) {
|
||||
nv4.setOriginateTimestamp(request.getTransmitTimestamp());
|
||||
nv4.setReceiveTimestamp64(NTPTimestamps.ntp128To64(request.getDestinationTimestamp128()));
|
||||
setNTPInformation(nv4, peer);
|
||||
}
|
||||
|
||||
private static double log(double value, double base) {
|
||||
|
||||
return Math.log(value) / Math.log(base);
|
||||
|
||||
}
|
||||
|
||||
public void request(NTPPeer peer) throws IOException {
|
||||
NTPv4Packet nv4 = new NTPv4Packet(context.getClock());
|
||||
nv4.setMode(peer.requestMode);
|
||||
setNTPInformation(nv4, peer);
|
||||
putOriginTimestamp(nv4, peer);
|
||||
peer.address.getInetAddress();
|
||||
sendNTPPacket(nv4, peer.address.getSocketAddress());
|
||||
}
|
||||
|
||||
private void putOriginTimestamp(NTPv4Packet nv4, NTPPeer peer) {
|
||||
checkLock.lock();
|
||||
try {
|
||||
removeTimeoutTimestamp();
|
||||
checkList.add(new CheckListItem(nv4.getTransmitTimestamp128(), peer));
|
||||
} finally {
|
||||
checkLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTimeoutTimestamp() {
|
||||
for (Iterator<CheckListItem> iterator = checkList.iterator(); iterator.hasNext();) {
|
||||
CheckListItem bigInteger = (CheckListItem) iterator.next();
|
||||
if (bigInteger.checkTimeout()) {
|
||||
if (showPacket)
|
||||
System.out.println("timeout:" + bigInteger.timeStamp);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNTPPacket(NTPv4Packet nv4, SocketAddress target) throws IOException {
|
||||
ByteBuffer bbf = ByteBuffer.allocate(65535);
|
||||
nv4.writeToChannel(KNEChannels.newWritableChannel(bbf));
|
||||
bbf.flip();
|
||||
DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit());
|
||||
dp.setSocketAddress(target);
|
||||
dgs.send(dp);
|
||||
if (showPacket)
|
||||
System.out.println("NTPv4 TX:" + target + " " + nv4);
|
||||
}
|
||||
|
||||
private NTPPeer checkOriginTimestamp(NTPv4Packet nv4) {
|
||||
checkLock.lock();
|
||||
try {
|
||||
BigInteger ori = nv4.getOriginateTimestamp128();
|
||||
removeTimeoutTimestamp();
|
||||
for (Iterator<CheckListItem> iterator = checkList.iterator(); iterator.hasNext();) {
|
||||
CheckListItem item = (CheckListItem) iterator.next();
|
||||
if (item.timeStamp.equals(ori)) {
|
||||
iterator.remove();
|
||||
return item.peer;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
checkLock.unlock();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
NTPPeer findPeer(MultipurposeSocketAddress addr) {
|
||||
Object[] objs = peers.toArray();
|
||||
for (int i = 0; i < objs.length; i++) {
|
||||
NTPPeer np = (NTPPeer) objs[i];
|
||||
if (np.getAddress().equals2(addr)) {
|
||||
return np;
|
||||
}
|
||||
}
|
||||
objs = ephemeralPeers.toArray();
|
||||
for (int i = 0; i < objs.length; i++) {
|
||||
NTPPeer np = (NTPPeer) objs[i];
|
||||
if (np.getAddress().equals2(addr)) {
|
||||
return np;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Runnable recv = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!dgs.isClosed()) {
|
||||
AtomicReference<InetSocketAddress> isa = new AtomicReference<>();
|
||||
NTPv4Packet nv4 = receiveNTPPacket(isa);
|
||||
MultipurposeSocketAddress mpsafrom = new MultipurposeSocketAddress(bind.getType(), isa.get());
|
||||
switch (nv4.getMode()) {
|
||||
case NTPv4Packet.NTP_SYMMETRIC_ACTIVE:
|
||||
NTPv4Packet nv4r = new NTPv4Packet(context.getClock());
|
||||
nv4r.setMode(NTPv4Packet.NTP_SYMMETRIC_PASSIVE);
|
||||
setNTPInformation(nv4r, nv4, findPeer(mpsafrom));
|
||||
sendNTPPacket(nv4r, isa.get());
|
||||
ephemeralPeers.add(new NTPPeer(mpsafrom, NTPv4Packet.NTP_SYMMETRIC_ACTIVE, true));
|
||||
break;
|
||||
case NTPv4Packet.NTP_SYMMETRIC_PASSIVE:
|
||||
NTPPeer npr = checkOriginTimestamp(nv4);
|
||||
if (npr != null) {
|
||||
npr.resetEphemeralTimeout();
|
||||
npr.setPeerPollInterval((1L << nv4.getPollInterval()) * 1000000000);
|
||||
context.putPacket(nv4, mpsafrom);
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
case NTPv4Packet.NTP_CLIENT:
|
||||
NTPv4Packet nv4s = new NTPv4Packet(context.getClock());
|
||||
nv4s.setMode(NTPv4Packet.NTP_SERVER);
|
||||
setNTPInformation(nv4s, nv4, findPeer(mpsafrom));
|
||||
sendNTPPacket(nv4s, isa.get());
|
||||
break;
|
||||
case NTPv4Packet.NTP_SERVER:
|
||||
NTPPeer nprx = checkOriginTimestamp(nv4);
|
||||
if (nprx != null) {
|
||||
nprx.resetEphemeralTimeout();
|
||||
nprx.setPeerPollInterval((1L << nv4.getPollInterval()) * 1000000000);
|
||||
context.putPacket(nv4, mpsafrom);
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
protected void sendReqPackets() throws IOException {
|
||||
List<NTPPeer> sends = getPeersWillSend();
|
||||
for (NTPPeer npr : sends) {
|
||||
request(npr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected List<NTPPeer> getPeersWillSend() {
|
||||
List<NTPPeer> sends = new ArrayList<>();
|
||||
peers.forEach((pr) -> {
|
||||
if (pr.checkPollInterval()) {
|
||||
sends.add(pr);
|
||||
}
|
||||
});
|
||||
ephemeralPeers.removeIf((pr) -> {
|
||||
return pr.checkEphemeralTimeout();
|
||||
});
|
||||
ephemeralPeers.forEach((pr) -> {
|
||||
if (pr.checkPollInterval()) {
|
||||
if (!peers.contains(pr)) {
|
||||
sends.add(pr);
|
||||
}
|
||||
}
|
||||
});
|
||||
return sends;
|
||||
}
|
||||
|
||||
private NTPv4Packet receiveNTPPacket(AtomicReference<InetSocketAddress> isa) throws IOException {
|
||||
ByteBuffer bbf = ByteBuffer.allocate(65535);
|
||||
DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit());
|
||||
dgs.receive(dp);
|
||||
isa.set((InetSocketAddress) dp.getSocketAddress());
|
||||
NTPv4Packet nv4 = new NTPv4Packet(context.getClock());
|
||||
nv4.readFromChannel(KNEChannels.newReadableChannel(bbf), dp.getLength());
|
||||
if (showPacket)
|
||||
System.out.println("NTPv4 RX:" + isa.get() + " " + nv4);
|
||||
return nv4;
|
||||
}
|
||||
|
||||
public Set<NTPPeer> getPeers() {
|
||||
return peers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
dgs.close();
|
||||
context.unregisterIO(this);
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return dgs.isClosed();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
HighAccuracyClock hac = new HighAccuracyClock();
|
||||
NTPContext context = new NTPContext(hac);
|
||||
System.out.println(context);
|
||||
NTPv4Protocol nvc = new NTPv4Protocol(context, new MultipurposeSocketAddress("{UDP}0.0.0.0:123"));// 106.55.184.199
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}106.55.184.199:123", NTPv4Packet.NTP_CLIENT));
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}time.windows.com:123", NTPv4Packet.NTP_CLIENT));
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}127.0.0.1:123", NTPv4Packet.NTP_SYMMETRIC_ACTIVE));
|
||||
/*
|
||||
* for (int i = 0; i < 10000; i++) { Thread.sleep(1000);
|
||||
* System.out.println(context.getCurrentSelfDispersion128());
|
||||
* System.out.println(context); }
|
||||
*/
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user