mirror of
https://github.com/accel-ppp/accel-ppp.git
synced 2025-12-20 01:21:49 +08:00
Co-authored-by: Sergey V. Lobanov <svlobanov@users.noreply.github.com> Co-authored-by: Vladislav Grishenko <themiron@users.noreply.github.com>
379 lines
8.8 KiB
C
379 lines
8.8 KiB
C
#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/route.h>
|
|
#include "linux_ppp.h"
|
|
|
|
#include "triton.h"
|
|
#include "iputils.h"
|
|
#include "events.h"
|
|
#include "ppp.h"
|
|
#include "ipdb.h"
|
|
#include "log.h"
|
|
#include "backup.h"
|
|
#include "config.h"
|
|
#include "memdebug.h"
|
|
|
|
#define VRF_DEFAULT_NAME "default"
|
|
|
|
// 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);
|
|
}
|
|
|
|
void ap_session_ifup(struct ap_session *ses)
|
|
{
|
|
if (ses->ifname_rename) {
|
|
if (ap_session_rename(ses, ses->ifname_rename, -1)) {
|
|
ap_session_terminate(ses, TERM_NAS_ERROR, 0);
|
|
return;
|
|
}
|
|
_free(ses->ifname_rename);
|
|
ses->ifname_rename = NULL;
|
|
}
|
|
|
|
#ifdef HAVE_VRF
|
|
if (ses->vrf_name) {
|
|
if (ap_session_vrf(ses, ses->vrf_name, -1)) {
|
|
ap_session_terminate(ses, TERM_NAS_ERROR, 0);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
triton_event_fire(EV_SES_ACCT_START, ses);
|
|
|
|
if (ses->stop_time)
|
|
return;
|
|
|
|
if (!ses->acct_start) {
|
|
ses->acct_start = 1;
|
|
ap_session_accounting_started(ses);
|
|
}
|
|
}
|
|
|
|
void __export ap_session_accounting_started(struct ap_session *ses)
|
|
{
|
|
struct ipv6db_addr_t *a;
|
|
struct ifreq ifr;
|
|
//struct rtentry rt;
|
|
struct in6_ifreq ifr6;
|
|
struct npioctl np;
|
|
struct ppp_t *ppp;
|
|
|
|
if (ses->stop_time)
|
|
return;
|
|
|
|
if (--ses->acct_start)
|
|
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->ctrl->dont_ifcfg) {
|
|
if (net->sock_ioctl(SIOCGIFFLAGS, &ifr))
|
|
log_ppp_error("failed to get interface flags: %s\n", strerror(errno));
|
|
|
|
if (!(ifr.ifr_flags & IFF_UP)) {
|
|
ifr.ifr_flags |= IFF_UP;
|
|
|
|
if (net->sock_ioctl(SIOCSIFFLAGS, &ifr))
|
|
log_ppp_error("failed to set interface flags: %s\n", strerror(errno));
|
|
}
|
|
} else {
|
|
#ifdef USE_BACKUP
|
|
if (!ses->backup || !ses->backup->internal) {
|
|
#endif
|
|
if (ses->ipv4) {
|
|
if (ses->ipv4->mask == 0 || ses->ipv4->mask == 32) {
|
|
if (ipaddr_add_peer(ses->ifindex, ses->ipv4->addr, ses->ipv4->peer_addr))
|
|
log_ppp_error("failed to set IPv4 address: %s\n", strerror(errno));
|
|
} else {
|
|
if (ipaddr_add(ses->ifindex, ses->ipv4->addr, ses->ipv4->mask))
|
|
log_ppp_error("failed to set IPv4 address: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
|
|
if (ses->ipv6) {
|
|
net->enter_ns();
|
|
devconf(ses, "accept_ra", "0");
|
|
devconf(ses, "autoconf", "0");
|
|
devconf(ses, "forwarding", "1");
|
|
net->exit_ns();
|
|
|
|
memset(&ifr6, 0, sizeof(ifr6));
|
|
|
|
if (ses->ctrl->ppp) {
|
|
ifr6.ifr6_addr.s6_addr32[0] = htonl(0xfe800000);
|
|
memcpy(ifr6.ifr6_addr.s6_addr + 8, &ses->ipv6->intf_id, 8);
|
|
ifr6.ifr6_prefixlen = 64;
|
|
ifr6.ifr6_ifindex = ses->ifindex;
|
|
|
|
if (net->sock6_ioctl(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) {
|
|
a->installed = 0;
|
|
/*if (a->prefix_len < 128) {
|
|
build_ip6_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));
|
|
} else
|
|
if (ip6route_add(ses->ifindex, &a->addr, a->prefix_len, 0))
|
|
log_ppp_error("failed to add IPv6 route: %s\n", strerror(errno));*/
|
|
}
|
|
}
|
|
|
|
if (net->sock_ioctl(SIOCGIFFLAGS, &ifr))
|
|
log_ppp_error("failed to get interface flags: %s\n", strerror(errno));
|
|
|
|
ifr.ifr_flags |= IFF_UP;
|
|
|
|
if (net->sock_ioctl(SIOCSIFFLAGS, &ifr))
|
|
log_ppp_error("failed to set interface flags: %s\n", strerror(errno));
|
|
|
|
if (ses->ctrl->ppp) {
|
|
ppp = container_of(ses, typeof(*ppp), ses);
|
|
if (ses->ipv4) {
|
|
np.protocol = PPP_IP;
|
|
np.mode = ses->ctrl->ppp_npmode ? : NPMODE_PASS;
|
|
|
|
if (net->ppp_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 = ses->ctrl->ppp_npmode ? : NPMODE_PASS;
|
|
|
|
if (net->ppp_ioctl(ppp->unit_fd, PPPIOCSNPMODE, &np))
|
|
log_ppp_error("failed to set NP (IPv6) mode: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
#ifdef USE_BACKUP
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ses->ctrl->started(ses);
|
|
|
|
triton_event_fire(EV_SES_STARTED, ses);
|
|
triton_event_fire(EV_SES_POST_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;
|
|
|
|
if (ses->ifindex == -1)
|
|
return;
|
|
|
|
strcpy(ifr.ifr_name, ses->ifname);
|
|
|
|
if (!ses->ctrl->dont_ifcfg) {
|
|
ifr.ifr_flags = 0;
|
|
net->sock_ioctl(SIOCSIFFLAGS, &ifr);
|
|
}
|
|
|
|
if (ses->ipv4) {
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
memcpy(&ifr.ifr_addr,&addr,sizeof(addr));
|
|
net->sock_ioctl(SIOCSIFADDR, &ifr);
|
|
}
|
|
|
|
if (ses->ipv6) {
|
|
memset(&ifr6, 0, sizeof(ifr6));
|
|
ifr6.ifr6_ifindex = ses->ifindex;
|
|
|
|
if (ses->ctrl->ppp) {
|
|
ifr6.ifr6_addr.s6_addr32[0] = htonl(0xfe800000);
|
|
*(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ses->ipv6->intf_id;
|
|
ifr6.ifr6_prefixlen = 64;
|
|
net->sock6_ioctl(SIOCDIFADDR, &ifr6);
|
|
}
|
|
|
|
list_for_each_entry(a, &ses->ipv6->addr_list, entry) {
|
|
if (!a->installed)
|
|
continue;
|
|
if (a->prefix_len > 64)
|
|
ip6route_del(ses->ifindex, &a->addr, a->prefix_len, NULL, 0, 0);
|
|
else {
|
|
struct in6_addr addr;
|
|
memcpy(addr.s6_addr, &a->addr, 8);
|
|
memcpy(addr.s6_addr + 8, &ses->ipv6->intf_id, 8);
|
|
ip6addr_del(ses->ifindex, &addr, a->prefix_len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int __export ap_session_rename(struct ap_session *ses, const char *ifname, int len)
|
|
{
|
|
struct ifreq ifr;
|
|
int i, r, up = 0;
|
|
struct ap_net *ns = NULL;
|
|
char ns_name[256];
|
|
|
|
if (len == -1)
|
|
len = strlen(ifname);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (ifname[i] == '/') {
|
|
memcpy(ns_name, ifname, i);
|
|
ns_name[i] = 0;
|
|
|
|
ns = ap_net_open_ns(ns_name);
|
|
if (!ns)
|
|
return -1;
|
|
|
|
ifname += i + 1;
|
|
len -= i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (len >= IFNAMSIZ) {
|
|
log_ppp_error("cannot rename interface (name is too long)\n");
|
|
return -1;
|
|
}
|
|
|
|
if (len) {
|
|
strcpy(ifr.ifr_name, ses->ifname);
|
|
memcpy(ifr.ifr_newname, ifname, len);
|
|
ifr.ifr_newname[len] = 0;
|
|
|
|
r = net->sock_ioctl(SIOCSIFNAME, &ifr);
|
|
if (r < 0 && errno == EBUSY) {
|
|
net->sock_ioctl(SIOCGIFFLAGS, &ifr);
|
|
ifr.ifr_flags &= ~IFF_UP;
|
|
net->sock_ioctl(SIOCSIFFLAGS, &ifr);
|
|
|
|
memcpy(ifr.ifr_newname, ifname, len);
|
|
ifr.ifr_newname[len] = 0;
|
|
r = net->sock_ioctl(SIOCSIFNAME, &ifr);
|
|
|
|
up = 1;
|
|
}
|
|
|
|
if (r < 0) {
|
|
if (!ses->ifname_rename)
|
|
ses->ifname_rename = _strdup(ifr.ifr_newname);
|
|
else
|
|
log_ppp_warn("interface rename to %s failed: %s\n", ifr.ifr_newname, strerror(errno));
|
|
} else {
|
|
/* required since 2.6.27 */
|
|
if (strchr(ifr.ifr_newname, '%')) {
|
|
ifr.ifr_ifindex = ses->ifindex;
|
|
r = net->sock_ioctl(SIOCGIFNAME, &ifr);
|
|
if (r < 0) {
|
|
log_ppp_error("failed to get new interface name: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
len = strnlen(ifr.ifr_name, IFNAMSIZ);
|
|
if (len >= IFNAMSIZ) {
|
|
log_ppp_error("cannot rename interface (name is too long)\n");
|
|
return -1;
|
|
}
|
|
ifr.ifr_name[len] = 0;
|
|
ifname = ifr.ifr_name;
|
|
} else
|
|
ifname = ifr.ifr_newname;
|
|
|
|
log_ppp_info2("rename interface to '%s'\n", ifname);
|
|
memcpy(ses->ifname, ifname, len);
|
|
ses->ifname[len] = 0;
|
|
}
|
|
}
|
|
|
|
if (ns) {
|
|
if (net->move_link(ns, ses->ifindex)) {
|
|
log_ppp_error("failed to attach namespace\n");
|
|
ns->release(ns);
|
|
return -1;
|
|
}
|
|
ses->net = ns;
|
|
net = ns;
|
|
|
|
/* Refresh the index now that it is in a new namespace */
|
|
ses->ifindex = net->get_ifindex(ses->ifname);
|
|
log_ppp_info2("move to namespace %s\n", ns->name);
|
|
}
|
|
|
|
if (up) {
|
|
strcpy(ifr.ifr_name, ses->ifname);
|
|
ifr.ifr_flags |= IFF_UP;
|
|
net->sock_ioctl(SIOCSIFFLAGS, &ifr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_VRF
|
|
int __export ap_session_vrf(struct ap_session *ses, const char *vrf_name, int len)
|
|
{
|
|
if (len == -1)
|
|
len = strlen(vrf_name);
|
|
|
|
int vrf_ifindex = 0;
|
|
|
|
if (len) {
|
|
vrf_ifindex = ses->net->get_ifindex(vrf_name);
|
|
if (vrf_ifindex < 0) {
|
|
log_ppp_error("vrf '%s' not found\n", vrf_name);
|
|
return -1;
|
|
}
|
|
} else
|
|
vrf_name = VRF_DEFAULT_NAME;
|
|
|
|
if (ses->net->set_vrf(ses->ifindex, vrf_ifindex)) {
|
|
log_ppp_error("set vrf %s failed ifindex=%d, vrf_ifindex=%d\n", vrf_name, ses->ifindex, vrf_ifindex);
|
|
return -1;
|
|
} else
|
|
log_ppp_info2("set vrf %s\n", vrf_name);
|
|
|
|
if (!len) {
|
|
_free(ses->vrf_name);
|
|
ses->vrf_name = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif |