pf patch for timed table addr expire

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

pf patch for timed table addr expire

blank-5
Hello tech@, first posting here so please don't laugh too hard.

My idea was to have pf automatically purge addresses from specified
tables after period of time from the insertion has gone by. The patch
is bare minimum but functional - adds the option "expire timeout",
(timeout parameter in seconds) to pf.conf table definitions.
After reading the source for quite some time and at the same time
writing this stuff, I can't quite decide on many things about the
implementation. If you see this is a bad thing to do to/in the kernel,
please let me know..

There are probably many flaws in the code and many more aspects I have
overlooked (probably some interference with something I haven't
stumbled upon in the source yet?). I'd appreciate some answers/pointers
to where to look at on these:
- timeouts (as state expiry and my original plan for 3.6 worked) vs.
pfpurge thread?
- locking in the thread, do I need to rw_enter_write to safely delete
kentries with pfr_unroute_kentry() and pfr_destroy_kentry() - is this
even the correct method for removing addresses from tables or should I
use "higher" level functions? Or lower?
- cpu and memory implications: is it OK to add 4 bytes per table? I see
the structures are not used that much. This could build up in high
traffic/DoS situation, up to probably not-very-many k? The purge
"algorithm" surely is stupid, thinking about adding another time_second
stamp in pfr_ktable describing the next timeout and just testing that
before iterating every kentry in ktable, etc. The problem with these is
that development happens on a very low-end setup so I don't reach wire
speed anyways, but this would need to be working in a real-world
carp'ed fw setup to be installed soon. Routing between fastethernet
LANs where wire speed would be desirable.
- IOCTL - what's going on here? I've chosen not to update as it seems I
don't need to. But anyways I would like to do this right.
- many more ideas and todos that I'm only thinking about yet, not
knowing if all this code is just bad bs and pointless..

-blank

diff for 3.8-current follows

Index: src/sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.494
diff -u -r1.494 parse.y
--- src/sbin/pfctl/parse.y      2005/11/17 20:52:39     1.494
+++ src/sbin/pfctl/parse.y      2005/11/19 20:30:55
@@ -235,6 +235,7 @@
 struct table_opts {
        int                     flags;
        int                     init_addr;
+       int                     expire;
        struct node_tinithead   init_nodes;
 } table_opts;

@@ -411,6 +412,7 @@
 %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
 %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH
 %token TAGGED TAG IFBOUND FLOATING STATEPOLICY ROUTE
+%token EXPIRE
 %token <v.string>              STRING
 %token <v.i>                   PORTBINARY
 %type  <v.interface>           interface if_list if_item_not if_item
@@ -1026,11 +1028,17 @@
                                free($3);
                                YYERROR;
                        }
-                       if (pf->loadopt & PFCTL_FLAG_TABLE)
+                       if (pf->loadopt & PFCTL_FLAG_TABLE) {
+                               if ($5.expire && ($5.flags & PFR_TFLAG_CONST)) {
+                                       yyerror("const tables can't expire");
+                                       free($3);
+                                       YYERROR;
+                               }
                                if (process_tabledef($3, &$5)) {
                                        free($3);
                                        YYERROR;
                                }
+                       }
                        free($3);
                        for (ti = SIMPLEQ_FIRST(&$5.init_nodes);
                            ti != SIMPLEQ_END(&$5.init_nodes); ti = nti) {
@@ -1119,6 +1127,13 @@
                            entries);
                        table_opts.init_addr = 1;
                }
+               | EXPIRE number         {
+                       if ($2<1) {
+                               printf("minimum table expire time is 1\n");
+                               YYERROR;
+                       }
+                       table_opts.expire = $2;
+               }
                ;

 altqif         : ALTQ interface queue_opts QUEUE qassign {
@@ -3859,7 +3874,7 @@
                print_tabledef(name, opts->flags, opts->init_addr,
                    &opts->init_nodes);
        if (!(pf->opts & PF_OPT_NOACTION) &&
-           pfctl_define_table(name, opts->flags, opts->init_addr,
+           pfctl_define_table(name, opts->flags, opts->expire, opts->init_addr,
            pf->anchor, &ab, pf->tticket)) {
                yyerror("cannot define table %s: %s", name,
                    pfr_strerror(errno));
@@ -4583,6 +4598,7 @@
                { "drop",               DROP},
                { "drop-ovl",           FRAGDROP},
                { "dup-to",             DUPTO},
+               { "expire",             EXPIRE},
                { "fastroute",          FASTROUTE},
                { "file",               FILENAME},
                { "fingerprints",       FINGERPRINTS},
Index: src/sbin/pfctl/pfctl_optimize.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_optimize.c,v
retrieving revision 1.9
diff -u -r1.9 pfctl_optimize.c
--- src/sbin/pfctl/pfctl_optimize.c     2005/06/13 20:17:26     1.9
+++ src/sbin/pfctl/pfctl_optimize.c     2005/11/19 20:30:58
@@ -1283,7 +1283,7 @@
        tablenum++;


-       if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, pf->anchor,
+       if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 0, 1, pf->anchor,
            tbl->pt_buf, pf->tticket)) {
                warn("failed to create table %s", tbl->pt_name);
                return (1);
Index: src/sbin/pfctl/pfctl_parser.h
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_parser.h,v
retrieving revision 1.83
diff -u -r1.83 pfctl_parser.h
--- src/sbin/pfctl/pfctl_parser.h       2005/11/17 20:52:39     1.83
+++ src/sbin/pfctl/pfctl_parser.h       2005/11/19 20:30:58
@@ -219,8 +219,8 @@
 void    print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *,
            int, struct node_queue_opt *);

-int    pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *,
-           u_int32_t);
+int    pfctl_define_table(char *, int, int, int, const char *,
+           struct pfr_buffer *, u_int32_t);

 void            pfctl_clear_fingerprints(int, int);
 int             pfctl_file_fingerprints(int, int, const char *);
