Commit d63a650736f566a1f9e9434725d2089597c0d2cc

Authored by Patrick McHardy
1 parent 6185f870e2

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