1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <stddef.h> 6 #include <errno.h> 7 8 #include <rte_memcpy.h> 9 #include <rte_ether.h> 10 11 #include "ip_frag_common.h" 12 13 /* Fragment Offset */ 14 #define RTE_IPV4_HDR_DF_SHIFT 14 15 #define RTE_IPV4_HDR_MF_SHIFT 13 16 #define RTE_IPV4_HDR_FO_SHIFT 3 17 18 #define IPV4_HDR_DF_MASK (1 << RTE_IPV4_HDR_DF_SHIFT) 19 #define IPV4_HDR_MF_MASK (1 << RTE_IPV4_HDR_MF_SHIFT) 20 21 #define IPV4_HDR_FO_ALIGN (1 << RTE_IPV4_HDR_FO_SHIFT) 22 23 static inline void __fill_ipv4hdr_frag(struct rte_ipv4_hdr *dst, 24 const struct rte_ipv4_hdr *src, uint16_t header_len, 25 uint16_t len, uint16_t fofs, uint16_t dofs, uint32_t mf) 26 { 27 rte_memcpy(dst, src, header_len); 28 fofs = (uint16_t)(fofs + (dofs >> RTE_IPV4_HDR_FO_SHIFT)); 29 fofs = (uint16_t)(fofs | mf << RTE_IPV4_HDR_MF_SHIFT); 30 dst->fragment_offset = rte_cpu_to_be_16(fofs); 31 dst->total_length = rte_cpu_to_be_16(len); 32 dst->hdr_checksum = 0; 33 } 34 35 static inline void __free_fragments(struct rte_mbuf *mb[], uint32_t num) 36 { 37 uint32_t i; 38 for (i = 0; i != num; i++) 39 rte_pktmbuf_free(mb[i]); 40 } 41 42 /** 43 * IPv4 fragmentation. 44 * 45 * This function implements the fragmentation of IPv4 packets. 46 * 47 * @param pkt_in 48 * The input packet. 49 * @param pkts_out 50 * Array storing the output fragments. 51 * @param mtu_size 52 * Size in bytes of the Maximum Transfer Unit (MTU) for the outgoing IPv4 53 * datagrams. This value includes the size of the IPv4 header. 54 * @param pool_direct 55 * MBUF pool used for allocating direct buffers for the output fragments. 56 * @param pool_indirect 57 * MBUF pool used for allocating indirect buffers for the output fragments. 58 * @return 59 * Upon successful completion - number of output fragments placed 60 * in the pkts_out array. 61 * Otherwise - (-1) * <errno>. 62 */ 63 int32_t 64 rte_ipv4_fragment_packet(struct rte_mbuf *pkt_in, 65 struct rte_mbuf **pkts_out, 66 uint16_t nb_pkts_out, 67 uint16_t mtu_size, 68 struct rte_mempool *pool_direct, 69 struct rte_mempool *pool_indirect) 70 { 71 struct rte_mbuf *in_seg = NULL; 72 struct rte_ipv4_hdr *in_hdr; 73 uint32_t out_pkt_pos, in_seg_data_pos; 74 uint32_t more_in_segs; 75 uint16_t fragment_offset, flag_offset, frag_size, header_len; 76 uint16_t frag_bytes_remaining; 77 78 /* 79 * Formal parameter checking. 80 */ 81 if (unlikely(pkt_in == NULL) || unlikely(pkts_out == NULL) || 82 unlikely(nb_pkts_out == 0) || 83 unlikely(pool_direct == NULL) || unlikely(pool_indirect == NULL) || 84 unlikely(mtu_size < RTE_ETHER_MIN_MTU)) 85 return -EINVAL; 86 87 in_hdr = rte_pktmbuf_mtod(pkt_in, struct rte_ipv4_hdr *); 88 header_len = (in_hdr->version_ihl & RTE_IPV4_HDR_IHL_MASK) * 89 RTE_IPV4_IHL_MULTIPLIER; 90 91 /* Check IP header length */ 92 if (unlikely(pkt_in->data_len < header_len) || 93 unlikely(mtu_size < header_len)) 94 return -EINVAL; 95 96 /* 97 * Ensure the IP payload length of all fragments is aligned to a 98 * multiple of 8 bytes as per RFC791 section 2.3. 99 */ 100 frag_size = RTE_ALIGN_FLOOR((mtu_size - header_len), 101 IPV4_HDR_FO_ALIGN); 102 103 flag_offset = rte_cpu_to_be_16(in_hdr->fragment_offset); 104 105 /* If Don't Fragment flag is set */ 106 if (unlikely ((flag_offset & IPV4_HDR_DF_MASK) != 0)) 107 return -ENOTSUP; 108 109 /* Check that pkts_out is big enough to hold all fragments */ 110 if (unlikely(frag_size * nb_pkts_out < 111 (uint16_t)(pkt_in->pkt_len - header_len))) 112 return -EINVAL; 113 114 in_seg = pkt_in; 115 in_seg_data_pos = header_len; 116 out_pkt_pos = 0; 117 fragment_offset = 0; 118 119 more_in_segs = 1; 120 while (likely(more_in_segs)) { 121 struct rte_mbuf *out_pkt = NULL, *out_seg_prev = NULL; 122 uint32_t more_out_segs; 123 struct rte_ipv4_hdr *out_hdr; 124 125 /* Allocate direct buffer */ 126 out_pkt = rte_pktmbuf_alloc(pool_direct); 127 if (unlikely(out_pkt == NULL)) { 128 __free_fragments(pkts_out, out_pkt_pos); 129 return -ENOMEM; 130 } 131 132 /* Reserve space for the IP header that will be built later */ 133 out_pkt->data_len = header_len; 134 out_pkt->pkt_len = header_len; 135 frag_bytes_remaining = frag_size; 136 137 out_seg_prev = out_pkt; 138 more_out_segs = 1; 139 while (likely(more_out_segs && more_in_segs)) { 140 struct rte_mbuf *out_seg = NULL; 141 uint32_t len; 142 143 /* Allocate indirect buffer */ 144 out_seg = rte_pktmbuf_alloc(pool_indirect); 145 if (unlikely(out_seg == NULL)) { 146 rte_pktmbuf_free(out_pkt); 147 __free_fragments(pkts_out, out_pkt_pos); 148 return -ENOMEM; 149 } 150 out_seg_prev->next = out_seg; 151 out_seg_prev = out_seg; 152 153 /* Prepare indirect buffer */ 154 rte_pktmbuf_attach(out_seg, in_seg); 155 len = frag_bytes_remaining; 156 if (len > (in_seg->data_len - in_seg_data_pos)) { 157 len = in_seg->data_len - in_seg_data_pos; 158 } 159 out_seg->data_off = in_seg->data_off + in_seg_data_pos; 160 out_seg->data_len = (uint16_t)len; 161 out_pkt->pkt_len = (uint16_t)(len + 162 out_pkt->pkt_len); 163 out_pkt->nb_segs += 1; 164 in_seg_data_pos += len; 165 frag_bytes_remaining -= len; 166 167 /* Current output packet (i.e. fragment) done ? */ 168 if (unlikely(frag_bytes_remaining == 0)) 169 more_out_segs = 0; 170 171 /* Current input segment done ? */ 172 if (unlikely(in_seg_data_pos == in_seg->data_len)) { 173 in_seg = in_seg->next; 174 in_seg_data_pos = 0; 175 176 if (unlikely(in_seg == NULL)) 177 more_in_segs = 0; 178 } 179 } 180 181 /* Build the IP header */ 182 183 out_hdr = rte_pktmbuf_mtod(out_pkt, struct rte_ipv4_hdr *); 184 185 __fill_ipv4hdr_frag(out_hdr, in_hdr, header_len, 186 (uint16_t)out_pkt->pkt_len, 187 flag_offset, fragment_offset, more_in_segs); 188 189 fragment_offset = (uint16_t)(fragment_offset + 190 out_pkt->pkt_len - header_len); 191 192 out_pkt->l3_len = header_len; 193 194 /* Write the fragment to the output list */ 195 pkts_out[out_pkt_pos] = out_pkt; 196 out_pkt_pos ++; 197 } 198 199 return out_pkt_pos; 200 } 201