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.

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;
}