#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "radish.h"
#include <netinet/in.h>
#include <strings.h>
#include <stdio.h>
#define FATAL(x) \
do { \
fputs(x, stderr); \
abort(); \
} while (0)
static u_char rd_bmask [] = {
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe,
};
static u_char rd_btest [] = {
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
};
u_char rd_deleted_km[1024];
int
rd_inithead(void **headp, int family, int slen, int off, int alen,
int (*match)(void *, void *))
{
struct radish_head *head;
struct radish *new;
struct sockaddr *masks;
u_char *m;
int num = alen * 8 + 1, i, j, q, r;
int len = sizeof(*head) + sizeof(*new) + slen * num;
if (*headp) return (1);
R_Malloc(head, struct radish_head *, len);
if (head == NULL)
return 0;
Bzero(head, len);
new = (struct radish *)(head + 1);
masks = (struct sockaddr *)(new +1);
*headp = head;
m = (u_char *)masks;
for (i = 0; i < num; i++, m += slen) {
*m = slen;
*(m + 1) = family;
q = i >> 3;
r = i & 7;
for(j = 0; j < q; j++)
*(m + off + j) = 0xff;
*(m + off + j) = rd_bmask[r];
}
head->rdh_slen = slen;
head->rdh_offset = off;
head->rdh_alen = alen;
head->rdh_masks = masks;
head->rdh_match = match;
head->rdh_top = new;
new->rd_route = masks;
new->rd_mask = masks;
new->rd_btest = rd_btest[0];
return(1);
}
struct sockaddr *
rd_mask(struct sockaddr *m_arg, struct radish_head *head, int *maskp)
{
u_char *mp, *masks = (u_char *)head->rdh_masks;
int off = head->rdh_offset;
int slen = head->rdh_slen;
int alen = head->rdh_alen;
int i = 0, masklen = 0;
if (m_arg == NULL) {
masklen = alen * 8;
*maskp = masklen;
return((struct sockaddr *)(masks + slen * masklen));
}
mp = (u_char *)m_arg + off;
while ((i < alen) && (mp[i] == 0xff)) {
masklen += 8;
i++;
}
if (i < alen)
switch (mp[i]) {
case 0xfe: masklen += 7; break;
case 0xfc: masklen += 6; break;
case 0xf8: masklen += 5; break;
case 0xf0: masklen += 4; break;
case 0xe0: masklen += 3; break;
case 0xc0: masklen += 2; break;
case 0x80: masklen += 1; break;
case 0x00: break;
}
*maskp = masklen;
return((struct sockaddr *)(masks + slen * masklen));
}
int
rd_insert(struct sockaddr *d_arg, struct sockaddr *m_arg,
struct radish_head *head, void *rt)
{
struct radish *cur = head->rdh_top, *parent, *new;
int off = head->rdh_offset;
int slen = head->rdh_slen;
int alen = head->rdh_alen;
int i, lim, q, r, masklen;
u_char *dp, *np, *rp;
struct sockaddr *mask;
mask = rd_mask(m_arg, head, &masklen);
q = masklen >> 3;
r = masklen & 7;
R_Malloc(new, struct radish *, sizeof(*new) + slen);
if (new == NULL)
return ENOBUFS;
Bzero(new, sizeof(*new) + slen);
new->rd_route = (struct sockaddr *)(new + 1);
new->rd_mask = mask;
new->rd_masklen = masklen;
new->rd_masklim = q;
new->rd_bmask = rd_bmask[r];
new->rd_btest = rd_btest[r];
new->rd_rtent = rt;
np = (u_char *)new->rd_route;
dp = (u_char *)d_arg;
*np = *dp;
np[1] = dp[1];
dp += off;
np += off;
i = 0;
while (i < q) {
np[i] = dp[i];
i++;
}
np[i] = dp[i] & rd_bmask[r];
while (cur) {
if (masklen == cur->rd_masklen) {
rp = (u_char *)cur->rd_route + off;
for (i = 0; i < alen; i++)
if (np[i] != rp[i]) {
return rd_glue(cur, new, i, head);
}
Free(new);
if (cur->rd_rtent != NULL)
return EEXIST;
cur->rd_rtent = rt;
return 0;
}
if (masklen > cur->rd_masklen) {
rp = (u_char *)cur->rd_route + off;
lim = cur->rd_masklim;
for (i = 0; i < lim; i++)
if(np[i] != rp[i]) {
return rd_glue(cur, new, i, head);
}
if (cur->rd_bmask)
if ((np[lim] & cur->rd_bmask) != rp[lim]) {
return rd_glue(cur, new, lim, head);
}
if (cur->rd_btest & np[cur->rd_masklim]) {
if (cur->rd_r != NULL) {
cur = cur->rd_r;
continue;
}
cur->rd_r = new;
new->rd_p = cur;
return 0;
} else {
if (cur->rd_l != NULL) {
cur = cur->rd_l;
continue;
}
cur->rd_l = new;
new->rd_p = cur;
return 0;
}
}
rp = (u_char *)cur->rd_route + off;
lim = new->rd_masklim;
for (i = 0; i < lim; i++)
if(np[i] != rp[i]) {
return rd_glue(cur, new, i, head);
}
if (new->rd_bmask)
if (np[lim] != (rp[lim] & new->rd_bmask)) {
return rd_glue(cur, new, lim, head);
}
parent = cur->rd_p;
new->rd_p = parent;
if (parent->rd_l == cur)
parent->rd_l = new;
else if (parent->rd_r == cur)
parent->rd_r = new;
else
FATAL("rd_insert");
if (new->rd_btest & rp[new->rd_masklim])
new->rd_r = cur;
else
new->rd_l = cur;
cur->rd_p = new;
return 0;
}
return 1;
}
int
rd_glue(struct radish *cur, struct radish *new, int misbyte,
struct radish_head *head)
{
struct radish *parent = cur->rd_p, *glue;
u_char *cp = (u_char *)cur->rd_route;
u_char *np = (u_char *)new->rd_route;
u_char *gp;
int off = head->rdh_offset, slen = head->rdh_slen;
int maskb, xor, i;
R_Malloc(glue, struct radish *, sizeof(*glue) + slen);
if (glue == NULL) {
Free (new);
return ENOBUFS;
}
Bzero(glue, sizeof(*glue) + slen);
xor = (*(cp + off + misbyte) ^ *(np + off + misbyte)) & 0xff;
maskb = 8;
while(xor) {
xor >>= 1;
maskb--;
}
glue->rd_route = (struct sockaddr *)(glue + 1);
glue->rd_masklen = 8 * misbyte + maskb;
glue->rd_masklim = misbyte;
glue->rd_bmask = rd_bmask[maskb];
glue->rd_btest = rd_btest[maskb];
glue->rd_rtent = NULL;
glue->rd_p = parent;
glue->rd_mask = (struct sockaddr *)
((u_char *)head->rdh_masks + slen * glue->rd_masklen);
gp = (u_char *)glue->rd_route;
*gp = *cp;
*(gp + 1) = *(cp + 1);
cp += off;
gp += off;
for(i = 0; i < misbyte; i++)
gp[i] = cp[i];
gp[misbyte] = cp[misbyte] & glue->rd_bmask;
if (glue->rd_btest & cp[misbyte]) {
glue->rd_r = cur;
glue->rd_l = new;
} else {
glue->rd_r = new;
glue->rd_l = cur;
}
new->rd_p = cur->rd_p = glue;
if (parent->rd_l == cur)
parent->rd_l = glue;
else if (parent->rd_r == cur)
parent->rd_r = glue;
else
FATAL("rd_insert");
return 0;
}
int
rd_match(struct sockaddr *d_arg, struct radish_head *head, struct radish **rdp)
{
return rd_match_next(d_arg, head, rdp, NULL);
}
int
rd_match_next(struct sockaddr *d_arg, struct radish_head *head,
struct radish **rdp, struct radish *cur)
{
struct radish *target = NULL;
int off = head->rdh_offset, i, lim;
u_char *dp = (u_char *)d_arg + off, *cp;
if (cur == NULL) {
cur = head->rdh_top;
while (cur) {
target = cur;
if (cur->rd_btest & *(dp + cur->rd_masklim))
cur = cur->rd_r;
else
cur = cur->rd_l;
}
} else {
target = cur->rd_p;
if (target == NULL) {
*rdp = NULL;
return 0;
}
}
do {
if (target->rd_rtent != NULL) {
cp = (u_char *)target->rd_route + off;
lim = target->rd_masklim;
if (target->rd_bmask)
if ((*(dp + lim) & target->rd_bmask)
!= *(cp + lim)){
nextparent:
continue;
}
for (i = 0; i < lim; i++)
if(dp[i] != cp[i])
goto nextparent;
*rdp = target;
return 1;
}
} while ((target = target->rd_p));
*rdp = NULL;
return 0;
}
void *
rd_lookup(struct sockaddr *d_arg, struct sockaddr *m_arg,
struct radish_head *head)
{
struct radish *cur = head->rdh_top;
int off = head->rdh_offset, i, lim, olim = 0, masklen;
u_char *dp = (u_char *)d_arg + off, *cp;
rd_mask(m_arg, head, &masklen);
while (cur) {
if (cur->rd_rtent != NULL) {
cp = (u_char *)(cur->rd_route) + off;
lim = cur->rd_masklim;
if (cur->rd_bmask)
if ((*(dp + lim) & cur->rd_bmask)
!= *(cp + lim))
return NULL;
for (i = olim; i < lim; i++)
if(dp[i] != cp[i])
return NULL;
if (masklen == cur->rd_masklen)
return cur->rd_rtent;
olim = lim;
}
if (cur->rd_btest & *(dp + cur->rd_masklim))
cur = cur->rd_r;
else
cur = cur->rd_l;
}
return NULL;
}
int
rd_delete(struct sockaddr *d_arg, struct sockaddr *m_arg,
struct radish_head *head, void **item)
{
struct radish *cur = head->rdh_top;
int off = head->rdh_offset, i, lim, masklen;
u_char *dp = (u_char *)d_arg + off, *cp;
rd_mask(m_arg, head, &masklen);
*item = NULL;
while (cur) {
cp = (u_char *)cur->rd_route + off;
if (cur->rd_bmask)
if ((*(dp + cur->rd_masklim) & cur->rd_bmask)
!= *(cp + cur->rd_masklim))
return ENOENT;
lim = cur->rd_masklim;
for (i = 0; i < lim; i++)
if(dp[i] != cp[i])
return ENOENT;
if (cur->rd_masklen != masklen)
goto next;
cp = (u_char *)cur->rd_route + off;
lim = head->rdh_alen;
for (i = 0; i < lim; i++)
if (dp[i] != cp[i])
goto next;
if (cur->rd_rtent == NULL) {
return EFAULT;
}
*item = cur->rd_rtent;
{
u_char rl = cur->rd_route->sa_len;
u_char ml = cur->rd_mask->sa_len;
bcopy(cur->rd_route, rd_deleted_km, rl);
bcopy(cur->rd_mask, rd_deleted_km + rl, ml);
}
if (cur->rd_l && cur->rd_r) {
cur->rd_rtent = NULL;
return 0;
}
rd_unlink(cur, head->rdh_top);
return 0;
next:
if (cur->rd_btest & *(dp + cur->rd_masklim)) {
if (cur->rd_r) {
cur = cur->rd_r;
continue;
} else
break;
} else {
if (cur->rd_l) {
cur = cur->rd_l;
continue;
} else
break;
}
}
return ENOENT;
}
void
rd_unlink(struct radish *cur, struct radish *top)
{
struct radish *parent, *child;
if (cur == top) {
cur->rd_rtent = NULL;
return;
}
if ((cur->rd_l == 0) && (cur->rd_r == 0)) {
parent = cur->rd_p;
if (parent->rd_l == cur) {
parent->rd_l = NULL;
Free(cur);
} else if (parent->rd_r == cur) {
parent->rd_r = NULL;
Free(cur);
} else
FATAL("rd_unlink");
if (parent->rd_rtent == NULL && parent != top)
rd_unlink(parent, top);
} else {
if (cur->rd_l)
child = cur->rd_l;
else
child = cur->rd_r;
parent = cur->rd_p;
child->rd_p = parent;
if (parent->rd_l == cur) {
parent->rd_l = child;
Free(cur);
} else if (parent->rd_r == cur) {
parent->rd_r = child;
Free(cur);
} else
FATAL("rd_unlink");
}
}
int
rd_walktree(struct radish_head *h, register int (*f)(struct radish *, void *),
void *w)
{
int error = 0;
struct radish *par = NULL, *cur, *top = h->rdh_top;
cur = top;
for (;;) {
while (cur) {
if (cur->rd_rtent != NULL)
if ((error = (*f)(cur, w)))
return error;
par = cur;
cur = cur->rd_l;
}
cur = par;
while (cur->rd_r == NULL || par == cur->rd_r) {
par = cur;
cur = cur->rd_p;
if (cur == NULL) return 0;
}
par = cur;
cur = cur->rd_r;
}
}
int
rd_refines(void *m_arg, void *n_arg)
{
register caddr_t m = m_arg, n = n_arg;
register caddr_t lim, lim2 = lim = n + *(u_char *)n;
int longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
int masks_are_equal = 1;
if (longer > 0)
lim -= longer;
while (n < lim) {
if (*n & ~(*m))
return 0;
if (*n++ != *m++)
masks_are_equal = 0;
}
while (n < lim2)
if (*n++)
return 0;
if (masks_are_equal && (longer < 0))
for (lim2 = m - longer; m < lim2; )
if (*m++)
return 1;
return (!masks_are_equal);
}