Commit 9ba99b0d authored by Denys Vlasenko's avatar Denys Vlasenko Committed by David S. Miller

[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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 022748a9
...@@ -35,11 +35,8 @@ MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv4"); ...@@ -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) ...@@ -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_reset_network_header(nskb);
skb_init_secmark(nskb); niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
niph->version = 4;
skb_shinfo(nskb)->gso_size = 0; niph->ihl = sizeof(struct iphdr) / 4;
skb_shinfo(nskb)->gso_segs = 0; niph->tos = 0;
skb_shinfo(nskb)->gso_type = 0; niph->id = 0;
niph->frag_off = htons(IP_DF);
tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); niph->protocol = IPPROTO_TCP;
niph->check = 0;
/* Swap source and dest */ niph->saddr = oiph->daddr;
niph = ip_hdr(nskb); niph->daddr = oiph->saddr;
tmp_addr = niph->saddr;
niph->saddr = niph->daddr; tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
niph->daddr = tmp_addr; memset(tcph, 0, sizeof(*tcph));
tmp_port = tcph->source; tcph->source = oth->dest;
tcph->source = tcph->dest; tcph->dest = oth->source;
tcph->dest = tmp_port; tcph->doff = sizeof(struct tcphdr) / 4;
/* Truncate to length (no data) */ if (oth->ack)
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) ...@@ -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, ...@@ -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 */
......
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