diff options
Diffstat (limited to 'sys/netinet')
| -rw-r--r-- | sys/netinet/ip_divert.c | 23 | ||||
| -rw-r--r-- | sys/netinet/ip_fw.c | 581 | ||||
| -rw-r--r-- | sys/netinet/ip_fw.h | 141 | ||||
| -rw-r--r-- | sys/netinet/ip_input.c | 32 | ||||
| -rw-r--r-- | sys/netinet/ip_output.c | 29 |
5 files changed, 528 insertions, 278 deletions
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index dab18649f1a6..0c941a382c5c 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_divert.c,v 1.10 1997/05/26 03:33:48 peter Exp $ + * $Id: ip_divert.c,v 1.11 1997/06/01 15:58:44 peter Exp $ */ #include <sys/param.h> @@ -124,13 +124,20 @@ div_init(void) void div_input(struct mbuf *m, int hlen) { - register struct ip *ip = mtod(m, struct ip *); - register struct inpcb *inp; - register struct socket *sa; + struct ip *ip; + struct inpcb *inp; + struct socket *sa; /* Sanity check */ if (ip_divert_port == 0) - panic("div_input"); + panic("div_input: port is 0"); + + /* Assure header */ + if (m->m_len < sizeof(struct ip) && + (m = m_pullup(m, sizeof(struct ip))) == 0) { + return; + } + ip = mtod(m, struct ip *); /* Record divert port */ divsrc.sin_port = htons(ip_divert_port); @@ -145,6 +152,12 @@ div_input(struct mbuf *m, int hlen) if (hlen) { struct ifaddr *ifa; +#ifdef DIAGNOSTIC + /* Sanity check */ + if (!(m->m_flags & M_PKTHDR)) + panic("div_input: no pkt hdr"); +#endif + /* More fields affected by ip_input() */ HTONS(ip->ip_id); diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c index 71fa62c54742..0886a438122b 100644 --- a/sys/netinet/ip_fw.c +++ b/sys/netinet/ip_fw.c @@ -12,7 +12,7 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id: ip_fw.c,v 1.56 1997/04/06 11:09:03 dufault Exp $ + * $Id: ip_fw.c,v 1.3 1997/05/15 04:20:17 archie Exp $ */ /* @@ -37,11 +37,14 @@ #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> -#include <netinet/tcp.h> -#include <netinet/udp.h> #include <netinet/ip_var.h> #include <netinet/ip_icmp.h> #include <netinet/ip_fw.h> +#include <netinet/tcp.h> +#include <netinet/tcp_timer.h> +#include <netinet/tcp_var.h> +#include <netinet/tcpip.h> +#include <netinet/udp.h> static int fw_debug = 1; #ifdef IPFIREWALL_VERBOSE @@ -74,24 +77,25 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw_verbose_lim #define dprint_ip(a) if (!fw_debug); else print_ip(a) static int add_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl)); -static int del_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl)); +static int del_entry __P((struct ip_fw_head *chainptr, u_short number)); static int zero_entry __P((struct mbuf *m)); -static struct ip_fw * - check_ipfw_struct __P(( struct mbuf *m)); +static struct ip_fw *check_ipfw_struct __P((struct ip_fw *m)); +static struct ip_fw *check_ipfw_mbuf __P((struct mbuf *fw)); static int ipopts_match __P((struct ip *ip, struct ip_fw *f)); static int port_match __P((u_short *portptr, int nports, u_short port, int range_flag)); static int tcpflg_match __P((struct tcphdr *tcp, struct ip_fw *f)); static int icmptype_match __P((struct icmp * icmp, struct ip_fw * f)); -static void ipfw_report __P((char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif)); +static void ipfw_report __P((struct ip_fw *f, struct ip *ip, + struct ifnet *rif, struct ifnet *oif)); #ifdef IPFIREWALL_MODULE static ip_fw_chk_t *old_chk_ptr; static ip_fw_ctl_t *old_ctl_ptr; #endif -static int ip_fw_chk __P((struct ip **pip, int hlen, struct ifnet *rif, - int dirport, struct mbuf **m)); +static int ip_fw_chk __P((struct ip **pip, int hlen, + struct ifnet *oif, int ignport, struct mbuf **m)); static int ip_fw_ctl __P((int stage, struct mbuf **mm)); static char err_prefix[] = "ip_fw_ctl:"; @@ -214,17 +218,88 @@ ipopts_match(struct ip *ip, struct ip_fw *f) return 0; } +static inline int +iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname) +{ + /* Check by name or by IP address */ + if (byname) { + /* Check unit number (-1 is wildcard) */ + if (ifu->fu_via_if.unit != -1 + && ifp->if_unit != ifu->fu_via_if.unit) + return(0); + /* Check name */ + if (strncmp(ifp->if_name, ifu->fu_via_if.name, FW_IFNLEN)) + return(0); + return(1); + } else if (ifu->fu_via_ip.s_addr != 0) { /* Zero == wildcard */ + struct ifaddr *ia; + + for (ia = ifp->if_addrhead.tqh_first; + ia != NULL; ia = ia->ifa_link.tqe_next) { + if (ia->ifa_addr == NULL) + continue; + if (ia->ifa_addr->sa_family != AF_INET) + continue; + if (ifu->fu_via_ip.s_addr != ((struct sockaddr_in *) + (ia->ifa_addr))->sin_addr.s_addr) + continue; + return(1); + } + return(0); + } + return(1); +} + static void -ipfw_report(char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif) +ipfw_report(struct ip_fw *f, struct ip *ip, + struct ifnet *rif, struct ifnet *oif) { - struct tcphdr *tcp = (struct tcphdr *) ((u_long *) ip + ip->ip_hl); - struct udphdr *udp = (struct udphdr *) ((u_long *) ip + ip->ip_hl); - struct icmp *icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); - if (!fw_verbose) - return; - if (fw_verbose_limit != 0 && counter > fw_verbose_limit) + static int counter; + struct tcphdr *const tcp = (struct tcphdr *) ((u_long *) ip+ ip->ip_hl); + struct udphdr *const udp = (struct udphdr *) ((u_long *) ip+ ip->ip_hl); + struct icmp *const icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); + char *cmd; + int count; + + /* Print command name */ + printf("ipfw: %d ", f ? f->fw_number : -1); + if (!f) + printf("Refuse"); + else + switch (f->fw_flg & IP_FW_F_COMMAND) { + case IP_FW_F_DENY: + printf("Deny"); + break; + case IP_FW_F_REJECT: + if (f->fw_reject_code == IP_FW_REJECT_RST) + printf("Reset"); + else + printf("Unreach"); + break; + case IP_FW_F_ACCEPT: + printf("Accept"); + break; + case IP_FW_F_COUNT: + printf("Count"); + break; + case IP_FW_F_DIVERT: + printf("Divert %d", f->fw_divert_port); + break; + case IP_FW_F_TEE: + printf("Tee %d", f->fw_divert_port); + break; + case IP_FW_F_SKIPTO: + printf("SkipTo %d", f->fw_skipto_rule); + break; + default: + printf("UNKNOWN"); + break; + } + printf(" "); + + count = f ? f->fw_pcnt : ++counter; + if (fw_verbose_limit != 0 && count > fw_verbose_limit) return; - printf("ipfw: %d %s ",rule, txt); switch (ip->ip_p) { case IPPROTO_TCP: printf("TCP "); @@ -253,66 +328,57 @@ ipfw_report(char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif) print_ip(ip->ip_dst); break; } - printf(" via %s%d", rif->if_name, rif->if_unit); + if (oif) + printf(" out via %s%d", oif->if_name, oif->if_unit); + else if (rif) + printf(" in via %s%d", rif->if_name, rif->if_unit); if ((ip->ip_off & IP_OFFMASK)) printf(" Fragment = %d",ip->ip_off & IP_OFFMASK); printf("\n"); - if (fw_verbose_limit != 0 && counter == fw_verbose_limit) - printf("ipfw: limit reached on rule #%d\n", rule); + if (fw_verbose_limit != 0 && count == fw_verbose_limit) + printf("ipfw: limit reached on rule #%d\n", + f ? f->fw_number : -1); } /* - * We overload the "dirport" parameter: + * Parameters: * - * If dirport is negative, packet is outgoing; otherwise incoming. - * The low order 16 bits of dirport, if non-zero, indicate that - * we should ignore all ``divert <port>'' rules, where <port> is - * the low order 16 bits. + * ip Pointer to packet header (struct ip *) + * hlen Packet header length + * oif Outgoing interface, or NULL if packet is incoming + * ignport Ignore all divert/tee rules to this port (if non-zero) + * *m The packet; we set to NULL when/if we nuke it. * * Return value: * - * -1 The packet was denied/rejected and has been dropped - * 0 The packet is to be accepted; route normally - * <port> Divert the packet to divert <port>, if any socket - * is bound to it; otherwise just drop it. + * 0 The packet is to be accepted and routed normally OR + * the packet was denied/rejected and has been dropped; + * in the latter case, *m is equal to NULL upon return. + * port Divert the packet to port. */ static int ip_fw_chk(struct ip **pip, int hlen, - struct ifnet *rif, int dirport, struct mbuf **m) + struct ifnet *oif, int ignport, struct mbuf **m) { struct ip_fw_chain *chain; - register struct ip_fw *f = NULL; + struct ip_fw *rule; struct ip *ip = *pip; - struct tcphdr *tcp = (struct tcphdr *) ((u_long *) ip + ip->ip_hl); - struct udphdr *udp = (struct udphdr *) ((u_long *) ip + ip->ip_hl); - struct icmp *icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); - struct ifaddr *ia = NULL, *ia_p; - struct in_addr src, dst, ia_i; + struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short src_port, dst_port, offset; - src = ip->ip_src; - dst = ip->ip_dst; - - /* - * If we got interface from which packet came-store pointer to it's - * first adress - */ - if (rif != NULL) - ia = rif->if_addrhead.tqh_first; - /* * Go down the chain, looking for enlightment */ for (chain=ip_fw_chain.lh_first; chain; chain = chain->chain.le_next) { - f = chain->rule; + register struct ip_fw *const f = chain->rule; /* Check direction inbound */ - if (dirport >= 0 && !(f->fw_flg & IP_FW_F_IN)) + if (!oif && !(f->fw_flg & IP_FW_F_IN)) continue; /* Check direction outbound */ - if (dirport < 0 && !(f->fw_flg & IP_FW_F_OUT)) + if (oif && !(f->fw_flg & IP_FW_F_OUT)) continue; /* Fragments */ @@ -321,169 +387,234 @@ ip_fw_chk(struct ip **pip, int hlen, /* If src-addr doesn't match, not this rule. */ if ((f->fw_flg & IP_FW_F_INVSRC) != 0 - ^ (src.s_addr & f->fw_smsk.s_addr) != f->fw_src.s_addr) + ^ (ip->ip_src.s_addr & f->fw_smsk.s_addr) != f->fw_src.s_addr) continue; /* If dest-addr doesn't match, not this rule. */ if ((f->fw_flg & IP_FW_F_INVDST) != 0 - ^ (dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr) - continue; - - /* If a i/f name was specified, and we don't know */ - if ((f->fw_flg & IP_FW_F_IFNAME) && !rif) + ^ (ip->ip_dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr) continue; - /* If a i/f name was specified, check it */ - if ((f->fw_flg & IP_FW_F_IFNAME) && f->fw_via_name[0]) { + /* Interface check */ + if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { + struct ifnet *const iface = oif ? oif : rif; - /* Not same unit, don't match */ - if (!(f->fw_flg & IP_FW_F_IFUWILD) && rif->if_unit != f->fw_via_unit) + /* Backwards compatibility hack for "via" */ + if (!iface || !iface_match(iface, + &f->fw_in_if, f->fw_flg & IP_FW_F_OIFNAME)) continue; - - /* Not same name */ - if (strncmp(rif->if_name, f->fw_via_name, FW_IFNLEN)) + } else { + /* Check receive interface */ + if ((f->fw_flg & IP_FW_F_IIFACE) + && (!rif || !iface_match(rif, + &f->fw_in_if, f->fw_flg & IP_FW_F_IIFNAME))) continue; - } - - /* If a i/f addr was specified, check it */ - if (!(f->fw_flg & IP_FW_F_IFNAME) && f->fw_via_ip.s_addr) { - int match = 0; - - for (ia_p = ia; ia_p != NULL; - ia_p = ia_p->ifa_link.tqe_next) { - if ((ia_p->ifa_addr == NULL)) - continue; - if (ia_p->ifa_addr->sa_family != AF_INET) - continue; - ia_i.s_addr = - ((struct sockaddr_in *) - (ia_p->ifa_addr))->sin_addr.s_addr; - if (ia_i.s_addr != f->fw_via_ip.s_addr) - continue; - match = 1; - break; - } - if (!match) + /* Check outgoing interface */ + if ((f->fw_flg & IP_FW_F_OIFACE) + && (!oif || !iface_match(oif, + &f->fw_out_if, f->fw_flg & IP_FW_F_OIFNAME))) continue; } - /* - * Check IP options - */ - if (f->fw_ipopt != f->fw_ipnopt) - if (!ipopts_match(ip, f)) - continue; + /* Check IP options */ + if (f->fw_ipopt != f->fw_ipnopt && !ipopts_match(ip, f)) + continue; - /* If wildcard, match */ + /* Check protocol; if wildcard, match */ if (f->fw_prot == IPPROTO_IP) goto got_match; - /* If different, dont match */ + /* If different, don't match */ if (ip->ip_p != f->fw_prot) continue; + /* Get fragment offset (if any) */ + offset = (ip->ip_off & IP_OFFMASK); + +#define PULLUP_TO(len) do { \ + if ((*m)->m_len < (len) \ + && (*m = m_pullup(*m, (len))) == 0) { \ + goto bogusfrag; \ + } \ + *pip = ip = mtod(*m, struct ip *); \ + } while (0) + + /* Protocol specific checks */ switch (ip->ip_p) { case IPPROTO_TCP: - offset = ip->ip_off & IP_OFFMASK; - if (offset == 1) { - static int frag_counter = 0; - ++frag_counter; - ipfw_report("Refuse", -1, ip, frag_counter, rif); - m_freem(*m); - return -1; - } - if ((offset == 0) && - (f->fw_tcpf != f->fw_tcpnf) && - !tcpflg_match(tcp, f)) - continue; + { + struct tcphdr *tcp; + if (offset == 1) /* cf. RFC 1858 */ + goto bogusfrag; + if (offset != 0) /* Flags, ports aren't valid */ + break; + PULLUP_TO(hlen + 14); + tcp = (struct tcphdr *) ((u_long *)ip + ip->ip_hl); + if (f->fw_tcpf != f->fw_tcpnf && !tcpflg_match(tcp, f)) + continue; src_port = ntohs(tcp->th_sport); dst_port = ntohs(tcp->th_dport); goto check_ports; + } case IPPROTO_UDP: + { + struct udphdr *udp; + + if (offset != 0) /* Ports aren't valid */ + break; + PULLUP_TO(hlen + 4); + udp = (struct udphdr *) ((u_long *)ip + ip->ip_hl); src_port = ntohs(udp->uh_sport); dst_port = ntohs(udp->uh_dport); - check_ports: - if (!port_match(&f->fw_pts[0], f->fw_nsp, - src_port, f->fw_flg & IP_FW_F_SRNG)) + if (!port_match(&f->fw_pts[0], + IP_FW_GETNSRCP(f), src_port, + f->fw_flg & IP_FW_F_SRNG)) continue; - if (!port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, - dst_port, f->fw_flg & IP_FW_F_DRNG)) + if (!port_match(&f->fw_pts[IP_FW_GETNSRCP(f)], + IP_FW_GETNDSTP(f), dst_port, + f->fw_flg & IP_FW_F_DRNG)) continue; break; + } case IPPROTO_ICMP: + { + struct icmp *icmp; + + if (offset != 0) /* Type isn't valid */ + break; + PULLUP_TO(hlen + 2); + icmp = (struct icmp *) ((u_long *)ip + ip->ip_hl); if (!icmptype_match(icmp, f)) continue; - goto got_match; - - default: break; + } + +bogusfrag: + if (fw_verbose) + ipfw_report(NULL, ip, rif, oif); + goto dropit; } got_match: - f->fw_pcnt++; - f->fw_bcnt+=ip->ip_len; - f->timestamp = time.tv_sec; - if (f->fw_flg & IP_FW_F_PRN) { - if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_ACCEPT) { - ipfw_report("Allow", - f->fw_number, ip, f->fw_pcnt, rif); - } else if ((f->fw_flg & IP_FW_F_COMMAND) - == IP_FW_F_DIVERT) { - if (f->fw_divert_port != (dirport & 0xffff)) - ipfw_report("Divert", f->fw_number, - ip, f->fw_pcnt, rif); - } else if ((f->fw_flg & IP_FW_F_COMMAND) - == IP_FW_F_COUNT) { - ipfw_report("Count", - f->fw_number, ip, f->fw_pcnt, rif); - } else { - ipfw_report("Deny", - f->fw_number, ip, f->fw_pcnt, rif); - } + /* Ignore divert/tee rule if socket port is "ignport" */ + switch (f->fw_flg & IP_FW_F_COMMAND) { + case IP_FW_F_DIVERT: + case IP_FW_F_TEE: + if (f->fw_divert_port == ignport) + continue; /* ignore this rule */ + break; } + /* Update statistics */ + f->fw_pcnt += 1; + f->fw_bcnt += ip->ip_len; + f->timestamp = time.tv_sec; + + /* Log to console if desired */ + if ((f->fw_flg & IP_FW_F_PRN) && fw_verbose) + ipfw_report(f, ip, rif, oif); + /* Take appropriate action */ - if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_ACCEPT) { - return 0; - } else if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_COUNT) { + switch (f->fw_flg & IP_FW_F_COMMAND) { + case IP_FW_F_ACCEPT: + return(0); + case IP_FW_F_COUNT: continue; - } else if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DIVERT) { - if (f->fw_divert_port == (dirport & 0xffff)) - continue; /* ignore this rule */ - return (f->fw_divert_port); - } else - break; /* ie, deny/reject */ + case IP_FW_F_DIVERT: + return(f->fw_divert_port); + case IP_FW_F_TEE: + /* + * XXX someday tee packet here, but beware that you + * can't use m_copym() or m_copypacket() because + * the divert input routine modifies the mbuf + * (and these routines only increment reference + * counts in the case of mbuf clusters), so need + * to write custom routine. + */ + continue; + case IP_FW_F_SKIPTO: +#ifdef DIAGNOSTIC + while (chain->chain.le_next + && chain->chain.le_next->rule->fw_number + < f->fw_skipto_rule) +#else + while (chain->chain.le_next->rule->fw_number + < f->fw_skipto_rule) +#endif + chain = chain->chain.le_next; + continue; + } + + /* Deny/reject this packet using this rule */ + rule = f; + break; } #ifdef DIAGNOSTIC - if (!chain) /* rule 65535 should always be there */ + /* Rule 65535 should always be there and should always match */ + if (!chain) panic("ip_fw: chain"); - if (!f) - panic("ip_fw: entry"); #endif /* * At this point, we're going to drop the packet. - * Send an ICMP only if all of the following are true: + * Send a reject notice if all of the following are true: * - * - The packet is an incoming packet - * - The packet matched a deny rule + * - The packet matched a reject rule * - The packet is not an ICMP packet - * - The rule has the special ICMP reply flag set + * - The packet is not a multicast or broadcast packet */ - if (dirport >= 0 - && (f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DENY - && (ip->ip_p != IPPROTO_ICMP) - && (f->fw_flg & IP_FW_F_ICMPRPL)) { - icmp_error(*m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0L, 0); - return -1; - } - m_freem(*m); - return -1; + if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT + && ip->ip_p != IPPROTO_ICMP + && !((*m)->m_flags & (M_BCAST|M_MCAST)) + && !IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { + switch (rule->fw_reject_code) { + case IP_FW_REJECT_RST: + { + struct tcphdr *const tcp = + (struct tcphdr *) ((u_long *)ip + ip->ip_hl); + struct tcpiphdr ti; + + if (offset != 0 || (tcp->th_flags & TH_RST)) + break; + ti.ti_i = *((struct ipovly *) ip); + ti.ti_t = *tcp; + NTOHL(ti.ti_seq); + NTOHL(ti.ti_ack); + ti.ti_len = ip->ip_len - hlen - (ti.ti_off << 2); + if (tcp->th_flags & TH_ACK) { + tcp_respond(NULL, &ti, *m, + (tcp_seq)0, ntohl(tcp->th_ack), TH_RST); + } else { + if (tcp->th_flags & TH_SYN) + ti.ti_len++; + tcp_respond(NULL, &ti, *m, ti.ti_seq + + ti.ti_len, (tcp_seq)0, TH_RST|TH_ACK); + } + *m = NULL; + break; + } + default: /* Send an ICMP unreachable using code */ + icmp_error(*m, ICMP_UNREACH, + rule->fw_reject_code, 0L, 0); + *m = NULL; + break; + } + } + +dropit: + /* + * Finally, drop the packet. + */ + if (*m) { + m_freem(*m); + *m = NULL; + } + return(0); } static int @@ -518,6 +649,7 @@ add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) if (fwc) free(fwc, M_IPFW); if (ftmp) free(ftmp, M_IPFW); splx(s); + dprintf(("%s bad rule number\n", err_prefix)); return (EINVAL); } @@ -553,7 +685,7 @@ add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) } static int -del_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) +del_entry(struct ip_fw_head *chainptr, u_short number) { struct ip_fw_chain *fcp; int s; @@ -561,9 +693,9 @@ del_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) s = splnet(); fcp = chainptr->lh_first; - if (frwl->fw_number != (u_short)-1) { + if (number != (u_short)-1) { for (; fcp; fcp = fcp->chain.le_next) { - if (fcp->rule->fw_number == frwl->fw_number) { + if (fcp->rule->fw_number == number) { LIST_REMOVE(fcp, chain); splx(s); free(fcp->rule, M_IPFW); @@ -585,10 +717,9 @@ zero_entry(struct mbuf *m) int s; if (m) { - frwl = check_ipfw_struct(m); - - if (!frwl) + if (m->m_len != sizeof(struct ip_fw)) return(EINVAL); + frwl = mtod(m, struct ip_fw *); } else frwl = NULL; @@ -606,56 +737,80 @@ zero_entry(struct mbuf *m) } splx(s); +#if 0 if ( frwl ) printf("ipfw: Entry %d cleared.\n", frwl->fw_number); else printf("ipfw: Accounting cleared.\n"); +#endif return(0); } static struct ip_fw * -check_ipfw_struct(struct mbuf *m) +check_ipfw_mbuf(struct mbuf *m) { - struct ip_fw *frwl; - + /* Check length */ if (m->m_len != sizeof(struct ip_fw)) { dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, sizeof(struct ip_fw))); return (NULL); } - frwl = mtod(m, struct ip_fw *); + return(check_ipfw_struct(mtod(m, struct ip_fw *))); +} +static struct ip_fw * +check_ipfw_struct(struct ip_fw *frwl) +{ + /* Check for invalid flag bits */ if ((frwl->fw_flg & ~IP_FW_F_MASK) != 0) { dprintf(("%s undefined flag bits set (flags=%x)\n", err_prefix, frwl->fw_flg)); return (NULL); } - - /* If neither In nor Out, then both */ - if (!(frwl->fw_flg & (IP_FW_F_IN | IP_FW_F_OUT))) - frwl->fw_flg |= IP_FW_F_IN | IP_FW_F_OUT; - - if ((frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2) { + /* Must apply to incoming or outgoing (or both) */ + if (!(frwl->fw_flg & (IP_FW_F_IN | IP_FW_F_OUT))) { + dprintf(("%s neither in nor out\n", err_prefix)); + return (NULL); + } + /* Empty interface name is no good */ + if (((frwl->fw_flg & IP_FW_F_IIFNAME) + && !*frwl->fw_in_if.fu_via_if.name) + || ((frwl->fw_flg & IP_FW_F_OIFNAME) + && !*frwl->fw_out_if.fu_via_if.name)) { + dprintf(("%s empty interface name\n", err_prefix)); + return (NULL); + } + /* Sanity check interface matching */ + if ((frwl->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { + ; /* allow "via" backwards compatibility */ + } else if ((frwl->fw_flg & IP_FW_F_IN) + && (frwl->fw_flg & IP_FW_F_OIFACE)) { + dprintf(("%s outgoing interface check on incoming\n", + err_prefix)); + return (NULL); + } + /* Sanity check port ranges */ + if ((frwl->fw_flg & IP_FW_F_SRNG) && IP_FW_GETNSRCP(frwl) < 2) { dprintf(("%s src range set but n_src_p=%d\n", - err_prefix, frwl->fw_nsp)); + err_prefix, IP_FW_GETNSRCP(frwl))); return (NULL); } - if ((frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2) { + if ((frwl->fw_flg & IP_FW_F_DRNG) && IP_FW_GETNDSTP(frwl) < 2) { dprintf(("%s dst range set but n_dst_p=%d\n", - err_prefix, frwl->fw_ndp)); + err_prefix, IP_FW_GETNDSTP(frwl))); return (NULL); } - if (frwl->fw_nsp + frwl->fw_ndp > IP_FW_MAX_PORTS) { + if (IP_FW_GETNSRCP(frwl) + IP_FW_GETNDSTP(frwl) > IP_FW_MAX_PORTS) { dprintf(("%s too many ports (%d+%d)\n", - err_prefix, frwl->fw_nsp, frwl->fw_ndp)); + err_prefix, IP_FW_GETNSRCP(frwl), IP_FW_GETNDSTP(frwl))); return (NULL); } /* - * ICMP protocol doesn't use port range + * Protocols other than TCP/UDP don't use port range */ if ((frwl->fw_prot != IPPROTO_TCP) && (frwl->fw_prot != IPPROTO_UDP) && - (frwl->fw_nsp || frwl->fw_ndp)) { + (IP_FW_GETNSRCP(frwl) || IP_FW_GETNDSTP(frwl))) { dprintf(("%s port(s) specified for non TCP/UDP rule\n", err_prefix)); return(NULL); @@ -672,12 +827,34 @@ check_ipfw_struct(struct mbuf *m) return(NULL); } - /* Diverting to port zero is illegal */ - if ((frwl->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DIVERT - && frwl->fw_divert_port == 0) { - dprintf(("ip_fw_ctl: can't divert to port 0\n")); - return (NULL); + /* Check command specific stuff */ + switch (frwl->fw_flg & IP_FW_F_COMMAND) + { + case IP_FW_F_REJECT: + if (frwl->fw_reject_code >= 0x100 + && !(frwl->fw_prot == IPPROTO_TCP + && frwl->fw_reject_code == IP_FW_REJECT_RST)) { + dprintf(("%s unknown reject code\n", err_prefix)); + return(NULL); + } + break; + case IP_FW_F_DIVERT: /* Diverting to port zero is invalid */ + case IP_FW_F_TEE: + if (frwl->fw_divert_port == 0) { + dprintf(("%s can't divert to port 0\n", err_prefix)); + return (NULL); + } + break; + case IP_FW_F_DENY: + case IP_FW_F_ACCEPT: + case IP_FW_F_COUNT: + case IP_FW_F_SKIPTO: + break; + default: + dprintf(("%s invalid command\n", err_prefix)); + return(NULL); } + return frwl; } @@ -728,21 +905,31 @@ ip_fw_ctl(int stage, struct mbuf **mm) return (EINVAL); } - if (stage == IP_FW_ADD || stage == IP_FW_DEL) { - struct ip_fw *frwl = check_ipfw_struct(m); - - if (!frwl) { - if (m) (void)m_free(m); - return (EINVAL); - } + if (stage == IP_FW_ADD) { + struct ip_fw *frwl = check_ipfw_mbuf(m); - if (stage == IP_FW_ADD) - error = add_entry(&ip_fw_chain, frwl); + if (!frwl) + error = EINVAL; else - error = del_entry(&ip_fw_chain, frwl); + error = add_entry(&ip_fw_chain, frwl); if (m) (void)m_free(m); return error; } + if (stage == IP_FW_DEL) { + if (m->m_len != sizeof(struct ip_fw)) { + dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, + sizeof(struct ip_fw))); + error = EINVAL; + } else if (mtod(m, struct ip_fw *)->fw_number == (u_short)-1) { + dprintf(("%s can't delete rule 65535\n", err_prefix)); + error = EINVAL; + } else + error = del_entry(&ip_fw_chain, + mtod(m, struct ip_fw *)->fw_number); + if (m) (void)m_free(m); + return error; + } + dprintf(("%s unknown request %d\n", err_prefix, stage)); if (m) (void)m_free(m); return (EINVAL); @@ -760,8 +947,10 @@ ip_fw_init(void) bzero(&deny, sizeof deny); deny.fw_prot = IPPROTO_IP; deny.fw_number = (u_short)-1; - deny.fw_flg = IP_FW_F_IN | IP_FW_F_OUT; - add_entry(&ip_fw_chain, &deny); + deny.fw_flg |= IP_FW_F_DENY; + deny.fw_flg |= IP_FW_F_IN | IP_FW_F_OUT; + if (check_ipfw_struct(&deny) == NULL || add_entry(&ip_fw_chain, &deny)) + panic(__FUNCTION__); printf("IP packet filtering initialized, " #ifdef IPDIVERT diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index b3ae6ad4bd61..96d735766da5 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -11,51 +11,84 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id$ + * $Id: ip_fw.h,v 1.1 1997/05/09 17:46:45 archie Exp $ */ +#ifndef _IP_FW_H +#define _IP_FW_H + +/* + * This union structure identifies an interface, either explicitly + * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME + * and IP_FW_F_OIFNAME say how to interpret this structure. An + * interface unit number of -1 matches any unit number, while an + * IP address of 0.0.0.0 indicates matches any interface. + * + * The receive and transmit interfaces are only compared against the + * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE) + * is set. Note some packets lack a receive or transmit interface + * (in which case the missing "interface" never matches). + */ + +union ip_fw_if { + struct in_addr fu_via_ip; /* Specified by IP address */ + struct { /* Specified by interface name */ +#define FW_IFNLEN 6 /* To keep structure on 2^x boundary */ + char name[FW_IFNLEN]; + short unit; /* -1 means match any unit */ + } fu_via_if; +}; + /* * Format of an IP firewall descriptor * * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order. * fw_flg and fw_n*p are stored in host byte order (of course). * Port numbers are stored in HOST byte order. + * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108) */ -#ifndef _IP_FW_H -#define _IP_FW_H struct ip_fw { u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ struct in_addr fw_src, fw_dst; /* Source and destination IP addr */ struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */ - union { - struct in_addr fu_via_ip; /* Specified by IP address */ - struct { /* Specified by interface name */ -#define FW_IFNLEN 6 /* To keep structure on 2^x boundary */ - char fu_via_name[FW_IFNLEN]; - short fu_via_unit; - } fu_via_if; - } fu_via_un; -#define fw_via_ip fu_via_un.fu_via_ip -#define fw_via_name fu_via_un.fu_via_if.fu_via_name -#define fw_via_unit fu_via_un.fu_via_if.fu_via_unit - u_short fw_number; + u_short fw_number; /* Rule number */ u_short fw_flg; /* Flags word */ - u_short fw_nsp, fw_ndp; /* N'of src ports and # of dst ports */ - /* in ports array (dst ports follow */ - /* src ports; max of 10 ports in all; */ - /* count of 0 means match all ports) */ -#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */ - u_short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */ +#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */ + u_short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */ u_char fw_ipopt,fw_ipnopt; /* IP options set/unset */ u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ #define IP_FW_ICMPTYPES_DIM (256 / (sizeof(unsigned) * 8)) unsigned fw_icmptypes[IP_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */ - long timestamp; /* timestamp (tv_sec) of last match */ - u_short fw_divert_port; /* Divert port (options IPDIVERT) */ + long timestamp; /* timestamp (tv_sec) of last match */ + union ip_fw_if fw_in_if, fw_out_if; /* Incoming and outgoing interfaces */ + union { + u_short fu_divert_port; /* Divert/tee port (options IPDIVERT) */ + u_short fu_skipto_rule; /* SKIPTO command rule number */ + u_short fu_reject_code; /* REJECT response code */ + } fw_un; u_char fw_prot; /* IP protocol */ + u_char fw_nports; /* N'of src ports and # of dst ports */ + /* in ports array (dst ports follow */ + /* src ports; max of 10 ports in all; */ + /* count of 0 means match all ports) */ }; +#define IP_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) +#define IP_FW_SETNSRCP(rule, n) do { \ + (rule)->fw_nports &= ~0x0f; \ + (rule)->fw_nports |= (n); \ + } while (0) +#define IP_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) +#define IP_FW_SETNDSTP(rule, n) do { \ + (rule)->fw_nports &= ~0xf0; \ + (rule)->fw_nports |= (n) << 4;\ + } while (0) + +#define fw_divert_port fw_un.fu_divert_port +#define fw_skipto_rule fw_un.fu_skipto_rule +#define fw_reject_code fw_un.fu_reject_code + struct ip_fw_chain { LIST_ENTRY(ip_fw_chain) chain; struct ip_fw *rule; @@ -64,37 +97,55 @@ struct ip_fw_chain { /* * Values for "flags" field . */ -#define IP_FW_F_INVSRC 0x0001 /* Invert sense of src check */ -#define IP_FW_F_INVDST 0x0002 /* Invert sense of dst check */ -#define IP_FW_F_IN 0x0004 /* Inbound */ -#define IP_FW_F_OUT 0x0008 /* Outbound */ +#define IP_FW_F_IN 0x0001 /* Check inbound packets */ +#define IP_FW_F_OUT 0x0002 /* Check outbound packets */ +#define IP_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ +#define IP_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ + +#define IP_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ +#define IP_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IP_FW_F_REJECT 0x0010 /* Deny and send a response packet */ +#define IP_FW_F_ACCEPT 0x0020 /* This is an accept rule */ +#define IP_FW_F_COUNT 0x0030 /* This is a count rule */ +#define IP_FW_F_DIVERT 0x0040 /* This is a divert rule */ +#define IP_FW_F_TEE 0x0050 /* This is a tee rule */ +#define IP_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ + +#define IP_FW_F_PRN 0x0080 /* Print if this rule matches */ -#define IP_FW_F_COMMAND 0x0030 /* Mask for type of chain entry: */ -#define IP_FW_F_ACCEPT 0x0010 /* This is an accept rule */ -#define IP_FW_F_COUNT 0x0020 /* This is a count rule */ -#define IP_FW_F_DIVERT 0x0030 /* This is a divert rule */ -#define IP_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IP_FW_F_SRNG 0x0100 /* The first two src ports are a min * + * and max range (stored in host byte * + * order). */ -#define IP_FW_F_PRN 0x0040 /* Print if this rule matches */ -#define IP_FW_F_ICMPRPL 0x0080 /* Send back icmp unreachable packet */ +#define IP_FW_F_DRNG 0x0200 /* The first two dst ports are a min * + * and max range (stored in host byte * + * order). */ -#define IP_FW_F_SRNG 0x0100 /* The first two src ports are a min * - * and max range (stored in host byte * - * order). */ +#define IP_FW_F_IIFNAME 0x0400 /* In interface by name/unit (not IP) */ +#define IP_FW_F_OIFNAME 0x0800 /* Out interface by name/unit (not IP) */ -#define IP_FW_F_DRNG 0x0200 /* The first two dst ports are a min * - * and max range (stored in host byte * - * order). */ +#define IP_FW_F_INVSRC 0x1000 /* Invert sense of src check */ +#define IP_FW_F_INVDST 0x2000 /* Invert sense of dst check */ -#define IP_FW_F_IFNAME 0x0400 /* Use interface name/unit (not IP) */ +#define IP_FW_F_FRAG 0x4000 /* Fragment */ -#define IP_FW_F_FRAG 0x0800 /* Fragment */ +#define IP_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ -#define IP_FW_F_ICMPBIT 0x1000 /* ICMP type bitmap is valid */ +#define IP_FW_F_MASK 0xFFFF /* All possible flag bits mask */ -#define IP_FW_F_IFUWILD 0x2000 /* Match all interface units */ +/* + * For backwards compatibility with rules specifying "via iface" but + * not restricted to only "in" or "out" packets, we define this combination + * of bits to represent this configuration. + */ + +#define IF_FW_F_VIAHACK (IP_FW_F_IN|IP_FW_F_OUT|IP_FW_F_IIFACE|IP_FW_F_OIFACE) -#define IP_FW_F_MASK 0x3FFF /* All possible flag bits mask */ +/* + * Definitions for REJECT response codes. + * Values less than 256 correspond to ICMP unreachable codes. + */ +#define IP_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ /* * Definitions for IP option names. diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 538d1c859503..903adaf19f34 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 - * $Id: ip_input.c,v 1.61 1997/04/03 10:47:10 darrenr Exp $ + * $Id: ip_input.c,v 1.62 1997/05/11 18:05:37 tegge Exp $ * $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $ */ @@ -334,25 +334,24 @@ tooshort: #endif #ifdef COMPAT_IPFW if (ip_fw_chk_ptr) { - int action; - #ifdef IPDIVERT - action = (*ip_fw_chk_ptr)(&ip, hlen, - m->m_pkthdr.rcvif, ip_divert_ignore, &m); + u_short port; + + port = (*ip_fw_chk_ptr)(&ip, hlen, NULL, ip_divert_ignore, &m); ip_divert_ignore = 0; -#else - action = (*ip_fw_chk_ptr)(&ip, hlen, m->m_pkthdr.rcvif, 0, &m); -#endif - if (action == -1) - return; - if (action != 0) { -#ifdef IPDIVERT - frag_divert_port = action; + if (port) { /* Divert packet */ + frag_divert_port = port; goto ours; + } #else - goto bad; /* ipfw said divert but we can't */ -#endif + /* If ipfw says divert, we have to just drop packet */ + if ((*ip_fw_chk_ptr)(&ip, hlen, NULL, 0, &m)) { + m_freem(m); + m = NULL; } +#endif + if (!m) + return; } if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN)) @@ -521,9 +520,10 @@ found: #ifdef IPDIVERT /* - * Divert packets here to the divert protocol if required + * Divert reassembled packets to the divert protocol if required */ if (frag_divert_port) { + ipstat.ips_delivered++; ip_divert_port = frag_divert_port; frag_divert_port = 0; (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen); diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index defc06a4d430..4401f6bf8a94 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 - * $Id: ip_output.c,v 1.55 1997/04/27 20:01:07 wollman Exp $ + * $Id: ip_output.c,v 1.56 1997/05/06 21:22:04 fenner Exp $ */ #define _IP_VHL @@ -357,7 +357,7 @@ sendit: /* * IpHack's section. * - Xlate: translate packet's addr/port (NAT). - * - Firewall: deny/allow + * - Firewall: deny/allow/etc. * - Wrap: fake packet's addr/port <unimpl.> * - Encapsulate: put it in another IP and send out. <unimp.> */ @@ -372,27 +372,24 @@ sendit: * Check with the firewall... */ if (ip_fw_chk_ptr) { - int action; - #ifdef IPDIVERT - action = (*ip_fw_chk_ptr)(&ip, - hlen, ifp, (~0 << 16) | ip_divert_ignore, &m); + ip_divert_port = (*ip_fw_chk_ptr)(&ip, + hlen, ifp, ip_divert_ignore, &m); ip_divert_ignore = 0; -#else - action = (*ip_fw_chk_ptr)(&ip, hlen, ifp, (~0 << 16), &m); -#endif - if (action == -1) { - error = EACCES; /* XXX is this appropriate? */ - goto done; - } else if (action != 0) { -#ifdef IPDIVERT - ip_divert_port = action; /* divert to port */ + if (ip_divert_port) { /* Divert packet */ (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0); goto done; + } #else - m_freem(m); /* ipfw says divert, but we can't */ + /* If ipfw says divert, we have to just drop packet */ + if ((*ip_fw_chk_ptr)(&ip, hlen, ifp, 0, &m)) { + m_freem(m); goto done; + } #endif + if (!m) { + error = EACCES; + goto done; } } #endif /* COMPAT_IPFW */ |
