ipconfig: Write NTP server IPs to /proc/net/ipconfig/ntp_servers

Distributed filesystems are most effective when the server and client
clocks are synchronised. Embedded devices often use NFS for their
root filesystem but typically do not contain an RTC, so the clocks of
the NFS server and the embedded device will be out-of-sync when the root
filesystem is mounted (and may not be synchronised until late in the
boot process).

Extend ipconfig with the ability to export IP addresses of NTP servers
it discovers to /proc/net/ipconfig/ntp_servers. They can be supplied as
follows:

 - If ipconfig is configured manually via the "ip=" or "nfsaddrs="
   kernel command line parameters, one NTP server can be specified in
   the new "<ntp0-ip>" parameter.
 - If ipconfig is autoconfigured via DHCP, request DHCP option 42 in
   the DHCPDISCOVER message, and record the IP addresses of up to three
   NTP servers sent by the responding DHCP server in the subsequent
   DHCPOFFER message.

ipconfig will only write the NTP server IP addresses it discovers to
/proc/net/ipconfig/ntp_servers, one per line (in the order received from
the DHCP server, if DHCP autoconfiguration is used); making use of these
NTP servers is the responsibility of a user space process (e.g. an
initrd/initram script that invokes an NTP client before mounting an NFS
root filesystem).

Signed-off-by: Chris Novakovic <chris@chrisn.me.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Chris Novakovic 2018-04-24 03:56:39 +01:00 committed by David S. Miller
parent 4d019b3f80
commit c04d2cb200
2 changed files with 136 additions and 17 deletions

View File

@ -5,6 +5,7 @@ Written 1996 by Gero Kuhlmann <gero@gkminix.han.de>
Updated 1997 by Martin Mares <mj@atrey.karlin.mff.cuni.cz> Updated 1997 by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
Updated 2006 by Nico Schottelius <nico-kernel-nfsroot@schottelius.org> Updated 2006 by Nico Schottelius <nico-kernel-nfsroot@schottelius.org>
Updated 2006 by Horms <horms@verge.net.au> Updated 2006 by Horms <horms@verge.net.au>
Updated 2018 by Chris Novakovic <chris@chrisn.me.uk>
@ -79,7 +80,7 @@ nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>: ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:
<dns0-ip>:<dns1-ip> <dns0-ip>:<dns1-ip>:<ntp0-ip>
This parameter tells the kernel how to configure IP addresses of devices This parameter tells the kernel how to configure IP addresses of devices
and also how to set up the IP routing table. It was originally called and also how to set up the IP routing table. It was originally called
@ -178,9 +179,18 @@ ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:
<dns1-ip> IP address of secondary nameserver. <dns1-ip> IP address of secondary nameserver.
See <dns0-ip>. See <dns0-ip>.
After configuration (whether manual or automatic) is complete, a file is <ntp0-ip> IP address of a Network Time Protocol (NTP) server.
created at /proc/net/pnp in the following format; lines are omitted if Value is exported to /proc/net/ipconfig/ntp_servers, but is
their respective value is empty following configuration. otherwise unused (see below).
Default: None if not using autoconfiguration; determined
automatically if using autoconfiguration.
After configuration (whether manual or automatic) is complete, two files
are created in the following format; lines are omitted if their respective
value is empty following configuration:
- /proc/net/pnp:
#PROTO: <DHCP|BOOTP|RARP|MANUAL> (depending on configuration method) #PROTO: <DHCP|BOOTP|RARP|MANUAL> (depending on configuration method)
domain <dns-domain> (if autoconfigured, the DNS domain) domain <dns-domain> (if autoconfigured, the DNS domain)
@ -189,13 +199,26 @@ ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:
nameserver <dns2-ip> (tertiary name server IP) nameserver <dns2-ip> (tertiary name server IP)
bootserver <server-ip> (NFS server IP) bootserver <server-ip> (NFS server IP)
<dns-domain> and <dns2-ip> are requested during autoconfiguration; they - /proc/net/ipconfig/ntp_servers:
cannot be specified as part of the "ip=" kernel command line parameter.
<ntp0-ip> (NTP server IP)
<ntp1-ip> (NTP server IP)
<ntp2-ip> (NTP server IP)
<dns-domain> and <dns2-ip> (in /proc/net/pnp) and <ntp1-ip> and <ntp2-ip>
(in /proc/net/ipconfig/ntp_servers) are requested during autoconfiguration;
they cannot be specified as part of the "ip=" kernel command line parameter.
Because the "domain" and "nameserver" options are recognised by DNS Because the "domain" and "nameserver" options are recognised by DNS
resolvers, /etc/resolv.conf is often linked to /proc/net/pnp on systems resolvers, /etc/resolv.conf is often linked to /proc/net/pnp on systems
that use an NFS root filesystem. that use an NFS root filesystem.
Note that the kernel will not synchronise the system time with any NTP
servers it discovers; this is the responsibility of a user space process
(e.g. an initrd/initramfs script that passes the IP addresses listed in
/proc/net/ipconfig/ntp_servers to an NTP client before mounting the real
root filesystem if it is on NFS).
nfsrootdebug nfsrootdebug

