From: Paul Mackerras Date: Wed, 11 Oct 2023 00:44:36 +0000 (+1100) Subject: Merge pull request #367 from jkroonza/consolidated-ifup-down-and-scripts X-Git-Url: http://git.ozlabs.org/?a=commitdiff_plain;h=1480d439ca62d17d7a8a4704194860148dc27689;hp=39063981f7605a994851eec4a7acae86b2327983;p=ppp.git Merge pull request #367 from jkroonza/consolidated-ifup-down-and-scripts Implement net-init, net-pre-up and net-down scripts in pppd. --- diff --git a/pppd/Makefile.am b/pppd/Makefile.am index e5bedf2..c5fe107 100644 --- a/pppd/Makefile.am +++ b/pppd/Makefile.am @@ -20,6 +20,12 @@ utest_pppcrypt_LDFLAGS = check_PROGRAMS += utest_crypto +utest_utils_SOURCES = utils.c utils_utest.c +utest_utils_CPPFLAGS = -DUNIT_TEST +utest_utils_LDFLAGS = + +check_PROGRAMS += utest_utils + if WITH_SRP sbin_PROGRAMS += srp-entry dist_man8_MANS += srp-entry.8 @@ -83,7 +89,7 @@ pppd_SOURCES = \ upap.c \ utils.c -pppd_CPPFLAGS = -DSYSCONFDIR=\"${sysconfdir}\" -DLOCALSTATEDIR=\"${localstatedir}\" -DPPPD_RUNTIME_DIR='"@PPPD_RUNTIME_DIR@"' -DPPPD_LOGFILE_DIR='"@PPPD_LOGFILE_DIR@"' +pppd_CPPFLAGS = -DSYSCONFDIR=\"${sysconfdir}\" -DPPPD_RUNTIME_DIR='"@PPPD_RUNTIME_DIR@"' -DPPPD_LOGFILE_DIR='"@PPPD_LOGFILE_DIR@"' pppd_LDFLAGS = pppd_LIBS = diff --git a/pppd/lcp.c b/pppd/lcp.c index c72c2ef..d174b8f 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -47,6 +47,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "pppd-private.h" #include "options.h" @@ -67,12 +72,22 @@ static void lcp_delayed_up(void *); +/* + * These definitions relate to the measurement and logging of round-trip + * time (RTT) of LCP echo-requests implemented in lcp_rtt_update_buffer(). + */ +#define LCP_RTT_MAGIC 0x19450425 +#define LCP_RTT_HEADER_LENGTH 4 +#define LCP_RTT_FILE_SIZE 8192 +#define LCP_RTT_ELEMENTS (LCP_RTT_FILE_SIZE / sizeof(u_int32_t) - LCP_RTT_HEADER_LENGTH) / 2 + /* * LCP-related command-line options. */ int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ bool lcp_echo_adaptive = 0; /* request echo only if the link was idle */ +char *lcp_rtt_file = NULL; /* measure the RTT of LCP echo-requests */ bool lax_recv = 0; /* accept control chars in asyncmap */ bool noendpoint = 0; /* don't send/accept endpoint discriminator */ @@ -152,6 +167,9 @@ static struct option lcp_option_list[] = { "Set time in seconds between LCP echo requests", OPT_PRIO }, { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive, "Suppress LCP echo requests if traffic was received", 1 }, + { "lcp-rtt-file", o_string, &lcp_rtt_file, + "Filename for logging the round-trip time of LCP echo requests", + OPT_PRIO | OPT_PRIV }, { "lcp-restart", o_int, &lcp_fsm[0].timeouttime, "Set time in seconds between LCP retransmissions", OPT_PRIO }, { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits, @@ -197,6 +215,8 @@ lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ static int lcp_echos_pending = 0; /* Number of outstanding echo msgs */ static int lcp_echo_number = 0; /* ID number of next echo frame */ static int lcp_echo_timer_running = 0; /* set if a timer is running */ +static int lcp_rtt_file_fd = 0; /* fd for the opened LCP RTT file */ +static volatile u_int32_t *lcp_rtt_buffer = NULL; /* the mmap'ed LCP RTT file */ static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ @@ -2225,6 +2245,72 @@ LcpEchoTimeout (void *arg) } } +/* + * Log the round-trip time (RTT) of the received LCP echo-request. + * + * The header section at the beginning of lcp_rtt_file contains + * LCP_RTT_HEADER_LENGTH fields, each a u_int32_t in network byte order: + * [0] LCP_RTT_MAGIC + * [1] status (1: the file is open and is being written) + * [2] index of the most recently updated element + * [3] the value of the lcp-echo-interval parameter + * + * The header is followed by a ring buffer of LCP_RTT_ELEMENTS elements, each + * containing a pair of u_int32_t in network byte order with this content: + * [0] UNIX timestamp + * [1] bits 24-31: the number of lost LCP echo replies + * bits 0-23: the measured RTT in microseconds + * + * The timestamp is unsigned to support storing dates beyond 2038. + * + * Consumers of lcp_rtt_file are expected to: + * - read the complete file of arbitrary length + * - check the magic number + * - process the data elements starting at the index + * - ignore any elements with a timestamp of 0 + */ +static void +lcp_rtt_update_buffer (unsigned long rtt) +{ + volatile u_int32_t *const ring_buffer = lcp_rtt_buffer + + LCP_RTT_HEADER_LENGTH; + unsigned int next_entry, lost; + + /* choose the next entry where the data will be stored */ + if (ntohl(lcp_rtt_buffer[2]) >= (LCP_RTT_ELEMENTS - 1) * 2) + next_entry = 0; /* go back to the beginning */ + else + next_entry = ntohl(lcp_rtt_buffer[2]) + 2; /* use the next one */ + + /* update the data element */ + /* storing the timestamp in an *unsigned* long allows dates up to 2106 */ + ring_buffer[next_entry] = htonl((u_int32_t) time(NULL)); + lost = lcp_echos_pending - 1; + if (lost > 0xFF) + lost = 0xFF; /* truncate the lost packets count to 256 */ + if (rtt > 0xFFFFFF) + rtt = 0xFFFFFF; /* truncate the RTT to 16777216 */ + /* use bits 24-31 for the lost packets count and bits 0-23 for the RTT */ + ring_buffer[next_entry + 1] = htonl((u_int32_t) ((lost << 24) + rtt)); + + /* update the pointer to the (just updated) most current data element */ + lcp_rtt_buffer[2] = htonl(next_entry); + + /* In theory, CPUs implementing a weakly-consistent memory model do not + * guarantee that these three memory store operations to the buffer will + * be seen in the same order by the reader process. + * This means that a process reading the file could see the index + * having been updated before the element that the index points to had + * been written. + * But in practice we expect that the read(2) system call used by + * consumers processes is atomic with respect to the following msync(2) + * call, so we ignore the issue. + */ + + if (msync(lcp_rtt_buffer, LCP_RTT_FILE_SIZE, MS_ASYNC) < 0) + error("msync() for %s failed: %m", lcp_rtt_file); +} + /* * LcpEchoReply - LCP has received a reply to the echo */ @@ -2246,6 +2332,30 @@ lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) return; } + if (lcp_rtt_file_fd && len >= 16) { + long lcp_rtt_magic; + + /* + * If the magic word is found at the beginning of the data section + * of the frame then read the timestamp which follows and subtract + * it from the current time to compute the round trip time. + */ + GETLONG(lcp_rtt_magic, inp); + if (lcp_rtt_magic == LCP_RTT_MAGIC) { + struct timespec ts; + unsigned long req_sec, req_nsec, rtt; + + clock_gettime(CLOCK_MONOTONIC, &ts); + GETLONG(req_sec, inp); + GETLONG(req_nsec, inp); + /* compute the RTT in microseconds */ + rtt = (ts.tv_sec - req_sec) * 1000000 + + (ts.tv_nsec / 1000 - req_nsec / 1000); + /* log the RTT */ + lcp_rtt_update_buffer(rtt); + } + } + /* Reset the number of outstanding echo frames */ lcp_echos_pending = 0; } @@ -2258,7 +2368,7 @@ static void LcpSendEchoRequest (fsm *f) { u_int32_t lcp_magic; - u_char pkt[4], *pktp; + u_char pkt[16], *pktp; /* * Detect the failure of the peer at this point. @@ -2293,11 +2403,68 @@ LcpSendEchoRequest (fsm *f) lcp_magic = lcp_gotoptions[f->unit].magicnumber; pktp = pkt; PUTLONG(lcp_magic, pktp); + + /* Put a timestamp in the data section of the frame */ + if (lcp_rtt_file_fd) { + struct timespec ts; + + PUTLONG(LCP_RTT_MAGIC, pktp); + clock_gettime(CLOCK_MONOTONIC, &ts); + PUTLONG((u_int32_t)ts.tv_sec, pktp); + PUTLONG((u_int32_t)ts.tv_nsec, pktp); + } + fsm_sdata(f, ECHOREQ, lcp_echo_number++ & 0xFF, pkt, pktp - pkt); ++lcp_echos_pending; } } +static void +lcp_rtt_open_file (void) +{ + if (!lcp_rtt_file) + return; + + lcp_rtt_file_fd = open(lcp_rtt_file, O_RDWR | O_CREAT, 0644); + if (lcp_rtt_file_fd < 0) { + error("Can't open the RTT log file %s: %m", lcp_rtt_file); + lcp_rtt_file_fd = 0; + return; + } + + if (ftruncate(lcp_rtt_file_fd, LCP_RTT_FILE_SIZE) < 0) + fatal("ftruncate() of %s failed: %m", lcp_rtt_file); + lcp_rtt_buffer = mmap(0, LCP_RTT_FILE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, lcp_rtt_file_fd, 0); + if (lcp_rtt_buffer == MAP_FAILED) + fatal("mmap() of %s failed: %m", lcp_rtt_file); + + /* initialize the ring buffer */ + if (lcp_rtt_buffer[0] != htonl(LCP_RTT_MAGIC)) { + memset(lcp_rtt_buffer, 0, LCP_RTT_FILE_SIZE); + lcp_rtt_buffer[0] = htonl(LCP_RTT_MAGIC); + } + + lcp_rtt_buffer[3] = htonl(lcp_echo_interval); + lcp_rtt_buffer[1] = htonl(1); /* status: LCP up, file opened */ +} + +static void +lcp_rtt_close_file (void) +{ + if (!lcp_rtt_file_fd) + return; + + lcp_rtt_buffer[1] = htonl(0); /* status: LCP down, file closed */ + + if (munmap(lcp_rtt_buffer, LCP_RTT_FILE_SIZE) < 0) + error("munmap() of %s failed: %m", lcp_rtt_file); + if (close(lcp_rtt_file_fd) < 0) + error("close() of %s failed: %m", lcp_rtt_file); + lcp_rtt_buffer = NULL; + lcp_rtt_file_fd = 0; +} + /* * lcp_echo_lowerup - Start the timer for the LCP frame */ @@ -2311,6 +2478,9 @@ lcp_echo_lowerup (int unit) lcp_echos_pending = 0; lcp_echo_number = 0; lcp_echo_timer_running = 0; + + /* Open the file where the LCP RTT data will be logged */ + lcp_rtt_open_file(); /* If a timeout interval is specified then start the timer */ if (lcp_echo_interval != 0) @@ -2330,4 +2500,7 @@ lcp_echo_lowerdown (int unit) UNTIMEOUT (LcpEchoTimeout, f); lcp_echo_timer_running = 0; } + + /* Close the file containing the LCP RTT data */ + lcp_rtt_close_file(); } diff --git a/pppd/pathnames.h b/pppd/pathnames.h index 439c6c8..e50132f 100644 --- a/pppd/pathnames.h +++ b/pppd/pathnames.h @@ -124,12 +124,12 @@ #define PPP_PATH_PPPDB PPP_PATH_VARRUN "/pppd2.tdb" #ifdef __linux__ -#define PPP_PATH_LOCKDIR PPP_PATH_VARRUN "/lock" +#define PPP_PATH_LOCKDIR "/var/lock" #else #ifdef SVR4 -#define PPP_PATH_LOCKDIR LOCALSTATEDIR "/spool/locks" +#define PPP_PATH_LOCKDIR "/var/spool/locks" #else -#define PPP_PATH_LOCKDIR LOCALSTATEDIR "/spool/lock" +#define PPP_PATH_LOCKDIR "/var/spool/lock" #endif #endif diff --git a/pppd/plugins/pppoe/if.c b/pppd/plugins/pppoe/if.c index 87e5f6e..4b13ecc 100644 --- a/pppd/plugins/pppoe/if.c +++ b/pppd/plugins/pppoe/if.c @@ -175,7 +175,7 @@ openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) sa.sll_ifindex = ifr.ifr_ifindex; #else - strcpy(sa.sa_data, ifname); + strlcpy(sa.sa_data, ifname, sizeof(sa.sa_data)); #endif /* We're only interested in packets on specified interface */ @@ -212,7 +212,7 @@ sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) #else struct sockaddr sa; - strcpy(sa.sa_data, conn->ifName); + strlcpy(sa.sa_data, conn->ifName, sizeof(sa.sa_data)); err = sendto(sock, pkt, size, 0, &sa, sizeof(sa)); #endif if (err < 0) { diff --git a/pppd/plugins/pppoe/pppoe-discovery.8 b/pppd/plugins/pppoe/pppoe-discovery.8 index 523ef8c..3ea62d1 100644 --- a/pppd/plugins/pppoe/pppoe-discovery.8 +++ b/pppd/plugins/pppoe/pppoe-discovery.8 @@ -83,4 +83,4 @@ version number and usage information, then exit. \fBpppoe\-discovery\fR was written by Marco d'Itri , based on \fBpppoe\fR by Dianne Skoll . .SH SEE ALSO -pppoe(8), pppoe-sniff(8) +pppoe(8), pppoe\-sniff(8) diff --git a/pppd/plugins/radius/pppd-radattr.8 b/pppd/plugins/radius/pppd-radattr.8 index bcaad3b..1346fdd 100644 --- a/pppd/plugins/radius/pppd-radattr.8 +++ b/pppd/plugins/radius/pppd-radattr.8 @@ -24,7 +24,7 @@ where .I pppN is the name of the PPP interface. The RADIUS attributes are stored one per line in the format "Attribute-Name Attribute-Value". This -format is convenient for use in /etc/ppp/ip-up and /etc/ppp/ip-down +format is convenient for use in /etc/ppp/ip\-up and /etc/ppp/ip\-down scripts. .LP Note that you @@ -38,7 +38,7 @@ To use the plugin, simply supply the options to pppd. .SH SEE ALSO -.BR pppd (8) " pppd-radius" (8) +.BR pppd (8) " pppd\-radius" (8) .SH AUTHOR Dianne Skoll diff --git a/pppd/plugins/radius/pppd-radius.8 b/pppd/plugins/radius/pppd-radius.8 index 08b97fb..8628d08 100644 --- a/pppd/plugins/radius/pppd-radius.8 +++ b/pppd/plugins/radius/pppd-radius.8 @@ -19,9 +19,9 @@ plugin radius.so .LP The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, MS-CHAP and MS-CHAPv2 authentication against a RADIUS server instead of the usual -.I /etc/ppp/pap-secrets +.I /etc/ppp/pap\-secrets and -.I /etc/ppp/chap-secrets +.I /etc/ppp/chap\-secrets files. .LP The RADIUS plugin is built on a library called @@ -33,7 +33,7 @@ plugin .SH OPTIONS The RADIUS plugin introduces one additional pppd option: .TP -.BI "radius-config-file " filename +.BI "radius\-config\-file " filename The file .I filename is taken as the radiusclient configuration file. If this option is not @@ -44,10 +44,10 @@ as the configuration file. .BI "avpair " attribute=value Adds an Attribute-Value pair to be passed on to the RADIUS server on each request. .TP -.BI map-to-ifname +.BI map\-to\-ifname Sets Radius NAS-Port attribute to number equal to interface name (Default) .TP -.BI map-to-ttyname +.BI map\-to\-ttyname Sets Radius NAS-Port attribute value via libradiusclient library .SH USAGE @@ -61,7 +61,7 @@ RADIUS server should assign an IP address to the peer using the RADIUS Framed-IP-Address attribute. .SH SEE ALSO -.BR pppd (8) " pppd-radattr" (8) +.BR pppd (8) " pppd\-radattr" (8) .SH AUTHOR Dianne Skoll diff --git a/pppd/pppd-private.h b/pppd/pppd-private.h index 2883e46..46ce0c8 100644 --- a/pppd/pppd-private.h +++ b/pppd/pppd-private.h @@ -437,6 +437,7 @@ int sifproxyarp(int, u_int32_t); int cifproxyarp(int, u_int32_t); /* Delete proxy ARP entry for peer */ u_int32_t GetMask(u_int32_t); /* Get appropriate netmask for address */ +int mkdir_recursive(const char *); /* Recursively create directory */ int lock(char *); /* Create lock file for device */ int relock(int); /* Rewrite lock file with new pid */ void unlock(void); /* Delete previously-created lock file */ diff --git a/pppd/pppd.8 b/pppd/pppd.8 index f66ca89..7be3e80 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -637,6 +637,10 @@ Set the maximum number of LCP terminate-request transmissions to Set the LCP restart interval (retransmission timeout) to \fIn\fR seconds (default 3). .TP +.B lcp\-rtt\-file \fIfilename +Sets the file where the round-trip time (RTT) of LCP echo-request frames +will be logged. +.TP .B linkname \fIname\fR Sets the logical name of the link to \fIname\fR. Pppd will create a file named \fBppp\-\fIname\fB.pid\fR in /var/run (or /etc/ppp on some @@ -687,7 +691,7 @@ network control protocol comes up). Terminate after \fIn\fR consecutive failed connection attempts. A value of 0 means no limit. The default value is 10. .TP -.B max-tls-version \fIstring +.B max\-tls-\version \fIstring (EAP-TLS, or PEAP) Configures the max allowed TLS version used during negotiation with a peer. The default value for this is \fI1.2\fR. Values allowed for this option is \fI1.0.\fR, \fI1.1\fR, \fI1.2\fR, \fI1.3\fR. @@ -760,7 +764,7 @@ name to \fIname\fR.) Disable Address/Control compression in both directions (send and receive). .TP -.B need-peer-eap +.B need\-peer\-eap (EAP-TLS) Require the peer to verify our authentication credentials. .TP .B noauth @@ -1136,13 +1140,13 @@ The device used by pppd with this option must have sync support. Currently supports Microgate SyncLink adapters under Linux and FreeBSD 2.2.8 and later. .TP -.B tls-verify-method \fIstring +.B tls\-verify\-method \fIstring (EAP-TLS, or PEAP) Match the value specified for \fIremotename\fR to that that of the X509 certificates subject name, common name, or suffix of the common name. Respective values allowed for this option is: \fInone\fR, \fIsubject\fR, \fIname\fR, or \fIsuffix\fR. The default value for this option is \fIname\fR. .TP -.B tls-verify-key-usage +.B tls\-verify\-key\-usage (EAP-TLS, or PEAP) Enables examination of peer certificate's purpose, and extended key usage attributes. .TP @@ -1233,36 +1237,36 @@ by specifying ppp'd option \fBnic-eth0\fR. Prefix \fBnic-\fR for this option may be avoided if interface name is unambiguous and does not look like any other pppd's option. .TP -.B pppoe-service \fIname +.B pppoe\-service \fIname Connect to specified PPPoE service name. For backward compatibility also \fBrp_pppoe_service\fP option name is supported. .TP -.B pppoe-ac \fIname +.B pppoe\-ac \fIname Connect to specified PPPoE access concentrator name. For backward compatibility also \fBrp_pppoe_ac\fP option name is supported. .TP -.B pppoe-sess \fIsessid\fP:\fImacaddr +.B pppoe\-sess \fIsessid\fP:\fImacaddr Attach to existing PPPoE session. For backward compatibility also \fBrp_pppoe_sess\fP option name is supported. .TP -.B pppoe-verbose \fIn +.B pppoe\-verbose \fIn Be verbose about discovered access concentrators. When set to 2 or bigger value then dump also discovery packets. For backward compatibility also \fBrp_pppoe_verbose\fP option name is supported. .TP -.B pppoe-mac \fImacaddr +.B pppoe\-mac \fImacaddr Connect to specified MAC address. .TP -.B pppoe-host-uniq \fIstring +.B pppoe\-host\-uniq \fIstring Set the PPPoE Host-Uniq tag to the supplied hex string. By default PPPoE Host-Uniq tag is set to the pppd's process PID. For backward compatibility this option may be specified without \fBpppoe-\fP prefix. .TP -.B pppoe-padi-timeout \fIn +.B pppoe\-padi\-timeout \fIn Initial timeout for discovery packets in seconds (default 5). .TP -.B pppoe-padi-attempts \fIn +.B pppoe\-padi\-attempts \fIn Number of discovery attempts (default 3). .SH OPTIONS FILES Options can be taken from files as well as the command line. Pppd @@ -1729,8 +1733,8 @@ We failed to authenticate ourselves to the peer. Pppd invokes scripts at various stages in its processing which can be used to perform site-specific ancillary processing. These scripts are usually shell scripts, but could be executable code files instead. -Pppd does not wait for the scripts to finish (except for the net-init, -net-pre-up and ip-pre-up scripts). The scripts are +Pppd does not wait for the scripts to finish (except for the net\-init, +net\-pre\-up and ip\-pre\-up scripts). The scripts are executed as root (with the real and effective user-id set to 0), so that they can do things such as update routing tables or run privileged daemons. Be careful that the contents of these scripts do @@ -2104,7 +2108,7 @@ are met: .br (412) 268-4387, fax: (412) 268-7395 .br - tech-transfer@andrew.cmu.edu + tech\-transfer@andrew.cmu.edu .LP 3b. The name(s) of the authors of this software must not be used to endorse or promote products derived from this software without diff --git a/pppd/tdb.c b/pppd/tdb.c index 8a563f8..6264417 100644 --- a/pppd/tdb.c +++ b/pppd/tdb.c @@ -60,8 +60,11 @@ #include #include #include + +#include "pppd-private.h" #include "tdb.h" #include "spinlock.h" +#include "pathnames.h" #define TDB_MAGIC_FOOD "TDB file\n" #define TDB_VERSION (0x26011967 + 6) @@ -1728,7 +1731,12 @@ TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, goto internal; } +again: if ((tdb->fd = open(name, open_flags, mode)) == -1) { + if ((open_flags & O_CREAT) && errno == ENOENT && + mkdir_recursive(PPP_PATH_VARRUN) == 0) + goto again; + TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n", name, strerror(errno))); goto fail; /* errno set by open(2) */ diff --git a/pppd/utils.c b/pppd/utils.c index c1bdbbb..c47192e 100644 --- a/pppd/utils.c +++ b/pppd/utils.c @@ -781,6 +781,88 @@ complete_read(int fd, void *buf, size_t count) } #endif +/* + * mkdir_check - helper for mkdir_recursive, creates a directory + * but do not error on EEXIST if and only if entry is a directory + * The caller must check for errno == ENOENT if appropriate. + */ +static int +mkdir_check(const char *path) +{ + struct stat statbuf; + + if (mkdir(path, 0755) >= 0) + return 0; + + if (errno == EEXIST) { + if (stat(path, &statbuf) < 0) + /* got raced? */ + return -1; + + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + return 0; + + /* already exists but not a dir, treat as failure */ + errno = EEXIST; + return -1; + } + + return -1; +} + +/* + * mkdir_parent - helper for mkdir_recursive, modifies the string in place + * Assumes mkdir(path) already failed, so it first creates the parent then + * full path again. + */ +static int +mkdir_parent(char *path) +{ + char *slash; + int rc; + + slash = strrchr(path, '/'); + if (!slash) + return -1; + + *slash = 0; + if (mkdir_check(path) < 0) { + if (errno != ENOENT) { + *slash = '/'; + return -1; + } + if (mkdir_parent(path) < 0) { + *slash = '/'; + return -1; + } + } + *slash = '/'; + + return mkdir_check(path); +} + +/* + * mkdir_recursive - recursively create directory if it didn't exist + */ +int +mkdir_recursive(const char *path) +{ + char *copy; + int rc; + + // optimistically try on full path first to avoid allocation + if (mkdir_check(path) == 0) + return 0; + + copy = strdup(path); + if (!copy) + return -1; + + rc = mkdir_parent(copy); + free(copy); + return rc; +} + /* Procedures for locking the serial device using a lock file. */ static char lock_file[MAXPATHLEN]; diff --git a/pppd/utils_utest.c b/pppd/utils_utest.c new file mode 100644 index 0000000..cdca97e --- /dev/null +++ b/pppd/utils_utest.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include + +#include "pppd-private.h" + +/* globals used in test.c... */ +int debug = 1; +int error_count; +int unsuccess; + +/* check if path exists and returns its type */ +static int +file_type(char *path) +{ + struct stat statbuf; + + if (stat(path, &statbuf) < 0) + return -1; + + return statbuf.st_mode & S_IFMT; +} + +int +test_simple() { + if (mkdir_recursive("dir")) + return -1; + + if (file_type("dir") != S_IFDIR) + return -1; + + rmdir("dir"); + return 0; +} + +int +test_recurse() { + if (mkdir_recursive("dir/subdir/subsubdir")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + + /* try again with partial existence */ + if (mkdir_recursive("dir/subdir/subsubdir")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + rmdir("dir/subdir"); + rmdir("dir"); + return 0; +} + +int +test_recurse_multislash() { + if (mkdir_recursive("dir/subdir///subsubdir")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + rmdir("dir/subdir"); + + /* try again with partial existence */ + if (mkdir_recursive("dir/subdir/subsubdir///")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + rmdir("dir/subdir"); + rmdir("dir"); + return 0; +} + +int +test_parent_notdir() { + int fd = open("file", O_CREAT, 0600); + if (fd < 0) + return -1; + close(fd); + + if (mkdir_recursive("file") == 0) + return -1; + if (mkdir_recursive("file/dir") == 0) + return -1; + + unlink("file"); + return 0; +} + +int +main() +{ + char *base_dir = strdup("/tmp/ppp_utils_utest.XXXXXX"); + int failure = 0; + + if (mkdtemp(base_dir) == NULL) { + printf("Could not create test directory, aborting\n"); + return 1; + } + + if (chdir(base_dir) < 0) { + printf("Could not enter newly created test dir, aborting\n"); + return 1; + } + + if (test_simple()) { + printf("Could not create simple directory\n"); + failure++; + } + + if (test_recurse()) { + printf("Could not create recursive directory\n"); + failure++; + } + + if (test_recurse_multislash()) { + printf("Could not create recursive directory with multiple slashes\n"); + failure++; + } + + if (test_parent_notdir()) { + printf("Creating over a file appeared to work?\n"); + failure++; + } + + rmdir(base_dir); + free(base_dir); + return failure; +} diff --git a/scripts/lcp_rtt_dump b/scripts/lcp_rtt_dump new file mode 100755 index 0000000..7898f08 --- /dev/null +++ b/scripts/lcp_rtt_dump @@ -0,0 +1,81 @@ +#!/usr/bin/perl +# vim: shiftwidth=4 tabstop=4 +# +# This program dumps to standard output the content of the file written +# by pppd's lcp-rtt-file configuration option. +# +# Copyright (C) Marco d'Itri +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +use v5.14; +use warnings; +use autodie; + +use POSIX qw(strftime); + +{ + my $data = read_data($ARGV[0] || '/run/ppp-rtt.data'); + die "The data file is invalid!\n" if not $data; + dump_data($data); +} + +sub dump_data { + my ($s) = @_; + + say "status: $s->{status}"; + say "interval: $s->{echo_interval}"; + say "position: $s->{position}"; + say 'elements: ' . scalar(@{ $s->{data} }); + say ''; + + foreach (my $i= 0; $i < @{ $s->{data} }; $i++) { + my $date = strftime('%F %T', localtime($s->{data}->[$i]->[0])); + print "$i\t$date\t$s->{data}->[$i]->[1]\t$s->{data}->[$i]->[2]\n"; + } +} + +sub read_data { + my ($file) = @_; + + my $data; + open(my $fh, '<', $file); + binmode($fh); + my $bytes_read; + do { + $bytes_read = sysread($fh, $data, 8192, length($data)); + } while ($bytes_read == 8192); + close($fh); + + my ($magic, $status, $position, $echo_interval, $rest) + = unpack('NNNN a*', $data); + return undef if $magic != 0x19450425; + + # the position is relative to the C array, not to the logical entries + $position /= 2; + + my @rawdata = unpack('(N C a3)*', $rest); + my @data; + while (my ($time, $loss, $rtt) = splice(@rawdata, 0, 3)) { + push(@data, [ $time, unpack('N', "\000$rtt"), $loss ]); + } + + if (0) { + @data = + # skip any "empty" (null) entries + grep { $_->[0] } + # rearrange the list in chronological order + (@data[$position+1 .. $#data], @data[0 .. $position]); + } + + return { + status => $status, + echo_interval => $echo_interval, + position => $position, + data => \@data, + }; +} + diff --git a/scripts/lcp_rtt_exporter b/scripts/lcp_rtt_exporter new file mode 100644 index 0000000..6fab745 --- /dev/null +++ b/scripts/lcp_rtt_exporter @@ -0,0 +1,108 @@ +#!/usr/bin/perl +# vim: shiftwidth=4 tabstop=4 +# +# This CGI program is a Prometheus exporter for pppd's lcp-rtt-file feature. +# +# Copyright (C) Marco d'Itri +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +use v5.14; +use warnings; +use autodie; + +use List::Util qw(sum max min); + +{ + my $data = read_data('/run/ppp-rtt.data'); + my $stats = compute_statistics($data, 60); + + my $s = metrics($stats); + + print "Content-type: text/plain\n\n$s"; + exit; +} + +sub metrics { + my ($stats) = @_; + + my $s = <{status} +END + foreach (qw(average min max loss)) { + next if not exists $stats->{$_}; + $s .= <{$_} +END + } + + return $s; +} + +sub compute_statistics { + my ($data, $length) = @_; + + my $cutoff = time() - $length; + my @e = grep { $_->[0] >= $cutoff } @{ $data->{data} }; + return { status => -1 } if not @e; # no data + + my $average = (sum map { $_->[1] } @e) / scalar(@e); + my $min = min map { $_->[1] } @e; + my $max = max map { $_->[1] } @e; + my $loss = sum map { $_->[2] } @e; + + return { + status => $data->{status}, + average => $average, + min => $min, + max => $max, + loss => $loss, + }; +} + +sub read_data { + my ($file) = @_; + + my $data; + open(my $fh, '<', $file); + binmode($fh); + my $bytes_read; + do { + $bytes_read = sysread($fh, $data, 8192, length($data)); + } while ($bytes_read == 8192); + close($fh); + + my ($magic, $status, $position, $echo_interval, $rest) + = unpack('NNNN a*', $data); + return undef if $magic != 0x19450425; + + # the position is relative to the C array, not to the logical entries + $position /= 2; + + my @rawdata = unpack('(N C a3)*', $rest); + my @data; + while (my ($time, $loss, $rtt) = splice(@rawdata, 0, 3)) { + push(@data, [ $time, unpack('N', "\000$rtt"), $loss ]); + } + + @data = + # skip any "empty" (null) entries + grep { $_->[0] } + # rearrange the list in chronological order + (@data[$position+1 .. $#data], @data[0 .. $position]); + + return { + status => $status, + echo_interval => $echo_interval, + position => $position, + data => \@data, + }; +} +