Commit 3e93cabb authored by Harald Welte's avatar Harald Welte Committed by David S. Miller

[NETFILTER]: Re-sync ipt_REJECT with 2.4.x

parent 3335c2ba
...@@ -36,133 +36,148 @@ static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct) ...@@ -36,133 +36,148 @@ static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct)
} }
/* Send RST reply */ /* Send RST reply */
static unsigned int send_reset(struct sk_buff **pskb, int local) static void send_reset(struct sk_buff *oldskb, int local)
{ {
struct tcphdr tcph; struct sk_buff *nskb;
struct tcphdr *otcph, *tcph;
struct rtable *rt; struct rtable *rt;
unsigned int otcplen;
u_int16_t tmp_port; u_int16_t tmp_port;
u_int32_t tmp_addr; u_int32_t tmp_addr;
int needs_ack, hh_len, datalen; int needs_ack;
struct nf_ct_info *oldnfct; int hh_len;
/* No RSTs for fragments. */ /* IP header checks: fragment, too short. */
if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
return NF_DROP; || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
return;
if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
&tcph, sizeof(tcph)) < 0) otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
return NF_DROP;
if (skb_copy_bits(oldskb, oldskb->nh.iph->ihl*4,
otcph, sizeof(*otcph)) < 0)
return;
/* No RST for RST. */ /* No RST for RST. */
if (tcph.rst) if (otcph->rst)
return NF_DROP; 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;
/* FIXME: Check checksum. */
{ {
struct flowi fl = { .nl_u = { .ip4_u = struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = (*pskb)->nh.iph->saddr, { .daddr = oldskb->nh.iph->saddr,
.saddr = (local ? .saddr = (local ?
(*pskb)->nh.iph->daddr : oldskb->nh.iph->daddr :
0), 0),
.tos = RT_TOS((*pskb)->nh.iph->tos) } } }; .tos = RT_TOS(oldskb->nh.iph->tos) } } };
/* Routing: if not headed for us, route won't like source */
if (ip_route_output_key(&rt, &fl))
return NF_DROP;
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15; /* Routing: if not headed for us, route won't like source */
} if (ip_route_output_key(&rt, &fl))
return;
}
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
/* We're going to flip the header around, drop options and data. */ /* Copy skb (even if skb is about to be dropped, we can't just
if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(tcph))) { clone it because there may be other things, such as tcpdump,
ip_rt_put(rt); interested in it). We also need to expand headroom in case
return NF_DROP; 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;
} }
(*pskb)->h.th = (void *)(*pskb)->nh.iph + sizeof(tcph); dst_release(nskb->dst);
datalen = (*pskb)->len - (*pskb)->nh.iph->ihl*4 - tcph.doff*4; nskb->dst = &rt->u.dst;
/* 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 */ /* This packet will not be the same as the other: clear nf fields */
(*pskb)->nfcache = 0; nf_conntrack_put(nskb->nfct);
nskb->nfct = NULL;
nskb->nfcache = 0;
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
(*pskb)->nf_debug = 0; nskb->nf_debug = 0;
#endif #endif
(*pskb)->nfmark = 0; nskb->nfmark = 0;
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
/* Swap source and dest */ /* Swap source and dest */
tmp_addr = (*pskb)->nh.iph->saddr; tmp_addr = nskb->nh.iph->saddr;
(*pskb)->nh.iph->saddr = (*pskb)->nh.iph->daddr; nskb->nh.iph->saddr = nskb->nh.iph->daddr;
(*pskb)->nh.iph->daddr = tmp_addr; nskb->nh.iph->daddr = tmp_addr;
tmp_port = (*pskb)->h.th->source; tmp_port = tcph->source;
(*pskb)->h.th->source = (*pskb)->h.th->dest; tcph->source = tcph->dest;
(*pskb)->h.th->dest = tmp_port; tcph->dest = tmp_port;
/* Truncate to length (no data) */ /* Truncate to length (no data) */
(*pskb)->h.th->doff = sizeof(struct tcphdr)/4; tcph->doff = sizeof(struct tcphdr)/4;
skb_trim(*pskb, (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr)); skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
(*pskb)->nh.iph->tot_len = htons((*pskb)->len); nskb->nh.iph->tot_len = htons(nskb->len);
if ((*pskb)->h.th->ack) { if (tcph->ack) {
needs_ack = 0; needs_ack = 0;
(*pskb)->h.th->seq = tcph.ack_seq; tcph->seq = otcph->ack_seq;
(*pskb)->h.th->ack_seq = 0; tcph->ack_seq = 0;
} else { } else {
needs_ack = 1; needs_ack = 1;
(*pskb)->h.th->ack_seq = htonl(ntohl(tcph.seq) tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
+ tcph.syn + tcph.fin + otcplen - (otcph->doff<<2));
+ datalen); tcph->seq = 0;
(*pskb)->h.th->seq = 0;
} }
/* Reset flags */ /* Reset flags */
memset((*pskb)->h.raw + 13, 0, 1); ((u_int8_t *)tcph)[13] = 0;
(*pskb)->h.th->rst = 1; tcph->rst = 1;
(*pskb)->h.th->ack = needs_ack; tcph->ack = needs_ack;
(*pskb)->h.th->window = 0; tcph->window = 0;
(*pskb)->h.th->urg_ptr = 0; tcph->urg_ptr = 0;
/* Adjust TCP checksum */ /* Adjust TCP checksum */
(*pskb)->h.th->check = 0; tcph->check = 0;
(*pskb)->h.th->check tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
= tcp_v4_check((*pskb)->h.th, nskb->nh.iph->saddr,
sizeof(struct tcphdr), nskb->nh.iph->daddr,
(*pskb)->nh.iph->saddr, csum_partial((char *)tcph,
(*pskb)->nh.iph->daddr, sizeof(struct tcphdr), 0));
csum_partial((*pskb)->h.raw,
sizeof(struct tcphdr), 0));
/* Adjust IP TTL, DF */ /* Adjust IP TTL, DF */
(*pskb)->nh.iph->ttl = MAXTTL; nskb->nh.iph->ttl = MAXTTL;
/* Set DF, id = 0 */ /* Set DF, id = 0 */
(*pskb)->nh.iph->frag_off = htons(IP_DF); nskb->nh.iph->frag_off = htons(IP_DF);
(*pskb)->nh.iph->id = 0; nskb->nh.iph->id = 0;
/* Adjust IP checksum */ /* Adjust IP checksum */
(*pskb)->nh.iph->check = 0; nskb->nh.iph->check = 0;
(*pskb)->nh.iph->check = ip_fast_csum((*pskb)->nh.raw, nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
(*pskb)->nh.iph->ihl); nskb->nh.iph->ihl);
/* "Never happens" */ /* "Never happens" */
if ((*pskb)->len > dst_pmtu((*pskb)->dst)) if (nskb->len > dst_pmtu(nskb->dst))
return NF_DROP; goto free_nskb;
/* Related to old connection. */ connection_attach(nskb, oldskb->nfct);
oldnfct = (*pskb)->nfct;
connection_attach(*pskb, oldnfct);
nf_conntrack_put(oldnfct);
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, *pskb, NULL, (*pskb)->dst->dev, NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
ip_finish_output); ip_finish_output);
return NF_STOLEN; return;
free_nskb:
kfree_skb(nskb);
} }
static void send_unreach(const struct sk_buff *skb_in, int code) static void send_unreach(struct sk_buff *skb_in, int code)
{ {
struct iphdr *iph;
struct udphdr *udph;
struct icmphdr *icmph;
struct sk_buff *nskb; struct sk_buff *nskb;
u32 saddr; u32 saddr;
u8 tos; u8 tos;
...@@ -177,6 +192,8 @@ static void send_unreach(const struct sk_buff *skb_in, int code) ...@@ -177,6 +192,8 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
if (!xrlim_allow(&rt->u.dst, 1*HZ)) if (!xrlim_allow(&rt->u.dst, 1*HZ))
return; return;
iph = skb_in->nh.iph;
/* No replies to physical multicast/broadcast */ /* No replies to physical multicast/broadcast */
if (skb_in->pkt_type!=PACKET_HOST) if (skb_in->pkt_type!=PACKET_HOST)
return; return;
...@@ -186,38 +203,52 @@ static void send_unreach(const struct sk_buff *skb_in, int code) ...@@ -186,38 +203,52 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
return; return;
/* Only reply to fragment 0. */ /* Only reply to fragment 0. */
if (skb_in->nh.iph->frag_off&htons(IP_OFFSET)) if (iph->frag_off&htons(IP_OFFSET))
return; return;
/* Ensure we have at least 8 bytes of proto header. */ /* Ensure we have at least 8 bytes of proto header. */
if (skb_in->len < skb_in->nh.iph->ihl*4 + 8) if (skb_in->len < skb_in->nh.iph->ihl*4 + 8)
return; 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)
return;
}
/* If we send an ICMP error to an ICMP error a mess would result.. */ /* If we send an ICMP error to an ICMP error a mess would result.. */
if (skb_in->nh.iph->protocol == IPPROTO_ICMP) { if (iph->protocol == IPPROTO_ICMP
struct icmphdr icmph; && skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
if (skb_copy_bits(skb_in, skb_in->nh.iph->ihl*4, if (skb_copy_bits(skb_in, skb_in->nh.iph->ihl*4,
&icmph, sizeof(icmph)) < 0) icmph, sizeof(*icmph)) < 0)
return; return;
/* Between echo-reply (0) and timestamp (13), /* Between echo-reply (0) and timestamp (13),
everything except echo-request (8) is an error. everything except echo-request (8) is an error.
Also, anything greater than NR_ICMP_TYPES is Also, anything greater than NR_ICMP_TYPES is
unknown, and hence should be treated as an error... */ unknown, and hence should be treated as an error... */
if ((icmph.type < ICMP_TIMESTAMP if ((icmph->type < ICMP_TIMESTAMP
&& icmph.type != ICMP_ECHOREPLY && icmph->type != ICMP_ECHOREPLY
&& icmph.type != ICMP_ECHO) && icmph->type != ICMP_ECHO)
|| icmph.type > NR_ICMP_TYPES) || icmph->type > NR_ICMP_TYPES)
return; return;
} }
saddr = skb_in->nh.iph->daddr; saddr = iph->daddr;
if (!(rt->rt_flags & RTCF_LOCAL)) if (!(rt->rt_flags & RTCF_LOCAL))
saddr = 0; saddr = 0;
tos = (skb_in->nh.iph->tos & IPTOS_TOS_MASK) tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
| IPTOS_PREC_INTERNETCONTROL;
{ {
struct flowi fl = { .nl_u = { .ip4_u = struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = skb_in->nh.iph->saddr, { .daddr = skb_in->nh.iph->saddr,
...@@ -247,38 +278,41 @@ static void send_unreach(const struct sk_buff *skb_in, int code) ...@@ -247,38 +278,41 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
skb_reserve(nskb, hh_len); skb_reserve(nskb, hh_len);
/* Set up IP header */ /* Set up IP header */
nskb->nh.iph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); iph = nskb->nh.iph
nskb->nh.iph->version=4; = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
nskb->nh.iph->ihl=5; iph->version=4;
nskb->nh.iph->tos=tos; iph->ihl=5;
nskb->nh.iph->tot_len = htons(length); iph->tos=tos;
iph->tot_len = htons(length);
/* PMTU discovery never applies to ICMP packets. */ /* PMTU discovery never applies to ICMP packets. */
nskb->nh.iph->frag_off = 0; iph->frag_off = 0;
nskb->nh.iph->ttl = MAXTTL; iph->ttl = MAXTTL;
ip_select_ident(nskb->nh.iph, &rt->u.dst, NULL); ip_select_ident(iph, &rt->u.dst, NULL);
nskb->nh.iph->protocol=IPPROTO_ICMP; iph->protocol=IPPROTO_ICMP;
nskb->nh.iph->saddr=rt->rt_src; iph->saddr=rt->rt_src;
nskb->nh.iph->daddr=rt->rt_dst; iph->daddr=rt->rt_dst;
nskb->nh.iph->check=0; iph->check=0;
nskb->nh.iph->check = ip_fast_csum(nskb->nh.raw, iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
nskb->nh.iph->ihl);
/* Set up ICMP header. */ /* Set up ICMP header. */
nskb->h.icmph = (struct icmphdr *)skb_put(nskb,sizeof(struct icmphdr)); icmph = nskb->h.icmph
nskb->h.icmph->type = ICMP_DEST_UNREACH; = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
nskb->h.icmph->code = code; icmph->type = ICMP_DEST_UNREACH;
nskb->h.icmph->un.gateway = 0; icmph->code = code;
nskb->h.icmph->checksum = 0; icmph->un.gateway = 0;
icmph->checksum = 0;
/* Copy as much of original packet as will fit */ /* Copy as much of original packet as will fit */
data = skb_put(nskb, data = skb_put(nskb,
length - sizeof(struct iphdr) - sizeof(struct icmphdr)); length - sizeof(struct iphdr) - sizeof(struct icmphdr));
skb_copy_bits(skb_in, 0, data, skb_copy_bits(skb_in, 0, data,
length - sizeof(struct iphdr) - sizeof(struct icmphdr)); length - sizeof(struct iphdr) - sizeof(struct icmphdr));
nskb->h.icmph->checksum = ip_compute_csum(nskb->h.raw,
length-sizeof(struct iphdr)); icmph->checksum = ip_compute_csum((unsigned char *)icmph,
length - sizeof(struct iphdr));
connection_attach(nskb, skb_in->nfct); connection_attach(nskb, skb_in->nfct);
...@@ -323,7 +357,7 @@ static unsigned int reject(struct sk_buff **pskb, ...@@ -323,7 +357,7 @@ static unsigned int reject(struct sk_buff **pskb,
send_unreach(*pskb, ICMP_HOST_ANO); send_unreach(*pskb, ICMP_HOST_ANO);
break; break;
case IPT_TCP_RESET: case IPT_TCP_RESET:
return send_reset(pskb, hooknum == NF_IP_LOCAL_IN); send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
case IPT_ICMP_ECHOREPLY: case IPT_ICMP_ECHOREPLY:
/* Doesn't happen. */ /* Doesn't happen. */
break; break;
......
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