From 9aad3cd91d8e2bac585cf77c71aaaa76e53d49fe Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 22:15:30 +0200 Subject: [PATCH] IPv6 support. Several fixes and unifications --- grub-core/net/arp.c | 142 +++++++++++++++++++----- grub-core/net/bootp.c | 40 +++---- grub-core/net/ethernet.c | 20 ++-- grub-core/net/ip.c | 208 +++++++++++++++++++++++++++++------- grub-core/net/net.c | 109 ++++++++++++++++++- grub-core/net/tcp.c | 53 ++++++--- grub-core/net/tftp.c | 5 +- include/grub/err.h | 3 +- include/grub/net.h | 20 +++- include/grub/net/arp.h | 24 ----- include/grub/net/ethernet.h | 13 +-- include/grub/net/ip.h | 10 +- include/grub/types.h | 14 +-- 13 files changed, 505 insertions(+), 156 deletions(-) diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index b45bd4c9f..b1b719dbe 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -1,3 +1,21 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 Free Software Foundation, Inc. + * + * GRUB 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. + * + * GRUB 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 GRUB. If not, see . + */ + #include #include #include @@ -6,6 +24,34 @@ #include #include +/* ARP header operation codes */ +enum + { + ARP_REQUEST = 1, + ARP_REPLY = 2 + }; + +enum + { + /* IANA ARP constant to define hardware type as ethernet. */ + GRUB_NET_ARPHRD_ETHERNET = 1 + }; + +struct arphdr { + grub_uint16_t hrd; + grub_uint16_t pro; + grub_uint8_t hln; + grub_uint8_t pln; + grub_uint16_t op; +} __attribute__ ((packed)); + + +struct arp_entry { + int avail; + grub_net_network_level_address_t nl_address; + grub_net_link_level_address_t ll_address; +}; + static struct arp_entry arp_table[10]; static grub_int8_t new_table_entry = -1; @@ -23,7 +69,7 @@ arp_find_entry (const grub_net_network_level_address_t *proto) for (i = 0; i < ARRAY_SIZE (arp_table); i++) { if (arp_table[i].avail == 1 && - arp_table[i].nl_address.ipv4 == proto->ipv4) + grub_net_addr_cmp (&arp_table[i].nl_address, proto) == 0) return &(arp_table[i]); } return NULL; @@ -41,6 +87,8 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, grub_uint8_t *aux, arp_data[128]; grub_err_t err; int i; + grub_size_t addrlen; + grub_uint16_t etherpro; if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 && proto_addr->ipv4 == 0xffffffff) @@ -57,22 +105,35 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, *hw_addr = entry->ll_address; return GRUB_ERR_NONE; } + + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + { + addrlen = 4; + etherpro = GRUB_NET_ETHERTYPE_IP; + } + else if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) + { + addrlen = 16; + etherpro = GRUB_NET_ETHERTYPE_IP6; + } + else + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "unsupported address family"); + /* Build a request packet. */ nb.head = arp_data; nb.end = arp_data + sizeof (arp_data); grub_netbuff_clear (&nb); grub_netbuff_reserve (&nb, 128); - err = grub_netbuff_push (&nb, sizeof (*arp_header) + 2 * (6 + 4)); + err = grub_netbuff_push (&nb, sizeof (*arp_header) + 2 * (6 + addrlen)); if (err) return err; arp_header = (struct arphdr *) nb.data; arp_header->hrd = grub_cpu_to_be16 (GRUB_NET_ARPHRD_ETHERNET); - arp_header->pro = grub_cpu_to_be16 (GRUB_NET_ETHERTYPE_IP); - /* FIXME Add support to ipv6 address. */ arp_header->hln = 6; - arp_header->pln = 4; + arp_header->pro = grub_cpu_to_be16 (etherpro); + arp_header->pln = addrlen; arp_header->op = grub_cpu_to_be16 (ARP_REQUEST); aux = (grub_uint8_t *) arp_header + sizeof (*arp_header); /* Sender hardware address. */ @@ -80,18 +141,24 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, aux += 6; /* Sender protocol address */ - grub_memcpy (aux, &inf->address.ipv4, 4); - aux += 4; + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + grub_memcpy (aux, &inf->address.ipv4, 4); + else + grub_memcpy (aux, &inf->address.ipv6, 16); + aux += addrlen; /* Target hardware address */ for (i = 0; i < 6; i++) aux[i] = 0x00; aux += 6; /* Target protocol address */ - grub_memcpy (aux, &proto_addr->ipv4, 4); + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + grub_memcpy (aux, &proto_addr->ipv4, 4); + else + grub_memcpy (aux, &proto_addr->ipv6, 16); grub_memset (&target_hw_addr.mac, 0xff, 6); send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); - for (i = 0; i < 3; i++) + for (i = 0; i < GRUB_NET_TRIES; i++) { entry = arp_find_entry (proto_addr); if (entry) @@ -99,7 +166,7 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, grub_memcpy (hw_addr, &entry->ll_address, sizeof (*hw_addr)); return GRUB_ERR_NONE; } - grub_net_poll_cards (200); + grub_net_poll_cards (GRUB_NET_INTERVAL); } return grub_error (GRUB_ERR_TIMEOUT, "timeout: could not resolve hardware address"); @@ -110,20 +177,38 @@ grub_net_arp_receive (struct grub_net_buff *nb) { struct arphdr *arp_header = (struct arphdr *) nb->data; struct arp_entry *entry; - grub_uint8_t *sender_hardware_address, *sender_protocol_address; - grub_uint8_t *target_hardware_address, *target_protocol_address; - grub_net_network_level_address_t hwaddress; + grub_uint8_t *sender_hardware_address; + grub_uint8_t *target_hardware_address; + grub_net_network_level_address_t sender_addr, target_addr; struct grub_net_network_level_interface *inf; + grub_uint8_t *sender_protocol_address, *target_protocol_address; sender_hardware_address = (grub_uint8_t *) arp_header + sizeof (*arp_header); sender_protocol_address = sender_hardware_address + arp_header->hln; target_hardware_address = sender_protocol_address + arp_header->pln; target_protocol_address = target_hardware_address + arp_header->hln; - grub_memcpy (&hwaddress.ipv4, sender_protocol_address, 4); - + if (grub_be_to_cpu16 (arp_header->pro) == GRUB_NET_ETHERTYPE_IP + && arp_header->pln == 4) + { + sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&sender_addr.ipv4, sender_protocol_address, 4); + grub_memcpy (&target_addr.ipv4, target_protocol_address, 4); + } + else if (grub_be_to_cpu16 (arp_header->pro) == GRUB_NET_ETHERTYPE_IP6 + && arp_header->pln == 16) + { + sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&sender_addr.ipv6, sender_protocol_address, 16); + grub_memcpy (&target_addr.ipv6, target_protocol_address, 16); + } + else + return GRUB_ERR_NONE; + /* Check if the sender is in the cache table. */ - entry = arp_find_entry (&hwaddress); + entry = arp_find_entry (&sender_addr); /* Update sender hardware address. */ if (entry) grub_memcpy (entry->ll_address.mac, sender_hardware_address, 6); @@ -134,7 +219,7 @@ grub_net_arp_receive (struct grub_net_buff *nb) arp_init_table (); entry = &(arp_table[new_table_entry]); entry->avail = 1; - grub_memcpy (&entry->nl_address.ipv4, sender_protocol_address, 4); + entry->nl_address = sender_addr; grub_memcpy (entry->ll_address.mac, sender_hardware_address, 6); new_table_entry++; if (new_table_entry == ARRAY_SIZE (arp_table)) @@ -144,23 +229,26 @@ grub_net_arp_receive (struct grub_net_buff *nb) FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { /* Am I the protocol address target? */ - if (inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && grub_memcmp (target_protocol_address, &inf->address.ipv4, 4) == 0 + if (grub_net_addr_cmp (&inf->address, &target_addr) == 0 && grub_be_to_cpu16 (arp_header->op) == ARP_REQUEST) { - grub_net_link_level_address_t aux; - /* Swap hardware fields */ - grub_memcpy (target_hardware_address, sender_hardware_address, - arp_header->hln); + grub_net_link_level_address_t target; + /* We've already checked that pln is either 4 or 16. */ + char tmp[arp_header->pln]; + + target.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (target.mac, sender_hardware_address, 6); + grub_memcpy (target_hardware_address, target.mac, 6); grub_memcpy (sender_hardware_address, inf->hwaddress.mac, 6); - grub_memcpy (aux.mac, sender_protocol_address, 6); + + grub_memcpy (tmp, sender_protocol_address, arp_header->pln); grub_memcpy (sender_protocol_address, target_protocol_address, arp_header->pln); - grub_memcpy (target_protocol_address, aux.mac, arp_header->pln); + grub_memcpy (target_protocol_address, tmp, arp_header->pln); + /* Change operation to REPLY and send packet */ arp_header->op = grub_be_to_cpu16 (ARP_REPLY); - grub_memcpy (aux.mac, target_hardware_address, 6); - send_ethernet_packet (inf, nb, aux, GRUB_NET_ETHERTYPE_ARP); + send_ethernet_packet (inf, nb, target, GRUB_NET_ETHERTYPE_ARP); } } return GRUB_ERR_NONE; diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index b1db05147..3187f2e9c 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -131,26 +131,28 @@ grub_net_configure_by_dhcp_ack (const char *name, hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; inter = grub_net_add_addr (name, card, addr, hwaddr, flags); - { - grub_net_network_level_netaddress_t target; - grub_net_network_level_address_t gw; - char rname[grub_strlen (name) + sizeof ("_gw")]; + if (bp->gateway_ip) + { + grub_net_network_level_netaddress_t target; + grub_net_network_level_address_t gw; + char rname[grub_strlen (name) + sizeof (":gw")]; - target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base = bp->server_ip; - target.ipv4.masksize = 32; - gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - gw.ipv4 = bp->gateway_ip; - grub_snprintf (rname, sizeof (rname), "%s_gw", name); - grub_net_add_route_gw (rname, target, gw); - } - { - grub_net_network_level_netaddress_t target; - target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base = bp->gateway_ip; - target.ipv4.masksize = 32; - grub_net_add_route (name, target, inter); - } + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base = bp->server_ip; + target.ipv4.masksize = 32; + gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + gw.ipv4 = bp->gateway_ip; + grub_snprintf (rname, sizeof (rname), "%s:gw", name); + grub_net_add_route_gw (rname, target, gw); + } + if (bp->gateway_ip || bp->server_ip) + { + grub_net_network_level_netaddress_t target; + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base = bp->gateway_ip ? bp->gateway_ip : bp->server_ip; + target.ipv4.masksize = 32; + grub_net_add_route (name, target, inter); + } if (size > OFFSET_OF (boot_file, bp)) set_env_limn_ro (name, "boot_file", (char *) bp->boot_file, diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index c368eda54..053f87b56 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -52,7 +52,7 @@ grub_err_t send_ethernet_packet (struct grub_net_network_level_interface *inf, struct grub_net_buff *nb, grub_net_link_level_address_t target_addr, - grub_uint16_t ethertype) + grub_net_ethertype_t ethertype) { struct etherhdr *eth; grub_err_t err; @@ -86,7 +86,7 @@ grub_net_recv_ethernet_packet (struct grub_net_buff * nb, struct etherhdr *eth; struct llchdr *llch; struct snaphdr *snaph; - grub_uint16_t type; + grub_net_ethertype_t type; grub_net_link_level_address_t hwaddress; grub_err_t err; @@ -114,17 +114,19 @@ grub_net_recv_ethernet_packet (struct grub_net_buff * nb, hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; grub_memcpy (hwaddress.mac, eth->dst, sizeof (hwaddress.mac)); - /* ARP packet. */ - if (type == GRUB_NET_ETHERTYPE_ARP) + switch (type) { + /* ARP packet. */ + case GRUB_NET_ETHERTYPE_ARP: grub_net_arp_receive (nb); grub_netbuff_free (nb); return GRUB_ERR_NONE; - } - /* IP packet. */ - if (type == GRUB_NET_ETHERTYPE_IP) - { - grub_net_recv_ip_packets (nb, card, &hwaddress); + /* IP packet. */ + case GRUB_NET_ETHERTYPE_IP: + grub_net_recv_ip4_packets (nb, card, &hwaddress); + return GRUB_ERR_NONE; + case GRUB_NET_ETHERTYPE_IP6: + grub_net_recv_ip4_packets (nb, card, &hwaddress); return GRUB_ERR_NONE; } grub_netbuff_free (nb); diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 69579970f..32a7d7fe7 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -47,16 +47,16 @@ enum OFFSET_MASK = 0x1fff }; -struct ip6hdr -{ - grub_uint8_t version:4, priority:4; - grub_uint8_t flow_lbl[3]; - grub_uint16_t payload_len; - grub_uint8_t nexthdr; - grub_uint8_t hop_limit; - grub_uint8_t saddr[16]; - grub_uint8_t daddr[16]; -} __attribute__ ((packed)); +typedef grub_uint64_t ip6addr[2]; + +struct ip6hdr { + grub_uint32_t version_class_flow; + grub_uint16_t len; + grub_uint8_t protocol; + grub_uint8_t ttl; + ip6addr src; + ip6addr dest; +} __attribute__ ((packed)) ; static int cmp (const void *a__, const void *b__) @@ -179,11 +179,11 @@ send_fragmented (struct grub_net_network_level_interface * inf, return GRUB_ERR_NONE; } -grub_err_t -grub_net_send_ip_packet (struct grub_net_network_level_interface * inf, - const grub_net_network_level_address_t * target, - struct grub_net_buff * nb, - grub_net_ip_protocol_t proto) +static grub_err_t +grub_net_send_ip4_packet (struct grub_net_network_level_interface * inf, + const grub_net_network_level_address_t * target, + struct grub_net_buff * nb, + grub_net_ip_protocol_t proto) { struct iphdr *iph; grub_net_link_level_address_t ll_target_addr; @@ -223,20 +223,13 @@ static grub_err_t handle_dgram (struct grub_net_buff *nb, const struct grub_net_card *card, const grub_net_link_level_address_t *hwaddress, - grub_net_ip_protocol_t proto, grub_uint32_t src, - grub_uint32_t dst) + grub_net_ip_protocol_t proto, + const grub_net_network_level_address_t *source, + const grub_net_network_level_address_t *dest) { struct grub_net_network_level_interface *inf = NULL; grub_err_t err; - grub_net_network_level_address_t source; - grub_net_network_level_address_t dest; - - source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - source.ipv4 = src; - - dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - dest.ipv4 = dst; - + /* DHCP needs special treatment since we don't know IP yet. */ { struct udphdr *udph; @@ -255,8 +248,8 @@ handle_dgram (struct grub_net_buff *nb, udph->chksum = 0; expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, - &source, - &dest); + source, + dest); if (expected != chk) { grub_dprintf ("net", "Invalid UDP checksum. " @@ -284,8 +277,7 @@ handle_dgram (struct grub_net_buff *nb, FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { if (inf->card == card - && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && inf->address.ipv4 == dst + && grub_net_addr_cmp (&inf->address, dest) == 0 && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) break; } @@ -299,11 +291,11 @@ handle_dgram (struct grub_net_buff *nb, switch (proto) { case GRUB_NET_IP_UDP: - return grub_net_recv_udp_packet (nb, inf, &source); + return grub_net_recv_udp_packet (nb, inf, source); case GRUB_NET_IP_TCP: - return grub_net_recv_tcp_packet (nb, inf, &source); + return grub_net_recv_tcp_packet (nb, inf, source); case GRUB_NET_IP_ICMP: - return grub_net_recv_icmp_packet (nb, inf, &source); + return grub_net_recv_icmp_packet (nb, inf, source); default: grub_netbuff_free (nb); break; @@ -339,9 +331,9 @@ free_old_fragments (void) } grub_err_t -grub_net_recv_ip_packets (struct grub_net_buff * nb, - const struct grub_net_card * card, - const grub_net_link_level_address_t * hwaddress) +grub_net_recv_ip4_packets (struct grub_net_buff * nb, + const struct grub_net_card * card, + const grub_net_link_level_address_t * hwaddress) { struct iphdr *iph = (struct iphdr *) nb->data; grub_err_t err; @@ -365,8 +357,8 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, if (nb->tail - nb->data < (grub_ssize_t) ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t))) { - grub_dprintf ("net", "IP packet too short: %d\n", - (iph->verhdrlen & 0xf)); + grub_dprintf ("net", "IP packet too short: %" PRIdGRUB_SSIZE "\n", + (nb->tail - nb->data)); grub_netbuff_free (nb); return GRUB_ERR_NONE; } @@ -398,6 +390,9 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, if (((grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS) == 0) && (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) == 0) { + grub_net_network_level_address_t source; + grub_net_network_level_address_t dest; + err = grub_netbuff_pull (nb, ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t))); if (err) @@ -405,8 +400,15 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, grub_netbuff_free (nb); return err; } + + source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + source.ipv4 = iph->src; + + dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + dest.ipv4 = iph->dest; + return handle_dgram (nb, card, hwaddress, iph->protocol, - iph->src, iph->dest); + &source, &dest); } for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev) @@ -469,6 +471,8 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, grub_net_ip_protocol_t proto; grub_uint32_t src; grub_uint32_t dst; + grub_net_network_level_address_t source; + grub_net_network_level_address_t dest; nb_top_p = grub_priority_queue_top (rsm->pq); if (!nb_top_p) @@ -524,8 +528,132 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, } ret->data = ret->head = res; ret->tail = ret->end = res + res_len; - return handle_dgram (ret, card, hwaddress, proto, src, dst); + + source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + source.ipv4 = src; + + dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + dest.ipv4 = dst; + + return handle_dgram (ret, card, hwaddress, proto, &source, &dest); } return GRUB_ERR_NONE; } + +static grub_err_t +grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, + const grub_net_network_level_address_t * target, + struct grub_net_buff * nb, + grub_net_ip_protocol_t proto) +{ + struct ip6hdr *iph; + grub_net_link_level_address_t ll_target_addr; + grub_err_t err; + + COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV6_HEADER_SIZE == sizeof (*iph)); + + /* Determine link layer target address via ARP. */ + err = grub_net_arp_resolve (inf, target, &ll_target_addr); + if (err) + return err; + + if (nb->tail - nb->data + sizeof (struct iphdr) > inf->card->mtu) + return grub_error (GRUB_ERR_NET_PACKET_TOO_BIG, "packet too big"); + + grub_netbuff_push (nb, sizeof (*iph)); + iph = (struct ip6hdr *) nb->data; + + iph->version_class_flow = grub_cpu_to_be32 ((6 << 28)); + iph->len = grub_cpu_to_be16 (nb->tail - nb->data) - sizeof (*iph); + iph->protocol = proto; + iph->ttl = 0xff; + grub_memcpy (&iph->src, inf->address.ipv6, sizeof (iph->src)); + grub_memcpy (&iph->dest, target->ipv6, sizeof (iph->dest)); + + return send_ethernet_packet (inf, nb, ll_target_addr, + GRUB_NET_ETHERTYPE_IP); +} + +grub_err_t +grub_net_send_ip_packet (struct grub_net_network_level_interface * inf, + const grub_net_network_level_address_t * target, + struct grub_net_buff * nb, + grub_net_ip_protocol_t proto) +{ + switch (target->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + return grub_net_send_ip4_packet (inf, target, nb, proto); + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + return grub_net_send_ip6_packet (inf, target, nb, proto); + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP"); + } + +} + +grub_err_t +grub_net_recv_ip6_packets (struct grub_net_buff * nb, + const struct grub_net_card * card, + const grub_net_link_level_address_t * hwaddress) +{ + struct ip6hdr *iph = (struct ip6hdr *) nb->data; + grub_err_t err; + grub_net_network_level_address_t source; + grub_net_network_level_address_t dest; + + if ((grub_be_to_cpu32 (iph->version_class_flow) >> 28) != 6) + { + grub_dprintf ("net", "Bad IP version: %d\n", + (grub_be_to_cpu32 (iph->version_class_flow) >> 28)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + if (nb->tail - nb->data < (grub_ssize_t) sizeof (*iph)) + { + grub_dprintf ("net", "IP packet too short: %" PRIdGRUB_SSIZE "\n", + nb->tail - nb->data); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + err = grub_netbuff_pull (nb, sizeof (*iph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + /* Check size. */ + { + grub_size_t expected_size = grub_be_to_cpu16 (iph->len); + grub_size_t actual_size = (nb->tail - nb->data); + if (actual_size > expected_size) + { + err = grub_netbuff_unput (nb, actual_size - expected_size); + if (err) + { + grub_netbuff_free (nb); + return err; + } + } + if (actual_size < expected_size) + { + grub_dprintf ("net", "Cut IP packet actual: %" PRIuGRUB_SIZE + ", expected %" PRIuGRUB_SIZE "\n", actual_size, + expected_size); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + } + + source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (source.ipv6, &iph->src, sizeof (source.ipv6)); + grub_memcpy (dest.ipv6, &iph->src, sizeof (dest.ipv6)); + + return handle_dgram (nb, card, hwaddress, iph->protocol, + &source, &dest); +} diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0bde49322..7d6ef2759 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -131,10 +131,23 @@ match_net (const grub_net_network_level_netaddress_t *net, return 0; case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: { - grub_int32_t mask = ((1 << net->ipv4.masksize) - 1) << (32 - net->ipv4.masksize); + grub_uint32_t mask = (0xffffffffU << (32 - net->ipv4.masksize)); return ((grub_be_to_cpu32 (net->ipv4.base) & mask) == (grub_be_to_cpu32 (addr->ipv4) & mask)); } + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + { + grub_int64_t mask[2]; + mask[1] = 0xffffffffffffffffULL << (128 - net->ipv6.masksize); + if (net->ipv6.masksize < 64) + mask[0] = 0xffffffffffffffffULL; + else + mask[0] = 0xffffffffffffffffULL << (64 - net->ipv6.masksize); + return (((grub_be_to_cpu64 (net->ipv6.base[0]) & mask[0]) + == (grub_be_to_cpu32 (addr->ipv6[0]) & mask[0])) + && ((grub_be_to_cpu64 (net->ipv6.base[1]) & mask[1]) + == (grub_be_to_cpu32 (addr->ipv6[1]) & mask[1]))); + } } return 0; } @@ -249,6 +262,25 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf) case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: grub_strcpy (buf, "temporary"); return; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + { + char *ptr = buf; + grub_uint32_t n = grub_be_to_cpu32 (target->ipv6[0]); + int i; + for (i = 0; i < 4; i++) + { + grub_snprintf (ptr, 6, "%x:", (n >> (48 - 16 * i)) & 0xffff); + ptr += grub_strlen (ptr); + } + n = grub_be_to_cpu32 (target->ipv6[1]); + for (i = 0; i < 3; i++) + { + grub_snprintf (ptr, 6, "%x:", (n >> (48 - 16 * i)) & 0xffff); + ptr += grub_strlen (ptr); + } + grub_snprintf (ptr, 5, "%x", n & 0xffff); + return; + } case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: { grub_uint32_t n = grub_be_to_cpu32 (target->ipv4); @@ -307,6 +339,27 @@ grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, return 1; } +int +grub_net_addr_cmp (const grub_net_network_level_address_t *a, + const grub_net_network_level_address_t *b) +{ + if (a->type < b->type) + return -1; + if (a->type > b->type) + return +1; + switch (a->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + return grub_memcmp (&a->ipv4, &b->ipv4, sizeof (a->ipv4)); + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + return grub_memcmp (&a->ipv6, &b->ipv6, sizeof (a->ipv6)); + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: + return 0; + } + grub_printf ("Unsupported address type %d\n", a->type); + return 1; +} + /* FIXME: implement this. */ static char * hwaddr_set_env (struct grub_env_var *var __attribute__ ((unused)), @@ -375,6 +428,43 @@ grub_net_add_addr (const char *name, grub_net_network_level_interface_register (inter); + if (addr.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + { + int mask = -1; + grub_uint32_t ip_cpu = grub_be_to_cpu32 (addr.ipv4); + if (!(ip_cpu & 0x80000000)) + mask = 8; + else if (!(ip_cpu & 0x40000000)) + mask = 16; + else if (!(ip_cpu & 0x20000000)) + mask = 24; + else + mask = -1; + if (mask != -1) + { + struct grub_net_route *route; + + route = grub_zalloc (sizeof (*route)); + if (!route) + return NULL; + + route->name = grub_xasprintf ("%s:local", name); + if (!route->name) + { + grub_free (route); + return NULL; + } + + route->target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + route->target.ipv4.base = grub_cpu_to_be32 (ip_cpu & (0xffffffff << (32 - mask))); + route->target.ipv4.masksize = mask; + route->is_gateway = 0; + route->interface = inter; + + grub_net_route_register (route); + } + } + return inter; } @@ -532,7 +622,7 @@ print_net_address (const grub_net_network_level_netaddress_t *target) { case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: grub_printf ("temporary\n"); - break; + return; case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: { grub_uint32_t n = grub_be_to_cpu32 (target->ipv4.base); @@ -543,6 +633,16 @@ print_net_address (const grub_net_network_level_netaddress_t *target) target->ipv4.masksize); } return; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + { + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + struct grub_net_network_level_address base; + base.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (&base.ipv6, &target->ipv6, 16); + grub_net_addr_to_str (&base, buf); + grub_printf ("%s/%d ", buf, target->ipv6.masksize); + } + return; } grub_printf ("Unknown address type %d\n", target->type); } @@ -764,6 +864,7 @@ receive_packets (struct grub_net_card *card) /* Maybe should be better have a fixed number of packets for each card and just mark them as used and not used. */ struct grub_net_buff *nb; + struct grub_net_buff *nb2; nb = card->driver->recv (card); if (!nb) @@ -820,7 +921,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) char *ptr = buf; grub_size_t amount, total = 0; int try = 0; - while (try <= 3) + while (try <= GRUB_NET_TRIES) { while (net->packs.first) { @@ -851,7 +952,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) if (!net->eof) { try++; - grub_net_poll_cards (200); + grub_net_poll_cards (GRUB_NET_INTERVAL); } else return total; diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index cb5b6a698..b1eb3d9a8 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -23,10 +23,10 @@ #include #include -#define TCP_SYN_RETRANSMISSION_TIMEOUT 1000 -#define TCP_SYN_RETRANSMISSION_COUNT 3 -#define TCP_RETRANSMISSION_TIMEOUT 1000 -#define TCP_RETRANSMISSION_COUNT 10 +#define TCP_SYN_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL +#define TCP_SYN_RETRANSMISSION_COUNT GRUB_NET_TRIES +#define TCP_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL +#define TCP_RETRANSMISSION_COUNT GRUB_NET_TRIES struct unacked { @@ -109,6 +109,15 @@ struct tcp_pseudohdr grub_uint16_t tcp_length; } __attribute__ ((packed)); +struct tcp6_pseudohdr +{ + grub_uint64_t src[2]; + grub_uint64_t dst[2]; + grub_uint32_t tcp_length; + grub_uint8_t zero[3]; + grub_uint8_t proto; +} __attribute__ ((packed)); + static struct grub_net_tcp_socket *tcp_sockets; static struct grub_net_tcp_listen *tcp_listens; @@ -377,19 +386,39 @@ grub_net_ip_transport_checksum (struct grub_net_buff *nb, const grub_net_network_level_address_t *src, const grub_net_network_level_address_t *dst) { - struct tcp_pseudohdr ph; grub_uint16_t a, b; grub_uint32_t c; - a = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) nb->data, nb->tail - nb->data)); - ph.src = src->ipv4; - ph.dst = dst->ipv4; - ph.zero = 0; - ph.tcp_length = grub_cpu_to_be16 (nb->tail - nb->data); - ph.proto = proto; - b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph))); + switch (dst->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + { + struct tcp_pseudohdr ph; + ph.src = src->ipv4; + ph.dst = dst->ipv4; + ph.zero = 0; + ph.tcp_length = grub_cpu_to_be16 (nb->tail - nb->data); + ph.proto = proto; + b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph))); + break; + } + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + { + struct tcp6_pseudohdr ph; + grub_memcpy (ph.src, src->ipv6, sizeof (ph.src)); + grub_memcpy (ph.dst, dst->ipv6, sizeof (ph.dst)); + grub_memset (ph.zero, 0, sizeof (ph.zero)); + ph.tcp_length = grub_cpu_to_be32 (nb->tail - nb->data); + ph.proto = proto; + b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph))); + break; + } + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: + b = 0; + break; + } c = (grub_uint32_t) a + (grub_uint32_t) b; if (c >= 0xffff) c -= 0xffff; diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 7c58e640b..9be7294e8 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -167,6 +167,7 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), case TFTP_OACK: data->block_size = TFTP_DEFAULTSIZE_PACKET; data->have_oack = 1; + grub_printf ("OACK\n"); for (ptr = nb->data + sizeof (tftph->opcode); ptr < nb->tail;) { if (grub_memcmp (ptr, "tsize\0", sizeof ("tsize\0") - 1) == 0) @@ -345,7 +346,7 @@ tftp_open (struct grub_file *file, const char *filename) /* Receive OACK packet. */ nbd = nb.data; - for (i = 0; i < 3; i++) + for (i = 0; i < GRUB_NET_TRIES; i++) { nb.data = nbd; err = grub_net_send_udp_packet (data->sock, &nb); @@ -355,7 +356,7 @@ tftp_open (struct grub_file *file, const char *filename) destroy_pq (data); return err; } - grub_net_poll_cards (100); + grub_net_poll_cards (GRUB_NET_INTERVAL); if (data->have_oack) break; } diff --git a/include/grub/err.h b/include/grub/err.h index f6610193c..d13f2c9ce 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -64,7 +64,8 @@ typedef enum GRUB_ERR_BUG, GRUB_ERR_NET_PORT_CLOSED, GRUB_ERR_NET_INVALID_RESPONSE, - GRUB_ERR_NET_UNKNOWN_ERROR + GRUB_ERR_NET_UNKNOWN_ERROR, + GRUB_ERR_NET_PACKET_TOO_BIG } grub_err_t; diff --git a/include/grub/net.h b/include/grub/net.h index ac011520a..cb9d40cd0 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -33,6 +33,7 @@ enum GRUB_NET_UDP_HEADER_SIZE = 8, GRUB_NET_TCP_HEADER_SIZE = 20, GRUB_NET_OUR_IPV4_HEADER_SIZE = 20, + GRUB_NET_OUR_IPV6_HEADER_SIZE = 40, GRUB_NET_TCP_RESERVE_SIZE = GRUB_NET_TCP_HEADER_SIZE + GRUB_NET_OUR_IPV4_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE @@ -127,7 +128,8 @@ struct grub_net_network_level_interface; typedef enum grub_network_level_protocol_id { GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV, - GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4, + GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 } grub_network_level_protocol_id_t; typedef struct grub_net_network_level_address @@ -136,6 +138,7 @@ typedef struct grub_net_network_level_address union { grub_uint32_t ipv4; + grub_uint64_t ipv6[2]; }; } grub_net_network_level_address_t; @@ -148,6 +151,10 @@ typedef struct grub_net_network_level_netaddress grub_uint32_t base; int masksize; } ipv4; + struct { + grub_uint64_t base[2]; + int masksize; + } ipv6; }; } grub_net_network_level_netaddress_t; @@ -389,13 +396,17 @@ grub_net_process_dhcp (struct grub_net_buff *nb, int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, const grub_net_link_level_address_t *b); +int +grub_net_addr_cmp (const grub_net_network_level_address_t *a, + const grub_net_network_level_address_t *b); /* - Currently suppoerted adresses: + Currently supported adresses: IPv4: XXX.XXX.XXX.XXX + IPv&: XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX */ -#define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXX.XXX.XXX.XXX") +#define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") void grub_net_addr_to_str (const grub_net_network_level_address_t *target, @@ -428,4 +439,7 @@ grub_net_tcp_retransmit (void); extern char *grub_net_default_server; +#define GRUB_NET_TRIES 40 +#define GRUB_NET_INTERVAL 400 + #endif /* ! GRUB_NET_HEADER */ diff --git a/include/grub/net/arp.h b/include/grub/net/arp.h index 27a6719c4..49682184e 100644 --- a/include/grub/net/arp.h +++ b/include/grub/net/arp.h @@ -21,30 +21,6 @@ #include #include -enum -{ -/* IANA ARP constant to define hardware type as ethernet. */ - GRUB_NET_ARPHRD_ETHERNET = 1 -}; - -/* ARP header operation codes */ -#define ARP_REQUEST 1 -#define ARP_REPLY 2 - -struct arp_entry { - int avail; - grub_net_network_level_address_t nl_address; - grub_net_link_level_address_t ll_address; -}; - -struct arphdr { - grub_uint16_t hrd; - grub_uint16_t pro; - grub_uint8_t hln; - grub_uint8_t pln; - grub_uint16_t op; -} __attribute__ ((packed)); - extern grub_err_t grub_net_arp_receive(struct grub_net_buff *nb); extern grub_err_t grub_net_arp_resolve(struct grub_net_network_level_interface *inf, diff --git a/include/grub/net/ethernet.h b/include/grub/net/ethernet.h index a68aafd96..0749d9d87 100644 --- a/include/grub/net/ethernet.h +++ b/include/grub/net/ethernet.h @@ -22,17 +22,18 @@ #include /* IANA Ethertype */ -enum -{ - GRUB_NET_ETHERTYPE_IP = 0x0800, - GRUB_NET_ETHERTYPE_ARP = 0x0806 -}; +typedef enum + { + GRUB_NET_ETHERTYPE_IP = 0x0800, + GRUB_NET_ETHERTYPE_ARP = 0x0806, + GRUB_NET_ETHERTYPE_IP6 = 0x86DD, + } grub_net_ethertype_t; grub_err_t send_ethernet_packet (struct grub_net_network_level_interface *inf, struct grub_net_buff *nb, grub_net_link_level_address_t target_addr, - grub_uint16_t ethertype); + grub_net_ethertype_t ethertype); grub_err_t grub_net_recv_ethernet_packet (struct grub_net_buff *nb, const struct grub_net_card *card); diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index e385e5ac1..759577522 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -32,9 +32,13 @@ typedef enum grub_net_ip_protocol grub_uint16_t grub_net_ip_chksum(void *ipv, grub_size_t len); grub_err_t -grub_net_recv_ip_packets (struct grub_net_buff *nb, - const struct grub_net_card *card, - const grub_net_link_level_address_t *hwaddress); +grub_net_recv_ip4_packets (struct grub_net_buff *nb, + const struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddress); +grub_err_t +grub_net_recv_ip6_packets (struct grub_net_buff *nb, + const struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddress); grub_err_t grub_net_send_ip_packet (struct grub_net_network_level_interface *inf, diff --git a/include/grub/types.h b/include/grub/types.h index 4fd7a3a4b..4dde10a16 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -98,13 +98,15 @@ typedef grub_uint64_t grub_size_t; typedef grub_int64_t grub_ssize_t; # if GRUB_CPU_SIZEOF_LONG == 8 -# define PRIxGRUB_SIZE "lx" -# define PRIxGRUB_ADDR "lx" -# define PRIuGRUB_SIZE "lu" +# define PRIxGRUB_SIZE "lx" +# define PRIxGRUB_ADDR "lx" +# define PRIuGRUB_SIZE "lu" +# define PRIdGRUB_SSIZE "ld" # else -# define PRIxGRUB_SIZE "llx" -# define PRIxGRUB_ADDR "llx" -# define PRIuGRUB_SIZE "llu" +# define PRIxGRUB_SIZE "llx" +# define PRIxGRUB_ADDR "llx" +# define PRIuGRUB_SIZE "llu" +# define PRIdGRUB_SSIZE "lld" # endif #else typedef grub_uint32_t grub_addr_t;