Subject: [ippm] OWAMP implementation

From: "Hélder Veiga e Teresa Pinho" <[email-masked]>
Date: Tue, 08 Jun 2004 15:50:12 +0100

Dear Jeff,

We have been carrying out conformance tests between our 
OWAMP implementation and yours. Fortunately for us :) 
almost all the tests were successful. We still have the 
following problems:

1. Definition of Timestamp. 
The date received and decoded in our system doesn?t match 
with what we are expecting (it represents a date in the 
past - year 1938 - when we are in year 2004). Maybe there 
is a problem with the function we are using to decode the 
time value in the timestamp received (below we send our 
function). In this function we presume to receive the time 
value in timestamp format, with the first 32 bits 
representing the unsigned integer number of seconds 
elapsed since 0h on 1 January 1900, and the next 32 bits 
representing the fractional part of a second that has 
elapsed since then (as it seems the specification in 
RFC1305).

Also we didn?t understand quite well your implementation. 
Can you explain us what does the function 
OWPTimespecToNum64? (How it converts the time received 
with function ?gettimeofday? to Timestamp format?)
We also didn?t understand why you use this part of code in 
it:
  
while(nsec >= BILLION){
		sec++;
		nsec -= BILLION;
	}
  
2. Another problem that we have with our implementation is 
how to compute the estimate for error and synchronization. 
We don?t know how to calculate and use the fields of the 
Error Estimate format used in OWAMP. Can you help us on 
it?

Sorry for bother you with all these issues.
Best regards.

Teresa Pinho
Hélder Veiga
José Luis Oliveira
Rui Valadas

***  Our Function ***
/**
  * This is a constructor of class Timestamp. It receives 
a time in milliseconds since 0h 1    
  * Jan 1970 and creates a variable of class Timestamp.
  * NTP2UNIX = (70 * 365 + 17) * 86400 Seconds between 
1900-01-01 and 1970-01-01
  * @param secondsSince0h1Jan1970 long
  */
    public Timestamp(long milliSecondsSince0h1Jan1970){
      long milliSecondsSince0h1Jan1900 = 
milliSecondsSince0h1Jan1970 + NTP2UNIX*1000;
      integerPartOfSeconds = 
(int)(milliSecondsSince0h1Jan1900/1000);
      fractionalPartOfSeconds = 
(int)(((milliSecondsSince0h1Jan1900)%1000)*1000);   // 
Resolution to microsecond
    }

    /**
     * This method returns the time in milliseconds since 
0h 1 Jan 1970
     * @param time Timestamp - time in milliseconds since 
0h 1 Jan 1900
     * @return long - time in milliseconds since 0h 1 Jan 
1970
     */
    public long timeInMillisecondsSince1970(Timestamp 
time){
      return ((long)(((long)time.integerPartOfSeconds*1000 
)+(long)time.fractionalPartOfSeconds/1000) - 
NTP2UNIX*1000);
    }

***  Your Function ***

/*
  * Function:	OWPTimespecToNum64
  *
  * Description:	
  *
  * 	Convert a time value in timespec representation to an 
OWPNum64
  * 	representation. These are "relative" time values. 
(Not absolutes - i.e.
  * 	they are not relative to some "epoch".) OWPNum64 
values are
  * 	unsigned 64 integral types with the Most Significant 
32 of those
  * 	64 bits representing seconds. The Least Significant 
32 bits
  * 	represent fractional seconds at a resolution of 32 
bits.
  *
  * In Args:	
  *
  * Out Args:	
  *
  * Scope:	
  * Returns:	
  * Side Effect:	
  */
void
OWPTimespecToNum64(
	OWPNum64	*to,
	struct timespec	*from
	)
{
	u_int32_t	sec = from->tv_sec;
	u_int32_t	nsec = from->tv_nsec;

	*to = 0;

	/*
	 * Ensure nsec's is only fractional.
	 */
	while(nsec >= BILLION){
		sec++;
		nsec -= BILLION;
	}

	/*
	 * Place seconds in MS 32 bits.
	 */
	*to = (u_int64_t)MASK32(sec) << 32;
	/*
	 * Normalize nsecs to 32bit fraction, then set that to LS 
32 bits.
	 */
	*to |= MASK32(((u_int64_t)nsec << 32)/BILLION);

	return;
}

