1 /* 2 * Checksum updating actions 3 * 4 * Copyright (c) 2010 Gregoire Baron <baronchon@n7mm.org> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the Free 8 * Software Foundation; either version 2 of the License, or (at your option) 9 * any later version. 10 * 11 */ 12 13 #include <linux/types.h> 14 #include <linux/init.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/spinlock.h> 18 19 #include <linux/netlink.h> 20 #include <net/netlink.h> 21 #include <linux/rtnetlink.h> 22 23 #include <linux/skbuff.h> 24 25 #include <net/ip.h> 26 #include <net/ipv6.h> 27 #include <net/icmp.h> 28 #include <linux/icmpv6.h> 29 #include <linux/igmp.h> 30 #include <net/tcp.h> 31 #include <net/udp.h> 32 #include <net/ip6_checksum.h> 33 34 #include <net/act_api.h> 35 36 #include <linux/tc_act/tc_csum.h> 37 #include <net/tc_act/tc_csum.h> 38 39 #define CSUM_TAB_MASK 15 40 static struct tcf_common *tcf_csum_ht[CSUM_TAB_MASK + 1]; 41 static u32 csum_idx_gen; 42 static DEFINE_RWLOCK(csum_lock); 43 44 static struct tcf_hashinfo csum_hash_info = { 45 .htab = tcf_csum_ht, 46 .hmask = CSUM_TAB_MASK, 47 .lock = &csum_lock, 48 }; 49 50 static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { 51 [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, 52 }; 53 54 static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, 55 struct tc_action *a, int ovr, int bind) 56 { 57 struct nlattr *tb[TCA_CSUM_MAX + 1]; 58 struct tc_csum *parm; 59 struct tcf_common *pc; 60 struct tcf_csum *p; 61 int ret = 0, err; 62 63 if (nla == NULL) 64 return -EINVAL; 65 66 err = nla_parse_nested(tb, TCA_CSUM_MAX, nla, csum_policy); 67 if (err < 0) 68 return err; 69 70 if (tb[TCA_CSUM_PARMS] == NULL) 71 return -EINVAL; 72 parm = nla_data(tb[TCA_CSUM_PARMS]); 73 74 pc = tcf_hash_check(parm->index, a, bind, &csum_hash_info); 75 if (!pc) { 76 pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, 77 &csum_idx_gen, &csum_hash_info); 78 if (IS_ERR(pc)) 79 return PTR_ERR(pc); 80 p = to_tcf_csum(pc); 81 ret = ACT_P_CREATED; 82 } else { 83 p = to_tcf_csum(pc); 84 if (!ovr) { 85 tcf_hash_release(pc, bind, &csum_hash_info); 86 return -EEXIST; 87 } 88 } 89 90 spin_lock_bh(&p->tcf_lock); 91 p->tcf_action = parm->action; 92 p->update_flags = parm->update_flags; 93 spin_unlock_bh(&p->tcf_lock); 94 95 if (ret == ACT_P_CREATED) 96 tcf_hash_insert(pc, &csum_hash_info); 97 98 return ret; 99 } 100 101 static int tcf_csum_cleanup(struct tc_action *a, int bind) 102 { 103 struct tcf_csum *p = a->priv; 104 return tcf_hash_release(&p->common, bind, &csum_hash_info); 105 } 106 107 /** 108 * tcf_csum_skb_nextlayer - Get next layer pointer 109 * @skb: sk_buff to use 110 * @ihl: previous summed headers length 111 * @ipl: complete packet length 112 * @jhl: next header length 113 * 114 * Check the expected next layer availability in the specified sk_buff. 115 * Return the next layer pointer if pass, NULL otherwise. 116 */ 117 static void *tcf_csum_skb_nextlayer(struct sk_buff *skb, 118 unsigned int ihl, unsigned int ipl, 119 unsigned int jhl) 120 { 121 int ntkoff = skb_network_offset(skb); 122 int hl = ihl + jhl; 123 124 if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) || 125 (skb_cloned(skb) && 126 !skb_clone_writable(skb, hl + ntkoff) && 127 pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) 128 return NULL; 129 else 130 return (void *)(skb_network_header(skb) + ihl); 131 } 132 133 static int tcf_csum_ipv4_icmp(struct sk_buff *skb, 134 unsigned int ihl, unsigned int ipl) 135 { 136 struct icmphdr *icmph; 137 138 icmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmph)); 139 if (icmph == NULL) 140 return 0; 141 142 icmph->checksum = 0; 143 skb->csum = csum_partial(icmph, ipl - ihl, 0); 144 icmph->checksum = csum_fold(skb->csum); 145 146 skb->ip_summed = CHECKSUM_NONE; 147 148 return 1; 149 } 150 151 static int tcf_csum_ipv4_igmp(struct sk_buff *skb, 152 unsigned int ihl, unsigned int ipl) 153 { 154 struct igmphdr *igmph; 155 156 igmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*igmph)); 157 if (igmph == NULL) 158 return 0; 159 160 igmph->csum = 0; 161 skb->csum = csum_partial(igmph, ipl - ihl, 0); 162 igmph->csum = csum_fold(skb->csum); 163 164 skb->ip_summed = CHECKSUM_NONE; 165 166 return 1; 167 } 168 169 static int tcf_csum_ipv6_icmp(struct sk_buff *skb, 170 unsigned int ihl, unsigned int ipl) 171 { 172 struct icmp6hdr *icmp6h; 173 const struct ipv6hdr *ip6h; 174 175 icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h)); 176 if (icmp6h == NULL) 177 return 0; 178 179 ip6h = ipv6_hdr(skb); 180 icmp6h->icmp6_cksum = 0; 181 skb->csum = csum_partial(icmp6h, ipl - ihl, 0); 182 icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 183 ipl - ihl, IPPROTO_ICMPV6, 184 skb->csum); 185 186 skb->ip_summed = CHECKSUM_NONE; 187 188 return 1; 189 } 190 191 static int tcf_csum_ipv4_tcp(struct sk_buff *skb, 192 unsigned int ihl, unsigned int ipl) 193 { 194 struct tcphdr *tcph; 195 const struct iphdr *iph; 196 197 tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); 198 if (tcph == NULL) 199 return 0; 200 201 iph = ip_hdr(skb); 202 tcph->check = 0; 203 skb->csum = csum_partial(tcph, ipl - ihl, 0); 204 tcph->check = tcp_v4_check(ipl - ihl, 205 iph->saddr, iph->daddr, skb->csum); 206 207 skb->ip_summed = CHECKSUM_NONE; 208 209 return 1; 210 } 211 212 static int tcf_csum_ipv6_tcp(struct sk_buff *skb, 213 unsigned int ihl, unsigned int ipl) 214 { 215 struct tcphdr *tcph; 216 const struct ipv6hdr *ip6h; 217 218 tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); 219 if (tcph == NULL) 220 return 0; 221 222 ip6h = ipv6_hdr(skb); 223 tcph->check = 0; 224 skb->csum = csum_partial(tcph, ipl - ihl, 0); 225 tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 226 ipl - ihl, IPPROTO_TCP, 227 skb->csum); 228 229 skb->ip_summed = CHECKSUM_NONE; 230 231 return 1; 232 } 233 234 static int tcf_csum_ipv4_udp(struct sk_buff *skb, 235 unsigned int ihl, unsigned int ipl, int udplite) 236 { 237 struct udphdr *udph; 238 const struct iphdr *iph; 239 u16 ul; 240 241 /* 242 * Support both UDP and UDPLITE checksum algorithms, Don't use 243 * udph->len to get the real length without any protocol check, 244 * UDPLITE uses udph->len for another thing, 245 * Use iph->tot_len, or just ipl. 246 */ 247 248 udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); 249 if (udph == NULL) 250 return 0; 251 252 iph = ip_hdr(skb); 253 ul = ntohs(udph->len); 254 255 if (udplite || udph->check) { 256 257 udph->check = 0; 258 259 if (udplite) { 260 if (ul == 0) 261 skb->csum = csum_partial(udph, ipl - ihl, 0); 262 else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) 263 skb->csum = csum_partial(udph, ul, 0); 264 else 265 goto ignore_obscure_skb; 266 } else { 267 if (ul != ipl - ihl) 268 goto ignore_obscure_skb; 269 270 skb->csum = csum_partial(udph, ul, 0); 271 } 272 273 udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, 274 ul, iph->protocol, 275 skb->csum); 276 277 if (!udph->check) 278 udph->check = CSUM_MANGLED_0; 279 } 280 281 skb->ip_summed = CHECKSUM_NONE; 282 283 ignore_obscure_skb: 284 return 1; 285 } 286 287 static int tcf_csum_ipv6_udp(struct sk_buff *skb, 288 unsigned int ihl, unsigned int ipl, int udplite) 289 { 290 struct udphdr *udph; 291 const struct ipv6hdr *ip6h; 292 u16 ul; 293 294 /* 295 * Support both UDP and UDPLITE checksum algorithms, Don't use 296 * udph->len to get the real length without any protocol check, 297 * UDPLITE uses udph->len for another thing, 298 * Use ip6h->payload_len + sizeof(*ip6h) ... , or just ipl. 299 */ 300 301 udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); 302 if (udph == NULL) 303 return 0; 304 305 ip6h = ipv6_hdr(skb); 306 ul = ntohs(udph->len); 307 308 udph->check = 0; 309 310 if (udplite) { 311 if (ul == 0) 312 skb->csum = csum_partial(udph, ipl - ihl, 0); 313 314 else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) 315 skb->csum = csum_partial(udph, ul, 0); 316 317 else 318 goto ignore_obscure_skb; 319 } else { 320 if (ul != ipl - ihl) 321 goto ignore_obscure_skb; 322 323 skb->csum = csum_partial(udph, ul, 0); 324 } 325 326 udph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ul, 327 udplite ? IPPROTO_UDPLITE : IPPROTO_UDP, 328 skb->csum); 329 330 if (!udph->check) 331 udph->check = CSUM_MANGLED_0; 332 333 skb->ip_summed = CHECKSUM_NONE; 334 335 ignore_obscure_skb: 336 return 1; 337 } 338 339 static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) 340 { 341 const struct iphdr *iph; 342 int ntkoff; 343 344 ntkoff = skb_network_offset(skb); 345 346 if (!pskb_may_pull(skb, sizeof(*iph) + ntkoff)) 347 goto fail; 348 349 iph = ip_hdr(skb); 350 351 switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { 352 case IPPROTO_ICMP: 353 if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) 354 if (!tcf_csum_ipv4_icmp(skb, iph->ihl * 4, 355 ntohs(iph->tot_len))) 356 goto fail; 357 break; 358 case IPPROTO_IGMP: 359 if (update_flags & TCA_CSUM_UPDATE_FLAG_IGMP) 360 if (!tcf_csum_ipv4_igmp(skb, iph->ihl * 4, 361 ntohs(iph->tot_len))) 362 goto fail; 363 break; 364 case IPPROTO_TCP: 365 if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) 366 if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4, 367 ntohs(iph->tot_len))) 368 goto fail; 369 break; 370 case IPPROTO_UDP: 371 if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) 372 if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, 373 ntohs(iph->tot_len), 0)) 374 goto fail; 375 break; 376 case IPPROTO_UDPLITE: 377 if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) 378 if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, 379 ntohs(iph->tot_len), 1)) 380 goto fail; 381 break; 382 } 383 384 if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) { 385 if (skb_cloned(skb) && 386 !skb_clone_writable(skb, sizeof(*iph) + ntkoff) && 387 pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 388 goto fail; 389 390 ip_send_check(ip_hdr(skb)); 391 } 392 393 return 1; 394 395 fail: 396 return 0; 397 } 398 399 static int tcf_csum_ipv6_hopopts(struct ipv6_opt_hdr *ip6xh, 400 unsigned int ixhl, unsigned int *pl) 401 { 402 int off, len, optlen; 403 unsigned char *xh = (void *)ip6xh; 404 405 off = sizeof(*ip6xh); 406 len = ixhl - off; 407 408 while (len > 1) { 409 switch (xh[off]) { 410 case IPV6_TLV_PAD1: 411 optlen = 1; 412 break; 413 case IPV6_TLV_JUMBO: 414 optlen = xh[off + 1] + 2; 415 if (optlen != 6 || len < 6 || (off & 3) != 2) 416 /* wrong jumbo option length/alignment */ 417 return 0; 418 *pl = ntohl(*(__be32 *)(xh + off + 2)); 419 goto done; 420 default: 421 optlen = xh[off + 1] + 2; 422 if (optlen > len) 423 /* ignore obscure options */ 424 goto done; 425 break; 426 } 427 off += optlen; 428 len -= optlen; 429 } 430 431 done: 432 return 1; 433 } 434 435 static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags) 436 { 437 struct ipv6hdr *ip6h; 438 struct ipv6_opt_hdr *ip6xh; 439 unsigned int hl, ixhl; 440 unsigned int pl; 441 int ntkoff; 442 u8 nexthdr; 443 444 ntkoff = skb_network_offset(skb); 445 446 hl = sizeof(*ip6h); 447 448 if (!pskb_may_pull(skb, hl + ntkoff)) 449 goto fail; 450 451 ip6h = ipv6_hdr(skb); 452 453 pl = ntohs(ip6h->payload_len); 454 nexthdr = ip6h->nexthdr; 455 456 do { 457 switch (nexthdr) { 458 case NEXTHDR_FRAGMENT: 459 goto ignore_skb; 460 case NEXTHDR_ROUTING: 461 case NEXTHDR_HOP: 462 case NEXTHDR_DEST: 463 if (!pskb_may_pull(skb, hl + sizeof(*ip6xh) + ntkoff)) 464 goto fail; 465 ip6xh = (void *)(skb_network_header(skb) + hl); 466 ixhl = ipv6_optlen(ip6xh); 467 if (!pskb_may_pull(skb, hl + ixhl + ntkoff)) 468 goto fail; 469 ip6xh = (void *)(skb_network_header(skb) + hl); 470 if ((nexthdr == NEXTHDR_HOP) && 471 !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl))) 472 goto fail; 473 nexthdr = ip6xh->nexthdr; 474 hl += ixhl; 475 break; 476 case IPPROTO_ICMPV6: 477 if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) 478 if (!tcf_csum_ipv6_icmp(skb, 479 hl, pl + sizeof(*ip6h))) 480 goto fail; 481 goto done; 482 case IPPROTO_TCP: 483 if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) 484 if (!tcf_csum_ipv6_tcp(skb, 485 hl, pl + sizeof(*ip6h))) 486 goto fail; 487 goto done; 488 case IPPROTO_UDP: 489 if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) 490 if (!tcf_csum_ipv6_udp(skb, hl, 491 pl + sizeof(*ip6h), 0)) 492 goto fail; 493 goto done; 494 case IPPROTO_UDPLITE: 495 if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) 496 if (!tcf_csum_ipv6_udp(skb, hl, 497 pl + sizeof(*ip6h), 1)) 498 goto fail; 499 goto done; 500 default: 501 goto ignore_skb; 502 } 503 } while (pskb_may_pull(skb, hl + 1 + ntkoff)); 504 505 done: 506 ignore_skb: 507 return 1; 508 509 fail: 510 return 0; 511 } 512 513 static int tcf_csum(struct sk_buff *skb, 514 const struct tc_action *a, struct tcf_result *res) 515 { 516 struct tcf_csum *p = a->priv; 517 int action; 518 u32 update_flags; 519 520 spin_lock(&p->tcf_lock); 521 p->tcf_tm.lastuse = jiffies; 522 bstats_update(&p->tcf_bstats, skb); 523 action = p->tcf_action; 524 update_flags = p->update_flags; 525 spin_unlock(&p->tcf_lock); 526 527 if (unlikely(action == TC_ACT_SHOT)) 528 goto drop; 529 530 switch (skb->protocol) { 531 case cpu_to_be16(ETH_P_IP): 532 if (!tcf_csum_ipv4(skb, update_flags)) 533 goto drop; 534 break; 535 case cpu_to_be16(ETH_P_IPV6): 536 if (!tcf_csum_ipv6(skb, update_flags)) 537 goto drop; 538 break; 539 } 540 541 return action; 542 543 drop: 544 spin_lock(&p->tcf_lock); 545 p->tcf_qstats.drops++; 546 spin_unlock(&p->tcf_lock); 547 return TC_ACT_SHOT; 548 } 549 550 static int tcf_csum_dump(struct sk_buff *skb, 551 struct tc_action *a, int bind, int ref) 552 { 553 unsigned char *b = skb_tail_pointer(skb); 554 struct tcf_csum *p = a->priv; 555 struct tc_csum opt = { 556 .update_flags = p->update_flags, 557 .index = p->tcf_index, 558 .action = p->tcf_action, 559 .refcnt = p->tcf_refcnt - ref, 560 .bindcnt = p->tcf_bindcnt - bind, 561 }; 562 struct tcf_t t; 563 564 if (nla_put(skb, TCA_CSUM_PARMS, sizeof(opt), &opt)) 565 goto nla_put_failure; 566 t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); 567 t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); 568 t.expires = jiffies_to_clock_t(p->tcf_tm.expires); 569 if (nla_put(skb, TCA_CSUM_TM, sizeof(t), &t)) 570 goto nla_put_failure; 571 572 return skb->len; 573 574 nla_put_failure: 575 nlmsg_trim(skb, b); 576 return -1; 577 } 578 579 static struct tc_action_ops act_csum_ops = { 580 .kind = "csum", 581 .hinfo = &csum_hash_info, 582 .type = TCA_ACT_CSUM, 583 .capab = TCA_CAP_NONE, 584 .owner = THIS_MODULE, 585 .act = tcf_csum, 586 .dump = tcf_csum_dump, 587 .cleanup = tcf_csum_cleanup, 588 .lookup = tcf_hash_search, 589 .init = tcf_csum_init, 590 .walk = tcf_generic_walker 591 }; 592 593 MODULE_DESCRIPTION("Checksum updating actions"); 594 MODULE_LICENSE("GPL"); 595 596 static int __init csum_init_module(void) 597 { 598 return tcf_register_action(&act_csum_ops); 599 } 600 601 static void __exit csum_cleanup_module(void) 602 { 603 tcf_unregister_action(&act_csum_ops); 604 } 605 606 module_init(csum_init_module); 607 module_exit(csum_cleanup_module); 608