| @@ -539,6 +539,46 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, · 538 unmodified lines |
| 539 | 539 | return 1; |
| 540 | 540 | } |
| 541 | 541 | |
| 542 | +static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, |
| 543 | + unsigned int off, |
| 544 | + struct vxlanhdr *vh, size_t hdrlen, |
| 545 | + u32 data) |
| 546 | +{ |
| 547 | + size_t start, offset, plen; |
| 548 | + __wsum delta; |
| 549 | + |
| 550 | + if (skb->remcsum_offload) |
| 551 | + return vh; |
| 552 | + |
| 553 | + if (!NAPI_GRO_CB(skb)->csum_valid) |
| 554 | + return NULL; |
| 555 | + |
| 556 | + start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; |
| 557 | + offset = start + ((data & VXLAN_RCO_UDP) ? |
| 558 | + offsetof(struct udphdr, check) : |
| 559 | + offsetof(struct tcphdr, check)); |
| 560 | + |
| 561 | + plen = hdrlen + offset + sizeof(u16); |
| 562 | + |
| 563 | + /* Pull checksum that will be written */ |
| 564 | + if (skb_gro_header_hard(skb, off + plen)) { |
| 565 | + vh = skb_gro_header_slow(skb, off + plen, off); |
| 566 | + if (!vh) |
| 567 | + return NULL; |
| 568 | + } |
| 569 | + |
| 570 | + delta = remcsum_adjust((void *)vh + hdrlen, |
| 571 | + NAPI_GRO_CB(skb)->csum, start, offset); |
| 572 | + |
| 573 | + /* Adjust skb->csum since we changed the packet */ |
| 574 | + skb->csum = csum_add(skb->csum, delta); |
| 575 | + NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); |
| 576 | + |
| 577 | + skb->remcsum_offload = 1; |
| 578 | + |
| 579 | + return vh; |
| 580 | +} |
| 581 | + |
| 542 | 582 | static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, |
| 543 | 583 | struct sk_buff *skb, |
| 544 | 584 | struct udp_offload *uoff) |
| @@ -547,6 +587,9 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, · 2 unmodified lines |
| 547 | 587 | struct vxlanhdr *vh, *vh2; |
| 548 | 588 | unsigned int hlen, off_vx; |
| 549 | 589 | int flush = 1; |
| 590 | + struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock, |
| 591 | + udp_offloads); |
| 592 | + u32 flags; |
| 550 | 593 | |
| 551 | 594 | off_vx = skb_gro_offset(skb); |
| 552 | 595 | hlen = off_vx + sizeof(*vh); |
| @@ -557,6 +600,19 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, · 4 unmodified lines |
| 557 | 600 | goto out; |
| 558 | 601 | } |
| 559 | 602 | |
| 603 | + skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */ |
| 604 | + skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr)); |
| 605 | + |
| 606 | + flags = ntohl(vh->vx_flags); |
| 607 | + |
| 608 | + if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { |
| 609 | + vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), |
| 610 | + ntohl(vh->vx_vni)); |
| 611 | + |
| 612 | + if (!vh) |
| 613 | + goto out; |
| 614 | + } |
| 615 | + |
| 560 | 616 | flush = 0; |
| 561 | 617 | |
| 562 | 618 | for (p = *head; p; p = p->next) { |
| @@ -570,8 +626,6 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, · 7 unmodified lines |
| 570 | 626 | } |
| 571 | 627 | } |
| 572 | 628 | |
| 573 | | − skb_gro_pull(skb, sizeof(struct vxlanhdr)); |
| 574 | | − skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr)); |
| 575 | 629 | pp = eth_gro_receive(head, skb); |
| 576 | 630 | |
| 577 | 631 | out: |
| @@ -1087,6 +1141,42 @@ static void vxlan_igmp_leave(struct work_struct *work) · 509 unmodified lines |
| 1087 | 1141 | dev_put(vxlan->dev); |
| 1088 | 1142 | } |
| 1089 | 1143 | |
| 1144 | +static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh, |
| 1145 | + size_t hdrlen, u32 data) |
| 1146 | +{ |
| 1147 | + size_t start, offset, plen; |
| 1148 | + __wsum delta; |
| 1149 | + |
| 1150 | + if (skb->remcsum_offload) { |
| 1151 | + /* Already processed in GRO path */ |
| 1152 | + skb->remcsum_offload = 0; |
| 1153 | + return vh; |
| 1154 | + } |
| 1155 | + |
| 1156 | + start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; |
| 1157 | + offset = start + ((data & VXLAN_RCO_UDP) ? |
| 1158 | + offsetof(struct udphdr, check) : |
| 1159 | + offsetof(struct tcphdr, check)); |
| 1160 | + |
| 1161 | + plen = hdrlen + offset + sizeof(u16); |
| 1162 | + |
| 1163 | + if (!pskb_may_pull(skb, plen)) |
| 1164 | + return NULL; |
| 1165 | + |
| 1166 | + vh = (struct vxlanhdr *)(udp_hdr(skb) + 1); |
| 1167 | + |
| 1168 | + if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE)) |
| 1169 | + __skb_checksum_complete(skb); |
| 1170 | + |
| 1171 | + delta = remcsum_adjust((void *)vh + hdrlen, |
| 1172 | + skb->csum, start, offset); |
| 1173 | + |
| 1174 | + /* Adjust skb->csum since we changed the packet */ |
| 1175 | + skb->csum = csum_add(skb->csum, delta); |
| 1176 | + |
| 1177 | + return vh; |
| 1178 | +} |
| 1179 | + |
| 1090 | 1180 | /* Callback from net/ipv4/udp.c to receive packets */ |
| 1091 | 1181 | static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) |
| 1092 | 1182 | { |
| @@ -1111,12 +1201,22 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) · 18 unmodified lines |
| 1111 | 1201 | |
| 1112 | 1202 | if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB))) |
| 1113 | 1203 | goto drop; |
| 1204 | + vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1); |
| 1114 | 1205 | |
| 1115 | 1206 | vs = rcu_dereference_sk_user_data(sk); |
| 1116 | 1207 | if (!vs) |
| 1117 | 1208 | goto drop; |
| 1118 | 1209 | |
| 1119 | | − if (flags || (vni & 0xff)) { |
| 1210 | + if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { |
| 1211 | + vxh = vxlan_remcsum(skb, vxh, sizeof(struct vxlanhdr), vni); |
| 1212 | + if (!vxh) |
| 1213 | + goto drop; |
| 1214 | + |
| 1215 | + flags &= ~VXLAN_HF_RCO; |
| 1216 | + vni &= VXLAN_VID_MASK; |
| 1217 | + } |
| 1218 | + |
| 1219 | + if (flags || (vni & ~VXLAN_VID_MASK)) { |
| 1120 | 1220 | /* If there are any unprocessed flags remaining treat |
| 1121 | 1221 | * this as a malformed packet. This behavior diverges from |
| 1122 | 1222 | * VXLAN RFC (RFC7348) which stipulates that bits in reserved |
| @@ -1553,8 +1653,23 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, · 430 unmodified lines |
| 1553 | 1653 | int min_headroom; |
| 1554 | 1654 | int err; |
| 1555 | 1655 | bool udp_sum = !udp_get_no_check6_tx(vs->sock->sk); |
| 1656 | + int type = udp_sum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; |
| 1657 | + u16 hdrlen = sizeof(struct vxlanhdr); |
| 1658 | + |
| 1659 | + if ((vs->flags & VXLAN_F_REMCSUM_TX) && |
| 1660 | + skb->ip_summed == CHECKSUM_PARTIAL) { |
| 1661 | + int csum_start = skb_checksum_start_offset(skb); |
| 1662 | + |
| 1663 | + if (csum_start <= VXLAN_MAX_REMCSUM_START && |
| 1664 | + !(csum_start & VXLAN_RCO_SHIFT_MASK) && |
| 1665 | + (skb->csum_offset == offsetof(struct udphdr, check) || |
| 1666 | + skb->csum_offset == offsetof(struct tcphdr, check))) { |
| 1667 | + udp_sum = false; |
| 1668 | + type |= SKB_GSO_TUNNEL_REMCSUM; |
| 1669 | + } |
| 1670 | + } |
| 1556 | 1671 | |
| 1557 | | − skb = udp_tunnel_handle_offloads(skb, udp_sum); |
| 1672 | + skb = iptunnel_handle_offloads(skb, udp_sum, type); |
| 1558 | 1673 | if (IS_ERR(skb)) { |
| 1559 | 1674 | err = -EINVAL; |
| 1560 | 1675 | goto err; |
| @@ -1583,6 +1698,22 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, · 22 unmodified lines |
| 1583 | 1698 | vxh->vx_flags = htonl(VXLAN_HF_VNI); |
| 1584 | 1699 | vxh->vx_vni = vni; |
| 1585 | 1700 | |
| 1701 | + if (type & SKB_GSO_TUNNEL_REMCSUM) { |
| 1702 | + u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> |
| 1703 | + VXLAN_RCO_SHIFT; |
| 1704 | + |
| 1705 | + if (skb->csum_offset == offsetof(struct udphdr, check)) |
| 1706 | + data |= VXLAN_RCO_UDP; |
| 1707 | + |
| 1708 | + vxh->vx_vni |= htonl(data); |
| 1709 | + vxh->vx_flags |= htonl(VXLAN_HF_RCO); |
| 1710 | + |
| 1711 | + if (!skb_is_gso(skb)) { |
| 1712 | + skb->ip_summed = CHECKSUM_NONE; |
| 1713 | + skb->encapsulation = 0; |
| 1714 | + } |
| 1715 | + } |
| 1716 | + |
| 1586 | 1717 | skb_set_inner_protocol(skb, htons(ETH_P_TEB)); |
| 1587 | 1718 | |
| 1588 | 1719 | udp_tunnel6_xmit_skb(vs->sock, dst, skb, dev, saddr, daddr, prio, |
| @@ -1603,8 +1734,23 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, · 14 unmodified lines |
| 1603 | 1734 | int min_headroom; |
| 1604 | 1735 | int err; |
| 1605 | 1736 | bool udp_sum = !vs->sock->sk->sk_no_check_tx; |
| 1737 | + int type = udp_sum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; |
| 1738 | + u16 hdrlen = sizeof(struct vxlanhdr); |
| 1739 | + |
| 1740 | + if ((vs->flags & VXLAN_F_REMCSUM_TX) && |
| 1741 | + skb->ip_summed == CHECKSUM_PARTIAL) { |
| 1742 | + int csum_start = skb_checksum_start_offset(skb); |
| 1743 | + |
| 1744 | + if (csum_start <= VXLAN_MAX_REMCSUM_START && |
| 1745 | + !(csum_start & VXLAN_RCO_SHIFT_MASK) && |
| 1746 | + (skb->csum_offset == offsetof(struct udphdr, check) || |
| 1747 | + skb->csum_offset == offsetof(struct tcphdr, check))) { |
| 1748 | + udp_sum = false; |
| 1749 | + type |= SKB_GSO_TUNNEL_REMCSUM; |
| 1750 | + } |
| 1751 | + } |
| 1606 | 1752 | |
| 1607 | | − skb = udp_tunnel_handle_offloads(skb, udp_sum); |
| 1753 | + skb = iptunnel_handle_offloads(skb, udp_sum, type); |
| 1608 | 1754 | if (IS_ERR(skb)) |
| 1609 | 1755 | return PTR_ERR(skb); |
| 1610 | 1756 | |
| @@ -1627,6 +1773,22 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, · 16 unmodified lines |
| 1627 | 1773 | vxh->vx_flags = htonl(VXLAN_HF_VNI); |
| 1628 | 1774 | vxh->vx_vni = vni; |
| 1629 | 1775 | |
| 1776 | + if (type & SKB_GSO_TUNNEL_REMCSUM) { |
| 1777 | + u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> |
| 1778 | + VXLAN_RCO_SHIFT; |
| 1779 | + |
| 1780 | + if (skb->csum_offset == offsetof(struct udphdr, check)) |
| 1781 | + data |= VXLAN_RCO_UDP; |
| 1782 | + |
| 1783 | + vxh->vx_vni |= htonl(data); |
| 1784 | + vxh->vx_flags |= htonl(VXLAN_HF_RCO); |
| 1785 | + |
| 1786 | + if (!skb_is_gso(skb)) { |
| 1787 | + skb->ip_summed = CHECKSUM_NONE; |
| 1788 | + skb->encapsulation = 0; |
| 1789 | + } |
| 1790 | + } |
| 1791 | + |
| 1630 | 1792 | skb_set_inner_protocol(skb, htons(ETH_P_TEB)); |
| 1631 | 1793 | |
| 1632 | 1794 | return udp_tunnel_xmit_skb(vs->sock, rt, skb, src, dst, tos, |
| @@ -2218,6 +2380,8 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { · 585 unmodified lines |
| 2218 | 2380 | [IFLA_VXLAN_UDP_CSUM] = { .type = NLA_U8 }, |
| 2219 | 2381 | [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, |
| 2220 | 2382 | [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, |
| 2383 | + [IFLA_VXLAN_REMCSUM_TX] = { .type = NLA_U8 }, |
| 2384 | + [IFLA_VXLAN_REMCSUM_RX] = { .type = NLA_U8 }, |
| 2221 | 2385 | }; |
| 2222 | 2386 | |
| 2223 | 2387 | static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) |
| @@ -2339,6 +2503,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, · 115 unmodified lines |
| 2339 | 2503 | atomic_set(&vs->refcnt, 1); |
| 2340 | 2504 | vs->rcv = rcv; |
| 2341 | 2505 | vs->data = data; |
| 2506 | + vs->flags = flags; |
| 2342 | 2507 | |
| 2343 | 2508 | /* Initialize the vxlan udp offloads structure */ |
| 2344 | 2509 | vs->udp_offloads.port = port; |
| @@ -2533,6 +2698,14 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, · 188 unmodified lines |
| 2533 | 2698 | nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])) |
| 2534 | 2699 | vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX; |
| 2535 | 2700 | |
| 2701 | + if (data[IFLA_VXLAN_REMCSUM_TX] && |
| 2702 | + nla_get_u8(data[IFLA_VXLAN_REMCSUM_TX])) |
| 2703 | + vxlan->flags |= VXLAN_F_REMCSUM_TX; |
| 2704 | + |
| 2705 | + if (data[IFLA_VXLAN_REMCSUM_RX] && |
| 2706 | + nla_get_u8(data[IFLA_VXLAN_REMCSUM_RX])) |
| 2707 | + vxlan->flags |= VXLAN_F_REMCSUM_RX; |
| 2708 | + |
| 2536 | 2709 | if (vxlan_find_vni(net, vni, use_ipv6 ? AF_INET6 : AF_INET, |
| 2537 | 2710 | vxlan->dst_port)) { |
| 2538 | 2711 | pr_info("duplicate VNI %u\n", vni); |
| @@ -2601,6 +2774,8 @@ static size_t vxlan_get_size(const struct net_device *dev) · 62 unmodified lines |
| 2601 | 2774 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */ |
| 2602 | 2775 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */ |
| 2603 | 2776 | nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */ |
| 2777 | + nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_TX */ |
| 2778 | + nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_RX */ |
| 2604 | 2779 | 0; |
| 2605 | 2780 | } |
| 2606 | 2781 | |
| @@ -2666,7 +2841,11 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) · 59 unmodified lines |
| 2666 | 2841 | nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, |
| 2667 | 2842 | !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) || |
| 2668 | 2843 | nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, |
| 2669 | | − !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX))) |
| 2844 | + !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) || |
| 2845 | + nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX, |
| 2846 | + !!(vxlan->flags & VXLAN_F_REMCSUM_TX)) || |
| 2847 | + nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX, |
| 2848 | + !!(vxlan->flags & VXLAN_F_REMCSUM_RX))) |
| 2670 | 2849 | goto nla_put_failure; |
| 2671 | 2850 | |
| 2672 | 2851 | if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports)) |