X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fsys-linux.c;h=e7f851c02512394cc1ebe5b63f771eb4890846b4;hp=ff3a249e81758ec042a6cd67991e6412532f8d1b;hb=HEAD;hpb=33e8c95bf873cdce5ff2e2b4d8a76bdae792c699 diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index ff3a249..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 @@ -131,15 +131,8 @@ #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. - */ -#ifndef RTM_NEWSTATS -#define RTM_NEWSTATS 92 -#define RTM_GETSTATS 94 -#define IFLA_STATS_LINK_64 1 - #include + /* glibc versions prior to 2.24 do not define SOL_NETLINK */ #ifndef SOL_NETLINK #define SOL_NETLINK 270 @@ -149,9 +142,15 @@ #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 "pppd-private.h" +#include "options.h" #include "fsm.h" #include "ipcp.h" @@ -159,6 +158,8 @@ #include "eui64.h" #endif /* PPP_WITH_IPV6CP */ +#include "multilink.h" + #ifdef PPP_WITH_FILTER #include #include @@ -290,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. */ @@ -398,10 +540,10 @@ 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); @@ -465,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"); @@ -473,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 \ @@ -494,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; @@ -631,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); @@ -648,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; @@ -662,6 +804,84 @@ 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; + int resp; + + 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; + + /* + * 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); + + if (resp) { + /* + * 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 = (resp < 0) ? -resp : 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. @@ -682,6 +902,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) { @@ -689,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"); @@ -1244,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; } @@ -1290,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; @@ -1306,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; @@ -1341,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); } @@ -1514,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; } @@ -2007,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); @@ -2666,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; @@ -3167,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); @@ -3233,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; } @@ -3573,7 +3685,7 @@ int get_host_seed(void) { int h; - char *p = hostname; + const char *p; h = 407; for (p = hostname; *p != 0; ++p) @@ -3590,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; @@ -3607,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. */