📄 ipchains_core.c
字号:
FWC_HAVE_LOCK(fwc_rlocks | fwc_wlocks); for (tmp = ip_fw_chains; tmp; tmp = tmp->next) if (strcmp(tmp->label,label) == 0) break; return tmp;}/* This function returns a boolean which when true sets answer to one of the FW_*. */static int find_special(ip_chainlabel label, int *answer){ if (label[0] == '\0') { *answer = FW_SKIP; /* => pass-through rule */ return 1; } else if (strcmp(label,IP_FW_LABEL_ACCEPT) == 0) { *answer = FW_ACCEPT; return 1; } else if (strcmp(label,IP_FW_LABEL_BLOCK) == 0) { *answer = FW_BLOCK; return 1; } else if (strcmp(label,IP_FW_LABEL_REJECT) == 0) { *answer = FW_REJECT; return 1; } else if (strcmp(label,IP_FW_LABEL_REDIRECT) == 0) { *answer = FW_REDIRECT; return 1; } else if (strcmp(label,IP_FW_LABEL_MASQUERADE) == 0) { *answer = FW_MASQUERADE; return 1; } else if (strcmp(label, IP_FW_LABEL_RETURN) == 0) { *answer = FW_SKIP+1; return 1; } else { return 0; }}/* This function cleans up the prevchain and prevrule. If the verbose * flag is set then he names of the chains will be printed as it * cleans up. */static void cleanup(struct ip_chain *chain, const int verbose, unsigned int slot){ struct ip_chain *tmpchain = chain->reent[slot].prevchain; if (verbose) printk(KERN_ERR "Chain backtrace: "); while (tmpchain) { if (verbose) printk("%s<-",chain->label); chain->reent[slot].prevchain = NULL; chain = tmpchain; tmpchain = chain->reent[slot].prevchain; } if (verbose) printk("%s\n",chain->label);}static inline intip_fw_domatch(struct ip_fwkernel *f, const char *rif, const ip_chainlabel label, struct sk_buff **pskb, unsigned int slot, __u16 src_port, __u16 dst_port, unsigned int count, int tcpsyn, unsigned char *tos){ f->counters[slot].bcnt+=ntohs((*pskb)->nh.iph->tot_len); f->counters[slot].pcnt++; if (f->ipfw.fw_flg & IP_FW_F_PRN) { dump_packet(pskb,rif,f,label,src_port,dst_port,count,tcpsyn); } *tos = (*tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;/* This functionality is useless in stock 2.0.x series, but we don't * discard the mark thing altogether, to avoid breaking ipchains (and, * more importantly, the ipfwadm wrapper) --PR */ if (f->ipfw.fw_flg & IP_FW_F_MARKABS) { (*pskb)->nfmark = f->ipfw.fw_mark; } else { (*pskb)->nfmark += f->ipfw.fw_mark; } if (f->ipfw.fw_flg & IP_FW_F_NETLINK) {#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE) size_t len = min_t(unsigned int, f->ipfw.fw_outputsize, ntohs((*pskb)->nh.iph->tot_len)) + sizeof(__u32) + sizeof((*pskb)->nfmark) + IFNAMSIZ; struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC); duprintf("Sending packet out NETLINK (length = %u).\n", (unsigned int)len); if (outskb) { /* Prepend length, mark & interface */ skb_put(outskb, len); *((__u32 *)outskb->data) = (__u32)len; *((__u32 *)(outskb->data+sizeof(__u32))) = (*pskb)->nfmark; strcpy(outskb->data+sizeof(__u32)*2, rif); skb_copy_bits(*pskb, ((char *)(*pskb)->nh.iph - (char *)(*pskb)->data), outskb->data+sizeof(__u32)*2+IFNAMSIZ, len-(sizeof(__u32)*2+IFNAMSIZ)); netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC); } else {#endif if (net_ratelimit()) printk(KERN_WARNING "ip_fw: packet drop due to " "netlink failure\n"); return 0;#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE) }#endif } return 1;}/* * Returns one of the generic firewall policies, like FW_ACCEPT. * * The testing is either false for normal firewall mode or true for * user checking mode (counters are not updated, TOS & mark not done). */static intip_fw_check(const char *rif, __u16 *redirport, struct ip_chain *chain, struct sk_buff **pskb, unsigned int slot, int testing){ __u32 src, dst; __u16 src_port = 0xFFFF, dst_port = 0xFFFF; char tcpsyn=0; __u16 offset; unsigned char tos; struct ip_fwkernel *f; int ret = FW_SKIP+2; unsigned int count; /* We handle fragments by dealing with the first fragment as * if it was a normal packet. All other fragments are treated * normally, except that they will NEVER match rules that ask * things we don't know, ie. tcp syn flag or ports). If the * rule is also a fragment-specific rule, non-fragments won't * match it. */ offset = ntohs((*pskb)->nh.iph->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 && (*pskb)->nh.iph->protocol == IPPROTO_TCP) { if (!testing && net_ratelimit()) { printk("Suspect TCP fragment.\n"); dump_packet(pskb,rif,NULL,NULL,0,0,0,0); } return FW_BLOCK; } /* If we can't investigate ports, treat as fragment. It's * either a trucated whole packet, or a truncated first * fragment, or a TCP first fragment of length 8-15, in which * case the above rule stops reassembly. */ if (offset == 0) { unsigned int size_req; switch ((*pskb)->nh.iph->protocol) { case IPPROTO_TCP: /* Don't care about things past flags word */ size_req = 16; break; case IPPROTO_UDP: case IPPROTO_ICMP: size_req = 8; break; default: size_req = 0; } /* If it is a truncated first fragment then it can be * used to rewrite port information, and thus should * be blocked. */ if (ntohs((*pskb)->nh.iph->tot_len) < ((*pskb)->nh.iph->ihl<<2)+size_req) { if (!testing && net_ratelimit()) { printk("Suspect short first fragment.\n"); dump_packet(pskb,rif,NULL,NULL,0,0,0,0); } return FW_BLOCK; } } src = (*pskb)->nh.iph->saddr; dst = (*pskb)->nh.iph->daddr; tos = (*pskb)->nh.iph->tos; /* * If we got interface from which packet came * we can use the address directly. Linux 2.1 now uses address * chains per device too, but unlike BSD we first check if the * incoming packet matches a device address and the routing * table before calling the firewall. */ dprintf("Packet "); switch ((*pskb)->nh.iph->protocol) { case IPPROTO_TCP: dprintf("TCP "); if (!offset) { struct tcphdr tcph; if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl * 4, &tcph, sizeof(tcph))) return FW_BLOCK; src_port = ntohs(tcph.source); dst_port = ntohs(tcph.dest); /* Connection initilisation can only * be made when the syn bit is set and * neither of the ack or reset is * set. */ if (tcph.syn && !(tcph.ack || tcph.rst)) tcpsyn = 1; } break; case IPPROTO_UDP: dprintf("UDP "); if (!offset) { struct udphdr udph; if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl * 4, &udph, sizeof(udph))) return FW_BLOCK; src_port = ntohs(udph.source); dst_port = ntohs(udph.dest); } break; case IPPROTO_ICMP: if (!offset) { struct icmphdr icmph; if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl * 4, &icmph, sizeof(icmph))) return FW_BLOCK; src_port = (__u16) icmph.type; dst_port = (__u16) icmph.code; } dprintf("ICMP "); break; default: dprintf("p=%d ", (*pskb)->nh.iph->protocol); break; }#ifdef DEBUG_IP_FIREWALL print_ip((*pskb)->nh.iph->saddr); if (offset) dprintf(":fragment (%i) ", ((int)offset)<<2); else if ((*pskb)->nh.iph->protocol == IPPROTO_TCP || (*pskb)->nh.iph->protocol == IPPROTO_UDP || (*pskb)->nh.iph->protocol == IPPROTO_ICMP) dprintf(":%hu:%hu", src_port, dst_port); dprintf("\n");#endif if (!testing) FWC_READ_LOCK(&ip_fw_lock); else FWC_HAVE_LOCK(fwc_rlocks); f = chain->chain; do { count = 0; for (; f; f = f->next) { count++; if (ip_rule_match(f, rif, pskb, tcpsyn, src_port, dst_port, offset)) { if (!testing && !ip_fw_domatch(f, rif, chain->label, pskb, slot, src_port, dst_port, count, tcpsyn, &tos)) { ret = FW_BLOCK; cleanup(chain, 0, slot); goto out; } break; } } if (f) { if (f->branch) { /* Do sanity check to see if we have * already set prevchain and if so we * must be in a loop */ if (f->branch->reent[slot].prevchain) { if (!testing) { printk(KERN_ERR "IP firewall: " "Loop detected " "at `%s'.\n", f->branch->label); cleanup(chain, 1, slot); ret = FW_BLOCK; } else { cleanup(chain, 0, slot); ret = FW_SKIP+1; } } else { f->branch->reent[slot].prevchain = chain; f->branch->reent[slot].prevrule = f->next; chain = f->branch; f = chain->chain; } } else if (f->simplebranch == FW_SKIP) f = f->next; else if (f->simplebranch == FW_SKIP+1) { /* Just like falling off the chain */ goto fall_off_chain; } else { cleanup(chain, 0, slot); ret = f->simplebranch; } } /* f == NULL */ else { fall_off_chain: if (chain->reent[slot].prevchain) { struct ip_chain *tmp = chain; f = chain->reent[slot].prevrule; chain = chain->reent[slot].prevchain; tmp->reent[slot].prevchain = NULL; } else { ret = chain->policy; if (!testing) { chain->reent[slot].counters.pcnt++; chain->reent[slot].counters.bcnt += ntohs((*pskb)->nh.iph->tot_len); } } } } while (ret == FW_SKIP+2); out: if (!testing) FWC_READ_UNLOCK(&ip_fw_lock); /* Recalculate checksum if not going to reject, and TOS changed. */ if ((*pskb)->nh.iph->tos != tos && ret != FW_REJECT && ret != FW_BLOCK && !testing) { if (!skb_ip_make_writable(pskb, offsetof(struct iphdr, tos)+1)) ret = FW_BLOCK; else { (*pskb)->nh.iph->tos = tos; ip_send_check((*pskb)->nh.iph); } } if (ret == FW_REDIRECT && redirport) { if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) { /* Wildcard redirection. * Note that redirport will become * 0xFFFF for non-TCP/UDP packets. */ *redirport = htons(dst_port); } }#ifdef DEBUG_ALLOW_ALL return (testing ? ret : FW_ACCEPT);#else return ret;#endif}/* Must have write lock & interrupts off for any of these *//* This function sets all the byte counters in a chain to zero. The * input is a pointer to the chain required for zeroing */static int zero_fw_chain(struct ip_chain *chainptr){ struct ip_fwkernel *i; FWC_HAVE_LOCK(fwc_wlocks); for (i = chainptr->chain; i; i = i->next) memset(i->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS); return 0;}static int clear_fw_chain(struct ip_chain *chainptr){ struct ip_fwkernel *i= chainptr->chain; FWC_HAVE_LOCK(fwc_wlocks); chainptr->chain=NULL; while (i) { struct ip_fwkernel *tmp = i->next; if (i->branch) i->branch->refcount--; kfree(i); i = tmp; /* We will block in cleanup's unregister sockopt if unloaded, so this is safe. */ module_put(THIS_MODULE); } return 0;}static int replace_in_chain(struct ip_chain *chainptr, struct ip_fwkernel *frwl, __u32 position){ struct ip_fwkernel *f = chainptr->chain; FWC_HAVE_LOCK(fwc_wlocks); while (--position && f != NULL) f = f->next; if (f == NULL) return EINVAL; if (f->branch) f->branch->refcount--; if (frwl->branch) frwl->branch->refcount++; frwl->next = f->next; memcpy(f,frwl,sizeof(struct ip_fwkernel)); kfree(frwl); return 0;}static int append_to_chain(struct ip_chain *chainptr, struct ip_fwkernel *rule){ struct ip_fwkernel *i; FWC_HAVE_LOCK(fwc_wlocks); /* Are we unloading now? We will block on nf_unregister_sockopt */ if (!try_module_get(THIS_MODULE)) return ENOPROTOOPT; /* Special case if no rules already present */ if (chainptr->chain == NULL) { /* If pointer writes are atomic then turning off * interrupts is not necessary. */ chainptr->chain = rule;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -