--- sys/netinet/ip_dummynet.c Thu Sep 29 08:48:50 2005 +++ sys/netinet/ip_dummynet.c Wed Oct 12 08:01:24 2005 @@ -31,6 +31,7 @@ #if !defined(KLD_MODULE) #include "opt_inet6.h" +#include "opt_ipdn.h" #endif /* @@ -84,6 +85,21 @@ #include /* for ip6_input, ip6_output prototypes */ #include +#ifdef DEV_DNCTL +#include +#include +#include +#include + +static d_ioctl_t ip_dn_ioctl; +static struct cdevsw ip_dn_cdevsw = { + .d_ioctl = ip_dn_ioctl, + .d_name = "dummynet", + .d_version = D_VERSION, +}; +static struct cdev *ip_dn_dev; +#endif + /* * We keep a private variable for the simulation time, but we could * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) @@ -186,7 +202,7 @@ } while (0) static int config_pipe(struct dn_pipe *p); -static int ip_dn_ctl(struct sockopt *sopt); +static int ip_dn_sopt(struct sockopt *sopt); static void dummynet(void *); static void dummynet_flush(void); @@ -1940,7 +1956,8 @@ } static int -dummynet_get(struct sockopt *sopt) +dummynet_get(struct sockopt *sopt, enum sopt_dir dir, uint32_t cmd, + void* value, size_t* valsize, struct thread *td) { char *buf, *bp ; /* bp is the "copy-pointer" */ size_t size ; @@ -2006,24 +2023,43 @@ } DUMMYNET_UNLOCK(); + if (sopt != NULL) { error = sooptcopyout(sopt, buf, size); + } else { + if (*valsize > size) + *valsize = size; + if (value != NULL) + error = copyout(buf, value, *valsize); + } free(buf, M_TEMP); return error ; } -/* - * Handler for the various dummynet socket options (get, flush, config, del) - */ static int -ip_dn_ctl(struct sockopt *sopt) +ip_dn_control(struct sockopt *sopt, enum sopt_dir dir, uint32_t cmd, + void* value, size_t* valsize, struct thread *td) { int error = 0 ; struct dn_pipe *p, tmp_pipe; + #define SOOPTCOPYIN(sopt,buf,len,minlen) \ + do { \ + if ((sopt) != NULL) \ + error = sooptcopyin((sopt),(buf),(len),(minlen)); \ + else { \ + if (*valsize < (minlen)) \ + error = EINVAL; \ + else { \ + if (*valsize > (len)) \ + *valsize = (len); \ + error = copyin(value, (buf), *valsize); \ + } \ + } \ + } while(0) /* Disallow sets in really-really secure mode. */ - if (sopt->sopt_dir == SOPT_SET) { + if (dir == SOPT_SET) { #if __FreeBSD_version >= 500034 - error = securelevel_ge(sopt->sopt_td->td_ucred, 3); + error = securelevel_ge(td->td_ucred, 3); if (error) return (error); #else @@ -2032,39 +2068,102 @@ #endif } - switch (sopt->sopt_name) { + switch (cmd) { default : - printf("dummynet: -- unknown option %d", sopt->sopt_name); + printf("dummynet: ip_dn_conftrol invalid option %d", cmd); return EINVAL ; - case IP_DUMMYNET_GET : - error = dummynet_get(sopt); - break ; + case IP_DUMMYNET_GET: + error = dummynet_get(sopt, dir, cmd, value, valsize, td); + break; - case IP_DUMMYNET_FLUSH : - dummynet_flush() ; - break ; + case IP_DUMMYNET_FLUSH: + dummynet_flush(); + break; - case IP_DUMMYNET_CONFIGURE : - p = &tmp_pipe ; - error = sooptcopyin(sopt, p, sizeof *p, sizeof *p); - if (error) - break ; + case IP_DUMMYNET_CONFIGURE: + p = &tmp_pipe; + SOOPTCOPYIN(sopt, p, sizeof(struct dn_pipe), sizeof(struct dn_pipe)); + if (error == 0) error = config_pipe(p); break ; - case IP_DUMMYNET_DEL : /* remove a pipe or queue */ - p = &tmp_pipe ; - error = sooptcopyin(sopt, p, sizeof *p, sizeof *p); - if (error) - break ; - + case IP_DUMMYNET_DEL: /* remove a pipe or queue */ + p = &tmp_pipe; + SOOPTCOPYIN(sopt, p, sizeof(struct dn_pipe), sizeof(struct dn_pipe)); + if (error == 0) error = delete_pipe(p); break ; } return error ; +#undef SOOPTCOPYIN +} + +/* + * Handler for the various dummynet socket options (get, flush, config, del) + */ +static int +ip_dn_sopt(struct sockopt *sopt) +{ + size_t valsize = sopt->sopt_valsize; + return ip_dn_control(sopt, sopt->sopt_dir, + sopt->sopt_name, sopt->sopt_val, + &valsize, sopt->sopt_td); } +#ifdef DEV_DNCTL +static int +ip_dn_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td) +{ + int error = 0; + size_t size; + struct ip_dummynet_ctl* ctl = (struct ip_dummynet_ctl*)data; + + if (jailed(td->td_ucred)) { + return EPERM; + } + switch(cmd) { + case IPDNIOCSCMD: + if ((fflag & FWRITE) != FWRITE) { + error = EPERM; + break; + } + if (ctl->opcode == IP_DUMMYNET_GET || ( + ctl->opcode != IP_DUMMYNET_CONFIGURE && + ctl->opcode != IP_DUMMYNET_DEL && + ctl->opcode != IP_DUMMYNET_FLUSH )) { + error = EINVAL; + break; + } + size = ctl->valsize; + error = ip_dn_control(NULL, SOPT_SET, ctl->opcode, + ctl->value, &size, td); + break; + case IPDNIOCGCMD: + if ((fflag & FREAD) != FREAD) { + error = EPERM; + break; + } + if (ctl->opcode != IP_DUMMYNET_GET) { + error = EINVAL; + break; + } + error = copyin((void*)ctl->valsize, &size, sizeof(size_t)); + if (error != 0) + break; + error = ip_dn_control(NULL, SOPT_GET, ctl->opcode, + ctl->value, (size_t*)&size, td); + if (error == 0) + error = copyout(&size, (void*)ctl->valsize, sizeof(size_t)); + break; + default: + error = ENODEV; + }; + return error; +} +#endif + static void ip_dn_init(void) { @@ -2084,7 +2183,10 @@ extract_heap.size = extract_heap.elements = 0 ; extract_heap.offset = 0 ; - ip_dn_ctl_ptr = ip_dn_ctl; +#ifdef DEV_DNCTL + ip_dn_dev = make_dev(&ip_dn_cdevsw, 0, 0, 0, 0600, "dnctl"); +#endif + ip_dn_ctl_ptr = ip_dn_sopt; ip_dn_io_ptr = dummynet_io; ip_dn_ruledel_ptr = dn_rule_delete; @@ -2096,6 +2198,9 @@ static void ip_dn_destroy(void) { +#ifdef DEV_DNCTL + destroy_dev(ip_dn_dev); +#endif ip_dn_ctl_ptr = NULL; ip_dn_io_ptr = NULL; ip_dn_ruledel_ptr = NULL; --- sys/netinet/ip_dummynet_io.h Fri Oct 7 13:08:35 2005 +++ sys/netinet/ip_dummynet_io.h Thu Oct 6 15:33:44 2005 @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2005 Andrey V. Elsukov, + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _IP_DUMMYNET_IO_H_ +#define _IP_DUMMYNET_IO_H__ + +#include + +struct ip_dummynet_ctl { + int opcode; /* control code */ + uintptr_t valsize; /* size of value or pointer to size of value */ + char* value; /* pointer to value */ +}; + +#define IPDNIOCSCMD _IOW('D', 2, struct ip_dummynet_ctl) +#define IPDNIOCGCMD _IOWR('D', 3, struct ip_dummynet_ctl) + +#endif /* _IP_DUMMYNET_IO_H_ */ --- sys/netinet/ip_fw2.c Thu Sep 29 08:48:50 2005 +++ sys/netinet/ip_fw2.c Wed Oct 12 07:58:55 2005 @@ -93,6 +93,20 @@ #include /* XXX for in_cksum */ +#ifdef DEV_IPFWCTL +#include +#include +#include + +static d_ioctl_t ipfw_ioctl; +static struct cdevsw ip_fw_cdevsw = { + .d_ioctl = ipfw_ioctl, + .d_name = "ipfw", + .d_version = D_VERSION, +}; +static struct cdev *ip_fw_dev; +#endif + /* * set_disable contains one bit per set value (0..31). * If the bit is set, all rules with the corresponding set @@ -3854,30 +3869,56 @@ } -/** - * {set|get}sockopt parser. - */ + static int -ipfw_ctl(struct sockopt *sopt) +ipfw_control(struct sockopt *sopt, enum sopt_dir dir, uint32_t cmd, + void* value, size_t* valsize, struct thread *td) { #define RULE_MAXSIZE (256*sizeof(u_int32_t)) - int error, rule_num; +/* + * SOOPTCOPYIN and SOOPCOPYOUT affects a valsize and error variables. + * */ +#define SOOPTCOPYIN(sopt,buf,len,minlen) \ + do { \ + if ((sopt) != NULL) { \ + error = sooptcopyin((sopt),(buf),(len),(minlen)); \ + *valsize = (sopt)->sopt_valsize; \ + } else { \ + if (*valsize < (minlen)) \ + error = EINVAL; \ + else { \ + if (*valsize > (len)) \ + *valsize = (len); \ + error = copyin(value, (buf), *valsize); \ + } \ + } \ + } while(0) +#define SOOPTCOPYOUT(sopt,buf,len) \ + do { \ + if ((sopt) != NULL) { \ + error = sooptcopyout((sopt),(buf),(len)); \ + *valsize = (sopt)->sopt_valsize; \ + } else { \ + if (*valsize > (len)) \ + *valsize = (len); \ + if (value != NULL) \ + error = copyout((buf), value, *valsize); \ + } \ + } while(0) + + int rule_num, error = 0; size_t size; + uint32_t rulenum[2]; struct ip_fw *buf, *rule; - u_int32_t rulenum[2]; - - error = suser(sopt->sopt_td); - if (error) - return (error); /* * Disallow modifications in really-really secure mode, but still allow * the logging counters to be reset. */ - if (sopt->sopt_name == IP_FW_ADD || - (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) { + if (cmd == IP_FW_ADD || ((dir == SOPT_SET) && cmd != IP_FW_RESETLOG)) + { #if __FreeBSD_version >= 500034 - error = securelevel_ge(sopt->sopt_td->td_ucred, 3); + error = securelevel_ge(td->td_ucred, 3); if (error) return (error); #else /* FreeBSD 4.x */ @@ -3888,7 +3929,7 @@ error = 0; - switch (sopt->sopt_name) { + switch (cmd) { case IP_FW_GET: /* * pass up a copy of the current rules. Static rules @@ -3911,8 +3952,8 @@ * buffer, just jump to the sooptcopyout. */ buf = malloc(size, M_TEMP, M_WAITOK); - error = sooptcopyout(sopt, buf, - ipfw_getrules(&layer3_chain, buf, size)); + size = ipfw_getrules(&layer3_chain, buf, size); + SOOPTCOPYOUT(sopt, buf, size); free(buf, M_TEMP); break; @@ -3939,17 +3980,17 @@ reap_rules(rule); break; - case IP_FW_ADD: +case IP_FW_ADD: rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, rule, RULE_MAXSIZE, - sizeof(struct ip_fw) ); + SOOPTCOPYIN(sopt, rule, RULE_MAXSIZE, sizeof(struct ip_fw)); if (error == 0) - error = check_ipfw_struct(rule, sopt->sopt_valsize); + error = check_ipfw_struct(rule, *valsize); if (error == 0) { error = add_rule(&layer3_chain, rule); size = RULESIZE(rule); - if (!error && sopt->sopt_dir == SOPT_GET) - error = sooptcopyout(sopt, rule, size); + } + if (error == 0 && (dir == SOPT_GET)) { + SOOPTCOPYOUT(sopt, rule, size); } free(rule, M_TEMP); break; @@ -3967,16 +4008,13 @@ * first u_int32_t contains sets to be disabled, * second u_int32_t contains sets to be enabled. */ - error = sooptcopyin(sopt, rulenum, - 2*sizeof(u_int32_t), sizeof(u_int32_t)); + SOOPTCOPYIN(sopt, rulenum, 2*sizeof(uint32_t), sizeof(uint32_t)); if (error) break; - size = sopt->sopt_valsize; - if (size == sizeof(u_int32_t)) /* delete or reassign */ + if (*valsize == sizeof(uint32_t)) /* delete or reassign */ error = del_entry(&layer3_chain, rulenum[0]); - else if (size == 2*sizeof(u_int32_t)) /* set enable/disable */ - set_disable = - (set_disable | rulenum[0]) & ~rulenum[1] & + else if (*valsize == 2*sizeof(uint32_t)) /* set enable/disable */ + set_disable = (set_disable | rulenum[0]) & ~rulenum[1] & ~(1<sopt_val != 0) { - error = sooptcopyin(sopt, &rule_num, - sizeof(int), sizeof(int)); - if (error) - break; - } + if (value != 0) + SOOPTCOPYIN(sopt, &rule_num, sizeof(int), sizeof(int)); error = zero_entry(&layer3_chain, rule_num, - sopt->sopt_name == IP_FW_RESETLOG); + cmd == IP_FW_RESETLOG); break; case IP_FW_TABLE_ADD: { ipfw_table_entry ent; - error = sooptcopyin(sopt, &ent, - sizeof(ent), sizeof(ent)); + SOOPTCOPYIN(sopt, &ent, sizeof(ent), sizeof(ent)); if (error) break; error = add_table_entry(ent.tbl, ent.addr, @@ -4012,8 +4045,7 @@ { ipfw_table_entry ent; - error = sooptcopyin(sopt, &ent, - sizeof(ent), sizeof(ent)); + SOOPTCOPYIN(sopt, &ent, sizeof(ent), sizeof(ent)); if (error) break; error = del_table_entry(ent.tbl, ent.addr, ent.masklen); @@ -4024,8 +4056,7 @@ { u_int16_t tbl; - error = sooptcopyin(sopt, &tbl, - sizeof(tbl), sizeof(tbl)); + SOOPTCOPYIN(sopt, &tbl, sizeof(tbl), sizeof(tbl)); if (error) break; error = flush_table(tbl); @@ -4036,12 +4067,12 @@ { u_int32_t tbl, cnt; - if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), - sizeof(tbl)))) + SOOPTCOPYIN(sopt, &tbl, sizeof(tbl), sizeof(tbl)); + if (error) break; if ((error = count_table(tbl, &cnt))) break; - error = sooptcopyout(sopt, &cnt, sizeof(cnt)); + SOOPTCOPYOUT(sopt, &cnt, sizeof(cnt)); } break; @@ -4049,40 +4080,128 @@ { ipfw_table *tbl; - if (sopt->sopt_valsize < sizeof(*tbl)) { + if (*valsize < sizeof(ipfw_table)) { error = EINVAL; break; } - size = sopt->sopt_valsize; + size = *valsize; tbl = malloc(size, M_TEMP, M_WAITOK); if (tbl == NULL) { error = ENOMEM; break; } - error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); + SOOPTCOPYIN(sopt, tbl, size, sizeof(ipfw_table)); if (error) { free(tbl, M_TEMP); break; } - tbl->size = (size - sizeof(*tbl)) / + tbl->size = (size - sizeof(ipfw_table)) / sizeof(ipfw_table_entry); error = dump_table(tbl); if (error) { free(tbl, M_TEMP); break; } - error = sooptcopyout(sopt, tbl, size); + SOOPTCOPYOUT(sopt, tbl, size); free(tbl, M_TEMP); } break; default: - printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); + printf("ipfw: ipfw_control invalid option %d\n", cmd); error = EINVAL; } return (error); #undef RULE_MAXSIZE +#undef SOOPTCOPYIN +#undef SOOPTCOPYOUT +} + +#ifdef DEV_IPFWCTL +static int +ipfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td) +{ + int error; + size_t size; + struct ip_fw_ctl* ctl = (struct ip_fw_ctl*)data; + + if (jailed(td->td_ucred)) { + return EPERM; + } + switch(cmd) { + /* + * IPFWIOCSCMD makes some modifications of ipfw's state + * and therefore need a writing permissions to /dev/ipfwctl + * */ + case IPFWIOCSCMD: + if ((fflag & FWRITE) != FWRITE) { + error = EPERM; + break; + } + if (ctl->opcode == IP_FW_GET || + ctl->opcode == IP_FW_TABLE_GETSIZE || + ctl->opcode == IP_FW_TABLE_LIST || ( + ctl->opcode != IP_FW_TABLE_ADD && + ctl->opcode != IP_FW_TABLE_DEL && + ctl->opcode != IP_FW_TABLE_FLUSH && + ctl->opcode != IP_FW_ADD && + ctl->opcode != IP_FW_DEL && + ctl->opcode != IP_FW_FLUSH && + ctl->opcode != IP_FW_ZERO && + ctl->opcode != IP_FW_RESETLOG)) + { + error = EINVAL; + break; + } + size = ctl->valsize; + error = ipfw_control(NULL, SOPT_SET, ctl->opcode, + ctl->value, &size, td); + break; + case IPFWIOCGCMD: + if ((fflag & FREAD) != FREAD) { + error = EPERM; + break; + } + /* We need a write permission for change IPFW state. */ + if (ctl->opcode == IP_FW_ADD && (fflag & FWRITE) != FWRITE) { + error = EPERM; + break; + } + /* + * Allowed only IP_FW_GET, IP_FW_ADD, IP_FW_TABLE_GETSIZE, + * IP_FW_TABLE_LIST opcodes + * */ + if (ctl->opcode != IP_FW_GET && ctl->opcode != IP_FW_ADD && + ctl->opcode != IP_FW_TABLE_GETSIZE && + ctl->opcode != IP_FW_TABLE_LIST) + { + error = EINVAL; + break; + } + error = copyin((void*)ctl->valsize, &size, sizeof(size_t)); + if (error != 0) + break; + error = ipfw_control(NULL, SOPT_GET, ctl->opcode, + ctl->value, (size_t*)&size, td); + if (error == 0) /* copy new valsize out */ + error = copyout(&size, (void*)ctl->valsize, sizeof(size_t)); + break; + default: + error = ENODEV; + }; + return error; +} +#endif + +static int +ipfw_sopt(struct sockopt *sopt) +{ + size_t valsize = sopt->sopt_valsize; + return ipfw_control(sopt, sopt->sopt_dir, + sopt->sopt_name, sopt->sopt_val, + &valsize, sopt->sopt_td); } /** @@ -4233,8 +4352,11 @@ printf("limited to %d packets/entry by default\n", verbose_limit); +#ifdef DEV_IPFWCTL + ip_fw_dev = make_dev(&ip_fw_cdevsw, 0, 0, 0, 0600, "ipfwctl"); +#endif init_tables(); - ip_fw_ctl_ptr = ipfw_ctl; + ip_fw_ctl_ptr = ipfw_sopt; ip_fw_chk_ptr = ipfw_chk; callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL); @@ -4246,6 +4368,9 @@ { struct ip_fw *reap; +#ifdef DEV_IPFWCTL + destroy_dev(ip_fw_dev); +#endif ip_fw_chk_ptr = NULL; ip_fw_ctl_ptr = NULL; callout_drain(&ipfw_timeout); --- sys/netinet/ip_fw_io.h Fri Oct 7 13:08:31 2005 +++ sys/netinet/ip_fw_io.h Mon Oct 3 16:06:49 2005 @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2005 Andrey V. Elsukov, + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _IP_FW_IO_H_ +#define _IP_FW_IO_H_ + +#include + +struct ip_fw_ctl { + int opcode; /* IP_FW_XXX control code */ + uintptr_t valsize; /* size of value or pointer to size of value */ + char* value; /* pointer to value */ +}; + +#define IPFWIOCSCMD _IOW('D', 0, struct ip_fw_ctl) +#define IPFWIOCGCMD _IOWR('D', 1, struct ip_fw_ctl) + +#endif /* _IP_FW_IO_H_ */