|
138 | 138 | #include <linux/errqueue.h>
|
139 | 139 | #include <linux/hrtimer.h>
|
140 | 140 | #include <linux/netfilter_ingress.h>
|
| 141 | +#include <linux/sctp.h> |
141 | 142 |
|
142 | 143 | #include "net-sysfs.h"
|
143 | 144 |
|
@@ -2471,6 +2472,141 @@ int skb_checksum_help(struct sk_buff *skb)
|
2471 | 2472 | }
|
2472 | 2473 | EXPORT_SYMBOL(skb_checksum_help);
|
2473 | 2474 |
|
| 2475 | +/* skb_csum_offload_check - Driver helper function to determine if a device |
| 2476 | + * with limited checksum offload capabilities is able to offload the checksum |
| 2477 | + * for a given packet. |
| 2478 | + * |
| 2479 | + * Arguments: |
| 2480 | + * skb - sk_buff for the packet in question |
| 2481 | + * spec - contains the description of what device can offload |
| 2482 | + * csum_encapped - returns true if the checksum being offloaded is |
| 2483 | + * encpasulated. That is it is checksum for the transport header |
| 2484 | + * in the inner headers. |
| 2485 | + * checksum_help - when set indicates that helper function should |
| 2486 | + * call skb_checksum_help if offload checks fail |
| 2487 | + * |
| 2488 | + * Returns: |
| 2489 | + * true: Packet has passed the checksum checks and should be offloadable to |
| 2490 | + * the device (a driver may still need to check for additional |
| 2491 | + * restrictions of its device) |
| 2492 | + * false: Checksum is not offloadable. If checksum_help was set then |
| 2493 | + * skb_checksum_help was called to resolve checksum for non-GSO |
| 2494 | + * packets and when IP protocol is not SCTP |
| 2495 | + */ |
| 2496 | +bool __skb_csum_offload_chk(struct sk_buff *skb, |
| 2497 | + const struct skb_csum_offl_spec *spec, |
| 2498 | + bool *csum_encapped, |
| 2499 | + bool csum_help) |
| 2500 | +{ |
| 2501 | + struct iphdr *iph; |
| 2502 | + struct ipv6hdr *ipv6; |
| 2503 | + void *nhdr; |
| 2504 | + int protocol; |
| 2505 | + u8 ip_proto; |
| 2506 | + |
| 2507 | + if (skb->protocol == htons(ETH_P_8021Q) || |
| 2508 | + skb->protocol == htons(ETH_P_8021AD)) { |
| 2509 | + if (!spec->vlan_okay) |
| 2510 | + goto need_help; |
| 2511 | + } |
| 2512 | + |
| 2513 | + /* We check whether the checksum refers to a transport layer checksum in |
| 2514 | + * the outermost header or an encapsulated transport layer checksum that |
| 2515 | + * corresponds to the inner headers of the skb. If the checksum is for |
| 2516 | + * something else in the packet we need help. |
| 2517 | + */ |
| 2518 | + if (skb_checksum_start_offset(skb) == skb_transport_offset(skb)) { |
| 2519 | + /* Non-encapsulated checksum */ |
| 2520 | + protocol = eproto_to_ipproto(vlan_get_protocol(skb)); |
| 2521 | + nhdr = skb_network_header(skb); |
| 2522 | + *csum_encapped = false; |
| 2523 | + if (spec->no_not_encapped) |
| 2524 | + goto need_help; |
| 2525 | + } else if (skb->encapsulation && spec->encap_okay && |
| 2526 | + skb_checksum_start_offset(skb) == |
| 2527 | + skb_inner_transport_offset(skb)) { |
| 2528 | + /* Encapsulated checksum */ |
| 2529 | + *csum_encapped = true; |
| 2530 | + switch (skb->inner_protocol_type) { |
| 2531 | + case ENCAP_TYPE_ETHER: |
| 2532 | + protocol = eproto_to_ipproto(skb->inner_protocol); |
| 2533 | + break; |
| 2534 | + case ENCAP_TYPE_IPPROTO: |
| 2535 | + protocol = skb->inner_protocol; |
| 2536 | + break; |
| 2537 | + } |
| 2538 | + nhdr = skb_inner_network_header(skb); |
| 2539 | + } else { |
| 2540 | + goto need_help; |
| 2541 | + } |
| 2542 | + |
| 2543 | + switch (protocol) { |
| 2544 | + case IPPROTO_IP: |
| 2545 | + if (!spec->ipv4_okay) |
| 2546 | + goto need_help; |
| 2547 | + iph = nhdr; |
| 2548 | + ip_proto = iph->protocol; |
| 2549 | + if (iph->ihl != 5 && !spec->ip_options_okay) |
| 2550 | + goto need_help; |
| 2551 | + break; |
| 2552 | + case IPPROTO_IPV6: |
| 2553 | + if (!spec->ipv6_okay) |
| 2554 | + goto need_help; |
| 2555 | + if (spec->no_encapped_ipv6 && *csum_encapped) |
| 2556 | + goto need_help; |
| 2557 | + ipv6 = nhdr; |
| 2558 | + nhdr += sizeof(*ipv6); |
| 2559 | + ip_proto = ipv6->nexthdr; |
| 2560 | + break; |
| 2561 | + default: |
| 2562 | + goto need_help; |
| 2563 | + } |
| 2564 | + |
| 2565 | +ip_proto_again: |
| 2566 | + switch (ip_proto) { |
| 2567 | + case IPPROTO_TCP: |
| 2568 | + if (!spec->tcp_okay || |
| 2569 | + skb->csum_offset != offsetof(struct tcphdr, check)) |
| 2570 | + goto need_help; |
| 2571 | + break; |
| 2572 | + case IPPROTO_UDP: |
| 2573 | + if (!spec->udp_okay || |
| 2574 | + skb->csum_offset != offsetof(struct udphdr, check)) |
| 2575 | + goto need_help; |
| 2576 | + break; |
| 2577 | + case IPPROTO_SCTP: |
| 2578 | + if (!spec->sctp_okay || |
| 2579 | + skb->csum_offset != offsetof(struct sctphdr, checksum)) |
| 2580 | + goto cant_help; |
| 2581 | + break; |
| 2582 | + case NEXTHDR_HOP: |
| 2583 | + case NEXTHDR_ROUTING: |
| 2584 | + case NEXTHDR_DEST: { |
| 2585 | + u8 *opthdr = nhdr; |
| 2586 | + |
| 2587 | + if (protocol != IPPROTO_IPV6 || !spec->ext_hdrs_okay) |
| 2588 | + goto need_help; |
| 2589 | + |
| 2590 | + ip_proto = opthdr[0]; |
| 2591 | + nhdr += (opthdr[1] + 1) << 3; |
| 2592 | + |
| 2593 | + goto ip_proto_again; |
| 2594 | + } |
| 2595 | + default: |
| 2596 | + goto need_help; |
| 2597 | + } |
| 2598 | + |
| 2599 | + /* Passed the tests for offloading checksum */ |
| 2600 | + return true; |
| 2601 | + |
| 2602 | +need_help: |
| 2603 | + if (csum_help && !skb_shinfo(skb)->gso_size) |
| 2604 | + skb_checksum_help(skb); |
| 2605 | +cant_help: |
| 2606 | + return false; |
| 2607 | +} |
| 2608 | +EXPORT_SYMBOL(__skb_csum_offload_chk); |
| 2609 | + |
2474 | 2610 | __be16 skb_network_protocol(struct sk_buff *skb, int *depth)
|
2475 | 2611 | {
|
2476 | 2612 | __be16 type = skb->protocol;
|
|
0 commit comments