From 4db34616962a6350cc94c07b8eddb18f66aae0f6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 8 Jul 2011 04:07:15 +0200 Subject: [PATCH 01/37] Priority queue implementation --- grub-core/Makefile.core.def | 5 + grub-core/lib/priority_queue.c | 246 +++++++++++++++++++++++++++++++++ include/grub/priority_queue.h | 36 +++++ 3 files changed, 287 insertions(+) create mode 100644 grub-core/lib/priority_queue.c create mode 100644 include/grub/priority_queue.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 7200bd3e9..a31d9963c 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1667,6 +1667,11 @@ module = { enable = videomodules; }; +module = { + name = priority_queue; + common = lib/priority_queue.c; +}; + module = { name = time; common = commands/time.c; diff --git a/grub-core/lib/priority_queue.c b/grub-core/lib/priority_queue.c new file mode 100644 index 000000000..5b02ef381 --- /dev/null +++ b/grub-core/lib/priority_queue.c @@ -0,0 +1,246 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifndef TEST +#include +#include +#else +#include +#include +#include + +#include + +using namespace std; + +typedef size_t grub_size_t; +typedef int (*grub_comparator_t) (const void *a, const void *b); +typedef unsigned char grub_uint8_t; +#define grub_malloc malloc +#define grub_memcpy memcpy +#define grub_realloc realloc +#define grub_free free + +typedef enum + { + GRUB_ERR_NONE, + grub_errno + } grub_err_t; +#endif + +struct grub_priority_queue +{ + grub_size_t elsize; + grub_size_t allocated; + grub_size_t used; + grub_comparator_t cmp; + void *els; +}; + +#ifdef TEST +typedef struct grub_priority_queue *grub_priority_queue_t; +#endif + +static inline void * +element (struct grub_priority_queue *pq, grub_size_t k) +{ + return ((grub_uint8_t *) pq->els) + k * pq->elsize; +} + +static inline void +swap (struct grub_priority_queue *pq, grub_size_t m, grub_size_t n) +{ + grub_uint8_t *p1, *p2; + grub_size_t l; + p1 = (grub_uint8_t *) element (pq, m); + p2 = (grub_uint8_t *) element (pq, n); + for (l = pq->elsize; l; l--, p1++, p2++) + { + grub_uint8_t t; + t = *p1; + *p1 = *p2; + *p2 = t; + } +} + +static inline grub_size_t +parent (grub_size_t v) +{ + return (v - 1) / 2; +} + +static inline grub_size_t +left_child (grub_size_t v) +{ + return 2 * v + 1; +} + +static inline grub_size_t +right_child (grub_size_t v) +{ + return 2 * v + 2; +} + +void * +grub_priority_queue_top (grub_priority_queue_t pq) +{ + if (!pq->used) + return 0; + return element (pq, 0); +} + +grub_priority_queue_t +grub_priority_queue_new (grub_size_t elsize, + grub_comparator_t cmp) +{ + struct grub_priority_queue *ret; + void *els; + els = grub_malloc (elsize * 8); + if (!els) + return 0; + ret = (struct grub_priority_queue *) grub_malloc (sizeof (*ret)); + if (!ret) + { + grub_free (els); + return 0; + } + ret->elsize = elsize; + ret->allocated = 8; + ret->used = 0; + ret->cmp = cmp; + ret->els = els; + return ret; +} + +/* Heap property: pq->cmp (element (pq, p), element (pq, parent (p))) <= 0. */ +grub_err_t +grub_priority_queue_push (grub_priority_queue_t pq, void *el) +{ + grub_size_t p; + if (pq->used == pq->allocated) + { + void *els; + els = grub_realloc (pq->els, pq->elsize * 2 * pq->allocated); + if (!els) + return grub_errno; + pq->allocated *= 2; + pq->els = els; + } + pq->used++; + grub_memcpy (element (pq, pq->used - 1), el, pq->elsize); + for (p = pq->used - 1; p; p = parent (p)) + { + if (pq->cmp (element (pq, p), element (pq, parent (p))) <= 0) + break; + swap (pq, p, parent (p)); + } + + return GRUB_ERR_NONE; +} + +void +grub_priority_queue_pop (grub_priority_queue_t pq) +{ + grub_size_t p; + + swap (pq, 0, pq->used - 1); + pq->used--; + for (p = 0; left_child (p) < pq->used; ) + { + grub_size_t c; + if (pq->cmp (element (pq, left_child (p)), element (pq, p)) <= 0 + && (right_child (p) >= pq->used + || pq->cmp (element (pq, right_child (p)), element (pq, p)) <= 0)) + break; + if (right_child (p) >= pq->used + || pq->cmp (element (pq, left_child (p)), + element (pq, right_child (p))) > 0) + c = left_child (p); + else + c = right_child (p); + swap (pq, p, c); + p = c; + } +} + +#ifdef TEST + +static int +compar (const void *a_, const void *b_) +{ + int a = *(int *) a_; + int b = *(int *) b_; + if (a < b) + return -1; + if (a > b) + return +1; + return 0; +} + +int +main (void) +{ + priority_queue pq; + grub_priority_queue_t pq2; + int counter; + int s = 0; + pq2 = grub_priority_queue_new (sizeof (int), compar); + if (!pq2) + return 1; + srand (1); + + for (counter = 0; counter < 1000000; counter++) + { + int op = rand () % 10; + if (s && *(int *) grub_priority_queue_top (pq2) != pq.top ()) + { + printf ("Error at %d\n", counter); + return 2; + } + if (op < 3 && s) + { + grub_priority_queue_pop (pq2); + pq.pop (); + s--; + } + else + { + int v = rand (); + int e; + pq.push (v); + e = grub_priority_queue_push (pq2, &v); + if (e) + return 3; + s++; + } + } + while (s) + { + if (*(int *) grub_priority_queue_top (pq2) != pq.top ()) + { + printf ("Error at the end. %d elements remaining.\n", s); + return 4; + } + grub_priority_queue_pop (pq2); + pq.pop (); + s--; + } + printf ("All tests passed successfully\n"); + return 0; +} +#endif diff --git a/include/grub/priority_queue.h b/include/grub/priority_queue.h new file mode 100644 index 000000000..335ea9a39 --- /dev/null +++ b/include/grub/priority_queue.h @@ -0,0 +1,36 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifndef GRUB_PRIORITY_QUEUE_HEADER +#define GRUB_PRIORITY_QUEUE_HEADER 1 + +#include +#include + +struct grub_priority_queue; +typedef struct grub_priority_queue *grub_priority_queue_t; +typedef int (*grub_comparator_t) (const void *a, const void *b); + +grub_priority_queue_t grub_priority_queue_new (grub_size_t elsize, + grub_comparator_t cmp); +void grub_priority_queue_destroy (grub_priority_queue_t pq); +void *grub_priority_queue_top (grub_priority_queue_t pq); +void grub_priority_queue_pop (grub_priority_queue_t pq); +grub_err_t grub_priority_queue_push (grub_priority_queue_t pq, void *el); + +#endif From 1367c143dd181a84e459c3444ed08fc9ee683e12 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 8 Jul 2011 14:41:52 +0200 Subject: [PATCH 02/37] several cleanups. Ping reply support --- grub-core/Makefile.core.def | 1 + grub-core/net/bootp.c | 3 +- grub-core/net/icmp.c | 107 ++++++++++++++++++++++++++++++++++++ grub-core/net/ip.c | 28 +++++++--- grub-core/net/tftp.c | 4 +- grub-core/net/udp.c | 107 ++++++++++++++++++++++++++++-------- include/grub/net.h | 38 +------------ include/grub/net/arp.h | 18 ++++++ include/grub/net/ip.h | 24 ++++++-- include/grub/net/udp.h | 21 +++---- 10 files changed, 263 insertions(+), 88 deletions(-) create mode 100644 grub-core/net/icmp.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 2d96d6739..9bc893595 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1597,6 +1597,7 @@ module = { common = net/bootp.c; common = net/ip.c; common = net/udp.c; + common = net/icmp.c; common = net/ethernet.c; common = net/arp.c; common = net/netbuff.c; diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 84bdc04d7..1428c2a07 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -488,7 +488,8 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; target.ipv4 = 0xffffffff; - err = grub_net_send_ip_packet (&ifaces[j], &target, nb); + err = grub_net_send_ip_packet (&ifaces[j], &target, nb, + GRUB_NET_IP_UDP); grub_netbuff_free (nb); if (err) return err; diff --git a/grub-core/net/icmp.c b/grub-core/net/icmp.c new file mode 100644 index 000000000..8ea020107 --- /dev/null +++ b/grub-core/net/icmp.c @@ -0,0 +1,107 @@ +/* + * 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 + +struct icmp_header +{ + grub_uint8_t type; + grub_uint8_t code; + grub_uint16_t checksum; +}; + +struct ping_header +{ + grub_uint16_t id; + grub_uint16_t seq; +}; + +enum + { + ICMP_ECHO_REPLY = 0, + ICMP_ECHO = 8, + }; + +grub_err_t +grub_net_recv_icmp_packet (struct grub_net_buff * nb, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *src) +{ + struct icmp_header *icmph; + grub_err_t err; + grub_uint16_t checksum; + + icmph = (struct icmp_header *) nb->data; + + if (nb->tail - nb->data < (grub_ssize_t) sizeof (*icmph)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "ICMP packet too small"); + + checksum = icmph->checksum; + icmph->checksum = 0; + if (checksum != grub_net_ip_chksum (nb->data, nb->tail - nb->data)) + return GRUB_ERR_NONE; + icmph->checksum = checksum; + + err = grub_netbuff_pull (nb, sizeof (*icmph)); + if (err) + return err; + + switch (icmph->type) + { + case ICMP_ECHO: + { + struct grub_net_buff *nb_reply; + struct icmp_header *icmphr; + if (icmph->code) + break; + nb_reply = grub_netbuff_alloc (nb->tail - nb->data + 512); + if (!nb_reply) + { + grub_netbuff_free (nb); + return grub_errno; + } + err = grub_netbuff_reserve (nb_reply, nb->tail - nb->data + 512); + if (err) + goto ping_fail; + err = grub_netbuff_push (nb_reply, nb->tail - nb->data); + if (err) + goto ping_fail; + grub_memcpy (nb_reply->data, nb->data, nb->tail - nb->data); + err = grub_netbuff_push (nb_reply, sizeof (*icmphr)); + if (err) + goto ping_fail; + icmphr = (struct icmp_header *) nb_reply->data; + icmphr->type = ICMP_ECHO_REPLY; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = grub_net_ip_chksum ((void *) nb_reply->data, + nb_reply->tail - nb_reply->data); + err = grub_net_send_ip_packet (inf, src, nb_reply, GRUB_NET_IP_ICMP); + + ping_fail: + grub_netbuff_free (nb); + grub_netbuff_free (nb_reply); + return err; + } + }; + + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 642a67f18..e2ba4adfb 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -50,18 +50,23 @@ struct ip6hdr } __attribute__ ((packed)); grub_uint16_t -grub_net_ip_chksum (void *ipv, int len) +grub_net_ip_chksum (void *ipv, grub_size_t len) { grub_uint16_t *ip = (grub_uint16_t *) ipv; grub_uint32_t sum = 0; - len >>= 1; - while (len--) + for (; len >= 2; len -= 2) { sum += grub_be_to_cpu16 (*(ip++)); if (sum > 0xFFFF) sum -= 0xFFFF; } + if (len) + { + sum += *((grub_uint8_t *) ip) << 8; + if (sum > 0xFFFF) + sum -= 0xFFFF; + } return grub_cpu_to_be16 ((~sum) & 0x0000FFFF); } @@ -69,7 +74,8 @@ grub_net_ip_chksum (void *ipv, int len) 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) + struct grub_net_buff * nb, + grub_net_ip_protocol_t proto) { struct iphdr *iph; static int id = 0x2400; @@ -85,7 +91,7 @@ grub_net_send_ip_packet (struct grub_net_network_level_interface * inf, iph->ident = grub_cpu_to_be16 (++id); iph->frags = 0; iph->ttl = 0xff; - iph->protocol = 0x11; + iph->protocol = proto; iph->src = inf->address.ipv4; iph->dest = target->ipv4; @@ -108,6 +114,7 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, struct iphdr *iph = (struct iphdr *) nb->data; grub_err_t err; struct grub_net_network_level_interface *inf = NULL; + grub_net_network_level_address_t source; err = grub_netbuff_pull (nb, sizeof (*iph)); if (err) @@ -117,7 +124,7 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, { struct udphdr *udph; udph = (struct udphdr *) nb->data; - if (iph->protocol == IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) + if (iph->protocol == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) { FOR_NET_NETWORK_LEVEL_INTERFACES (inf) if (inf->card == card @@ -146,10 +153,15 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, } } + source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + source.ipv4 = iph->src; + switch (iph->protocol) { - case IP_UDP: - return grub_net_recv_udp_packet (nb, inf); + case GRUB_NET_IP_UDP: + return grub_net_recv_udp_packet (nb, inf, &source); + case GRUB_NET_IP_ICMP: + return grub_net_recv_icmp_packet (nb, inf, &source); default: grub_netbuff_free (nb); break; diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index be1534021..13c4971f9 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -102,11 +102,11 @@ typedef struct tftp_data grub_uint64_t block; grub_uint32_t block_size; int have_oack; - grub_net_socket_t sock; + grub_net_udp_socket_t sock; } *tftp_data_t; static grub_err_t -tftp_receive (grub_net_socket_t sock __attribute__ ((unused)), +tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), struct grub_net_buff *nb, void *f) { diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index 47a67a967..15bc1f490 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -1,13 +1,66 @@ +/* + * 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 #include #include -grub_net_socket_t +struct grub_net_udp_socket +{ + struct grub_net_udp_socket *next; + + enum { GRUB_NET_SOCKET_START, + GRUB_NET_SOCKET_ESTABLISHED, + GRUB_NET_SOCKET_CLOSED } status; + int in_port; + int out_port; + grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, + void *recv); + void *recv_hook_data; + grub_net_network_level_address_t out_nla; + struct grub_net_network_level_interface *inf; +}; + +struct grub_net_udp_socket *udp_sockets; + +#define FOR_UDP_SOCKETS(var) for (var = udp_sockets; var; var = var->next) + +static inline void +udp_socket_register (grub_net_udp_socket_t sock) +{ + grub_list_push (GRUB_AS_LIST_P (&udp_sockets), + GRUB_AS_LIST (sock)); +} + +void +grub_net_udp_close (grub_net_udp_socket_t sock) +{ + grub_list_remove (GRUB_AS_LIST_P (&udp_sockets), + GRUB_AS_LIST (sock)); + grub_free (sock); +} + +grub_net_udp_socket_t grub_net_udp_open (char *server, grub_uint16_t out_port, - grub_err_t (*recv_hook) (grub_net_socket_t sock, + grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, void *data), void *recv_hook_data) @@ -16,12 +69,18 @@ grub_net_udp_open (char *server, grub_net_network_level_address_t addr; struct grub_net_network_level_interface *inf; grub_net_network_level_address_t gateway; - grub_net_socket_t socket; + grub_net_udp_socket_t socket; static int in_port = 25300; err = grub_net_resolve_address (server, &addr); if (err) return NULL; + + if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "not a IPv4 address"); + return NULL; + } err = grub_net_route_address (addr, &gateway, &inf); if (err) @@ -31,21 +90,21 @@ grub_net_udp_open (char *server, if (socket == NULL) return NULL; - socket->x_out_port = out_port; - socket->x_inf = inf; - socket->x_out_nla = addr; - socket->x_in_port = in_port++; - socket->x_status = GRUB_NET_SOCKET_START; + socket->out_port = out_port; + socket->inf = inf; + socket->out_nla = addr; + socket->in_port = in_port++; + socket->status = GRUB_NET_SOCKET_START; socket->recv_hook = recv_hook; socket->recv_hook_data = recv_hook_data; - grub_net_socket_register (socket); + udp_socket_register (socket); return socket; } grub_err_t -grub_net_send_udp_packet (const grub_net_socket_t socket, +grub_net_send_udp_packet (const grub_net_udp_socket_t socket, struct grub_net_buff *nb) { struct udphdr *udph; @@ -56,37 +115,41 @@ grub_net_send_udp_packet (const grub_net_socket_t socket, return err; udph = (struct udphdr *) nb->data; - udph->src = grub_cpu_to_be16 (socket->x_in_port); - udph->dst = grub_cpu_to_be16 (socket->x_out_port); + udph->src = grub_cpu_to_be16 (socket->in_port); + udph->dst = grub_cpu_to_be16 (socket->out_port); /* No chechksum. */ udph->chksum = 0; udph->len = grub_cpu_to_be16 (nb->tail - nb->data); - return grub_net_send_ip_packet (socket->x_inf, &(socket->x_out_nla), nb); + return grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, + GRUB_NET_IP_UDP); } grub_err_t -grub_net_recv_udp_packet (struct grub_net_buff * nb, - struct grub_net_network_level_interface * inf) +grub_net_recv_udp_packet (struct grub_net_buff *nb, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *source) { struct udphdr *udph; - grub_net_socket_t sock; + grub_net_udp_socket_t sock; grub_err_t err; udph = (struct udphdr *) nb->data; err = grub_netbuff_pull (nb, sizeof (*udph)); if (err) return err; - FOR_NET_SOCKETS (sock) + FOR_UDP_SOCKETS (sock) { - if (grub_be_to_cpu16 (udph->dst) == sock->x_in_port - && inf == sock->x_inf && sock->recv_hook) + if (grub_be_to_cpu16 (udph->dst) == sock->in_port + && inf == sock->inf && sock->recv_hook + && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && source->ipv4 == sock->out_nla.ipv4) { - if (sock->x_status == GRUB_NET_SOCKET_START) + if (sock->status == GRUB_NET_SOCKET_START) { - sock->x_out_port = grub_be_to_cpu16 (udph->src); - sock->x_status = GRUB_NET_SOCKET_ESTABLISHED; + sock->out_port = grub_be_to_cpu16 (udph->src); + sock->status = GRUB_NET_SOCKET_ESTABLISHED; } /* App protocol remove its own reader. */ diff --git a/include/grub/net.h b/include/grub/net.h index 45d08f3f5..f6600690a 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -196,40 +196,6 @@ struct grub_net_app_protocol grub_err_t (*close) (struct grub_file *file); }; -struct grub_net_socket -{ - struct grub_net_socket *next; - - enum { GRUB_NET_SOCKET_START, - GRUB_NET_SOCKET_ESTABLISHED, - GRUB_NET_SOCKET_CLOSED } x_status; - int x_in_port; - int x_out_port; - grub_err_t (*recv_hook) (grub_net_socket_t sock, struct grub_net_buff *nb, - void *recv); - void *recv_hook_data; - grub_net_network_level_address_t x_out_nla; - struct grub_net_network_level_interface *x_inf; -}; - -extern struct grub_net_socket *grub_net_sockets; - -static inline void -grub_net_socket_register (grub_net_socket_t sock) -{ - grub_list_push (GRUB_AS_LIST_P (&grub_net_sockets), - GRUB_AS_LIST (sock)); -} - -static inline void -grub_net_socket_unregister (grub_net_socket_t sock) -{ - grub_list_remove (GRUB_AS_LIST_P (&grub_net_sockets), - GRUB_AS_LIST (sock)); -} - -#define FOR_NET_SOCKETS(var) for (var = grub_net_sockets; var; var = var->next) - typedef struct grub_net { char *server; @@ -337,7 +303,7 @@ void grub_net_card_unregister (struct grub_net_card *card); #define FOR_NET_CARDS(var) for (var = grub_net_cards; var; var = var->next) -#define FOR_NET_CARDS_SAFE(var, next) for (var = grub_net_cards, next = var->next; var; var = next, next = var->next) +#define FOR_NET_CARDS_SAFE(var, next) for (var = grub_net_cards, next = (var ? var->next : 0); var; var = next, next = (var ? var->next : 0)) struct grub_net_session * @@ -426,7 +392,7 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, extern struct grub_net_network_level_interface *grub_net_network_level_interfaces; #define FOR_NET_NETWORK_LEVEL_INTERFACES(var) for (var = grub_net_network_level_interfaces; var; var = var->next) -#define FOR_NET_NETWORK_LEVEL_INTERFACES_SAFE(var,next) for (var = grub_net_network_level_interfaces, next = var->next; var; var = next, next = var->next) +#define FOR_NET_NETWORK_LEVEL_INTERFACES_SAFE(var,next) for (var = grub_net_network_level_interfaces, next = (var ? var->next : 0); var; var = next, next = (var ? var->next : 0)) void grub_net_poll_cards (unsigned time); diff --git a/include/grub/net/arp.h b/include/grub/net/arp.h index c60ea333f..27a6719c4 100644 --- a/include/grub/net/arp.h +++ b/include/grub/net/arp.h @@ -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 . + */ + #ifndef GRUB_NET_ARP_HEADER #define GRUB_NET_ARP_HEADER 1 #include diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index 9bed1e19c..a3c64c1cb 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -21,13 +21,15 @@ #include #include -enum +typedef enum grub_net_ip_protocol { - IP_UDP = 0x11 /* UDP protocol */ - }; -#define IP_BROADCAST 0xFFFFFFFF + GRUB_NET_IP_ICMP = 1, + GRUB_NET_IP_TCP = 6, + GRUB_NET_IP_UDP = 17 + } grub_net_ip_protocol_t; +#define GRUB_NET_IP_BROADCAST 0xFFFFFFFF -grub_uint16_t grub_net_ip_chksum(void *ipv, int len); +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, @@ -37,6 +39,16 @@ grub_net_recv_ip_packets (struct grub_net_buff *nb, 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); + struct grub_net_buff *nb, + grub_net_ip_protocol_t proto); + +grub_err_t +grub_net_recv_icmp_packet (struct grub_net_buff *nb, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *src); +grub_err_t +grub_net_recv_udp_packet (struct grub_net_buff *nb, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *src); #endif diff --git a/include/grub/net/udp.h b/include/grub/net/udp.h index 5aacf8abb..8d978569d 100644 --- a/include/grub/net/udp.h +++ b/include/grub/net/udp.h @@ -11,28 +11,23 @@ struct udphdr grub_uint16_t chksum; } __attribute__ ((packed)); +struct grub_net_udp_socket; +typedef struct grub_net_udp_socket *grub_net_udp_socket_t; -grub_net_socket_t +grub_net_udp_socket_t grub_net_udp_open (char *server, grub_uint16_t out_port, - grub_err_t (*recv_hook) (grub_net_socket_t sock, + grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, void *data), void *recv_hook_data); -static inline void -grub_net_udp_close (grub_net_socket_t sock) -{ - grub_net_socket_unregister (sock); - grub_free (sock); -} +void +grub_net_udp_close (grub_net_udp_socket_t sock); grub_err_t -grub_net_send_udp_packet (const grub_net_socket_t socket, struct grub_net_buff *nb); - -grub_err_t -grub_net_recv_udp_packet (struct grub_net_buff *nb, - struct grub_net_network_level_interface *inf); +grub_net_send_udp_packet (const grub_net_udp_socket_t socket, + struct grub_net_buff *nb); #endif From 5438143da6b709575b9467e6660cb3f383509695 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 8 Jul 2011 15:43:34 +0200 Subject: [PATCH 03/37] improve robustness of IPv4 receiving code --- grub-core/net/ip.c | 86 +++++++++++++++++++++++++++++++++++++++------ grub-core/net/net.c | 6 ++++ 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index e2ba4adfb..ee3ccd4d6 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -35,9 +35,16 @@ struct iphdr { grub_uint8_t protocol; grub_uint16_t chksum; grub_uint32_t src; - grub_uint32_t dest; + grub_uint32_t dest; } __attribute__ ((packed)) ; +enum +{ + DONT_FRAGMENT = 0x4000, + MORE_FRAGMENTS = 0x2000, + OFFSET_MASK = 0x1fff +}; + struct ip6hdr { grub_uint8_t version:4, priority:4; @@ -116,9 +123,61 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, struct grub_net_network_level_interface *inf = NULL; grub_net_network_level_address_t source; - err = grub_netbuff_pull (nb, sizeof (*iph)); + if ((iph->verhdrlen >> 4) != 4) + { + grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + if ((iph->verhdrlen & 0xf) < 5) + { + grub_dprintf ("net", "IP header too short: %d\n", + (iph->verhdrlen & 0xf)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + err = grub_netbuff_pull (nb, (iph->verhdrlen & 0xf) * sizeof (grub_uint32_t)); if (err) - return 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 + + (iph->verhdrlen & 0xf) + * sizeof (grub_uint32_t)); + 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; + } + } + + /* Fragmented packet. Bad. */ + if (((grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS) != 0) + || (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) != 0) + { + /* FIXME. */ + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } /* DHCP needs special treatment since we don't know IP yet. */ { @@ -136,21 +195,26 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, return err; grub_net_process_dhcp (nb, inf->card); grub_netbuff_free (nb); + return GRUB_ERR_NONE; } + grub_netbuff_free (nb); return GRUB_ERR_NONE; } } + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + if (inf->card == card + && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && inf->address.ipv4 == iph->dest + && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) + break; + } + if (!inf) { - FOR_NET_NETWORK_LEVEL_INTERFACES (inf) - { - if (inf->card == card - && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && inf->address.ipv4 == iph->dest - && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) - break; - } + grub_netbuff_free (nb); + return GRUB_ERR_NONE; } source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0f8a60413..7bed078e3 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -781,6 +781,12 @@ receive_packets (struct grub_net_card *card) break; } grub_net_recv_ethernet_packet (nb, card); + if (grub_errno) + { + grub_dprintf ("net", "error receiving: %d: %s\n", grub_errno, + grub_errmsg); + grub_errno = GRUB_ERR_NONE; + } } grub_print_error (); } From 6a1af81a97eb146e1b99dcbb039d722dbfa605c4 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 8 Jul 2011 18:49:35 +0200 Subject: [PATCH 04/37] add mtu information --- grub-core/net/drivers/efi/efinet.c | 1 + grub-core/net/drivers/emu/emunet.c | 18 ++++++++++++++++++ grub-core/net/drivers/i386/pc/pxe.c | 1 + grub-core/net/drivers/ieee1275/ofnet.c | 18 ++++++++++-------- include/grub/net.h | 1 + 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 5c6aac608..0fc779966 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -136,6 +136,7 @@ grub_efinet_findcards (void) card->driver = &efidriver; card->flags = 0; card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->mtu = net->mode->max_packet_size; grub_memcpy (card->default_address.mac, net->mode->current_address, sizeof (card->default_address.mac)); diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c index d1e49a2f4..96f773340 100644 --- a/grub-core/net/drivers/emu/emunet.c +++ b/grub-core/net/drivers/emu/emunet.c @@ -1,3 +1,20 @@ +/* + * 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 @@ -52,6 +69,7 @@ static struct grub_net_card emucard = { .name = "emu0", .driver = &emudriver, + .mtu = 1500, .default_address = { .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET, {.mac = {0, 1, 2, 3, 4, 5}} diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c index cd598ea72..588673177 100644 --- a/grub-core/net/drivers/i386/pc/pxe.c +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -345,6 +345,7 @@ GRUB_MOD_INIT(pxe) if (i == sizeof (grub_pxe_card.default_address.mac)) grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr, sizeof (grub_pxe_card.default_address.mac)); + grub_pxe_card.mtu = ui->mtu; grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 20d45ee3d..035a96220 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -28,7 +28,6 @@ struct grub_ofnetcard_data { char *path; grub_ieee1275_ihandle_t handle; - grub_uint32_t mtu; }; static grub_err_t @@ -85,7 +84,7 @@ get_card_packet (const struct grub_net_card *dev, struct grub_net_buff *nb) grub_netbuff_clear (nb); start_time = grub_get_time_ms (); do - rc = grub_ieee1275_read (data->handle, nb->data, data->mtu, &actual); + rc = grub_ieee1275_read (data->handle, nb->data, dev->mtu, &actual); while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); if (actual) { @@ -228,12 +227,15 @@ grub_ofnet_findcards (void) grub_ieee1275_finddevice (ofdata->path, &devhandle); - if (grub_ieee1275_get_integer_property - (devhandle, "max-frame-size", &(ofdata->mtu), - sizeof (ofdata->mtu), 0)) - { - ofdata->mtu = 1500; - } + { + grub_uint32_t t; + if (grub_ieee1275_get_integer_property (devhandle, + "max-frame-size", &t, + sizeof (t), 0)) + card->mtu = 1500; + else + card->mtu = t; + } if (grub_ieee1275_get_property (devhandle, "mac-address", &(lla.mac), 6, 0) diff --git a/include/grub/net.h b/include/grub/net.h index f6600690a..f3c0489e2 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -97,6 +97,7 @@ struct grub_net_card int opened; unsigned idle_poll_delay_ms; grub_uint64_t last_poll; + grub_size_t mtu; union { #ifdef GRUB_MACHINE_EFI From e2955971a3cd4e22e523fcc2ec07c1f3029060de Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 8 Jul 2011 20:38:12 +0200 Subject: [PATCH 05/37] move packet allocation to recv code to allow bigger buffers --- grub-core/net/drivers/efi/efinet.c | 58 ++++++++++++++++++-------- grub-core/net/drivers/emu/emunet.c | 31 ++++++++++---- grub-core/net/drivers/i386/pc/pxe.c | 30 ++++++++----- grub-core/net/drivers/ieee1275/ofnet.c | 22 +++++++--- grub-core/net/ip.c | 3 ++ grub-core/net/net.c | 13 +----- include/grub/net.h | 3 +- 7 files changed, 105 insertions(+), 55 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 0fc779966..906a688dc 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -42,43 +42,65 @@ send_card_buffer (const struct grub_net_card *dev, return GRUB_ERR_NONE; } -static grub_ssize_t -get_card_packet (const struct grub_net_card *dev, - struct grub_net_buff *nb) +static struct grub_net_buff * +get_card_packet (const struct grub_net_card *dev) { grub_efi_simple_network_t *net = dev->efi_net; grub_err_t err; grub_efi_status_t st; - grub_efi_uintn_t bufsize = 1500; + grub_efi_uintn_t bufsize = 1536; + struct grub_net_buff *nb; - err = grub_netbuff_clear (nb); - if (err) - return -1; + nb = grub_netbuff_alloc (bufsize); + if (!nb) + return NULL; - err = grub_netbuff_put (nb, 1500); - if (err) - return -1; + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + if (!nb) + { + grub_netbuff_free (nb); + return NULL; + } st = efi_call_7 (net->receive, net, NULL, &bufsize, nb->data, NULL, NULL, NULL); if (st == GRUB_EFI_BUFFER_TOO_SMALL) { - err = grub_netbuff_put (nb, bufsize - 1500); - if (err) - return -1; + grub_netbuff_free (nb); + + bufsize = ALIGN_UP (bufsize, 32); + + nb = grub_netbuff_alloc (bufsize); + if (!nb) + return NULL; + + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + if (!nb) + { + grub_netbuff_free (nb); + return NULL; + } + st = efi_call_7 (net->receive, net, NULL, &bufsize, nb->data, NULL, NULL, NULL); } if (st != GRUB_EFI_SUCCESS) { - grub_netbuff_clear (nb); - return -1; + grub_netbuff_free (nb); + return NULL; } - err = grub_netbuff_unput (nb, (nb->tail - nb->data) - bufsize); + err = grub_netbuff_put (nb, bufsize); if (err) - return -1; + { + grub_netbuff_free (nb); + return NULL; + } - return bufsize; + return nb; } static struct grub_net_card_driver efidriver = diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c index 96f773340..ff333d370 100644 --- a/grub-core/net/drivers/emu/emunet.c +++ b/grub-core/net/drivers/emu/emunet.c @@ -43,19 +43,34 @@ send_card_buffer (const struct grub_net_card *dev __attribute__ ((unused)), return GRUB_ERR_NONE; } -static grub_ssize_t -get_card_packet (const struct grub_net_card *dev __attribute__ ((unused)), - struct grub_net_buff *pack) +static struct grub_net_buff * +get_card_packet (const struct grub_net_card *dev __attribute__ ((unused))) { ssize_t actual; + struct grub_net_buff *nb; - grub_netbuff_clear (pack); - actual = read (fd, pack->data, 1500); + nb = grub_netbuff_alloc (1536); + if (!nb) + return NULL; + + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + if (!nb) + { + grub_netbuff_free (nb); + return NULL; + } + + actual = read (fd, nb->data, 1536); if (actual < 0) - return -1; - grub_netbuff_put (pack, actual); + { + grub_netbuff_free (nb); + return NULL; + } + grub_netbuff_put (nb, actual); - return actual; + return nb; } static struct grub_net_card_driver emudriver = diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c index 588673177..6c5f2fdc4 100644 --- a/grub-core/net/drivers/i386/pc/pxe.c +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -164,14 +164,13 @@ grub_pxe_scan (void) return bangpxe; } -static grub_ssize_t -grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused)), - struct grub_net_buff *buf) +static struct grub_net_buff * +grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused))) { struct grub_pxe_undi_isr *isr; static int in_progress = 0; char *ptr, *end; - int len; + struct grub_net_buff *buf; isr = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; @@ -183,7 +182,7 @@ grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused)), if (isr->status || isr->func_flag != GRUB_PXE_ISR_OUT_OURS) { in_progress = 0; - return -1; + return NULL; } grub_memset (isr, 0, sizeof (*isr)); isr->func_flag = GRUB_PXE_ISR_IN_PROCESS; @@ -201,17 +200,27 @@ grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused)), if (isr->status || isr->func_flag == GRUB_PXE_ISR_OUT_DONE) { in_progress = 0; - return -1; + return NULL; } grub_memset (isr, 0, sizeof (*isr)); isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT; grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry); } - grub_netbuff_put (buf, isr->frame_len); + buf = grub_netbuff_alloc (isr->frame_len); + if (!buf) + return NULL; + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (buf, 2); + if (!buf) + { + grub_netbuff_free (buf); + return NULL; + } ptr = buf->data; end = ptr + isr->frame_len; - len = isr->frame_len; + grub_netbuff_put (buf, isr->frame_len); grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len); ptr += isr->buffer_len; while (ptr < end) @@ -222,7 +231,8 @@ grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused)), if (isr->status || isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE) { in_progress = 1; - return -1; + grub_netbuff_free (buf); + return NULL; } grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len); @@ -230,7 +240,7 @@ grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused)), } in_progress = 1; - return len; + return buf; } static grub_err_t diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 035a96220..9c0c84c65 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -73,25 +73,35 @@ send_card_buffer (const struct grub_net_card *dev, struct grub_net_buff *pack) return GRUB_ERR_NONE; } -static grub_ssize_t -get_card_packet (const struct grub_net_card *dev, struct grub_net_buff *nb) +static struct grub_net_buff * +get_card_packet (const struct grub_net_card *dev) { grub_ssize_t actual; int rc; struct grub_ofnetcard_data *data = dev->data; grub_uint64_t start_time; + struct grub_net_buff *nb; - grub_netbuff_clear (nb); + nb = grub_netbuff_alloc (dev->mtu + 64); + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + if (!nb) + { + grub_netbuff_free (nb); + return NULL; + } start_time = grub_get_time_ms (); do - rc = grub_ieee1275_read (data->handle, nb->data, dev->mtu, &actual); + rc = grub_ieee1275_read (data->handle, nb->data, dev->mtu + 64, &actual); while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); if (actual) { grub_netbuff_put (nb, actual); - return actual; + return nb; } - return -1; + grub_netbuff_free (nb); + return NULL; } static struct grub_net_card_driver ofdriver = diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index ee3ccd4d6..eaecc9ea6 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -123,6 +123,9 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, struct grub_net_network_level_interface *inf = NULL; grub_net_network_level_address_t source; + if (((grub_addr_t) nb->data) & 3) + grub_fatal ("unaligned %p\n", nb->data); + if ((iph->verhdrlen >> 4) != 4) { grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 7bed078e3..ab0fbd3c5 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -764,19 +764,10 @@ 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; - grub_ssize_t actual; - nb = grub_netbuff_alloc (1500); + + nb = card->driver->recv (card); if (!nb) { - grub_print_error (); - card->last_poll = grub_get_time_ms (); - return; - } - - actual = card->driver->recv (card, nb); - if (actual < 0) - { - grub_netbuff_free (nb); card->last_poll = grub_get_time_ms (); break; } diff --git a/include/grub/net.h b/include/grub/net.h index f3c0489e2..83f260d63 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -64,8 +64,7 @@ struct grub_net_card_driver void (*close) (const struct grub_net_card *dev); grub_err_t (*send) (const struct grub_net_card *dev, struct grub_net_buff *buf); - grub_ssize_t (*recv) (const struct grub_net_card *dev, - struct grub_net_buff *buf); + struct grub_net_buff * (*recv) (const struct grub_net_card *dev); }; typedef struct grub_net_packet From fecdbd6b17e438f2e2ebd1b56e5745c6046e4ad0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 9 Jul 2011 00:27:27 +0200 Subject: [PATCH 06/37] support ip fragmentation --- grub-core/lib/priority_queue.c | 7 + grub-core/net/arp.c | 4 +- grub-core/net/ethernet.c | 2 + grub-core/net/ip.c | 414 +++++++++++++++++++++++++++------ grub-core/net/netbuff.c | 3 +- grub-core/net/tftp.c | 20 +- grub-core/net/udp.c | 2 + include/grub/net.h | 4 + include/grub/net/netbuff.h | 16 +- 9 files changed, 374 insertions(+), 98 deletions(-) diff --git a/grub-core/lib/priority_queue.c b/grub-core/lib/priority_queue.c index 5b02ef381..772b7b854 100644 --- a/grub-core/lib/priority_queue.c +++ b/grub-core/lib/priority_queue.c @@ -104,6 +104,13 @@ grub_priority_queue_top (grub_priority_queue_t pq) return element (pq, 0); } +void +grub_priority_queue_destroy (grub_priority_queue_t pq) +{ + grub_free (pq->els); + grub_free (pq); +} + grub_priority_queue_t grub_priority_queue_new (grub_size_t elsize, grub_comparator_t cmp) diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index d726f2c3a..b45bd4c9f 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -38,7 +38,7 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, struct grub_net_buff nb; struct arphdr *arp_header; grub_net_link_level_address_t target_hw_addr; - char *aux, arp_data[128]; + grub_uint8_t *aux, arp_data[128]; grub_err_t err; int i; @@ -74,7 +74,7 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, arp_header->hln = 6; arp_header->pln = 4; arp_header->op = grub_cpu_to_be16 (ARP_REQUEST); - aux = (char *) arp_header + sizeof (*arp_header); + aux = (grub_uint8_t *) arp_header + sizeof (*arp_header); /* Sender hardware address. */ grub_memcpy (aux, &inf->hwaddress.mac, 6); diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index acd33bcf6..c368eda54 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -57,6 +57,8 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, struct etherhdr *eth; grub_err_t err; + COMPILE_TIME_ASSERT (sizeof (*eth) < GRUB_NET_MAX_LINK_HEADER_SIZE); + err = grub_netbuff_push (nb, sizeof (*eth)); if (err) return err; diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index eaecc9ea6..9144eabc6 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include struct iphdr { grub_uint8_t verhdrlen; @@ -56,6 +58,39 @@ struct ip6hdr grub_uint8_t daddr[16]; } __attribute__ ((packed)); +static int +cmp (const void *a__, const void *b__) +{ + struct grub_net_buff *a_ = *(struct grub_net_buff **) a__; + struct grub_net_buff *b_ = *(struct grub_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 ((grub_be_to_cpu16 (a->frags) & OFFSET_MASK) + < (grub_be_to_cpu16 (b->frags) & OFFSET_MASK)) + return +1; + if ((grub_be_to_cpu16 (a->frags) & OFFSET_MASK) + > (grub_be_to_cpu16 (b->frags) & OFFSET_MASK)) + return -1; + return 0; +} + +struct reassemble +{ + struct reassemble *next; + grub_uint32_t source; + grub_uint32_t dest; + grub_uint16_t id; + grub_uint8_t proto; + grub_uint64_t last_time; + grub_priority_queue_t pq; + grub_uint8_t *asm_buffer; + grub_size_t total_len; + grub_size_t cur_ptr; +}; + +struct reassemble *reassembles; + grub_uint16_t grub_net_ip_chksum (void *ipv, grub_size_t len) { @@ -78,6 +113,72 @@ grub_net_ip_chksum (void *ipv, grub_size_t len) return grub_cpu_to_be16 ((~sum) & 0x0000FFFF); } +static int id = 0x2400; + +static grub_err_t +send_fragmented (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, + grub_net_link_level_address_t ll_target_addr) +{ + grub_size_t off = 0; + grub_size_t fraglen; + grub_err_t err; + + fraglen = (inf->card->mtu - sizeof (struct iphdr)) & ~7; + id++; + + while (nb->tail - nb->data) + { + grub_size_t len = fraglen; + struct grub_net_buff *nb2; + struct iphdr *iph; + + if ((grub_ssize_t) len > nb->tail - nb->data) + len = nb->tail - nb->data; + nb2 = grub_netbuff_alloc (fraglen + sizeof (struct iphdr) + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb2) + return grub_errno; + err = grub_netbuff_reserve (nb2, GRUB_NET_MAX_LINK_HEADER_SIZE); + if (err) + return err; + err = grub_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 = grub_cpu_to_be16 (len + sizeof (struct iphdr)); + iph->ident = grub_cpu_to_be16 (id); + iph->frags = grub_cpu_to_be16 (off | (((grub_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 = grub_net_ip_chksum ((void *) nb2->data, sizeof (*iph)); + err = grub_netbuff_put (nb2, len); + if (err) + return err; + grub_memcpy (iph + 1, nb->data, len); + err = grub_netbuff_pull (nb, len); + if (err) + return err; + err = send_ethernet_packet (inf, nb2, ll_target_addr, + GRUB_NET_ETHERTYPE_IP); + if (err) + return err; + } + 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, @@ -85,10 +186,19 @@ grub_net_send_ip_packet (struct grub_net_network_level_interface * inf, grub_net_ip_protocol_t proto) { struct iphdr *iph; - static int id = 0x2400; grub_net_link_level_address_t ll_target_addr; grub_err_t err; + COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV4_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 send_fragmented (inf, target, nb, proto, ll_target_addr); + grub_netbuff_push (nb, sizeof (*iph)); iph = (struct iphdr *) nb->data; @@ -105,88 +215,26 @@ grub_net_send_ip_packet (struct grub_net_network_level_interface * inf, iph->chksum = 0; iph->chksum = grub_net_ip_chksum ((void *) nb->data, sizeof (*iph)); - /* Determine link layer target address via ARP. */ - err = grub_net_arp_resolve (inf, target, &ll_target_addr); - if (err) - return err; return send_ethernet_packet (inf, nb, ll_target_addr, GRUB_NET_ETHERTYPE_IP); } -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) +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) { - struct iphdr *iph = (struct iphdr *) nb->data; - grub_err_t err; struct grub_net_network_level_interface *inf = NULL; + grub_err_t err; grub_net_network_level_address_t source; - if (((grub_addr_t) nb->data) & 3) - grub_fatal ("unaligned %p\n", nb->data); - - if ((iph->verhdrlen >> 4) != 4) - { - grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - - if ((iph->verhdrlen & 0xf) < 5) - { - grub_dprintf ("net", "IP header too short: %d\n", - (iph->verhdrlen & 0xf)); - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - - err = grub_netbuff_pull (nb, (iph->verhdrlen & 0xf) * sizeof (grub_uint32_t)); - 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 - + (iph->verhdrlen & 0xf) - * sizeof (grub_uint32_t)); - 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; - } - } - - /* Fragmented packet. Bad. */ - if (((grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS) != 0) - || (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) != 0) - { - /* FIXME. */ - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - /* DHCP needs special treatment since we don't know IP yet. */ { struct udphdr *udph; udph = (struct udphdr *) nb->data; - if (iph->protocol == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) { FOR_NET_NETWORK_LEVEL_INTERFACES (inf) if (inf->card == card @@ -209,7 +257,7 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, { if (inf->card == card && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && inf->address.ipv4 == iph->dest + && inf->address.ipv4 == dst && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) break; } @@ -221,9 +269,9 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, } source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - source.ipv4 = iph->src; + source.ipv4 = src; - switch (iph->protocol) + switch (proto) { case GRUB_NET_IP_UDP: return grub_net_recv_udp_packet (nb, inf, &source); @@ -233,6 +281,224 @@ grub_net_recv_ip_packets (struct grub_net_buff * nb, grub_netbuff_free (nb); break; } + return GRUB_ERR_NONE; +} + +static void +free_rsm (struct reassemble *rsm) +{ + struct grub_net_buff **nb; + while ((nb = grub_priority_queue_top (rsm->pq))) + { + grub_netbuff_free (*nb); + grub_priority_queue_pop (rsm->pq); + } + grub_free (rsm->asm_buffer); + grub_priority_queue_destroy (rsm->pq); +} + +static void +free_old_fragments (void) +{ + struct reassemble *rsm, **prev; + grub_uint64_t limit_time = grub_get_time_ms () - 90000; + + for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev) + if (rsm->last_time < limit_time) + { + *prev = rsm->next; + free_rsm (rsm); + } +} + +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) +{ + struct iphdr *iph = (struct iphdr *) nb->data; + grub_err_t err; + struct reassemble *rsm, **prev; + + if ((iph->verhdrlen >> 4) != 4) + { + grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + if ((iph->verhdrlen & 0xf) < 5) + { + grub_dprintf ("net", "IP header too short: %d\n", + (iph->verhdrlen & 0xf)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + 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_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + /* 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; + } + } + + /* Unfragmented packet. Good. */ + if (((grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS) == 0) + && (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) == 0) + { + err = grub_netbuff_pull (nb, ((iph->verhdrlen & 0xf) + * sizeof (grub_uint32_t))); + if (err) + { + grub_netbuff_free (nb); + return err; + } + return handle_dgram (nb, card, hwaddress, iph->protocol, + iph->src, iph->dest); + } + + 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 = grub_malloc (sizeof (*rsm)); + if (!rsm) + return grub_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 = grub_priority_queue_new (sizeof (struct grub_net_buff **), cmp); + if (!rsm->pq) + { + grub_free (rsm); + return grub_errno; + } + rsm->asm_buffer = 0; + rsm->total_len = 0; + rsm->cur_ptr = 0; + } + + rsm->last_time = grub_get_time_ms (); + free_old_fragments (); + + err = grub_priority_queue_push (rsm->pq, &nb); + if (err) + return err; + + if (!(grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS)) + { + rsm->total_len = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) + + (nb->tail - nb->data)); + rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t)); + rsm->asm_buffer = grub_zalloc (rsm->total_len); + if (!rsm->asm_buffer) + { + *prev = rsm->next; + free_rsm (rsm); + return grub_errno; + } + } + if (!rsm->asm_buffer) + return GRUB_ERR_NONE; + + while (1) + { + struct grub_net_buff **nb_top_p, *nb_top; + grub_size_t copy; + grub_uint8_t *res; + grub_size_t res_len; + struct grub_net_buff *ret; + grub_net_ip_protocol_t proto; + grub_uint32_t src; + grub_uint32_t dst; + + nb_top_p = grub_priority_queue_top (rsm->pq); + if (!nb_top_p) + return GRUB_ERR_NONE; + nb_top = *nb_top_p; + grub_priority_queue_pop (rsm->pq); + iph = (struct iphdr *) nb_top->data; + err = grub_netbuff_pull (nb_top, ((iph->verhdrlen & 0xf) + * sizeof (grub_uint32_t))); + if (err) + { + grub_netbuff_free (nb_top); + return err; + } + if (rsm->cur_ptr < (grub_size_t) 8 * (grub_be_to_cpu16 (iph->frags) + & OFFSET_MASK)) + return GRUB_ERR_NONE; + + rsm->cur_ptr = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) + + (nb_top->tail - nb_top->head)); + if ((grub_size_t) 8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) + >= rsm->total_len) + { + grub_netbuff_free (nb_top); + continue; + } + copy = nb_top->tail - nb_top->data; + if (rsm->total_len - 8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) + < copy) + copy = rsm->total_len - 8 * (grub_be_to_cpu16 (iph->frags) + & OFFSET_MASK); + grub_memcpy (&rsm->asm_buffer[8 * (grub_be_to_cpu16 (iph->frags) + & OFFSET_MASK)], + nb_top->data, copy); + + if ((grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS)) + continue; + + res = rsm->asm_buffer; + proto = rsm->proto; + src = rsm->source; + dst = rsm->dest; + + rsm->asm_buffer = 0; + res_len = rsm->total_len; + *prev = rsm->next; + free_rsm (rsm); + ret = grub_malloc (sizeof (*ret)); + if (!ret) + { + grub_free (res); + return grub_errno; + } + ret->data = ret->head = res; + ret->tail = ret->end = res + res_len; + return handle_dgram (ret, card, hwaddress, proto, src, dst); + } return GRUB_ERR_NONE; } diff --git a/grub-core/net/netbuff.c b/grub-core/net/netbuff.c index d20104ab0..cb6106fc7 100644 --- a/grub-core/net/netbuff.c +++ b/grub-core/net/netbuff.c @@ -21,7 +21,6 @@ #include #include - grub_err_t grub_netbuff_put (struct grub_net_buff *nb, grub_size_t len) { @@ -87,7 +86,7 @@ grub_netbuff_alloc (grub_size_t len) return NULL; nb = (struct grub_net_buff *) ((grub_uint8_t *) data + len); nb->head = nb->data = nb->tail = data; - nb->end = (char *) nb; + nb->end = (grub_uint8_t *) nb; return nb; } diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 13c4971f9..659e836c0 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -112,10 +112,10 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), { grub_file_t file = f; struct tftphdr *tftph = (void *) nb->data; - char nbdata[512]; + grub_uint8_t nbdata[512]; tftp_data_t data = file->data; grub_err_t err; - char *ptr; + grub_uint8_t *ptr; struct grub_net_buff nb_ack; nb_ack.head = nbdata; @@ -130,15 +130,11 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), for (ptr = nb->data + sizeof (tftph->opcode); ptr < nb->tail;) { if (grub_memcmp (ptr, "tsize\0", sizeof ("tsize\0") - 1) == 0) - { - data->file_size = grub_strtoul (ptr + sizeof ("tsize\0") - 1, - 0, 0); - } + data->file_size = grub_strtoul ((char *) ptr + sizeof ("tsize\0") + - 1, 0, 0); if (grub_memcmp (ptr, "blksize\0", sizeof ("blksize\0") - 1) == 0) - { - data->block_size = grub_strtoul (ptr + sizeof ("blksize\0") - 1, - 0, 0); - } + data->block_size = grub_strtoul ((char *) ptr + sizeof ("blksize\0") + - 1, 0, 0); while (ptr < nb->tail && *ptr) ptr++; ptr++; @@ -210,7 +206,7 @@ tftp_open (struct grub_file *file, const char *filename) int i; int rrqlen; int hdrlen; - char open_data[1500]; + grub_uint8_t open_data[1500]; struct grub_net_buff nb; tftp_data_t data; grub_err_t err; @@ -312,7 +308,7 @@ tftp_close (struct grub_file *file) if (data->sock) { - char nbdata[512]; + grub_uint8_t nbdata[512]; grub_err_t err; struct grub_net_buff nb_err; struct tftphdr *tftph; diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index 15bc1f490..0a43fe24f 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -110,6 +110,8 @@ grub_net_send_udp_packet (const grub_net_udp_socket_t socket, struct udphdr *udph; grub_err_t err; + COMPILE_TIME_ASSERT (GRUB_NET_UDP_HEADER_SIZE == sizeof (*udph)); + err = grub_netbuff_push (nb, sizeof (*udph)); if (err) return err; diff --git a/include/grub/net.h b/include/grub/net.h index 83f260d63..e7631b884 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -27,6 +27,10 @@ #include #include +#define GRUB_NET_MAX_LINK_HEADER_SIZE 64 +#define GRUB_NET_UDP_HEADER_SIZE 8 +#define GRUB_NET_OUR_IPV4_HEADER_SIZE 20 + typedef enum grub_link_level_protocol_id { GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET diff --git a/include/grub/net/netbuff.h b/include/grub/net/netbuff.h index 245e813c3..5fafd89f6 100644 --- a/include/grub/net/netbuff.h +++ b/include/grub/net/netbuff.h @@ -8,14 +8,14 @@ struct grub_net_buff { - /*Pointer to the start of the buffer*/ - char *head; - /*Pointer to the data */ - char *data; - /*Pointer to the tail */ - char *tail; - /*Pointer to the end of the buffer*/ - char *end; + /* Pointer to the start of the buffer. */ + grub_uint8_t *head; + /* Pointer to the data. */ + grub_uint8_t *data; + /* Pointer to the tail. */ + grub_uint8_t *tail; + /* Pointer to the end of the buffer. */ + grub_uint8_t *end; }; grub_err_t grub_netbuff_put (struct grub_net_buff *net_buff ,grub_size_t len); From 92bb3cfc1792b0fe0cc6888223f319dfd9384432 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 9 Jul 2011 01:28:47 +0200 Subject: [PATCH 07/37] Use priority queue to reassemble TFTP packets --- grub-core/lib/priority_queue.c | 2 +- grub-core/net/tftp.c | 187 +++++++++++++++++++++++---------- include/grub/priority_queue.h | 2 +- 3 files changed, 135 insertions(+), 56 deletions(-) diff --git a/grub-core/lib/priority_queue.c b/grub-core/lib/priority_queue.c index 772b7b854..0cf258ecf 100644 --- a/grub-core/lib/priority_queue.c +++ b/grub-core/lib/priority_queue.c @@ -136,7 +136,7 @@ grub_priority_queue_new (grub_size_t elsize, /* Heap property: pq->cmp (element (pq, p), element (pq, parent (p))) <= 0. */ grub_err_t -grub_priority_queue_push (grub_priority_queue_t pq, void *el) +grub_priority_queue_push (grub_priority_queue_t pq, const void *el) { grub_size_t p; if (pq->used == pq->allocated) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 659e836c0..16616cfea 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -103,8 +105,49 @@ typedef struct tftp_data grub_uint32_t block_size; int have_oack; grub_net_udp_socket_t sock; + grub_priority_queue_t pq; } *tftp_data_t; +static int +cmp (const void *a__, const void *b__) +{ + struct grub_net_buff *a_ = *(struct grub_net_buff **) a__; + struct grub_net_buff *b_ = *(struct grub_net_buff **) b__; + struct tftphdr *a = (struct tftphdr *) a_->data; + struct tftphdr *b = (struct tftphdr *) b_->data; + /* We want the first elements to be on top. */ + if (grub_be_to_cpu16 (a->u.data.block) < grub_be_to_cpu16 (b->u.data.block)) + return +1; + if (grub_be_to_cpu16 (a->u.data.block) > grub_be_to_cpu16 (b->u.data.block)) + return -1; + return 0; +} + +static grub_err_t +ack (grub_net_udp_socket_t sock, grub_uint16_t block) +{ + struct tftphdr *tftph_ack; + grub_uint8_t nbdata[512]; + struct grub_net_buff nb_ack; + grub_err_t err; + + nb_ack.head = nbdata; + nb_ack.end = nbdata + sizeof (nbdata); + grub_netbuff_clear (&nb_ack); + grub_netbuff_reserve (&nb_ack, 512); + err = grub_netbuff_push (&nb_ack, sizeof (tftph_ack->opcode) + + sizeof (tftph_ack->u.ack.block)); + if (err) + return err; + + tftph_ack = (struct tftphdr *) nb_ack.data; + tftph_ack->opcode = grub_cpu_to_be16 (TFTP_ACK); + tftph_ack->u.ack.block = block; + + err = grub_net_send_udp_packet (sock, &nb_ack); + return err; +} + static grub_err_t tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), struct grub_net_buff *nb, @@ -112,14 +155,12 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), { grub_file_t file = f; struct tftphdr *tftph = (void *) nb->data; - grub_uint8_t nbdata[512]; tftp_data_t data = file->data; grub_err_t err; grub_uint8_t *ptr; - struct grub_net_buff nb_ack; - nb_ack.head = nbdata; - nb_ack.end = nbdata + sizeof (nbdata); + if (nb->tail - nb->data < (grub_ssize_t) sizeof (tftph->opcode)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "TFTP packet too small"); tftph = (struct tftphdr *) nb->data; switch (grub_be_to_cpu16 (tftph->opcode)) @@ -141,61 +182,89 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), } data->block = 0; grub_netbuff_free (nb); - break; - case TFTP_DATA: - err = grub_netbuff_pull (nb, sizeof (tftph->opcode) + - sizeof (tftph->u.data.block)); + err = ack (data->sock, 0); if (err) return err; - if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1) - { - unsigned size = nb->tail - nb->data; - data->block++; - if (size < data->block_size) - { - file->device->net->eof = 1; - } - /* Prevent garbage in broken cards. */ - if (size > data->block_size) - { - err = grub_netbuff_unput (nb, size - data->block_size); - if (err) - return err; - } - /* If there is data, puts packet in socket list. */ - if ((nb->tail - nb->data) > 0) - grub_net_put_packet (&file->device->net->packs, nb); - else - grub_netbuff_free (nb); - } - else - { - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - break; + return GRUB_ERR_NONE; + case TFTP_DATA: + if (nb->tail - nb->data < (grub_ssize_t) (sizeof (tftph->opcode) + + sizeof (tftph->u.data.block))) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "TFTP packet too small"); + err = ack (data->sock, tftph->u.data.block); + if (err) + return err; + + err = grub_priority_queue_push (data->pq, &nb); + if (err) + return err; + + { + struct grub_net_buff **nb_top_p, *nb_top; + while (1) + { + nb_top_p = grub_priority_queue_top (data->pq); + if (!nb_top_p) + return GRUB_ERR_NONE; + nb_top = *nb_top_p; + tftph = (struct tftphdr *) nb_top->data; + grub_refresh (); + if (grub_be_to_cpu16 (tftph->u.data.block) >= data->block + 1) + break; + grub_priority_queue_pop (data->pq); + } + if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1) + { + unsigned size; + + grub_priority_queue_pop (data->pq); + + err = grub_netbuff_pull (nb_top, sizeof (tftph->opcode) + + sizeof (tftph->u.data.block)); + if (err) + return err; + size = nb_top->tail - nb_top->data; + + data->block++; + if (size < data->block_size) + { + file->device->net->eof = 1; + grub_net_udp_close (data->sock); + data->sock = NULL; + } + /* Prevent garbage in broken cards. Is it still necessary + given that IP implementation has been fixed? + */ + if (size > data->block_size) + { + err = grub_netbuff_unput (nb_top, size - data->block_size); + if (err) + return err; + } + /* If there is data, puts packet in socket list. */ + if ((nb_top->tail - nb_top->data) > 0) + grub_net_put_packet (&file->device->net->packs, nb_top); + else + grub_netbuff_free (nb); + } + } + return GRUB_ERR_NONE; case TFTP_ERROR: grub_netbuff_free (nb); return grub_error (GRUB_ERR_IO, (char *) tftph->u.err.errmsg); + default: + grub_netbuff_free (nb); + return GRUB_ERR_NONE; } - grub_netbuff_clear (&nb_ack); - grub_netbuff_reserve (&nb_ack, 512); - err = grub_netbuff_push (&nb_ack, sizeof (tftph->opcode) - + sizeof (tftph->u.ack.block)); - if (err) - return err; +} - tftph = (struct tftphdr *) nb_ack.data; - tftph->opcode = grub_cpu_to_be16 (TFTP_ACK); - tftph->u.ack.block = grub_cpu_to_be16 (data->block); +static void +destroy_pq (tftp_data_t data) +{ + struct grub_net_buff **nb_p; + while ((nb_p = grub_priority_queue_top (data->pq))) + grub_netbuff_free (*nb_p); - err = grub_net_send_udp_packet (data->sock, &nb_ack); - if (file->device->net->eof) - { - grub_net_udp_close (data->sock); - data->sock = NULL; - } - return err; + grub_priority_queue_destroy (data->pq); } static grub_err_t @@ -261,16 +330,25 @@ tftp_open (struct grub_file *file, const char *filename) file->not_easily_seekable = 1; file->data = data; + + data->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp); + if (!data->pq) + return grub_errno; + data->sock = grub_net_udp_open (file->device->net->server, TFTP_SERVER_PORT, tftp_receive, file); if (!data->sock) - return grub_errno; + { + destroy_pq (data); + return grub_errno; + } err = grub_net_send_udp_packet (data->sock, &nb); if (err) { grub_net_udp_close (data->sock); + destroy_pq (data); return err; } @@ -278,8 +356,6 @@ tftp_open (struct grub_file *file, const char *filename) for (i = 0; i < 3; i++) { grub_net_poll_cards (100); - if (grub_errno) - return grub_errno; if (data->have_oack) break; /* Retry. */ @@ -287,6 +363,7 @@ tftp_open (struct grub_file *file, const char *filename) if (err) { grub_net_udp_close (data->sock); + destroy_pq (data); return err; } } @@ -294,6 +371,7 @@ tftp_open (struct grub_file *file, const char *filename) if (!data->have_oack) { grub_net_udp_close (data->sock); + destroy_pq (data); return grub_error (GRUB_ERR_TIMEOUT, "Time out opening tftp."); } file->size = data->file_size; @@ -334,6 +412,7 @@ tftp_close (struct grub_file *file) grub_print_error (); grub_net_udp_close (data->sock); } + destroy_pq (data); grub_free (data); return GRUB_ERR_NONE; } diff --git a/include/grub/priority_queue.h b/include/grub/priority_queue.h index 335ea9a39..a5d98c8c9 100644 --- a/include/grub/priority_queue.h +++ b/include/grub/priority_queue.h @@ -31,6 +31,6 @@ grub_priority_queue_t grub_priority_queue_new (grub_size_t elsize, void grub_priority_queue_destroy (grub_priority_queue_t pq); void *grub_priority_queue_top (grub_priority_queue_t pq); void grub_priority_queue_pop (grub_priority_queue_t pq); -grub_err_t grub_priority_queue_push (grub_priority_queue_t pq, void *el); +grub_err_t grub_priority_queue_push (grub_priority_queue_t pq, const void *el); #endif From 3a7af37260e317bd555988d9e8a457d9d6767247 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 10 Jul 2011 08:46:48 +0200 Subject: [PATCH 08/37] basic tcp implementation --- grub-core/Makefile.core.def | 1 + grub-core/net/bootp.c | 6 +- grub-core/net/ip.c | 37 ++- grub-core/net/net.c | 2 + grub-core/net/tcp.c | 637 ++++++++++++++++++++++++++++++++++++ grub-core/net/tftp.c | 19 +- grub-core/net/udp.c | 51 ++- include/grub/err.h | 1 + include/grub/net.h | 3 + include/grub/net/ip.h | 10 + include/grub/net/tcp.h | 44 +++ include/grub/net/udp.h | 18 + 12 files changed, 802 insertions(+), 27 deletions(-) create mode 100644 grub-core/net/tcp.c create mode 100644 include/grub/net/tcp.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 9bc893595..673092b4f 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1597,6 +1597,7 @@ module = { common = net/bootp.c; common = net/ip.c; common = net/udp.c; + common = net/tcp.c; common = net/icmp.c; common = net/ethernet.c; common = net/arp.c; diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 1428c2a07..b1db05147 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -473,7 +473,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), t = 0; } pack->ident = grub_cpu_to_be32 (t); - pack->seconds = 0;//grub_cpu_to_be16 (t); + pack->seconds = grub_cpu_to_be16 (t); grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6); @@ -484,9 +484,11 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), udph->dst = grub_cpu_to_be16 (67); udph->chksum = 0; udph->len = grub_cpu_to_be16 (nb->tail - nb->data); - target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; target.ipv4 = 0xffffffff; + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &ifaces[j].address, + &target); err = grub_net_send_ip_packet (&ifaces[j], &target, nb, GRUB_NET_IP_UDP); diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 9144eabc6..69579970f 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -100,13 +100,13 @@ grub_net_ip_chksum (void *ipv, grub_size_t len) for (; len >= 2; len -= 2) { sum += grub_be_to_cpu16 (*(ip++)); - if (sum > 0xFFFF) + if (sum >= 0xFFFF) sum -= 0xFFFF; } if (len) { sum += *((grub_uint8_t *) ip) << 8; - if (sum > 0xFFFF) + if (sum >= 0xFFFF) sum -= 0xFFFF; } @@ -229,6 +229,13 @@ handle_dgram (struct grub_net_buff *nb, 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. */ { @@ -241,6 +248,27 @@ handle_dgram (struct grub_net_buff *nb, && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) { + if (udph->chksum) + { + grub_uint16_t chk, expected; + chk = udph->chksum; + udph->chksum = 0; + expected = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_UDP, + &source, + &dest); + if (expected != chk) + { + grub_dprintf ("net", "Invalid UDP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph->chksum = chk; + } + err = grub_netbuff_pull (nb, sizeof (*udph)); if (err) return err; @@ -268,13 +296,12 @@ handle_dgram (struct grub_net_buff *nb, return GRUB_ERR_NONE; } - source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - source.ipv4 = src; - switch (proto) { case GRUB_NET_IP_UDP: return grub_net_recv_udp_packet (nb, inf, &source); + case GRUB_NET_IP_TCP: + return grub_net_recv_tcp_packet (nb, inf, &source); case GRUB_NET_IP_ICMP: return grub_net_recv_icmp_packet (nb, inf, &source); default: diff --git a/grub-core/net/net.c b/grub-core/net/net.c index ab0fbd3c5..0ff8d01ff 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -793,6 +793,7 @@ grub_net_poll_cards (unsigned time) while ((grub_get_time_ms () - start_time) < time) receive_packets (card); } + grub_net_tcp_retransmit (); } static void @@ -807,6 +808,7 @@ grub_net_poll_cards_idle_real (void) || ctime >= card->last_poll + card->idle_poll_delay_ms) receive_packets (card); } + grub_net_tcp_retransmit (); } /* Read from the packets list*/ diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c new file mode 100644 index 000000000..3bc3d5c9c --- /dev/null +++ b/grub-core/net/tcp.c @@ -0,0 +1,637 @@ +/* + * 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 +#include +#include +#include + +#define TCP_SYN_RETRANSMISSION_TIMEOUT 1000 +#define TCP_SYN_RETRANSMISSION_COUNT 3 +#define TCP_RETRANSMISSION_TIMEOUT 10000 +#define TCP_RETRANSMISSION_COUNT 5 + +struct unacked +{ + struct unacked *next; + struct grub_net_buff *nb; + grub_uint64_t last_try; + int try_count; +}; + +enum + { + TCP_FIN = 0x1, + TCP_SYN = 0x2, + TCP_ACK = 0x10 + }; + +struct grub_net_tcp_socket +{ + struct grub_net_tcp_socket *next; + + int established; + int i_closed; + int they_closed; + int in_port; + int out_port; + int errors; + grub_uint32_t my_start_seq; + grub_uint32_t my_cur_seq; + grub_uint32_t their_start_seq; + grub_uint32_t their_cur_seq; + grub_uint16_t my_window; + struct unacked *unack_first; + struct unacked *unack_last; + grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, struct grub_net_buff *nb, + void *recv); + void (*error_hook) (grub_net_tcp_socket_t sock, void *recv); + void *hook_data; + grub_net_network_level_address_t out_nla; + struct grub_net_network_level_interface *inf; + grub_net_packets_t packs; + grub_priority_queue_t pq; +}; + +struct tcphdr +{ + grub_uint16_t src; + grub_uint16_t dst; + grub_uint32_t seqnr; + grub_uint32_t ack; + grub_uint16_t flags; + grub_uint16_t window; + grub_uint16_t checksum; + grub_uint16_t urgent; +} __attribute__ ((packed)); + +struct tcp_pseudohdr +{ + grub_uint32_t src; + grub_uint32_t dst; + grub_uint8_t zero; + grub_uint8_t proto; + grub_uint16_t tcp_length; +} __attribute__ ((packed)); + +struct grub_net_tcp_socket *tcp_sockets; + +#define FOR_TCP_SOCKETS(var) for (var = tcp_sockets; var; var = var->next) + +static inline void +tcp_socket_register (grub_net_tcp_socket_t sock) +{ + grub_list_push (GRUB_AS_LIST_P (&tcp_sockets), + GRUB_AS_LIST (sock)); +} + +static grub_err_t +tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) +{ + grub_err_t err; + grub_uint8_t *nbd; + struct unacked *unack; + struct tcphdr *tcph; + grub_size_t size; + + tcph = (struct tcphdr *) nb->data; + + tcph->seqnr = grub_cpu_to_be32 (socket->my_cur_seq); + size = (nb->tail - nb->data - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4); + if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) + size++; + socket->my_cur_seq += size; + tcph->src = grub_cpu_to_be16 (socket->in_port); + tcph->dst = grub_cpu_to_be16 (socket->out_port); + tcph->checksum = 0; + tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, + &socket->inf->address, + &socket->out_nla); + nbd = nb->data; + if (size) + { + unack = grub_malloc (sizeof (*unack)); + if (!unack) + return grub_errno; + + unack->next = NULL; + unack->nb = nb; + unack->try_count = 1; + unack->last_try = grub_get_time_ms (); + if (!socket->unack_last) + socket->unack_first = socket->unack_last = unack; + else + socket->unack_last->next = unack; + } + + err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, + GRUB_NET_IP_TCP); + if (err) + return err; + nb->data = nbd; + return GRUB_ERR_NONE; +} + +void +grub_net_tcp_close (grub_net_tcp_socket_t sock) +{ + struct grub_net_buff *nb_fin; + struct tcphdr *tcph_fin; + grub_err_t err; + + sock->i_closed = 1; + + nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + 128); + if (!nb_fin) + return; + err = grub_netbuff_reserve (nb_fin, 128); + if (err) + { + grub_netbuff_free (nb_fin); + grub_dprintf ("net", "error closing socket\n"); + grub_errno = GRUB_ERR_NONE; + return; + } + + err = grub_netbuff_put (nb_fin, sizeof (*tcph_fin)); + if (err) + { + grub_netbuff_free (nb_fin); + grub_dprintf ("net", "error closing socket\n"); + grub_errno = GRUB_ERR_NONE; + return; + } + tcph_fin = (void *) nb_fin->data; + tcph_fin->ack = grub_cpu_to_be32 (0); + tcph_fin->flags = grub_cpu_to_be16 ((5 << 12) | TCP_FIN); + tcph_fin->window = grub_cpu_to_be16 (0); + tcph_fin->urgent = 0; + err = tcp_send (nb_fin, sock); + if (err) + { + grub_netbuff_free (nb_fin); + grub_dprintf ("net", "error closing socket\n"); + grub_errno = GRUB_ERR_NONE; + } + return; +} + +static void +ack (grub_net_tcp_socket_t sock) +{ + struct grub_net_buff *nb_ack; + struct tcphdr *tcph_ack; + grub_err_t err; + + nb_ack = grub_netbuff_alloc (sizeof (*tcph_ack) + 128); + if (!nb_ack) + return; + err = grub_netbuff_reserve (nb_ack, 128); + if (err) + { + grub_netbuff_free (nb_ack); + grub_dprintf ("net", "error closing socket\n"); + grub_errno = GRUB_ERR_NONE; + return; + } + + err = grub_netbuff_put (nb_ack, sizeof (*tcph_ack)); + if (err) + { + grub_netbuff_free (nb_ack); + grub_dprintf ("net", "error closing socket\n"); + grub_errno = GRUB_ERR_NONE; + return; + } + tcph_ack = (void *) nb_ack->data; + tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); + tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK); + tcph_ack->window = grub_cpu_to_be16 (sock->my_window); + tcph_ack->urgent = 0; + tcph_ack->src = grub_cpu_to_be16 (sock->in_port); + tcph_ack->dst = grub_cpu_to_be16 (sock->out_port); + err = tcp_send (nb_ack, sock); + if (err) + { + grub_dprintf ("net", "error acking socket\n"); + grub_errno = GRUB_ERR_NONE; + } +} + +static void +kill_socket (grub_net_tcp_socket_t sock) +{ + if (sock->error_hook) + sock->error_hook (sock, sock->hook_data); + grub_net_tcp_close (sock); +} + +void +grub_net_tcp_retransmit (void) +{ + grub_net_tcp_socket_t sock; + grub_uint64_t ctime = grub_get_time_ms (); + grub_uint64_t limit_time = ctime - TCP_RETRANSMISSION_TIMEOUT; + + FOR_TCP_SOCKETS (sock) + { + struct unacked *unack; + for (unack = sock->unack_first; unack; unack = unack->next) + { + grub_uint8_t *nbd; + grub_err_t err; + + if (unack->last_try > limit_time) + continue; + + if (unack->try_count > TCP_RETRANSMISSION_COUNT) + { + kill_socket (sock); + break; + } + unack->try_count++; + unack->last_try = ctime; + nbd = unack->nb->data; + err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla), unack->nb, + GRUB_NET_IP_TCP); + unack->nb->data = nbd; + if (err) + { + grub_dprintf ("net", "TCP retransmit failed: %s\n", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + } + } + } +} + +grub_uint16_t +grub_net_ip_transport_checksum (struct grub_net_buff *nb, + grub_uint16_t proto, + 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))); + c = (grub_uint32_t) a + (grub_uint32_t) b; + if (c >= 0xffff) + c -= 0xffff; + return grub_cpu_to_be16 (~c); +} + +/* FIXME: overflow. */ +static int +cmp (const void *a__, const void *b__) +{ + struct grub_net_buff *a_ = *(struct grub_net_buff **) a__; + struct grub_net_buff *b_ = *(struct grub_net_buff **) b__; + struct tcphdr *a = (struct tcphdr *) a_->data; + struct tcphdr *b = (struct tcphdr *) b_->data; + /* We want the first elements to be on top. */ + if (grub_be_to_cpu32 (a->seqnr) < grub_be_to_cpu32 (b->seqnr)) + return +1; + if (grub_be_to_cpu32 (a->seqnr) > grub_be_to_cpu32 (b->seqnr)) + return -1; + return 0; +} + +static void +destroy_pq (grub_net_tcp_socket_t sock) +{ + struct grub_net_buff **nb_p; + while ((nb_p = grub_priority_queue_top (sock->pq))) + grub_netbuff_free (*nb_p); + + grub_priority_queue_destroy (sock->pq); +} + +grub_net_tcp_socket_t +grub_net_tcp_open (char *server, + grub_uint16_t out_port, + grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, + struct grub_net_buff *nb, + void *data), + void (*error_hook) (grub_net_tcp_socket_t sock, + void *data), + void *hook_data) +{ + grub_err_t err; + grub_net_network_level_address_t addr; + struct grub_net_network_level_interface *inf; + grub_net_network_level_address_t gateway; + grub_net_tcp_socket_t socket; + static grub_uint16_t in_port = 21550; + struct grub_net_buff *nb; + struct tcphdr *tcph; + int i; + grub_uint8_t *nbd; + + err = grub_net_resolve_address (server, &addr); + if (err) + return NULL; + + if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "not a IPv4 address"); + return NULL; + } + + err = grub_net_route_address (addr, &gateway, &inf); + if (err) + return NULL; + + socket = grub_zalloc (sizeof (*socket)); + if (socket == NULL) + return NULL; + + socket->out_port = out_port; + socket->inf = inf; + socket->out_nla = addr; + socket->in_port = in_port++; + socket->recv_hook = recv_hook; + socket->error_hook = error_hook; + socket->hook_data = hook_data; + + nb = grub_netbuff_alloc (sizeof (*tcph) + 128); + if (!nb) + return NULL; + err = grub_netbuff_reserve (nb, 128); + if (err) + { + grub_netbuff_free (nb); + return NULL; + } + + err = grub_netbuff_put (nb, sizeof (*tcph)); + if (err) + { + grub_netbuff_free (nb); + return NULL; + } + socket->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp); + if (!socket->pq) + { + grub_netbuff_free (nb); + return NULL; + } + + tcph = (void *) nb->data; + socket->my_start_seq = grub_get_time_ms (); + socket->my_cur_seq = socket->my_start_seq + 1; + socket->my_window = 8192; + tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq); + tcph->ack = grub_cpu_to_be32 (0); + tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN); + tcph->window = grub_cpu_to_be16 (socket->my_window); + tcph->urgent = 0; + tcph->src = grub_cpu_to_be16 (socket->in_port); + tcph->dst = grub_cpu_to_be16 (socket->out_port); + tcph->checksum = 0; + tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, + &socket->inf->address, + &socket->out_nla); + + tcp_socket_register (socket); + + nbd = nb->data; + for (i = 0; i < TCP_SYN_RETRANSMISSION_COUNT; i++) + { + int j; + nb->data = nbd; + err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, + GRUB_NET_IP_TCP); + if (err) + { + grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets), + GRUB_AS_LIST (socket)); + grub_free (socket); + grub_netbuff_free (nb); + return NULL; + } + for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 10 + && !socket->established); j++) + grub_net_poll_cards (10); + if (socket->established) + break; + } + if (!socket->established) + { + grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets), + GRUB_AS_LIST (socket)); + grub_free (socket); + grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer"); + + grub_netbuff_free (nb); + destroy_pq (socket); + return NULL; + } + + grub_netbuff_free (nb); + return socket; +} + +grub_err_t +grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, + struct grub_net_buff *nb) +{ + struct tcphdr *tcph; + grub_err_t err; + + err = grub_netbuff_push (nb, sizeof (*tcph)); + if (err) + return err; + + tcph = (struct tcphdr *) nb->data; + tcph->ack = grub_cpu_to_be32 (0); + tcph->flags = grub_cpu_to_be16 ((5 << 12)); + tcph->window = grub_cpu_to_be16 (socket->my_window); + tcph->urgent = 0; + return tcp_send (nb, socket); +} + +grub_err_t +grub_net_recv_tcp_packet (struct grub_net_buff *nb, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *source) +{ + struct tcphdr *tcph; + grub_net_tcp_socket_t sock; + grub_err_t err; + + tcph = (struct tcphdr *) nb->data; + if ((grub_be_to_cpu16 (tcph->flags) >> 12) < 5) + { + grub_dprintf ("net", "TCP header too short: %u\n", + grub_be_to_cpu16 (tcph->flags) >> 12); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (nb->tail - nb->data < (grub_ssize_t) ((grub_be_to_cpu16 (tcph->flags) + >> 12) * sizeof (grub_uint32_t))) + { + grub_dprintf ("net", "TCP packet too short: %" PRIuGRUB_SIZE "\n", + nb->tail - nb->data); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + FOR_TCP_SOCKETS (sock) + { + if (grub_be_to_cpu16 (tcph->dst) == sock->in_port + && grub_be_to_cpu16 (tcph->src) == sock->out_port + && inf == sock->inf + && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && source->ipv4 == sock->out_nla.ipv4) + { + if (tcph->checksum) + { + grub_uint16_t chk, expected; + chk = tcph->checksum; + tcph->checksum = 0; + expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, + &sock->out_nla, + &sock->inf->address); + if (expected != chk) + { + grub_dprintf ("net", "Invalid TCP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + tcph->checksum = chk; + } + + if ((grub_be_to_cpu16 (tcph->flags) & TCP_SYN) + && (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) + && !sock->established) + { + sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr); + sock->their_cur_seq = sock->their_start_seq + 1; + sock->established = 1; + } + + if (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) + { + struct unacked *unack, *next; + grub_uint32_t acked = grub_be_to_cpu16 (tcph->ack); + for (unack = sock->unack_first; unack; unack = next) + { + grub_uint32_t seqnr; + next = unack->next; + seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data) + ->seqnr); + seqnr += (nb->tail - nb->data + - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4); + if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) + seqnr++; + + if (seqnr > acked) + break; + grub_netbuff_free (unack->nb); + grub_free (unack); + } + sock->unack_first = unack; + if (!sock->unack_last) + sock->unack_last = NULL; + } + + if (grub_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq) + { + ack (sock); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + err = grub_priority_queue_push (sock->pq, &nb); + if (err) + return err; + + { + struct grub_net_buff **nb_top_p, *nb_top; + while (1) + { + nb_top_p = grub_priority_queue_top (sock->pq); + if (!nb_top_p) + return GRUB_ERR_NONE; + nb_top = *nb_top_p; + tcph = (struct tcphdr *) nb_top->data; + if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq) + break; + grub_priority_queue_pop (sock->pq); + } + if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) + return GRUB_ERR_NONE; + while (1) + { + nb_top_p = grub_priority_queue_top (sock->pq); + if (!nb_top_p) + break; + nb_top = *nb_top_p; + tcph = (struct tcphdr *) nb_top->data; + + if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) + break; + grub_priority_queue_pop (sock->pq); + + err = grub_netbuff_pull (nb, (grub_be_to_cpu16 (tcph->flags) + >> 12) * sizeof (grub_uint32_t)); + if (err) + return err; + + sock->their_cur_seq += (nb_top->tail - nb_top->data); + if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) + { + sock->they_closed = 1; + sock->their_cur_seq++; + } + /* If there is data, puts packet in socket list. */ + if ((nb_top->tail - nb_top->data) > 0) + grub_net_put_packet (&sock->packs, nb_top); + else + grub_netbuff_free (nb); + } + ack (sock); + } + while (sock->packs.first) + { + nb = sock->packs.first->nb; + if (sock->recv_hook) + sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); + grub_net_remove_packet (sock->packs.first); + } + + return GRUB_ERR_NONE; + } + } + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 16616cfea..bc8886442 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -207,7 +207,6 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), return GRUB_ERR_NONE; nb_top = *nb_top_p; tftph = (struct tftphdr *) nb_top->data; - grub_refresh (); if (grub_be_to_cpu16 (tftph->u.data.block) >= data->block + 1) break; grub_priority_queue_pop (data->pq); @@ -279,6 +278,7 @@ tftp_open (struct grub_file *file, const char *filename) struct grub_net_buff nb; tftp_data_t data; grub_err_t err; + grub_uint8_t *nbd; data = grub_zalloc (sizeof (*data)); if (!data) @@ -344,21 +344,11 @@ tftp_open (struct grub_file *file, const char *filename) return grub_errno; } - err = grub_net_send_udp_packet (data->sock, &nb); - if (err) - { - grub_net_udp_close (data->sock); - destroy_pq (data); - return err; - } - /* Receive OACK packet. */ + nbd = nb.data; for (i = 0; i < 3; i++) { - grub_net_poll_cards (100); - if (data->have_oack) - break; - /* Retry. */ + nb.data = nbd; err = grub_net_send_udp_packet (data->sock, &nb); if (err) { @@ -366,6 +356,9 @@ tftp_open (struct grub_file *file, const char *filename) destroy_pq (data); return err; } + grub_net_poll_cards (100); + if (data->have_oack) + break; } if (!data->have_oack) diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index 0a43fe24f..e816547ac 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -120,10 +120,13 @@ grub_net_send_udp_packet (const grub_net_udp_socket_t socket, udph->src = grub_cpu_to_be16 (socket->in_port); udph->dst = grub_cpu_to_be16 (socket->out_port); - /* No chechksum. */ udph->chksum = 0; udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &socket->inf->address, + &socket->out_nla); + return grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, GRUB_NET_IP_UDP); } @@ -136,26 +139,60 @@ grub_net_recv_udp_packet (struct grub_net_buff *nb, struct udphdr *udph; grub_net_udp_socket_t sock; grub_err_t err; + udph = (struct udphdr *) nb->data; - err = grub_netbuff_pull (nb, sizeof (*udph)); - if (err) - return err; + if (nb->tail - nb->data < (grub_ssize_t) sizeof (*udph)) + { + grub_dprintf ("net", "UDP packet too short: %" PRIuGRUB_SIZE "\n", + nb->tail - nb->data); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } FOR_UDP_SOCKETS (sock) { if (grub_be_to_cpu16 (udph->dst) == sock->in_port - && inf == sock->inf && sock->recv_hook + && inf == sock->inf && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && source->ipv4 == sock->out_nla.ipv4) + && source->ipv4 == sock->out_nla.ipv4 + && (sock->status == GRUB_NET_SOCKET_START + || grub_be_to_cpu16 (udph->src) == sock->out_port)) { + if (udph->chksum) + { + grub_uint16_t chk, expected; + chk = udph->chksum; + udph->chksum = 0; + expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &sock->out_nla, + &sock->inf->address); + if (expected != chk) + { + grub_dprintf ("net", "Invalid UDP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph->chksum = chk; + } + if (sock->status == GRUB_NET_SOCKET_START) { sock->out_port = grub_be_to_cpu16 (udph->src); sock->status = GRUB_NET_SOCKET_ESTABLISHED; } + err = grub_netbuff_pull (nb, sizeof (*udph)); + if (err) + return err; + /* App protocol remove its own reader. */ - sock->recv_hook (sock, nb, sock->recv_hook_data); + if (sock->recv_hook) + sock->recv_hook (sock, nb, sock->recv_hook_data); + else + grub_netbuff_free (nb); return GRUB_ERR_NONE; } } diff --git a/include/grub/err.h b/include/grub/err.h index 40f39dd5d..e03378d70 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -59,6 +59,7 @@ typedef enum GRUB_ERR_NET_BAD_ADDRESS, GRUB_ERR_NET_ROUTE_LOOP, GRUB_ERR_NET_NO_ROUTE, + GRUB_ERR_NET_NO_ANSWER, GRUB_ERR_WAIT, GRUB_ERR_BUG } diff --git a/include/grub/net.h b/include/grub/net.h index e7631b884..d71edfd1e 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -415,6 +415,9 @@ grub_net_network_level_interface_unregister (struct grub_net_network_level_inter inter->prev = 0; } +void +grub_net_tcp_retransmit (void); + extern char *grub_net_default_server; #endif /* ! GRUB_NET_HEADER */ diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index a3c64c1cb..e385e5ac1 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -50,5 +50,15 @@ grub_err_t grub_net_recv_udp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *src); +grub_err_t +grub_net_recv_tcp_packet (struct grub_net_buff *nb, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *source); + +grub_uint16_t +grub_net_ip_transport_checksum (struct grub_net_buff *nb, + grub_uint16_t proto, + const grub_net_network_level_address_t *src, + const grub_net_network_level_address_t *dst); #endif diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h new file mode 100644 index 000000000..88cc6878e --- /dev/null +++ b/include/grub/net/tcp.h @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +#ifndef GRUB_NET_TCP_HEADER +#define GRUB_NET_TCP_HEADER 1 +#include +#include + +struct grub_net_tcp_socket; +typedef struct grub_net_tcp_socket *grub_net_tcp_socket_t; + +grub_net_tcp_socket_t +grub_net_tcp_open (char *server, + grub_uint16_t out_port, + grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, + struct grub_net_buff *nb, + void *data), + void (*error_hook) (grub_net_tcp_socket_t sock, + void *data), + void *hook_data); + +grub_err_t +grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, + struct grub_net_buff *nb); + +void +grub_net_tcp_close (grub_net_tcp_socket_t sock); + +#endif diff --git a/include/grub/net/udp.h b/include/grub/net/udp.h index 8d978569d..fca94ba47 100644 --- a/include/grub/net/udp.h +++ b/include/grub/net/udp.h @@ -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 . + */ + #ifndef GRUB_NET_UDP_HEADER #define GRUB_NET_UDP_HEADER 1 #include From 3a2e86033488dc0a20cfcca9a4fa228e28e17558 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 10 Jul 2011 09:52:08 +0200 Subject: [PATCH 09/37] TCP improvements --- grub-core/net/tcp.c | 56 ++++++++++++++++++++++++++++++++------------- include/grub/err.h | 3 ++- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 3bc3d5c9c..8fbf0bdfa 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -40,6 +40,7 @@ enum { TCP_FIN = 0x1, TCP_SYN = 0x2, + TCP_RST = 0x4, TCP_ACK = 0x10 }; @@ -53,6 +54,7 @@ struct grub_net_tcp_socket int in_port; int out_port; int errors; + int reseted; grub_uint32_t my_start_seq; grub_uint32_t my_cur_seq; grub_uint32_t their_start_seq; @@ -146,6 +148,8 @@ tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) if (err) return err; nb->data = nbd; + if (!size) + grub_netbuff_free (nb); return GRUB_ERR_NONE; } @@ -235,14 +239,6 @@ ack (grub_net_tcp_socket_t sock) } } -static void -kill_socket (grub_net_tcp_socket_t sock) -{ - if (sock->error_hook) - sock->error_hook (sock, sock->hook_data); - grub_net_tcp_close (sock); -} - void grub_net_tcp_retransmit (void) { @@ -263,7 +259,9 @@ grub_net_tcp_retransmit (void) if (unack->try_count > TCP_RETRANSMISSION_COUNT) { - kill_socket (sock); + if (sock->error_hook) + sock->error_hook (sock, sock->hook_data); + grub_net_tcp_close (sock); break; } unack->try_count++; @@ -435,9 +433,9 @@ grub_net_tcp_open (char *server, grub_netbuff_free (nb); return NULL; } - for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 10 + for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 50 && !socket->established); j++) - grub_net_poll_cards (10); + grub_net_poll_cards (50); if (socket->established) break; } @@ -445,11 +443,14 @@ grub_net_tcp_open (char *server, { grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets), GRUB_AS_LIST (socket)); - grub_free (socket); - grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer"); + if (socket->reseted) + grub_error (GRUB_ERR_NET_PORT_CLOSED, "port closed"); + else + grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer"); grub_netbuff_free (nb); destroy_pq (socket); + grub_free (socket); return NULL; } @@ -539,10 +540,27 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, sock->established = 1; } + if (grub_be_to_cpu16 (tcph->flags) & TCP_RST) + { + struct unacked *unack, *next; + sock->reseted = 1; + for (unack = sock->unack_first; unack; unack = next) + { + next = unack->next; + grub_netbuff_free (unack->nb); + grub_free (unack); + } + + sock->unack_first = NULL; + sock->unack_last = NULL; + + return GRUB_ERR_NONE; + } + if (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) { struct unacked *unack, *next; - grub_uint32_t acked = grub_be_to_cpu16 (tcph->ack); + grub_uint32_t acked = grub_be_to_cpu32 (tcph->ack); for (unack = sock->unack_first; unack; unack = next) { grub_uint32_t seqnr; @@ -577,6 +595,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, { struct grub_net_buff **nb_top_p, *nb_top; + int do_ack = 0; while (1) { nb_top_p = grub_priority_queue_top (sock->pq); @@ -612,14 +631,19 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, { sock->they_closed = 1; sock->their_cur_seq++; + do_ack = 1; } /* If there is data, puts packet in socket list. */ if ((nb_top->tail - nb_top->data) > 0) - grub_net_put_packet (&sock->packs, nb_top); + { + grub_net_put_packet (&sock->packs, nb_top); + do_ack = 1; + } else grub_netbuff_free (nb); } - ack (sock); + if (do_ack) + ack (sock); } while (sock->packs.first) { diff --git a/include/grub/err.h b/include/grub/err.h index e03378d70..c91911644 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -61,7 +61,8 @@ typedef enum GRUB_ERR_NET_NO_ROUTE, GRUB_ERR_NET_NO_ANSWER, GRUB_ERR_WAIT, - GRUB_ERR_BUG + GRUB_ERR_BUG, + GRUB_ERR_NET_PORT_CLOSED } grub_err_t; From bfca152e5a5c4f077b27c806245ebb7e11c9dadd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 10 Jul 2011 09:53:29 +0200 Subject: [PATCH 10/37] Fix a memory leak --- grub-core/net/tcp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 8fbf0bdfa..c64170edf 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -554,6 +554,8 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, sock->unack_first = NULL; sock->unack_last = NULL; + grub_netbuff_free (nb); + return GRUB_ERR_NONE; } From f4e6e2909bdca758733afb2009fe06d7a8a15448 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 10 Jul 2011 11:26:34 +0200 Subject: [PATCH 11/37] TCP fragmentation --- grub-core/net/tcp.c | 46 +++++++++++++++++++++++++++++++++++++++--- include/grub/net/tcp.h | 3 ++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index c64170edf..489277678 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -41,7 +41,9 @@ enum TCP_FIN = 0x1, TCP_SYN = 0x2, TCP_RST = 0x4, - TCP_ACK = 0x10 + TCP_PUSH = 0x8, + TCP_ACK = 0x10, + TCP_URG = 0x20, }; struct grub_net_tcp_socket @@ -460,10 +462,48 @@ grub_net_tcp_open (char *server, grub_err_t grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, - struct grub_net_buff *nb) + struct grub_net_buff *nb, int push) { struct tcphdr *tcph; grub_err_t err; + grub_ssize_t fraglen; + fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE + - sizeof (*tcph)); + + while (nb->tail - nb->data > fraglen) + { + struct grub_net_buff *nb2; + + nb2 = grub_netbuff_alloc (fraglen + sizeof (*tcph) + + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb2) + return grub_errno; + err = grub_netbuff_reserve (nb2, GRUB_NET_MAX_LINK_HEADER_SIZE + + GRUB_NET_OUR_IPV4_HEADER_SIZE); + if (err) + return err; + err = grub_netbuff_put (nb2, sizeof (*tcph)); + if (err) + return err; + + tcph = (struct tcphdr *) nb2->data; + tcph->ack = grub_cpu_to_be32 (0); + tcph->flags = grub_cpu_to_be16 ((5 << 12)); + tcph->window = grub_cpu_to_be16 (socket->my_window); + tcph->urgent = 0; + err = grub_netbuff_put (nb2, fraglen); + if (err) + return err; + grub_memcpy (tcph + 1, nb->data, fraglen); + err = grub_netbuff_pull (nb, fraglen); + if (err) + return err; + + err = tcp_send (nb2, socket); + if (err) + return err; + } err = grub_netbuff_push (nb, sizeof (*tcph)); if (err) @@ -471,7 +511,7 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, tcph = (struct tcphdr *) nb->data; tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12)); + tcph->flags = grub_cpu_to_be16 ((5 << 12) | (push ? TCP_PUSH : 0)); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; return tcp_send (nb, socket); diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index 88cc6878e..cd05074f5 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -36,7 +36,8 @@ grub_net_tcp_open (char *server, grub_err_t grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, - struct grub_net_buff *nb); + struct grub_net_buff *nb, + int push); void grub_net_tcp_close (grub_net_tcp_socket_t sock); From c410299b05a5cc61f31ecf1b5be639ce94094637 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 10 Jul 2011 23:37:28 +0200 Subject: [PATCH 12/37] TCP listening support --- grub-core/net/tcp.c | 432 +++++++++++++++++++++++++++-------------- include/grub/net/tcp.h | 23 +++ 2 files changed, 308 insertions(+), 147 deletions(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 489277678..f6d864523 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -74,6 +74,19 @@ struct grub_net_tcp_socket grub_priority_queue_t pq; }; +struct grub_net_tcp_listen +{ + struct grub_net_tcp_listen *next; + + grub_uint16_t port; + const struct grub_net_network_level_interface *inf; + + grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen, + grub_net_tcp_socket_t sock, + void *data); + void *hook_data; +}; + struct tcphdr { grub_uint16_t src; @@ -95,9 +108,38 @@ struct tcp_pseudohdr grub_uint16_t tcp_length; } __attribute__ ((packed)); -struct grub_net_tcp_socket *tcp_sockets; +static struct grub_net_tcp_socket *tcp_sockets; +static struct grub_net_tcp_listen *tcp_listens; -#define FOR_TCP_SOCKETS(var) for (var = tcp_sockets; var; var = var->next) +#define FOR_TCP_SOCKETS(var) FOR_LIST_ELEMENTS (var, tcp_sockets) +#define FOR_TCP_LISTENS(var) FOR_LIST_ELEMENTS (var, tcp_listens) + +grub_net_tcp_listen_t +grub_net_tcp_listen (grub_uint16_t port, + const struct grub_net_network_level_interface *inf, + grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen, + grub_net_tcp_socket_t sock, + void *data), + void *hook_data) +{ + grub_net_tcp_listen_t ret; + ret = grub_malloc (sizeof (*ret)); + if (!ret) + return NULL; + ret->listen_hook = listen_hook; + ret->hook_data = hook_data; + ret->port = port; + ret->inf = inf; + grub_list_push (GRUB_AS_LIST_P (&tcp_listens), GRUB_AS_LIST (ret)); + return ret; +} + +void +grub_net_tcp_stop_listen (grub_net_tcp_listen_t listen) +{ + grub_list_remove (GRUB_AS_LIST_P (&tcp_listens), + GRUB_AS_LIST (listen)); +} static inline void tcp_socket_register (grub_net_tcp_socket_t sock) @@ -106,6 +148,25 @@ tcp_socket_register (grub_net_tcp_socket_t sock) GRUB_AS_LIST (sock)); } +static void +error (grub_net_tcp_socket_t sock) +{ + struct unacked *unack, *next; + + if (sock->established && sock->error_hook) + sock->error_hook (sock, sock->hook_data); + + for (unack = sock->unack_first; unack; unack = next) + { + next = unack->next; + grub_netbuff_free (unack->nb); + grub_free (unack); + } + + sock->unack_first = NULL; + sock->unack_last = NULL; +} + static grub_err_t tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) { @@ -164,10 +225,13 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) sock->i_closed = 1; - nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + 128); + nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); if (!nb_fin) return; - err = grub_netbuff_reserve (nb_fin, 128); + err = grub_netbuff_reserve (nb_fin, GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); if (err) { grub_netbuff_free (nb_fin); @@ -261,9 +325,7 @@ grub_net_tcp_retransmit (void) if (unack->try_count > TCP_RETRANSMISSION_COUNT) { - if (sock->error_hook) - sock->error_hook (sock, sock->hook_data); - grub_net_tcp_close (sock); + error (sock); break; } unack->try_count++; @@ -332,6 +394,54 @@ destroy_pq (grub_net_tcp_socket_t sock) grub_priority_queue_destroy (sock->pq); } +grub_err_t +grub_net_tcp_accept (grub_net_tcp_socket_t sock, + grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, + struct grub_net_buff *nb, + void *data), + void (*error_hook) (grub_net_tcp_socket_t sock, + void *data), + void *hook_data) +{ + struct grub_net_buff *nb_ack; + struct tcphdr *tcph; + grub_err_t err; + + sock->recv_hook = recv_hook; + sock->error_hook = error_hook; + sock->hook_data = hook_data; + nb_ack = grub_netbuff_alloc (sizeof (*tcph) + + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb_ack) + return grub_errno; + err = grub_netbuff_reserve (nb_ack, GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (err) + { + grub_netbuff_free (nb_ack); + return err; + } + + err = grub_netbuff_put (nb_ack, sizeof (*tcph)); + if (err) + { + grub_netbuff_free (nb_ack); + return err; + } + tcph = (void *) nb_ack->data; + tcph->ack = grub_cpu_to_be32 (sock->their_cur_seq); + tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK); + tcph->window = grub_cpu_to_be16 (sock->my_window); + tcph->urgent = 0; + tcp_socket_register (sock); + err = tcp_send (nb_ack, sock); + if (err) + return err; + sock->my_cur_seq++; + return GRUB_ERR_NONE; +} + grub_net_tcp_socket_t grub_net_tcp_open (char *server, grub_uint16_t out_port, @@ -545,159 +655,187 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, FOR_TCP_SOCKETS (sock) { - if (grub_be_to_cpu16 (tcph->dst) == sock->in_port - && grub_be_to_cpu16 (tcph->src) == sock->out_port - && inf == sock->inf - && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && source->ipv4 == sock->out_nla.ipv4) + if (!(grub_be_to_cpu16 (tcph->dst) == sock->in_port + && grub_be_to_cpu16 (tcph->src) == sock->out_port + && inf == sock->inf + && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && source->ipv4 == sock->out_nla.ipv4)) + continue; + if (tcph->checksum) { - if (tcph->checksum) + grub_uint16_t chk, expected; + chk = tcph->checksum; + tcph->checksum = 0; + expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, + &sock->out_nla, + &sock->inf->address); + if (expected != chk) { - grub_uint16_t chk, expected; - chk = tcph->checksum; - tcph->checksum = 0; - expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, - &sock->out_nla, - &sock->inf->address); - if (expected != chk) - { - grub_dprintf ("net", "Invalid TCP checksum. " - "Expected %x, got %x\n", - grub_be_to_cpu16 (expected), - grub_be_to_cpu16 (chk)); - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - tcph->checksum = chk; - } - - if ((grub_be_to_cpu16 (tcph->flags) & TCP_SYN) - && (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) - && !sock->established) - { - sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr); - sock->their_cur_seq = sock->their_start_seq + 1; - sock->established = 1; - } - - if (grub_be_to_cpu16 (tcph->flags) & TCP_RST) - { - struct unacked *unack, *next; - sock->reseted = 1; - for (unack = sock->unack_first; unack; unack = next) - { - next = unack->next; - grub_netbuff_free (unack->nb); - grub_free (unack); - } - - sock->unack_first = NULL; - sock->unack_last = NULL; - - grub_netbuff_free (nb); - - return GRUB_ERR_NONE; - } - - if (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) - { - struct unacked *unack, *next; - grub_uint32_t acked = grub_be_to_cpu32 (tcph->ack); - for (unack = sock->unack_first; unack; unack = next) - { - grub_uint32_t seqnr; - next = unack->next; - seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data) - ->seqnr); - seqnr += (nb->tail - nb->data - - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4); - if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) - seqnr++; - - if (seqnr > acked) - break; - grub_netbuff_free (unack->nb); - grub_free (unack); - } - sock->unack_first = unack; - if (!sock->unack_last) - sock->unack_last = NULL; - } - - if (grub_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq) - { - ack (sock); + grub_dprintf ("net", "Invalid TCP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); grub_netbuff_free (nb); return GRUB_ERR_NONE; } + tcph->checksum = chk; + } - err = grub_priority_queue_push (sock->pq, &nb); - if (err) - return err; + if ((grub_be_to_cpu16 (tcph->flags) & TCP_SYN) + && (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) + && !sock->established) + { + sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr); + sock->their_cur_seq = sock->their_start_seq + 1; + sock->established = 1; + } - { - struct grub_net_buff **nb_top_p, *nb_top; - int do_ack = 0; - while (1) - { - nb_top_p = grub_priority_queue_top (sock->pq); - if (!nb_top_p) - return GRUB_ERR_NONE; - nb_top = *nb_top_p; - tcph = (struct tcphdr *) nb_top->data; - if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq) - break; - grub_priority_queue_pop (sock->pq); - } - if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) - return GRUB_ERR_NONE; - while (1) - { - nb_top_p = grub_priority_queue_top (sock->pq); - if (!nb_top_p) - break; - nb_top = *nb_top_p; - tcph = (struct tcphdr *) nb_top->data; + if (grub_be_to_cpu16 (tcph->flags) & TCP_RST) + { + sock->reseted = 1; - if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) - break; - grub_priority_queue_pop (sock->pq); + error (sock); - err = grub_netbuff_pull (nb, (grub_be_to_cpu16 (tcph->flags) - >> 12) * sizeof (grub_uint32_t)); - if (err) - return err; + grub_netbuff_free (nb); - sock->their_cur_seq += (nb_top->tail - nb_top->data); - if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) - { - sock->they_closed = 1; - sock->their_cur_seq++; - do_ack = 1; - } - /* If there is data, puts packet in socket list. */ - if ((nb_top->tail - nb_top->data) > 0) - { - grub_net_put_packet (&sock->packs, nb_top); - do_ack = 1; - } - else - grub_netbuff_free (nb); - } - if (do_ack) - ack (sock); - } - while (sock->packs.first) - { - nb = sock->packs.first->nb; - if (sock->recv_hook) - sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); - grub_net_remove_packet (sock->packs.first); - } - return GRUB_ERR_NONE; } + + if (grub_be_to_cpu16 (tcph->flags) & TCP_ACK) + { + struct unacked *unack, *next; + grub_uint32_t acked = grub_be_to_cpu32 (tcph->ack); + for (unack = sock->unack_first; unack; unack = next) + { + grub_uint32_t seqnr; + next = unack->next; + seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data) + ->seqnr); + seqnr += (nb->tail - nb->data + - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4); + if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) + seqnr++; + + if (seqnr > acked) + break; + grub_netbuff_free (unack->nb); + grub_free (unack); + } + sock->unack_first = unack; + if (!sock->unack_last) + sock->unack_last = NULL; + } + + if (grub_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq) + { + ack (sock); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + err = grub_priority_queue_push (sock->pq, &nb); + if (err) + return err; + + { + struct grub_net_buff **nb_top_p, *nb_top; + int do_ack = 0; + while (1) + { + nb_top_p = grub_priority_queue_top (sock->pq); + if (!nb_top_p) + return GRUB_ERR_NONE; + nb_top = *nb_top_p; + tcph = (struct tcphdr *) nb_top->data; + if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq) + break; + grub_priority_queue_pop (sock->pq); + } + if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) + return GRUB_ERR_NONE; + while (1) + { + nb_top_p = grub_priority_queue_top (sock->pq); + if (!nb_top_p) + break; + nb_top = *nb_top_p; + tcph = (struct tcphdr *) nb_top->data; + + if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) + break; + grub_priority_queue_pop (sock->pq); + + err = grub_netbuff_pull (nb, (grub_be_to_cpu16 (tcph->flags) + >> 12) * sizeof (grub_uint32_t)); + if (err) + return err; + + sock->their_cur_seq += (nb_top->tail - nb_top->data); + if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) + { + sock->they_closed = 1; + sock->their_cur_seq++; + do_ack = 1; + } + /* If there is data, puts packet in socket list. */ + if ((nb_top->tail - nb_top->data) > 0) + { + grub_net_put_packet (&sock->packs, nb_top); + do_ack = 1; + } + else + grub_netbuff_free (nb); + } + if (do_ack) + ack (sock); + } + while (sock->packs.first) + { + nb = sock->packs.first->nb; + if (sock->recv_hook) + sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); + grub_net_remove_packet (sock->packs.first); + } + + return GRUB_ERR_NONE; } + if (grub_be_to_cpu16 (tcph->flags) & TCP_SYN) + { + grub_net_tcp_listen_t listen; + + FOR_TCP_LISTENS (listen) + { + if (!(grub_be_to_cpu16 (tcph->dst) == listen->port + && (inf == listen->inf || listen->inf == NULL))) + continue; + sock = grub_zalloc (sizeof (*sock)); + if (sock == NULL) + return grub_errno; + + sock->out_port = grub_be_to_cpu16 (tcph->src); + sock->in_port = grub_be_to_cpu16 (tcph->dst); + sock->inf = inf; + sock->out_nla = *source; + sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr); + sock->their_cur_seq = sock->their_start_seq + 1; + sock->my_cur_seq = sock->my_start_seq = grub_get_time_ms (); + sock->my_window = 8192; + + sock->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), + cmp); + if (!sock->pq) + { + grub_netbuff_free (nb); + return grub_errno; + } + + err = listen->listen_hook (listen, sock, listen->hook_data); + + grub_netbuff_free (nb); + return err; + + } + } grub_netbuff_free (nb); return GRUB_ERR_NONE; } diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index cd05074f5..0ac7819bc 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -24,6 +24,9 @@ struct grub_net_tcp_socket; typedef struct grub_net_tcp_socket *grub_net_tcp_socket_t; +struct grub_net_tcp_listen; +typedef struct grub_net_tcp_listen *grub_net_tcp_listen_t; + grub_net_tcp_socket_t grub_net_tcp_open (char *server, grub_uint16_t out_port, @@ -34,6 +37,17 @@ grub_net_tcp_open (char *server, void *data), void *hook_data); +grub_net_tcp_listen_t +grub_net_tcp_listen (grub_uint16_t port, + const struct grub_net_network_level_interface *inf, + grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen, + grub_net_tcp_socket_t sock, + void *data), + void *hook_data); + +void +grub_net_tcp_stop_listen (grub_net_tcp_listen_t listen); + grub_err_t grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, struct grub_net_buff *nb, @@ -42,4 +56,13 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, void grub_net_tcp_close (grub_net_tcp_socket_t sock); +grub_err_t +grub_net_tcp_accept (grub_net_tcp_socket_t sock, + grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, + struct grub_net_buff *nb, + void *data), + void (*error_hook) (grub_net_tcp_socket_t sock, + void *data), + void *hook_data); + #endif From 4ebb4c616f98b0c9da54a5c38502a3c11acf0b25 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Jul 2011 03:47:10 +0200 Subject: [PATCH 13/37] Remove useless include in tftp --- grub-core/net/tftp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index bc8886442..7c58e640b 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -26,7 +26,6 @@ #include #include #include -#include GRUB_MOD_LICENSE ("GPLv3+"); From 440694e3d39beeb144dd600b76c5245f935bc22e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Jul 2011 03:48:10 +0200 Subject: [PATCH 14/37] Fix TCP retransmission --- grub-core/net/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index f6d864523..578a254f4 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -708,12 +708,14 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, for (unack = sock->unack_first; unack; unack = next) { grub_uint32_t seqnr; + struct tcphdr *unack_tcph; next = unack->next; seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data) ->seqnr); - seqnr += (nb->tail - nb->data - - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4); - if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) + unack_tcph = (struct tcphdr *) unack->nb->data; + seqnr += (unack->nb->tail - unack->nb->data + - (grub_be_to_cpu16 (unack_tcph->flags) >> 12) * 4); + if (grub_be_to_cpu16 (unack_tcph->flags) & TCP_FIN) seqnr++; if (seqnr > acked) From 80e722366d307f93c94ab54d4717d973887c87b9 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Jul 2011 03:49:02 +0200 Subject: [PATCH 15/37] First attempt at http --- grub-core/Makefile.core.def | 5 + grub-core/net/http.c | 356 ++++++++++++++++++++++++++++++++++++ grub-core/net/tcp.c | 5 +- include/grub/err.h | 4 +- include/grub/misc.h | 14 ++ include/grub/net.h | 13 +- 6 files changed, 391 insertions(+), 6 deletions(-) create mode 100644 grub-core/net/http.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 673092b4f..f42b272ca 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1609,6 +1609,11 @@ module = { common = net/tftp.c; }; +module = { + name = http; + common = net/http.c; +}; + module = { name = ofnet; common = net/drivers/ieee1275/ofnet.c; diff --git a/grub-core/net/http.c b/grub-core/net/http.c new file mode 100644 index 000000000..b5de4c5c6 --- /dev/null +++ b/grub-core/net/http.c @@ -0,0 +1,356 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + HTTP_PORT = 80 + }; + + +typedef struct http_data +{ + grub_uint64_t file_size; + grub_uint64_t position; + char *current_line; + grub_size_t current_line_len; + int headers_recv; + int first_line_recv; + grub_net_tcp_socket_t sock; +} *http_data_t; + +static grub_err_t +parse_line (http_data_t data, char *ptr, grub_size_t len) +{ + char *end = ptr + len; + while (end > ptr && *(end - 1) == '\r') + end--; + *end = 0; + if (ptr == end) + { + data->headers_recv = 1; + return GRUB_ERR_NONE; + } + + if (!data->first_line_recv) + { + int code; + if (grub_memcmp (ptr, "HTTP/1.1 ", sizeof ("HTTP/1.1 ") - 1) != 0) + return grub_error (GRUB_ERR_NET_INVALID_RESPONSE, + "unsupported HTTP response"); + ptr += sizeof ("HTTP/1.1 ") - 1; + code = grub_strtoul (ptr, &ptr, 10); + if (grub_errno) + return grub_errno; + switch (code) + { + case 200: + break; + default: + return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, + "unsupported HTTP error %d: %s", + code, ptr); + } + data->first_line_recv = 1; + return GRUB_ERR_NONE; + } + if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1) + == 0) + { + ptr += sizeof ("Content-Length: ") - 1; + data->file_size = grub_strtoull (ptr, &ptr, 10); + return GRUB_ERR_NONE; + } + return GRUB_ERR_NONE; +} + +static void +http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), + void *f) +{ + grub_file_t file = f; + http_data_t data = file->data; + + if (data->sock) + grub_net_tcp_close (data->sock); + grub_free (data); + if (data->current_line) + grub_free (data->current_line); + file->device->net->eof = 1; +} + +static grub_err_t +http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), + struct grub_net_buff *nb, + void *f) +{ + grub_file_t file = f; + http_data_t data = file->data; + char *ptr = (char *) nb->data; + grub_err_t err; + + if (!data->headers_recv && data->current_line) + { + int have_line = 1; + char *t; + ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); + if (ptr) + ptr++; + else + { + have_line = 0; + ptr = (char *) nb->tail; + } + t = grub_realloc (data->current_line, + data->current_line_len + (ptr - (char *) nb->data)); + if (!t) + { + grub_netbuff_free (nb); + grub_net_tcp_close (data->sock); + return grub_errno; + } + + data->current_line = t; + grub_memcpy (data->current_line + data->current_line_len, + nb->data, ptr - (char *) nb->data); + data->current_line_len += ptr - (char *) nb->data; + if (!have_line) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + err = parse_line (data, data->current_line, data->current_line_len); + grub_free (data->current_line); + data->current_line = 0; + data->current_line_len = 0; + if (err) + { + grub_net_tcp_close (data->sock); + grub_netbuff_free (nb); + return err; + } + } + + while (ptr < (char *) nb->tail && !data->headers_recv) + { + char *ptr2; + ptr2 = grub_memchr (ptr, '\n', (char *) nb->tail - ptr); + if (!ptr2) + { + data->current_line = grub_malloc ((char *) nb->tail - ptr); + if (!data->current_line) + { + grub_netbuff_free (nb); + grub_net_tcp_close (data->sock); + return grub_errno; + } + data->current_line_len = (char *) nb->tail - ptr; + grub_memcpy (data->current_line, ptr, data->current_line_len); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + err = parse_line (data, ptr, ptr2 - ptr); + if (err) + { + grub_net_tcp_close (data->sock); + grub_netbuff_free (nb); + return err; + } + ptr = ptr2 + 1; + } + + if (((char *) nb->tail - ptr) > 0) + { + data->position += ((char *) nb->tail - ptr); + err = grub_netbuff_pull (nb, ptr - (char *) nb->data); + if (err) + { + grub_net_tcp_close (data->sock); + grub_netbuff_free (nb); + return err; + } + grub_net_put_packet (&file->device->net->packs, nb); + } + else + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} + +static grub_err_t +http_open (struct grub_file *file, const char *filename) +{ + struct grub_net_buff *nb; + http_data_t data; + grub_err_t err; + grub_uint8_t *ptr; + int i; + + data = grub_zalloc (sizeof (*data)); + if (!data) + return grub_errno; + + nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + + sizeof ("GET ") - 1 + + grub_strlen (filename) + + sizeof (" HTTP/1.1\r\nHost: ") - 1 + + grub_strlen (file->device->net->server) + + sizeof ("\r\nUser-Agent: " PACKAGE_STRING + "\r\n\r\n") - 1); + if (!nb) + { + grub_free (data); + return grub_errno; + } + + grub_netbuff_reserve (nb, GRUB_NET_TCP_RESERVE_SIZE); + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof ("GET ") - 1); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, "GET ", sizeof ("GET ") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, grub_strlen (filename)); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, filename, grub_strlen (filename)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (" HTTP/1.1\r\nHost: ") - 1); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, " HTTP/1.1\r\nHost: ", + sizeof (" HTTP/1.1\r\nHost: ") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, grub_strlen (file->device->net->server)); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, file->device->net->server, + grub_strlen (file->device->net->server)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") + - 1); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n", + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") - 1); + + file->not_easily_seekable = 1; + file->data = data; + + data->sock = grub_net_tcp_open (file->device->net->server, + HTTP_PORT, http_receive, + http_err, + file); + if (!data->sock) + { + grub_free (data); + grub_netbuff_free (nb); + return grub_errno; + } + + // grub_net_poll_cards (5000); + + err = grub_net_send_tcp_packet (data->sock, nb, 1); + if (err) + { + grub_free (data); + grub_net_tcp_close (data->sock); + return err; + } + + for (i = 0; !data->headers_recv && i < 100; i++) + { + grub_net_tcp_retransmit (); + grub_net_poll_cards (300); + } + + if (!data->headers_recv) + { + grub_net_tcp_close (data->sock); + grub_free (data); + return grub_error (GRUB_ERR_TIMEOUT, "Time out opening http."); + } + file->size = data->file_size; + + return GRUB_ERR_NONE; +} + +static grub_err_t +http_close (struct grub_file *file) +{ + http_data_t data = file->data; + + if (data->sock) + grub_net_tcp_close (data->sock); + grub_free (data); + if (data->current_line) + grub_free (data->current_line); + return GRUB_ERR_NONE; +} + +static struct grub_net_app_protocol grub_http_protocol = + { + .name = "http", + .open = http_open, + .close = http_close + }; + +GRUB_MOD_INIT (http) +{ + grub_net_app_level_register (&grub_http_protocol); +} + +GRUB_MOD_FINI (http) +{ + grub_net_app_level_unregister (&grub_http_protocol); +} diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 578a254f4..25e8ab4ed 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -25,8 +25,8 @@ #define TCP_SYN_RETRANSMISSION_TIMEOUT 1000 #define TCP_SYN_RETRANSMISSION_COUNT 3 -#define TCP_RETRANSMISSION_TIMEOUT 10000 -#define TCP_RETRANSMISSION_COUNT 5 +#define TCP_RETRANSMISSION_TIMEOUT 1000 +#define TCP_RETRANSMISSION_COUNT 10 struct unacked { @@ -577,6 +577,7 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, struct tcphdr *tcph; grub_err_t err; grub_ssize_t fraglen; + COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == GRUB_NET_TCP_HEADER_SIZE); fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE - sizeof (*tcph)); diff --git a/include/grub/err.h b/include/grub/err.h index c91911644..f6610193c 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -62,7 +62,9 @@ typedef enum GRUB_ERR_NET_NO_ANSWER, GRUB_ERR_WAIT, GRUB_ERR_BUG, - GRUB_ERR_NET_PORT_CLOSED + GRUB_ERR_NET_PORT_CLOSED, + GRUB_ERR_NET_INVALID_RESPONSE, + GRUB_ERR_NET_UNKNOWN_ERROR } grub_err_t; diff --git a/include/grub/misc.h b/include/grub/misc.h index da4bd4a7e..e608ddfb8 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -302,6 +302,20 @@ void EXPORT_FUNC (__deregister_frame_info) (void); /* Inline functions. */ +static inline char * +grub_memchr (const void *p, int c, grub_size_t len) +{ + const char *s = p; + const char *e = s + len; + + for (; s < e; s++) + if (*s == c) + return (char *) s; + + return 0; +} + + static inline unsigned int grub_abs (int x) { diff --git a/include/grub/net.h b/include/grub/net.h index d71edfd1e..255784315 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -27,9 +27,16 @@ #include #include -#define GRUB_NET_MAX_LINK_HEADER_SIZE 64 -#define GRUB_NET_UDP_HEADER_SIZE 8 -#define GRUB_NET_OUR_IPV4_HEADER_SIZE 20 +enum + { + GRUB_NET_MAX_LINK_HEADER_SIZE = 64, + GRUB_NET_UDP_HEADER_SIZE = 8, + GRUB_NET_TCP_HEADER_SIZE = 20, + GRUB_NET_OUR_IPV4_HEADER_SIZE = 20, + GRUB_NET_TCP_RESERVE_SIZE = GRUB_NET_TCP_HEADER_SIZE + + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE + }; typedef enum grub_link_level_protocol_id { From 805448e987bc4327ee654e4a439922827c1f3b6b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 10:21:32 +0200 Subject: [PATCH 16/37] Add network functions for grub-menulst2cfg --- grub-core/lib/legacy_parse.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/grub-core/lib/legacy_parse.c b/grub-core/lib/legacy_parse.c index 659fa7061..e3f4864ce 100644 --- a/grub-core/lib/legacy_parse.c +++ b/grub-core/lib/legacy_parse.c @@ -42,7 +42,8 @@ struct legacy_command TYPE_BOOL, TYPE_INT, TYPE_REST_VERBATIM, - TYPE_VBE_MODE + TYPE_VBE_MODE, + TYPE_WITH_CONFIGFILE_OPTION } argt[4]; enum { FLAG_IGNORE_REST = 0x001, @@ -64,7 +65,13 @@ static struct legacy_command legacy_commands[] = "Print the blocklist notation of the file FILE."}, {"boot", "boot\n", NULL, 0, 0, {}, 0, 0, "Boot the OS/chain-loader which has been loaded."}, - /* FIXME: bootp unsupported. */ + {"bootp", "net_bootp; net_ls_addr; if [ x%s = x--with-configfile ]; then " + "if net_get_dhcp_option configfile_name pxe 150 string; then " + "configfile $configfile_name; fi; fi\n", NULL, 0, 1, + {TYPE_WITH_CONFIGFILE_OPTION}, FLAG_IGNORE_REST, "[--with-configfile]", + "Initialize a network device via BOOTP. If the option `--with-configfile'" + " is given, try to load a configuration file specified by the 150 vendor" + " tag."}, {"cat", "cat '%s'\n", NULL, 0, 1, {TYPE_FILE}, 0, "FILE", "Print the contents of the file FILE."}, {"chainloader", "chainloader %s '%s'\n", NULL, 0, @@ -102,7 +109,13 @@ static struct legacy_command legacy_commands[] = "[NUM | `saved']", "Set the default entry to entry number NUM (if not specified, it is" " 0, the first entry) or the entry number saved by savedefault."}, - /* FIXME: dhcp unsupported. */ + {"dhcp", "net_bootp; net_ls_addr; if [ x%s = x--with-configfile ]; then " + "if net_get_dhcp_option configfile_name pxe 150 string; then " + "configfile $configfile_name; fi; fi\n", NULL, 0, 1, + {TYPE_WITH_CONFIGFILE_OPTION}, FLAG_IGNORE_REST, "[--with-configfile]", + "Initialize a network device via BOOTP. If the option `--with-configfile'" + " is given, try to load a configuration file specified by the 150 vendor" + " tag."}, {"displayapm", "lsapm\n", NULL, 0, 0, {}, 0, 0, "Display APM BIOS information."}, {"displaymem", "lsmmap\n", NULL, 0, 0, {}, 0, 0, @@ -411,6 +424,8 @@ is_option (enum arg_type opt, const char *curarg, grub_size_t len) { switch (opt) { + case TYPE_WITH_CONFIGFILE_OPTION: + return check_option (curarg, "--with-configfile", len); case TYPE_NOAPM_OPTION: return check_option (curarg, "--no-apm", len); case TYPE_FORCE_OPTION: @@ -662,6 +677,7 @@ grub_legacy_parse (const char *buf, char **entryname, char **suffix) case TYPE_VERBATIM: args[i] = grub_legacy_escape (curarg, curarglen); break; + case TYPE_WITH_CONFIGFILE_OPTION: case TYPE_FORCE_OPTION: case TYPE_NOAPM_OPTION: case TYPE_TYPE_OR_NOMEM_OPTION: @@ -756,6 +772,7 @@ grub_legacy_parse (const char *buf, char **entryname, char **suffix) case TYPE_FILE: case TYPE_REST_VERBATIM: case TYPE_VERBATIM: + case TYPE_WITH_CONFIGFILE_OPTION: case TYPE_FORCE_OPTION: case TYPE_NOAPM_OPTION: case TYPE_TYPE_OR_NOMEM_OPTION: From 5c62099a8fade900d47e4c75378605a2b0a461b9 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 10:22:23 +0200 Subject: [PATCH 17/37] Fix warning on i386-pc --- grub-core/net/drivers/i386/pc/pxe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c index 6c5f2fdc4..3343533ef 100644 --- a/grub-core/net/drivers/i386/pc/pxe.c +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -169,7 +169,7 @@ grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused))) { struct grub_pxe_undi_isr *isr; static int in_progress = 0; - char *ptr, *end; + grub_uint8_t *ptr, *end; struct grub_net_buff *buf; isr = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; From 4f71e077b92f521c3ec9b8d75d3fc37be57b5d55 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 17:39:13 +0200 Subject: [PATCH 18/37] HTTP seek support. Various bugfixes. --- grub-core/net/http.c | 177 ++++++++++++++++++++++++++++++----------- grub-core/net/net.c | 72 +++++++++++------ grub-core/net/tcp.c | 55 ++++++++++--- include/grub/net.h | 1 + include/grub/net/tcp.h | 9 ++- 5 files changed, 231 insertions(+), 83 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index b5de4c5c6..e4664757c 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -42,7 +42,11 @@ typedef struct http_data grub_size_t current_line_len; int headers_recv; int first_line_recv; + int size_recv; grub_net_tcp_socket_t sock; + char *filename; + grub_err_t err; + char *errmsg; } *http_data_t; static grub_err_t @@ -72,19 +76,25 @@ parse_line (http_data_t data, char *ptr, grub_size_t len) { case 200: break; + case 404: + data->err = GRUB_ERR_FILE_NOT_FOUND; + data->errmsg = grub_xasprintf ("file `%s' not found", data->filename); + return GRUB_ERR_NONE; default: - return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, - "unsupported HTTP error %d: %s", - code, ptr); + data->err = GRUB_ERR_NET_UNKNOWN_ERROR; + data->errmsg = grub_xasprintf ("unsupported HTTP error %d: %s", + code, ptr); + return GRUB_ERR_NONE; } data->first_line_recv = 1; return GRUB_ERR_NONE; } if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1) - == 0) + == 0 && !data->size_recv) { ptr += sizeof ("Content-Length: ") - 1; data->file_size = grub_strtoull (ptr, &ptr, 10); + data->size_recv = 1; return GRUB_ERR_NONE; } return GRUB_ERR_NONE; @@ -98,10 +108,10 @@ http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), http_data_t data = file->data; if (data->sock) - grub_net_tcp_close (data->sock); - grub_free (data); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->current_line) grub_free (data->current_line); + grub_free (data); file->device->net->eof = 1; } @@ -132,7 +142,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), if (!t) { grub_netbuff_free (nb); - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return grub_errno; } @@ -151,7 +161,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), data->current_line_len = 0; if (err) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; } @@ -167,7 +177,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), if (!data->current_line) { grub_netbuff_free (nb); - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return grub_errno; } data->current_line_len = (char *) nb->tail - ptr; @@ -178,7 +188,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), err = parse_line (data, ptr, ptr2 - ptr); if (err) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; } @@ -191,7 +201,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), err = grub_netbuff_pull (nb, ptr - (char *) nb->data); if (err) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; } @@ -203,57 +213,51 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), } static grub_err_t -http_open (struct grub_file *file, const char *filename) +http_establish (struct grub_file *file, grub_off_t offset, int initial) { - struct grub_net_buff *nb; - http_data_t data; - grub_err_t err; + http_data_t data = file->data; grub_uint8_t *ptr; int i; - - data = grub_zalloc (sizeof (*data)); - if (!data) - return grub_errno; + struct grub_net_buff *nb; + grub_err_t err; nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + sizeof ("GET ") - 1 - + grub_strlen (filename) + + grub_strlen (data->filename) + sizeof (" HTTP/1.1\r\nHost: ") - 1 + grub_strlen (file->device->net->server) + sizeof ("\r\nUser-Agent: " PACKAGE_STRING - "\r\n\r\n") - 1); + "\r\n") - 1 + + sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX" + "-XXXXXXXXXXXXXXXXXXXX/" + "XXXXXXXXXXXXXXXXXXXX\r\n\r\n")); if (!nb) - { - grub_free (data); - return grub_errno; - } + return grub_errno; grub_netbuff_reserve (nb, GRUB_NET_TCP_RESERVE_SIZE); ptr = nb->tail; err = grub_netbuff_put (nb, sizeof ("GET ") - 1); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } grub_memcpy (ptr, "GET ", sizeof ("GET ") - 1); ptr = nb->tail; - err = grub_netbuff_put (nb, grub_strlen (filename)); + + err = grub_netbuff_put (nb, grub_strlen (data->filename)); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } - grub_memcpy (ptr, filename, grub_strlen (filename)); + grub_memcpy (ptr, data->filename, grub_strlen (data->filename)); ptr = nb->tail; err = grub_netbuff_put (nb, sizeof (" HTTP/1.1\r\nHost: ") - 1); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } @@ -264,7 +268,6 @@ http_open (struct grub_file *file, const char *filename) err = grub_netbuff_put (nb, grub_strlen (file->device->net->server)); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } @@ -273,19 +276,30 @@ http_open (struct grub_file *file, const char *filename) ptr = nb->tail; err = grub_netbuff_put (nb, - sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } - grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n", - sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") - 1); - - file->not_easily_seekable = 1; - file->data = data; + grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n", + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1); + if (!initial) + { + ptr = nb->tail; + grub_snprintf ((char *) ptr, + sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX-" + "XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXX\r\n" + "\r\n"), + "Content-Range: bytes %" PRIuGRUB_UINT64_T "-%" + PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n", + offset, data->file_size - 1, data->file_size); + grub_netbuff_put (nb, grub_strlen ((char *) ptr)); + } + ptr = nb->tail; + grub_netbuff_put (nb, 2); + grub_memcpy (ptr, "\r\n", 2); data->sock = grub_net_tcp_open (file->device->net->server, HTTP_PORT, http_receive, @@ -293,7 +307,6 @@ http_open (struct grub_file *file, const char *filename) file); if (!data->sock) { - grub_free (data); grub_netbuff_free (nb); return grub_errno; } @@ -303,8 +316,7 @@ http_open (struct grub_file *file, const char *filename) err = grub_net_send_tcp_packet (data->sock, nb, 1); if (err) { - grub_free (data); - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return err; } @@ -316,9 +328,81 @@ http_open (struct grub_file *file, const char *filename) if (!data->headers_recv) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + if (data->err) + { + char *str = data->errmsg; + err = grub_error (data->err, "%s", str); + grub_free (str); + return data->err; + } + return grub_error (GRUB_ERR_TIMEOUT, "timeout opening http"); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +http_seek (struct grub_file *file, grub_off_t off) +{ + struct http_data *old_data, *data; + grub_err_t err; + old_data = file->data; + /* FIXME: Reuse socket? */ + grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT); + + while (file->device->net->packs.first) + grub_net_remove_packet (file->device->net->packs.first); + + data = grub_zalloc (sizeof (*data)); + if (!data) + return grub_errno; + + data->file_size = old_data->file_size; + data->size_recv = 1; + data->filename = old_data->filename; + if (!data->filename) + { grub_free (data); - return grub_error (GRUB_ERR_TIMEOUT, "Time out opening http."); + return grub_errno; + } + grub_free (old_data); + + err = http_establish (file, off, 0); + if (err) + { + grub_free (data->filename); + grub_free (data); + return err; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +http_open (struct grub_file *file, const char *filename) +{ + grub_err_t err; + struct http_data *data; + + data = grub_zalloc (sizeof (*data)); + if (!data) + return grub_errno; + + data->filename = grub_strdup (filename); + if (!data->filename) + { + grub_free (data); + return grub_errno; + } + + file->not_easily_seekable = 0; + file->data = data; + + err = http_establish (file, 0, 1); + if (err) + { + grub_free (data->filename); + grub_free (data); + return err; } file->size = data->file_size; @@ -331,10 +415,10 @@ http_close (struct grub_file *file) http_data_t data = file->data; if (data->sock) - grub_net_tcp_close (data->sock); - grub_free (data); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->current_line) grub_free (data->current_line); + grub_free (data); return GRUB_ERR_NONE; } @@ -342,7 +426,8 @@ static struct grub_net_app_protocol grub_http_protocol = { .name = "http", .open = http_open, - .close = http_close + .close = http_close, + .seek = http_seek }; GRUB_MOD_INIT (http) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0ff8d01ff..0bde49322 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -815,17 +815,17 @@ grub_net_poll_cards_idle_real (void) static grub_ssize_t grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) { - grub_net_t sock = file->device->net; + grub_net_t net = file->device->net; struct grub_net_buff *nb; char *ptr = buf; grub_size_t amount, total = 0; int try = 0; while (try <= 3) { - while (sock->packs.first) + while (net->packs.first) { try = 0; - nb = sock->packs.first->nb; + nb = net->packs.first->nb; amount = nb->tail - nb->data; if (amount > len) amount = len; @@ -840,7 +840,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) if (amount == (grub_size_t) (nb->tail - nb->data)) { grub_netbuff_free (nb); - grub_net_remove_packet (sock->packs.first); + grub_net_remove_packet (net->packs.first); } else nb->data += amount; @@ -848,7 +848,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) if (!len) return total; } - if (!sock->eof) + if (!net->eof) { try++; grub_net_poll_cards (200); @@ -856,39 +856,59 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) else return total; } - return total; + grub_error (GRUB_ERR_TIMEOUT, "timeout reading '%s'", net->name); + return -1; +} + +static grub_off_t +have_ahead (struct grub_file *file) +{ + grub_net_t net = file->device->net; + grub_off_t ret = net->offset; + struct grub_net_packet *pack; + for (pack = net->packs.first; pack; pack = pack->next) + ret += pack->nb->tail - pack->nb->data; + return ret; } static grub_err_t grub_net_seek_real (struct grub_file *file, grub_off_t offset) { - grub_size_t len = offset - file->device->net->offset; - - if (!len) + if (offset == file->device->net->offset) return GRUB_ERR_NONE; - if (file->device->net->offset > offset) + if (offset > file->device->net->offset) { - grub_err_t err; - while (file->device->net->packs.first) + if (!file->device->net->protocol->seek || have_ahead (file) >= offset) { - grub_netbuff_free (file->device->net->packs.first->nb); - grub_net_remove_packet (file->device->net->packs.first); + grub_net_fs_read_real (file, NULL, + offset - file->device->net->offset); + return grub_errno; } - file->device->net->protocol->close (file); - - file->device->net->packs.first = NULL; - file->device->net->packs.last = NULL; - file->device->net->offset = 0; - file->device->net->eof = 0; - err = file->device->net->protocol->open (file, file->device->net->name); - if (err) - return err; - len = offset; + return file->device->net->protocol->seek (file, offset); } - grub_net_fs_read_real (file, NULL, len); - return GRUB_ERR_NONE; + { + grub_err_t err; + if (file->device->net->protocol->seek) + return file->device->net->protocol->seek (file, offset); + while (file->device->net->packs.first) + { + grub_netbuff_free (file->device->net->packs.first->nb); + grub_net_remove_packet (file->device->net->packs.first); + } + file->device->net->protocol->close (file); + + file->device->net->packs.first = NULL; + file->device->net->packs.last = NULL; + file->device->net->offset = 0; + file->device->net->eof = 0; + err = file->device->net->protocol->open (file, file->device->net->name); + if (err) + return err; + grub_net_fs_read_real (file, NULL, offset); + return grub_errno; + } } static grub_ssize_t diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 25e8ab4ed..7f120ac16 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -56,7 +56,8 @@ struct grub_net_tcp_socket int in_port; int out_port; int errors; - int reseted; + int they_reseted; + int i_reseted; grub_uint32_t my_start_seq; grub_uint32_t my_cur_seq; grub_uint32_t their_start_seq; @@ -153,7 +154,7 @@ error (grub_net_tcp_socket_t sock) { struct unacked *unack, *next; - if (sock->established && sock->error_hook) + if (sock->error_hook) sock->error_hook (sock, sock->hook_data); for (unack = sock->unack_first; unack; unack = next) @@ -217,7 +218,8 @@ tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) } void -grub_net_tcp_close (grub_net_tcp_socket_t sock) +grub_net_tcp_close (grub_net_tcp_socket_t sock, + int discard_received) { struct grub_net_buff *nb_fin; struct tcphdr *tcph_fin; @@ -225,6 +227,9 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) sock->i_closed = 1; + if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING) + sock->recv_hook = NULL; + nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + GRUB_NET_OUR_IPV4_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); @@ -254,6 +259,8 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) tcph_fin->window = grub_cpu_to_be16 (0); tcph_fin->urgent = 0; err = tcp_send (nb_fin, sock); + if (discard_received == GRUB_NET_TCP_ABORT) + sock->i_reseted = 1; if (err) { grub_netbuff_free (nb_fin); @@ -264,7 +271,7 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) } static void -ack (grub_net_tcp_socket_t sock) +ack_real (grub_net_tcp_socket_t sock, int res) { struct grub_net_buff *nb_ack; struct tcphdr *tcph_ack; @@ -291,9 +298,18 @@ ack (grub_net_tcp_socket_t sock) return; } tcph_ack = (void *) nb_ack->data; - tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); - tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK); - tcph_ack->window = grub_cpu_to_be16 (sock->my_window); + if (res) + { + tcph_ack->ack = grub_cpu_to_be32 (0); + tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_RST); + tcph_ack->window = grub_cpu_to_be16 (0); + } + else + { + tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); + tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK); + tcph_ack->window = grub_cpu_to_be16 (sock->my_window); + } tcph_ack->urgent = 0; tcph_ack->src = grub_cpu_to_be16 (sock->in_port); tcph_ack->dst = grub_cpu_to_be16 (sock->out_port); @@ -305,6 +321,18 @@ ack (grub_net_tcp_socket_t sock) } } +static void +ack (grub_net_tcp_socket_t sock) +{ + ack_real (sock, 0); +} + +static void +reset (grub_net_tcp_socket_t sock) +{ + ack_real (sock, 1); +} + void grub_net_tcp_retransmit (void) { @@ -434,6 +462,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK); tcph->window = grub_cpu_to_be16 (sock->my_window); tcph->urgent = 0; + sock->established = 1; tcp_socket_register (sock); err = tcp_send (nb_ack, sock); if (err) @@ -555,7 +584,7 @@ grub_net_tcp_open (char *server, { grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets), GRUB_AS_LIST (socket)); - if (socket->reseted) + if (socket->they_reseted) grub_error (GRUB_ERR_NET_PORT_CLOSED, "port closed"); else grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer"); @@ -693,7 +722,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, if (grub_be_to_cpu16 (tcph->flags) & TCP_RST) { - sock->reseted = 1; + sock->they_reseted = 1; error (sock); @@ -725,7 +754,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_free (unack); } sock->unack_first = unack; - if (!sock->unack_last) + if (!sock->unack_first) sock->unack_last = NULL; } @@ -735,6 +764,12 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_netbuff_free (nb); return GRUB_ERR_NONE; } + if (sock->i_reseted) + { + reset (sock); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } err = grub_priority_queue_push (sock->pq, &nb); if (err) diff --git a/include/grub/net.h b/include/grub/net.h index 255784315..ac011520a 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -204,6 +204,7 @@ struct grub_net_app_protocol int (*hook) (const char *filename, const struct grub_dirhook_info *info)); grub_err_t (*open) (struct grub_file *file, const char *filename); + grub_err_t (*seek) (struct grub_file *file, grub_off_t off); grub_err_t (*close) (struct grub_file *file); }; diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index 0ac7819bc..b25ceff1b 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -53,8 +53,15 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, struct grub_net_buff *nb, int push); +enum + { + GRUB_NET_TCP_CONTINUE_RECEIVING, + GRUB_NET_TCP_DISCARD, + GRUB_NET_TCP_ABORT + }; + void -grub_net_tcp_close (grub_net_tcp_socket_t sock); +grub_net_tcp_close (grub_net_tcp_socket_t sock, int discard_received); grub_err_t grub_net_tcp_accept (grub_net_tcp_socket_t sock, From 96bd62413f8781ca5d5c0d3f9ba82463f568d06a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 17:50:52 +0200 Subject: [PATCH 19/37] Don't set RST in response to empty packets --- grub-core/net/tcp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 7f120ac16..cb5b6a698 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -764,11 +764,11 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_netbuff_free (nb); return GRUB_ERR_NONE; } - if (sock->i_reseted) + if (sock->i_reseted && (nb->tail - nb->data + - (grub_be_to_cpu16 (tcph->flags) + >> 12) * sizeof (grub_uint32_t)) > 0) { reset (sock); - grub_netbuff_free (nb); - return GRUB_ERR_NONE; } err = grub_priority_queue_push (sock->pq, &nb); From 9aad3cd91d8e2bac585cf77c71aaaa76e53d49fe Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 22:15:30 +0200 Subject: [PATCH 20/37] 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; From faba379c969841b31a419ac36f5bbee68730ea4c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 23:18:22 +0200 Subject: [PATCH 21/37] put license on priority_queue --- grub-core/lib/priority_queue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/lib/priority_queue.c b/grub-core/lib/priority_queue.c index 0cf258ecf..a790910a8 100644 --- a/grub-core/lib/priority_queue.c +++ b/grub-core/lib/priority_queue.c @@ -19,6 +19,10 @@ #ifndef TEST #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + #else #include #include From 140674be60bec29fac7128ebaa6ac99df059b14c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 23:18:44 +0200 Subject: [PATCH 22/37] Remove leftover variabel --- grub-core/net/net.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 7d6ef2759..398e6f5ae 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -864,7 +864,6 @@ 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) From 8530b00aa904481dd9b90970fd3c4fa412215344 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 23:19:11 +0200 Subject: [PATCH 23/37] Fix compilation on 64-bit targets --- include/grub/types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/grub/types.h b/include/grub/types.h index 4dde10a16..84bc97455 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -116,6 +116,7 @@ typedef grub_int32_t grub_ssize_t; # define PRIxGRUB_SIZE "x" # define PRIxGRUB_ADDR "x" # define PRIuGRUB_SIZE "u" +# define PRIdGRUB_SSIZE "d" #endif #if GRUB_CPU_SIZEOF_LONG == 8 From eb6a7b9788747839c9ce1805e86c42e5eb5de585 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 23:33:36 +0200 Subject: [PATCH 24/37] Hopefully fix EFI problem. Not tested --- grub-core/net/drivers/efi/efinet.c | 15 +++++++++++++-- include/grub/efi/api.h | 4 +++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 906a688dc..2a392bc98 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -35,11 +35,22 @@ send_card_buffer (const struct grub_net_card *dev, { grub_efi_status_t st; grub_efi_simple_network_t *net = dev->efi_net; + grub_uint64_t limit_time = grub_get_time_ms () + 4000; st = efi_call_7 (net->transmit, net, 0, (pack->tail - pack->data), pack->data, NULL, NULL, NULL); if (st != GRUB_EFI_SUCCESS) - return grub_error (GRUB_ERR_IO, "Couldn't send network packet."); - return GRUB_ERR_NONE; + return grub_error (GRUB_ERR_IO, "couldn't send network packet"); + while (1) + { + void *txbuf = NULL; + st = efi_call_3 (net->get_status, net, 0, &txbuf); + if (st != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_IO, "couldn't send network packet"); + if (txbuf) + return GRUB_ERR_NONE; + if (limit_time < grub_get_time_ms ()) + return grub_error (GRUB_ERR_TIMEOUT, "couldn't send network packet"); + } } static struct grub_net_buff * diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index b20baa015..6f3e2a71d 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -1301,7 +1301,9 @@ struct grub_efi_simple_network void (*statistics) (void); void (*mcastiptomac) (void); void (*nvdata) (void); - void (*getstatus) (void); + grub_efi_status_t (*get_status) (struct grub_efi_simple_network *this, + grub_uint32_t *int_status, + void **txbuf); grub_efi_status_t (*transmit) (struct grub_efi_simple_network *this, grub_efi_uintn_t header_size, grub_efi_uintn_t buffer_size, From d5e60b1ba06af4a63605e4f23b1299cf3422d5e7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 5 Oct 2011 23:50:43 +0200 Subject: [PATCH 25/37] Add license statement to the emunet --- grub-core/net/drivers/emu/emunet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c index ff333d370..2aba28d23 100644 --- a/grub-core/net/drivers/emu/emunet.c +++ b/grub-core/net/drivers/emu/emunet.c @@ -28,6 +28,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static int fd; static grub_err_t From a53cf6534d5a0acd02120c6dff949bf01dfe38a9 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 12 Oct 2011 10:20:55 +0200 Subject: [PATCH 26/37] some more ipv6 code --- grub-core/Makefile.core.def | 1 + grub-core/net/bootp.c | 3 +- grub-core/net/ethernet.c | 7 +- grub-core/net/icmp.c | 23 +++- grub-core/net/icmp6.c | 235 ++++++++++++++++++++++++++++++++++++ grub-core/net/ip.c | 37 ++++-- grub-core/net/net.c | 207 ++++++++++++++++++++++++++----- grub-core/net/tcp.c | 7 ++ grub-core/net/udp.c | 7 ++ include/grub/net.h | 24 +++- include/grub/net/ethernet.h | 2 +- include/grub/net/ip.h | 35 ++++-- 12 files changed, 528 insertions(+), 60 deletions(-) create mode 100644 grub-core/net/icmp6.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index aa5f2bfe7..3827227b8 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1603,6 +1603,7 @@ module = { common = net/udp.c; common = net/tcp.c; common = net/icmp.c; + common = net/icmp6.c; common = net/ethernet.c; common = net/arp.c; common = net/netbuff.c; diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 3187f2e9c..1b8527255 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -130,7 +130,7 @@ grub_net_configure_by_dhcp_ack (const char *name, : sizeof (hwaddr.mac)); hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - inter = grub_net_add_addr (name, card, addr, hwaddr, flags); + inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags); if (bp->gateway_ip) { grub_net_network_level_netaddress_t target; @@ -379,6 +379,7 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), "unrecognised format specification %s", args[3]); } +/* FIXME: allow to specify mac address. */ static grub_err_t grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), int argc, char **args) diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index 053f87b56..8d7f66830 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -81,7 +81,7 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, grub_err_t grub_net_recv_ethernet_packet (struct grub_net_buff * nb, - const struct grub_net_card * card) + struct grub_net_card * card) { struct etherhdr *eth; struct llchdr *llch; @@ -123,11 +123,8 @@ grub_net_recv_ethernet_packet (struct grub_net_buff * nb, return GRUB_ERR_NONE; /* 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; + return grub_net_recv_ip_packets (nb, card, &hwaddress); } grub_netbuff_free (nb); return GRUB_ERR_NONE; diff --git a/grub-core/net/icmp.c b/grub-core/net/icmp.c index 8ea020107..88132b142 100644 --- a/grub-core/net/icmp.c +++ b/grub-core/net/icmp.c @@ -25,13 +25,13 @@ struct icmp_header grub_uint8_t type; grub_uint8_t code; grub_uint16_t checksum; -}; +} __attribute__ ((packed)); struct ping_header { grub_uint16_t id; grub_uint16_t seq; -}; +} __attribute__ ((packed)); enum { @@ -40,7 +40,7 @@ enum }; grub_err_t -grub_net_recv_icmp_packet (struct grub_net_buff * nb, +grub_net_recv_icmp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *src) { @@ -48,15 +48,28 @@ grub_net_recv_icmp_packet (struct grub_net_buff * nb, grub_err_t err; grub_uint16_t checksum; + /* Ignore broadcast. */ + if (!inf) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + icmph = (struct icmp_header *) nb->data; if (nb->tail - nb->data < (grub_ssize_t) sizeof (*icmph)) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "ICMP packet too small"); + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } checksum = icmph->checksum; icmph->checksum = 0; if (checksum != grub_net_ip_chksum (nb->data, nb->tail - nb->data)) - return GRUB_ERR_NONE; + { + icmph->checksum = checksum; + return GRUB_ERR_NONE; + } icmph->checksum = checksum; err = grub_netbuff_pull (nb, sizeof (*icmph)); diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c new file mode 100644 index 000000000..ac12c5733 --- /dev/null +++ b/grub-core/net/icmp6.c @@ -0,0 +1,235 @@ +/* + * 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 + +struct icmp_header +{ + grub_uint8_t type; + grub_uint8_t code; + grub_uint16_t checksum; +} __attribute__ ((packed)); + +struct ping_header +{ + grub_uint16_t id; + grub_uint16_t seq; +} __attribute__ ((packed)); + +struct router_adv +{ + grub_uint8_t ttl; + grub_uint8_t flags; + grub_uint16_t router_lifetime; + grub_uint32_t reachable_time; + grub_uint32_t retrans_timer; + grub_uint8_t options[0]; +} __attribute__ ((packed)); + +struct prefix_option +{ + grub_uint8_t type; + grub_uint8_t len; + grub_uint8_t prefixlen; + grub_uint8_t flags; + grub_uint32_t valid_lifetime; + grub_uint32_t prefered_lifetime; + grub_uint32_t reserved; + grub_uint64_t prefix[2]; +} __attribute__ ((packed)); + +enum + { + FLAG_SLAAC = 0x40 + }; + +enum + { + ICMP6_ECHO = 128, + ICMP6_ECHO_REPLY = 129, + ICMP6_ROUTER_ADVERTISE = 134 + }; + +grub_err_t +grub_net_recv_icmp6_packet (struct grub_net_buff *nb, + struct grub_net_card *card, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *source, + const grub_net_network_level_address_t *dest) +{ + struct icmp_header *icmph; + grub_err_t err; + grub_uint16_t checksum; + + icmph = (struct icmp_header *) nb->data; + + if (nb->tail - nb->data < (grub_ssize_t) sizeof (*icmph)) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + checksum = icmph->checksum; + icmph->checksum = 0; + if (checksum != grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_ICMPV6, + source, + dest)) + { + grub_dprintf ("net", "invalid ICMPv6 checksum: %04x instead of %04x\n", + checksum, + grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_ICMPV6, + source, + dest)); + icmph->checksum = checksum; + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + icmph->checksum = checksum; + + err = grub_netbuff_pull (nb, sizeof (*icmph)); + if (err) + return err; + + grub_dprintf ("net", "ICMPv6 message: %02x, %02x\n", + icmph->type, icmph->code); + switch (icmph->type) + { + case ICMP6_ECHO: + /* Don't accept multicast pings. */ + if (!inf) + break; + { + struct grub_net_buff *nb_reply; + struct icmp_header *icmphr; + if (icmph->code) + break; + nb_reply = grub_netbuff_alloc (nb->tail - nb->data + 512); + if (!nb_reply) + { + grub_netbuff_free (nb); + return grub_errno; + } + err = grub_netbuff_reserve (nb_reply, nb->tail - nb->data + 512); + if (err) + goto ping_fail; + err = grub_netbuff_push (nb_reply, nb->tail - nb->data); + if (err) + goto ping_fail; + grub_memcpy (nb_reply->data, nb->data, nb->tail - nb->data); + err = grub_netbuff_push (nb_reply, sizeof (*icmphr)); + if (err) + goto ping_fail; + icmphr = (struct icmp_header *) nb_reply->data; + icmphr->type = ICMP6_ECHO_REPLY; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = grub_net_ip_transport_checksum (nb_reply, + GRUB_NET_IP_ICMPV6, + &inf->address, + source); + err = grub_net_send_ip_packet (inf, source, nb_reply, + GRUB_NET_IP_ICMPV6); + + ping_fail: + grub_netbuff_free (nb); + grub_netbuff_free (nb_reply); + return err; + } + case ICMP6_ROUTER_ADVERTISE: + { + grub_uint8_t *ptr; + if (icmph->code) + break; + err = grub_netbuff_pull (nb, sizeof (struct router_adv)); + if (err) + return err; + for (ptr = (grub_uint8_t *) nb->data + sizeof (struct router_adv); + ptr < nb->tail; ) + { + grub_dprintf ("net", "option %02x, %02x\n", ptr[0], ptr[1]); + if (ptr + 2 >= nb->tail || ptr[1] == 0) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (ptr[0] == 3 && ptr[1] == 4) + { + struct prefix_option *opt = (struct prefix_option *) ptr; + struct grub_net_slaac_mac_list *slaac; + if (!(opt->flags & FLAG_SLAAC) + || (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80 + || (grub_be_to_cpu32 (opt->prefered_lifetime) + > grub_be_to_cpu32 (opt->valid_lifetime)) + || opt->prefixlen != 64) + { + ptr += ptr[1] * 8; + grub_dprintf ("net", "discarded prefix: %d, %d, %d, %d\n", + !(opt->flags & FLAG_SLAAC), + (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80, + (grub_be_to_cpu32 (opt->prefered_lifetime) + > grub_be_to_cpu32 (opt->valid_lifetime)), + opt->prefixlen != 64); + continue; + } + for (slaac = card->slaac_list; slaac; slaac = slaac->next) + { + grub_net_network_level_address_t addr; + if (slaac->address.type + != GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET) + continue; + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + addr.ipv6[0] = opt->prefix[0]; + addr.ipv6[1] = grub_net_ipv6_get_id (&slaac->address); + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + if (inf->card == card + && grub_net_addr_cmp (&inf->address, &addr) == 0) + break; + } + /* Update lease time if needed here once we have + lease times. */ + if (inf) + continue; + + grub_dprintf ("net", "creating slaac\n"); + + { + char name[grub_strlen (slaac->name) + + sizeof (":XXXXXXXXXXXXXXXXXXXX")]; + grub_snprintf (name, sizeof (name), "%s:%d", + slaac->name, slaac->slaac_counter++); + grub_net_add_addr (name, + card, &addr, + &slaac->address, 0); + } + } + } + ptr += ptr[1] * 8; + } + if (ptr != nb->tail) + break; + } + }; + + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 32a7d7fe7..18593da49 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -221,7 +221,7 @@ grub_net_send_ip4_packet (struct grub_net_network_level_interface * inf, static grub_err_t handle_dgram (struct grub_net_buff *nb, - const struct grub_net_card *card, + struct grub_net_card *card, const grub_net_link_level_address_t *hwaddress, grub_net_ip_protocol_t proto, const grub_net_network_level_address_t *source, @@ -282,8 +282,12 @@ handle_dgram (struct grub_net_buff *nb, break; } - if (!inf) + if (!inf && !(dest->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && dest->ipv6[0] == grub_be_to_cpu64 (0xff02ULL << 48) + && dest->ipv6[1] == grub_be_to_cpu64 (1))) { + grub_dprintf ("net", "undirected dgram discarded: %x, %lx, %lx\n", + dest->type, dest->ipv6[0], dest->ipv6[1]); grub_netbuff_free (nb); return GRUB_ERR_NONE; } @@ -296,6 +300,8 @@ handle_dgram (struct grub_net_buff *nb, return grub_net_recv_tcp_packet (nb, inf, source); case GRUB_NET_IP_ICMP: return grub_net_recv_icmp_packet (nb, inf, source); + case GRUB_NET_IP_ICMPV6: + return grub_net_recv_icmp6_packet (nb, card, inf, source, dest); default: grub_netbuff_free (nb); break; @@ -330,9 +336,9 @@ free_old_fragments (void) } } -grub_err_t +static grub_err_t grub_net_recv_ip4_packets (struct grub_net_buff * nb, - const struct grub_net_card * card, + struct grub_net_card * card, const grub_net_link_level_address_t * hwaddress) { struct iphdr *iph = (struct iphdr *) nb->data; @@ -590,12 +596,11 @@ grub_net_send_ip_packet (struct grub_net_network_level_interface * inf, default: return grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP"); } - } -grub_err_t +static grub_err_t grub_net_recv_ip6_packets (struct grub_net_buff * nb, - const struct grub_net_card * card, + struct grub_net_card * card, const grub_net_link_level_address_t * hwaddress) { struct ip6hdr *iph = (struct ip6hdr *) nb->data; @@ -652,8 +657,24 @@ grub_net_recv_ip6_packets (struct grub_net_buff * nb, 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)); + grub_memcpy (dest.ipv6, &iph->dest, sizeof (dest.ipv6)); return handle_dgram (nb, card, hwaddress, iph->protocol, &source, &dest); } + +grub_err_t +grub_net_recv_ip_packets (struct grub_net_buff * nb, + struct grub_net_card * card, + const grub_net_link_level_address_t * hwaddress) +{ + struct iphdr *iph = (struct iphdr *) nb->data; + + if ((iph->verhdrlen >> 4) == 4) + return grub_net_recv_ip4_packets (nb, card, hwaddress); + if ((iph->verhdrlen >> 4) == 6) + return grub_net_recv_ip6_packets (nb, card, hwaddress); + grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 398e6f5ae..1c6d47757 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,151 @@ grub_net_card_unregister (struct grub_net_card *card) GRUB_AS_LIST (card)); } +static struct grub_net_slaac_mac_list * +grub_net_ipv6_get_slaac (struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddr) +{ + struct grub_net_slaac_mac_list *slaac; + char *ptr; + + for (slaac = card->slaac_list; slaac; slaac = slaac->next) + if (grub_net_hwaddr_cmp (&slaac->address, hwaddr) == 0) + return slaac; + + slaac = grub_zalloc (sizeof (*slaac)); + if (!slaac) + return NULL; + + slaac->name = grub_malloc (grub_strlen (card->name) + + GRUB_NET_MAX_STR_HWADDR_LEN + + sizeof (":slaac")); + ptr = grub_stpcpy (slaac->name, card->name); + if (grub_net_hwaddr_cmp (&card->default_address, hwaddr) != 0) + { + ptr = grub_stpcpy (ptr, ":"); + grub_net_hwaddr_to_str (hwaddr, ptr); + ptr += grub_strlen (ptr); + } + ptr = grub_stpcpy (ptr, ":slaac"); + + grub_memcpy (&slaac->address, hwaddr, sizeof (slaac->address)); + slaac->next = card->slaac_list; + card->slaac_list = slaac; + return slaac; +} + +struct grub_net_network_level_interface * +grub_net_ipv6_get_link_local (struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddr) +{ + struct grub_net_network_level_interface *inf; + char name[grub_strlen (card->name) + + GRUB_NET_MAX_STR_HWADDR_LEN + + sizeof (":link")]; + char *ptr; + grub_net_network_level_address_t addr; + + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + addr.ipv6[0] = grub_cpu_to_be64 (0xfe80ULL << 48); + addr.ipv6[1] = grub_net_ipv6_get_id (hwaddr); + + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + if (inf->card == card + && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddr) == 0 + && grub_net_addr_cmp (&inf->address, &addr) == 0) + return inf; + } + + ptr = grub_stpcpy (name, card->name); + if (grub_net_hwaddr_cmp (&card->default_address, hwaddr) != 0) + { + ptr = grub_stpcpy (ptr, ":"); + grub_net_hwaddr_to_str (hwaddr, ptr); + ptr += grub_strlen (ptr); + } + ptr = grub_stpcpy (ptr, ":link"); + return grub_net_add_addr (name, card, &addr, hwaddr, 0); +} + +/* FIXME: allow to specify mac address. */ +static grub_err_t +grub_cmd_ipv6_autoconf (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_card *card; + struct grub_net_network_level_interface **ifaces; + grub_size_t ncards = 0; + unsigned j = 0; + int interval; + grub_err_t err; + struct grub_net_slaac_mac_list **slaacs; + + FOR_NET_CARDS (card) + { + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + ncards++; + } + + ifaces = grub_zalloc (ncards * sizeof (ifaces[0])); + slaacs = grub_zalloc (ncards * sizeof (slaacs[0])); + if (!ifaces || !slaacs) + { + grub_free (ifaces); + grub_free (slaacs); + return grub_errno; + } + + FOR_NET_CARDS (card) + { + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + ifaces[j] = grub_net_ipv6_get_link_local (card, &card->default_address); + if (!ifaces[j]) + { + grub_free (ifaces); + grub_free (slaacs); + return grub_errno; + } + slaacs[j] = grub_net_ipv6_get_slaac (card, &card->default_address); + if (!slaacs[j]) + { + grub_free (ifaces); + grub_free (slaacs); + return grub_errno; + } + j++; + } + + for (interval = 200; interval < 10000; interval *= 2) + { + /* FIXME: send router solicitation. */ + int done = 1; + for (j = 0; j < ncards; j++) + { + if (slaacs[j]->slaac_counter) + continue; + done = 0; + } + if (done) + break; + grub_net_poll_cards (interval); + } + + err = GRUB_ERR_NONE; + for (j = 0; j < ncards; j++) + { + if (slaacs[j]->slaac_counter) + continue; + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "couldn't configure %s", + ifaces[j]->card->name); + } + + grub_free (ifaces); + grub_free (slaacs); + return err; +} static inline void grub_net_route_register (struct grub_net_route *route) @@ -265,20 +411,22 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf) case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: { char *ptr = buf; - grub_uint32_t n = grub_be_to_cpu32 (target->ipv6[0]); + grub_uint64_t n = grub_be_to_cpu64 (target->ipv6[0]); int i; for (i = 0; i < 4; i++) { - grub_snprintf (ptr, 6, "%x:", (n >> (48 - 16 * i)) & 0xffff); + grub_snprintf (ptr, 6, "%" PRIxGRUB_UINT64_T ":", + (n >> (48 - 16 * i)) & 0xffff); ptr += grub_strlen (ptr); } - n = grub_be_to_cpu32 (target->ipv6[1]); + n = grub_be_to_cpu64 (target->ipv6[1]); for (i = 0; i < 3; i++) { - grub_snprintf (ptr, 6, "%x:", (n >> (48 - 16 * i)) & 0xffff); + grub_snprintf (ptr, 6, "%" PRIxGRUB_UINT64_T ":", + (n >> (48 - 16 * i)) & 0xffff); ptr += grub_strlen (ptr); } - grub_snprintf (ptr, 5, "%x", n & 0xffff); + grub_snprintf (ptr, 5, "%" PRIxGRUB_UINT64_T, n & 0xffff); return; } case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: @@ -290,18 +438,13 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf) } return; } - grub_printf ("Unknown address type %d\n", target->type); + grub_snprintf (buf, GRUB_NET_MAX_STR_ADDR_LEN, + "Unknown address type %d", target->type); } -/* - Currently suppoerted adresses: - ethernet: XX:XX:XX:XX:XX:XX - */ -#define MAX_STR_HWADDR_LEN (sizeof ("XX:XX:XX:XX:XX:XX")) - -static void -hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str) +void +grub_net_hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str) { str[0] = 0; switch (addr->type) @@ -312,7 +455,7 @@ hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str) unsigned i; for (ptr = str, i = 0; i < ARRAY_SIZE (addr->mac); i++) { - grub_snprintf (ptr, MAX_STR_HWADDR_LEN - (ptr - str), + grub_snprintf (ptr, GRUB_NET_MAX_STR_HWADDR_LEN - (ptr - str), "%02x:", addr->mac[i] & 0xff); ptr += (sizeof ("XX:") - 1); } @@ -380,9 +523,9 @@ static void grub_net_network_level_interface_register (struct grub_net_network_level_interface *inter) { { - char buf[MAX_STR_HWADDR_LEN]; + char buf[GRUB_NET_MAX_STR_HWADDR_LEN]; char name[grub_strlen (inter->name) + sizeof ("net__mac")]; - hwaddr_to_str (&inter->hwaddress, buf); + grub_net_hwaddr_to_str (&inter->hwaddress, buf); grub_snprintf (name, sizeof (name), "net_%s_mac", inter->name); grub_env_set (name, buf); grub_register_variable_hook (name, 0, hwaddr_set_env); @@ -408,8 +551,8 @@ grub_net_network_level_interface_register (struct grub_net_network_level_interfa struct grub_net_network_level_interface * grub_net_add_addr (const char *name, struct grub_net_card *card, - grub_net_network_level_address_t addr, - grub_net_link_level_address_t hwaddress, + const grub_net_network_level_address_t *addr, + const grub_net_link_level_address_t *hwaddress, grub_net_interface_flags_t flags) { struct grub_net_network_level_interface *inter; @@ -419,8 +562,8 @@ grub_net_add_addr (const char *name, return NULL; inter->name = grub_strdup (name); - grub_memcpy (&(inter->address), &addr, sizeof (inter->address)); - grub_memcpy (&(inter->hwaddress), &hwaddress, sizeof (inter->hwaddress)); + grub_memcpy (&(inter->address), addr, sizeof (inter->address)); + grub_memcpy (&(inter->hwaddress), hwaddress, sizeof (inter->hwaddress)); inter->flags = flags; inter->card = card; inter->dhcp_ack = NULL; @@ -428,10 +571,10 @@ grub_net_add_addr (const char *name, grub_net_network_level_interface_register (inter); - if (addr.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + if (addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) { int mask = -1; - grub_uint32_t ip_cpu = grub_be_to_cpu32 (addr.ipv4); + grub_uint32_t ip_cpu = grub_be_to_cpu32 (addr->ipv4); if (!(ip_cpu & 0x80000000)) mask = 8; else if (!(ip_cpu & 0x40000000)) @@ -498,7 +641,7 @@ grub_cmd_addaddr (struct grub_command *cmd __attribute__ ((unused)), if (card->flags & GRUB_NET_CARD_HWADDRESS_IMMUTABLE) flags |= GRUB_NET_INTERFACE_HWADDRESS_IMMUTABLE; - grub_net_add_addr (args[0], card, addr, card->default_address, + grub_net_add_addr (args[0], card, &addr, &card->default_address, flags); return grub_errno; } @@ -685,8 +828,8 @@ grub_cmd_listcards (struct grub_command *cmd __attribute__ ((unused)), struct grub_net_card *card; FOR_NET_CARDS(card) { - char buf[MAX_STR_HWADDR_LEN]; - hwaddr_to_str (&card->default_address, buf); + char buf[GRUB_NET_MAX_STR_HWADDR_LEN]; + grub_net_hwaddr_to_str (&card->default_address, buf); grub_printf ("%s %s\n", card->name, buf); } return GRUB_ERR_NONE; @@ -700,9 +843,9 @@ grub_cmd_listaddrs (struct grub_command *cmd __attribute__ ((unused)), struct grub_net_network_level_interface *inf; FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { - char bufh[MAX_STR_HWADDR_LEN]; + char bufh[GRUB_NET_MAX_STR_HWADDR_LEN]; char bufn[GRUB_NET_MAX_STR_ADDR_LEN]; - hwaddr_to_str (&inf->hwaddress, bufh); + grub_net_hwaddr_to_str (&inf->hwaddress, bufh); grub_net_addr_to_str (&inf->address, bufn); grub_printf ("%s %s %s\n", inf->name, bufh, bufn); } @@ -1060,13 +1203,18 @@ static void *fini_hnd; static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute; static grub_command_t cmd_lsroutes, cmd_lscards; -static grub_command_t cmd_lsaddr; +static grub_command_t cmd_lsaddr, cmd_slaac; GRUB_MOD_INIT(net) { cmd_addaddr = grub_register_command ("net_add_addr", grub_cmd_addaddr, "SHORTNAME CARD ADDRESS [HWADDRESS]", N_("Add a network address.")); + cmd_slaac = grub_register_command ("net_ipv6_autoconf", + grub_cmd_ipv6_autoconf, + "[CARD [HWADDRESS]]", + N_("Perform an IPV6 autoconfiguration")); + cmd_deladdr = grub_register_command ("net_del_addr", grub_cmd_deladdr, "SHORTNAME", N_("Delete a network address.")); @@ -1102,6 +1250,7 @@ GRUB_MOD_FINI(net) grub_unregister_command (cmd_lsroutes); grub_unregister_command (cmd_lscards); grub_unregister_command (cmd_lsaddr); + grub_unregister_command (cmd_slaac); grub_fs_unregister (&grub_net_fs); grub_net_open = NULL; grub_net_fini_hw (0); diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index b1eb3d9a8..72ee8a239 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -695,6 +695,13 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_net_tcp_socket_t sock; grub_err_t err; + /* Ignore broadcast. */ + if (!inf) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + tcph = (struct tcphdr *) nb->data; if ((grub_be_to_cpu16 (tcph->flags) >> 12) < 5) { diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index e816547ac..a2315dd3f 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -140,6 +140,13 @@ grub_net_recv_udp_packet (struct grub_net_buff *nb, grub_net_udp_socket_t sock; grub_err_t err; + /* Ignore broadcast. */ + if (!inf) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph = (struct udphdr *) nb->data; if (nb->tail - nb->data < (grub_ssize_t) sizeof (*udph)) { diff --git a/include/grub/net.h b/include/grub/net.h index cb9d40cd0..a4785d48c 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -97,6 +97,14 @@ typedef struct grub_net_packets #include #endif +struct grub_net_slaac_mac_list +{ + struct grub_net_slaac_mac_list *next; + grub_net_link_level_address_t address; + int slaac_counter; + char *name; +}; + struct grub_net_card { struct grub_net_card *next; @@ -109,6 +117,7 @@ struct grub_net_card unsigned idle_poll_delay_ms; grub_uint64_t last_poll; grub_size_t mtu; + struct grub_net_slaac_mac_list *slaac_list; union { #ifdef GRUB_MACHINE_EFI @@ -282,8 +291,8 @@ grub_net_session_recv (struct grub_net_session *session, void *buf, struct grub_net_network_level_interface * grub_net_add_addr (const char *name, struct grub_net_card *card, - grub_net_network_level_address_t addr, - grub_net_link_level_address_t hwaddress, + const grub_net_network_level_address_t *addr, + const grub_net_link_level_address_t *hwaddress, grub_net_interface_flags_t flags); extern struct grub_net_network_level_interface *grub_net_network_level_interfaces; @@ -404,13 +413,22 @@ grub_net_addr_cmp (const grub_net_network_level_address_t *a, /* Currently supported adresses: IPv4: XXX.XXX.XXX.XXX - IPv&: XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX + IPv6: XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX */ #define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") +/* + Currently suppoerted adresses: + ethernet: XX:XX:XX:XX:XX:XX + */ + +#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof ("XX:XX:XX:XX:XX:XX")) + void grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf); +void +grub_net_hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str); extern struct grub_net_network_level_interface *grub_net_network_level_interfaces; #define FOR_NET_NETWORK_LEVEL_INTERFACES(var) for (var = grub_net_network_level_interfaces; var; var = var->next) diff --git a/include/grub/net/ethernet.h b/include/grub/net/ethernet.h index 0749d9d87..23a935e98 100644 --- a/include/grub/net/ethernet.h +++ b/include/grub/net/ethernet.h @@ -36,6 +36,6 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, grub_net_ethertype_t ethertype); grub_err_t grub_net_recv_ethernet_packet (struct grub_net_buff *nb, - const struct grub_net_card *card); + struct grub_net_card *card); #endif diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index 759577522..e086f4411 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -25,20 +25,29 @@ typedef enum grub_net_ip_protocol { GRUB_NET_IP_ICMP = 1, GRUB_NET_IP_TCP = 6, - GRUB_NET_IP_UDP = 17 + GRUB_NET_IP_UDP = 17, + GRUB_NET_IP_ICMPV6 = 58 } grub_net_ip_protocol_t; #define GRUB_NET_IP_BROADCAST 0xFFFFFFFF +static inline grub_uint64_t +grub_net_ipv6_get_id (const grub_net_link_level_address_t *addr) +{ + return grub_cpu_to_be64 (((grub_uint64_t) (addr->mac[0] ^ 2) << 56) + | ((grub_uint64_t) addr->mac[1] << 48) + | ((grub_uint64_t) addr->mac[2] << 40) + | 0xfffe000000ULL + | ((grub_uint64_t) addr->mac[3] << 16) + | ((grub_uint64_t) addr->mac[4] << 8) + | ((grub_uint64_t) addr->mac[5])); +} + grub_uint16_t grub_net_ip_chksum(void *ipv, grub_size_t len); grub_err_t -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_net_recv_ip_packets (struct grub_net_buff *nb, + 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, @@ -51,6 +60,12 @@ grub_net_recv_icmp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *src); grub_err_t +grub_net_recv_icmp6_packet (struct grub_net_buff *nb, + struct grub_net_card *card, + struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *source, + const grub_net_network_level_address_t *dest); +grub_err_t grub_net_recv_udp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *src); @@ -65,4 +80,8 @@ 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 grub_net_network_level_interface * +grub_net_ipv6_get_link_local (struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddr); + #endif From 7c006811f8a641b32fc483182e83eaecf9645116 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 12 Oct 2011 23:15:02 +0200 Subject: [PATCH 27/37] more ipv6 code. Now ipv6 ping succeeds --- grub-core/net/arp.c | 116 +++------------ grub-core/net/ethernet.c | 6 +- grub-core/net/icmp6.c | 304 +++++++++++++++++++++++++++++++++++++-- grub-core/net/ip.c | 66 ++++++--- grub-core/net/net.c | 139 ++++++++++++++++++ include/grub/net.h | 17 +++ include/grub/net/arp.h | 9 +- include/grub/net/ip.h | 6 +- include/grub/types.h | 19 +++ 9 files changed, 541 insertions(+), 141 deletions(-) diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index b1b719dbe..b4c10531b 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -46,41 +46,10 @@ struct arphdr { } __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; - -static void -arp_init_table (void) -{ - grub_memset (arp_table, 0, sizeof (arp_table)); - new_table_entry = 0; -} - -static struct arp_entry * -arp_find_entry (const grub_net_network_level_address_t *proto) -{ - unsigned i; - for (i = 0; i < ARRAY_SIZE (arp_table); i++) - { - if (arp_table[i].avail == 1 && - grub_net_addr_cmp (&arp_table[i].nl_address, proto) == 0) - return &(arp_table[i]); - } - return NULL; -} - grub_err_t -grub_net_arp_resolve (struct grub_net_network_level_interface *inf, - const grub_net_network_level_address_t *proto_addr, - grub_net_link_level_address_t *hw_addr) +grub_net_arp_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr) { - struct arp_entry *entry; struct grub_net_buff nb; struct arphdr *arp_header; grub_net_link_level_address_t target_hw_addr; @@ -90,32 +59,11 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, grub_size_t addrlen; grub_uint16_t etherpro; - if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && proto_addr->ipv4 == 0xffffffff) - { - hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memset (hw_addr->mac, -1, 6); - return GRUB_ERR_NONE; - } - - /* Check cache table. */ - entry = arp_find_entry (proto_addr); - if (entry) - { - *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"); @@ -141,45 +89,39 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, aux += 6; /* Sender protocol address */ - 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); + grub_memcpy (aux, &inf->address.ipv4, 4); aux += addrlen; /* Target hardware address */ for (i = 0; i < 6; i++) aux[i] = 0x00; aux += 6; /* Target protocol address */ - 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_memcpy (aux, &proto_addr->ipv4, 4); grub_memset (&target_hw_addr.mac, 0xff, 6); send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); for (i = 0; i < GRUB_NET_TRIES; i++) { - entry = arp_find_entry (proto_addr); - if (entry) - { - grub_memcpy (hw_addr, &entry->ll_address, sizeof (*hw_addr)); - return GRUB_ERR_NONE; - } + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + return GRUB_ERR_NONE; grub_net_poll_cards (GRUB_NET_INTERVAL); + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + return GRUB_ERR_NONE; + send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); } - return grub_error (GRUB_ERR_TIMEOUT, "timeout: could not resolve hardware address"); + return GRUB_ERR_NONE; } grub_err_t -grub_net_arp_receive (struct grub_net_buff *nb) +grub_net_arp_receive (struct grub_net_buff *nb, + struct grub_net_card *card) { struct arphdr *arp_header = (struct arphdr *) nb->data; - struct arp_entry *entry; grub_uint8_t *sender_hardware_address; grub_uint8_t *target_hardware_address; grub_net_network_level_address_t sender_addr, target_addr; + grub_net_link_level_address_t sender_hw_addr; struct grub_net_network_level_interface *inf; grub_uint8_t *sender_protocol_address, *target_protocol_address; @@ -196,35 +138,13 @@ grub_net_arp_receive (struct grub_net_buff *nb) 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 (&sender_addr); - /* Update sender hardware address. */ - if (entry) - grub_memcpy (entry->ll_address.mac, sender_hardware_address, 6); - else - { - /* Add sender to cache table. */ - if (new_table_entry == -1) - arp_init_table (); - entry = &(arp_table[new_table_entry]); - entry->avail = 1; - 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)) - new_table_entry = 0; - } + + sender_hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (sender_hw_addr.mac, &sender_hardware_address, + sizeof (sender_hw_addr.mac)); + grub_net_link_layer_add_address (card, &sender_addr, &sender_hw_addr, 1); FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index 8d7f66830..6b5db9aaa 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -80,8 +80,8 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, } grub_err_t -grub_net_recv_ethernet_packet (struct grub_net_buff * nb, - struct grub_net_card * card) +grub_net_recv_ethernet_packet (struct grub_net_buff *nb, + struct grub_net_card *card) { struct etherhdr *eth; struct llchdr *llch; @@ -118,7 +118,7 @@ grub_net_recv_ethernet_packet (struct grub_net_buff * nb, { /* ARP packet. */ case GRUB_NET_ETHERTYPE_ARP: - grub_net_arp_receive (nb); + grub_net_arp_receive (nb, card); grub_netbuff_free (nb); return GRUB_ERR_NONE; /* IP packet. */ diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c index ac12c5733..1f9278910 100644 --- a/grub-core/net/icmp6.c +++ b/grub-core/net/icmp6.c @@ -43,10 +43,15 @@ struct router_adv grub_uint8_t options[0]; } __attribute__ ((packed)); -struct prefix_option +struct option_header { grub_uint8_t type; grub_uint8_t len; +} __attribute__ ((packed)); + +struct prefix_option +{ + struct option_header header; grub_uint8_t prefixlen; grub_uint8_t flags; grub_uint32_t valid_lifetime; @@ -55,6 +60,18 @@ struct prefix_option grub_uint64_t prefix[2]; } __attribute__ ((packed)); +struct neighbour_solicit +{ + grub_uint32_t reserved; + grub_uint64_t target[2]; +} __attribute__ ((packed)); + +struct neighbour_advertise +{ + grub_uint32_t flags; + grub_uint64_t target[2]; +} __attribute__ ((packed)); + enum { FLAG_SLAAC = 0x40 @@ -64,7 +81,22 @@ enum { ICMP6_ECHO = 128, ICMP6_ECHO_REPLY = 129, - ICMP6_ROUTER_ADVERTISE = 134 + ICMP6_ROUTER_ADVERTISE = 134, + ICMP6_NEIGHBOUR_SOLICIT = 135, + ICMP6_NEIGHBOUR_ADVERTISE = 136, + }; + +enum + { + OPTION_SOURCE_LINK_LAYER_ADDRESS = 1, + OPTION_TARGET_LINK_LAYER_ADDRESS = 2, + OPTION_PREFIX = 3 + }; + +enum + { + FLAG_SOLICITED = (1 << 30), + FLAG_OVERRIDE = (1 << 29) }; grub_err_t @@ -72,7 +104,8 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, struct grub_net_card *card, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *source, - const grub_net_network_level_address_t *dest) + const grub_net_network_level_address_t *dest, + grub_uint8_t ttl) { struct icmp_header *icmph; grub_err_t err; @@ -107,7 +140,10 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, err = grub_netbuff_pull (nb, sizeof (*icmph)); if (err) - return err; + { + grub_netbuff_free (nb); + return err; + } grub_dprintf ("net", "ICMPv6 message: %02x, %02x\n", icmph->type, icmph->code); @@ -154,24 +190,180 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, grub_netbuff_free (nb_reply); return err; } - case ICMP6_ROUTER_ADVERTISE: + case ICMP6_NEIGHBOUR_SOLICIT: { + struct neighbour_solicit *nbh; + struct grub_net_buff *nb_reply; + struct option_header *ohdr; + struct neighbour_advertise *adv; + struct icmp_header *icmphr; grub_uint8_t *ptr; + if (icmph->code) break; + if (ttl != 0xff) + break; + nbh = (struct neighbour_solicit *) nb->data; err = grub_netbuff_pull (nb, sizeof (struct router_adv)); if (err) - return err; - for (ptr = (grub_uint8_t *) nb->data + sizeof (struct router_adv); - ptr < nb->tail; ) { - grub_dprintf ("net", "option %02x, %02x\n", ptr[0], ptr[1]); - if (ptr + 2 >= nb->tail || ptr[1] == 0) + grub_netbuff_free (nb); + return err; + } + for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail; + ptr += ohdr->len * 8) + { + ohdr = (struct option_header *) ptr; + if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail) { grub_netbuff_free (nb); return GRUB_ERR_NONE; } - if (ptr[0] == 3 && ptr[1] == 4) + if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS + && ohdr->len == 1) + { + grub_net_link_level_address_t ll_address; + ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + grub_net_link_layer_add_address (card, source, &ll_address, 0); + } + } + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + if (inf->card == card + && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && grub_memcmp (&inf->address.ipv6, &nbh->target, 16) == 0) + break; + } + if (!inf) + break; + + nb_reply = grub_netbuff_alloc (sizeof (struct neighbour_advertise) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb_reply) + { + grub_netbuff_free (nb); + return grub_errno; + } + err = grub_netbuff_reserve (nb_reply, + sizeof (struct neighbour_advertise) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (err) + goto ndp_fail; + + err = grub_netbuff_push (nb_reply, 6); + if (err) + goto ndp_fail; + grub_memcpy (nb_reply->data, inf->hwaddress.mac, 6); + err = grub_netbuff_push (nb_reply, sizeof (*ohdr)); + if (err) + goto ndp_fail; + ohdr = (struct option_header *) nb_reply->data; + ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS; + ohdr->len = 1; + err = grub_netbuff_push (nb_reply, sizeof (*adv)); + if (err) + goto ndp_fail; + adv = (struct neighbour_advertise *) nb_reply->data; + adv->flags = grub_cpu_to_be32_compile_time (FLAG_SOLICITED + | FLAG_OVERRIDE); + grub_memcpy (&adv->target, &nbh->target, 16); + + err = grub_netbuff_push (nb_reply, sizeof (*icmphr)); + if (err) + goto ndp_fail; + icmphr = (struct icmp_header *) nb_reply->data; + icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = grub_net_ip_transport_checksum (nb_reply, + GRUB_NET_IP_ICMPV6, + &inf->address, + source); + err = grub_net_send_ip_packet (inf, source, nb_reply, + GRUB_NET_IP_ICMPV6); + + ndp_fail: + grub_netbuff_free (nb); + grub_netbuff_free (nb_reply); + return err; + } + case ICMP6_NEIGHBOUR_ADVERTISE: + { + struct neighbour_advertise *nbh; + grub_uint8_t *ptr; + struct option_header *ohdr; + + if (icmph->code) + break; + if (ttl != 0xff) + break; + nbh = (struct neighbour_advertise *) nb->data; + err = grub_netbuff_pull (nb, sizeof (*nbh)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail; + ptr += ohdr->len * 8) + { + ohdr = (struct option_header *) ptr; + if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (ohdr->type == OPTION_TARGET_LINK_LAYER_ADDRESS + && ohdr->len == 1) + { + grub_net_link_level_address_t ll_address; + ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + grub_net_link_layer_add_address (card, source, &ll_address, 0); + } + } + break; + } + case ICMP6_ROUTER_ADVERTISE: + { + grub_uint8_t *ptr; + struct option_header *ohdr; + if (icmph->code) + break; + err = grub_netbuff_pull (nb, sizeof (struct router_adv)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail; + ptr += ohdr->len * 8) + { + ohdr = (struct option_header *) ptr; + if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS + && ohdr->len == 1) + { + grub_net_link_level_address_t ll_address; + ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + grub_net_link_layer_add_address (card, source, &ll_address, 0); + } + if (ohdr->type == OPTION_PREFIX && ohdr->len == 4) { struct prefix_option *opt = (struct prefix_option *) ptr; struct grub_net_slaac_mac_list *slaac; @@ -181,7 +373,6 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, > grub_be_to_cpu32 (opt->valid_lifetime)) || opt->prefixlen != 64) { - ptr += ptr[1] * 8; grub_dprintf ("net", "discarded prefix: %d, %d, %d, %d\n", !(opt->flags & FLAG_SLAAC), (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80, @@ -223,7 +414,6 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, } } } - ptr += ptr[1] * 8; } if (ptr != nb->tail) break; @@ -233,3 +423,91 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, grub_netbuff_free (nb); return GRUB_ERR_NONE; } + +grub_err_t +grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr) +{ + struct grub_net_buff *nb; + grub_err_t err = GRUB_ERR_NONE; + int i; + struct option_header *ohdr; + struct neighbour_solicit *sol; + struct icmp_header *icmphr; + grub_net_network_level_address_t multicast; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_be_to_cpu64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = (grub_be_to_cpu64_compile_time (0x01ff000000ULL) + | (proto_addr->ipv6[1] + & grub_be_to_cpu64_compile_time (0xffffff))); + + nb = grub_netbuff_alloc (sizeof (struct neighbour_solicit) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb) + return grub_errno; + err = grub_netbuff_reserve (nb, + sizeof (struct neighbour_solicit) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + err = grub_netbuff_push (nb, 6); + if (err) + goto fail; + + grub_memcpy (nb->data, inf->hwaddress.mac, 6); + err = grub_netbuff_push (nb, sizeof (*ohdr)); + if (err) + goto fail; + + ohdr = (struct option_header *) nb->data; + ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS; + ohdr->len = 1; + err = grub_netbuff_push (nb, sizeof (*sol)); + if (err) + goto fail; + + sol = (struct neighbour_solicit *) nb->data; + sol->reserved = 0; + grub_memcpy (&sol->target, &proto_addr->ipv6, 16); + + err = grub_netbuff_push (nb, sizeof (*icmphr)); + if (err) + goto fail; + + icmphr = (struct icmp_header *) nb->data; + icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_ICMPV6, + &inf->address, + &multicast); + err = grub_net_send_ip_packet (inf, &multicast, nb, + GRUB_NET_IP_ICMPV6); + if (err) + goto fail; + + for (i = 0; i < GRUB_NET_TRIES; i++) + { + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + break; + grub_net_poll_cards (GRUB_NET_INTERVAL); + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + break; + err = grub_net_send_ip_packet (inf, &multicast, nb, + GRUB_NET_IP_ICMPV6); + if (err) + break; + } + + fail: + grub_netbuff_free (nb); + return err; +} diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 18593da49..27b8221fb 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -87,6 +87,7 @@ struct reassemble grub_uint8_t *asm_buffer; grub_size_t total_len; grub_size_t cur_ptr; + grub_uint8_t ttl; }; struct reassemble *reassembles; @@ -192,7 +193,7 @@ grub_net_send_ip4_packet (struct grub_net_network_level_interface * inf, COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV4_HEADER_SIZE == sizeof (*iph)); /* Determine link layer target address via ARP. */ - err = grub_net_arp_resolve (inf, target, &ll_target_addr); + err = grub_net_link_layer_resolve (inf, target, &ll_target_addr); if (err) return err; @@ -225,10 +226,12 @@ handle_dgram (struct grub_net_buff *nb, const grub_net_link_level_address_t *hwaddress, grub_net_ip_protocol_t proto, const grub_net_network_level_address_t *source, - const grub_net_network_level_address_t *dest) + const grub_net_network_level_address_t *dest, + grub_uint8_t ttl) { struct grub_net_network_level_interface *inf = NULL; grub_err_t err; + int multicast = 0; /* DHCP needs special treatment since we don't know IP yet. */ { @@ -280,17 +283,39 @@ handle_dgram (struct grub_net_buff *nb, && grub_net_addr_cmp (&inf->address, dest) == 0 && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) break; + /* Solicited node multicast. */ + if (inf->card == card + && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && dest->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && dest->ipv6[0] == grub_be_to_cpu64_compile_time (0xff02ULL << 48) + && dest->ipv6[1] == (grub_be_to_cpu64_compile_time (0x01ff000000ULL) + | (inf->address.ipv6[1] + & grub_be_to_cpu64_compile_time (0xffffff))) + && hwaddress->type == GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET + && hwaddress->mac[0] == 0x33 && hwaddress->mac[1] == 0x33 + && hwaddress->mac[2] == 0xff + && hwaddress->mac[3] == ((grub_be_to_cpu64 (inf->address.ipv6[1]) + >> 16) & 0xff) + && hwaddress->mac[4] == ((grub_be_to_cpu64 (inf->address.ipv6[1]) + >> 8) & 0xff) + && hwaddress->mac[5] == ((grub_be_to_cpu64 (inf->address.ipv6[1]) + >> 0) & 0xff)) + { + multicast = 1; + break; + } } if (!inf && !(dest->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 - && dest->ipv6[0] == grub_be_to_cpu64 (0xff02ULL << 48) - && dest->ipv6[1] == grub_be_to_cpu64 (1))) + && dest->ipv6[0] == grub_be_to_cpu64_compile_time (0xff02ULL + << 48) + && dest->ipv6[1] == grub_be_to_cpu64_compile_time (1))) { - grub_dprintf ("net", "undirected dgram discarded: %x, %lx, %lx\n", - dest->type, dest->ipv6[0], dest->ipv6[1]); grub_netbuff_free (nb); return GRUB_ERR_NONE; } + if (multicast) + inf = NULL; switch (proto) { @@ -301,7 +326,7 @@ handle_dgram (struct grub_net_buff *nb, case GRUB_NET_IP_ICMP: return grub_net_recv_icmp_packet (nb, inf, source); case GRUB_NET_IP_ICMPV6: - return grub_net_recv_icmp6_packet (nb, card, inf, source, dest); + return grub_net_recv_icmp6_packet (nb, card, inf, source, dest, ttl); default: grub_netbuff_free (nb); break; @@ -414,7 +439,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, dest.ipv4 = iph->dest; return handle_dgram (nb, card, hwaddress, iph->protocol, - &source, &dest); + &source, &dest, iph->ttl); } for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev) @@ -442,8 +467,10 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, rsm->asm_buffer = 0; rsm->total_len = 0; rsm->cur_ptr = 0; + rsm->ttl = 0xff; } - + if (rsm->ttl > iph->ttl) + rsm->ttl = iph->ttl; rsm->last_time = grub_get_time_ms (); free_old_fragments (); @@ -479,6 +506,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, grub_uint32_t dst; grub_net_network_level_address_t source; grub_net_network_level_address_t dest; + grub_uint8_t ttl; nb_top_p = grub_priority_queue_top (rsm->pq); if (!nb_top_p) @@ -521,6 +549,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, proto = rsm->proto; src = rsm->source; dst = rsm->dest; + ttl = rsm->ttl; rsm->asm_buffer = 0; res_len = rsm->total_len; @@ -541,7 +570,8 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; dest.ipv4 = dst; - return handle_dgram (ret, card, hwaddress, proto, &source, &dest); + return handle_dgram (ret, card, hwaddress, proto, &source, &dest, + ttl); } return GRUB_ERR_NONE; @@ -560,7 +590,7 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, 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); + err = grub_net_link_layer_resolve (inf, target, &ll_target_addr); if (err) return err; @@ -571,14 +601,14 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, 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->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_NET_ETHERTYPE_IP6); } grub_err_t @@ -608,14 +638,6 @@ grub_net_recv_ip6_packets (struct grub_net_buff * nb, 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", @@ -660,7 +682,7 @@ grub_net_recv_ip6_packets (struct grub_net_buff * nb, grub_memcpy (dest.ipv6, &iph->dest, sizeof (dest.ipv6)); return handle_dgram (nb, card, hwaddress, iph->protocol, - &source, &dest); + &source, &dest, iph->ttl); } grub_err_t diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 1c6d47757..1134223b2 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,144 @@ struct grub_net_card *grub_net_cards = NULL; struct grub_net_network_level_protocol *grub_net_network_level_protocols = NULL; static struct grub_fs grub_net_fs; +struct grub_net_link_layer_entry { + int avail; + grub_net_network_level_address_t nl_address; + grub_net_link_level_address_t ll_address; +}; + +#define LINK_LAYER_CACHE_SIZE 256 + +static struct grub_net_link_layer_entry * +link_layer_find_entry (const grub_net_network_level_address_t *proto, + const struct grub_net_card *card) +{ + unsigned i; + if (!card->link_layer_table) + return NULL; + for (i = 0; i < LINK_LAYER_CACHE_SIZE; i++) + { + if (card->link_layer_table[i].avail == 1 + && grub_net_addr_cmp (&card->link_layer_table[i].nl_address, + proto) == 0) + return &card->link_layer_table[i]; + } + return NULL; +} + +void +grub_net_link_layer_add_address (struct grub_net_card *card, + const grub_net_network_level_address_t *nl, + const grub_net_link_level_address_t *ll, + int override) +{ + struct grub_net_link_layer_entry *entry; + + /* Check if the sender is in the cache table. */ + entry = link_layer_find_entry (nl, card); + /* Update sender hardware address. */ + if (entry && override) + grub_memcpy (&entry->ll_address, ll, sizeof (entry->ll_address)); + if (entry) + return; + + /* Add sender to cache table. */ + if (card->link_layer_table == NULL) + card->link_layer_table = grub_zalloc (LINK_LAYER_CACHE_SIZE + * sizeof (card->link_layer_table[0])); + entry = &(card->link_layer_table[card->new_ll_entry]); + entry->avail = 1; + grub_memcpy (&entry->ll_address, ll, sizeof (entry->ll_address)); + grub_memcpy (&entry->nl_address, nl, sizeof (entry->nl_address)); + card->new_ll_entry++; + if (card->new_ll_entry == LINK_LAYER_CACHE_SIZE) + card->new_ll_entry = 0; +} + +int +grub_net_link_layer_resolve_check (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr) +{ + struct grub_net_link_layer_entry *entry; + + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && proto_addr->ipv4 == 0xffffffff) + return 1; + entry = link_layer_find_entry (proto_addr, inf->card); + if (entry) + return 1; + return 0; +} + +grub_err_t +grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr, + grub_net_link_level_address_t *hw_addr) +{ + struct grub_net_link_layer_entry *entry; + grub_err_t err; + + if ((proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && proto_addr->ipv4 == 0xffffffff) + || proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV + || (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && proto_addr->ipv6[0] == grub_be_to_cpu64_compile_time (0xff02ULL + << 48) + && proto_addr->ipv6[1] == (grub_be_to_cpu64_compile_time (1)))) + { + hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memset (hw_addr->mac, -1, 6); + return GRUB_ERR_NONE; + } + + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && ((grub_be_to_cpu64 (proto_addr->ipv6[0]) >> 56) == 0xff)) + { + hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + hw_addr->mac[0] = 0x33; + hw_addr->mac[1] = 0x33; + hw_addr->mac[2] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 24) & 0xff); + hw_addr->mac[3] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 16) & 0xff); + hw_addr->mac[4] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 8) & 0xff); + hw_addr->mac[5] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 0) & 0xff); + return GRUB_ERR_NONE; + } + + + + /* Check cache table. */ + entry = link_layer_find_entry (proto_addr, inf->card); + if (entry) + { + *hw_addr = entry->ll_address; + return GRUB_ERR_NONE; + } + switch (proto_addr->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + err = grub_net_arp_send_request (inf, proto_addr); + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + err = grub_net_icmp6_send_request (inf, proto_addr); + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: + return grub_error (GRUB_ERR_BUG, "shouldn't reach here"); + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported address type %d", proto_addr->type); + } + if (err) + return err; + entry = link_layer_find_entry (proto_addr, inf->card); + if (entry) + { + *hw_addr = entry->ll_address; + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_TIMEOUT, + "timeout: could not resolve hardware address"); +} + void grub_net_card_unregister (struct grub_net_card *card) { diff --git a/include/grub/net.h b/include/grub/net.h index a4785d48c..e3f7525be 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -105,6 +105,8 @@ struct grub_net_slaac_mac_list char *name; }; +struct grub_net_link_layer_entry; + struct grub_net_card { struct grub_net_card *next; @@ -118,6 +120,8 @@ struct grub_net_card grub_uint64_t last_poll; grub_size_t mtu; struct grub_net_slaac_mac_list *slaac_list; + grub_ssize_t new_ll_entry; + struct grub_net_link_layer_entry *link_layer_table; union { #ifdef GRUB_MACHINE_EFI @@ -455,6 +459,19 @@ grub_net_network_level_interface_unregister (struct grub_net_network_level_inter void grub_net_tcp_retransmit (void); +void +grub_net_link_layer_add_address (struct grub_net_card *card, + const grub_net_network_level_address_t *nl, + const grub_net_link_level_address_t *ll, + int override); +int +grub_net_link_layer_resolve_check (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr); +grub_err_t +grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr, + grub_net_link_level_address_t *hw_addr); + extern char *grub_net_default_server; #define GRUB_NET_TRIES 40 diff --git a/include/grub/net/arp.h b/include/grub/net/arp.h index 49682184e..bb1703622 100644 --- a/include/grub/net/arp.h +++ b/include/grub/net/arp.h @@ -21,10 +21,11 @@ #include #include -extern grub_err_t grub_net_arp_receive(struct grub_net_buff *nb); +extern grub_err_t grub_net_arp_receive (struct grub_net_buff *nb, + struct grub_net_card *card); -extern grub_err_t grub_net_arp_resolve(struct grub_net_network_level_interface *inf, - const grub_net_network_level_address_t *addr, - grub_net_link_level_address_t *hw_addr); +grub_err_t +grub_net_arp_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr); #endif diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index e086f4411..78053de60 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -64,7 +64,8 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, struct grub_net_card *card, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *source, - const grub_net_network_level_address_t *dest); + const grub_net_network_level_address_t *dest, + grub_uint8_t ttl); grub_err_t grub_net_recv_udp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, @@ -83,5 +84,8 @@ grub_net_ip_transport_checksum (struct grub_net_buff *nb, struct grub_net_network_level_interface * grub_net_ipv6_get_link_local (struct grub_net_card *card, const grub_net_link_level_address_t *hwaddr); +grub_err_t +grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr); #endif diff --git a/include/grub/types.h b/include/grub/types.h index 84bc97455..61cfb0291 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -154,6 +154,18 @@ typedef grub_uint64_t grub_disk_addr_t; #define grub_swap_bytes16_compile_time(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) #define grub_swap_bytes32_compile_time(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) & 0xff000000UL) >> 24)) +#define grub_swap_bytes64_compile_time(x) \ +({ \ + grub_uint64_t _x = (x); \ + (grub_uint64_t) ((_x << 56) \ + | ((_x & (grub_uint64_t) 0xFF00ULL) << 40) \ + | ((_x & (grub_uint64_t) 0xFF0000ULL) << 24) \ + | ((_x & (grub_uint64_t) 0xFF000000ULL) << 8) \ + | ((_x & (grub_uint64_t) 0xFF00000000ULL) >> 8) \ + | ((_x & (grub_uint64_t) 0xFF0000000000ULL) >> 24) \ + | ((_x & (grub_uint64_t) 0xFF000000000000ULL) >> 40) \ + | (_x >> 56)); \ +}) #if defined(__GNUC__) && (__GNUC__ > 3) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3) static inline grub_uint32_t grub_swap_bytes32(grub_uint32_t x) @@ -202,6 +214,9 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) ((grub_uint16_t) (x)) # define grub_be_to_cpu32(x) ((grub_uint32_t) (x)) # define grub_be_to_cpu64(x) ((grub_uint64_t) (x)) +# define grub_cpu_to_be32_compile_time(x) ((grub_uint32_t) (x)) +# define grub_cpu_to_be64_compile_time(x) ((grub_uint64_t) (x)) +# define grub_be_to_cpu64_compile_time(x) ((grub_uint64_t) (x)) # define grub_cpu_to_le32_compile_time(x) grub_swap_bytes32_compile_time(x) # define grub_cpu_to_le16_compile_time(x) grub_swap_bytes16_compile_time(x) #else /* ! WORDS_BIGENDIAN */ @@ -217,8 +232,12 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) grub_swap_bytes16(x) # define grub_be_to_cpu32(x) grub_swap_bytes32(x) # define grub_be_to_cpu64(x) grub_swap_bytes64(x) +# define grub_cpu_to_be32_compile_time(x) grub_swap_bytes32_compile_time(x) +# define grub_cpu_to_be64_compile_time(x) grub_swap_bytes64_compile_time(x) +# define grub_be_to_cpu64_compile_time(x) grub_swap_bytes64_compile_time(x) # define grub_cpu_to_le16_compile_time(x) ((grub_uint16_t) (x)) # define grub_cpu_to_le32_compile_time(x) ((grub_uint32_t) (x)) + #endif /* ! WORDS_BIGENDIAN */ #endif /* ! GRUB_TYPES_HEADER */ From da1b289afc78bec05607972a832f6efa70383560 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 13 Oct 2011 01:22:56 +0200 Subject: [PATCH 28/37] IPv6. Apparently working. At least I could retrieve a file with http6 --- grub-core/net/arp.c | 5 +- grub-core/net/bootp.c | 2 +- grub-core/net/icmp.c | 4 +- grub-core/net/icmp6.c | 31 ++++++---- grub-core/net/ip.c | 29 ++++----- grub-core/net/net.c | 117 +++++++++++++++++++++++++++++++------ grub-core/net/tcp.c | 40 +++++++------ grub-core/net/udp.c | 15 ++--- include/grub/net.h | 1 + include/grub/net/ip.h | 1 + include/grub/net/netbuff.h | 12 ++-- 11 files changed, 184 insertions(+), 73 deletions(-) diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index b4c10531b..c9a7cb21e 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -58,6 +58,7 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, int i; grub_size_t addrlen; grub_uint16_t etherpro; + grub_uint8_t *nbd; if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) { @@ -99,6 +100,7 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, grub_memcpy (aux, &proto_addr->ipv4, 4); grub_memset (&target_hw_addr.mac, 0xff, 6); + nbd = nb.data; send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); for (i = 0; i < GRUB_NET_TRIES; i++) { @@ -107,6 +109,7 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, grub_net_poll_cards (GRUB_NET_INTERVAL); if (grub_net_link_layer_resolve_check (inf, proto_addr)) return GRUB_ERR_NONE; + nb.data = nbd; send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); } @@ -142,7 +145,7 @@ grub_net_arp_receive (struct grub_net_buff *nb, return GRUB_ERR_NONE; sender_hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (sender_hw_addr.mac, &sender_hardware_address, + grub_memcpy (sender_hw_addr.mac, sender_hardware_address, sizeof (sender_hw_addr.mac)); grub_net_link_layer_add_address (card, &sender_addr, &sender_hw_addr, 1); diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 1b8527255..be79463ce 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -493,7 +493,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), &ifaces[j].address, &target); - err = grub_net_send_ip_packet (&ifaces[j], &target, nb, + err = grub_net_send_ip_packet (&ifaces[j], &target, NULL, nb, GRUB_NET_IP_UDP); grub_netbuff_free (nb); if (err) diff --git a/grub-core/net/icmp.c b/grub-core/net/icmp.c index 88132b142..9e6bc5c6f 100644 --- a/grub-core/net/icmp.c +++ b/grub-core/net/icmp.c @@ -106,7 +106,9 @@ grub_net_recv_icmp_packet (struct grub_net_buff *nb, icmphr->checksum = 0; icmphr->checksum = grub_net_ip_chksum ((void *) nb_reply->data, nb_reply->tail - nb_reply->data); - err = grub_net_send_ip_packet (inf, src, nb_reply, GRUB_NET_IP_ICMP); + /* FIXME: gateway pings. */ + err = grub_net_send_ip_packet (inf, src, NULL, + nb_reply, GRUB_NET_IP_ICMP); ping_fail: grub_netbuff_free (nb); diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c index 1f9278910..b231f17e9 100644 --- a/grub-core/net/icmp6.c +++ b/grub-core/net/icmp6.c @@ -182,7 +182,8 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, GRUB_NET_IP_ICMPV6, &inf->address, source); - err = grub_net_send_ip_packet (inf, source, nb_reply, + /* FIXME: gateway pings. */ + err = grub_net_send_ip_packet (inf, source, NULL, nb_reply, GRUB_NET_IP_ICMPV6); ping_fail: @@ -288,7 +289,7 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, GRUB_NET_IP_ICMPV6, &inf->address, source); - err = grub_net_send_ip_packet (inf, source, nb_reply, + err = grub_net_send_ip_packet (inf, source, NULL, nb_reply, GRUB_NET_IP_ICMPV6); ndp_fail: @@ -384,12 +385,19 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, for (slaac = card->slaac_list; slaac; slaac = slaac->next) { grub_net_network_level_address_t addr; + grub_net_network_level_netaddress_t netaddr; + if (slaac->address.type != GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET) continue; addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; addr.ipv6[0] = opt->prefix[0]; addr.ipv6[1] = grub_net_ipv6_get_id (&slaac->address); + netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + netaddr.ipv6.base[0] = opt->prefix[0]; + netaddr.ipv6.base[1] = 0; + netaddr.ipv6.masksize = 64; + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { if (inf->card == card @@ -408,9 +416,10 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, + sizeof (":XXXXXXXXXXXXXXXXXXXX")]; grub_snprintf (name, sizeof (name), "%s:%d", slaac->name, slaac->slaac_counter++); - grub_net_add_addr (name, - card, &addr, - &slaac->address, 0); + inf = grub_net_add_addr (name, + card, &addr, + &slaac->address, 0); + grub_net_add_route (name, netaddr, inf); } } } @@ -435,7 +444,7 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, struct neighbour_solicit *sol; struct icmp_header *icmphr; grub_net_network_level_address_t multicast; - + grub_uint8_t *nbd; multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; multicast.ipv6[0] = grub_be_to_cpu64_compile_time (0xff02ULL << 48); multicast.ipv6[1] = (grub_be_to_cpu64_compile_time (0x01ff000000ULL) @@ -467,7 +476,7 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, goto fail; ohdr = (struct option_header *) nb->data; - ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS; + ohdr->type = OPTION_SOURCE_LINK_LAYER_ADDRESS; ohdr->len = 1; err = grub_netbuff_push (nb, sizeof (*sol)); if (err) @@ -482,14 +491,15 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, goto fail; icmphr = (struct icmp_header *) nb->data; - icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE; + icmphr->type = ICMP6_NEIGHBOUR_SOLICIT; icmphr->code = 0; icmphr->checksum = 0; icmphr->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_ICMPV6, &inf->address, &multicast); - err = grub_net_send_ip_packet (inf, &multicast, nb, + nbd = nb->data; + err = grub_net_send_ip_packet (inf, &multicast, NULL, nb, GRUB_NET_IP_ICMPV6); if (err) goto fail; @@ -501,7 +511,8 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, grub_net_poll_cards (GRUB_NET_INTERVAL); if (grub_net_link_layer_resolve_check (inf, proto_addr)) break; - err = grub_net_send_ip_packet (inf, &multicast, nb, + nb->data = nbd; + err = grub_net_send_ip_packet (inf, &multicast, NULL, nb, GRUB_NET_IP_ICMPV6); if (err) break; diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 27b8221fb..19e12ddac 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -181,9 +181,10 @@ send_fragmented (struct grub_net_network_level_interface * inf, } 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_send_ip4_packet (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *target, + const grub_net_network_level_address_t *gw, + struct grub_net_buff *nb, grub_net_ip_protocol_t proto) { struct iphdr *iph; @@ -193,7 +194,7 @@ grub_net_send_ip4_packet (struct grub_net_network_level_interface * inf, COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV4_HEADER_SIZE == sizeof (*iph)); /* Determine link layer target address via ARP. */ - err = grub_net_link_layer_resolve (inf, target, &ll_target_addr); + err = grub_net_link_layer_resolve (inf, gw ? : target, &ll_target_addr); if (err) return err; @@ -578,9 +579,10 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, } 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_send_ip6_packet (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *target, + const grub_net_network_level_address_t *gw, + struct grub_net_buff *nb, grub_net_ip_protocol_t proto) { struct ip6hdr *iph; @@ -590,7 +592,7 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV6_HEADER_SIZE == sizeof (*iph)); /* Determine link layer target address via ARP. */ - err = grub_net_link_layer_resolve (inf, target, &ll_target_addr); + err = grub_net_link_layer_resolve (inf, gw ? : target, &ll_target_addr); if (err) return err; @@ -612,17 +614,18 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, } 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_send_ip_packet (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *target, + const grub_net_network_level_address_t *gw, + 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); + return grub_net_send_ip4_packet (inf, target, gw, nb, proto); case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: - return grub_net_send_ip6_packet (inf, target, nb, proto); + return grub_net_send_ip6_packet (inf, target, gw, nb, proto); default: return grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP"); } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 1134223b2..2e8d9678e 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -160,8 +160,6 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, return GRUB_ERR_NONE; } - - /* Check cache table. */ entry = link_layer_find_entry (proto_addr, inf->card); if (entry) @@ -378,18 +376,23 @@ static int parse_ip (const char *val, grub_uint32_t *ip, const char **rest) { grub_uint32_t newip = 0; - unsigned long t; int i; const char *ptr = val; for (i = 0; i < 4; i++) { + unsigned long t; t = grub_strtoul (ptr, (char **) &ptr, 0); if (grub_errno) { grub_errno = GRUB_ERR_NONE; return 0; } + if (*ptr != '.' && i == 0) + { + newip = t; + break; + } if (t & ~0xff) return 0; newip >>= 8; @@ -400,7 +403,56 @@ parse_ip (const char *val, grub_uint32_t *ip, const char **rest) } *ip = grub_cpu_to_le32 (newip); if (rest) - *rest = ptr - 1; + *rest = (ptr - 1); + return 1; +} + +static int +parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) +{ + grub_uint16_t newip[8]; + const char *ptr = val; + int word, quaddot = -1; + + if (ptr[0] == ':' && ptr[1] != ':') + return 0; + if (ptr[0] == ':') + ptr++; + + for (word = 0; word < 8; word++) + { + unsigned long t; + if (*ptr == ':') + { + quaddot = word; + word--; + ptr++; + continue; + } + t = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + break; + } + if (t & ~0xffff) + return 0; + newip[word] = grub_cpu_to_be16 (t); + if (*ptr != ':') + break; + ptr++; + } + if (quaddot == -1 && word < 7) + return 0; + if (quaddot != -1) + { + grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], + (word - quaddot + 1) * sizeof (newip[0])); + grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); + } + grub_memcpy (ip, newip, 16); + if (rest) + *rest = ptr; return 1; } @@ -422,16 +474,21 @@ match_net (const grub_net_network_level_netaddress_t *net, } 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; + grub_uint64_t mask[2]; + if (net->ipv6.masksize <= 64) + { + mask[0] = 0xffffffffffffffffULL << (64 - net->ipv6.masksize); + mask[1] = 0; + } else - mask[0] = 0xffffffffffffffffULL << (64 - net->ipv6.masksize); + { + mask[0] = 0xffffffffffffffffULL; + mask[1] = 0xffffffffffffffffULL << (128 - 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 (addr->ipv6[0]) & mask[0])) && ((grub_be_to_cpu64 (net->ipv6.base[1]) & mask[1]) - == (grub_be_to_cpu32 (addr->ipv6[1]) & mask[1]))); + == (grub_be_to_cpu64 (addr->ipv6[1]) & mask[1]))); } } return 0; @@ -441,11 +498,17 @@ grub_err_t grub_net_resolve_address (const char *name, grub_net_network_level_address_t *addr) { - if (parse_ip (name, &addr->ipv4, NULL)) + const char *rest; + if (parse_ip (name, &addr->ipv4, &rest) && *rest == 0) { addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; return GRUB_ERR_NONE; } + if (parse_ip6 (name, addr->ipv6, &rest) && *rest == 0) + { + addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + return GRUB_ERR_NONE; + } return grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("unrecognised address %s"), name); } @@ -460,12 +523,32 @@ grub_net_resolve_net_address (const char *name, addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; if (*rest == '/') { - addr->ipv4.masksize = grub_strtoul (rest + 1, NULL, 0); - if (!grub_errno) - return GRUB_ERR_NONE; + addr->ipv4.masksize = grub_strtoul (rest + 1, (char **) &rest, 0); + if (!grub_errno && *rest == 0) + return GRUB_ERR_NONE; + grub_errno = GRUB_ERR_NONE; + } + else if (*rest == 0) + { + addr->ipv4.masksize = 32; + return GRUB_ERR_NONE; + } + } + if (parse_ip6 (name, addr->ipv6.base, &rest)) + { + addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + if (*rest == '/') + { + addr->ipv6.masksize = grub_strtoul (rest + 1, (char **) &rest, 0); + if (!grub_errno && *rest == 0) + return GRUB_ERR_NONE; + grub_errno = GRUB_ERR_NONE; + } + else if (*rest == 0) + { + addr->ipv6.masksize = 128; + return GRUB_ERR_NONE; } - addr->ipv4.masksize = 32; - return GRUB_ERR_NONE; } return grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("unrecognised address %s"), name); diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 72ee8a239..ae1dc57ed 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -69,7 +69,7 @@ struct grub_net_tcp_socket void *recv); void (*error_hook) (grub_net_tcp_socket_t sock, void *recv); void *hook_data; - grub_net_network_level_address_t out_nla; + grub_net_network_level_address_t out_nla, gw; struct grub_net_network_level_interface *inf; grub_net_packets_t packs; grub_priority_queue_t pq; @@ -216,8 +216,8 @@ tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) socket->unack_last->next = unack; } - err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, - GRUB_NET_IP_TCP); + err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), + &(socket->gw),nb, GRUB_NET_IP_TCP); if (err) return err; nb->data = nbd; @@ -240,11 +240,11 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock, sock->recv_hook = NULL; nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) - + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); if (!nb_fin) return; - err = grub_netbuff_reserve (nb_fin, GRUB_NET_OUR_IPV4_HEADER_SIZE + err = grub_netbuff_reserve (nb_fin, GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); if (err) { @@ -368,7 +368,8 @@ grub_net_tcp_retransmit (void) unack->try_count++; unack->last_try = ctime; nbd = unack->nb->data; - err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla), unack->nb, + err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla), + &(sock->gw), unack->nb, GRUB_NET_IP_TCP); unack->nb->data = nbd; if (err) @@ -468,11 +469,11 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, sock->error_hook = error_hook; sock->hook_data = hook_data; nb_ack = grub_netbuff_alloc (sizeof (*tcph) - + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); if (!nb_ack) return grub_errno; - err = grub_netbuff_reserve (nb_ack, GRUB_NET_OUR_IPV4_HEADER_SIZE + err = grub_netbuff_reserve (nb_ack, GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); if (err) { @@ -525,9 +526,10 @@ grub_net_tcp_open (char *server, if (err) return NULL; - if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) { - grub_error (GRUB_ERR_BAD_ARGUMENT, "not a IPv4 address"); + grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP address"); return NULL; } @@ -542,6 +544,7 @@ grub_net_tcp_open (char *server, socket->out_port = out_port; socket->inf = inf; socket->out_nla = addr; + socket->gw = gateway; socket->in_port = in_port++; socket->recv_hook = recv_hook; socket->error_hook = error_hook; @@ -593,7 +596,8 @@ grub_net_tcp_open (char *server, { int j; nb->data = nbd; - err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, + err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), + &(socket->gw), nb, GRUB_NET_IP_TCP); if (err) { @@ -636,20 +640,23 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, grub_err_t err; grub_ssize_t fraglen; COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == GRUB_NET_TCP_HEADER_SIZE); - fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE - - sizeof (*tcph)); + if (socket->out_nla.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE + - sizeof (*tcph)); + else + fraglen = 1280 - GRUB_NET_OUR_IPV6_HEADER_SIZE; while (nb->tail - nb->data > fraglen) { struct grub_net_buff *nb2; nb2 = grub_netbuff_alloc (fraglen + sizeof (*tcph) - + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); if (!nb2) return grub_errno; err = grub_netbuff_reserve (nb2, GRUB_NET_MAX_LINK_HEADER_SIZE - + GRUB_NET_OUR_IPV4_HEADER_SIZE); + + GRUB_NET_OUR_MAX_IP_HEADER_SIZE); if (err) return err; err = grub_netbuff_put (nb2, sizeof (*tcph)); @@ -724,8 +731,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, if (!(grub_be_to_cpu16 (tcph->dst) == sock->in_port && grub_be_to_cpu16 (tcph->src) == sock->out_port && inf == sock->inf - && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && source->ipv4 == sock->out_nla.ipv4)) + && grub_net_addr_cmp (source, &sock->out_nla) == 0)) continue; if (tcph->checksum) { diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index a2315dd3f..2284d8f6c 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -34,7 +34,7 @@ struct grub_net_udp_socket grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, void *recv); void *recv_hook_data; - grub_net_network_level_address_t out_nla; + grub_net_network_level_address_t out_nla, gw; struct grub_net_network_level_interface *inf; }; @@ -76,9 +76,10 @@ grub_net_udp_open (char *server, if (err) return NULL; - if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) { - grub_error (GRUB_ERR_BAD_ARGUMENT, "not a IPv4 address"); + grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP address"); return NULL; } @@ -93,6 +94,7 @@ grub_net_udp_open (char *server, socket->out_port = out_port; socket->inf = inf; socket->out_nla = addr; + socket->gw = gateway; socket->in_port = in_port++; socket->status = GRUB_NET_SOCKET_START; socket->recv_hook = recv_hook; @@ -127,8 +129,8 @@ grub_net_send_udp_packet (const grub_net_udp_socket_t socket, &socket->inf->address, &socket->out_nla); - return grub_net_send_ip_packet (socket->inf, &(socket->out_nla), nb, - GRUB_NET_IP_UDP); + return grub_net_send_ip_packet (socket->inf, &(socket->out_nla), + &(socket->gw), nb, GRUB_NET_IP_UDP); } grub_err_t @@ -160,8 +162,7 @@ grub_net_recv_udp_packet (struct grub_net_buff *nb, { if (grub_be_to_cpu16 (udph->dst) == sock->in_port && inf == sock->inf - && source->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && source->ipv4 == sock->out_nla.ipv4 + && grub_net_addr_cmp (source, &sock->out_nla) == 0 && (sock->status == GRUB_NET_SOCKET_START || grub_be_to_cpu16 (udph->src) == sock->out_port)) { diff --git a/include/grub/net.h b/include/grub/net.h index e3f7525be..b3adb74f9 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -34,6 +34,7 @@ enum GRUB_NET_TCP_HEADER_SIZE = 20, GRUB_NET_OUR_IPV4_HEADER_SIZE = 20, GRUB_NET_OUR_IPV6_HEADER_SIZE = 40, + GRUB_NET_OUR_MAX_IP_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 diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index 78053de60..1b3c0893a 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -52,6 +52,7 @@ grub_net_recv_ip_packets (struct grub_net_buff *nb, grub_err_t grub_net_send_ip_packet (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *target, + const grub_net_network_level_address_t *gw, struct grub_net_buff *nb, grub_net_ip_protocol_t proto); diff --git a/include/grub/net/netbuff.h b/include/grub/net/netbuff.h index 5fafd89f6..c745d51d7 100644 --- a/include/grub/net/netbuff.h +++ b/include/grub/net/netbuff.h @@ -18,13 +18,13 @@ struct grub_net_buff grub_uint8_t *end; }; -grub_err_t grub_netbuff_put (struct grub_net_buff *net_buff ,grub_size_t len); -grub_err_t grub_netbuff_unput (struct grub_net_buff *net_buff ,grub_size_t len); -grub_err_t grub_netbuff_push (struct grub_net_buff *net_buff ,grub_size_t len); -grub_err_t grub_netbuff_pull (struct grub_net_buff *net_buff ,grub_size_t len); -grub_err_t grub_netbuff_reserve (struct grub_net_buff *net_buff ,grub_size_t len); +grub_err_t grub_netbuff_put (struct grub_net_buff *net_buff, grub_size_t len); +grub_err_t grub_netbuff_unput (struct grub_net_buff *net_buff, grub_size_t len); +grub_err_t grub_netbuff_push (struct grub_net_buff *net_buff, grub_size_t len); +grub_err_t grub_netbuff_pull (struct grub_net_buff *net_buff, grub_size_t len); +grub_err_t grub_netbuff_reserve (struct grub_net_buff *net_buff, grub_size_t len); grub_err_t grub_netbuff_clear (struct grub_net_buff *net_buff); -struct grub_net_buff * grub_netbuff_alloc ( grub_size_t len ); +struct grub_net_buff * grub_netbuff_alloc (grub_size_t len); grub_err_t grub_netbuff_free (struct grub_net_buff *net_buff); #endif From bd40efbf0b38c0398bf732504e3219df55862012 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 13 Oct 2011 18:31:53 +0200 Subject: [PATCH 29/37] several net bugfixes and improvements and fix some memory leaks --- grub-core/io/bufio.c | 124 +++++++++++----------- grub-core/kern/disk.c | 3 + grub-core/net/bootp.c | 16 +++ grub-core/net/http.c | 227 +++++++++++++++++++++++++++-------------- grub-core/net/net.c | 60 +++++++++-- grub-core/net/tcp.c | 109 ++++++++++++++------ include/grub/net/tcp.h | 4 + include/grub/types.h | 2 + 8 files changed, 365 insertions(+), 180 deletions(-) diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c index adb38af56..0d01e62f9 100644 --- a/grub-core/io/bufio.c +++ b/grub-core/io/bufio.c @@ -35,6 +35,7 @@ struct grub_bufio grub_file_t file; grub_size_t block_size; grub_size_t buffer_len; + grub_off_t buffer_at; char buffer[0]; }; typedef struct grub_bufio *grub_bufio_t; @@ -70,6 +71,7 @@ grub_bufio_open (grub_file_t io, int size) bufio->file = io; bufio->block_size = size; bufio->buffer_len = 0; + bufio->buffer_at = 0; file->device = io->device; file->offset = 0; @@ -104,83 +106,87 @@ grub_buffile_open (const char *name, int size) static grub_ssize_t grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) { - grub_size_t res = len; + grub_size_t res = 0; grub_bufio_t bufio = file->data; - grub_uint64_t pos; - if ((file->offset >= bufio->file->offset) && - (file->offset < bufio->file->offset + bufio->buffer_len)) + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + + /* First part: use whatever we already have in the buffer. */ + if ((file->offset >= bufio->buffer_at) && + (file->offset < bufio->buffer_at + bufio->buffer_len)) { grub_size_t n; + grub_uint64_t pos; - pos = file->offset - bufio->file->offset; + pos = file->offset - bufio->buffer_at; n = bufio->buffer_len - pos; if (n > len) n = len; grub_memcpy (buf, &bufio->buffer[pos], n); len -= n; - if (! len) - return res; + res += n; buf += n; - bufio->file->offset += bufio->buffer_len; - pos = 0; + } + if (! len) + return res; + + /* Need to read some more. */ + bufio->buffer_at = grub_divmod64 (file->offset + res + len, bufio->block_size, + 0) * bufio->block_size; + + /* Now read between file->offset + res and bufio->buffer_at. */ + if (file->offset + res < bufio->buffer_at) + { + grub_size_t read_now; + grub_ssize_t really_read; + read_now = bufio->buffer_at - (file->offset + res); + grub_file_seek (bufio->file, file->offset + res); + really_read = grub_file_read (bufio->file, buf, read_now); + if (grub_errno) + return -1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + len -= really_read; + buf += really_read; + res += really_read; + + /* Partial read. File ended unexpectedly. Save the last chunk in buffer. + */ + if (really_read != (grub_ssize_t) read_now) + { + bufio->buffer_len = really_read; + if (bufio->buffer_len > bufio->block_size) + bufio->buffer_len = bufio->block_size; + bufio->buffer_at = file->offset + res - bufio->buffer_len; + grub_memcpy (&bufio->buffer[0], buf - bufio->buffer_len, + bufio->buffer_len); + return res; + } + } + + /* Read into buffer. */ + grub_file_seek (bufio->file, bufio->buffer_at); + bufio->buffer_len = grub_file_read (bufio->file, bufio->buffer, + bufio->block_size); + if (grub_errno) + return -1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + + if (len < bufio->buffer_len) + { + grub_memcpy (buf, &bufio->buffer[0], len); + res += len; } else { - bufio->file->offset = grub_divmod64 (file->offset, bufio->block_size, - &pos); - bufio->file->offset *= bufio->block_size; + grub_memcpy (buf, &bufio->buffer[0], bufio->buffer_len); + res += bufio->buffer_len; } - if (pos + len >= bufio->block_size) - { - if (pos) - { - grub_size_t n; - - bufio->file->fs->read (bufio->file, bufio->buffer, - bufio->block_size); - if (grub_errno) - return -1; - - n = bufio->block_size - pos; - grub_memcpy (buf, &bufio->buffer[pos], n); - len -= n; - buf += n; - bufio->file->offset += bufio->block_size; - pos = 0; - } - - while (len >= bufio->block_size) - { - bufio->file->fs->read (bufio->file, buf, bufio->block_size); - if (grub_errno) - return -1; - - len -= bufio->block_size; - buf += bufio->block_size; - bufio->file->offset += bufio->block_size; - } - - if (! len) - { - bufio->buffer_len = 0; - return res; - } - } - - bufio->buffer_len = bufio->file->size - bufio->file->offset; - if (bufio->buffer_len > bufio->block_size) - bufio->buffer_len = bufio->block_size; - - bufio->file->fs->read (bufio->file, bufio->buffer, bufio->buffer_len); - if (grub_errno) - return -1; - - grub_memcpy (buf, &bufio->buffer[pos], len); - return res; } diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index f296b9d0f..460d8778f 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -442,6 +442,7 @@ grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, } } + grub_free (tmp_buf); grub_errno = GRUB_ERR_NONE; { @@ -468,9 +469,11 @@ grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, grub_error_push (); grub_dprintf ("disk", "%s read failed\n", disk->name); grub_error_pop (); + grub_free (tmp_buf); return grub_errno; } grub_memcpy (buf, tmp_buf + offset, size); + grub_free (tmp_buf); return GRUB_ERR_NONE; } } diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index be79463ce..c41e9968a 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -79,6 +79,22 @@ parse_dhcp_vendor (const char *name, void *vend, int limit) switch (tagtype) { + case 3: + if (taglength == 4) + { + grub_net_network_level_netaddress_t target; + grub_net_network_level_address_t gw; + char rname[grub_strlen (name) + sizeof (":default")]; + + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base = 0; + target.ipv4.masksize = 0; + gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4)); + grub_snprintf (rname, sizeof (rname), "%s:default", name); + grub_net_add_route_gw (rname, target, gw); + } + break; case 12: set_env_limn_ro (name, "hostname", (char *) ptr, taglength); break; diff --git a/grub-core/net/http.c b/grub-core/net/http.c index e4664757c..9f6628cb9 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -36,8 +36,6 @@ enum typedef struct http_data { - grub_uint64_t file_size; - grub_uint64_t position; char *current_line; grub_size_t current_line_len; int headers_recv; @@ -47,18 +45,53 @@ typedef struct http_data char *filename; grub_err_t err; char *errmsg; + int chunked; + grub_size_t chunk_rem; + int in_chunk_len; } *http_data_t; +static grub_off_t +have_ahead (struct grub_file *file) +{ + grub_net_t net = file->device->net; + grub_off_t ret = net->offset; + struct grub_net_packet *pack; + for (pack = net->packs.first; pack; pack = pack->next) + ret += pack->nb->tail - pack->nb->data; + return ret; +} + static grub_err_t -parse_line (http_data_t data, char *ptr, grub_size_t len) +parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) { char *end = ptr + len; while (end > ptr && *(end - 1) == '\r') end--; *end = 0; + /* Trailing CRLF. */ + if (data->in_chunk_len == 1) + { + data->in_chunk_len = 2; + return GRUB_ERR_NONE; + } + if (data->in_chunk_len == 2) + { + data->chunk_rem = grub_strtoul (ptr, 0, 16); + grub_errno = GRUB_ERR_NONE; + if (data->chunk_rem == 0) + { + file->device->net->eof = 1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = have_ahead (file); + } + data->in_chunk_len = 0; + return GRUB_ERR_NONE; + } if (ptr == end) { data->headers_recv = 1; + if (data->chunked) + data->in_chunk_len = 2; return GRUB_ERR_NONE; } @@ -93,10 +126,17 @@ parse_line (http_data_t data, char *ptr, grub_size_t len) == 0 && !data->size_recv) { ptr += sizeof ("Content-Length: ") - 1; - data->file_size = grub_strtoull (ptr, &ptr, 10); + file->size = grub_strtoull (ptr, &ptr, 10); data->size_recv = 1; return GRUB_ERR_NONE; } + if (grub_memcmp (ptr, "Transfer-Encoding: chunked", + sizeof ("Transfer-Encoding: chunked") - 1) == 0) + { + data->chunked = 1; + return GRUB_ERR_NONE; + } + return GRUB_ERR_NONE; } @@ -113,6 +153,8 @@ http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), grub_free (data->current_line); grub_free (data); file->device->net->eof = 1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = have_ahead (file); } static grub_err_t @@ -122,94 +164,116 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), { grub_file_t file = f; http_data_t data = file->data; - char *ptr = (char *) nb->data; grub_err_t err; - if (!data->headers_recv && data->current_line) + while (1) { - int have_line = 1; - char *t; - ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); - if (ptr) - ptr++; - else + char *ptr = (char *) nb->data; + if ((!data->headers_recv || data->in_chunk_len) && data->current_line) { - have_line = 0; - ptr = (char *) nb->tail; - } - t = grub_realloc (data->current_line, - data->current_line_len + (ptr - (char *) nb->data)); - if (!t) - { - grub_netbuff_free (nb); - grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); - return grub_errno; - } - - data->current_line = t; - grub_memcpy (data->current_line + data->current_line_len, - nb->data, ptr - (char *) nb->data); - data->current_line_len += ptr - (char *) nb->data; - if (!have_line) - { - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - err = parse_line (data, data->current_line, data->current_line_len); - grub_free (data->current_line); - data->current_line = 0; - data->current_line_len = 0; - if (err) - { - grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); - grub_netbuff_free (nb); - return err; - } - } - - while (ptr < (char *) nb->tail && !data->headers_recv) - { - char *ptr2; - ptr2 = grub_memchr (ptr, '\n', (char *) nb->tail - ptr); - if (!ptr2) - { - data->current_line = grub_malloc ((char *) nb->tail - ptr); - if (!data->current_line) + int have_line = 1; + char *t; + ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); + if (ptr) + ptr++; + else + { + have_line = 0; + ptr = (char *) nb->tail; + } + t = grub_realloc (data->current_line, + data->current_line_len + (ptr - (char *) nb->data)); + if (!t) { grub_netbuff_free (nb); grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return grub_errno; } - data->current_line_len = (char *) nb->tail - ptr; - grub_memcpy (data->current_line, ptr, data->current_line_len); + + data->current_line = t; + grub_memcpy (data->current_line + data->current_line_len, + nb->data, ptr - (char *) nb->data); + data->current_line_len += ptr - (char *) nb->data; + if (!have_line) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + err = parse_line (file, data, data->current_line, + data->current_line_len); + grub_free (data->current_line); + data->current_line = 0; + data->current_line_len = 0; + if (err) + { + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + grub_netbuff_free (nb); + return err; + } + } + + while (ptr < (char *) nb->tail && (!data->headers_recv + || data->in_chunk_len)) + { + char *ptr2; + ptr2 = grub_memchr (ptr, '\n', (char *) nb->tail - ptr); + if (!ptr2) + { + data->current_line = grub_malloc ((char *) nb->tail - ptr); + if (!data->current_line) + { + grub_netbuff_free (nb); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + return grub_errno; + } + data->current_line_len = (char *) nb->tail - ptr; + grub_memcpy (data->current_line, ptr, data->current_line_len); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + err = parse_line (file, data, ptr, ptr2 - ptr); + if (err) + { + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + grub_netbuff_free (nb); + return err; + } + ptr = ptr2 + 1; + } + + if (((char *) nb->tail - ptr) <= 0) + { grub_netbuff_free (nb); return GRUB_ERR_NONE; - } - err = parse_line (data, ptr, ptr2 - ptr); - if (err) - { - grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); - grub_netbuff_free (nb); - return err; - } - ptr = ptr2 + 1; - } - - if (((char *) nb->tail - ptr) > 0) - { - data->position += ((char *) nb->tail - ptr); + } err = grub_netbuff_pull (nb, ptr - (char *) nb->data); if (err) { grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; - } - grub_net_put_packet (&file->device->net->packs, nb); + } + if (!(data->chunked && (grub_ssize_t) data->chunk_rem + < nb->tail - nb->data)) + { + grub_net_put_packet (&file->device->net->packs, nb); + if (data->chunked) + data->chunk_rem -= nb->tail - nb->data; + return GRUB_ERR_NONE; + } + if (data->chunk_rem) + { + struct grub_net_buff *nb2; + nb2 = grub_netbuff_alloc (data->chunk_rem); + if (!nb2) + return grub_errno; + grub_netbuff_put (nb2, data->chunk_rem); + grub_memcpy (nb2->data, nb->data, data->chunk_rem); + grub_net_put_packet (&file->device->net->packs, nb2); + grub_netbuff_pull (nb, data->chunk_rem); + } + data->in_chunk_len = 1; } - else - grub_netbuff_free (nb); - return GRUB_ERR_NONE; } static grub_err_t @@ -294,7 +358,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) "\r\n"), "Content-Range: bytes %" PRIuGRUB_UINT64_T "-%" PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n", - offset, data->file_size - 1, data->file_size); + offset, file->size - 1, file->size); grub_netbuff_put (nb, grub_strlen ((char *) ptr)); } ptr = nb->tail; @@ -303,7 +367,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) data->sock = grub_net_tcp_open (file->device->net->server, HTTP_PORT, http_receive, - http_err, + http_err, http_err, file); if (!data->sock) { @@ -351,13 +415,17 @@ http_seek (struct grub_file *file, grub_off_t off) grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT); while (file->device->net->packs.first) - grub_net_remove_packet (file->device->net->packs.first); + { + grub_netbuff_free (file->device->net->packs.first->nb); + grub_net_remove_packet (file->device->net->packs.first); + } + + file->device->net->offset = off; data = grub_zalloc (sizeof (*data)); if (!data) return grub_errno; - data->file_size = old_data->file_size; data->size_recv = 1; data->filename = old_data->filename; if (!data->filename) @@ -367,6 +435,7 @@ http_seek (struct grub_file *file, grub_off_t off) } grub_free (old_data); + file->data = data; err = http_establish (file, off, 0); if (err) { @@ -386,6 +455,7 @@ http_open (struct grub_file *file, const char *filename) data = grub_zalloc (sizeof (*data)); if (!data) return grub_errno; + file->size = GRUB_FILE_SIZE_UNKNOWN; data->filename = grub_strdup (filename); if (!data->filename) @@ -404,7 +474,6 @@ http_open (struct grub_file *file, const char *filename) grub_free (data); return err; } - file->size = data->file_size; return GRUB_ERR_NONE; } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 2e8d9678e..fe713f05c 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -469,6 +469,8 @@ match_net (const grub_net_network_level_netaddress_t *net, case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: { grub_uint32_t mask = (0xffffffffU << (32 - net->ipv4.masksize)); + if (net->ipv4.masksize == 0) + mask = 0; return ((grub_be_to_cpu32 (net->ipv4.base) & mask) == (grub_be_to_cpu32 (addr->ipv4) & mask)); } @@ -554,6 +556,39 @@ grub_net_resolve_net_address (const char *name, name); } +static int +route_cmp (const struct grub_net_route *a, const struct grub_net_route *b) +{ + if (a == NULL && b == NULL) + return 0; + if (b == NULL) + return +1; + if (a == NULL) + return -1; + if (a->target.type < b->target.type) + return -1; + if (a->target.type > b->target.type) + return +1; + switch (a->target.type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + if (a->target.ipv6.masksize > b->target.ipv6.masksize) + return +1; + if (a->target.ipv6.masksize < b->target.ipv6.masksize) + return -1; + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + if (a->target.ipv4.masksize > b->target.ipv4.masksize) + return +1; + if (a->target.ipv4.masksize < b->target.ipv4.masksize) + return -1; + break; + } + return 0; +} + grub_err_t grub_net_route_address (grub_net_network_level_address_t addr, grub_net_network_level_address_t *gateway, @@ -572,25 +607,27 @@ grub_net_route_address (grub_net_network_level_address_t addr, for (depth = 0; depth < routecnt + 2; depth++) { + struct grub_net_route *bestroute = NULL; FOR_NET_ROUTES(route) { if (depth && prot != route->prot) continue; if (!match_net (&route->target, &curtarget)) continue; - - if (route->is_gateway) - { - if (depth == 0) - *gateway = route->gw; - curtarget = route->gw; - break; - } - *interf = route->interface; - return GRUB_ERR_NONE; + if (route_cmp (route, bestroute) > 0) + bestroute = route; } - if (route == NULL) + if (bestroute == NULL) return grub_error (GRUB_ERR_NET_NO_ROUTE, "destination unreachable"); + + if (!bestroute->is_gateway) + { + *interf = bestroute->interface; + return GRUB_ERR_NONE; + } + if (depth == 0) + *gateway = bestroute->gw; + curtarget = bestroute->gw; } return grub_error (GRUB_ERR_NET_ROUTE_LOOP, "route loop detected"); @@ -1285,6 +1322,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 <= GRUB_NET_TRIES) { while (net->packs.first) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index ae1dc57ed..579f94e99 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -68,6 +68,7 @@ struct grub_net_tcp_socket grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, struct grub_net_buff *nb, void *recv); void (*error_hook) (grub_net_tcp_socket_t sock, void *recv); + void (*fin_hook) (grub_net_tcp_socket_t sock, void *recv); void *hook_data; grub_net_network_level_address_t out_nla, gw; struct grub_net_network_level_interface *inf; @@ -234,10 +235,20 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock, struct tcphdr *tcph_fin; grub_err_t err; - sock->i_closed = 1; - if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING) - sock->recv_hook = NULL; + { + sock->recv_hook = NULL; + sock->error_hook = NULL; + sock->fin_hook = NULL; + } + + if (discard_received == GRUB_NET_TCP_ABORT) + sock->i_reseted = 1; + + if (sock->i_closed) + return; + + sock->i_closed = 1; nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + GRUB_NET_OUR_MAX_IP_HEADER_SIZE @@ -263,13 +274,12 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock, return; } tcph_fin = (void *) nb_fin->data; - tcph_fin->ack = grub_cpu_to_be32 (0); - tcph_fin->flags = grub_cpu_to_be16 ((5 << 12) | TCP_FIN); - tcph_fin->window = grub_cpu_to_be16 (0); + tcph_fin->ack = grub_cpu_to_be32 (sock->their_cur_seq); + tcph_fin->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_FIN + | TCP_ACK); + tcph_fin->window = grub_cpu_to_be16_compile_time (0); tcph_fin->urgent = 0; err = tcp_send (nb_fin, sock); - if (discard_received == GRUB_NET_TCP_ABORT) - sock->i_reseted = 1; if (err) { grub_netbuff_free (nb_fin); @@ -309,14 +319,14 @@ ack_real (grub_net_tcp_socket_t sock, int res) tcph_ack = (void *) nb_ack->data; if (res) { - tcph_ack->ack = grub_cpu_to_be32 (0); - tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_RST); - tcph_ack->window = grub_cpu_to_be16 (0); + tcph_ack->ack = grub_cpu_to_be32_compile_time (0); + tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_RST); + tcph_ack->window = grub_cpu_to_be16_compile_time (0); } else { tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); - tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK); + tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK); tcph_ack->window = grub_cpu_to_be16 (sock->my_window); } tcph_ack->urgent = 0; @@ -354,6 +364,7 @@ grub_net_tcp_retransmit (void) struct unacked *unack; for (unack = sock->unack_first; unack; unack = unack->next) { + struct tcphdr *tcph; grub_uint8_t *nbd; grub_err_t err; @@ -368,6 +379,18 @@ grub_net_tcp_retransmit (void) unack->try_count++; unack->last_try = ctime; nbd = unack->nb->data; + tcph = (struct tcphdr *) nbd; + + if ((tcph->flags & grub_cpu_to_be16_compile_time (TCP_ACK)) + && tcph->ack != grub_cpu_to_be32 (sock->their_cur_seq)) + { + tcph->checksum = 0; + tcph->checksum = grub_net_ip_transport_checksum (unack->nb, + GRUB_NET_IP_TCP, + &sock->inf->address, + &sock->out_nla); + } + err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla), &(sock->gw), unack->nb, GRUB_NET_IP_TCP); @@ -447,7 +470,10 @@ destroy_pq (grub_net_tcp_socket_t sock) { struct grub_net_buff **nb_p; while ((nb_p = grub_priority_queue_top (sock->pq))) - grub_netbuff_free (*nb_p); + { + grub_netbuff_free (*nb_p); + grub_priority_queue_pop (sock->pq); + } grub_priority_queue_destroy (sock->pq); } @@ -459,6 +485,8 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data) { struct grub_net_buff *nb_ack; @@ -467,6 +495,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, sock->recv_hook = recv_hook; sock->error_hook = error_hook; + sock->fin_hook = fin_hook; sock->hook_data = hook_data; nb_ack = grub_netbuff_alloc (sizeof (*tcph) + GRUB_NET_OUR_MAX_IP_HEADER_SIZE @@ -489,7 +518,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, } tcph = (void *) nb_ack->data; tcph->ack = grub_cpu_to_be32 (sock->their_cur_seq); - tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK); + tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN | TCP_ACK); tcph->window = grub_cpu_to_be16 (sock->my_window); tcph->urgent = 0; sock->established = 1; @@ -509,6 +538,8 @@ grub_net_tcp_open (char *server, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data) { grub_err_t err; @@ -548,6 +579,7 @@ grub_net_tcp_open (char *server, socket->in_port = in_port++; socket->recv_hook = recv_hook; socket->error_hook = error_hook; + socket->fin_hook = fin_hook; socket->hook_data = hook_data; nb = grub_netbuff_alloc (sizeof (*tcph) + 128); @@ -578,8 +610,8 @@ grub_net_tcp_open (char *server, socket->my_cur_seq = socket->my_start_seq + 1; socket->my_window = 8192; tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq); - tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN); + tcph->ack = grub_cpu_to_be32_compile_time (0); + tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; tcph->src = grub_cpu_to_be16 (socket->in_port); @@ -664,8 +696,8 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, return err; tcph = (struct tcphdr *) nb2->data; - tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12)); + tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq); + tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; err = grub_netbuff_put (nb2, fraglen); @@ -686,8 +718,9 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, return err; tcph = (struct tcphdr *) nb->data; - tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12) | (push ? TCP_PUSH : 0)); + tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq); + tcph->flags = (grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK) + | (push ? grub_cpu_to_be16_compile_time (TCP_PUSH) : 0)); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; return tcp_send (nb, socket); @@ -815,11 +848,15 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, err = grub_priority_queue_push (sock->pq, &nb); if (err) - return err; + { + grub_netbuff_free (nb); + return err; + } { struct grub_net_buff **nb_top_p, *nb_top; int do_ack = 0; + int just_closed = 0; while (1) { nb_top_p = grub_priority_queue_top (sock->pq); @@ -829,6 +866,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, tcph = (struct tcphdr *) nb_top->data; if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq) break; + grub_netbuff_free (nb_top); grub_priority_queue_pop (sock->pq); } if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) @@ -845,15 +883,19 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, break; grub_priority_queue_pop (sock->pq); - err = grub_netbuff_pull (nb, (grub_be_to_cpu16 (tcph->flags) - >> 12) * sizeof (grub_uint32_t)); + err = grub_netbuff_pull (nb_top, (grub_be_to_cpu16 (tcph->flags) + >> 12) * sizeof (grub_uint32_t)); if (err) - return err; + { + grub_netbuff_free (nb_top); + return err; + } sock->their_cur_seq += (nb_top->tail - nb_top->data); if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) { sock->they_closed = 1; + just_closed = 1; sock->their_cur_seq++; do_ack = 1; } @@ -868,14 +910,19 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, } if (do_ack) ack (sock); + while (sock->packs.first) + { + nb = sock->packs.first->nb; + if (sock->recv_hook) + sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); + else + grub_netbuff_free (nb); + grub_net_remove_packet (sock->packs.first); + } + + if (sock->fin_hook && just_closed) + sock->fin_hook (sock, sock->hook_data); } - while (sock->packs.first) - { - nb = sock->packs.first->nb; - if (sock->recv_hook) - sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); - grub_net_remove_packet (sock->packs.first); - } return GRUB_ERR_NONE; } diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index b25ceff1b..62bfd2eba 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -35,6 +35,8 @@ grub_net_tcp_open (char *server, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data); grub_net_tcp_listen_t @@ -70,6 +72,8 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data); #endif diff --git a/include/grub/types.h b/include/grub/types.h index 61cfb0291..68f28d01d 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -214,6 +214,7 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) ((grub_uint16_t) (x)) # define grub_be_to_cpu32(x) ((grub_uint32_t) (x)) # define grub_be_to_cpu64(x) ((grub_uint64_t) (x)) +# define grub_cpu_to_be16_compile_time(x) ((grub_uint16_t) (x)) # define grub_cpu_to_be32_compile_time(x) ((grub_uint32_t) (x)) # define grub_cpu_to_be64_compile_time(x) ((grub_uint64_t) (x)) # define grub_be_to_cpu64_compile_time(x) ((grub_uint64_t) (x)) @@ -232,6 +233,7 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) grub_swap_bytes16(x) # define grub_be_to_cpu32(x) grub_swap_bytes32(x) # define grub_be_to_cpu64(x) grub_swap_bytes64(x) +# define grub_cpu_to_be16_compile_time(x) grub_swap_bytes16_compile_time(x) # define grub_cpu_to_be32_compile_time(x) grub_swap_bytes32_compile_time(x) # define grub_cpu_to_be64_compile_time(x) grub_swap_bytes64_compile_time(x) # define grub_be_to_cpu64_compile_time(x) grub_swap_bytes64_compile_time(x) From 0cb9503b70136eca27b56c607a57d352d5764715 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 13 Oct 2011 20:53:22 +0200 Subject: [PATCH 30/37] support routed pings --- grub-core/net/bootp.c | 7 ++++- grub-core/net/ethernet.c | 5 ++- grub-core/net/icmp.c | 4 +-- grub-core/net/icmp6.c | 15 ++++++--- grub-core/net/ip.c | 68 ++++++++++++++++++---------------------- grub-core/net/tcp.c | 17 +++++++--- grub-core/net/udp.c | 13 ++++++-- include/grub/net/ip.h | 7 +++-- 8 files changed, 79 insertions(+), 57 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index c41e9968a..0c060fe19 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -458,6 +458,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), struct grub_net_buff *nb; struct udphdr *udph; grub_net_network_level_address_t target; + grub_net_link_level_address_t ll_target; if (!ifaces[j].prev) continue; @@ -505,11 +506,15 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), udph->len = grub_cpu_to_be16 (nb->tail - nb->data); target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; target.ipv4 = 0xffffffff; + err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target); + if (err) + return err; + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, &ifaces[j].address, &target); - err = grub_net_send_ip_packet (&ifaces[j], &target, NULL, nb, + err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb, GRUB_NET_IP_UDP); grub_netbuff_free (nb); if (err) diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index 6b5db9aaa..b38e2c83e 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -88,6 +88,7 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb, struct snaphdr *snaph; grub_net_ethertype_t type; grub_net_link_level_address_t hwaddress; + grub_net_link_level_address_t src_hwaddress; grub_err_t err; eth = (struct etherhdr *) nb->data; @@ -113,6 +114,8 @@ 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)); + src_hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (src_hwaddress.mac, eth->src, sizeof (src_hwaddress.mac)); switch (type) { @@ -124,7 +127,7 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb, /* IP packet. */ case GRUB_NET_ETHERTYPE_IP: case GRUB_NET_ETHERTYPE_IP6: - return grub_net_recv_ip_packets (nb, card, &hwaddress); + return grub_net_recv_ip_packets (nb, card, &hwaddress, &src_hwaddress); } grub_netbuff_free (nb); return GRUB_ERR_NONE; diff --git a/grub-core/net/icmp.c b/grub-core/net/icmp.c index 9e6bc5c6f..e55a0d86a 100644 --- a/grub-core/net/icmp.c +++ b/grub-core/net/icmp.c @@ -42,6 +42,7 @@ enum grub_err_t grub_net_recv_icmp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, + const grub_net_link_level_address_t *ll_src, const grub_net_network_level_address_t *src) { struct icmp_header *icmph; @@ -106,8 +107,7 @@ grub_net_recv_icmp_packet (struct grub_net_buff *nb, icmphr->checksum = 0; icmphr->checksum = grub_net_ip_chksum ((void *) nb_reply->data, nb_reply->tail - nb_reply->data); - /* FIXME: gateway pings. */ - err = grub_net_send_ip_packet (inf, src, NULL, + err = grub_net_send_ip_packet (inf, src, ll_src, nb_reply, GRUB_NET_IP_ICMP); ping_fail: diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c index b231f17e9..9a5f3cafe 100644 --- a/grub-core/net/icmp6.c +++ b/grub-core/net/icmp6.c @@ -103,6 +103,7 @@ grub_err_t grub_net_recv_icmp6_packet (struct grub_net_buff *nb, struct grub_net_card *card, struct grub_net_network_level_interface *inf, + const grub_net_link_level_address_t *ll_src, const grub_net_network_level_address_t *source, const grub_net_network_level_address_t *dest, grub_uint8_t ttl) @@ -182,8 +183,7 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, GRUB_NET_IP_ICMPV6, &inf->address, source); - /* FIXME: gateway pings. */ - err = grub_net_send_ip_packet (inf, source, NULL, nb_reply, + err = grub_net_send_ip_packet (inf, source, ll_src, nb_reply, GRUB_NET_IP_ICMPV6); ping_fail: @@ -289,7 +289,7 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, GRUB_NET_IP_ICMPV6, &inf->address, source); - err = grub_net_send_ip_packet (inf, source, NULL, nb_reply, + err = grub_net_send_ip_packet (inf, source, ll_src, nb_reply, GRUB_NET_IP_ICMPV6); ndp_fail: @@ -444,6 +444,7 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, struct neighbour_solicit *sol; struct icmp_header *icmphr; grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; grub_uint8_t *nbd; multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; multicast.ipv6[0] = grub_be_to_cpu64_compile_time (0xff02ULL << 48); @@ -451,6 +452,10 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, | (proto_addr->ipv6[1] & grub_be_to_cpu64_compile_time (0xffffff))); + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast); + if (err) + return err; + nb = grub_netbuff_alloc (sizeof (struct neighbour_solicit) + sizeof (struct option_header) + 6 @@ -499,7 +504,7 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, &inf->address, &multicast); nbd = nb->data; - err = grub_net_send_ip_packet (inf, &multicast, NULL, nb, + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, GRUB_NET_IP_ICMPV6); if (err) goto fail; @@ -512,7 +517,7 @@ grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, if (grub_net_link_layer_resolve_check (inf, proto_addr)) break; nb->data = nbd; - err = grub_net_send_ip_packet (inf, &multicast, NULL, nb, + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, GRUB_NET_IP_ICMPV6); if (err) break; diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 19e12ddac..b56c9d14a 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -183,23 +183,16 @@ send_fragmented (struct grub_net_network_level_interface * inf, static grub_err_t grub_net_send_ip4_packet (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *target, - const grub_net_network_level_address_t *gw, + const grub_net_link_level_address_t *ll_target_addr, struct grub_net_buff *nb, grub_net_ip_protocol_t proto) { struct iphdr *iph; - grub_net_link_level_address_t ll_target_addr; - grub_err_t err; COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV4_HEADER_SIZE == sizeof (*iph)); - /* Determine link layer target address via ARP. */ - err = grub_net_link_layer_resolve (inf, gw ? : target, &ll_target_addr); - if (err) - return err; - if (nb->tail - nb->data + sizeof (struct iphdr) > inf->card->mtu) - return send_fragmented (inf, target, nb, proto, ll_target_addr); + return send_fragmented (inf, target, nb, proto, *ll_target_addr); grub_netbuff_push (nb, sizeof (*iph)); iph = (struct iphdr *) nb->data; @@ -217,13 +210,14 @@ grub_net_send_ip4_packet (struct grub_net_network_level_interface *inf, iph->chksum = 0; iph->chksum = grub_net_ip_chksum ((void *) nb->data, sizeof (*iph)); - return send_ethernet_packet (inf, nb, ll_target_addr, + return send_ethernet_packet (inf, nb, *ll_target_addr, GRUB_NET_ETHERTYPE_IP); } static grub_err_t handle_dgram (struct grub_net_buff *nb, struct grub_net_card *card, + const grub_net_link_level_address_t *source_hwaddress, const grub_net_link_level_address_t *hwaddress, grub_net_ip_protocol_t proto, const grub_net_network_level_address_t *source, @@ -325,9 +319,10 @@ handle_dgram (struct grub_net_buff *nb, case GRUB_NET_IP_TCP: 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_hwaddress, source); case GRUB_NET_IP_ICMPV6: - return grub_net_recv_icmp6_packet (nb, card, inf, source, dest, ttl); + return grub_net_recv_icmp6_packet (nb, card, inf, source_hwaddress, + source, dest, ttl); default: grub_netbuff_free (nb); break; @@ -363,9 +358,10 @@ free_old_fragments (void) } static grub_err_t -grub_net_recv_ip4_packets (struct grub_net_buff * nb, - struct grub_net_card * card, - const grub_net_link_level_address_t * hwaddress) +grub_net_recv_ip4_packets (struct grub_net_buff *nb, + struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddress, + const grub_net_link_level_address_t *src_hwaddress) { struct iphdr *iph = (struct iphdr *) nb->data; grub_err_t err; @@ -439,7 +435,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; dest.ipv4 = iph->dest; - return handle_dgram (nb, card, hwaddress, iph->protocol, + return handle_dgram (nb, card, src_hwaddress, hwaddress, iph->protocol, &source, &dest, iph->ttl); } @@ -571,7 +567,8 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; dest.ipv4 = dst; - return handle_dgram (ret, card, hwaddress, proto, &source, &dest, + return handle_dgram (ret, card, src_hwaddress, + hwaddress, proto, &source, &dest, ttl); } @@ -581,21 +578,14 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, static grub_err_t grub_net_send_ip6_packet (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *target, - const grub_net_network_level_address_t *gw, + const grub_net_link_level_address_t *ll_target_addr, 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_link_layer_resolve (inf, gw ? : 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"); @@ -609,32 +599,33 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface *inf, 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, + return send_ethernet_packet (inf, nb, *ll_target_addr, GRUB_NET_ETHERTYPE_IP6); } grub_err_t grub_net_send_ip_packet (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *target, - const grub_net_network_level_address_t *gw, + const grub_net_link_level_address_t *ll_target_addr, 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, gw, nb, proto); + return grub_net_send_ip4_packet (inf, target, ll_target_addr, nb, proto); case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: - return grub_net_send_ip6_packet (inf, target, gw, nb, proto); + return grub_net_send_ip6_packet (inf, target, ll_target_addr, nb, proto); default: return grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP"); } } static grub_err_t -grub_net_recv_ip6_packets (struct grub_net_buff * nb, - struct grub_net_card * card, - const grub_net_link_level_address_t * hwaddress) +grub_net_recv_ip6_packets (struct grub_net_buff *nb, + struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddress, + const grub_net_link_level_address_t *src_hwaddress) { struct ip6hdr *iph = (struct ip6hdr *) nb->data; grub_err_t err; @@ -684,21 +675,22 @@ grub_net_recv_ip6_packets (struct grub_net_buff * nb, grub_memcpy (source.ipv6, &iph->src, sizeof (source.ipv6)); grub_memcpy (dest.ipv6, &iph->dest, sizeof (dest.ipv6)); - return handle_dgram (nb, card, hwaddress, iph->protocol, + return handle_dgram (nb, card, src_hwaddress, hwaddress, iph->protocol, &source, &dest, iph->ttl); } grub_err_t -grub_net_recv_ip_packets (struct grub_net_buff * nb, - struct grub_net_card * card, - const grub_net_link_level_address_t * hwaddress) +grub_net_recv_ip_packets (struct grub_net_buff *nb, + struct grub_net_card *card, + const grub_net_link_level_address_t *hwaddress, + const grub_net_link_level_address_t *src_hwaddress) { struct iphdr *iph = (struct iphdr *) nb->data; if ((iph->verhdrlen >> 4) == 4) - return grub_net_recv_ip4_packets (nb, card, hwaddress); + return grub_net_recv_ip4_packets (nb, card, hwaddress, src_hwaddress); if ((iph->verhdrlen >> 4) == 6) - return grub_net_recv_ip6_packets (nb, card, hwaddress); + return grub_net_recv_ip6_packets (nb, card, hwaddress, src_hwaddress); grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); grub_netbuff_free (nb); return GRUB_ERR_NONE; diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 579f94e99..77cf073ee 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -70,7 +70,8 @@ struct grub_net_tcp_socket void (*error_hook) (grub_net_tcp_socket_t sock, void *recv); void (*fin_hook) (grub_net_tcp_socket_t sock, void *recv); void *hook_data; - grub_net_network_level_address_t out_nla, gw; + grub_net_network_level_address_t out_nla; + grub_net_link_level_address_t ll_target_addr; struct grub_net_network_level_interface *inf; grub_net_packets_t packs; grub_priority_queue_t pq; @@ -218,7 +219,8 @@ tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) } err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), - &(socket->gw),nb, GRUB_NET_IP_TCP); + &(socket->ll_target_addr), nb, + GRUB_NET_IP_TCP); if (err) return err; nb->data = nbd; @@ -392,7 +394,7 @@ grub_net_tcp_retransmit (void) } err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla), - &(sock->gw), unack->nb, + &(sock->ll_target_addr), unack->nb, GRUB_NET_IP_TCP); unack->nb->data = nbd; if (err) @@ -552,6 +554,7 @@ grub_net_tcp_open (char *server, struct tcphdr *tcph; int i; grub_uint8_t *nbd; + grub_net_link_level_address_t ll_target_addr; err = grub_net_resolve_address (server, &addr); if (err) @@ -568,6 +571,10 @@ grub_net_tcp_open (char *server, if (err) return NULL; + err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr); + if (err) + return NULL; + socket = grub_zalloc (sizeof (*socket)); if (socket == NULL) return NULL; @@ -575,7 +582,7 @@ grub_net_tcp_open (char *server, socket->out_port = out_port; socket->inf = inf; socket->out_nla = addr; - socket->gw = gateway; + socket->ll_target_addr = ll_target_addr; socket->in_port = in_port++; socket->recv_hook = recv_hook; socket->error_hook = error_hook; @@ -629,7 +636,7 @@ grub_net_tcp_open (char *server, int j; nb->data = nbd; err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), - &(socket->gw), nb, + &(socket->ll_target_addr), nb, GRUB_NET_IP_TCP); if (err) { diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index 2284d8f6c..739541097 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -34,7 +34,8 @@ struct grub_net_udp_socket grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, void *recv); void *recv_hook_data; - grub_net_network_level_address_t out_nla, gw; + grub_net_network_level_address_t out_nla; + grub_net_link_level_address_t ll_target_addr; struct grub_net_network_level_interface *inf; }; @@ -71,6 +72,7 @@ grub_net_udp_open (char *server, grub_net_network_level_address_t gateway; grub_net_udp_socket_t socket; static int in_port = 25300; + grub_net_link_level_address_t ll_target_addr; err = grub_net_resolve_address (server, &addr); if (err) @@ -87,6 +89,10 @@ grub_net_udp_open (char *server, if (err) return NULL; + err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr); + if (err) + return NULL; + socket = grub_zalloc (sizeof (*socket)); if (socket == NULL) return NULL; @@ -94,7 +100,7 @@ grub_net_udp_open (char *server, socket->out_port = out_port; socket->inf = inf; socket->out_nla = addr; - socket->gw = gateway; + socket->ll_target_addr = ll_target_addr; socket->in_port = in_port++; socket->status = GRUB_NET_SOCKET_START; socket->recv_hook = recv_hook; @@ -130,7 +136,8 @@ grub_net_send_udp_packet (const grub_net_udp_socket_t socket, &socket->out_nla); return grub_net_send_ip_packet (socket->inf, &(socket->out_nla), - &(socket->gw), nb, GRUB_NET_IP_UDP); + &(socket->ll_target_addr), nb, + GRUB_NET_IP_UDP); } grub_err_t diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index 1b3c0893a..7a8e61479 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -47,23 +47,26 @@ 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, struct grub_net_card *card, - const grub_net_link_level_address_t *hwaddress); + const grub_net_link_level_address_t *hwaddress, + const grub_net_link_level_address_t *src_hwaddress); grub_err_t grub_net_send_ip_packet (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *target, - const grub_net_network_level_address_t *gw, + const grub_net_link_level_address_t *ll_target_addr, struct grub_net_buff *nb, grub_net_ip_protocol_t proto); grub_err_t grub_net_recv_icmp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, + const grub_net_link_level_address_t *ll_src, const grub_net_network_level_address_t *src); grub_err_t grub_net_recv_icmp6_packet (struct grub_net_buff *nb, struct grub_net_card *card, struct grub_net_network_level_interface *inf, + const grub_net_link_level_address_t *ll_src, const grub_net_network_level_address_t *source, const grub_net_network_level_address_t *dest, grub_uint8_t ttl); From 038ec56a31bb55a928905daf8b4eb46824c8939d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 14 Oct 2011 19:21:59 +0200 Subject: [PATCH 31/37] nslookup implementation --- grub-core/Makefile.core.def | 1 + grub-core/net/bootp.c | 6 +----- grub-core/net/net.c | 2 ++ grub-core/net/tftp.c | 10 +++++++++- grub-core/net/udp.c | 7 +------ include/grub/err.h | 3 ++- include/grub/net.h | 10 ++++++++++ include/grub/net/udp.h | 2 +- 8 files changed, 27 insertions(+), 14 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 3827227b8..2b4c22e01 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1598,6 +1598,7 @@ module = { module = { name = net; common = net/net.c; + common = net/dns.c; common = net/bootp.c; common = net/ip.c; common = net/udp.c; diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 0c060fe19..b83b343c8 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -541,7 +541,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), return err; } -static grub_command_t cmd_dhcp, cmd_getdhcp, cmd_bootp; +static grub_command_t cmd_getdhcp, cmd_bootp; void grub_bootp_init (void) @@ -549,9 +549,6 @@ grub_bootp_init (void) cmd_bootp = grub_register_command ("net_bootp", grub_cmd_bootp, "[CARD]", N_("perform a bootp autoconfiguration")); - cmd_dhcp = grub_register_command ("net_dhcp", grub_cmd_bootp, - "[CARD]", - N_("perform a bootp autoconfiguration")); cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt, N_("VAR INTERFACE NUMBER DESCRIPTION"), N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value.")); @@ -561,6 +558,5 @@ void grub_bootp_fini (void) { grub_unregister_command (cmd_getdhcp); - grub_unregister_command (cmd_dhcp); grub_unregister_command (cmd_bootp); } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index fe713f05c..269786fd8 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1491,6 +1491,7 @@ GRUB_MOD_INIT(net) cmd_lsaddr = grub_register_command ("net_ls_addr", grub_cmd_listaddrs, "", N_("list network addresses")); grub_bootp_init (); + grub_dns_init (); grub_fs_register (&grub_net_fs); grub_net_open = grub_net_open_real; @@ -1503,6 +1504,7 @@ GRUB_MOD_INIT(net) GRUB_MOD_FINI(net) { grub_bootp_fini (); + grub_dns_fini (); grub_unregister_command (cmd_addaddr); grub_unregister_command (cmd_deladdr); grub_unregister_command (cmd_addroute); diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 9be7294e8..9accdf5d2 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -279,6 +279,7 @@ tftp_open (struct grub_file *file, const char *filename) tftp_data_t data; grub_err_t err; grub_uint8_t *nbd; + grub_net_network_level_address_t addr; data = grub_zalloc (sizeof (*data)); if (!data) @@ -335,7 +336,14 @@ tftp_open (struct grub_file *file, const char *filename) if (!data->pq) return grub_errno; - data->sock = grub_net_udp_open (file->device->net->server, + err = grub_net_resolve_address (file->device->net->server, &addr); + if (err) + { + destroy_pq (data); + return err; + } + + data->sock = grub_net_udp_open (addr, TFTP_SERVER_PORT, tftp_receive, file); if (!data->sock) diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c index 739541097..e26608e36 100644 --- a/grub-core/net/udp.c +++ b/grub-core/net/udp.c @@ -59,7 +59,7 @@ grub_net_udp_close (grub_net_udp_socket_t sock) } grub_net_udp_socket_t -grub_net_udp_open (char *server, +grub_net_udp_open (grub_net_network_level_address_t addr, grub_uint16_t out_port, grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, @@ -67,17 +67,12 @@ grub_net_udp_open (char *server, void *recv_hook_data) { grub_err_t err; - grub_net_network_level_address_t addr; struct grub_net_network_level_interface *inf; grub_net_network_level_address_t gateway; grub_net_udp_socket_t socket; static int in_port = 25300; grub_net_link_level_address_t ll_target_addr; - err = grub_net_resolve_address (server, &addr); - if (err) - return NULL; - if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) { diff --git a/include/grub/err.h b/include/grub/err.h index d13f2c9ce..3a75f5698 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -65,7 +65,8 @@ typedef enum GRUB_ERR_NET_PORT_CLOSED, GRUB_ERR_NET_INVALID_RESPONSE, GRUB_ERR_NET_UNKNOWN_ERROR, - GRUB_ERR_NET_PACKET_TOO_BIG + GRUB_ERR_NET_PACKET_TOO_BIG, + GRUB_ERR_NET_NO_DOMAIN } grub_err_t; diff --git a/include/grub/net.h b/include/grub/net.h index b3adb74f9..f39c9d70b 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -446,6 +446,9 @@ grub_net_poll_cards (unsigned time); void grub_bootp_init (void); void grub_bootp_fini (void); +void grub_dns_init (void); +void grub_dns_fini (void); + static inline void grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) { @@ -472,6 +475,13 @@ grub_err_t grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *proto_addr, grub_net_link_level_address_t *hw_addr); +grub_err_t +grub_net_dns_lookup (const char *name, + const struct grub_net_network_level_address *servers, + grub_size_t n_servers, + grub_size_t *naddresses, + struct grub_net_network_level_address **addresses, + int cache); extern char *grub_net_default_server; diff --git a/include/grub/net/udp.h b/include/grub/net/udp.h index fca94ba47..1a7efa777 100644 --- a/include/grub/net/udp.h +++ b/include/grub/net/udp.h @@ -33,7 +33,7 @@ struct grub_net_udp_socket; typedef struct grub_net_udp_socket *grub_net_udp_socket_t; grub_net_udp_socket_t -grub_net_udp_open (char *server, +grub_net_udp_open (grub_net_network_level_address_t addr, grub_uint16_t out_port, grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb, From 93f87ec28d1e4bf67883407f80cce15dc094fc2c Mon Sep 17 00:00:00 2001 From: Yves Blusseau Date: Sun, 30 Oct 2011 10:50:26 +0100 Subject: [PATCH 32/37] * grub-core/net/tcp.c (grub_net_ip_transport_checksum): Silence spurious warning. --- grub-core/net/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 579f94e99..d1f329fe2 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -410,7 +410,7 @@ 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) { - grub_uint16_t a, b; + grub_uint16_t a, b = 0; grub_uint32_t c; a = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) nb->data, nb->tail - nb->data)); From 078d2b2e6853466ff84bc5fc777ac3d7305e6f8d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 15 Dec 2011 20:50:21 +0100 Subject: [PATCH 33/37] Add forgotten file --- grub-core/net/dns.c | 424 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 grub-core/net/dns.c diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c new file mode 100644 index 000000000..47eb68e01 --- /dev/null +++ b/grub-core/net/dns.c @@ -0,0 +1,424 @@ +/* + * 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 +#include +#include + +struct dns_cache_element +{ + char *name; + grub_size_t naddresses; + struct grub_net_network_level_address *addresses; + struct grub_net_network_level_address *source; + grub_uint64_t limit_time; +}; + +struct dns_header +{ + grub_uint16_t id; + grub_uint8_t flags; + grub_uint8_t ra_z_r_code; + grub_uint16_t qdcount; + grub_uint16_t ancount; + grub_uint16_t nscount; + grub_uint16_t arcount; +} __attribute__ ((packed)); + +enum + { + FLAGS_RESPONSE = 0x80, + FLAGS_OPCODE = 0x78, + FLAGS_RD = 0x01 + }; + +enum + { + ERRCODE_MASK = 0x0f + }; + +enum + { + DNS_PORT = 53 + }; + +struct recv_data +{ + grub_size_t *naddresses; + struct grub_net_network_level_address **addresses; + int cache; + grub_uint16_t id; + int dns_err; + const char *name; +}; + +static int +check_name (const grub_uint8_t *name_at, const grub_uint8_t *head, + const grub_uint8_t *tail, const char *check_with) +{ + const char *readable_ptr = check_with; + const grub_uint8_t *ptr; + int bytes_processed = 0; + for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; ) + { + /* End marker. */ + if (!*ptr) + return (*readable_ptr == 0); + if (*ptr & 0xc0) + { + bytes_processed += 2; + if (ptr + 1 >= tail) + return 0; + ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]); + continue; + } + if (grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0) + return 0; + if (grub_memchr (ptr + 1, 0, *ptr) + || grub_memchr (ptr + 1, '.', *ptr)) + return 0; + if (readable_ptr[*ptr] != '.' && readable_ptr[*ptr] != 0) + return 0; + bytes_processed += *ptr + 1; + readable_ptr += *ptr; + if (*readable_ptr) + readable_ptr++; + ptr += *ptr + 1; + } + return 0; +} + +static grub_err_t +recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), + struct grub_net_buff *nb, + void *data_) +{ + struct dns_header *head; + struct recv_data *data = data_; + int i, j; + grub_uint8_t *ptr; + + head = (struct dns_header *) nb->data; + ptr = (grub_uint8_t *) (head + 1); + if (ptr >= nb->tail) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + if (head->id != data->id) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (!(head->flags & FLAGS_RESPONSE) || (head->flags & FLAGS_OPCODE)) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (head->ra_z_r_code & ERRCODE_MASK) + { + data->dns_err = 1; + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + for (i = 0; i < grub_cpu_to_be16 (head->qdcount); i++) + { + if (ptr >= nb->tail) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0)) + ptr += *ptr + 1; + if (ptr < nb->tail && (*ptr & 0xc0)) + ptr++; + ptr++; + ptr += 4; + } + *data->addresses = grub_malloc (sizeof ((*data->addresses)[0]) + * grub_cpu_to_be16 (head->ancount)); + if (!*data->addresses) + { + grub_errno = GRUB_ERR_NONE; + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + for (i = 0; i < grub_cpu_to_be16 (head->ancount); i++) + { + int ignored = 0; + int is_ipv6 = 0; + grub_uint32_t ttl = 0; + grub_uint16_t length; + if (ptr >= nb->tail) + { + if (!*data->naddresses) + grub_free (*data->addresses); + return GRUB_ERR_NONE; + } + ignored = !check_name (ptr, nb->data, nb->tail, data->name); + while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0)) + ptr += *ptr + 1; + if (ptr < nb->tail && (*ptr & 0xc0)) + ptr++; + ptr++; + if (ptr + 10 >= nb->tail) + { + if (!*data->naddresses) + grub_free (*data->addresses); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (*ptr++ != 0) + ignored = 1; + if (*ptr != 1 && *ptr != 28) + ignored = 1; + if (*ptr == 28) + is_ipv6 = 1; + ptr++; + if (*ptr++ != 0) + ignored = 1; + if (*ptr++ != 1) + ignored = 1; + for (j = 0; j < 4; j++) + { + ttl >>= 8; + ttl = *ptr++ << 28; + } + length = *ptr++ << 8; + length |= *ptr++; + if (ptr + length > nb->tail) + { + if (!*data->naddresses) + grub_free (*data->addresses); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (!ignored && !is_ipv6 && length == 4) + { + (*data->addresses)[*data->naddresses].type + = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, + ptr, 4); + (*data->naddresses)++; + } + if (!ignored && is_ipv6 && length == 16) + { + (*data->addresses)[*data->naddresses].type + = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, + ptr, 16); + (*data->naddresses)++; + } + ptr += length; + } + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_net_dns_lookup (const char *name, + const struct grub_net_network_level_address *servers, + grub_size_t n_servers, + grub_size_t *naddresses, + struct grub_net_network_level_address **addresses, + int cache) +{ + grub_size_t send_servers = 0; + grub_size_t i, j; + struct grub_net_buff *nb; + grub_net_udp_socket_t sockets[n_servers]; + grub_uint8_t *optr; + const char *iptr; + struct dns_header *head; + static grub_uint16_t id = 1; + grub_err_t err = GRUB_ERR_NONE; + struct recv_data data = {naddresses, addresses, cache, + grub_cpu_to_be16 (id++), 0, name}; + grub_uint8_t *nbd; + int have_server = 0; + + *naddresses = 0; + /* if (cache && cache_lookup (name, servers, n_servers, addresses)) + return GRUB_ERR_NONE;*/ + + nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE + + GRUB_NET_UDP_HEADER_SIZE + + sizeof (struct dns_header) + + grub_strlen (name) + 2 + 4 + + 2 + 4); + if (!nb) + return grub_errno; + grub_netbuff_reserve (nb, GRUB_NET_OUR_MAX_IP_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE + + GRUB_NET_UDP_HEADER_SIZE); + grub_netbuff_put (nb, sizeof (struct dns_header) + + grub_strlen (name) + 2 + 4 + 2 + 4); + head = (struct dns_header *) nb->data; + optr = (grub_uint8_t *) (head + 1); + for (iptr = name; *iptr; ) + { + const char *dot; + dot = grub_strchr (iptr, '.'); + if (!dot) + dot = iptr + grub_strlen (iptr); + if ((dot - iptr) >= 64) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "domain component is too long"); + *optr = (dot - iptr); + optr++; + grub_memcpy (optr, iptr, dot - iptr); + optr += dot - iptr; + iptr = dot; + if (*iptr) + iptr++; + } + *optr++ = 0; + + /* Type: A. */ + *optr++ = 0; + *optr++ = 1; + + /* Class. */ + *optr++ = 0; + *optr++ = 1; + + /* Compressed name. */ + *optr++ = 0xc0; + *optr++ = 0x0c; + /* Type: AAAA. */ + *optr++ = 0; + *optr++ = 28; + + /* Class. */ + *optr++ = 0; + *optr++ = 1; + + head->id = data.id; + head->flags = FLAGS_RD; + head->ra_z_r_code = 0; + head->qdcount = grub_cpu_to_be16_compile_time (2); + head->ancount = grub_cpu_to_be16_compile_time (0); + head->nscount = grub_cpu_to_be16_compile_time (0); + head->arcount = grub_cpu_to_be16_compile_time (0); + + nbd = nb->data; + + for (i = 0; i < n_servers * 4; i++) + { + /* Connect to a next server. */ + while (!(i & 1) && send_servers < n_servers) + { + sockets[send_servers] = grub_net_udp_open (servers[send_servers], + DNS_PORT, + recv_hook, + &data); + send_servers++; + if (!sockets[send_servers - 1]) + { + err = grub_errno; + grub_errno = GRUB_ERR_NONE; + } + else + { + have_server = 1; + break; + } + } + if (!have_server) + goto out; + if (*data.naddresses) + goto out; + for (j = 0; j < send_servers; j++) + { + grub_err_t err2; + if (!sockets[j]) + continue; + nb->data = nbd; + err2 = grub_net_send_udp_packet (sockets[j], nb); + if (err2) + { + grub_errno = GRUB_ERR_NONE; + err = err2; + } + if (*data.naddresses) + goto out; + } + grub_net_poll_cards (200); + } + out: + grub_netbuff_free (nb); + for (j = 0; j < send_servers; j++) + grub_net_udp_close (sockets[j]); + if (*data.naddresses) + return GRUB_ERR_NONE; + if (data.dns_err) + return grub_error (GRUB_ERR_NET_NO_DOMAIN, "no DNS domain found"); + + if (err) + { + grub_errno = err; + return err; + } + return grub_error (GRUB_ERR_TIMEOUT, "no DNS reply received"); +} + +static grub_err_t +grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_err_t err; + struct grub_net_network_level_address server; + grub_size_t naddresses, i; + struct grub_net_network_level_address *addresses; + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "2 arguments expected"); + err = grub_net_resolve_address (args[1], &server); + if (err) + return err; + + err = grub_net_dns_lookup (args[0], &server, 1, &naddresses, &addresses, 0); + if (err) + return err; + for (i = 0; i < naddresses; i++) + { + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + grub_net_addr_to_str (&addresses[i], buf); + grub_printf ("%s\n", buf); + } + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +void +grub_dns_init (void) +{ + cmd = grub_register_command ("net_nslookup", grub_cmd_nslookup, + "ADDRESS DNSSERVER", + N_("perform a DNS lookup")); +} + +void +grub_dns_fini (void) +{ + grub_unregister_command (cmd); +} From 3729fcfc1ac31ec5feeba15d8e03cdd6586e2967 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 16 Dec 2011 13:43:31 +0100 Subject: [PATCH 34/37] support cname --- grub-core/net/dns.c | 167 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 133 insertions(+), 34 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 47eb68e01..b3ea83223 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -66,21 +66,33 @@ struct recv_data int cache; grub_uint16_t id; int dns_err; - const char *name; + char *name; }; static int -check_name (const grub_uint8_t *name_at, const grub_uint8_t *head, - const grub_uint8_t *tail, const char *check_with) +check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, + const grub_uint8_t *tail, const char *check_with, + int *length, char *set) { const char *readable_ptr = check_with; const grub_uint8_t *ptr; + char *optr = set; int bytes_processed = 0; + if (length) + *length = 0; for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; ) { /* End marker. */ if (!*ptr) - return (*readable_ptr == 0); + { + if (length && *length) + (*length)--; + if (optr && optr != set) + optr--; + if (optr) + *optr = 0; + return !readable_ptr || (*readable_ptr == 0); + } if (*ptr & 0xc0) { bytes_processed += 2; @@ -89,22 +101,66 @@ check_name (const grub_uint8_t *name_at, const grub_uint8_t *head, ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]); continue; } - if (grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0) + if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0) return 0; if (grub_memchr (ptr + 1, 0, *ptr) || grub_memchr (ptr + 1, '.', *ptr)) return 0; - if (readable_ptr[*ptr] != '.' && readable_ptr[*ptr] != 0) + if (readable_ptr) + readable_ptr += *ptr; + if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0) return 0; bytes_processed += *ptr + 1; - readable_ptr += *ptr; - if (*readable_ptr) + if (length) + *length += *ptr + 1; + if (optr) + { + grub_memcpy (optr, ptr + 1, *ptr); + optr += *ptr; + } + if (optr) + *optr++ = '.'; + if (readable_ptr && *readable_ptr) readable_ptr++; ptr += *ptr + 1; } return 0; } +static int +check_name (const grub_uint8_t *name_at, const grub_uint8_t *head, + const grub_uint8_t *tail, const char *check_with) +{ + return check_name_real (name_at, head, tail, check_with, NULL, NULL); +} + +static char * +get_name (const grub_uint8_t *name_at, const grub_uint8_t *head, + const grub_uint8_t *tail) +{ + int length; + char *ret; + + if (!check_name_real (name_at, head, tail, NULL, &length, NULL)) + return NULL; + ret = grub_malloc (length + 1); + if (!ret) + return NULL; + if (!check_name_real (name_at, head, tail, NULL, NULL, ret)) + { + grub_free (ret); + return NULL; + } + return ret; +} + +enum + { + DNS_CLASS_A = 1, + DNS_CLASS_CNAME = 5, + DNS_CLASS_AAAA = 28 + }; + static grub_err_t recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), struct grub_net_buff *nb, @@ -113,7 +169,9 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), struct dns_header *head; struct recv_data *data = data_; int i, j; - grub_uint8_t *ptr; + grub_uint8_t *ptr, *reparse_ptr; + int redirect_cnt = 0; + char *redirect_save = NULL; head = (struct dns_header *) nb->data; ptr = (grub_uint8_t *) (head + 1); @@ -161,10 +219,12 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), grub_netbuff_free (nb); return GRUB_ERR_NONE; } - for (i = 0; i < grub_cpu_to_be16 (head->ancount); i++) + reparse_ptr = ptr; + reparse: + for (i = 0, ptr = reparse_ptr; i < grub_cpu_to_be16 (head->ancount); i++) { int ignored = 0; - int is_ipv6 = 0; + grub_uint8_t class; grub_uint32_t ttl = 0; grub_uint16_t length; if (ptr >= nb->tail) @@ -188,11 +248,7 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), } if (*ptr++ != 0) ignored = 1; - if (*ptr != 1 && *ptr != 28) - ignored = 1; - if (*ptr == 28) - is_ipv6 = 1; - ptr++; + class = *ptr++; if (*ptr++ != 0) ignored = 1; if (*ptr++ != 1) @@ -211,25 +267,56 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), grub_netbuff_free (nb); return GRUB_ERR_NONE; } - if (!ignored && !is_ipv6 && length == 4) - { - (*data->addresses)[*data->naddresses].type - = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, - ptr, 4); - (*data->naddresses)++; - } - if (!ignored && is_ipv6 && length == 16) - { - (*data->addresses)[*data->naddresses].type - = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; - grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, + if (!ignored) + switch (class) + { + case DNS_CLASS_A: + if (length != 4) + break; + (*data->addresses)[*data->naddresses].type + = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, + ptr, 4); + (*data->naddresses)++; + break; + case DNS_CLASS_AAAA: + if (length != 16) + break; + (*data->addresses)[*data->naddresses].type + = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, ptr, 16); - (*data->naddresses)++; + (*data->naddresses)++; + break; + case DNS_CLASS_CNAME: + if (!(redirect_cnt & (redirect_cnt - 1))) + { + grub_free (redirect_save); + redirect_save = data->name; + } + else + grub_free (data->name); + redirect_cnt++; + data->name = get_name (ptr, nb->data, nb->tail); + if (!data->name) + { + data->dns_err = 1; + grub_errno = 0; + return GRUB_ERR_NONE; + } + grub_dprintf ("dns", "CNAME %s\n", data->name); + if (grub_strcmp (redirect_save, data->name) == 0) + { + data->dns_err = 1; + grub_free (redirect_save); + return GRUB_ERR_NONE; + } + goto reparse; } ptr += length; } grub_netbuff_free (nb); + grub_free (redirect_save); return GRUB_ERR_NONE; } @@ -251,10 +338,14 @@ grub_net_dns_lookup (const char *name, static grub_uint16_t id = 1; grub_err_t err = GRUB_ERR_NONE; struct recv_data data = {naddresses, addresses, cache, - grub_cpu_to_be16 (id++), 0, name}; + grub_cpu_to_be16 (id++), 0, 0}; grub_uint8_t *nbd; int have_server = 0; + data.name = grub_strdup (name); + if (!data.name) + return grub_errno; + *naddresses = 0; /* if (cache && cache_lookup (name, servers, n_servers, addresses)) return GRUB_ERR_NONE;*/ @@ -266,7 +357,10 @@ grub_net_dns_lookup (const char *name, + grub_strlen (name) + 2 + 4 + 2 + 4); if (!nb) - return grub_errno; + { + grub_free (data.name); + return grub_errno; + } grub_netbuff_reserve (nb, GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE + GRUB_NET_UDP_HEADER_SIZE); @@ -281,8 +375,11 @@ grub_net_dns_lookup (const char *name, if (!dot) dot = iptr + grub_strlen (iptr); if ((dot - iptr) >= 64) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "domain component is too long"); + { + grub_free (data.name); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "domain component is too long"); + } *optr = (dot - iptr); optr++; grub_memcpy (optr, iptr, dot - iptr); @@ -365,9 +462,11 @@ grub_net_dns_lookup (const char *name, grub_net_poll_cards (200); } out: + grub_free (data.name); grub_netbuff_free (nb); for (j = 0; j < send_servers; j++) grub_net_udp_close (sockets[j]); + if (*data.naddresses) return GRUB_ERR_NONE; if (data.dns_err) From 8d4e4fc0e58e63105ba04e6d80b0ff2c1eaea08e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 16 Dec 2011 15:08:46 +0100 Subject: [PATCH 35/37] DNS cache support --- grub-core/net/dns.c | 169 +++++++++++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 50 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index b3ea83223..93550171a 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -21,16 +21,21 @@ #include #include #include +#include struct dns_cache_element { char *name; grub_size_t naddresses; struct grub_net_network_level_address *addresses; - struct grub_net_network_level_address *source; grub_uint64_t limit_time; }; +#define DNS_CACHE_SIZE 1021 +#define DNS_HASH_BASE 423 + +static struct dns_cache_element dns_cache[DNS_CACHE_SIZE]; + struct dns_header { grub_uint16_t id; @@ -67,8 +72,25 @@ struct recv_data grub_uint16_t id; int dns_err; char *name; + const char *oname; }; +static inline int +hash (const char *str) +{ + int v = 0, xn = 1; + const char *ptr; + for (ptr = str; *ptr; ) + { + v = (v + xn * *ptr); + xn = (DNS_HASH_BASE * xn) % DNS_CACHE_SIZE; + ptr++; + if (((ptr - str) & 0x3ff) == 0) + v %= DNS_CACHE_SIZE; + } + return v % DNS_CACHE_SIZE; +} + static int check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, const grub_uint8_t *tail, const char *check_with, @@ -172,6 +194,7 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), grub_uint8_t *ptr, *reparse_ptr; int redirect_cnt = 0; char *redirect_save = NULL; + grub_uint32_t ttl_all = ~0U; head = (struct dns_header *) nb->data; ptr = (grub_uint8_t *) (head + 1); @@ -255,8 +278,8 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), ignored = 1; for (j = 0; j < 4; j++) { - ttl >>= 8; - ttl = *ptr++ << 28; + ttl <<= 8; + ttl |= *ptr++; } length = *ptr++ << 8; length |= *ptr++; @@ -268,53 +291,82 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), return GRUB_ERR_NONE; } if (!ignored) - switch (class) - { - case DNS_CLASS_A: - if (length != 4) + { + if (ttl_all > ttl) + ttl_all = ttl; + switch (class) + { + case DNS_CLASS_A: + if (length != 4) + break; + (*data->addresses)[*data->naddresses].type + = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, + ptr, 4); + (*data->naddresses)++; break; - (*data->addresses)[*data->naddresses].type - = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4, - ptr, 4); - (*data->naddresses)++; - break; - case DNS_CLASS_AAAA: - if (length != 16) + case DNS_CLASS_AAAA: + if (length != 16) + break; + (*data->addresses)[*data->naddresses].type + = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, + ptr, 16); + (*data->naddresses)++; break; - (*data->addresses)[*data->naddresses].type - = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; - grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6, - ptr, 16); - (*data->naddresses)++; - break; - case DNS_CLASS_CNAME: - if (!(redirect_cnt & (redirect_cnt - 1))) - { - grub_free (redirect_save); - redirect_save = data->name; - } - else - grub_free (data->name); - redirect_cnt++; - data->name = get_name (ptr, nb->data, nb->tail); - if (!data->name) - { - data->dns_err = 1; - grub_errno = 0; - return GRUB_ERR_NONE; - } - grub_dprintf ("dns", "CNAME %s\n", data->name); - if (grub_strcmp (redirect_save, data->name) == 0) - { - data->dns_err = 1; - grub_free (redirect_save); - return GRUB_ERR_NONE; - } - goto reparse; + case DNS_CLASS_CNAME: + if (!(redirect_cnt & (redirect_cnt - 1))) + { + grub_free (redirect_save); + redirect_save = data->name; + } + else + grub_free (data->name); + redirect_cnt++; + data->name = get_name (ptr, nb->data, nb->tail); + if (!data->name) + { + data->dns_err = 1; + grub_errno = 0; + return GRUB_ERR_NONE; + } + grub_dprintf ("dns", "CNAME %s\n", data->name); + if (grub_strcmp (redirect_save, data->name) == 0) + { + data->dns_err = 1; + grub_free (redirect_save); + return GRUB_ERR_NONE; + } + goto reparse; + } } ptr += length; } + if (ttl_all && *data->naddresses && data->cache) + { + int h; + grub_dprintf ("dns", "caching for %d seconds\n", ttl_all); + h = hash (data->oname); + grub_free (dns_cache[h].name); + dns_cache[h].name = 0; + grub_free (dns_cache[h].addresses); + dns_cache[h].addresses = 0; + dns_cache[h].name = grub_strdup (data->oname); + dns_cache[h].naddresses = *data->naddresses; + dns_cache[h].addresses = grub_malloc (*data->naddresses + * sizeof (dns_cache[h].addresses[0])); + dns_cache[h].limit_time = grub_get_time_ms () + 1000 * ttl_all; + if (!dns_cache[h].addresses || !dns_cache[h].name) + { + grub_free (dns_cache[h].name); + dns_cache[h].name = 0; + grub_free (dns_cache[h].addresses); + dns_cache[h].addresses = 0; + } + grub_memcpy (dns_cache[h].addresses, *data->addresses, + *data->naddresses + * sizeof (dns_cache[h].addresses[0])); + } grub_netbuff_free (nb); grub_free (redirect_save); return GRUB_ERR_NONE; @@ -338,18 +390,35 @@ grub_net_dns_lookup (const char *name, static grub_uint16_t id = 1; grub_err_t err = GRUB_ERR_NONE; struct recv_data data = {naddresses, addresses, cache, - grub_cpu_to_be16 (id++), 0, 0}; + grub_cpu_to_be16 (id++), 0, 0, name}; grub_uint8_t *nbd; int have_server = 0; + *naddresses = 0; + if (cache) + { + int h; + h = hash (name); + if (dns_cache[h].name && grub_strcmp (dns_cache[h].name, name) == 0 + && grub_get_time_ms () < dns_cache[h].limit_time) + { + grub_dprintf ("dns", "retrieved from cache\n"); + *addresses = grub_malloc (dns_cache[h].naddresses + * sizeof ((*addresses)[0])); + if (!*addresses) + return grub_errno; + *naddresses = dns_cache[h].naddresses; + grub_memcpy (*addresses, dns_cache[h].addresses, + dns_cache[h].naddresses + * sizeof ((*addresses)[0])); + return GRUB_ERR_NONE; + } + } + data.name = grub_strdup (name); if (!data.name) return grub_errno; - *naddresses = 0; - /* if (cache && cache_lookup (name, servers, n_servers, addresses)) - return GRUB_ERR_NONE;*/ - nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE + GRUB_NET_UDP_HEADER_SIZE From f0f4253cd2fbe207461167ad3f6d0202d2f2e396 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 16 Dec 2011 17:12:01 +0100 Subject: [PATCH 36/37] Integrate DNS with address parsing --- grub-core/commands/i386/pc/drivemap.c | 2 +- grub-core/net/dns.c | 105 +++++++++++++++++++++++++- grub-core/net/net.c | 16 +++- include/grub/net.h | 5 ++ 4 files changed, 124 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/i386/pc/drivemap.c b/grub-core/commands/i386/pc/drivemap.c index 4f752bed5..a562b33e5 100644 --- a/grub-core/commands/i386/pc/drivemap.c +++ b/grub-core/commands/i386/pc/drivemap.c @@ -32,7 +32,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Real mode IVT slot (seg:off far pointer) for interrupt 0x13. */ -static grub_uint32_t *const int13slot = UINT_TO_PTR (4 * 0x13); +static grub_uint32_t *const int13slot = (void *) (4 * 0x13); /* Remember to update enum opt_idxs accordingly. */ static const struct grub_arg_option options[] = { diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 93550171a..696b3ccca 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -35,6 +35,41 @@ struct dns_cache_element #define DNS_HASH_BASE 423 static struct dns_cache_element dns_cache[DNS_CACHE_SIZE]; +static struct grub_net_network_level_address *dns_servers; +static grub_size_t dns_nservers, dns_servers_alloc; + +grub_err_t +grub_net_add_dns_server (const struct grub_net_network_level_address *s) +{ + if (dns_servers_alloc <= dns_nservers) + { + int na = dns_servers_alloc * 2; + struct grub_net_network_level_address *ns; + if (na < 8) + na = 8; + ns = grub_malloc (na * sizeof (ns[0])); + if (!ns) + return grub_errno; + dns_servers_alloc = na; + dns_servers = ns; + } + dns_servers[dns_nservers++] = *s; + return GRUB_ERR_NONE; +} + +void +grub_net_remove_dns_server (const struct grub_net_network_level_address *s) +{ + grub_size_t i; + for (i = 0; i < dns_nservers; i++) + if (grub_net_addr_cmp (s, &dns_servers[i]) == 0) + break; + if (i < dns_nservers) + { + dns_servers[i] = dns_servers[dns_nservers - 1]; + dns_nservers--; + } +} struct dns_header { @@ -394,6 +429,15 @@ grub_net_dns_lookup (const char *name, grub_uint8_t *nbd; int have_server = 0; + if (!servers) + { + servers = dns_servers; + n_servers = dns_nservers; + } + + if (!n_servers) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no DNS servers"); + *naddresses = 0; if (cache) { @@ -572,10 +616,58 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)), grub_net_addr_to_str (&addresses[i], buf); grub_printf ("%s\n", buf); } + grub_free (addresses); return GRUB_ERR_NONE; } -static grub_command_t cmd; +static grub_err_t +grub_cmd_list_dns (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_size_t i; + for (i = 0; i < dns_nservers; i++) + { + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + grub_net_addr_to_str (&dns_servers[i], buf); + grub_printf ("%s\n", buf); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_add_dns (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_err_t err; + struct grub_net_network_level_address server; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "1 argument expected"); + err = grub_net_resolve_address (args[0], &server); + if (err) + return err; + + return grub_net_add_dns_server (&server); +} + +static grub_err_t +grub_cmd_del_dns (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_err_t err; + struct grub_net_network_level_address server; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "1 argument expected"); + err = grub_net_resolve_address (args[1], &server); + if (err) + return err; + + return grub_net_add_dns_server (&server); +} + +static grub_command_t cmd, cmd_add, cmd_del, cmd_list; void grub_dns_init (void) @@ -583,10 +675,21 @@ grub_dns_init (void) cmd = grub_register_command ("net_nslookup", grub_cmd_nslookup, "ADDRESS DNSSERVER", N_("perform a DNS lookup")); + cmd_add = grub_register_command ("net_add_dns", grub_cmd_add_dns, + "DNSSERVER", + N_("add a DNS server")); + cmd_del = grub_register_command ("net_del_dns", grub_cmd_del_dns, + "DNSSERVER", + N_("remove a DNS server")); + cmd_list = grub_register_command ("net_del_dns", grub_cmd_list_dns, + NULL, N_("remove a DNS server")); } void grub_dns_fini (void) { grub_unregister_command (cmd); + grub_unregister_command (cmd_add); + grub_unregister_command (cmd_del); + grub_unregister_command (cmd_list); } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index ed6229807..d64206432 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -501,6 +501,10 @@ grub_net_resolve_address (const char *name, grub_net_network_level_address_t *addr) { const char *rest; + grub_err_t err; + grub_size_t naddresses; + struct grub_net_network_level_address *addresses; + if (parse_ip (name, &addr->ipv4, &rest) && *rest == 0) { addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; @@ -511,8 +515,16 @@ grub_net_resolve_address (const char *name, addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; return GRUB_ERR_NONE; } - return grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("unrecognised address %s"), - name); + err = grub_net_dns_lookup (name, 0, 0, &naddresses, &addresses, 1); + if (err) + return err; + if (!naddresses) + grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("unresolvable address %s"), + name); + /* FIXME: use other results as well. */ + *addr = addresses[0]; + grub_free (addresses); + return GRUB_ERR_NONE; } grub_err_t diff --git a/include/grub/net.h b/include/grub/net.h index 6b2ba7ed2..dd566354d 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -483,6 +483,11 @@ grub_net_dns_lookup (const char *name, grub_size_t *naddresses, struct grub_net_network_level_address **addresses, int cache); +grub_err_t +grub_net_add_dns_server (const struct grub_net_network_level_address *s); +void +grub_net_remove_dns_server (const struct grub_net_network_level_address *s); + extern char *grub_net_default_server; From aa7d205236d311af61181aee7e9882d6f97df7af Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 16 Dec 2011 17:37:47 +0100 Subject: [PATCH 37/37] Retrieve DNS names from DHCP/BOOTP --- grub-core/net/bootp.c | 27 ++++++++++++++++++++------- grub-core/net/dns.c | 10 +++++----- include/grub/net.h | 12 ++++++++++++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index f84d4ff66..ea8943a32 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -68,18 +68,18 @@ parse_dhcp_vendor (const char *name, void *vend, int limit) tagtype = *ptr++; /* Pad tag. */ - if (tagtype == 0) + if (tagtype == GRUB_NET_BOOTP_PAD) continue; /* End tag. */ - if (tagtype == 0xff) + if (tagtype == GRUB_NET_BOOTP_END) return; taglength = *ptr++; switch (tagtype) { - case 3: + case GRUB_NET_BOOTP_ROUTER: if (taglength == 4) { grub_net_network_level_netaddress_t target; @@ -95,19 +95,32 @@ parse_dhcp_vendor (const char *name, void *vend, int limit) grub_net_add_route_gw (rname, target, gw); } break; - case 12: + case GRUB_NET_BOOTP_DNS: + { + int i; + for (i = 0; i < taglength / 4; i++) + { + struct grub_net_network_level_address s; + s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + s.ipv4 = grub_get_unaligned32 (ptr); + grub_net_add_dns_server (&s); + ptr += 4; + } + } + break; + case GRUB_NET_BOOTP_HOSTNAME: set_env_limn_ro (name, "hostname", (char *) ptr, taglength); break; - case 15: + case GRUB_NET_BOOTP_DOMAIN: set_env_limn_ro (name, "domain", (char *) ptr, taglength); break; - case 17: + case GRUB_NET_BOOTP_ROOT_PATH: set_env_limn_ro (name, "rootpath", (char *) ptr, taglength); break; - case 18: + case GRUB_NET_BOOTP_EXTENSIONS_PATH: set_env_limn_ro (name, "extensionspath", (char *) ptr, taglength); break; diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 696b3ccca..bad2745a7 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -674,15 +674,15 @@ grub_dns_init (void) { cmd = grub_register_command ("net_nslookup", grub_cmd_nslookup, "ADDRESS DNSSERVER", - N_("perform a DNS lookup")); + N_("Perform a DNS lookup")); cmd_add = grub_register_command ("net_add_dns", grub_cmd_add_dns, "DNSSERVER", - N_("add a DNS server")); + N_("Add a DNS server")); cmd_del = grub_register_command ("net_del_dns", grub_cmd_del_dns, "DNSSERVER", - N_("remove a DNS server")); - cmd_list = grub_register_command ("net_del_dns", grub_cmd_list_dns, - NULL, N_("remove a DNS server")); + N_("Remove a DNS server")); + cmd_list = grub_register_command ("net_ls_dns", grub_cmd_list_dns, + NULL, N_("List DNS servers")); } void diff --git a/include/grub/net.h b/include/grub/net.h index dd566354d..3913272eb 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -396,6 +396,18 @@ struct grub_net_bootp_packet #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53 #define GRUB_NET_BOOTP_RFC1048_MAGIC_3 0x63 +enum + { + GRUB_NET_BOOTP_PAD = 0x00, + GRUB_NET_BOOTP_ROUTER = 0x03, + GRUB_NET_BOOTP_DNS = 0x06, + GRUB_NET_BOOTP_HOSTNAME = 0x0c, + GRUB_NET_BOOTP_DOMAIN = 0x0f, + GRUB_NET_BOOTP_ROOT_PATH = 0x11, + GRUB_NET_BOOTP_EXTENSIONS_PATH = 0x12, + GRUB_NET_BOOTP_END = 0xff + }; + struct grub_net_network_level_interface * grub_net_configure_by_dhcp_ack (const char *name, struct grub_net_card *card,