act_csum.c 13 KB

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