Commit 046e4b0a authored by Marek Vavrusa's avatar Marek Vavrusa

Initial implementation of ACL and universal address structure.

Commit refs #789.
parent faedc128
......@@ -33,6 +33,10 @@ src/common/evsched.c
src/common/evsched.h
src/common/errors.h
src/common/errors.c
src/common/acl.c
src/common/acl.h
src/common/sockaddr.h
src/common/sockaddr.c
src/dnslib/dnslib-common.h
src/dnslib/dname.h
src/dnslib/dname.c
......
......@@ -132,6 +132,10 @@ libknot_la_SOURCES = \
common/evsched.c \
common/errors.h \
common/errors.c \
common/acl.h \
common/acl.c \
common/sockaddr.h \
common/sockaddr.c \
knot/stat/gatherer.c \
knot/stat/stat.c \
knot/stat/gatherer.h \
......
#include <string.h>
#include <stdlib.h>
#include "common/acl.h"
static int acl_compare(void *k1, void *k2)
{
sockaddr_t* a1 = (sockaddr_t *)k1;
sockaddr_t* a2 = (sockaddr_t *)k2;
/* Check different length, IPv4 goes first. */
int ldiff = a1->len - a2->len;
if (ldiff != 0) {
return ldiff < 0 ? -1 : 1;
}
/* Compare integers if IPv4. */
if (a1->len == sizeof(struct sockaddr_in)) {
/* Compare address. */
ldiff = a1->addr4.sin_addr.s_addr - a2->addr4.sin_addr.s_addr;
if (ldiff != 0) {
return ldiff < 0 ? -1 : 1;
}
/* Compare ports on address match. */
ldiff = ntohs(a1->addr4.sin_port) - ntohs(a2->addr4.sin_port);
return ldiff < 0 ? -1 : 1;
}
/* IPv6 matching. */
/*! \todo */
return 0;
}
acl_t *acl_new(acl_rule_t default_rule, const char *name)
{
/* Trailing '\0' for NULL name. */
size_t name_len = 1;
if (name) {
name_len += strlen(name);
} else {
name = "";
}
/* Allocate memory for ACL. */
acl_t* acl = malloc(sizeof(acl_t) + name_len);
if (!acl) {
return 0;
}
/* Initialize skip list. */
acl->rules = skip_create_list(acl_compare);
if (!acl->rules) {
free(acl);
return 0;
}
/* Initialize. */
memcpy(&acl->name, name, name_len);
acl->default_rule = default_rule;
return acl;
}
void acl_delete(acl_t **acl)
{
if (!acl) {
return;
}
/* Truncate rules. */
acl_truncate(*acl);
/* Free ACL. */
free(*acl);
*acl = 0;
}
int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule)
{
if (!acl || !addr || rule < 0) {
return ACL_ERROR;
}
/* Insert into skip list. */
void *key = malloc(sizeof(sockaddr_t));
memcpy(key,addr, sizeof(sockaddr_t));
skip_insert(acl->rules, key, (void*)((ssize_t)rule + 1), 0);
return ACL_ACCEPT;
}
int acl_match(acl_t *acl, sockaddr_t* addr)
{
if (!acl || !addr) {
return ACL_ERROR;
}
/* Return default rule if not found.
* Conversion to the same length integer is made,
* but we can be sure, that the value range is within <-1,1>.
*/
ssize_t val = ((ssize_t)skip_find(acl->rules, addr)) - 1;
if(val < 0) {
return acl->default_rule;
}
/* Return stored rule if found. */
return (int)val;
}
int acl_truncate(acl_t *acl)
{
if (!acl) {
return ACL_ERROR;
}
/* Destroy all rules. */
skip_destroy_list(&acl->rules, free, 0);
return ACL_ACCEPT;
}
/*!
* \file acl.h
*
* \author Marek Vavrusa <marek.vavrusa@nic.cz>
*
* \brief Access control lists.
*
* An access control list is a named structure
* for efficient IP address and port matching.
*
* \addtogroup common_lib
* @{
*/
#ifndef _KNOT_ACL_H_
#define _KNOT_ACL_H_
#include "common/skip-list.h"
#include "common/sockaddr.h"
/*! \brief ACL rules types. */
typedef enum acl_rule_t {
ACL_ERROR = -1,
ACL_DENY = 0,
ACL_ACCEPT = 1
} acl_rule_t;
/*! \brief ACL structure. */
typedef struct acl_t {
acl_rule_t default_rule;
skip_list_t *rules;
const char name[];
} acl_t;
/*!
* \brief Create a new ACL.
*
* \param default_rule Default rule for address matching.
* \param name ACL symbolic name (or NULL).
*
* \retval New ACL instance when successful.
* \retval NULL on errors.
*/
acl_t *acl_new(acl_rule_t default_rule, const char *name);
/*!
* \brief Delete ACL structure.
*
* \param acl Pointer to ACL instance.
*/
void acl_delete(acl_t **acl);
/*!
* \brief Create new ACL rule.
*
* \todo Support address subnets.
*
* \param acl Pointer to ACL instance.
* \param addr IP address (will be duplicated).
* \param rule Rule.
*
* \retval ACL_ACCEPT if successful.
* \retval ACP_ERROR on error.
*/
int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule);
/*!
* \brief Match address against ACL.
*
* \param acl Pointer to ACL instance.
* \param addr IP address.
*
* \retval ACL_ACCEPT if the address is accepted.
* \retval ACL_DENY if the address is not accepted.
* \retval ACP_ERROR on error.
*/
int acl_match(acl_t *acl, sockaddr_t* addr);
/*!
* \brief Truncate ACL.
*
* All but the default rule will be dropped.
*
* \param acl Pointer to ACL instance.
*
* \retval ACL_ACCEPT if successful.
* \retval ACP_ERROR on error.
*/
int acl_truncate(acl_t *acl);
/*!
* \brief Return ACL name.
*
* \param acl Pointer to ACL instance.
*
* \retval ACL name.
*/
static inline const char* acl_name(acl_t *acl) {
return acl->name;
}
#endif /* _KNOT_ACL_H_ */
/*! @} */
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "common/sockaddr.h"
int sockaddr_init(sockaddr_t *addr, int af)
{
/* Reset pointer. */
addr->ptr = 0;
/* Initialize address size. */
switch(af) {
case AF_INET:
addr->len = sizeof(struct sockaddr_in);
break;
#ifndef DISABLE_IPV6
case AF_INET6:
addr->len = sizeof(struct sockaddr_in6);
break;
#endif
default:
return -1;
}
/* Update pointer. */
return sockaddr_update(addr);
}
int sockaddr_update(sockaddr_t *addr)
{
/* Update internal pointer. */
switch(addr->len) {
case sizeof(struct sockaddr_in):
addr->ptr = (struct sockaddr*)&addr->addr4;
break;
#ifndef DISABLE_IPV6
case sizeof(struct sockaddr_in6):
addr->ptr = (struct sockaddr*)&addr->addr6;
break;
#endif
default:
return -1;
}
return 0;
}
int sockaddr_set(sockaddr_t *dst, int family, const char* addr, int port)
{
if (!dst || !addr || port < 0) {
return -1;
}
/* Initialize. */
dst->ptr = 0;
dst->len = 0;
sockaddr_init(dst, family);
/* Initialize depending on address family. */
void *paddr = 0;
switch(family) {
case AF_INET:
dst->addr4.sin_port = htons(port);
paddr = &dst->addr4.sin_addr;
dst->addr4.sin_addr.s_addr = INADDR_ANY;
break;
#ifndef DISABLE_IPV6
case AF_INET6:
dst->addr6.sin6_port = htons(port);
paddr = &dst->addr6.sin6_addr;
memcpy(&dst->addr6.sin6_addr,
&in6addr_any, sizeof(in6addr_any));
break;
#endif
default:
return -1;
}
/* Convert address. */
return inet_pton(family, addr, paddr);
}
/*!
* \file sockaddr.h
*
* \author Marek Vavrusa <marek.vavrusa@nic.cz>
*
* \brief Socket address abstraction layer.
*
* \addtogroup common_lib
* @{
*/
#ifndef _KNOT_SOCKADDR_H_
#define _KNOT_SOCKADDR_H_
#include <netinet/in.h>
/*! \brief Universal socket address. */
typedef struct sockaddr_t {
struct sockaddr* ptr; /*!< Pointer to used sockaddr. */
socklen_t len; /*!< Length of used sockaddr. */
union {
struct sockaddr_in addr4; /*!< IPv4 sockaddr. */
#ifndef DISABLE_IPV6
struct sockaddr_in6 addr6; /*!< IPv6 sockaddr. */
#endif
};
} sockaddr_t;
/*!
* \brief Initialize address structure.
*
* Members ptr and len will be initialized to correct address family.
*
* \param addr Socket address structure
* \param af Requested address family.
*
* \retval 0 on success.
* \retval -1 on unsupported address family (probably INET6).
*/
int sockaddr_init(sockaddr_t *addr, int af);
/*!
* \brief Update internal pointers according to length.
*
* \param addr Socket address structure
*
* \retval 0 on success.
* \retval -1 on invalid size.
*/
int sockaddr_update(sockaddr_t *addr);
/*! \brief Set address and port.
*
* \brief dst Target address structure.
* \brief family Address family.
* \brief addr IP address in string format.
* \brief port Port.
*
* \retval 0 if addr is not valid address in string format.
* \retval positive value in case of success.
* \retval -1 on error.
* \see inet_pton(3)
*/
int sockaddr_set(sockaddr_t *dst, int family, const char* addr, int port);
#endif /* _KNOT_SOCKADDR_H_ */
/*! @} */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment