| /** |
| * @file |
| * Ethernet common functions |
| * |
| * @defgroup ethernet Ethernet |
| * @ingroup callbackstyle_api |
| */ |
| |
| /* |
| * Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
| * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv> |
| * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
| * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
| * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
| * OF SUCH DAMAGE. |
| * |
| * This file is part of the lwIP TCP/IP stack. |
| * |
| */ |
| |
| #include "lwip/opt.h" |
| |
| #if LWIP_ARP || LWIP_ETHERNET |
| |
| #include "netif/ethernet.h" |
| #include "lwip/def.h" |
| #include "lwip/stats.h" |
| #include "lwip/etharp.h" |
| #include "lwip/ip.h" |
| #include "lwip/snmp.h" |
| |
| #include <string.h> |
| |
| #include "netif/ppp/ppp_opts.h" |
| #if PPPOE_SUPPORT |
| #include "netif/ppp/pppoe.h" |
| #endif /* PPPOE_SUPPORT */ |
| |
| #ifdef LWIP_HOOK_FILENAME |
| #include LWIP_HOOK_FILENAME |
| #endif |
| |
| const struct eth_addr ethbroadcast = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; |
| const struct eth_addr ethzero = {{0, 0, 0, 0, 0, 0}}; |
| |
| /** |
| * @ingroup lwip_nosys |
| * Process received ethernet frames. Using this function instead of directly |
| * calling ip_input and passing ARP frames through etharp in ethernetif_input, |
| * the ARP cache is protected from concurrent access.\n |
| * Don't call directly, pass to netif_add() and call netif->input(). |
| * |
| * @param p the received packet, p->payload pointing to the ethernet header |
| * @param netif the network interface on which the packet was received |
| * |
| * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL |
| * @see ETHARP_SUPPORT_VLAN |
| * @see LWIP_HOOK_VLAN_CHECK |
| */ |
| err_t |
| ethernet_input(struct pbuf *p, struct netif *netif) |
| { |
| struct eth_hdr *ethhdr; |
| u16_t type; |
| #if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 |
| u16_t next_hdr_offset = SIZEOF_ETH_HDR; |
| #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ |
| |
| LWIP_ASSERT_CORE_LOCKED(); |
| |
| if (p->len <= SIZEOF_ETH_HDR) { |
| /* a packet with only an ethernet header (or less) is not valid for us */ |
| ETHARP_STATS_INC(etharp.proterr); |
| ETHARP_STATS_INC(etharp.drop); |
| MIB2_STATS_NETIF_INC(netif, ifinerrors); |
| goto free_and_return; |
| } |
| |
| if (p->if_idx == NETIF_NO_INDEX) { |
| p->if_idx = netif_get_index(netif); |
| } |
| |
| /* points to packet payload, which starts with an Ethernet header */ |
| ethhdr = (struct eth_hdr *)p->payload; |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, |
| ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", |
| (unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2], |
| (unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5], |
| (unsigned char)ethhdr->src.addr[0], (unsigned char)ethhdr->src.addr[1], (unsigned char)ethhdr->src.addr[2], |
| (unsigned char)ethhdr->src.addr[3], (unsigned char)ethhdr->src.addr[4], (unsigned char)ethhdr->src.addr[5], |
| lwip_htons(ethhdr->type))); |
| |
| type = ethhdr->type; |
| #if ETHARP_SUPPORT_VLAN |
| if (type == PP_HTONS(ETHTYPE_VLAN)) { |
| struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR); |
| next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; |
| if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { |
| /* a packet with only an ethernet/vlan header (or less) is not valid for us */ |
| ETHARP_STATS_INC(etharp.proterr); |
| ETHARP_STATS_INC(etharp.drop); |
| MIB2_STATS_NETIF_INC(netif, ifinerrors); |
| goto free_and_return; |
| } |
| #if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ |
| #ifdef LWIP_HOOK_VLAN_CHECK |
| if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) { |
| #elif defined(ETHARP_VLAN_CHECK_FN) |
| if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { |
| #elif defined(ETHARP_VLAN_CHECK) |
| if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { |
| #endif |
| /* silently ignore this packet: not for our VLAN */ |
| pbuf_free(p); |
| return ERR_OK; |
| } |
| #endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ |
| type = vlan->tpid; |
| } |
| #endif /* ETHARP_SUPPORT_VLAN */ |
| |
| #if LWIP_ARP_FILTER_NETIF |
| netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type)); |
| #endif /* LWIP_ARP_FILTER_NETIF*/ |
| |
| if (ethhdr->dest.addr[0] & 1) { |
| /* this might be a multicast or broadcast packet */ |
| if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) { |
| #if LWIP_IPV4 |
| if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && |
| (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) { |
| /* mark the pbuf as link-layer multicast */ |
| p->flags |= PBUF_FLAG_LLMCAST; |
| } |
| #endif /* LWIP_IPV4 */ |
| } |
| #if LWIP_IPV6 |
| else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) && |
| (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) { |
| /* mark the pbuf as link-layer multicast */ |
| p->flags |= PBUF_FLAG_LLMCAST; |
| } |
| #endif /* LWIP_IPV6 */ |
| else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { |
| /* mark the pbuf as link-layer broadcast */ |
| p->flags |= PBUF_FLAG_LLBCAST; |
| } |
| } |
| |
| switch (type) { |
| #if LWIP_IPV4 && LWIP_ARP |
| /* IP packet? */ |
| case PP_HTONS(ETHTYPE_IP): |
| if (!(netif->flags & NETIF_FLAG_ETHARP)) { |
| goto free_and_return; |
| } |
| /* skip Ethernet header (min. size checked above) */ |
| if (pbuf_remove_header(p, next_hdr_offset)) { |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, |
| ("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n", |
| p->tot_len, next_hdr_offset)); |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); |
| goto free_and_return; |
| } else { |
| /* pass to IP layer */ |
| ip4_input(p, netif); |
| } |
| break; |
| |
| case PP_HTONS(ETHTYPE_ARP): |
| if (!(netif->flags & NETIF_FLAG_ETHARP)) { |
| goto free_and_return; |
| } |
| /* skip Ethernet header (min. size checked above) */ |
| if (pbuf_remove_header(p, next_hdr_offset)) { |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, |
| ("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n", |
| p->tot_len, next_hdr_offset)); |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); |
| ETHARP_STATS_INC(etharp.lenerr); |
| ETHARP_STATS_INC(etharp.drop); |
| goto free_and_return; |
| } else { |
| /* pass p to ARP module */ |
| etharp_input(p, netif); |
| } |
| break; |
| #endif /* LWIP_IPV4 && LWIP_ARP */ |
| #if PPPOE_SUPPORT |
| case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ |
| pppoe_disc_input(netif, p); |
| break; |
| |
| case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ |
| pppoe_data_input(netif, p); |
| break; |
| #endif /* PPPOE_SUPPORT */ |
| |
| #if LWIP_IPV6 |
| case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ |
| /* skip Ethernet header */ |
| if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, |
| ("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n", |
| p->tot_len, next_hdr_offset)); |
| goto free_and_return; |
| } else { |
| /* pass to IPv6 layer */ |
| ip6_input(p, netif); |
| } |
| break; |
| #endif /* LWIP_IPV6 */ |
| |
| default: |
| #ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL |
| if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { |
| break; |
| } |
| #endif |
| ETHARP_STATS_INC(etharp.proterr); |
| ETHARP_STATS_INC(etharp.drop); |
| MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); |
| goto free_and_return; |
| } |
| |
| /* This means the pbuf is freed or consumed, |
| so the caller doesn't have to free it again */ |
| return ERR_OK; |
| |
| free_and_return: |
| pbuf_free(p); |
| return ERR_OK; |
| } |
| |
| /** |
| * @ingroup ethernet |
| * Send an ethernet packet on the network using netif->linkoutput(). |
| * The ethernet header is filled in before sending. |
| * |
| * @see LWIP_HOOK_VLAN_SET |
| * |
| * @param netif the lwIP network interface on which to send the packet |
| * @param p the packet to send. pbuf layer must be @ref PBUF_LINK. |
| * @param src the source MAC address to be copied into the ethernet header |
| * @param dst the destination MAC address to be copied into the ethernet header |
| * @param eth_type ethernet type (@ref lwip_ieee_eth_type) |
| * @return ERR_OK if the packet was sent, any other err_t on failure |
| */ |
| err_t |
| ethernet_output(struct netif * netif, struct pbuf * p, |
| const struct eth_addr * src, const struct eth_addr * dst, |
| u16_t eth_type) { |
| struct eth_hdr *ethhdr; |
| u16_t eth_type_be = lwip_htons(eth_type); |
| |
| #if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) |
| s32_t vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type); |
| if (vlan_prio_vid >= 0) { |
| struct eth_vlan_hdr *vlanhdr; |
| |
| LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF); |
| |
| if (pbuf_add_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) { |
| goto pbuf_header_failed; |
| } |
| vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR); |
| vlanhdr->tpid = eth_type_be; |
| vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid); |
| |
| eth_type_be = PP_HTONS(ETHTYPE_VLAN); |
| } else |
| #endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ |
| { |
| if (pbuf_add_header(p, SIZEOF_ETH_HDR) != 0) { |
| goto pbuf_header_failed; |
| } |
| } |
| |
| LWIP_ASSERT_CORE_LOCKED(); |
| |
| ethhdr = (struct eth_hdr *)p->payload; |
| ethhdr->type = eth_type_be; |
| SMEMCPY(ðhdr->dest, dst, ETH_HWADDR_LEN); |
| SMEMCPY(ðhdr->src, src, ETH_HWADDR_LEN); |
| |
| LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!", |
| (netif->hwaddr_len == ETH_HWADDR_LEN)); |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, |
| ("ethernet_output: sending packet %p\n", (void *)p)); |
| |
| /* send the packet */ |
| return netif->linkoutput(netif, p); |
| |
| pbuf_header_failed: |
| LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, |
| ("ethernet_output: could not allocate room for header.\n")); |
| LINK_STATS_INC(link.lenerr); |
| return ERR_BUF; |
| } |
| |
| #endif /* LWIP_ARP || LWIP_ETHERNET */ |