mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-08-09 18:01:49 +08:00
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
314 lines
6.6 KiB
C
314 lines
6.6 KiB
C
/*
|
|
* Copyright (C) 2013-2016 Nikos Mavrogiannopoulos
|
|
* Copyright (C) 2015-2016 Red Hat, Inc.
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 "ip-util.h"
|
|
#include "common/common.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <talloc.h>
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
/* for inet_ntop */
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include "log.h"
|
|
|
|
int ip_cmp(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2)
|
|
{
|
|
if (((struct sockaddr *)s1)->sa_family == AF_INET) {
|
|
return memcmp(SA_IN_P(s1), SA_IN_P(s2), sizeof(struct in_addr));
|
|
} else { /* inet6 */
|
|
return memcmp(SA_IN6_P(s1), SA_IN6_P(s2),
|
|
sizeof(struct in6_addr));
|
|
}
|
|
}
|
|
|
|
/* returns an allocated string with the mask to apply for the prefix
|
|
*/
|
|
char *ipv4_prefix_to_strmask(void *pool, unsigned int prefix)
|
|
{
|
|
struct in_addr in;
|
|
char str[MAX_IP_STR];
|
|
|
|
if (prefix == 0 || prefix > 32)
|
|
return NULL;
|
|
|
|
in.s_addr = ntohl(((uint32_t)0xFFFFFFFF) << (32 - prefix));
|
|
if (inet_ntop(AF_INET, &in, str, sizeof(str)) == NULL)
|
|
return NULL;
|
|
|
|
return talloc_strdup(pool, str);
|
|
}
|
|
|
|
unsigned int ipv6_prefix_to_mask(struct in6_addr *in6, unsigned int prefix)
|
|
{
|
|
int i, j;
|
|
|
|
if (prefix == 0 || prefix > 128)
|
|
return 0;
|
|
|
|
memset(in6, 0x0, sizeof(*in6));
|
|
for (i = prefix, j = 0; i > 0; i -= 8, j++) {
|
|
if (i >= 8) {
|
|
in6->s6_addr[j] = 0xff;
|
|
} else {
|
|
in6->s6_addr[j] = (unsigned long)(0xffU << (8 - i));
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* check whether a route is on the expected format, and if it cannot be
|
|
* fixed, then returns a negative code.
|
|
*
|
|
* The expected format by clients for IPv4 is xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx, i.e.,
|
|
* this function converts xxx.xxx.xxx.xxx/prefix to the above for IPv4.
|
|
*/
|
|
int ip_route_sanity_check(void *pool, char **_route)
|
|
{
|
|
char *p;
|
|
unsigned int prefix;
|
|
char *route = *_route, *n;
|
|
char *slash_ptr, *pstr;
|
|
|
|
/* this check is valid for IPv4 only */
|
|
p = strchr(route, '.');
|
|
if (p == NULL)
|
|
return 0;
|
|
|
|
p = strchr(p, '/');
|
|
if (p == NULL) {
|
|
oc_syslog(
|
|
LOG_ERR,
|
|
"route '%s' in wrong format, use xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx\n",
|
|
route);
|
|
return -1;
|
|
}
|
|
slash_ptr = p;
|
|
p++;
|
|
|
|
/* if we are in dotted notation exit */
|
|
if (strchr(p, '.') != 0)
|
|
return 0;
|
|
|
|
/* we are most likely in the xxx.xxx.xxx.xxx/prefix format */
|
|
prefix = atoi(p);
|
|
|
|
pstr = ipv4_prefix_to_strmask(pool, prefix);
|
|
if (pstr == NULL) {
|
|
oc_syslog(LOG_ERR, "cannot figure format of route '%s'\n",
|
|
route);
|
|
return -1;
|
|
}
|
|
|
|
*slash_ptr = 0;
|
|
|
|
n = talloc_asprintf(pool, "%s/%s", route, pstr);
|
|
if (n == NULL) {
|
|
oc_syslog(LOG_ERR, "memory error\n");
|
|
return -1;
|
|
}
|
|
*_route = n;
|
|
|
|
talloc_free(pstr);
|
|
talloc_free(route);
|
|
return 0;
|
|
}
|
|
|
|
static int bit_count(uint32_t i)
|
|
{
|
|
int c = 0;
|
|
unsigned int seen_one = 0;
|
|
|
|
while (i > 0) {
|
|
if (i & 1) {
|
|
seen_one = 1;
|
|
c++;
|
|
} else {
|
|
if (seen_one) {
|
|
return -1;
|
|
}
|
|
}
|
|
i >>= 1;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
static int mask2prefix(struct in_addr mask)
|
|
{
|
|
return bit_count(ntohl(mask.s_addr));
|
|
}
|
|
|
|
static int ipv4_mask_to_int(const char *prefix)
|
|
{
|
|
int ret;
|
|
struct in_addr in;
|
|
|
|
ret = inet_pton(AF_INET, prefix, &in);
|
|
if (ret == 0)
|
|
return -1;
|
|
|
|
return mask2prefix(in);
|
|
}
|
|
|
|
/* Converts a route from xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx format, to
|
|
* xxx.xxx.xxx.xxx/prefix format.
|
|
*/
|
|
char *ipv4_route_to_cidr(void *pool, const char *route)
|
|
{
|
|
int prefix;
|
|
int len;
|
|
const char *p;
|
|
|
|
/* this check is valid for IPv4 only */
|
|
p = strchr(route, '.');
|
|
if (p == NULL)
|
|
return talloc_strdup(pool, route);
|
|
|
|
p = strchr(p, '/');
|
|
if (p == NULL) {
|
|
return NULL;
|
|
}
|
|
len = (ptrdiff_t)(p - route);
|
|
p++;
|
|
|
|
/* if we are in CIDR format exit */
|
|
if (strchr(p, '.') == 0)
|
|
return talloc_strdup(pool, route);
|
|
|
|
prefix = ipv4_mask_to_int(p);
|
|
if (prefix <= 0 || prefix > 32)
|
|
return NULL;
|
|
|
|
return talloc_asprintf(pool, "%.*s/%d", len, route, prefix);
|
|
}
|
|
|
|
char *human_addr2(const struct sockaddr *sa, socklen_t salen, void *_buf,
|
|
size_t buflen, unsigned int full)
|
|
{
|
|
char *save_buf = _buf;
|
|
char *buf = _buf;
|
|
size_t l;
|
|
const char *ret;
|
|
unsigned int port;
|
|
|
|
if (!buf || !buflen)
|
|
return NULL;
|
|
|
|
if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) {
|
|
return NULL;
|
|
}
|
|
|
|
if (salen == sizeof(struct sockaddr_in6)) {
|
|
port = (unsigned int)ntohs(
|
|
((struct sockaddr_in6 *)sa)->sin6_port);
|
|
|
|
if (full != 0 && port != 0) {
|
|
assert(buflen >
|
|
0); /* already checked, but to avoid regression */
|
|
*buf = '[';
|
|
buf++;
|
|
buflen--;
|
|
}
|
|
|
|
ret = inet_ntop(AF_INET6,
|
|
&((struct sockaddr_in6 *)sa)->sin6_addr, buf,
|
|
buflen);
|
|
} else {
|
|
port = (unsigned int)ntohs(
|
|
((struct sockaddr_in *)sa)->sin_port);
|
|
|
|
ret = inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr,
|
|
buf, buflen);
|
|
}
|
|
|
|
if (ret == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (full == 0)
|
|
goto finish;
|
|
|
|
l = strlen(buf);
|
|
buf += l;
|
|
buflen -= l;
|
|
|
|
if (salen == sizeof(struct sockaddr_in6) && port != 0 && buflen > 0) {
|
|
*buf = ']';
|
|
buf++;
|
|
buflen--;
|
|
}
|
|
|
|
if (port != 0 && buflen > 0) {
|
|
*buf = ':';
|
|
buf++;
|
|
buflen--;
|
|
|
|
snprintf(buf, buflen, "%u", port);
|
|
}
|
|
|
|
finish:
|
|
return save_buf;
|
|
}
|
|
|
|
void set_mtu_disc(int fd, int family, int val)
|
|
{
|
|
int y;
|
|
|
|
if (family == AF_INET6) {
|
|
y = val;
|
|
#if defined(IPV6_DONTFRAG)
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG,
|
|
(const void *)&y, sizeof(y)) < 0)
|
|
oc_syslog(LOG_INFO, "setsockopt(IPV6_DF) failed");
|
|
#elif defined(IPV6_MTU_DISCOVER)
|
|
if (val)
|
|
y = IP_PMTUDISC_DO;
|
|
else
|
|
y = IP_PMTUDISC_DONT;
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
|
|
(const void *)&y, sizeof(y)) < 0)
|
|
oc_syslog(LOG_INFO,
|
|
"setsockopt(IPV6_MTU_DISCOVER) failed");
|
|
#endif
|
|
} else {
|
|
y = val;
|
|
#if defined(IP_DONTFRAG)
|
|
if (setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, (const void *)&y,
|
|
sizeof(y)) < 0)
|
|
oc_syslog(LOG_INFO, "setsockopt(IP_DF) failed");
|
|
#elif defined(IP_MTU_DISCOVER)
|
|
if (val)
|
|
y = IP_PMTUDISC_DO;
|
|
else
|
|
y = IP_PMTUDISC_DONT;
|
|
if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER,
|
|
(const void *)&y, sizeof(y)) < 0)
|
|
oc_syslog(LOG_INFO,
|
|
"setsockopt(IP_MTU_DISCOVER) failed");
|
|
#endif
|
|
}
|
|
}
|