Commit 473ba53f authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER]: Linearize iptables targets.

Adjusts the IPTables targets to handle non-linear packets.
ipt_ULOG untested.
parent 8dcfc9e3
......@@ -23,37 +23,31 @@ MODULE_LICENSE("GPL");
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_DSCP_info *dinfo = targinfo;
u_int8_t sh_dscp = ((dinfo->dscp << IPT_DSCP_SHIFT) & IPT_DSCP_MASK);
if ((iph->tos & IPT_DSCP_MASK) != sh_dscp) {
if (((*pskb)->nh.iph->tos & IPT_DSCP_MASK) != sh_dscp) {
u_int16_t diffs[2];
/* 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)
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
iph = (*pskb)->nh.iph;
}
diffs[0] = htons(iph->tos) ^ 0xFFFF;
iph->tos = (iph->tos & ~IPT_DSCP_MASK) | sh_dscp;
diffs[1] = htons(iph->tos);
iph->check = csum_fold(csum_partial((char *)diffs,
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
(*pskb)->nh.iph->tos = ((*pskb)->nh.iph->tos & ~IPT_DSCP_MASK)
| sh_dscp;
diffs[1] = htons((*pskb)->nh.iph->tos);
(*pskb)->nh.iph->check
= csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
iph->check^0xFFFF));
(*pskb)->nh.iph->check
^ 0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
......
......@@ -19,105 +19,85 @@
MODULE_LICENSE("GPL");
/* set ECT codepoint from IP header.
* return 0 in case there was no ECT codepoint
* return 1 in case ECT codepoint has been overwritten
* return < 0 in case there was error */
* return 0 if there was an error. */
static inline int
set_ect_ip(struct sk_buff **pskb, struct iphdr *iph,
const struct ipt_ECN_info *einfo)
set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
{
if ((iph->tos & IPT_ECN_IP_MASK)
if (((*pskb)->nh.iph->tos & IPT_ECN_IP_MASK)
!= (einfo->ip_ect & IPT_ECN_IP_MASK)) {
u_int16_t diffs[2];
/* 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;
iph = (*pskb)->nh.iph;
}
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return 0;
diffs[0] = htons(iph->tos) ^ 0xFFFF;
iph->tos = iph->tos & ~IPT_ECN_IP_MASK;
iph->tos = iph->tos | (einfo->ip_ect & IPT_ECN_IP_MASK);
diffs[1] = htons(iph->tos);
iph->check = csum_fold(csum_partial((char *)diffs,
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
(*pskb)->nh.iph->tos &= ~IPT_ECN_IP_MASK;
(*pskb)->nh.iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK);
diffs[1] = htons((*pskb)->nh.iph->tos);
(*pskb)->nh.iph->check
= csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
iph->check^0xFFFF));
(*pskb)->nh.iph->check
^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
return 1;
}
return 0;
return 1;
}
/* Return 0 if there was an error. */
static inline int
set_ect_tcp(struct sk_buff **pskb, struct iphdr *iph,
const struct ipt_ECN_info *einfo)
set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
{
struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
u_int16_t *tcpflags = (u_int16_t *)tcph + 6;
struct tcphdr tcph;
u_int16_t diffs[2];
/* 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;
iph = (*pskb)->nh.iph;
}
diffs[0] = *tcpflags;
/* Not enought header? */
if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, &tcph, sizeof(tcph))
< 0)
return 0;
if (einfo->operation & IPT_ECN_OP_SET_ECE
&& tcph->ece != einfo->proto.tcp.ece) {
tcph->ece = einfo->proto.tcp.ece;
}
diffs[0] = ((u_int16_t *)&tcph)[6];
if (einfo->operation & IPT_ECN_OP_SET_ECE)
tcph.ece = einfo->proto.tcp.ece;
if (einfo->operation & IPT_ECN_OP_SET_CWR
&& tcph->cwr != einfo->proto.tcp.cwr) {
tcph->cwr = einfo->proto.tcp.cwr;
}
if (einfo->operation & IPT_ECN_OP_SET_CWR)
tcph.cwr = einfo->proto.tcp.cwr;
diffs[1] = ((u_int16_t *)&tcph)[6];
if (diffs[0] != *tcpflags) {
/* Only mangle if it's changed. */
if (diffs[0] != diffs[1]) {
diffs[0] = diffs[0] ^ 0xFFFF;
diffs[1] = *tcpflags;
tcph->check = csum_fold(csum_partial((char *)diffs,
if (!skb_ip_make_writable(pskb,
(*pskb)->nh.iph->ihl*4+sizeof(tcph)))
return 0;
tcph.check = csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
tcph->check^0xFFFF));
tcph.check^0xFFFF));
memcpy((*pskb)->data + (*pskb)->nh.iph->ihl*4,
&tcph, sizeof(tcph));
(*pskb)->nfcache |= NFC_ALTERED;
return 1;
}
return 0;
return 1;
}
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_ECN_info *einfo = targinfo;
if (einfo->operation & IPT_ECN_OP_SET_IP)
set_ect_ip(pskb, iph, einfo);
if (!set_ect_ip(pskb, einfo))
return NF_DROP;
if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR)
&& iph->protocol == IPPROTO_TCP)
set_ect_tcp(pskb, iph, einfo);
&& (*pskb)->nh.iph->protocol == IPPROTO_TCP)
if (!set_ect_tcp(pskb, einfo))
return NF_DROP;
return IPT_CONTINUE;
}
......
......@@ -29,127 +29,151 @@ static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
/* One level of recursion won't kill us */
static void dump_packet(const struct ipt_log_info *info,
struct iphdr *iph, unsigned int len, int recurse)
const struct sk_buff *skb,
unsigned int iphoff)
{
void *protoh = (u_int32_t *)iph + iph->ihl;
unsigned int datalen = len - iph->ihl * 4;
struct iphdr iph;
if (skb_copy_bits(skb, iphoff, &iph, sizeof(iph)) < 0) {
printk("TRUNCATED");
return;
}
/* Important fields:
* TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
/* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
NIPQUAD(iph.saddr), NIPQUAD(iph.daddr));
/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK,
iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id));
ntohs(iph.tot_len), iph.tos & IPTOS_TOS_MASK,
iph.tos & IPTOS_PREC_MASK, iph.ttl, ntohs(iph.id));
/* Max length: 6 "CE DF MF " */
if (ntohs(iph->frag_off) & IP_CE)
if (ntohs(iph.frag_off) & IP_CE)
printk("CE ");
if (ntohs(iph->frag_off) & IP_DF)
if (ntohs(iph.frag_off) & IP_DF)
printk("DF ");
if (ntohs(iph->frag_off) & IP_MF)
if (ntohs(iph.frag_off) & IP_MF)
printk("MF ");
/* Max length: 11 "FRAG:65535 " */
if (ntohs(iph->frag_off) & IP_OFFSET)
printk("FRAG:%u ", ntohs(iph->frag_off) & IP_OFFSET);
if (ntohs(iph.frag_off) & IP_OFFSET)
printk("FRAG:%u ", ntohs(iph.frag_off) & IP_OFFSET);
if ((info->logflags & IPT_LOG_IPOPT)
&& iph->ihl * 4 != sizeof(struct iphdr)) {
unsigned int i;
&& iph.ihl * 4 != sizeof(struct iphdr)) {
unsigned char opt[4 * 15 - sizeof(struct iphdr)];
unsigned int i, optsize;
optsize = iph.ihl * 4 - sizeof(struct iphdr);
if (skb_copy_bits(skb, iphoff+sizeof(iph), opt, optsize) < 0) {
printk("TRUNCATED");
return;
}
/* Max length: 127 "OPT (" 15*4*2chars ") " */
printk("OPT (");
for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++)
printk("%02X", ((u_int8_t *)iph)[i]);
for (i = 0; i < optsize; i++)
printk("%02X", opt[i]);
printk(") ");
}
switch (iph->protocol) {
switch (iph.protocol) {
case IPPROTO_TCP: {
struct tcphdr *tcph = protoh;
struct tcphdr tcph;
/* Max length: 10 "PROTO=TCP " */
printk("PROTO=TCP ");
if (ntohs(iph->frag_off) & IP_OFFSET)
if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
if (datalen < sizeof (*tcph)) {
printk("INCOMPLETE [%u bytes] ", datalen);
if (skb_copy_bits(skb, iphoff+iph.ihl*4, &tcph, sizeof(tcph))
< 0) {
printk("INCOMPLETE [%u bytes] ",
skb->len - iphoff - iph.ihl*4);
break;
}
/* Max length: 20 "SPT=65535 DPT=65535 " */
printk("SPT=%u DPT=%u ",
ntohs(tcph->source), ntohs(tcph->dest));
ntohs(tcph.source), ntohs(tcph.dest));
/* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
if (info->logflags & IPT_LOG_TCPSEQ)
printk("SEQ=%u ACK=%u ",
ntohl(tcph->seq), ntohl(tcph->ack_seq));
ntohl(tcph.seq), ntohl(tcph.ack_seq));
/* Max length: 13 "WINDOW=65535 " */
printk("WINDOW=%u ", ntohs(tcph->window));
printk("WINDOW=%u ", ntohs(tcph.window));
/* Max length: 9 "RES=0x3F " */
printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
printk("RES=0x%02x ", (u8)(ntohl(tcp_flag_word(&tcph) & TCP_RESERVED_BITS) >> 22));
/* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
if (tcph->cwr)
if (tcph.cwr)
printk("CWR ");
if (tcph->ece)
if (tcph.ece)
printk("ECE ");
if (tcph->urg)
if (tcph.urg)
printk("URG ");
if (tcph->ack)
if (tcph.ack)
printk("ACK ");
if (tcph->psh)
if (tcph.psh)
printk("PSH ");
if (tcph->rst)
if (tcph.rst)
printk("RST ");
if (tcph->syn)
if (tcph.syn)
printk("SYN ");
if (tcph->fin)
if (tcph.fin)
printk("FIN ");
/* Max length: 11 "URGP=65535 " */
printk("URGP=%u ", ntohs(tcph->urg_ptr));
printk("URGP=%u ", ntohs(tcph.urg_ptr));
if ((info->logflags & IPT_LOG_TCPOPT)
&& tcph->doff * 4 != sizeof(struct tcphdr)) {
unsigned int i;
&& tcph.doff * 4 != sizeof(struct tcphdr)) {
unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
unsigned int i, optsize;
optsize = tcph.doff * 4 - sizeof(struct tcphdr);
if (skb_copy_bits(skb, iphoff+iph.ihl*4 + sizeof(tcph),
opt, optsize) < 0) {
printk("TRUNCATED");
return;
}
/* Max length: 127 "OPT (" 15*4*2chars ") " */
printk("OPT (");
for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++)
printk("%02X", ((u_int8_t *)tcph)[i]);
for (i = 0; i < optsize; i++)
printk("%02X", opt[i]);
printk(") ");
}
break;
}
case IPPROTO_UDP: {
struct udphdr *udph = protoh;
struct udphdr udph;
/* Max length: 10 "PROTO=UDP " */
printk("PROTO=UDP ");
if (ntohs(iph->frag_off) & IP_OFFSET)
if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
if (datalen < sizeof (*udph)) {
printk("INCOMPLETE [%u bytes] ", datalen);
if (skb_copy_bits(skb, iphoff+iph.ihl*4, &udph, sizeof(udph))
< 0) {
printk("INCOMPLETE [%u bytes] ",
skb->len - iphoff - iph.ihl*4);
break;
}
/* Max length: 20 "SPT=65535 DPT=65535 " */
printk("SPT=%u DPT=%u LEN=%u ",
ntohs(udph->source), ntohs(udph->dest),
ntohs(udph->len));
ntohs(udph.source), ntohs(udph.dest),
ntohs(udph.len));
break;
}
case IPPROTO_ICMP: {
struct icmphdr *icmph = protoh;
struct icmphdr icmph;
static size_t required_len[NR_ICMP_TYPES+1]
= { [ICMP_ECHOREPLY] = 4,
[ICMP_DEST_UNREACH]
......@@ -171,89 +195,93 @@ static void dump_packet(const struct ipt_log_info *info,
/* Max length: 11 "PROTO=ICMP " */
printk("PROTO=ICMP ");
if (ntohs(iph->frag_off) & IP_OFFSET)
if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
if (datalen < 4) {
printk("INCOMPLETE [%u bytes] ", datalen);
if (skb_copy_bits(skb, iphoff+iph.ihl*4, &icmph, sizeof(icmph))
< 0) {
printk("INCOMPLETE [%u bytes] ",
skb->len - iphoff - iph.ihl*4);
break;
}
/* Max length: 18 "TYPE=255 CODE=255 " */
printk("TYPE=%u CODE=%u ", icmph->type, icmph->code);
printk("TYPE=%u CODE=%u ", icmph.type, icmph.code);
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
if (icmph->type <= NR_ICMP_TYPES
&& required_len[icmph->type]
&& datalen < required_len[icmph->type]) {
printk("INCOMPLETE [%u bytes] ", datalen);
if (icmph.type <= NR_ICMP_TYPES
&& required_len[icmph.type]
&& skb->len-iphoff-iph.ihl*4 < required_len[icmph.type]) {
printk("INCOMPLETE [%u bytes] ",
skb->len - iphoff - iph.ihl*4);
break;
}
switch (icmph->type) {
switch (icmph.type) {
case ICMP_ECHOREPLY:
case ICMP_ECHO:
/* Max length: 19 "ID=65535 SEQ=65535 " */
printk("ID=%u SEQ=%u ",
ntohs(icmph->un.echo.id),
ntohs(icmph->un.echo.sequence));
ntohs(icmph.un.echo.id),
ntohs(icmph.un.echo.sequence));
break;
case ICMP_PARAMETERPROB:
/* Max length: 14 "PARAMETER=255 " */
printk("PARAMETER=%u ",
ntohl(icmph->un.gateway) >> 24);
ntohl(icmph.un.gateway) >> 24);
break;
case ICMP_REDIRECT:
/* Max length: 24 "GATEWAY=255.255.255.255 " */
printk("GATEWAY=%u.%u.%u.%u ", NIPQUAD(icmph->un.gateway));
printk("GATEWAY=%u.%u.%u.%u ",
NIPQUAD(icmph.un.gateway));
/* Fall through */
case ICMP_DEST_UNREACH:
case ICMP_SOURCE_QUENCH:
case ICMP_TIME_EXCEEDED:
/* Max length: 3+maxlen */
if (recurse) {
if (!iphoff) { /* Only recurse once. */
printk("[");
dump_packet(info,
(struct iphdr *)(icmph + 1),
datalen-sizeof(struct icmphdr),
0);
dump_packet(info, skb,
iphoff + iph.ihl*4+sizeof(icmph));
printk("] ");
}
/* Max length: 10 "MTU=65535 " */
if (icmph->type == ICMP_DEST_UNREACH
&& icmph->code == ICMP_FRAG_NEEDED)
printk("MTU=%u ", ntohs(icmph->un.frag.mtu));
if (icmph.type == ICMP_DEST_UNREACH
&& icmph.code == ICMP_FRAG_NEEDED)
printk("MTU=%u ", ntohs(icmph.un.frag.mtu));
}
break;
}
/* Max Length */
case IPPROTO_AH:
case IPPROTO_ESP: {
struct esphdr *esph = protoh;
int esp= (iph->protocol==IPPROTO_ESP);
struct esphdr esph;
int esp = (iph.protocol==IPPROTO_ESP);
/* Max length: 10 "PROTO=ESP " */
printk("PROTO=%s ",esp? "ESP" : "AH");
if (ntohs(iph->frag_off) & IP_OFFSET)
if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
if (datalen < sizeof (*esph)) {
printk("INCOMPLETE [%u bytes] ", datalen);
if (skb_copy_bits(skb, iphoff+iph.ihl*4, &esph, sizeof(esph))
< 0) {
printk("INCOMPLETE [%u bytes] ",
skb->len - iphoff - iph.ihl*4);
break;
}
/* Length: 15 "SPI=0xF1234567 " */
printk("SPI=0x%x ", ntohl(esph->spi) );
printk("SPI=0x%x ", ntohl(esph.spi));
break;
}
/* Max length: 10 "PROTO 255 " */
default:
printk("PROTO=%u ", iph->protocol);
printk("PROTO=%u ", iph.protocol);
}
/* Proto Max log string length */
......@@ -272,13 +300,12 @@ static void dump_packet(const struct ipt_log_info *info,
static unsigned int
ipt_log_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_log_info *loginfo = targinfo;
char level_string[4] = "< >";
......@@ -304,7 +331,8 @@ ipt_log_target(struct sk_buff **pskb,
if (in && !out) {
/* MAC logging for input chain only. */
printk("MAC=");
if ((*pskb)->dev && (*pskb)->dev->hard_header_len && (*pskb)->mac.raw != (void*)iph) {
if ((*pskb)->dev && (*pskb)->dev->hard_header_len
&& (*pskb)->mac.raw != (void*)(*pskb)->nh.iph) {
int i;
unsigned char *p = (*pskb)->mac.raw;
for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++)
......@@ -315,7 +343,7 @@ ipt_log_target(struct sk_buff **pskb,
printk(" ");
}
dump_packet(loginfo, iph, (*pskb)->len, 1);
dump_packet(loginfo, *pskb, 0);
printk("\n");
spin_unlock_bh(&log_lock);
......
......@@ -9,9 +9,9 @@
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
......
......@@ -57,9 +57,9 @@ masquerade_check(const char *tablename,
static unsigned int
masquerade_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
......
......@@ -65,18 +65,22 @@ static int route_mirror(struct sk_buff *skb)
return 0;
}
static void
ip_rewrite(struct sk_buff *skb)
static int ip_rewrite(struct sk_buff **pskb)
{
struct iphdr *iph = skb->nh.iph;
u32 odaddr = iph->saddr;
u32 osaddr = iph->daddr;
u32 odaddr, osaddr;
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return 0;
skb->nfcache |= NFC_ALTERED;
odaddr = (*pskb)->nh.iph->saddr;
osaddr = (*pskb)->nh.iph->daddr;
(*pskb)->nfcache |= NFC_ALTERED;
/* Rewrite IP header */
iph->daddr = odaddr;
iph->saddr = osaddr;
(*pskb)->nh.iph->daddr = odaddr;
(*pskb)->nh.iph->saddr = osaddr;
return 1;
}
/* Stolen from ip_finish_output2 */
......@@ -100,29 +104,28 @@ static void ip_direct_send(struct sk_buff *skb)
}
static unsigned int ipt_mirror_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
if (((*pskb)->dst != NULL) &&
route_mirror(*pskb)) {
ip_rewrite(*pskb);
if (((*pskb)->dst != NULL) && route_mirror(*pskb)) {
if (!ip_rewrite(pskb))
return NF_DROP;
/* If we are not at FORWARD hook (INPUT/PREROUTING),
* the TTL isn't decreased by the IP stack */
if (hooknum != NF_IP_FORWARD) {
struct iphdr *iph = (*pskb)->nh.iph;
if (iph->ttl <= 1) {
if ((*pskb)->nh.iph->ttl <= 1) {
/* this will traverse normal stack, and
* thus call conntrack on the icmp packet */
icmp_send(*pskb, ICMP_TIME_EXCEEDED,
ICMP_EXC_TTL, 0);
return NF_DROP;
}
ip_decrease_ttl(iph);
/* Made writable by ip_rewrite */
ip_decrease_ttl((*pskb)->nh.iph);
}
/* Don't let conntrack code see this packet:
......
......@@ -53,9 +53,9 @@ redirect_check(const char *tablename,
static unsigned int
redirect_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
......
......@@ -29,152 +29,140 @@ static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct)
void (*attach)(struct sk_buff *, struct nf_ct_info *);
/* Avoid module unload race with ip_ct_attach being NULLed out */
if (nfct && (attach = ip_ct_attach) != NULL)
if (nfct && (attach = ip_ct_attach) != NULL) {
mb(); /* Just to be sure: must be read before executing this */
attach(new_skb, nfct);
}
}
/* Send RST reply */
static void send_reset(struct sk_buff *oldskb, int local)
static unsigned int send_reset(struct sk_buff **pskb, int local)
{
struct sk_buff *nskb;
struct tcphdr *otcph, *tcph;
struct tcphdr tcph;
struct rtable *rt;
unsigned int otcplen;
u_int16_t tmp_port;
u_int32_t tmp_addr;
int needs_ack;
int hh_len;
int needs_ack, hh_len, datalen;
struct nf_ct_info *oldnfct;
/* IP header checks: fragment, too short. */
if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
|| oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
return;
/* No RSTs for fragments. */
if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET))
return NF_DROP;
otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
&tcph, sizeof(tcph)) < 0)
return NF_DROP;
/* No RST for RST. */
if (otcph->rst)
return;
/* Check checksum. */
if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
oldskb->nh.iph->daddr,
csum_partial((char *)otcph, otcplen, 0)) != 0)
return;
if (tcph.rst)
return NF_DROP;
/* FIXME: Check checksum. */
{
struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = oldskb->nh.iph->saddr,
{ .daddr = (*pskb)->nh.iph->saddr,
.saddr = (local ?
oldskb->nh.iph->daddr :
(*pskb)->nh.iph->daddr :
0),
.tos = RT_TOS(oldskb->nh.iph->tos) } } };
.tos = RT_TOS((*pskb)->nh.iph->tos) } } };
/* Routing: if not headed for us, route won't like source */
if (ip_route_output_key(&rt, &fl))
return;
return NF_DROP;
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
}
/* Copy skb (even if skb is about to be dropped, we can't just
clone it because there may be other things, such as tcpdump,
interested in it). We also need to expand headroom in case
hh_len of incoming interface < hh_len of outgoing interface */
nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
GFP_ATOMIC);
if (!nskb) {
dst_release(&rt->u.dst);
return;
/* We're going to flip the header around, drop options and data. */
if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(tcph))) {
ip_rt_put(rt);
return NF_DROP;
}
dst_release(nskb->dst);
nskb->dst = &rt->u.dst;
(*pskb)->h.th = (void *)(*pskb)->nh.iph + sizeof(tcph);
datalen = (*pskb)->len - (*pskb)->nh.iph->ihl*4 - tcph.doff*4;
/* Change over route. */
dst_release((*pskb)->dst);
(*pskb)->dst = &rt->u.dst;
/* This packet will not be the same as the other: clear nf fields */
nf_conntrack_put(nskb->nfct);
nskb->nfct = NULL;
nskb->nfcache = 0;
(*pskb)->nfcache = 0;
#ifdef CONFIG_NETFILTER_DEBUG
nskb->nf_debug = 0;
(*pskb)->nf_debug = 0;
#endif
nskb->nfmark = 0;
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
(*pskb)->nfmark = 0;
/* Swap source and dest */
tmp_addr = nskb->nh.iph->saddr;
nskb->nh.iph->saddr = nskb->nh.iph->daddr;
nskb->nh.iph->daddr = tmp_addr;
tmp_port = tcph->source;
tcph->source = tcph->dest;
tcph->dest = tmp_port;
tmp_addr = (*pskb)->nh.iph->saddr;
(*pskb)->nh.iph->saddr = (*pskb)->nh.iph->daddr;
(*pskb)->nh.iph->daddr = tmp_addr;
tmp_port = (*pskb)->h.th->source;
(*pskb)->h.th->source = (*pskb)->h.th->dest;
(*pskb)->h.th->dest = tmp_port;
/* Truncate to length (no data) */
tcph->doff = sizeof(struct tcphdr)/4;
skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
nskb->nh.iph->tot_len = htons(nskb->len);
(*pskb)->h.th->doff = sizeof(struct tcphdr)/4;
skb_trim(*pskb, (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr));
(*pskb)->nh.iph->tot_len = htons((*pskb)->len);
if (tcph->ack) {
if ((*pskb)->h.th->ack) {
needs_ack = 0;
tcph->seq = otcph->ack_seq;
tcph->ack_seq = 0;
(*pskb)->h.th->seq = tcph.ack_seq;
(*pskb)->h.th->ack_seq = 0;
} else {
needs_ack = 1;
tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
+ otcplen - (otcph->doff<<2));
tcph->seq = 0;
(*pskb)->h.th->ack_seq = htonl(ntohl(tcph.seq)
+ tcph.syn + tcph.fin
+ datalen);
(*pskb)->h.th->seq = 0;
}
/* Reset flags */
((u_int8_t *)tcph)[13] = 0;
tcph->rst = 1;
tcph->ack = needs_ack;
memset((*pskb)->h.raw + 13, 0, 1);
(*pskb)->h.th->rst = 1;
(*pskb)->h.th->ack = needs_ack;
tcph->window = 0;
tcph->urg_ptr = 0;
(*pskb)->h.th->window = 0;
(*pskb)->h.th->urg_ptr = 0;
/* Adjust TCP checksum */
tcph->check = 0;
tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
nskb->nh.iph->saddr,
nskb->nh.iph->daddr,
csum_partial((char *)tcph,
(*pskb)->h.th->check = 0;
(*pskb)->h.th->check
= tcp_v4_check((*pskb)->h.th,
sizeof(struct tcphdr),
(*pskb)->nh.iph->saddr,
(*pskb)->nh.iph->daddr,
csum_partial((*pskb)->h.raw,
sizeof(struct tcphdr), 0));
/* Adjust IP TTL, DF */
nskb->nh.iph->ttl = MAXTTL;
(*pskb)->nh.iph->ttl = MAXTTL;
/* Set DF, id = 0 */
nskb->nh.iph->frag_off = htons(IP_DF);
nskb->nh.iph->id = 0;
(*pskb)->nh.iph->frag_off = htons(IP_DF);
(*pskb)->nh.iph->id = 0;
/* Adjust IP checksum */
nskb->nh.iph->check = 0;
nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
nskb->nh.iph->ihl);
(*pskb)->nh.iph->check = 0;
(*pskb)->nh.iph->check = ip_fast_csum((*pskb)->nh.raw,
(*pskb)->nh.iph->ihl);
/* "Never happens" */
if (nskb->len > dst_pmtu(nskb->dst))
goto free_nskb;
if ((*pskb)->len > dst_pmtu((*pskb)->dst))
return NF_DROP;
connection_attach(nskb, oldskb->nfct);
/* Related to old connection. */
oldnfct = (*pskb)->nfct;
connection_attach(*pskb, oldnfct);
nf_conntrack_put(oldnfct);
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, *pskb, NULL, (*pskb)->dst->dev,
ip_finish_output);
return;
free_nskb:
kfree_skb(nskb);
return NF_STOLEN;
}
static void send_unreach(struct sk_buff *skb_in, int code)
static void send_unreach(const struct sk_buff *skb_in, int code)
{
struct iphdr *iph;
struct udphdr *udph;
struct icmphdr *icmph;
struct sk_buff *nskb;
u32 saddr;
u8 tos;
......@@ -189,8 +177,6 @@ static void send_unreach(struct sk_buff *skb_in, int code)
if (!xrlim_allow(&rt->u.dst, 1*HZ))
return;
iph = skb_in->nh.iph;
/* No replies to physical multicast/broadcast */
if (skb_in->pkt_type!=PACKET_HOST)
return;
......@@ -200,46 +186,41 @@ static void send_unreach(struct sk_buff *skb_in, int code)
return;
/* Only reply to fragment 0. */
if (iph->frag_off&htons(IP_OFFSET))
if (skb_in->nh.iph->frag_off&htons(IP_OFFSET))
return;
/* if UDP checksum is set, verify it's correct */
if (iph->protocol == IPPROTO_UDP
&& skb_in->tail-(u8*)iph >= sizeof(struct udphdr)) {
int datalen = skb_in->len - (iph->ihl<<2);
udph = (struct udphdr *)((char *)iph + (iph->ihl<<2));
if (udph->check
&& csum_tcpudp_magic(iph->saddr, iph->daddr,
datalen, IPPROTO_UDP,
csum_partial((char *)udph, datalen,
0)) != 0)
/* Ensure we have at least 8 bytes of proto header. */
if (skb_in->len < skb_in->nh.iph->ihl*4 + 8)
return;
}
/* If we send an ICMP error to an ICMP error a mess would result.. */
if (iph->protocol == IPPROTO_ICMP
&& skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
if (skb_in->nh.iph->protocol == IPPROTO_ICMP) {
struct icmphdr icmph;
if (skb_copy_bits(skb_in, skb_in->nh.iph->ihl*4,
&icmph, sizeof(icmph)) < 0)
return;
/* Between echo-reply (0) and timestamp (13),
everything except echo-request (8) is an error.
Also, anything greater than NR_ICMP_TYPES is
unknown, and hence should be treated as an error... */
if ((icmph->type < ICMP_TIMESTAMP
&& icmph->type != ICMP_ECHOREPLY
&& icmph->type != ICMP_ECHO)
|| icmph->type > NR_ICMP_TYPES)
if ((icmph.type < ICMP_TIMESTAMP
&& icmph.type != ICMP_ECHOREPLY
&& icmph.type != ICMP_ECHO)
|| icmph.type > NR_ICMP_TYPES)
return;
}
saddr = iph->daddr;
saddr = skb_in->nh.iph->daddr;
if (!(rt->rt_flags & RTCF_LOCAL))
saddr = 0;
tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
tos = (skb_in->nh.iph->tos & IPTOS_TOS_MASK)
| IPTOS_PREC_INTERNETCONTROL;
{
struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = iph->saddr,
{ .daddr = skb_in->nh.iph->saddr,
.saddr = saddr,
.tos = RT_TOS(tos) } } };
if (ip_route_output_key(&rt, &fl))
......@@ -266,40 +247,38 @@ static void send_unreach(struct sk_buff *skb_in, int code)
skb_reserve(nskb, hh_len);
/* Set up IP header */
iph = nskb->nh.iph
= (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
iph->version=4;
iph->ihl=5;
iph->tos=tos;
iph->tot_len = htons(length);
nskb->nh.iph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
nskb->nh.iph->version=4;
nskb->nh.iph->ihl=5;
nskb->nh.iph->tos=tos;
nskb->nh.iph->tot_len = htons(length);
/* PMTU discovery never applies to ICMP packets. */
iph->frag_off = 0;
nskb->nh.iph->frag_off = 0;
iph->ttl = MAXTTL;
ip_select_ident(iph, &rt->u.dst, NULL);
iph->protocol=IPPROTO_ICMP;
iph->saddr=rt->rt_src;
iph->daddr=rt->rt_dst;
iph->check=0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
nskb->nh.iph->ttl = MAXTTL;
ip_select_ident(nskb->nh.iph, &rt->u.dst, NULL);
nskb->nh.iph->protocol=IPPROTO_ICMP;
nskb->nh.iph->saddr=rt->rt_src;
nskb->nh.iph->daddr=rt->rt_dst;
nskb->nh.iph->check=0;
nskb->nh.iph->check = ip_fast_csum(nskb->nh.raw,
nskb->nh.iph->ihl);
/* Set up ICMP header. */
icmph = nskb->h.icmph
= (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
icmph->type = ICMP_DEST_UNREACH;
icmph->code = code;
icmph->un.gateway = 0;
icmph->checksum = 0;
nskb->h.icmph = (struct icmphdr *)skb_put(nskb,sizeof(struct icmphdr));
nskb->h.icmph->type = ICMP_DEST_UNREACH;
nskb->h.icmph->code = code;
nskb->h.icmph->un.gateway = 0;
nskb->h.icmph->checksum = 0;
/* Copy as much of original packet as will fit */
data = skb_put(nskb,
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
/* FIXME: won't work with nonlinear skbs --RR */
memcpy(data, skb_in->nh.iph,
skb_copy_bits(skb_in, 0, data,
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
icmph->checksum = ip_compute_csum((unsigned char *)icmph,
length - sizeof(struct iphdr));
nskb->h.icmph->checksum = ip_compute_csum(nskb->h.raw,
length-sizeof(struct iphdr));
connection_attach(nskb, skb_in->nfct);
......@@ -308,9 +287,9 @@ static void send_unreach(struct sk_buff *skb_in, int code)
}
static unsigned int reject(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
......@@ -344,7 +323,7 @@ static unsigned int reject(struct sk_buff **pskb,
send_unreach(*pskb, ICMP_HOST_ANO);
break;
case IPT_TCP_RESET:
send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
return send_reset(pskb, hooknum == NF_IP_LOCAL_IN);
case IPT_ICMP_ECHOREPLY:
/* Doesn't happen. */
break;
......
......@@ -36,9 +36,9 @@ optlen(const u_int8_t *opt, unsigned int offset)
static unsigned int
ipt_tcpmss_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
......@@ -49,15 +49,8 @@ ipt_tcpmss_target(struct sk_buff **pskb,
unsigned int i;
u_int8_t *opt;
/* 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)
if (!skb_ip_make_writable(pskb, (*pskb)->len))
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
}
iph = (*pskb)->nh.iph;
tcplen = (*pskb)->len - iph->ihl*4;
......
......@@ -9,35 +9,30 @@
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_tos_target_info *tosinfo = targinfo;
if ((iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
if (((*pskb)->nh.iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
u_int16_t diffs[2];
/* 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)
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
iph = (*pskb)->nh.iph;
}
diffs[0] = htons(iph->tos) ^ 0xFFFF;
iph->tos = (iph->tos & IPTOS_PREC_MASK) | tosinfo->tos;
diffs[1] = htons(iph->tos);
iph->check = csum_fold(csum_partial((char *)diffs,
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
(*pskb)->nh.iph->tos
= ((*pskb)->nh.iph->tos & IPTOS_PREC_MASK)
| tosinfo->tos;
diffs[1] = htons((*pskb)->nh.iph->tos);
(*pskb)->nh.iph->check
= csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
iph->check^0xFFFF));
(*pskb)->nh.iph->check
^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
......
......@@ -155,9 +155,9 @@ struct sk_buff *ulog_alloc_skb(unsigned int size)
}
static unsigned int ipt_ulog_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo, void *userinfo)
{
ulog_buff_t *ub;
......@@ -238,8 +238,9 @@ static unsigned int ipt_ulog_target(struct sk_buff **pskb,
else
pm->outdev_name[0] = '\0';
if (copy_len)
memcpy(pm->payload, (*pskb)->data, copy_len);
/* copy_len <= (*pskb)->len, so can't fail. */
if (skb_copy_bits(*pskb, 0, pm->payload, copy_len) < 0)
BUG();
/* check if we are building multi-part messages */
if (ub->qlen > 1) {
......
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