cidr

From: Paul A Vixie <vixie(at)vix(dot)com>
To: pgsql-hackers(at)hub(dot)org
Subject: cidr
Date: 1998-07-19 19:27:13
Message-ID: 199807191927.MAA17100@bb.rc.vix.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

i didn't realize that anybody else was working on an IP address
data type or i'd've posted this six months ago when i first wrote
it. it lacks only the stuff needed to make it usable as a UNIQUE
KEY. it depends on BIND-8's libraries.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1998-07-19 12:25 PDT by <vixie(at)bb(dot)rc(dot)vix(dot)com>.
# Source directory was `/tmp_mnt/mb/mb0/user/vixie/src/postgres-cidrtype'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 671 -r--r--r-- Makefile
# 4572 -r--r--r-- cidr.c
# 2877 -r--r--r-- cidr.source
# 3068 -r--r--r-- cidr.sql
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
set `$dir/gettext --version 2>&1`
if test "$3" = GNU
then
gettext_dir=$dir
fi
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
shar_touch=touch
else
shar_touch=:
echo
$echo 'WARNING: not restoring timestamps. Consider getting and'
$echo "installing GNU \`touch', distributed in GNU File Utilities..."
echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh17086; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= Makefile ==============
if test -f 'Makefile' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'Makefile' '(file already exists)'
else
$echo 'x -' extracting 'Makefile' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
ifndef PGDIR
PGDIR= /db0/local/postgresql-6.2
endif
X
SRCDIR= $(PGDIR)/src
X
include $(SRCDIR)/Makefile.global
X
CFLAGS+= -I$(PGDIR)/include -I$(PGDIR)/src/include -I$(LIBPQDIR)
CFLAGS+= -I/usr/local/bind/include
X
CLIBS+= -L/usr/local/bind/lib -lbind
X
TARGETS= cidr.sql cidr${DLSUFFIX}
X
DLSUFFIX=.so
X
all: $(TARGETS)
X
cidr${DLSUFFIX}: cidr.o
X shlicc2 -r -o cidr${DLSUFFIX} cidr.o -L/usr/local/bind/lib -lbind
X
install:
X $(MAKE) all
X cp -p cidr$(DLSUFFIX) $(LIBDIR)
X
%.sql: %.source
X rm -f $@; C=`pwd`; O=$$C; \
X if [ -d ${LIBDIR} ]; then O=${LIBDIR}; fi; \
X sed -e "s:_OBJWD_:$$O:g" \
X -e "s:_DLSUFFIX_:$(DLSUFFIX):g" \
X < $< > $@
X
clean:
X rm -f $(TARGETS) cidr.o
X
SHAR_EOF
$shar_touch -am 1108213897 'Makefile' &&
chmod 0444 'Makefile' ||
$echo 'restore of' 'Makefile' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'Makefile:' 'MD5 check failed'
ecb325bcab4a92f4fd5657cdc29a9f63 Makefile
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'Makefile'`"
test 671 -eq "$shar_count" ||
$echo 'Makefile:' 'original size' '671,' 'current size' "$shar_count!"
fi
fi
# ============= cidr.c ==============
if test -f 'cidr.c' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'cidr.c' '(file already exists)'
else
$echo 'x -' extracting 'cidr.c' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'cidr.c' &&
/*
X * cidr.c - Internal Classless InterDomain Routing entities for PostGreSQL
X *
X * Paul Vixie <paul(at)vix(dot)com>, Internet Software Consortium, October 1997.
X *
X * $Id: cidr.c,v 1.4 1998/07/15 19:36:56 vixie Exp $
X */
X
/* Import. */
X
#include <sys/types.h>
#include <sys/socket.h>
X
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
X
#include <netinet/in.h>
#include <arpa/inet.h>
#include <isc/misc.h>
X
#include "postgres.h"
#include "utils/palloc.h"
X
/* Define. */
X
#define cidr_min(a, b) (((a) < (b)) ? (a) : (b))
X
typedef struct {
X unsigned char family;
X unsigned char bits;
X unsigned char bytes[1]; /* This is really an open array. */
} cidr;
X
#define cidr_addrsize(fam) ((fam) == AF_INET ? 4 : -1)
#define cidr_size(addrsize) (sizeof(cidr) - sizeof(unsigned char) + addrsize)
X
/* Export. */
X
cidr * cidr_in(const char *);
char * cidr_out(const cidr *);
X
bool cidr_eq(const cidr *, const cidr *);
bool cidr_ne(const cidr *, const cidr *);
bool cidr_lt(const cidr *, const cidr *);
bool cidr_gt(const cidr *, const cidr *);
bool cidr_le(const cidr *, const cidr *);
bool cidr_ge(const cidr *, const cidr *);
bool cidr_sub(const cidr *, const cidr *);
bool cidr_subeq(const cidr *, const cidr *);
bool cidr_sup(const cidr *, const cidr *);
bool cidr_supeq(const cidr *, const cidr *);
int4 cidr_span(const cidr *, const cidr *);
int4 cidr_cmp(const cidr *, const cidr *);
X
/* Functions. */
X
cidr *
cidr_in(const char *src) {
X int bits, bytes;
X cidr *dst;
X
X bytes = cidr_addrsize(AF_INET);
X if (bytes == -1) {
X elog(WARN, "Programming error in cidr_in()");
X return (NULL);
X }
X dst = palloc(cidr_size(bytes));
X if (dst == NULL) {
X elog(WARN, "Unable to allocate memory in cidr_in()");
X return (NULL);
X }
X bits = inet_net_pton(AF_INET, src, &dst->bytes, bytes);
X if (bits < 0 || bits > 32) {
X elog(WARN, "Bad CIDR expression (%s)", src);
X pfree(dst);
X return (NULL);
X }
X dst->bits = (unsigned char)bits;
X return (dst);
}
X
char *
cidr_out(const cidr *src) {
X char *dst, tmp[sizeof "255.255.255.255/32"];
X
X if (inet_net_ntop(AF_INET, &src->bytes, src->bits,
X tmp, sizeof tmp) < 0) {
X elog(WARN, "Unable to format CIDR (%s)", strerror(errno));
X pfree(dst);
X return (NULL);
X }
X dst = palloc(strlen(tmp) + 1);
X if (dst == NULL) {
X elog(WARN, "Unable to allocate memory in cidr_out()");
X return (NULL);
X }
X strcpy(dst, tmp);
X return (dst);
}
X
/* Equality. */
X
bool
cidr_eq(const cidr *lhs, const cidr *rhs) {
X return (lhs->bits == rhs->bits &&
X bitncmp(lhs->bytes, rhs->bytes, lhs->bits) == 0);
}
X
bool
cidr_ne(const cidr *lhs, const cidr *rhs) {
X return (!cidr_eq(lhs, rhs));
}
X
/* Ordering. */
X
bool
cidr_lt(const cidr *lhs, const cidr *rhs) {
X int x = bitncmp(lhs->bytes, rhs->bytes,
X cidr_min(lhs->bits, rhs->bits));
X
X return (x < 0 || (x == 0 && lhs->bits < rhs->bits));
}
X
bool
cidr_le(const cidr *lhs, const cidr *rhs) {
X return (cidr_lt(lhs, rhs) || cidr_eq(lhs, rhs));
}
X
bool
cidr_gt(const cidr *lhs, const cidr *rhs) {
X int x = bitncmp(lhs->bytes, rhs->bytes,
X cidr_min(lhs->bits, rhs->bits));
X
X return (x > 0 || (x == 0 && lhs->bits > rhs->bits));
}
X
bool
cidr_ge(const cidr *lhs, const cidr *rhs) {
X return (cidr_gt(lhs, rhs) || cidr_eq(lhs, rhs));
}
X
/* Subnetting. */
X
bool
cidr_sub(const cidr *lhs, const cidr *rhs) {
X return (lhs->bits > rhs->bits &&
X bitncmp(lhs->bytes, rhs->bytes, rhs->bits) == 0);
}
X
bool
cidr_subeq(const cidr *lhs, const cidr *rhs) {
X return (lhs->bits >= rhs->bits &&
X bitncmp(lhs->bytes, rhs->bytes, rhs->bits) == 0);
}
X
/* Supernetting. */
X
bool
cidr_sup(const cidr *lhs, const cidr *rhs) {
X return (lhs->bits < rhs->bits &&
X bitncmp(lhs->bytes, rhs->bytes, lhs->bits) == 0);
}
X
bool
cidr_supeq(const cidr *lhs, const cidr *rhs) {
X return (lhs->bits <= rhs->bits &&
X bitncmp(lhs->bytes, rhs->bytes, lhs->bits) == 0);
}
X
int4
cidr_span(const cidr *lhs, const cidr *rhs) {
X const u_char *l = lhs->bytes, *r = rhs->bytes;
X int n = cidr_min(lhs->bits, rhs->bits);
X int b = n >> 3;
X int4 result = 0;
X u_int lb, rb;
X
X /* Find out how many full octets match. */
X while (b > 0 && *l == *r)
X b--, l++, r++, result += 8;
X /* Find out how many bits to check. */
X if (b == 0)
X b = n & 07;
X else
X b = 8;
X /* Find out how many bits match. */
X lb = *l, rb = *r;
X while (b > 0 && (lb & 0x80) == (rb & 0x80))
X b--, lb <<= 1, rb <<= 1, result++;
X return (result);
}
X
int4
cidr_cmp(const cidr *lhs, const cidr *rhs) {
X int x = bitncmp(lhs->bytes, rhs->bytes,
X cidr_min(lhs->bits, rhs->bits));
X
X if (x < 0)
X return (-1);
X if (x > 0)
X return (1);
X return (0);
}
SHAR_EOF
$shar_touch -am 0715123698 'cidr.c' &&
chmod 0444 'cidr.c' ||
$echo 'restore of' 'cidr.c' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'cidr.c:' 'MD5 check failed'
f8fd720dbffa7ab05d594c9953b75170 cidr.c
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'cidr.c'`"
test 4572 -eq "$shar_count" ||
$echo 'cidr.c:' 'original size' '4572,' 'current size' "$shar_count!"
fi
fi
# ============= cidr.source ==============
if test -f 'cidr.source' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'cidr.source' '(file already exists)'
else
$echo 'x -' extracting 'cidr.source' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'cidr.source' &&
---------------------------------------------------------------------------
--
-- cidr.sql-
-- This file defines operators Classless InterDomain Routing entities.
--
---------------------------------------------------------------------------
X
LOAD '_OBJWD_/cidr_DLSUFFIX_';
X
CREATE FUNCTION cidr_in(opaque)
X RETURNS cidr
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE FUNCTION cidr_out(opaque)
X RETURNS opaque
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE TYPE cidr (
X internallength = 6,
X input = cidr_in,
X output = cidr_out
);
X
CREATE FUNCTION cidr_cmp(cidr, cidr)
X RETURNS int4
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
-----------------------------
-- Create operators
-----------------------------
X
-- equality (=)
X
CREATE FUNCTION cidr_eq(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR = (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_eq,
X commutator = =
);
X
-- inequality (<>)
X
CREATE FUNCTION cidr_ne(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR <> (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_ne,
X commutator = <>
);
X
-- less (<, <=)
X
CREATE FUNCTION cidr_lt(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR < (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_lt
);
X
CREATE FUNCTION cidr_le(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR <= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_le
);
X
-- greater (>, >=)
X
CREATE FUNCTION cidr_gt(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR > (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_gt
);
X
CREATE FUNCTION cidr_ge(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR >= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_ge
);
X
-- subnet (<<, <<=)
X
CREATE FUNCTION cidr_sub(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR << (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_sub
);
X
CREATE FUNCTION cidr_subeq(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR <<= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_subeq
);
X
-- supernet (>>, >>=)
X
CREATE FUNCTION cidr_sup(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR >> (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_sup
);
X
CREATE FUNCTION cidr_supeq(cidr, cidr)
X RETURNS bool
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE OPERATOR >>= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_supeq
);
X
-- spanning (length of prefix match)
X
CREATE FUNCTION cidr_span(cidr, cidr)
X RETURNS int4
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
X
CREATE FUNCTION cidr_masklen(cidr)
X RETURNS int2
X AS '_OBJWD_/cidr_DLSUFFIX_'
X LANGUAGE 'c';
SHAR_EOF
$shar_touch -am 0719122498 'cidr.source' &&
chmod 0444 'cidr.source' ||
$echo 'restore of' 'cidr.source' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'cidr.source:' 'MD5 check failed'
dca27b8d433d030e5049bb04ad15df03 cidr.source
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'cidr.source'`"
test 2877 -eq "$shar_count" ||
$echo 'cidr.source:' 'original size' '2877,' 'current size' "$shar_count!"
fi
fi
# ============= cidr.sql ==============
if test -f 'cidr.sql' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'cidr.sql' '(file already exists)'
else
$echo 'x -' extracting 'cidr.sql' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'cidr.sql' &&
---------------------------------------------------------------------------
--
-- cidr.sql-
-- This file defines operators Classless InterDomain Routing entities.
--
---------------------------------------------------------------------------
X
LOAD '/var/home/vixie/postgres-cidrtype/cidr.so';
X
CREATE FUNCTION cidr_in(opaque)
X RETURNS cidr
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE FUNCTION cidr_out(opaque)
X RETURNS opaque
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE TYPE cidr (
X internallength = 5,
X input = cidr_in,
X output = cidr_out
);
X
CREATE FUNCTION cidr_cmp(cidr, cidr)
X RETURNS int4
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
-----------------------------
-- Create operators
-----------------------------
X
-- equality (=)
X
CREATE FUNCTION cidr_eq(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR = (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_eq,
X commutator = =
);
X
-- inequality (<>)
X
CREATE FUNCTION cidr_ne(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR <> (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_ne,
X commutator = <>
);
X
-- less (<, <=)
X
CREATE FUNCTION cidr_lt(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR < (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_lt
);
X
CREATE FUNCTION cidr_le(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR <= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_le
);
X
-- greater (>, >=)
X
CREATE FUNCTION cidr_gt(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR > (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_gt
);
X
CREATE FUNCTION cidr_ge(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR >= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_ge
);
X
-- subnet (<<, <<=)
X
CREATE FUNCTION cidr_sub(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR << (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_sub
);
X
CREATE FUNCTION cidr_subeq(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR <<= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_subeq
);
X
-- supernet (>>, >>=)
X
CREATE FUNCTION cidr_sup(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR >> (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_sup
);
X
CREATE FUNCTION cidr_supeq(cidr, cidr)
X RETURNS bool
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
X
CREATE OPERATOR >>= (
X leftarg = cidr,
X rightarg = cidr,
X procedure = cidr_supeq
);
X
-- spanning (length of prefix match)
X
CREATE FUNCTION cidr_span(cidr, cidr)
X RETURNS int4
X AS '/var/home/vixie/postgres-cidrtype/cidr.so'
X LANGUAGE 'c';
SHAR_EOF
$shar_touch -am 1201183797 'cidr.sql' &&
chmod 0444 'cidr.sql' ||
$echo 'restore of' 'cidr.sql' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'cidr.sql:' 'MD5 check failed'
097a4f0f2b5915fc5478976233c714f3 cidr.sql
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'cidr.sql'`"
test 3068 -eq "$shar_count" ||
$echo 'cidr.sql:' 'original size' '3068,' 'current size' "$shar_count!"
fi
fi
rm -fr _sh17086
exit 0

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Michal Mosiewicz 1998-07-19 23:46:27 Re: [HACKERS] using C++ to define new functions
Previous Message Bruce Momjian 1998-07-19 18:53:14 Re: [HACKERS] Removal of recipe/tioga/Tee node