diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9487d966fb..0903f296eb 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -96,7 +96,7 @@ */ #ifndef CONFIG_TUN_NINTERFACES -# define CONFIG_TUN_NINTERFACES 1 +# define CONFIG_TUN_NINTERFACES 1 #endif /* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per @@ -105,6 +105,12 @@ #define TUN_WDDELAY (1*CLK_TCK) +/* This is a helper pointer for accessing the contents of the Ethernet header */ + +#ifdef CONFIG_NET_ETHERNET +# define BUF ((struct eth_hdr_s *)priv->dev.d_buf) +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -157,10 +163,19 @@ static void tun_unlock(FAR struct tun_device_s *priv); static int tun_fd_transmit(FAR struct tun_device_s *priv); static int tun_txpoll(struct net_driver_s *dev); +#ifdef CONFIG_NET_ETHERNET +static int tun_txpoll_tap(struct net_driver_s *dev); +#endif +static int tun_txpoll_tun(struct net_driver_s *dev); /* Interrupt handling */ static void tun_net_receive(FAR struct tun_device_s *priv); +#ifdef CONFIG_NET_ETHERNET +static void tun_net_receive_tap(FAR struct tun_device_s *priv); +#endif +static void tun_net_receive_tun(FAR struct tun_device_s *priv); + static void tun_txdone(FAR struct tun_device_s *priv); /* Watchdog timer expirations */ @@ -378,6 +393,123 @@ static int tun_fd_transmit(FAR struct tun_device_s *priv) ****************************************************************************/ static int tun_txpoll(struct net_driver_s *dev) +{ + int ret; + +#ifdef CONFIG_NET_ETHERNET + if (dev->d_lltype == NET_LL_ETHERNET) + { + ret = tun_txpoll_tap(dev); + } + else +#endif + { + ret = tun_txpoll_tun(dev); + } + + return ret; +} + +/**************************************************************************** + * Name: tun_txpoll_tap : for tap (ethernet bridge) mode + * + * Description: + * The transmitter is available, check if the network has any outgoing packets + * ready to send. This is a callback from devif_poll(). devif_poll() may + * be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ETHERNET +static int tun_txpoll_tap(struct net_driver_s *dev) +{ + FAR struct tun_device_s *priv = (FAR struct tun_device_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Look up the destination MAC address and add it to the Ethernet + * header. + */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + neighbor_out(&priv->dev); + } +#endif /* CONFIG_NET_IPv6 */ + + /* Send the packet */ + + priv->read_d_len = priv->dev.d_len; + tun_fd_transmit(priv); + + return 1; + } + + /* If zero is returned, the polling will continue until all connections have + * been examined. + */ + + return 0; +} +#endif + +/**************************************************************************** + * Name: tun_txpoll_tun : for tun (IP tunneling) mode + * + * Description: + * The transmitter is available, check if the network has any outgoing packets + * ready to send. This is a callback from devif_poll(). devif_poll() may + * be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int tun_txpoll_tun(struct net_driver_s *dev) { FAR struct tun_device_s *priv = (FAR struct tun_device_s *)dev->d_private; @@ -403,7 +535,7 @@ static int tun_txpoll(struct net_driver_s *dev) } /**************************************************************************** - * Name: tun_receive + * Name: tun_net_receive * * Description: * An interrupt was received indicating the availability of a new RX packet @@ -420,6 +552,203 @@ static int tun_txpoll(struct net_driver_s *dev) ****************************************************************************/ static void tun_net_receive(FAR struct tun_device_s *priv) +{ +#ifdef CONFIG_NET_ETHERNET + if (priv->dev.d_lltype == NET_LL_ETHERNET) + { + tun_net_receive_tap(priv); + } + else +#endif + { + tun_net_receive_tun(priv); + } +} + +/**************************************************************************** + * Name: tun_net_receive_tap : for tap (ethernet bridge) mode + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ETHERNET +static void tun_net_receive_tap(FAR struct tun_device_s *priv) +{ + int ret; + + /* Copy the data data from the hardware to priv->dev.d_buf. Set amount of + * data in priv->dev.d_len + */ + + NETDEV_RXPACKETS(&priv->dev); + +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the packet tap */ + + pkt_input(&priv->dev); +#endif + + /* We only accept IP packets of the configured type and ARP packets */ + +#if defined(CONFIG_NET_IPv4) + if (BUF->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + NETDEV_RXIPV4(&priv->dev); + + /* Give the IPv4 packet to the network layer. ipv4_input will return + * an error if it is unable to dispatch the packet at this time. + */ + + arp_ipin(&priv->dev); + ret = ipv4_input(&priv->dev); + + if (ret == OK) + { + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#ifdef CONFIG_NET_IPv6 + else + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + priv->write_d_len = priv->dev.d_len; + tun_fd_transmit(priv); + } + else + { + tun_pollnotify(priv, POLLOUT); + } + } + else + { + priv->dev.d_len = 0; + tun_pollnotify(priv, POLLOUT); + } + + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("Iv6 frame\n"); + NETDEV_RXIPV6(&priv->dev); + + /* Give the IPv6 packet to the network layer. ipv6_input will return + * an error if it is unable to dispatch the packet at this time. + */ + + ret = ipv6_input(&priv->dev); + + if (ret == OK) + { + + if (priv->dev.d_len > 0) + { + + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv4(priv->dev.d_flags)) + { + arp_out(&priv->dev); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + { + neighbor_out(&priv->dev); + } +#endif + + priv->write_d_len = priv->dev.d_len; + tun_fd_transmit(priv); + } + else + { + tun_pollnotify(priv, POLLOUT); + } + } + else + { + priv->write_d_len = 0; + tun_pollnotify(priv, POLLOUT); + } + } + else +#endif +#ifdef CONFIG_NET_ARP + if (BUF->type == htons(ETHTYPE_ARP)) + { + arp_arpin(&priv->dev); + NETDEV_RXARP(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + priv->write_d_len = priv->dev.d_len; + tun_fd_transmit(priv); + } + } + else +#endif + { + NETDEV_RXDROPPED(&priv->dev); + } + +} +#endif + +/**************************************************************************** + * Name: tun_net_receive_tun : for tun (IP tunneling) mode + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void tun_net_receive_tun(FAR struct tun_device_s *priv) { int ret; @@ -904,14 +1233,10 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, tun_ifdown(&priv->dev); - if (devfmt) - { - strncpy(priv->dev.d_ifname, devfmt, IFNAMSIZ); - } - /* Register the device with the OS so that socket IOCTLs can be performed */ ret = netdev_register(&priv->dev, NET_LL_TUN); + if (ret != OK) { nxsem_destroy(&priv->waitsem); @@ -919,6 +1244,13 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, return ret; } + /* Assign d_ifname if specified. This must be done after registration */ + + if (devfmt) + { + strncpy(priv->dev.d_ifname, devfmt, IFNAMSIZ); + } + priv->filep = filep; /* Set link to file */ filep->f_priv = priv; /* Set link to TUN device */ @@ -1242,6 +1574,10 @@ static int tun_ioctl(FAR struct file *filep, int cmd, unsigned long arg) */ priv->dev.d_llhdrlen = ETH_HDRLEN; + + /* Also, set the link type to NET_LL_ETHERNET */ + + priv->dev.d_lltype = NET_LL_ETHERNET; } else if ((ifr->ifr_flags & IFF_MASK) == IFF_TUN) #endif