* Reduce size of ipfw table entries for cidr/iface:

Since old structures had _value as the last field,
every table match required 3 cache lines instead of 2.
Fix this by
- using the fact that supplied masks are suplicated inside radix
- using lightweigth sa_in6 structure as key for IPv6

Before (amd64):
  sizeof(table_entry): 136
  sizeof(table_xentry): 160
After (amd64):
  sizeof(radix_cidr_entry): 120
  sizeof(radix_cidr_xentry): 128
  sizeof(radix_iface): 128

* Fix memory leak for table entry update
* Do some more sanity checks while deleting entry
* Do not store masks for host routes

Sponsored by:	Yandex LLC
This commit is contained in:
Alexander V. Chernikov 2014-07-09 18:52:12 +00:00
parent 7e767c791f
commit e0a8b9ee29
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=268465

View File

@ -78,50 +78,50 @@ static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
* But for portability, let's avoid assumption and make the code explicit
*/
#define KEY_LEN(v) *((uint8_t *)&(v))
#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr))
/*
* Do not require radix to compare more than actual IPv4/IPv6 address
*/
#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
#define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr))
#define KEY_LEN_INET6 (offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
#define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname))
#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr))
#define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr))
#define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_addr))
#define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname))
struct table_entry {
struct radix_cidr_entry {
struct radix_node rn[2];
struct sockaddr_in addr, mask;
u_int32_t value;
struct sockaddr_in addr;
uint32_t value;
uint8_t masklen;
};
struct sa_in6 {
uint8_t sin6_len;
uint8_t sin6_family;
uint8_t pad[2];
struct in6_addr sin6_addr;
};
struct radix_cidr_xentry {
struct radix_node rn[2];
struct sa_in6 addr6;
uint32_t value;
uint8_t masklen;
};
struct xaddr_iface {
uint8_t if_len; /* length of this struct */
uint8_t pad[7]; /* Align name */
char ifname[IF_NAMESIZE]; /* Interface name */
uint8_t if_len; /* length of this struct */
uint8_t pad[7]; /* Align name */
char ifname[IF_NAMESIZE]; /* Interface name */
};
struct table_xentry {
struct radix_iface {
struct radix_node rn[2];
union {
#ifdef INET6
struct sockaddr_in6 addr6;
#endif
struct xaddr_iface iface;
} a;
union {
#ifdef INET6
struct sockaddr_in6 mask6;
#endif
struct xaddr_iface ifmask;
} m;
u_int32_t value;
struct xaddr_iface iface;
uint32_t value;
};
/*
* CIDR implementation using radix
*
@ -134,23 +134,23 @@ ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
struct radix_node_head *rnh;
if (keylen == sizeof(in_addr_t)) {
struct table_entry *ent;
struct radix_cidr_entry *ent;
struct sockaddr_in sa;
KEY_LEN(sa) = KEY_LEN_INET;
sa.sin_addr.s_addr = *((in_addr_t *)key);
rnh = (struct radix_node_head *)ti->state;
ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh));
ent = (struct radix_cidr_entry *)(rnh->rnh_matchaddr(&sa, rnh));
if (ent != NULL) {
*val = ent->value;
return (1);
}
} else {
struct table_xentry *xent;
struct sockaddr_in6 sa6;
struct radix_cidr_xentry *xent;
struct sa_in6 sa6;
KEY_LEN(sa6) = KEY_LEN_INET6;
memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
rnh = (struct radix_node_head *)ti->xstate;
xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
xent = (struct radix_cidr_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
if (xent != NULL) {
*val = xent->value;
return (1);
@ -184,9 +184,9 @@ static int
flush_table_entry(struct radix_node *rn, void *arg)
{
struct radix_node_head * const rnh = arg;
struct table_entry *ent;
struct radix_cidr_entry *ent;
ent = (struct table_entry *)
ent = (struct radix_cidr_entry *)
rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
if (ent != NULL)
free(ent, M_IPFW_TBL);
@ -211,33 +211,22 @@ static int
ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
ipfw_obj_tentry *tent)
{
struct table_entry *n;
struct table_xentry *xn;
#ifdef INET6
int i;
uint32_t *v;
#endif
struct radix_cidr_entry *n;
struct radix_cidr_xentry *xn;
n = (struct table_entry *)e;
n = (struct radix_cidr_entry *)e;
/* Guess IPv4/IPv6 radix by sockaddr family */
if (n->addr.sin_family == AF_INET) {
if (in_nullhost(n->mask.sin_addr))
tent->masklen = 0;
else
tent->masklen = 33-ffs(ntohl(n->mask.sin_addr.s_addr));
/* Save IPv4 address as deprecated IPv6 compatible */
tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
tent->masklen = n->masklen;
tent->subtype = AF_INET;
tent->value = n->value;
#ifdef INET6
} else {
xn = (struct table_xentry *)e;
/* Count IPv6 mask */
v = (uint32_t *)&xn->m.mask6.sin6_addr;
for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++)
tent->masklen += bitcount32(*v);
memcpy(&tent->k, &xn->a.addr6.sin6_addr, sizeof(struct in6_addr));
xn = (struct radix_cidr_xentry *)e;
memcpy(&tent->k, &xn->addr6.sin6_addr, sizeof(struct in6_addr));
tent->masklen = xn->masklen;
tent->subtype = AF_INET6;
tent->value = xn->value;
#endif
@ -261,7 +250,7 @@ ta_find_radix_tentry(void *ta_state, struct table_info *ti, void *key,
rnh = (struct radix_node_head *)ti->state;
e = rnh->rnh_matchaddr(&sa, rnh);
} else {
struct sockaddr_in6 sa6;
struct sa_in6 sa6;
KEY_LEN(sa6) = KEY_LEN_INET6;
memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
rnh = (struct radix_node_head *)ti->xstate;
@ -301,8 +290,8 @@ struct ta_buf_cidr
struct sockaddr_in ma;
} a4;
struct {
struct sockaddr_in6 sa;
struct sockaddr_in6 ma;
struct sa_in6 sa;
struct sa_in6 ma;
} a6;
} addr;
};
@ -324,9 +313,11 @@ static int
ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
{
struct ta_buf_cidr *tb;
struct table_entry *ent;
struct table_xentry *xent;
struct radix_cidr_entry *ent;
struct radix_cidr_xentry *xent;
in_addr_t addr;
struct sockaddr_in *mask;
struct sa_in6 *mask6;
int mlen;
tb = (struct ta_buf_cidr *)ta_buf;
@ -336,24 +327,25 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
if (tei->subtype == AF_INET) {
#ifdef INET
/* IPv4 case */
if (mlen > 32)
return (EINVAL);
ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
ent->value = tei->value;
mask = &tb->addr.a4.ma;
/* Set 'total' structure length */
KEY_LEN(ent->addr) = KEY_LEN_INET;
KEY_LEN(ent->mask) = KEY_LEN_INET;
KEY_LEN(*mask) = KEY_LEN_INET;
ent->addr.sin_family = AF_INET;
ent->mask.sin_addr.s_addr =
mask->sin_addr.s_addr =
htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
addr = *((in_addr_t *)tei->paddr);
ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
ent->addr.sin_addr.s_addr = addr & mask->sin_addr.s_addr;
ent->masklen = mlen;
/* Set pointers */
tb->ent_ptr = ent;
tb->addr_ptr = (struct sockaddr *)&ent->addr;
tb->mask_ptr = (struct sockaddr *)&ent->mask;
if (mlen != 32)
tb->mask_ptr = (struct sockaddr *)mask;
#endif
#ifdef INET6
} else if (tei->subtype == AF_INET6) {
@ -362,18 +354,21 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
return (EINVAL);
xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
xent->value = tei->value;
mask6 = &tb->addr.a6.ma;
/* Set 'total' structure length */
KEY_LEN(xent->a.addr6) = KEY_LEN_INET6;
KEY_LEN(xent->m.mask6) = KEY_LEN_INET6;
xent->a.addr6.sin6_family = AF_INET6;
ipv6_writemask(&xent->m.mask6.sin6_addr, mlen);
memcpy(&xent->a.addr6.sin6_addr, tei->paddr,
KEY_LEN(xent->addr6) = KEY_LEN_INET6;
KEY_LEN(*mask6) = KEY_LEN_INET6;
xent->addr6.sin6_family = AF_INET6;
ipv6_writemask(&mask6->sin6_addr, mlen);
memcpy(&xent->addr6.sin6_addr, tei->paddr,
sizeof(struct in6_addr));
APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr);
APPLY_MASK(&xent->addr6.sin6_addr, &mask6->sin6_addr);
xent->masklen = mlen;
/* Set pointers */
tb->ent_ptr = xent;
tb->addr_ptr = (struct sockaddr *)&xent->a.addr6;
tb->mask_ptr = (struct sockaddr *)&xent->m.mask6;
tb->addr_ptr = (struct sockaddr *)&xent->addr6;
if (mlen != 128)
tb->mask_ptr = (struct sockaddr *)mask6;
#endif
} else {
/* Unknown CIDR type */
@ -417,16 +412,18 @@ ta_add_cidr(void *ta_state, struct table_info *ti,
if (tei->subtype == AF_INET) {
/* IPv4. */
value = ((struct table_entry *)tb->ent_ptr)->value;
((struct table_entry *)rn)->value = value;
value = ((struct radix_cidr_entry *)tb->ent_ptr)->value;
((struct radix_cidr_entry *)rn)->value = value;
} else {
/* IPv6 */
value = ((struct table_xentry *)tb->ent_ptr)->value;
((struct table_xentry *)rn)->value = value;
value = ((struct radix_cidr_xentry *)tb->ent_ptr)->value;
((struct radix_cidr_xentry *)rn)->value = value;
}
/* Indicate that update has happened instead of addition */
tei->flags |= TEI_FLAGS_UPDATED;
return (0);
}
tb->ent_ptr = NULL;
@ -438,6 +435,8 @@ static int
ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
{
struct ta_buf_cidr *tb;
struct sockaddr_in sa, mask;
struct sa_in6 sa6, mask6;
in_addr_t addr;
int mlen;
@ -447,8 +446,11 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
mlen = tei->masklen;
if (tei->subtype == AF_INET) {
if (mlen > 32)
return (EINVAL);
memset(&sa, 0, sizeof(struct sockaddr_in));
memset(&mask, 0, sizeof(struct sockaddr_in));
/* Set 'total' structure length */
struct sockaddr_in sa, mask;
KEY_LEN(sa) = KEY_LEN_INET;
KEY_LEN(mask) = KEY_LEN_INET;
mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
@ -457,15 +459,14 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
tb->addr.a4.sa = sa;
tb->addr.a4.ma = mask;
tb->addr_ptr = (struct sockaddr *)&tb->addr.a4.sa;
tb->mask_ptr = (struct sockaddr *)&tb->addr.a4.ma;
if (mlen != 32)
tb->mask_ptr = (struct sockaddr *)&tb->addr.a4.ma;
#ifdef INET6
} else if (tei->subtype == AF_INET6) {
/* IPv6 case */
if (mlen > 128)
return (EINVAL);
struct sockaddr_in6 sa6, mask6;
memset(&sa6, 0, sizeof(struct sockaddr_in6));
memset(&mask6, 0, sizeof(struct sockaddr_in6));
memset(&sa6, 0, sizeof(struct sa_in6));
memset(&mask6, 0, sizeof(struct sa_in6));
/* Set 'total' structure length */
KEY_LEN(sa6) = KEY_LEN_INET6;
KEY_LEN(mask6) = KEY_LEN_INET6;
@ -476,7 +477,8 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
tb->addr.a6.sa = sa6;
tb->addr.a6.ma = mask6;
tb->addr_ptr = (struct sockaddr *)&tb->addr.a6.sa;
tb->mask_ptr = (struct sockaddr *)&tb->addr.a6.ma;
if (mlen != 128)
tb->mask_ptr = (struct sockaddr *)&tb->addr.a6.ma;
#endif
} else
return (EINVAL);
@ -547,13 +549,13 @@ ta_lookup_iface(struct table_info *ti, void *key, uint32_t keylen,
{
struct radix_node_head *rnh;
struct xaddr_iface iface;
struct table_xentry *xent;
struct radix_iface *xent;
KEY_LEN(iface) = KEY_LEN_IFACE +
strlcpy(iface.ifname, (char *)key, IF_NAMESIZE) + 1;
rnh = (struct radix_node_head *)ti->xstate;
xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh));
xent = (struct radix_iface *)(rnh->rnh_matchaddr(&iface, rnh));
if (xent != NULL) {
*val = xent->value;
return (1);
@ -566,12 +568,12 @@ static int
flush_iface_entry(struct radix_node *rn, void *arg)
{
struct radix_node_head * const rnh = arg;
struct table_entry *ent;
struct radix_iface *xent;
ent = (struct table_entry *)
xent = (struct radix_iface *)
rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
if (ent != NULL)
free(ent, M_IPFW_TBL);
if (xent != NULL)
free(xent, M_IPFW_TBL);
return (0);
}
@ -611,34 +613,29 @@ static int
ta_prepare_add_iface(struct tentry_info *tei, void *ta_buf)
{
struct ta_buf_iface *tb;
struct table_xentry *xent;
struct radix_iface *xent;
int mlen;
char c;
char *ifname;
tb = (struct ta_buf_iface *)ta_buf;
memset(tb, 0, sizeof(struct ta_buf_cidr));
mlen = tei->masklen;
/* Check if string is terminated */
c = ((char *)tei->paddr)[IF_NAMESIZE - 1];
((char *)tei->paddr)[IF_NAMESIZE - 1] = '\0';
mlen = strlen((char *)tei->paddr);
if ((mlen == IF_NAMESIZE - 1) && (c != '\0'))
ifname = (char *)tei->paddr;
if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
return (EINVAL);
/* Include last \0 into comparison */
mlen++;
mlen = strlen(ifname) + 1;
xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
xent->value = tei->value;
/* Set 'total' structure length */
KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen;
KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen;
memcpy(xent->a.iface.ifname, tei->paddr, mlen);
KEY_LEN(xent->iface) = KEY_LEN_IFACE + mlen;
memcpy(xent->iface.ifname, tei->paddr, mlen);
/* Set pointers */
tb->ent_ptr = xent;
tb->addr_ptr = (struct sockaddr *)&xent->a.iface;
tb->addr_ptr = (struct sockaddr *)&xent->iface;
/* Assume direct match */
tb->mask_ptr = NULL;
@ -669,11 +666,13 @@ ta_add_iface(void *ta_state, struct table_info *ti,
return (EINVAL);
}
value = ((struct table_xentry *)tb->ent_ptr)->value;
((struct table_xentry *)rn)->value = value;
value = ((struct radix_iface *)tb->ent_ptr)->value;
((struct radix_iface *)rn)->value = value;
/* Indicate that update has happened instead of addition */
tei->flags |= TEI_FLAGS_UPDATED;
return (0);
}
tb->ent_ptr = NULL;
@ -753,11 +752,11 @@ static int
ta_dump_iface_tentry(void *ta_state, struct table_info *ti, void *e,
ipfw_obj_tentry *tent)
{
struct table_xentry *xn;
struct radix_iface *xn;
xn = (struct table_xentry *)e;
xn = (struct radix_iface *)e;
tent->masklen = 8 * IF_NAMESIZE;
memcpy(&tent->k, &xn->a.iface.ifname, IF_NAMESIZE);
memcpy(&tent->k, &xn->iface.ifname, IF_NAMESIZE);
tent->value = xn->value;
return (0);