#include #include #include #include #include #include #include #include #include #include #include #include "linux_ppp.h" #include "triton.h" #include "events.h" #include "ppp.h" #include "ipdb.h" #include "log.h" // from /usr/include/linux/ipv6.h struct in6_ifreq { struct in6_addr ifr6_addr; __u32 ifr6_prefixlen; int ifr6_ifindex; }; static void devconf(struct ppp_t *ppp, const char *attr, const char *val) { int fd; char fname[PATH_MAX]; sprintf(fname, "/proc/sys/net/ipv6/conf/%s/%s", ppp->ifname, attr); fd = open(fname, O_WRONLY); if (!fd) { log_ppp_error("ppp: failed to open '%s': %s\n", fname, strerror(errno)); return; } write(fd, val, strlen(val)); close(fd); } static void build_addr(struct ipv6db_addr_t *a, uint64_t intf_id, struct in6_addr *addr) { memcpy(addr, &a->addr, sizeof(*addr)); if (a->prefix_len <= 64) *(uint64_t *)(addr->s6_addr + 8) = intf_id; else *(uint64_t *)(addr->s6_addr + 8) |= intf_id & ((1 << (128 - a->prefix_len)) - 1); } void ppp_ifup(struct ppp_t *ppp) { struct ipv6db_addr_t *a; struct ifreq ifr; struct in6_ifreq ifr6; struct npioctl np; struct sockaddr_in addr; triton_event_fire(EV_SES_ACCT_START, ppp); if (ppp->stop_time) return; triton_event_fire(EV_SES_PRE_UP, ppp); if (ppp->stop_time) return; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ppp->ifname); if (ppp->ses.ipv4) { memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = ppp->ses.ipv4->addr; memcpy(&ifr.ifr_addr,&addr,sizeof(addr)); if (ioctl(sock_fd, SIOCSIFADDR, &ifr)) log_ppp_error("ppp: failed to set IPv4 address: %s\n", strerror(errno)); addr.sin_addr.s_addr = ppp->ses.ipv4->peer_addr; memcpy(&ifr.ifr_dstaddr,&addr,sizeof(addr)); if (ioctl(sock_fd, SIOCSIFDSTADDR, &ifr)) log_ppp_error("ppp: failed to set peer IPv4 address: %s\n", strerror(errno)); } if (ppp->ses.ipv6) { devconf(ppp, "accept_ra", "0"); devconf(ppp, "autoconf", "0"); devconf(ppp, "forwarding", "1"); memset(&ifr6, 0, sizeof(ifr6)); ifr6.ifr6_addr.s6_addr32[0] = htons(0xfe80); *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ppp->ses.ipv6->intf_id; ifr6.ifr6_prefixlen = 64; ifr6.ifr6_ifindex = ppp->ifindex; if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) log_ppp_error("ppp: failed to set LL IPv6 address: %s\n", strerror(errno)); list_for_each_entry(a, &ppp->ses.ipv6->addr_list, entry) { if (a->prefix_len == 128) continue; build_addr(a, ppp->ses.ipv6->intf_id, &ifr6.ifr6_addr); ifr6.ifr6_prefixlen = a->prefix_len; if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) log_ppp_error("ppp: failed to add IPv6 address: %s\n", strerror(errno)); } } if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr)) log_ppp_error("ppp: failed to get interface flags: %s\n", strerror(errno)); ifr.ifr_flags |= IFF_UP | IFF_POINTOPOINT; if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr)) log_ppp_error("ppp: failed to set interface flags: %s\n", strerror(errno)); if (ppp->ses.ipv4) { np.protocol = PPP_IP; np.mode = NPMODE_PASS; if (ioctl(ppp->unit_fd, PPPIOCSNPMODE, &np)) log_ppp_error("ppp: failed to set NP (IPv4) mode: %s\n", strerror(errno)); } if (ppp->ses.ipv6) { np.protocol = PPP_IPV6; np.mode = NPMODE_PASS; if (ioctl(ppp->unit_fd, PPPIOCSNPMODE, &np)) log_ppp_error("ppp: failed to set NP (IPv6) mode: %s\n", strerror(errno)); } ppp->ses.ctrl->started(ppp); triton_event_fire(EV_SES_STARTED, ppp); } void __export ppp_ifdown(struct ppp_t *ppp) { struct ifreq ifr; struct sockaddr_in addr; struct in6_ifreq ifr6; struct ipv6db_addr_t *a; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ppp->ifname); ioctl(sock_fd, SIOCSIFFLAGS, &ifr); if (ppp->ses.ipv4) { memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; memcpy(&ifr.ifr_addr,&addr,sizeof(addr)); ioctl(sock_fd, SIOCSIFADDR, &ifr); } if (ppp->ses.ipv6) { memset(&ifr6, 0, sizeof(ifr6)); ifr6.ifr6_addr.s6_addr32[0] = htons(0xfe80); *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ppp->ses.ipv6->intf_id; ifr6.ifr6_prefixlen = 64; ifr6.ifr6_ifindex = ppp->ifindex; ioctl(sock6_fd, SIOCDIFADDR, &ifr6); list_for_each_entry(a, &ppp->ses.ipv6->addr_list, entry) { if (a->prefix_len == 128) continue; build_addr(a, ppp->ses.ipv6->intf_id, &ifr6.ifr6_addr); ifr6.ifr6_prefixlen = a->prefix_len; ioctl(sock6_fd, SIOCDIFADDR, &ifr6); } } }