X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Fsys-linux.c;h=85033d97124fc0c49e2b274b80277aefc8e7893e;hb=ab1b84327c5ecddf485734baa49b0b846f756c05;hp=8b538f0e4ac4431688ac421b7c622032d3f7589f;hpb=d0ccb87156c295da4f679f35936a976cea46d38c;p=ppp.git diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index 8b538f0..85033d9 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -121,6 +121,12 @@ #include #include +#ifdef INET6 +#include +#include +#include +#endif + #include "pppd.h" #include "fsm.h" #include "ipcp.h" @@ -2159,12 +2165,11 @@ get_if_hwaddr(u_char *addr, char *name) } /* - * get_first_ethernet - return the name of the first ethernet-style - * interface on this system. + * get_first_ether_hwaddr - get the hardware address for the first + * ethernet-style interface on this system. */ -static char first_ether_name[IF_NAMESIZE]; -char * -get_first_ethernet(void) +int +get_first_ether_hwaddr(u_char *addr) { struct if_nameindex *if_ni, *i; struct ifreq ifreq; @@ -2172,33 +2177,31 @@ get_first_ethernet(void) sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) - return NULL; + return -1; if_ni = if_nameindex(); if (!if_ni) { close(sock_fd); - return NULL; + return -1; } - first_ether_name[0] = 0; + ret = -1; for (i = if_ni; !(i->if_index == 0 && i->if_name == NULL); i++) { memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr)); strlcpy(ifreq.ifr_name, i->if_name, sizeof(ifreq.ifr_name)); ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq); if (ret >= 0 && ifreq.ifr_hwaddr.sa_family == ARPHRD_ETHER) { - strlcpy(first_ether_name, i->if_name, sizeof(first_ether_name)); + memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6); break; } + ret = -1; } if_freenameindex(if_ni); close(sock_fd); - if (!first_ether_name[0]) - return NULL; - - return first_ether_name; + return ret; } /******************************************************************** @@ -2821,6 +2824,135 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr) } #ifdef INET6 +static int append_peer_ipv6_address(unsigned int iface, struct in6_addr *local_addr, struct in6_addr *remote_addr) +{ + struct msghdr msg; + struct sockaddr_nl sa; + struct iovec iov; + struct nlmsghdr *nlmsg; + struct ifaddrmsg *ifa; + struct rtattr *local_rta; + struct rtattr *remote_rta; + char buf[NLMSG_LENGTH(sizeof(*ifa) + RTA_LENGTH(sizeof(*local_addr)) + RTA_LENGTH(sizeof(*remote_addr)))]; + ssize_t nlmsg_len; + struct nlmsgerr *errmsg; + int one; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + return 0; + + /* do not ask for error message content */ + one = 1; + setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &one, sizeof(one)); + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_pid = 0; + sa.nl_groups = 0; + + if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + close(fd); + return 0; + } + + memset(buf, 0, sizeof(buf)); + + nlmsg = (struct nlmsghdr *)buf; + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa) + RTA_LENGTH(sizeof(*local_addr)) + RTA_LENGTH(sizeof(*remote_addr))); + nlmsg->nlmsg_type = RTM_NEWADDR; + nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE; + nlmsg->nlmsg_seq = 1; + nlmsg->nlmsg_pid = 0; + + ifa = NLMSG_DATA(nlmsg); + ifa->ifa_family = AF_INET6; + ifa->ifa_prefixlen = 128; + ifa->ifa_flags = 0; + ifa->ifa_scope = RT_SCOPE_UNIVERSE; + ifa->ifa_index = iface; + + local_rta = IFA_RTA(ifa); + local_rta->rta_len = RTA_LENGTH(sizeof(*local_addr)); + local_rta->rta_type = IFA_LOCAL; + memcpy(RTA_DATA(local_rta), local_addr, sizeof(*local_addr)); + + remote_rta = (struct rtattr *)((char *)local_rta + local_rta->rta_len); + remote_rta->rta_len = RTA_LENGTH(sizeof(*remote_addr)); + remote_rta->rta_type = IFA_ADDRESS; + memcpy(RTA_DATA(remote_rta), remote_addr, sizeof(*remote_addr)); + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_pid = 0; + sa.nl_groups = 0; + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = nlmsg; + iov.iov_len = nlmsg->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof(sa); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (sendmsg(fd, &msg, 0) < 0) { + close(fd); + return 0; + } + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + nlmsg_len = recvmsg(fd, &msg, 0); + close(fd); + + if (nlmsg_len < 0) + return 0; + + if ((size_t)nlmsg_len < sizeof(*nlmsg)) { + errno = EINVAL; + return 0; + } + + nlmsg = (struct nlmsghdr *)buf; + + /* acknowledgment packet for NLM_F_ACK is NLMSG_ERROR */ + if (nlmsg->nlmsg_type != NLMSG_ERROR) { + errno = EINVAL; + return 0; + } + + if ((size_t)nlmsg_len < NLMSG_LENGTH(sizeof(*errmsg))) { + errno = EINVAL; + return 0; + } + + errmsg = NLMSG_DATA(nlmsg); + + /* error == 0 indicates success */ + if (errmsg->error == 0) + return 1; + + errno = -errmsg->error; + return 0; +} + /******************************************************************** * * sif6addr - Config the interface with an IPv6 link-local address @@ -2830,6 +2962,7 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) struct in6_ifreq ifr6; struct ifreq ifr; struct in6_rtmsg rt6; + struct in6_addr remote_addr; if (sock6_fd < 0) { errno = -sock6_fd; @@ -2854,17 +2987,28 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) return 0; } - /* Route to remote host */ - memset(&rt6, 0, sizeof(rt6)); - IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64); - rt6.rtmsg_flags = RTF_UP; - rt6.rtmsg_dst_len = 128; - rt6.rtmsg_ifindex = ifr.ifr_ifindex; - rt6.rtmsg_metric = 1; + if (kernel_version >= KVERSION(2,1,16)) { + /* Set remote peer address (and route for it) */ + IN6_LLADDR_FROM_EUI64(remote_addr, his_eui64); + if (!append_peer_ipv6_address(ifr.ifr_ifindex, &ifr6.ifr6_addr, &remote_addr)) { + error("sif6addr: setting remote peer address failed: %m"); + return 0; + } + } - if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) { - error("sif6addr: ioctl(SIOCADDRT): %m (line %d)", __LINE__); - return 0; + if (kernel_version < KVERSION(2,1,16)) { + /* Route to remote host */ + memset(&rt6, 0, sizeof(rt6)); + IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64); + rt6.rtmsg_flags = RTF_UP; + rt6.rtmsg_dst_len = 128; + rt6.rtmsg_ifindex = ifr.ifr_ifindex; + rt6.rtmsg_metric = 1; + + if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) { + error("sif6addr: ioctl(SIOCADDRT): %m (line %d)", __LINE__); + return 0; + } } return 1;