Index: sbin/ip6fw/ip6fw.c diff -u sbin/ip6fw/ip6fw.c.orig sbin/ip6fw/ip6fw.c --- sbin/ip6fw/ip6fw.c.orig Fri Aug 3 02:00:29 2001 +++ sbin/ip6fw/ip6fw.c Wed Feb 13 23:03:00 2002 @@ -415,22 +415,21 @@ char **av; { struct ip6_fw *r, *rules; - int l,i; + int l; unsigned long rulenum; - int nalloc, bytes, maxbytes; + int nalloc, bytes; /* extract rules from kernel, resizing array as necessary */ rules = NULL; nalloc = sizeof *rules; bytes = nalloc; - maxbytes = 65536 * sizeof *rules; while (bytes >= nalloc) { nalloc = nalloc * 2 + 200; bytes = nalloc; if ((rules = realloc(rules, bytes)) == NULL) err(EX_OSERR, "realloc"); - i = getsockopt(s, IPPROTO_IPV6, IPV6_FW_GET, rules, &bytes); - if ((i < 0 && errno != EINVAL) || nalloc > maxbytes) + if (getsockopt(s, IPPROTO_IPV6, IPV6_FW_GET, + rules, &bytes) < 0) err(EX_OSERR, "getsockopt(IPV6_FW_GET)"); } if (!ac) { @@ -1116,11 +1115,11 @@ show_usage("can't mix 'frag' and port specifications"); } + i = sizeof(rule); + if (getsockopt(s, IPPROTO_IPV6, IPV6_FW_ADD, &rule, &i) == -1) + err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ADD"); if (!do_quiet) show_ip6fw(&rule); - i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_ADD, &rule, sizeof rule); - if (i) - err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ADD"); } static void Index: sys/netinet6/ip6_fw.c diff -u sys/netinet6/ip6_fw.c.orig sys/netinet6/ip6_fw.c --- sys/netinet6/ip6_fw.c.orig Wed Nov 21 00:56:45 2001 +++ sys/netinet6/ip6_fw.c Wed Feb 13 23:31:14 2002 @@ -82,6 +82,8 @@ static int fw6_verbose_limit = 0; #endif +#define IP6FW_DEFAULT_RULE ((u_int)(u_short)~0) + LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain; #ifdef SYSCTL_NODE @@ -102,9 +104,8 @@ static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl)); static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number)); -static int zero_entry6 __P((struct mbuf *m)); -static struct ip6_fw *check_ip6fw_struct __P((struct ip6_fw *m)); -static struct ip6_fw *check_ip6fw_mbuf __P((struct mbuf *fw)); +static int zero_entry6 __P((struct ip6_fw *)); +static int check_ip6fw_struct __P((struct ip6_fw *frwl)); static int ip6opts_match __P((struct ip6_hdr **ip6, struct ip6_fw *f, struct mbuf **m, int *off, int *nxt, u_short *offset)); @@ -117,7 +118,7 @@ static int ip6_fw_chk __P((struct ip6_hdr **pip6, struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)); -static int ip6_fw_ctl __P((int stage, struct mbuf **mm)); +static int ip6_fw_ctl __P((struct sockopt *sopt)); static char err_prefix[] = "ip6_fw_ctl:"; @@ -492,7 +493,7 @@ chain = LIST_FIRST(&ip6_fw_chain); #ifdef IP6FW_DIVERT_RESTART if (skipto) { - if (skipto >= 65535) + if (skipto >= IP6FW_DEFAULT_RULE) goto dropit; while (chain && (chain->rule->fw_number <= skipto)) { chain = LIST_NEXT(chain, chain); @@ -722,7 +723,7 @@ } #ifdef DIAGNOSTIC - /* Rule 65535 should always be there and should always match */ + /* Rule IP6FW_DEFAULT_RULE should always be there and should always match */ if (!chain) panic("ip6_fw: chain"); #endif @@ -835,25 +836,19 @@ LIST_INSERT_HEAD(chainptr, fwc, chain); splx(s); return(0); - } else if (ftmp->fw_number == (u_short)-1) { - if (fwc) free(fwc, M_IP6FW); - if (ftmp) free(ftmp, M_IP6FW); - splx(s); - dprintf(("%s bad rule number\n", err_prefix)); - return (EINVAL); } /* If entry number is 0, find highest numbered rule and add 100 */ if (ftmp->fw_number == 0) { for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) { - if (fcp->rule->fw_number != (u_short)-1) + if (fcp->rule->fw_number != IP6FW_DEFAULT_RULE) nbr = fcp->rule->fw_number; else break; } - if (nbr < (u_short)-1 - 100) + if (nbr < IP6FW_DEFAULT_RULE - 100) nbr += 100; - ftmp->fw_number = nbr; + ftmp->fw_number = frwl->fw_number = nbr; } /* Got a valid number; now insert it, keeping the list ordered */ @@ -883,7 +878,7 @@ s = splnet(); fcp = chainptr->lh_first; - if (number != (u_short)-1) { + if (number != IP6FW_DEFAULT_RULE) { for (; fcp; fcp = fcp->chain.le_next) { if (fcp->rule->fw_number == number) { LIST_REMOVE(fcp, chain); @@ -900,33 +895,40 @@ } static int -zero_entry6(struct mbuf *m) +zero_entry6(struct ip6_fw *frwl) { - struct ip6_fw *frwl; struct ip6_fw_chain *fcp; int s; - if (m && m->m_len != 0) { - if (m->m_len != sizeof(struct ip6_fw)) - return(EINVAL); - frwl = mtod(m, struct ip6_fw *); - } - else - frwl = NULL; - - /* - * It's possible to insert multiple chain entries with the - * same number, so we don't stop after finding the first - * match if zeroing a specific entry. - */ - s = splnet(); - for (fcp = ip6_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next) - if (!frwl || frwl->fw_number == fcp->rule->fw_number) { + if (frwl == 0) { + s = splnet(); + for (fcp = LIST_FIRST(&ip6_fw_chain); fcp; fcp = LIST_NEXT(fcp, chain)) { fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0; fcp->rule->timestamp = 0; } - splx(s); - + splx(s); + } else { + int cleared = 0; + /* + * It is possible to insert multiple chain entries with the + * same number, so we don't stop after finding the first + * match if zeroing a specific entry. + */ + for (fcp = LIST_FIRST(&ip6_fw_chain); fcp; fcp = LIST_NEXT(fcp, chain)) + if (frwl->fw_number == fcp->rule->fw_number) { + s = splnet(); + while (fcp && frwl->fw_number == fcp->rule->fw_number) { + fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0; + fcp->rule->timestamp = 0; + fcp = LIST_NEXT(fcp, chain); + } + splx(s); + cleared = 1; + break; + } + if (!cleared) /* we did not find any matching rules */ + return (EINVAL); + } if (fw6_verbose) { if (frwl) log(LOG_SECURITY | LOG_NOTICE, @@ -936,34 +938,22 @@ "ip6fw: Accounting cleared.\n"); } - return(0); -} - -static struct ip6_fw * -check_ip6fw_mbuf(struct mbuf *m) -{ - /* Check length */ - if (m->m_len != sizeof(struct ip6_fw)) { - dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, - sizeof(struct ip6_fw))); - return (NULL); - } - return(check_ip6fw_struct(mtod(m, struct ip6_fw *))); + return (0); } -static struct ip6_fw * +static int check_ip6fw_struct(struct ip6_fw *frwl) { /* Check for invalid flag bits */ if ((frwl->fw_flg & ~IPV6_FW_F_MASK) != 0) { dprintf(("%s undefined flag bits set (flags=%x)\n", err_prefix, frwl->fw_flg)); - return (NULL); + return (EINVAL); } /* Must apply to incoming or outgoing (or both) */ if (!(frwl->fw_flg & (IPV6_FW_F_IN | IPV6_FW_F_OUT))) { dprintf(("%s neither in nor out\n", err_prefix)); - return (NULL); + return (EINVAL); } /* Empty interface name is no good */ if (((frwl->fw_flg & IPV6_FW_F_IIFNAME) @@ -971,7 +961,7 @@ || ((frwl->fw_flg & IPV6_FW_F_OIFNAME) && !*frwl->fw_out_if.fu_via_if.name)) { dprintf(("%s empty interface name\n", err_prefix)); - return (NULL); + return (EINVAL); } /* Sanity check interface matching */ if ((frwl->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { @@ -980,23 +970,23 @@ && (frwl->fw_flg & IPV6_FW_F_OIFACE)) { dprintf(("%s outgoing interface check on incoming\n", err_prefix)); - return (NULL); + return (EINVAL); } /* Sanity check port ranges */ if ((frwl->fw_flg & IPV6_FW_F_SRNG) && IPV6_FW_GETNSRCP(frwl) < 2) { dprintf(("%s src range set but n_src_p=%d\n", err_prefix, IPV6_FW_GETNSRCP(frwl))); - return (NULL); + return (EINVAL); } if ((frwl->fw_flg & IPV6_FW_F_DRNG) && IPV6_FW_GETNDSTP(frwl) < 2) { dprintf(("%s dst range set but n_dst_p=%d\n", err_prefix, IPV6_FW_GETNDSTP(frwl))); - return (NULL); + return (EINVAL); } if (IPV6_FW_GETNSRCP(frwl) + IPV6_FW_GETNDSTP(frwl) > IPV6_FW_MAX_PORTS) { dprintf(("%s too many ports (%d+%d)\n", err_prefix, IPV6_FW_GETNSRCP(frwl), IPV6_FW_GETNDSTP(frwl))); - return (NULL); + return (EINVAL); } /* * Protocols other than TCP/UDP don't use port range @@ -1006,7 +996,7 @@ (IPV6_FW_GETNSRCP(frwl) || IPV6_FW_GETNDSTP(frwl))) { dprintf(("%s port(s) specified for non TCP/UDP rule\n", err_prefix)); - return(NULL); + return (EINVAL); } /* @@ -1023,38 +1013,37 @@ (frwl->fw_dst.s6_addr32[2] & (~frwl->fw_dmsk.s6_addr32[2])) || (frwl->fw_dst.s6_addr32[3] & (~frwl->fw_dmsk.s6_addr32[3]))) { dprintf(("%s rule never matches\n", err_prefix)); - return(NULL); + return (EINVAL); } if ((frwl->fw_flg & IPV6_FW_F_FRAG) && (frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) { if (frwl->fw_nports) { dprintf(("%s cannot mix 'frag' and ports\n", err_prefix)); - return(NULL); + return (EINVAL); } if (frwl->fw_prot == IPPROTO_TCP && frwl->fw_tcpf != frwl->fw_tcpnf) { dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix)); - return(NULL); + return (EINVAL); } } /* Check command specific stuff */ - switch (frwl->fw_flg & IPV6_FW_F_COMMAND) - { + switch (frwl->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_REJECT: if (frwl->fw_reject_code >= 0x100 && !(frwl->fw_prot == IPPROTO_TCP && frwl->fw_reject_code == IPV6_FW_REJECT_RST)) { dprintf(("%s unknown reject code\n", err_prefix)); - return(NULL); + return (EINVAL); } break; case IPV6_FW_F_DIVERT: /* Diverting to port zero is invalid */ case IPV6_FW_F_TEE: if (frwl->fw_divert_port == 0) { dprintf(("%s can't divert to port 0\n", err_prefix)); - return (NULL); + return (EINVAL); } break; case IPV6_FW_F_DENY: @@ -1064,125 +1053,127 @@ break; default: dprintf(("%s invalid command\n", err_prefix)); - return(NULL); + return (EINVAL); } - return frwl; + return 0; } static int -ip6_fw_ctl(int stage, struct mbuf **mm) +ip6_fw_ctl(struct sockopt *sopt) { - int error; - struct mbuf *m; + int error, s; + size_t size; + char *buf, *bp; + struct ip6_fw_chain *fcp; + struct ip6_fw frwl; - if (stage == IPV6_FW_GET) { - struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first; - *mm = m = m_get(M_WAIT, MT_DATA); /* XXX */ - if (!m) - return(ENOBUFS); - if (sizeof *(fcp->rule) > MLEN) { - MCLGET(m, M_WAIT); - if ((m->m_flags & M_EXT) == 0) { - m_free(m); - return(ENOBUFS); - } + /* + * Disallow modifications in really-really secure mode, but still allow + * the logging counters to be reset. + */ + if (sopt->sopt_name == IPV6_FW_ADD || sopt->sopt_dir == SOPT_SET) { + if (securelevel >= 3) + return (EPERM); + } + + error = 0; + + switch (sopt->sopt_name) { + case IPV6_FW_GET: + s = splnet(); + /* size of static rules */ + for (fcp = LIST_FIRST(&ip6_fw_chain), size = 0; fcp; + fcp = LIST_NEXT(fcp, chain)) + size += sizeof *fcp->rule; + + /* + * XXX todo: if the user passes a short length to know how + * much room is needed, do not + * bother filling up the buffer, just jump to the + * sooptcopyout. + */ + buf = malloc(size, M_TEMP, M_WAITOK); + if (buf == 0) { + splx(s); + error = ENOBUFS; + break; } - for (; fcp; fcp = fcp->chain.le_next) { - bcopy(fcp->rule, m->m_data, sizeof *(fcp->rule)); - m->m_len = sizeof *(fcp->rule); - m->m_next = m_get(M_WAIT, MT_DATA); /* XXX */ - if (!m->m_next) { - m_freem(*mm); - return(ENOBUFS); - } - m = m->m_next; - if (sizeof *(fcp->rule) > MLEN) { - MCLGET(m, M_WAIT); - if ((m->m_flags & M_EXT) == 0) { - m_freem(*mm); - return(ENOBUFS); - } - } - m->m_len = 0; + + for (fcp = LIST_FIRST(&ip6_fw_chain), bp = buf; fcp; + fcp = LIST_NEXT(fcp, chain)) { + bcopy(fcp->rule, bp, sizeof *fcp->rule); + bp += sizeof *fcp->rule; } - return (0); - } - m = *mm; - /* only allow get calls if secure mode > 2 */ - if (securelevel > 2) { - if (m) { - (void)m_freem(m); - *mm = 0; - } - return(EPERM); - } - if (stage == IPV6_FW_FLUSH) { - while (ip6_fw_chain.lh_first != NULL && - ip6_fw_chain.lh_first->rule->fw_number != (u_short)-1) { - struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first; - int s = splnet(); - LIST_REMOVE(ip6_fw_chain.lh_first, chain); - splx(s); + splx(s); + + error = sooptcopyout(sopt, buf, size); + free(buf, M_TEMP); + break; + + case IPV6_FW_FLUSH: + for (fcp = ip6_fw_chain.lh_first; + fcp && fcp->rule->fw_number != IP6FW_DEFAULT_RULE; + fcp = ip6_fw_chain.lh_first) { + s = splnet(); + LIST_REMOVE(fcp, chain); free(fcp->rule, M_IP6FW); free(fcp, M_IP6FW); + splx(s); } - if (m) { - (void)m_freem(m); - *mm = 0; - } - return (0); - } - if (stage == IPV6_FW_ZERO) { - error = zero_entry6(m); - if (m) { - (void)m_freem(m); - *mm = 0; - } - return (error); - } - if (m == NULL) { - printf("%s NULL mbuf ptr\n", err_prefix); - return (EINVAL); - } + break; - if (stage == IPV6_FW_ADD) { - struct ip6_fw *frwl = check_ip6fw_mbuf(m); + case IPV6_FW_ADD: + error = sooptcopyin(sopt, &frwl, sizeof frwl, sizeof frwl); + if (error || (error = check_ip6fw_struct(&frwl))) + break; - if (!frwl) - error = EINVAL; - else - error = add_entry6(&ip6_fw_chain, frwl); - if (m) { - (void)m_freem(m); - *mm = 0; - } - return error; - } - if (stage == IPV6_FW_DEL) { - if (m->m_len != sizeof(struct ip6_fw)) { - dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, - sizeof(struct ip6_fw))); + if (frwl.fw_number == IP6FW_DEFAULT_RULE) { + dprintf(("%s can't add rule %u\n", err_prefix, + (unsigned)IP6FW_DEFAULT_RULE)); error = EINVAL; - } else if (mtod(m, struct ip6_fw *)->fw_number == (u_short)-1) { - dprintf(("%s can't delete rule 65535\n", err_prefix)); + } else { + error = add_entry6(&ip6_fw_chain, &frwl); + if (!error && sopt->sopt_dir == SOPT_GET) + error = sooptcopyout(sopt, &frwl, sizeof frwl); + } + break; + + case IPV6_FW_DEL: + error = sooptcopyin(sopt, &frwl, sizeof frwl, sizeof frwl); + if (error) + break; + + if (frwl.fw_number == IP6FW_DEFAULT_RULE) { + dprintf(("%s can't delete rule %u\n", err_prefix, + (unsigned)IP6FW_DEFAULT_RULE)); error = EINVAL; - } else - error = del_entry6(&ip6_fw_chain, - mtod(m, struct ip6_fw *)->fw_number); - if (m) { - (void)m_freem(m); - *mm = 0; - } - return error; - } + } else { + error = del_entry6(&ip6_fw_chain, frwl.fw_number); + } + break; - dprintf(("%s unknown request %d\n", err_prefix, stage)); - if (m) { - (void)m_freem(m); - *mm = 0; + case IPV6_FW_ZERO: + { + void *arg = NULL; + + if (sopt->sopt_val != 0) { + error = sooptcopyin(sopt, &frwl, sizeof frwl, + sizeof frwl); + if (error) + break; + arg = &frwl; + } + error = zero_entry6(arg); + } + break; + + default: + printf("ip6_fw_ctl invalid option %d\n", sopt->sopt_name); + error = EINVAL; } - return (EINVAL); + + return (error); } void @@ -1196,14 +1187,14 @@ bzero(&default_rule, sizeof default_rule); default_rule.fw_prot = IPPROTO_IPV6; - default_rule.fw_number = (u_short)-1; + default_rule.fw_number = IP6FW_DEFAULT_RULE; #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT default_rule.fw_flg |= IPV6_FW_F_ACCEPT; #else default_rule.fw_flg |= IPV6_FW_F_DENY; #endif default_rule.fw_flg |= IPV6_FW_F_IN | IPV6_FW_F_OUT; - if (check_ip6fw_struct(&default_rule) == NULL || + if (check_ip6fw_struct(&default_rule) != 0 || add_entry6(&ip6_fw_chain, &default_rule)) panic(__FUNCTION__); Index: sys/netinet6/ip6_fw.h diff -u sys/netinet6/ip6_fw.h.orig sys/netinet6/ip6_fw.h --- sys/netinet6/ip6_fw.h.orig Tue Jul 3 20:01:53 2001 +++ sys/netinet6/ip6_fw.h Wed Feb 13 23:03:00 2002 @@ -192,7 +192,7 @@ struct ip6_hdr; typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, u_short *, struct mbuf**)); -typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); +typedef int ip6_fw_ctl_t __P((struct sockopt *)); extern ip6_fw_chk_t *ip6_fw_chk_ptr; extern ip6_fw_ctl_t *ip6_fw_ctl_ptr; extern int ip6_fw_enable; Index: sys/netinet6/ip6_output.c diff -u sys/netinet6/ip6_output.c.orig sys/netinet6/ip6_output.c --- sys/netinet6/ip6_output.c.orig Sun Nov 4 02:45:26 2001 +++ sys/netinet6/ip6_output.c Wed Feb 13 23:20:24 2002 @@ -1494,21 +1494,10 @@ case IPV6_FW_DEL: case IPV6_FW_FLUSH: case IPV6_FW_ZERO: - { - struct mbuf *m; - struct mbuf **mp = &m; - if (ip6_fw_ctl_ptr == NULL) - return EINVAL; - /* XXX */ - if ((error = soopt_getm(sopt, &m)) != 0) - break; - /* XXX */ - if ((error = soopt_mcopyin(sopt, m)) != 0) - break; - error = (*ip6_fw_ctl_ptr)(optname, mp); - m = *mp; - } + error = ENOPROTOOPT; + else + error = (*ip6_fw_ctl_ptr)(sopt); break; default: @@ -1651,21 +1640,12 @@ } #endif /* KAME IPSEC */ + case IPV6_FW_ADD: /* ADD actually returns the body... */ case IPV6_FW_GET: - { - struct mbuf *m; - struct mbuf **mp = &m; - if (ip6_fw_ctl_ptr == NULL) - { - return EINVAL; - } - error = (*ip6_fw_ctl_ptr)(optname, mp); - if (error == 0) - error = soopt_mcopyout(sopt, m); /* XXX */ - if (error == 0 && m) - m_freem(m); - } + error = ENOPROTOOPT; + else + error = (*ip6_fw_ctl_ptr)(sopt); break; default: