Files
ocserv/src/ip-util.c
T
2024-12-01 17:01:05 +01:00

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
}
}