Index: src/sbin/pfctl/pfctl_table.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_table.c,v
retrieving revision 1.64
diff -u -r1.64 pfctl_table.c
--- src/sbin/pfctl/pfctl_table.c        2005/08/17 14:54:59     1.64
+++ src/sbin/pfctl/pfctl_table.c        2005/11/19 20:30:58
@@ -338,6 +338,8 @@
                    ta->pfrt_name);
                if (ta->pfrt_anchor[0])
                        printf("\t%s", ta->pfrt_anchor);
+               if (ta->pfrt_expire)
+                       printf("\texpire=%d", ta->pfrt_expire);
                puts("");
        } else
                puts(ta->pfrt_name);
@@ -454,8 +456,8 @@
 }

 int
-pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
-    struct pfr_buffer *ab, u_int32_t ticket)
+pfctl_define_table(char *name, int flags, int expire, int addrs,
+    const char *anchor, struct pfr_buffer *ab, u_int32_t ticket)
 {
        struct pfr_table tbl;

@@ -465,6 +467,7 @@
            sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor))
                errx(1, "pfctl_define_table: strlcpy");
        tbl.pfrt_flags = flags;
+       tbl.pfrt_expire = expire;

        return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
            NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
Index: src/sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.508
diff -u -r1.508 pf.c
--- src/sys/net/pf.c    2005/11/14 09:18:55     1.508
+++ src/sys/net/pf.c    2005/11/19 20:31:39
@@ -877,6 +877,7 @@
                if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) {
                        pf_purge_expired_fragments();
                        pf_purge_expired_src_nodes(0);
+                       pfr_purge_expired_kentries();
                        nloops = 0;
                }

Index: src/sys/net/pf_table.c
===================================================================
RCS file: /cvs/src/sys/net/pf_table.c,v
retrieving revision 1.67
diff -u -r1.67 pf_table.c
--- src/sys/net/pf_table.c      2005/08/02 12:40:42     1.67
+++ src/sys/net/pf_table.c      2005/11/19 20:31:44
@@ -897,6 +897,32 @@
 }

 void
+pfr_purge_expired_kentries(void)
+{
+       struct pfr_ktable       *p;
+       struct pfr_kentryworkq  workq;
+       struct pfr_kentry       *pa,*pb;
+       int                     n = 0;
+
+       RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
+               if (!p->pfrkt_expire)
+                       continue;
+               if (!(p->pfrkt_flags & (PFR_TFLAG_ACTIVE & !PFR_TFLAG_CONST)))
+                       continue;
+               pfr_enqueue_addrs(p, &workq, NULL, 0);
+               for (pa = SLIST_FIRST(&workq); pa != NULL; pa = pb) {
+                       pb = SLIST_NEXT(pa, pfrke_workq);
+                       if ((pa->pfrke_tzero+p->pfrkt_expire) < time_second) {
+                               pfr_unroute_kentry(p, pa);
+                               pfr_destroy_kentry(pa);
+                               n++;
+                       }
+               }
+               p->pfrkt_cnt -= n;
+       }
+}
+
+void
 pfr_clean_node_mask(struct pfr_ktable *kt,
     struct pfr_kentryworkq *workq)
 {
@@ -1902,6 +1928,7 @@
                return (NULL);
        }
        kt->pfrkt_tzero = tzero;
+       kt->pfrkt_expire = tbl->pfrt_expire;

        return (kt);
 }
Index: src/sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.233
diff -u -r1.233 pfvar.h
--- src/sys/net/pfvar.h 2005/11/04 08:24:15     1.233
+++ src/sys/net/pfvar.h 2005/11/19 20:31:46
@@ -784,6 +784,7 @@
        char                     pfrt_anchor[MAXPATHLEN];
        char                     pfrt_name[PF_TABLE_NAME_SIZE];
        u_int32_t                pfrt_flags;
+       u_int32_t                pfrt_expire;
        u_int8_t                 pfrt_fback;
 };

@@ -858,6 +859,7 @@
        struct pf_ruleset       *pfrkt_rs;
        long                     pfrkt_larg;
        int                      pfrkt_nflags;
+       long                     pfrkt_expire;
 };
 #define pfrkt_t                pfrkt_ts.pfrts_t
 #define pfrkt_name     pfrkt_t.pfrt_name
@@ -1535,6 +1537,7 @@
 int    pfr_set_tflags(struct pfr_table *, int, int, int, int *, int *, int);
 int    pfr_clr_addrs(struct pfr_table *, int *, int);
 int    pfr_insert_kentry(struct pfr_ktable *, struct pfr_addr *, long);
+void   pfr_purge_expired_kentries(void);
 int    pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *,
            int);
 int    pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *,

Reply | Threaded
Open this post in threaded view
|

Re: pf patch for timed table addr expire

blank-5
Ok, I did a terrible newbie mistake there, the diff didn't quite make it through mailer.
I apologise for wasting your time, if someone is still interested the diff's available
at

http://www.tpu.fi/~t3vojamo/pf-exp.diff

-blank

Reply | Threaded
Open this post in threaded view
|

Re: pf patch for timed table addr expire

Johan Torin
In reply to this post by blank-5
On Saturday 19 November 2005 22:17, you wrote:
> Hello tech@, first posting here so please don't laugh too hard.
>
> My idea was to have pf automatically purge addresses from specified
> tables after period of time from the insertion has gone by.

[...]

It might be worth to mention ports/sysutils/expiretable. Pretty much
the same functionality and also has a daemonised mode.

/Johan