673 lines
20 KiB
C
673 lines
20 KiB
C
/****************************************************************************
|
|
* net/ipfrag/ipv6_frag.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#if defined(CONFIG_NET_IPv6) && defined (CONFIG_NET_IPFRAG)
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <debug.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <net/if.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/net/netconfig.h>
|
|
#include <nuttx/net/netdev.h>
|
|
#include <nuttx/net/netstats.h>
|
|
#include <nuttx/net/ip.h>
|
|
#include <nuttx/net/ipv6ext.h>
|
|
|
|
#include "netdev/netdev.h"
|
|
#include "inet/inet.h"
|
|
#include "ipfrag.h"
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* The increasing number used for the IP ID field of IPv6 Fragment Header. */
|
|
|
|
static uint32_t g_ipv6id;
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int32_t ipv6_fragin_getinfo(FAR struct iob_s *iob,
|
|
FAR struct ip_fraglink_s *fraglink);
|
|
static uint32_t ipv6_fragin_reassemble(FAR struct ip_fragsnode_s *node);
|
|
static inline void
|
|
ipv6_fragout_buildipv6header(FAR struct ipv6_hdr_s *ref,
|
|
FAR struct ipv6_hdr_s *ipv6,
|
|
uint16_t hdrlen, uint16_t datalen,
|
|
uint16_t nxthdroff, uint16_t nxtprot);
|
|
static inline void
|
|
ipv6_fragout_buildipv6fragheader(FAR struct ipv6_fragment_extension_s *frag,
|
|
uint8_t nxthdr, uint16_t ipoff,
|
|
uint32_t ipid);
|
|
static uint16_t ipv6_fragout_getunfraginfo(FAR struct iob_s *iob,
|
|
uint16_t *hdroff,
|
|
uint16_t *hdrtype);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragin_getinfo
|
|
*
|
|
* Description:
|
|
* Polulate fragment information from the input ipv6 packet data.
|
|
*
|
|
* Input Parameters:
|
|
* iob - An IPv6 fragment
|
|
* fraglink - node of the lower-level linked list, it maintains information
|
|
* of one fragment
|
|
*
|
|
* Returned Value:
|
|
* OK - Got fragment information.
|
|
* EINVAL - The input ipv6 packet is not a fragment.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int32_t ipv6_fragin_getinfo(FAR struct iob_s *iob,
|
|
FAR struct ip_fraglink_s *fraglink)
|
|
{
|
|
FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *)
|
|
(iob->io_data + iob->io_offset);
|
|
FAR struct ipv6_extension_s *exthdr;
|
|
FAR uint8_t *payload;
|
|
uint16_t paylen;
|
|
uint8_t nxthdr;
|
|
|
|
paylen = ((uint16_t)ipv6->len[0] << 8) + (uint16_t)ipv6->len[1];
|
|
payload = (FAR uint8_t *)(ipv6 + 1);
|
|
exthdr = (FAR struct ipv6_extension_s *)payload;
|
|
nxthdr = ipv6->proto;
|
|
|
|
while (nxthdr != NEXT_FRAGMENT_EH && ipv6_exthdr(nxthdr))
|
|
{
|
|
uint16_t extlen;
|
|
|
|
exthdr = (FAR struct ipv6_extension_s *)payload;
|
|
extlen = EXTHDR_LEN((unsigned int)exthdr->len);
|
|
payload += extlen;
|
|
paylen -= extlen;
|
|
nxthdr = exthdr->nxthdr;
|
|
};
|
|
|
|
if (nxthdr == NEXT_FRAGMENT_EH)
|
|
{
|
|
FAR struct ipv6_fragment_extension_s *fraghdr;
|
|
|
|
fraghdr = (FAR struct ipv6_fragment_extension_s *)exthdr;
|
|
|
|
/* Cut the size of fragment header, notice fragment header don't has a
|
|
* length filed.
|
|
*/
|
|
|
|
paylen -= EXTHDR_FRAG_LEN;
|
|
|
|
fraglink->flink = NULL;
|
|
fraglink->fragsnode = NULL;
|
|
|
|
fraglink->isipv4 = FALSE;
|
|
fraglink->fragoff = (fraghdr->msoffset << 8) + fraghdr->lsoffset;
|
|
fraglink->morefrags = fraglink->fragoff & 0x1;
|
|
fraglink->fragoff &= 0xfff8;
|
|
fraglink->fraglen = paylen;
|
|
fraglink->ipid = NTOHL(
|
|
((uint32_t)(*(FAR uint16_t *)(&fraghdr->id[0])) << 16) +
|
|
(uint32_t)(*(FAR uint16_t *)(&fraghdr->id[2])));
|
|
|
|
fraglink->frag = iob;
|
|
|
|
return OK;
|
|
}
|
|
else
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragin_reassemble
|
|
*
|
|
* Description:
|
|
* Reassemble all ipv6 fragments to build an IP frame.
|
|
*
|
|
* Input Parameters:
|
|
* node - node of the upper-level linked list, it maintains
|
|
* information about all fragments belonging to an IP datagram
|
|
*
|
|
* Returned Value:
|
|
* The length of the reassembled IP frame
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint32_t ipv6_fragin_reassemble(FAR struct ip_fragsnode_s *node)
|
|
{
|
|
FAR struct iob_s *head = NULL;
|
|
FAR struct ipv6_hdr_s *ipv6;
|
|
FAR struct ip_fraglink_s *fraglink;
|
|
|
|
/* Loop to walk through the fragment list and reassemble those fragments,
|
|
* the fraglink list was ordered by fragment offset value
|
|
*/
|
|
|
|
fraglink = node->frags;
|
|
node->frags = NULL;
|
|
|
|
while (fraglink != NULL)
|
|
{
|
|
FAR uint8_t *payload;
|
|
uint8_t nxthdr;
|
|
FAR struct iob_s *iob;
|
|
FAR struct ip_fraglink_s *linknext;
|
|
FAR struct ipv6_extension_s *exthdr;
|
|
FAR struct ipv6_fragment_extension_s *fraghdr;
|
|
|
|
iob = fraglink->frag;
|
|
ipv6 = (FAR struct ipv6_hdr_s *)(iob->io_data + iob->io_offset);
|
|
payload = (FAR uint8_t *)(ipv6 + 1);
|
|
exthdr = (FAR struct ipv6_extension_s *)payload;
|
|
nxthdr = ipv6->proto;
|
|
|
|
/* Find fragment header and the front header which is close to the
|
|
* fragment header
|
|
*/
|
|
|
|
while (nxthdr != NEXT_FRAGMENT_EH && ipv6_exthdr(nxthdr))
|
|
{
|
|
uint16_t extlen;
|
|
|
|
exthdr = (FAR struct ipv6_extension_s *)payload;
|
|
extlen = EXTHDR_LEN((unsigned int)exthdr->len);
|
|
payload += extlen;
|
|
nxthdr = exthdr->nxthdr;
|
|
};
|
|
|
|
fraghdr = (FAR struct ipv6_fragment_extension_s *)payload;
|
|
|
|
/* Skip fragment header, notice fragment header don't has a length
|
|
* filed
|
|
*/
|
|
|
|
payload += EXTHDR_FRAG_LEN;
|
|
|
|
if (fraglink->fragoff == 0)
|
|
{
|
|
/* This is the zero fragment, Set the front header's next header
|
|
* filed to the next header value of the fragment header
|
|
*/
|
|
|
|
if (ipv6->proto == NEXT_FRAGMENT_EH)
|
|
{
|
|
ipv6->proto = fraghdr->nxthdr;
|
|
}
|
|
else
|
|
{
|
|
exthdr->nxthdr = fraghdr->nxthdr;
|
|
}
|
|
|
|
/* Remove fragment header and fix up the data length */
|
|
|
|
memmove(fraghdr, payload,
|
|
iob->io_len - (payload - (iob->io_data + iob->io_offset)));
|
|
iob->io_len -= EXTHDR_FRAG_LEN;
|
|
iob->io_pktlen -= EXTHDR_FRAG_LEN;
|
|
|
|
/* Remember the head iob */
|
|
|
|
head = iob;
|
|
}
|
|
else
|
|
{
|
|
uint16_t new_off;
|
|
|
|
/* Fix up the value of offset and length for this none zero
|
|
* fragment
|
|
*/
|
|
|
|
new_off = payload - iob->io_data;
|
|
iob->io_len -= new_off - iob->io_offset;
|
|
iob->io_pktlen -= new_off - iob->io_offset;
|
|
iob->io_offset = new_off;
|
|
|
|
/* Concatenate this iob to the reassembly chain */
|
|
|
|
iob_concat(head, iob);
|
|
}
|
|
|
|
linknext = fraglink->flink;
|
|
kmm_free(fraglink);
|
|
fraglink = linknext;
|
|
}
|
|
|
|
/* Remember the reassembled outgoing IP frame */
|
|
|
|
node->outgoframe = head;
|
|
|
|
/* Adjust the length value in the IP Header */
|
|
|
|
ipv6 = (FAR struct ipv6_hdr_s *)(head->io_data + head->io_offset);
|
|
ipv6->len[0] = (head->io_pktlen - IPv6_HDRLEN) >> 8;
|
|
ipv6->len[1] = (head->io_pktlen - IPv6_HDRLEN) & 0xff;
|
|
|
|
return head->io_pktlen;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragout_buildipv6header
|
|
*
|
|
* Description:
|
|
* Build IPv6 header for an IPv6 fragment.
|
|
*
|
|
* Input Parameters:
|
|
* ref - The reference IPv6 Header
|
|
* ipv6 - The pointer of the newly generated IPv6 Header
|
|
* hdrlen - Including the length of IPv6 basic header and all
|
|
* extention headers
|
|
* datalen - The data length follows the IPv6 basic header
|
|
* nxthdroff - The offset of 'next header' to be updated
|
|
* nxtprot - The value of 'next header' to be updated
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void
|
|
ipv6_fragout_buildipv6header(FAR struct ipv6_hdr_s *ref,
|
|
FAR struct ipv6_hdr_s *ipv6,
|
|
uint16_t hdrlen, uint16_t datalen,
|
|
uint16_t nxthdroff, uint16_t nxtprot)
|
|
{
|
|
if (ref != ipv6)
|
|
{
|
|
/* Copy unfragmentable header data from reference header */
|
|
|
|
memcpy(ipv6, ref, hdrlen);
|
|
}
|
|
|
|
/* Update length filed */
|
|
|
|
ipv6->len[0] = datalen >> 8;
|
|
ipv6->len[1] = datalen & 0xff;
|
|
|
|
/* If extension headers exist, update the Next Header field in the
|
|
* last extension header of the unfragmentable part; Otherwise update
|
|
* the Next Header field of the basic IPv6 header.
|
|
*/
|
|
|
|
*((uint8_t *)ipv6 + nxthdroff) = nxtprot;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragout_buildipv6fragheader
|
|
*
|
|
* Description:
|
|
* Build IPv6 fragment extension header for an IPv6 fragment.
|
|
*
|
|
* Input Parameters:
|
|
* frag - The pointer of the newly generated IPv6 fragment Header
|
|
* nxthdr - The first header type in the fragmentable part
|
|
* ipoff - Fragment offset
|
|
* ipid - The value of IPv6 IP ID
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void
|
|
ipv6_fragout_buildipv6fragheader(FAR struct ipv6_fragment_extension_s *frag,
|
|
uint8_t nxthdr, uint16_t ipoff,
|
|
uint32_t ipid)
|
|
{
|
|
frag->nxthdr = nxthdr;
|
|
frag->reserved = 0;
|
|
frag->msoffset = ipoff >> 8;
|
|
frag->lsoffset = ipoff & 0xff;
|
|
*(FAR uint16_t *)&frag->id[0] = HTONL(ipid) & 0xffff;
|
|
*(FAR uint16_t *)&frag->id[2] = HTONL(ipid) >> 16;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragout_getunfraginfo
|
|
*
|
|
* Description:
|
|
* Get the length of Unfragmentable Part of the original ipv6 packet,
|
|
* remember the offset and value of nextheader in the last extension
|
|
* header of the unfragmentable part.
|
|
* Refer to rfc2460, section-4.1, section-4.5
|
|
*
|
|
* Input Parameters:
|
|
* iob - Outgoing data waiting for fragment
|
|
* hdroff - The offset of the last next header position in the
|
|
* unfragmentable part
|
|
* hdrtype - The first header type in the fragmentable part
|
|
*
|
|
* Returned Value:
|
|
* Unfragmentable Part length
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint16_t ipv6_fragout_getunfraginfo(FAR struct iob_s *iob,
|
|
uint16_t *hdroff,
|
|
uint16_t *hdrtype)
|
|
{
|
|
uint32_t iter = 0;
|
|
bool destopt = false;
|
|
uint16_t delta = sizeof(struct ipv6_hdr_s);
|
|
uint16_t unfraglen = delta;
|
|
uint8_t nxthdr;
|
|
FAR struct ipv6_hdr_s *ipv6;
|
|
FAR struct ipv6_extension_s *exthdr;
|
|
FAR uint8_t *payload;
|
|
|
|
ipv6 = (FAR struct ipv6_hdr_s *)(iob->io_data + iob->io_offset);
|
|
payload = (FAR uint8_t *)(ipv6 + 1);
|
|
exthdr = (FAR struct ipv6_extension_s *)payload;
|
|
nxthdr = ipv6->proto;
|
|
|
|
*hdroff = offsetof(struct ipv6_hdr_s, proto);
|
|
*hdrtype = ipv6->proto;
|
|
|
|
/* Traverse up to three extension headers, if the Destination Options
|
|
* Header appears repeatedly, ingore the secondary one and end the search.
|
|
* refer to rfc2460, section-4.1
|
|
*/
|
|
|
|
while (ipv6_exthdr(nxthdr) && iter++ < 3)
|
|
{
|
|
uint16_t extlen;
|
|
|
|
exthdr = (FAR struct ipv6_extension_s *)payload;
|
|
extlen = EXTHDR_LEN((unsigned int)exthdr->len);
|
|
|
|
switch (nxthdr)
|
|
{
|
|
case NEXT_DESTOPT_EH:
|
|
if (!destopt)
|
|
{
|
|
destopt = true;
|
|
}
|
|
else
|
|
{
|
|
/* This is the secondary Destination Options Header,
|
|
* end the search
|
|
*/
|
|
|
|
goto done;
|
|
}
|
|
|
|
case NEXT_ROUTING_EH:
|
|
case NEXT_HOPBYBOT_EH:
|
|
unfraglen = delta + extlen;
|
|
*hdroff = delta;
|
|
*hdrtype = exthdr->nxthdr;
|
|
}
|
|
|
|
payload += extlen;
|
|
delta += extlen;
|
|
nxthdr = exthdr->nxthdr;
|
|
}
|
|
|
|
done:
|
|
return unfraglen;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragin
|
|
*
|
|
* Description:
|
|
* Handling incoming IPv6 fragment input, the input data
|
|
* (dev->d_iob) can be an I/O buffer chain
|
|
*
|
|
* Input Parameters:
|
|
* dev - The NIC device that the fragmented data comes from
|
|
*
|
|
* Returned Value:
|
|
* ENOMEM - No memory
|
|
* OK - The input fragment is processed as expected
|
|
*
|
|
****************************************************************************/
|
|
|
|
int32_t ipv6_fragin(FAR struct net_driver_s *dev)
|
|
{
|
|
FAR struct ip_fragsnode_s *node = NULL;
|
|
FAR struct ip_fraglink_s *fraginfo = NULL;
|
|
bool restartwdog;
|
|
|
|
if (dev->d_len != dev->d_iob->io_pktlen)
|
|
{
|
|
nerr("ERROR: Parameters error.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
fraginfo = kmm_malloc(sizeof(struct ip_fraglink_s));
|
|
if (fraginfo == NULL)
|
|
{
|
|
nerr("ERROR: Failed to allocate buffer.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Polulate fragment information from input packet data */
|
|
|
|
ipv6_fragin_getinfo(dev->d_iob, fraginfo);
|
|
|
|
nxmutex_lock(&g_ipfrag_lock);
|
|
|
|
/* Need to restart reassembly worker if the original linked list is empty */
|
|
|
|
restartwdog = ip_fragin_enqueue(dev, fraginfo);
|
|
|
|
node = fraginfo->fragsnode;
|
|
if (node->verifyflag & IP_FRAGVERIFY_RECVDALLFRAGS)
|
|
{
|
|
/* Well, all fragments of an IP frame have been received, remove
|
|
* node from link list first, then reassemble and dispatch to the
|
|
* stack.
|
|
*/
|
|
|
|
ip_frag_remnode(node);
|
|
|
|
/* All fragments belonging to one IP frame have been separated
|
|
* from the fragment processing module, unlocks mutex as soon
|
|
* as possible
|
|
*/
|
|
|
|
nxmutex_unlock(&g_ipfrag_lock);
|
|
|
|
/* Reassemble fragments to one IP frame and set the resulting
|
|
* IP frame to dev->d_iob
|
|
*/
|
|
|
|
ipv6_fragin_reassemble(node);
|
|
netdev_iob_replace(dev, node->outgoframe);
|
|
|
|
/* Free the memory of node */
|
|
|
|
kmm_free(node);
|
|
|
|
return ipv6_input(dev);
|
|
}
|
|
|
|
nxmutex_unlock(&g_ipfrag_lock);
|
|
|
|
if (restartwdog)
|
|
{
|
|
/* Restart the work queue for fragment processing */
|
|
|
|
ip_frag_startwdog();
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ipv6_fragout
|
|
*
|
|
* Description:
|
|
* Execute the ipv6 fragment function. After this work is done, all
|
|
* fragments are maintained by dev->d_fragout. In order to reduce the
|
|
* cyclomatic complexity and facilitate maintenance, fragmentation is
|
|
* performed in two steps:
|
|
* 1. Reconstruct I/O Buffer according to MTU, which will reserve
|
|
* the space for the L3 header;
|
|
* 2. Fill the L3 header into the reserved space.
|
|
*
|
|
* Input Parameters:
|
|
* dev - The NIC device
|
|
* mtu - The MTU of current NIC
|
|
*
|
|
* Returned Value:
|
|
* 0 if success or a negative value if fail.
|
|
*
|
|
* Assumptions:
|
|
* Data length(dev->d_iob->io_pktlen) is grater than the MTU of
|
|
* current NIC
|
|
*
|
|
****************************************************************************/
|
|
|
|
int32_t ipv6_fragout(FAR struct net_driver_s *dev, uint16_t mtu)
|
|
{
|
|
uint16_t unfraglen;
|
|
uint16_t offset = 0;
|
|
uint32_t ipid;
|
|
uint32_t iter;
|
|
uint32_t nfrags;
|
|
uint16_t hdroff;
|
|
uint16_t hdrtype;
|
|
FAR struct iob_s *frag;
|
|
FAR struct ipv6_hdr_s *ref = NULL;
|
|
FAR struct ipv6_fragment_extension_s *fraghdr;
|
|
struct iob_queue_s fragq =
|
|
{
|
|
NULL, NULL
|
|
};
|
|
|
|
/* Get the length of Unfragmentable Part of the original ipv6 packet,
|
|
* Get the offset and value of nextheader filed in the last extension
|
|
* header of the unfragmentable part.
|
|
*/
|
|
|
|
unfraglen = ipv6_fragout_getunfraginfo(dev->d_iob, &hdroff, &hdrtype);
|
|
|
|
/* Reconstruct I/O Buffer according to MTU, which will reserve
|
|
* the space for the L3 header
|
|
*/
|
|
|
|
nfrags = ip_fragout_slice(dev->d_iob, PF_INET6, mtu, unfraglen, &fragq);
|
|
ASSERT(nfrags > 1);
|
|
netdev_iob_clear(dev);
|
|
|
|
ipid = ++g_ipv6id;
|
|
|
|
/* Fill the L3 header into the reserved space */
|
|
|
|
for (iter = 0; iter < nfrags; iter++)
|
|
{
|
|
frag = iob_remove_queue(&fragq);
|
|
|
|
if (iter == 0)
|
|
{
|
|
ref = (FAR struct ipv6_hdr_s *)(frag->io_data + frag->io_offset);
|
|
|
|
/* Update the IPv6 header for the zero fragment */
|
|
|
|
ipv6_fragout_buildipv6header(ref, ref, unfraglen,
|
|
frag->io_pktlen - IPv6_HDRLEN, hdroff, NEXT_FRAGMENT_EH);
|
|
|
|
/* Build the fragment header for the zero fragment */
|
|
|
|
fraghdr = (FAR struct ipv6_fragment_extension_s *)
|
|
(frag->io_data + frag->io_offset + unfraglen);
|
|
ipv6_fragout_buildipv6fragheader(fraghdr, hdrtype,
|
|
FRAGHDR_FRAG_MOREFRAGS, ipid);
|
|
}
|
|
else
|
|
{
|
|
uint16_t ipoff = offset - iter * (unfraglen + EXTHDR_FRAG_LEN);
|
|
|
|
if (iter < nfrags - 1)
|
|
{
|
|
ipoff |= FRAGHDR_FRAG_MOREFRAGS;
|
|
}
|
|
|
|
/* Refer to the zero fragment ipv6 header to construct the ipv6
|
|
* header of non-zero fragment
|
|
*/
|
|
|
|
ipv6_fragout_buildipv6header(ref,
|
|
(FAR struct ipv6_hdr_s *)(frag->io_data + frag->io_offset),
|
|
unfraglen, frag->io_pktlen - IPv6_HDRLEN, hdroff,
|
|
NEXT_FRAGMENT_EH);
|
|
|
|
/* Build extension fragment header for non-zero fragment */
|
|
|
|
fraghdr = (FAR struct ipv6_fragment_extension_s *)
|
|
(frag->io_data + frag->io_offset + unfraglen);
|
|
ipv6_fragout_buildipv6fragheader(fraghdr, hdrtype, ipoff, ipid);
|
|
}
|
|
|
|
/* Enqueue this fragment to dev->d_fragout */
|
|
|
|
if (iob_tryadd_queue(frag, &dev->d_fragout) < 0)
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
offset += frag->io_pktlen;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_STATISTICS
|
|
g_netstats.ipv6.sent += nfrags - 1;
|
|
#endif
|
|
|
|
netdev_txnotify_dev(dev);
|
|
|
|
return OK;
|
|
|
|
fail:
|
|
netdev_iob_release(dev);
|
|
iob_free_chain(frag);
|
|
iob_free_queue(&fragq);
|
|
iob_free_queue(&dev->d_fragout);
|
|
--g_ipv6id;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 && CONFIG_NET_IPFRAG */
|