_______________________________________________
ippm mailing list
[email-masked] 
https://www1.ietf.org/mailman/listinfo/ippm

---

From: "Jeff W. Boote" <[email-masked]>
Date: Tue, 08 Jun 2004 10:01:18 -0600

> elapsed since then (as it seems the specification in

You say unsigned here, but in your implementation you cast to (int) in a number
of places. This is highly suspect...

> We also didn?t understand why you use this part of code in

The function you pulled out is not used to create a true timestamp. It is used
to create a relative time value. There are places in the implementation where I
do arithmetic on time values. Sometimes I do that with timespec structures. If I
add two timespec structures - it could be possible to get more than 1 billion
nsec's in the tv_nsec portion of the structure. This would represent more than
one second. This loop ensures that the nsec variable holds less than 1 billion
nsecs and therefore less than one second. This ensures I can compute the
fractional part of the timestamp without overflowing.

> Error Estimate format used in OWAMP. Can you help us on

Those fields are only useful if you are synchronizing your clock with an
external standard for time. Say UTC. If you are not synchronizing the time, then
set the S bit to 0. I'm not sure how I could help you on it more than just
pointing you at the sample implementation. There are functions in there that
convert from a floating point double representation of the error to the format
in the timestamp and back. If you have specific questions about them, I could
answer them.

> milliSecondsSince0h1Jan1970 + NTP2UNIX*1000;

You certainly overflow right here. It takes an unsigned 32 bit integer to hold
the number of SECONDS since Jan1900. If you are counting seconds, a signed 32
bit integer can only go ~68 years. An unsigned can go ~136 years. (So, my code
will need something more by the year 2036 - but I hope to be retired by then, or
at least working on something else. ;) )

If you are counting the number of MSEC, it would be far less than 68 years -
I'll let you do the math. In other words, you are not going to be able to hold
the number of MSEC since 1900 in a 32 bit integer. There may be other overflow
errors in this code... I stopped here because you already have no hope of this
working.

jeff

---

From: stanislav shalunov <[email-masked]>
Date: 08 Jun 2004 12:59:12 -0400

[Trimmed co-authors in the CC line.]

The answer to your first question is being researched.

"HИlder Veiga e Teresa Pinho" <[email-masked]> writes:

> 2. Another problem that we have with our implementation is how to
> compute the estimate for error and synchronization.  We don't know
> how to calculate and use the fields of the Error Estimate format
> used in OWAMP.  Can you help us on it?

Where do you get the time?  From the rest of your message it seems
apparent that you call gettimeofday() to read the system clock to
produce timestamps.  This is fine, but where does the system time come
from?  One approach is to use NTP.  NTP, if configured properly (with
at least four servers) will tell you if a clock is synchronized and
what it thinks the error estimate is.  If you're assuming that NTP
will be used, you could propagate these values from NTP (after
suitable format conversion).

If you do not use NTP, the time still has to come from somewhere.  The
semantics of the error estimate field are defined in the specification
as follows:

#   The Error Estimate specifies the estimate of the error and
#   synchronization.  It has the following format:
#
#         0                   1
#         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
#        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#        |S|Z|   Scale   |   Multiplier  |
#        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
#   The first bit S SHOULD be set if the party generating the timestamp
#   has a clock that is synchronized to UTC using an external source
#   (e.g., the bit should be set if GPS hardware is used and it indicates
#   that it has acquired current position and time or if NTP is used and
#   it indicates that it has synchronized to an external source, which
#   includes stratum 0 source, etc.); if there is no notion of external
#   synchronization for the time source, the bit SHOULD NOT be set.  The
#   next bit has the same semantics as MBZ fields elsewhere: it MUST be
#   set to zero by the sender and ignored by everyone else.  The next six
#   bits Scale form an unsigned integer; Multiplier is an unsigned
#   integer as well.  They are interpreted as follows: the error estimate
#   is equal to Multiplier*2^(-32)*2^Scale (in seconds).  [Notation
#   clarification: 2^Scale is two to the power of Scale.]  Multiplier
#   MUST NOT be set to zero.  If Multiplier is zero, the packet SHOULD be
#   considered corrupt and discarded.

