#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
-#include <termios.h>
#include <unistd.h>
/* This is in netdevice.h. However, this compile will fail miserably if
#if !defined(__GLIBC__) || __GLIBC__ >= 2
#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */
-#include <asm/ioctls.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/route.h>
#include <sys/locks.h>
#endif
-#ifndef BOTHER
-#define BOTHER 0010000
-#endif
-struct termios2 {
- unsigned int c_iflag;
- unsigned int c_oflag;
- unsigned int c_cflag;
- unsigned int c_lflag;
- unsigned char c_line;
- unsigned char c_cc[19];
- unsigned int c_ispeed;
- unsigned int c_ospeed;
-};
+/*
+ * Instead of system header file <termios.h> use local "termios_linux.h" header
+ * file as it provides additional support for arbitrary baud rates via BOTHER.
+ */
+#include "termios_linux.h"
#ifdef INET6
#ifndef _LINUX_IN6_H
if (x == 0 && req_ifname[0] != '\0') {
struct ifreq ifr;
- char t[MAXIFNAMELEN];
+ char t[IFNAMSIZ];
memset(&ifr, 0, sizeof(struct ifreq));
slprintf(t, sizeof(t), "%s%d", PPP_DRV_NAME, ifunit);
- strlcpy(ifr.ifr_name, t, IF_NAMESIZE);
- strlcpy(ifr.ifr_newname, req_ifname, IF_NAMESIZE);
+ strlcpy(ifr.ifr_name, t, IFNAMSIZ);
+ strlcpy(ifr.ifr_newname, req_ifname, IFNAMSIZ);
x = ioctl(sock_fd, SIOCSIFNAME, &ifr);
if (x < 0)
error("Couldn't rename interface %s to %s: %m", t, req_ifname);
cfsetospeed (&tios, speed);
cfsetispeed (&tios, speed);
speed = cfgetospeed(&tios);
+ baud_rate = baud_rate_of(speed);
+ } else {
+#ifdef BOTHER
+ tios.c_cflag &= ~CBAUD;
+ tios.c_cflag |= BOTHER;
+ tios.c_ospeed = inspeed;
+#ifdef IBSHIFT
+ /* B0 sets input baudrate to the output baudrate */
+ tios.c_cflag &= ~(CBAUD << IBSHIFT);
+ tios.c_cflag |= B0 << IBSHIFT;
+ tios.c_ispeed = inspeed;
+#endif
+ baud_rate = inspeed;
+#else
+ baud_rate = 0;
+#endif
}
- baud_rate = baud_rate_of(speed);
}
else {
speed = cfgetospeed(&tios);
baud_rate = baud_rate_of(speed);
- }
-
- while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
- if (errno != EINTR)
- fatal("tcsetattr: %m (line %d)", __LINE__);
- restore_term = 1;
-
-/* Most Linux architectures and drivers support arbitrary baud rate values via BOTHER */
-#ifdef TCGETS2
- if (!baud_rate) {
- struct termios2 tios2;
- if (ioctl(tty_fd, TCGETS2, &tios2) == 0) {
- if (inspeed) {
- tios2.c_cflag &= ~CBAUD;
- tios2.c_cflag |= BOTHER;
- tios2.c_ispeed = inspeed;
- tios2.c_ospeed = inspeed;
-#ifdef TCSETS2
- if (ioctl(tty_fd, TCSETS2, &tios2) == 0)
- baud_rate = inspeed;
+#ifdef BOTHER
+ if (!baud_rate)
+ baud_rate = tios.c_ospeed;
#endif
- } else {
- if ((tios2.c_cflag & CBAUD) == BOTHER && tios2.c_ospeed)
- baud_rate = tios2.c_ospeed;
- }
- }
}
-#endif
/*
* We can't proceed if the serial port baud rate is unknown,
else
fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
}
+
+ while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
+ if (errno != EINTR)
+ fatal("tcsetattr: %m (line %d)", __LINE__);
+ restore_term = 1;
}
/********************************************************************
IN6_LLADDR_FROM_EUI64(nlreq.addrs[0].addr, our_eui64);
nlreq.addrs[1].rta.rta_len = sizeof(nlreq.addrs[1]);
nlreq.addrs[1].rta.rta_type = IFA_ADDRESS;
- IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, his_eui64);
+
+ /*
+ * To set only local address, older kernel expects that local address is
+ * in IFA_ADDRESS field (not IFA_LOCAL). New kernels with support for peer
+ * address, ignore IFA_ADDRESS if is same as IFA_LOCAL. So for backward
+ * compatibility when setting only local address, set it via both IFA_LOCAL
+ * and IFA_ADDRESS fields. Same logic is implemented in 'ip address' command
+ * from iproute2 project.
+ */
+ if (!eui64_iszero(his_eui64))
+ IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, his_eui64);
+ else
+ IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, our_eui64);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
/* error == 0 indicates success, negative value is errno code */
if (nlresp.nlerr.error != 0) {
- error("sif6addr_rtnetlink: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__);
+ /*
+ * 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.
+ */
+ if (kernel_version >= KVERSION(3,11,0))
+ error("sif6addr_rtnetlink: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__);
return 0;
}
struct in6_ifreq ifr6;
struct ifreq ifr;
struct in6_rtmsg rt6;
+ int ret;
if (sock6_fd < 0) {
errno = -sock6_fd;
if (kernel_version >= KVERSION(2,1,16)) {
/* Set both local address and remote peer address (with route for it) via rtnetlink */
- return sif6addr_rtnetlink(ifr.ifr_ifindex, our_eui64, his_eui64);
+ ret = sif6addr_rtnetlink(ifr.ifr_ifindex, our_eui64, his_eui64);
} else {
+ ret = 0;
+ }
+
+ /*
+ * Linux kernel versions prior 3.11 do not support setting IPv6 peer address
+ * via rtnetlink. So if sif6addr_rtnetlink() fails then try old IOCTL method.
+ */
+ if (!ret) {
/* Local interface */
memset(&ifr6, 0, sizeof(ifr6));
IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
error("sif6addr: ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
return 0;
}
+ }
+ if (!ret && !eui64_iszero(his_eui64)) {
/*
* Linux kernel does not provide AF_INET6 ioctl SIOCSIFDSTADDR for
* setting remote peer host address, so set only route to remote host.
int
get_pty(int *master_fdp, int *slave_fdp, char *slave_name, int uid)
{
- int i, mfd, sfd = -1;
+ int i, mfd, ret, sfd = -1;
char pty_name[16];
struct termios tios;
pty_name[5] = 't';
sfd = open(pty_name, O_RDWR | O_NOCTTY, 0);
if (sfd >= 0) {
- fchown(sfd, uid, -1);
- fchmod(sfd, S_IRUSR | S_IWUSR);
+ ret = fchown(sfd, uid, -1);
+ if (ret != 0) {
+ warn("Couldn't change ownership of %s, %m", pty_name);
+ }
+ ret = fchmod(sfd, S_IRUSR | S_IWUSR);
+ if (ret != 0) {
+ warn("Couldn't change permissions of %s, %m", pty_name);
+ }
break;
}
close(mfd);