View File

@ -28,6 +28,9 @@
* *
* Multiple Nameservers in /proc/net/pnp * Multiple Nameservers in /proc/net/pnp
* -- Josef Siemes <jsiemes@web.de>, Aug 2002 * -- Josef Siemes <jsiemes@web.de>, Aug 2002
*
* NTP servers in /proc/net/ipconfig/ntp_servers
* -- Chris Novakovic <chris@chrisn.me.uk>, April 2018
*/ */
#include <linux/types.h> #include <linux/types.h>
@ -93,6 +96,7 @@
#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */ #define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
#define CONF_NAMESERVERS_MAX 3 /* Maximum number of nameservers #define CONF_NAMESERVERS_MAX 3 /* Maximum number of nameservers
- '3' from resolv.h */ - '3' from resolv.h */
#define CONF_NTP_SERVERS_MAX 3 /* Maximum number of NTP servers */
#define NONE cpu_to_be32(INADDR_NONE) #define NONE cpu_to_be32(INADDR_NONE)
#define ANY cpu_to_be32(INADDR_ANY) #define ANY cpu_to_be32(INADDR_ANY)
@ -152,6 +156,7 @@ static int ic_proto_used; /* Protocol used, if any */
#define ic_proto_used 0 #define ic_proto_used 0
#endif #endif
static __be32 ic_nameservers[CONF_NAMESERVERS_MAX]; /* DNS Server IP addresses */ static __be32 ic_nameservers[CONF_NAMESERVERS_MAX]; /* DNS Server IP addresses */
static __be32 ic_ntp_servers[CONF_NTP_SERVERS_MAX]; /* NTP server IP addresses */
static u8 ic_domain[64]; /* DNS (not NIS) domain name */ static u8 ic_domain[64]; /* DNS (not NIS) domain name */
/* /*
@ -579,6 +584,15 @@ static inline void __init ic_nameservers_predef(void)
ic_nameservers[i] = NONE; ic_nameservers[i] = NONE;
} }
/* Predefine NTP servers */
static inline void __init ic_ntp_servers_predef(void)
{
int i;
for (i = 0; i < CONF_NTP_SERVERS_MAX; i++)
ic_ntp_servers[i] = NONE;
}
/* /*
* DHCP/BOOTP support. * DHCP/BOOTP support.
*/ */
@ -674,6 +688,7 @@ ic_dhcp_init_options(u8 *options, struct ic_device *d)
17, /* Boot path */ 17, /* Boot path */
26, /* MTU */ 26, /* MTU */
40, /* NIS domain name */ 40, /* NIS domain name */
42, /* NTP servers */
}; };
*e++ = 55; /* Parameter request list */ *e++ = 55; /* Parameter request list */
@ -753,12 +768,13 @@ static void __init ic_bootp_init_ext(u8 *e)
*/ */
static inline void __init ic_bootp_init(void) static inline void __init ic_bootp_init(void)
{ {
/* Re-initialise all name servers to NONE, in case any were set via the /* Re-initialise all name servers and NTP servers to NONE, in case any
* "ip=" or "nfsaddrs=" kernel command line parameters: any IP addresses * were set via the "ip=" or "nfsaddrs=" kernel command line parameters:
* specified there will already have been decoded but are no longer * any IP addresses specified there will already have been decoded but
* needed * are no longer needed
*/ */
ic_nameservers_predef(); ic_nameservers_predef();
ic_ntp_servers_predef();
dev_add_pack(&bootp_packet_type); dev_add_pack(&bootp_packet_type);
} }
@ -922,6 +938,15 @@ static void __init ic_do_bootp_ext(u8 *ext)
ic_bootp_string(utsname()->domainname, ext+1, *ext, ic_bootp_string(utsname()->domainname, ext+1, *ext,
__NEW_UTS_LEN); __NEW_UTS_LEN);
break; break;
case 42: /* NTP servers */
servers = *ext / 4;
if (servers > CONF_NTP_SERVERS_MAX)
servers = CONF_NTP_SERVERS_MAX;
for (i = 0; i < servers; i++) {
if (ic_ntp_servers[i] == NONE)
memcpy(&ic_ntp_servers[i], ext+1+4*i, 4);
}
break;
} }
} }
@ -1268,6 +1293,7 @@ static int __init ic_dynamic(void)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
/* Name servers: */
static int pnp_seq_show(struct seq_file *seq, void *v) static int pnp_seq_show(struct seq_file *seq, void *v)
{ {
int i; int i;
@ -1306,7 +1332,7 @@ static const struct file_operations pnp_seq_fops = {
}; };
/* Create the /proc/net/ipconfig directory */ /* Create the /proc/net/ipconfig directory */
static int ipconfig_proc_net_init(void) static int __init ipconfig_proc_net_init(void)
{ {
ipconfig_dir = proc_net_mkdir(&init_net, "ipconfig", init_net.proc_net); ipconfig_dir = proc_net_mkdir(&init_net, "ipconfig", init_net.proc_net);
if (!ipconfig_dir) if (!ipconfig_dir)
@ -1314,6 +1340,52 @@ static int ipconfig_proc_net_init(void)
return 0; return 0;
} }
/* Create a new file under /proc/net/ipconfig */
static int ipconfig_proc_net_create(const char *name,
const struct file_operations *fops)
{
char *pname;
struct proc_dir_entry *p;
if (!ipconfig_dir)
return -ENOMEM;
pname = kasprintf(GFP_KERNEL, "%s%s", "ipconfig/", name);
if (!pname)
return -ENOMEM;
p = proc_create(pname, 0444, init_net.proc_net, fops);
kfree(pname);
if (!p)
return -ENOMEM;
return 0;
}
/* Write NTP server IP addresses to /proc/net/ipconfig/ntp_servers */
static int ntp_servers_seq_show(struct seq_file *seq, void *v)
{
int i;
for (i = 0; i < CONF_NTP_SERVERS_MAX; i++) {
if (ic_ntp_servers[i] != NONE)
seq_printf(seq, "%pI4\n", &ic_ntp_servers[i]);
}
return 0;
}
static int ntp_servers_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, ntp_servers_seq_show, NULL);
}
static const struct file_operations ntp_servers_seq_fops = {
.open = ntp_servers_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
/* /*
@ -1388,17 +1460,20 @@ static int __init ip_auto_config(void)
int err; int err;
unsigned int i; unsigned int i;
/* Initialise all name servers to NONE (but only if the "ip=" or /* Initialise all name servers and NTP servers to NONE (but only if the
* "nfsaddrs=" kernel command line parameters weren't decoded, otherwise * "ip=" or "nfsaddrs=" kernel command line parameters weren't decoded,
* we'll overwrite the IP addresses specified there) * otherwise we'll overwrite the IP addresses specified there)
*/ */
if (ic_set_manually == 0) if (ic_set_manually == 0) {
ic_nameservers_predef(); ic_nameservers_predef();
ic_ntp_servers_predef();
}
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_create("pnp", 0444, init_net.proc_net, &pnp_seq_fops); proc_create("pnp", 0444, init_net.proc_net, &pnp_seq_fops);
ipconfig_proc_net_init(); if (ipconfig_proc_net_init() == 0)
ipconfig_proc_net_create("ntp_servers", &ntp_servers_seq_fops);
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
if (!ic_enable) if (!ic_enable)
@ -1523,6 +1598,19 @@ static int __init ip_auto_config(void)
if (i + 1 == CONF_NAMESERVERS_MAX) if (i + 1 == CONF_NAMESERVERS_MAX)
pr_cont("\n"); pr_cont("\n");
} }
/* NTP servers (if any): */
for (i = 0; i < CONF_NTP_SERVERS_MAX; i++) {
if (ic_ntp_servers[i] != NONE) {
if (i == 0)
pr_info(" ntpserver%u=%pI4",
i, &ic_ntp_servers[i]);
else
pr_cont(", ntpserver%u=%pI4",
i, &ic_ntp_servers[i]);
}
if (i + 1 == CONF_NTP_SERVERS_MAX)
pr_cont("\n");
}
#endif /* !SILENT */ #endif /* !SILENT */
/* /*
@ -1620,8 +1708,9 @@ static int __init ip_auto_config_setup(char *addrs)
return 1; return 1;
} }
/* Initialise all name servers to NONE */ /* Initialise all name servers and NTP servers to NONE */
ic_nameservers_predef(); ic_nameservers_predef();
ic_ntp_servers_predef();
/* Parse string for static IP assignment. */ /* Parse string for static IP assignment. */
ip = addrs; ip = addrs;
@ -1680,6 +1769,13 @@ static int __init ip_auto_config_setup(char *addrs)
ic_nameservers[1] = NONE; ic_nameservers[1] = NONE;
} }
break; break;
case 9:
if (CONF_NTP_SERVERS_MAX >= 1) {
ic_ntp_servers[0] = in_aton(ip);
if (ic_ntp_servers[0] == ANY)
ic_ntp_servers[0] = NONE;
}
break;
} }
} }
ip = cp; ip = cp;