original files of m241
This commit is contained in:
@@ -0,0 +1,493 @@
|
||||
/*-
|
||||
* Copyright (c) 2013-2014 Wojciech Owczarek,
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ipv4_acl.c
|
||||
* @date Sun Oct 13 21:02:23 2013
|
||||
*
|
||||
* @brief Code to handle IPv4 access control lists
|
||||
*
|
||||
* Functions in this file parse, create and match IPv4 ACLs.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../ptpd.h"
|
||||
#include "string.h"
|
||||
|
||||
/**
|
||||
* strdup + free are used across code using strtok_r, so as to
|
||||
* protect the original string, as strtok* modify it.
|
||||
*/
|
||||
|
||||
/* count tokens in string delimited by delim */
|
||||
static int countTokens(const char* text, const char* delim) {
|
||||
|
||||
int count=0;
|
||||
char* stash = NULL;
|
||||
char* text_;
|
||||
char* text__;
|
||||
|
||||
if(text==NULL || delim==NULL)
|
||||
return 0;
|
||||
|
||||
text_=strdup(text);
|
||||
|
||||
for(text__=text_,count=0;strtok_r(text__,delim, &stash) != NULL; text__=NULL) {
|
||||
count++;
|
||||
}
|
||||
free(text_);
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
/* Parse a dotted-decimal string into an uint8_t array - return -1 on error */
|
||||
static int ipToArray(const char* text, uint8_t dest[], int maxOctets, Boolean isMask)
|
||||
{
|
||||
|
||||
char* text_;
|
||||
char* text__;
|
||||
char* subtoken;
|
||||
char* stash = NULL;
|
||||
char* endptr;
|
||||
long octet;
|
||||
|
||||
int count = 0;
|
||||
int result = 0;
|
||||
|
||||
memset(&dest[0], 0, maxOctets * sizeof(uint8_t));
|
||||
|
||||
text_=strdup(text);
|
||||
|
||||
for(text__=text_;;text__=NULL) {
|
||||
|
||||
if(count > maxOctets) {
|
||||
result = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
subtoken = strtok_r(text__,".",&stash);
|
||||
|
||||
if(subtoken == NULL)
|
||||
goto end;
|
||||
|
||||
errno = 0;
|
||||
octet=strtol(subtoken,&endptr,10);
|
||||
if(errno!=0 || !IN_RANGE(octet,0,255)) {
|
||||
result = -1;
|
||||
goto end;
|
||||
|
||||
}
|
||||
|
||||
dest[count++] = (uint8_t)octet;
|
||||
|
||||
/* If we're parsing a mask and an octet is less than 0xFF, whole rest is zeros */
|
||||
if(isMask && octet < 255)
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
free(text_);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/* Parse a single net mask into an AclEntry */
|
||||
static int parseAclEntry(const char* line, AclEntry* acl) {
|
||||
|
||||
int result = 1;
|
||||
char* stash;
|
||||
char* text_;
|
||||
char* token;
|
||||
char* endptr;
|
||||
long octet = 0;
|
||||
|
||||
uint8_t net_octets[4];
|
||||
uint8_t mask_octets[4];
|
||||
|
||||
if(line == NULL || acl == NULL)
|
||||
return -1;
|
||||
|
||||
if(countTokens(line,"/") == 0)
|
||||
return -1;
|
||||
|
||||
text_=strdup(line);
|
||||
|
||||
token=strtok_r(text_,"/",&stash);
|
||||
|
||||
if((countTokens(token,".") > 4) ||
|
||||
(countTokens(token,".") < 1) ||
|
||||
(ipToArray(token,net_octets,4,0) < 0)) {
|
||||
result=-1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
acl->network = (net_octets[0] << 24) | (net_octets[1] << 16) | ( net_octets[2] << 8) | net_octets[3];
|
||||
|
||||
token=strtok_r(NULL,"/",&stash);
|
||||
|
||||
if(token == NULL) {
|
||||
acl->netmask=32;
|
||||
acl->bitmask=~0;
|
||||
} else if(countTokens(token,".") == 1) {
|
||||
errno = 0;
|
||||
octet = strtol(token,&endptr,10);
|
||||
if(errno != 0 || !IN_RANGE(octet,0,32)) {
|
||||
result = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if(octet == 0)
|
||||
acl->bitmask = 0;
|
||||
else
|
||||
acl->bitmask = ~0 << (32 - octet);
|
||||
acl->netmask = (uint16_t)octet;
|
||||
|
||||
} else if((countTokens(token,".") > 4) ||
|
||||
(ipToArray(token,mask_octets,4,1) < 0)) {
|
||||
result=-1;
|
||||
goto end;
|
||||
} else {
|
||||
|
||||
acl->bitmask = (mask_octets[0] << 24) | (mask_octets[1] << 16) | ( mask_octets[2] << 8) | mask_octets[3];
|
||||
uint32_t tmp = acl->bitmask;
|
||||
int count = 0;
|
||||
for(tmp = acl->bitmask;tmp<<count;count++);
|
||||
acl->netmask=count;
|
||||
}
|
||||
|
||||
end:
|
||||
free(text_);
|
||||
|
||||
acl->network &= acl->bitmask;
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* qsort() compare function for sorting ACL entries */
|
||||
static int
|
||||
cmpAclEntry(const void *p1, const void *p2)
|
||||
{
|
||||
|
||||
const AclEntry *left = p1;
|
||||
const AclEntry *right = p2;
|
||||
|
||||
if(left->network > right->network)
|
||||
return 1;
|
||||
if(left->network < right->network)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Parse an ACL string into an aclEntry table and return number of entries. output can be NULL */
|
||||
int
|
||||
maskParser(const char* input, AclEntry* output)
|
||||
{
|
||||
|
||||
char* token;
|
||||
char* stash;
|
||||
int found = 0;
|
||||
char* text_;
|
||||
char* text__;
|
||||
AclEntry tmp;
|
||||
|
||||
memset(&tmp,0,sizeof(AclEntry));
|
||||
|
||||
if(strlen(input)==0) return 0;
|
||||
|
||||
text_=strdup(input);
|
||||
|
||||
for(text__=text_;;text__=NULL) {
|
||||
|
||||
token=strtok_r(text__,", ;\t",&stash);
|
||||
if(token==NULL) break;
|
||||
|
||||
if(parseAclEntry(token,&tmp)<1) {
|
||||
|
||||
found = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if(output != NULL)
|
||||
output[found]=tmp;
|
||||
|
||||
found++;
|
||||
|
||||
|
||||
}
|
||||
|
||||
if((found>0) && (output != NULL))
|
||||
qsort(output, found, sizeof(AclEntry), cmpAclEntry);
|
||||
|
||||
/* We got input but found nothing - error */
|
||||
if (!found)
|
||||
found = -1;
|
||||
|
||||
free(text_);
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
/* Create a maskTable from a text ACL */
|
||||
static MaskTable*
|
||||
createMaskTable(const char* input)
|
||||
{
|
||||
MaskTable* ret;
|
||||
int masksFound = maskParser(input, NULL);
|
||||
if(masksFound>=0) {
|
||||
ret=(MaskTable*)calloc(1,sizeof(MaskTable));
|
||||
if(ret == NULL)
|
||||
return NULL;
|
||||
ret->entries = (AclEntry*)calloc(masksFound, sizeof(AclEntry));
|
||||
if(ret->entries == NULL)
|
||||
{
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->numEntries = maskParser(input,ret->entries);
|
||||
return ret;
|
||||
} else {
|
||||
printf("Error while parsing access list: \"%s\"\n", input);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the contents of a single mask table */
|
||||
static void
|
||||
dumpMaskTable(MaskTable* table)
|
||||
{
|
||||
int i;
|
||||
uint32_t network;
|
||||
if(table == NULL)
|
||||
return;
|
||||
INFO("number of entries: %d\n",table->numEntries);
|
||||
if(table->entries != NULL) {
|
||||
for(i = 0; i < table->numEntries; i++) {
|
||||
AclEntry this = table->entries[i];
|
||||
#ifdef PTPD_MSBF
|
||||
network = this.network;
|
||||
#else
|
||||
network = htonl(this.network);
|
||||
#endif
|
||||
INFO("%d.%d.%d.%d/%d\t(0x%.8x/0x%.8x), matches: %d\n",
|
||||
*((uint8_t*)&network), *((uint8_t*)&network+1),
|
||||
*((uint8_t*)&network+2), *((uint8_t*)&network+3), this.netmask,
|
||||
this.network, this.bitmask, this.hitCount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Free a MaskTable structure */
|
||||
static void freeMaskTable(MaskTable** table)
|
||||
{
|
||||
if(*table == NULL)
|
||||
return;
|
||||
|
||||
if((*table)->entries != NULL) {
|
||||
free((*table)->entries);
|
||||
(*table)->entries = NULL;
|
||||
}
|
||||
free(*table);
|
||||
*table = NULL;
|
||||
}
|
||||
|
||||
/* Destroy an Ipv4AccessList structure */
|
||||
void
|
||||
freeIpv4AccessList(Ipv4AccessList** acl)
|
||||
{
|
||||
if(*acl == NULL)
|
||||
return;
|
||||
|
||||
freeMaskTable(&(*acl)->permitTable);
|
||||
freeMaskTable(&(*acl)->denyTable);
|
||||
|
||||
free(*acl);
|
||||
*acl = NULL;
|
||||
}
|
||||
|
||||
/* Structure initialisation for Ipv4AccessList */
|
||||
Ipv4AccessList*
|
||||
createIpv4AccessList(const char* permitList, const char* denyList, int processingOrder)
|
||||
{
|
||||
Ipv4AccessList* ret;
|
||||
ret = (Ipv4AccessList*)calloc(1,sizeof(Ipv4AccessList));
|
||||
if(ret == NULL)
|
||||
return NULL;
|
||||
ret->permitTable = createMaskTable(permitList);
|
||||
ret->denyTable = createMaskTable(denyList);
|
||||
if(ret->permitTable == NULL || ret->denyTable == NULL) {
|
||||
freeIpv4AccessList(&ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->processingOrder = processingOrder;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Match an IP address against a MaskTable */
|
||||
static int
|
||||
matchAddress(const uint32_t addr, MaskTable* table)
|
||||
{
|
||||
|
||||
int i;
|
||||
if(table == NULL || table->entries == NULL || table->numEntries==0)
|
||||
return -1;
|
||||
for(i = 0; i < table->numEntries; i++) {
|
||||
DBGV("addr: %08x, addr & mask: %08x, network: %08x\n",addr, table->entries[i].bitmask & addr, table->entries[i].network);
|
||||
if((table->entries[i].bitmask & addr) == table->entries[i].network) {
|
||||
table->entries[i].hitCount++;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Test an IP address against an ACL */
|
||||
int
|
||||
matchIpv4AccessList(Ipv4AccessList* acl, const uint32_t addr)
|
||||
{
|
||||
|
||||
int ret;
|
||||
int matchPermit = 0;
|
||||
int matchDeny = 0;
|
||||
|
||||
/* Non-functional ACL permits everything */
|
||||
if(acl == NULL)
|
||||
return 1;
|
||||
|
||||
if(acl->permitTable != NULL)
|
||||
matchPermit = matchAddress(addr,acl->permitTable) > 0;
|
||||
|
||||
if(acl->denyTable != NULL)
|
||||
matchDeny = matchAddress(addr,acl->denyTable) > 0;
|
||||
|
||||
switch(acl->processingOrder) {
|
||||
case ACL_PERMIT_DENY:
|
||||
if(!matchPermit) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if(matchDeny) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
case ACL_DENY_PERMIT:
|
||||
if (matchDeny && !matchPermit) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
if(ret)
|
||||
acl->passedCounter++;
|
||||
else
|
||||
acl->droppedCounter++;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Dump the contents and hit counters of an ACL */
|
||||
void dumpIpv4AccessList(Ipv4AccessList* acl)
|
||||
{
|
||||
|
||||
INFO("\n\n");
|
||||
if(acl == NULL) {
|
||||
INFO("(uninitialised ACL)\n");
|
||||
return;
|
||||
}
|
||||
switch(acl->processingOrder) {
|
||||
case ACL_DENY_PERMIT:
|
||||
INFO("ACL order: deny,permit\n");
|
||||
INFO("Passed packets: %d, dropped packets: %d\n",
|
||||
acl->passedCounter, acl->droppedCounter);
|
||||
INFO("--------\n");
|
||||
INFO("Deny list:\n");
|
||||
dumpMaskTable(acl->denyTable);
|
||||
INFO("--------\n");
|
||||
INFO("Permit list:\n");
|
||||
dumpMaskTable(acl->permitTable);
|
||||
break;
|
||||
case ACL_PERMIT_DENY:
|
||||
default:
|
||||
INFO("ACL order: permit,deny\n");
|
||||
INFO("Passed packets: %d, dropped packets: %d\n",
|
||||
acl->passedCounter, acl->droppedCounter);
|
||||
INFO("--------\n");
|
||||
INFO("Permit list:\n");
|
||||
dumpMaskTable(acl->permitTable);
|
||||
INFO("--------\n");
|
||||
INFO("Deny list:\n");
|
||||
dumpMaskTable(acl->denyTable);
|
||||
break;
|
||||
}
|
||||
INFO("\n\n");
|
||||
}
|
||||
|
||||
/* Clear counters in a MaskTable */
|
||||
static void
|
||||
clearMaskTableCounters(MaskTable* table)
|
||||
{
|
||||
|
||||
int i, count;
|
||||
if(table==NULL || table->numEntries==0)
|
||||
return;
|
||||
count = table->numEntries;
|
||||
|
||||
for(i=0; i<count; i++) {
|
||||
table->entries[i].hitCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Clear ACL counter */
|
||||
void clearIpv4AccessListCounters(Ipv4AccessList* acl) {
|
||||
|
||||
if(acl == NULL)
|
||||
return;
|
||||
acl->passedCounter=0;
|
||||
acl->droppedCounter=0;
|
||||
clearMaskTableCounters(acl->permitTable);
|
||||
clearMaskTableCounters(acl->denyTable);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user