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;
/************************************************************************** Return the (untranslated) rule name of the base type. You don't have to free the return pointer. **************************************************************************/ const char *base_rule_name(const struct base_type *pbase) { return rule_name(&pbase->name); }