diff options
author | Patrick McHardy <kaber@trash.net> | 2008-03-20 10:15:53 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2008-04-14 05:15:49 -0400 |
commit | d63a650736f566a1f9e9434725d2089597c0d2cc (patch) | |
tree | f0a3d5dbc9ced46f95582e4133b55bb70a1ae365 | |
parent | 6185f870e293a0a3eae5c81eb0106480cf03dfde (diff) |
[NETFILTER]: Add partial checksum validation helper
Move the UDP-Lite conntrack checksum validation to a generic helper
similar to nf_checksum() and make it fall back to nf_checksum()
in case the full packet is to be checksummed and hardware checksums
are available. This is to be used by DCCP conntrack, which also
needs to verify partial checksums.
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | include/linux/netfilter.h | 22 | ||||
-rw-r--r-- | net/ipv4/netfilter.c | 37 | ||||
-rw-r--r-- | net/ipv6/netfilter.c | 42 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_proto_udplite.c | 33 |
4 files changed, 94 insertions, 40 deletions
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 66bc52060fd6..e4c66593b5c6 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h | |||
@@ -234,6 +234,11 @@ struct nf_afinfo { | |||
234 | unsigned short family; | 234 | unsigned short family; |
235 | __sum16 (*checksum)(struct sk_buff *skb, unsigned int hook, | 235 | __sum16 (*checksum)(struct sk_buff *skb, unsigned int hook, |
236 | unsigned int dataoff, u_int8_t protocol); | 236 | unsigned int dataoff, u_int8_t protocol); |
237 | __sum16 (*checksum_partial)(struct sk_buff *skb, | ||
238 | unsigned int hook, | ||
239 | unsigned int dataoff, | ||
240 | unsigned int len, | ||
241 | u_int8_t protocol); | ||
237 | int (*route)(struct dst_entry **dst, struct flowi *fl); | 242 | int (*route)(struct dst_entry **dst, struct flowi *fl); |
238 | void (*saveroute)(const struct sk_buff *skb, | 243 | void (*saveroute)(const struct sk_buff *skb, |
239 | struct nf_queue_entry *entry); | 244 | struct nf_queue_entry *entry); |
@@ -263,6 +268,23 @@ nf_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, | |||
263 | return csum; | 268 | return csum; |
264 | } | 269 | } |
265 | 270 | ||
271 | static inline __sum16 | ||
272 | nf_checksum_partial(struct sk_buff *skb, unsigned int hook, | ||
273 | unsigned int dataoff, unsigned int len, | ||
274 | u_int8_t protocol, unsigned short family) | ||
275 | { | ||
276 | const struct nf_afinfo *afinfo; | ||
277 | __sum16 csum = 0; | ||
278 | |||
279 | rcu_read_lock(); | ||
280 | afinfo = nf_get_afinfo(family); | ||
281 | if (afinfo) | ||
282 | csum = afinfo->checksum_partial(skb, hook, dataoff, len, | ||
283 | protocol); | ||
284 | rcu_read_unlock(); | ||
285 | return csum; | ||
286 | } | ||
287 | |||
266 | extern int nf_register_afinfo(const struct nf_afinfo *afinfo); | 288 | extern int nf_register_afinfo(const struct nf_afinfo *afinfo); |
267 | extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); | 289 | extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); |
268 | 290 | ||
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 9a904c6c0dc8..f8edacdf991d 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c | |||
@@ -182,21 +182,44 @@ __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, | |||
182 | } | 182 | } |
183 | return csum; | 183 | return csum; |
184 | } | 184 | } |
185 | |||
186 | EXPORT_SYMBOL(nf_ip_checksum); | 185 | EXPORT_SYMBOL(nf_ip_checksum); |
187 | 186 | ||
187 | static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook, | ||
188 | unsigned int dataoff, unsigned int len, | ||
189 | u_int8_t protocol) | ||
190 | { | ||
191 | const struct iphdr *iph = ip_hdr(skb); | ||
192 | __sum16 csum = 0; | ||
193 | |||
194 | switch (skb->ip_summed) { | ||
195 | case CHECKSUM_COMPLETE: | ||
196 | if (len == skb->len - dataoff) | ||
197 | return nf_ip_checksum(skb, hook, dataoff, protocol); | ||
198 | /* fall through */ | ||
199 | case CHECKSUM_NONE: | ||
200 | skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol, | ||
201 | skb->len - dataoff, 0); | ||
202 | skb->ip_summed = CHECKSUM_NONE; | ||
203 | csum = __skb_checksum_complete_head(skb, dataoff + len); | ||
204 | if (!csum) | ||
205 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
206 | } | ||
207 | return csum; | ||
208 | } | ||
209 | |||
188 | static int nf_ip_route(struct dst_entry **dst, struct flowi *fl) | 210 | static int nf_ip_route(struct dst_entry **dst, struct flowi *fl) |
189 | { | 211 | { |
190 | return ip_route_output_key(&init_net, (struct rtable **)dst, fl); | 212 | return ip_route_output_key(&init_net, (struct rtable **)dst, fl); |
191 | } | 213 | } |
192 | 214 | ||
193 | static const struct nf_afinfo nf_ip_afinfo = { | 215 | static const struct nf_afinfo nf_ip_afinfo = { |
194 | .family = AF_INET, | 216 | .family = AF_INET, |
195 | .checksum = nf_ip_checksum, | 217 | .checksum = nf_ip_checksum, |
196 | .route = nf_ip_route, | 218 | .checksum_partial = nf_ip_checksum_partial, |
197 | .saveroute = nf_ip_saveroute, | 219 | .route = nf_ip_route, |
198 | .reroute = nf_ip_reroute, | 220 | .saveroute = nf_ip_saveroute, |
199 | .route_key_size = sizeof(struct ip_rt_info), | 221 | .reroute = nf_ip_reroute, |
222 | .route_key_size = sizeof(struct ip_rt_info), | ||
200 | }; | 223 | }; |
201 | 224 | ||
202 | static int ipv4_netfilter_init(void) | 225 | static int ipv4_netfilter_init(void) |
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index aed51bcc66b4..8c6c5e71f210 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c | |||
@@ -121,16 +121,44 @@ __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, | |||
121 | } | 121 | } |
122 | return csum; | 122 | return csum; |
123 | } | 123 | } |
124 | |||
125 | EXPORT_SYMBOL(nf_ip6_checksum); | 124 | EXPORT_SYMBOL(nf_ip6_checksum); |
126 | 125 | ||
126 | static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook, | ||
127 | unsigned int dataoff, unsigned int len, | ||
128 | u_int8_t protocol) | ||
129 | { | ||
130 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | ||
131 | __wsum hsum; | ||
132 | __sum16 csum = 0; | ||
133 | |||
134 | switch (skb->ip_summed) { | ||
135 | case CHECKSUM_COMPLETE: | ||
136 | if (len == skb->len - dataoff) | ||
137 | return nf_ip6_checksum(skb, hook, dataoff, protocol); | ||
138 | /* fall through */ | ||
139 | case CHECKSUM_NONE: | ||
140 | hsum = skb_checksum(skb, 0, dataoff, 0); | ||
141 | skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr, | ||
142 | &ip6h->daddr, | ||
143 | skb->len - dataoff, | ||
144 | protocol, | ||
145 | csum_sub(0, hsum))); | ||
146 | skb->ip_summed = CHECKSUM_NONE; | ||
147 | csum = __skb_checksum_complete_head(skb, dataoff + len); | ||
148 | if (!csum) | ||
149 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
150 | } | ||
151 | return csum; | ||
152 | }; | ||
153 | |||
127 | static const struct nf_afinfo nf_ip6_afinfo = { | 154 | static const struct nf_afinfo nf_ip6_afinfo = { |
128 | .family = AF_INET6, | 155 | .family = AF_INET6, |
129 | .checksum = nf_ip6_checksum, | 156 | .checksum = nf_ip6_checksum, |
130 | .route = nf_ip6_route, | 157 | .checksum_partial = nf_ip6_checksum_partial, |
131 | .saveroute = nf_ip6_saveroute, | 158 | .route = nf_ip6_route, |
132 | .reroute = nf_ip6_reroute, | 159 | .saveroute = nf_ip6_saveroute, |
133 | .route_key_size = sizeof(struct ip6_rt_info), | 160 | .reroute = nf_ip6_reroute, |
161 | .route_key_size = sizeof(struct ip6_rt_info), | ||
134 | }; | 162 | }; |
135 | 163 | ||
136 | int __init ipv6_netfilter_init(void) | 164 | int __init ipv6_netfilter_init(void) |
diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 9dd03c7aeac6..c3eaee6afffd 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c | |||
@@ -127,32 +127,13 @@ static int udplite_error(struct sk_buff *skb, unsigned int dataoff, | |||
127 | } | 127 | } |
128 | 128 | ||
129 | /* Checksum invalid? Ignore. */ | 129 | /* Checksum invalid? Ignore. */ |
130 | if (nf_conntrack_checksum && !skb_csum_unnecessary(skb) && | 130 | if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING && |
131 | hooknum == NF_INET_PRE_ROUTING) { | 131 | nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP, |
132 | if (pf == PF_INET) { | 132 | pf)) { |
133 | struct iphdr *iph = ip_hdr(skb); | 133 | if (LOG_INVALID(IPPROTO_UDPLITE)) |
134 | 134 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | |
135 | skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, | 135 | "nf_ct_udplite: bad UDPLite checksum "); |
136 | udplen, IPPROTO_UDPLITE, 0); | 136 | return -NF_ACCEPT; |
137 | } else { | ||
138 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||
139 | __wsum hsum = skb_checksum(skb, 0, dataoff, 0); | ||
140 | |||
141 | skb->csum = ~csum_unfold( | ||
142 | csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, | ||
143 | udplen, IPPROTO_UDPLITE, | ||
144 | csum_sub(0, hsum))); | ||
145 | } | ||
146 | |||
147 | skb->ip_summed = CHECKSUM_NONE; | ||
148 | if (__skb_checksum_complete_head(skb, dataoff + cscov)) { | ||
149 | if (LOG_INVALID(IPPROTO_UDPLITE)) | ||
150 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | ||
151 | "nf_ct_udplite: bad UDPLite " | ||
152 | "checksum "); | ||
153 | return -NF_ACCEPT; | ||
154 | } | ||
155 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
156 | } | 137 | } |
157 | 138 | ||
158 | return NF_ACCEPT; | 139 | return NF_ACCEPT; |