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