diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2008-01-15 02:44:26 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:02:30 -0500 |
commit | 9ba99b0d3f45d0aedeafce1cfa4f720b19d04477 (patch) | |
tree | 54c5aab40ef717d111deecc1f28fe3d09bda13b9 /net | |
parent | 022748a9357c4c1a0113ec1ce5612f383b80156f (diff) |
[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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/ipt_REJECT.c | 102 |
1 files changed, 37 insertions, 65 deletions
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index e3c2ecc341e4..22606e2baa16 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c | |||
@@ -35,11 +35,8 @@ MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv4"); | |||
35 | static void send_reset(struct sk_buff *oldskb, int hook) | 35 | static void send_reset(struct sk_buff *oldskb, int hook) |
36 | { | 36 | { |
37 | struct sk_buff *nskb; | 37 | struct sk_buff *nskb; |
38 | struct iphdr *niph; | 38 | struct iphdr *oiph, *niph; |
39 | struct tcphdr _otcph, *oth, *tcph; | 39 | struct tcphdr _otcph, *oth, *tcph; |
40 | __be16 tmp_port; | ||
41 | __be32 tmp_addr; | ||
42 | int needs_ack; | ||
43 | unsigned int addr_type; | 40 | unsigned int addr_type; |
44 | 41 | ||
45 | /* IP header checks: fragment. */ | 42 | /* IP header checks: fragment. */ |
@@ -58,69 +55,47 @@ static void send_reset(struct sk_buff *oldskb, int hook) | |||
58 | /* Check checksum */ | 55 | /* Check checksum */ |
59 | if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) | 56 | if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) |
60 | return; | 57 | return; |
58 | oiph = ip_hdr(oldskb); | ||
61 | 59 | ||
62 | /* We need a linear, writeable skb. We also need to expand | 60 | nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + |
63 | headroom in case hh_len of incoming interface < hh_len of | 61 | LL_MAX_HEADER, GFP_ATOMIC); |
64 | outgoing interface */ | ||
65 | nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), | ||
66 | GFP_ATOMIC); | ||
67 | if (!nskb) | 62 | if (!nskb) |
68 | return; | 63 | return; |
69 | 64 | ||
70 | /* This packet will not be the same as the other: clear nf fields */ | 65 | skb_reserve(nskb, LL_MAX_HEADER); |
71 | nf_reset(nskb); | 66 | |
72 | nskb->mark = 0; | 67 | skb_reset_network_header(nskb); |
73 | skb_init_secmark(nskb); | 68 | niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); |
74 | 69 | niph->version = 4; | |
75 | skb_shinfo(nskb)->gso_size = 0; | 70 | niph->ihl = sizeof(struct iphdr) / 4; |
76 | skb_shinfo(nskb)->gso_segs = 0; | 71 | niph->tos = 0; |
77 | skb_shinfo(nskb)->gso_type = 0; | 72 | niph->id = 0; |
78 | 73 | niph->frag_off = htons(IP_DF); | |
79 | tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); | 74 | niph->protocol = IPPROTO_TCP; |
80 | 75 | niph->check = 0; | |
81 | /* Swap source and dest */ | 76 | niph->saddr = oiph->daddr; |
82 | niph = ip_hdr(nskb); | 77 | niph->daddr = oiph->saddr; |
83 | tmp_addr = niph->saddr; | 78 | |
84 | niph->saddr = niph->daddr; | 79 | tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); |
85 | niph->daddr = tmp_addr; | 80 | memset(tcph, 0, sizeof(*tcph)); |
86 | tmp_port = tcph->source; | 81 | tcph->source = oth->dest; |
87 | tcph->source = tcph->dest; | 82 | tcph->dest = oth->source; |
88 | tcph->dest = tmp_port; | 83 | tcph->doff = sizeof(struct tcphdr) / 4; |
89 | 84 | ||
90 | /* Truncate to length (no data) */ | 85 | if (oth->ack) |
91 | tcph->doff = sizeof(struct tcphdr)/4; | ||
92 | skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); | ||
93 | |||
94 | if (tcph->ack) { | ||
95 | needs_ack = 0; | ||
96 | tcph->seq = oth->ack_seq; | 86 | tcph->seq = oth->ack_seq; |
97 | tcph->ack_seq = 0; | 87 | else { |
98 | } else { | ||
99 | needs_ack = 1; | ||
100 | tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + | 88 | tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + |
101 | oldskb->len - ip_hdrlen(oldskb) - | 89 | oldskb->len - ip_hdrlen(oldskb) - |
102 | (oth->doff << 2)); | 90 | (oth->doff << 2)); |
103 | tcph->seq = 0; | 91 | tcph->ack = 1; |
104 | } | 92 | } |
105 | 93 | ||
106 | /* Reset flags */ | 94 | tcph->rst = 1; |
107 | ((u_int8_t *)tcph)[13] = 0; | 95 | tcph->check = tcp_v4_check(sizeof(struct tcphdr), |
108 | tcph->rst = 1; | 96 | niph->saddr, niph->daddr, |
109 | tcph->ack = needs_ack; | 97 | csum_partial(tcph, |
110 | 98 | sizeof(struct tcphdr), 0)); | |
111 | tcph->window = 0; | ||
112 | tcph->urg_ptr = 0; | ||
113 | |||
114 | /* Adjust TCP checksum */ | ||
115 | tcph->check = 0; | ||
116 | tcph->check = tcp_v4_check(sizeof(struct tcphdr), | ||
117 | niph->saddr, niph->daddr, | ||
118 | csum_partial(tcph, | ||
119 | sizeof(struct tcphdr), 0)); | ||
120 | |||
121 | /* Set DF, id = 0 */ | ||
122 | niph->frag_off = htons(IP_DF); | ||
123 | niph->id = 0; | ||
124 | 99 | ||
125 | addr_type = RTN_UNSPEC; | 100 | addr_type = RTN_UNSPEC; |
126 | if (hook != NF_INET_FORWARD | 101 | if (hook != NF_INET_FORWARD |
@@ -130,14 +105,16 @@ static void send_reset(struct sk_buff *oldskb, int hook) | |||
130 | ) | 105 | ) |
131 | addr_type = RTN_LOCAL; | 106 | addr_type = RTN_LOCAL; |
132 | 107 | ||
108 | /* ip_route_me_harder expects skb->dst to be set */ | ||
109 | dst_hold(oldskb->dst); | ||
110 | nskb->dst = oldskb->dst; | ||
111 | |||
133 | if (ip_route_me_harder(nskb, addr_type)) | 112 | if (ip_route_me_harder(nskb, addr_type)) |
134 | goto free_nskb; | 113 | goto free_nskb; |
135 | 114 | ||
115 | niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); | ||
136 | nskb->ip_summed = CHECKSUM_NONE; | 116 | nskb->ip_summed = CHECKSUM_NONE; |
137 | 117 | ||
138 | /* Adjust IP TTL */ | ||
139 | niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); | ||
140 | |||
141 | /* "Never happens" */ | 118 | /* "Never happens" */ |
142 | if (nskb->len > dst_mtu(nskb->dst)) | 119 | if (nskb->len > dst_mtu(nskb->dst)) |
143 | goto free_nskb; | 120 | goto free_nskb; |
@@ -163,11 +140,6 @@ reject_tg(struct sk_buff *skb, const struct net_device *in, | |||
163 | { | 140 | { |
164 | const struct ipt_reject_info *reject = targinfo; | 141 | const struct ipt_reject_info *reject = targinfo; |
165 | 142 | ||
166 | /* Our naive response construction doesn't deal with IP | ||
167 | options, and probably shouldn't try. */ | ||
168 | if (ip_hdrlen(skb) != sizeof(struct iphdr)) | ||
169 | return NF_DROP; | ||
170 | |||
171 | /* WARNING: This code causes reentry within iptables. | 143 | /* WARNING: This code causes reentry within iptables. |
172 | This means that the iptables jump stack is now crap. We | 144 | This means that the iptables jump stack is now crap. We |
173 | must return an absolute verdict. --RR */ | 145 | must return an absolute verdict. --RR */ |