+/********************************************************************
+ *
+ * sifvjcomp - config tcp header compression
+ */
+
+int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
+{
+ u_int x;
+
+ if (vjcomp) {
+ if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
+ error("Couldn't set up TCP header compression: %m");
+ vjcomp = 0;
+ }
+ }
+
+ x = (vjcomp? SC_COMP_TCP: 0) | (cidcomp? 0: SC_NO_TCP_CCID);
+ modify_flags(ppp_dev_fd, SC_COMP_TCP|SC_NO_TCP_CCID, x);
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+
+int sifup(int u)
+{
+ int ret;
+
+ if ((ret = setifstate(u, 1)))
+ if_is_up++;
+
+ return ret;
+}
+
+/********************************************************************
+ *
+ * sifdown - Disable the indicated protocol and config the interface
+ * down if there are no remaining protocols.
+ */
+
+int sifdown (int u)
+{
+ if (if_is_up && --if_is_up > 0)
+ return 1;
+
+#ifdef INET6
+ if (if6_is_up)
+ return 1;
+#endif /* INET6 */
+
+ return setifstate(u, 0);
+}
+
+#ifdef INET6
+/********************************************************************
+ *
+ * sif6up - Config the interface up for IPv6
+ */
+
+int sif6up(int u)
+{
+ int ret;
+
+ if ((ret = setifstate(u, 1)))
+ if6_is_up = 1;
+
+ return ret;
+}
+
+/********************************************************************
+ *
+ * sif6down - Disable the IPv6CP protocol and config the interface
+ * down if there are no remaining protocols.
+ */
+
+int sif6down (int u)
+{
+ if6_is_up = 0;
+
+ if (if_is_up)
+ return 1;
+
+ return setifstate(u, 0);
+}
+#endif /* INET6 */
+
+/********************************************************************
+ *
+ * setifstate - Config the interface up or down
+ */
+
+static int setifstate (int u, int state)
+{
+ struct ifreq ifr;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
+ return 0;
+ }
+
+ if (state)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+ ifr.ifr_flags |= IFF_POINTOPOINT;
+ if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+
+int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
+ u_int32_t net_mask)
+{
+ struct ifreq ifr;
+ struct rtentry rt;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ memset (&rt, '\0', sizeof (rt));
+
+ SET_SA_FAMILY (ifr.ifr_addr, AF_INET);
+ SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+ SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
+
+ strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+/*
+ * Set our IP address
+ */
+ SIN_ADDR(ifr.ifr_addr) = our_adr;
+ if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (errno != EEXIST) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
+ }
+ else {
+ warn("ioctl(SIOCSIFADDR): Address already exists");
+ }
+ return (0);
+ }
+/*
+ * Set the gateway address
+ */
+ if (his_adr != 0) {
+ SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
+ if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__);
+ return (0);
+ }
+ }
+/*
+ * Set the netmask.
+ * For recent kernels, force the netmask to 255.255.255.255.
+ */
+ if (kernel_version >= KVERSION(2,1,16))
+ net_mask = ~0L;
+ if (net_mask != 0) {
+ SIN_ADDR(ifr.ifr_netmask) = net_mask;
+ if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFNETMASK): %m (line %d)", __LINE__);
+ return (0);
+ }
+ }
+/*
+ * Add the device route
+ */
+ if (kernel_version < KVERSION(2,1,16)) {
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+ rt.rt_dev = ifname;
+
+ SIN_ADDR(rt.rt_gateway) = 0L;
+ SIN_ADDR(rt.rt_dst) = his_adr;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = -1L;
+ }
+
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCADDRT) device route: %m (line %d)", __LINE__);
+ return (0);
+ }
+ }
+
+ /* set ip_dynaddr in demand mode if address changes */
+ if (demand && tune_kernel && !dynaddr_set
+ && our_old_addr && our_old_addr != our_adr) {
+ /* set ip_dynaddr if possible */
+ char *path;
+ int fd;
+
+ path = path_to_procfs("/sys/net/ipv4/ip_dynaddr");
+ if (path != 0 && (fd = open(path, O_WRONLY)) >= 0) {
+ if (write(fd, "1", 1) != 1)
+ error("Couldn't enable dynamic IP addressing: %m");
+ close(fd);
+ }
+ dynaddr_set = 1; /* only 1 attempt */
+ }
+ our_old_addr = 0;
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+
+int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
+{
+ struct ifreq ifr;
+
+ if (kernel_version < KVERSION(2,1,16)) {
+/*
+ * Delete the route through the device
+ */
+ struct rtentry rt;
+ memset (&rt, '\0', sizeof (rt));
+
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+ rt.rt_dev = ifname;
+
+ SIN_ADDR(rt.rt_gateway) = 0;
+ SIN_ADDR(rt.rt_dst) = his_adr;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = -1L;
+ }
+
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp() && ! ok_error (errno))
+ error("ioctl(SIOCDELRT) device route: %m (line %d)", __LINE__);
+ return (0);
+ }
+ }
+
+ /* This way it is possible to have an IPX-only or 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));
+
+ if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno)) {
+ error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
+ return 0;
+ }
+ }
+
+ our_old_addr = our_adr;
+
+ return 1;
+}
+
+#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;