aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-03-20 10:15:53 -0400
committerPatrick McHardy <kaber@trash.net>2008-04-14 05:15:49 -0400
commitd63a650736f566a1f9e9434725d2089597c0d2cc (patch)
treef0a3d5dbc9ced46f95582e4133b55bb70a1ae365
parent6185f870e293a0a3eae5c81eb0106480cf03dfde (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.h22
-rw-r--r--net/ipv4/netfilter.c37
-rw-r--r--net/ipv6/netfilter.c42
-rw-r--r--net/netfilter/nf_conntrack_proto_udplite.c33
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
271static inline __sum16
272nf_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
266extern int nf_register_afinfo(const struct nf_afinfo *afinfo); 288extern int nf_register_afinfo(const struct nf_afinfo *afinfo);
267extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); 289extern 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
186EXPORT_SYMBOL(nf_ip_checksum); 185EXPORT_SYMBOL(nf_ip_checksum);
187 186
187static __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
188static int nf_ip_route(struct dst_entry **dst, struct flowi *fl) 210static 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
193static const struct nf_afinfo nf_ip_afinfo = { 215static 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
202static int ipv4_netfilter_init(void) 225static 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
125EXPORT_SYMBOL(nf_ip6_checksum); 124EXPORT_SYMBOL(nf_ip6_checksum);
126 125
126static __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
127static const struct nf_afinfo nf_ip6_afinfo = { 154static 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
136int __init ipv6_netfilter_init(void) 164int __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;