📄 ip_fw.c
字号:
/* * Copyright (c) 1996 Alex Nash * Copyright (c) 1993 Daniel Boulet * Copyright (c) 1994 Ugen J.S.Antsilevich * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * $Id: ip_fw.c,v 1.2 2003/01/03 18:09:25 joel Exp $ *//* * Implement IP packet firewall */#ifndef IPFIREWALL_MODULE#include "opt_ipfw.h"#endif#include <sys/param.h>#include <sys/systm.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/queue.h>#include <sys/kernel.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/sysctl.h>#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.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_VERBOSEstatic int fw_verbose = 1;#elsestatic int fw_verbose = 0;#endif#ifdef IPFIREWALL_VERBOSE_LIMITstatic int fw_verbose_limit = IPFIREWALL_VERBOSE_LIMIT;#elsestatic int fw_verbose_limit = 0;#endifLIST_HEAD (ip_fw_head, ip_fw_chain) ip_fw_chain;/* * ccj - No current need for firewall so have provided the MIB. */#if 0#ifdef SYSCTL_NODESYSCTL_NODE(_net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall");SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, debug, CTLFLAG_RW, &fw_debug, 0, "");SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw_verbose, 0, "");SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw_verbose_limit, 0, "");#endif#endif#define dprintf(a) if (!fw_debug); else printf a#define print_ip(a) printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\ (ntohl(a.s_addr)>>16)&0xFF,\ (ntohl(a.s_addr)>>8)&0xFF,\ (ntohl(a.s_addr))&0xFF);#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, u_short number));static int zero_entry __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((struct ip_fw *f, struct ip *ip, struct ifnet *rif, struct ifnet *oif));#ifdef IPFIREWALL_MODULEstatic ip_fw_chk_t *old_chk_ptr;static ip_fw_ctl_t *old_ctl_ptr;#endifstatic 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:";/* * Returns 1 if the port is matched by the vector, 0 otherwise */static inline int port_match(u_short *portptr, int nports, u_short port, int range_flag){ if (!nports) return 1; if (range_flag) { if (portptr[0] <= port && port <= portptr[1]) { return 1; } nports -= 2; portptr += 2; } while (nports-- > 0) { if (*portptr++ == port) { return 1; } } return 0;}static inttcpflg_match(struct tcphdr *tcp, struct ip_fw *f){ u_char flg_set, flg_clr; if ((f->fw_tcpf & IP_FW_TCPF_ESTAB) && (tcp->th_flags & (IP_FW_TCPF_RST | IP_FW_TCPF_ACK))) return 1; flg_set = tcp->th_flags & f->fw_tcpf; flg_clr = tcp->th_flags & f->fw_tcpnf; if (flg_set != f->fw_tcpf) return 0; if (flg_clr) return 0; return 1;}static inticmptype_match(struct icmp *icmp, struct ip_fw *f){ int type; if (!(f->fw_flg & IP_FW_F_ICMPBIT)) return(1); type = icmp->icmp_type; /* check for matching type in the bitmap */ if (type < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8 && (f->fw_icmptypes[type / (sizeof(unsigned) * 8)] & (1U << (type % (8 * sizeof(unsigned)))))) return(1); return(0); /* no match */}static intipopts_match(struct ip *ip, struct ip_fw *f){ register u_char *cp; int opt, optlen, cnt; u_char opts, nopts, nopts_sve; cp = (u_char *)(ip + 1); cnt = (ip->ip_hl << 2) - sizeof (struct ip); opts = f->fw_ipopt; nopts = nopts_sve = f->fw_ipnopt; for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[IPOPT_OPTVAL]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) optlen = 1; else { optlen = cp[IPOPT_OLEN]; if (optlen <= 0 || optlen > cnt) { return 0; /*XXX*/ } } switch (opt) { default: break; case IPOPT_LSRR: opts &= ~IP_FW_IPOPT_LSRR; nopts &= ~IP_FW_IPOPT_LSRR; break; case IPOPT_SSRR: opts &= ~IP_FW_IPOPT_SSRR; nopts &= ~IP_FW_IPOPT_SSRR; break; case IPOPT_RR: opts &= ~IP_FW_IPOPT_RR; nopts &= ~IP_FW_IPOPT_RR; break; case IPOPT_TS: opts &= ~IP_FW_IPOPT_TS; nopts &= ~IP_FW_IPOPT_TS; break; } if (opts == nopts) break; } if (opts == 0 && nopts == nopts_sve) return 1; else return 0;}static inline intiface_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_addrlist; ia; ia = ia->ifa_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 voidipfw_report(struct ip_fw *f, struct ip *ip, struct ifnet *rif, struct ifnet *oif){ 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); int count; count = f ? f->fw_pcnt : ++counter; if (fw_verbose_limit != 0 && count > fw_verbose_limit) return; /* 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(" "); switch (ip->ip_p) { case IPPROTO_TCP: printf("TCP "); print_ip(ip->ip_src); if ((ip->ip_off & IP_OFFMASK) == 0) printf(":%d ", ntohs(tcp->th_sport)); else printf(" "); print_ip(ip->ip_dst); if ((ip->ip_off & IP_OFFMASK) == 0) printf(":%d", ntohs(tcp->th_dport)); break; case IPPROTO_UDP: printf("UDP "); print_ip(ip->ip_src); if ((ip->ip_off & IP_OFFMASK) == 0) printf(":%d ", ntohs(udp->uh_sport)); else printf(" "); print_ip(ip->ip_dst); if ((ip->ip_off & IP_OFFMASK) == 0) printf(":%d", ntohs(udp->uh_dport)); break; case IPPROTO_ICMP: printf("ICMP:%u.%u ", icmp->icmp_type, icmp->icmp_code); print_ip(ip->ip_src); printf(" "); print_ip(ip->ip_dst); break; default: printf("P:%d ", ip->ip_p); print_ip(ip->ip_src); printf(" "); print_ip(ip->ip_dst); break; } 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 && count == fw_verbose_limit) printf("ipfw: limit reached on rule #%d\n", f ? f->fw_number : -1);}/* * Parameters: * * 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: * * 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 *oif, int ignport, struct mbuf **m){ struct ip_fw_chain *chain; struct ip_fw *rule = NULL; struct ip *ip = *pip; struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short offset = (ip->ip_off & IP_OFFMASK); u_short src_port, dst_port; /* * Go down the chain, looking for enlightment */ for (chain=ip_fw_chain.lh_first; chain; chain = chain->chain.le_next) { register struct ip_fw *const f = chain->rule; /* Check direction inbound */ if (!oif && !(f->fw_flg & IP_FW_F_IN)) continue; /* Check direction outbound */ if (oif && !(f->fw_flg & IP_FW_F_OUT)) continue; /* Fragments */ if ((f->fw_flg & IP_FW_F_FRAG) && !(ip->ip_off & IP_OFFMASK)) continue; /* If src-addr doesn't match, not this rule. */ if (((f->fw_flg & IP_FW_F_INVSRC) != 0) ^ ((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) ^ ((ip->ip_dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr)) continue; /* Interface check */ if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { struct ifnet *const iface = oif ? oif : rif; /* Backwards compatibility hack for "via" */ if (!iface || !iface_match(iface, &f->fw_in_if, f->fw_flg & IP_FW_F_OIFNAME)) continue; } 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; /* 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 && !ipopts_match(ip, f)) continue; /* Check protocol; if wildcard, match */ if (f->fw_prot == IPPROTO_IP) goto got_match; /* If different, don't match */ if (ip->ip_p != f->fw_prot) continue;#define PULLUP_TO(len) do { \ if ((*m)->m_len < (len) \ && (*m = m_pullup(*m, (len))) == 0) { \ goto bogusfrag; \ } \ *pip = ip = mtod(*m, struct ip *); \ offset = (ip->ip_off & IP_OFFMASK); \ } while (0) /* Protocol specific checks */ switch (ip->ip_p) { case IPPROTO_TCP: { struct tcphdr *tcp; if (offset == 1) /* cf. RFC 1858 */ goto bogusfrag; if (offset != 0) { /* * TCP flags and ports aren't available in this * packet -- if this rule specified either one, * we consider the rule a non-match. */ if (f->fw_nports != 0 || f->fw_tcpf != f->fw_tcpnf) continue; 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) { /* * Port specification is unavailable -- if this * rule specifies a port, we consider the rule * a non-match. */ if (f->fw_nports != 0) continue; 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], IP_FW_GETNSRCP(f), src_port, f->fw_flg & IP_FW_F_SRNG)) continue; 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; break; }#undef PULLUP_TObogusfrag: if (fw_verbose) ipfw_report(NULL, ip, rif, oif); goto dropit; }got_match: /* 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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -