#define _KERNEL
#include <sys/refcnt.h>
#include <stdint.h>
#include <sys/rwlock.h>
#undef _KERNEL
#include "srp_compat.h"
#include "smr_compat.h"
#include <sys/socket.h>
#include <sys/domain.h>
#include <sys/queue.h>
#include <sys/srp.h>
#include <net/rtable.h>
#define _KERNEL
#include <net/route.h>
#undef _KERNEL
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <err.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
struct sockaddr *rt_plen2mask(const struct rtentry *, struct sockaddr_in6 *);
struct domain inetdomain = {
.dom_family = AF_INET,
.dom_name = "inet",
.dom_init = NULL,
.dom_externalize = NULL,
.dom_dispose = NULL,
.dom_protosw = NULL,
.dom_protoswNPROTOSW = NULL,
.dom_rtoffset = offsetof(struct sockaddr_in, sin_addr),
.dom_maxplen = 32,
};
struct domain inet6domain = {
.dom_family = AF_INET6,
.dom_name = "inet6",
.dom_init = NULL,
.dom_externalize = NULL,
.dom_dispose = NULL,
.dom_protosw = NULL,
.dom_protoswNPROTOSW = NULL,
.dom_rtoffset = offsetof(struct sockaddr_in6, sin6_addr),
.dom_maxplen = 128,
};
struct domain *domains[] = { &inetdomain, &inet6domain, NULL };
void
route_insert(unsigned int rid, sa_family_t af, char *string)
{
struct sockaddr_storage ss, ms;
struct sockaddr *ndst, *dst = (struct sockaddr *)&ss;
struct sockaddr *mask = (struct sockaddr *)&ms;
struct rtentry *rt, *nrt;
char ip[INET6_ADDRSTRLEN];
int plen, error;
rt = calloc(1, sizeof(*rt));
if (rt == NULL)
errx(1, "out of memory");
refcnt_init(&rt->rt_refcnt);
plen = inet_net_ptosa(af, string, dst, mask);
if (plen == -1)
err(1, "wrong line: %s", string);
ndst = malloc(dst->sa_len);
if (ndst == NULL)
errx(1, "out of memory");
rt_maskedcopy(dst, ndst, mask);
if ((error = rtable_insert(rid, ndst, mask, NULL, 0, rt)) != 0) {
inet_net_satop(af, ndst, plen, ip, sizeof(ip));
errx(1, "can't add route: %s, %s", ip, strerror(error));
}
nrt = rtable_lookup(rid, dst, mask, NULL, RTP_ANY);
if (nrt != rt) {
inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip));
errx(1, "added route not found: %s", ip);
}
rtfree(rt);
rtfree(nrt);
}
void
route_delete(unsigned int rid, sa_family_t af, char *string)
{
struct sockaddr_storage ss, ms;
struct sockaddr *dst = (struct sockaddr *)&ss;
struct sockaddr *mask = (struct sockaddr *)&ms;
struct rtentry *rt, *nrt;
char ip[INET6_ADDRSTRLEN];
int plen, error;
plen = inet_net_ptosa(af, string, dst, mask);
if (plen == -1)
err(1, "wrong line: %s", string);
rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
if (rt == NULL) {
inet_net_satop(af, dst, plen, ip, sizeof(ip));
errx(1, "can't find route: %s", ip);
}
assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0);
assert(rt_plen(rt) == rtable_satoplen(af, mask));
if ((error = rtable_delete(0, dst, mask, rt)) != 0) {
inet_net_satop(af, dst, plen, ip, sizeof(ip));
errx(1, "can't rm route: %s, %s", ip, strerror(error));
}
nrt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
if (nrt != NULL) {
char ip0[INET6_ADDRSTRLEN];
inet_net_satop(af, rt_key(nrt), plen, ip, sizeof(ip));
inet_net_satop(af, rt_key(rt), plen, ip0, sizeof(ip0));
errx(1, "found: %s after deleting: %s", ip, ip0);
}
rtfree(rt);
assert(refcnt_read(&rt->rt_refcnt) == 0);
free(rt_key(rt));
free(rt);
}
void
route_lookup(unsigned int rid, sa_family_t af, char *string)
{
struct sockaddr_storage ss, ms;
struct sockaddr *dst = (struct sockaddr *)&ss;
struct sockaddr *mask = (struct sockaddr *)&ms;
struct rtentry *rt;
char ip[INET6_ADDRSTRLEN];
int plen;
plen = inet_net_ptosa(af, string, dst, mask);
if (plen == -1)
err(1, "wrong line: %s", string);
rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
if (rt == NULL) {
inet_net_satop(af, dst, plen, ip, sizeof(ip));
errx(1, "%s not found", ip);
}
assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0);
assert(rt_plen(rt) == rtable_satoplen(af, mask));
rtfree(rt);
}
int
do_from_file(unsigned int rid, sa_family_t af, char *filename,
void (*func)(unsigned int, sa_family_t, char *))
{
FILE *fp;
char *buf;
size_t len;
int lines = 0;
if ((fp = fopen(filename, "r")) == NULL)
errx(1, "No such file: %s", filename);
while ((buf = fgetln(fp, &len)) != NULL) {
if (buf[len - 1] == '\n')
buf[len - 1] = '\0';
(*func)(rid, af, buf);
lines++;
}
fclose(fp);
return (lines);
}
int
rtentry_dump(struct rtentry *rt, void *w, unsigned int rid)
{
char dest[INET6_ADDRSTRLEN];
sa_family_t af = rt_key(rt)->sa_family;
inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest));
printf("%s\n", dest);
return (0);
}
int
rtentry_delete(struct rtentry *rt, void *w, unsigned int rid)
{
char dest[INET6_ADDRSTRLEN];
sa_family_t af = rt_key(rt)->sa_family;
struct sockaddr_in6 sa_mask;
struct sockaddr *mask = rt_plen2mask(rt, &sa_mask);
int error;
unsigned int refs;
assert(rt_plen(rt) == rtable_satoplen(af, mask));
refs = refcnt_read(&rt->rt_refcnt);
assert(refs > 0);
if ((error = rtable_delete(0, rt_key(rt), mask, rt)) != 0) {
inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest));
errx(1, "can't rm route: %s, %s", dest, strerror(error));
}
assert(refcnt_read(&rt->rt_refcnt) == refs - 1);
return (0);
}
void
rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst,
struct sockaddr *netmask)
{
uint8_t *cp1 = (uint8_t *)src;
uint8_t *cp2 = (uint8_t *)dst;
uint8_t *cp3 = (uint8_t *)netmask;
uint8_t *cplim = cp2 + *cp3;
uint8_t *cplim2 = cp2 + *cp1;
*cp2++ = *cp1++; *cp2++ = *cp1++;
cp3 += 2;
if (cplim > cplim2)
cplim = cplim2;
while (cp2 < cplim)
*cp2++ = *cp1++ & *cp3++;
if (cp2 < cplim2)
memset(cp2, 0, (unsigned int)(cplim2 - cp2));
}
void
rtref(struct rtentry *rt)
{
refcnt_take(&rt->rt_refcnt);
}
void
rtfree(struct rtentry *rt)
{
if (refcnt_rele(&rt->rt_refcnt) == 0)
return;
}
void
refcnt_init(struct refcnt *r)
{
r->r_refs = 1;
}
void
refcnt_take(struct refcnt *r)
{
u_int refs;
refs = ++r->r_refs;
assert(refs != 0);
}
int
refcnt_rele(struct refcnt *r)
{
u_int refs;
refs = --r->r_refs;
assert(refs != ~0);
if (r->r_refs == 0)
return (1);
return (0);
}
unsigned int
refcnt_read(const struct refcnt *r)
{
return (r->r_refs);
}
void
in_prefixlen2mask(struct in_addr *maskp, int plen)
{
if (plen == 0)
maskp->s_addr = 0;
else
maskp->s_addr = htonl(0xffffffff << (32 - plen));
}
void
in6_prefixlen2mask(struct in6_addr *maskp, int len)
{
uint8_t maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
int bytelen, bitlen, i;
assert(0 <= len && len <= 128);
memset(maskp, 0, sizeof(*maskp));
bytelen = len / 8;
bitlen = len % 8;
for (i = 0; i < bytelen; i++)
maskp->s6_addr[i] = 0xff;
if (bitlen)
maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
}
struct sockaddr *
rt_plentosa(sa_family_t af, int plen, struct sockaddr_in6 *sa_mask)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa_mask;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa_mask;
assert(plen >= 0 || plen == -1);
if (plen == -1)
return (NULL);
memset(sa_mask, 0, sizeof(*sa_mask));
switch (af) {
case AF_INET:
sin->sin_family = AF_INET;
sin->sin_len = sizeof(struct sockaddr_in);
in_prefixlen2mask(&sin->sin_addr, plen);
break;
case AF_INET6:
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
in6_prefixlen2mask(&sin6->sin6_addr, plen);
break;
default:
return (NULL);
}
return ((struct sockaddr *)sa_mask);
}
struct sockaddr *
rt_plen2mask(const struct rtentry *rt, struct sockaddr_in6 *sa_mask)
{
return (rt_plentosa(rt_key(rt)->sa_family, rt_plen(rt), sa_mask));
}
int
inet_net_ptosa(sa_family_t af, const char *buf, struct sockaddr *sa,
struct sockaddr *ma)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
int i, plen;
switch (af) {
case AF_INET:
memset(sin, 0, sizeof(*sin));
sin->sin_family = af;
sin->sin_len = sizeof(*sin);
plen = inet_net_pton(af, buf, &sin->sin_addr,
sizeof(sin->sin_addr));
if (plen == -1 || ma == NULL)
break;
sin = (struct sockaddr_in *)ma;
memset(sin, 0, sizeof(*sin));
sin->sin_len = sizeof(*sin);
sin->sin_family = 0;
in_prefixlen2mask(&sin->sin_addr, plen);
break;
case AF_INET6:
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = af;
sin6->sin6_len = sizeof(*sin6);
plen = inet_net_pton(af, buf, &sin6->sin6_addr,
sizeof(sin6->sin6_addr));
if (plen == -1 || ma == NULL)
break;
sin6 = (struct sockaddr_in6 *)ma;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = 0;
for (i = 0; i < plen / 8; i++)
sin6->sin6_addr.s6_addr[i] = 0xff;
i = plen % 8;
if (i)
sin6->sin6_addr.s6_addr[plen / 8] = 0xff00 >> i;
break;
default:
plen = -1;
}
return (plen);
}
int
maskcmp(sa_family_t af, struct sockaddr *sa1, struct sockaddr *sa2)
{
struct sockaddr_in *sin1, *sin2;
struct sockaddr_in6 *sin61, *sin62;
int len;
switch (af) {
case AF_INET:
sin1 = (struct sockaddr_in *)sa1;
sin2 = (struct sockaddr_in *)sa2;
len = sizeof(sin1->sin_addr);
return memcmp(&sin1->sin_addr, &sin2->sin_addr, len);
case AF_INET6:
sin61 = (struct sockaddr_in6 *)sa1;
sin62 = (struct sockaddr_in6 *)sa2;
len = sizeof(sin61->sin6_addr);
return memcmp(&sin61->sin6_addr, &sin62->sin6_addr, len);
default:
return (-1);
}
}
char *
inet_net_satop(sa_family_t af, struct sockaddr *sa, int plen, char *buf,
size_t len)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
switch (af) {
case AF_INET:
return inet_net_ntop(af, &sin->sin_addr, plen, buf, len);
case AF_INET6:
return inet_net_ntop(af, &sin6->sin6_addr, plen, buf, len);
default:
return (NULL);
}
}
static uint32_t rt_hashjitter;
#define mix(a, b, c) do { \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
} while (0)
int
rt_hash(struct rtentry *rt, const struct sockaddr *dst, uint32_t *src)
{
uint32_t a, b, c;
while (rt_hashjitter == 0)
rt_hashjitter = arc4random();
if (src == NULL)
return (-1);
a = b = 0x9e3779b9;
c = rt_hashjitter;
switch (dst->sa_family) {
case AF_INET:
{
struct sockaddr_in *sin;
sin = satosin(dst);
a += sin->sin_addr.s_addr;
b += (src != NULL) ? src[0] : 0;
mix(a, b, c);
break;
}
#ifdef INET6
case AF_INET6:
{
struct sockaddr_in6 *sin6;
sin6 = satosin6(dst);
a += sin6->sin6_addr.s6_addr32[0];
b += sin6->sin6_addr.s6_addr32[2];
c += (src != NULL) ? src[0] : 0;
mix(a, b, c);
a += sin6->sin6_addr.s6_addr32[1];
b += sin6->sin6_addr.s6_addr32[3];
c += (src != NULL) ? src[1] : 0;
mix(a, b, c);
a += sin6->sin6_addr.s6_addr32[2];
b += sin6->sin6_addr.s6_addr32[1];
c += (src != NULL) ? src[2] : 0;
mix(a, b, c);
a += sin6->sin6_addr.s6_addr32[3];
b += sin6->sin6_addr.s6_addr32[0];
c += (src != NULL) ? src[3] : 0;
mix(a, b, c);
break;
}
#endif
}
return (c & 0xffff);
}
void
rt_timer_init(void)
{
}
unsigned int smr_crit;
void
smr_read_enter(void)
{
smr_crit++;
}
void
smr_read_leave(void)
{
smr_crit--;
}
void
SMR_ASSERT_CRITICAL(void)
{
assert(smr_crit > 0);
}