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