📄 ipchains_core.c
字号:
/* This function returns a pointer to the first chain with a label * that matches the one given. */static struct ip_chain *find_label(ip_chainlabel label){ struct ip_chain *tmp; 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, struct iphdr *ip, const char *rif, const ip_chainlabel label, struct sk_buff *skb, unsigned int slot, __u16 src_port, __u16 dst_port, unsigned int count, int tcpsyn){ f->counters[slot].bcnt+=ntohs(ip->tot_len); f->counters[slot].pcnt++; if (f->ipfw.fw_flg & IP_FW_F_PRN) { dump_packet(ip,rif,f,label,src_port,dst_port,count,tcpsyn); } ip->tos = (ip->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) { skb->nfmark = f->ipfw.fw_mark; } else { skb->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(ip->tot_len)) + sizeof(__u32) + sizeof(skb->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))) = skb->nfmark; strcpy(outskb->data+sizeof(__u32)*2, rif); memcpy(outskb->data+sizeof(__u32)*2+IFNAMSIZ, ip, len-(sizeof(__u32)*2+IFNAMSIZ)); netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_KERNEL); } 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(struct iphdr *ip, const char *rif, __u16 *redirport, struct ip_chain *chain, struct sk_buff *skb, unsigned int slot, int testing){ 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; char tcpsyn=0; __u16 offset; unsigned char oldtos; 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(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) { if (!testing && net_ratelimit()) { printk("Suspect TCP fragment.\n"); dump_packet(ip,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 (ip->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(ip->tot_len) < (ip->ihl<<2)+size_req) { if (!testing && net_ratelimit()) { printk("Suspect short first fragment.\n"); dump_packet(ip,rif,NULL,NULL,0,0,0,0); } return FW_BLOCK; } } src = ip->saddr; dst = ip->daddr; oldtos = ip->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(ip->protocol) { case IPPROTO_TCP: dprintf("TCP "); if (!offset) { src_port=ntohs(tcp->source); dst_port=ntohs(tcp->dest); /* Connection initilisation can only * be made when the syn bit is set and * neither of the ack or reset is * set. */ if(tcp->syn && !(tcp->ack || tcp->rst)) tcpsyn=1; } break; case IPPROTO_UDP: dprintf("UDP "); if (!offset) { src_port=ntohs(udp->source); dst_port=ntohs(udp->dest); } break; case IPPROTO_ICMP: if (!offset) { src_port=(__u16)icmp->type; dst_port=(__u16)icmp->code; } dprintf("ICMP "); break; default: dprintf("p=%d ",ip->protocol); break; }#ifdef DEBUG_IP_FIREWALL print_ip(ip->saddr); if (offset) dprintf(":fragment (%i) ", ((int)offset)<<2); else if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP || ip->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,ip, tcpsyn,src_port,dst_port,offset)) { if (!testing && !ip_fw_domatch(f, ip, rif, chain->label, skb, slot, src_port, dst_port, count, tcpsyn)) { ret = FW_BLOCK; 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(ip->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 (ip->tos != oldtos && ret != FW_REJECT && ret != FW_BLOCK && !testing) ip_send_check(ip); 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; MOD_DEC_USE_COUNT; } 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); /* 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; if (rule->branch) rule->branch->refcount++; goto append_successful; } /* Find the rule before the end of the chain */ for (i = chainptr->chain; i->next; i = i->next); i->next = rule; if (rule->branch) rule->branch->refcount++;append_successful: MOD_INC_USE_COUNT; return 0;}/* This function inserts a rule at the position of position in the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -