From c1aeb8abb8688a6a4e1019a8cd35bddd9d24b34d Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 30 2018 05:13:00 +0000 Subject: import linuxptp-1.8-6.el7 --- diff --git a/SOURCES/linuxptp-bonding.patch b/SOURCES/linuxptp-bonding.patch index b3737bb..6742d70 100644 --- a/SOURCES/linuxptp-bonding.patch +++ b/SOURCES/linuxptp-bonding.patch @@ -1669,3 +1669,199 @@ index 2559c74..4fc4fa3 100644 .TP .BI \-i " interface" Performs the exact same function as + +Backport of +commit 742f8788211d2a861a0214e76449390b89ba55c2 +Author: Miroslav Lichvar +Date: Fri Apr 13 17:11:56 2018 +0200 + + rtnl: remove dependency on config.h. + + Change the rtnl_get_ts_label() function to accept the name of the master + interface and the buffer for the slave interface directly instead of the + struct interface from config.h. + + Also, rename the function to rtnl_get_ts_device(). + + Signed-off-by: Miroslav Lichvar + +diff --git a/clock.c b/clock.c +index 9ffb43e..afee960 100644 +--- a/clock.c ++++ b/clock.c +@@ -948,7 +948,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + c->timestamping = timestamping; + required_modes = clock_required_modes(c); + STAILQ_FOREACH(iface, &config->interfaces, list) { +- rtnl_get_ts_label(iface); ++ rtnl_get_ts_device(iface->name, iface->ts_label); + ensure_ts_label(iface); + sk_get_ts_info(iface->ts_label, &iface->ts_info); + if (iface->ts_info.valid && +diff --git a/rtnl.c b/rtnl.c +index cea936b..f9a572b 100644 +--- a/rtnl.c ++++ b/rtnl.c +@@ -43,13 +43,13 @@ int rtnl_close(int fd) + return close(fd); + } + +-static void rtnl_get_ts_label_callback(void *ctx, int linkup, int ts_index) ++static void rtnl_get_ts_device_callback(void *ctx, int linkup, int ts_index) + { + int *dst = ctx; + *dst = ts_index; + } + +-int rtnl_get_ts_label(struct interface *iface) ++int rtnl_get_ts_device(char *device, char *ts_device) + { + int err, fd; + int ts_index = -1; +@@ -58,13 +58,13 @@ int rtnl_get_ts_label(struct interface *iface) + if (fd < 0) + return fd; + +- err = rtnl_link_query(fd, iface->name); ++ err = rtnl_link_query(fd, device); + if (err) { + goto no_info; + } + +- rtnl_link_status(fd, iface->name, rtnl_get_ts_label_callback, &ts_index); +- if (ts_index > 0 && if_indextoname(ts_index, iface->ts_label)) ++ rtnl_link_status(fd, device, rtnl_get_ts_device_callback, &ts_index); ++ if (ts_index > 0 && if_indextoname(ts_index, ts_device)) + err = 0; + else + err = -1; +diff --git a/rtnl.h b/rtnl.h +index 2906d74..f877cd2 100644 +--- a/rtnl.h ++++ b/rtnl.h +@@ -20,8 +20,6 @@ + #ifndef HAVE_RTNL_H + #define HAVE_RTNL_H + +-#include "config.h" +- + typedef void (*rtnl_callback)(void *ctx, int linkup, int ts_index); + + /** +@@ -32,11 +30,14 @@ typedef void (*rtnl_callback)(void *ctx, int linkup, int ts_index); + int rtnl_close(int fd); + + /** +- * Get interface ts_label information +- * @param iface struct interface. +- * @return Zero on success, or -1 on error. ++ * Get name of the slave interface which timestamps packets going through ++ * a master interface (e.g. bond0) ++ * @param device Name of the master interface. ++ * @param ts_device Buffer for the name of the slave interface, which must be ++ * at least IF_NAMESIZE bytes long. ++ * @return Zero on success, or -1 on error. + */ +-int rtnl_get_ts_label(struct interface *iface); ++int rtnl_get_ts_device(char *device, char *ts_device); + + /** + * Request the link status from the kernel. + +commit e5ba2dae5f102a66e152b96f9cc7b06aff4b90b5 +Author: Miroslav Lichvar +Date: Fri Apr 13 17:11:57 2018 +0200 + + timemaster: add support for bonded interfaces. + + Use the rtnl_get_ts_device() function to get the name of the slave + interface which will be timestamping PTP packets and use it instead of + the master interface to check the timestamping capabilities and PHC. + + Signed-off-by: Miroslav Lichvar + +diff --git a/makefile b/makefile +index 6f5321c..17189e6 100644 +--- a/makefile ++++ b/makefile +@@ -60,7 +60,7 @@ hwstamp_ctl: hwstamp_ctl.o version.o + + phc_ctl: phc_ctl.o phc.o sk.o util.o clockadj.o sysoff.o print.o version.o + +-timemaster: print.o sk.o timemaster.o util.o version.o ++timemaster: print.o rtnl.o sk.o timemaster.o util.o version.o + + version.o: .version version.sh $(filter-out version.d,$(DEPEND)) + +diff --git a/timemaster.c b/timemaster.c +index cda2d90..fc3ba31 100644 +--- a/timemaster.c ++++ b/timemaster.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -35,6 +36,7 @@ + #include + + #include "print.h" ++#include "rtnl.h" + #include "sk.h" + #include "util.h" + #include "version.h" +@@ -674,6 +676,7 @@ static int add_ptp_source(struct ptp_domain *source, + { + struct config_file *config_file; + char **command, *uds_path, **interfaces, *message_tag; ++ char ts_interface[IF_NAMESIZE]; + int i, j, num_interfaces, *phc, *phcs, hw_ts, sw_ts; + struct sk_ts_info ts_info; + +@@ -696,26 +699,38 @@ static int add_ptp_source(struct ptp_domain *source, + for (i = 0; i < num_interfaces; i++) { + phcs[i] = -1; + ++ /* ++ * if it is a bonded interface, use the name of the active ++ * slave interface (which will be timestamping packets) ++ */ ++ if (!rtnl_get_ts_device(source->interfaces[i], ts_interface)) { ++ pr_debug("slave interface of %s: %s", ++ source->interfaces[i], ts_interface); ++ } else { ++ snprintf(ts_interface, sizeof(ts_interface), "%s", ++ source->interfaces[i]); ++ } ++ + /* check if the interface has a usable PHC */ +- if (sk_get_ts_info(source->interfaces[i], &ts_info)) { ++ if (sk_get_ts_info(ts_interface, &ts_info)) { + pr_err("failed to get time stamping info for %s", +- source->interfaces[i]); ++ ts_interface); + free(phcs); + return 1; + } + + if (((ts_info.so_timestamping & hw_ts) != hw_ts)) { +- pr_debug("interface %s: no PHC", source->interfaces[i]); ++ pr_debug("interface %s: no PHC", ts_interface); + if ((ts_info.so_timestamping & sw_ts) != sw_ts) { + pr_err("time stamping not supported on %s", +- source->interfaces[i]); ++ ts_interface); + free(phcs); + return 1; + } + continue; + } + +- pr_debug("interface %s: PHC %d", source->interfaces[i], ++ pr_debug("interface %s: PHC %d", ts_interface, + ts_info.phc_index); + + /* and the PHC isn't already used in another source */ diff --git a/SOURCES/linuxptp-tmasterrestart.patch b/SOURCES/linuxptp-tmasterrestart.patch new file mode 100644 index 0000000..dc8f7eb --- /dev/null +++ b/SOURCES/linuxptp-tmasterrestart.patch @@ -0,0 +1,388 @@ +commit e79e5040a0e7efd622ecdd572bee40c90e59c3bd +Author: Miroslav Lichvar +Date: Fri Apr 13 17:11:58 2018 +0200 + + timemaster: restart terminated processes. + + If a ptp4l or phc2sys process is terminated (e.g. due to a crash) and + timemaster was running for at least one second (i.e. it's not an error + in ptp4l/phc2sys configuration), start the process again. Restart all + processes corresponding to the same time source at the same time to + ensure phc2sys is always connected to the currently running ptp4l. + + Add a new option to disable the restarting. + + Signed-off-by: Miroslav Lichvar + +diff --git a/timemaster.8 b/timemaster.8 +index e0e22eb..7288972 100644 +--- a/timemaster.8 ++++ b/timemaster.8 +@@ -87,6 +87,16 @@ Specify the first number in a sequence of SHM segments that will be used by + can be useful to avoid conflicts with time sources that are not started by + \fBtimemaster\fR, e.g. \fBgpsd\fR using segments number 0 and 1. + ++.TP ++.B restart_processes ++Enable or disable restarting of processes started by \fBtimemaster\fR. If the ++option is set to a non-zero value, all processes except \fBchronyd\fR and ++\fBntpd\fR will be automatically restarted when terminated and \fBtimemaster\fR ++is running for at least one second (i.e. the process did not terminate due to a ++configuration error). If a process was terminated and is not started again, ++\fBtimemaster\fR will kill the other processes and exit with a non-zero status. ++The default value is 1 (enabled). ++ + .SS [ntp_server address] + + The \fBntp_server\fR section specifies an NTP server that should be used as a +@@ -318,6 +328,7 @@ ptp4l_option delay_mechanism P2P + ntp_program chronyd + rundir /var/run/timemaster + first_shm_segment 1 ++restart_processes 0 + + [chronyd] + path /usr/sbin/chronyd +diff --git a/timemaster.c b/timemaster.c +index fc3ba31..4ba921e 100644 +--- a/timemaster.c ++++ b/timemaster.c +@@ -44,6 +44,7 @@ + #define DEFAULT_RUNDIR "/var/run/timemaster" + + #define DEFAULT_FIRST_SHM_SEGMENT 0 ++#define DEFAULT_RESTART_PROCESSES 1 + + #define DEFAULT_NTP_PROGRAM CHRONYD + #define DEFAULT_NTP_MINPOLL 6 +@@ -108,6 +109,7 @@ struct timemaster_config { + enum ntp_program ntp_program; + char *rundir; + int first_shm_segment; ++ int restart_processes; + struct program_config chronyd; + struct program_config ntpd; + struct program_config phc2sys; +@@ -122,6 +124,9 @@ struct config_file { + struct script { + struct config_file **configs; + char ***commands; ++ int **command_groups; ++ int restart_groups; ++ int no_restart_group; + }; + + static void free_parray(void **a) +@@ -385,6 +390,8 @@ static int parse_timemaster_settings(char **settings, + replace_string(value, &config->rundir); + } else if (!strcasecmp(name, "first_shm_segment")) { + r = parse_int(value, &config->first_shm_segment); ++ } else if (!strcasecmp(name, "restart_processes")) { ++ r = parse_int(value, &config->restart_processes); + } else { + pr_err("unknown timemaster setting %s", name); + return 1; +@@ -508,6 +515,7 @@ static struct timemaster_config *config_parse(char *path) + config->ntp_program = DEFAULT_NTP_PROGRAM; + config->rundir = xstrdup(DEFAULT_RUNDIR); + config->first_shm_segment = DEFAULT_FIRST_SHM_SEGMENT; ++ config->restart_processes = DEFAULT_RESTART_PROCESSES; + + init_program_config(&config->chronyd, "chronyd", + NULL, DEFAULT_CHRONYD_SETTINGS, NULL); +@@ -632,6 +640,18 @@ static char *get_refid(char *prefix, unsigned int number) + return NULL; + }; + ++static void add_command(char **command, int command_group, ++ struct script *script) ++{ ++ int *group; ++ ++ parray_append((void ***)&script->commands, command); ++ ++ group = xmalloc(sizeof(int)); ++ *group = command_group; ++ parray_append((void ***)&script->command_groups, group); ++} ++ + static void add_shm_source(int shm_segment, int poll, int dpoll, double delay, + char *ntp_options, char *prefix, + struct timemaster_config *config, char **ntp_config) +@@ -671,8 +691,8 @@ static int add_ntp_source(struct ntp_server *source, char **ntp_config) + + static int add_ptp_source(struct ptp_domain *source, + struct timemaster_config *config, int *shm_segment, +- int ***allocated_phcs, char **ntp_config, +- struct script *script) ++ int *command_group, int ***allocated_phcs, ++ char **ntp_config, struct script *script) + { + struct config_file *config_file; + char **command, *uds_path, **interfaces, *message_tag; +@@ -798,19 +818,19 @@ static int add_ptp_source(struct ptp_domain *source, + /* HW time stamping */ + command = get_ptp4l_command(&config->ptp4l, config_file, + interfaces, 1); +- parray_append((void ***)&script->commands, command); ++ add_command(command, *command_group, script); + + command = get_phc2sys_command(&config->phc2sys, + source->domain, + source->phc2sys_poll, + *shm_segment, uds_path, + message_tag); +- parray_append((void ***)&script->commands, command); ++ add_command(command, (*command_group)++, script); + } else { + /* SW time stamping */ + command = get_ptp4l_command(&config->ptp4l, config_file, + interfaces, 0); +- parray_append((void ***)&script->commands, command); ++ add_command(command, (*command_group)++, script); + + string_appendf(&config_file->content, + "clock_servo ntpshm\n" +@@ -862,7 +882,8 @@ static char **get_ntpd_command(struct program_config *config, + } + + static struct config_file *add_ntp_program(struct timemaster_config *config, +- struct script *script) ++ struct script *script, ++ int command_group) + { + struct config_file *ntp_config = xmalloc(sizeof(*ntp_config)); + char **command = NULL; +@@ -886,7 +907,7 @@ static struct config_file *add_ntp_program(struct timemaster_config *config, + } + + parray_append((void ***)&script->configs, ntp_config); +- parray_append((void ***)&script->commands, command); ++ add_command(command, command_group, script); + + return ntp_config; + } +@@ -894,6 +915,7 @@ static struct config_file *add_ntp_program(struct timemaster_config *config, + static void script_destroy(struct script *script) + { + char ***commands, **command; ++ int **groups; + struct config_file *config, **configs; + + for (configs = script->configs; *configs; configs++) { +@@ -911,6 +933,10 @@ static void script_destroy(struct script *script) + } + free(script->commands); + ++ for (groups = script->command_groups; *groups; groups++) ++ free(*groups); ++ free(script->command_groups); ++ + free(script); + } + +@@ -920,12 +946,15 @@ static struct script *script_create(struct timemaster_config *config) + struct source *source, **sources; + struct config_file *ntp_config = NULL; + int **allocated_phcs = (int **)parray_new(); +- int ret = 0, shm_segment; ++ int ret = 0, shm_segment, command_group = 0; + + script->configs = (struct config_file **)parray_new(); + script->commands = (char ***)parray_new(); ++ script->command_groups = (int **)parray_new(); ++ script->no_restart_group = command_group; ++ script->restart_groups = config->restart_processes; + +- ntp_config = add_ntp_program(config, script); ++ ntp_config = add_ntp_program(config, script, command_group++); + shm_segment = config->first_shm_segment; + + for (sources = config->sources; (source = *sources); sources++) { +@@ -936,7 +965,7 @@ static struct script *script_create(struct timemaster_config *config) + break; + case PTP_DOMAIN: + if (add_ptp_source(&source->ptp, config, &shm_segment, +- &allocated_phcs, ++ &command_group, &allocated_phcs, + &ntp_config->content, script)) + ret = 1; + break; +@@ -1063,10 +1092,11 @@ static int remove_config_files(struct config_file **configs) + + static int script_run(struct script *script) + { ++ struct timespec ts_start, ts_now; + sigset_t mask, old_mask; + siginfo_t info; + pid_t pid, *pids; +- int i, num_commands, status, ret = 0; ++ int i, group, num_commands, status, quit = 0, ret = 0; + + for (num_commands = 0; script->commands[num_commands]; num_commands++) + ; +@@ -1101,7 +1131,9 @@ static int script_run(struct script *script) + } + } + +- /* wait for one of the blocked signals */ ++ clock_gettime(CLOCK_MONOTONIC, &ts_start); ++ ++ /* process the blocked signals */ + while (1) { + if (sigwaitinfo(&mask, &info) < 0) { + if (errno == EINTR) +@@ -1110,36 +1142,111 @@ static int script_run(struct script *script) + break; + } + +- /* +- * assume only the first process (i.e. chronyd or ntpd) is +- * essential and continue if other processes terminate +- */ +- if (info.si_signo == SIGCHLD && info.si_pid != pids[0]) { +- pr_info("process %d terminated (ignored)", info.si_pid); ++ clock_gettime(CLOCK_MONOTONIC, &ts_now); ++ ++ if (info.si_signo != SIGCHLD) { ++ if (quit) ++ continue; ++ ++ quit = 1; ++ pr_debug("exiting on signal %d", info.si_signo); ++ ++ /* terminate remaining processes */ ++ for (i = 0; i < num_commands; i++) { ++ if (pids[i] > 0) { ++ pr_debug("killing process %d", pids[i]); ++ kill(pids[i], SIGTERM); ++ } ++ } ++ + continue; + } + +- pr_info("received signal %d", info.si_signo); +- break; +- } ++ /* wait for all terminated processes */ ++ while (1) { ++ pid = waitpid(-1, &status, WNOHANG); ++ if (pid <= 0) ++ break; + +- /* kill all started processes */ +- for (i = 0; i < num_commands; i++) { +- if (pids[i] > 0) { +- pr_debug("killing process %d", pids[i]); +- kill(pids[i], SIGTERM); ++ if (!WIFEXITED(status)) { ++ pr_info("process %d terminated abnormally", ++ pid); ++ } else { ++ pr_info("process %d terminated with status %d", ++ pid, WEXITSTATUS(status)); ++ } ++ ++ for (i = 0; i < num_commands; i++) { ++ if (pids[i] == pid) ++ pids[i] = 0; ++ } + } +- } + +- while ((pid = wait(&status)) >= 0) { +- if (!WIFEXITED(status)) { +- pr_info("process %d terminated abnormally", pid); +- ret = 1; +- } else { +- if (WEXITSTATUS(status)) ++ /* wait for all processes to terminate when exiting */ ++ if (quit) { ++ for (i = 0; i < num_commands; i++) { ++ if (pids[i]) ++ break; ++ } ++ if (i == num_commands) ++ break; ++ ++ pr_debug("waiting for other processes to terminate"); ++ continue; ++ } ++ ++ /* ++ * terminate (and then restart if allowed) all processes in ++ * groups that have a terminated process ++ */ ++ for (group = 0; group < num_commands; group++) { ++ int terminated = 0, running = 0; ++ ++ for (i = 0; i < num_commands; i++) { ++ if (*(script->command_groups[i]) != group) ++ continue; ++ if (pids[i]) ++ running++; ++ else ++ terminated++; ++ } ++ ++ if (!terminated) ++ continue; ++ ++ /* ++ * exit with a non-zero status if the group should not ++ * be restarted (i.e. chronyd/ntpd), timemaster is ++ * running only for a short time (and it is likely a ++ * configuration error), or restarting is disabled ++ * completely ++ */ ++ if (group == script->no_restart_group || ++ ts_now.tv_sec - ts_start.tv_sec <= 1 || ++ !script->restart_groups) { ++ kill(getpid(), SIGTERM); + ret = 1; +- pr_info("process %d terminated with status %d", pid, +- WEXITSTATUS(status)); ++ break; ++ } ++ ++ for (i = 0; i < num_commands; i++) { ++ if (*(script->command_groups[i]) != group) ++ continue; ++ ++ /* terminate all processes in the group first */ ++ if (running && pids[i]) { ++ pr_debug("killing process %d", pids[i]); ++ kill(pids[i], SIGTERM); ++ } else if (!running && !pids[i]) { ++ pids[i] = start_program(script->commands[i], ++ &old_mask); ++ if (!pids[i]) ++ kill(getpid(), SIGTERM); ++ ++ /* limit restarting rate */ ++ sleep(1); ++ } ++ } + } + } + +@@ -1154,6 +1261,7 @@ static int script_run(struct script *script) + static void script_print(struct script *script) + { + char ***commands, **command; ++ int **groups; + struct config_file *config, **configs; + + for (configs = script->configs; *configs; configs++) { +@@ -1162,7 +1270,9 @@ static void script_print(struct script *script) + } + + fprintf(stderr, "commands:\n\n"); +- for (commands = script->commands; *commands; commands++) { ++ for (commands = script->commands, groups = script->command_groups; ++ *commands; commands++, groups++) { ++ fprintf(stderr, "[%d] ", **groups); + for (command = *commands; *command; command++) + fprintf(stderr, "%s ", *command); + fprintf(stderr, "\n"); diff --git a/SOURCES/linuxptp-udsmgt.patch b/SOURCES/linuxptp-udsmgt.patch new file mode 100644 index 0000000..c42ca74 --- /dev/null +++ b/SOURCES/linuxptp-udsmgt.patch @@ -0,0 +1,37 @@ +commit 303b08cbf55096aba55bd08a314e0701e5c33482 +Author: Miroslav Lichvar +Date: Mon Dec 11 14:21:25 2017 +0100 + + clock: Don't forward management requests to UDS port. + + The UDS port is not expected to be used by PTP clocks and forwarding of + management messages to the port can be limited to responses. + + This prevents ptp4l from printing error messages when a management + request is received from a non-UDS port and the last client which used + the UDS port is no longer listening. + + Signed-off-by: Miroslav Lichvar + +diff --git a/clock.c b/clock.c +index 41c8f81..6dbc45e 100644 +--- a/clock.c ++++ b/clock.c +@@ -1243,6 +1243,17 @@ static int clock_do_forward_mgmt(struct clock *c, + { + if (in == out || !forwarding(c, out)) + return 0; ++ ++ /* Don't forward any requests to the UDS port. */ ++ if (out == c->uds_port) { ++ switch (management_action(msg)) { ++ case GET: ++ case SET: ++ case COMMAND: ++ return 0; ++ } ++ } ++ + if (!*pre_sent) { + /* delay calling msg_pre_send until + * actually forwarding */ diff --git a/SOURCES/phc2sys.service b/SOURCES/phc2sys.service index 78ed68a..ff2f77e 100644 --- a/SOURCES/phc2sys.service +++ b/SOURCES/phc2sys.service @@ -1,6 +1,6 @@ [Unit] Description=Synchronize system clock or PTP hardware clock (PHC) -After=ntpdate.service +After=ntpdate.service ptp4l.service [Service] Type=simple diff --git a/SOURCES/ptp4l.service b/SOURCES/ptp4l.service index 428394f..fbb26d1 100644 --- a/SOURCES/ptp4l.service +++ b/SOURCES/ptp4l.service @@ -1,5 +1,7 @@ [Unit] Description=Precision Time Protocol (PTP) service +After=network-online.target +Wants=network-online.target [Service] Type=simple diff --git a/SOURCES/timemaster.service b/SOURCES/timemaster.service index 7505387..a6bda33 100644 --- a/SOURCES/timemaster.service +++ b/SOURCES/timemaster.service @@ -1,7 +1,8 @@ [Unit] Description=Synchronize system clock to NTP and PTP time sources -After=chronyd.service ntpd.service ntpdate.service sntp.service +After=chronyd.service ntpd.service ntpdate.service sntp.service network-online.target Conflicts=chronyd.service ntpd.service phc2sys.service ptp4l.service +Wants=network-online.target [Service] Type=simple diff --git a/SPECS/linuxptp.spec b/SPECS/linuxptp.spec index 44ed08c..e03e295 100644 --- a/SPECS/linuxptp.spec +++ b/SPECS/linuxptp.spec @@ -3,7 +3,7 @@ %global clknetsim_ver ce89a1 Name: linuxptp Version: 1.8 -Release: 5%{?dist} +Release: 6%{?dist} Summary: PTP implementation for Linux Group: System Environment/Base @@ -42,6 +42,10 @@ Patch9: linuxptp-linkdown.patch Patch10: linuxptp-multiport.patch # add support for active-backup bonding Patch11: linuxptp-bonding.patch +# don't forward management requests to UDS port +Patch12: linuxptp-udsmgt.patch +# improve timemaster to restart terminated processes +Patch13: linuxptp-tmasterrestart.patch BuildRequires: systemd-units @@ -69,6 +73,8 @@ Supporting legacy APIs and other platforms is not a goal. %patch9 -p1 -b .linkdown %patch10 -p1 -b .multiport %patch11 -p1 -b .bonding +%patch12 -p1 -b .udsmgt +%patch13 -p1 -b .tmasterrestart mv linuxptp-testsuite-%{testsuite_ver}* testsuite mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim @@ -127,6 +133,12 @@ PATH=..:$PATH ./run %{_mandir}/man8/*.8* %changelog +* Wed May 30 2018 Miroslav Lichvar 1.8-6 +- add support for bonding to timemaster (#1549015) +- improve timemaster to restart terminated processes (#1527170) +- start ptp4l, timemaster and phc2sys after network-online target (#1541991) +- don't forward management requests to UDS port (#1520366) + * Tue Oct 24 2017 Miroslav Lichvar 1.8-5 - add support for active-backup bonding (#1002657) - add support for IP over InfiniBand (#1472880)