Commit 6ec6f76e authored by David S. Miller's avatar David S. Miller

[NETFILTER]: Teach ip_fw_compat and modules to handle non-linear SKBs.

Much help provided by Rusty Russell in fixing device leak
and TOS modification handling bugs.
parent a3bc528c
...@@ -21,20 +21,20 @@ struct firewall_ops ...@@ -21,20 +21,20 @@ struct firewall_ops
{ {
struct firewall_ops *next; struct firewall_ops *next;
int (*fw_forward)(struct firewall_ops *this, int pf, int (*fw_forward)(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb); struct sk_buff **pskb);
int (*fw_input)(struct firewall_ops *this, int pf, int (*fw_input)(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb); struct sk_buff **pskb);
int (*fw_output)(struct firewall_ops *this, int pf, int (*fw_output)(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb); struct sk_buff **pskb);
/* These may be NULL. */ /* These may be NULL. */
int (*fw_acct_in)(struct firewall_ops *this, int pf, int (*fw_acct_in)(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb); struct sk_buff **pskb);
int (*fw_acct_out)(struct firewall_ops *this, int pf, int (*fw_acct_out)(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb); struct sk_buff **pskb);
}; };
......
...@@ -250,7 +250,7 @@ extern int ip_masq_ctl(int, void *, int); ...@@ -250,7 +250,7 @@ extern int ip_masq_ctl(int, void *, int);
extern int ip_fw_masq_timeouts(void *user, int len); extern int ip_fw_masq_timeouts(void *user, int len);
extern int ip_fw_chk(struct iphdr *, struct net_device *, __u16 *, extern int ip_fw_chk(struct sk_buff **, struct net_device *, __u16 *,
struct ip_fw *, int, int); struct ip_fw *, int, int);
#endif /* KERNEL */ #endif /* KERNEL */
#endif /* _IP_FW_H */ #endif /* _IP_FW_H */
...@@ -51,31 +51,17 @@ fw_in(unsigned int hooknum, ...@@ -51,31 +51,17 @@ fw_in(unsigned int hooknum,
int ret = FW_BLOCK; int ret = FW_BLOCK;
u_int16_t redirpt; u_int16_t redirpt;
/* FIXME: Push down to extensions --RR */
if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
/* Assume worse case: any hook could change packet */ /* Assume worse case: any hook could change packet */
(*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED; (*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
if ((*pskb)->ip_summed == CHECKSUM_HW) if ((*pskb)->ip_summed == CHECKSUM_HW)
(*pskb)->ip_summed = CHECKSUM_NONE; (*pskb)->ip_summed = CHECKSUM_NONE;
/* Firewall rules can alter TOS: raw socket (tcpdump) may have
clone of incoming skb: don't disturb it --RR */
if (skb_cloned(*pskb) && !(*pskb)->sk) {
struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
if (!nskb)
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
}
switch (hooknum) { switch (hooknum) {
case NF_IP_PRE_ROUTING: case NF_IP_PRE_ROUTING:
if (fwops->fw_acct_in) if (fwops->fw_acct_in)
fwops->fw_acct_in(fwops, PF_INET, fwops->fw_acct_in(fwops, PF_INET,
(struct net_device *)in, (struct net_device *)in,
(*pskb)->nh.raw, &redirpt, pskb); &redirpt, pskb);
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb); *pskb = ip_ct_gather_frags(*pskb);
...@@ -85,7 +71,7 @@ fw_in(unsigned int hooknum, ...@@ -85,7 +71,7 @@ fw_in(unsigned int hooknum,
} }
ret = fwops->fw_input(fwops, PF_INET, (struct net_device *)in, ret = fwops->fw_input(fwops, PF_INET, (struct net_device *)in,
(*pskb)->nh.raw, &redirpt, pskb); &redirpt, pskb);
break; break;
case NF_IP_FORWARD: case NF_IP_FORWARD:
...@@ -95,18 +81,18 @@ fw_in(unsigned int hooknum, ...@@ -95,18 +81,18 @@ fw_in(unsigned int hooknum,
ret = FW_ACCEPT; ret = FW_ACCEPT;
else ret = fwops->fw_forward(fwops, PF_INET, else ret = fwops->fw_forward(fwops, PF_INET,
(struct net_device *)out, (struct net_device *)out,
(*pskb)->nh.raw, &redirpt, pskb); &redirpt, pskb);
break; break;
case NF_IP_POST_ROUTING: case NF_IP_POST_ROUTING:
ret = fwops->fw_output(fwops, PF_INET, ret = fwops->fw_output(fwops, PF_INET,
(struct net_device *)out, (struct net_device *)out,
(*pskb)->nh.raw, &redirpt, pskb); &redirpt, pskb);
if (ret == FW_ACCEPT || ret == FW_SKIP) { if (ret == FW_ACCEPT || ret == FW_SKIP) {
if (fwops->fw_acct_out) if (fwops->fw_acct_out)
fwops->fw_acct_out(fwops, PF_INET, fwops->fw_acct_out(fwops, PF_INET,
(struct net_device *)out, (struct net_device *)out,
(*pskb)->nh.raw, &redirpt, &redirpt,
pskb); pskb);
/* ip_conntrack_confirm return NF_DROP or NF_ACCEPT */ /* ip_conntrack_confirm return NF_DROP or NF_ACCEPT */
...@@ -169,10 +155,6 @@ static unsigned int fw_confirm(unsigned int hooknum, ...@@ -169,10 +155,6 @@ static unsigned int fw_confirm(unsigned int hooknum,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
/* FIXME: Push down to extensions --RR */
if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
return ip_conntrack_confirm(*pskb); return ip_conntrack_confirm(*pskb);
} }
......
...@@ -94,6 +94,7 @@ ...@@ -94,6 +94,7 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4/compat_firewall.h> #include <linux/netfilter_ipv4/compat_firewall.h>
#include <linux/netfilter_ipv4/ipchains_core.h> #include <linux/netfilter_ipv4/ipchains_core.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
...@@ -280,11 +281,13 @@ extern inline int port_match(__u16 min, __u16 max, __u16 port, ...@@ -280,11 +281,13 @@ extern inline int port_match(__u16 min, __u16 max, __u16 port,
/* Returns whether matches rule or not. */ /* Returns whether matches rule or not. */
static int ip_rule_match(struct ip_fwkernel *f, static int ip_rule_match(struct ip_fwkernel *f,
const char *ifname, const char *ifname,
struct iphdr *ip, struct sk_buff **pskb,
char tcpsyn, char tcpsyn,
__u16 src_port, __u16 dst_port, __u16 src_port, __u16 dst_port,
char isfrag) char isfrag)
{ {
struct iphdr *ip = (*pskb)->nh.iph;
#define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg)) #define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg))
/* /*
* This is a bit simpler as we don't have to walk * This is a bit simpler as we don't have to walk
...@@ -401,7 +404,7 @@ static const char *branchname(struct ip_chain *branch,int simplebranch) ...@@ -401,7 +404,7 @@ static const char *branchname(struct ip_chain *branch,int simplebranch)
* VERY ugly piece of code which actually * VERY ugly piece of code which actually
* makes kernel printf for matching packets... * makes kernel printf for matching packets...
*/ */
static void dump_packet(const struct iphdr *ip, static void dump_packet(struct sk_buff **pskb,
const char *ifname, const char *ifname,
struct ip_fwkernel *f, struct ip_fwkernel *f,
const ip_chainlabel chainlabel, const ip_chainlabel chainlabel,
...@@ -410,7 +413,7 @@ static void dump_packet(const struct iphdr *ip, ...@@ -410,7 +413,7 @@ static void dump_packet(const struct iphdr *ip,
unsigned int count, unsigned int count,
int syn) int syn)
{ {
__u32 *opt = (__u32 *) (ip + 1); __u32 *opt = (__u32 *) ((*pskb)->nh.iph + 1);
int opti; int opti;
if (f) { if (f) {
...@@ -422,13 +425,18 @@ static void dump_packet(const struct iphdr *ip, ...@@ -422,13 +425,18 @@ static void dump_packet(const struct iphdr *ip,
printk("%s PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu" printk("%s PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu"
" L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu", " L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
ifname, ip->protocol, NIPQUAD(ip->saddr), ifname, (*pskb)->nh.iph->protocol,
src_port, NIPQUAD(ip->daddr), NIPQUAD((*pskb)->nh.iph->saddr),
src_port,
NIPQUAD((*pskb)->nh.iph->daddr),
dst_port, dst_port,
ntohs(ip->tot_len), ip->tos, ntohs(ip->id), ntohs((*pskb)->nh.iph->tot_len),
ntohs(ip->frag_off), ip->ttl); (*pskb)->nh.iph->tos,
ntohs((*pskb)->nh.iph->id),
ntohs((*pskb)->nh.iph->frag_off),
(*pskb)->nh.iph->ttl);
for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++) for (opti = 0; opti < ((*pskb)->nh.iph->ihl - sizeof(struct iphdr) / 4); opti++)
printk(" O=0x%8.8X", *opt++); printk(" O=0x%8.8X", *opt++);
printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count); printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count);
} }
...@@ -509,34 +517,35 @@ static void cleanup(struct ip_chain *chain, ...@@ -509,34 +517,35 @@ static void cleanup(struct ip_chain *chain,
static inline int static inline int
ip_fw_domatch(struct ip_fwkernel *f, ip_fw_domatch(struct ip_fwkernel *f,
struct iphdr *ip,
const char *rif, const char *rif,
const ip_chainlabel label, const ip_chainlabel label,
struct sk_buff *skb, struct sk_buff **pskb,
unsigned int slot, unsigned int slot,
__u16 src_port, __u16 dst_port, __u16 src_port, __u16 dst_port,
unsigned int count, unsigned int count,
int tcpsyn) int tcpsyn,
unsigned char *tos)
{ {
f->counters[slot].bcnt+=ntohs(ip->tot_len); f->counters[slot].bcnt+=ntohs((*pskb)->nh.iph->tot_len);
f->counters[slot].pcnt++; f->counters[slot].pcnt++;
if (f->ipfw.fw_flg & IP_FW_F_PRN) { if (f->ipfw.fw_flg & IP_FW_F_PRN) {
dump_packet(ip,rif,f,label,src_port,dst_port,count,tcpsyn); dump_packet(pskb,rif,f,label,src_port,dst_port,count,tcpsyn);
} }
ip->tos = (ip->tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;
*tos = (*tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;
/* This functionality is useless in stock 2.0.x series, but we don't /* This functionality is useless in stock 2.0.x series, but we don't
* discard the mark thing altogether, to avoid breaking ipchains (and, * discard the mark thing altogether, to avoid breaking ipchains (and,
* more importantly, the ipfwadm wrapper) --PR */ * more importantly, the ipfwadm wrapper) --PR */
if (f->ipfw.fw_flg & IP_FW_F_MARKABS) { if (f->ipfw.fw_flg & IP_FW_F_MARKABS) {
skb->nfmark = f->ipfw.fw_mark; (*pskb)->nfmark = f->ipfw.fw_mark;
} else { } else {
skb->nfmark += f->ipfw.fw_mark; (*pskb)->nfmark += f->ipfw.fw_mark;
} }
if (f->ipfw.fw_flg & IP_FW_F_NETLINK) { if (f->ipfw.fw_flg & IP_FW_F_NETLINK) {
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE) #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)) size_t len = min_t(unsigned int, f->ipfw.fw_outputsize, ntohs((*pskb)->nh.iph->tot_len))
+ sizeof(__u32) + sizeof(skb->nfmark) + IFNAMSIZ; + sizeof(__u32) + sizeof((*pskb)->nfmark) + IFNAMSIZ;
struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC); struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC);
duprintf("Sending packet out NETLINK (length = %u).\n", duprintf("Sending packet out NETLINK (length = %u).\n",
...@@ -545,10 +554,13 @@ ip_fw_domatch(struct ip_fwkernel *f, ...@@ -545,10 +554,13 @@ ip_fw_domatch(struct ip_fwkernel *f,
/* Prepend length, mark & interface */ /* Prepend length, mark & interface */
skb_put(outskb, len); skb_put(outskb, len);
*((__u32 *)outskb->data) = (__u32)len; *((__u32 *)outskb->data) = (__u32)len;
*((__u32 *)(outskb->data+sizeof(__u32))) = skb->nfmark; *((__u32 *)(outskb->data+sizeof(__u32))) =
(*pskb)->nfmark;
strcpy(outskb->data+sizeof(__u32)*2, rif); strcpy(outskb->data+sizeof(__u32)*2, rif);
memcpy(outskb->data+sizeof(__u32)*2+IFNAMSIZ, ip, skb_copy_bits(*pskb,
len-(sizeof(__u32)*2+IFNAMSIZ)); ((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); netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC);
} }
else { else {
...@@ -571,22 +583,18 @@ ip_fw_domatch(struct ip_fwkernel *f, ...@@ -571,22 +583,18 @@ ip_fw_domatch(struct ip_fwkernel *f,
* user checking mode (counters are not updated, TOS & mark not done). * user checking mode (counters are not updated, TOS & mark not done).
*/ */
static int static int
ip_fw_check(struct iphdr *ip, ip_fw_check(const char *rif,
const char *rif,
__u16 *redirport, __u16 *redirport,
struct ip_chain *chain, struct ip_chain *chain,
struct sk_buff *skb, struct sk_buff **pskb,
unsigned int slot, unsigned int slot,
int testing) 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; __u32 src, dst;
__u16 src_port = 0xFFFF, dst_port = 0xFFFF; __u16 src_port = 0xFFFF, dst_port = 0xFFFF;
char tcpsyn=0; char tcpsyn=0;
__u16 offset; __u16 offset;
unsigned char oldtos; unsigned char tos;
struct ip_fwkernel *f; struct ip_fwkernel *f;
int ret = FW_SKIP+2; int ret = FW_SKIP+2;
unsigned int count; unsigned int count;
...@@ -598,7 +606,7 @@ ip_fw_check(struct iphdr *ip, ...@@ -598,7 +606,7 @@ ip_fw_check(struct iphdr *ip,
* rule is also a fragment-specific rule, non-fragments won't * rule is also a fragment-specific rule, non-fragments won't
* match it. */ * match it. */
offset = ntohs(ip->frag_off) & IP_OFFSET; offset = ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET;
/* /*
* Don't allow a fragment of TCP 8 bytes in. Nobody * Don't allow a fragment of TCP 8 bytes in. Nobody
...@@ -606,10 +614,10 @@ ip_fw_check(struct iphdr *ip, ...@@ -606,10 +614,10 @@ ip_fw_check(struct iphdr *ip,
* in by doing a flag overwrite to pass the direction * in by doing a flag overwrite to pass the direction
* checks. * checks.
*/ */
if (offset == 1 && ip->protocol == IPPROTO_TCP) { if (offset == 1 && (*pskb)->nh.iph->protocol == IPPROTO_TCP) {
if (!testing && net_ratelimit()) { if (!testing && net_ratelimit()) {
printk("Suspect TCP fragment.\n"); printk("Suspect TCP fragment.\n");
dump_packet(ip,rif,NULL,NULL,0,0,0,0); dump_packet(pskb,rif,NULL,NULL,0,0,0,0);
} }
return FW_BLOCK; return FW_BLOCK;
} }
...@@ -621,7 +629,7 @@ ip_fw_check(struct iphdr *ip, ...@@ -621,7 +629,7 @@ ip_fw_check(struct iphdr *ip,
*/ */
if (offset == 0) { if (offset == 0) {
unsigned int size_req; unsigned int size_req;
switch (ip->protocol) { switch ((*pskb)->nh.iph->protocol) {
case IPPROTO_TCP: case IPPROTO_TCP:
/* Don't care about things past flags word */ /* Don't care about things past flags word */
size_req = 16; size_req = 16;
...@@ -640,18 +648,19 @@ ip_fw_check(struct iphdr *ip, ...@@ -640,18 +648,19 @@ ip_fw_check(struct iphdr *ip,
* used to rewrite port information, and thus should * used to rewrite port information, and thus should
* be blocked. * be blocked.
*/ */
if (ntohs(ip->tot_len) < (ip->ihl<<2)+size_req) { if (ntohs((*pskb)->nh.iph->tot_len) <
((*pskb)->nh.iph->ihl<<2)+size_req) {
if (!testing && net_ratelimit()) { if (!testing && net_ratelimit()) {
printk("Suspect short first fragment.\n"); printk("Suspect short first fragment.\n");
dump_packet(ip,rif,NULL,NULL,0,0,0,0); dump_packet(pskb,rif,NULL,NULL,0,0,0,0);
} }
return FW_BLOCK; return FW_BLOCK;
} }
} }
src = ip->saddr; src = (*pskb)->nh.iph->saddr;
dst = ip->daddr; dst = (*pskb)->nh.iph->daddr;
oldtos = ip->tos; tos = (*pskb)->nh.iph->tos;
/* /*
* If we got interface from which packet came * If we got interface from which packet came
...@@ -662,47 +671,68 @@ ip_fw_check(struct iphdr *ip, ...@@ -662,47 +671,68 @@ ip_fw_check(struct iphdr *ip,
*/ */
dprintf("Packet "); dprintf("Packet ");
switch(ip->protocol) switch ((*pskb)->nh.iph->protocol) {
{
case IPPROTO_TCP: case IPPROTO_TCP:
dprintf("TCP "); dprintf("TCP ");
if (!offset) { if (!offset) {
src_port=ntohs(tcp->source); struct tcphdr tcph;
dst_port=ntohs(tcp->dest);
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 /* Connection initilisation can only
* be made when the syn bit is set and * be made when the syn bit is set and
* neither of the ack or reset is * neither of the ack or reset is
* set. */ * set. */
if(tcp->syn && !(tcp->ack || tcp->rst)) if (tcph.syn && !(tcph.ack || tcph.rst))
tcpsyn=1; tcpsyn = 1;
} }
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
dprintf("UDP "); dprintf("UDP ");
if (!offset) { if (!offset) {
src_port=ntohs(udp->source); struct udphdr udph;
dst_port=ntohs(udp->dest);
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; break;
case IPPROTO_ICMP: case IPPROTO_ICMP:
if (!offset) { if (!offset) {
src_port=(__u16)icmp->type; struct icmphdr icmph;
dst_port=(__u16)icmp->code;
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 "); dprintf("ICMP ");
break; break;
default: default:
dprintf("p=%d ",ip->protocol); dprintf("p=%d ", (*pskb)->nh.iph->protocol);
break; break;
} }
#ifdef DEBUG_IP_FIREWALL #ifdef DEBUG_IP_FIREWALL
print_ip(ip->saddr); print_ip((*pskb)->nh.iph->saddr);
if (offset) if (offset)
dprintf(":fragment (%i) ", ((int)offset)<<2); dprintf(":fragment (%i) ", ((int)offset)<<2);
else if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP else if ((*pskb)->nh.iph->protocol == IPPROTO_TCP ||
|| ip->protocol==IPPROTO_ICMP) (*pskb)->nh.iph->protocol == IPPROTO_UDP ||
(*pskb)->nh.iph->protocol == IPPROTO_ICMP)
dprintf(":%hu:%hu", src_port, dst_port); dprintf(":%hu:%hu", src_port, dst_port);
dprintf("\n"); dprintf("\n");
#endif #endif
...@@ -715,13 +745,14 @@ ip_fw_check(struct iphdr *ip, ...@@ -715,13 +745,14 @@ ip_fw_check(struct iphdr *ip,
count = 0; count = 0;
for (; f; f = f->next) { for (; f; f = f->next) {
count++; count++;
if (ip_rule_match(f,rif,ip, if (ip_rule_match(f, rif, pskb,
tcpsyn,src_port,dst_port,offset)) { tcpsyn, src_port, dst_port,
offset)) {
if (!testing if (!testing
&& !ip_fw_domatch(f, ip, rif, chain->label, && !ip_fw_domatch(f, rif, chain->label,
skb, slot, pskb, slot,
src_port, dst_port, src_port, dst_port,
count, tcpsyn)) { count, tcpsyn, &tos)) {
ret = FW_BLOCK; ret = FW_BLOCK;
cleanup(chain, 0, slot); cleanup(chain, 0, slot);
goto out; goto out;
...@@ -780,7 +811,7 @@ ip_fw_check(struct iphdr *ip, ...@@ -780,7 +811,7 @@ ip_fw_check(struct iphdr *ip,
if (!testing) { if (!testing) {
chain->reent[slot].counters.pcnt++; chain->reent[slot].counters.pcnt++;
chain->reent[slot].counters.bcnt chain->reent[slot].counters.bcnt
+= ntohs(ip->tot_len); += ntohs((*pskb)->nh.iph->tot_len);
} }
} }
} }
...@@ -790,10 +821,16 @@ ip_fw_check(struct iphdr *ip, ...@@ -790,10 +821,16 @@ ip_fw_check(struct iphdr *ip,
if (!testing) FWC_READ_UNLOCK(&ip_fw_lock); if (!testing) FWC_READ_UNLOCK(&ip_fw_lock);
/* Recalculate checksum if not going to reject, and TOS changed. */ /* Recalculate checksum if not going to reject, and TOS changed. */
if (ip->tos != oldtos if ((*pskb)->nh.iph->tos != tos
&& ret != FW_REJECT && ret != FW_BLOCK && ret != FW_REJECT && ret != FW_BLOCK
&& !testing) && !testing) {
ip_send_check(ip); 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 (ret == FW_REDIRECT && redirport) {
if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) { if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) {
...@@ -1349,18 +1386,40 @@ int ip_fw_ctl(int cmd, void *m, int len) ...@@ -1349,18 +1386,40 @@ int ip_fw_ctl(int cmd, void *m, int len)
if ((chain = find_label(new->fwt_label)) == NULL) if ((chain = find_label(new->fwt_label)) == NULL)
ret = ENOENT; ret = ENOENT;
else { else {
struct sk_buff *tmp_skb;
int hdrlen;
hdrlen = sizeof(struct ip_fwpkt) -
sizeof(struct in_addr) -
IFNAMSIZ;
ip = &(new->fwt_packet.fwp_iph); ip = &(new->fwt_packet.fwp_iph);
if (ip->ihl != sizeof(struct iphdr) / sizeof(int)) { /* Fix this one up by hand, who knows how many
duprintf("ip_fw_ctl: ip->ihl=%d, want %d\n", * tools will break if we start to barf on this.
ip->ihl, */
sizeof(struct iphdr) / sizeof(int)); if (ntohs(ip->tot_len) > hdrlen)
ret = EINVAL; ip->tot_len = htons(hdrlen);
}
else { if (ip->ihl != sizeof(struct iphdr) / sizeof(u32)) {
ret = ip_fw_check(ip, new->fwt_packet.fwp_vianame, duprintf("ip_fw_ctl: ip->ihl=%d, want %d\n",
ip->ihl,
sizeof(struct iphdr) / sizeof(u32));
ret = EINVAL;
} else if ((tmp_skb = alloc_skb(hdrlen,
GFP_ATOMIC)) == NULL) {
duprintf("ip_fw_ctl: tmp_skb alloc failure\n");
ret = EFAULT;
} else {
skb_reserve(tmp_skb, hdrlen);
skb_push(tmp_skb, hdrlen);
memcpy(tmp_skb->data, ip, hdrlen);
tmp_skb->nh.raw =
(unsigned char *) tmp_skb->data;
ret = ip_fw_check(new->fwt_packet.fwp_vianame,
NULL, chain, NULL, chain,
NULL, SLOT_NUMBER(), 1); &tmp_skb, SLOT_NUMBER(), 1);
kfree_skb(tmp_skb);
switch (ret) { switch (ret) {
case FW_ACCEPT: case FW_ACCEPT:
ret = 0; break; ret = 0; break;
...@@ -1690,41 +1749,37 @@ static int ip_chain_name_procinfo(char *buffer, char **start, ...@@ -1690,41 +1749,37 @@ static int ip_chain_name_procinfo(char *buffer, char **start,
* Interface to the generic firewall chains. * Interface to the generic firewall chains.
*/ */
int ipfw_input_check(struct firewall_ops *this, int pf, int ipfw_input_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
return ip_fw_check(phdr, dev->name, return ip_fw_check(dev->name,
arg, IP_FW_INPUT_CHAIN, *pskb, SLOT_NUMBER(), 0); arg, IP_FW_INPUT_CHAIN, pskb, SLOT_NUMBER(), 0);
} }
int ipfw_output_check(struct firewall_ops *this, int pf, int ipfw_output_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
/* Locally generated bogus packets by root. <SIGH>. */ /* Locally generated bogus packets by root. <SIGH>. */
if (((struct iphdr *)phdr)->ihl * 4 < sizeof(struct iphdr) if ((*pskb)->len < sizeof(struct iphdr) ||
|| (*pskb)->len < sizeof(struct iphdr)) (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
return FW_ACCEPT; return FW_ACCEPT;
return ip_fw_check(phdr, dev->name, return ip_fw_check(dev->name,
arg, IP_FW_OUTPUT_CHAIN, *pskb, SLOT_NUMBER(), 0); arg, IP_FW_OUTPUT_CHAIN, pskb, SLOT_NUMBER(), 0);
} }
int ipfw_forward_check(struct firewall_ops *this, int pf, int ipfw_forward_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
return ip_fw_check(phdr, dev->name, return ip_fw_check(dev->name,
arg, IP_FW_FORWARD_CHAIN, *pskb, SLOT_NUMBER(), 0); arg, IP_FW_FORWARD_CHAIN, pskb, SLOT_NUMBER(), 0);
} }
struct firewall_ops ipfw_ops= struct firewall_ops ipfw_ops = {
{ .fw_forward = ipfw_forward_check,
NULL, .fw_input = ipfw_input_check,
ipfw_forward_check, .fw_output = ipfw_output_check,
ipfw_input_check,
ipfw_output_check,
NULL,
NULL
}; };
int ipfw_init_or_cleanup(int init) int ipfw_init_or_cleanup(int init)
......
...@@ -126,6 +126,7 @@ ...@@ -126,6 +126,7 @@
#include <linux/netfilter_ipv4/ipfwadm_core.h> #include <linux/netfilter_ipv4/ipfwadm_core.h>
#include <linux/netfilter_ipv4/compat_firewall.h> #include <linux/netfilter_ipv4/compat_firewall.h>
#include <linux/netfilter_ipv4/lockhelp.h> #include <linux/netfilter_ipv4/lockhelp.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
...@@ -259,18 +260,18 @@ static char *rule_name(struct ip_fw *f, int mode, char *buf) ...@@ -259,18 +260,18 @@ static char *rule_name(struct ip_fw *f, int mode, char *buf)
} }
} }
static void print_packet(struct iphdr *ip, static void print_packet(struct sk_buff **pskb,
u16 src_port, u16 dst_port, u16 icmp_type, u16 src_port, u16 dst_port, u16 icmp_type,
char *chain, char *rule, char *devname) char *chain, char *rule, char *devname)
{ {
__u32 *opt = (__u32 *) (ip + 1); __u32 *opt = (__u32 *) ((*pskb)->nh.iph + 1);
int opti; int opti;
__u16 foff = ntohs(ip->frag_off); __u16 foff = ntohs((*pskb)->nh.iph->frag_off);
int protocol = (*pskb)->nh.iph->protocol;
printk(KERN_INFO "IP %s %s%s", chain, rule, devname); printk(KERN_INFO "IP %s %s%s", chain, rule, devname);
switch(ip->protocol) switch (protocol) {
{
case IPPROTO_TCP: case IPPROTO_TCP:
printk(" TCP "); printk(" TCP ");
break; break;
...@@ -281,22 +282,28 @@ static void print_packet(struct iphdr *ip, ...@@ -281,22 +282,28 @@ static void print_packet(struct iphdr *ip,
printk(" ICMP/%d ", icmp_type); printk(" ICMP/%d ", icmp_type);
break; break;
default: default:
printk(" PROTO=%d ", ip->protocol); printk(" PROTO=%d ", protocol);
break; break;
} };
print_ip(ip->saddr);
if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP) print_ip((*pskb)->nh.iph->saddr);
if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
printk(":%hu", src_port); printk(":%hu", src_port);
printk(" "); printk(" ");
print_ip(ip->daddr); print_ip((*pskb)->nh.iph->daddr);
if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP) if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
printk(":%hu", dst_port); printk(":%hu", dst_port);
printk(" L=%hu S=0x%2.2hX I=%hu FO=0x%4.4hX T=%hu", printk(" L=%hu S=0x%2.2hX I=%hu FO=0x%4.4hX T=%hu",
ntohs(ip->tot_len), ip->tos, ntohs(ip->id), ntohs((*pskb)->nh.iph->tot_len),
foff & IP_OFFSET, ip->ttl); (*pskb)->nh.iph->tos,
if (foff & IP_DF) printk(" DF=1"); ntohs((*pskb)->nh.iph->id),
if (foff & IP_MF) printk(" MF=1"); foff & IP_OFFSET,
for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++) (*pskb)->nh.iph->ttl);
if (foff & IP_DF)
printk(" DF=1");
if (foff & IP_MF)
printk(" MF=1");
for (opti = 0; opti < ((*pskb)->nh.iph->ihl - sizeof(struct iphdr) / 4); opti++)
printk(" O=0x%8.8X", *opt++); printk(" O=0x%8.8X", *opt++);
printk("\n"); printk("\n");
} }
...@@ -314,13 +321,11 @@ static void print_packet(struct iphdr *ip, ...@@ -314,13 +321,11 @@ static void print_packet(struct iphdr *ip,
*/ */
int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, int ip_fw_chk(struct sk_buff **pskb,
struct net_device *rif, __u16 *redirport,
struct ip_fw *chain, int policy, int mode) struct ip_fw *chain, int policy, int mode)
{ {
struct ip_fw *f; 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; __u32 src, dst;
__u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF; __u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
unsigned short f_prt=0, prt; unsigned short f_prt=0, prt;
...@@ -328,6 +333,7 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -328,6 +333,7 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
unsigned short offset; unsigned short offset;
int answer; int answer;
unsigned char tosand, tosxor; unsigned char tosand, tosxor;
int protocol;
/* /*
* If the chain is empty follow policy. The BSD one * If the chain is empty follow policy. The BSD one
...@@ -335,9 +341,6 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -335,9 +341,6 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
* flushing and rebuilding the tables. * flushing and rebuilding the tables.
*/ */
src = ip->saddr;
dst = ip->daddr;
/* /*
* This way we handle fragmented packets. * This way we handle fragmented packets.
* we ignore all fragments but the first one * we ignore all fragments but the first one
...@@ -352,7 +355,8 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -352,7 +355,8 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
* of system. * of system.
*/ */
offset = ntohs(ip->frag_off) & IP_OFFSET; offset = ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET;
protocol = (*pskb)->nh.iph->protocol;
/* /*
* Don't allow a fragment of TCP 8 bytes in. Nobody * Don't allow a fragment of TCP 8 bytes in. Nobody
...@@ -361,19 +365,21 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -361,19 +365,21 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
* checks. * checks.
*/ */
if (offset == 1 && ip->protocol == IPPROTO_TCP) if (offset == 1 && protocol == IPPROTO_TCP)
return FW_BLOCK; return FW_BLOCK;
if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) && if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) &&
(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP || (protocol == IPPROTO_TCP ||
ip->protocol == IPPROTO_ICMP)) protocol == IPPROTO_UDP ||
protocol == IPPROTO_ICMP))
return FW_ACCEPT; return FW_ACCEPT;
/* /*
* Header fragment for TCP is too small to check the bits. * Header fragment for TCP is too small to check the bits.
*/ */
if(ip->protocol==IPPROTO_TCP && (ip->ihl<<2)+16 > ntohs(ip->tot_len)) if (protocol == IPPROTO_TCP &&
((*pskb)->nh.iph->ihl<<2)+16 > ntohs((*pskb)->nh.iph->tot_len))
return FW_BLOCK; return FW_BLOCK;
/* /*
...@@ -382,11 +388,13 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -382,11 +388,13 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
* But only too short for a packet with ports... * 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)) else if ((ntohs((*pskb)->nh.iph->tot_len) <
8 + ((*pskb)->nh.iph->ihl << 2)) &&
(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP))
return FW_BLOCK; return FW_BLOCK;
src = ip->saddr; src = (*pskb)->nh.iph->saddr;
dst = ip->daddr; dst = (*pskb)->nh.iph->daddr;
/* /*
* If we got interface from which packet came * If we got interface from which packet came
...@@ -397,54 +405,76 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -397,54 +405,76 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
*/ */
dprintf1("Packet "); dprintf1("Packet ");
switch(ip->protocol) switch (protocol) {
{
case IPPROTO_TCP: case IPPROTO_TCP:
dprintf1("TCP "); dprintf1("TCP ");
/* ports stay 0xFFFF if it is not the first fragment */ /* ports stay 0xFFFF if it is not the first fragment */
if (!offset) { if (!offset) {
src_port=ntohs(tcp->source); struct tcphdr tcph;
dst_port=ntohs(tcp->dest);
if(!tcp->ack && !tcp->rst) 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);
if(!tcph.ack && !tcph.rst)
/* We do NOT have ACK, value TRUE */ /* We do NOT have ACK, value TRUE */
notcpack=1; notcpack = 1;
if(!tcp->syn || !notcpack) if(!tcph.syn || !notcpack)
/* We do NOT have SYN, value TRUE */ /* We do NOT have SYN, value TRUE */
notcpsyn=1; notcpsyn = 1;
} }
prt=IP_FW_F_TCP; prt = IP_FW_F_TCP;
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
dprintf1("UDP "); dprintf1("UDP ");
/* ports stay 0xFFFF if it is not the first fragment */ /* ports stay 0xFFFF if it is not the first fragment */
if (!offset) { if (!offset) {
src_port=ntohs(udp->source); struct udphdr udph;
dst_port=ntohs(udp->dest);
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);
} }
prt=IP_FW_F_UDP; prt = IP_FW_F_UDP;
break; break;
case IPPROTO_ICMP: case IPPROTO_ICMP:
/* icmp_type stays 255 if it is not the first fragment */ /* icmp_type stays 255 if it is not the first fragment */
if (!offset) if (!offset) {
icmp_type=(__u16)(icmp->type); struct icmphdr icmph;
dprintf2("ICMP:%d ",icmp_type);
prt=IP_FW_F_ICMP; if (skb_copy_bits(*pskb,
(*pskb)->nh.iph->ihl * 4,
&icmph, sizeof(icmph)))
return FW_BLOCK;
icmp_type = (__u16) icmph.type;
}
dprintf2("ICMP:%d ", icmp_type);
prt = IP_FW_F_ICMP;
break; break;
default: default:
dprintf2("p=%d ",ip->protocol); dprintf2("p=%d ", protocol);
prt=IP_FW_F_ALL; prt = IP_FW_F_ALL;
break; break;
} }
#ifdef DEBUG_IP_FIREWALL #ifdef DEBUG_IP_FIREWALL
dprint_ip(ip->saddr); dprint_ip(src);
if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
/* This will print 65535 when it is not the first fragment! */ /* This will print 65535 when it is not the first fragment! */
dprintf2(":%d ", src_port); dprintf2(":%d ", src_port);
dprint_ip(ip->daddr); dprint_ip(dst);
if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
/* This will print 65535 when it is not the first fragment! */ /* This will print 65535 when it is not the first fragment! */
dprintf2(":%d ",dst_port); dprintf2(":%d ", dst_port);
dprintf1("\n"); dprintf1("\n");
#endif #endif
...@@ -453,8 +483,7 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -453,8 +483,7 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
else else
WRITE_LOCK(&ip_fw_lock); WRITE_LOCK(&ip_fw_lock);
for (f=chain;f;f=f->fw_next) for (f = chain; f; f = f->fw_next) {
{
/* /*
* This is a bit simpler as we don't have to walk * This is a bit simpler as we don't have to walk
* an interface chain as you do in BSD - same logic * an interface chain as you do in BSD - same logic
...@@ -474,14 +503,14 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -474,14 +503,14 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
*/ */
match = 0x00; match = 0x00;
if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr if ((src & f->fw_smsk.s_addr) == f->fw_src.s_addr &&
&& (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) (dst & f->fw_dmsk.s_addr) == f->fw_dst.s_addr)
/* normal direction */ /* normal direction */
match |= 0x01; match |= 0x01;
if ((f->fw_flg & IP_FW_F_BIDIR) && if ((f->fw_flg & IP_FW_F_BIDIR) &&
(dst&f->fw_smsk.s_addr)==f->fw_src.s_addr (dst & f->fw_smsk.s_addr) == f->fw_src.s_addr &&
&& (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) (src & f->fw_dmsk.s_addr) == f->fw_dst.s_addr)
/* reverse direction */ /* reverse direction */
match |= 0x02; match |= 0x02;
...@@ -491,9 +520,8 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -491,9 +520,8 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
/* /*
* Look for a VIA device match * Look for a VIA device match
*/ */
if(f->fw_viadev) if (f->fw_viadev) {
{ if (rif != f->fw_viadev)
if(rif!=f->fw_viadev)
continue; /* Mismatch */ continue; /* Mismatch */
} }
...@@ -560,14 +588,13 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -560,14 +588,13 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
continue; continue;
f_prt=f->fw_flg&IP_FW_F_KIND; f_prt=f->fw_flg&IP_FW_F_KIND;
if (f_prt!=IP_FW_F_ALL) if (f_prt != IP_FW_F_ALL) {
{
/* /*
* Specific firewall - packet's protocol * Specific firewall - packet's protocol
* must match firewall's. * must match firewall's.
*/ */
if(prt!=f_prt) if (prt != f_prt)
continue; continue;
if((prt==IP_FW_F_ICMP && if((prt==IP_FW_F_ICMP &&
...@@ -592,14 +619,14 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -592,14 +619,14 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
{ {
char buf[16]; char buf[16];
print_packet(ip, src_port, dst_port, icmp_type, print_packet(pskb, src_port, dst_port, icmp_type,
chain_name(chain, mode), chain_name(chain, mode),
rule_name(f, mode, buf), rule_name(f, mode, buf),
rif ? rif->name : "-"); rif ? rif->name : "-");
} }
#endif #endif
if (mode != IP_FW_MODE_CHK) { if (mode != IP_FW_MODE_CHK) {
f->fw_bcnt+=ntohs(ip->tot_len); f->fw_bcnt += ntohs((*pskb)->nh.iph->tot_len);
f->fw_pcnt++; f->fw_pcnt++;
} }
if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)))
...@@ -614,23 +641,30 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -614,23 +641,30 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
* of firewall. * of firewall.
*/ */
if (f!=NULL) { if (f != NULL) {
policy=f->fw_flg; policy = f->fw_flg;
tosand=f->fw_tosand; tosand = f->fw_tosand;
tosxor=f->fw_tosxor; tosxor = f->fw_tosxor;
} else { } else {
tosand=0xFF; tosand = 0xFF;
tosxor=0x00; tosxor = 0x00;
} }
if (policy&IP_FW_F_ACCEPT) { if (policy & IP_FW_F_ACCEPT) {
/* Adjust priority and recompute checksum */ /* Adjust priority and recompute checksum */
__u8 old_tos = ip->tos; __u8 tos = (*pskb)->nh.iph->tos;
ip->tos = (old_tos & tosand) ^ tosxor;
if (ip->tos != old_tos) if (((tos & tosand) ^ tosxor) != tos) {
ip_send_check(ip); if (!skb_ip_make_writable(pskb,
offsetof(struct iphdr, tos)+1))
goto drop_it;
(*pskb)->nh.iph->tos = (tos & tosand) ^ tosxor;
ip_send_check((*pskb)->nh.iph);
}
#ifdef CONFIG_IP_TRANSPARENT_PROXY #ifdef CONFIG_IP_TRANSPARENT_PROXY
if (policy&IP_FW_F_REDIR) { if (policy & IP_FW_F_REDIR) {
if (redirport) if (redirport)
if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) { if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) {
/* Wildcard redirection. /* Wildcard redirection.
...@@ -643,30 +677,36 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport, ...@@ -643,30 +677,36 @@ int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
} else } else
#endif #endif
#ifdef CONFIG_IP_MASQUERADE #ifdef CONFIG_IP_MASQUERADE
if (policy&IP_FW_F_MASQ) if (policy & IP_FW_F_MASQ)
answer = FW_MASQUERADE; answer = FW_MASQUERADE;
else else
#endif #endif
answer = FW_ACCEPT; answer = FW_ACCEPT;
} else if(policy&IP_FW_F_ICMPRPL) } else if (policy & IP_FW_F_ICMPRPL)
answer = FW_REJECT; answer = FW_REJECT;
else else {
drop_it:
answer = FW_BLOCK; answer = FW_BLOCK;
}
#ifdef CONFIG_IP_FIREWALL_NETLINK #ifdef CONFIG_IP_FIREWALL_NETLINK
if((policy&IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK)) if ((policy & IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK))
{ {
struct sk_buff *skb=alloc_skb(128, (mode == IP_FW_MODE_CHK) ? struct sk_buff *skb = alloc_skb(128,
GFP_KERNEL : GFP_ATOMIC); (mode == IP_FW_MODE_CHK) ?
if(skb) GFP_KERNEL : GFP_ATOMIC);
{ if (skb) {
int len = min_t(unsigned int, int len = min_t(unsigned int,
128, ntohs(ip->tot_len)); 128,
ntohs((*pskb)->nh.iph->tot_len));
skb_put(skb,len);
memcpy(skb->data,ip,len); skb_put(skb, len);
if(netlink_post(NETLINK_FIREWALL, skb)) skb_copy_bits(*pskb,
((char *)(*pskb)->nh.iph -
(char *)(*pskb)->data),
skb->data, len);
if (netlink_post(NETLINK_FIREWALL, skb))
kfree_skb(skb); kfree_skb(skb);
} }
} }
...@@ -706,6 +746,9 @@ static void free_fw_chain(struct ip_fw *volatile* chainptr) ...@@ -706,6 +746,9 @@ static void free_fw_chain(struct ip_fw *volatile* chainptr)
struct ip_fw *ftmp; struct ip_fw *ftmp;
ftmp = *chainptr; ftmp = *chainptr;
*chainptr = ftmp->fw_next; *chainptr = ftmp->fw_next;
if (ftmp->fw_viadev
&& ftmp->fw_viadev != (struct net_device *)-1)
dev_put(ftmp->fw_viadev);
kfree(ftmp); kfree(ftmp);
/* We will block in cleanup's unregister sockopt if unloaded, /* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */ so this is safe. */
...@@ -856,6 +899,9 @@ static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl) ...@@ -856,6 +899,9 @@ static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl)
if(matches) if(matches)
{ {
was_found=1; was_found=1;
if (ftmp->fw_viadev
&& ftmp->fw_viadev != (struct net_device *)-1)
dev_put(ftmp->fw_viadev);
if (ltmp) if (ltmp)
{ {
ltmp->fw_next=ftmp->fw_next; ltmp->fw_next=ftmp->fw_next;
...@@ -1035,9 +1081,15 @@ int ip_fw_ctl(int stage, void *m, int len) ...@@ -1035,9 +1081,15 @@ int ip_fw_ctl(int stage, void *m, int len)
if ( cmd == IP_FW_CHECK ) if ( cmd == IP_FW_CHECK )
{ {
struct sk_buff *tmp_skb;
struct net_device *viadev; struct net_device *viadev;
struct ip_fwpkt *ipfwp; struct ip_fwpkt *ipfwp;
struct iphdr *ip; struct iphdr *ip;
int hdrlen, ret;
hdrlen = sizeof(struct ip_fwpkt) -
sizeof(struct in_addr) -
IFNAMSIZ;
if ( len != sizeof(struct ip_fwpkt) ) if ( len != sizeof(struct ip_fwpkt) )
{ {
...@@ -1061,12 +1113,34 @@ int ip_fw_ctl(int stage, void *m, int len) ...@@ -1061,12 +1113,34 @@ int ip_fw_ctl(int stage, void *m, int len)
printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl, printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
sizeof(struct iphdr)/sizeof(int)); sizeof(struct iphdr)/sizeof(int));
#endif #endif
dev_put(viadev);
return(EINVAL); return(EINVAL);
} }
switch (ip_fw_chk(ip, viadev, NULL, *chains[fwtype], /* Fix this one up by hand, who knows how many
*policies[fwtype], IP_FW_MODE_CHK)) * tools will break if we start to barf on this.
{ */
if (ntohs(ip->tot_len) > hdrlen)
ip->tot_len = htons(hdrlen);
if ((tmp_skb = alloc_skb(hdrlen, GFP_ATOMIC)) == NULL) {
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: tmp_skb alloc failure\n");
#endif
dev_put(viadev);
return(EFAULT);
}
skb_reserve(tmp_skb, hdrlen);
skb_push(tmp_skb, hdrlen);
memcpy(tmp_skb->data, ip, hdrlen);
ret = ip_fw_chk(&tmp_skb, viadev, NULL, *chains[fwtype],
*policies[fwtype], IP_FW_MODE_CHK);
kfree_skb(tmp_skb);
dev_put(viadev);
switch (ret) {
case FW_ACCEPT: case FW_ACCEPT:
return(0); return(0);
case FW_REDIRECT: case FW_REDIRECT:
...@@ -1242,55 +1316,50 @@ static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset, ...@@ -1242,55 +1316,50 @@ static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
*/ */
int ipfw_input_check(struct firewall_ops *this, int pf, int ipfw_input_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
return ip_fw_chk(phdr, dev, arg, ip_fw_in_chain, ip_fw_in_policy, return ip_fw_chk(pskb, dev, arg, ip_fw_in_chain, ip_fw_in_policy,
IP_FW_MODE_FW); IP_FW_MODE_FW);
} }
int ipfw_output_check(struct firewall_ops *this, int pf, int ipfw_output_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy, return ip_fw_chk(pskb, dev, arg, ip_fw_out_chain, ip_fw_out_policy,
IP_FW_MODE_FW); IP_FW_MODE_FW);
} }
int ipfw_forward_check(struct firewall_ops *this, int pf, int ipfw_forward_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *phdr, void *arg, struct net_device *dev, void *arg,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
return ip_fw_chk(phdr, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy, return ip_fw_chk(pskb, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy,
IP_FW_MODE_FW); IP_FW_MODE_FW);
} }
#ifdef CONFIG_IP_ACCT #ifdef CONFIG_IP_ACCT
int ipfw_acct_in(struct firewall_ops *this, int pf, struct net_device *dev, int ipfw_acct_in(struct firewall_ops *this, int pf, struct net_device *dev,
void *phdr, void *arg, struct sk_buff **pskb) void *arg, struct sk_buff **pskb)
{ {
return ip_fw_chk(phdr,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN); return ip_fw_chk(pskb,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
} }
int ipfw_acct_out(struct firewall_ops *this, int pf, struct net_device *dev, int ipfw_acct_out(struct firewall_ops *this, int pf, struct net_device *dev,
void *phdr, void *arg, struct sk_buff **pskb) void *arg, struct sk_buff **pskb)
{ {
return ip_fw_chk(phdr,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT); return ip_fw_chk(pskb,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
} }
#endif #endif
struct firewall_ops ipfw_ops= struct firewall_ops ipfw_ops = {
{ .fw_forward = ipfw_forward_check,
NULL, .fw_input = ipfw_input_check,
ipfw_forward_check, .fw_output = ipfw_output_check,
ipfw_input_check,
ipfw_output_check,
#ifdef CONFIG_IP_ACCT #ifdef CONFIG_IP_ACCT
ipfw_acct_in, .fw_acct_in = ipfw_acct_in,
ipfw_acct_out .fw_acct_out = ipfw_acct_out,
#else
NULL,
NULL
#endif #endif
}; };
...@@ -1311,23 +1380,29 @@ int ipfw_device_event(struct notifier_block *this, unsigned long event, void *pt ...@@ -1311,23 +1380,29 @@ int ipfw_device_event(struct notifier_block *this, unsigned long event, void *pt
for (chn = 0; chn < IP_FW_CHAINS; chn++) for (chn = 0; chn < IP_FW_CHAINS; chn++)
for (fw = *chains[chn]; fw; fw = fw->fw_next) for (fw = *chains[chn]; fw; fw = fw->fw_next)
if ((fw->fw_vianame)[0] && !strncmp(devname, if ((fw->fw_vianame)[0] && !strncmp(devname,
fw->fw_vianame, IFNAMSIZ)) fw->fw_vianame, IFNAMSIZ)) {
dev_hold(dev);
fw->fw_viadev = dev; fw->fw_viadev = dev;
}
} else if (event == NETDEV_DOWN) { } else if (event == NETDEV_DOWN) {
for (chn = 0; chn < IP_FW_CHAINS; chn++) for (chn = 0; chn < IP_FW_CHAINS; chn++)
for (fw = *chains[chn]; fw; fw = fw->fw_next) for (fw = *chains[chn]; fw; fw = fw->fw_next)
/* we could compare just the pointers ... */ /* we could compare just the pointers ... */
if ((fw->fw_vianame)[0] && !strncmp(devname, if ((fw->fw_vianame)[0] && !strncmp(devname,
fw->fw_vianame, IFNAMSIZ)) fw->fw_vianame, IFNAMSIZ)){
if (fw->fw_viadev
&& fw->fw_viadev != (struct net_device *)-1)
dev_put(fw->fw_viadev);
fw->fw_viadev = (struct net_device*)-1; fw->fw_viadev = (struct net_device*)-1;
}
} }
WRITE_UNLOCK(&ip_fw_lock); WRITE_UNLOCK(&ip_fw_lock);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static struct notifier_block ipfw_dev_notifier={ static struct notifier_block ipfw_dev_notifier = {
.notifier_call = ipfw_device_event, .notifier_call = ipfw_device_event,
}; };
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment