X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fsys-linux.c;fp=pppd%2Fsys-linux.c;h=e7f851c02512394cc1ebe5b63f771eb4890846b4;hp=6eed86f3f2a2b0bd6f1754375d1c6734380ce6a2;hb=HEAD;hpb=4a54e34cf5629f9fed61f0b7d69ee3ba4d874bc6 diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index 6eed86f..c0955a0 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -18,7 +18,7 @@ * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras - * ". + * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -125,25 +126,12 @@ #include #include -#include -#include +#include #include #include #include - -#ifdef INET6 #include -#endif - -/* Attempt at retaining compile-support with older than 4.7 kernels, or kernels - * where RTM_NEWSTATS isn't defined for whatever reason. - */ -#ifndef RTM_NEWSTATS -#define RTM_NEWSTATS 92 -#define RTM_GETSTATS 94 -#define IFLA_STATS_LINK_64 1 -#endif /* glibc versions prior to 2.24 do not define SOL_NETLINK */ #ifndef SOL_NETLINK @@ -161,14 +149,21 @@ #define IFLA_PPP_DEV_FD 1 #endif -#include "pppd.h" +#include "pppd-private.h" +#include "options.h" #include "fsm.h" #include "ipcp.h" -#ifdef PPP_FILTER +#ifdef PPP_WITH_IPV6CP +#include "eui64.h" +#endif /* PPP_WITH_IPV6CP */ + +#include "multilink.h" + +#ifdef PPP_WITH_FILTER #include #include -#endif /* PPP_FILTER */ +#endif /* PPP_WITH_FILTER */ #ifdef LOCKLIB #include @@ -180,7 +175,7 @@ */ #include "termios_linux.h" -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP #ifndef _LINUX_IN6_H /* * This is in linux/include/net/ipv6.h. @@ -200,7 +195,7 @@ struct in6_ifreq { } while (0) static const eui64_t nulleui64; -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /* We can get an EIO error on an ioctl if the modem has hung up */ #define ok_error(num) ((num)==EIO) @@ -212,9 +207,9 @@ static int ppp_fd = -1; /* fd which is set to PPP discipline */ static int sock_fd = -1; /* socket for doing interface ioctls */ static int slave_fd = -1; /* pty for old-style demand mode, slave */ static int master_fd = -1; /* pty for old-style demand mode, master */ -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP static int sock6_fd = -1; -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /* * For the old-style kernel driver, this is the same as ppp_fd. @@ -296,6 +291,147 @@ extern int dfl_route_metric; memset ((char *) &(addr), '\0', sizeof(addr)); \ addr.sa_family = (family); + +/* + * rtnetlink_msg - send rtnetlink message, receive response + * and return received error code: + * 0 - success + * positive value - error during sending / receiving message + * negative value - rtnetlink responce error code + */ +static int rtnetlink_msg(const char *desc, int *shared_fd, void *nlreq, size_t nlreq_len, void *nlresp_data, size_t *nlresp_size, unsigned nlresp_type) +{ + struct nlresp_hdr { + struct nlmsghdr nlh; + struct nlmsgerr nlerr; + } nlresp_hdr; + struct sockaddr_nl nladdr; + struct iovec iov[2]; + struct msghdr msg; + ssize_t nlresp_len; + int one; + int fd; + + if (shared_fd && *shared_fd >= 0) { + fd = *shared_fd; + } else { + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + error("rtnetlink_msg: socket(NETLINK_ROUTE): %m (line %d)", __LINE__); + return 1; + } + + /* + * Tell kernel to not send to us payload of acknowledgment error message. + * NETLINK_CAP_ACK option is supported since Linux kernel version 4.3 and + * older kernel versions always send full payload in acknowledgment netlink + * message. We ignore payload of this message as we need only error code, + * to check if our set remote peer address request succeeded or failed. + * So ignore return value from the following setsockopt() call as setting + * option NETLINK_CAP_ACK means for us just a kernel hint / optimization. + */ + one = 1; + setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &one, sizeof(one)); + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { + error("rtnetlink_msg: bind(AF_NETLINK): %m (line %d)", __LINE__); + close(fd); + return 1; + } + + if (shared_fd) + *shared_fd = fd; + } + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + memset(&iov[0], 0, sizeof(iov[0])); + iov[0].iov_base = nlreq; + iov[0].iov_len = nlreq_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov[0]; + msg.msg_iovlen = 1; + + if (sendmsg(fd, &msg, 0) < 0) { + error("rtnetlink_msg: sendmsg(%s): %m (line %d)", desc, __LINE__); + if (!shared_fd) + close(fd); + return 1; + } + + memset(iov, 0, sizeof(iov)); + iov[0].iov_base = &nlresp_hdr; + if (nlresp_size && *nlresp_size > sizeof(nlresp_hdr)) { + iov[0].iov_len = offsetof(struct nlresp_hdr, nlerr); + iov[1].iov_base = nlresp_data; + iov[1].iov_len = *nlresp_size; + } else { + iov[0].iov_len = sizeof(nlresp_hdr); + } + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = iov; + msg.msg_iovlen = (nlresp_size && *nlresp_size > sizeof(nlresp_hdr)) ? 2 : 1; + + nlresp_len = recvmsg(fd, &msg, 0); + + if (!shared_fd) + close(fd); + + if (nlresp_len < 0) { + error("rtnetlink_msg: recvmsg(%s): %m (line %d)", desc, __LINE__); + return 1; + } + + if (nladdr.nl_family != AF_NETLINK) { + error("rtnetlink_msg: recvmsg(%s): Not a netlink packet (line %d)", desc, __LINE__); + return 1; + } + + if (!nlresp_size) { + if ((size_t)nlresp_len < sizeof(nlresp_hdr) || nlresp_hdr.nlh.nlmsg_len < sizeof(nlresp_hdr)) { + error("rtnetlink_msg: recvmsg(%s): Acknowledgment netlink packet too short (line %d)", desc, __LINE__); + return 1; + } + + /* acknowledgment packet for NLM_F_ACK is NLMSG_ERROR */ + if (nlresp_hdr.nlh.nlmsg_type != NLMSG_ERROR) { + error("rtnetlink_msg: recvmsg(%s): Not an acknowledgment netlink packet (line %d)", desc, __LINE__); + return 1; + } + } + + if (nlresp_size) { + if (*nlresp_size > sizeof(nlresp_hdr)) + memcpy((unsigned char *)&nlresp_hdr + offsetof(struct nlresp_hdr, nlerr), nlresp_data, sizeof(nlresp_hdr.nlerr)); + else + memcpy(nlresp_data, (unsigned char *)&nlresp_hdr + offsetof(struct nlresp_hdr, nlerr), *nlresp_size); + } + + /* error == 0 indicates success, negative value is errno code */ + if (nlresp_hdr.nlh.nlmsg_type == NLMSG_ERROR && nlresp_hdr.nlerr.error) + return nlresp_hdr.nlerr.error; + + if (nlresp_size) { + if (nlresp_hdr.nlh.nlmsg_type != nlresp_type) { + error("rtnetlink_msg: recvmsg(%s): Not a netlink packet of type 0x%x (line %d)", desc, nlresp_type, __LINE__); + return 1; + } + *nlresp_size = nlresp_len - offsetof(struct nlresp_hdr, nlerr); + } + + return 0; +} + /* * Determine if the PPP connection should still be present. */ @@ -357,7 +493,7 @@ void sys_init(void) if (sock_fd < 0) fatal("Couldn't create IP socket: %m(%d)", errno); -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0); if (sock6_fd < 0) sock6_fd = -errno; /* save errno for later */ @@ -383,15 +519,17 @@ void sys_cleanup(void) if_is_up = 0; sifdown(0); } +#ifdef PPP_WITH_IPV6CP if (if6_is_up) sif6down(0); +#endif /* * Delete any routes through the device. */ if (have_default_route) cifdefaultroute(0, 0, 0); -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP if (have_default_route6) cif6defaultroute(0, nulleui64, nulleui64); #endif @@ -402,16 +540,16 @@ void sys_cleanup(void) /******************************************************************** * - * sys_close - Clean up in a child process before execing. + * ppp_sys_close - Clean up in a child process before execing. */ void -sys_close(void) +ppp_sys_close(void) { if (new_style_driver && ppp_dev_fd >= 0) close(ppp_dev_fd); if (sock_fd >= 0) close(sock_fd); -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP if (sock6_fd >= 0) close(sock6_fd); #endif @@ -469,7 +607,7 @@ int tty_establish_ppp (int tty_fd) #ifndef N_SYNC_PPP #define N_SYNC_PPP 14 #endif - ppp_disc = (new_style_driver && sync_serial)? N_SYNC_PPP: N_PPP; + ppp_disc = (new_style_driver && ppp_sync_serial())? N_SYNC_PPP: N_PPP; if (ioctl(tty_fd, TIOCSETD, &ppp_disc) < 0) { if ( ! ok_error (errno) ) { error("Couldn't set tty to PPP discipline: %m"); @@ -477,7 +615,7 @@ int tty_establish_ppp (int tty_fd) } } - ret_fd = generic_establish_ppp(tty_fd); + ret_fd = ppp_generic_establish(tty_fd); #define SC_RCVB (SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP) #define SC_LOGB (SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \ @@ -498,7 +636,7 @@ int tty_establish_ppp (int tty_fd) * * generic_establish_ppp - Turn the fd into a ppp interface. */ -int generic_establish_ppp (int fd) +int ppp_generic_establish (int fd) { int x; @@ -635,16 +773,16 @@ void tty_disestablish_ppp(int tty_fd) flushfailed: initfdflags = -1; - generic_disestablish_ppp(tty_fd); + ppp_generic_disestablish(tty_fd); } /******************************************************************** * - * generic_disestablish_ppp - Restore device components to normal + * ppp_generic_disestablish - Restore device components to normal * operation, and reconnect the ppp unit to the loopback if in demand * mode. This shouldn't call die() because it's called from die(). */ -void generic_disestablish_ppp(int dev_fd) +void ppp_generic_disestablish(int dev_fd) { if (new_style_driver) { close(ppp_fd); @@ -652,7 +790,7 @@ void generic_disestablish_ppp(int dev_fd) if (demand) { modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC); looped = 1; - } else if (!doing_multilink && ppp_dev_fd >= 0) { + } else if (!mp_on() && ppp_dev_fd >= 0) { close(ppp_dev_fd); remove_fd(ppp_dev_fd); ppp_dev_fd = -1; @@ -697,35 +835,7 @@ static int make_ppp_unit_rtnetlink(void) } ifid; } ifli; } nlreq; - struct { - struct nlmsghdr nlh; - struct nlmsgerr nlerr; - } nlresp; - struct sockaddr_nl nladdr; - struct iovec iov; - struct msghdr msg; - ssize_t nlresplen; - int one; - int fd; - - fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd < 0) { - error("make_ppp_unit_rtnetlink: socket(NETLINK_ROUTE): %m (line %d)", __LINE__); - return 0; - } - - /* Tell kernel to not send to us payload of acknowledgment error message. */ - one = 1; - setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &one, sizeof(one)); - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - if (bind(fd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { - error("make_ppp_unit_rtnetlink: bind(AF_NETLINK): %m (line %d)", __LINE__); - close(fd); - return 0; - } + int resp; memset(&nlreq, 0, sizeof(nlreq)); nlreq.nlh.nlmsg_len = sizeof(nlreq); @@ -747,63 +857,15 @@ static int make_ppp_unit_rtnetlink(void) nlreq.ifli.ifid.ifdata[0].rta.rta_type = IFLA_PPP_DEV_FD; nlreq.ifli.ifid.ifdata[0].ppp.ppp_dev_fd = ppp_dev_fd; - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = &nlreq; - iov.iov_len = sizeof(nlreq); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &nladdr; - msg.msg_namelen = sizeof(nladdr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (sendmsg(fd, &msg, 0) < 0) { - error("make_ppp_unit_rtnetlink: sendmsg(RTM_NEWLINK/NLM_F_CREATE): %m (line %d)", __LINE__); - close(fd); - return 0; - } - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = &nlresp; - iov.iov_len = sizeof(nlresp); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &nladdr; - msg.msg_namelen = sizeof(nladdr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - nlresplen = recvmsg(fd, &msg, 0); - - if (nlresplen < 0) { - error("make_ppp_unit_rtnetlink: recvmsg(NLM_F_ACK): %m (line %d)", __LINE__); - close(fd); - return 0; - } - - close(fd); - - if (nladdr.nl_family != AF_NETLINK) { - error("make_ppp_unit_rtnetlink: recvmsg(NLM_F_ACK): Not a netlink packet (line %d)", __LINE__); - return 0; - } - - if ((size_t)nlresplen < sizeof(nlresp) || nlresp.nlh.nlmsg_len < sizeof(nlresp)) { - error("make_ppp_unit_rtnetlink: recvmsg(NLM_F_ACK): Acknowledgment netlink packet too short (line %d)", __LINE__); - return 0; - } - - /* acknowledgment packet for NLM_F_ACK is NLMSG_ERROR */ - if (nlresp.nlh.nlmsg_type != NLMSG_ERROR) { - error("make_ppp_unit_rtnetlink: recvmsg(NLM_F_ACK): Not an acknowledgment netlink packet (line %d)", __LINE__); - return 0; - } + /* + * See kernel function ppp_nl_newlink(), which may return -EBUSY to prevent + * possible deadlock in kernel and ask userspace to retry request again. + */ + do { + resp = rtnetlink_msg("RTM_NEWLINK/NLM_F_CREATE", NULL, &nlreq, sizeof(nlreq), NULL, NULL, 0); + } while (resp == -EBUSY); - /* error == 0 indicates success, negative value is errno code */ - if (nlresp.nlerr.error != 0) { + if (resp) { /* * Linux kernel versions prior to 4.7 do not support creating ppp * interfaces via rtnetlink API and therefore error response is @@ -811,7 +873,7 @@ static int make_ppp_unit_rtnetlink(void) * When error is different than EEXIST then pppd tries to fallback to * the old ioctl method. */ - errno = (nlresp.nlerr.error < 0) ? -nlresp.nlerr.error : EINVAL; + errno = (resp < 0) ? -resp : EINVAL; if (kernel_version >= KVERSION(4,7,0)) error("Couldn't create ppp interface %s: %m", req_ifname); return 0; @@ -874,6 +936,11 @@ static int make_ppp_unit(void) ifunit = -1; x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit); } + if (x < 0 && errno == EEXIST) { + srand(time(NULL) * getpid()); + ifunit = rand() % 10000; + x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit); + } if (x < 0) error("Couldn't create new ppp unit: %m"); @@ -1429,7 +1496,7 @@ int read_packet (unsigned char *buf) error("read /dev/ppp: %m"); if (nr < 0 && errno == ENXIO) nr = 0; - if (nr == 0 && doing_multilink) { + if (nr == 0 && mp_on()) { remove_fd(ppp_dev_fd); bundle_eof = 1; } @@ -1475,7 +1542,7 @@ get_loop_output(void) * netif_set_mtu - set the MTU on the PPP network interface. */ void -netif_set_mtu(int unit, int mtu) +ppp_set_mtu(int unit, int mtu) { struct ifreq ifr; @@ -1491,7 +1558,7 @@ netif_set_mtu(int unit, int mtu) * netif_get_mtu - get the MTU on the PPP network interface. */ int -netif_get_mtu(int unit) +ppp_get_mtu(int unit) { struct ifreq ifr; @@ -1526,7 +1593,7 @@ void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp) } x = (pcomp? SC_COMP_PROT: 0) | (accomp? SC_COMP_AC: 0) - | (sync_serial? SC_SYNC: 0); + | (ppp_sync_serial()? SC_SYNC: 0); modify_flags(ppp_fd, SC_COMP_PROT|SC_COMP_AC|SC_SYNC, x); } @@ -1612,7 +1679,7 @@ void ccp_flags_set (int unit, int isopen, int isup) modify_flags(ppp_dev_fd, SC_CCP_OPEN|SC_CCP_UP, x); } -#ifdef PPP_FILTER +#ifdef PPP_WITH_FILTER /* * set_filters - set the active and pass filters in the kernel driver. */ @@ -1637,7 +1704,7 @@ int set_filters(struct bpf_program *pass, struct bpf_program *active) } return 1; } -#endif /* PPP_FILTER */ +#endif /* PPP_WITH_FILTER */ /******************************************************************** * @@ -1662,20 +1729,21 @@ get_ppp_stats_ioctl(int u, struct pppd_stats *stats) static u_int32_t iwraps = 0; static u_int32_t owraps = 0; - struct ifpppstatsreq req; + struct ifreq req; + struct ppp_stats data; memset (&req, 0, sizeof (req)); - req.stats_ptr = (caddr_t) &req.stats; - strlcpy(req.ifr__name, ifname, sizeof(req.ifr__name)); + req.ifr_data = (caddr_t) &data; + strlcpy(req.ifr_name, ifname, sizeof(req.ifr_name)); if (ioctl(sock_fd, SIOCGPPPSTATS, &req) < 0) { error("Couldn't get PPP statistics: %m"); return 0; } - stats->bytes_in = req.stats.p.ppp_ibytes; - stats->bytes_out = req.stats.p.ppp_obytes; - stats->pkts_in = req.stats.p.ppp_ipackets; - stats->pkts_out = req.stats.p.ppp_opackets; + stats->bytes_in = data.p.ppp_ibytes; + stats->bytes_out = data.p.ppp_obytes; + stats->pkts_in = data.p.ppp_ipackets; + stats->pkts_out = data.p.ppp_opackets; if (stats->bytes_in < previbytes) ++iwraps; @@ -1698,123 +1766,57 @@ get_ppp_stats_ioctl(int u, struct pppd_stats *stats) static int get_ppp_stats_rtnetlink(int u, struct pppd_stats *stats) { - static int rtnl_fd = -1; +#ifdef RTM_NEWSTATS + static int fd = -1; - struct sockaddr_nl nladdr; struct { struct nlmsghdr nlh; struct if_stats_msg ifsm; } nlreq; - struct nlresp { - struct nlmsghdr nlh; - union { - struct { - struct nlmsgerr nlerr; - char __end_err[0]; - }; - struct { - struct rtmsg rth; - struct { - /* We only case about these first fields from rtnl_link_stats64 */ - uint64_t rx_packets; - uint64_t tx_packets; - uint64_t rx_bytes; - uint64_t tx_bytes; - } stats; - char __end_stats[0]; - }; - }; - } nlresp; - ssize_t nlresplen; - struct iovec iov; - struct msghdr msg; - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - if (rtnl_fd < 0) { - rtnl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (rtnl_fd < 0) { - error("get_ppp_stats_rtnetlink: error creating NETLINK socket: %m (line %d)", __LINE__); - return 0; - } - - if (bind(rtnl_fd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { - error("get_ppp_stats_rtnetlink: bind(AF_NETLINK): %m (line %d)", __LINE__); - goto err; - } - } + struct { + struct rtmsg rth; + struct { + /* We only case about these first fields from rtnl_link_stats64 */ + uint64_t rx_packets; + uint64_t tx_packets; + uint64_t rx_bytes; + uint64_t tx_bytes; + } stats; + } nlresp_data; + size_t nlresp_size; + int resp; memset(&nlreq, 0, sizeof(nlreq)); nlreq.nlh.nlmsg_len = sizeof(nlreq); nlreq.nlh.nlmsg_type = RTM_GETSTATS; nlreq.nlh.nlmsg_flags = NLM_F_REQUEST; - nlreq.ifsm.ifindex = if_nametoindex(ifname); nlreq.ifsm.filter_mask = IFLA_STATS_LINK_64; - memset(&iov, 0, sizeof(iov)); - iov.iov_base = &nlreq; - iov.iov_len = sizeof(nlreq); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &nladdr; - msg.msg_namelen = sizeof(nladdr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (sendmsg(rtnl_fd, &msg, 0) < 0) { - error("get_ppp_stats_rtnetlink: sendmsg(RTM_GETSTATS): %m (line %d)", __LINE__); - goto err; - } - - /* We just need to repoint to IOV ... everything else stays the same */ - iov.iov_base = &nlresp; - iov.iov_len = sizeof(nlresp); - - nlresplen = recvmsg(rtnl_fd, &msg, 0); - - if (nlresplen < 0) { - error("get_ppp_stats_rtnetlink: recvmsg(RTM_GETSTATS): %m (line %d)", __LINE__); - goto err; - } - - if (nlresplen < sizeof(nlresp.nlh)) { - error("get_ppp_stats_rtnetlink: Netlink response message was incomplete (line %d)", __LINE__); - goto err; - } - - if (nlresp.nlh.nlmsg_type == NLMSG_ERROR) { - if (nlresplen < offsetof(struct nlresp, __end_err)) { - if (kernel_version >= KVERSION(4,7,0)) - error("get_ppp_stats_rtnetlink: Netlink responded with error: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__); - } else { - error("get_ppp_stats_rtnetlink: Netlink responded with an error message, but the nlmsgerr structure is incomplete (line %d).", - __LINE__); - } - goto err; - } - - if (nlresp.nlh.nlmsg_type != RTM_NEWSTATS) { - error("get_ppp_stats_rtnetlink: Expected RTM_NEWSTATS response, found something else (mlmsg_type %d, line %d)", - nlresp.nlh.nlmsg_type, __LINE__); - goto err; + nlresp_size = sizeof(nlresp_data); + resp = rtnetlink_msg("RTM_GETSTATS/NLM_F_REQUEST", &fd, &nlreq, sizeof(nlreq), &nlresp_data, &nlresp_size, RTM_NEWSTATS); + if (resp) { + errno = (resp < 0) ? -resp : EINVAL; + if (kernel_version >= KVERSION(4,7,0)) + error("get_ppp_stats_rtnetlink: %m (line %d)", __LINE__); + goto err; } - if (nlresplen < offsetof(struct nlresp, __end_stats)) { + if (nlresp_size < sizeof(nlresp_data)) { error("get_ppp_stats_rtnetlink: Obtained an insufficiently sized rtnl_link_stats64 struct from the kernel (line %d).", __LINE__); goto err; } - stats->bytes_in = nlresp.stats.rx_bytes; - stats->bytes_out = nlresp.stats.tx_bytes; - stats->pkts_in = nlresp.stats.rx_packets; - stats->pkts_out = nlresp.stats.tx_packets; + stats->bytes_in = nlresp_data.stats.rx_bytes; + stats->bytes_out = nlresp_data.stats.tx_bytes; + stats->pkts_in = nlresp_data.stats.rx_packets; + stats->pkts_out = nlresp_data.stats.tx_packets; return 1; err: - close(rtnl_fd); - rtnl_fd = -1; + close(fd); + fd = -1; +#endif return 0; } @@ -2191,11 +2193,27 @@ int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway, bool replac * - this is normally only the case the doing demand: */ if (defaultroute_exists(&tmp_rt, -1)) del_rt = &tmp_rt; + } else if (!replace) { + /* + * We don't want to replace an existing route. + * We may however add our route along an existing route with a different + * metric. + */ + if (defaultroute_exists(&rt, dfl_route_metric) && strcmp(rt.rt_dev, ifname) != 0) { + if (rt.rt_flags & RTF_GATEWAY) + error("not replacing existing default route via %I with metric %d", + SIN_ADDR(rt.rt_gateway), dfl_route_metric); + else + error("not replacing existing default route through %s with metric %d", + rt.rt_dev, dfl_route_metric); + return 0; + } } else if (defaultroute_exists(&old_def_rt, -1 ) && strcmp( old_def_rt.rt_dev, ifname) != 0) { /* - * We did not yet replace an existing default route, let's - * check if we should save and replace a default route: + * We want to replace an existing route and did not replace an existing + * default route yet, let's check if we should save and replace an + * existing default route: */ u_int32_t old_gateway = SIN_ADDR(old_def_rt.rt_gateway); @@ -2293,7 +2311,7 @@ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway) return 1; } -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP /* * /proc/net/ipv6_route parsing stuff. */ @@ -2483,7 +2501,7 @@ int cif6defaultroute (int unit, eui64_t ouraddr, eui64_t gateway) return 1; } -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /******************************************************************** * @@ -2850,11 +2868,11 @@ ppp_registered(void) /******************************************************************** * - * ppp_available - check whether the system has any ppp interfaces + * ppp_check_kernel_support - check whether the system has any ppp interfaces * (in fact we check whether we can do an ioctl on ppp0). */ -int ppp_available(void) +int ppp_check_kernel_support(void) { int s, ok, fd; struct ifreq ifr; @@ -3109,15 +3127,15 @@ int sifdown (int u) if (if_is_up && --if_is_up > 0) return 1; -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP if (if6_is_up) return 1; -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ return setifstate(u, 0); } -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP /******************************************************************** * * sif6up - Config the interface up for IPv6 @@ -3148,7 +3166,7 @@ int sif6down (int u) return setifstate(u, 0); } -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /******************************************************************** * @@ -3336,7 +3354,7 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr) return 1; } -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP /******************************************************************** * * sif6addr_rtnetlink - Config the interface with both IPv6 link-local addresses via rtnetlink @@ -3351,43 +3369,7 @@ static int sif6addr_rtnetlink(unsigned int iface, eui64_t our_eui64, eui64_t his struct in6_addr addr; } addrs[2]; } nlreq; - struct { - struct nlmsghdr nlh; - struct nlmsgerr nlerr; - } nlresp; - struct sockaddr_nl nladdr; - struct iovec iov; - struct msghdr msg; - ssize_t nlresplen; - int one; - int fd; - - fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd < 0) { - error("sif6addr_rtnetlink: socket(NETLINK_ROUTE): %m (line %d)", __LINE__); - return 0; - } - - /* - * Tell kernel to not send to us payload of acknowledgment error message. - * NETLINK_CAP_ACK option is supported since Linux kernel version 4.3 and - * older kernel versions always send full payload in acknowledgment netlink - * message. We ignore payload of this message as we need only error code, - * to check if our set remote peer address request succeeded or failed. - * So ignore return value from the following setsockopt() call as setting - * option NETLINK_CAP_ACK means for us just a kernel hint / optimization. - */ - one = 1; - setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &one, sizeof(one)); - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - if (bind(fd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { - error("sif6addr_rtnetlink: bind(AF_NETLINK): %m (line %d)", __LINE__); - close(fd); - return 0; - } + int resp; memset(&nlreq, 0, sizeof(nlreq)); nlreq.nlh.nlmsg_len = sizeof(nlreq); @@ -3417,71 +3399,17 @@ static int sif6addr_rtnetlink(unsigned int iface, eui64_t our_eui64, eui64_t his else IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, our_eui64); - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = &nlreq; - iov.iov_len = sizeof(nlreq); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &nladdr; - msg.msg_namelen = sizeof(nladdr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (sendmsg(fd, &msg, 0) < 0) { - error("sif6addr_rtnetlink: sendmsg(RTM_NEWADDR/NLM_F_CREATE): %m (line %d)", __LINE__); - close(fd); - return 0; - } - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = &nlresp; - iov.iov_len = sizeof(nlresp); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &nladdr; - msg.msg_namelen = sizeof(nladdr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - nlresplen = recvmsg(fd, &msg, 0); - - if (nlresplen < 0) { - error("sif6addr_rtnetlink: recvmsg(NLM_F_ACK): %m (line %d)", __LINE__); - close(fd); - return 0; - } - - close(fd); - - if (nladdr.nl_family != AF_NETLINK) { - error("sif6addr_rtnetlink: recvmsg(NLM_F_ACK): Not a netlink packet (line %d)", __LINE__); - return 0; - } - - if ((size_t)nlresplen != sizeof(nlresp) || nlresp.nlh.nlmsg_len < sizeof(nlresp)) { - error("sif6addr_rtnetlink: recvmsg(NLM_F_ACK): Acknowledgment netlink packet too short (line %d)", __LINE__); - return 0; - } - - /* acknowledgment packet for NLM_F_ACK is NLMSG_ERROR */ - if (nlresp.nlh.nlmsg_type != NLMSG_ERROR) { - error("sif6addr_rtnetlink: recvmsg(NLM_F_ACK): Not an acknowledgment netlink packet (line %d)", __LINE__); - return 0; - } - - /* error == 0 indicates success, negative value is errno code */ - if (nlresp.nlerr.error != 0) { + resp = rtnetlink_msg("RTM_NEWADDR/NLM_F_CREATE", NULL, &nlreq, sizeof(nlreq), NULL, NULL, 0); + if (resp) { /* * Linux kernel versions prior 3.11 do not support setting IPv6 peer * addresses and error response is expected. On older kernel versions * do not show this error message. On error pppd tries to fallback to * the old IOCTL method. */ + errno = (resp < 0) ? -resp : EINVAL; if (kernel_version >= KVERSION(3,11,0)) - error("sif6addr_rtnetlink: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__); + error("sif6addr_rtnetlink: %m (line %d)", __LINE__); return 0; } @@ -3597,7 +3525,7 @@ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) } return 1; } -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /* * get_pty - get a pty master/slave pair and chown the slave side @@ -3757,7 +3685,7 @@ int get_host_seed(void) { int h; - char *p = hostname; + const char *p; h = 407; for (p = hostname; *p != 0; ++p) @@ -3774,7 +3702,7 @@ int sys_check_options(void) { if (demand && driver_is_old) { - option_error("demand dialling is not supported by kernel driver " + ppp_option_error("demand dialling is not supported by kernel driver " "version %d.%d.%d", driver_version, driver_modification, driver_patch); return 0; @@ -3791,7 +3719,7 @@ sys_check_options(void) * get_time - Get current time, monotonic if possible. */ int -get_time(struct timeval *tv) +ppp_get_time(struct timeval *tv) { /* Old glibc (< 2.3.4) does define CLOCK_MONOTONIC, but kernel may have it. * Runtime checking makes it safe. */