Files
m241Neelima/components/ip_net2-6.9/ipptp/src/dep/ipv4_acl.c
T
2026-07-30 11:19:44 +05:30

494 lines
10 KiB
C

/*-
* 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);
}