static int udp_match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, int *hotdrop) { struct udphdr _udph, *uh; const struct xt_udp *udpinfo = matchinfo; /* Must not be a fragment. */ if (offset) return 0; uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); if (uh == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ duprintf("Dropping evil UDP tinygram.\n"); *hotdrop = 1; return 0; } return port_match(udpinfo->spts[0], udpinfo->spts[1], ntohs(uh->source), !!(udpinfo->invflags & XT_UDP_INV_SRCPT)) && port_match(udpinfo->dpts[0], udpinfo->dpts[1], ntohs(uh->dest), !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); }
static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct udphdr *uh; struct udphdr _udph; const struct xt_udp *udpinfo = par->matchinfo; /* Must not be a fragment. */ if (par->fragoff != 0) return false; uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph); if (uh == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ pr_debug("Dropping evil UDP tinygram.\n"); par->hotdrop = true; return false; } return port_match(udpinfo->spts[0], udpinfo->spts[1], ntohs(uh->source), !!(udpinfo->invflags & XT_UDP_INV_SRCPT)) && port_match(udpinfo->dpts[0], udpinfo->dpts[1], ntohs(uh->dest), !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); }
static bool tcp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct tcphdr *th; struct tcphdr _tcph; const struct xt_tcp *tcpinfo = par->matchinfo; if (par->fragoff != 0) { /* To quote Alan: Don't allow a fragment of TCP 8 bytes in. Nobody normal causes this. Its a cracker trying to break in by doing a flag overwrite to pass the direction checks. */ if (par->fragoff == 1) { pr_debug("Dropping evil TCP offset=1 frag.\n"); *par->hotdrop = true; } /* Must not be a fragment. */ return false; } #define FWINVTCP(bool, invflg) ((bool) ^ !!(tcpinfo->invflags & (invflg))) th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); if (th == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ pr_debug("Dropping evil TCP offset=0 tinygram.\n"); *par->hotdrop = true; return false; } if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1], ntohs(th->source), !!(tcpinfo->invflags & XT_TCP_INV_SRCPT))) return false; if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], ntohs(th->dest), !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) return false; if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp, XT_TCP_INV_FLAGS)) return false; if (tcpinfo->option) { if (th->doff * 4 < sizeof(_tcph)) { *par->hotdrop = true; return false; } if (!tcp_find_option(tcpinfo->option, skb, par->thoff, th->doff*4 - sizeof(_tcph), tcpinfo->invflags & XT_TCP_INV_OPTION, par->hotdrop)) return false; } return true; }
static void deps_diff_inv (struct hs *hs, uint32_t port, const struct deps *deps, const struct tf *tf) { for (int i = 0; i < deps->n; i++) { const struct dep *dep = &deps->deps[i]; if (dep->port > 0 && dep->port != port) continue; if (dep->port < 0 && !port_match (port, dep->port, tf)) continue; hs_diff (hs, DATA_ARR (dep->match)); } }
static void deps_diff (struct hs *hs, uint32_t port, const struct deps *deps, const struct tf *tf, const uint32_t *app, int napp) { for (int i = 0; i < deps->n; i++) { const struct dep *dep = &deps->deps[i]; if (app && !int_find (dep->rule, app, napp)) continue; if (dep->port > 0 && dep->port != port) continue; if (dep->port < 0 && !port_match (port, dep->port, tf)) continue; hs_diff (hs, DATA_ARR (dep->match)); } }
static struct user_cap *user_db_get_pref( const struct passwd *pw, in_port_t lport, in_port_t fport, struct sockaddr_storage *laddr, struct sockaddr_storage *faddr) { list_t *cap_list; list_t *cur; cap_list = user_db_get_pref_list(pw); for (cur = cap_list ; cur != NULL ; cur = cur->next) { struct user_cap *cur_cap = cur->data; if (port_match(lport, cur_cap->lport) == false) continue; if (port_match(fport, cur_cap->fport) == false) continue; if (addr_match(laddr, cur_cap->src) == false) continue; if (addr_match(faddr, cur_cap->dest) == false) continue; /* ** Don't let list_destroy destroy this one. */ cur->data = NULL; list_destroy(cap_list, user_db_cap_destroy_data); return (cur_cap); } list_destroy(cap_list, user_db_cap_destroy_data); return (NULL); }
struct list_res rule_inv_apply (const struct tf *tf, const struct rule *r, const struct res *in, bool append) { /* Given a rule `r` in a tf `tf`, apply the inverse of `r` on the input (headerspace,port) `in`. */ struct list_res res = {0}; // prune cases where rule outport doesn't include the current port if (r->out > 0 && r->out != in->port) return res; if (r->out < 0 && !port_match(in->port, r->out, tf)) return res; if (!r->out) return res; // set up inverse match and rewrite arrays array_t *inv_rw=0, *inv_mat=0; if (r->mask) { // rewrite rule inv_mat = rule_set_inv_mat (r, in->hs.len); inv_rw = rule_set_inv_rw (r, in->hs.len); } else { // fwding and topology rules if (r->match) inv_mat = array_copy (DATA_ARR (r->match), in->hs.len); } struct hs hs; if (!r->match) hs_copy (&hs, &in->hs); // topology rule else { // fwding and rewrite rules if (!hs_isect_arr (&hs, &in->hs, inv_mat)) return res; if (r->mask) hs_rewrite (&hs, DATA_ARR (r->mask), inv_rw); } // there is a new hs result corresponding to each rule inport bool used_hs = port_append_res (&res, r, tf, in, r->in, append, &hs, true); if (inv_rw) array_free (inv_rw); if (inv_mat) array_free (inv_mat); if (!used_hs) hs_destroy (&hs); return res; }
static int match_cookie(ParsedURL *pu, struct cookie *cookie, char *domainname) { if (!domainname) return 0; if (!domain_match(domainname, cookie->domain->ptr)) return 0; if (strncmp(cookie->path->ptr, pu->file, cookie->path->length) != 0) return 0; #ifdef USE_SSL if (cookie->flag & COO_SECURE && pu->scheme != SCM_HTTPS) return 0; #else /* not USE_SSL */ if (cookie->flag & COO_SECURE) return 0; #endif /* not USE_SSL */ if (cookie->portl && !port_match(cookie->portl, pu->port)) return 0; return 1; }
struct list_res tf_apply (const struct tf *tf, const struct res *in, bool append) { assert (in->hs.len == data_arrs_len); uint32_t app[MAX_APP]; int napp = 0; struct list_res res = {0}; const struct port_map_elem *rules = rule_get (tf, in->port); if (!rules) return res; if (rules->start != UINT32_MAX) { for (uint32_t cur = rules->start; cur < tf->nrules; cur++) { const struct rule *r = &tf->rules[cur]; assert (r->in > 0); if (r->in != in->port) break; struct list_res tmp; tmp = rule_apply (r, tf, in, append, app, &napp); list_concat (&res, &tmp); } } /* Check all rules with multiple ports. */ for (int i = 0; i < tf->nrules; i++) { const struct rule *r = &tf->rules[i]; if (r->in >= 0) break; if (!port_match (in->port, r->in, tf)) continue; struct list_res tmp; tmp = rule_apply (r, tf, in, append, app, &napp); list_concat (&res, &tmp); } return res; }
int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_fw *chain, int policy, int mode) { struct ip_fw *f; struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl); struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl); struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl); __u32 src, dst; __u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF; unsigned short f_prt=0, prt; char notcpsyn=0, notcpack=0, match; unsigned short offset; int answer; unsigned char tosand, tosxor; /* * If the chain is empty follow policy. The BSD one * accepts anything giving you a time window while * flushing and rebuilding the tables. */ src = ip->saddr; dst = ip->daddr; /* * This way we handle fragmented packets. * we ignore all fragments but the first one * so the whole packet can't be reassembled. * This way we relay on the full info which * stored only in first packet. * * Note that this theoretically allows partial packet * spoofing. Not very dangerous but paranoid people may * wish to play with this. It also allows the so called * "fragment bomb" denial of service attack on some types * of system. */ offset = ntohs(ip->frag_off) & IP_OFFSET; /* * Don't allow a fragment of TCP 8 bytes in. Nobody * normal causes this. Its a cracker trying to break * in by doing a flag overwrite to pass the direction * checks. */ if (offset == 1 && ip->protocol == IPPROTO_TCP) return FW_BLOCK; if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) && (ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP || ip->protocol == IPPROTO_ICMP)) return FW_ACCEPT; /* * Header fragment for TCP is too small to check the bits. */ if(ip->protocol==IPPROTO_TCP && (ip->ihl<<2)+16 > ntohs(ip->tot_len)) return FW_BLOCK; /* * Too short. * * But only too short for a packet with ports... */ else if((ntohs(ip->tot_len)<8+(ip->ihl<<2))&&(ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)) return FW_BLOCK; src = ip->saddr; dst = ip->daddr; /* * If we got interface from which packet came * we can use the address directly. This is unlike * 4.4BSD derived systems that have an address chain * per device. We have a device per address with dummy * devices instead. */ dprintf1("Packet "); switch(ip->protocol) { case IPPROTO_TCP: dprintf1("TCP "); /* ports stay 0xFFFF if it is not the first fragment */ if (!offset) { src_port=ntohs(tcp->source); dst_port=ntohs(tcp->dest); if(!tcp->ack && !tcp->rst) /* We do NOT have ACK, value TRUE */ notcpack=1; if(!tcp->syn || !notcpack) /* We do NOT have SYN, value TRUE */ notcpsyn=1; } prt=IP_FW_F_TCP; break; case IPPROTO_UDP: dprintf1("UDP "); /* ports stay 0xFFFF if it is not the first fragment */ if (!offset) { src_port=ntohs(udp->source); dst_port=ntohs(udp->dest); } prt=IP_FW_F_UDP; break; case IPPROTO_ICMP: /* icmp_type stays 255 if it is not the first fragment */ if (!offset) icmp_type=(__u16)(icmp->type); dprintf2("ICMP:%d ",icmp_type); prt=IP_FW_F_ICMP; break; default: dprintf2("p=%d ",ip->protocol); prt=IP_FW_F_ALL; break; } #ifdef DEBUG_IP_FIREWALL dprint_ip(ip->saddr); if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) /* This will print 65535 when it is not the first fragment! */ dprintf2(":%d ", src_port); dprint_ip(ip->daddr); if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) /* This will print 65535 when it is not the first fragment! */ dprintf2(":%d ",dst_port); dprintf1("\n"); #endif for (f=chain;f;f=f->fw_next) { /* * This is a bit simpler as we don't have to walk * an interface chain as you do in BSD - same logic * however. */ /* * Match can become 0x01 (a "normal" match was found), * 0x02 (a reverse match was found), and 0x03 (the * IP addresses match in both directions). * Now we know in which direction(s) we should look * for a match for the TCP/UDP ports. Both directions * might match (e.g., when both addresses are on the * same network for which an address/mask is given), but * the ports might only match in one direction. * This was obviously wrong in the original BSD code. */ match = 0x00; if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr && (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) /* normal direction */ match |= 0x01; if ((f->fw_flg & IP_FW_F_BIDIR) && (dst&f->fw_smsk.s_addr)==f->fw_src.s_addr && (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) /* reverse direction */ match |= 0x02; if (!match) continue; /* * Look for a VIA address match */ if(f->fw_via.s_addr && rif) { if(rif->pa_addr!=f->fw_via.s_addr) continue; /* Mismatch */ } /* * Look for a VIA device match */ if(f->fw_viadev) { if(rif!=f->fw_viadev) continue; /* Mismatch */ } /* * Ok the chain addresses match. */ #ifdef CONFIG_IP_ACCT /* * See if we're in accounting mode and only want to * count incoming or outgoing packets. */ if (mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT) && ((mode == IP_FW_MODE_ACCT_IN && f->fw_flg&IP_FW_F_ACCTOUT) || (mode == IP_FW_MODE_ACCT_OUT && f->fw_flg&IP_FW_F_ACCTIN))) continue; #endif /* * For all non-TCP packets and/or non-first fragments, * notcpsyn and notcpack will always be FALSE, * so the IP_FW_F_TCPSYN and IP_FW_F_TCPACK flags * are actually ignored for these packets. */ if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn) continue; if((f->fw_flg&IP_FW_F_TCPACK) && notcpack) continue; f_prt=f->fw_flg&IP_FW_F_KIND; if (f_prt!=IP_FW_F_ALL) { /* * Specific firewall - packet's protocol * must match firewall's. */ if(prt!=f_prt) continue; if((prt==IP_FW_F_ICMP && ! port_match(&f->fw_pts[0], f->fw_nsp, icmp_type,f->fw_flg&IP_FW_F_SRNG)) || !(prt==IP_FW_F_ICMP || ((match & 0x01) && port_match(&f->fw_pts[0], f->fw_nsp, src_port, f->fw_flg&IP_FW_F_SRNG) && port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port, f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) && port_match(&f->fw_pts[0], f->fw_nsp, dst_port, f->fw_flg&IP_FW_F_SRNG) && port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port, f->fw_flg&IP_FW_F_DRNG)))) { continue; } } #ifdef CONFIG_IP_FIREWALL_VERBOSE /* * VERY ugly piece of code which actually * makes kernel printf for matching packets... */ if (f->fw_flg & IP_FW_F_PRN) { __u32 *opt = (__u32 *) (ip + 1); int opti; if(mode == IP_FW_MODE_ACCT_IN) printk(KERN_INFO "IP acct in "); else if(mode == IP_FW_MODE_ACCT_OUT) printk(KERN_INFO "IP acct out "); else { if(chain == ip_fw_fwd_chain) printk(KERN_INFO "IP fw-fwd "); else if(chain == ip_fw_in_chain) printk(KERN_INFO "IP fw-in "); else printk(KERN_INFO "IP fw-out "); if(f->fw_flg&IP_FW_F_ACCEPT) { if(f->fw_flg&IP_FW_F_REDIR) printk("acc/r%d ", f->fw_pts[f->fw_nsp+f->fw_ndp]); else if(f->fw_flg&IP_FW_F_MASQ) printk("acc/masq "); else printk("acc "); } else if(f->fw_flg&IP_FW_F_ICMPRPL) printk("rej "); else printk("deny "); } printk(rif ? rif->name : "-"); switch(ip->protocol) { case IPPROTO_TCP: printk(" TCP "); break; case IPPROTO_UDP: printk(" UDP "); break; case IPPROTO_ICMP: printk(" ICMP/%d ", icmp_type); break; default: printk(" PROTO=%d ", ip->protocol); break; } print_ip(ip->saddr); if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP) printk(":%hu", src_port); printk(" "); print_ip(ip->daddr); if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP) printk(":%hu", dst_port); printk(" L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu", ntohs(ip->tot_len), ip->tos, ntohs(ip->id), ntohs(ip->frag_off), ip->ttl); for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++) printk(" O=0x%8.8X", *opt++); printk("\n"); } #endif if (mode != IP_FW_MODE_CHK) { f->fw_bcnt+=ntohs(ip->tot_len); f->fw_pcnt++; } if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) break; } /* Loop */ if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) { /* * We rely on policy defined in the rejecting entry or, if no match * was found, we rely on the general policy variable for this type * of firewall. */ if (f!=NULL) { policy=f->fw_flg; tosand=f->fw_tosand; tosxor=f->fw_tosxor; } else { tosand=0xFF; tosxor=0x00; } if (policy&IP_FW_F_ACCEPT) { /* Adjust priority and recompute checksum */ __u8 old_tos = ip->tos; ip->tos = (old_tos & tosand) ^ tosxor; if (ip->tos != old_tos) ip_send_check(ip); #ifdef CONFIG_IP_TRANSPARENT_PROXY if (policy&IP_FW_F_REDIR) { if (redirport) if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) { /* Wildcard redirection. * Note that redirport will become * 0xFFFF for non-TCP/UDP packets. */ *redirport = htons(dst_port); } answer = FW_REDIRECT; } else #endif #ifdef CONFIG_IP_MASQUERADE if (policy&IP_FW_F_MASQ) answer = FW_MASQUERADE; else #endif answer = FW_ACCEPT; } else if(policy&IP_FW_F_ICMPRPL) answer = FW_REJECT; else answer = FW_BLOCK; return answer; } else /* we're doing accounting, always ok */ return 0; }
static int tcp_match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, int *hotdrop) { struct tcphdr _tcph, *th; const struct xt_tcp *tcpinfo = matchinfo; if (offset) { /* To quote Alan: Don't allow a fragment of TCP 8 bytes in. Nobody normal causes this. Its a cracker trying to break in by doing a flag overwrite to pass the direction checks. */ if (offset == 1) { duprintf("Dropping evil TCP offset=1 frag.\n"); *hotdrop = 1; } /* Must not be a fragment. */ return 0; } #define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg)) th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); if (th == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ duprintf("Dropping evil TCP offset=0 tinygram.\n"); *hotdrop = 1; return 0; } if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1], ntohs(th->source), !!(tcpinfo->invflags & XT_TCP_INV_SRCPT))) return 0; if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], ntohs(th->dest), !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) return 0; if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp, XT_TCP_INV_FLAGS)) return 0; if (tcpinfo->option) { if (th->doff * 4 < sizeof(_tcph)) { *hotdrop = 1; return 0; } if (!tcp_find_option(tcpinfo->option, skb, protoff, th->doff*4 - sizeof(_tcph), tcpinfo->invflags & XT_TCP_INV_OPTION, hotdrop)) return 0; } return 1; }
static int ip6_fw_accept_trans(struct ip6_fw_rule *rl, struct fl_acc_args *args) { int res = FLOWR_NODECISION; int proto = 0; int sport = 0; int dport = 0; switch (args->type) { case FL_ARG_FORWARD: { struct sk_buff *skb = args->fl_u.skb; struct ipv6hdr *hdr = skb->nh.ipv6h; int len; len = skb->len - sizeof(struct ipv6hdr); proto = hdr->nexthdr; switch (proto) { case IPPROTO_TCP: { struct tcphdr *th; if (len < sizeof(struct tcphdr)) { res = FLOWR_ERROR; goto out; } th = (struct tcphdr *)(hdr + 1); sport = th->source; dport = th->dest; break; } case IPPROTO_UDP: { struct udphdr *uh; if (len < sizeof(struct udphdr)) { res = FLOWR_ERROR; goto out; } uh = (struct udphdr *)(hdr + 1); sport = uh->source; dport = uh->dest; break; } default: goto out; }; break; } case FL_ARG_ORIGIN: { proto = args->fl_u.fl_o.flow->proto; if (proto == IPPROTO_ICMPV6) { goto out; } else { sport = args->fl_u.fl_o.flow->uli_u.ports.sport; dport = args->fl_u.fl_o.flow->uli_u.ports.dport; } break; } if (proto == rl->info.proto && port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) && port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) { if (rl->policy & IP6_FW_REJECT) res = FLOWR_SELECT; else res = FLOWR_CLEAR; } default: #if IP6_FW_DEBUG >= 1 printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n"); #endif goto out; }; out: return res; }
/* Returns whether matches rule or not. */ static int ip_rule_match(struct ip_fwkernel *f, const char *ifname, struct iphdr *ip, char tcpsyn, __u16 src_port, __u16 dst_port, char isfrag) { #define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg)) /* * This is a bit simpler as we don't have to walk * an interface chain as you do in BSD - same logic * however. */ if (FWINV((ip->saddr&f->ipfw.fw_smsk.s_addr) != f->ipfw.fw_src.s_addr, IP_FW_INV_SRCIP) || FWINV((ip->daddr&f->ipfw.fw_dmsk.s_addr)!=f->ipfw.fw_dst.s_addr, IP_FW_INV_DSTIP)) { dprintf("Source or dest mismatch.\n"); dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr, f->ipfw.fw_smsk.s_addr, f->ipfw.fw_src.s_addr, f->ipfw.fw_invflg & IP_FW_INV_SRCIP ? " (INV)" : ""); dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr, f->ipfw.fw_dmsk.s_addr, f->ipfw.fw_dst.s_addr, f->ipfw.fw_invflg & IP_FW_INV_DSTIP ? " (INV)" : ""); return 0; } /* * Look for a VIA device match */ if (f->ipfw.fw_flg & IP_FW_F_WILDIF) { if (FWINV(strncmp(ifname, f->ipfw.fw_vianame, strlen(f->ipfw.fw_vianame)) != 0, IP_FW_INV_VIA)) { dprintf("Wildcard interface mismatch.%s\n", f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : ""); return 0; /* Mismatch */ } } else if (FWINV(strcmp(ifname, f->ipfw.fw_vianame) != 0, IP_FW_INV_VIA)) { dprintf("Interface name does not match.%s\n", f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : ""); return 0; /* Mismatch */ } /* * Ok the chain addresses match. */ /* If we have a fragment rule but the packet is not a fragment * the we return zero */ if (FWINV((f->ipfw.fw_flg&IP_FW_F_FRAG) && !isfrag, IP_FW_INV_FRAG)) { dprintf("Fragment rule but not fragment.%s\n", f->ipfw.fw_invflg & IP_FW_INV_FRAG ? " (INV)" : ""); return 0; } /* Fragment NEVER passes a SYN test, even an inverted one. */ if (FWINV((f->ipfw.fw_flg&IP_FW_F_TCPSYN) && !tcpsyn, IP_FW_INV_SYN) || (isfrag && (f->ipfw.fw_flg&IP_FW_F_TCPSYN))) { dprintf("Rule requires SYN and packet has no SYN.%s\n", f->ipfw.fw_invflg & IP_FW_INV_SYN ? " (INV)" : ""); return 0; } if (f->ipfw.fw_proto) { /* * Specific firewall - packet's protocol * must match firewall's. */ if (FWINV(ip->protocol!=f->ipfw.fw_proto, IP_FW_INV_PROTO)) { dprintf("Packet protocol %hi does not match %hi.%s\n", ip->protocol, f->ipfw.fw_proto, f->ipfw.fw_invflg&IP_FW_INV_PROTO ? " (INV)":""); return 0; } /* For non TCP/UDP/ICMP, port range is max anyway. */ if (!port_match(f->ipfw.fw_spts[0], f->ipfw.fw_spts[1], src_port, isfrag, !!(f->ipfw.fw_invflg&IP_FW_INV_SRCPT)) || !port_match(f->ipfw.fw_dpts[0], f->ipfw.fw_dpts[1], dst_port, isfrag, !!(f->ipfw.fw_invflg &IP_FW_INV_DSTPT))) { dprintf("Port match failed.\n"); return 0; } } dprintf("Match succeeded.\n"); return 1; }
int add_cookie(ParsedURL *pu, Str name, Str value, time_t expires, Str domain, Str path, int flag, Str comment, int version, Str port, Str commentURL) { struct cookie *p; char *domainname = (version == 0) ? FQDN(pu->host) : pu->host; Str odomain = domain, opath = path; struct portlist *portlist = NULL; int use_security = !(flag & COO_OVERRIDE); #define COOKIE_ERROR(err) if(!((err) & COO_OVERRIDE_OK) || use_security) return (err) #ifdef DEBUG fprintf(stderr, "host: [%s, %s] %d\n", pu->host, pu->file, flag); fprintf(stderr, "cookie: [%s=%s]\n", name->ptr, value->ptr); fprintf(stderr, "expires: [%s]\n", asctime(gmtime(&expires))); if (domain) fprintf(stderr, "domain: [%s]\n", domain->ptr); if (path) fprintf(stderr, "path: [%s]\n", path->ptr); fprintf(stderr, "version: [%d]\n", version); if (port) fprintf(stderr, "port: [%s]\n", port->ptr); #endif /* DEBUG */ /* [RFC 2109] s. 4.3.2 case 2; but this (no request-host) shouldn't happen */ if (!domainname) return COO_ENODOT; if (domain) { char *dp; /* [DRAFT 12] s. 4.2.2 (does not apply in the case that * host name is the same as domain attribute for version 0 * cookie) * I think that this rule has almost the same effect as the * tail match of [NETSCAPE]. */ if (domain->ptr[0] != '.' && (version > 0 || strcasecmp(domainname, domain->ptr) != 0)) domain = Sprintf(".%s", domain->ptr); if (version == 0) { /* [NETSCAPE] rule */ int n = total_dot_number(domain->ptr, domain->ptr + domain->length, 3); if (n < 2) { if (! check_avoid_wrong_number_of_dots_domain(domain)) { COOKIE_ERROR(COO_ESPECIAL); } } else if (n == 2) { char **sdomain; int ok = 0; for (sdomain = special_domain; !ok && *sdomain; sdomain++) { int offset = domain->length - strlen(*sdomain); if (offset >= 0 && strcasecmp(*sdomain, &domain->ptr[offset]) == 0) ok = 1; } if (!ok && ! check_avoid_wrong_number_of_dots_domain(domain)) { COOKIE_ERROR(COO_ESPECIAL); } } } else { /* [DRAFT 12] s. 4.3.2 case 2 */ if (strcasecmp(domain->ptr, ".local") != 0 && contain_no_dots(&domain->ptr[1], &domain->ptr[domain->length])) COOKIE_ERROR(COO_ENODOT); } /* [RFC 2109] s. 4.3.2 case 3 */ if (!(dp = domain_match(domainname, domain->ptr))) COOKIE_ERROR(COO_EDOM); /* [RFC 2409] s. 4.3.2 case 4 */ /* Invariant: dp contains matched domain */ if (version > 0 && !contain_no_dots(domainname, dp)) COOKIE_ERROR(COO_EBADHOST); } if (path) { /* [RFC 2109] s. 4.3.2 case 1 */ if (version > 0 && strncmp(path->ptr, pu->file, path->length) != 0) COOKIE_ERROR(COO_EPATH); } if (port) { /* [DRAFT 12] s. 4.3.2 case 5 */ portlist = make_portlist(port); if (portlist && !port_match(portlist, pu->port)) COOKIE_ERROR(COO_EPORT); } if (!domain) domain = Strnew_charp(domainname); if (!path) { path = Strnew_charp(pu->file); while (path->length > 0 && Strlastchar(path) != '/') Strshrink(path, 1); if (Strlastchar(path) == '/') Strshrink(path, 1); } p = get_cookie_info(domain, path, name); if (!p) { p = New(struct cookie); p->flag = 0; if (default_use_cookie) p->flag |= COO_USE; p->next = First_cookie; First_cookie = p; }
int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, struct ip_fw *chain, int policy, int mode) { struct ip_fw *f; struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl); struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl); struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl); __u32 src, dst; __u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF; unsigned short f_prt=0, prt; char notcpsyn=0, notcpack=0, match; unsigned short offset; int answer; unsigned char tosand, tosxor; /* * If the chain is empty follow policy. The BSD one * accepts anything giving you a time window while * flushing and rebuilding the tables. */ src = ip->saddr; dst = ip->daddr; /* * This way we handle fragmented packets. * we ignore all fragments but the first one * so the whole packet can't be reassembled. * This way we relay on the full info which * stored only in first packet. * * Note that this theoretically allows partial packet * spoofing. Not very dangerous but paranoid people may * wish to play with this. It also allows the so called * "fragment bomb" denial of service attack on some types * of system. */ offset = ntohs(ip->frag_off) & IP_OFFSET; /* * Don't allow a fragment of TCP 8 bytes in. Nobody * normal causes this. Its a cracker trying to break * in by doing a flag overwrite to pass the direction * checks. */ if (offset == 1 && ip->protocol == IPPROTO_TCP) return FW_BLOCK; if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) && (ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP || ip->protocol == IPPROTO_ICMP)) return FW_ACCEPT; /* * Header fragment for TCP is too small to check the bits. */ if(ip->protocol==IPPROTO_TCP && (ip->ihl<<2)+16 > ntohs(ip->tot_len)) return FW_BLOCK; /* * Too short. * * But only too short for a packet with ports... */ else if((ntohs(ip->tot_len)<8+(ip->ihl<<2))&&(ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)) return FW_BLOCK; src = ip->saddr; dst = ip->daddr; /* * If we got interface from which packet came * we can use the address directly. This is unlike * 4.4BSD derived systems that have an address chain * per device. We have a device per address with dummy * devices instead. */ dprintf1("Packet "); switch(ip->protocol) { case IPPROTO_TCP: dprintf1("TCP "); /* ports stay 0xFFFF if it is not the first fragment */ if (!offset) { src_port=ntohs(tcp->source); dst_port=ntohs(tcp->dest); if(!tcp->ack && !tcp->rst) /* We do NOT have ACK, value TRUE */ notcpack=1; if(!tcp->syn || !notcpack) /* We do NOT have SYN, value TRUE */ notcpsyn=1; } prt=IP_FW_F_TCP; break; case IPPROTO_UDP: dprintf1("UDP "); /* ports stay 0xFFFF if it is not the first fragment */ if (!offset) { src_port=ntohs(udp->source); dst_port=ntohs(udp->dest); } prt=IP_FW_F_UDP; break; case IPPROTO_ICMP: /* icmp_type stays 255 if it is not the first fragment */ if (!offset) icmp_type=(__u16)(icmp->type); dprintf2("ICMP:%d ",icmp_type); prt=IP_FW_F_ICMP; break; default: dprintf2("p=%d ",ip->protocol); prt=IP_FW_F_ALL; break; } #ifdef DEBUG_IP_FIREWALL dprint_ip(ip->saddr); if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) /* This will print 65535 when it is not the first fragment! */ dprintf2(":%d ", src_port); dprint_ip(ip->daddr); if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) /* This will print 65535 when it is not the first fragment! */ dprintf2(":%d ",dst_port); dprintf1("\n"); #endif for (f=chain;f;f=f->fw_next) { /* * This is a bit simpler as we don't have to walk * an interface chain as you do in BSD - same logic * however. */ /* * Match can become 0x01 (a "normal" match was found), * 0x02 (a reverse match was found), and 0x03 (the * IP addresses match in both directions). * Now we know in which direction(s) we should look * for a match for the TCP/UDP ports. Both directions * might match (e.g., when both addresses are on the * same network for which an address/mask is given), but * the ports might only match in one direction. * This was obviously wrong in the original BSD code. */ match = 0x00; if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr && (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) /* normal direction */ match |= 0x01; if ((f->fw_flg & IP_FW_F_BIDIR) && (dst&f->fw_smsk.s_addr)==f->fw_src.s_addr && (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) /* reverse direction */ match |= 0x02; if (!match) continue; /* * Look for a VIA device match */ if(f->fw_viadev) { if(rif!=f->fw_viadev) continue; /* Mismatch */ } /* This looks stupid, because we scan almost static list, searching for static key. However, this way seems to be only reasonable way of handling fw_via rules (btw bsd makes the same thing). It will not affect performance if you will follow the following simple rules: - if inteface is aliased, ALWAYS specify fw_viadev, so that previous check will guarantee, that we will not waste time when packet arrive on another interface. - avoid using fw_via.s_addr if fw_via.s_addr is owned by an aliased interface. --ANK */ if (f->fw_via.s_addr && rif) { struct in_ifaddr *ifa; if (rif->ip_ptr == NULL) continue; /* Mismatch */ for (ifa = ((struct in_device*)(rif->ip_ptr))->ifa_list; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_local == f->fw_via.s_addr) goto ifa_ok; } continue; /* Mismatch */ ifa_ok:; } /* * Ok the chain addresses match. */ #ifdef CONFIG_IP_ACCT /* * See if we're in accounting mode and only want to * count incoming or outgoing packets. */ if (mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT) && ((mode == IP_FW_MODE_ACCT_IN && f->fw_flg&IP_FW_F_ACCTOUT) || (mode == IP_FW_MODE_ACCT_OUT && f->fw_flg&IP_FW_F_ACCTIN))) continue; #endif /* * For all non-TCP packets and/or non-first fragments, * notcpsyn and notcpack will always be FALSE, * so the IP_FW_F_TCPSYN and IP_FW_F_TCPACK flags * are actually ignored for these packets. */ if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn) continue; if((f->fw_flg&IP_FW_F_TCPACK) && notcpack) continue; f_prt=f->fw_flg&IP_FW_F_KIND; if (f_prt!=IP_FW_F_ALL) { /* * Specific firewall - packet's protocol * must match firewall's. */ if(prt!=f_prt) continue; if((prt==IP_FW_F_ICMP && ! port_match(&f->fw_pts[0], f->fw_nsp, icmp_type,f->fw_flg&IP_FW_F_SRNG)) || !(prt==IP_FW_F_ICMP || ((match & 0x01) && port_match(&f->fw_pts[0], f->fw_nsp, src_port, f->fw_flg&IP_FW_F_SRNG) && port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port, f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) && port_match(&f->fw_pts[0], f->fw_nsp, dst_port, f->fw_flg&IP_FW_F_SRNG) && port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port, f->fw_flg&IP_FW_F_DRNG)))) { continue; } } #ifdef CONFIG_IP_FIREWALL_VERBOSE if (f->fw_flg & IP_FW_F_PRN) { char buf[16]; print_packet(ip, src_port, dst_port, icmp_type, chain_name(chain, mode), rule_name(f, mode, buf), rif ? rif->name : "-"); } #endif if (mode != IP_FW_MODE_CHK) { f->fw_bcnt+=ntohs(ip->tot_len); f->fw_pcnt++; } if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) break; } /* Loop */ if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) { /* * We rely on policy defined in the rejecting entry or, if no match * was found, we rely on the general policy variable for this type * of firewall. */ if (f!=NULL) { policy=f->fw_flg; tosand=f->fw_tosand; tosxor=f->fw_tosxor; } else { tosand=0xFF; tosxor=0x00; } if (policy&IP_FW_F_ACCEPT) { /* Adjust priority and recompute checksum */ __u8 old_tos = ip->tos; ip->tos = (old_tos & tosand) ^ tosxor; if (ip->tos != old_tos) ip_send_check(ip); #ifdef CONFIG_IP_TRANSPARENT_PROXY if (policy&IP_FW_F_REDIR) { if (redirport) if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) { /* Wildcard redirection. * Note that redirport will become * 0xFFFF for non-TCP/UDP packets. */ *redirport = htons(dst_port); } answer = FW_REDIRECT; } else #endif #ifdef CONFIG_IP_MASQUERADE if (policy&IP_FW_F_MASQ) answer = FW_MASQUERADE; else #endif answer = FW_ACCEPT; } else if(policy&IP_FW_F_ICMPRPL) answer = FW_REJECT; else answer = FW_BLOCK; #ifdef CONFIG_IP_FIREWALL_NETLINK if((policy&IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK)) { struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC); if(skb) { int len = min_t(unsigned int, 128, ntohs(ip->tot_len)); skb_put(skb,len); memcpy(skb->data,ip,len); if(netlink_post(NETLINK_FIREWALL, skb)) kfree_skb(skb); } } #endif return answer; } else /* we're doing accounting, always ok */ return 0;
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_TO bogusfrag: 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: 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 = rtems_bsdnet_seconds_since_boot(); /* Log to console if desired */ if ((f->fw_flg & IP_FW_F_PRN) && fw_verbose) ipfw_report(f, ip, rif, oif); /* Take appropriate action */ switch (f->fw_flg & IP_FW_F_COMMAND) { case IP_FW_F_ACCEPT: return(0); case IP_FW_F_COUNT: continue; 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 /* Rule 65535 should always be there and should always match */ if (!chain) panic("ip_fw: chain"); #endif /* * At this point, we're going to drop the packet. * Send a reject notice if all of the following are true: * * - The packet matched a reject rule * - The packet is not an ICMP packet * - The packet is not a multicast or broadcast packet */ 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, *const tip = (struct tcpiphdr *) ip; if (offset != 0 || (tcp->th_flags & TH_RST)) break; ti.ti_i = *((struct ipovly *) ip); ti.ti_t = *tcp; bcopy(&ti, ip, sizeof(ti)); NTOHL(tip->ti_seq); NTOHL(tip->ti_ack); tip->ti_len = ip->ip_len - hlen - (tip->ti_off << 2); if (tcp->th_flags & TH_ACK) { tcp_respond(NULL, tip, *m, (tcp_seq)0, ntohl(tcp->th_ack), TH_RST); } else { if (tcp->th_flags & TH_SYN) tip->ti_len++; tcp_respond(NULL, tip, *m, tip->ti_seq + tip->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 ip_fw_chk(struct ip **pip, int hlen, struct ifnet *oif, u_int16_t *cookie, struct mbuf **m, struct ip_fw_chain **flow_id) { struct ip_fw_chain *chain; struct ip_fw *rule = NULL; struct ip *ip = NULL ; struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short offset ; u_short src_port, dst_port; #ifdef IPFW_DIVERT_RESTART u_int16_t skipto = *cookie; #else u_int16_t ignport = ntohs(*cookie); #endif if (pip) { /* normal ip packet */ ip = *pip; offset = (ip->ip_off & IP_OFFMASK); } else { /* bridged or non-ip packet */ struct ether_header *eh = mtod(*m, struct ether_header *); switch (ntohs(eh->ether_type)) { case ETHERTYPE_IP : if ((*m)->m_len<sizeof(struct ether_header) + sizeof(struct ip)) goto non_ip ; ip = (struct ip *)(eh + 1 ); if (ip->ip_v != IPVERSION) goto non_ip ; hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) /* minimum header length */ goto non_ip ; if ((*m)->m_len < 14 + hlen + 14) { printf("-- m_len %d, need more...\n", (*m)->m_len); goto non_ip ; } offset = (ip->ip_off & IP_OFFMASK); break ; default : non_ip: ip = NULL ; break ; } } if (*flow_id) { if (fw_one_pass) return 0 ; /* accept if passed first test */ /* * pkt has already been tagged. Look for the next rule * to restart processing */ if ( (chain = (*flow_id)->rule->next_rule_ptr) == NULL ) chain = (*flow_id)->rule->next_rule_ptr = lookup_next_rule(*flow_id) ; if (!chain) goto dropit; } else { chain=LIST_FIRST(&ip_fw_chain); #ifdef IPFW_DIVERT_RESTART if ( skipto ) { /* * If we've been asked to start at a given rule immediatly, * do so. */ if (skipto >= 65535) goto dropit; while (chain && (chain->rule->fw_number <= skipto)) { chain = LIST_NEXT(chain, chain); } if (! chain) goto dropit; } #endif /* IPFW_DIVERT_RESTART */ } *cookie = 0; for (; chain; chain = LIST_NEXT(chain, chain)) { register struct ip_fw *f; again: f = chain->rule; if (oif) { /* Check direction outbound */ if (!(f->fw_flg & IP_FW_F_OUT)) continue; } else { /* Check direction inbound */ if (!(f->fw_flg & IP_FW_F_IN)) continue; } if (ip == NULL ) { /* * do relevant checks for non-ip packets: * after this, only goto got_match or continue */ struct ether_header *eh = mtod(*m, struct ether_header *); int i, h, l ; #if 0 printf("-- ip_fw: rule %d(%d) for %6D <- %6D type 0x%04x\n", f->fw_number, IP_FW_GETNSRCP(f), eh->ether_dhost, ".", eh->ether_shost, ".", ntohs(eh->ether_type) ); #endif /* * make default rule always match or we have a panic */ if (f->fw_number == 65535) goto got_match ; /* * temporary hack: * udp from 0.0.0.0 means this rule applies. * 1 src port is match ether type * 2 src ports (interval) is match ether type * 3 src ports is match ether address */ if (f->fw_src.s_addr != 0 || f->fw_prot != IPPROTO_UDP) continue ; switch (IP_FW_GETNSRCP(f)) { case 1: /* match one type */ if ( /* ( (f->fw_flg & IP_FW_F_INVSRC) != 0) ^ */ ( f->fw_pts[0] == ntohs(eh->ether_type) ) ) { printf("match!\n"); goto got_match ; } default: break ; } 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) ) { \ if ( (*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_TO bogusfrag: if (fw_verbose) ipfw_report(NULL, ip, rif, oif); goto dropit; } got_match: *flow_id = chain ; /* XXX set flow id */ #ifndef IPFW_DIVERT_RESTART /* 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; } #endif /* IPFW_DIVERT_RESTART */ /* Update statistics */ f->fw_pcnt += 1; /* * note -- bridged-ip packets still have some fields * in network order, including ip_len */ if (ip) { if (pip) f->fw_bcnt += ip->ip_len; else f->fw_bcnt += ntohs(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 */ switch (f->fw_flg & IP_FW_F_COMMAND) { case IP_FW_F_ACCEPT: return(0); case IP_FW_F_COUNT: continue; #ifdef IPDIVERT case IP_FW_F_DIVERT: #ifdef IPFW_DIVERT_RESTART *cookie = f->fw_number; #else *cookie = htons(f->fw_divert_port); #endif /* IPFW_DIVERT_RESTART */ return(f->fw_divert_port); #endif 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: /* XXX check */ if ( f->next_rule_ptr ) chain = f->next_rule_ptr ; else chain = lookup_next_rule(chain) ; if (!chain) goto dropit; goto again ; #ifdef DUMMYNET case IP_FW_F_PIPE: return(f->fw_pipe_nr | 0x10000 ); #endif } /* Deny/reject this packet using this rule */ rule = f; break; } #ifdef DIAGNOSTIC /* Rule 65535 should always be there and should always match */ if (!chain) panic("ip_fw: chain"); #endif /* * At this point, we're going to drop the packet. * Send a reject notice if all of the following are true: * * - The packet matched a reject rule * - The packet is not an ICMP packet, or is an ICMP query packet * - The packet is not a multicast or broadcast packet */ if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT && ip && (ip->ip_p != IPPROTO_ICMP || is_icmp_query(ip)) && !((*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, *const tip = (struct tcpiphdr *) ip; if (offset != 0 || (tcp->th_flags & TH_RST)) break; ti.ti_i = *((struct ipovly *) ip); ti.ti_t = *tcp; bcopy(&ti, ip, sizeof(ti)); NTOHL(tip->ti_seq); NTOHL(tip->ti_ack); tip->ti_len = ip->ip_len - hlen - (tip->ti_off << 2); if (tcp->th_flags & TH_ACK) { tcp_respond(NULL, tip, *m, (tcp_seq)0, ntohl(tcp->th_ack), TH_RST); } else { if (tcp->th_flags & TH_SYN) tip->ti_len++; tcp_respond(NULL, tip, *m, tip->ti_seq + tip->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); }