X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Fsys-linux.c;h=9c0455c0da62e4fb31ed0e4b3ae542bab293844d;hb=8cbd7dd098cbb565dd9b01397fb352f1c98376f3;hp=025d70d23d63f80b8d2f630a5589e4baf9dc3465;hpb=81ad945630120cc1c27c8bb00503be42b76ff202;p=ppp.git diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index 025d70d..9c0455c 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -125,12 +126,12 @@ #include #include -#include -#include +#include #include #include #include + /* Attempt at retaining compile-support with older than 4.7 kernels, or kernels * where RTM_NEWSTATS isn't defined for whatever reason. */ @@ -140,36 +141,36 @@ #define IFLA_STATS_LINK_64 1 #endif -#ifdef INET6 #include + /* glibc versions prior to 2.24 do not define SOL_NETLINK */ #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif + /* linux kernel versions prior to 4.3 do not define/support NETLINK_CAP_ACK */ #ifndef NETLINK_CAP_ACK #define NETLINK_CAP_ACK 10 #endif + +/* linux kernel versions prior to 4.7 do not define/support IFLA_PPP_DEV_FD */ +#ifndef IFLA_PPP_MAX +/* IFLA_PPP_DEV_FD is declared as enum when IFLA_PPP_MAX is defined */ +#define IFLA_PPP_DEV_FD 1 #endif #include "pppd.h" #include "fsm.h" #include "ipcp.h" -#ifdef IPX_CHANGE -#include "ipxcp.h" -#if __GLIBC__ >= 2 && \ - !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0) -#include -#else -#include -#endif -#endif /* IPX_CHANGE */ +#ifdef PPP_WITH_IPV6CP +#include "eui64.h" +#endif /* PPP_WITH_IPV6CP */ -#ifdef PPP_FILTER +#ifdef PPP_WITH_FILTER #include #include -#endif /* PPP_FILTER */ +#endif /* PPP_WITH_FILTER */ #ifdef LOCKLIB #include @@ -181,7 +182,7 @@ */ #include "termios_linux.h" -#ifdef INET6 +#ifdef PPP_WITH_IPV6CP #ifndef _LINUX_IN6_H /* * This is in linux/include/net/ipv6.h. @@ -201,7 +202,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) @@ -213,9 +214,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. @@ -358,7 +359,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 */ @@ -384,15 +385,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 @@ -412,7 +415,7 @@ sys_close(void) 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 @@ -667,6 +670,160 @@ void generic_disestablish_ppp(int dev_fd) } } +/* + * make_ppp_unit_rtnetlink - register a new ppp network interface for ppp_dev_fd + * with specified req_ifname via rtnetlink. Interface name req_ifname must not + * be empty. Custom ppp unit id req_unit is ignored and kernel choose some free. + */ +static int make_ppp_unit_rtnetlink(void) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + struct { + struct rtattr rta; + char ifname[IFNAMSIZ]; + } ifn; + struct { + struct rtattr rta; + struct { + struct rtattr rta; + char ifkind[sizeof("ppp")]; + } ifik; + struct { + struct rtattr rta; + struct { + struct rtattr rta; + union { + int ppp_dev_fd; + } ppp; + } ifdata[1]; + } 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; + } + + memset(&nlreq, 0, sizeof(nlreq)); + nlreq.nlh.nlmsg_len = sizeof(nlreq); + nlreq.nlh.nlmsg_type = RTM_NEWLINK; + nlreq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE; + nlreq.ifm.ifi_family = AF_UNSPEC; + nlreq.ifm.ifi_type = ARPHRD_NETROM; + nlreq.ifn.rta.rta_len = sizeof(nlreq.ifn); + nlreq.ifn.rta.rta_type = IFLA_IFNAME; + strlcpy(nlreq.ifn.ifname, req_ifname, sizeof(nlreq.ifn.ifname)); + nlreq.ifli.rta.rta_len = sizeof(nlreq.ifli); + nlreq.ifli.rta.rta_type = IFLA_LINKINFO; + nlreq.ifli.ifik.rta.rta_len = sizeof(nlreq.ifli.ifik); + nlreq.ifli.ifik.rta.rta_type = IFLA_INFO_KIND; + strcpy(nlreq.ifli.ifik.ifkind, "ppp"); + nlreq.ifli.ifid.rta.rta_len = sizeof(nlreq.ifli.ifid); + nlreq.ifli.ifid.rta.rta_type = IFLA_INFO_DATA; + nlreq.ifli.ifid.ifdata[0].rta.rta_len = sizeof(nlreq.ifli.ifid.ifdata[0]); + 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; + } + + /* error == 0 indicates success, negative value is errno code */ + if (nlresp.nlerr.error != 0) { + /* + * Linux kernel versions prior to 4.7 do not support creating ppp + * interfaces via rtnetlink API and therefore error response is + * expected. On older kernel versions do not show this error message. + * 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; + if (kernel_version >= KVERSION(4,7,0)) + error("Couldn't create ppp interface %s: %m", req_ifname); + return 0; + } + + return 1; +} + /* * make_ppp_unit - make a new ppp unit for ppp_dev_fd. * Assumes new_style_driver. @@ -687,6 +844,33 @@ static int make_ppp_unit(void) || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1) warn("Couldn't set /dev/ppp to nonblock: %m"); + /* + * Via rtnetlink it is possible to create ppp network interface with + * custom ifname atomically. But it is not possible to specify custom + * ppp unit id. + * + * Tools like systemd, udev or NetworkManager are trying to query + * interface attributes based on interface name immediately when new + * network interface is created. And therefore immediate interface + * renaming is causing issues. + * + * So use rtnetlink API only when user requested custom ifname. It will + * avoid system issues with interface renaming. + */ + if (req_unit == -1 && req_ifname[0] != '\0' && kernel_version >= KVERSION(2,1,16)) { + if (make_ppp_unit_rtnetlink()) { + if (ioctl(ppp_dev_fd, PPPIOCGUNIT, &ifunit)) + fatal("Couldn't retrieve PPP unit id: %m"); + return 0; + } + /* + * If interface with requested name already exist return error + * otherwise fallback to old ioctl method. + */ + if (errno == EEXIST) + return -1; + } + ifunit = req_unit; x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit); if (x < 0 && req_unit >= 0 && errno == EEXIST) { @@ -694,6 +878,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"); @@ -909,6 +1098,9 @@ struct speed { #ifdef B115200 { 115200, B115200 }, #endif +#ifdef B153600 + { 153600, B153600 }, +#endif #ifdef EXTA { 19200, EXTA }, #endif @@ -918,6 +1110,9 @@ struct speed { #ifdef B230400 { 230400, B230400 }, #endif +#ifdef B307200 + { 307200, B307200 }, +#endif #ifdef B460800 { 460800, B460800 }, #endif @@ -927,6 +1122,9 @@ struct speed { #ifdef B576000 { 576000, B576000 }, #endif +#ifdef B614400 + { 614400, B614400 }, +#endif #ifdef B921600 { 921600, B921600 }, #endif @@ -1423,7 +1621,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. */ @@ -1448,7 +1646,7 @@ int set_filters(struct bpf_program *pass, struct bpf_program *active) } return 1; } -#endif /* PPP_FILTER */ +#endif /* PPP_WITH_FILTER */ /******************************************************************** * @@ -1473,20 +1671,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; @@ -1596,13 +1795,10 @@ get_ppp_stats_rtnetlink(int u, struct pppd_stats *stats) } 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__); - } + if (nlresplen < offsetof(struct nlresp, __end_err)) + error("get_ppp_stats_rtnetlink: Netlink responded with an error message, but the nlmsgerr structure is incomplete (line %d).", __LINE__); + else if (kernel_version >= KVERSION(4,7,0)) + error("get_ppp_stats_rtnetlink: Netlink responded with error: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__); goto err; } @@ -2104,7 +2300,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. */ @@ -2294,7 +2490,7 @@ int cif6defaultroute (int unit, eui64_t ouraddr, eui64_t gateway) return 1; } -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /******************************************************************** * @@ -2920,15 +3116,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 @@ -2959,7 +3155,7 @@ int sif6down (int u) return setifstate(u, 0); } -#endif /* INET6 */ +#endif /* PPP_WITH_IPV6CP */ /******************************************************************** * @@ -3130,7 +3326,7 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr) } } - /* This way it is possible to have an IPX-only or IPv6-only interface */ + /* This way it is possible to have an IPv6-only interface */ memset(&ifr, 0, sizeof(ifr)); SET_SA_FAMILY(ifr.ifr_addr, AF_INET); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); @@ -3147,7 +3343,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 @@ -3408,7 +3604,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 @@ -3561,99 +3757,6 @@ sifnpmode(int u, int proto, enum NPmode mode) return 1; } - -/******************************************************************** - * - * sipxfaddr - Config the interface IPX networknumber - */ - -int sipxfaddr (int unit, unsigned long int network, unsigned char * node ) -{ - int result = 1; - -#ifdef IPX_CHANGE - int skfd; - struct ifreq ifr; - struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr; - - skfd = socket (AF_IPX, SOCK_DGRAM, 0); - if (skfd < 0) { - if (! ok_error (errno)) - dbglog("socket(AF_IPX): %m (line %d)", __LINE__); - result = 0; - } - else { - memset (&ifr, '\0', sizeof (ifr)); - strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - - memcpy (sipx->sipx_node, node, IPX_NODE_LEN); - sipx->sipx_family = AF_IPX; - sipx->sipx_port = 0; - sipx->sipx_network = htonl (network); - sipx->sipx_type = IPX_FRAME_ETHERII; - sipx->sipx_action = IPX_CRTITF; -/* - * Set the IPX device - */ - if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) { - result = 0; - if (errno != EEXIST) { - if (! ok_error (errno)) - dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (line %d)", __LINE__); - } - else { - warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists"); - } - } - close (skfd); - } -#endif - return result; -} - -/******************************************************************** - * - * cipxfaddr - Clear the information for the IPX network. The IPX routes - * are removed and the device is no longer able to pass IPX - * frames. - */ - -int cipxfaddr (int unit) -{ - int result = 1; - -#ifdef IPX_CHANGE - int skfd; - struct ifreq ifr; - struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr; - - skfd = socket (AF_IPX, SOCK_DGRAM, 0); - if (skfd < 0) { - if (! ok_error (errno)) - dbglog("socket(AF_IPX): %m (line %d)", __LINE__); - result = 0; - } - else { - memset (&ifr, '\0', sizeof (ifr)); - strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - - sipx->sipx_type = IPX_FRAME_ETHERII; - sipx->sipx_action = IPX_DLTITF; - sipx->sipx_family = AF_IPX; -/* - * Set the IPX device - */ - if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) { - if (! ok_error (errno)) - info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (line %d)", __LINE__); - result = 0; - } - close (skfd); - } -#endif - return result; -} - /* * Use the hostname as part of the random number seed. */ @@ -3677,22 +3780,6 @@ get_host_seed(void) int sys_check_options(void) { -#ifdef IPX_CHANGE -/* - * Disable the IPX protocol if the support is not present in the kernel. - */ - char *path; - - if (ipxcp_protent.enabled_flag) { - struct stat stat_buf; - if ( ((path = path_to_procfs("/net/ipx/interface")) == NULL - && (path = path_to_procfs("/net/ipx_interface")) == NULL) - || lstat(path, &stat_buf) < 0) { - error("IPX support is not present in the kernel\n"); - ipxcp_protent.enabled_flag = 0; - } - } -#endif if (demand && driver_is_old) { option_error("demand dialling is not supported by kernel driver " "version %d.%d.%d", driver_version, driver_modification,