summaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
authorKjell Wooding <kjell@cvs.openbsd.org>2001-06-24 19:49:00 +0000
committerKjell Wooding <kjell@cvs.openbsd.org>2001-06-24 19:49:00 +0000
commit6b7e146b5046259ba9faa9444114b5c4c18070fe (patch)
tree6769ac6a79b4a0c4a79698115a952bc6f6a41909 /sys/net
parent4d339188d7d3d36098d08968a138bb031f00562d (diff)
Initial import of pf, an all-new ipf-compatable packet filter.
Insane amounts of work done my dhartmei. Great work!
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/pf.c1704
-rw-r--r--sys/net/pfvar.h170
2 files changed, 1874 insertions, 0 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c
new file mode 100644
index 00000000000..8025d79cef6
--- /dev/null
+++ b/sys/net/pf.c
@@ -0,0 +1,1704 @@
+/* $OpenBSD: pf.c,v 1.1 2001/06/24 19:48:58 kjell Exp $ */
+
+/*
+ * Copyright (c) 2001, Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/pfvar.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+/*
+ * Tree data structure
+ */
+
+struct tree_node {
+ struct tree_key {
+ u_int8_t proto;
+ u_int32_t addr[2];
+ u_int16_t port[2];
+ } key;
+ struct state *state;
+ signed char balance;
+ struct tree_node *left,
+ *right;
+};
+
+/*
+ * Global variables
+ */
+
+struct rule *rulehead = NULL;
+struct nat *nathead = NULL;
+struct rdr *rdrhead = NULL;
+struct state *statehead = NULL;
+struct tree_node *tree_lan_ext = NULL,
+ *tree_ext_gwy = NULL;
+struct timeval tv;
+struct status status;
+struct ifnet *status_ifp = NULL;
+u_int32_t last_purge = 0;
+u_int16_t next_port_tcp = 50001,
+ next_port_udp = 50001;
+
+/*
+ * Prototypes
+ */
+
+signed char tree_key_compare (struct tree_key *a, struct tree_key *b);
+void tree_rotate_left (struct tree_node **p);
+void tree_rotate_right (struct tree_node **p);
+int tree_insert (struct tree_node **p, struct tree_key *key,
+ struct state *state);
+int tree_remove (struct tree_node **p, struct tree_key *key);
+struct state *find_state (struct tree_node *p, struct tree_key *key);
+void insert_state (struct state *state);
+void purge_expired_states (void);
+void print_ip (struct ifnet *ifp, struct ip *h);
+void print_host (u_int32_t a, u_int16_t p);
+void print_state (int direction, struct state *s);
+void print_flags (u_int8_t f);
+void pfattach (int num);
+int pfopen (dev_t dev, int flags, int fmt, struct proc *p);
+int pfclose (dev_t dev, int flags, int fmt, struct proc *p);
+int pfioctl (dev_t dev, u_long cmd, caddr_t addr, int flags,
+ struct proc *p);
+u_short fix (u_short cksum, u_short old, u_short new);
+void change_ap (u_int32_t *a, u_int16_t *p, u_int16_t *ic, u_int16_t
+ *pc, u_int32_t an, u_int16_t pn);
+void change_a (u_int32_t *a, u_int16_t *c, u_int32_t an);
+void change_icmp (u_int32_t *ia, u_int16_t *ip, u_int32_t *oa,
+ u_int32_t na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c,
+ u_int16_t *ic, u_int16_t *hc);
+void send_reset (int direction, struct ifnet *ifp, struct ip *h,
+ struct tcphdr *th);
+int match_addr (u_int8_t n, u_int32_t a, u_int32_t m, u_int32_t b);
+int match_port (u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p);
+struct nat *get_nat (struct ifnet *ifp, u_int8_t proto, u_int32_t addr);
+struct rdr *get_rdr (struct ifnet *ifp, u_int8_t proto, u_int32_t addr,
+ u_int16_t port);
+int pf_test_tcp (int direction, struct ifnet *ifp, struct ip *h,
+ struct tcphdr *th);
+int pf_test_udp (int direction, struct ifnet *ifp, struct ip *h,
+ struct udphdr *uh);
+int pf_test_icmp (int direction, struct ifnet *ifp, struct ip *h,
+ struct icmp *ih);
+struct state *pf_test_state_tcp (int direction, struct ifnet *ifp, struct ip *h,
+ struct tcphdr *th);
+struct state *pf_test_state_udp (int direction, struct ifnet *ifp, struct ip *h,
+ struct udphdr *uh);
+struct state *pf_test_state_icmp (int direction, struct ifnet *ifp,
+ struct ip *h, struct icmp *ih);
+void *pull_hdr (struct ifnet *ifp, struct mbuf **m, struct ip *h,
+ int *action, u_int8_t len);
+int pf_test (int direction, struct ifnet *ifp, struct mbuf **m);
+
+/* ------------------------------------------------------------------------ */
+
+inline signed char
+tree_key_compare(struct tree_key *a, struct tree_key *b)
+{
+ /*
+ * could use memcmp(), but with the best manual order, we can
+ * minimize the number of average compares. what is faster?
+ */
+ if (a->proto < b->proto ) return -1;
+ if (a->proto > b->proto ) return 1;
+ if (a->addr[0] < b->addr[0]) return -1;
+ if (a->addr[0] > b->addr[0]) return 1;
+ if (a->addr[1] < b->addr[1]) return -1;
+ if (a->addr[1] > b->addr[1]) return 1;
+ if (a->port[0] < b->port[0]) return -1;
+ if (a->port[0] > b->port[0]) return 1;
+ if (a->port[1] < b->port[1]) return -1;
+ if (a->port[1] > b->port[1]) return 1;
+ return 0;
+}
+
+inline void
+tree_rotate_left(struct tree_node **p)
+{
+ struct tree_node *q = *p;
+ *p = (*p)->right;
+ q->right = (*p)->left;
+ (*p)->left = q;
+ q->balance--;
+ if ((*p)->balance > 0)
+ q->balance -= (*p)->balance;
+ (*p)->balance--;
+ if (q->balance < 0)
+ (*p)->balance += q->balance;
+}
+
+inline void
+tree_rotate_right(struct tree_node **p)
+{
+ struct tree_node *q = *p;
+ *p = (*p)->left;
+ q->left = (*p)->right;
+ (*p)->right = q;
+ q->balance++;
+ if ((*p)->balance < 0)
+ q->balance -= (*p)->balance;
+ (*p)->balance++;
+ if (q->balance > 0)
+ (*p)->balance += q->balance;
+}
+
+int
+tree_insert(struct tree_node **p, struct tree_key *key, struct state *state)
+{
+ int deltaH = 0;
+ if (*p == NULL) {
+ *p = malloc(sizeof(struct tree_node), M_DEVBUF, M_NOWAIT);
+ if (*p == NULL) {
+ printf("packetfilter: malloc() failed\n");
+ return 0;
+ }
+ memcpy(&(*p)->key, key, sizeof(struct tree_key));
+ (*p)->state = state;
+ (*p)->balance = 0;
+ (*p)->left = (*p)->right = NULL;
+ deltaH = 1;
+ } else if (tree_key_compare(key, &(*p)->key) > 0) {
+ if (tree_insert(&(*p)->right, key, state)) {
+ (*p)->balance++;
+ if ((*p)->balance == 1)
+ deltaH = 1;
+ else if ((*p)->balance == 2) {
+ if ((*p)->right->balance == -1)
+ tree_rotate_right(&(*p)->right);
+ tree_rotate_left(p);
+ }
+ }
+ } else {
+ if (tree_insert(&(*p)->left, key, state)) {
+ (*p)->balance--;
+ if ((*p)->balance == -1)
+ deltaH = 1;
+ else if ((*p)->balance == -2) {
+ if ((*p)->left->balance == 1)
+ tree_rotate_left(&(*p)->left);
+ tree_rotate_right(p);
+ }
+ }
+ }
+ return deltaH;
+}
+
+int
+tree_remove(struct tree_node **p, struct tree_key *key)
+{
+ int deltaH = 0;
+ signed char c;
+ if (*p == NULL)
+ return 0;
+ c = tree_key_compare(key, &(*p)->key);
+ if (c < 0) {
+ if (tree_remove(&(*p)->left, key)) {
+ (*p)->balance++;
+ if ((*p)->balance == 0)
+ deltaH = 1;
+ else if ((*p)->balance == 2) {
+ if ((*p)->right->balance == -1)
+ tree_rotate_right(&(*p)->right);
+ tree_rotate_left(p);
+ if ((*p)->balance == 0)
+ deltaH = 1;
+ }
+ }
+ } else if (c > 0) {
+ if (tree_remove(&(*p)->right, key)) {
+ (*p)->balance--;
+ if ((*p)->balance == 0)
+ deltaH = 1;
+ else if ((*p)->balance == -2) {
+ if ((*p)->left->balance == 1)
+ tree_rotate_left(&(*p)->left);
+ tree_rotate_right(p);
+ if ((*p)->balance == 0)
+ deltaH = 1;
+ }
+ }
+ } else {
+ if ((*p)->right == NULL) {
+ struct tree_node *p0 = *p;
+ *p = (*p)->left;
+ free(p0, M_DEVBUF);
+ deltaH = 1;
+ } else if ((*p)->left == NULL) {
+ struct tree_node *p0 = *p;
+ *p = (*p)->right;
+ free(p0, M_DEVBUF);
+ deltaH = 1;
+ } else {
+ struct tree_node **qq = &(*p)->left;
+ while ((*qq)->right != NULL)
+ qq = &(*qq)->right;
+ memcpy(&(*p)->key, &(*qq)->key, sizeof(struct tree_key));
+ (*p)->state = (*qq)->state;
+ memcpy(&(*qq)->key, key, sizeof(struct tree_key));
+ if (tree_remove(&(*p)->left, key)) {
+ (*p)->balance++;
+ if ((*p)->balance == 0)
+ deltaH = 1;
+ else if ((*p)->balance == 2) {
+ if ((*p)->right->balance == -1)
+ tree_rotate_right(&(*p)->right);
+ tree_rotate_left(p);
+ if ((*p)->balance == 0)
+ deltaH = 1;
+ }
+ }
+ }
+ }
+ return deltaH;
+}
+
+inline struct state *
+find_state(struct tree_node *p, struct tree_key *key)
+{
+ signed char c;
+ while ((p != NULL) && (c = tree_key_compare(&p->key, key)))
+ p = (c > 0) ? p->left : p->right;
+ status.state_searches++;
+ return p ? p->state : NULL;
+}
+
+void
+insert_state(struct state *state)
+{
+ struct tree_key key;
+
+ key.proto = state->proto;
+ key.addr[0] = state->lan.addr;
+ key.port[0] = state->lan.port;
+ key.addr[1] = state->ext.addr;
+ key.port[1] = state->ext.port;
+ /* sanity checks can be removed later, should never occur */
+ if (find_state(tree_lan_ext, &key) != NULL)
+ printf("packetfilter: ERROR! insert invalid\n");
+ else {
+ tree_insert(&tree_lan_ext, &key, state);
+ if (find_state(tree_lan_ext, &key) != state)
+ printf("packetfilter: ERROR! insert failed\n");
+ }
+
+ key.proto = state->proto;
+ key.addr[0] = state->ext.addr;
+ key.port[0] = state->ext.port;
+ key.addr[1] = state->gwy.addr;
+ key.port[1] = state->gwy.port;
+ if (find_state(tree_ext_gwy, &key) != NULL)
+ printf("packetfilter: ERROR! insert invalid\n");
+ else {
+ tree_insert(&tree_ext_gwy, &key, state);
+ if (find_state(tree_ext_gwy, &key) != state)
+ printf("packetfilter: ERROR! insert failed\n");
+ }
+
+ state->next = statehead;
+ statehead = state;
+
+ status.state_inserts++;
+ status.states++;
+}
+
+void
+purge_expired_states(void)
+{
+ struct tree_key key;
+ struct state *cur = statehead, *prev = NULL;
+ while (cur != NULL) {
+ if (cur->expire <= tv.tv_sec) {
+ key.proto = cur->proto;
+ key.addr[0] = cur->lan.addr;
+ key.port[0] = cur->lan.port;
+ key.addr[1] = cur->ext.addr;
+ key.port[1] = cur->ext.port;
+ /* sanity checks can be removed later */
+ if (find_state(tree_lan_ext, &key) != cur)
+ printf("packetfilter: ERROR! remove invalid\n");
+ tree_remove(&tree_lan_ext, &key);
+ if (find_state(tree_lan_ext, &key) != NULL)
+ printf("packetfilter: ERROR! remove failed\n");
+ key.proto = cur->proto;
+ key.addr[0] = cur->ext.addr;
+ key.port[0] = cur->ext.port;
+ key.addr[1] = cur->gwy.addr;
+ key.port[1] = cur->gwy.port;
+ if (find_state(tree_ext_gwy, &key) != cur)
+ printf("packetfilter: ERROR! remove invalid\n");
+ tree_remove(&tree_ext_gwy, &key);
+ if (find_state(tree_ext_gwy, &key) != NULL)
+ printf("packetfilter: ERROR! remove failed\n");
+ (prev ? prev->next : statehead) = cur->next;
+ free(cur, M_DEVBUF);
+ cur = (prev ? prev->next : statehead);
+ status.state_removals++;
+ status.states--;
+ } else {
+ prev = cur;
+ cur = cur->next;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+
+inline void
+print_ip(struct ifnet *ifp, struct ip *h)
+{
+ u_int32_t a;
+ printf(" %s:", ifp->if_xname);
+ a = ntohl(h->ip_src.s_addr);
+ printf(" %u.%u.%u.%u", (a>>24)&255, (a>>16)&255, (a>>8)&255, a&255);
+ a = ntohl(h->ip_dst.s_addr);
+ printf(" -> %u.%u.%u.%u", (a>>24)&255, (a>>16)&255, (a>>8)&255, a&255);
+ printf(" hl=%u len=%u id=%u", h->ip_hl << 2, h->ip_len - (h->ip_hl << 2),
+ h->ip_id);
+ if (h->ip_off & IP_RF) printf(" RF");
+ if (h->ip_off & IP_DF) printf(" DF");
+ if (h->ip_off & IP_MF) printf(" MF");
+ printf(" off=%u proto=%u\n", (h->ip_off & IP_OFFMASK) << 3, h->ip_p);
+}
+
+void
+print_host(u_int32_t a, u_int16_t p)
+{
+ a = ntohl(a);
+ p = ntohs(p);
+ printf("%u.%u.%u.%u:%u", (a>>24)&255, (a>>16)&255, (a>>8)&255, a&255, p);
+}
+
+void
+print_state(int direction, struct state *s)
+{
+ print_host(s->lan.addr, s->lan.port);
+ printf(" ");
+ print_host(s->gwy.addr, s->gwy.port);
+ printf(" ");
+ print_host(s->ext.addr, s->ext.port);
+ printf(" [%lu+%lu]", s->src.seqlo, s->src.seqhi - s->src.seqlo);
+ printf(" [%lu+%lu]", s->dst.seqlo, s->dst.seqhi - s->dst.seqlo);
+ printf(" %u:%u", s->src.state, s->dst.state);
+}
+
+void
+print_flags(u_int8_t f)
+{
+ if (f) printf(" ");
+ if (f & TH_FIN ) printf("F");
+ if (f & TH_SYN ) printf("S");
+ if (f & TH_RST ) printf("R");
+ if (f & TH_PUSH) printf("P");
+ if (f & TH_ACK ) printf("A");
+ if (f & TH_URG ) printf("U");
+}
+
+/* ------------------------------------------------------------------------ */
+
+void
+pfattach(int num)
+{
+ memset(&status, 0, sizeof(struct status));
+ printf("packetfilter: attached\n");
+}
+
+/* ------------------------------------------------------------------------ */
+
+int
+pfopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ if (minor(dev) >= 1)
+ return ENXIO;
+ return NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int
+pfclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ if (minor(dev) >= 1)
+ return ENXIO;
+ return NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int
+pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
+{
+ int error = NO_ERROR;
+ struct ioctlbuffer *ub;
+ void *kb = NULL;
+ int s;
+
+ if (!(flags & FWRITE))
+ return EACCES;
+
+ if ((cmd != DIOCSTART) && (cmd != DIOCSTOP) && (cmd != DIOCCLRSTATES)) {
+ ub = (struct ioctlbuffer *)addr;
+ if (ub == NULL)
+ return ERROR_INVALID_PARAMETERS;
+ kb = malloc(ub->size, M_DEVBUF, M_NOWAIT);
+ if (kb == NULL)
+ return ERROR_MALLOC;
+ if (copyin(ub->buffer, kb, ub->size)) {
+ free(kb, M_DEVBUF);
+ return ERROR_INVALID_PARAMETERS;
+ }
+ }
+
+ s = splsoftnet();
+
+ microtime(&tv);
+ if (tv.tv_sec - last_purge >= 10) {
+ purge_expired_states();
+ last_purge = tv.tv_sec;
+ }
+
+ switch (cmd) {
+
+ case DIOCSTART:
+ if (status.running)
+ error = ERROR_ALREADY_RUNNING;
+ else {
+ u_int32_t states = status.states;
+ memset(&status, 0, sizeof(struct status));
+ status.running = 1;
+ status.states = states;
+ status.since = tv.tv_sec;
+ printf("packetfilter: started\n");
+ }
+ break;
+
+ case DIOCSTOP:
+ if (!status.running)
+ error = ERROR_NOT_RUNNING;
+ else {
+ status.running = 0;
+ printf("packetfilter: stopped\n");
+ }
+ break;
+
+ case DIOCSETRULES: {
+ struct rule *rules = (struct rule *)kb, *ruletail = NULL;
+ u_int16_t n;
+ while (rulehead != NULL) {
+ struct rule *next = rulehead->next;
+ free(rulehead, M_DEVBUF);
+ rulehead = next;
+ }
+ for (n = 0; n < ub->entries; ++n) {
+ struct rule *rule;
+ rule = malloc(sizeof(struct rule), M_DEVBUF, M_NOWAIT);
+ if (rule == NULL) {
+ error = ERROR_MALLOC;
+ goto done;
+ }
+ memcpy(rule, rules + n, sizeof(struct rule));
+ rule->ifp = NULL;
+ if (rule->ifname[0]) {
+ rule->ifp = ifunit(rule->ifname);
+ if (rule->ifp == NULL) {
+ free(rule, M_DEVBUF);
+ error = ERROR_INVALID_PARAMETERS;
+ goto done;
+ }
+ }
+ rule->next = NULL;
+ if (ruletail != NULL) {
+ ruletail->next = rule;
+ ruletail = rule;
+ } else
+ rulehead = ruletail = rule;
+ }
+ break;
+ }
+
+ case DIOCGETRULES: {
+ struct rule *rules = (struct rule *)kb;
+ struct rule *rule = rulehead;
+ u_int16_t n = 0;
+ while ((rule != NULL) && (n < ub->entries)) {
+ memcpy(rules + n, rule, sizeof(struct rule));
+ n++;
+ rule = rule->next;
+ }
+ ub->entries = n;
+ break;
+ }
+
+ case DIOCSETNAT: {
+ struct nat *nats = (struct nat *)kb;
+ u_int16_t n;
+ while (nathead != NULL) {
+ struct nat *next = nathead->next;
+ free(nathead, M_DEVBUF);
+ nathead = next;
+ }
+ for (n = 0; n < ub->entries; ++n) {
+ struct nat *nat;
+ nat = malloc(sizeof(struct nat), M_DEVBUF, M_NOWAIT);
+ if (nat == NULL) {
+ error = ERROR_MALLOC;
+ goto done;
+ }
+ memcpy(nat, nats + n, sizeof(struct nat));
+ nat->ifp = ifunit(nat->ifname);
+ if (nat->ifp == NULL) {
+ free(nat, M_DEVBUF);
+ error = ERROR_INVALID_PARAMETERS;
+ goto done;
+ }
+ nat->next = nathead;
+ nathead = nat;
+ }
+ break;
+ }
+
+ case DIOCGETNAT: {
+ struct nat *nats = (struct nat *)kb;
+ struct nat *nat = nathead;
+ u_int16_t n = 0;
+ while ((nat != NULL) && (n < ub->entries)) {
+ memcpy(nats + n, nat, sizeof(struct nat));
+ n++;
+ nat = nat->next;
+ }
+ ub->entries = n;
+ break;
+ }
+
+ case DIOCSETRDR: {
+ struct rdr *rdrs = (struct rdr *)kb;
+ u_int16_t n;
+ while (rdrhead != NULL) {
+ struct rdr *next = rdrhead->next;
+ free(rdrhead, M_DEVBUF);
+ rdrhead = next;
+ }
+ for (n = 0; n < ub->entries; ++n) {
+ struct rdr *rdr;
+ rdr = malloc(sizeof(struct rdr), M_DEVBUF, M_NOWAIT);
+ if (rdr == NULL) {
+ error = ERROR_MALLOC;
+ goto done;
+ }
+ memcpy(rdr, rdrs + n, sizeof(struct rdr));
+ rdr->ifp = ifunit(rdr->ifname);
+ if (rdr->ifp == NULL) {
+ free(rdr, M_DEVBUF);
+ error = ERROR_INVALID_PARAMETERS;
+ goto done;
+ }
+ rdr->next = rdrhead;
+ rdrhead = rdr;
+ }
+ break;
+ }
+
+ case DIOCGETRDR: {
+ struct rdr *rdrs = (struct rdr *)kb;
+ struct rdr *rdr = rdrhead;
+ u_int16_t n = 0;
+ while ((rdr != NULL) && (n < ub->entries)) {
+ memcpy(rdrs + n, rdr, sizeof(struct rdr));
+ n++;
+ rdr = rdr->next;
+ }
+ ub->entries = n;
+ break;
+ }
+
+ case DIOCCLRSTATES: {
+ struct state *state = statehead;
+ while (state != NULL) {
+ state->expire = 0;
+ state = state->next;
+ }
+ purge_expired_states();
+ break;
+ }
+
+ case DIOCGETSTATES: {
+ struct state *states = (struct state *)kb;
+ struct state *state;
+ u_int16_t n = 0;
+ state = statehead;
+ while ((state != NULL) && (n < ub->entries)) {
+ memcpy(states + n, state, sizeof(struct state));
+ states[n].creation = tv.tv_sec - states[n].creation;
+ if (states[n].expire <= tv.tv_sec)
+ states[n].expire = 0;
+ else
+ states[n].expire -= tv.tv_sec;
+ n++;
+ state = state->next;
+ }
+ ub->entries = n;
+ break;
+ }
+
+ case DIOCSETSTATUSIF: {
+ char *ifname = (char *)kb;
+ struct ifnet *ifp = ifunit(ifname);
+ if (ifp == NULL)
+ error = ERROR_INVALID_PARAMETERS;
+ else
+ status_ifp = ifp;
+ break;
+ }
+
+ case DIOCGETSTATUS: {
+ struct status *st = (struct status *)kb;
+ u_int8_t running = status.running;
+ u_int32_t states = status.states;
+ memcpy(st, &status, sizeof(struct status));
+ st->since = st->since ? tv.tv_sec - st->since : 0;
+ ub->entries = 1;
+ memset(&status, 0, sizeof(struct status));
+ status.running = running;
+ status.states = states;
+ status.since = tv.tv_sec;
+ break;
+ }
+
+ default:
+ error = ERROR_INVALID_OP;
+ break;
+ }
+
+done:
+ splx(s);
+ if (kb != NULL) {
+ if (copyout(kb, ub->buffer, ub->size))
+ error = ERROR_INVALID_PARAMETERS;
+ free(kb, M_DEVBUF);
+ }
+ return error;
+}
+
+/* ------------------------------------------------------------------------ */
+
+inline u_short
+fix(u_short cksum, u_short old, u_short new)
+{
+ u_long l = cksum + old - new;
+ l = (l >> 16) + (l & 65535);
+ l = l & 65535;
+ return l ? l : 65535;
+}
+
+void
+change_ap(u_int32_t *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, u_int32_t an,
+ u_int16_t pn)
+{
+ u_int32_t ao = *a;
+ u_int16_t po = *p;
+ *a = an;
+ *ic = fix(fix(*ic, ao / 65536, an / 65536), ao % 65536, an % 65536);
+ *p = pn;
+ *pc = fix(fix(fix(*pc, ao / 65536, an / 65536), ao % 65536, an % 65536),
+ po, pn);
+}
+
+void
+change_a(u_int32_t *a, u_int16_t *c, u_int32_t an)
+{
+ u_int32_t ao = *a;
+ *a = an;
+ *c = fix(fix(*c, ao / 65536, an / 65536), ao % 65536, an % 65536);
+}
+
+void
+change_icmp(u_int32_t *ia, u_int16_t *ip, u_int32_t *oa, u_int32_t na,
+ u_int16_t np, u_int16_t *pc, u_int16_t *h2c, u_int16_t *ic, u_int16_t *hc)
+{
+ u_int32_t oia = *ia, ooa = *oa, opc = *pc, oh2c = *h2c;
+ u_int16_t oip = *ip;
+ // change inner protocol port, fix inner protocol checksum
+ *ip = np;
+ *pc = fix(*pc, oip, *ip);
+ *ic = fix(*ic, oip, *ip);
+ *ic = fix(*ic, opc, *pc);
+ // change inner ip address, fix inner ip checksum and icmp checksum
+ *ia = na;
+ *h2c = fix(fix(*h2c, oia / 65536, *ia / 65536), oia % 65536, *ia % 65536);
+ *ic = fix(fix(*ic, oia / 65536, *ia / 65536), oia % 65536, *ia % 65536);
+ *ic = fix(*ic, oh2c, *h2c);
+ // change outer ip address, fix outer ip checksum
+ *oa = na;
+ *hc = fix(fix(*hc, ooa / 65536, *oa / 65536), ooa % 65536, *oa % 65536);
+}
+
+/* ------------------------------------------------------------------------ */
+
+void
+send_reset(int direction, struct ifnet *ifp, struct ip *h, struct tcphdr *th)
+{
+ struct mbuf *m;
+ int len = sizeof(struct ip) + sizeof(struct tcphdr);
+ struct ip *h2;
+ struct tcphdr *th2;
+
+ /* don't reply to RST packets */
+ if (th->th_flags & TH_RST)
+ return;
+
+ /* create outgoing mbuf */
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+ m->m_data += max_linkhdr;
+ m->m_pkthdr.len = m->m_len = len;
+ m->m_pkthdr.rcvif = NULL;
+ bzero((caddr_t)m->m_data, len);
+ h2 = mtod(m, struct ip *);
+
+ /* IP header fields included in the TCP checksum */
+ h2->ip_p = IPPROTO_TCP;
+ h2->ip_len = htons(sizeof(struct tcphdr));
+ h2->ip_src.s_addr = h->ip_dst.s_addr;
+ h2->ip_dst.s_addr = h->ip_src.s_addr;
+
+ /* TCP header */
+ th2 = (struct tcphdr *)((caddr_t)h2 + sizeof(struct ip));
+ th2->th_sport = th->th_dport;
+ th2->th_dport = th->th_sport;
+ if (th->th_flags & TH_ACK) {
+ th2->th_seq = th->th_ack;
+ th2->th_flags = TH_RST;
+ } else {
+ int tlen = h->ip_len - ((h->ip_hl + th->th_off) << 2) +
+ ((th->th_flags & TH_SYN) ? 1 : 0) +
+ ((th->th_flags & TH_FIN) ? 1 : 0);
+ th2->th_ack = htonl(ntohl(th->th_seq) + tlen);
+ th2->th_flags = TH_RST | TH_ACK;
+ }
+ th2->th_off = sizeof(*th2) >> 2;
+
+ /* TCP checksum */
+ th2->th_sum = in_cksum(m, len);
+
+ /* Finish the IP header */
+ h2->ip_v = 4;
+ h2->ip_hl = sizeof(struct ip) >> 2;
+ h2->ip_len = htons(len);
+ h2->ip_ttl = 128;
+ h2->ip_sum = 0;
+
+ /* IP header checksum */
+ h2->ip_sum = in_cksum(m, sizeof(struct ip));
+
+ if (direction == PF_IN) {
+ /* set up route and send RST out through the same interface */
+ struct route iproute;
+ struct route *ro = &iproute;
+ struct sockaddr_in *dst;
+ int error;
+ bzero((caddr_t)ro, sizeof(*ro));
+ dst = (struct sockaddr_in *)&ro->ro_dst;
+ dst->sin_family = AF_INET;
+ dst->sin_addr = h2->ip_dst;
+ dst->sin_len = sizeof(*dst);
+ rtalloc(ro);
+ if (ro->ro_rt != NULL)
+ ro->ro_rt->rt_use++;
+ error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
+ ro->ro_rt);
+ } else {
+ /* send RST through the loopback interface */
+ struct sockaddr_in dst;
+ dst.sin_family = AF_INET;
+ dst.sin_addr = h2->ip_dst;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ m->m_pkthdr.rcvif = ifp;
+ looutput(lo0ifp, m, sintosa(&dst), NULL);
+ }
+ return;
+}
+
+/* ------------------------------------------------------------------------ */
+
+inline int
+match_addr(u_int8_t n, u_int32_t a, u_int32_t m, u_int32_t b)
+{
+ return n == !((a & m) == (b & m));
+}
+
+inline int
+match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p)
+{
+ switch (op) {
+ case 1: return (p >= a1) && (p <= a2);
+ case 2: return p == a1;
+ case 3: return p != a1;
+ case 4: return p < a1;
+ case 5: return p <= a1;
+ case 6: return p > a1;
+ case 7: return p >= a1;
+ }
+ return 0; /* never reached */
+}
+
+/* ------------------------------------------------------------------------ */
+
+struct nat *
+get_nat(struct ifnet *ifp, u_int8_t proto, u_int32_t addr)
+{
+ struct nat *n = nathead, *nm = NULL;
+ while ((n != NULL) && (nm == NULL)) {
+ if ((n->ifp == ifp) &&
+ (!n->proto || (n->proto == proto)) &&
+ match_addr(n->not, n->saddr, n->smask, addr))
+ nm = n;
+ else
+ n = n->next;
+ }
+ return nm;
+}
+
+struct rdr *
+get_rdr(struct ifnet *ifp, u_int8_t proto, u_int32_t addr, u_int16_t port)
+{
+ struct rdr *r = rdrhead, *rm = NULL;
+ while ((r != NULL) && (rm == NULL)) {
+ if ((r->ifp == ifp) &&
+ (!r->proto || (r->proto == proto)) &&
+ match_addr(r->not, r->daddr, r->dmask, addr) &&
+ (r->dport == port))
+ rm = r;
+ else
+ r = r->next;
+ }
+ return rm;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int
+pf_test_tcp(int direction, struct ifnet *ifp, struct ip *h, struct tcphdr *th)
+{
+ struct nat *nat = NULL;
+ struct rdr *rdr = NULL;
+ u_int32_t baddr;
+ u_int16_t bport;
+ struct rule *r = rulehead, *rm = NULL;
+ u_int16_t nr = 1, mnr = 0;
+
+ if (direction == PF_OUT) {
+ /* check outgoing packet for NAT */
+ if ((nat = get_nat(ifp, IPPROTO_TCP, h->ip_src.s_addr)) != NULL) {
+ baddr = h->ip_src.s_addr;
+ bport = th->th_sport;
+ change_ap(&h->ip_src.s_addr, &th->th_sport, &h->ip_sum,
+ &th->th_sum, nat->daddr, htons(next_port_tcp));
+ }
+ } else {
+ /* check incoming packet for RDR */
+ if ((rdr = get_rdr(ifp, IPPROTO_TCP, h->ip_dst.s_addr,
+ th->th_dport)) != NULL) {
+ baddr = h->ip_dst.s_addr;
+ bport = th->th_dport;
+ change_ap(&h->ip_dst.s_addr, &th->th_dport,
+ &h->ip_sum, &th->th_sum, rdr->raddr, rdr->rport);
+ }
+ }
+
+ while (r != NULL) {
+ if ((r->direction == direction) &&
+ ((r->ifp == NULL) || (r->ifp == ifp)) &&
+ (!r->proto || (r->proto == IPPROTO_TCP)) &&
+ ((th->th_flags & r->flagset) == r->flags) &&
+ (!r->src.addr || match_addr(r->src.not, r->src.addr,
+ r->src.mask, h->ip_src.s_addr)) &&
+ (!r->dst.addr || match_addr(r->dst.not, r->dst.addr,
+ r->dst.mask, h->ip_dst.s_addr)) &&
+ (!r->dst.port_op || match_port(r->dst.port_op, r->dst.port[0],
+ r->dst.port[1], th->th_dport)) &&
+ (!r->src.port_op || match_port(r->src.port_op, r->src.port[0],
+ r->src.port[1], th->th_sport)) ) {
+ rm = r;
+ mnr = nr;
+ if (r->quick)
+ break;
+ }
+ r = r->next;
+ nr++;
+ }
+
+ if ((rm != NULL) && rm->log) {
+ u_int32_t seq = ntohl(th->th_seq);
+ u_int16_t len = h->ip_len - ((h->ip_hl + th->th_off) << 2);
+ printf("packetfilter: @%u", mnr);
+ printf(" %s %s", rm->action ? "block" : "pass", direction ? "in" :
+ "out");
+ printf(" on %s proto tcp", ifp->if_xname);
+ printf(" from ");
+ print_host(h->ip_src.s_addr, th->th_sport);
+ printf(" to ");
+ print_host(h->ip_dst.s_addr, th->th_dport);
+ print_flags(th->th_flags);
+ if (len || (th->th_flags & (TH_SYN | TH_FIN | TH_RST)))
+ printf(" %lu:%lu(%u)", seq, seq + len, len);
+ if (th->th_ack) printf(" ack=%lu", ntohl(th->th_ack));
+ printf("\n");
+ }
+
+ if ((rm != NULL) && (rm->action == PF_DROP_RST)) {
+ /* undo NAT/RST changes, if they have taken place */
+ if (nat != NULL)
+ change_ap(&h->ip_src.s_addr, &th->th_sport,
+ &h->ip_sum, &th->th_sum, baddr, bport);
+ else if (rdr != NULL)
+ change_ap(&h->ip_dst.s_addr, &th->th_dport,
+ &h->ip_sum, &th->th_sum, baddr, bport);
+ send_reset(direction, ifp, h, th);
+ return PF_DROP;
+ }
+
+ if ((rm != NULL) && (rm->action == PF_DROP))
+ return PF_DROP;
+
+ if (((rm != NULL) && rm->keep_state) || (nat != NULL) || (rdr != NULL)) {
+ /* create new state */
+ u_int16_t len = h->ip_len - ((h->ip_hl + th->th_off) << 2);
+ struct state *s = malloc(sizeof(struct state), M_DEVBUF, M_NOWAIT);
+ if (s == NULL) {
+ printf("packetfilter: malloc() failed\n");
+ return PF_DROP;
+ }
+ s->proto = IPPROTO_TCP;
+ s->direction = direction;
+ if (direction == PF_OUT) {
+ s->gwy.addr = h->ip_src.s_addr;
+ s->gwy.port = th->th_sport;
+ s->ext.addr = h->ip_dst.s_addr;
+ s->ext.port = th->th_dport;
+ if (nat != NULL) {
+ s->lan.addr = baddr;
+ s->lan.port = bport;
+ next_port_tcp++;
+ if (next_port_tcp == 65535)
+ next_port_tcp = 50001;
+ } else {
+ s->lan.addr = s->gwy.addr;
+ s->lan.port = s->gwy.port;
+ }
+ } else {
+ s->lan.addr = h->ip_dst.s_addr;
+ s->lan.port = th->th_dport;
+ s->ext.addr = h->ip_src.s_addr;
+ s->ext.port = th->th_sport;
+ if (rdr != NULL) {
+ s->gwy.addr = baddr;
+ s->gwy.port = bport;
+ } else {
+ s->gwy.addr = s->lan.addr;
+ s->gwy.port = s->lan.port;
+ }
+ }
+ s->src.seqlo = ntohl(th->th_seq) + len; // ???
+ s->src.seqhi = s->src.seqlo + 1;
+ s->src.state = 1;
+ s->dst.seqlo = 0;
+ s->dst.seqhi = 0;
+ s->dst.state = 0;
+ s->creation = tv.tv_sec;
+ s->expire = tv.tv_sec + 60;
+ s->packets = 1;
+ s->bytes = len;
+ insert_state(s);
+ }
+
+ return PF_PASS;
+}
+
+int
+pf_test_udp(int direction, struct ifnet *ifp, struct ip *h, struct udphdr *uh)
+{
+ struct nat *nat = NULL;
+ struct rdr *rdr = NULL;
+ u_int32_t baddr;
+ u_int16_t bport;
+ struct rule *r = rulehead, *rm = NULL;
+ u_int16_t nr = 1, mnr = 0;
+
+ if (direction == PF_OUT) {
+ /* check outgoing packet for NAT */
+ if ((nat = get_nat(ifp, IPPROTO_UDP, h->ip_src.s_addr)) != NULL) {
+ baddr = h->ip_src.s_addr;
+ bport = uh->uh_sport;
+ change_ap(&h->ip_src.s_addr, &uh->uh_sport, &h->ip_sum,
+ &uh->uh_sum, nat->daddr, htons(next_port_udp));
+ }
+ } else {
+ /* check incoming packet for RDR */
+ if ((rdr = get_rdr(ifp, IPPROTO_UDP, h->ip_dst.s_addr,
+ uh->uh_dport)) != NULL) {
+ baddr = h->ip_dst.s_addr;
+ bport = uh->uh_dport;
+ change_ap(&h->ip_dst.s_addr, &uh->uh_dport,
+ &h->ip_sum, &uh->uh_sum, rdr->raddr, rdr->rport);
+ }
+ }
+
+ while (r != NULL) {
+ if ((r->direction == direction) &&
+ ((r->ifp == NULL) || (r->ifp == ifp)) &&
+ (!r->proto || (r->proto == IPPROTO_UDP)) &&
+ (!r->src.addr || match_addr(r->src.not, r->src.addr,
+ r->src.mask, h->ip_src.s_addr)) &&
+ (!r->dst.addr || match_addr(r->dst.not, r->dst.addr,
+ r->dst.mask, h->ip_dst.s_addr)) &&
+ (!r->dst.port_op || match_port(r->dst.port_op, r->dst.port[0],
+ r->dst.port[1], uh->uh_dport)) &&
+ (!r->src.port_op || match_port(r->src.port_op, r->src.port[0],
+ r->src.port[1], uh->uh_sport)) ) {
+ rm = r;
+ mnr = nr;
+ if (r->quick)
+ break;
+ }
+ r = r->next;
+ nr++;
+ }
+
+ if ((rm != NULL) && rm->log) {
+ printf("packetfilter: @%u", mnr);
+ printf(" %s %s", rm->action ? "block" : "pass", direction ? "in" :
+ "out");
+ printf(" on %s proto udp", ifp->if_xname);
+ printf(" from ");
+ print_host(h->ip_src.s_addr, uh->uh_sport);
+ printf(" to ");
+ print_host(h->ip_dst.s_addr, uh->uh_dport);
+ printf("\n");
+ }
+
+ if ((rm != NULL) && (rm->action != PF_PASS))
+ return PF_DROP;
+
+ if (((rm != NULL) && rm->keep_state) || (nat != NULL) || (rdr != NULL)) {
+ /* create new state */
+ u_int16_t len = h->ip_len - (h->ip_hl << 2) - 8;
+ struct state *s = malloc(sizeof(struct state), M_DEVBUF, M_NOWAIT);
+ if (s == NULL) {
+ printf("packetfilter: malloc() failed\n");
+ return PF_DROP;
+ }
+ s->proto = IPPROTO_UDP;
+ s->direction = direction;
+ if (direction == PF_OUT) {
+ s->gwy.addr = h->ip_src.s_addr;
+ s->gwy.port = uh->uh_sport;
+ s->ext.addr = h->ip_dst.s_addr;
+ s->ext.port = uh->uh_dport;
+ if (nat != NULL) {
+ s->lan.addr = baddr;
+ s->lan.port = bport;
+ next_port_udp++;
+ if (next_port_udp == 65535)
+ next_port_udp = 50001;
+ } else {
+ s->lan.addr = s->gwy.addr;
+ s->lan.port = s->gwy.port;
+ }
+ } else {
+ s->lan.addr = h->ip_dst.s_addr;
+ s->lan.port = uh->uh_dport;
+ s->ext.addr = h->ip_src.s_addr;
+ s->ext.port = uh->uh_sport;
+ if (rdr != NULL) {
+ s->gwy.addr = baddr;
+ s->gwy.port = bport;
+ } else {
+ s->gwy.addr = s->lan.addr;
+ s->gwy.port = s->lan.port;
+ }
+ }
+ s->src.seqlo = 0;
+ s->src.seqhi = 0;
+ s->src.state = 1;
+ s->dst.seqlo = 0;
+ s->dst.seqhi = 0;
+ s->dst.state = 0;
+ s->creation = tv.tv_sec;
+ s->expire = tv.tv_sec + 30;
+ s->packets = 1;
+ s->bytes = len;
+ insert_state(s);
+ }
+
+ return PF_PASS;
+}
+
+int
+pf_test_icmp(int direction, struct ifnet *ifp, struct ip *h, struct icmp *ih)
+{
+ struct nat *nat = NULL;
+ u_int32_t baddr;
+ struct rule *r = rulehead, *rm = NULL;
+ u_int16_t nr = 1, mnr = 0;
+
+ if (direction == PF_OUT) {
+ /* check outgoing packet for NAT */
+ if ((nat = get_nat(ifp, IPPROTO_ICMP, h->ip_src.s_addr)) != NULL) {
+ baddr = h->ip_src.s_addr;
+ change_a(&h->ip_src.s_addr, &h->ip_sum, nat->daddr);
+ }
+ }
+
+ while (r != NULL) {
+ if ((r->direction == direction) &&
+ ((r->ifp == NULL) || (r->ifp == ifp)) &&
+ (!r->proto || (r->proto == IPPROTO_ICMP)) &&
+ (!r->src.addr || match_addr(r->src.not, r->src.addr,
+ r->src.mask, h->ip_src.s_addr)) &&
+ (!r->dst.addr || match_addr(r->dst.not, r->dst.addr,
+ r->dst.mask, h->ip_dst.s_addr)) &&
+ (!r->type || (r->type == ih->icmp_type + 1)) &&
+ (!r->code || (r->code == ih->icmp_code + 1)) ) {
+ rm = r;
+ mnr = nr;
+ if (r->quick)
+ break;
+ }
+ r = r->next;
+ nr++;
+ }
+
+ if ((rm != NULL) && rm->log) {
+ printf("packetfilter: @%u", mnr);
+ printf(" %s %s", rm->action ? "block" : "pass", direction ? "in" :
+ "out");
+ printf(" on %s proto icmp", ifp->if_xname);
+ printf(" from ");
+ print_host(h->ip_src.s_addr, 0);
+ printf(" to ");
+ print_host(h->ip_dst.s_addr, 0);
+ printf(" type %u/%u", ih->icmp_type, ih->icmp_code);
+ printf("\n");
+ }
+
+ if ((rm != NULL) && (rm->action != PF_PASS))
+ return PF_DROP;
+
+ if (((rm != NULL) && rm->keep_state) || (nat != NULL)) {
+ /* create new state */
+ u_int16_t len = h->ip_len - (h->ip_hl << 2) - 8;
+ u_int16_t id = ih->icmp_hun.ih_idseq.icd_id;
+ struct state *s = malloc(sizeof(struct state), M_DEVBUF, M_NOWAIT);
+ if (s == NULL) {
+ printf("packetfilter: malloc() failed\n");
+ return PF_DROP;
+ }
+ s->proto = IPPROTO_ICMP;
+ s->direction = direction;
+ if (direction == PF_OUT) {
+ s->gwy.addr = h->ip_src.s_addr;
+ s->gwy.port = id;
+ s->ext.addr = h->ip_dst.s_addr;
+ s->ext.port = id;
+ s->lan.addr = nat ? baddr : s->gwy.addr;
+ s->lan.port = id;
+ } else {
+ s->lan.addr = h->ip_dst.s_addr;
+ s->lan.port = id;
+ s->ext.addr = h->ip_src.s_addr;
+ s->ext.port = id;
+ s->gwy.addr = s->lan.addr;
+ s->gwy.port = id;
+ }
+ s->src.seqlo = 0;
+ s->src.seqhi = 0;
+ s->src.state = 0;
+ s->dst.seqlo = 0;
+ s->dst.seqhi = 0;
+ s->dst.state = 0;
+ s->creation = tv.tv_sec;
+ s->expire = tv.tv_sec + 20;
+ s->packets = 1;
+ s->bytes = len;
+ insert_state(s);
+ }
+
+ return PF_PASS;
+}
+
+/* ------------------------------------------------------------------------ */
+
+struct state *
+pf_test_state_tcp(int direction, struct ifnet *ifp, struct ip *h, struct tcphdr *th)
+{
+ struct state *s;
+ struct tree_key key;
+
+ key.proto = IPPROTO_TCP;
+ key.addr[0] = h->ip_src.s_addr;
+ key.port[0] = th->th_sport;
+ key.addr[1] = h->ip_dst.s_addr;
+ key.port[1] = th->th_dport;
+
+ s = find_state((direction == PF_IN) ? tree_ext_gwy : tree_lan_ext, &key);
+ if (s != NULL) {
+
+ u_int16_t len = h->ip_len - ((h->ip_hl + th->th_off) << 2);
+ u_int32_t seq = ntohl(th->th_seq), ack = ntohl(th->th_ack);
+
+ struct peer *src, *dst;
+ if (direction == s->direction) {
+ src = &s->src;
+ dst = &s->dst;
+ } else {
+ src = &s->dst;
+ dst = &s->src;
+ }
+
+ /* some senders do that instead of ACKing FIN */
+ if ((th->th_flags == TH_RST) && !ack && !len &&
+ ((seq == src->seqhi) || (seq == src->seqhi-1)) &&
+ (src->state >= 4) && (dst->state >= 3))
+ ack = dst->seqhi;
+
+ if ((dst->seqhi >= dst->seqlo ?
+ (ack >= dst->seqlo) && (ack <= dst->seqhi) :
+ (ack >= dst->seqlo) || (ack <= dst->seqhi)) ||
+ (seq == src->seqlo) || (seq == src->seqlo-1)) {
+
+ s->packets++;
+ s->bytes += len;
+
+ /* update sequence number range */
+ if (th->th_flags & TH_ACK)
+ dst->seqlo = ack;
+ if (th->th_flags & (TH_SYN | TH_FIN))
+ len++;
+ if (th->th_flags & TH_SYN) {
+ src->seqhi = seq + len;
+ src->seqlo = src->seqhi - 1;
+ } else if ((seq + len) - src->seqhi < 65536)
+ src->seqhi = seq + len;
+
+ /* update states */
+ if (th->th_flags & TH_SYN)
+ if (src->state < 1)
+ src->state = 1;
+ if (th->th_flags & TH_FIN)
+ if (src->state < 3)
+ src->state = 3;
+ if ((th->th_flags & TH_ACK) && (ack == dst->seqhi)) {
+ if (dst->state == 1)
+ dst->state = 2;
+ else if (dst->state == 3)
+ dst->state = 4;
+ }
+ if (th->th_flags & TH_RST)
+ src->state = dst->state = 5;
+
+ /* update expire time */
+ if ((src->state >= 4) && (dst->state >= 4))
+ s->expire = tv.tv_sec + 5;
+ else if ((src->state >= 3) || (dst->state >= 3))
+ s->expire = tv.tv_sec + 300;
+ else if ((src->state < 2) || (dst->state < 2))
+ s->expire = tv.tv_sec + 30;
+ else
+ s->expire = tv.tv_sec + 24*60*60;
+
+ /* translate source/destination address, if necessary */
+ if ((s->lan.addr != s->gwy.addr)
+ || (s->lan.port != s->gwy.port)) {
+ if (direction == PF_OUT)
+ change_ap(&h->ip_src.s_addr, &th->th_sport,
+ &h->ip_sum, &th->th_sum,
+ s->gwy.addr, s->gwy.port);
+ else
+ change_ap(&h->ip_dst.s_addr, &th->th_dport,
+ &h->ip_sum, &th->th_sum,
+ s->lan.addr, s->lan.port);
+ }
+
+ } else {
+ printf("packetfilter: BAD state: ");
+ print_state(direction, s);
+ print_flags(th->th_flags);
+ printf(" seq=%lu ack=%lu len=%u ", seq, ack, len);
+ printf("\n");
+ s = NULL;
+ }
+
+ return s;
+ }
+ return NULL;
+}
+
+struct state *
+pf_test_state_udp(int direction, struct ifnet *ifp, struct ip *h, struct udphdr *uh)
+{
+ struct state *s;
+ struct tree_key key;
+
+ key.proto = IPPROTO_UDP;
+ key.addr[0] = h->ip_src.s_addr;
+ key.port[0] = uh->uh_sport;
+ key.addr[1] = h->ip_dst.s_addr;
+ key.port[1] = uh->uh_dport;
+
+ s = find_state((direction == PF_IN) ? tree_ext_gwy : tree_lan_ext, &key);
+ if (s != NULL) {
+
+ u_int16_t len = h->ip_len - (h->ip_hl << 2) - 8;
+
+ struct peer *src, *dst;
+ if (direction == s->direction) {
+ src = &s->src;
+ dst = &s->dst;
+ } else {
+ src = &s->dst;
+ dst = &s->src;
+ }
+
+ s->packets++;
+ s->bytes += len;
+
+ /* update states */
+ if (src->state < 1)
+ src->state = 1;
+ if (dst->state == 1)
+ dst->state = 2;
+
+ /* update expire time */
+ if ((src->state == 2) && (dst->state == 2))
+ s->expire = tv.tv_sec + 60;
+ else
+ s->expire = tv.tv_sec + 20;
+
+ /* translate source/destination address, if necessary */
+ if ((s->lan.addr != s->gwy.addr)
+ || (s->lan.port != s->gwy.port)) {
+ if (direction == PF_OUT)
+ change_ap(&h->ip_src.s_addr, &uh->uh_sport,
+ &h->ip_sum, &uh->uh_sum,
+ s->gwy.addr, s->gwy.port);
+ else
+ change_ap(&h->ip_dst.s_addr, &uh->uh_dport,
+ &h->ip_sum, &uh->uh_sum,
+ s->lan.addr, s->lan.port);
+ }
+
+ return s;
+ }
+ return NULL;
+}
+
+struct state *
+pf_test_state_icmp(int direction, struct ifnet *ifp, struct ip *h, struct icmp *ih)
+{
+ u_int16_t len = h->ip_len - (h->ip_hl << 2) - 8;
+
+ if ((ih->icmp_type != ICMP_UNREACH) &&
+ (ih->icmp_type != ICMP_SOURCEQUENCH) &&
+ (ih->icmp_type != ICMP_REDIRECT) &&
+ (ih->icmp_type != ICMP_TIMXCEED) &&
+ (ih->icmp_type != ICMP_PARAMPROB)) {
+
+ /*
+ * ICMP query/reply message not related to a TCP/UDP packet.
+ * Search for an ICMP state.
+ */
+
+ struct state *s;
+ struct tree_key key;
+
+ key.proto = IPPROTO_ICMP;
+ key.addr[0] = h->ip_src.s_addr;
+ key.port[0] = ih->icmp_hun.ih_idseq.icd_id;
+ key.addr[1] = h->ip_dst.s_addr;
+ key.port[1] = ih->icmp_hun.ih_idseq.icd_id;
+
+ s = find_state((direction == PF_IN) ? tree_ext_gwy :
+ tree_lan_ext, &key);
+ if (s != NULL) {
+
+ s->packets++;
+ s->bytes += len;
+ s->expire = tv.tv_sec + 10;
+
+ /* translate source/destination address, if necessary */
+ if (s->lan.addr != s->gwy.addr) {
+ if (direction == PF_OUT)
+ change_a(&h->ip_src.s_addr, &h->ip_sum,
+ s->gwy.addr);
+ else
+ change_a(&h->ip_dst.s_addr, &h->ip_sum,
+ s->lan.addr);
+ }
+
+ return s;
+ }
+ return NULL;
+
+ } else {
+
+ /*
+ * ICMP error message in response to a TCP/UDP packet.
+ * Extract the inner TCP/UDP header and search for that state.
+ */
+
+ struct ip *h2 = (struct ip *)(((char *)ih) + 8);
+ if (len < 28) {
+ printf("packetfilter: ICMP error message too short\n");
+ return NULL;
+ }
+ switch (h2->ip_p) {
+ case IPPROTO_TCP: {
+ struct tcphdr *th = (struct tcphdr *)(((char *)h2) + 20);
+ u_int32_t seq = ntohl(th->th_seq);
+ struct state *s;
+ struct tree_key key;
+ struct peer *src;
+
+ key.proto = IPPROTO_TCP;
+ key.addr[0] = h2->ip_dst.s_addr;
+ key.port[0] = th->th_dport;
+ key.addr[1] = h2->ip_src.s_addr;
+ key.port[1] = th->th_sport;
+
+ s = find_state((direction == PF_IN) ? tree_ext_gwy :
+ tree_lan_ext, &key);
+ if (s == NULL)
+ return NULL;
+
+ src = (direction == s->direction) ? &s->dst : &s->src;
+
+ if ((src->seqhi >= src->seqlo ?
+ (seq < src->seqlo) || (seq > src->seqhi) :
+ (seq < src->seqlo) && (seq > src->seqhi))) {
+ printf("packetfilter: BAD ICMP state: ");
+ print_state(direction, s);
+ print_flags(th->th_flags);
+ printf(" seq=%lu\n", seq);
+ return NULL;
+ }
+
+ if ((s->lan.addr != s->gwy.addr) ||
+ (s->lan.port != s->gwy.port)) {
+ if (direction == PF_IN) {
+ change_icmp(&h2->ip_src.s_addr,
+ &th->th_sport, &h->ip_dst.s_addr,
+ s->lan.addr, s->lan.port, &th->th_sum,
+ &h2->ip_sum, &ih->icmp_cksum,
+ &h->ip_sum);
+ } else {
+ change_icmp(&h2->ip_dst.s_addr,
+ &th->th_dport, &h->ip_src.s_addr,
+ s->gwy.addr, s->gwy.port, &th->th_sum,
+ &h2->ip_sum, &ih->icmp_cksum,
+ &h->ip_sum);
+ }
+ }
+ return s;
+ break;
+ }
+ case IPPROTO_UDP: {
+ struct udphdr *uh = (struct udphdr *)(((char *)h2) + 20);
+ struct state *s;
+ struct tree_key key;
+
+ key.proto = IPPROTO_UDP;
+ key.addr[0] = h2->ip_dst.s_addr;
+ key.port[0] = uh->uh_dport;
+ key.addr[1] = h2->ip_src.s_addr;
+ key.port[1] = uh->uh_sport;
+
+ s = find_state((direction == PF_IN) ? tree_ext_gwy :
+ tree_lan_ext, &key);
+ if (s == NULL)
+ return NULL;
+
+ if ((s->lan.addr != s->gwy.addr) ||
+ (s->lan.port != s->gwy.port)) {
+ if (direction == PF_IN) {
+ change_icmp(&h2->ip_src.s_addr,
+ &uh->uh_sport, &h->ip_dst.s_addr,
+ s->lan.addr, s->lan.port, &uh->uh_sum,
+ &h2->ip_sum, &ih->icmp_cksum,
+ &h->ip_sum);
+ } else {
+ change_icmp(&h2->ip_dst.s_addr,
+ &uh->uh_dport, &h->ip_src.s_addr,
+ s->gwy.addr, s->gwy.port, &uh->uh_sum,
+ &h2->ip_sum, &ih->icmp_cksum,
+ &h->ip_sum);
+ }
+ }
+ return s;
+ break;
+ }
+ default:
+ printf("packetfilter: ICMP error message for bad proto\n");
+ return NULL;
+ }
+ return NULL;
+
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+
+inline void *
+pull_hdr(struct ifnet *ifp, struct mbuf **m, struct ip *h, int *action,
+ u_int8_t len)
+{
+ u_int16_t hl = h->ip_hl << 2;
+ u_int16_t off = (h->ip_off & IP_OFFMASK) << 3;
+ if (off) {
+ if (off >= len)
+ *action = PF_PASS;
+ else {
+ *action = PF_DROP;
+ printf("packetfilter: dropping following fragment");
+ print_ip(ifp, h);
+ }
+ return NULL;
+ }
+ if ((h->ip_len - hl) < len) {
+ *action = PF_DROP;
+ printf("packetfilter: dropping first fragment");
+ print_ip(ifp, h);
+ return NULL;
+ }
+ if ((*m)->m_len < (hl + len))
+ if ((*m = m_pullup(*m, hl + len)) == NULL) {
+ printf("packetfilter: pullup proto header failed\n");
+ *action = PF_DROP;
+ return NULL;
+ }
+ return mtod(*m, char *) + hl;
+}
+
+int
+pf_test(int direction, struct ifnet *ifp, struct mbuf **m)
+{
+ int action;
+ struct ip *h = mtod(*m, struct ip *);
+
+ if (!status.running)
+ return PF_PASS;
+
+ /* purge expire states, at most once every 10 seconds */
+ microtime(&tv);
+ if ((tv.tv_sec - last_purge) >= 10) {
+ purge_expired_states();
+ last_purge = tv.tv_sec;
+ }
+
+ /* ensure we have at least the complete ip header pulled up */
+ if ((*m)->m_len < (h->ip_hl << 2))
+ if ((*m = m_pullup(*m, h->ip_hl << 2)) == NULL) {
+ printf("packetfilter: pullup ip header failed\n");
+ action = PF_DROP;
+ goto done;
+ }
+
+ switch (h->ip_p) {
+
+ case IPPROTO_TCP: {
+ struct tcphdr *th = pull_hdr(ifp, m, h, &action, 20);
+ if (th == NULL)
+ goto done;
+ if (pf_test_state_tcp(direction, ifp, h, th))
+ action = PF_PASS;
+ else
+ action = pf_test_tcp(direction, ifp, h, th);
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct udphdr *uh = pull_hdr(ifp, m, h, &action, 8);
+ if (uh == NULL)
+ goto done;
+ if (pf_test_state_udp(direction, ifp, h, uh))
+ action = PF_PASS;
+ else
+ action = pf_test_udp(direction, ifp, h, uh);
+ break;
+ }
+
+ case IPPROTO_ICMP: {
+ struct icmp *ih = pull_hdr(ifp, m, h, &action, 8);
+ if (ih == NULL)
+ goto done;
+ if (pf_test_state_icmp(direction, ifp, h, ih))
+ action = PF_PASS;
+ else
+ action = pf_test_icmp(direction, ifp, h, ih);
+ break;
+ }
+
+ default:
+ printf("packetfilter: dropping unsupported protocol");
+ print_ip(ifp, h);
+ action = PF_DROP;
+ break;
+ }
+
+done:
+ if (ifp == status_ifp) {
+ status.bytes[direction] += h->ip_len;
+ status.packets[direction][action]++;
+ }
+ return action;
+}
+
+/* ------------------------------------------------------------------------ */
+
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
new file mode 100644
index 00000000000..c771abece86
--- /dev/null
+++ b/sys/net/pfvar.h
@@ -0,0 +1,170 @@
+/* $OpenBSD: pfvar.h,v 1.1 2001/06/24 19:48:58 kjell Exp $ */
+
+/*
+ * Copyright (c) 2001, Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _NETINET_PACKETFILTER_H_
+#define _NETINET_PACKETFILTER_H_
+
+#include <sys/types.h>
+
+enum { PF_IN=0, PF_OUT=1 };
+enum { PF_PASS=0, PF_DROP=1, PF_DROP_RST=2 };
+
+struct rule {
+ u_int8_t action;
+ u_int8_t direction;
+ u_int8_t log;
+ u_int8_t quick;
+ u_int8_t keep_state;
+ char ifname[16];
+ struct ifnet *ifp;
+ u_int8_t proto;
+ struct {
+ u_int8_t not;
+ u_int32_t addr,
+ mask;
+ u_int8_t port_op;
+ u_int16_t port[2];
+ } src,
+ dst;
+ u_int8_t type,
+ code;
+ u_int8_t flags,
+ flagset;
+ struct rule *next;
+};
+
+struct state {
+ u_int8_t proto;
+ u_int8_t direction;
+ struct host {
+ u_int32_t addr;
+ u_int16_t port;
+ } lan,
+ gwy,
+ ext;
+ struct peer {
+ u_int32_t seqlo,
+ seqhi;
+ u_int8_t state;
+ } src,
+ dst;
+ u_int32_t creation,
+ expire;
+ u_int32_t packets,
+ bytes;
+ struct state *next;
+};
+
+struct nat {
+ char ifname[16];
+ struct ifnet *ifp;
+ u_int8_t proto;
+ u_int8_t not;
+ u_int32_t saddr,
+ smask,
+ daddr;
+ struct nat *next;
+};
+
+struct rdr {
+ char ifname[16];
+ struct ifnet *ifp;
+ u_int8_t proto;
+ u_int8_t not;
+ u_int32_t daddr,
+ dmask,
+ raddr;
+ u_int16_t dport,
+ rport;
+ struct rdr *next;
+};
+
+struct status {
+ u_int8_t running;
+ u_int32_t bytes[2];
+ u_int32_t packets[2][2];
+ u_int32_t states,
+ state_inserts,
+ state_removals,
+ state_searches;
+ u_int32_t since;
+};
+
+/*
+ * ioctl parameter structure
+ */
+
+struct ioctlbuffer {
+ u_int32_t size;
+ u_int16_t entries;
+ void *buffer;
+};
+
+/*
+ * ioctl operations
+ */
+
+#define DIOCSTART _IO ('D', 1)
+#define DIOCSTOP _IO ('D', 2)
+#define DIOCSETRULES _IOWR('D', 3, struct ioctlbuffer)
+#define DIOCGETRULES _IOWR('D', 4, struct ioctlbuffer)
+#define DIOCSETNAT _IOWR('D', 5, struct ioctlbuffer)
+#define DIOCGETNAT _IOWR('D', 6, struct ioctlbuffer)
+#define DIOCSETRDR _IOWR('D', 7, struct ioctlbuffer)
+#define DIOCGETRDR _IOWR('D', 8, struct ioctlbuffer)
+#define DIOCCLRSTATES _IO ('D', 9)
+#define DIOCGETSTATES _IOWR('D', 10, struct ioctlbuffer)
+#define DIOCSETSTATUSIF _IOWR('D', 11, struct ioctlbuffer)
+#define DIOCGETSTATUS _IOWR('D', 12, struct ioctlbuffer)
+
+/*
+ * ioctl errors
+ */
+
+enum error_msg {
+ NO_ERROR=0,
+ ERROR_INVALID_OP=100,
+ ERROR_ALREADY_RUNNING,
+ ERROR_NOT_RUNNING,
+ ERROR_INVALID_PARAMETERS,
+ ERROR_MALLOC,
+ MAX_ERROR_NUM
+};
+
+
+#ifdef _KERNEL
+
+int pf_test (int, struct ifnet *, struct mbuf **);
+
+#endif /* _KERNEL */
+
+#endif /* _NET_PACKETFILTER_H_ */