123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 |
- /****************************************************************************
- * net/ipforward/ipv6_forward.c
- *
- * 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>
- #include <string.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/mm/iob.h>
- #include <nuttx/net/net.h>
- #include <nuttx/net/netdev.h>
- #include <nuttx/net/netstats.h>
- #include "netdev/netdev.h"
- #include "sixlowpan/sixlowpan.h"
- #include "devif/devif.h"
- #include "ipforward/ipforward.h"
- #if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6)
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- #define PACKET_FORWARDED 0
- #define PACKET_NOT_FORWARDED 1
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: ipv6_hdrsize
- *
- * Description:
- * Return the size of the IPv6 header and the following.
- *
- * Input Parameters:
- * ipv6 - A pointer to the IPv6 header in within the IPv6 packet. This
- * is immediately followed by the L3 header which may be TCP, UDP,
- * or ICMPv6.
- *
- * Returned Value:
- * The size of the combined L2 + L3 headers is returned on success. An
- * error is returned only if the prototype is not supported.
- *
- ****************************************************************************/
- #ifdef CONFIG_DEBUG_NET_WARN
- static int ipv6_hdrsize(FAR struct ipv6_hdr_s *ipv6)
- {
- /* Size is determined by the following protocol header, */
- switch (ipv6->proto)
- {
- #ifdef CONFIG_NET_TCP
- case IP_PROTO_TCP:
- {
- FAR struct tcp_hdr_s *tcp =
- (FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv6 + IPv6_HDRLEN);
- unsigned int tcpsize;
- /* The TCP header length is encoded in the top 4 bits of the
- * tcpoffset field (in units of 32-bit words).
- */
- tcpsize = ((uint16_t)tcp->tcpoffset >> 4) << 2;
- return IPv6_HDRLEN + tcpsize;
- }
- break;
- #endif
- #ifdef CONFIG_NET_UDP
- case IP_PROTO_UDP:
- return IPv6_HDRLEN + UDP_HDRLEN;
- break;
- #endif
- #ifdef CONFIG_NET_ICMPv6
- case IP_PROTO_ICMP6:
- return IPv6_HDRLEN + ICMPv6_HDRLEN;
- break;
- #endif
- default:
- nwarn("WARNING: Unrecognized proto: %u\n", ipv6->proto);
- return -EPROTONOSUPPORT;
- }
- }
- #endif
- /****************************************************************************
- * Name: ipv6_decr_ttl
- *
- * Description:
- * Decrement the IPv6 TTL (time to live value). TTL field is set by the
- * sender of the packet and reduced by every router on the route to its
- * destination. If the TTL field reaches zero before the datagram arrives
- * at its destination, then the datagram is discarded and an ICMP error
- * packet (11 - Time Exceeded) is sent back to the sender.
- *
- * The purpose of the TTL field is to avoid a situation in which an
- * undeliverable datagram keeps circulating on an Internet system, and
- * such a system eventually becoming swamped by such "immortals".
- *
- * Input Parameters:
- * ipv6 - A pointer to the IPv6 header in within the IPv6 packet to be
- * forwarded.
- *
- * Returned Value:
- * The new TTL value is returned. A value <= 0 means the hop limit has
- * expired.
- *
- ****************************************************************************/
- static int ipv6_decr_ttl(FAR struct ipv6_hdr_s *ipv6)
- {
- int ttl = (int)ipv6->ttl - 1;
- if (ttl <= 0)
- {
- #ifdef CONFIG_NET_ICMPv6
- /* Return an ICMPv6 error packet back to the sender. */
- # warning Missing logic
- #endif
- /* Return zero which must cause the packet to be dropped */
- return 0;
- }
- /* Save the updated TTL value */
- ipv6->ttl = ttl;
- /* NOTE: We do not have to recalculate the IPv6 checksum because (1) the
- * IPv6 header does not include a checksum itself and (2) the TTL is not
- * included in the sum for the TCP and UDP headers.
- */
- return ttl;
- }
- /****************************************************************************
- * Name: ipv6_packet_conversion
- *
- * Description:
- * Generic output conversion hook. Only needed for IEEE802.15.4 for now
- * but this is a point where support for other conversions may be
- * provided.
- *
- * Returned Value:
- * PACKET_FORWARDED - Packet was forwarded
- * PACKET_NOT_FORWARDED - Packet was not forwarded
- * < 0 - And error occurred (and packet not forwarded).
- *
- ****************************************************************************/
- #ifdef CONFIG_NET_6LOWPAN
- static int ipv6_packet_conversion(FAR struct net_driver_s *dev,
- FAR struct net_driver_s *fwddev,
- FAR struct ipv6_hdr_s *ipv6)
- {
- int ret = PACKET_NOT_FORWARDED;
- if (dev->d_len > 0)
- {
- /* Check if this is a device served by 6LoWPAN */
- if (fwddev->d_lltype != NET_LL_IEEE802154 &&
- fwddev->d_lltype != NET_LL_PKTRADIO)
- {
- nwarn("WARNING: Unsupported link layer... Not forwarded\n");
- }
- else
- #ifdef CONFIG_NET_TCP
- if (ipv6->proto == IP_PROTO_TCP)
- {
- /* Decrement the TTL in the IPv6 header. If it decrements to
- * zero, then drop the packet.
- */
- ret = ipv6_decr_ttl(ipv6);
- if (ret < 1)
- {
- nwarn("WARNING: Hop limit exceeded... Dropping!\n");
- ret = -EMULTIHOP;
- }
- else
- {
- /* Let 6LoWPAN convert IPv6 TCP output into IEEE802.15.4
- * frames.
- */
- sixlowpan_tcp_send(dev, fwddev, ipv6);
- /* The packet was forwarded */
- dev->d_len = 0;
- return PACKET_FORWARDED;
- }
- }
- else
- #endif
- #ifdef CONFIG_NET_UDP
- if (ipv6->proto == IP_PROTO_UDP)
- {
- /* Decrement the TTL in the IPv6 header. If it decrements to
- * zero, then drop the packet.
- */
- ret = ipv6_decr_ttl(ipv6);
- if (ret < 1)
- {
- nwarn("WARNING: Hop limit exceeded... Dropping!\n");
- ret = -EMULTIHOP;
- }
- else
- {
- /* Let 6LoWPAN convert IPv6 UDP output into IEEE802.15.4
- * frames.
- */
- sixlowpan_udp_send(dev, fwddev, ipv6);
- /* The packet was forwarded */
- dev->d_len = 0;
- return PACKET_FORWARDED;
- }
- }
- else
- #endif
- #ifdef CONFIG_NET_ICMPv6
- if (ipv6->proto == IP_PROTO_ICMP6)
- {
- /* Decrement the TTL in the IPv6 header. If it decrements to
- * zero, then drop the packet.
- */
- ret = ipv6_decr_ttl(ipv6);
- if (ret < 1)
- {
- nwarn("WARNING: Hop limit exceeded... Dropping!\n");
- ret = -EMULTIHOP;
- }
- else
- {
- /* Let 6LoWPAN convert IPv6 ICMPv6 output into IEEE802.15.4
- * frames.
- */
- sixlowpan_icmpv6_send(dev, fwddev, ipv6);
- /* The packet was forwarded */
- dev->d_len = 0;
- return PACKET_FORWARDED;
- }
- }
- else
- #endif
- {
- /* Otherwise, we cannot forward the packet */
- nwarn("WARNING: Dropping. Unsupported 6LoWPAN protocol: %d\n",
- ipv6->proto);
- }
- }
- /* The packet was not forwarded (or the HOP limit was exceeded) */
- ipv6_dropstats(ipv6);
- return ret;
- }
- #else
- # define ipv6_packet_conversion(dev, fwddev, ipv6) (PACKET_NOT_FORWARDED)
- #endif /* CONFIG_NET_6LOWPAN */
- /****************************************************************************
- * Name: ipv6_dev_forward
- *
- * Description:
- * This function is called from ipv6_forward when it is necessary to
- * forward a packet from the current device to different device. In this
- * case, the forwarding operation must be performed asynchronously when
- * the TX poll is received from the forwarding device.
- *
- * Input Parameters:
- * dev - The device on which the packet was received and which
- * contains the IPv6 packet.
- * fwdddev - The device on which the packet must be forwarded.
- * ipv6 - A pointer to the IPv6 header in within the IPv6 packet
- *
- * Returned Value:
- * Zero is returned if the packet was successfully forwarded; A negated
- * errno value is returned if the packet is not forwardable. In that
- * latter case, the caller (ipv6_input()) should drop the packet.
- *
- ****************************************************************************/
- static int ipv6_dev_forward(FAR struct net_driver_s *dev,
- FAR struct net_driver_s *fwddev,
- FAR struct ipv6_hdr_s *ipv6)
- {
- FAR struct forward_s *fwd = NULL;
- #ifdef CONFIG_DEBUG_NET_WARN
- int hdrsize;
- #endif
- int ret;
- /* If the interface isn't "up", we can't forward. */
- if ((fwddev->d_flags & IFF_UP) == 0)
- {
- nwarn("WARNING: device is DOWN\n");
- ret = -EHOSTUNREACH;
- goto errout;
- }
- /* Perform any necessary packet conversions. */
- ret = ipv6_packet_conversion(dev, fwddev, ipv6);
- if (ret < 0)
- {
- nwarn("WARNING: ipv6_packet_conversion failed: %d\n", ret);
- goto errout;
- }
- else if (ret == PACKET_NOT_FORWARDED)
- {
- /* Verify that the full packet will fit within the forwarding devices
- * MTU. We provide no support for fragmenting forwarded packets.
- */
- if (NET_LL_HDRLEN(fwddev) + dev->d_len > NETDEV_PKTSIZE(fwddev))
- {
- nwarn("WARNING: Packet > MTU... Dropping\n");
- ret = -EFBIG;
- goto errout;
- }
- /* Get a pre-allocated forwarding structure, This structure will be
- * completely zeroed when we receive it.
- */
- fwd = ipfwd_alloc();
- if (fwd == NULL)
- {
- nwarn("WARNING: Failed to allocate forwarding structure\n");
- ret = -ENOMEM;
- goto errout;
- }
- /* Initialize the easy stuff in the forwarding structure */
- fwd->f_dev = fwddev; /* Forwarding device */
- #ifdef CONFIG_NET_IPv4
- fwd->f_domain = PF_INET6; /* IPv6 address domain */
- #endif
- #ifdef CONFIG_DEBUG_NET_WARN
- /* Get the size of the IPv6 + L3 header. */
- hdrsize = ipv6_hdrsize(ipv6);
- if (hdrsize < IPv6_HDRLEN)
- {
- nwarn("WARNING: Could not determine L2+L3 header size\n");
- ret = -EPROTONOSUPPORT;
- goto errout_with_fwd;
- }
- /* The L2/L3 headers must fit within one, contiguous IOB. */
- if (hdrsize > CONFIG_IOB_BUFSIZE)
- {
- nwarn("WARNING: Header is too big for pre-allocated structure\n");
- ret = -E2BIG;
- goto errout_with_fwd;
- }
- #endif
- /* Try to allocate the head of an IOB chain. If this fails, the
- * packet will be dropped; we are not operating in a context where
- * waiting for an IOB is a good idea
- */
- fwd->f_iob = iob_tryalloc(false, IOBUSER_NET_IPFORWARD);
- if (fwd->f_iob == NULL)
- {
- nwarn("WARNING: iob_tryalloc() failed\n");
- ret = -ENOMEM;
- goto errout_with_fwd;
- }
- /* Copy the L2/L3 headers plus any following payload into an IOB
- * chain. iob_trycopin() will not wait, but will fail there are no
- * available IOBs.
- *
- * REVISIT: Consider an alternative design that does not require data
- * copying. This would require a pool of d_buf's that are managed by
- * the network rather than the network device.
- */
- ret = iob_trycopyin(fwd->f_iob, (FAR const uint8_t *)ipv6,
- dev->d_len, 0, false, IOBUSER_NET_IPFORWARD);
- if (ret < 0)
- {
- nwarn("WARNING: iob_trycopyin() failed: %d\n", ret);
- goto errout_with_iobchain;
- }
- /* Decrement the TTL in the copy of the IPv6 header (retaining the
- * original TTL in the sourcee to handle the broadcast case). If the
- * TTL decrements to zero, then do not forward the packet.
- */
- ret = ipv6_decr_ttl((FAR struct ipv6_hdr_s *)fwd->f_iob->io_data);
- if (ret < 1)
- {
- nwarn("WARNING: Hop limit exceeded... Dropping!\n");
- ret = -EMULTIHOP;
- goto errout_with_iobchain;
- }
- /* Then set up to forward the packet according to the protocol. */
- ret = ipfwd_forward(fwd);
- if (ret >= 0)
- {
- dev->d_len = 0;
- return OK;
- }
- }
- errout_with_iobchain:
- if (fwd != NULL && fwd->f_iob != NULL)
- {
- iob_free_chain(fwd->f_iob, IOBUSER_NET_IPFORWARD);
- }
- errout_with_fwd:
- if (fwd != NULL)
- {
- ipfwd_free(fwd);
- }
- errout:
- return ret;
- }
- /****************************************************************************
- * Name: ipv6_forward_callback
- *
- * Description:
- * This function is a callback from netdev_foreach. It implements the
- * the broadcast forwarding action for each network device (other than, of
- * course, the device that received the packet).
- *
- * Input Parameters:
- * dev - The device on which the packet was received and which contains
- * the IPv6 packet.
- * ipv6 - A convenience pointer to the IPv6 header in within the IPv6
- * packet
- *
- * Returned Value:
- * Typically returns zero (meaning to continue the enumeration), but will
- * return a non-zero to stop the enumeration if an error occurs.
- *
- ****************************************************************************/
- #ifdef CONFIG_NET_IPFORWARD_BROADCAST
- int ipv6_forward_callback(FAR struct net_driver_s *fwddev, FAR void *arg)
- {
- FAR struct net_driver_s *dev = (FAR struct net_driver_s *)arg;
- FAR struct ipv6_hdr_s *ipv6;
- int ret;
- DEBUGASSERT(fwddev != NULL && dev != NULL && dev->d_buf != NULL);
- /* Check if we are forwarding on the same device that we received the
- * packet from.
- */
- if (fwddev != dev)
- {
- /* Recover the pointer to the IPv6 header in the receiving device's
- * d_buf.
- */
- ipv6 = (FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)];
- /* Send the packet asynchrously on the forwarding device. */
- ret = ipv6_dev_forward(dev, fwddev, ipv6);
- if (ret < 0)
- {
- nwarn("WARNING: ipv6_dev_forward failed: %d\n", ret);
- return ret;
- }
- }
- return OK;
- }
- #endif
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: ipv6_forward
- *
- * Description:
- * This function is called from ipv6_input when a packet is received that
- * is not destined for us. In this case, the packet may need to be
- * forwarded to another device (or sent back out the same device)
- * depending configuration, routing table information, and the IPv6
- * networks served by various network devices.
- *
- * Input Parameters:
- * dev - The device on which the packet was received and which contains
- * the IPv6 packet.
- * ipv6 - A convenience pointer to the IPv6 header in within the IPv6
- * packet
- *
- * On input:
- * - dev->d_buf holds the received packet.
- * - dev->d_len holds the length of the received packet MINUS the
- * size of the L1 header. That was subtracted out by ipv6_input.
- * - ipv6 points to the IPv6 header with dev->d_buf.
- *
- * Returned Value:
- * Zero is returned if the packet was successfully forward; A negated
- * errno value is returned if the packet is not forwardable. In that
- * latter case, the caller (ipv6_input()) should drop the packet.
- *
- ****************************************************************************/
- int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6)
- {
- FAR struct net_driver_s *fwddev;
- int ret;
- /* Search for a device that can forward this packet. */
- fwddev = netdev_findby_ripv6addr(ipv6->srcipaddr, ipv6->destipaddr);
- if (fwddev == NULL)
- {
- nwarn("WARNING: Not routable\n");
- return (ssize_t)-ENETUNREACH;
- }
- /* Check if we are forwarding on the same device that we received the
- * packet from.
- */
- if (fwddev != dev)
- {
- /* Send the packet asynchrously on the forwarding device. */
- ret = ipv6_dev_forward(dev, fwddev, ipv6);
- if (ret < 0)
- {
- nwarn("WARNING: ipv6_dev_forward failed: %d\n", ret);
- goto drop;
- }
- }
- else
- #if defined(CONFIG_NET_6LOWPAN) /* REVISIT: Currently only support for 6LoWPAN */
- {
- /* Single network device. The use case here is where an endpoint acts
- * as a hub in a star configuration. This is typical for a wireless
- * star configuration where not all endpoints are accessible from all
- * other endpoints, but seems less useful for a wired network.
- */
- /* Perform any necessary packet conversions. If the packet was handled
- * via a backdoor path (or dropped), then dev->d_len will be zero. If
- * the packet needs to be forwarded in the normal manner then
- * dev->d_len will be unchanged.
- */
- ret = ipv6_packet_conversion(dev, dev, ipv6);
- if (ret < 0)
- {
- nwarn("WARNING: ipv6_packet_conversion failed: %d\n", ret);
- goto drop;
- }
- else if (ret == PACKET_NOT_FORWARDED)
- {
- #ifdef CONFIG_NET_ETHERNET
- /* REVISIT:
- * For Ethernet we may have to fix up the Ethernet header:
- * - source MAC, the MAC of the current device.
- * - dest MAC, the MAC associated with the destination IPv6
- * address.
- * This will involve ICMPv6 and Neighbor Discovery.
- */
- /* Correct dev->d_buf by adding back the L1 header length */
- #endif
- /* Nothing other 6LoWPAN forwarding is currently handled and that
- * case was dealt with in ipv6_packet_conversion().
- *
- * REVISIT: Is this an issue? Do other use cases make sense?
- */
- nwarn("WARNING: Packet forwarding supported only for 6LoWPAN\n");
- ret = -ENOSYS;
- goto drop;
- }
- }
- #else /* CONFIG_NET_6LOWPAN */
- {
- nwarn(
- "WARNING: Packet forwarding not supported in this configuration\n");
- ret = -ENOSYS;
- goto drop;
- }
- #endif /* CONFIG_NET_6LOWPAN */
- /* Return success. ipv6_input will return to the network driver with
- * dev->d_len set to the packet size and the network driver will perform
- * the transfer.
- */
- return OK;
- drop:
- ipv6_dropstats(ipv6);
- dev->d_len = 0;
- return ret;
- }
- /****************************************************************************
- * Name: ipv6_forward_broadcast
- *
- * Description:
- * This function is called from ipv6_input when a broadcast or multicast
- * packet is received. If CONFIG_NET_IPFORWARD_BROADCAST is enabled, this
- * function will forward the broadcast packet to other networks through
- * other network devices.
- *
- * Input Parameters:
- * dev - The device on which the packet was received and which contains
- * the IPv6 packet.
- * ipv6 - A convenience pointer to the IPv6 header in within the IPv6
- * packet
- *
- * On input:
- * - dev->d_buf holds the received packet.
- * - dev->d_len holds the length of the received packet MINUS the
- * size of the L1 header. That was subtracted out by ipv6_input.
- * - ipv6 points to the IPv6 header with dev->d_buf.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifdef CONFIG_NET_IPFORWARD_BROADCAST
- void ipv6_forward_broadcast(FAR struct net_driver_s *dev,
- FAR struct ipv6_hdr_s *ipv6)
- {
- /* Don't bother if the TTL would expire */
- if (ipv6->ttl > 1)
- {
- /* Forward the the broadcast/multicast packet to all devices except,
- * of course, the device that received the packet.
- */
- netdev_foreach(ipv6_forward_callback, dev);
- }
- }
- #endif
- #endif /* CONFIG_NET_IPFORWARD && CONFIG_NET_IPv6 */
|