So, if you read the system clock on a system with a clock untrained by
NTP or anything else, the S bit SHOULD NOT be set.  The Z bit is
always set to zero and ignored by all data consumers.  Scale and
Multiplier form a number that has units of seconds and presents your
best conservative guess of clock precision.  If the clock is
untrained, this should probably be some huge number that should,
perhaps, come from configuration.

-- 
Stanislav Shalunov		http://www.internet2.edu/~shalunov/

This message is designed to be viewed with 0.06479891g of NaCl.

---

From: stanislav shalunov <[email-masked]>
Date: 09 Jun 2004 10:06:10 -0400

"Henk Uijterwaal (RIPE NCC)" <[email-masked]> writes:

> Then, NTP can also be used to sync the machine to a hardware device
> connected to the machine running OWAMP.  In that case, there is even an
> advantage in configuring NTP with just that peer.

While it's not on topic for IPPM, it turns out you need at least four
NTP servers to get meaningful error estimates.  If you have a local
stratum 0 source, that's what would normally be used because of
stratum selection.  (If you configure one server---wherever it is and
whatever its stratum---you'll get an unrealistically low error
estimate.)

This message is designed to be viewed upside down.

---

From: "Henk Uijterwaal (RIPE NCC)" <[email-masked]>
Date: Wed, 9 Jun 2004 10:29:04 +0200 (CEST)

On Tue, 8 Jun 2004, Jeff W. Boote wrote:

> You certainly overflow right here. It takes an unsigned 32 bit integer
> to hold the number of SECONDS since Jan1900. If you are counting
> seconds, a signed 32 bit integer can only go ~68 years. An unsigned can
> go ~136 years. (So, my code will need something more by the year 2036 -
> but I hope to be retired by then, or at least working on something else.
> ;) )

(Speaking as a potential implementor): Is there any particular reason why
we start on 1-1-1900 and not 1-1-1970? (as NTP does).  I'd hate to do a
calculation every time I read a timestamp.

Henk

------------------------------------------------------------------------------
Henk Uijterwaal                           Email: henk.uijterwaal(at)ripe.net
RIPE Network Coordination Centre          http://www.amsterdamned.org/~henk
P.O.Box 10096          Singel 258         Phone: +31.20.5354414
1001 EB Amsterdam      1016 AB Amsterdam  Fax: +31.20.5354445
The Netherlands        The Netherlands    Mobile: +31.6.55861746
------------------------------------------------------------------------------

Process and Procedure are the last hiding place of people without the wit
and wisdom to do their job properly.                          (David Brent).

---

From: "Jeff W. Boote" <[email-masked]>
Date: Wed, 09 Jun 2004 02:38:45 -0600

"Henk Uijterwaal (RIPE NCC)" wrote:
> we start on 1-1-1900 and not 1-1-1970? (as NTP does).

We use exactly what RFC 1305 (NTP) specifies. NTP does not use 1970.

In fact, it was very useful to be able to download the ntp source code and look
at the time conversion functions it uses...

jeff

---

From: "Henk Uijterwaal (RIPE NCC)" <[email-masked]>
Date: Wed, 9 Jun 2004 10:37:49 +0200 (CEST)

On Tue, 8 Jun 2004, stanislav shalunov wrote:

> "Hélder Veiga e Teresa Pinho" <[email-masked]> writes:
>
> what it thinks the error estimate is.

This is not entirely correct.  Not only the number of peers matters, their
location is important as well.  One NTP peer on the same network segment
will give better results than 4 peers farther away.  In fact, NTP might
not even use those peers (except as a backup).

Henk

---

From: "Henk Uijterwaal (RIPE NCC)" <[email-masked]>
Date: Thu, 10 Jun 2004 08:27:47 +0200 (CEST)

> at the time conversion functions it uses...

Oops, you're right, forget it,

Henk