mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-15 02:43:41 +00:00
lib: rework printfrr extensions to output directly
Allowing printfrr extensions to directly write to the output buffer has a few advantages: - there is no arbitrary length limit imposed (previously 64) - the output doesn't need to be copied another time - the extension can directly use bprintfrr() to put together pieces The downside is that the theoretical length (regardless of available buffer space) must be computed correctly. Extended unit tests to test these paths a bit more thoroughly. Signed-off-by: David Lamparter <equinox@diac24.net>
This commit is contained in:
parent
bcf9d7d8aa
commit
212e04e5a7
@ -201,18 +201,17 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table,
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("BD", printfrr_bd)
|
||||
static ssize_t printfrr_bd(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_bd(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const struct bgp_dest *dest = ptr;
|
||||
const struct prefix *p;
|
||||
const struct prefix *p = bgp_dest_get_prefix(dest);
|
||||
char cbuf[PREFIX_STRLEN];
|
||||
|
||||
if (dest) {
|
||||
p = bgp_dest_get_prefix(dest);
|
||||
prefix2str(p, buf, bsz);
|
||||
} else {
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
}
|
||||
if (!dest)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
return 2;
|
||||
/* need to get the real length even if buffer too small */
|
||||
prefix2str(p, cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
@ -730,80 +730,99 @@ int nexthop_str2backups(const char *str, int *num_backups,
|
||||
* nexthop2str()
|
||||
*/
|
||||
printfrr_ext_autoreg_p("NH", printfrr_nh)
|
||||
static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_nh(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const struct nexthop *nexthop = ptr;
|
||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
|
||||
bool do_ifi = false;
|
||||
const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
|
||||
ssize_t ret = 3;
|
||||
const char *v_is = "", *v_via = "", *v_viaif = "via ";
|
||||
ssize_t ret = 0;
|
||||
|
||||
/* NULL-check */
|
||||
if (nexthop == NULL) {
|
||||
if (fmt[2] == 'v' && fmt[3] == 'v')
|
||||
ret++;
|
||||
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (fmt[2]) {
|
||||
switch (**fmt) {
|
||||
case 'v':
|
||||
if (fmt[3] == 'v') {
|
||||
(*fmt)++;
|
||||
if (**fmt == 'v') {
|
||||
v_is = "is ";
|
||||
v_via = "via ";
|
||||
v_viaif = "";
|
||||
ret++;
|
||||
(*fmt)++;
|
||||
}
|
||||
|
||||
if (!nexthop)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
switch (nexthop->type) {
|
||||
case NEXTHOP_TYPE_IPV4:
|
||||
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
||||
bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4);
|
||||
ret += bprintfrr(buf, "%s%pI4", v_via,
|
||||
&nexthop->gate.ipv4);
|
||||
do_ifi = true;
|
||||
break;
|
||||
case NEXTHOP_TYPE_IPV6:
|
||||
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
||||
bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6);
|
||||
ret += bprintfrr(buf, "%s%pI6", v_via,
|
||||
&nexthop->gate.ipv6);
|
||||
do_ifi = true;
|
||||
break;
|
||||
case NEXTHOP_TYPE_IFINDEX:
|
||||
bprintfrr(&fb, "%sdirectly connected, %s", v_is,
|
||||
ifindex2ifname(nexthop->ifindex,
|
||||
nexthop->vrf_id));
|
||||
ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
|
||||
ifindex2ifname(nexthop->ifindex,
|
||||
nexthop->vrf_id));
|
||||
break;
|
||||
case NEXTHOP_TYPE_BLACKHOLE:
|
||||
ret += bputs(buf, "unreachable");
|
||||
|
||||
switch (nexthop->bh_type) {
|
||||
case BLACKHOLE_REJECT:
|
||||
s = " (ICMP unreachable)";
|
||||
ret += bputs(buf, " (ICMP unreachable)");
|
||||
break;
|
||||
case BLACKHOLE_ADMINPROHIB:
|
||||
s = " (ICMP admin-prohibited)";
|
||||
ret += bputs(buf, " (ICMP admin-prohibited)");
|
||||
break;
|
||||
case BLACKHOLE_NULL:
|
||||
s = " (blackhole)";
|
||||
ret += bputs(buf, " (blackhole)");
|
||||
break;
|
||||
default:
|
||||
s = "";
|
||||
break;
|
||||
}
|
||||
bprintfrr(&fb, "unreachable%s", s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (do_ifi && nexthop->ifindex)
|
||||
bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
|
||||
nexthop->ifindex,
|
||||
nexthop->vrf_id));
|
||||
ret += bprintfrr(buf, ", %s%s", v_viaif,
|
||||
ifindex2ifname(nexthop->ifindex,
|
||||
nexthop->vrf_id));
|
||||
|
||||
*fb.pos = '\0';
|
||||
return ret;
|
||||
case 's':
|
||||
nexthop2str(nexthop, buf, bsz);
|
||||
return 3;
|
||||
(*fmt)++;
|
||||
|
||||
if (!nexthop)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
switch (nexthop->type) {
|
||||
case NEXTHOP_TYPE_IFINDEX:
|
||||
ret += bprintfrr(buf, "if %u", nexthop->ifindex);
|
||||
break;
|
||||
case NEXTHOP_TYPE_IPV4:
|
||||
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
||||
ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4,
|
||||
nexthop->ifindex);
|
||||
break;
|
||||
case NEXTHOP_TYPE_IPV6:
|
||||
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
||||
ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6,
|
||||
nexthop->ifindex);
|
||||
break;
|
||||
case NEXTHOP_TYPE_BLACKHOLE:
|
||||
ret += bputs(buf, "blackhole");
|
||||
break;
|
||||
default:
|
||||
ret += bputs(buf, "unknown");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
94
lib/prefix.c
94
lib/prefix.c
@ -1361,92 +1361,92 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len)
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("EA", printfrr_ea)
|
||||
static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_ea(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const struct ethaddr *mac = ptr;
|
||||
char cbuf[ETHER_ADDR_STRLEN];
|
||||
|
||||
if (mac)
|
||||
prefix_mac2str(mac, buf, bsz);
|
||||
else
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
if (!mac)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
return 2;
|
||||
/* need real length even if buffer is too short */
|
||||
prefix_mac2str(mac, cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("IA", printfrr_ia)
|
||||
static ssize_t printfrr_ia(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_ia(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const struct ipaddr *ipa = ptr;
|
||||
char cbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (ipa)
|
||||
ipaddr2str(ipa, buf, bsz);
|
||||
else
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
if (!ipa)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
return 2;
|
||||
ipaddr2str(ipa, cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("I4", printfrr_i4)
|
||||
static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_i4(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
inet_ntop(AF_INET, ptr, buf, bsz);
|
||||
else
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
char cbuf[INET_ADDRSTRLEN];
|
||||
|
||||
return 2;
|
||||
if (!ptr)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("I6", printfrr_i6)
|
||||
static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_i6(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
inet_ntop(AF_INET6, ptr, buf, bsz);
|
||||
else
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
char cbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
return 2;
|
||||
if (!ptr)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("FX", printfrr_pfx)
|
||||
static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_pfx(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
prefix2str(ptr, buf, bsz);
|
||||
else
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
char cbuf[PREFIX_STRLEN];
|
||||
|
||||
return 2;
|
||||
if (!ptr)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
prefix2str(ptr, cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("SG4", printfrr_psg)
|
||||
static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_psg(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const struct prefix_sg *sg = ptr;
|
||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (sg) {
|
||||
if (sg->src.s_addr == INADDR_ANY)
|
||||
bprintfrr(&fb, "(*,");
|
||||
else
|
||||
bprintfrr(&fb, "(%pI4,", &sg->src);
|
||||
if (!sg)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
if (sg->grp.s_addr == INADDR_ANY)
|
||||
bprintfrr(&fb, "*)");
|
||||
else
|
||||
bprintfrr(&fb, "%pI4)", &sg->grp);
|
||||
if (sg->src.s_addr == INADDR_ANY)
|
||||
ret += bputs(buf, "(*,");
|
||||
else
|
||||
ret += bprintfrr(buf, "(%pI4,", &sg->src);
|
||||
|
||||
fb.pos[0] = '\0';
|
||||
if (sg->grp.s_addr == INADDR_ANY)
|
||||
ret += bputs(buf, "*)");
|
||||
else
|
||||
ret += bprintfrr(buf, "%pI4)", &sg->grp);
|
||||
|
||||
} else {
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
}
|
||||
|
||||
return 3;
|
||||
return ret;
|
||||
}
|
||||
|
@ -210,15 +210,16 @@ void printfrr_ext_reg(const struct printfrr_ext *ext)
|
||||
exts[i] = ext;
|
||||
}
|
||||
|
||||
ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
|
||||
ssize_t printfrr_extp(struct fbuf *buf, const char **fmtp, int prec,
|
||||
const void *ptr)
|
||||
{
|
||||
const char *fmt = *fmtp;
|
||||
const struct printfrr_ext *ext;
|
||||
size_t i;
|
||||
|
||||
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
|
||||
if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
|
||||
return 0;
|
||||
return -1;
|
||||
if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
|
||||
continue;
|
||||
ext = exts[i];
|
||||
@ -226,20 +227,22 @@ ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
|
||||
continue;
|
||||
if (strncmp(ext->match, fmt, strlen(ext->match)))
|
||||
continue;
|
||||
return ext->print_ptr(buf, sz, fmt, prec, ptr);
|
||||
*fmtp += strlen(ext->match);
|
||||
return ext->print_ptr(buf, fmtp, prec, ptr);
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
|
||||
ssize_t printfrr_exti(struct fbuf *buf, const char **fmtp, int prec,
|
||||
uintmax_t num)
|
||||
{
|
||||
const char *fmt = *fmtp;
|
||||
const struct printfrr_ext *ext;
|
||||
size_t i;
|
||||
|
||||
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
|
||||
if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
|
||||
return 0;
|
||||
return -1;
|
||||
if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
|
||||
continue;
|
||||
ext = exts[i];
|
||||
@ -247,7 +250,8 @@ ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
|
||||
continue;
|
||||
if (strncmp(ext->match, fmt, strlen(ext->match)))
|
||||
continue;
|
||||
return ext->print_int(buf, sz, fmt, prec, num);
|
||||
*fmtp += strlen(ext->match);
|
||||
return ext->print_int(buf, fmtp, prec, num);
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL;
|
||||
int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL;
|
||||
#endif
|
||||
|
||||
/* returns number of bytes consumed for extended specifier */
|
||||
ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL;
|
||||
ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL;
|
||||
/* returns number of bytes needed for full output, or -1 */
|
||||
ssize_t printfrr_extp(struct fbuf *, const char **, int, const void *)
|
||||
DSO_LOCAL;
|
||||
ssize_t printfrr_exti(struct fbuf *, const char **, int, uintmax_t) DSO_LOCAL;
|
||||
|
@ -177,6 +177,7 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
|
||||
int nextarg; /* 1-based argument index */
|
||||
va_list orgap; /* original argument pointer */
|
||||
char *convbuf; /* wide to multibyte conversion result */
|
||||
char *extstart = NULL; /* where printfrr_ext* started printing */
|
||||
|
||||
static const char xdigs_lower[16] = "0123456789abcdef";
|
||||
static const char xdigs_upper[16] = "0123456789ABCDEF";
|
||||
@ -438,16 +439,14 @@ reswitch: switch (ch) {
|
||||
ulval = SARG();
|
||||
|
||||
if (printfrr_ext_char(fmt[0])) {
|
||||
n2 = printfrr_exti(buf, sizeof(buf), fmt, prec,
|
||||
if (cb)
|
||||
extstart = cb->pos;
|
||||
|
||||
size = printfrr_exti(cb, &fmt, prec,
|
||||
(flags & INTMAX_SIZE) ? ujval
|
||||
: (uintmax_t)ulval);
|
||||
if (n2 > 0) {
|
||||
fmt += n2;
|
||||
cp = buf;
|
||||
size = strlen(cp);
|
||||
sign = '\0';
|
||||
break;
|
||||
}
|
||||
if (size >= 0)
|
||||
goto ext_printed;
|
||||
}
|
||||
if (flags & INTMAX_SIZE) {
|
||||
if ((intmax_t)ujval < 0) {
|
||||
@ -551,14 +550,13 @@ reswitch: switch (ch) {
|
||||
* -- ANSI X3J11
|
||||
*/
|
||||
ptrval = GETARG(void *);
|
||||
if (printfrr_ext_char(fmt[0]) &&
|
||||
(n2 = printfrr_extp(buf, sizeof(buf),
|
||||
fmt, prec, ptrval)) > 0) {
|
||||
fmt += n2;
|
||||
cp = buf;
|
||||
size = strlen(cp);
|
||||
sign = '\0';
|
||||
break;
|
||||
if (printfrr_ext_char(fmt[0])) {
|
||||
if (cb)
|
||||
extstart = cb->pos;
|
||||
|
||||
size = printfrr_extp(cb, &fmt, prec, ptrval);
|
||||
if (size >= 0)
|
||||
goto ext_printed;
|
||||
}
|
||||
ujval = (uintmax_t)(uintptr_t)ptrval;
|
||||
base = 16;
|
||||
@ -686,7 +684,7 @@ number: if ((dprec = prec) >= 0)
|
||||
realsz += 2;
|
||||
|
||||
prsize = width > realsz ? width : realsz;
|
||||
if ((unsigned)ret + prsize > INT_MAX) {
|
||||
if ((unsigned int)ret + prsize > INT_MAX) {
|
||||
ret = EOF;
|
||||
errno = EOVERFLOW;
|
||||
goto error;
|
||||
@ -720,6 +718,58 @@ number: if ((dprec = prec) >= 0)
|
||||
/* finally, adjust ret */
|
||||
ret += prsize;
|
||||
|
||||
FLUSH(); /* copy out the I/O vectors */
|
||||
continue;
|
||||
|
||||
ext_printed:
|
||||
/* when we arrive here, a printfrr extension has written to cb
|
||||
* (if non-NULL), but we still need to handle padding. The
|
||||
* original cb->pos is in extstart; the return value from the
|
||||
* ext is in size.
|
||||
*
|
||||
* Keep analogous to code above please.
|
||||
*/
|
||||
|
||||
realsz = size;
|
||||
prsize = width > realsz ? width : realsz;
|
||||
if ((unsigned int)ret + prsize > INT_MAX) {
|
||||
ret = EOF;
|
||||
errno = EOVERFLOW;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* right-adjusting blank padding - need to move the chars
|
||||
* that the extension has already written. Should be very
|
||||
* rare.
|
||||
*/
|
||||
if (cb && width > size && (flags & (LADJUST|ZEROPAD)) == 0) {
|
||||
size_t nwritten = cb->pos - extstart;
|
||||
size_t navail = cb->buf + cb->len - extstart;
|
||||
size_t npad = width - realsz;
|
||||
size_t nmove;
|
||||
|
||||
if (navail < npad)
|
||||
navail = 0;
|
||||
else
|
||||
navail -= npad;
|
||||
nmove = MIN(nwritten, navail);
|
||||
|
||||
memmove(extstart + npad, extstart, nmove);
|
||||
|
||||
cb->pos = extstart;
|
||||
PAD(npad, blanks);
|
||||
cb->pos += nmove;
|
||||
}
|
||||
|
||||
io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0;
|
||||
|
||||
/* left-adjusting padding (always blank) */
|
||||
if (flags & LADJUST)
|
||||
PAD(width - realsz, blanks);
|
||||
|
||||
/* finally, adjust ret */
|
||||
ret += prsize;
|
||||
|
||||
FLUSH(); /* copy out the I/O vectors */
|
||||
}
|
||||
done:
|
||||
|
@ -112,20 +112,21 @@ struct printfrr_ext {
|
||||
/* both can be given, if not the code continues searching
|
||||
* (you can do %pX and %dX in 2 different entries)
|
||||
*
|
||||
* return value: number of bytes consumed from the format string, so
|
||||
* you can consume extra flags (e.g. register for "%pX", consume
|
||||
* "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags
|
||||
* lowercase letters or numbers.
|
||||
* return value: number of bytes that would be printed if the buffer
|
||||
* was large enough. be careful about not under-reporting this;
|
||||
* otherwise asnprintf() & co. will get broken. Returning -1 means
|
||||
* something went wrong & default %p/%d handling should be executed.
|
||||
*
|
||||
* bsz is a compile-time constant in printf; it's gonna be relatively
|
||||
* small. This isn't designed to print Shakespeare from a pointer.
|
||||
* to consume extra input flags after %pXY, increment *fmt. It points
|
||||
* at the first character after %pXY at entry. Convention is to make
|
||||
* those flags lowercase letters or numbers.
|
||||
*
|
||||
* prec is the precision specifier (the 999 in "%.999p") -1 means
|
||||
* none given (value in the format string cannot be negative)
|
||||
*/
|
||||
ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec,
|
||||
ssize_t (*print_ptr)(struct fbuf *buf, const char **fmt, int prec,
|
||||
const void *);
|
||||
ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec,
|
||||
ssize_t (*print_int)(struct fbuf *buf, const char **fmt, int prec,
|
||||
uintmax_t);
|
||||
};
|
||||
|
||||
@ -136,7 +137,7 @@ struct printfrr_ext {
|
||||
void printfrr_ext_reg(const struct printfrr_ext *);
|
||||
|
||||
#define printfrr_ext_autoreg_p(matchs, print_fn) \
|
||||
static ssize_t print_fn(char *, size_t, const char *, int, \
|
||||
static ssize_t print_fn(struct fbuf *, const char **, int, \
|
||||
const void *); \
|
||||
static const struct printfrr_ext _printext_##print_fn = { \
|
||||
.match = matchs, \
|
||||
@ -149,7 +150,7 @@ void printfrr_ext_reg(const struct printfrr_ext *);
|
||||
/* end */
|
||||
|
||||
#define printfrr_ext_autoreg_i(matchs, print_fn) \
|
||||
static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \
|
||||
static ssize_t print_fn(struct fbuf *, const char **, int, uintmax_t); \
|
||||
static const struct printfrr_ext _printext_##print_fn = { \
|
||||
.match = matchs, \
|
||||
.print_int = print_fn, \
|
||||
|
@ -664,54 +664,51 @@ void sockunion_init(union sockunion *su)
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("SU", printfrr_psu)
|
||||
static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_psu(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const union sockunion *su = ptr;
|
||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
|
||||
bool include_port = false;
|
||||
bool endflags = false;
|
||||
ssize_t consumed = 2;
|
||||
ssize_t ret = 0;
|
||||
char cbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (su) {
|
||||
while (!endflags) {
|
||||
switch (fmt[consumed++]) {
|
||||
case 'p':
|
||||
include_port = true;
|
||||
break;
|
||||
default:
|
||||
consumed--;
|
||||
endflags = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
if (!su)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
switch (sockunion_family(su)) {
|
||||
case AF_UNSPEC:
|
||||
bprintfrr(&fb, "(unspec)");
|
||||
break;
|
||||
case AF_INET:
|
||||
inet_ntop(AF_INET, &su->sin.sin_addr, buf, bsz);
|
||||
fb.pos += strlen(fb.buf);
|
||||
if (include_port)
|
||||
bprintfrr(&fb, ":%d", su->sin.sin_port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, bsz);
|
||||
fb.pos += strlen(fb.buf);
|
||||
if (include_port)
|
||||
bprintfrr(&fb, ":%d", su->sin6.sin6_port);
|
||||
while (!endflags) {
|
||||
switch (**fmt) {
|
||||
case 'p':
|
||||
(*fmt)++;
|
||||
include_port = true;
|
||||
break;
|
||||
default:
|
||||
bprintfrr(&fb, "(af %d)", sockunion_family(su));
|
||||
endflags = true;
|
||||
break;
|
||||
}
|
||||
|
||||
fb.pos[0] = '\0';
|
||||
} else {
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
switch (sockunion_family(su)) {
|
||||
case AF_UNSPEC:
|
||||
ret += bputs(buf, "(unspec)");
|
||||
break;
|
||||
case AF_INET:
|
||||
inet_ntop(AF_INET, &su->sin.sin_addr, cbuf, sizeof(cbuf));
|
||||
ret += bputs(buf, cbuf);
|
||||
if (include_port)
|
||||
ret += bprintfrr(buf, ":%d", su->sin.sin_port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
inet_ntop(AF_INET6, &su->sin6.sin6_addr, cbuf, sizeof(cbuf));
|
||||
ret += bputs(buf, cbuf);
|
||||
if (include_port)
|
||||
ret += bprintfrr(buf, ":%d", su->sin6.sin6_port);
|
||||
break;
|
||||
default:
|
||||
ret += bprintfrr(buf, "(af %d)", sockunion_family(su));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sockunion_is_null(const union sockunion *su)
|
||||
|
@ -307,20 +307,20 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size)
|
||||
}
|
||||
|
||||
printfrr_ext_autoreg_p("RN", printfrr_rn)
|
||||
static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt,
|
||||
static ssize_t printfrr_rn(struct fbuf *buf, const char **fmt,
|
||||
int prec, const void *ptr)
|
||||
{
|
||||
const struct route_node *rn = ptr;
|
||||
const struct prefix *dst_p, *src_p;
|
||||
char cbuf[PREFIX_STRLEN * 2 + 6];
|
||||
|
||||
if (rn) {
|
||||
srcdest_rnode_prefixes(rn, &dst_p, &src_p);
|
||||
srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz);
|
||||
} else {
|
||||
strlcpy(buf, "NULL", bsz);
|
||||
}
|
||||
if (!rn)
|
||||
return bputs(buf, "NULL");
|
||||
|
||||
return 2;
|
||||
srcdest_rnode_prefixes(rn, &dst_p, &src_p);
|
||||
srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p,
|
||||
cbuf, sizeof(cbuf));
|
||||
return bputs(buf, cbuf);
|
||||
}
|
||||
|
||||
struct route_table *srcdest_srcnode_table(struct route_node *rn)
|
||||
|
@ -64,8 +64,37 @@ static void printchk(const char *ref, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char bufrr[256];
|
||||
bool truncfail = false;
|
||||
size_t i;
|
||||
size_t expectlen;
|
||||
|
||||
memset(bufrr, 0xcc, sizeof(bufrr));
|
||||
|
||||
va_start(ap, fmt);
|
||||
expectlen = vsnprintfrr(NULL, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintfrr(bufrr, 7, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (strnlen(bufrr, 7) == 7)
|
||||
truncfail = true;
|
||||
if (strnlen(bufrr, 7) < 7 && strncmp(ref, bufrr, 6) != 0)
|
||||
truncfail = true;
|
||||
for (i = 7; i < sizeof(bufrr); i++)
|
||||
if (bufrr[i] != (char)0xcc) {
|
||||
truncfail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (truncfail) {
|
||||
printf("truncation test FAILED:\n"
|
||||
"fmt: \"%s\"\nref: \"%s\"\nfrr[:7]: \"%s\"\n%s\n\n",
|
||||
fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
|
||||
errors++;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
|
||||
va_end(ap);
|
||||
@ -74,6 +103,10 @@ static void printchk(const char *ref, const char *fmt, ...)
|
||||
fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
|
||||
if (strcmp(ref, bufrr))
|
||||
errors++;
|
||||
if (strlen(bufrr) != expectlen) {
|
||||
printf("return value <> length mismatch\n");
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -112,6 +145,7 @@ int main(int argc, char **argv)
|
||||
inet_aton("192.168.1.2", &ip);
|
||||
printchk("192.168.1.2", "%pI4", &ip);
|
||||
printchk(" 192.168.1.2", "%20pI4", &ip);
|
||||
printchk("192.168.1.2 ", "%-20pI4", &ip);
|
||||
|
||||
printcmp("%p", &ip);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user