Support fragmentation and reassembly

Signed-off-by: luojun1 <luojun1@xiaomi.com>
This commit is contained in:
luojun1234
2023-01-09 20:47:11 +08:00
committed by Xiang Xiao
parent b1899ffbfd
commit ff3733b5b5
25 changed files with 3152 additions and 43 deletions
+5
View File
@@ -114,6 +114,11 @@
#define ICMP_PREC_CUTOFF 15 /* Precedence cut off */
#define NR_ICMP_UNREACH 15 /* instead of hardcoding immediate value */
/* Codes for TIME_EXCEEDED. */
#define ICMP_EXC_TTL 0 /* TTL count exceeded */
#define ICMP_EXC_FRAGTIME 1 /* Fragment Reassembly time exceeded */
/****************************************************************************
* Public Type Definitions
****************************************************************************/
+5
View File
@@ -141,6 +141,11 @@
#define ICMPv6_POLICY_FAIL 5
#define ICMPv6_REJECT_ROUTE 6
/* Codes for Time Exceeded */
#define ICMPV6_EXC_HOPLIMIT 0
#define ICMPV6_EXC_FRAGTIME 1
/****************************************************************************
* Public Type Definitions
****************************************************************************/
+2
View File
@@ -217,6 +217,8 @@ struct ipv6_stats_s
net_stats_t sent; /* Number of sent packets at the IP layer */
net_stats_t vhlerr; /* Number of packets dropped due to wrong
* IP version or header length */
net_stats_t fragerr; /* Number of packets dropped since they
* were IP fragments */
net_stats_t protoerr; /* Number of packets dropped since they
* were neither ICMP, UDP nor TCP */
};
+28
View File
@@ -124,6 +124,14 @@
#define EXTHDR_LEN(hdrlen) ((hdrlen + 1) << 3)
/* Fragment header has no length field and has a fixed size */
#define EXTHDR_FRAG_LEN 8
/* More frags flag bits in 16-bit flags in fragment header */
#define FRAGHDR_FRAG_MOREFRAGS 0x0001
/* Values of the Two High-Order Bits in the Hop-to-hop Option Type Field */
#define HOPBYHOP_TYPE_MASK 0xc0
@@ -217,4 +225,24 @@ struct ipv6_router_alert_s
uint8_t pad[4]; /* Pad to a multiple of 8 bytes */
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: ipv6_exthdr
*
* Description:
* Check whether it is an IPv6 extension header.
*
* Input Parameters:
* The next header value extracted from an IPv6 frame.
*
* Returned Value:
* Return true if the next header value is an IPv6 extension header.
*
****************************************************************************/
bool ipv6_exthdr(uint8_t nxthdr);
#endif /* __INCLUDE_NUTTX_NET_IPV6EXT_H */
+6
View File
@@ -320,6 +320,12 @@ struct net_driver_s
FAR struct iob_s *d_iob;
/* Remember the outgoing fragments waiting to be sent */
#ifdef CONFIG_NET_IPFRAG
FAR struct iob_queue_s d_fragout;
#endif
/* The d_buf array is used to hold incoming and outgoing packets. The
* device driver should place incoming data into this buffer. When sending
* data, the device driver should read the link level headers and the
+1
View File
@@ -331,6 +331,7 @@ source "net/sixlowpan/Kconfig"
source "net/ipforward/Kconfig"
source "net/nat/Kconfig"
source "net/netfilter/Kconfig"
source "net/ipfrag/Kconfig"
endmenu # Internet Protocol Selection
+1
View File
@@ -28,6 +28,7 @@ NET_CSRCS = net_initialize.c
include socket/Make.defs
include inet/Make.defs
include ipfrag/Make.defs
include netdev/Make.defs
include arp/Make.defs
include icmp/Make.defs
+1
View File
@@ -11,6 +11,7 @@ Directory Structure
+- arp - Address resolution protocol (IPv4)
+- bluetooth - PF_BLUETOOTH socket interface
+- devif - Stack/device interface layer
+- ipfrag - Fragmentation and reassembly
+- icmp - Internet Control Message Protocol (IPv4)
+- icmpv6 - Internet Control Message Protocol (IPv6)
+- ieee802154 - PF_IEEE802154 socket interface
+15
View File
@@ -511,6 +511,21 @@ void devif_out(FAR struct net_driver_s *dev);
int devif_poll_out(FAR struct net_driver_s *dev,
devif_poll_callback_t callback);
/****************************************************************************
* Name: devif_is_loopback
*
* Description:
* The function checks the destination address of the packet to see
* whether the target of packet is ourself.
*
* Returned Value:
* true is returned if the packet need loop back to ourself, otherwise
* false is returned.
*
****************************************************************************/
bool devif_is_loopback(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: devif_loopback
*
+10
View File
@@ -56,19 +56,29 @@ void devif_iob_send(FAR struct net_driver_s *dev, FAR struct iob_s *iob,
unsigned int len, unsigned int offset,
unsigned int target_offset)
{
#ifndef CONFIG_NET_IPFRAG
unsigned int limit = NETDEV_PKTSIZE(dev) -
NET_LL_HDRLEN(dev) - target_offset;
#endif
int ret;
#ifndef CONFIG_NET_IPFRAG
if (dev == NULL || len == 0 || len > limit)
#else
if (dev == NULL || len == 0)
#endif
{
if (dev->d_iob == NULL)
{
iob_free_chain(iob);
}
#ifndef CONFIG_NET_IPFRAG
nerr("devif_iob_send error, %p, send len: %u, limit len: %u\n",
dev, len, limit);
#else
nerr("devif_iob_send error, %p, send len: %u\n", dev, len);
#endif
return;
}
+16 -3
View File
@@ -32,10 +32,23 @@
#include <nuttx/net/netdev.h>
/****************************************************************************
* Private Functions
* Public Functions
****************************************************************************/
static bool is_loopback(FAR struct net_driver_s *dev)
/****************************************************************************
* Name: devif_is_loopback
*
* Description:
* The function checks the destination address of the packet to see
* whether the target of packet is ourself.
*
* Returned Value:
* true is returned if the packet need loop back to ourself, otherwise
* false is returned.
*
****************************************************************************/
bool devif_is_loopback(FAR struct net_driver_s *dev)
{
if (dev->d_len > 0)
{
@@ -76,7 +89,7 @@ static bool is_loopback(FAR struct net_driver_s *dev)
int devif_loopback(FAR struct net_driver_s *dev)
{
if (!is_loopback(dev))
if (!devif_is_loopback(dev))
{
return 0;
}
+99 -3
View File
@@ -33,6 +33,7 @@
#include <nuttx/net/net.h>
#include "devif/devif.h"
#include "netdev/netdev.h"
#include "arp/arp.h"
#include "can/can.h"
#include "tcp/tcp.h"
@@ -46,6 +47,7 @@
#include "mld/mld.h"
#include "ipforward/ipforward.h"
#include "sixlowpan/sixlowpan.h"
#include "ipfrag/ipfrag.h"
/****************************************************************************
* Private Types
@@ -616,6 +618,79 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev,
# define devif_poll_tcp_connections(dev, callback) (0)
#endif
/****************************************************************************
* Name: devif_poll_ipfrag
*
* Description:
* Poll all ip fragments for available packets to send.
*
* Input Parameters:
* dev - NIC Device instance.
* callback - the actual sending API provided by each NIC driver.
*
* Returned Value:
* Zero indicated the polling will continue, else stop the polling.
*
* Assumptions:
* This function is called from the MAC device driver with the network
* locked.
*
****************************************************************************/
#ifdef CONFIG_NET_IPFRAG
static int devif_poll_ipfrag(FAR struct net_driver_s *dev,
devif_poll_callback_t callback)
{
FAR struct iob_s *frag;
bool reused = false;
int bstop = false;
while (!bstop)
{
/* Dequeue outgoing fragment from dev->d_fragout */
frag = iob_remove_queue(&dev->d_fragout);
if (frag == NULL)
{
break;
}
/* Frag buffer could be reused for other protocols */
reused = true;
/* Replace original iob */
netdev_iob_replace(dev, frag);
/* build L2 headers */
devif_out(dev);
/* Call back into the driver */
bstop = callback(dev);
}
/* Notify the device driver that ip fragments is available. */
if (iob_peek_queue(&dev->d_fragout) != NULL)
{
netdev_txnotify_dev(dev);
}
/* Reuse iob buffer */
if (!bstop && reused)
{
iob_update_pktlen(dev->d_iob, 0);
netdev_iob_prepare(dev, true, 0);
}
return bstop;
}
#endif
/****************************************************************************
* Name: devif_poll_connections
*
@@ -654,10 +729,19 @@ static int devif_poll_connections(FAR struct net_driver_s *dev,
* action.
*/
#ifdef CONFIG_NET_ARP_SEND
/* Check for pending ARP requests */
#ifdef CONFIG_NET_IPFRAG
/* Traverse all of ip fragments for available packets to transfer */
bstop = devif_poll_ipfrag(dev, callback);
if (!bstop)
#endif
#ifdef CONFIG_NET_ARP_SEND
{
/* Check for pending ARP requests */
bstop = arp_poll(dev, callback);
}
bstop = arp_poll(dev, callback);
if (!bstop)
#endif
#ifdef CONFIG_NET_PKT
@@ -1048,6 +1132,18 @@ int devif_poll_out(FAR struct net_driver_s *dev,
if (callback)
{
#ifdef CONFIG_NET_IPFRAG
if (ip_fragout(dev) != OK)
{
netdev_iob_release(dev);
return 1;
}
else if (iob_peek_queue(&dev->d_fragout) != NULL)
{
return devif_poll_ipfrag(dev, callback);
}
#endif
return callback(dev);
}
+13
View File
@@ -105,6 +105,7 @@
#include "ipforward/ipforward.h"
#include "devif/devif.h"
#include "nat/nat.h"
#include "ipfrag/ipfrag.h"
#include "utils/utils.h"
/****************************************************************************
@@ -219,6 +220,13 @@ static int ipv4_in(FAR struct net_driver_s *dev)
if ((ipv4->ipoffset[0] & 0x3f) != 0 || ipv4->ipoffset[1] != 0)
{
#ifdef CONFIG_NET_IPFRAG
if (ipv4_fragin(dev) == OK)
{
return OK;
}
#endif
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.drop++;
g_netstats.ipv4.fragerr++;
@@ -434,6 +442,11 @@ static int ipv4_in(FAR struct net_driver_s *dev)
(defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK))
done:
#endif
#ifdef CONFIG_NET_IPFRAG
ip_fragout(dev);
#endif
devif_out(dev);
/* Return and let the caller do any pending transmission. */
+75 -32
View File
@@ -51,6 +51,7 @@
#include "ipforward/ipforward.h"
#include "inet/inet.h"
#include "devif/devif.h"
#include "ipfrag/ipfrag.h"
/****************************************************************************
* Pre-processor Definitions
@@ -62,37 +63,6 @@
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ipv6_exthdr
*
* Description:
* Return true if the next header value is an IPv6 extension header.
*
****************************************************************************/
static bool ipv6_exthdr(uint8_t nxthdr)
{
switch (nxthdr)
{
case NEXT_HOPBYBOT_EH: /* Hop-by-Hop Options Header */
case NEXT_ENCAP_EH: /* Encapsulated IPv6 Header */
case NEXT_ROUTING_EH: /* Routing Header */
case NEXT_FRAGMENT_EH: /* Fragment Header */
case NEXT_RRSVP_EH: /* Resource ReSerVation Protocol */
case NEXT_ENCAPSEC_EH: /* Encapsulating Security Payload */
case NEXT_AUTH_EH: /* Authentication Header */
case NEXT_DESTOPT_EH: /* Destination Options Header */
case NEXT_MOBILITY_EH: /* Mobility */
case NEXT_HOSTID_EH: /* Host Identity Protocol */
case NEXT_SHIM6_EH: /* Shim6 Protocol */
return true;
case NEXT_NOHEADER: /* No next header */
default:
return false;
}
}
/****************************************************************************
* Name: check_dev_destipaddr
*
@@ -228,6 +198,9 @@ static int ipv6_in(FAR struct net_driver_s *dev)
#ifdef CONFIG_NET_IPFORWARD
int ret;
#endif
#ifdef CONFIG_NET_IPFRAG
bool isfrag = false;
#endif
/* This is where the input processing starts. */
@@ -312,7 +285,18 @@ static int ipv6_in(FAR struct net_driver_s *dev)
/* Just skip over the extension header */
exthdr = (FAR struct ipv6_extension_s *)payload;
extlen = EXTHDR_LEN((unsigned int)exthdr->len);
if (nxthdr == NEXT_FRAGMENT_EH)
{
extlen = EXTHDR_FRAG_LEN;
#ifdef CONFIG_NET_IPFRAG
isfrag = true;
#endif
}
else
{
extlen = EXTHDR_LEN((unsigned int)exthdr->len);
}
payload += extlen;
iphdrlen += extlen;
nxthdr = exthdr->nxthdr;
@@ -418,6 +402,23 @@ static int ipv6_in(FAR struct net_driver_s *dev)
}
#endif
#ifdef CONFIG_NET_IPFRAG
if (isfrag)
{
if (ipv6_fragin(dev) == OK)
{
return OK;
}
else
{
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv6.fragerr++;
#endif
goto drop;
}
}
#endif
/* Now process the incoming packet according to the protocol specified in
* the next header IPv6 field.
*/
@@ -519,6 +520,11 @@ static int ipv6_in(FAR struct net_driver_s *dev)
#ifdef CONFIG_NET_IPFORWARD
done:
#endif
#ifdef CONFIG_NET_IPFRAG
ip_fragout(dev);
#endif
devif_out(dev);
/* Return and let the caller do any pending transmission. */
@@ -541,6 +547,43 @@ drop:
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipv6_exthdr
*
* Description:
* Check whether it is an IPv6 extension header.
*
* Input Parameters:
* The next header value extracted from an IPv6 frame.
*
* Returned Value:
* Return true if the next header value is an IPv6 extension header.
*
****************************************************************************/
bool ipv6_exthdr(uint8_t nxthdr)
{
switch (nxthdr)
{
case NEXT_HOPBYBOT_EH: /* Hop-by-Hop Options Header */
case NEXT_ENCAP_EH: /* Encapsulated IPv6 Header */
case NEXT_ROUTING_EH: /* Routing Header */
case NEXT_FRAGMENT_EH: /* Fragment Header */
case NEXT_RRSVP_EH: /* Resource ReSerVation Protocol */
case NEXT_ENCAPSEC_EH: /* Encapsulating Security Payload */
case NEXT_AUTH_EH: /* Authentication Header */
case NEXT_DESTOPT_EH: /* Destination Options Header */
case NEXT_MOBILITY_EH: /* Mobility */
case NEXT_HOSTID_EH: /* Host Identity Protocol */
case NEXT_SHIM6_EH: /* Shim6 Protocol */
return true;
case NEXT_NOHEADER: /* No next header */
default:
return false;
}
}
/****************************************************************************
* Name: ipv6_input
*
+2
View File
@@ -321,6 +321,7 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
goto errout;
}
#ifndef CONFIG_NET_IPFRAG
/* Sanity check if the request len is greater than the net payload len */
if (len > NETDEV_PKTSIZE(dev) - (NET_LL_HDRLEN(dev) + IPv4_HDRLEN))
@@ -328,6 +329,7 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
nerr("ERROR: Invalid packet length\n");
return -EINVAL;
}
#endif
/* If we are no longer processing the same ping ID, then flush any pending
* packets from the read-ahead buffer.
+2
View File
@@ -308,6 +308,7 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
goto errout;
}
#ifndef CONFIG_NET_IPFRAG
/* Sanity check if the request len is greater than the net payload len */
if (len > NETDEV_PKTSIZE(dev) - (NET_LL_HDRLEN(dev) + IPv6_HDRLEN))
@@ -315,6 +316,7 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
nerr("ERROR: Invalid packet length\n");
return -EINVAL;
}
#endif
/* If we are no longer processing the same ping ID, then flush any pending
* packets from the read-ahead buffer.
+8 -4
View File
@@ -214,11 +214,15 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
#endif
int ret;
/* Verify that the full packet will fit within the forwarding devices MTU.
* We provide no support for fragmenting forwarded packets.
/* Verify that the full packet will fit within the forwarding device's MTU
* if DF is set.
*/
if (NET_LL_HDRLEN(fwddev) + dev->d_len > NETDEV_PKTSIZE(fwddev))
if (NET_LL_HDRLEN(fwddev) + dev->d_len > NETDEV_PKTSIZE(fwddev)
#ifdef CONFIG_NET_IPFRAG
&& (ipv4->ipoffset[0] & (IP_FLAG_DONTFRAG >> 8))
#endif
)
{
nwarn("WARNING: Packet > MTU... Dropping\n");
ret = -EFBIG;
@@ -490,7 +494,7 @@ drop:
case -EMULTIHOP:
icmp_reply_type = ICMP_TIME_EXCEEDED;
icmp_reply_code = 0;
icmp_reply_code = ICMP_EXC_TTL;
goto reply;
default:
+9 -1
View File
@@ -30,6 +30,7 @@
#include <debug.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/ipv6ext.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
@@ -106,6 +107,12 @@ static int ipv6_hdrsize(FAR struct ipv6_hdr_s *ipv6)
break;
#endif
#ifdef CONFIG_NET_IPFRAG
case NEXT_FRAGMENT_EH:
return IPv6_HDRLEN + EXTHDR_FRAG_LEN;
break;
#endif
default:
nwarn("WARNING: Unrecognized proto: %u\n", ipv6->proto);
return -EPROTONOSUPPORT;
@@ -636,7 +643,8 @@ drop:
return OK;
case -EMULTIHOP:
icmpv6_reply(dev, ICMPv6_PACKET_TIME_EXCEEDED, 0, 0);
icmpv6_reply(dev, ICMPv6_PACKET_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
0);
return OK;
default:
+23
View File
@@ -0,0 +1,23 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config NET_IPFRAG
bool "IP fragmentation support"
default n
depends on (NET_IPv4 || NET_IPv6)
---help---
Enable support IP packet fragmentation and IP packet reassembly of
fragmented IP packets.
if NET_IPFRAG
config NET_IPFRAG_REASS_MAXAGE
int "IP fragmentation timeout"
default 20
---help---
The maximum time an IP fragment should wait in the reassembly buffer
before it is dropped. Units are deci-seconds. Default: 2 seconds.
endif # NET_IPFRAG
+40
View File
@@ -0,0 +1,40 @@
############################################################################
# net/ipfrag/Make.defs
#
# 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.
#
############################################################################
ifeq ($(CONFIG_NET_IPFRAG),y)
# fragment processing source files
NET_CSRCS += ipfrag.c
ifeq ($(CONFIG_NET_IPv4),y)
NET_CSRCS += ipv4_frag.c
endif
ifeq ($(CONFIG_NET_IPv6),y)
NET_CSRCS += ipv6_frag.c
endif
# Include fragment build support
DEPPATH += --dep-path ipfrag
VPATH += :ipfrag
endif # CONFIG_NET_IPFRAG
+1283
View File
File diff suppressed because it is too large Load Diff
+388
View File
@@ -0,0 +1,388 @@
/****************************************************************************
* net/ipfrag/ipfrag.h
*
* 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.
*
****************************************************************************/
#ifndef __NET_IPFRAG_IPFRAG_H
#define __NET_IPFRAG_IPFRAG_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <assert.h>
#include <nuttx/mutex.h>
#include <nuttx/queue.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include "devif/devif.h"
#if defined(CONFIG_NET_IPFRAG)
/****************************************************************************
* Public types
****************************************************************************/
enum ip_fragverify_e
{
/* Indicates whether received all fragments */
IP_FRAGVERIFY_RECVDALLFRAGS = 0x01 << 0,
/* Indicates whether received the first fragment which is used to:
* 1.construct the ICMP time exceeded msg(type=11, code=1) when reassembly
* timeout, but if the first fragment has not been received when timeout,
* no ICMP error message will be sent;
* 2.build NAT entry with the L4 port number and do forwarding.
*/
IP_FRAGVERIFY_RECVDZEROFRAG = 0x01 << 1,
/* Indicates whether the tail fragment is received(which morefrag flag is
* set to 0)
*/
IP_FRAGVERIFY_RECVDTAILFRAG = 0x01 << 2,
};
struct ip_fraglink_s
{
/* This link is used to maintain a single-linked list of ip_fraglink_s,
* it links all framgents with the same IP ID
*/
FAR struct ip_fraglink_s *flink;
FAR struct ip_fragsnode_s *fragsnode; /* Point to parent struct */
FAR struct iob_s *frag; /* Point to fragment data */
uint8_t isipv4; /* IPv4 or IPv6 */
uint16_t fragoff; /* Fragment offset */
uint16_t fraglen; /* Payload length */
uint16_t morefrags; /* The more frag flag */
/* The identification field is 16 bits in IPv4 header but 32 bits in IPv6
* fragment header
*/
uint32_t ipid;
};
struct ip_fragsnode_s
{
/* This link is used to maintain a single-linked list of ip_fragsnode_s.
* Must be the first field in the structure due to flink type casting.
*/
FAR struct ip_fragsnode_s *flink;
/* Another link which connects all ip_fragsnode_s in order of addition
* time
*/
FAR sq_entry_t *flinkat;
/* Interface understood by the network */
FAR struct net_driver_s *dev;
/* IP Identification (IP ID) field defined in ipv4 header or in ipv6
* fragment header.
*/
uint32_t ipid;
/* Count ticks, used by ressembly timer */
clock_t tick;
/* Remember some running flags */
uint16_t verifyflag;
/* Remember the total number of I/O buffers of this node */
uint32_t bufcnt;
/* Linked all fragments with the same IP ID. */
FAR struct ip_fraglink_s *frags;
/* Points to the reassembled outgoing IP frame */
FAR struct iob_s *outgoframe;
};
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
# define EXTERN extern "C"
extern "C"
{
#else
# define EXTERN extern
#endif
/* Only one thread can access g_assemblyhead_ipid and g_assemblyhead_time
* at a time
*/
extern mutex_t g_ipfrag_lock;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: ip_frag_remnode
*
* Description:
* free ip_fragsnode_s
*
* Input Parameters:
* node - node of the upper-level linked list, it maintains
* information about all fragments belonging to an IP datagram
*
* Returned Value:
* I/O buffer count of this node
*
****************************************************************************/
uint32_t ip_frag_remnode(FAR struct ip_fragsnode_s *node);
/****************************************************************************
* Name: ip_fragin_enqueue
*
* Description:
* Enqueue one fragment.
* All fragments belonging to one IP frame are organized in a linked list
* form, that is a ip_fragsnode_s node. All ip_fragsnode_s nodes are also
* organized in an upper-level linked list.
*
* Input Parameters:
* dev - NIC Device instance
* curfraglink - node of the lower-level linked list, it maintains
* information of one fragment
*
* Returned Value:
* Whether queue is empty before enqueue the new node
*
****************************************************************************/
bool ip_fragin_enqueue(FAR struct net_driver_s *dev,
FAR struct ip_fraglink_s *curfraglink);
/****************************************************************************
* Name: ipv4_fragin
*
* Description:
* Handling incoming IPv4 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 ipv4_fragin(FAR struct net_driver_s *dev);
/****************************************************************************
* 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);
/****************************************************************************
* Name: ip_fragout_slice
*
* Description:
* According to the MTU of a given NIC, split the original data into
* multiple data pieces, and the space for filling the L3 header is
* reserved at the forefront of each piece. Each piece is stored in
* independent I/O buffer(s) and eventually forms an I/O buffer queue.
* Note:
* 1.About the 'piece' above
* 1).If MTU < CONFIG_IOB_BUFSIZE, a piece consists of an I/O buffer;
* 2).If MTU >= CONFIG_IOB_BUFSIZE, a piece consists of multiple I/O
* buffers.
* 2.This function split and gathers the incoming data into outgoing
* I/O buffers according to the MTU, but is not responsible for
* building the L3 header related to the fragmentation.
*
* Input Parameters:
* iob - The data comes from
* domain - PF_INET or PF_INET6
* mtu - MTU of given NIC
* unfraglen - The starting position to fragmentation processing
* fragq - Those output slices
*
* Returned Value:
* Number of fragments
*
* Assumptions:
* Data length(iob->io_pktlen) is grater than the MTU of current NIC
*
****************************************************************************/
int32_t ip_fragout_slice(FAR struct iob_s *iob, uint8_t domain, uint16_t mtu,
uint16_t unfraglen, FAR struct iob_queue_s *fragq);
/****************************************************************************
* Name: ipv4_fragout
*
* Description:
* Execute the ipv4 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 ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu);
/****************************************************************************
* 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);
/****************************************************************************
* Name: ip_frag_startwdog
*
* Description:
* Start the reassembly timeout timer
*
* Returned Value:
* None
*
****************************************************************************/
void ip_frag_startwdog(void);
/****************************************************************************
* Name: ip_frag_stop
*
* Description:
* Stop the fragment process function for the specified NIC.
*
* Input Parameters:
* dev - NIC Device instance which will be bring down
*
* Returned Value:
* None
*
****************************************************************************/
void ip_frag_stop(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: ip_frag_remallfrags
*
* Description:
* Release all I/O Buffers used by fragment processing module when
* I/O Buffer resources are exhausted.
*
* Returned Value:
* None
*
****************************************************************************/
void ip_frag_remallfrags(void);
/****************************************************************************
* Name: ip_fragout
*
* Description:
* Fragout processing
*
* Input Parameters:
* dev - The NIC device
*
* Returned Value:
* A non-negative value is returned on success; negative value on failure.
*
****************************************************************************/
int32_t ip_fragout(FAR struct net_driver_s *dev);
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_NET_IPFRAG */
#endif /* __NET_IPFRAG_IPFRAG_H */
+443
View File
@@ -0,0 +1,443 @@
/****************************************************************************
* net/ipfrag/ipv4_frag.c
* Handling incoming IPv4 fragment input
*
* 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_IPv4) && 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 "netdev/netdev.h"
#include "inet/inet.h"
#include "utils/utils.h"
#include "ipfrag.h"
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline int32_t
ipv4_fragin_getinfo(FAR struct iob_s *iob,
FAR struct ip_fraglink_s *fraglink);
static uint32_t ipv4_fragin_reassemble(FAR struct ip_fragsnode_s *node);
static inline void
ipv4_fragout_buildipv4header(FAR struct ipv4_hdr_s *ref,
FAR struct ipv4_hdr_s *ipv4,
uint16_t len, uint16_t ipoff);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ipv4_fragin_getinfo
*
* Description:
* Polulate fragment information from the input ipv4 packet data.
*
* Input Parameters:
* iob - An IPv4 fragment
* fraglink - node of the lower-level linked list, it maintains information
* of one fragment
*
* Returned Value:
* None
*
****************************************************************************/
static inline int32_t
ipv4_fragin_getinfo(FAR struct iob_s *iob,
FAR struct ip_fraglink_s *fraglink)
{
FAR struct ipv4_hdr_s *ipv4 = (FAR struct ipv4_hdr_s *)
(iob->io_data + iob->io_offset);
uint16_t offset;
fraglink->flink = NULL;
fraglink->fragsnode = NULL;
fraglink->isipv4 = true;
offset = (ipv4->ipoffset[0] << 8) + ipv4->ipoffset[1];
fraglink->morefrags = offset & IP_FLAG_MOREFRAGS;
fraglink->fragoff = ((offset & 0x1fff) << 3);
fraglink->fraglen = (ipv4->len[0] << 8) + ipv4->len[1] - IPv4_HDRLEN;
fraglink->ipid = (ipv4->ipid[0] << 8) + ipv4->ipid[1];
fraglink->frag = iob;
return OK;
}
/****************************************************************************
* Name: ipv4_fragin_reassemble
*
* Description:
* Reassemble all ipv4 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 ipv4_fragin_reassemble(FAR struct ip_fragsnode_s *node)
{
FAR struct iob_s *head;
FAR struct ipv4_hdr_s *ipv4;
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 struct ip_fraglink_s *linknext;
FAR struct iob_s *iob = fraglink->frag;
if (fraglink->fragoff != 0)
{
uint16_t iphdrlen;
/* Get IPv4 header length from IPv4 header (it may carry some
* IPv4 options)
*/
ipv4 = (FAR struct ipv4_hdr_s *)(head->io_data + head->io_offset);
iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
/* Just modify the offset and length of all none zero fragments */
iob->io_offset += iphdrlen;
iob->io_len -= iphdrlen;
iob->io_pktlen -= iphdrlen;
/* Concatenate this iob to the reassembly chain */
iob_concat(head, iob);
}
else
{
/* Remember the head iob */
head = iob;
}
linknext = fraglink->flink;
kmm_free(fraglink);
fraglink = linknext;
}
/* Remember the reassembled outgoing IP frame */
node->outgoframe = head;
/* Get pointer of the new IPv4 header */
ipv4 = (FAR struct ipv4_hdr_s *)(head->io_data + head->io_offset);
/* Update the length value in the IP Header */
ipv4->len[0] = head->io_pktlen >> 8;
ipv4->len[1] = head->io_pktlen & 0xff;
/* Set ipoffset to zero */
ipv4->ipoffset[0] = 0;
ipv4->ipoffset[1] = 0;
/* Calculate IP checksum. */
ipv4->ipchksum = 0;
ipv4->ipchksum = ~(ipv4_chksum(ipv4));
return head->io_pktlen;
}
/****************************************************************************
* Name: ipv4_fragout_buildipv4header
*
* Description:
* Build IPv4 header for an IPv4 fragment.
*
* Input Parameters:
* ref - The reference IPv4 Header
* ipv4 - The pointer of the newly generated IPv4 Header
* len - Total Length of this IP frame
* ipoff - Fragment offset
*
* Returned Value:
* None
*
****************************************************************************/
static inline void
ipv4_fragout_buildipv4header(FAR struct ipv4_hdr_s *ref,
FAR struct ipv4_hdr_s *ipv4,
uint16_t len, uint16_t ipoff)
{
if (ref != ipv4)
{
uint32_t iphdrlen = (ref->vhl & IPv4_HLMASK) << 2;
memcpy(ipv4, ref, iphdrlen);
}
ipv4->len[0] = len >> 8;
ipv4->len[1] = len & 0xff;
ipv4->ipoffset[0] = ipoff >> 8;
ipv4->ipoffset[1] = ipoff & 0xff;
/* Calculate IP checksum. */
ipv4->ipchksum = 0;
ipv4->ipchksum = ~(ipv4_chksum(ipv4));
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipv4_fragin
*
* Description:
* Handling incoming IPv4 and 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 ipv4_fragin(FAR struct net_driver_s *dev)
{
FAR struct ip_fragsnode_s *node;
FAR struct ip_fraglink_s *fraginfo;
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 */
ipv4_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
*/
ipv4_fragin_reassemble(node);
netdev_iob_replace(dev, node->outgoframe);
/* Free the memory of node */
kmm_free(node);
return ipv4_input(dev);
}
nxmutex_unlock(&g_ipfrag_lock);
if (restartwdog)
{
/* Restart the work queue for fragment processing */
ip_frag_startwdog();
}
return OK;
}
/****************************************************************************
* Name: ipv4_fragout
*
* Description:
* Execute the ipv4 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 ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu)
{
uint32_t iter;
uint32_t nfrags;
uint16_t offset = 0;
uint16_t hdrlen;
FAR struct iob_s *frag;
FAR struct ipv4_hdr_s *ref;
struct iob_queue_s fragq =
{
NULL, NULL
};
/* Get the total length of L3 Header(if IPv4 options are present, then this
* length includes the size of all the IPv4 options)
*/
hdrlen = (IPv4BUF->vhl & IPv4_HLMASK) << 2;
/* Reconstruct I/O Buffer according to MTU, which will reserve
* the space for the L3 header
*/
nfrags = ip_fragout_slice(dev->d_iob, PF_INET, mtu, hdrlen, &fragq);
assert(nfrags > 1);
netdev_iob_clear(dev);
/* 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 ipv4_hdr_s *)(frag->io_data + frag->io_offset);
/* Update the IPv4 header of the first fragment */
ipv4_fragout_buildipv4header(ref, ref, frag->io_pktlen,
IP_FLAG_MOREFRAGS);
}
else
{
uint16_t ipoff = (offset - iter * hdrlen) >> 3;
if (iter < nfrags - 1)
{
ipoff |= IP_FLAG_MOREFRAGS;
}
/* Refer to the zero fragment ipv4 header to construct the ipv4
* header of non-zero fragment
*/
ipv4_fragout_buildipv4header(ref,
(FAR struct ipv4_hdr_s *)(frag->io_data + frag->io_offset),
frag->io_pktlen, ipoff);
}
/* 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.ipv4.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);
return -ENOMEM;
}
#endif /* CONFIG_NET_IPv4 && CONFIG_NET_IPFRAG */
+670
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -35,6 +35,7 @@
#include <net/ethernet.h>
#include <nuttx/net/netdev.h>
#include "ipfrag/ipfrag.h"
#include "netdev/netdev.h"
#include "netlink/netlink.h"
#include "arp/arp.h"
@@ -92,6 +93,12 @@ int netdev_carrier_off(FAR struct net_driver_s *dev)
dev->d_flags &= ~IFF_RUNNING;
netlink_device_notify(dev);
#ifdef CONFIG_NET_IPFRAG
/* Clean up fragment data for this NIC (if any) */
ip_frag_stop(dev);
#endif
/* Notify clients that the network has been taken down */
devif_dev_event(dev, NETDEV_DOWN);