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)
}
/* 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;
unsigned int otcplen;
u_int16_t tmp_port;
u_int32_t tmp_addr;
int needs_ack, hh_len, datalen;
struct nf_ct_info *oldnfct;
int needs_ack;
int hh_len;
/* No RSTs for fragments. */
if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET))
return NF_DROP;
/* 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;
if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
&tcph, sizeof(tcph)) < 0)
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(oldskb, oldskb->nh.iph->ihl*4,
otcph, sizeof(*otcph)) < 0)
return;
/* No RST for RST. */
if (tcph.rst)
return NF_DROP;
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;
/* FIXME: Check checksum. */
{
struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = (*pskb)->nh.iph->saddr,
{ .daddr = oldskb->nh.iph->saddr,
.saddr = (local ?
(*pskb)->nh.iph->daddr :
oldskb->nh.iph->daddr :
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;
return;
}
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
/* 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;
/* 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;
}
(*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;
dst_release(nskb->dst);
nskb->dst = &rt->u.dst;
/* 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
(*pskb)->nf_debug = 0;
nskb->nf_debug = 0;
#endif
(*pskb)->nfmark = 0;
nskb->nfmark = 0;
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
/* Swap source and dest */
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;
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;
/* Truncate to length (no data) */
(*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);
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);
if ((*pskb)->h.th->ack) {
if (tcph->ack) {
needs_ack = 0;
(*pskb)->h.th->seq = tcph.ack_seq;
(*pskb)->h.th->ack_seq = 0;
tcph->seq = otcph->ack_seq;
tcph->ack_seq = 0;
} else {
needs_ack = 1;
(*pskb)->h.th->ack_seq = htonl(ntohl(tcph.seq)
+ tcph.syn + tcph.fin
+ datalen);
(*pskb)->h.th->seq = 0;
tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
+ otcplen - (otcph->doff<<2));
tcph->seq = 0;
}
/* Reset flags */
memset((*pskb)->h.raw + 13, 0, 1);
(*pskb)->h.th->rst = 1;
(*pskb)->h.th->ack = needs_ack;
((u_int8_t *)tcph)[13] = 0;
tcph->rst = 1;
tcph->ack = needs_ack;
(*pskb)->h.th->window = 0;
(*pskb)->h.th->urg_ptr = 0;
tcph->window = 0;
tcph->urg_ptr = 0;
/* Adjust TCP checksum */
(*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,
tcph->check = 0;
tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
nskb->nh.iph->saddr,
nskb->nh.iph->daddr,
csum_partial((char *)tcph,
sizeof(struct tcphdr), 0));
/* Adjust IP TTL, DF */
(*pskb)->nh.iph->ttl = MAXTTL;
nskb->nh.iph->ttl = MAXTTL;
/* Set DF, id = 0 */
(*pskb)->nh.iph->frag_off = htons(IP_DF);
(*pskb)->nh.iph->id = 0;
nskb->nh.iph->frag_off = htons(IP_DF);
nskb->nh.iph->id = 0;
/* Adjust IP checksum */
(*pskb)->nh.iph->check = 0;
(*pskb)->nh.iph->check = ip_fast_csum((*pskb)->nh.raw,
(*pskb)->nh.iph->ihl);
nskb->nh.iph->check = 0;
nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
nskb->nh.iph->ihl);
/* "Never happens" */
if ((*pskb)->len > dst_pmtu((*pskb)->dst))
return NF_DROP;
if (nskb->len > dst_pmtu(nskb->dst))
goto free_nskb;
/* Related to old connection. */
oldnfct = (*pskb)->nfct;
connection_attach(*pskb, oldnfct);
nf_conntrack_put(oldnfct);
connection_attach(nskb, oldskb->nfct);
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);
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;
u32 saddr;
u8 tos;
......@@ -177,6 +192,8 @@ static void send_unreach(const 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;
......@@ -186,38 +203,52 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
return;
/* Only reply to fragment 0. */
if (skb_in->nh.iph->frag_off&htons(IP_OFFSET))
if (iph->frag_off&htons(IP_OFFSET))
return;
/* Ensure we have at least 8 bytes of proto header. */
if (skb_in->len < skb_in->nh.iph->ihl*4 + 8)
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 (skb_in->nh.iph->protocol == IPPROTO_ICMP) {
struct icmphdr icmph;
if (iph->protocol == IPPROTO_ICMP
&& 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,
&icmph, sizeof(icmph)) < 0)
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 = skb_in->nh.iph->daddr;
saddr = iph->daddr;
if (!(rt->rt_flags & RTCF_LOCAL))
saddr = 0;
tos = (skb_in->nh.iph->tos & IPTOS_TOS_MASK)
| IPTOS_PREC_INTERNETCONTROL;
tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
{
struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = skb_in->nh.iph->saddr,
......@@ -247,38 +278,41 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
skb_reserve(nskb, hh_len);
/* Set up IP header */
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);
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);
/* PMTU discovery never applies to ICMP packets. */
nskb->nh.iph->frag_off = 0;
iph->frag_off = 0;
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);
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);
/* Set up ICMP header. */
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;
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;
/* Copy as much of original packet as will fit */
data = skb_put(nskb,
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
skb_copy_bits(skb_in, 0, data,
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);
......@@ -323,7 +357,7 @@ static unsigned int reject(struct sk_buff **pskb,
send_unreach(*pskb, ICMP_HOST_ANO);
break;
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:
/* Doesn't happen. */
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