vBulletin Search Engine Optimization
| |||||||
| Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| Did theo send you anything about this? I think you need to get it KNF'ed for sure. -bob * Chris Kuethe <chris.kuethe@gmail.com> [2006-03-09 12:53]: > Below is a patch to allow dhcpd to manipulate certain pf tables when > an address is abandoned or leased. It adds two new options to dhcpd, > "-A table" and "-C table". > > When an address is abandoned for some reason and -A is given, dhcpd > will stuff the address into the named table. This table can then be > used to redirect web traffic into a page that tells the user to stop > camping and just use dhcp. When an IP is properly leased, dhcpd will > remove the IP from the the table. > > When the hardware address for an IP changes and -C is given, dhcpd > will remove the address from the named table. The is a complement to > the overload table feature in pf. > > Assume you have a net with dhcp addressing. Some random misbehaving > machine starts scanning, and trips the pf state limit thereby ending > up in the overload table. You then use the overload table to block > forwarding for this IP and redirect web connections to a page that > says "you've been bad, please come to the help desk." The user > obligingly packs up the laptop and trundles off the the help desk. In > the mean time, a new user comes along, and dhcp assigns him an address > that was overloaded. As the hardware address changed, there is a > reasonable chance that this is a new machine, and is possibly not > guilty of the sin that put the first address into overload. We remove > the address from overload, and give this new machine a chance to > commit its own sins. > > These are, of course, subject to all the usual caveats of placing too > much trust in an IP or Ethernet address - think about "ifconfig $if > lladdr 08:04:de:ad:be:ef". That being said, this patch is fairly > handy. I've been running this for a few days on our production > wireless net with no ill effect. > > While the example below doesn't take into account things like > ftp-proxy or vpn back to the gateway, you'd use the tables like this: > ######################################### > table <OVERLOAD> persist; > table <CAMPERS> persist; > > # trap overloaded ips and campers > rdr on $INT_IF proto tcp from <OVERLOAD> to port 80 -> 127.0.0.1 port 8001 > rdr on $INT_IF proto from <CAMPERS> to port 80 -> 127.0.0.1 port 8002 > > # allow authpf users to bypass the web redirect > rdr-anchor "authpf/*" > # trap all web requests to tell users about authpf > rdr on $INT_IF proto tcp from any to any port 80 -> 127.0.0.1 port 80 > > #other stuff here.... > > #rules. default deny yadda yadda yadda > block in on $INT_IF all > # at least allow web, ssh and dns to the gateway to work > pass in quick on $INT_IF inet proto tcp to self port 22 keep state > pass in quick on $INT_IF inet proto tcp to self port 80 keep state > pass in quick on $INT_IF inet proto {tcp udp} to self port 53 keep state > > #squash bad stuff in the tables > block in quick on $INT_IF from <CAMPERS> > block in quick on $INT_IF from <OVERLOAD> > > #authpf hooks > pass in log on $INT_IF inet proto tcp from <authpf_users> keep state > (max-src-conn 60, max-src-conn-rate 20/2) > pass in log on $INT_IF inet proto udp from <authpf_users> keep state > (max-src-conn 40, max-src-conn-rate 20/2) > pass in quick on $INT_IF from <authpf_users> keep state > ######################################### > > And invoke dhcpd something like "dhcpd -A CAMPERS -C OVERLOAD fxp0". > > diff --exclude CVS -urN ../dhcpd/Makefile ./Makefile > --- ../dhcpd/Makefile Tue Apr 20 17:01:09 2004 > +++ ./Makefile Tue Feb 28 12:45:58 2006 > @@ -4,7 +4,7 @@ > > SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c bpf.c packet.c errwarn.c \ > dispatch.c print.c memory.c options.c inet.c conflex.c parse.c \ > - alloc.c tables.c tree.c hash.c convert.c icmp.c > + alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c > PROG= dhcpd > MAN= dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 dhcp-options.5 > > diff --exclude CVS -urN ../dhcpd/dhcpd.8 ./dhcpd.8 > --- ../dhcpd/dhcpd.8 Fri Sep 30 14:34:26 2005 > +++ ./dhcpd.8 Tue Feb 28 13:43:28 2006 > @@ -47,6 +47,9 @@ > .Op Fl dfn > .Op Fl c Ar config-file > .Op Fl l Ar lease-file > +.Op Fl p Ar pf-device > +.Op Fl A Ar abandoned_ip_table > +.Op Fl C Ar changed_ip_table > .Op Ar if0 Op Ar ... ifN > .Sh DESCRIPTION > The Internet Software Consortium DHCP Server, > @@ -75,6 +78,11 @@ > allocates an address for it. > Each client is assigned a lease, which expires after an amount of time > chosen by the administrator (by default, one day). > +When a leased IP address is assigned to a new hardware address, > +.Nm > +may delete the leased IP from certain > +.Xr pf 4 > +tables. > Before leases expire, the clients to which leases are assigned are expected > to renew them in order to continue to use the addresses. > Once a lease has expired, the client to which that lease was assigned is no > @@ -185,6 +193,33 @@ > in production, this option should be used > .Em only > for testing lease files in a non-production environment. > +.It Fl p Ar pf-device > +Use an alternate pf device, > +.Ar pf-device . > +.It Fl A Ar abandoned_ip_table > +When an address is abandoned for some reason, add it to the > +.Xr pf 4 > +table named > +.Ar abandoned_ip_table . > +This can be used to defend against machines "camping" on an address > +without obtaining a lease. > +When an address is properly leased, > +.Nm > +will remove the address from this table. > +.It Fl C Ar changed_ip_table > +When an address is leased to a different hardware address, delete it from the > +.Xr pf 4 > +table named > +.Ar changed_ip_table . > +This feature complements the overload table in a stateful > +.Xr pf 4 > +rule. > +If a host appears to be misbehaving, it can be quarantined by using the > +overload feature. > +When the address is leased to a different machine, > +.Nm > +can remove the address from the overload table, thus allowing a well-behaved > +machine to reuse the address. > .It Fl n > Only test configuration, do not run > .Nm . > diff --exclude CVS -urN ../dhcpd/dhcpd.c ./dhcpd.c > --- ../dhcpd/dhcpd.c Mon May 23 16:54:55 2005 > +++ ./dhcpd.c Thu Mar 2 12:20:59 2006 > @@ -52,22 +52,33 @@ > > int log_priority; > int log_perror = 0; > +int pfpipe[2]; > char *path_dhcpd_conf = _PATH_DHCPD_CONF; > char *path_dhcpd_db = _PATH_DHCPD_DB; > +char *abandoned_tab = NULL; > +char *changedmac_tab = NULL; > +struct passwd *pw; > +pid_t pfproc_pid = -1; > +int gotpipe = 0; > > int > main(int argc, char *argv[]) > { > int ch, cftest = 0, daemonize = 1; > - struct passwd *pw; > extern char *__progname; > > /* Initially, log errors to stderr as well as to syslogd. */ > openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY); > setlogmask(LOG_UPTO(LOG_INFO)); > > - while ((ch = getopt(argc, argv, "c:dfl:nq")) != -1) > + while ((ch = getopt(argc, argv, "A:C:c:dfl:nq")) != -1) > switch (ch) { > + case 'A': > + abandoned_tab = optarg; > + break; > + case 'C': > + changedmac_tab = optarg; > + break; > case 'c': > path_dhcpd_conf = optarg; > break; > @@ -130,6 +141,23 @@ > if (daemonize) > daemon(0, 0); > > + /* don't go near /dev/pf unless we actually intend to use it */ > + if ((abandoned_tab != NULL) || (changedmac_tab != NULL)){ > + if (pipe(pfpipe) == -1) > + error("pipe (%m)"); > + if ((pfproc_pid = fork()) == -1) > + error("fork (%m)"); > + if (pfproc_pid == 0){ > + /* child process. start up table engine */ > + pftable_handler(); > + > + /* not reached */ > + _exit(0); > + } > + gotpipe = 1; > + /* parent process continues. */ > + } > + > if (chroot(_PATH_VAREMPTY) == -1) > error("chroot %s: %m", _PATH_VAREMPTY); > if (chdir("/") == -1) > @@ -151,9 +179,10 @@ > { > extern char *__progname; > > - fprintf(stderr, "usage: %s [-dfn] [-c config-file] [-l lease-file]", > - __progname); > - fprintf(stderr, " [if0 [...ifN]]\n"); > + fprintf(stderr, "usage: %s [-dfn] [-c config-file]", __progname); > + fprintf(stderr, " [-l lease-file] [-p pf-device]\n"); > + fprintf(stderr, " [-A abandoned_ip_table]"); > + fprintf(stderr, " [-C changed_ip_table] [if0 [...ifN]]\n"); > exit(1); > } > > diff --exclude CVS -urN ../dhcpd/dhcpd.h ./dhcpd.h > --- ../dhcpd/dhcpd.h Wed Mar 1 15:40:16 2006 > +++ ./dhcpd.h Thu Mar 2 13:16:29 2006 > @@ -84,6 +84,10 @@ > #define _PATH_DHCPD_DB "/var/db/dhcpd.leases" > #endif > > +#ifndef _PATH_DEV_PF > +#define _PATH_DEV_PF "/dev/pf" > +#endif > + > /* Time stuff... */ > #include <sys/time.h> > > @@ -334,6 +338,12 @@ > struct option_data options [256]; /* Options supplied with lease. */ > }; > > +/* privsep message. fixed length for easy parsing */ > +struct pf_cmd{ > + u_int32_t type; /* 4 bytes to make alignment easy */ > + struct in_addr ip; > +}; > + > /* Possible states in which the client can be. */ > enum dhcp_state { > S_REBOOTING, > @@ -454,8 +464,8 @@ > struct hardware address; > }; > > -struct timeout { > - struct timeout *next; > +struct dhcpd_timeout { > + struct dhcpd_timeout *next; > time_t when; > void (*func)(void *); > void *what; > @@ -722,7 +732,7 @@ > extern struct protocol *protocols; > extern void (*bootp_packet_handler)(struct interface_info *, > struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *); > -extern struct timeout *timeouts; > +extern struct dhcpd_timeout *timeouts; > void discover_interfaces(int); > void dispatch(void); > int locate_network(struct packet *); > @@ -786,3 +796,8 @@ > void icmp_startup(int, void (*)(struct iaddr, u_int8_t *, int)); > int icmp_echorequest(struct iaddr *); > void icmp_echoreply(struct protocol *); > + > +/* pfutils.c */ > +__dead void pftable_handler(void); > +int pf_change_table(int , int , struct in_addr *, char *); > +int pf_kill_state(int , struct in_addr *); > diff --exclude CVS -urN ../dhcpd/dispatch.c ./dispatch.c > --- ../dhcpd/dispatch.c Wed Mar 1 15:40:16 2006 > +++ ./dispatch.c Tue Feb 28 12:33:42 2006 > @@ -50,8 +50,8 @@ > > struct interface_info *interfaces; > struct protocol *protocols; > -struct timeout *timeouts; > -static struct timeout *free_timeouts; > +struct dhcpd_timeout *timeouts; > +static struct dhcpd_timeout *free_timeouts; > static int interfaces_invalidated; > void (*bootp_packet_handler)(struct interface_info *, > struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *); > @@ -304,7 +304,7 @@ > another: > if (timeouts) { > if (timeouts->when <= cur_time) { > - struct timeout *t = timeouts; > + struct dhcpd_timeout *t = timeouts; > timeouts = timeouts->next; > (*(t->func))(t->what); > t->next = free_timeouts; > @@ -494,7 +494,7 @@ > void > add_timeout(time_t when, void (*where)(void *), void *what) > { > - struct timeout *t, *q; > + struct dhcpd_timeout *t, *q; > > /* See if this timeout supersedes an existing timeout. */ > t = NULL; > @@ -518,7 +518,7 @@ > q->func = where; > q->what = what; > } else { > - q = (struct timeout *)malloc(sizeof (struct timeout)); > + q = (struct dhcpd_timeout *)malloc(sizeof (struct dhcpd_timeout)); > if (!q) > error("Can't allocate timeout structure!"); > q->func = where; > @@ -554,7 +554,7 @@ > void > cancel_timeout(void (*where)(void *), void *what) > { > - struct timeout *t, *q; > + struct dhcpd_timeout *t, *q; > > /* Look for this timeout on the list, and unlink it if we find it. */ > t = NULL; > diff --exclude CVS -urN ../dhcpd/memory.c ./memory.c > --- ../dhcpd/memory.c Mon Sep 20 22:07:04 2004 > +++ ./memory.c Thu Mar 2 13:17:19 2006 > @@ -38,7 +38,15 @@ > * Enterprises, see ``http://www.vix.com''. > */ > > +#include <errno.h> > +#include <string.h> > +extern int errno; > + > #include "dhcpd.h" > +extern char *abandoned_tab; > +extern char *changedmac_tab; > +extern int pfpipe[2]; > +extern int gotpipe; > > static struct subnet *subnets; > static struct shared_network *shared_networks; > @@ -435,7 +443,10 @@ > { > int enter_uid = 0; > int enter_hwaddr = 0; > + int do_pftable = 0; > struct lease *lp; > + struct pf_cmd cmd; > + int l, r, n; > > /* Static leases are not currently kept in the database... */ > if (lease->flags & STATIC_LEASE) > @@ -489,8 +500,11 @@ > comp->hardware_addr.hlen))) { > hw_hash_delete(comp); > enter_hwaddr = 1; > - } else if (!comp->hardware_addr.htype) > + do_pftable = 1; > + } else if (!comp->hardware_addr.htype) { > enter_hwaddr = 1; > + do_pftable = 1; > + } > > /* Copy the data files, but not the linkages. */ > comp->starts = lease->starts; > @@ -595,9 +609,30 @@ > comp->ends = lease->ends; > } > > + if (gotpipe && (do_pftable == 1) && (changedmac_tab != NULL)){ > + l = sizeof(struct pf_cmd); > + n = 0; > + cmd.type = 'C'; > + bcopy(lease->ip_addr.iabuf, &cmd.ip.s_addr, 4); > + > + r = write(pfpipe[1], &cmd, l); > + if ((r == -1) || (r == l)) > + goto _fin; > + > + n += r; > + do { > + r = write(pfpipe[1], &cmd+n, (l-n)); > + if (r == -1) { > + if (errno != EINTR && errno != EAGAIN) > + goto _fin; > + } else > + n += r; > + } while (n < l); > + } > + > /* Return zero if we didn't commit the lease to permanent storage; > nonzero if we did. */ > - return commit && write_lease(comp) && commit_leases(); > +_fin: return commit && write_lease(comp) && commit_leases(); > } > > /* Release the specified lease and re-hash it as appropriate. */ > @@ -626,7 +661,9 @@ > abandon_lease(struct lease *lease, char *message) > { > struct lease lt; > + struct pf_cmd cmd; > time_t abtime; > + int l, r, n; > > abtime = lease->subnet->group->default_lease_time; > lease->flags |= ABANDONED_LEASE; > @@ -639,6 +676,28 @@ > lt.uid = NULL; > lt.uid_len = 0; > supersede_lease(lease, <, 1); > + > + if (gotpipe && abandoned_tab != NULL){ > + l = sizeof(struct pf_cmd); > + n = 0; > + cmd.type = 'A'; > + bcopy(lease->ip_addr.iabuf, &cmd.ip.s_addr, 4); > + > + r = write(pfpipe[1], &cmd, l); > + if ((r == -1) || (r == l)) > + return; > + > + n += r; > + do { > + r = write(pfpipe[1], &cmd+n, (l-n)); > + if (r == -1) { > + if (errno != EINTR && errno != EAGAIN) > + return; > + } else > + n += r; > + } while (n < l); > + } > + return; > } > > /* Locate the lease associated with a given IP address... */ > diff --exclude CVS -urN ../dhcpd/pfutils.c ./pfutils.c > --- ../dhcpd/pfutils.c Wed Dec 31 17:00:00 1969 > +++ ./pfutils.c Thu Mar 2 13:35:13 2006 > @@ -0,0 +1,158 @@ > +/* > + * Copyright (c) 2006 Chris Kuethe <chris.kuethe@gmail.com> > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <sys/types.h> > +#include <sys/ioctl.h> > +#include <sys/param.h> > +#include <sys/select.h> > +#include <sys/socket.h> > +#include <sys/time.h> > + > +#include <net/if.h> > +#include <net/pfvar.h> > +#include <arpa/inet.h> > + > +#include <ctype.h> > +#include <err.h> > +#include <errno.h> > +#include <fcntl.h> > +#include <pwd.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > + > +#include "dhcpd.h" > + > +extern char *abandoned_tab; > +extern char *changedmac_tab; > +extern int pfpipe[2]; > +extern struct passwd *pw; > + > +__dead void > +pftable_handler(){ > + int l, r, fd = -1; > + struct timeval tv; > + fd_set fds; > + struct pf_cmd cmd; > + > + if ((fd = open(_PATH_DEV_PF, O_RDWR|O_NOFOLLOW, 0660)) == -1) > + error("can't open pf device: %m"); > + if (chroot(_PATH_VAREMPTY) == -1) > + error("chroot %s: %m", _PATH_VAREMPTY); > + if (chdir("/") == -1) > + error("chdir(\"/\"): %m"); > + if (setgroups(1, &pw->pw_gid) || > + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || > + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) > + error("can't drop privileges: %m"); > + > + setproctitle("pf table handler"); > + l = sizeof(struct pf_cmd); > + > + for(; > + tv.tv_usec = 250000; > + tv.tv_sec = 0; > + FD_ZERO(&fds); > + FD_SET(pfpipe[0], &fds); > + > + if (select(pfpipe[0]+2, &fds, NULL, NULL, &tv) > 0){ > + bzero(&cmd, l); > + r = read(pfpipe[0], &cmd, l); > + if (r < l){ > + r = read(pfpipe[0], &cmd+r, l-r); > + if (r < l-r) > + continue; > + } > + > + switch (cmd.type){ > + case 'A': > + pf_change_table(fd, 1, &cmd.ip, abandoned_tab); > + pf_kill_state(fd, &cmd.ip); > + break; > + case 'C': > + pf_change_table(fd, 0, &cmd.ip, abandoned_tab); > + pf_change_table(fd, 0, &cmd.ip, changedmac_tab); > + break; > + default: > + break; > + } > + } > + } > + /* not reached */ > + _exit(0); > +} > + > +int > +pf_change_table(int fd, int op, struct in_addr *ip, char *table){ > + struct pfioc_table io; > + struct pfr_addr addr; > + > + bzero(&io, sizeof(io)); > + strlcpy(io.pfrio_table.pfrt_name, table, sizeof(io.pfrio_table)); > + io.pfrio_buffer = &addr; > + io.pfrio_esize = sizeof(addr); > + io.pfrio_size = 1; > + > + bzero(&addr, sizeof(addr)); > + memcpy(&addr.pfra_ip4addr, (char *)ip->s_addr, 4); > + addr.pfra_af = AF_INET; > + addr.pfra_net = 32; > + > + if (ioctl(fd, op ? DIOCRADDADDRS : DIOCRDELADDRS, &io) && > + errno != ESRCH) { > + warning( "DIOCR%sADDRS on table %s: %s", > + op ? "ADD" : "DEL", table, strerror(errno)); > + return (-1); > + } > + return (0); > +} > + > + > +int > +pf_kill_state(int fd, struct in_addr *ip){ > + struct pfioc_state_kill psk; > + struct pf_addr target; > + int r = 0; > + > + memset(&psk, 0, sizeof(psk)); > + memset(&target, 0, sizeof(target)); > + > + memcpy(&target.v4, (char *)ip->s_addr, 4); > + psk.psk_af = AF_INET; > + > + /* Kill all states from ipsrc */ > + memcpy(&psk.psk_src.addr.v.a.addr, &target, > + sizeof(psk.psk_src.addr.v.a.addr)); > + memset(&psk.psk_src.addr.v.a.mask, 0xff, > + sizeof(psk.psk_src.addr.v.a.mask)); > + if (ioctl(fd, DIOCKILLSTATES, &psk)){ > + warning("DIOCKILLSTATES failed (%s)", strerror(errno)); > + r--; > + } > + > + /* Kill all states to ipsrc */ > + memset(&psk.psk_src, 0, sizeof(psk.psk_src)); > + memcpy(&psk.psk_dst.addr.v.a.addr, &target, > + sizeof(psk.psk_dst.addr.v.a.addr)); > + memset(&psk.psk_dst.addr.v.a.mask, 0xff, > + sizeof(psk.psk_dst.addr.v.a.mask)); > + if (ioctl(fd, DIOCKILLSTATES, &psk)){ > + warning("DIOCKILLSTATES failed (%s)", strerror(errno)); > + r--; > + } > > > -- > GDB has a 'break' feature; why doesn't it have 'fix' too? > -- | | | The ASCII Fork Campaign \|/ against gratuitous use of threads. | |