mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-10-13 18:12:03 +08:00
290 lines
6.8 KiB
C
290 lines
6.8 KiB
C
/*
|
|
* Copyright (C) 2013 Nikos Mavrogiannopoulos
|
|
*
|
|
* This file is part of ocserv.
|
|
*
|
|
* ocserv is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuTLS is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <sys/ioctl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <cloexec.h>
|
|
#include <ip-lease.h>
|
|
|
|
#if defined(HAVE_LINUX_IF_TUN_H)
|
|
# include <linux/if_tun.h>
|
|
#elif defined(HAVE_NET_IF_TUN_H)
|
|
# include <net/if_tun.h>
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
struct in6_ifreq {
|
|
struct in6_addr ifr6_addr;
|
|
__u32 ifr6_prefixlen;
|
|
unsigned int ifr6_ifindex;
|
|
};
|
|
#endif
|
|
|
|
#include <netdb.h>
|
|
#include <vpn.h>
|
|
#include <tun.h>
|
|
#include <main.h>
|
|
#include <ccan/list/list.h>
|
|
|
|
static int set_network_info( main_server_st* s, struct proc_st* proc)
|
|
{
|
|
int fd = -1, ret, e;
|
|
int fd6 = -1;
|
|
struct ifreq ifr;
|
|
|
|
if (proc->ipv4 && proc->ipv4->lip_len > 0 && proc->ipv4->rip_len > 0) {
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd == -1)
|
|
return -1;
|
|
|
|
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name);
|
|
memcpy(&ifr.ifr_addr, &proc->ipv4->lip, proc->ipv4->lip_len);
|
|
ifr.ifr_addr.sa_family = AF_INET;
|
|
|
|
ret = ioctl(fd, SIOCSIFADDR, &ifr);
|
|
if (ret != 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: Error setting IPv4: %s\n", proc->tun_lease.name, strerror(e));
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name);
|
|
memcpy(&ifr.ifr_dstaddr, &proc->ipv4->rip, proc->ipv4->rip_len);
|
|
ifr.ifr_dstaddr.sa_family = AF_INET;
|
|
|
|
ret = ioctl(fd, SIOCSIFDSTADDR, &ifr);
|
|
if (ret != 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv4: %s\n", proc->tun_lease.name, strerror(e));
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* bring interface up */
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
ifr.ifr_addr.sa_family = AF_INET;
|
|
ifr.ifr_flags |= IFF_UP;
|
|
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name);
|
|
|
|
ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
|
|
if (ret != 0) {
|
|
mslog(s, NULL, LOG_ERR, "%s: Could not bring up interface.\n", proc->tun_lease.name);
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (proc->ipv6 && proc->ipv6->lip_len > 0 && proc->ipv6->rip_len > 0) {
|
|
#ifdef __linux__
|
|
struct in6_ifreq ifr6;
|
|
unsigned idx;
|
|
|
|
fd6 = socket(AF_INET6, SOCK_STREAM, 0);
|
|
if (fd6 == -1) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: Error socket(AF_INET6): %s\n", proc->tun_lease.name, strerror(e));
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name);
|
|
|
|
ret = ioctl(fd6, SIOGIFINDEX, &ifr);
|
|
if (ret != 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: Error in SIOGIFINDEX: %s\n", proc->tun_lease.name, strerror(e));
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
idx = ifr.ifr_ifindex;
|
|
|
|
memset(&ifr6, 0, sizeof(ifr6));
|
|
memcpy(&ifr6.ifr6_addr, SA_IN6_P(&proc->ipv6->lip), SA_IN_SIZE(proc->ipv6->lip_len));
|
|
ifr6.ifr6_ifindex = idx;
|
|
ifr6.ifr6_prefixlen = 64;
|
|
|
|
ret = ioctl(fd6, SIOCSIFADDR, &ifr6);
|
|
if (ret != 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: Error setting IPv6: %s\n", proc->tun_lease.name, strerror(e));
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
ifr.ifr_addr.sa_family = AF_INET6;
|
|
ifr.ifr_flags |= IFF_UP;
|
|
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name);
|
|
|
|
ret = ioctl(fd6, SIOCSIFFLAGS, &ifr);
|
|
if (ret != 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: Could not bring up interface: %s\n", proc->tun_lease.name, strerror(e));
|
|
ret = -1;
|
|
}
|
|
|
|
#else
|
|
# warn "No IPv6 support on this platform"
|
|
#endif
|
|
}
|
|
|
|
if (proc->ipv6 == 0 && proc->ipv4 == 0) {
|
|
mslog(s, NULL, LOG_ERR, "%s: Could not set any IP.\n", proc->tun_lease.name);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (fd != -1)
|
|
close(fd);
|
|
if (fd6 != -1)
|
|
close(fd6);
|
|
return ret;
|
|
}
|
|
|
|
#include <ccan/hash/hash.h>
|
|
|
|
int open_tun(main_server_st* s, struct proc_st* proc)
|
|
{
|
|
int tunfd, ret, e;
|
|
struct ifreq ifr;
|
|
unsigned int t;
|
|
|
|
ret = get_ip_leases(s, proc);
|
|
if (ret < 0)
|
|
return ret;
|
|
snprintf(proc->tun_lease.name, sizeof(proc->tun_lease.name), "%s%%d", s->config->network.name);
|
|
|
|
/* No need to free the lease after this point.
|
|
*/
|
|
|
|
/* Obtain a free tun device */
|
|
#ifdef __linux__
|
|
tunfd = open("/dev/net/tun", O_RDWR);
|
|
if (tunfd < 0) {
|
|
int e = errno;
|
|
mslog(s, NULL, LOG_ERR, "Can't open /dev/net/tun: %s\n",
|
|
strerror(e));
|
|
return -1;
|
|
}
|
|
|
|
set_cloexec_flag (tunfd, 1);
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
|
|
|
memcpy(ifr.ifr_name, proc->tun_lease.name, IFNAMSIZ);
|
|
|
|
if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: TUNSETIFF: %s\n", proc->tun_lease.name, strerror(e));
|
|
goto fail;
|
|
}
|
|
memcpy(proc->tun_lease.name, ifr.ifr_name, IFNAMSIZ);
|
|
mslog(s, proc, LOG_INFO, "assigning tun device %s\n", proc->tun_lease.name);
|
|
|
|
/* we no longer use persistent tun */
|
|
if (ioctl(tunfd, TUNSETPERSIST, (void *)0) < 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: TUNSETPERSIST: %s\n", proc->tun_lease.name, strerror(e));
|
|
goto fail;
|
|
}
|
|
|
|
if (s->config->uid != -1) {
|
|
t = s->config->uid;
|
|
ret = ioctl(tunfd, TUNSETOWNER, t);
|
|
if (ret < 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_INFO, "%s: TUNSETOWNER: %s\n",
|
|
proc->tun_lease.name, strerror(e));
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
# ifdef TUNSETGROUP
|
|
if (s->config->gid != -1) {
|
|
t = s->config->uid;
|
|
ret = ioctl(tunfd, TUNSETGROUP, t);
|
|
if (ret < 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: TUNSETGROUP: %s\n",
|
|
proc->tun_lease.name, strerror(e));
|
|
goto fail;
|
|
}
|
|
}
|
|
# endif
|
|
#else /* freebsd */
|
|
tunfd = open("/dev/tun", O_RDWR);
|
|
if (tunfd < 0) {
|
|
int e = errno;
|
|
mslog(s, NULL, LOG_ERR, "Can't open /dev/tun: %s\n",
|
|
strerror(e));
|
|
return -1;
|
|
}
|
|
|
|
/* find device name */
|
|
{
|
|
struct stat st;
|
|
|
|
ret = fstat(tunfd, &st);
|
|
if (ret < 0) {
|
|
e = errno;
|
|
mslog(s, NULL, LOG_ERR, "%s: stat: %s\n", strerror(e));
|
|
goto fail;
|
|
}
|
|
|
|
snprintf(proc->tun_lease.name, sizeof(proc->tun_lease.name), "%s", devname(st.st_rdev, S_IFCHR));
|
|
}
|
|
|
|
set_cloexec_flag (tunfd, 1);
|
|
#endif
|
|
|
|
/* set IP/mask */
|
|
ret = set_network_info(s, proc);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
|
|
proc->tun_lease.fd = tunfd;
|
|
|
|
return 0;
|
|
fail:
|
|
close(tunfd);
|
|
return -1;
|
|
}
|