Skip to content

Commit ddd230a

Browse files
committed
Replace lpm with our own based on Verstable.
Sadly lpm isn't available on all platforms I need it. So lets take the path of least resistance and use Verstable for our own variant. We take the big hammer approach of a map per prefix length and lookup in that.
1 parent f5bcb37 commit ddd230a

File tree

4 files changed

+167
-103
lines changed

4 files changed

+167
-103
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ ignore 10.0.0.0/24
4242
attack 169.254.0.0/16
4343
```
4444

45-
parpd uses the [Longest Prefix Match library](https://github.com/rmind/liblpm)
46-
and [Verstable](https://github.com/JacksonAllan/Verstable) to manage
45+
parpd [Verstable](https://github.com/JacksonAllan/Verstable) to manage
4746
large rulesets and addresses so it remains performant in the most challenging
4847
of networks.

configure

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -627,34 +627,6 @@ pselect)
627627
;;
628628
esac
629629

630-
printf "Testing for lpm_create ... "
631-
cat <<EOF >_lpm.c
632-
#include <stddef.h>
633-
#include <lpm.h>
634-
int main(void) {
635-
lpm_t *lpm = lpm_create();
636-
return lpm ? 0 : -1;
637-
}
638-
EOF
639-
if $XCC _lpm.c -o _lpm -Wl,-lpm 2>&3; then
640-
HAVE_LIBLPM="yes"
641-
elif $XCC _lpm.c -o _lpm -I/usr/pkg/include -L/usr/pkg/lib -Wl,-llpm 2>&3; then
642-
HAVE_LIBLPM="yes (/usr/pkg)"
643-
echo "CPPFLAGS+= -I/usr/pkg/include" >>$CONFIG_MK
644-
echo "LDFLAGS+= -L/usr/pkg/lib -llpm" >>$CONFIG_MK
645-
elif $XCC _lpm.c -o _lpm -I/usr/local/include -L/usr/local/lib -Wl,-llpm 2>&3; then
646-
HAVE_LIBLPM="yes (/usr/local)"
647-
echo "CPPFLAGS+= -I/usr/local/include" >>$CONFIG_MK
648-
echo "LDFLAGS+= -L/usr/local/lib -llpm" >>$CONFIG_MK
649-
else
650-
HAVE_LIBLPM=no
651-
fi
652-
echo "$HAVE_LIBLPM"
653-
rm -f _lpm.c _lpm
654-
if [ "$HAVE_LIBLPM" = no ]; then
655-
exit 1
656-
fi
657-
658630
echo
659631
echo " SYSCONFDIR = $SYSCONFDIR"
660632
echo " SBINDIR = $SBINDIR"

src/parpd.c

Lines changed: 129 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@ hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
131131
return hwaddr_buffer;
132132
}
133133

134+
static void
135+
in_len2mask(in_addr_t *mask, unsigned int len)
136+
{
137+
unsigned int i;
138+
uint8_t *p;
139+
140+
p = (uint8_t *)mask;
141+
memset(mask, 0, sizeof(*mask));
142+
for (i = 0; i < len / 8; i++)
143+
p[i] = 0xff;
144+
if (len % 8)
145+
p[i] = (uint8_t)((0xff00 >> (len % 8)) & 0xff);
146+
}
147+
134148
/* Because fgetln does not return C strings, we cannot use
135149
* functions such as strsep and friends to extract words.
136150
* This is no bad thing this below code handles extracting words
@@ -160,32 +174,108 @@ get_word(char **s, const char *e)
160174
}
161175

162176
static void
163-
free_prefix(__unused void *arg, __unused const void *key, __unused size_t len,
164-
void *val)
177+
pm_init(pstore_t *store) {
178+
unsigned int plen;
179+
pbucket_t *bucket;
180+
181+
for (plen = 0; plen <= PREFIX_MAX_LEN; plen++) {
182+
bucket = &store->buckets[plen];
183+
bucket->plen = plen;
184+
in_len2mask(&bucket->mask, bucket->plen);
185+
bucket->set = false;
186+
paction_map_init(&bucket->prefixes);
187+
}
188+
}
189+
190+
static void
191+
pm_cleanup(pstore_t *store)
165192
{
193+
unsigned int plen;
194+
pbucket_t *bucket;
166195

167-
free(val);
196+
for (plen = 0; plen <= PREFIX_MAX_LEN; plen++) {
197+
bucket = &store->buckets[plen];
198+
paction_map_cleanup(&bucket->prefixes);
199+
bucket->set = false;
200+
}
168201
}
169202

170-
static void free_prefixes(lpm_t **prefixes)
203+
static bool
204+
pm_anyset(pstore_t *store)
171205
{
206+
unsigned int plen;
172207

173-
if (*prefixes == NULL)
174-
return;
208+
for (plen = 0; plen < PREFIX_MAX_LEN; plen++) {
209+
if (store->buckets[plen].set)
210+
return true;
211+
}
212+
return false;
213+
}
214+
215+
static paction_t *
216+
pm_get(pstore_t *store, in_addr_t ip, unsigned int plen)
217+
{
218+
pbucket_t *bucket;
219+
paction_map_itr itr;
220+
221+
bucket = &store->buckets[plen];
222+
if (!bucket->set)
223+
return NULL;
224+
225+
itr = paction_map_get(&bucket->prefixes, ip);
226+
if (paction_map_is_end(itr))
227+
return NULL;
228+
229+
return itr.data->val;
230+
}
231+
232+
static paction_t *
233+
pm_lookup(pstore_t *store, in_addr_t ip)
234+
{
235+
paction_map_itr itr;
236+
int plen = PREFIX_MAX_LEN;
237+
pbucket_t *bucket;
238+
in_addr_t addr;
239+
240+
for (plen = PREFIX_MAX_LEN; plen >= 0; plen--) {
241+
bucket = &store->buckets[plen];
242+
if (!bucket->set) {
243+
continue;
244+
}
175245

176-
lpm_clear(*prefixes, free_prefix, NULL);
177-
free(*prefixes);
178-
*prefixes = NULL;
246+
addr = ip & bucket->mask;
247+
itr = paction_map_get(&bucket->prefixes, addr);
248+
if (!paction_map_is_end(itr))
249+
return itr.data->val;
250+
}
251+
252+
return NULL;
253+
}
254+
255+
static int
256+
pm_insert(pstore_t *store, in_addr_t ip, unsigned int plen, paction_t *pa)
257+
{
258+
pbucket_t *bucket;
259+
paction_map_itr itr;
260+
261+
bucket = &store->buckets[plen];
262+
ip &= bucket->mask;
263+
itr = paction_map_insert(&store->buckets[plen].prefixes, ip, pa);
264+
if (paction_map_is_end(itr))
265+
return -1;
266+
267+
bucket->set = true;
268+
return 0;
179269
}
180270

181271
static void
182272
free_config(struct ctx *ctx)
183273
{
184274
struct interface *ifp;
185275

186-
free_prefixes(&ctx->prefixes);
276+
pm_cleanup(&ctx->pstore);
187277
TAILQ_FOREACH(ifp, &ctx->ifaces, next) {
188-
free_prefixes(&ifp->prefixes);
278+
pm_cleanup(&ifp->pstore);
189279
}
190280
}
191281

@@ -198,12 +288,12 @@ load_config(struct ctx *ctx)
198288
char *buf, *cmd, *match, *hwaddr, *bp, *p, *e, *r, act;
199289
size_t buf_len;
200290
ssize_t len;
201-
struct prefix *pp;
202291
long plen;
203292
int in_interface;
204293
struct in_addr ina;
205294
struct interface *ifp;
206-
lpm_t *prefixes;
295+
paction_t *pa;
296+
pstore_t *pstore;
207297

208298
if (stat(ctx->cffile, &st) == -1) {
209299
free_config(ctx);
@@ -308,45 +398,30 @@ load_config(struct ctx *ctx)
308398
}
309399
}
310400
}
311-
312-
if (ifp != NULL) {
313-
if (ifp->prefixes == NULL)
314-
ifp->prefixes = lpm_create();
315-
prefixes = ifp->prefixes;
316-
} else {
317-
if (ctx->prefixes == NULL)
318-
ctx->prefixes = lpm_create();
319-
prefixes = ctx->prefixes;
320-
}
321-
if (prefixes == NULL) {
322-
syslog(LOG_ERR, "lpm_create: %m");
323-
goto err;
324-
}
401+
pstore = ifp != NULL ? &ifp->pstore : &ctx->pstore;
325402

326403
/* Check if we have already added the prefix,
327404
* overwrite it if we have. */
328-
pp = lpm_lookup_prefix(prefixes, &ina.s_addr,
329-
sizeof(ina.s_addr), (unsigned int)plen);
330-
if (pp == NULL) {
331-
pp = malloc(sizeof(*pp));
332-
if (pp == NULL)
405+
pa = pm_get(pstore, ina.s_addr, (unsigned int)plen);
406+
if (pa == NULL) {
407+
pa = malloc(sizeof(*pa));
408+
if (pa == NULL)
333409
goto err;
334-
pp->ip = ina.s_addr;
335-
pp->plen = (unsigned int)plen;
336-
if (lpm_insert(prefixes, &pp->ip, sizeof(pp->ip),
337-
pp->plen, pp) == -1)
410+
pa->ip = ina.s_addr;
411+
pa->plen = (unsigned int)plen;
412+
if (pm_insert(pstore, pa->ip, pa->plen, pa) == -1)
338413
{
339-
syslog(LOG_ERR, "lpm_insert: %m");
340-
free(pp);
414+
syslog(LOG_ERR, "pm_insert: %m");
415+
free(pa);
341416
goto err;
342417
}
343418
}
344419

345-
pp->action = act;
420+
pa->action = act;
346421
if (hwaddr == NULL)
347-
pp->hwlen = 0;
422+
pa->hwlen = 0;
348423
else
349-
pp->hwlen = hwaddr_aton(pp->hwaddr, hwaddr);
424+
pa->hwlen = hwaddr_aton(pa->hwaddr, hwaddr);
350425
}
351426

352427
error = 0;
@@ -361,25 +436,22 @@ static int
361436
proxy(struct ctx *ctx, struct interface *ifp, in_addr_t ip,
362437
const uint8_t **hw, size_t *hwlen)
363438
{
364-
struct prefix *pp;
439+
paction_t *pa;
365440

366441
if (load_config(ctx) == -1)
367442
return -1;
368443

369-
if (ifp->prefixes != NULL)
370-
pp = lpm_lookup(ifp->prefixes, &ip, sizeof(ip));
371-
else
372-
pp = NULL;
373-
if (pp == NULL && ctx->prefixes != NULL)
374-
pp = lpm_lookup(ctx->prefixes, &ip, sizeof(ip));
375-
if (pp == NULL)
444+
pa = pm_lookup(&ifp->pstore, ip);
445+
if (pa == NULL)
446+
pa = pm_lookup(&ctx->pstore, ip);
447+
if (pa == NULL)
376448
return PARPD_IGNORE;
377449

378-
if (pp->action) {
379-
*hw = pp->hwaddr;
380-
*hwlen = pp->hwlen;
450+
if (pa->action) {
451+
*hw = pa->hwaddr;
452+
*hwlen = pa->hwlen;
381453
}
382-
return pp->action;
454+
return pa->action;
383455
}
384456

385457
#define ARP_LEN \
@@ -419,7 +491,6 @@ expire_ipaddr(void *arg)
419491
struct ipaddr *ipa = arg;
420492

421493
ipaddr_map_erase(&ipa->ifp->ipaddrs, ipa->ipaddr);
422-
free(ipa);
423494
}
424495

425496
/* Checks an incoming ARP message to see if we should proxy for it. */
@@ -645,6 +716,7 @@ discover_interfaces(struct ctx *ctx, int argc, char * const *argv)
645716
memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
646717
#endif
647718

719+
pm_init(&ifp->pstore);
648720
vt_init(&ifp->ipaddrs);
649721

650722
TAILQ_INSERT_TAIL(&ctx->ifaces, ifp, next);
@@ -722,6 +794,7 @@ main(int argc, char **argv)
722794

723795
opt = EXIT_FAILURE;
724796

797+
pm_init(&ctx.pstore);
725798
TAILQ_INIT(&ctx.ifaces);
726799
discover_interfaces(&ctx, argc, argv);
727800

@@ -747,7 +820,7 @@ main(int argc, char **argv)
747820

748821
i = 0;
749822
TAILQ_FOREACH(ifp, &ctx.ifaces, next) {
750-
if (ctx.prefixes == NULL && ifp->prefixes == NULL)
823+
if (!pm_anyset(&ctx.pstore) && !pm_anyset(&ifp->pstore))
751824
continue;
752825

753826
if ((ifp->fd = bpf_open_arp(ifp)) == -1) {
@@ -783,15 +856,8 @@ main(int argc, char **argv)
783856
#ifdef SANITIZE_MEMORY
784857
free_config(&ctx);
785858
while ((ifp = TAILQ_FIRST(&ctx.ifaces)) != NULL) {
786-
ipaddr_map_itr itr;
787-
788859
TAILQ_REMOVE(&ctx.ifaces, ifp, next);
789-
for (itr = ipaddr_map_first(&ifp->ipaddrs);
790-
!ipaddr_map_is_end(itr);
791-
itr = ipaddr_map_next(itr))
792-
{
793-
free(itr.data->val);
794-
}
860+
ipaddr_map_cleanup(&ifp->ipaddrs);
795861
if (ifp->fd != -1)
796862
close(ifp->fd);
797863
free(ifp->buffer);

0 commit comments

Comments
 (0)