You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
606 lines
16 KiB
C
606 lines
16 KiB
C
/******************************************************************************
|
|
* file lib/management/sockunion.c
|
|
* author YuLiang
|
|
* version 1.0.0
|
|
* date 14-Sep-2021
|
|
* brief This file provides all the sockunion operation functions.
|
|
*
|
|
******************************************************************************
|
|
* Attention
|
|
*
|
|
* <h2><center>© COPYRIGHT(c) 2021 LandPower</center></h2>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of LandPower nor the names of its contributors may be used to
|
|
* endorse or promote products derived from this software without specific
|
|
* prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
******************************************************************************/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/in.h>
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "memory.h"
|
|
#include "sockunion.h"
|
|
|
|
/* Private define ------------------------------------------------------------*/
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
/* Maskbit. */
|
|
static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
|
|
0xf8, 0xfc, 0xfe, 0xff};
|
|
|
|
/* Static structure for IPv4 access_list's master. */
|
|
static access_master_t access_master_ipv4 =
|
|
{
|
|
{NULL, NULL},
|
|
{NULL, NULL},
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
#ifdef HAVE_IPV6
|
|
/* Static structure for IPv6 access_list's master. */
|
|
static access_master_t access_master_ipv6 =
|
|
{
|
|
{NULL, NULL},
|
|
{NULL, NULL},
|
|
NULL,
|
|
NULL,
|
|
};
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
|
|
/* Internal functions --------------------------------------------------------*/
|
|
/* Malloc prefix structure. */
|
|
static prefix_t *_prefix_new(void)
|
|
{
|
|
prefix_t *p = NULL;
|
|
|
|
p = XMALLOC(MTYPE_PREFIX, sizeof(prefix_t));
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Interface functions -------------------------------------------------------*/
|
|
/* Free prefix structure. */
|
|
void prefix_free(prefix_t *p)
|
|
{
|
|
XFREE(MTYPE_PREFIX, p);
|
|
}
|
|
|
|
/* Allocate new prefix_ipv4 structure. */
|
|
prefix_ipv4_t *prefix_ipv4_new(void)
|
|
{
|
|
prefix_ipv4_t *p = NULL;
|
|
|
|
/* Call prefix_new to allocate a full-size struct prefix to avoid problems
|
|
* where the prefix_ipv4_t is cast to struct prefix and unallocated
|
|
* bytes were being referenced (e.g. in structure assignments). */
|
|
p = (prefix_ipv4_t *)_prefix_new();
|
|
p->family = AF_INET;
|
|
|
|
return p;
|
|
}
|
|
|
|
#ifdef HAVE_IPV6
|
|
/* Allocate a new ip version 6 route */
|
|
prefix_ipv6_t *prefix_ipv6_new (void)
|
|
{
|
|
prefix_ipv6_t *p = NULL;
|
|
|
|
/* Allocate a full-size struct prefix to avoid problems with structure
|
|
* 8 size mismatches. */
|
|
p = (prefix_ipv6_t *)_prefix_new();
|
|
p->family = AF_INET6;
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
static access_master_t *_access_master_get(uint16_t afi)
|
|
{
|
|
if (AFI_IP == afi)
|
|
return &access_master_ipv4;
|
|
#ifdef HAVE_IPV6
|
|
else if (AFI_IP6 == afi)
|
|
return &access_master_ipv6;
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* If filter match to the prefix then return 1. */
|
|
static int _filter_match_cisco(filter_t *mfilter, prefix_t *p)
|
|
{
|
|
filter_cisco_t *filter = NULL;
|
|
struct in_addr mask;
|
|
uint32_t check_addr = 0;
|
|
uint32_t check_mask = 0;
|
|
|
|
filter = &mfilter->u.cfilter;
|
|
check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr;
|
|
|
|
if (filter->extended)
|
|
{
|
|
masklen2ip(p->prefixlen, &mask);
|
|
check_mask = mask.s_addr & ~filter->mask_mask.s_addr;
|
|
|
|
if (0 == memcmp(&check_addr, &filter->addr.s_addr, 4) &&
|
|
0 == memcmp(&check_mask, &filter->mask.s_addr, 4))
|
|
return 1;
|
|
}
|
|
else if (0 == memcmp(&check_addr, &filter->addr.s_addr, 4))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* If filter match to the prefix then return 1. */
|
|
static int _filter_match_zebra(filter_t *mfilter, prefix_t *p)
|
|
{
|
|
filter_zebra_t *filter= NULL;
|
|
|
|
filter = &mfilter->u.zfilter;
|
|
|
|
if (filter->prefix.family == p->family)
|
|
{
|
|
if (filter->exact)
|
|
{
|
|
if (filter->prefix.prefixlen == p->prefixlen)
|
|
return prefix_match(&filter->prefix, p);
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return prefix_match(&filter->prefix, p);
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Lookup access_list from list of access_list by name. */
|
|
access_list_t *access_list_lookup(uint16_t afi, const char *name)
|
|
{
|
|
access_list_t *access = NULL;
|
|
access_master_t *master = NULL;
|
|
|
|
if (NULL == name)
|
|
return NULL;
|
|
|
|
master = _access_master_get(afi);
|
|
if (NULL== master)
|
|
return NULL;
|
|
|
|
for(access = master->num.head; access; access = access->next)
|
|
if (0 == strcmp(access->name, name))
|
|
return access;
|
|
|
|
for(access = master->str.head; access; access = access->next)
|
|
if (0 == strcmp(access->name, name))
|
|
return access;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Apply access list to object (which should be prefix_t *). */
|
|
FILTER_TYPE_E access_list_apply(access_list_t *access, void *object)
|
|
{
|
|
filter_t *filter = NULL;
|
|
prefix_t *p = NULL;
|
|
|
|
p = (prefix_t *)object;
|
|
|
|
if (NULL == access)
|
|
return FILTER_DENY;
|
|
|
|
for (filter = access->head; filter; filter = filter->next)
|
|
{
|
|
if (filter->cisco)
|
|
{
|
|
if (_filter_match_cisco(filter, p))
|
|
return filter->type;
|
|
}
|
|
else
|
|
{
|
|
if (_filter_match_zebra(filter, p))
|
|
return filter->type;
|
|
}
|
|
}
|
|
|
|
return FILTER_DENY;
|
|
}
|
|
|
|
/* Convert masklen into IP address's netmask. */
|
|
void masklen2ip(int masklen, struct in_addr *netmask)
|
|
{
|
|
uint8_t *pnt = NULL;
|
|
int32_t bit = 0;
|
|
int32_t offset = 0;
|
|
|
|
memset(netmask, 0, sizeof(struct in_addr));
|
|
pnt = (unsigned char *)netmask;
|
|
|
|
offset = masklen / 8;
|
|
bit = masklen % 8;
|
|
|
|
while(offset--)
|
|
*pnt++ = 0xff;
|
|
|
|
if (bit)
|
|
*pnt = maskbit[bit];
|
|
}
|
|
|
|
/* If n includes p prefix then return 1 else return 0. */
|
|
int prefix_match(const prefix_t *n, const prefix_t *p)
|
|
{
|
|
int32_t offset = 0;
|
|
int32_t shift = 0;
|
|
const uint8_t *np = NULL;
|
|
const uint8_t *pp = NULL;
|
|
|
|
/* If n's prefix is longer than p's one return 0. */
|
|
if (n->prefixlen > p->prefixlen)
|
|
return 0;
|
|
|
|
/* Set both prefix's head pointer. */
|
|
np = (const u_char *)&n->u.prefix;
|
|
pp = (const u_char *)&p->u.prefix;
|
|
|
|
offset = n->prefixlen / PNBBY;
|
|
shift = n->prefixlen % PNBBY;
|
|
|
|
if (shift)
|
|
if (maskbit[shift] & (np[offset] ^ pp[offset]))
|
|
return 0;
|
|
|
|
while (offset--)
|
|
if (np[offset] != pp[offset])
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Convert IPv4 compatible IPv6 address to IPv4 address. */
|
|
static void _sockunion_normalise_mapped(SOCKUNION_U *su)
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
struct sockaddr_in sin;
|
|
|
|
if (AF_INET6 == su->sa.sa_family &&
|
|
IN6_IS_ADDR_V4MAPPED(&su->sin6.sin6_addr))
|
|
{
|
|
memset(&sin, 0, sizeof(struct sockaddr_in));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = su->sin6.sin6_port;
|
|
memcpy(&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
|
|
memcpy(su, &sin, sizeof(struct sockaddr_in));
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
|
|
int str2sockunion(const char *str, SOCKUNION_U *su)
|
|
{
|
|
int ret = 0;
|
|
|
|
memset(su, 0, sizeof(SOCKUNION_U));
|
|
|
|
ret = inet_pton(AF_INET, str, &su->sin.sin_addr);
|
|
/* Valid IPv4 address format. */
|
|
if (ret > 0)
|
|
{
|
|
su->sin.sin_family = AF_INET;
|
|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
|
|
su->sin.sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_IPV6
|
|
ret = inet_pton(AF_INET6, str, &su->sin6.sin6_addr);
|
|
/* Valid IPv6 address format. */
|
|
if (ret > 0)
|
|
{
|
|
su->sin6.sin6_family = AF_INET6;
|
|
#ifdef SIN6_LEN
|
|
su->sin6.sin6_len = sizeof(struct sockaddr_in6);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
const char *sockunion2str(SOCKUNION_U *su, char *buf, size_t len)
|
|
{
|
|
if (AF_INET == su->sa.sa_family)
|
|
return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len);
|
|
#ifdef HAVE_IPV6
|
|
else if(AF_INET6 == su->sa.sa_family)
|
|
return inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, len);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Return accepted new socket file descriptor. */
|
|
int sockunion_accept(int sock, SOCKUNION_U *su)
|
|
{
|
|
socklen_t len;
|
|
int client_sock;
|
|
|
|
len = sizeof(SOCKUNION_U);
|
|
client_sock = accept(sock, (struct sockaddr*)su, &len);
|
|
|
|
_sockunion_normalise_mapped(su);
|
|
return client_sock;
|
|
}
|
|
|
|
int set_nonblocking(int fd)
|
|
{
|
|
int flags = 0;
|
|
|
|
/* According to the Single UNIX Spec, the return value for F_GETFL should
|
|
* never be negative. */
|
|
if ((flags = fcntl(fd, F_GETFL)) < 0)
|
|
{
|
|
printh("fcntl(F_GETFL) failed for fd %d: %s", fd, safe_strerror(errno));
|
|
return -1;
|
|
}
|
|
if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) < 0)
|
|
{
|
|
printh("fcntl failed setting fd %d non-blocking: %s", fd, safe_strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Utility function of convert between struct prefix <=> union sockunion. */
|
|
prefix_t *sockunion2hostprefix(const SOCKUNION_U *su)
|
|
{
|
|
if (su->sa.sa_family == AF_INET)
|
|
{
|
|
prefix_ipv4_t *p = NULL;
|
|
|
|
p = prefix_ipv4_new();
|
|
p->family = AF_INET;
|
|
p->prefix = su->sin.sin_addr;
|
|
p->prefixlen = IPV4_MAX_BITLEN;
|
|
return (prefix_t*)p;
|
|
}
|
|
|
|
#ifdef HAVE_IPV6
|
|
if (su->sa.sa_family == AF_INET6)
|
|
{
|
|
prefix_ipv6_t *p = NULL;
|
|
|
|
p = prefix_ipv6_new();
|
|
p->family = AF_INET6;
|
|
p->prefixlen = IPV6_MAX_BITLEN;
|
|
memcpy(&p->prefix, &su->sin6.sin6_addr, sizeof(struct in6_addr));
|
|
return (prefix_t*)p;
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *sockunion_su2str(SOCKUNION_U *su)
|
|
{
|
|
char str[SU_ADDRSTRLEN] = {0};
|
|
|
|
switch (su->sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
inet_ntop(AF_INET, &su->sin.sin_addr, str, sizeof(str));
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
inet_ntop(AF_INET6, &su->sin6.sin6_addr, str, sizeof(str));
|
|
break;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
|
|
return XSTRDUP(MTYPE_PREFIX, str);
|
|
}
|
|
|
|
/* Make socket from sockunion union. */
|
|
int sockunion_stream_socket(SOCKUNION_U *su)
|
|
{
|
|
int32_t sock = 0;
|
|
|
|
if (0 == su->sa.sa_family)
|
|
su->sa.sa_family = AF_INET_UNION;
|
|
|
|
sock = socket(su->sa.sa_family, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
printh("can't make socket sockunion_stream_socket");
|
|
|
|
return sock;
|
|
}
|
|
|
|
int sockunion_reuseaddr(int sock)
|
|
{
|
|
int ret;
|
|
int on = 1;
|
|
|
|
ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
|
|
if (ret < 0)
|
|
{
|
|
printh("can't set sockopt SO_REUSEADDR to socket %d", sock);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Bind socket to specified address. */
|
|
int sockunion_bind(int sock, SOCKUNION_U *su, unsigned short port, SOCKUNION_U *su_addr)
|
|
{
|
|
int size = 0;
|
|
int ret = 0;
|
|
|
|
if (AF_INET == su->sa.sa_family)
|
|
{
|
|
size = sizeof(struct sockaddr_in);
|
|
su->sin.sin_port = htons(port);
|
|
if (NULL == su_addr)
|
|
su->sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else if(AF_INET6 == su->sa.sa_family)
|
|
{
|
|
size = sizeof(struct sockaddr_in6);
|
|
su->sin6.sin6_port = htons(port);
|
|
if (NULL == su_addr)
|
|
memset (&su->sin6.sin6_addr, 0, sizeof(struct in6_addr));
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
|
|
ret = bind(sock, (struct sockaddr *)su, size);
|
|
if (ret < 0)
|
|
printh("xxcan't bind socket : %s\n", safe_strerror(errno));
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sockunion_ip_set(char *name, unsigned int addr)
|
|
{
|
|
int fd = 0;
|
|
struct ifreq ifr;
|
|
struct sockaddr_in *sin = NULL;
|
|
|
|
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (fd < 0)
|
|
{
|
|
printh("Ip set socket error!\r\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strcpy(ifr.ifr_name, name);
|
|
sin = (struct sockaddr_in*)&ifr.ifr_addr;
|
|
sin->sin_family = AF_INET;
|
|
|
|
/* IP地址 */
|
|
sin->sin_addr.s_addr = addr;
|
|
if (ioctl(fd, SIOCSIFADDR, &ifr) < 0)
|
|
{
|
|
close(fd);
|
|
printh("Ip set ioctl SIOCSIFADDR error!\r\n");
|
|
return -2;
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
int sockunion_mask_set(char *name, unsigned int mask)
|
|
{
|
|
int fd = 0;
|
|
struct ifreq ifr;
|
|
struct sockaddr_in *sin = NULL;
|
|
|
|
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (fd < 0)
|
|
{
|
|
printh("socket error\r\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strcpy(ifr.ifr_name, name);
|
|
sin = (struct sockaddr_in*)&ifr.ifr_addr;
|
|
sin->sin_family = AF_INET;
|
|
|
|
/* 子网掩码 */
|
|
sin->sin_addr.s_addr = mask;
|
|
if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0)
|
|
{
|
|
close(fd);
|
|
printh("ioctl\r\n");
|
|
return -3;
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
int sockunion_gw_set(char *name, unsigned int gateway, unsigned int gateway_old)
|
|
{
|
|
int fd = 0;
|
|
struct rtentry rt;
|
|
int rv = 0;
|
|
|
|
fd = socket(PF_INET, SOCK_DGRAM, 0);
|
|
if (fd < 0)
|
|
{
|
|
printh("Gateway set socket error!\r\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Delete existing defalt gateway */
|
|
memset(&rt, 0, sizeof(rt));
|
|
rt.rt_dst.sa_family = AF_INET;
|
|
((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = 0;
|
|
rt.rt_gateway.sa_family = AF_INET;
|
|
((struct sockaddr_in *)&rt.rt_gateway)->sin_addr.s_addr = gateway_old;
|
|
rt.rt_genmask.sa_family = AF_INET;
|
|
((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = 0;
|
|
rt.rt_flags = RTF_UP;
|
|
rv = ioctl(fd, SIOCDELRT, &rt);
|
|
|
|
/* Set default gateway */
|
|
if ((rv == 0 || errno == ESRCH) && gateway)
|
|
{
|
|
memset(&rt, 0, sizeof(rt));
|
|
rt.rt_dst.sa_family = AF_INET;
|
|
((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = 0;
|
|
rt.rt_gateway.sa_family = AF_INET;
|
|
((struct sockaddr_in *)&rt.rt_gateway)->sin_addr.s_addr = gateway;
|
|
rt.rt_genmask.sa_family = AF_INET;
|
|
((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = 0;
|
|
rt.rt_flags = RTF_UP | RTF_GATEWAY;
|
|
rv = ioctl(fd, SIOCADDRT, &rt);
|
|
}
|
|
|
|
close(fd);
|
|
return rv;
|
|
}
|
|
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/
|