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; |
