Commit d63a650736f566a1f9e9434725d2089597c0d2cc
1 parent
6185f870e2
Exists in
master
and in
7 other branches
[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>
Showing 4 changed files with 94 additions and 40 deletions Side-by-side Diff
include/linux/netfilter.h
... | ... | @@ -234,6 +234,11 @@ |
234 | 234 | unsigned short family; |
235 | 235 | __sum16 (*checksum)(struct sk_buff *skb, unsigned int hook, |
236 | 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 | 242 | int (*route)(struct dst_entry **dst, struct flowi *fl); |
238 | 243 | void (*saveroute)(const struct sk_buff *skb, |
239 | 244 | struct nf_queue_entry *entry); |
... | ... | @@ -259,6 +264,23 @@ |
259 | 264 | afinfo = nf_get_afinfo(family); |
260 | 265 | if (afinfo) |
261 | 266 | csum = afinfo->checksum(skb, hook, dataoff, protocol); |
267 | + rcu_read_unlock(); | |
268 | + return csum; | |
269 | +} | |
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); | |
262 | 284 | rcu_read_unlock(); |
263 | 285 | return csum; |
264 | 286 | } |
net/ipv4/netfilter.c
... | ... | @@ -182,21 +182,44 @@ |
182 | 182 | } |
183 | 183 | return csum; |
184 | 184 | } |
185 | - | |
186 | 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 | 210 | static int nf_ip_route(struct dst_entry **dst, struct flowi *fl) |
189 | 211 | { |
190 | 212 | return ip_route_output_key(&init_net, (struct rtable **)dst, fl); |
191 | 213 | } |
192 | 214 | |
193 | 215 | static const struct nf_afinfo nf_ip_afinfo = { |
194 | - .family = AF_INET, | |
195 | - .checksum = nf_ip_checksum, | |
196 | - .route = nf_ip_route, | |
197 | - .saveroute = nf_ip_saveroute, | |
198 | - .reroute = nf_ip_reroute, | |
199 | - .route_key_size = sizeof(struct ip_rt_info), | |
216 | + .family = AF_INET, | |
217 | + .checksum = nf_ip_checksum, | |
218 | + .checksum_partial = nf_ip_checksum_partial, | |
219 | + .route = nf_ip_route, | |
220 | + .saveroute = nf_ip_saveroute, | |
221 | + .reroute = nf_ip_reroute, | |
222 | + .route_key_size = sizeof(struct ip_rt_info), | |
200 | 223 | }; |
201 | 224 | |
202 | 225 | static int ipv4_netfilter_init(void) |
net/ipv6/netfilter.c
... | ... | @@ -121,16 +121,44 @@ |
121 | 121 | } |
122 | 122 | return csum; |
123 | 123 | } |
124 | - | |
125 | 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 | 154 | static const struct nf_afinfo nf_ip6_afinfo = { |
128 | - .family = AF_INET6, | |
129 | - .checksum = nf_ip6_checksum, | |
130 | - .route = nf_ip6_route, | |
131 | - .saveroute = nf_ip6_saveroute, | |
132 | - .reroute = nf_ip6_reroute, | |
133 | - .route_key_size = sizeof(struct ip6_rt_info), | |
155 | + .family = AF_INET6, | |
156 | + .checksum = nf_ip6_checksum, | |
157 | + .checksum_partial = nf_ip6_checksum_partial, | |
158 | + .route = nf_ip6_route, | |
159 | + .saveroute = nf_ip6_saveroute, | |
160 | + .reroute = nf_ip6_reroute, | |
161 | + .route_key_size = sizeof(struct ip6_rt_info), | |
134 | 162 | }; |
135 | 163 | |
136 | 164 | int __init ipv6_netfilter_init(void) |
net/netfilter/nf_conntrack_proto_udplite.c
... | ... | @@ -127,32 +127,13 @@ |
127 | 127 | } |
128 | 128 | |
129 | 129 | /* Checksum invalid? Ignore. */ |
130 | - if (nf_conntrack_checksum && !skb_csum_unnecessary(skb) && | |
131 | - hooknum == NF_INET_PRE_ROUTING) { | |
132 | - if (pf == PF_INET) { | |
133 | - struct iphdr *iph = ip_hdr(skb); | |
134 | - | |
135 | - skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, | |
136 | - udplen, IPPROTO_UDPLITE, 0); | |
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; | |
130 | + if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING && | |
131 | + nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP, | |
132 | + pf)) { | |
133 | + if (LOG_INVALID(IPPROTO_UDPLITE)) | |
134 | + nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | |
135 | + "nf_ct_udplite: bad UDPLite checksum "); | |
136 | + return -NF_ACCEPT; | |
156 | 137 | } |
157 | 138 | |
158 | 139 | return NF_ACCEPT; |