/* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2010,2011 Free Software Foundation, Inc. * * VasEBoot is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VasEBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VasEBoot. If not, see . */ #include #include #include #include #include #include #include #include #include #include struct iphdr { VasEBoot_uint8_t verhdrlen; VasEBoot_uint8_t service; VasEBoot_uint16_t len; VasEBoot_uint16_t ident; VasEBoot_uint16_t frags; VasEBoot_uint8_t ttl; VasEBoot_uint8_t protocol; VasEBoot_uint16_t chksum; VasEBoot_uint32_t src; VasEBoot_uint32_t dest; } VasEBoot_PACKED ; enum { DONT_FRAGMENT = 0x4000, MORE_FRAGMENTS = 0x2000, OFFSET_MASK = 0x1fff }; typedef VasEBoot_uint64_t ip6addr[2]; struct ip6hdr { VasEBoot_uint32_t version_class_flow; VasEBoot_uint16_t len; VasEBoot_uint8_t protocol; VasEBoot_uint8_t ttl; ip6addr src; ip6addr dest; } VasEBoot_PACKED ; static int cmp (const void *a__, const void *b__) { struct VasEBoot_net_buff *a_ = *(struct VasEBoot_net_buff **) a__; struct VasEBoot_net_buff *b_ = *(struct VasEBoot_net_buff **) b__; struct iphdr *a = (struct iphdr *) a_->data; struct iphdr *b = (struct iphdr *) b_->data; /* We want the first elements to be on top. */ if ((VasEBoot_be_to_cpu16 (a->frags) & OFFSET_MASK) < (VasEBoot_be_to_cpu16 (b->frags) & OFFSET_MASK)) return +1; if ((VasEBoot_be_to_cpu16 (a->frags) & OFFSET_MASK) > (VasEBoot_be_to_cpu16 (b->frags) & OFFSET_MASK)) return -1; return 0; } struct reassemble { struct reassemble *next; VasEBoot_uint32_t source; VasEBoot_uint32_t dest; VasEBoot_uint16_t id; VasEBoot_uint8_t proto; VasEBoot_uint64_t last_time; VasEBoot_priority_queue_t pq; struct VasEBoot_net_buff *asm_netbuff; VasEBoot_size_t total_len; VasEBoot_size_t cur_ptr; VasEBoot_uint8_t ttl; }; static struct reassemble *reassembles; VasEBoot_uint16_t VasEBoot_net_ip_chksum (void *ipv, VasEBoot_size_t len) { VasEBoot_uint16_t *ip = (VasEBoot_uint16_t *) ipv; VasEBoot_uint32_t sum = 0; for (; len >= 2; len -= 2) { sum += VasEBoot_be_to_cpu16 (VasEBoot_get_unaligned16 (ip++)); if (sum > 0xFFFF) sum -= 0xFFFF; } if (len) { sum += *((VasEBoot_uint8_t *) ip) << 8; if (sum > 0xFFFF) sum -= 0xFFFF; } if (sum >= 0xFFFF) sum -= 0xFFFF; return VasEBoot_cpu_to_be16 ((~sum) & 0x0000FFFF); } static int id = 0x2400; static VasEBoot_err_t send_fragmented (struct VasEBoot_net_network_level_interface * inf, const VasEBoot_net_network_level_address_t * target, struct VasEBoot_net_buff * nb, VasEBoot_net_ip_protocol_t proto, VasEBoot_net_link_level_address_t ll_target_addr) { VasEBoot_size_t off = 0; VasEBoot_size_t fraglen; VasEBoot_err_t err; fraglen = (inf->card->mtu - sizeof (struct iphdr)) & ~7; id++; while (nb->tail - nb->data) { VasEBoot_size_t len = fraglen; struct VasEBoot_net_buff *nb2; struct iphdr *iph; if ((VasEBoot_ssize_t) len > nb->tail - nb->data) len = nb->tail - nb->data; nb2 = VasEBoot_netbuff_alloc (fraglen + sizeof (struct iphdr) + VasEBoot_NET_MAX_LINK_HEADER_SIZE); if (!nb2) return VasEBoot_errno; err = VasEBoot_netbuff_reserve (nb2, VasEBoot_NET_MAX_LINK_HEADER_SIZE); if (err) return err; err = VasEBoot_netbuff_put (nb2, sizeof (struct iphdr)); if (err) return err; iph = (struct iphdr *) nb2->data; iph->verhdrlen = ((4 << 4) | 5); iph->service = 0; iph->len = VasEBoot_cpu_to_be16 (len + sizeof (struct iphdr)); iph->ident = VasEBoot_cpu_to_be16 (id); iph->frags = VasEBoot_cpu_to_be16 (off | (((VasEBoot_ssize_t) len == nb->tail - nb->data) ? 0 : MORE_FRAGMENTS)); iph->ttl = 0xff; iph->protocol = proto; iph->src = inf->address.ipv4; iph->dest = target->ipv4; off += len / 8; iph->chksum = 0; iph->chksum = VasEBoot_net_ip_chksum ((void *) nb2->data, sizeof (*iph)); err = VasEBoot_netbuff_put (nb2, len); if (err) return err; VasEBoot_memcpy (iph + 1, nb->data, len); err = VasEBoot_netbuff_pull (nb, len); if (err) return err; err = send_ethernet_packet (inf, nb2, ll_target_addr, VasEBoot_NET_ETHERTYPE_IP); if (err) return err; } return VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_net_send_ip4_packet (struct VasEBoot_net_network_level_interface *inf, const VasEBoot_net_network_level_address_t *target, const VasEBoot_net_link_level_address_t *ll_target_addr, struct VasEBoot_net_buff *nb, VasEBoot_net_ip_protocol_t proto) { struct iphdr *iph; VasEBoot_err_t err; COMPILE_TIME_ASSERT (VasEBoot_NET_OUR_IPV4_HEADER_SIZE == sizeof (*iph)); if (nb->tail - nb->data + sizeof (struct iphdr) > inf->card->mtu) return send_fragmented (inf, target, nb, proto, *ll_target_addr); err = VasEBoot_netbuff_push (nb, sizeof (*iph)); if (err) return err; iph = (struct iphdr *) nb->data; iph->verhdrlen = ((4 << 4) | 5); iph->service = 0; iph->len = VasEBoot_cpu_to_be16 (nb->tail - nb->data); iph->ident = VasEBoot_cpu_to_be16 (++id); iph->frags = 0; iph->ttl = 0xff; iph->protocol = proto; iph->src = inf->address.ipv4; iph->dest = target->ipv4; iph->chksum = 0; iph->chksum = VasEBoot_net_ip_chksum ((void *) nb->data, sizeof (*iph)); return send_ethernet_packet (inf, nb, *ll_target_addr, VasEBoot_NET_ETHERTYPE_IP); } static VasEBoot_err_t handle_dgram (struct VasEBoot_net_buff *nb, struct VasEBoot_net_card *card, const VasEBoot_net_link_level_address_t *source_hwaddress, const VasEBoot_net_link_level_address_t *hwaddress, VasEBoot_net_ip_protocol_t proto, const VasEBoot_net_network_level_address_t *source, const VasEBoot_net_network_level_address_t *dest, VasEBoot_uint8_t ttl) { struct VasEBoot_net_network_level_interface *inf = NULL; VasEBoot_err_t err; int multicast = 0; /* DHCP needs special treatment since we don't know IP yet. */ { struct udphdr *udph; udph = (struct udphdr *) nb->data; if (proto == VasEBoot_NET_IP_UDP && VasEBoot_be_to_cpu16 (udph->dst) == 68) { const struct VasEBoot_net_bootp_packet *bootp; if (udph->chksum) { VasEBoot_uint16_t chk, expected; chk = udph->chksum; udph->chksum = 0; expected = VasEBoot_net_ip_transport_checksum (nb, VasEBoot_NET_IP_UDP, source, dest); if (expected != chk) { VasEBoot_dprintf ("net", "Invalid UDP checksum. " "Expected %x, got %x\n", VasEBoot_be_to_cpu16 (expected), VasEBoot_be_to_cpu16 (chk)); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } udph->chksum = chk; } err = VasEBoot_netbuff_pull (nb, sizeof (*udph)); if (err) { VasEBoot_netbuff_free (nb); return err; } bootp = (const struct VasEBoot_net_bootp_packet *) nb->data; FOR_NET_NETWORK_LEVEL_INTERFACES (inf) if (inf->card == card && inf->address.type == VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV && inf->hwaddress.type == VasEBoot_NET_LINK_LEVEL_PROTOCOL_ETHERNET && VasEBoot_memcmp (inf->hwaddress.mac, &bootp->mac_addr, sizeof (inf->hwaddress.mac)) == 0) { VasEBoot_net_process_dhcp (nb, inf->card); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } } FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { if (inf->card == card && VasEBoot_net_addr_cmp (&inf->address, dest) == 0 && VasEBoot_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) break; /* Solicited node multicast. */ if (inf->card == card && inf->address.type == VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV6 && dest->type == VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV6 && dest->ipv6[0] == VasEBoot_be_to_cpu64_compile_time (0xff02ULL << 48) && dest->ipv6[1] == (VasEBoot_be_to_cpu64_compile_time (0x01ff000000ULL) | (inf->address.ipv6[1] & VasEBoot_be_to_cpu64_compile_time (0xffffff))) && hwaddress->type == VasEBoot_NET_LINK_LEVEL_PROTOCOL_ETHERNET && hwaddress->mac[0] == 0x33 && hwaddress->mac[1] == 0x33 && hwaddress->mac[2] == 0xff && hwaddress->mac[3] == ((VasEBoot_be_to_cpu64 (inf->address.ipv6[1]) >> 16) & 0xff) && hwaddress->mac[4] == ((VasEBoot_be_to_cpu64 (inf->address.ipv6[1]) >> 8) & 0xff) && hwaddress->mac[5] == ((VasEBoot_be_to_cpu64 (inf->address.ipv6[1]) >> 0) & 0xff)) { multicast = 1; break; } } if (!inf && !(dest->type == VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV6 && dest->ipv6[0] == VasEBoot_be_to_cpu64_compile_time (0xff02ULL << 48) && dest->ipv6[1] == VasEBoot_be_to_cpu64_compile_time (1))) { VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } if (multicast) inf = NULL; switch (proto) { case VasEBoot_NET_IP_UDP: return VasEBoot_net_recv_udp_packet (nb, inf, source); case VasEBoot_NET_IP_TCP: return VasEBoot_net_recv_tcp_packet (nb, inf, source); case VasEBoot_NET_IP_ICMP: return VasEBoot_net_recv_icmp_packet (nb, inf, source_hwaddress, source); case VasEBoot_NET_IP_ICMPV6: return VasEBoot_net_recv_icmp6_packet (nb, card, inf, source_hwaddress, source, dest, ttl); default: VasEBoot_netbuff_free (nb); break; } return VasEBoot_ERR_NONE; } static void free_rsm (struct reassemble *rsm) { struct VasEBoot_net_buff **nb; while ((nb = VasEBoot_priority_queue_top (rsm->pq))) { VasEBoot_netbuff_free (*nb); VasEBoot_priority_queue_pop (rsm->pq); } VasEBoot_netbuff_free (rsm->asm_netbuff); VasEBoot_priority_queue_destroy (rsm->pq); VasEBoot_free (rsm); } static void free_old_fragments (void) { struct reassemble *rsm, **prev; VasEBoot_uint64_t limit_time = VasEBoot_get_time_ms (); limit_time = (limit_time > 90000) ? limit_time - 90000 : 0; for (prev = &reassembles, rsm = *prev; rsm; rsm = *prev) if (rsm->last_time < limit_time) { *prev = rsm->next; free_rsm (rsm); } else { prev = &rsm->next; } } static VasEBoot_err_t VasEBoot_net_recv_ip4_packets (struct VasEBoot_net_buff *nb, struct VasEBoot_net_card *card, const VasEBoot_net_link_level_address_t *hwaddress, const VasEBoot_net_link_level_address_t *src_hwaddress) { struct iphdr *iph = (struct iphdr *) nb->data; VasEBoot_err_t err; struct reassemble *rsm, **prev; if ((iph->verhdrlen >> 4) != 4) { VasEBoot_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } if ((iph->verhdrlen & 0xf) < 5) { VasEBoot_dprintf ("net", "IP header too short: %d\n", (iph->verhdrlen & 0xf)); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } if (nb->tail - nb->data < (VasEBoot_ssize_t) ((iph->verhdrlen & 0xf) * sizeof (VasEBoot_uint32_t))) { VasEBoot_dprintf ("net", "IP packet too short: %" PRIdVasEBoot_SSIZE "\n", (VasEBoot_ssize_t) (nb->tail - nb->data)); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } /* Check size. */ { VasEBoot_size_t expected_size = VasEBoot_be_to_cpu16 (iph->len); VasEBoot_size_t actual_size = (nb->tail - nb->data); if (actual_size > expected_size) { err = VasEBoot_netbuff_unput (nb, actual_size - expected_size); if (err) { VasEBoot_netbuff_free (nb); return err; } } if (actual_size < expected_size) { VasEBoot_dprintf ("net", "Cut IP packet actual: %" PRIuVasEBoot_SIZE ", expected %" PRIuVasEBoot_SIZE "\n", actual_size, expected_size); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } } /* Unfragmented packet. Good. */ if (((VasEBoot_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS) == 0) && (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK) == 0) { VasEBoot_net_network_level_address_t source; VasEBoot_net_network_level_address_t dest; err = VasEBoot_netbuff_pull (nb, ((iph->verhdrlen & 0xf) * sizeof (VasEBoot_uint32_t))); if (err) { VasEBoot_netbuff_free (nb); return err; } source.type = VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV4; source.ipv4 = iph->src; dest.type = VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV4; dest.ipv4 = iph->dest; return handle_dgram (nb, card, src_hwaddress, hwaddress, iph->protocol, &source, &dest, iph->ttl); } for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev) if (rsm->source == iph->src && rsm->dest == iph->dest && rsm->id == iph->ident && rsm->proto == iph->protocol) break; if (!rsm) { rsm = VasEBoot_malloc (sizeof (*rsm)); if (!rsm) return VasEBoot_errno; rsm->source = iph->src; rsm->dest = iph->dest; rsm->id = iph->ident; rsm->proto = iph->protocol; rsm->next = reassembles; reassembles = rsm; prev = &reassembles; rsm->pq = VasEBoot_priority_queue_new (sizeof (struct VasEBoot_net_buff **), cmp); if (!rsm->pq) { VasEBoot_free (rsm); return VasEBoot_errno; } rsm->asm_netbuff = 0; rsm->total_len = 0; rsm->cur_ptr = 0; rsm->ttl = 0xff; } if (rsm->ttl > iph->ttl) rsm->ttl = iph->ttl; rsm->last_time = VasEBoot_get_time_ms (); free_old_fragments (); err = VasEBoot_priority_queue_push (rsm->pq, &nb); if (err) return err; if (!(VasEBoot_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS)) { rsm->total_len = (8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK) + (nb->tail - nb->data)); rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (VasEBoot_uint32_t)); rsm->asm_netbuff = VasEBoot_netbuff_alloc (rsm->total_len); if (!rsm->asm_netbuff) { *prev = rsm->next; free_rsm (rsm); return VasEBoot_errno; } } if (!rsm->asm_netbuff) return VasEBoot_ERR_NONE; while (1) { struct VasEBoot_net_buff **nb_top_p, *nb_top; VasEBoot_size_t copy; VasEBoot_size_t res_len; struct VasEBoot_net_buff *ret; VasEBoot_net_ip_protocol_t proto; VasEBoot_uint32_t src; VasEBoot_uint32_t dst; VasEBoot_net_network_level_address_t source; VasEBoot_net_network_level_address_t dest; VasEBoot_uint8_t ttl; nb_top_p = VasEBoot_priority_queue_top (rsm->pq); if (!nb_top_p) return VasEBoot_ERR_NONE; nb_top = *nb_top_p; VasEBoot_priority_queue_pop (rsm->pq); iph = (struct iphdr *) nb_top->data; err = VasEBoot_netbuff_pull (nb_top, ((iph->verhdrlen & 0xf) * sizeof (VasEBoot_uint32_t))); if (err) { VasEBoot_netbuff_free (nb_top); return err; } if (rsm->cur_ptr < (VasEBoot_size_t) 8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK)) { VasEBoot_netbuff_free (nb_top); return VasEBoot_ERR_NONE; } rsm->cur_ptr = (8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK) + (nb_top->tail - nb_top->head)); if ((VasEBoot_size_t) 8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK) >= rsm->total_len) { VasEBoot_netbuff_free (nb_top); continue; } copy = nb_top->tail - nb_top->data; if (rsm->total_len - 8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK) < copy) copy = rsm->total_len - 8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK); VasEBoot_memcpy (&rsm->asm_netbuff->data[8 * (VasEBoot_be_to_cpu16 (iph->frags) & OFFSET_MASK)], nb_top->data, copy); if ((VasEBoot_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS)) { VasEBoot_netbuff_free (nb_top); continue; } VasEBoot_netbuff_free (nb_top); ret = rsm->asm_netbuff; proto = rsm->proto; src = rsm->source; dst = rsm->dest; ttl = rsm->ttl; rsm->asm_netbuff = 0; res_len = rsm->total_len; *prev = rsm->next; free_rsm (rsm); if (VasEBoot_netbuff_put (ret, res_len)) { VasEBoot_netbuff_free (ret); return VasEBoot_ERR_NONE; } source.type = VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV4; source.ipv4 = src; dest.type = VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV4; dest.ipv4 = dst; return handle_dgram (ret, card, src_hwaddress, hwaddress, proto, &source, &dest, ttl); } } static VasEBoot_err_t VasEBoot_net_send_ip6_packet (struct VasEBoot_net_network_level_interface *inf, const VasEBoot_net_network_level_address_t *target, const VasEBoot_net_link_level_address_t *ll_target_addr, struct VasEBoot_net_buff *nb, VasEBoot_net_ip_protocol_t proto) { struct ip6hdr *iph; VasEBoot_err_t err; COMPILE_TIME_ASSERT (VasEBoot_NET_OUR_IPV6_HEADER_SIZE == sizeof (*iph)); if (nb->tail - nb->data + sizeof (struct iphdr) > inf->card->mtu) return VasEBoot_error (VasEBoot_ERR_NET_PACKET_TOO_BIG, "packet too big"); err = VasEBoot_netbuff_push (nb, sizeof (*iph)); if (err) return err; iph = (struct ip6hdr *) nb->data; iph->version_class_flow = VasEBoot_cpu_to_be32_compile_time ((6 << 28)); iph->len = VasEBoot_cpu_to_be16 (nb->tail - nb->data - sizeof (*iph)); iph->protocol = proto; iph->ttl = 0xff; VasEBoot_memcpy (&iph->src, inf->address.ipv6, sizeof (iph->src)); VasEBoot_memcpy (&iph->dest, target->ipv6, sizeof (iph->dest)); return send_ethernet_packet (inf, nb, *ll_target_addr, VasEBoot_NET_ETHERTYPE_IP6); } VasEBoot_err_t VasEBoot_net_send_ip_packet (struct VasEBoot_net_network_level_interface *inf, const VasEBoot_net_network_level_address_t *target, const VasEBoot_net_link_level_address_t *ll_target_addr, struct VasEBoot_net_buff *nb, VasEBoot_net_ip_protocol_t proto) { switch (target->type) { case VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV4: return VasEBoot_net_send_ip4_packet (inf, target, ll_target_addr, nb, proto); case VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV6: return VasEBoot_net_send_ip6_packet (inf, target, ll_target_addr, nb, proto); default: return VasEBoot_error (VasEBoot_ERR_BUG, "not an IP"); } } static VasEBoot_err_t VasEBoot_net_recv_ip6_packets (struct VasEBoot_net_buff *nb, struct VasEBoot_net_card *card, const VasEBoot_net_link_level_address_t *hwaddress, const VasEBoot_net_link_level_address_t *src_hwaddress) { struct ip6hdr *iph = (struct ip6hdr *) nb->data; VasEBoot_err_t err; VasEBoot_net_network_level_address_t source; VasEBoot_net_network_level_address_t dest; if (nb->tail - nb->data < (VasEBoot_ssize_t) sizeof (*iph)) { VasEBoot_dprintf ("net", "IP packet too short: %" PRIdVasEBoot_SSIZE "\n", (VasEBoot_ssize_t) (nb->tail - nb->data)); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } err = VasEBoot_netbuff_pull (nb, sizeof (*iph)); if (err) { VasEBoot_netbuff_free (nb); return err; } /* Check size. */ { VasEBoot_size_t expected_size = VasEBoot_be_to_cpu16 (iph->len); VasEBoot_size_t actual_size = (nb->tail - nb->data); if (actual_size > expected_size) { err = VasEBoot_netbuff_unput (nb, actual_size - expected_size); if (err) { VasEBoot_netbuff_free (nb); return err; } } if (actual_size < expected_size) { VasEBoot_dprintf ("net", "Cut IP packet actual: %" PRIuVasEBoot_SIZE ", expected %" PRIuVasEBoot_SIZE "\n", actual_size, expected_size); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; } } source.type = VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV6; dest.type = VasEBoot_NET_NETWORK_LEVEL_PROTOCOL_IPV6; VasEBoot_memcpy (source.ipv6, &iph->src, sizeof (source.ipv6)); VasEBoot_memcpy (dest.ipv6, &iph->dest, sizeof (dest.ipv6)); return handle_dgram (nb, card, src_hwaddress, hwaddress, iph->protocol, &source, &dest, iph->ttl); } VasEBoot_err_t VasEBoot_net_recv_ip_packets (struct VasEBoot_net_buff *nb, struct VasEBoot_net_card *card, const VasEBoot_net_link_level_address_t *hwaddress, const VasEBoot_net_link_level_address_t *src_hwaddress) { struct iphdr *iph = (struct iphdr *) nb->data; if ((iph->verhdrlen >> 4) == 4) return VasEBoot_net_recv_ip4_packets (nb, card, hwaddress, src_hwaddress); if ((iph->verhdrlen >> 4) == 6) return VasEBoot_net_recv_ip6_packets (nb, card, hwaddress, src_hwaddress); VasEBoot_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); VasEBoot_netbuff_free (nb); return VasEBoot_ERR_NONE; }