diff --git a/ChangeLog b/ChangeLog index d70c7c6c0..4f42719bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,61 @@ +2011-06-24 Vladimir Serbinenko +2011-06-24 Manoel Rebelo Abranches + + Network infrastructure. + The ARP protocol was made by Paulo Pinatti + + * include/grub/net/arp.h: New file. + * include/grub/net/device.h: Likewise. + * include/grub/net/ethernet.h: Likewise. + * include/grub/net/ip.h: Likewise. + * include/grub/net/netbuff.h: Likewise. + * include/grub/net/tftp.h: Likewise. + * include/grub/net/udp.h: Likewise. + * include/grub/ieee1275/ofnet.h: Likewise. + * include/grub/emu/export.h: Likewise. + * include/grub/net.h: Likewise. + * grub-core/net/arp.c: Likewise. + * grub-core/net/ethernet.c: Likewise. + * grub-core/net/ip.c: Likewise. + * grub-core/net/udp.c: Likewise. + * grub-core/net/tftp.c: Likewise. + * grub-core/net/netbuff.c: Likewise. + * grub-core/net/net.c: Likewise. + * grub-core/net/drivers/emu/emunet.c: Likewise. + * grub-core/net/drivers/ieee1275/ofnet.c: Likewise. + * grub-core/Makefile.am (KERNEL_HEADER_FILES): Add net.h, ofnet.h and + export.h. + * grub-core/Makefile.core.def (net): New module. + (tftp): Likewise. + (ofnet): Likewise. + (emunet): Likewise. + * grub-core/commands/ls.c (grub_ls_list_devices) [!GRUB_UTIL]: List + network protocols. + * grub-core/kern/device.c (grub_net_open) : New variable. + (grub_device_open): Handle network device. + (grub_device_close): Likewise. + * grub-core/kern/file.c (grub_file_net_seek) : New variable. + (grub_grubnet_fini): Likewise. + (grub_file_seek): Seek in network device. + * grub-core/kern/fs.c (grub_fs_probe): Handle network devices. + * grub-core/kern/ieee1275/init.c (grub_machine_set_prefix): Handle + network root. + (grub_machine_fini): Call grub_grubnet_fini. + * grub-core/kern/ieee1275/openfw.c (grub_ieee1275_parse_args): Handle + network. + (grub_ieee1275_get_aliasdevname): New function. + * grub-core/loader/i386/multiboot_mbi.c (grub_multiboot_get_mbi_size): + Add unofficial Solaris network info. + (grub_multiboot_make_mbi): Likewise. + * grub-core/fs/i386/pc/pxe.c: Moved from here ... + * grub-core/net/i386/pc/pxe.c: ...here. Adapted for new design. + * include/grub/device.h (grub_fs): Removed. + * include/grub/err.h (grub_err_t): Add network-related values. + * include/grub/i386/pc/pxe.h: Removed bootp parts. + * include/grub/ieee1275/ieee1275.h (grub_ofnetcard_data): New struct. + (grub_ieee1275_get_aliasdevname): New proto. + * include/grub/net.h: Rewritten. + 2011-06-24 Vladimir Serbinenko * grub-core/disk/raid.c (insert_array): Ensure uniqueness of readable diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 98e275f7e..a8862b309 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -79,6 +79,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm_private.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/boot.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libgcc.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/memory.h @@ -112,6 +113,7 @@ endif if COND_i386_ieee1275 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ofnet.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h @@ -171,6 +173,7 @@ endif if COND_powerpc_ieee1275 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ofnet.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h @@ -178,6 +181,7 @@ endif if COND_sparc64_ieee1275 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ofnet.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sparc64/ieee1275/ieee1275.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h @@ -187,6 +191,7 @@ endif if COND_emu KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/datetime.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/misc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/export.h if COND_GRUB_EMU_SDL KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h endif diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 0799d3823..927718359 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1100,7 +1100,7 @@ module = { module = { name = pxe; - i386_pc = fs/i386/pc/pxe.c; + i386_pc = net/i386/pc/pxe.c; enable = i386_pc; }; @@ -1571,6 +1571,33 @@ module = { common = hook/datehook.c; }; +module = { + name = net; + common = net/net.c; + common = net/ip.c; + common = net/udp.c; + common = net/ethernet.c; + common = net/arp.c; + common = net/netbuff.c; +}; + +module = { + name = tftp; + common = net/tftp.c; +}; + +module = { + name = ofnet; + ieee1275 = net/drivers/ieee1275/ofnet.c; + enable = ieee1275; +}; + +module = { + name = emunet; + emu = net/drivers/emu/emunet.c; + enable = emu; +}; + module = { name = legacycfg; common = commands/legacycfg.c; diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 4c4051a22..db9307780 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -31,6 +31,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -60,6 +61,22 @@ grub_ls_list_devices (int longlist) grub_device_iterate (grub_ls_print_devices); grub_xputs ("\n"); + +#ifndef GRUB_UTIL + { + grub_net_app_level_t proto; + int first = 1; + FOR_NET_APP_LEVEL (proto) + { + if (first) + grub_puts_ (N_ ("Network protocols:")); + first = 0; + grub_printf ("%s ", proto->name); + } + grub_xputs ("\n"); + } +#endif + grub_refresh (); return 0; diff --git a/grub-core/commands/probe.c b/grub-core/commands/probe.c index c5f946340..7ed2a4e51 100644 --- a/grub-core/commands/probe.c +++ b/grub-core/commands/probe.c @@ -74,7 +74,7 @@ grub_cmd_probe (grub_extcmd_context_t ctxt, int argc, char **args) { const char *val = "none"; if (dev->net) - val = dev->net->dev->name; + val = dev->net->name; if (dev->disk) val = dev->disk->dev->name; if (state[0].set) diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c index 50f49ae6b..45bcd8210 100644 --- a/grub-core/kern/device.c +++ b/grub-core/kern/device.c @@ -26,6 +26,8 @@ #include #include +grub_net_t (*grub_net_open) (const char *name) = NULL; + grub_device_t grub_device_open (const char *name) { @@ -46,15 +48,19 @@ grub_device_open (const char *name) if (! dev) goto fail; + dev->net = NULL; /* Try to open a disk. */ - disk = grub_disk_open (name); - if (! disk) - goto fail; + dev->disk = grub_disk_open (name); + if (dev->disk) + return dev; + if (grub_net_open && grub_errno == GRUB_ERR_UNKNOWN_DEVICE) + { + grub_errno = GRUB_ERR_NONE; + dev->net = grub_net_open (name); + } - dev->disk = disk; - dev->net = 0; /* FIXME */ - - return dev; + if (dev->net) + return dev; fail: if (disk) @@ -71,6 +77,12 @@ grub_device_close (grub_device_t device) if (device->disk) grub_disk_close (device->disk); + if (device->net) + { + grub_free (device->net->name); + grub_free (device->net); + } + grub_free (device); return grub_errno; diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 9d5a51c48..2407e72c5 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -20,10 +20,14 @@ #include #include #include +#include #include #include #include +grub_err_t (*grub_file_net_seek) (struct grub_file *file, grub_off_t offset) = NULL; +void (*EXPORT_VAR (grub_grubnet_fini)) (void); + grub_file_filter_t grub_file_filters_all[GRUB_FILE_FILTER_MAX]; grub_file_filter_t grub_file_filters_enabled[GRUB_FILE_FILTER_MAX]; @@ -148,7 +152,6 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) if (len == 0) return 0; - res = (file->fs->read) (file, buf, len); if (res > 0) file->offset += res; @@ -179,8 +182,12 @@ grub_file_seek (grub_file_t file, grub_off_t offset) "attempt to seek outside of the file"); return -1; } + + if (file->device->net && grub_file_net_seek) + grub_file_net_seek (file, offset); old = file->offset; file->offset = offset; + return old; } diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c index cf800f4cc..14d389e07 100644 --- a/grub-core/kern/fs.c +++ b/grub-core/kern/fs.c @@ -94,7 +94,7 @@ grub_fs_probe (grub_device_t device) count--; } } - else if (device->net->fs) + else if (device->net && device->net->fs) return device->net->fs; grub_error (GRUB_ERR_UNKNOWN_FS, "unknown filesystem"); diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 682a8b5a4..3c55096a4 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -74,7 +76,22 @@ grub_machine_set_prefix (void) char bootpath[64]; /* XXX check length */ char *filename; char *prefix; - + grub_bootp_t bootp_pckt; + char addr[GRUB_NET_MAX_STR_ADDR_LEN]; + + /* Set the net prefix when possible. */ + if (grub_getbootp && (bootp_pckt = grub_getbootp())) + { + grub_uint32_t n = bootp_pckt->siaddr; + grub_snprintf (addr, GRUB_NET_MAX_STR_ADDR_LEN, "%d.%d.%d.%d", + ((n >> 24) & 0xff), ((n >> 16) & 0xff), + ((n >> 8) & 0xff), ((n >> 0) & 0xff)); + prefix = grub_xasprintf ("(tftp,%s)%s", addr,grub_prefix); + grub_env_set ("prefix", prefix); + grub_free (prefix); + return; + } + if (grub_prefix[0]) { grub_env_set ("prefix", grub_prefix); @@ -247,6 +264,8 @@ grub_machine_init (void) void grub_machine_fini (void) { + if (grub_grubnet_fini) + grub_grubnet_fini (); grub_ofdisk_fini (); grub_console_fini (); } diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index f5dc8efb1..db4bec90a 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -22,11 +22,16 @@ #include #include #include +#include +#include +#include +grub_bootp_t (*grub_getbootp) (void); enum grub_ieee1275_parse_type { GRUB_PARSE_FILENAME, GRUB_PARSE_PARTITION, + GRUB_PARSE_DEVICE }; /* Walk children of 'devpath', calling hook for each. */ @@ -366,12 +371,14 @@ grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype) ret = grub_strndup (args, (grub_size_t)(comma - args)); } } - else + + else if (!grub_strcmp ("network", type)) + { + if (ptype == GRUB_PARSE_DEVICE) + ret = grub_strdup(device); + } + else { - /* XXX Handle net devices by configuring & registering a grub_net_dev - here, then return its name? - Example path: "net:,,,,,". */ grub_printf ("Unsupported type %s for device %s\n", type, device); } @@ -381,6 +388,12 @@ fail: return ret; } +char * +grub_ieee1275_get_aliasdevname (const char *path) +{ + return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE); +} + char * grub_ieee1275_get_filename (const char *path) { @@ -467,3 +480,4 @@ grub_ieee1275_canonicalise_devname (const char *path) grub_free (buf); return NULL; } + diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index bef534296..abd4ae96d 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -33,6 +33,7 @@ #include #include #include +#include /* The bits in the required part of flags field we don't support. */ #define UNSUPPORTED_FLAGS 0x0000fff8 @@ -194,7 +195,10 @@ grub_multiboot_load (grub_file_t file) static grub_size_t grub_multiboot_get_mbi_size (void) { - return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4) + grub_size_t ret; + struct grub_net_network_level_interface *net; + + ret = sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4) + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd + ALIGN_UP (sizeof(PACKAGE_STRING), 4) + grub_get_multiboot_mmap_count () * sizeof (struct multiboot_mmap_entry) @@ -205,6 +209,15 @@ grub_multiboot_get_mbi_size (void) + sizeof (struct grub_vbe_mode_info_block) #endif + ALIGN_UP (sizeof (struct multiboot_apm_info), 4); + + FOR_NET_NETWORK_LEVEL_INTERFACES(net) + if (net->dhcp_ack) + { + ret += net->dhcp_acklen; + break; + } + + return ret; } /* Fill previously allocated Multiboot mmap. */ @@ -530,6 +543,20 @@ grub_multiboot_make_mbi (grub_uint32_t *target) mbi->flags |= MULTIBOOT_INFO_BOOTDEV; } + { + struct grub_net_network_level_interface *net; + FOR_NET_NETWORK_LEVEL_INTERFACES(net) + if (net->dhcp_ack) + { + grub_memcpy (ptrorig, net->dhcp_ack, net->dhcp_acklen); + mbi->drives_addr = ptrdest; + mbi->drives_length = net->dhcp_acklen; + ptrorig += net->dhcp_acklen; + ptrdest += net->dhcp_acklen; + break; + } + } + if (elf_sec_num) { mbi->u.elf_sec.addr = ptrdest; diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c new file mode 100644 index 000000000..fecde3282 --- /dev/null +++ b/grub-core/net/arp.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include + +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 && + arp_table[i].nl_address.ipv4 == proto->ipv4) + 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) +{ + struct arp_entry *entry; + struct grub_net_buff nb; + struct arphdr *arp_header; + grub_net_link_level_address_t target_hw_addr; + char *aux, arp_data[128]; + grub_err_t err; + int i; + + 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; + } + /* 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)); + 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->op = grub_cpu_to_be16 (ARP_REQUEST); + aux = (char *) arp_header + sizeof (*arp_header); + /* Sender hardware address. */ + grub_memcpy (aux, &inf->hwaddress.mac, 6); + + aux += 6; + /* Sender protocol address */ + grub_memcpy (aux, &inf->address.ipv4, 4); + aux += 4; + /* Target hardware address */ + for (i = 0; i < 6; i++) + aux[i] = 0x00; + aux += 6; + /* Target protocol address */ + 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 < 3; i++) + { + entry = arp_find_entry (proto_addr); + if (entry) + { + grub_memcpy (hw_addr, &entry->ll_address, sizeof (*hw_addr)); + return GRUB_ERR_NONE; + } + grub_net_poll_cards (200); + } + + return grub_error (GRUB_ERR_TIMEOUT, "timeout: could not resolve hardware address"); +} + +grub_err_t +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; + struct grub_net_network_level_interface *inf; + + 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); + + /* Check if the sender is in the cache table. */ + entry = arp_find_entry (&hwaddress); + /* 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; + grub_memcpy (&entry->nl_address.ipv4, sender_protocol_address, 4); + 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; + } + + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + /* Am I the protocol address target? */ + if (grub_memcmp (target_protocol_address, &inf->address.ipv4, 6) == 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_memcpy (sender_hardware_address, inf->hwaddress.mac, 6); + grub_memcpy (aux.mac, sender_protocol_address, 6); + grub_memcpy (sender_protocol_address, target_protocol_address, + arp_header->pln); + grub_memcpy (target_protocol_address, aux.mac, 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); + } + } + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c new file mode 100644 index 000000000..d1e49a2f4 --- /dev/null +++ b/grub-core/net/drivers/emu/emunet.c @@ -0,0 +1,86 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int fd; + +static grub_err_t +send_card_buffer (const struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *pack) +{ + ssize_t actual; + + actual = write (fd, pack->data, pack->tail - pack->data); + if (actual < 0) + return grub_error (GRUB_ERR_IO, "couldn't send packets"); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +get_card_packet (const struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *pack) +{ + ssize_t actual; + + grub_netbuff_clear (pack); + actual = read (fd, pack->data, 1500); + if (actual < 0) + return -1; + grub_netbuff_put (pack, actual); + + return actual; +} + +static struct grub_net_card_driver emudriver = + { + .name = "emu", + .send = send_card_buffer, + .recv = get_card_packet + }; + +static struct grub_net_card emucard = + { + .name = "emu0", + .driver = &emudriver, + .default_address = { + .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET, + {.mac = {0, 1, 2, 3, 4, 5}} + }, + .flags = 0 + }; + +GRUB_MOD_INIT(emunet) +{ + struct ifreq ifr; + fd = open ("/dev/net/tun", O_RDWR | O_NONBLOCK); + if (fd < 0) + return; + grub_memset (&ifr, 0, sizeof (ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (ioctl (fd, TUNSETIFF, &ifr) < 0) + { + close (fd); + fd = -1; + return; + } + grub_net_card_register (&emucard); +} + +GRUB_MOD_FINI(emunet) +{ + if (fd >= 0) + { + close (fd); + grub_net_card_unregister (&emucard); + } +} diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c new file mode 100644 index 000000000..2c264edb1 --- /dev/null +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +card_open (struct grub_net_card *dev) +{ + int status; + struct grub_ofnetcard_data *data = dev->data; + char path[grub_strlen (data->path) + + grub_strlen (":speed=auto,duplex=auto,1.1.1.1,dummy,1.1.1.1,1.1.1.1,5,5,1.1.1.1,512") + 1]; + + /* The full string will prevent a bootp packet to be sent. Just put some valid ip in there. */ + grub_snprintf (path, sizeof (path), "%s%s", data->path, + ":speed=auto,duplex=auto,1.1.1.1,dummy,1.1.1.1,1.1.1.1,5,5,1.1.1.1,512"); + status = grub_ieee1275_open (path, &(data->handle)); + + if (status) + return grub_error (GRUB_ERR_IO, "Couldn't open network card."); + + return GRUB_ERR_NONE; +} + +static grub_err_t +card_close (struct grub_net_card *dev) +{ + struct grub_ofnetcard_data *data = dev->data; + + if (data->handle) + grub_ieee1275_close (data->handle); + return GRUB_ERR_NONE; +} + +static grub_err_t +send_card_buffer (struct grub_net_card *dev, struct grub_net_buff *pack) +{ + int actual; + int status; + struct grub_ofnetcard_data *data = dev->data; + + status = grub_ieee1275_write (data->handle, pack->data, + pack->tail - pack->data, &actual); + + if (status) + return grub_error (GRUB_ERR_IO, "Couldn't send network packet."); + return GRUB_ERR_NONE; +} + +static grub_ssize_t +get_card_packet (struct grub_net_card *dev, struct grub_net_buff *nb) +{ + + int actual, rc; + struct grub_ofnetcard_data *data = dev->data; + grub_uint64_t start_time; + + grub_netbuff_clear (nb); + start_time = grub_get_time_ms (); + do + rc = grub_ieee1275_read (data->handle, nb->data, data->mtu, &actual); + while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); + if (actual) + { + grub_netbuff_put (nb, actual); + return actual; + } + return -1; +} + +static struct grub_net_card_driver ofdriver = + { + .name = "ofnet", + .init = card_open, + .fini = card_close, + .send = send_card_buffer, + .recv = get_card_packet + }; + +static const struct +{ + char *name; + int offset; +} + +bootp_response_properties[] = + { + { .name = "bootp-response", .offset = 0}, + { .name = "dhcp-response", .offset = 0}, + { .name = "bootpreply-packet", .offset = 0x2a}, + }; + +static grub_bootp_t +grub_getbootp_real (void) +{ + grub_bootp_t packet = grub_malloc (sizeof *packet); + char *bootp_response; + grub_ssize_t size; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE (bootp_response_properties); i++) + if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, + bootp_response_properties[i].name, + &size) >= 0) + break; + + if (size < 0) + return NULL; + + bootp_response = grub_malloc (size); + if (grub_ieee1275_get_property (grub_ieee1275_chosen, + bootp_response_properties[i].name, + bootp_response, size, 0) < 0) + return NULL; + + grub_memcpy (packet, bootp_response + bootp_response_properties[i].offset, sizeof (*packet)); + grub_free (bootp_response); + return packet; +} + +static void +grub_ofnet_findcards (void) +{ + struct grub_net_card *card; + grub_ieee1275_phandle_t devhandle; + grub_net_link_level_address_t lla; + int i = 0; + auto int search_net_devices (struct grub_ieee1275_devalias *alias); + + int search_net_devices (struct grub_ieee1275_devalias *alias) + { + if (!grub_strcmp (alias->type, "network")) + { + + card = grub_malloc (sizeof (struct grub_net_card)); + struct grub_ofnetcard_data *ofdata = + grub_malloc (sizeof (struct grub_ofnetcard_data)); + ofdata->path = grub_strdup (alias->path); + + grub_ieee1275_finddevice (ofdata->path, &devhandle); + + if (grub_ieee1275_get_integer_property + (devhandle, "max-frame-size", &(ofdata->mtu), + sizeof (ofdata->mtu), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve mtu size."); + + if (grub_ieee1275_get_property + (devhandle, "mac-address", &(lla.mac), 6, 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve mac address."); + + lla.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->default_address = lla; + + card->driver = NULL; + card->data = ofdata; + card->flags = 0; + card->name = grub_xasprintf ("eth%d", i++); + grub_net_card_register (card); + return 0; + } + return 0; + } + + /* Look at all nodes for devices of the type network. */ + grub_ieee1275_devices_iterate (search_net_devices); +} + +static void +grub_ofnet_probecards (void) +{ + struct grub_net_card *card; + struct grub_net_card_driver *driver; + struct grub_net_network_level_interface *inter; + grub_bootp_t bootp_pckt; + grub_net_network_level_address_t addr; + grub_net_network_level_netaddress_t net; + bootp_pckt = grub_getbootp (); + + /* Assign correspondent driver for each device. */ + FOR_NET_CARDS (card) + { + FOR_NET_CARD_DRIVERS (driver) + { + if (driver->init (card) == GRUB_ERR_NONE) + { + card->driver = driver; + if (bootp_pckt + && grub_memcmp (bootp_pckt->chaddr, card->default_address.mac, 6) == 0) + { + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + addr.ipv4 = bootp_pckt->yiaddr; + grub_net_add_addr ("bootp_cli_addr", card, addr, + card->default_address, 0); + FOR_NET_NETWORK_LEVEL_INTERFACES (inter) + if (grub_strcmp (inter->name, "bootp_cli_addr") == 0) + break; + net.type = addr.type; + net.ipv4.base = addr.ipv4; + net.ipv4.masksize = 24; + grub_net_add_route ("bootp-router", net, inter); + } + break; + } + } + } + grub_free (bootp_pckt); + +} + +GRUB_MOD_INIT(ofnet) +{ + struct grub_net_card *card; + grub_getbootp = grub_getbootp_real; + grub_net_card_driver_register (&ofdriver); + grub_ofnet_findcards (); + grub_ofnet_probecards (); + FOR_NET_CARDS (card) + if (card->driver == NULL) + grub_net_card_unregister (card); +} + +GRUB_MOD_FINI(ofnet) +{ + struct grub_net_card *card; + FOR_NET_CARDS (card) + if (card->driver && !grub_strcmp (card->driver->name, "ofnet")) + { + card->driver->fini (card); + card->driver = NULL; + } + grub_net_card_driver_unregister (&ofdriver); + grub_getbootp = NULL; +} diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c new file mode 100644 index 000000000..863e659a8 --- /dev/null +++ b/grub-core/net/ethernet.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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) +{ + struct etherhdr *eth; + grub_err_t err; + + err = grub_netbuff_push (nb, sizeof (*eth)); + if (err) + return err; + eth = (struct etherhdr *) nb->data; + grub_memcpy (eth->dst, target_addr.mac, 6); + grub_memcpy (eth->src, inf->hwaddress.mac, 6); + + eth->type = grub_cpu_to_be16 (ethertype); + + return inf->card->driver->send (inf->card, nb); +} + +grub_err_t +grub_net_recv_ethernet_packet (struct grub_net_buff * nb, + const struct grub_net_card * card) +{ + struct etherhdr *eth; + struct llchdr *llch; + struct snaphdr *snaph; + grub_uint16_t type; + grub_net_link_level_address_t hwaddress; + grub_err_t err; + + eth = (struct etherhdr *) nb->data; + type = grub_be_to_cpu16 (eth->type); + err = grub_netbuff_pull (nb, sizeof (*eth)); + if (err) + return err; + + if (type <= 1500) + { + llch = (struct llchdr *) nb->data; + type = llch->dsap & LLCADDRMASK; + + if (llch->dsap == 0xaa && llch->ssap == 0xaa && llch->ctrl == 0x3) + { + err = grub_netbuff_pull (nb, sizeof (*llch)); + if (err) + return err; + snaph = (struct snaphdr *) nb->data; + type = snaph->type; + } + } + + 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) + { + grub_net_arp_receive (nb); + grub_netbuff_free (nb); + } + /* IP packet. */ + if (type == GRUB_NET_ETHERTYPE_IP) + grub_net_recv_ip_packets (nb, card, &hwaddress); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/fs/i386/pc/pxe.c b/grub-core/net/i386/pc/pxe.c similarity index 51% rename from grub-core/fs/i386/pc/pxe.c rename to grub-core/net/i386/pc/pxe.c index 7d86b78bf..27c4af17f 100644 --- a/grub-core/fs/i386/pc/pxe.c +++ b/grub-core/net/i386/pc/pxe.c @@ -18,9 +18,8 @@ */ #include -#include +#include #include -#include #include #include #include @@ -30,35 +29,29 @@ #include #include -GRUB_MOD_LICENSE ("GPLv3+"); - #define SEGMENT(x) ((x) >> 4) #define OFFSET(x) ((x) & 0xF) #define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x)) -#define LINEAR(x) (void *) (((x >> 16) <<4) + (x & 0xFFFF)) - -struct grub_pxe_disk_data -{ - grub_uint32_t server_ip; - grub_uint32_t gateway_ip; -}; +#define LINEAR(x) (void *) (((x >> 16) << 4) + (x & 0xFFFF)) struct grub_pxe_bangpxe *grub_pxe_pxenv; -static grub_uint32_t grub_pxe_your_ip; static grub_uint32_t grub_pxe_default_server_ip; +#if 0 static grub_uint32_t grub_pxe_default_gateway_ip; +#endif static unsigned grub_pxe_blksize = GRUB_PXE_MIN_BLKSIZE; - +static grub_uint32_t pxe_rm_entry = 0; static grub_file_t curr_file = 0; struct grub_pxe_data { grub_uint32_t packet_number; grub_uint32_t block_size; + grub_uint32_t server_ip; + grub_uint32_t gateway_ip; char filename[0]; }; -static grub_uint32_t pxe_rm_entry = 0; static struct grub_pxe_bangpxe * grub_pxe_scan (void) @@ -103,132 +96,13 @@ grub_pxe_scan (void) return bangpxe; } -static int -grub_pxe_iterate (int (*hook) (const char *name)) -{ - if (hook ("pxe")) - return 1; - return 0; -} - static grub_err_t -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++) - { - t = grub_strtoul (ptr, (char **) &ptr, 0); - if (grub_errno) - return grub_errno; - if (t & ~0xff) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP."); - newip >>= 8; - newip |= (t << 24); - if (i != 3 && *ptr != '.') - return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP."); - ptr++; - } - *ip = newip; - if (rest) - *rest = ptr - 1; - return 0; -} - -static grub_err_t -grub_pxe_open (const char *name, grub_disk_t disk) -{ - struct grub_pxe_disk_data *data; - - if (grub_strcmp (name, "pxe") != 0 - && grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) != 0) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a pxe disk"); - - data = grub_malloc (sizeof (*data)); - if (!data) - return grub_errno; - - if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) - { - const char *ptr; - grub_err_t err; - - ptr = name + sizeof ("pxe:") - 1; - err = parse_ip (ptr, &(data->server_ip), &ptr); - if (err) - return err; - if (*ptr == ':') - { - err = parse_ip (ptr + 1, &(data->gateway_ip), 0); - if (err) - return err; - } - else - data->gateway_ip = grub_pxe_default_gateway_ip; - } - else - { - data->server_ip = grub_pxe_default_server_ip; - data->gateway_ip = grub_pxe_default_gateway_ip; - } - - disk->total_sectors = 0; - disk->id = (unsigned long) data; - - disk->data = data; - - return GRUB_ERR_NONE; -} - -static void -grub_pxe_close (grub_disk_t disk) -{ - grub_free (disk->data); -} - -static grub_err_t -grub_pxe_read (grub_disk_t disk __attribute((unused)), - grub_disk_addr_t sector __attribute((unused)), - grub_size_t size __attribute((unused)), - char *buf __attribute((unused))) -{ - return GRUB_ERR_OUT_OF_RANGE; -} - -static grub_err_t -grub_pxe_write (grub_disk_t disk __attribute((unused)), - grub_disk_addr_t sector __attribute((unused)), - grub_size_t size __attribute((unused)), - const char *buf __attribute((unused))) -{ - return GRUB_ERR_OUT_OF_RANGE; -} - -static struct grub_disk_dev grub_pxe_dev = - { - .name = "pxe", - .id = GRUB_DISK_DEVICE_PXE_ID, - .iterate = grub_pxe_iterate, - .open = grub_pxe_open, - .close = grub_pxe_close, - .read = grub_pxe_read, - .write = grub_pxe_write, - .next = 0 - }; - -static grub_err_t -grub_pxefs_dir (grub_device_t device, +grub_pxefs_dir (grub_device_t device __attribute__ ((unused)), const char *path __attribute__ ((unused)), int (*hook) (const char *filename, const struct grub_dirhook_info *info) __attribute__ ((unused))) { - if (device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID) - return grub_error (GRUB_ERR_BAD_FS, "not a pxe disk"); - return GRUB_ERR_NONE; } @@ -241,11 +115,45 @@ grub_pxefs_open (struct grub_file *file, const char *name) struct grub_pxenv_tftp_open c2; } c; struct grub_pxe_data *data; - struct grub_pxe_disk_data *disk_data = file->device->disk->data; grub_file_t file_int, bufio; - if (file->device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID) - return grub_error (GRUB_ERR_BAD_FS, "not a pxe disk"); + data = grub_zalloc (sizeof (*data) + grub_strlen (name) + 1); + if (!data) + return grub_errno; + + { + grub_net_network_level_address_t addr; + grub_net_network_level_address_t gateway; + struct grub_net_network_level_interface *interf; + grub_err_t err; + + if (grub_strncmp (file->device->net->name, + "pxe,", sizeof ("pxe,") - 1) == 0 + || grub_strncmp (file->device->net->name, + "pxe:", sizeof ("pxe:") - 1) == 0) + { + err = grub_net_resolve_address (file->device->net->name + + sizeof ("pxe,") - 1, &addr); + if (err) + { + grub_free (data); + return err; + } + } + else + { + addr.ipv4 = grub_pxe_default_server_ip; + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + } + err = grub_net_route_address (addr, &gateway, &interf); + if (err) + { + grub_free (data); + return err; + } + data->server_ip = addr.ipv4; + data->gateway_ip = gateway.ipv4; + } if (curr_file != 0) { @@ -253,12 +161,15 @@ grub_pxefs_open (struct grub_file *file, const char *name) curr_file = 0; } - c.c1.server_ip = disk_data->server_ip; - c.c1.gateway_ip = disk_data->gateway_ip; + c.c1.server_ip = data->server_ip; + c.c1.gateway_ip = data->gateway_ip; grub_strcpy ((char *)&c.c1.filename[0], name); grub_pxe_call (GRUB_PXENV_TFTP_GET_FSIZE, &c.c1, pxe_rm_entry); if (c.c1.status) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + { + grub_free (data); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + } file->size = c.c1.file_size; @@ -266,11 +177,10 @@ grub_pxefs_open (struct grub_file *file, const char *name) c.c2.packet_size = grub_pxe_blksize; grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &c.c2, pxe_rm_entry); if (c.c2.status) - return grub_error (GRUB_ERR_BAD_FS, "open fails"); - - data = grub_zalloc (sizeof (struct grub_pxe_data) + grub_strlen (name) + 1); - if (! data) - return grub_errno; + { + grub_free (data); + return grub_error (GRUB_ERR_BAD_FS, "open fails"); + } data->block_size = c.c2.packet_size; grub_strcpy (data->filename, name); @@ -305,7 +215,6 @@ grub_pxefs_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_pxenv_tftp_read c; struct grub_pxe_data *data; - struct grub_pxe_disk_data *disk_data = file->device->disk->data; grub_uint32_t pn; grub_uint64_t r; @@ -326,8 +235,8 @@ grub_pxefs_read (grub_file_t file, char *buf, grub_size_t len) if (curr_file != 0) grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &o, pxe_rm_entry); - o.server_ip = disk_data->server_ip; - o.gateway_ip = disk_data->gateway_ip; + o.server_ip = data->server_ip; + o.gateway_ip = data->gateway_ip; grub_strcpy ((char *)&o.filename[0], data->filename); o.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT); o.packet_size = data->block_size; @@ -386,7 +295,7 @@ grub_pxefs_label (grub_device_t device __attribute ((unused)), static struct grub_fs grub_pxefs_fs = { - .name = "pxefs", + .name = "pxe", .dir = grub_pxefs_dir, .open = grub_pxefs_open, .read = grub_pxefs_read, @@ -395,134 +304,31 @@ static struct grub_fs grub_pxefs_fs = .next = 0 }; -static char * -grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), - const char *val __attribute__ ((unused))) +static grub_ssize_t +grub_pxe_recv (struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *buf __attribute__ ((unused))) { - return NULL; + return 0; } -static void -set_mac_env (grub_uint8_t *mac_addr, grub_size_t mac_len) +static grub_err_t +grub_pxe_send (struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *buf __attribute__ ((unused))) { - char buf[(sizeof ("XX:") - 1) * mac_len + 1]; - char *ptr = buf; - unsigned i; - - for (i = 0; i < mac_len; i++) - { - grub_snprintf (ptr, sizeof (buf) - (ptr - buf), - "%02x:", mac_addr[i] & 0xff); - ptr += (sizeof ("XX:") - 1); - } - if (mac_len) - *(ptr - 1) = 0; - else - buf[0] = 0; - - grub_env_set ("net_pxe_mac", buf); - /* XXX: Is it possible to change MAC in PXE? */ - grub_register_variable_hook ("net_pxe_mac", 0, grub_env_write_readonly); - grub_env_export ("net_pxe_mac"); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "not implemented"); } -static void -set_env_limn_ro (const char *varname, char *value, grub_size_t len) +struct grub_net_card_driver grub_pxe_card_driver = { - char c; - c = value[len]; - value[len] = 0; - grub_env_set (varname, value); - value[len] = c; - grub_register_variable_hook (varname, 0, grub_env_write_readonly); - grub_env_export (varname); -} + .send = grub_pxe_send, + .recv = grub_pxe_recv +}; -static void -parse_dhcp_vendor (void *vend, int limit) +struct grub_net_card grub_pxe_card = { - grub_uint8_t *ptr, *ptr0; - - ptr = ptr0 = vend; - - if (grub_be_to_cpu32 (*(grub_uint32_t *) ptr) != 0x63825363) - return; - ptr = ptr + sizeof (grub_uint32_t); - while (ptr - ptr0 < limit) - { - grub_uint8_t tagtype; - grub_uint8_t taglength; - - tagtype = *ptr++; - - /* Pad tag. */ - if (tagtype == 0) - continue; - - /* End tag. */ - if (tagtype == 0xff) - return; - - taglength = *ptr++; - - switch (tagtype) - { - case 12: - set_env_limn_ro ("net_pxe_hostname", (char *) ptr, taglength); - break; - - case 15: - set_env_limn_ro ("net_pxe_domain", (char *) ptr, taglength); - break; - - case 17: - set_env_limn_ro ("net_pxe_rootpath", (char *) ptr, taglength); - break; - - case 18: - set_env_limn_ro ("net_pxe_extensionspath", (char *) ptr, taglength); - break; - - /* If you need any other options please contact GRUB - development team. */ - } - - ptr += taglength; - } -} - -static void -grub_pxe_detect (void) -{ - struct grub_pxe_bangpxe *pxenv; - struct grub_pxenv_get_cached_info ci; - struct grub_pxenv_boot_player *bp; - - pxenv = grub_pxe_scan (); - if (! pxenv) - return; - - ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK; - ci.buffer = 0; - ci.buffer_size = 0; - grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci, pxe_rm_entry); - if (ci.status) - return; - - bp = LINEAR (ci.buffer); - - grub_pxe_your_ip = bp->your_ip; - grub_pxe_default_server_ip = bp->server_ip; - grub_pxe_default_gateway_ip = bp->gateway_ip; - set_mac_env (bp->mac_addr, bp->hw_len < sizeof (bp->mac_addr) ? bp->hw_len - : sizeof (bp->mac_addr)); - set_env_limn_ro ("net_pxe_boot_file", (char *) bp->boot_file, - sizeof (bp->boot_file)); - set_env_limn_ro ("net_pxe_dhcp_server_name", (char *) bp->server_name, - sizeof (bp->server_name)); - parse_dhcp_vendor (&bp->vendor, sizeof (bp->vendor)); - grub_pxe_pxenv = pxenv; -} + .driver = &grub_pxe_card_driver, + .name = "pxe" +}; void grub_pxe_unload (void) @@ -530,8 +336,7 @@ grub_pxe_unload (void) if (grub_pxe_pxenv) { grub_fs_unregister (&grub_pxefs_fs); - grub_disk_dev_unregister (&grub_pxe_dev); - + grub_net_card_unregister (&grub_pxe_card); grub_pxe_pxenv = 0; } } @@ -539,10 +344,12 @@ grub_pxe_unload (void) static void set_ip_env (char *varname, grub_uint32_t ip) { - char buf[sizeof ("XXX.XXX.XXX.XXX")]; + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + grub_net_network_level_address_t addr; + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + addr.ipv4 = ip; - grub_snprintf (buf, sizeof (buf), "%d.%d.%d.%d", (ip & 0xff), - (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff); + grub_net_addr_to_str (&addr, buf); grub_env_set (varname, buf); } @@ -551,19 +358,21 @@ write_ip_env (grub_uint32_t *ip, const char *val) { char *buf; grub_err_t err; - grub_uint32_t newip; - - err = parse_ip (val, &newip, 0); + grub_net_network_level_address_t addr; + + err = grub_net_resolve_address (val, &addr); if (err) return 0; + if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) + return NULL; /* Normalize the IP. */ - buf = grub_xasprintf ("%d.%d.%d.%d", (newip & 0xff), (newip >> 8) & 0xff, - (newip >> 16) & 0xff, (newip >> 24) & 0xff); + buf = grub_malloc (GRUB_NET_MAX_STR_ADDR_LEN); if (!buf) return 0; + grub_net_addr_to_str (&addr, buf); - *ip = newip; + *ip = addr.ipv4; return buf; } @@ -576,6 +385,7 @@ grub_env_write_pxe_default_server (struct grub_env_var *var return write_ip_env (&grub_pxe_default_server_ip, val); } +#if 0 static char * grub_env_write_pxe_default_gateway (struct grub_env_var *var __attribute__ ((unused)), @@ -583,6 +393,7 @@ grub_env_write_pxe_default_gateway (struct grub_env_var *var { return write_ip_env (&grub_pxe_default_gateway_ip, val); } +#endif static char * grub_env_write_pxe_blocksize (struct grub_env_var *var __attribute__ ((unused)), @@ -609,42 +420,60 @@ grub_env_write_pxe_blocksize (struct grub_env_var *var __attribute__ ((unused)), return buf; } - GRUB_MOD_INIT(pxe) { - grub_pxe_detect (); - if (grub_pxe_pxenv) - { - char *buf; + struct grub_pxe_bangpxe *pxenv; + struct grub_pxenv_get_cached_info ci; + struct grub_net_bootp_ack *bp; + char *buf; - buf = grub_xasprintf ("%d", grub_pxe_blksize); - if (buf) - grub_env_set ("pxe_blksize", buf); - grub_free (buf); + pxenv = grub_pxe_scan (); + if (! pxenv) + return; - set_ip_env ("pxe_default_server", grub_pxe_default_server_ip); - set_ip_env ("pxe_default_gateway", grub_pxe_default_gateway_ip); - set_ip_env ("net_pxe_ip", grub_pxe_your_ip); - grub_register_variable_hook ("pxe_default_server", 0, - grub_env_write_pxe_default_server); - grub_register_variable_hook ("pxe_default_gateway", 0, - grub_env_write_pxe_default_gateway); - + ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK; + ci.buffer = 0; + ci.buffer_size = 0; + grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci, pxe_rm_entry); + if (ci.status) + return; - /* XXX: Is it possible to change IP in PXE? */ - grub_register_variable_hook ("net_pxe_ip", 0, - grub_env_write_readonly); - grub_register_variable_hook ("pxe_blksize", 0, - grub_env_write_pxe_blocksize); + bp = LINEAR (ci.buffer); - grub_env_export ("pxe_default_server"); - grub_env_export ("pxe_default_gateway"); - grub_env_export ("net_pxe_ip"); - grub_env_export ("pxe_blksize"); + grub_pxe_default_server_ip = bp->server_ip; + grub_pxe_pxenv = pxenv; - grub_disk_dev_register (&grub_pxe_dev); - grub_fs_register (&grub_pxefs_fs); - } + set_ip_env ("pxe_default_server", grub_pxe_default_server_ip); + grub_register_variable_hook ("pxe_default_server", 0, + grub_env_write_pxe_default_server); + +#if 0 + grub_pxe_default_gateway_ip = bp->gateway_ip; + + grub_register_variable_hook ("pxe_default_gateway", 0, + grub_env_write_pxe_default_gateway); +#endif + + buf = grub_xasprintf ("%d", grub_pxe_blksize); + if (buf) + grub_env_set ("pxe_blksize", buf); + grub_free (buf); + + grub_register_variable_hook ("pxe_blksize", 0, + grub_env_write_pxe_blocksize); + + grub_memcpy (grub_pxe_card.default_address.mac, bp->mac_addr, + bp->hw_len < sizeof (grub_pxe_card.default_address.mac) + ? bp->hw_len : sizeof (grub_pxe_card.default_address.mac)); + grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + + grub_fs_register (&grub_pxefs_fs); + grub_net_card_register (&grub_pxe_card); + grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card, + GRUB_NET_INTERFACE_PERMANENT + | GRUB_NET_INTERFACE_ADDRESS_IMMUTABLE + | GRUB_NET_INTERFACE_HWADDRESS_IMMUTABLE, + bp, GRUB_PXE_BOOTP_SIZE); } GRUB_MOD_FINI(pxe) diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c new file mode 100644 index 000000000..776937a6e --- /dev/null +++ b/grub-core/net/ip.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +grub_uint16_t +ipchksum (void *ipv, int len) +{ + grub_uint16_t *ip = (grub_uint16_t *) ipv; + grub_uint32_t sum = 0; + + len >>= 1; + while (len--) + { + sum += grub_be_to_cpu16 (*(ip++)); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + + return grub_cpu_to_be16 ((~sum) & 0x0000FFFF); +} + +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 iphdr *iph; + static int id = 0x2400; + grub_net_link_level_address_t ll_target_addr; + grub_err_t err; + + grub_netbuff_push (nb, sizeof (*iph)); + iph = (struct iphdr *) nb->data; + + iph->verhdrlen = ((4 << 4) | 5); + iph->service = 0; + iph->len = grub_cpu_to_be16 (nb->tail - nb->data); + iph->ident = grub_cpu_to_be16 (++id); + iph->frags = 0; + iph->ttl = 0xff; + iph->protocol = 0x11; + iph->src = inf->address.ipv4; + iph->dest = target->ipv4; + + iph->chksum = 0; + iph->chksum = ipchksum ((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) +{ + struct iphdr *iph = (struct iphdr *) nb->data; + grub_err_t err; + struct grub_net_network_level_interface *inf; + + err = grub_netbuff_pull (nb, sizeof (*iph)); + if (err) + return err; + + 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_PROMISC + && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) + break; + } + if (!inf) + { + if (iph->protocol == IP_UDP + && grub_net_hwaddr_cmp (&card->default_address, hwaddress) == 0) + { + struct udphdr *udph; + udph = (struct udphdr *) nb->data; + err = grub_netbuff_pull (nb, sizeof (*udph)); + if (err) + return err; + if (grub_be_to_cpu16 (udph->dst) == 68) + grub_net_process_dhcp (nb, card); + } + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + + switch (iph->protocol) + { + case IP_UDP: + return grub_net_recv_udp_packet (nb, inf); + break; + default: + grub_netbuff_free (nb); + break; + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/net.c b/grub-core/net/net.c new file mode 100644 index 000000000..5c7d00991 --- /dev/null +++ b/grub-core/net/net.c @@ -0,0 +1,1312 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 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 +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_net_route +{ + struct grub_net_route *next; + grub_net_network_level_netaddress_t target; + char *name; + struct grub_net_network_level_protocol *prot; + int is_gateway; + union + { + struct grub_net_network_level_interface *interface; + grub_net_network_level_address_t gw; + }; +}; + +struct grub_net_route *grub_net_routes = NULL; +struct grub_net_network_level_interface *grub_net_network_level_interfaces = NULL; +struct grub_net_card *grub_net_cards = NULL; +struct grub_net_card_driver *grub_net_card_drivers = NULL; +struct grub_net_network_level_protocol *grub_net_network_level_protocols = NULL; +static struct grub_fs grub_net_fs; + +static inline void +grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) +{ + *inter->prev = inter->next; + if (inter->next) + inter->next->prev = inter->prev; + inter->next = 0; + inter->prev = 0; +} + +static inline void +grub_net_route_register (struct grub_net_route *route) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_net_routes), + GRUB_AS_LIST (route)); +} + +static inline void +grub_net_route_unregister (struct grub_net_route *route) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_net_routes), + GRUB_AS_LIST (route)); +} + +#define FOR_NET_ROUTES(var) for (var = grub_net_routes; var; var = var->next) + +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++) + { + t = grub_strtoul (ptr, (char **) &ptr, 0); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (t & ~0xff) + return 0; + newip >>= 8; + newip |= (t << 24); + if (i != 3 && *ptr != '.') + return 0; + ptr++; + } + *ip = grub_cpu_to_le32 (newip); + if (rest) + *rest = ptr - 1; + return 1; +} + +static int +match_net (const grub_net_network_level_netaddress_t *net, + const grub_net_network_level_address_t *addr) +{ + if (net->type != addr->type) + return 0; + switch (net->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_PROMISC: + return 0; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + { + grub_int32_t mask = ((1 << net->ipv4.masksize) - 1) << (32 - net->ipv4.masksize); + return ((grub_be_to_cpu32 (net->ipv4.base) & mask) + == (grub_be_to_cpu32 (addr->ipv4) & mask)); + } + } + return 0; +} + +grub_err_t +grub_net_resolve_address (const char *name, + grub_net_network_level_address_t *addr) +{ + if (parse_ip (name, &addr->ipv4, NULL)) + { + addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("unrecognised address %s"), + name); +} + +grub_err_t +grub_net_resolve_net_address (const char *name, + grub_net_network_level_netaddress_t *addr) +{ + const char *rest; + if (parse_ip (name, &addr->ipv4.base, &rest)) + { + 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 = 32; + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("unrecognised address %s"), + name); +} + +grub_err_t +grub_net_route_address (grub_net_network_level_address_t addr, + grub_net_network_level_address_t *gateway, + struct grub_net_network_level_interface **interf) +{ + struct grub_net_route *route; + int depth = 0; + int routecnt = 0; + struct grub_net_network_level_protocol *prot = NULL; + grub_net_network_level_address_t curtarget = addr; + + *gateway = addr; + + FOR_NET_ROUTES(route) + routecnt++; + + for (depth = 0; depth < routecnt + 2; depth++) + { + 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 == NULL) + return grub_error (GRUB_ERR_NET_NO_ROUTE, "destination unreachable"); + } + + return grub_error (GRUB_ERR_NET_ROUTE_LOOP, "route loop detected"); +} + +static grub_err_t +grub_cmd_deladdr (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_network_level_interface *inter; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + FOR_NET_NETWORK_LEVEL_INTERFACES (inter) + if (grub_strcmp (inter->name, args[1]) == 0) + break; + if (inter == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("address not found")); + + if (inter->flags & GRUB_NET_INTERFACE_PERMANENT) + return grub_error (GRUB_ERR_IO, + N_("you can't delete this address")); + + grub_net_network_level_interface_unregister (inter); + grub_free (inter->name); + grub_free (inter); + + return GRUB_ERR_NONE; +} + +void +grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf) +{ + switch (target->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_PROMISC: + grub_strcpy (buf, "promisc"); + return; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + { + grub_uint32_t n = grub_be_to_cpu32 (target->ipv4); + grub_snprintf (buf, GRUB_NET_MAX_STR_ADDR_LEN, "%d.%d.%d.%d", + ((n >> 24) & 0xff), ((n >> 16) & 0xff), + ((n >> 8) & 0xff), ((n >> 0) & 0xff)); + } + return; + } + grub_printf ("Unknown address type %d\n", 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) +{ + str[0] = 0; + switch (addr->type) + { + case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET: + { + char *ptr; + unsigned i; + for (ptr = str, i = 0; i < ARRAY_SIZE (addr->mac); i++) + { + grub_snprintf (ptr, MAX_STR_HWADDR_LEN - (ptr - str), + "%02x:", addr->mac[i] & 0xff); + ptr += (sizeof ("XX:") - 1); + } + return; + } + } + grub_printf ("Unsupported hw address type %d\n", addr->type); +} + +int +grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, + const grub_net_link_level_address_t *b) +{ + if (a->type < b->type) + return -1; + if (a->type > b->type) + return +1; + switch (a->type) + { + case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET: + return grub_memcmp (a->mac, b->mac, sizeof (a->mac)); + } + grub_printf ("Unsupported hw address type %d\n", a->type); + return 1; +} + +/* FIXME: implement this. */ +static char * +hwaddr_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return NULL; +} + +/* FIXME: implement this. */ +static char * +addr_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return NULL; +} + +static void +grub_net_network_level_interface_register (struct grub_net_network_level_interface *inter) +{ + { + char buf[MAX_STR_HWADDR_LEN]; + char name[grub_strlen (inter->name) + sizeof ("net__mac")]; + 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); + } + + { + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + char name[grub_strlen (inter->name) + sizeof ("net__ip")]; + grub_net_addr_to_str (&inter->address, buf); + grub_snprintf (name, sizeof (name), "net_%s_ip", inter->name); + grub_env_set (name, buf); + grub_register_variable_hook (name, 0, addr_set_env); + } + + inter->prev = &grub_net_network_level_interfaces; + inter->next = grub_net_network_level_interfaces; + if (inter->next) + inter->next->prev = &inter->next; + grub_net_network_level_interfaces = inter; +} + +struct grub_net_network_level_interface * +grub_net_add_addr (const char *name, + const struct grub_net_card *card, + grub_net_network_level_address_t addr, + grub_net_link_level_address_t hwaddress, + grub_net_interface_flags_t flags) +{ + struct grub_net_network_level_interface *inter; + + inter = grub_zalloc (sizeof (*inter)); + if (!inter) + return NULL; + + inter->name = grub_strdup (name); + 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; + inter->dhcp_acklen = 0; + + grub_net_network_level_interface_register (inter); + + return inter; +} + +/* FIXME: support MAC specifying. */ +static grub_err_t +grub_cmd_addaddr (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_card *card; + grub_net_network_level_address_t addr; + grub_err_t err; + grub_net_interface_flags_t flags = 0; + + if (argc != 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("three arguments expected")); + + FOR_NET_CARDS (card) + if (grub_strcmp (card->name, args[1]) == 0) + break; + if (card == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found")); + + err = grub_net_resolve_address (args[2], &addr); + if (err) + return err; + + if (card->flags & GRUB_NET_CARD_NO_MANUAL_INTERFACES) + return grub_error (GRUB_ERR_IO, + "this card doesn't support address addition"); + + 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, + flags); + return grub_errno; +} + +static grub_err_t +grub_cmd_delroute (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_route *route; + struct grub_net_route **prev; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + for (prev = &grub_net_routes, route = *prev; route; prev = &((*prev)->next), + route = *prev) + if (grub_strcmp (route->name, args[0]) == 0) + { + *prev = route->next; + grub_free (route->name); + grub_free (route); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_net_add_route (const char *name, + grub_net_network_level_netaddress_t target, + struct grub_net_network_level_interface *inter) +{ + struct grub_net_route *route; + + route = grub_zalloc (sizeof (*route)); + if (!route) + return grub_errno; + + route->name = grub_strdup (name); + if (!route->name) + { + grub_free (route); + return grub_errno; + } + + route->target = target; + route->is_gateway = 0; + route->interface = inter; + + grub_net_route_register (route); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_net_add_route_gw (const char *name, + grub_net_network_level_netaddress_t target, + grub_net_network_level_address_t gw) +{ + struct grub_net_route *route; + + route = grub_zalloc (sizeof (*route)); + if (!route) + return grub_errno; + + route->name = grub_strdup (name); + if (!route->name) + { + grub_free (route); + return grub_errno; + } + + route->target = target; + route->is_gateway = 1; + route->gw = gw; + + grub_net_route_register (route); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_addroute (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_net_network_level_netaddress_t target; + if (argc < 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("At least 3 arguments are expected")); + + grub_net_resolve_net_address (args[1], &target); + + if (grub_strcmp (args[2], "gw") == 0 && argc >= 4) + { + grub_err_t err; + grub_net_network_level_address_t gw; + + err = grub_net_resolve_address (args[3], &gw); + if (err) + return err; + return grub_net_add_route_gw (args[0], target, gw); + } + else + { + struct grub_net_network_level_interface *inter; + + FOR_NET_NETWORK_LEVEL_INTERFACES (inter) + if (grub_strcmp (inter->name, args[2]) == 0) + break; + + if (!inter) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unrecognised interface %s"), args[2]); + return grub_net_add_route (args[0], target, inter); + } +} + +static void +print_net_address (const grub_net_network_level_netaddress_t *target) +{ + switch (target->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_PROMISC: + grub_printf ("promisc\n"); + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + { + grub_uint32_t n = grub_be_to_cpu32 (target->ipv4.base); + grub_printf ("%d.%d.%d.%d/%d ", ((n >> 24) & 0xff), + ((n >> 16) & 0xff), + ((n >> 8) & 0xff), + ((n >> 0) & 0xff), + target->ipv4.masksize); + } + return; + } + grub_printf ("Unknown address type %d\n", target->type); +} + +static void +print_address (const grub_net_network_level_address_t *target) +{ + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + grub_net_addr_to_str (target, buf); + grub_xputs (buf); +} + +static grub_err_t +grub_cmd_listroutes (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_net_route *route; + FOR_NET_ROUTES(route) + { + grub_printf ("%s ", route->name); + print_net_address (&route->target); + if (route->is_gateway) + { + grub_printf ("gw "); + print_address (&route->gw); + } + else + grub_printf ("%s", route->interface->name); + grub_printf ("\n"); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_listcards (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_net_card *card; + FOR_NET_CARDS(card) + { + grub_printf ("%s ", card->name); + } + grub_printf ("\n"); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_listaddrs (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_net_network_level_interface *inf; + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + char bufh[MAX_STR_HWADDR_LEN]; + char bufn[GRUB_NET_MAX_STR_ADDR_LEN]; + hwaddr_to_str (&inf->hwaddress, bufh); + grub_net_addr_to_str (&inf->address, bufn); + grub_printf ("%s %s %s\n", inf->name, bufh, bufn); + } + return GRUB_ERR_NONE; +} + +grub_net_app_level_t grub_net_app_level_list; +struct grub_net_socket *grub_net_sockets; + +static grub_net_t +grub_net_open_real (const char *name) +{ + const char *comma = grub_strchr (name, ','); + grub_net_app_level_t proto; + + if (!comma) + comma = name + grub_strlen (name); + FOR_NET_APP_LEVEL (proto) + { + if (comma - name == (grub_ssize_t) grub_strlen (proto->name) + && grub_memcmp (proto->name, name, comma - name) == 0) + { + grub_net_t ret = grub_malloc (sizeof (*ret)); + if (!ret) + return NULL; + ret->protocol = proto; + ret->name = grub_strdup (name); + if (!ret->name) + { + grub_free (ret); + return NULL; + } + ret->fs = &grub_net_fs; + return ret; + } + } + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); + + return NULL; +} + +static grub_err_t +grub_net_fs_dir (grub_device_t device, const char *path __attribute__ ((unused)), + int (*hook) (const char *filename, + const struct grub_dirhook_info *info) __attribute__ ((unused))) +{ + if (!device->net) + return grub_error (GRUB_ERR_BAD_FS, "invalid extent"); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_net_fs_open (struct grub_file *file, const char *name) +{ + 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_socket_t socket; + static int port = 25300; + const char *comma; + + comma = grub_strchr (file->device->net->name, ','); + if (!comma) + return grub_error (GRUB_ERR_NET_BAD_ADDRESS, "no separator"); + + err = grub_net_resolve_address (comma + 1, &addr); + if (err) + return err; + + err = grub_net_route_address (addr, &gateway, &inf); + if (err) + return err; + + socket = (grub_net_socket_t) grub_malloc (sizeof (*socket)); + if (socket == NULL) + return grub_errno; + + socket->inf = inf; + socket->out_nla = addr; + socket->in_port = port++; + socket->status = 0; + socket->app = file->device->net->protocol; + socket->packs.first = NULL; + socket->packs.last = NULL; + file->device->net->socket = socket; + grub_net_socket_register (socket); + + err = file->device->net->protocol->open (file,name); + if (err) + goto fail; + file->not_easily_seekable = 1; + + return GRUB_ERR_NONE; +fail: + grub_net_socket_unregister (socket); + grub_free (socket); + return err; + +} + +static grub_err_t +grub_net_fs_close (grub_file_t file) +{ + grub_net_socket_t sock = file->device->net->socket; + while (sock->packs.first) + { + grub_netbuff_free (sock->packs.first->nb); + grub_net_remove_packet (sock->packs.first); + } + grub_net_socket_unregister (sock); + grub_free (sock); + return GRUB_ERR_NONE; + +} + +static void +receive_packets (struct grub_net_card *card) +{ + while (1) + { + /* 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); + if (!nb) + { + grub_print_error (); + return; + } + + actual = card->driver->recv (card, nb); + if (actual < 0) + { + grub_netbuff_free (nb); + break; + } + grub_net_recv_ethernet_packet (nb, card); + } + grub_print_error (); +} + +void +grub_net_poll_cards (unsigned time) +{ + struct grub_net_card *card; + grub_uint64_t start_time; + FOR_NET_CARDS (card) + { + start_time = grub_get_time_ms (); + while ((grub_get_time_ms () - start_time) < time) + receive_packets (card); + } +} + +/* Read from the packets list*/ +static grub_ssize_t +grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_net_socket_t sock = file->device->net->socket; + struct grub_net_buff *nb; + char *ptr = buf; + grub_size_t amount, total = 0; + int try = 0; + while (try <= 3) + { + while (sock->packs.first) + { + try = 0; + nb = sock->packs.first->nb; + amount = nb->tail - nb->data; + if (amount > len) + amount = len; + len -= amount; + total += amount; + if (buf) + { + grub_memcpy (ptr, nb->data, amount); + ptr += amount; + } + if (amount == (grub_size_t) (nb->tail - nb->data)) + { + grub_netbuff_free (nb); + grub_net_remove_packet (sock->packs.first); + } + else + nb->data += amount; + + if (!len) + return total; + } + if (sock->status == 1) + { + try++; + grub_net_poll_cards (200); + } + else + return total; + } + return total; +} + +/* Read from the packets list*/ +static grub_err_t +grub_net_seek_real (struct grub_file *file, grub_off_t offset) +{ + grub_net_socket_t sock = file->device->net->socket; + struct grub_net_buff *nb; + grub_size_t len = offset - file->offset; + + if (!len) + return GRUB_ERR_NONE; + + /* We cant seek backwards past the current packet. */ + if (file->offset > offset) + { + nb = sock->packs.first->nb; + return grub_netbuff_push (nb, file->offset - offset); + } + + grub_net_fs_read (file, NULL, len); + return GRUB_ERR_NONE; +} + +static char * +grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return NULL; +} + +static void +set_env_limn_ro (const char *intername, const char *suffix, + char *value, grub_size_t len) +{ + char c; + char varname[sizeof ("net_") + grub_strlen (intername) + sizeof ("_") + + grub_strlen (suffix)]; + grub_snprintf (varname, sizeof (varname), "net_%s_%s", intername, suffix); + c = value[len]; + value[len] = 0; + grub_env_set (varname, value); + value[len] = c; + grub_register_variable_hook (varname, 0, grub_env_write_readonly); +} + +static void +parse_dhcp_vendor (const char *name, void *vend, int limit) +{ + grub_uint8_t *ptr, *ptr0; + + ptr = ptr0 = vend; + + if (grub_be_to_cpu32 (*(grub_uint32_t *) ptr) != GRUB_NET_BOOTP_RFC1048_MAGIC) + return; + ptr = ptr + sizeof (grub_uint32_t); + while (ptr - ptr0 < limit) + { + grub_uint8_t tagtype; + grub_uint8_t taglength; + + tagtype = *ptr++; + + /* Pad tag. */ + if (tagtype == 0) + continue; + + /* End tag. */ + if (tagtype == 0xff) + return; + + taglength = *ptr++; + + switch (tagtype) + { + case 12: + set_env_limn_ro (name, "hostname", (char *) ptr, taglength); + break; + + case 15: + set_env_limn_ro (name, "domain", (char *) ptr, taglength); + break; + + case 17: + set_env_limn_ro (name, "rootpath", (char *) ptr, taglength); + break; + + case 18: + set_env_limn_ro (name, "extensionspath", (char *) ptr, taglength); + break; + + /* If you need any other options please contact GRUB + developpement team. */ + } + + ptr += taglength; + } +} + +#define OFFSET_OF(x, y) ((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)) + +struct grub_net_network_level_interface * +grub_net_configure_by_dhcp_ack (const char *name, + const struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_bootp_packet *bp, + grub_size_t size) +{ + grub_net_network_level_address_t addr; + grub_net_link_level_address_t hwaddr; + struct grub_net_network_level_interface *inter; + + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + addr.ipv4 = bp->your_ip; + + grub_memcpy (hwaddr.mac, bp->mac_addr, + bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len + : sizeof (hwaddr.mac)); + hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + + inter = grub_net_add_addr (name, card, addr, hwaddr, flags); + if (bp->gateway_ip != bp->server_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); + } + + if (size > OFFSET_OF (boot_file, bp)) + set_env_limn_ro (name, "boot_file", (char *) bp->boot_file, + sizeof (bp->boot_file)); + if (size > OFFSET_OF (server_name, bp)) + set_env_limn_ro (name, "dhcp_server_name", (char *) bp->server_name, + sizeof (bp->server_name)); + if (size > OFFSET_OF (vendor, bp)) + parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp)); + + inter->dhcp_ack = grub_malloc (size); + if (inter->dhcp_ack) + { + grub_memcpy (inter->dhcp_ack, bp, size); + inter->dhcp_acklen = size; + } + else + grub_errno = GRUB_ERR_NONE; + + return inter; +} + +void +grub_net_process_dhcp (struct grub_net_buff *nb, + const struct grub_net_card *card) +{ + char *name; + struct grub_net_network_level_interface *inf; + + name = grub_xasprintf ("%s:dhcp", card->name); + if (!name) + { + grub_print_error (); + return; + } + grub_net_configure_by_dhcp_ack (name, card, + 0, (const struct grub_net_bootp_packet *) nb->data, + (nb->tail - nb->data)); + grub_free (name); + if (grub_errno) + grub_print_error (); + else + { + FOR_NET_NETWORK_LEVEL_INTERFACES(inf) + if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0 + && grub_memcmp (inf->name + grub_strlen (card->name), + ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0) + { + grub_net_network_level_interface_unregister (inf); + break; + } + } +} + +static char +hexdigit (grub_uint8_t val) +{ + if (val < 10) + return val + '0'; + return val + 'a' - 10; +} + +static grub_err_t +grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_network_level_interface *inter; + int num; + grub_uint8_t *ptr; + grub_uint8_t taglength; + + if (argc < 4) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "4 arguments expected"); + + FOR_NET_NETWORK_LEVEL_INTERFACES (inter) + if (grub_strcmp (inter->name, args[1]) == 0) + break; + + if (!inter) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unrecognised interface %s"), args[1]); + + if (!inter->dhcp_ack) + return grub_error (GRUB_ERR_IO, N_("no DHCP info found")); + + if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack)) + return grub_error (GRUB_ERR_IO, N_("no DHCP options found")); + + num = grub_strtoul (args[2], 0, 0); + if (grub_errno) + return grub_errno; + + ptr = inter->dhcp_ack->vendor; + + if (grub_be_to_cpu32 (*(grub_uint32_t *) ptr) + != GRUB_NET_BOOTP_RFC1048_MAGIC) + return grub_error (GRUB_ERR_IO, N_("no DHCP options found")); + ptr = ptr + sizeof (grub_uint32_t); + while (1) + { + grub_uint8_t tagtype; + + if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen) + return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num); + + tagtype = *ptr++; + + /* Pad tag. */ + if (tagtype == 0) + continue; + + /* End tag. */ + if (tagtype == 0xff) + return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num); + + taglength = *ptr++; + + if (tagtype == num) + break; + ptr += taglength; + } + + if (grub_strcmp (args[3], "string") == 0) + { + char *val = grub_malloc (taglength + 1); + if (!val) + return grub_errno; + grub_memcpy (val, ptr, taglength); + val[taglength] = 0; + if (args[0][0] == '-' && args[0][1] == 0) + grub_printf ("%s\n", val); + else + return grub_env_set (args[0], val); + return GRUB_ERR_NONE; + } + + if (grub_strcmp (args[3], "number") == 0) + { + grub_uint64_t val = 0; + int i; + for (i = 0; i < taglength; i++) + val = (val << 8) | ptr[i]; + if (args[0][0] == '-' && args[0][1] == 0) + grub_printf ("%llu\n", (unsigned long long) val); + else + { + char valn[64]; + grub_printf (valn, sizeof (valn), "%lld\n", (unsigned long long) val); + return grub_env_set (args[0], valn); + } + return GRUB_ERR_NONE; + } + + if (grub_strcmp (args[3], "hex") == 0) + { + char *val = grub_malloc (2 * taglength + 1); + int i; + if (!val) + return grub_errno; + for (i = 0; i < taglength; i++) + { + val[2 * i] = hexdigit (ptr[i] >> 4); + val[2 * i + 1] = hexdigit (ptr[i] & 0xf); + } + val[2 * taglength] = 0; + if (args[0][0] == '-' && args[0][1] == 0) + grub_printf ("%s\n", val); + else + return grub_env_set (args[0], val); + return GRUB_ERR_NONE; + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "unrecognised format specification %s", args[3]); +} + +static grub_err_t +grub_cmd_bootp (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; + + FOR_NET_CARDS (card) + { + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + ncards++; + } + + ifaces = grub_zalloc (ncards * sizeof (ifaces[0])); + if (!ifaces) + return grub_errno; + + j = 0; + FOR_NET_CARDS (card) + { + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + ifaces[j].card = card; + ifaces[j].next = &ifaces[j+1]; + if (j) + ifaces[j].prev = &ifaces[j-1].next; + ifaces[j].name = grub_xasprintf ("%s:dhcp_tmp", card->name); + if (!ifaces[j].name) + { + unsigned i; + for (i = 0; i < j; i++) + grub_free (ifaces[i].name); + grub_free (ifaces); + return grub_errno; + } + ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_PROMISC; + grub_memcpy (&ifaces[j].hwaddress, &card->default_address, + sizeof (ifaces[j].hwaddress)); + j++; + } + ifaces[ncards - 1].next = grub_net_network_level_interfaces; + if (grub_net_network_level_interfaces) + grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next; + grub_net_network_level_interfaces = &ifaces[0]; + ifaces[0].prev = &grub_net_network_level_interfaces; + for (interval = 200; interval < 10000; interval *= 2) + { + int done = 0; + for (j = 0; j < ncards; j++) + { + struct grub_net_bootp_packet *pack; + struct grub_datetime date; + grub_int32_t t; + struct grub_net_buff *nb; + struct udphdr *udph; + grub_net_network_level_address_t target; + + if (!ifaces[j].prev) + continue; + nb = grub_netbuff_alloc (sizeof (*pack)); + if (!nb) + return grub_errno; + err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128); + if (err) + return err; + err = grub_netbuff_push (nb, sizeof (*pack) + 64); + if (err) + return err; + pack = (void *) nb->data; + done = 1; + grub_memset (pack, 0, sizeof (*pack) + 64); + pack->opcode = 1; + pack->hw_type = 1; + pack->hw_len = 6; + err = grub_get_datetime (&date); + if (err || !grub_datetime2unixtime (&date, &t)) + { + grub_errno = GRUB_ERR_NONE; + t = 0; + } + pack->ident = grub_cpu_to_be32 (t); + pack->seconds = 0;//grub_cpu_to_be16 (t); + + grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6); + + grub_netbuff_push (nb, sizeof (*udph)); + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16 (68); + 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; + + err = grub_net_send_ip_packet (&ifaces[j], &target, nb); + if (err) + return err; + } + if (!done) + break; + grub_net_poll_cards (interval); + } + + err = GRUB_ERR_NONE; + for (j = 0; j < ncards; j++) + { + if (!ifaces[j].prev) + continue; + grub_error_push (); + grub_net_network_level_interface_unregister (&ifaces[j]); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "couldn't configure %s", + ifaces[j].card->name); + } + + return err; +} + +static void +grub_grubnet_fini_real (void) +{ + struct grub_net_card *card; + + FOR_NET_CARDS (card) + if (card->driver) + card->driver->fini (card); +} + +static struct grub_fs grub_net_fs = + { + .name = "netfs", + .dir = grub_net_fs_dir, + .open = grub_net_fs_open, + .read = grub_net_fs_read, + .close = grub_net_fs_close, + .label = NULL, + .uuid = NULL, + .mtime = NULL, + }; +static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute; +static grub_command_t cmd_lsroutes, cmd_lscards, cmd_getdhcp, cmd_bootp; +static grub_command_t cmd_dhcp, cmd_lsaddr; + +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_deladdr = grub_register_command ("net_del_addr", grub_cmd_deladdr, + "SHORTNAME", + N_("Delete a network address.")); + cmd_addroute = grub_register_command ("net_add_route", grub_cmd_addroute, + "SHORTNAME NET [INTERFACE| gw GATEWAY]", + N_("Add a network route.")); + cmd_delroute = grub_register_command ("net_del_route", grub_cmd_delroute, + "SHORTNAME", + N_("Delete a network route.")); + cmd_lsroutes = grub_register_command ("net_ls_routes", grub_cmd_listroutes, + "", N_("list network routes")); + cmd_lscards = grub_register_command ("net_ls_cards", grub_cmd_listcards, + "", N_("list network cards")); + cmd_lsaddr = grub_register_command ("net_ls_addr", grub_cmd_listaddrs, + "", N_("list network addresses")); + 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.")); + + grub_fs_register (&grub_net_fs); + grub_net_open = grub_net_open_real; + grub_file_net_seek = grub_net_seek_real; + grub_grubnet_fini = grub_grubnet_fini_real; +} + +GRUB_MOD_FINI(net) +{ + grub_unregister_command (cmd_addaddr); + grub_unregister_command (cmd_deladdr); + grub_unregister_command (cmd_addroute); + grub_unregister_command (cmd_delroute); + grub_unregister_command (cmd_lsroutes); + grub_unregister_command (cmd_lscards); + grub_unregister_command (cmd_lsaddr); + grub_unregister_command (cmd_getdhcp); + grub_fs_unregister (&grub_net_fs); + grub_net_open = NULL; + grub_file_net_seek = NULL; + grub_grubnet_fini = NULL; +} diff --git a/grub-core/net/netbuff.c b/grub-core/net/netbuff.c new file mode 100644 index 000000000..d20104ab0 --- /dev/null +++ b/grub-core/net/netbuff.c @@ -0,0 +1,106 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 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 + + +grub_err_t +grub_netbuff_put (struct grub_net_buff *nb, grub_size_t len) +{ + nb->tail += len; + if (nb->tail > nb->end) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "put out of the packet range."); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_netbuff_unput (struct grub_net_buff *nb, grub_size_t len) +{ + nb->tail -= len; + if (nb->tail < nb->head) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "unput out of the packet range."); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_netbuff_push (struct grub_net_buff *nb, grub_size_t len) +{ + nb->data -= len; + if (nb->data < nb->head) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "push out of the packet range."); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_netbuff_pull (struct grub_net_buff *nb, grub_size_t len) +{ + nb->data += len; + if (nb->data > nb->end) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "pull out of the packet range."); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_netbuff_reserve (struct grub_net_buff *nb, grub_size_t len) +{ + nb->data += len; + nb->tail += len; + if ((nb->tail > nb->end) || (nb->data > nb->end)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "reserve out of the packet range."); + return GRUB_ERR_NONE; +} + +struct grub_net_buff * +grub_netbuff_alloc (grub_size_t len) +{ + struct grub_net_buff *nb; + void *data; + + if (len < NETBUFFMINLEN) + len = NETBUFFMINLEN; + + len = ALIGN_UP (len, NETBUFF_ALIGN); + data = grub_memalign (NETBUFF_ALIGN, len + sizeof (*nb)); + if (!data) + return NULL; + nb = (struct grub_net_buff *) ((grub_uint8_t *) data + len); + nb->head = nb->data = nb->tail = data; + nb->end = (char *) nb; + return nb; +} + +grub_err_t +grub_netbuff_free (struct grub_net_buff *nb) +{ + grub_free (nb->head); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_netbuff_clear (struct grub_net_buff *nb) +{ + nb->data = nb->tail = nb->head; + return GRUB_ERR_NONE; +} diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c new file mode 100644 index 000000000..d0ed7c43a --- /dev/null +++ b/grub-core/net/tftp.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +tftp_open (struct grub_file *file, const char *filename) +{ + struct tftphdr *tftph; + char *rrq; + int i; + int rrqlen; + int hdrlen; + char open_data[1500]; + struct grub_net_buff nb; + tftp_data_t data; + grub_err_t err; + + data = grub_malloc (sizeof (*data)); + if (!data) + return grub_errno; + + file->device->net->socket->data = (void *) data; + nb.head = open_data; + nb.end = open_data + sizeof (open_data); + grub_netbuff_clear (&nb); + + grub_netbuff_reserve (&nb, 1500); + if ((err = grub_netbuff_push (&nb, sizeof (*tftph))) != GRUB_ERR_NONE) + return err; + + tftph = (struct tftphdr *) nb.data; + + rrq = (char *) tftph->u.rrq; + rrqlen = 0; + + tftph->opcode = grub_cpu_to_be16 (TFTP_RRQ); + grub_strcpy (rrq, filename); + rrqlen += grub_strlen (filename) + 1; + rrq += grub_strlen (filename) + 1; + + grub_strcpy (rrq, "octet"); + rrqlen += grub_strlen ("octet") + 1; + rrq += grub_strlen ("octet") + 1; + + grub_strcpy (rrq, "blksize"); + rrqlen += grub_strlen ("blksize") + 1; + rrq += grub_strlen ("blksize") + 1; + + grub_strcpy (rrq, "1024"); + rrqlen += grub_strlen ("1024") + 1; + rrq += grub_strlen ("1024") + 1; + + grub_strcpy (rrq, "tsize"); + rrqlen += grub_strlen ("tsize") + 1; + rrq += grub_strlen ("tsize") + 1; + + grub_strcpy (rrq, "0"); + rrqlen += grub_strlen ("0") + 1; + rrq += grub_strlen ("0") + 1; + hdrlen = sizeof (tftph->opcode) + rrqlen; + + if ((err = grub_netbuff_unput (&nb, nb.tail - (nb.data + hdrlen))) != GRUB_ERR_NONE) + return err; + file->device->net->socket->out_port = TFTP_SERVER_PORT; + + err = grub_net_send_udp_packet (file->device->net->socket, &nb); + if (err) + return err; + + /* Receive OACK packet. */ + for (i = 0; i < 3; i++) + { + grub_net_poll_cards (100); + if (grub_errno) + return grub_errno; + if (file->device->net->socket->status != 0) + break; + /* Retry. */ + /*err = grub_net_send_udp_packet (file->device->net->socket, &nb); + if (err) + return err; */ + } + + if (file->device->net->socket->status == 0) + return grub_error (GRUB_ERR_TIMEOUT, "Time out opening tftp."); + file->size = data->file_size; + + return GRUB_ERR_NONE; +} + +static grub_err_t +tftp_receive (grub_net_socket_t sock, struct grub_net_buff *nb) +{ + struct tftphdr *tftph; + char nbdata[128]; + tftp_data_t data = sock->data; + grub_err_t err; + char *ptr; + struct grub_net_buff nb_ack; + + nb_ack.head = nbdata; + nb_ack.end = nbdata + sizeof (nbdata); + + + tftph = (struct tftphdr *) nb->data; + switch (grub_be_to_cpu16 (tftph->opcode)) + { + case TFTP_OACK: + 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); + } + while (ptr < nb->tail && *ptr) + ptr++; + ptr++; + } + sock->status = 1; + data->block = 0; + grub_netbuff_clear (nb); + break; + case TFTP_DATA: + if ((err = grub_netbuff_pull (nb, sizeof (tftph->opcode) + + sizeof (tftph->u.data.block))) != GRUB_ERR_NONE) + return err; + if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1) + { + data->block++; + unsigned size = nb->tail - nb->data; + if (size < 1024) + sock->status = 2; + /* Prevent garbage in broken cards. */ + if (size > 1024) + if ((err = grub_netbuff_unput (nb, size - 1024)) != GRUB_ERR_NONE) + return err; + } + else + grub_netbuff_clear (nb); + + break; + case TFTP_ERROR: + grub_netbuff_clear (nb); + return grub_error (GRUB_ERR_IO, (char *) tftph->u.err.errmsg); + break; + } + grub_netbuff_clear (&nb_ack); + grub_netbuff_reserve (&nb_ack, 128); + 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); + + err = grub_net_send_udp_packet (sock, &nb_ack); + return err; +} + +static grub_err_t +tftp_close (struct grub_file *file __attribute__ ((unused))) +{ + grub_free (file->device->net->socket->data); + return GRUB_ERR_NONE; +} + +static struct grub_net_app_protocol grub_tftp_protocol = + { + .name = "tftp", + .open = tftp_open, + .read = tftp_receive, + .close = tftp_close + }; + +GRUB_MOD_INIT (tftp) +{ + grub_net_app_level_register (&grub_tftp_protocol); +} + +GRUB_MOD_FINI (tftp) +{ + grub_net_app_level_unregister (&grub_tftp_protocol); +} diff --git a/grub-core/net/udp.c b/grub-core/net/udp.c new file mode 100644 index 000000000..95134c0cf --- /dev/null +++ b/grub-core/net/udp.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +grub_err_t +grub_net_send_udp_packet (const grub_net_socket_t socket, + struct grub_net_buff *nb) +{ + struct udphdr *udph; + grub_err_t err; + + err = grub_netbuff_push (nb, sizeof (*udph)); + if (err) + return err; + + udph = (struct udphdr *) nb->data; + 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->inf, &(socket->out_nla), nb); +} + +grub_err_t +grub_net_recv_udp_packet (struct grub_net_buff * nb, + struct grub_net_network_level_interface * inf) +{ + struct udphdr *udph; + grub_net_socket_t sock; + udph = (struct udphdr *) nb->data; + grub_netbuff_pull (nb, sizeof (*udph)); + + FOR_NET_SOCKETS (sock) + { + if (grub_be_to_cpu16 (udph->dst) == sock->in_port + && inf == sock->inf && sock->app) + { + if (sock->status == 0) + sock->out_port = grub_be_to_cpu16 (udph->src); + + /* App protocol remove its own reader. */ + sock->app->read (sock, nb); + + /* If there is data, puts packet in socket list. */ + if ((nb->tail - nb->data) > 0) + grub_net_put_packet (&sock->packs, nb); + else + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + } + if (grub_be_to_cpu16 (udph->dst) == 68) + grub_net_process_dhcp (nb, inf->card); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} diff --git a/include/grub/device.h b/include/grub/device.h index f0e8a8ca8..f3e43bf60 100644 --- a/include/grub/device.h +++ b/include/grub/device.h @@ -25,7 +25,6 @@ struct grub_disk; struct grub_net; -struct grub_fs; struct grub_device { diff --git a/include/grub/emu/export.h b/include/grub/emu/export.h new file mode 100644 index 000000000..1e2f0432b --- /dev/null +++ b/include/grub/emu/export.h @@ -0,0 +1,6 @@ +void EXPORT_FUNC (open64) (void); +void EXPORT_FUNC (close) (void); +void EXPORT_FUNC (read) (void); +void EXPORT_FUNC (write) (void); +void EXPORT_FUNC (ioctl) (void); + diff --git a/include/grub/err.h b/include/grub/err.h index 69bc6ec79..40f39dd5d 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -56,6 +56,10 @@ typedef enum GRUB_ERR_IO, GRUB_ERR_ACCESS_DENIED, GRUB_ERR_EXTRACTOR, + GRUB_ERR_NET_BAD_ADDRESS, + GRUB_ERR_NET_ROUTE_LOOP, + GRUB_ERR_NET_NO_ROUTE, + GRUB_ERR_WAIT, GRUB_ERR_BUG } grub_err_t; diff --git a/include/grub/i386/pc/pxe.h b/include/grub/i386/pc/pxe.h index 62ece21b0..781b53df5 100644 --- a/include/grub/i386/pc/pxe.h +++ b/include/grub/i386/pc/pxe.h @@ -152,9 +152,9 @@ #define GRUB_PXE_BOOTP_BCAST 0x8000 #if 1 -#define GRUB_PXE_BOOTP_DHCPVEND 1024 /* DHCP extended vendor field size. */ +#define GRUB_PXE_BOOTP_SIZE (1024 + 236) /* DHCP extended vendor field size. */ #else -#define GRUB_PXE_BOOTP_DHCPVEND 312 /* DHCP standard vendor field size. */ +#define GRUB_PXE_BOOTP_SIZE (312 + 236) /* DHCP standard vendor field size. */ #endif #define GRUB_PXE_MIN_BLKSIZE 512 @@ -162,8 +162,6 @@ #define GRUB_PXE_TFTP_PORT 69 -#define GRUB_PXE_VM_RFC1048 0x63825363L - #define GRUB_PXE_ERR_LEN 0xFFFFFFFF #ifndef ASM_FILE @@ -214,38 +212,6 @@ struct grub_pxenv_get_cached_info grub_uint16_t buffer_limit; } __attribute__ ((packed)); -#define GRUB_PXE_MAC_ADDR_LEN 16 - -typedef grub_uint8_t grub_pxe_mac_addr_t[GRUB_PXE_MAC_ADDR_LEN]; - -struct grub_pxenv_boot_player -{ - grub_uint8_t opcode; - grub_uint8_t hw_type; /* hardware type. */ - grub_uint8_t hw_len; /* hardware addr len. */ - grub_uint8_t gate_hops; /* zero it. */ - grub_uint32_t ident; /* random number chosen by client. */ - grub_uint16_t seconds; /* seconds since did initial bootstrap. */ - grub_uint16_t flags; - grub_uint32_t client_ip; - grub_uint32_t your_ip; - grub_uint32_t server_ip; - grub_uint32_t gateway_ip; - grub_pxe_mac_addr_t mac_addr; - grub_uint8_t server_name[64]; - grub_uint8_t boot_file[128]; - union - { - grub_uint8_t d[GRUB_PXE_BOOTP_DHCPVEND]; /* raw array of vendor/dhcp options. */ - struct - { - grub_uint32_t magic; /* DHCP magic cookie. */ - grub_uint32_t flags; /* bootp flags/opcodes. */ - grub_uint8_t padding[56]; - } v; - } vendor; -} __attribute__ ((packed)); - struct grub_pxenv_tftp_open { grub_uint16_t status; diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 4c56cc20f..87294610d 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -24,7 +24,6 @@ #include #include -/* Maps a device alias to a pathname. */ struct grub_ieee1275_devalias { char *name; @@ -65,6 +64,14 @@ struct grub_ieee1275_common_hdr typedef grub_uint32_t grub_ieee1275_ihandle_t; typedef grub_uint32_t grub_ieee1275_phandle_t; +struct grub_ofnetcard_data +{ + char *path; + grub_ieee1275_ihandle_t handle; + grub_uint32_t mtu; +}; + +/* Maps a device alias to a pathname. */ extern grub_ieee1275_phandle_t EXPORT_VAR(grub_ieee1275_chosen); extern grub_ieee1275_ihandle_t EXPORT_VAR(grub_ieee1275_mmu); extern int (* EXPORT_VAR(grub_ieee1275_entry_fn)) (void *); @@ -191,11 +198,10 @@ EXPORT_FUNC(grub_ieee1275_map) (grub_addr_t phys, grub_addr_t virt, char *EXPORT_FUNC(grub_ieee1275_encode_devname) (const char *path); char *EXPORT_FUNC(grub_ieee1275_get_filename) (const char *path); - int EXPORT_FUNC(grub_ieee1275_devices_iterate) (int (*hook) (struct grub_ieee1275_devalias * alias)); - +char *EXPORT_FUNC(grub_ieee1275_get_aliasdevname) (const char *path); char *EXPORT_FUNC(grub_ieee1275_canonicalise_devname) (const char *path); #endif /* ! GRUB_IEEE1275_HEADER */ diff --git a/include/grub/ieee1275/ofnet.h b/include/grub/ieee1275/ofnet.h new file mode 100644 index 000000000..c7284df38 --- /dev/null +++ b/include/grub/ieee1275/ofnet.h @@ -0,0 +1,66 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2007 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_OFNET_HEADER +#define GRUB_OFNET_HEADER 1 + +#include +#include +#include + +struct grub_ofnet +{ + /* The net name. */ + const char *name; + + /* The OF device string. */ + char *dev; + /*server ip*/ + char *sip; + /*client ip*/ + char *cip; + /*gateway*/ + char *gat; + /**/ + int type; +}; + +typedef struct grub_ofnet *grub_ofnet_t; + +struct grub_bootp { + grub_uint8_t op; /* 1 = BOOTREQUEST, 2 = BOOTREPLY */ + grub_uint8_t htype; /* Hardware address type. */ + grub_uint8_t hlen; /* Hardware address length */ + grub_uint8_t hops; /* Used by gateways in cross-gateway booting. */ + grub_uint32_t xid; /* Transaction ID */ + grub_uint16_t secs; /* Seconds elapsed. */ + grub_uint16_t unused; /* Unused. */ + grub_uint32_t ciaddr; /* Client IP address, */ + grub_uint32_t yiaddr; /* Client IP address filled by server. */ + grub_uint32_t siaddr; /* Server IP address. */ + grub_uint32_t giaddr; /* Gateway IP address. */ + unsigned char chaddr [16]; /* Client hardware address */ + char sname [64]; /* Server name */ + char file [128]; /* Boot filename */ + unsigned char vend [64]; +}; + +typedef struct grub_bootp* grub_bootp_t; + +extern grub_bootp_t (*EXPORT_VAR (grub_getbootp)) (void); +#endif /* ! GRUB_NET_HEADER */ diff --git a/include/grub/net.h b/include/grub/net.h index c6d71d5b6..068ccec41 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2007 Free Software Foundation, Inc. + * Copyright (C) 2010 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 @@ -19,54 +19,437 @@ #ifndef GRUB_NET_HEADER #define GRUB_NET_HEADER 1 -#include -#include #include +#include +#include +#include +#include +#include +#include -struct grub_net; - -struct grub_net_dev +typedef enum grub_link_level_protocol_id { - /* The device name. */ - const char *name; + GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET +} grub_link_level_protocol_id_t; - /* FIXME: Just a template. */ - int (*probe) (struct grub_net *net, const void *addr); - void (*reset) (struct grub_net *net); - int (*poll) (struct grub_net *net); - void (*transmit) (struct grub_net *net, const void *destip, - unsigned srcsock, unsigned destsock, const void *packet); - void (*disable) (struct grub_net *net); +typedef struct grub_net_link_level_address +{ + grub_link_level_protocol_id_t type; + union + { + grub_uint8_t mac[6]; + }; +} grub_net_link_level_address_t; - /* The next net device. */ - struct grub_net_dev *next; +typedef enum grub_net_interface_flags + { + GRUB_NET_INTERFACE_HWADDRESS_IMMUTABLE = 1, + GRUB_NET_INTERFACE_ADDRESS_IMMUTABLE = 2, + GRUB_NET_INTERFACE_PERMANENT = 4 + } grub_net_interface_flags_t; + +typedef enum grub_net_card_flags + { + GRUB_NET_CARD_HWADDRESS_IMMUTABLE = 1, + GRUB_NET_CARD_NO_MANUAL_INTERFACES = 2 + } grub_net_card_flags_t; + +struct grub_net_card; + +struct grub_net_card_driver +{ + struct grub_net_card_driver *next; + char *name; + grub_err_t (*init) (struct grub_net_card *dev); + grub_err_t (*fini) (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); }; -typedef struct grub_net_dev *grub_net_dev_t; -struct grub_fs; +extern struct grub_net_card_driver *grub_net_card_drivers; -struct grub_net +static inline void +grub_net_card_driver_register (struct grub_net_card_driver *driver) { - /* The net name. */ - const char *name; + grub_list_push (GRUB_AS_LIST_P (&grub_net_card_drivers), + GRUB_AS_LIST (driver)); +} - /* The underlying disk device. */ - grub_net_dev_t dev; +static inline void +grub_net_card_driver_unregister (struct grub_net_card_driver *driver) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_net_card_drivers), + GRUB_AS_LIST (driver)); +} - /* The binding filesystem. */ - struct grub_fs *fs; +#define FOR_NET_CARD_DRIVERS(var) for (var = grub_net_card_drivers; var; var = var->next) - /* FIXME: More data would be required, such as an IP address, a mask, - a gateway, etc. */ +typedef struct grub_net_packet +{ + struct grub_net_packet *next; + struct grub_net_packet *prev; + struct grub_net_packets *up; + struct grub_net_buff *nb; +} grub_net_packet_t; - /* Device-specific data. */ +typedef struct grub_net_packets +{ + grub_net_packet_t *first; + grub_net_packet_t *last; +} grub_net_packets_t; + +struct grub_net_card +{ + struct grub_net_card *next; + char *name; + struct grub_net_card_driver *driver; + grub_net_link_level_address_t default_address; + grub_net_card_flags_t flags; + union + { + void *data; + int data_num; + }; +}; + +struct grub_net_network_level_interface; + +typedef enum grub_network_level_protocol_id +{ + GRUB_NET_NETWORK_LEVEL_PROTOCOL_PROMISC, + GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 +} grub_network_level_protocol_id_t; + +typedef struct grub_net_network_level_address +{ + grub_network_level_protocol_id_t type; + union + { + grub_uint32_t ipv4; + }; +} grub_net_network_level_address_t; + +typedef struct grub_net_network_level_netaddress +{ + grub_network_level_protocol_id_t type; + union + { + struct { + grub_uint32_t base; + int masksize; + } ipv4; + }; +} grub_net_network_level_netaddress_t; + +#define FOR_PACKETS(cont,var) for (var = (cont).first; var; var = var->next) + +static inline grub_err_t +grub_net_put_packet (grub_net_packets_t *pkts, struct grub_net_buff *nb) +{ + struct grub_net_packet *n; + + n = grub_malloc (sizeof (*n)); + if (!n) + return grub_errno; + + n->nb = nb; + n->next = NULL; + n->prev = NULL; + n->up = pkts; + if (pkts->first) + { + pkts->last->next = n; + pkts->last = n; + n->prev = pkts->last; + } + else + pkts->first = pkts->last = n; + + return GRUB_ERR_NONE; +} + +static inline void +grub_net_remove_packet (grub_net_packet_t *pkt) +{ + if (pkt->prev) + pkt->prev->next = pkt->next; + else + pkt->up->first = pkt->next; + if (pkt->next) + pkt->next->prev = pkt->prev; + else + pkt->up->last = pkt->prev; + grub_free (pkt); +} + +typedef struct grub_net_app_protocol *grub_net_app_level_t; + +typedef struct grub_net_socket *grub_net_socket_t; + +struct grub_net_app_protocol +{ + struct grub_net_app_protocol *next; + char *name; + grub_err_t (*dir) (grub_device_t device, const char *path, + 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 (*read) (grub_net_socket_t sock, struct grub_net_buff *nb); + grub_err_t (*close) (struct grub_file *file); + grub_err_t (*label) (grub_device_t device, char **label); +}; + +struct grub_net_socket +{ + struct grub_net_socket *next; + int status; + int in_port; + int out_port; + grub_net_app_level_t app; + grub_net_network_level_address_t out_nla; + struct grub_net_network_level_interface *inf; + grub_net_packets_t packs; void *data; }; -typedef struct grub_net *grub_net_t; -/* FIXME: How to abstract networks? More consideration is necessary. */ +extern struct grub_net_socket *grub_net_sockets; -/* Note: Networks are very different from disks, because networks must - be initialized before used, and the status is persistent. */ +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 *name; + grub_net_app_level_t protocol; + grub_net_socket_t socket; + grub_fs_t fs; +} *grub_net_t; + +extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name); +extern grub_err_t (*EXPORT_VAR (grub_file_net_seek)) (struct grub_file *file, grub_off_t offset); +extern void (*EXPORT_VAR (grub_grubnet_fini)) (void); + +struct grub_net_network_level_interface +{ + struct grub_net_network_level_interface *next; + struct grub_net_network_level_interface **prev; + char *name; + const struct grub_net_card *card; + grub_net_network_level_address_t address; + grub_net_link_level_address_t hwaddress; + grub_net_interface_flags_t flags; + struct grub_net_bootp_packet *dhcp_ack; + grub_size_t dhcp_acklen; + void *data; +}; + +struct grub_net_session; + +struct grub_net_session_level_protocol +{ + void (*close) (struct grub_net_session *session); + grub_ssize_t (*recv) (struct grub_net_session *session, void *buf, + grub_size_t size); + grub_err_t (*send) (struct grub_net_session *session, void *buf, + grub_size_t size); +}; + +struct grub_net_session +{ + struct grub_net_session_level_protocol *protocol; + void *data; +}; + +static inline void +grub_net_session_close (struct grub_net_session *session) +{ + session->protocol->close (session); +} + +static inline grub_err_t +grub_net_session_send (struct grub_net_session *session, void *buf, + grub_size_t size) +{ + return session->protocol->send (session, buf, size); +} + +static inline grub_ssize_t +grub_net_session_recv (struct grub_net_session *session, void *buf, + grub_size_t size) +{ + return session->protocol->recv (session, buf, size); +} + +struct grub_net_network_level_interface * +grub_net_add_addr (const char *name, + const struct grub_net_card *card, + grub_net_network_level_address_t addr, + 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; + +extern grub_net_app_level_t grub_net_app_level_list; + +#ifndef GRUB_LST_GENERATOR +static inline void +grub_net_app_level_register (grub_net_app_level_t proto) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_net_app_level_list), + GRUB_AS_LIST (proto)); +} +#endif + +static inline void +grub_net_app_level_unregister (grub_net_app_level_t proto) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_net_app_level_list), + GRUB_AS_LIST (proto)); +} + +#define FOR_NET_APP_LEVEL(var) FOR_LIST_ELEMENTS((var), \ + (grub_net_app_level_list)) + +extern struct grub_net_card *grub_net_cards; + +static inline void +grub_net_card_register (struct grub_net_card *card) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_net_cards), + GRUB_AS_LIST (card)); +} + +static inline void +grub_net_card_unregister (struct grub_net_card *card) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_net_cards), + GRUB_AS_LIST (card)); +} + +#define FOR_NET_CARDS(var) for (var = grub_net_cards; var; var = var->next) + +struct grub_net_session * +grub_net_open_tcp (char *address, grub_uint16_t port); + +grub_err_t +grub_net_resolve_address (const char *name, + grub_net_network_level_address_t *addr); + +grub_err_t +grub_net_resolve_net_address (const char *name, + grub_net_network_level_netaddress_t *addr); + +grub_err_t +grub_net_route_address (grub_net_network_level_address_t addr, + grub_net_network_level_address_t *gateway, + struct grub_net_network_level_interface **interf); + + +grub_err_t +grub_net_add_route (const char *name, + grub_net_network_level_netaddress_t target, + struct grub_net_network_level_interface *inter); + +grub_err_t +grub_net_add_route_gw (const char *name, + grub_net_network_level_netaddress_t target, + grub_net_network_level_address_t gw); + + +#define GRUB_NET_BOOTP_MAC_ADDR_LEN 16 + +typedef grub_uint8_t grub_net_bootp_mac_addr_t[GRUB_NET_BOOTP_MAC_ADDR_LEN]; + +struct grub_net_bootp_packet +{ + grub_uint8_t opcode; + grub_uint8_t hw_type; /* hardware type. */ + grub_uint8_t hw_len; /* hardware addr len. */ + grub_uint8_t gate_hops; /* zero it. */ + grub_uint32_t ident; /* random number chosen by client. */ + grub_uint16_t seconds; /* seconds since did initial bootstrap. */ + grub_uint16_t flags; + grub_uint32_t client_ip; + grub_uint32_t your_ip; + grub_uint32_t server_ip; + grub_uint32_t gateway_ip; + grub_net_bootp_mac_addr_t mac_addr; + grub_uint8_t server_name[64]; + grub_uint8_t boot_file[128]; + grub_uint8_t vendor[0]; +} __attribute__ ((packed)); + +#define GRUB_NET_BOOTP_RFC1048_MAGIC 0x63825363L + +struct grub_net_network_level_interface * +grub_net_configure_by_dhcp_ack (const char *name, + const struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_bootp_packet *bp, + grub_size_t size); + +void +grub_net_process_dhcp (struct grub_net_buff *nb, + const struct grub_net_card *card); + +int +grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, + const grub_net_link_level_address_t *b); + + +/* + Currently suppoerted adresses: + IPv4: XXX.XXX.XXX.XXX + */ +#define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXX.XXX.XXX.XXX") + +void +grub_net_addr_to_str (const grub_net_network_level_address_t *target, + char *buf); + +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) + +grub_err_t grub_net_send_link_layer (struct grub_net_network_level_interface *inf, + struct grub_net_buff *nb, + grub_net_link_level_address_t *target); + +typedef int +(*grub_net_packet_handler_t) (struct grub_net_buff *nb, + struct grub_net_network_level_interface *inf); + +grub_err_t grub_net_recv_link_layer (struct grub_net_network_level_interface *inf, + grub_net_packet_handler_t handler); + + +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_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); + +#define FOR_NET_NL_PACKETS(inf, var) FOR_PACKETS(inf->nl_pending, var) + +void +grub_net_poll_cards (unsigned time); #endif /* ! GRUB_NET_HEADER */ diff --git a/include/grub/net/arp.h b/include/grub/net/arp.h new file mode 100644 index 000000000..c60ea333f --- /dev/null +++ b/include/grub/net/arp.h @@ -0,0 +1,36 @@ +#ifndef GRUB_NET_ARP_HEADER +#define GRUB_NET_ARP_HEADER 1 +#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, + const grub_net_network_level_address_t *addr, + grub_net_link_level_address_t *hw_addr); + +#endif diff --git a/include/grub/net/device.h b/include/grub/net/device.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/grub/net/ethernet.h b/include/grub/net/ethernet.h new file mode 100644 index 000000000..a841dc12c --- /dev/null +++ b/include/grub/net/ethernet.h @@ -0,0 +1,45 @@ +#ifndef GRUB_NET_ETHERNET_HEADER +#define GRUB_NET_ETHERNET_HEADER 1 +#include +#include + +#define LLCADDRMASK 0x7f + +struct etherhdr +{ + grub_uint8_t dst[6]; + grub_uint8_t src[6]; + grub_uint16_t type; +} __attribute__ ((packed)); + +struct llchdr +{ + grub_uint8_t dsap; + grub_uint8_t ssap; + grub_uint8_t ctrl; +} __attribute__ ((packed)); + +struct snaphdr +{ + grub_uint8_t oui[3]; + grub_uint16_t type; +} __attribute__ ((packed)); + +/* IANA Ethertype */ +enum +{ + GRUB_NET_ETHERTYPE_IP = 0x0800, + GRUB_NET_ETHERTYPE_ARP = 0x0806 +}; + + +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_err_t +grub_net_recv_ethernet_packet (struct grub_net_buff *nb, + const struct grub_net_card *card); + +#endif diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h new file mode 100644 index 000000000..cb8481a7d --- /dev/null +++ b/include/grub/net/ip.h @@ -0,0 +1,35 @@ +#ifndef GRUB_NET_IP_HEADER +#define GRUB_NET_IP_HEADER 1 +#include + +struct iphdr { + grub_uint8_t verhdrlen; + grub_uint8_t service; + grub_uint16_t len; + grub_uint16_t ident; + grub_uint16_t frags; + grub_uint8_t ttl; + grub_uint8_t protocol; + grub_uint16_t chksum; + grub_uint32_t src; + grub_uint32_t dest; +} __attribute__ ((packed)) ; + +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)); + +#define IP_UDP 0x11 /* UDP protocol */ +#define IP_BROADCAST 0xFFFFFFFF + +grub_uint16_t ipchksum(void *ipv, int len); +void ipv4_ini(void); +void ipv4_fini(void); +#endif diff --git a/include/grub/net/netbuff.h b/include/grub/net/netbuff.h new file mode 100644 index 000000000..245e813c3 --- /dev/null +++ b/include/grub/net/netbuff.h @@ -0,0 +1,30 @@ +#ifndef GRUB_NETBUFF_HEADER +#define GRUB_NETBUFF_HEADER + +#include + +#define NETBUFF_ALIGN 2048 +#define NETBUFFMINLEN 64 + +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; +}; + +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 ); +grub_err_t grub_netbuff_free (struct grub_net_buff *net_buff); + +#endif diff --git a/include/grub/net/tftp.h b/include/grub/net/tftp.h new file mode 100644 index 000000000..c67380817 --- /dev/null +++ b/include/grub/net/tftp.h @@ -0,0 +1,74 @@ +#ifndef GRUB_NET_TFTP_HEADER +#define GRUB_NET_TFTP_HEADER 1 + +#include +#include +#include + +/* IP port for the MTFTP server used for Intel's PXE */ +#define MTFTP_SERVER_PORT 75 +#define MTFTP_CLIENT_PORT 76 + +#define TFTP_DEFAULTSIZE_PACKET 512 +#define TFTP_MAX_PACKET 1432 + +/* IP port for the TFTP server */ +#define TFTP_SERVER_PORT 69 + + +/* We define these based on what's in arpa/tftp.h. We just like our + * names better, cause they're clearer */ +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 +#define TFTP_OACK 6 + +#define TFTP_CODE_EOF 1 +#define TFTP_CODE_MORE 2 +#define TFTP_CODE_ERROR 3 +#define TFTP_CODE_BOOT 4 +#define TFTP_CODE_CFG 5 + +#define TFTP_EUNDEF 0 /* not defined */ +#define TFTP_ENOTFOUND 1 /* file not found */ +#define TFTP_EACCESS 2 /* access violation */ +#define TFTP_ENOSPACE 3 /* disk full or allocation exceeded */ +#define TFTP_EBADOP 4 /* illegal TFTP operation */ +#define TFTP_EBADID 5 /* unknown transfer ID */ +#define TFTP_EEXISTS 6 /* file already exists */ +#define TFTP_ENOUSER 7 /* no such user */ + + /* * own here because this is cleaner, and maps to the same data layout. + * */ + +typedef struct tftp_data + { + int file_size; + int block; + } *tftp_data_t; + + +struct tftphdr { + grub_uint16_t opcode; + union { + grub_int8_t rrq[TFTP_DEFAULTSIZE_PACKET]; + struct { + grub_uint16_t block; + grub_int8_t download[TFTP_MAX_PACKET]; + } data; + struct { + grub_uint16_t block; + } ack; + struct { + grub_uint16_t errcode; + grub_int8_t errmsg[TFTP_DEFAULTSIZE_PACKET]; + } err; + struct { + grub_int8_t data[TFTP_DEFAULTSIZE_PACKET+2]; + } oack; + } u; +} __attribute__ ((packed)) ; + +#endif diff --git a/include/grub/net/udp.h b/include/grub/net/udp.h new file mode 100644 index 000000000..eacf3325c --- /dev/null +++ b/include/grub/net/udp.h @@ -0,0 +1,24 @@ +#ifndef GRUB_NET_UDP_HEADER +#define GRUB_NET_UDP_HEADER 1 +#include +#include + +struct udphdr +{ + grub_uint16_t src; + grub_uint16_t dst; + grub_uint16_t len; + grub_uint16_t chksum; +} __attribute__ ((packed)); + +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); + + +#define FOR_NET_UDP_PACKETS(inf, var) FOR_PACKETS(inf->udp_pending, var) + +#endif