[NETFILTER]: ipt_REJECT: properly handle IP options

The current TCP RST construction reuses the old packet and can't
deal with IP options as a consequence of that. Construct the
RST from scratch instead.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Denys Vlasenko 2008-01-14 23:44:26 -08:00 committed by David S. Miller
parent 022748a935
commit 9ba99b0d3f
1 changed files with 34 additions and 62 deletions

View File

@ -35,11 +35,8 @@ MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv4");
static void send_reset(struct sk_buff *oldskb, int hook) static void send_reset(struct sk_buff *oldskb, int hook)
{ {
struct sk_buff *nskb; struct sk_buff *nskb;
struct iphdr *niph; struct iphdr *oiph, *niph;
struct tcphdr _otcph, *oth, *tcph; struct tcphdr _otcph, *oth, *tcph;
__be16 tmp_port;
__be32 tmp_addr;
int needs_ack;
unsigned int addr_type; unsigned int addr_type;
/* IP header checks: fragment. */ /* IP header checks: fragment. */
@ -58,69 +55,47 @@ static void send_reset(struct sk_buff *oldskb, int hook)
/* Check checksum */ /* Check checksum */
if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
return; return;
oiph = ip_hdr(oldskb);
/* We need a linear, writeable skb. We also need to expand nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
headroom in case hh_len of incoming interface < hh_len of LL_MAX_HEADER, GFP_ATOMIC);
outgoing interface */
nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb),
GFP_ATOMIC);
if (!nskb) if (!nskb)
return; return;
/* This packet will not be the same as the other: clear nf fields */ skb_reserve(nskb, LL_MAX_HEADER);
nf_reset(nskb);
nskb->mark = 0;
skb_init_secmark(nskb);
skb_shinfo(nskb)->gso_size = 0; skb_reset_network_header(nskb);
skb_shinfo(nskb)->gso_segs = 0; niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
skb_shinfo(nskb)->gso_type = 0; niph->version = 4;
niph->ihl = sizeof(struct iphdr) / 4;
niph->tos = 0;
niph->id = 0;
niph->frag_off = htons(IP_DF);
niph->protocol = IPPROTO_TCP;
niph->check = 0;
niph->saddr = oiph->daddr;
niph->daddr = oiph->saddr;
tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
memset(tcph, 0, sizeof(*tcph));
tcph->source = oth->dest;
tcph->dest = oth->source;
tcph->doff = sizeof(struct tcphdr) / 4;
/* Swap source and dest */ if (oth->ack)
niph = ip_hdr(nskb);
tmp_addr = niph->saddr;
niph->saddr = niph->daddr;
niph->daddr = tmp_addr;
tmp_port = tcph->source;
tcph->source = tcph->dest;
tcph->dest = tmp_port;
/* Truncate to length (no data) */
tcph->doff = sizeof(struct tcphdr)/4;
skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
if (tcph->ack) {
needs_ack = 0;
tcph->seq = oth->ack_seq; tcph->seq = oth->ack_seq;
tcph->ack_seq = 0; else {
} else {
needs_ack = 1;
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
oldskb->len - ip_hdrlen(oldskb) - oldskb->len - ip_hdrlen(oldskb) -
(oth->doff << 2)); (oth->doff << 2));
tcph->seq = 0; tcph->ack = 1;
} }
/* Reset flags */ tcph->rst = 1;
((u_int8_t *)tcph)[13] = 0; tcph->check = tcp_v4_check(sizeof(struct tcphdr),
tcph->rst = 1; niph->saddr, niph->daddr,
tcph->ack = needs_ack; csum_partial(tcph,
sizeof(struct tcphdr), 0));
tcph->window = 0;
tcph->urg_ptr = 0;
/* Adjust TCP checksum */
tcph->check = 0;
tcph->check = tcp_v4_check(sizeof(struct tcphdr),
niph->saddr, niph->daddr,
csum_partial(tcph,
sizeof(struct tcphdr), 0));
/* Set DF, id = 0 */
niph->frag_off = htons(IP_DF);
niph->id = 0;
addr_type = RTN_UNSPEC; addr_type = RTN_UNSPEC;
if (hook != NF_INET_FORWARD if (hook != NF_INET_FORWARD
@ -130,14 +105,16 @@ static void send_reset(struct sk_buff *oldskb, int hook)
) )
addr_type = RTN_LOCAL; addr_type = RTN_LOCAL;
/* ip_route_me_harder expects skb->dst to be set */
dst_hold(oldskb->dst);
nskb->dst = oldskb->dst;
if (ip_route_me_harder(nskb, addr_type)) if (ip_route_me_harder(nskb, addr_type))
goto free_nskb; goto free_nskb;
niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
nskb->ip_summed = CHECKSUM_NONE; nskb->ip_summed = CHECKSUM_NONE;
/* Adjust IP TTL */
niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
/* "Never happens" */ /* "Never happens" */
if (nskb->len > dst_mtu(nskb->dst)) if (nskb->len > dst_mtu(nskb->dst))
goto free_nskb; goto free_nskb;
@ -163,11 +140,6 @@ reject_tg(struct sk_buff *skb, const struct net_device *in,
{ {
const struct ipt_reject_info *reject = targinfo; const struct ipt_reject_info *reject = targinfo;
/* Our naive response construction doesn't deal with IP
options, and probably shouldn't try. */
if (ip_hdrlen(skb) != sizeof(struct iphdr))
return NF_DROP;
/* WARNING: This code causes reentry within iptables. /* WARNING: This code causes reentry within iptables.
This means that the iptables jump stack is now crap. We This means that the iptables jump stack is now crap. We
must return an absolute verdict. --RR */ must return an absolute verdict. --RR */