diff options
Diffstat (limited to 'accel-pppd/ifcfg.c')
-rw-r--r-- | accel-pppd/ifcfg.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/accel-pppd/ifcfg.c b/accel-pppd/ifcfg.c new file mode 100644 index 0000000..a1bb2e3 --- /dev/null +++ b/accel-pppd/ifcfg.c @@ -0,0 +1,186 @@ +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#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 ap_session *ses, const char *attr, const char *val) +{ + int fd; + char fname[PATH_MAX]; + + sprintf(fname, "/proc/sys/net/ipv6/conf/%s/%s", ses->ifname, attr); + fd = open(fname, O_WRONLY); + if (!fd) { + log_ppp_error("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 ap_session_ifup(struct ap_session *ses) +{ + struct ipv6db_addr_t *a; + struct ifreq ifr; + struct in6_ifreq ifr6; + struct npioctl np; + struct sockaddr_in addr; + struct ppp_t *ppp; + + triton_event_fire(EV_SES_ACCT_START, ses); + if (ses->stop_time) + return; + + triton_event_fire(EV_SES_PRE_UP, ses); + if (ses->stop_time) + return; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, ses->ifname); + + if (ses->ipv4) { + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ses->ipv4->addr; + memcpy(&ifr.ifr_addr,&addr,sizeof(addr)); + + if (ioctl(sock_fd, SIOCSIFADDR, &ifr)) + log_ppp_error("failed to set IPv4 address: %s\n", strerror(errno)); + + addr.sin_addr.s_addr = ses->ipv4->peer_addr; + memcpy(&ifr.ifr_dstaddr,&addr,sizeof(addr)); + + if (ioctl(sock_fd, SIOCSIFDSTADDR, &ifr)) + log_ppp_error("failed to set peer IPv4 address: %s\n", strerror(errno)); + } + + if (ses->ipv6) { + devconf(ses, "accept_ra", "0"); + devconf(ses, "autoconf", "0"); + devconf(ses, "forwarding", "1"); + + memset(&ifr6, 0, sizeof(ifr6)); + ifr6.ifr6_addr.s6_addr32[0] = htons(0xfe80); + *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ses->ipv6->intf_id; + ifr6.ifr6_prefixlen = 64; + ifr6.ifr6_ifindex = ses->ifindex; + + if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) + log_ppp_error("faild to set LL IPv6 address: %s\n", strerror(errno)); + + list_for_each_entry(a, &ses->ipv6->addr_list, entry) { + if (a->prefix_len == 128) + continue; + + build_addr(a, ses->ipv6->intf_id, &ifr6.ifr6_addr); + ifr6.ifr6_prefixlen = a->prefix_len; + + if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) + log_ppp_error("failed to add IPv6 address: %s\n", strerror(errno)); + } + } + + if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr)) + log_ppp_error("failed to get interface flags: %s\n", strerror(errno)); + + ifr.ifr_flags |= IFF_UP; + + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr)) + log_ppp_error("failed to set interface flags: %s\n", strerror(errno)); + + if (ses->ctrl->type != CTRL_TYPE_IPOE) { + ppp = container_of(ses, typeof(*ppp), ses); + if (ses->ipv4) { + np.protocol = PPP_IP; + np.mode = NPMODE_PASS; + + if (ioctl(ppp->unit_fd, PPPIOCSNPMODE, &np)) + log_ppp_error("failed to set NP (IPv4) mode: %s\n", strerror(errno)); + } + + if (ses->ipv6) { + np.protocol = PPP_IPV6; + np.mode = NPMODE_PASS; + + if (ioctl(ppp->unit_fd, PPPIOCSNPMODE, &np)) + log_ppp_error("failed to set NP (IPv6) mode: %s\n", strerror(errno)); + } + } + + ses->ctrl->started(ses); + + triton_event_fire(EV_SES_STARTED, ses); +} + +void __export ap_session_ifdown(struct ap_session *ses) +{ + 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, ses->ifname); + ioctl(sock_fd, SIOCSIFFLAGS, &ifr); + + if (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 (ses->ipv6) { + memset(&ifr6, 0, sizeof(ifr6)); + ifr6.ifr6_addr.s6_addr32[0] = htons(0xfe80); + *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ses->ipv6->intf_id; + ifr6.ifr6_prefixlen = 64; + ifr6.ifr6_ifindex = ses->ifindex; + + ioctl(sock6_fd, SIOCDIFADDR, &ifr6); + + list_for_each_entry(a, &ses->ipv6->addr_list, entry) { + if (a->prefix_len == 128) + continue; + + build_addr(a, ses->ipv6->intf_id, &ifr6.ifr6_addr); + ifr6.ifr6_prefixlen = a->prefix_len; + + ioctl(sock6_fd, SIOCDIFADDR, &ifr6); + } + } +} + |