pfctl: cache getprotobynumber results

As for example pfctl -ss keeps calling it, it saves a lot of overhead
from elided parsing of /etc/nsswitch.conf and /etc/protocols.

Sample result when running a pre-nvlist binary with nfs root and dumping
7 mln states:
before: 24.817u 62.993s 1:28.52 99.1%
after:	8.064u 1.117s 0:18.87 48.5%

Idea by Jim Thompson

Reviewed by:	kp
Sponsored by:	Rubicon Communications, LLC ("Netgate")
This commit is contained in:
Mateusz Guzik 2021-07-01 21:25:43 +02:00
parent fa3d57c256
commit 858937bea4
5 changed files with 55 additions and 10 deletions

View file

@ -5017,13 +5017,13 @@ expand_label_port(const char *name, char *label, size_t len,
void
expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto)
{
struct protoent *pe;
const char *protoname;
char n[4];
if (strstr(label, name) != NULL) {
pe = getprotobynumber(proto);
if (pe != NULL)
expand_label_str(label, len, name, pe->p_name);
protoname = pfctl_proto2name(proto);
if (protoname != NULL)
expand_label_str(label, len, name, protoname);
else {
snprintf(n, sizeof(n), "%u", proto);
expand_label_str(label, len, name, n);

View file

@ -211,7 +211,7 @@ print_state(struct pfctl_state *s, int opts)
{
struct pfctl_state_peer *src, *dst;
struct pfctl_state_key *key, *sk, *nk;
struct protoent *p;
const char *protoname;
int min, sec;
sa_family_t af;
uint8_t proto;
@ -243,8 +243,8 @@ print_state(struct pfctl_state *s, int opts)
sk->port[1] = nk->port[1];
}
printf("%s ", s->ifname);
if ((p = getprotobynumber(proto)) != NULL)
printf("%s ", p->p_name);
if ((protoname = pfctl_proto2name(proto)) != NULL)
printf("%s ", protoname);
else
printf("%u ", proto);

View file

@ -254,6 +254,49 @@ usage(void)
exit(1);
}
/*
* Cache protocol number to name translations.
*
* Translation is performed a lot e.g., when dumping states and
* getprotobynumber is incredibly expensive.
*
* Note from the getprotobynumber(3) manpage:
* <quote>
* These functions use a thread-specific data space; if the data is needed
* for future use, it should be copied before any subsequent calls overwrite
* it. Only the Internet protocols are currently understood.
* </quote>
*
* Consequently we only cache the name and strdup it for safety.
*
* At the time of writing this comment the last entry in /etc/protocols is:
* divert 258 DIVERT # Divert pseudo-protocol [non IANA]
*/
const char *
pfctl_proto2name(int proto)
{
static const char *pfctl_proto_cache[259];
struct protoent *p;
if (proto >= nitems(pfctl_proto_cache)) {
p = getprotobynumber(proto);
if (p == NULL) {
return (NULL);
}
return (p->p_name);
}
if (pfctl_proto_cache[proto] == NULL) {
p = getprotobynumber(proto);
if (p == NULL) {
return (NULL);
}
pfctl_proto_cache[proto] = strdup(p->p_name);
}
return (pfctl_proto_cache[proto]);
}
int
pfctl_enable(int dev, int opts)
{

View file

@ -138,4 +138,6 @@ void pf_remove_if_empty_ruleset(struct pfctl_ruleset *);
struct pfctl_ruleset *pf_find_ruleset(const char *);
struct pfctl_ruleset *pf_find_or_create_ruleset(const char *);
const char *pfctl_proto2name(int);
#endif /* _PFCTL_H_ */

View file

@ -812,10 +812,10 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" inet6");
}
if (r->proto) {
struct protoent *p;
const char *protoname;
if ((p = getprotobynumber(r->proto)) != NULL)
printf(" proto %s", p->p_name);
if ((protoname = pfctl_proto2name(r->proto)) != NULL)
printf(" proto %s", protoname);
else
printf(" proto %u", r->proto);
}