diff --git a/commands/net.c b/commands/net.c new file mode 100644 index 000000000..288ba4c2a --- /dev/null +++ b/commands/net.c @@ -0,0 +1,296 @@ +/* + * 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 + +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_network_level_protocol *grub_net_network_level_protocols = NULL; + +grub_err_t +grub_net_resolve_address (struct grub_net_network_level_protocol **prot, + char *name, + grub_net_network_level_address_t *addr) +{ + FOR_NET_NETWORK_LEVEL_PROTOCOLS (*prot) + { + grub_err_t err; + err = grub_net_resolve_address_in_protocol (*prot, name, addr); + if (err == GRUB_ERR_NET_BAD_ADDRESS) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + if (err) + return err; + 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; + prot = route->prot; + if (!route->prot->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 != 4) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + FOR_NET_NETWORK_LEVEL_INTERFACES (inter) + if (grub_strcmp (inter->name, args[1])) + break; + if (inter == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("address not found")); + + inter->protocol->fini (inter); + grub_net_network_level_interface_unregister (inter); + grub_free (inter->name); + grub_free (inter); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_addaddr (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_card *card; + struct grub_net_network_level_protocol *prot; + grub_err_t err; + grub_net_network_level_address_t addr; + struct grub_net_network_level_interface *inter; + + if (argc != 4) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("four arguments expected")); + + FOR_NET_CARDS (card) + if (grub_strcmp (card->name, args[1])) + break; + if (card == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found")); + + FOR_NET_NETWORK_LEVEL_PROTOCOLS (prot) + if (grub_strcmp (prot->name, args[2])) + break; + + if (card == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("protocol not found")); + + err = grub_net_resolve_address_in_protocol (prot, args[3], &addr); + if (err) + return err; + + inter = grub_zalloc (sizeof (*inter)); + if (!inter) + return grub_errno; + + inter->name = grub_strdup (args[0]); + inter->protocol = prot; + grub_memcpy (&(inter->address), &addr, sizeof (inter->address)); + inter->card = card; + + err = prot->init (inter); + if (err) + { + grub_free (inter->name); + grub_free (inter); + return err; + } + grub_net_network_level_interface_register (inter); + + return GRUB_ERR_NONE; +} + +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; +} + +static grub_err_t +grub_cmd_addroute (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_network_level_protocol *prot; + struct grub_net_route *route; + + if (argc < 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("At least 3 arguments are expected")); + + route = grub_zalloc (sizeof (*route)); + if (!route) + return grub_errno; + + route->name = grub_strdup (args[0]); + if (!route->name) + { + grub_free (route); + return grub_errno; + } + + FOR_NET_NETWORK_LEVEL_PROTOCOLS(prot) + { + grub_err_t err; + err = prot->net_ntoa (args[1], &(route->target)); + if (err == GRUB_ERR_NET_BAD_ADDRESS) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + if (err) + return err; + break; + } + + if (!prot) + { + grub_free (route->name); + grub_free (route); + return grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("Unrecognised address %s"), args[1]); + } + + if (grub_strcmp (args[2], "gw") == 0 && argc >= 4) + { + grub_err_t err; + route->is_gateway = 1; + err = grub_net_resolve_address_in_protocol (prot, + args[3], &(route->gw)); + if (err) + { + grub_free (route->name); + grub_free (route); + return err; + } + } + else + { + struct grub_net_network_level_interface *inter; + route->is_gateway = 0; + + FOR_NET_NETWORK_LEVEL_INTERFACES (inter) + if (grub_strcmp (inter->name, args[2])) + break; + + if (!inter) + { + grub_free (route->name); + grub_free (route); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Unrecognised interface %s"), args[2]); + } + route->interface = inter; + } + + grub_net_route_register (route); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute; + +GRUB_MOD_INIT(net) +{ + cmd_addaddr = grub_register_command ("net_add_addr", grub_cmd_addaddr, + "SHORTNAME CARD PROTOCOL ADDRESS", + 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.")); +} + +GRUB_MOD_FINI(net) +{ + grub_unregister_command (cmd_addaddr); + grub_unregister_command (cmd_deladdr); + grub_unregister_command (cmd_addroute); + grub_unregister_command (cmd_delroute); +} diff --git a/commands/probe.c b/commands/probe.c index c2cc599e9..002ede85e 100644 --- a/commands/probe.c +++ b/commands/probe.c @@ -72,7 +72,7 @@ grub_cmd_probe (grub_extcmd_t cmd, 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/conf/common.rmk b/conf/common.rmk index 0f67622b5..cee808a35 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -828,4 +828,10 @@ bin_UTILITIES += grub-mkpasswd-pbkdf2 grub_mkpasswd_pbkdf2_SOURCES = gnulib/progname.c gnulib/getdelim.c gnulib/getline.c util/grub-mkpasswd-pbkdf2.c lib/crypto.c lib/libgcrypt-grub/cipher/sha512.c lib/pbkdf2.c util/misc.c kern/emu/misc.c kern/emu/mm.c kern/err.c grub_mkpasswd_pbkdf2_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap -DGRUB_MKPASSWD=1 +pkglib_MODULES += net.mod +net_mod_SOURCES = commands/net.c +net_mod_CFLAGS = $(COMMON_CFLAGS) +net_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/gcry.mk + diff --git a/include/grub/device.h b/include/grub/device.h index f0e8a8ca8..d68c26e66 100644 --- a/include/grub/device.h +++ b/include/grub/device.h @@ -24,8 +24,12 @@ #include struct grub_disk; -struct grub_net; struct grub_fs; +struct grub_net +{ + char *name; + struct grub_fs *fs; +}; struct grub_device { diff --git a/include/grub/err.h b/include/grub/err.h index e44705389..493796d62 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -54,7 +54,10 @@ typedef enum GRUB_ERR_MENU, GRUB_ERR_TIMEOUT, GRUB_ERR_IO, - GRUB_ERR_ACCESS_DENIED + GRUB_ERR_ACCESS_DENIED, + GRUB_ERR_NET_BAD_ADDRESS, + GRUB_ERR_NET_ROUTE_LOOP, + GRUB_ERR_NET_NO_ROUTE } grub_err_t; diff --git a/include/grub/net.h b/include/grub/net.h index c6d71d5b6..4ca873f74 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,224 @@ #ifndef GRUB_NET_HEADER #define GRUB_NET_HEADER 1 -#include -#include #include +#include +#include -struct grub_net; +struct grub_net_card; -struct grub_net_dev +struct grub_net_card_driver { - /* The device name. */ - const char *name; - - /* 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); - - /* The next net device. */ - struct grub_net_dev *next; + grub_err_t (*send) (struct grub_net_card *dev, void *buf, + grub_size_t buflen); + grub_size_t (*recv) (struct grub_net_card *dev, void *buf, + grub_size_t buflen); }; -typedef struct grub_net_dev *grub_net_dev_t; -struct grub_fs; - -struct grub_net +struct grub_net_card { - /* The net name. */ - const char *name; - - /* The underlying disk device. */ - grub_net_dev_t dev; - - /* The binding filesystem. */ - struct grub_fs *fs; - - /* FIXME: More data would be required, such as an IP address, a mask, - a gateway, etc. */ - - /* Device-specific data. */ + struct grub_net_card *next; + char *name; + struct grub_net_card_driver *driver; void *data; }; -typedef struct grub_net *grub_net_t; -/* FIXME: How to abstract networks? More consideration is necessary. */ +struct grub_net_network_level_interface; + +typedef union grub_net_network_level_address +{ + grub_uint32_t ipv4; +} grub_net_network_level_netaddress_t; + +typedef union grub_net_network_level_netaddress +{ + struct { + grub_uint32_t base; + int masksize; + } ipv4; +} grub_net_network_level_address_t; + +typedef enum grub_network_level_protocol_id +{ + GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 +} grub_network_level_protocol_id_t; + +struct grub_net_network_level_interface; + +struct grub_net_network_level_protocol +{ + struct grub_net_network_level_protocol *next; + char *name; + grub_network_level_protocol_id_t id; + grub_err_t (*ntoa) (char *name, grub_net_network_level_address_t *addr); + char * (*aton) (union grub_net_network_level_address addr); + grub_err_t (*net_ntoa) (char *name, + grub_net_network_level_netaddress_t *addr); + char * (*net_aton) (grub_net_network_level_netaddress_t addr); + int (* match_net) (grub_net_network_level_netaddress_t net, + grub_net_network_level_address_t addr); + grub_err_t (*init) (struct grub_net_network_level_interface *dev); + grub_err_t (*fini) (struct grub_net_network_level_interface *dev); + grub_err_t (*send) (struct grub_net_network_level_interface *dev, void *buf, + grub_size_t buflen); + grub_size_t (*recv) (struct grub_net_network_level_interface *dev, void *buf, + grub_size_t buflen); +}; + +struct grub_net_network_level_interface +{ + struct grub_net_network_level_interface *next; + char *name; + /* Underlying protocol. */ + struct grub_net_network_level_protocol *protocol; + struct grub_net_card *card; + union grub_net_network_level_address address; + void *data; +}; + +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_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); +} + +extern struct grub_net_network_level_interface *grub_net_network_level_interfaces; + +static inline void +grub_net_network_level_interface_register (struct grub_net_network_level_interface *inter) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_net_network_level_interfaces), + GRUB_AS_LIST (inter)); +} + +static inline void +grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_net_network_level_interfaces), + GRUB_AS_LIST (inter)); +} + +#define FOR_NET_NETWORK_LEVEL_INTERFACES(var) for (var = grub_net_network_level_interfaces; var; var = var->next) + +extern struct grub_net_route *grub_net_routes; + +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) + +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) + +extern struct grub_net_network_level_protocol *grub_net_network_level_protocols; + +static inline void +grub_net_network_level_protocol_register (struct grub_net_network_level_protocol *prot) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_net_network_level_protocols), + GRUB_AS_LIST (prot)); +} + +static inline void +grub_net_network_level_protocol_unregister (struct grub_net_network_level_protocol *prot) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_net_network_level_protocols), + GRUB_AS_LIST (prot)); +} + +#define FOR_NET_NETWORK_LEVEL_PROTOCOLS(var) for ((var) = grub_net_network_level_protocols; (var); (var) = (var)->next) + +static inline grub_err_t +grub_net_resolve_address_in_protocol (struct grub_net_network_level_protocol *prot, + char *name, + grub_net_network_level_address_t *addr) +{ + return prot->ntoa (name, addr); +} + +struct grub_net_session * +grub_net_open_tcp (char *address, grub_uint16_t port); + +grub_err_t +grub_net_resolve_address (struct grub_net_network_level_protocol **prot, + char *name, + grub_net_network_level_address_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); -/* Note: Networks are very different from disks, because networks must - be initialized before used, and the status is persistent. */ #endif /* ! GRUB_NET_HEADER */ diff --git a/kern/fs.c b/kern/fs.c index cf800f4cc..8ffb93c8b 100644 --- a/kern/fs.c +++ b/kern/fs.c @@ -94,7 +94,7 @@ grub_fs_probe (grub_device_t device) count--; } } - else if (device->net->fs) + else if (device->net) return device->net->fs; grub_error (GRUB_ERR_UNKNOWN_FS, "unknown filesystem");