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