I played a bit with the idea this morning, and I think this is how I would
do it:

   +-------+-----------+-----+---------------------+---------------+
   |1 1 1 1|M M M M M M M M M|L L L L L L L L L|D D|C C C C C C C C|
   +-------+-----------+-----+---------------------+---------------+
    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 
    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0

The first four bits are constant 0xf putting the resulting IPv4 in
the "Class-E" segment which is reserved.  This should give us 100%
certainty in detecting meddling DNS resolvers.

The "M" field is nine bit unsigned month number:

        (year - 1972) * 12 + month - 1

The "L" field the eight bit signed(!) TAI-UTC difference before the
start of the indicated month.

The "D" field is two bit signed TAI-UTC change before the start of
the indicated month.

The "C" field is a CRC8 with poly=0xcf (found by exhaustive search)

Here are how recent Bulletin-C's would look:

YYYY MM before  after  encoded crc IP               Decoded
-------------------------------------------------------------------------
2012  1     34     34 f7808800  65 247.128.136.101 -> OK  2012  1  +34 +0
2012  7     34     35 f7988900  66 247.152.137.102 -> OK  2012  7  +34 +1
2013  1     35     35 f7b08c00  0b  247.176.140.11 -> OK  2013  1  +35 +0
2013  7     35     35 f7c88c00  58  247.200.140.88 -> OK  2013  7  +35 +0
2014  1     35     35 f7e08c00  69 247.224.140.105 -> OK  2014  1  +35 +0
2014  7     35     35 f7f88c00  f3 247.248.140.243 -> OK  2014  7  +35 +0
2015  1     35     35 f8108c00  be  248.16.140.190 -> OK  2015  1  +35 +0
2015  7     35     36 f8288d00  fa  248.40.141.250 -> OK  2015  7  +35 +1
-------------------------------------------------------------------------

Attached is a proof of concept in python.

You can poke the result at my test-domain "leap.net-tid.dk"

Poul-Henning

-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
[email protected]         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe    
Never attribute to malice what can adequately be explained by incompetence.
#!/usr/local/bin/python

from __future__ import print_function

import httplib

conn = None

def fetch_url(url):
	global conn

	print("Fetch", url)
	if conn == None:
		conn = httplib.HTTPConnection("hpiers.obspm.fr")
	conn.request("GET", url)
	r1 = conn.getresponse()
	if r1.status != 200:
		print (r1.status, r1.reason)
		exit(2)
	data1 = r1.read()
	time.sleep(random.uniform(1,2))
	return data1

def cache_url(fn, url):
	try:
		fi = open("_Cache_%s" % fn)
		x = fi.read()
		fi.close()
		return x
	except:
		x = fetch_url(url)
		fi = open("_Cache_%s" % fn, "w")
		fi.write(x)
		fi.close()
		return x

raw_data = cache_url(
    "Leap_Second_History.dat",
    "http://hpiers.obspm.fr/iers/bul/bulc/Leap_Second_History.dat";
)

def crc8(b):
	poly = 0xcf

	# I chose 0xcf after an exhaustive search for best performance
	# on 28 bit messages.
	#
 	# bit-     Missed     Total   Miss-Rate
	# errors
	# -------------------------------------
	#  <=1          0        28    0.000000
	#  <=2          0       406    0.000000
	#  <=3          0      3682    0.000000
	#  <=4         81     24157    0.003353
	#  <=5        532    122437    0.004345
	#  <=6       1972    499177    0.003951
	#  <=7       6494   1683217    0.003858
	#  <=8      18736   4791322    0.003910
	# -------------------------------------

	crc = 0
	for i in b:
		for j in range(8):
			mix = (crc ^ i) & 0x01
			crc >>= 1
			if mix:
				crc ^= poly
			i >>= 1
	return crc

def dec(i):
	r = ""
	j = i.split(".")
	assert len(j) == 4
	for k in range(4):
		j[k] = int(j[k])
	if crc8(j) == 0:
		r += "OK "
	else:
		r += "BAD"
	w = j[0] << 16
	w |= j[1] << 8
	w |= j[2]

	assert w & 0xf00000 == 0xf00000

	w &= ~0xf00000

	d = w & 0x03
	w >>= 2	
	b = w & 0xff
	w >>= 8	
	mn = w
	m = (mn % 12) + 1
	y = mn / 12 + 1972
	assert d != 2
	if d == 3:
		d = -1
	r += " %04d" % y
	r += " %2d" % m
	r += " %+4d" % b
	r += " %+2d" % d
	return r

def enc(y, m, b, a):
	mn = (y - 1972) * 12 + (m-1)
	w = mn
	w <<= 8
	w |= b
	w <<= 2
	if a == b + 1:
		w |= 0x1
	elif a == b - 1:
		w |= 0x3
	w <<= 8
	w |= 0xf0000000
	z = [w >> 24, (w >> 16) & 0xff, (w >> 8) & 0xff]
	z.append(crc8(z))
	assert crc8(z) == 0
	i = "%d.%d.%d.%d" % (z[0], z[1], z[2], z[3])
	print("%4d %2d %6d %6d %08x  %02x %15s -> %s" %
	    (y, m, b, a, w, z[3], i, dec(i)))

print("YYYY MM before  after  encoded crc IP               Decoded")
print("-" * 73)

ll = list()
lmnum = 6
ldut1 = 9
for l in raw_data.split("\n"):
	i = l.split()
	if len(i) == 0 or i[0][0] == "#":
		continue
	assert i[1] == "1"
	month = int(i[2])
	assert month >= 1 and month <= 12
	year = int(i[3])
	assert year >= 1972
	dut1 = int(i[4])
	mnum = (year - 1972) * 12 + month - 1
	for x in range(lmnum, mnum, 6):
		y = 1972 + (x+1) // 12
		m = (x+1) % 12
		enc(y, m, ldut1, ldut1)
	enc(year, month, ldut1, dut1)
	lmnum = mnum + 6
	ldut1 = dut1
print("")
for year in range(2015, 2143):
	enc(year, month, ldut1, dut1)
print("-" * 73)
_______________________________________________
LEAPSECS mailing list
[email protected]
https://pairlist6.pair.net/mailman/listinfo/leapsecs

Reply via email to