mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
195 lines
4.2 KiB
C
195 lines
4.2 KiB
C
/*
|
|
* Copyright (C) 2013-2015 Nikos Mavrogiannopoulos
|
|
* Copyright (C) 2015 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 <string.h>
|
|
#include <stdio.h>
|
|
#include <talloc.h>
|
|
/* for inet_ntop */
|
|
#include <arpa/inet.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 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 ipv6_prefix_to_mask(struct in6_addr *in6, unsigned 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 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) {
|
|
fprintf(stderr, "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) {
|
|
fprintf(stderr, "cannot figure format of route '%s'\n", route);
|
|
return -1;
|
|
}
|
|
|
|
*slash_ptr = 0;
|
|
|
|
n = talloc_asprintf(pool, "%s/%s", route, pstr);
|
|
if (n == NULL) {
|
|
fprintf(stderr, "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);
|
|
}
|