* Copyright (c) 1985 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
"@(#) Copyright (c) 1985 Regents of the University of California.\n\
static char sccsid
[] = "@(#)timed.c 2.16 (Berkeley) %G%";
#include <protocols/timed.h>
int slvcount
; /* no. of slaves controlled by master */
u_short sequence
; /* sequence number */
char hostname
[MAXHOSTNAMELEN
];
char tracefile
[] = _PATH_TIMEDLOG
;
struct netinfo
*nettab
= NULL
;
int nslavenets
; /* Number of networks were I could be a slave */
int nmasternets
; /* Number of networks were I could be a master */
int nignorednets
; /* Number of ignored networks */
int nnets
; /* Number of networks I am connected to */
struct netinfo
*slavenet
;
struct netinfo
*firstslavenet();
} *nets
= (struct nets
*)0;
* The timedaemons synchronize the clocks of hosts in a local area network.
* One daemon runs as master, all the others as slaves. The master
* performs the task of computing clock differences and sends correction
* Slaves start an election to choose a new master when the latter disappears
* because of a machine crash, network partition, or when killed.
* A resolution protocol is used to kill all but one of the masters
* that happen to exist in segments of a partitioned network when the
* network partition is fixed.
* Authors: Riccardo Gusella & Stefano Zatti
struct ifreq ifreq
, *ifr
;
register struct netinfo
*ntp
;
struct netinfo
*savefromnet
;
struct sockaddr_in server
;
openlog("timed", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
fprintf(stderr
, "Timed: not superuser\n");
while (--argc
> 0 && **++argv
== '-') {
"timed: -i and -n make no sense together\n");
"timed: -i and -n make no sense together\n");
fprintf(stderr
, "timed: -%c: unknown option\n",
fd
= fopen(tracefile
, "w");
fprintf(fd
, "Tracing started on: %s\n\n",
srvp
= getservbyname("timed", "udp");
syslog(LOG_CRIT
, "unknown service 'timed/udp'");
server
.sin_port
= srvp
->s_port
;
server
.sin_family
= AF_INET
;
sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
syslog(LOG_ERR
, "socket: %m");
if (setsockopt(sock
, SOL_SOCKET
, SO_BROADCAST
, (char *)&on
,
syslog(LOG_ERR
, "setsockopt: %m");
if (bind(sock
, &server
, sizeof(server
))) {
syslog(LOG_ERR
, "server already running");
syslog(LOG_ERR
, "bind: %m");
/* choose a unique seed for random number generation */
(void)gettimeofday(&time
, (struct timezone
*)0);
seed
= time
.tv_sec
+ time
.tv_usec
;
sequence
= random(); /* initial seq number */
/* rounds kernel variable time to multiple of 5 ms. */
time
.tv_usec
= -((time
.tv_usec
/1000) % 5) * 1000;
(void)adjtime(&time
, (struct timeval
*)0);
if (gethostname(hostname
, sizeof(hostname
) - 1) < 0) {
syslog(LOG_ERR
, "gethostname: %m");
struct netent
*getnetent();
for ( np
= nets
; np
; np
= np
->next
) {
n
= getnetbyname(np
->name
);
syslog(LOG_ERR
, "getnetbyname: unknown net %s",
ifc
.ifc_len
= sizeof(buf
);
if (ioctl(sock
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
syslog(LOG_ERR
, "get interface configuration: %m");
n
= ifc
.ifc_len
/sizeof(struct ifreq
);
for (ifr
= ifc
.ifc_req
; n
> 0; n
--, ifr
++) {
if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
ntp
= (struct netinfo
*)malloc(sizeof(struct netinfo
));
((struct sockaddr_in
*)&ifreq
.ifr_addr
)->sin_addr
;
if (ioctl(sock
, SIOCGIFFLAGS
,
syslog(LOG_ERR
, "get interface flags: %m");
if ((ifreq
.ifr_flags
& IFF_UP
) == 0 ||
((ifreq
.ifr_flags
& IFF_BROADCAST
) == 0 &&
(ifreq
.ifr_flags
& IFF_POINTOPOINT
) == 0)) {
if (ifreq
.ifr_flags
& IFF_BROADCAST
)
if (ioctl(sock
, SIOCGIFNETMASK
,
syslog(LOG_ERR
, "get netmask: %m");
ntp
->mask
= ((struct sockaddr_in
*)
&ifreq
.ifr_addr
)->sin_addr
.s_addr
;
if (ioctl(sock
, SIOCGIFBRDADDR
,
syslog(LOG_ERR
, "get broadaddr: %m");
ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_broadaddr
;
if (ioctl(sock
, SIOCGIFDSTADDR
,
syslog(LOG_ERR
, "get destaddr: %m");
ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_dstaddr
;
ntp
->dest_addr
.sin_port
= port
;
addr
= ntohl(ntp
->dest_addr
.sin_addr
.s_addr
);
while ((mask
& 1) == 0) {
for (n
= nets
; n
; n
= n
->next
)
if (nflag
&& !n
|| iflag
&& n
)
ntp
->net
= ntp
->mask
& ntp
->dest_addr
.sin_addr
.s_addr
;
(void) free((char *)ntp
);
syslog(LOG_ERR
, "No network usable");
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
)
* Take care of some basic initialization.
/* us. delay to be used in response to broadcast */
delay1
= casual((long)10000, 200000);
/* election timer delay in secs. */
delay2
= casual((long)MINTOUT
, (long)MAXTOUT
);
for (s
= getdtablesize(); s
>= 0; --s
)
(void) open("/dev/null", 0);
* number (increased by 1) of slaves controlled by master:
* used in master.c, candidate.c, networkdelta.c, and
makeslave(firstslavenet());
/* Just lost our master */
slavenet
->status
= election(slavenet
);
if (slavenet
->status
== MASTER
)
makeslave(firstslavenet());
/* Just been told to quit */
/* this should not happen */
syslog(LOG_ERR
, "Attempt to enter invalid state");
/* open raw socket used to measure time differences */
sock_raw
= socket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
);
syslog(LOG_ERR
, "opening raw socket: %m");
/* sock_raw is not being used now */
/* if Mflag is not set timedaemon is forced to act as a slave */
makeslave(firstslavenet());
makeslave(firstslavenet());
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
)
if (ntp
->status
== MASTER
)
* Try to become master over ignored nets..
register struct netinfo
*ntp
;
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
)
if (ntp
->status
== IGNORE
)
register struct netinfo
*ntp
;
struct tsp resp
, conflict
, *answer
, *readmsg(), *acksend();
char mastername
[MAXHOSTNAMELEN
];
struct sockaddr_in masteraddr
;
resp
.tsp_type
= TSP_MASTERREQ
;
(void)strcpy(resp
.tsp_name
, hostname
);
answer
= acksend(&resp
, &ntp
->dest_addr
, (char *)ANYADDR
,
* Various conditions can cause conflict: race between
* two just started timedaemons when no master is
* present, or timedaemon started during an election.
* Conservative approach is taken: give up and became a
* slave postponing election of a master until first
time
.tv_sec
= time
.tv_usec
= 0;
answer
= readmsg(TSP_MASTERREQ
, (char *)ANYADDR
,
time
.tv_sec
= time
.tv_usec
= 0;
answer
= readmsg(TSP_MASTERUP
, (char *)ANYADDR
,
time
.tv_sec
= time
.tv_usec
= 0;
answer
= readmsg(TSP_ELECTION
, (char *)ANYADDR
,
(void)strcpy(mastername
, answer
->tsp_name
);
* If network has been partitioned, there might be other
* masters; tell the one we have just acknowledged that
* it has to gain control over the others.
answer
= readmsg(TSP_MASTERACK
, (char *)ANYADDR
, &time
,
* checking also not to send CONFLICT to ack'ed master
* due to duplicated MASTERACKs
strcmp(answer
->tsp_name
, mastername
) != 0) {
conflict
.tsp_type
= TSP_CONFLICT
;
(void)strcpy(conflict
.tsp_name
, hostname
);
if (acksend(&conflict
, &masteraddr
, mastername
,
TSP_ACK
, (struct netinfo
*)NULL
) == NULL
) {
"error on sending TSP_CONFLICT");
* based on the current network configuration, set the status, and count
register struct netinfo
*ntp
;
nmasternets
= nslavenets
= nnets
= nignorednets
= 0;
fprintf(fd
, "Net status:\n");
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
switch ((int)ntp
->status
) {
fprintf(fd
, "\t%-16s", inet_ntoa(ntp
->net
));
switch ((int)ntp
->status
) {
fprintf(fd
, "invalid state %d\n",(int)ntp
->status
);
"\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
nnets
, nmasternets
, nslavenets
, nignorednets
);
register struct netinfo
*ntp
;
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
)
if (ntp
->status
== SLAVE
&& ntp
!= net
)
register struct netinfo
*ntp
;
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
)
if (ntp
->status
== SLAVE
)
return ((struct netinfo
*)0);
* `casual' returns a random number in the range [inf, sup]
value
= (float)(random() & 0x7fffffff) / 0x7fffffff;
return(inf
+ (sup
- inf
) * value
);
(void)gettimeofday(&tv
, (struct timezone
*)0);
return (ctime(&tv
.tv_sec
));
register struct nets
**netlist
= &nets
;
netlist
= &((*netlist
)->next
);
*netlist
= (struct nets
*)malloc(sizeof **netlist
);
if (*netlist
== (struct nets
*)0) {
syslog(LOG_ERR
, "malloc failed");
bzero((char *)*netlist
, sizeof(**netlist
));