187 lines
4.4 KiB
C
187 lines
4.4 KiB
C
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
/**
|
|
* \file bgp/prefix.c
|
|
*
|
|
* Deal with network prefixes.
|
|
*
|
|
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
|
* \author Lorenzo Cogotti
|
|
*/
|
|
|
|
#include "bgp/bgp_local.h"
|
|
#include "sys/endian.h"
|
|
#include "sys/ip.h"
|
|
#include "numlib.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
// ===========================================================================
|
|
// Some performance oriented macros to avoid branching during prefix iteration
|
|
|
|
/// Calculate the minimum size of a possibly ADD_PATH enabled prefix.
|
|
#define MINPFXSIZ(isAddPath) \
|
|
((((isAddPath) != 0) << 2) + 1)
|
|
|
|
/// Extract the prefix portion out of a possibly ADD_PATH enabled prefix pointer.
|
|
#define RAWPFXPTR(base, isAddPath) \
|
|
((RawPrefix *) ((Uint8 *) (base) + (((isAddPath) != 0) << 2)))
|
|
|
|
/// Calculate maximum prefix width in bits given an address family
|
|
#define MAXPFXWIDTH(family) (((family) == AFI_IP6) ? IPV6_WIDTH : IPV4_WIDTH) // simple CMOV
|
|
|
|
// ===========================================================================
|
|
|
|
char *Bgp_PrefixToString(Afi afi, const RawPrefix *prefix, char *dest)
|
|
{
|
|
Ipv4adr adr;
|
|
Ipv6adr adr6;
|
|
|
|
switch (afi) {
|
|
case AFI_IP:
|
|
memset(&adr, 0, sizeof(adr));
|
|
memcpy(&adr, prefix->bytes, PFXLEN(prefix->width));
|
|
dest = Ipv4_AdrToString(&adr, dest);
|
|
break;
|
|
|
|
case AFI_IP6:
|
|
memset(&adr6, 0, sizeof(adr6));
|
|
memcpy(&adr6, prefix->bytes, PFXLEN(prefix->width));
|
|
dest = Ipv6_AdrToString(&adr6, dest);
|
|
break;
|
|
|
|
default:
|
|
return NULL; // invalid argument
|
|
}
|
|
|
|
*dest++ = '/';
|
|
dest = Utoa(prefix->width, dest);
|
|
return dest;
|
|
}
|
|
|
|
char *Bgp_ApPrefixToString(Afi afi, const ApRawPrefix *prefix, char *dest)
|
|
{
|
|
// NOTE: Test early to avoid polluting `dest` in case of invalid argument;
|
|
// hopefully compilers will flatten this function to
|
|
// eliminate duplicate test inside switch
|
|
if (afi != AFI_IP && afi != AFI_IP6)
|
|
return NULL; // invalid argument
|
|
|
|
dest = Utoa(beswap32(prefix->pathId), dest);
|
|
*dest++ = ' ';
|
|
return Bgp_PrefixToString(afi, PLAINPFX(prefix), dest);
|
|
}
|
|
|
|
Judgement Bgp_StringToPrefix(const char *s, Prefix *dest)
|
|
{
|
|
Ipadr adr;
|
|
unsigned width;
|
|
NumConvRet res;
|
|
|
|
const char *ptr = s;
|
|
while (*ptr != '/' && *ptr != '\0') ptr++;
|
|
|
|
size_t len = ptr - s;
|
|
char *buf = (char *) alloca(len + 1);
|
|
|
|
memcpy(buf, s, len);
|
|
buf[len] = '\0';
|
|
|
|
if (Ip_StringToAdr(buf, &adr) != OK)
|
|
return NG; // Bad IP string
|
|
|
|
if (*ptr == '/') {
|
|
ptr++; // skip '/' separator
|
|
|
|
char *eptr;
|
|
width = Atou(ptr, &eptr, 10, &res);
|
|
|
|
if (res != NCVENOERR || *eptr != '\0')
|
|
return NG;
|
|
} else
|
|
width = (adr.family == IP6) ? IPV6_WIDTH : IPV4_WIDTH; // implicit full prefix
|
|
|
|
switch (adr.family) {
|
|
case IP4:
|
|
if (width > IPV4_WIDTH) return NG; // illegal prefix length
|
|
|
|
dest->afi = AFI_IP;
|
|
break;
|
|
|
|
case IP6:
|
|
if (width > IPV6_WIDTH) return NG; // illegal prefix length
|
|
|
|
dest->afi = AFI_IP6;
|
|
break;
|
|
|
|
default:
|
|
UNREACHABLE;
|
|
return NG;
|
|
}
|
|
|
|
dest->isAddPath = FALSE;
|
|
dest->width = width;
|
|
memcpy(dest->bytes, adr.bytes, PFXLEN(width));
|
|
return OK;
|
|
}
|
|
|
|
Judgement Bgp_StartPrefixes(Prefixiter *it,
|
|
Afi afi,
|
|
Safi safi,
|
|
const void *data,
|
|
size_t nbytes,
|
|
Boolean isAddPath)
|
|
{
|
|
if (afi != AFI_IP && afi != AFI_IP6)
|
|
return Bgp_SetErrStat(BGPEAFIUNSUP);
|
|
if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST)
|
|
return Bgp_SetErrStat(BGPESAFIUNSUP);
|
|
|
|
it->afi = afi;
|
|
it->safi = safi;
|
|
it->isAddPath = isAddPath;
|
|
it->base = (Uint8 *) data;
|
|
it->lim = it->base + nbytes;
|
|
it->ptr = it->base;
|
|
|
|
return Bgp_SetErrStat(BGPENOERR);
|
|
}
|
|
|
|
void *Bgp_NextPrefix(Prefixiter *it)
|
|
{
|
|
if (it->ptr >= it->lim) {
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return NULL; // end of iteration
|
|
}
|
|
|
|
// Basic check for prefix initial bytes
|
|
size_t left = it->lim - it->ptr;
|
|
size_t siz = MINPFXSIZ(it->isAddPath);
|
|
if (left < siz) {
|
|
Bgp_SetErrStat(BGPETRUNCPFX);
|
|
return NULL;
|
|
}
|
|
|
|
// Adjust a pointer to skip Path identifier info if necessary
|
|
const RawPrefix *rawPfx = RAWPFXPTR(it->ptr, it->isAddPath);
|
|
if (rawPfx->width > MAXPFXWIDTH(it->afi)) {
|
|
Bgp_SetErrStat(BGPEBADPFXWIDTH);
|
|
return NULL;
|
|
}
|
|
|
|
// Ensure all the necessary prefix bytes are present
|
|
siz += PFXLEN(rawPfx->width);
|
|
if (left < siz) {
|
|
Bgp_SetErrStat(BGPETRUNCPFX);
|
|
return NULL;
|
|
}
|
|
|
|
// All good, advance and return
|
|
void *pfx = it->ptr;
|
|
it->ptr += siz;
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
|
|
return pfx;
|
|
}
|