udpGRO evaluates the UDP packet at pktI in bufs for coalescing with existing packets tracked in table. It returns a groResultNoop when no action was taken, groResultTableInsert when the evaluated packet was inserted into table, and groResultCoalesced when the evaluated packet was coalesced with anot
(bufs [][]byte, offset int, pktI int, table *udpGROTable, isV6 bool)
| 784 | // inserted into table, and groResultCoalesced when the evaluated packet was |
| 785 | // coalesced with another packet in table. |
| 786 | func udpGRO(bufs [][]byte, offset int, pktI int, table *udpGROTable, isV6 bool) groResult { |
| 787 | pkt := bufs[pktI][offset:] |
| 788 | if len(pkt) > maxUint16 { |
| 789 | // A valid IPv4 or IPv6 packet will never exceed this. |
| 790 | return groResultNoop |
| 791 | } |
| 792 | iphLen := int((pkt[0] & 0x0F) * 4) |
| 793 | if isV6 { |
| 794 | iphLen = 40 |
| 795 | ipv6HPayloadLen := int(binary.BigEndian.Uint16(pkt[4:])) |
| 796 | if ipv6HPayloadLen != len(pkt)-iphLen { |
| 797 | return groResultNoop |
| 798 | } |
| 799 | } else { |
| 800 | totalLen := int(binary.BigEndian.Uint16(pkt[2:])) |
| 801 | if totalLen != len(pkt) { |
| 802 | return groResultNoop |
| 803 | } |
| 804 | } |
| 805 | if len(pkt) < iphLen { |
| 806 | return groResultNoop |
| 807 | } |
| 808 | if len(pkt) < iphLen+udphLen { |
| 809 | return groResultNoop |
| 810 | } |
| 811 | if !isV6 { |
| 812 | if pkt[6]&ipv4FlagMoreFragments != 0 || pkt[6]<<3 != 0 || pkt[7] != 0 { |
| 813 | // no GRO support for fragmented segments for now |
| 814 | return groResultNoop |
| 815 | } |
| 816 | } |
| 817 | gsoSize := uint16(len(pkt) - udphLen - iphLen) |
| 818 | // not a candidate if payload len is 0 |
| 819 | if gsoSize < 1 { |
| 820 | return groResultNoop |
| 821 | } |
| 822 | srcAddrOffset := ipv4SrcAddrOffset |
| 823 | addrLen := 4 |
| 824 | if isV6 { |
| 825 | srcAddrOffset = ipv6SrcAddrOffset |
| 826 | addrLen = 16 |
| 827 | } |
| 828 | items, existing := table.lookupOrInsert(pkt, srcAddrOffset, srcAddrOffset+addrLen, iphLen, pktI) |
| 829 | if !existing { |
| 830 | return groResultTableInsert |
| 831 | } |
| 832 | // With UDP we only check the last item, otherwise we could reorder packets |
| 833 | // for a given flow. We must also always insert a new item, or successfully |
| 834 | // coalesce with an existing item, for the same reason. |
| 835 | item := items[len(items)-1] |
| 836 | can := udpPacketsCanCoalesce(pkt, uint8(iphLen), gsoSize, item, bufs, offset) |
| 837 | var pktCSumKnownInvalid bool |
| 838 | if can == coalesceAppend { |
| 839 | result := coalesceUDPPackets(pkt, &item, bufs, offset, isV6) |
| 840 | switch result { |
| 841 | case coalesceSuccess: |
| 842 | table.updateAt(item, len(items)-1) |
| 843 | return groResultCoalesced |
no test coverage detected