diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/Kconfig | 29 | ||||
-rw-r--r-- | net/Makefile | 3 | ||||
-rw-r--r-- | net/dhcp.c | 47 | ||||
-rw-r--r-- | net/dns.c | 16 | ||||
-rw-r--r-- | net/eth.c | 294 | ||||
-rw-r--r-- | net/fastboot.c | 589 | ||||
-rw-r--r-- | net/ifup.c | 244 | ||||
-rw-r--r-- | net/lib.c | 34 | ||||
-rw-r--r-- | net/net.c | 288 | ||||
-rw-r--r-- | net/netconsole.c | 22 | ||||
-rw-r--r-- | net/nfs.c | 7 | ||||
-rw-r--r-- | net/nfs.h | 74 | ||||
-rw-r--r-- | net/ping.c | 2 | ||||
-rw-r--r-- | net/rarp.h | 40 | ||||
-rw-r--r-- | net/sntp.c | 13 |
15 files changed, 1347 insertions, 355 deletions
diff --git a/net/Kconfig b/net/Kconfig index 12b6bdb56d..07e623670e 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -1,8 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only + menuconfig NET bool "Networking Support" + select POLLER if NET +config NET_ETHADDR_FROM_MACHINE_ID + bool + prompt "generate stable Ethernet address" + depends on MACHINE_ID && HAVE_DIGEST_SHA256 && HAVE_DIGEST_HMAC + help + By default, barebox will generate random Ethernet addresses for + interfaces that had no explicit Ethernet address set via + either board code or NVMEM properties in device tree. + + Say y here, to randomize Ethernet addresses only if no machine ID + is available. Should barebox have a machine ID, it will be used + alongside the hostname to generate MAC addresses that are unlikely + to change between subsequent runs of barebox. + + This is not recommended for use in production as it may leak + information about the machine ID. + config NET_NFS bool prompt "nfs support" @@ -31,4 +51,13 @@ config NET_SNTP bool prompt "sntp support" +config NET_FASTBOOT + bool + select BANNER + select FASTBOOT_BASE + prompt "Android Fastboot support" + help + This option adds support for the UDP variant of the Fastboot + protocol. + endif diff --git a/net/Makefile b/net/Makefile index eb8d439150..c93d0772c3 100644 --- a/net/Makefile +++ b/net/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-y += lib.o obj-$(CONFIG_NET) += eth.o obj-$(CONFIG_NET) += net.o @@ -8,3 +10,4 @@ obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_NET_RESOLV)+= dns.o obj-$(CONFIG_NET_NETCONSOLE) += netconsole.o obj-$(CONFIG_NET_IFUP) += ifup.o +obj-$(CONFIG_NET_FASTBOOT) += fastboot.o diff --git a/net/dhcp.c b/net/dhcp.c index 670a6a6422..e1025bf91b 100644 --- a/net/dhcp.c +++ b/net/dhcp.c @@ -1,11 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 1994, 1995, 2000 Neil Russell +// SPDX-FileCopyrightText: 2000 Roland Borde +// SPDX-FileCopyrightText: 2000 Paolo Scaffardi +// SPDX-FileCopyrightText: 2000-2004 Wolfgang Denk, wd@denx.de /* - * Based on LiMon - BOOTP. + * dhcp.c - barebox dhcp support * - * Copyright 1994, 1995, 2000 Neil Russell. - * (See License) - * Copyright 2000 Roland Borde - * Copyright 2000 Paolo Scaffardi - * Copyright 2000-2004 Wolfgang Denk, wd@denx.de + * based on U-Boot (LiMon) code */ #include <common.h> @@ -444,6 +445,7 @@ static char *global_dhcp_user_class; static char *global_dhcp_vendor_id; static char *global_dhcp_client_uuid; static char *global_dhcp_client_id; +static char *global_dhcp_hostname; static char *global_dhcp_bootfile; static char *global_dhcp_oftree_file; static char *global_dhcp_rootpath; @@ -482,6 +484,12 @@ int dhcp_request(struct eth_device *edev, const struct dhcp_req_param *param, dhcp_param.client_uuid = global_dhcp_client_uuid; if (!dhcp_param.client_id) dhcp_param.client_id = global_dhcp_client_id; + if (!dhcp_param.hostname) { + if (global_dhcp_hostname && strlen(global_dhcp_hostname)) + dhcp_param.hostname = global_dhcp_hostname; + else + dhcp_param.hostname = (char *)barebox_get_hostname(); + } if (!dhcp_param.option224) dhcp_param.option224 = global_dhcp_option224; if (!dhcp_param.retries) @@ -609,6 +617,10 @@ int dhcp(struct eth_device *edev, const struct dhcp_req_param *param) struct dhcp_result *res; int ret; + ret = eth_open(edev); + if (ret) + return ret; + ret = dhcp_request(edev, param, &res); if (ret) return ret; @@ -629,9 +641,12 @@ static int dhcp_global_init(void) { globalvar_add_simple_string("dhcp.bootfile", &global_dhcp_bootfile); globalvar_add_simple_string("dhcp.rootpath", &global_dhcp_rootpath); + + global_dhcp_vendor_id = strdup("barebox"); globalvar_add_simple_string("dhcp.vendor_id", &global_dhcp_vendor_id); globalvar_add_simple_string("dhcp.client_uuid", &global_dhcp_client_uuid); globalvar_add_simple_string("dhcp.client_id", &global_dhcp_client_id); + globalvar_add_simple_string("dhcp.hostname", &global_dhcp_hostname); globalvar_add_simple_string("dhcp.user_class", &global_dhcp_user_class); globalvar_add_simple_string("dhcp.oftree_file", &global_dhcp_oftree_file); globalvar_add_simple_string("dhcp.tftp_server_name", &global_dhcp_tftp_server_name); @@ -643,13 +658,13 @@ static int dhcp_global_init(void) late_initcall(dhcp_global_init); #endif -BAREBOX_MAGICVAR_NAMED(global_dhcp_bootfile, global.dhcp.bootfile, "bootfile returned from DHCP request"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_rootpath, global.dhcp.rootpath, "rootpath returned from DHCP request"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_vendor_id, global.dhcp.vendor_id, "vendor id to send to the DHCP server"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_client_uuid, global.dhcp.client_uuid, "client uuid to send to the DHCP server"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_client_id, global.dhcp.client_id, "client id to send to the DHCP server"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_user_class, global.dhcp.user_class, "user class to send to the DHCP server"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_tftp_server_name, global.dhcp.tftp_server_name, "TFTP server Name returned from DHCP request"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_oftree_file, global.dhcp.oftree_file, "OF tree returned from DHCP request (option 224)"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_retries, global.dhcp.retries, "retry limit"); -BAREBOX_MAGICVAR_NAMED(global_dhcp_option224, global.dhcp.option224, "private data to send to the DHCP server (option 224)"); +BAREBOX_MAGICVAR(global.dhcp.bootfile, "bootfile returned from DHCP request"); +BAREBOX_MAGICVAR(global.dhcp.rootpath, "rootpath returned from DHCP request"); +BAREBOX_MAGICVAR(global.dhcp.vendor_id, "vendor id to send to the DHCP server"); +BAREBOX_MAGICVAR(global.dhcp.client_uuid, "client uuid to send to the DHCP server"); +BAREBOX_MAGICVAR(global.dhcp.client_id, "client id to send to the DHCP server"); +BAREBOX_MAGICVAR(global.dhcp.user_class, "user class to send to the DHCP server"); +BAREBOX_MAGICVAR(global.dhcp.tftp_server_name, "TFTP server Name returned from DHCP request"); +BAREBOX_MAGICVAR(global.dhcp.oftree_file, "OF tree returned from DHCP request (option 224)"); +BAREBOX_MAGICVAR(global.dhcp.retries, "retry limit"); +BAREBOX_MAGICVAR(global.dhcp.option224, "private data to send to the DHCP server (option 224)"); @@ -58,6 +58,7 @@ struct header { static struct net_connection *dns_con; static uint64_t dns_timer_start; +static uint16_t dns_req_id; static int dns_state; static IPaddr_t dns_ip; @@ -70,9 +71,12 @@ static int dns_send(const char *name) unsigned char *p, *s, *fullname, *dotptr; const unsigned char *domain; + /* generate "difficult" to predict transaction id */ + dns_req_id = dns_timer_start + (dns_timer_start >> 16); + /* Prepare DNS packet header */ header = (struct header *)packet; - header->tid = 1; + header->tid = htons(dns_req_id); header->flags = htons(0x100); /* standard query */ header->nqueries = htons(1); /* Just one query */ header->nanswers = 0; @@ -127,6 +131,12 @@ static void dns_recv(struct header *header, unsigned len) pr_debug("%s\n", __func__); + /* Only accept responses with the expected request id */ + if (ntohs(header->tid) != dns_req_id) { + pr_debug("DNS response with incorrect id\n"); + return; + } + /* We sent 1 query. We want to see more that 1 answer. */ if (ntohs(header->nqueries) != 1) return; @@ -215,8 +225,8 @@ int resolv(const char *host, IPaddr_t *ip) nameserver = net_get_nameserver(); if (!nameserver) { - pr_err("no nameserver specified in $net.nameserver\n"); - return 0; + pr_err("no nameserver specified in $global.net.nameserver\n"); + return -ENOENT; } pr_debug("resolving host %s via nameserver %pI4\n", host, &nameserver); @@ -1,21 +1,5 @@ -/* - * (C) Copyright 2001-2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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. - * - */ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2001-2004 Wolfgang Denk <wd@denx.de>, DENX Software Engineering #include <common.h> #include <command.h> @@ -25,7 +9,10 @@ #include <init.h> #include <dhcp.h> #include <net.h> +#include <dma.h> +#include <machine_id.h> #include <of.h> +#include <of_net.h> #include <linux/phy.h> #include <errno.h> #include <malloc.h> @@ -34,8 +21,6 @@ #include <linux/ctype.h> #include <linux/stat.h> -static uint64_t last_link_check; - LIST_HEAD(netdev_list); struct eth_ethaddr { @@ -47,6 +32,14 @@ struct eth_ethaddr { static LIST_HEAD(ethaddr_list); +int eth_set_promisc(struct eth_device *edev, bool enable) +{ + if (!edev->set_promisc) + return -EOPNOTSUPP; + + return edev->set_promisc(edev, enable); +} + int eth_set_ethaddr(struct eth_device *edev, const char *ethaddr) { int ret; @@ -62,11 +55,8 @@ int eth_set_ethaddr(struct eth_device *edev, const char *ethaddr) static void register_preset_mac_address(struct eth_device *edev, const char *ethaddr) { - unsigned char ethaddr_str[sizeof("xx:xx:xx:xx:xx:xx")]; - if (is_valid_ether_addr(ethaddr)) { - ethaddr_to_string(ethaddr, ethaddr_str); - dev_info(&edev->dev, "got preset MAC address: %s\n", ethaddr_str); + dev_info(&edev->dev, "got preset MAC address: %pM\n", ethaddr); eth_set_ethaddr(edev, ethaddr); } } @@ -77,7 +67,7 @@ static int eth_get_registered_ethaddr(struct eth_device *edev, void *buf) struct device_node *node = NULL; if (edev->parent) - node = edev->parent->device_node; + node = edev->parent->of_node; list_for_each_entry(addr, ðaddr_list, list) { if ((node && node == addr->node) || @@ -129,9 +119,9 @@ static struct eth_device *eth_get_by_node(struct device_node *node) for_each_netdev(edev) { if (!edev->parent) continue; - if (!edev->parent->device_node) + if (!edev->parent->of_node) continue; - if (edev->parent->device_node == node) + if (edev->parent->of_node == node) return edev; } return NULL; @@ -184,10 +174,7 @@ int eth_complete(struct string_list *sl, char *instr) } #endif -/* - * Check for link if we haven't done so for longer. - */ -static int eth_carrier_check(struct eth_device *edev, int force) +int eth_carrier_poll_once(struct eth_device *edev) { int ret; @@ -197,70 +184,119 @@ static int eth_carrier_check(struct eth_device *edev, int force) if (!edev->phydev) return 0; - if (force) - phy_wait_aneg_done(edev->phydev); - - if (force || is_timeout(last_link_check, 5 * SECOND) || - !edev->phydev->link) { - ret = phy_update_status(edev->phydev); - if (ret) - return ret; - last_link_check = get_time_ns(); - } + ret = phy_update_status(edev->phydev); + if (ret) + return ret; + edev->last_link_check = get_time_ns(); return edev->phydev->link ? 0 : -ENETDOWN; } /* - * Check if we have a current ethernet device and - * eventually open it if we have to. + * Check for link if we haven't done so for longer. */ -static int eth_check_open(struct eth_device *edev) +static int eth_carrier_check(struct eth_device *edev, bool may_wait) { - int ret; + if (!IS_ENABLED(CONFIG_PHYLIB)) + return 0; - if (edev->active) + if (!edev->phydev) return 0; - ret = edev->open(edev); - if (ret) - return ret; + if (!edev->last_link_check || + is_timeout(edev->last_link_check, 5 * SECOND)) + eth_carrier_poll_once(edev); + + if (may_wait && !edev->phydev->link) { + phy_wait_aneg_done(edev->phydev); + edev->last_link_check = get_time_ns(); + } + + return edev->phydev->link ? 0 : -ENETDOWN; +} + +struct eth_q { + struct eth_device *edev; + int length; + struct list_head list; + void *data; +}; + +static int eth_queue(struct eth_device *edev, void *packet, int length) +{ + struct eth_q *q; - edev->active = 1; + q = xzalloc(sizeof(*q)); + if (!q) + return -ENOMEM; - return eth_carrier_check(edev, 1); + q->data = dma_alloc(length); + if (!q->data) { + free(q); + return -ENOMEM; + } + + q->length = length; + q->edev = edev; + + memcpy(q->data, packet, length); + list_add_tail(&q->list, &edev->send_queue); + + return 0; } int eth_send(struct eth_device *edev, void *packet, int length) { int ret; - ret = eth_check_open(edev); - if (ret) - return ret; + if (!edev->active) + return -ENETDOWN; - ret = eth_carrier_check(edev, 0); + if (slice_acquired(eth_device_slice(edev))) + return eth_queue(edev, packet, length); + + ret = eth_carrier_check(edev, true); if (ret) return ret; + slice_acquire(eth_device_slice(edev)); + led_trigger_network(LED_TRIGGER_NET_TX); - return edev->send(edev, packet, length); + ret = eth_send_raw(edev, packet, length); + + slice_release(eth_device_slice(edev)); + + return ret; } -static int __eth_rx(struct eth_device *edev) +static void eth_do_work(struct eth_device *edev) { + struct eth_q *q, *tmp; int ret; - ret = eth_check_open(edev); - if (ret) - return ret; + if (!phy_acquired(edev->phydev)) { + ret = eth_carrier_check(edev, false); + if (ret) + return; + } - ret = eth_carrier_check(edev, 0); - if (ret) - return ret; + if (slice_acquired(eth_device_slice(edev))) + return; - return edev->recv(edev); + slice_acquire(eth_device_slice(edev)); + + edev->recv(edev); + + list_for_each_entry_safe(q, tmp, &edev->send_queue, list) { + led_trigger_network(LED_TRIGGER_NET_TX); + eth_send_raw(edev, q->data, q->length); + list_del(&q->list); + free(q->data); + free(q); + } + + slice_release(eth_device_slice(edev)); } int eth_rx(void) @@ -269,7 +305,7 @@ int eth_rx(void) for_each_netdev(edev) { if (edev->active) - __eth_rx(edev); + eth_do_work(edev); } return 0; @@ -287,7 +323,8 @@ static void eth_of_fixup_node(struct device_node *root, const char *node_path, int ethid, const u8 ethaddr[ETH_ALEN]) { - struct device_node *node; + struct device_node *bb_node, *fixup_node; + char *name; int ret; if (!is_valid_ether_addr(ethaddr)) { @@ -297,22 +334,25 @@ static void eth_of_fixup_node(struct device_node *root, } if (node_path) { - node = of_find_node_by_path_from(root, node_path); + bb_node = of_find_node_by_path_from(0, node_path); + name = of_get_reproducible_name(bb_node); + fixup_node = of_find_node_by_reproducible_name(root, name); + free(name); } else { char eth[12]; sprintf(eth, "ethernet%d", ethid); - node = of_find_node_by_alias(root, eth); + fixup_node = of_find_node_by_alias(root, eth); } - if (!node) { + if (!fixup_node) { pr_debug("%s: no node to fixup\n", __func__); return; } - ret = of_set_property(node, "mac-address", ethaddr, ETH_ALEN, 1); + ret = of_set_property(fixup_node, "mac-address", ethaddr, ETH_ALEN, 1); if (ret) pr_err("Setting mac-address property of %s failed with: %s\n", - node->full_name, strerror(-ret)); + fixup_node->full_name, strerror(-ret)); } static int eth_of_fixup(struct device_node *root, void *unused) @@ -355,7 +395,7 @@ static const char * const eth_mode_names[] = { int eth_register(struct eth_device *edev) { - struct device_d *dev = &edev->dev; + struct device *dev = &edev->dev; unsigned char ethaddr[ETH_ALEN]; int ret, found = 0; @@ -369,18 +409,23 @@ int eth_register(struct eth_device *edev) if (edev->parent) edev->dev.parent = edev->parent; - if (edev->dev.parent && edev->dev.parent->device_node) { - edev->dev.id = of_alias_get_id(edev->dev.parent->device_node, "ethernet"); + if (edev->dev.parent && edev->dev.parent->of_node) { + edev->dev.id = of_alias_get_id(edev->dev.parent->of_node, + "ethernet"); if (edev->dev.id < 0) edev->dev.id = DEVICE_ID_DYNAMIC; } else { edev->dev.id = DEVICE_ID_DYNAMIC; } + INIT_LIST_HEAD(&edev->send_queue); + ret = register_device(&edev->dev); if (ret) return ret; + slice_init(&edev->slice, dev_name(dev)); + edev->devname = xstrdup(dev_name(&edev->dev)); dev_add_param_ip(dev, "ipaddr", NULL, NULL, &edev->ipaddr, edev); @@ -416,23 +461,61 @@ int eth_register(struct eth_device *edev) register_preset_mac_address(edev, ethaddr); if (IS_ENABLED(CONFIG_OFDEVICE) && edev->parent && - edev->parent->device_node) - edev->nodepath = xstrdup(edev->parent->device_node->full_name); + edev->parent->of_node) + edev->nodepath = xstrdup(edev->parent->of_node->full_name); return 0; } +int eth_open(struct eth_device *edev) +{ + int ret; + + if (edev->active) + return 0; + + edev->last_link_check = 0; + + ret = edev->open(edev); + if (!ret) + edev->active = 1; + + return ret; +} + +void eth_close(struct eth_device *edev) +{ + if (!edev->active) + return; + + edev->halt(edev); + + edev->active = 0; +} + void eth_unregister(struct eth_device *edev) { + struct eth_q *q, *tmp; + if (edev->active) edev->halt(edev); + list_for_each_entry_safe(q, tmp, &edev->send_queue, list) { + if (q->edev != edev) + continue; + + list_del(&q->list); + free(q->data); + free(q); + } + if (IS_ENABLED(CONFIG_OFDEVICE)) free(edev->nodepath); free(edev->devname); unregister_device(&edev->dev); + slice_exit(&edev->slice); list_del(&edev->list); } @@ -441,3 +524,62 @@ void led_trigger_network(enum led_trigger trigger) led_trigger(trigger, TRIGGER_FLASH); led_trigger(LED_TRIGGER_NET_TXRX, TRIGGER_FLASH); } + +struct eth_device *of_find_eth_device_by_node(struct device_node *np) +{ + struct eth_device *edev; + int ret; + + ret = of_device_ensure_probed(np); + if (ret) + return NULL; + + list_for_each_entry(edev, &netdev_list, list) + if (edev->parent->of_node == np) + return edev; + return NULL; +} +EXPORT_SYMBOL(of_find_eth_device_by_node); + +void eth_open_all(void) +{ + struct eth_device *edev; + + list_for_each_entry(edev, &netdev_list, list) { + if (edev->global_mode == ETH_MODE_DISABLED) + continue; + eth_open(edev); + } +} + +static int populate_ethaddr(void) +{ + struct eth_device *edev; + bool generated = false; + int ret; + + list_for_each_entry(edev, &netdev_list, list) { + if (!edev->parent || is_valid_ether_addr(edev->ethaddr)) + continue; + + ret = of_get_mac_addr_nvmem(edev->parent->of_node, + edev->ethaddr); + if (IS_ENABLED(CONFIG_NET_ETHADDR_FROM_MACHINE_ID) && ret) { + ret = generate_ether_addr(edev->ethaddr, edev->dev.id); + generated = true; + } + if (ret) + continue; + + if (generated) + dev_notice(&edev->dev, "Generated MAC address from unique id: %pM\n", + edev->ethaddr); + else + dev_info(&edev->dev, "Got preset MAC address from NVMEM: %pM\n", + edev->ethaddr); + eth_set_ethaddr(edev, edev->ethaddr); + } + + return 0; +} +postenvironment_initcall(populate_ethaddr); diff --git a/net/fastboot.c b/net/fastboot.c new file mode 100644 index 0000000000..932eb05c43 --- /dev/null +++ b/net/fastboot.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Copyright 2020 Edmund Henniges <eh@emlix.com> + * Copyright 2020 Daniel Glöckner <dg@emlix.com> + * Ported from U-Boot to Barebox + */ + +#define pr_fmt(fmt) "net fastboot: " fmt + +#include <common.h> +#include <net.h> +#include <fastboot.h> +#include <fastboot_net.h> +#include <environment.h> +#include <progress.h> +#include <unistd.h> +#include <init.h> +#include <work.h> +#include <globalvar.h> +#include <magicvar.h> + +#define FASTBOOT_PORT 5554 +#define MAX_MTU 1500 +#define PACKET_SIZE (min(PKTSIZE, MAX_MTU + ETHER_HDR_SIZE) \ + - (net_eth_to_udp_payload(0) - (char *)0)) + +enum { + FASTBOOT_ERROR = 0, + FASTBOOT_QUERY = 1, + FASTBOOT_INIT = 2, + FASTBOOT_FASTBOOT = 3, +}; + +enum may_send { + MAY_NOT_SEND, + MAY_SEND_MESSAGE, + MAY_SEND_ACK, +}; + +struct __packed fastboot_header { + u8 id; + u8 flags; + u16 seq; +}; + +#define FASTBOOT_MAX_MSG_LEN 64 + +struct fastboot_net { + struct fastboot fastboot; + + struct net_connection *net_con; + struct fastboot_header response_header; + struct poller_struct poller; + struct work_queue wq; + u64 host_waits_since; + u64 last_download_pkt; + bool sequence_number_seen; + bool active_download; + bool reinit; + bool send_keep_alive; + enum may_send may_send; + + IPaddr_t host_addr; + u16 host_port; + u8 host_mac[ETH_ALEN]; + u16 sequence_number; + u16 last_payload_len; + void *last_payload; +}; + +static const ushort udp_version = 1; + +static bool is_current_connection(struct fastboot_net *fbn) +{ + return fbn->host_addr == net_read_ip(&fbn->net_con->ip->daddr) && + fbn->host_port == fbn->net_con->udp->uh_dport; +} + +static void fastboot_net_abort(struct fastboot_net *fbn) +{ + fbn->reinit = true; + + fastboot_abort(&fbn->fastboot); + + fbn->active_download = false; + + poller_unregister(&fbn->poller); + + /* + * If the host sends a data packet at a time when an empty packet was + * expected, fastboot_abort is called and an error message is sent. + * We don't want to execute the contents of the bad packet afterwards. + * Clearing command also tells our keep-alive poller to stop sending + * messages. + */ + wq_cancel_work(&fbn->wq); + + free(fbn->last_payload); + fbn->last_payload = NULL; +} + +static void fastboot_net_save_payload(struct fastboot_net *fbn, void *packet, + int len) +{ + /* Save packet for retransmitting */ + + fbn->last_payload_len = len; + free(fbn->last_payload); + fbn->last_payload = memdup(packet, len); +} + +static void fastboot_send(struct fastboot_net *fbn, + struct fastboot_header header, + const char *error_msg) +{ + short tmp; + uchar *packet = net_udp_get_payload(fbn->net_con); + uchar *packet_base = packet; + bool current_session = false; + + if (fbn->sequence_number == ntohs(header.seq) && + is_current_connection(fbn)) + current_session = true; + + if (error_msg) + header.id = FASTBOOT_ERROR; + else + error_msg = "no error"; + + /* send header */ + memcpy(packet, &header, sizeof(header)); + packet += sizeof(header); + + switch (header.id) { + case FASTBOOT_QUERY: + /* send sequence number */ + tmp = htons(fbn->sequence_number); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_INIT: + /* send udp version and packet size */ + tmp = htons(udp_version); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + tmp = htons(PACKET_SIZE); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_ERROR: + pr_err("%s\n", error_msg); + + /* send error message */ + tmp = strlen(error_msg); + memcpy(packet, error_msg, tmp); + packet += tmp; + + if (current_session) + fastboot_net_abort(fbn); + + break; + } + + if (current_session && header.id != FASTBOOT_QUERY) { + fbn->sequence_number++; + fbn->sequence_number_seen = false; + fastboot_net_save_payload(fbn, packet_base, packet - packet_base); + } + net_udp_send(fbn->net_con, packet - packet_base); +} + +static int fastboot_net_wait_may_send(struct fastboot_net *fbn) +{ + uint64_t start = get_time_ns(); + + while (!is_timeout(start, 2 * SECOND)) { + if (fbn->may_send != MAY_NOT_SEND) + return 0; + } + + return -ETIMEDOUT; +} + +static int fastboot_write_net(struct fastboot *fb, const char *buf, + unsigned int n) +{ + struct fastboot_net *fbn = container_of(fb, struct fastboot_net, + fastboot); + struct fastboot_header response_header; + uchar *packet; + uchar *packet_base; + int ret; + + if (fbn->reinit) + return 0; + + /* + * This function is either called in command context, in which + * case we may wait, or from the keepalive poller which explicitly + * only calls us when we don't have to wait here. + */ + ret = fastboot_net_wait_may_send(fbn); + if (ret) { + fastboot_net_abort(fbn); + return ret; + } + + if (n && fbn->may_send == MAY_SEND_ACK) { + fastboot_send(fbn, fbn->response_header, + "Have message but only ACK allowed"); + return -EPROTO; + } else if (!n && fbn->may_send == MAY_SEND_MESSAGE) { + fastboot_send(fbn, fbn->response_header, + "Want to send ACK but message expected"); + return -EPROTO; + } + + response_header = fbn->response_header; + response_header.flags = 0; + response_header.seq = htons(fbn->sequence_number); + ++fbn->sequence_number; + fbn->sequence_number_seen = false; + + packet = net_udp_get_payload(fbn->net_con); + packet_base = packet; + + /* Write headers */ + memcpy(packet, &response_header, sizeof(response_header)); + packet += sizeof(response_header); + /* Write response */ + memcpy(packet, buf, n); + packet += n; + + fastboot_net_save_payload(fbn, packet_base, packet - packet_base); + + memcpy(fbn->net_con->et->et_dest, fbn->host_mac, ETH_ALEN); + net_write_ip(&fbn->net_con->ip->daddr, fbn->host_addr); + fbn->net_con->udp->uh_dport = fbn->host_port; + + fbn->may_send = MAY_NOT_SEND; + + net_udp_send(fbn->net_con, fbn->last_payload_len); + + return 0; +} + +static void fastboot_start_download_net(struct fastboot *fb) +{ + struct fastboot_net *fbn = container_of(fb, struct fastboot_net, + fastboot); + + fastboot_start_download_generic(fb); + fbn->active_download = true; + fbn->last_download_pkt = get_time_ns(); +} + +/* must send exactly one packet on all code paths */ +static void fastboot_data_download(struct fastboot_net *fbn, + const void *fastboot_data, + unsigned int fastboot_data_len) +{ + int ret; + + if (fastboot_data_len == 0 || + (fbn->fastboot.download_bytes + fastboot_data_len) > + fbn->fastboot.download_size) { + fastboot_send(fbn, fbn->response_header, + "Received invalid data length"); + return; + } + + ret = fastboot_handle_download_data(&fbn->fastboot, fastboot_data, + fastboot_data_len); + if (ret < 0) { + fastboot_send(fbn, fbn->response_header, strerror(-ret)); + return; + } + + fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_NONE, ""); +} + +struct fastboot_work { + struct work_struct work; + struct fastboot_net *fbn; + bool download_finished; + char command[FASTBOOT_MAX_CMD_LEN + 1]; +}; + +static void fastboot_handle_type_fastboot(struct fastboot_net *fbn, + struct fastboot_header header, + char *fastboot_data, + unsigned int fastboot_data_len) +{ + struct fastboot_work *w; + + fbn->response_header = header; + fbn->host_waits_since = get_time_ns(); + fbn->may_send = fastboot_data_len ? MAY_SEND_ACK : MAY_SEND_MESSAGE; + + if (fbn->active_download) { + fbn->last_download_pkt = get_time_ns(); + + if (!fastboot_data_len && fbn->fastboot.download_bytes + == fbn->fastboot.download_size) { + + fbn->active_download = false; + + w = xzalloc(sizeof(*w)); + w->fbn = fbn; + w->download_finished = true; + + wq_queue_work(&fbn->wq, &w->work); + } else { + fastboot_data_download(fbn, fastboot_data, + fastboot_data_len); + } + return; + } + + if (fastboot_data_len > FASTBOOT_MAX_CMD_LEN) { + fastboot_send(fbn, header, "command too long"); + return; + } + + if (!list_empty(&fbn->wq.work)) + return; + + if (fastboot_data_len) { + w = xzalloc(sizeof(*w)); + w->fbn = fbn; + memcpy(w->command, fastboot_data, fastboot_data_len); + w->command[fastboot_data_len] = 0; + + wq_queue_work(&fbn->wq, &w->work); + } +} + +static void fastboot_check_retransmit(struct fastboot_net *fbn, + struct fastboot_header header) +{ + if (ntohs(header.seq) == fbn->sequence_number - 1 && + is_current_connection(fbn)) { + /* Retransmit last sent packet */ + memcpy(net_udp_get_payload(fbn->net_con), + fbn->last_payload, fbn->last_payload_len); + net_udp_send(fbn->net_con, fbn->last_payload_len); + } +} + +static void fastboot_handler(void *ctx, char *packet, unsigned int raw_len) +{ + unsigned int len = net_eth_to_udplen(packet); + struct ethernet *eth_header = (struct ethernet *)packet; + struct iphdr *ip_header = net_eth_to_iphdr(packet); + struct udphdr *udp_header = net_eth_to_udphdr(packet); + char *payload = net_eth_to_udp_payload(packet); + struct fastboot_net *fbn = ctx; + struct fastboot_header header; + char *fastboot_data = payload + sizeof(header); + u16 tot_len = ntohs(ip_header->tot_len); + int ret; + + /* catch bogus tot_len values */ + if ((char *)ip_header - packet + tot_len > raw_len) + return; + + /* catch packets split into fragments that are too small to reply */ + if (fastboot_data - (char *)ip_header > tot_len) + return; + + /* catch packets too small to be valid */ + if (len < sizeof(struct fastboot_header)) + return; + + memcpy(&header, payload, sizeof(header)); + header.flags = 0; + len -= sizeof(header); + + /* catch remaining fragmented packets */ + if (fastboot_data - (char *)ip_header + len > tot_len) { + fastboot_send(fbn, header, + "can't reassemble fragmented frames"); + return; + } + /* catch too large packets */ + if (len > PACKET_SIZE) { + fastboot_send(fbn, header, "packet too large"); + return; + } + + memcpy(fbn->net_con->et->et_dest, eth_header->et_src, ETH_ALEN); + net_copy_ip(&fbn->net_con->ip->daddr, &ip_header->saddr); + fbn->net_con->udp->uh_dport = udp_header->uh_sport; + + switch (header.id) { + case FASTBOOT_QUERY: + fastboot_send(fbn, header, NULL); + break; + case FASTBOOT_INIT: + if (ntohs(header.seq) != fbn->sequence_number) { + fastboot_check_retransmit(fbn, header); + break; + } + fbn->host_addr = net_read_ip(&ip_header->saddr); + fbn->host_port = udp_header->uh_sport; + memcpy(fbn->host_mac, eth_header->et_src, ETH_ALEN); + fastboot_net_abort(fbn); + /* poller just unregistered in fastboot_net_abort() */ + ret = poller_register(&fbn->poller, "fastboot"); + if (ret) { + pr_err("Cannot register poller: %s\n", strerror(-ret)); + return; + } + fastboot_send(fbn, header, NULL); + break; + case FASTBOOT_FASTBOOT: + if (!is_current_connection(fbn)) + break; + memcpy(fbn->host_mac, eth_header->et_src, ETH_ALEN); + + if (ntohs(header.seq) != fbn->sequence_number) { + fastboot_check_retransmit(fbn, header); + } else if (!fbn->sequence_number_seen) { + fbn->sequence_number_seen = true; + fastboot_handle_type_fastboot(fbn, header, + fastboot_data, len); + } + break; + default: + fastboot_send(fbn, header, "unknown packet type"); + break; + } +} + +static void fastboot_do_work(struct work_struct *w) +{ + struct fastboot_work *fw = container_of(w, struct fastboot_work, work); + struct fastboot_net *fbn = fw->fbn; + + if (fw->download_finished) { + fastboot_download_finished(&fbn->fastboot); + goto out; + } + + fbn->reinit = false; + fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_NONE, ""); + + fbn->send_keep_alive = true; + + fastboot_exec_cmd(&fbn->fastboot, fw->command); + fbn->send_keep_alive = false; +out: + free(fw); +} + +static void fastboot_work_cancel(struct work_struct *w) +{ + struct fastboot_work *fw = container_of(w, struct fastboot_work, work); + + free(fw); +} + +static void fastboot_poll(struct poller_struct *poller) +{ + struct fastboot_net *fbn = container_of(poller, struct fastboot_net, + poller); + + if (fbn->active_download) { + net_poll(); + if (is_timeout(fbn->last_download_pkt, 5 * SECOND)) { + pr_err("No progress for 5s, aborting\n"); + fastboot_net_abort(fbn); + return; + } + } + + if (!fbn->send_keep_alive) + return; + + if (!is_timeout(fbn->host_waits_since, 30ULL * SECOND)) + return; + + if (fbn->may_send != MAY_SEND_MESSAGE) + return; + + fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_INFO, "still busy"); +} + +void fastboot_net_free(struct fastboot_net *fbn) +{ + fastboot_generic_close(&fbn->fastboot); + net_unregister(fbn->net_con); + fastboot_generic_free(&fbn->fastboot); + wq_unregister(&fbn->wq); + free(fbn->last_payload); + free(fbn); +} + +struct fastboot_net *fastboot_net_init(struct fastboot_opts *opts) +{ + struct fastboot_net *fbn; + bool bbu = get_fastboot_bbu(); + int ret; + + fbn = xzalloc(sizeof(*fbn)); + fbn->fastboot.write = fastboot_write_net; + fbn->fastboot.start_download = fastboot_start_download_net; + + if (opts) { + fbn->fastboot.files = opts->files; + fbn->fastboot.cmd_exec = opts->cmd_exec; + fbn->fastboot.cmd_flash = opts->cmd_flash; + ret = fastboot_generic_init(&fbn->fastboot, opts->export_bbu); + } else { + fbn->fastboot.files = get_fastboot_partitions() ?: file_list_parse(""); + ret = fastboot_generic_init(&fbn->fastboot, bbu); + } + if (ret) + goto fail_generic_init; + + fbn->net_con = net_udp_new(IP_BROADCAST, FASTBOOT_PORT, + fastboot_handler, fbn); + if (IS_ERR(fbn->net_con)) { + ret = PTR_ERR(fbn->net_con); + goto fail_net_con; + } + net_udp_bind(fbn->net_con, FASTBOOT_PORT); + + eth_open(fbn->net_con->edev); + + fbn->poller.func = fastboot_poll; + + fbn->wq.fn = fastboot_do_work; + fbn->wq.cancel = fastboot_work_cancel; + + wq_register(&fbn->wq); + + return fbn; + +fail_net_con: + fastboot_generic_free(&fbn->fastboot); +fail_generic_init: + free(fbn); + return ERR_PTR(ret); +} + +static struct fastboot_net *fastboot_net_obj; +static int fastboot_net_autostart; + +static int fastboot_net_autostart_set(struct param_d *p, void * priv) +{ + struct fastboot_net *fbn; + static bool started; + + if (!fastboot_net_autostart || started) + return 0; + + ifup_all(0); + fbn = fastboot_net_init(NULL); + + if (IS_ERR(fbn)) + return PTR_ERR(fbn); + + fastboot_net_obj = fbn; + started = true; + + return 0; +} + +static int fastboot_net_init_globalvar(void) +{ + return globalvar_add_bool("fastboot.net.autostart", + fastboot_net_autostart_set, + &fastboot_net_autostart, NULL); +} + +static void fastboot_net_exit(void) +{ + if (fastboot_net_obj) + fastboot_net_free(fastboot_net_obj); +} + +postenvironment_initcall(fastboot_net_init_globalvar); +predevshutdown_exitcall(fastboot_net_exit); + +BAREBOX_MAGICVAR(global.fastboot.net.autostart, + "If true, automatically start fastboot over UDP during startup"); diff --git a/net/ifup.c b/net/ifup.c index d550f82530..5b92ee794d 100644 --- a/net/ifup.c +++ b/net/ifup.c @@ -1,26 +1,14 @@ -/* - * ifup.c - bring up network interfaces - * - * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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 detaiifup. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + +/* ifup.c - bring up network interfaces */ + #define pr_fmt(fmt) "ifup: " fmt #include <environment.h> #include <command.h> #include <common.h> +#include <complete.h> #include <getopt.h> #include <dhcp.h> #include <net.h> @@ -29,7 +17,6 @@ #include <string.h> #include <driver.h> #include <init.h> -#include <globalvar.h> #include <magicvar.h> #include <linux/stat.h> @@ -98,7 +85,7 @@ static int source_env_network(struct eth_device *edev) env_push_context(); for (i = 0; i < ARRAY_SIZE(vars); i++) - setenv(vars[i], ""); + unsetenv(vars[i]); cmd = basprintf("source /env/network/%s", edev->devname); ret = run_command(cmd); @@ -171,8 +158,8 @@ out: static void set_linux_bootarg(struct eth_device *edev) { + char *bootarg; if (edev->global_mode == ETH_MODE_STATIC) { - char *bootarg; IPaddr_t serverip; IPaddr_t gateway; @@ -188,10 +175,36 @@ static void set_linux_bootarg(struct eth_device *edev) dev_set_param(&edev->dev, "linux.bootargs", bootarg); free(bootarg); } else if (edev->global_mode == ETH_MODE_DHCP) { - dev_set_param(&edev->dev, "linux.bootargs", "ip=dhcp"); + bootarg = basprintf("ip=::::%s:%s:dhcp", + barebox_get_hostname(), + edev->linuxdevname ? edev->linuxdevname : ""); + dev_set_param(&edev->dev, "linux.bootargs", bootarg); + free(bootarg); } } +static int ifup_edev_conf(struct eth_device *edev, unsigned flags) +{ + int ret; + + if (edev->global_mode == ETH_MODE_DHCP) { + if (IS_ENABLED(CONFIG_NET_DHCP)) { + ret = dhcp(edev, NULL); + } else { + dev_err(&edev->dev, "DHCP support not available\n"); + ret = -ENOSYS; + } + if (ret) + return ret; + } + + set_linux_bootarg(edev); + + edev->ifup = true; + + return 0; +} + int ifup_edev(struct eth_device *edev, unsigned flags) { int ret; @@ -214,22 +227,20 @@ int ifup_edev(struct eth_device *edev, unsigned flags) if (ret) return ret; - if (edev->global_mode == ETH_MODE_DHCP) { - if (IS_ENABLED(CONFIG_NET_DHCP)) { - ret = dhcp(edev, NULL); - } else { - dev_err(&edev->dev, "DHCP support not available\n"); - ret = -ENOSYS; - } - if (ret) - return ret; - } + ret = eth_open(edev); + if (ret) + return ret; - set_linux_bootarg(edev); + if (flags & IFUP_FLAG_SKIP_CONF) + return 1; - edev->ifup = true; + return ifup_edev_conf(edev, flags); +} - return 0; +void ifdown_edev(struct eth_device *edev) +{ + eth_close(edev); + edev->ifup = false; } int ifup(const char *ethname, unsigned flags) @@ -248,11 +259,86 @@ int ifup(const char *ethname, unsigned flags) return ifup_edev(edev, flags); } +int ifdown(const char *ethname) +{ + struct eth_device *edev; + + edev = eth_get_byname(ethname); + if (!edev) + return -ENODEV; + + ifdown_edev(edev); + + return 0; +} + static int net_ifup_force_detect; -int ifup_all(unsigned flags) +static bool ifup_edev_need_conf(struct eth_device *edev) +{ + return edev->active && !edev->ifup && + edev->global_mode != ETH_MODE_DISABLED; +} + +static int __ifup_all_parallel(unsigned flags) { struct eth_device *edev; + unsigned netdev_count = 0; + u64 start; + int ret; + + for_each_netdev(edev) { + ret = ifup_edev(edev, flags | IFUP_FLAG_SKIP_CONF); + if (ret == 1) + netdev_count++; + } + + start = get_time_ns(); + while (netdev_count && !is_timeout(start, PHY_AN_TIMEOUT * SECOND)) { + for_each_netdev(edev) { + if ((flags & IFUP_FLAG_UNTIL_NET_SERVER) && net_get_server()) + return 0; + + if (ctrlc()) + return -EINTR; + + if (!ifup_edev_need_conf(edev)) + continue; + + ret = eth_carrier_poll_once(edev); + if (ret) + continue; + + ifup_edev_conf(edev, flags); + if (!edev->ifup) + continue; + + netdev_count--; + } + } + + return 0; +} + +static int __ifup_all_sequence(unsigned flags) +{ + struct eth_device *edev; + + for_each_netdev(edev) { + if ((flags & IFUP_FLAG_UNTIL_NET_SERVER) && net_get_server()) + return 0; + + if (ctrlc()) + return -EINTR; + + ifup_edev(edev, flags); + } + + return 0; +} + +int ifup_all(unsigned flags) +{ DIR *dir; struct dirent *d; @@ -275,10 +361,28 @@ int ifup_all(unsigned flags) list_empty(&netdev_list)) device_detect_all(); - for_each_netdev(edev) - ifup_edev(edev, flags); + /* + * In the future, we may add an iproute -r option that tries to + * resolve $global.net.server using each interface. For now, + * we only support the special case of $global.net.server being + * empty, i.e. the first DHCP lease setting $global.net.server + * will be what we're going with. + */ + if (net_get_server() && !net_get_gateway()) + flags &= ~IFUP_FLAG_UNTIL_NET_SERVER; + + if (flags & IFUP_FLAG_PARALLEL) + return __ifup_all_parallel(flags); + else + return __ifup_all_sequence(flags); +} - return 0; +void ifdown_all(void) +{ + struct eth_device *edev; + + for_each_netdev(edev) + ifdown_edev(edev); } static int ifup_all_init(void) @@ -289,23 +393,28 @@ static int ifup_all_init(void) } late_initcall(ifup_all_init); -BAREBOX_MAGICVAR_NAMED(global_net_ifup_force_detect, - global.net.ifup_force_detect, - "net: force detection of devices on ifup -a"); +BAREBOX_MAGICVAR(global.net.ifup_force_detect, + "net: force detection of devices on ifup -a"); #if IS_ENABLED(CONFIG_NET_CMD_IFUP) static int do_ifup(int argc, char *argv[]) { int opt; - unsigned flags = 0; + unsigned flags = IFUP_FLAG_PARALLEL; int all = 0; - while ((opt = getopt(argc, argv, "af")) > 0) { + while ((opt = getopt(argc, argv, "asf1")) > 0) { switch (opt) { case 'f': flags |= IFUP_FLAG_FORCE; break; + case '1': + flags |= IFUP_FLAG_UNTIL_NET_SERVER; + break; + case 's': + flags &= ~IFUP_FLAG_PARALLEL; + break; case 'a': all = 1; break; @@ -329,15 +438,60 @@ BAREBOX_CMD_HELP_TEXT("/env/network/<intf> file. See Documentation/user/networki BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("Options:") BAREBOX_CMD_HELP_OPT ("-a", "bring up all interfaces") +BAREBOX_CMD_HELP_OPT ("-s", "bring up interfaces in sequence, not in parallel") BAREBOX_CMD_HELP_OPT ("-f", "Force. Configure even if ip already set") +BAREBOX_CMD_HELP_OPT ("-1", "Early exit if DHCP sets $global.net.server") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(ifup) .cmd = do_ifup, BAREBOX_CMD_DESC("bring a network interface up") - BAREBOX_CMD_OPTS("[-af] [INTF]") + BAREBOX_CMD_OPTS("[-asf1] [INTF]") BAREBOX_CMD_GROUP(CMD_GRP_NET) + BAREBOX_CMD_COMPLETE(eth_complete) BAREBOX_CMD_HELP(cmd_ifup_help) BAREBOX_CMD_END +static int do_ifdown(int argc, char *argv[]) +{ + int opt; + int all = 0; + + while ((opt = getopt(argc, argv, "a")) > 0) { + switch (opt) { + case 'a': + all = 1; + break; + } + } + + if (all) { + ifdown_all(); + return 0; + } + + if (argc == optind) + return COMMAND_ERROR_USAGE; + + return ifdown(argv[optind]); +} + + + +BAREBOX_CMD_HELP_START(ifdown) +BAREBOX_CMD_HELP_TEXT("Disable a network interface") +BAREBOX_CMD_HELP_TEXT("") +BAREBOX_CMD_HELP_TEXT("Options:") +BAREBOX_CMD_HELP_OPT ("-a", "disable all interfaces") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(ifdown) + .cmd = do_ifdown, + BAREBOX_CMD_DESC("disable a network interface") + BAREBOX_CMD_OPTS("[-a] [INTF]") + BAREBOX_CMD_GROUP(CMD_GRP_NET) + BAREBOX_CMD_COMPLETE(eth_complete) + BAREBOX_CMD_HELP(cmd_ifdown_help) +BAREBOX_CMD_END + #endif @@ -1,27 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2015 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix +// SPDX-FileCopyrightText: 1994-2000 Neil Russell +// SPDX-FileCopyrightText: 2000 Roland Borde +// SPDX-FileCopyrightText: 2000 Paolo Scaffardi +// SPDX-FileCopyrightText: 2000-2002 Wolfgang Denk <wd@denx.de> + /* * net.c - barebox networking support * - * Copyright (c) 2015 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * * based on U-Boot (LiMon) code - * - * Copyright 1994 - 2000 Neil Russell. - * Copyright 2000 Roland Borde - * Copyright 2000 Paolo Scaffardi - * Copyright 2000-2002 Wolfgang Denk, wd@denx.de - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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. - * */ #include <common.h> @@ -50,13 +37,6 @@ int string_to_ethaddr(const char *str, u8 enetaddr[ETH_ALEN]) return 0; } -void ethaddr_to_string(const u8 enetaddr[ETH_ALEN], char *str) -{ - sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", - enetaddr[0], enetaddr[1], enetaddr[2], enetaddr[3], - enetaddr[4], enetaddr[5]); -} - int string_to_ip(const char *s, IPaddr_t *ip) { IPaddr_t addr = 0; @@ -1,27 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix +// SPDX-FileCopyrightText: 1994-2000 Neil Russell +// SPDX-FileCopyrightText: 2000 Roland Borde +// SPDX-FileCopyrightText: 2000 Paolo Scaffardi +// SPDX-FileCopyrightText: 2000-2002 Wolfgang Denk <wd@denx.de> + /* * net.c - barebox networking support * - * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * * based on U-Boot (LiMon) code - * - * Copyright 1994 - 2000 Neil Russell. - * Copyright 2000 Roland Borde - * Copyright 2000 Paolo Scaffardi - * Copyright 2000-2002 Wolfgang Denk, wd@denx.de - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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. - * */ #define pr_fmt(fmt) "net: " fmt @@ -38,10 +25,10 @@ #include <init.h> #include <globalvar.h> #include <magicvar.h> +#include <machine_id.h> #include <linux/ctype.h> #include <linux/err.h> -unsigned char *NetRxPackets[PKTBUFSRX]; /* Receive packets */ static unsigned int net_ip_id; char *net_server; @@ -254,8 +241,58 @@ static int arp_request(struct eth_device *edev, IPaddr_t dest, unsigned char *et void net_poll(void) { + static bool in_net_poll; + + if (in_net_poll) + return; + + in_net_poll = true; + eth_rx(); + + in_net_poll = false; +} + +static void __net_poll(struct poller_struct *poller) +{ + static uint64_t last; + + /* + * USB network controllers take a long time in the receive path, + * so limit the polling rate to once per 10ms. This is due to + * deficiencies in the barebox USB stack: We can't queue URBs and + * receive a callback when they are done. Instead, we always + * synchronously queue an URB and wait for its completion. In case + * of USB network adapters the only way to detect if packets have + * been received is to queue a RX URB and see if it completes (in + * which case we have received data) or if it timeouts (no data + * available). The timeout can't be arbitrarily small, 2ms is the + * smallest we can do with the 1ms USB frame size. + * + * Given that we do a mixture of polling-as-fast-as-possible when + * we are waiting for network traffic (tftp, nfs and other users + * actively calling net_poll()) and doing a low frequency polling + * here to still get packets when no user is actively waiting for + * incoming packets. This is used to receive incoming ping packets + * and to get fastboot over ethernet going. + */ + if (!is_timeout(last, 10 * MSECOND)) + return; + + net_poll(); + + last = get_time_ns(); +} + +static struct poller_struct net_poller = { + .func = __net_poll, +}; + +static int init_net_poll(void) +{ + return poller_register(&net_poller, "net"); } +device_initcall(init_net_poll); static uint16_t net_udp_new_localport(void) { @@ -288,9 +325,14 @@ void net_set_serverip(IPaddr_t ip) net_server = xasprintf("%pI4", &ip); } +const char *net_get_server(void) +{ + return net_server && *net_server ? net_server : NULL; +} + void net_set_serverip_empty(IPaddr_t ip) { - if (net_server && *net_server) + if (net_get_server()) return; net_set_serverip(ip); @@ -323,6 +365,43 @@ IPaddr_t net_get_gateway(void) static LIST_HEAD(connection_list); +/** + * generate_ether_addr - Generates stable software assigned Ethernet address + * @addr: Pointer to a six-byte array to contain the Ethernet address + * @ethid: index of the Ethernet interface + * + * Derives an Ethernet address (MAC) from the machine ID, that's stable + * per board that is not multicast and has the local assigned bit set. + * + * Return 0 if an address could be generated or a negative error code otherwise. + */ +int generate_ether_addr(u8 *ethaddr, int ethid) +{ + const char *hostname; + uuid_t id; + int ret; + + if (!IS_ENABLED(CONFIG_NET_ETHADDR_FROM_MACHINE_ID)) + return -ENOSYS; + + hostname = barebox_get_hostname(); + if (!hostname) + return -EINVAL; + + ret = machine_id_get_app_specific(&id, ARRAY_AND_SIZE("barebox-macaddr:"), + hostname, strlen(hostname), NULL); + if (ret) + return ret; + + memcpy(ethaddr, &id.b, ETH_ALEN); + eth_addr_add(ethaddr, ethid); + + ethaddr[0] &= 0xfe; /* clear multicast bit */ + ethaddr[0] |= 0x02; /* set local assignment bit (IEEE802) */ + + return 0; +} + static struct net_connection *net_new(struct eth_device *edev, IPaddr_t dest, rx_handler_f *handler, void *ctx) { @@ -338,10 +417,13 @@ static struct net_connection *net_new(struct eth_device *edev, IPaddr_t dest, } if (!is_valid_ether_addr(edev->ethaddr)) { - char str[sizeof("xx:xx:xx:xx:xx:xx")]; - random_ether_addr(edev->ethaddr); - ethaddr_to_string(edev->ethaddr, str); - dev_warn(&edev->dev, "No MAC address set. Using random address %s\n", str); + ret = generate_ether_addr(edev->ethaddr, edev->dev.id); + if (ret) + random_ether_addr(edev->ethaddr); + + dev_warn(&edev->dev, "No MAC address set. Using %s %pM\n", + ret == 1 ? "address computed from unique ID" : "random address", + edev->ethaddr); eth_set_ethaddr(edev, edev->ethaddr); } @@ -383,7 +465,7 @@ static struct net_connection *net_new(struct eth_device *edev, IPaddr_t dest, return con; out: - free(con->packet); + net_free_packet(con->packet); free(con); return ERR_PTR(ret); } @@ -428,7 +510,7 @@ struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler, void net_unregister(struct net_connection *con) { list_del(&con->list); - free(con->packet); + net_free_packet(con->packet); free(con); } @@ -482,7 +564,7 @@ static int net_answer_arp(struct eth_device *edev, unsigned char *pkt, int len) return 0; memcpy(packet, pkt, ETHER_HDR_SIZE + ARP_HDR_SIZE); ret = eth_send(edev, packet, ETHER_HDR_SIZE + ARP_HDR_SIZE); - free(packet); + net_free_packet(packet); return ret; } @@ -540,8 +622,6 @@ static int net_handle_arp(struct eth_device *edev, unsigned char *pkt, int len) return -EINVAL; } - return 0; - bad: net_bad_packet(pkt, len); return -EINVAL; @@ -565,12 +645,96 @@ static int net_handle_udp(unsigned char *pkt, int len) return -EINVAL; } -static int net_handle_icmp(unsigned char *pkt, int len) +static struct iphdr *ip_verify_size(unsigned char *pkt, int *total_len_nic) +{ + struct iphdr *ip = (struct iphdr *)(pkt + ETHER_HDR_SIZE); + int total_len_pkt; + + total_len_pkt = ntohs(ip->tot_len) + ETHER_HDR_SIZE; + + /* Only trust the packet's size if it's within bounds */ + if (*total_len_nic < sizeof(struct ethernet) + sizeof(struct iphdr) || + *total_len_nic < total_len_pkt) { + net_bad_packet(pkt, *total_len_nic); + return NULL; + } + +#ifdef DEBUG + /* Hitting this warning means we have trailing bytes after the IP + * payload that are not needed for padding. + * + * This may be an indication that the NIC driver is doing funny + * offloading stuff it shouldn't, but can also mean that some sender + * in the network likes to waste bit time for nought. + * + * We can't differentiate between the two, so we just print the + * warning when DEBUG is defined, so developers may investigate the + * reason without annoying users about something that might not even + * be barebox's fault. + */ + if (WARN_ON_ONCE(*total_len_nic > total_len_pkt && + *total_len_nic > 64)) { + net_bad_packet(pkt, *total_len_nic); + } +#endif + + *total_len_nic = total_len_pkt; + return ip; +} + +static int ping_reply(struct eth_device *edev, unsigned char *pkt, int len) +{ + struct ethernet *et = (struct ethernet *)pkt; + struct icmphdr *icmp; + struct iphdr *ip; + unsigned char *packet; + int ret; + + memcpy(et->et_dest, et->et_src, 6); + memcpy(et->et_src, edev->ethaddr, 6); + et->et_protlen = htons(PROT_IP); + + icmp = net_eth_to_icmphdr(pkt); + + ip = ip_verify_size(pkt, &len); + if (!ip) + return -EILSEQ; + + icmp->type = ICMP_ECHO_REPLY; + icmp->checksum = 0; + icmp->checksum = ~net_checksum((unsigned char *)icmp, + len - sizeof(struct iphdr) - ETHER_HDR_SIZE); + ip->check = 0; + ip->frag_off = htons(0x4000); + ip->ttl = 255; + net_copy_ip((void *)&ip->daddr, &ip->saddr); + net_copy_ip((void *)&ip->saddr, &edev->ipaddr); + ip->check = ~net_checksum((unsigned char *)ip, sizeof(struct iphdr)); + + packet = net_alloc_packet(); + if (!packet) + return 0; + + memcpy(packet, pkt, len); + + ret = eth_send(edev, packet, len); + + net_free_packet(packet); + + return ret; +} + +static int net_handle_icmp(struct eth_device *edev, unsigned char *pkt, int len) { struct net_connection *con; + struct icmphdr *icmp; pr_debug("%s\n", __func__); + icmp = net_eth_to_icmphdr(pkt); + if (icmp->type == ICMP_ECHO_REQUEST) + ping_reply(edev, pkt, len); + list_for_each_entry(con, &connection_list, list) { if (con->proto == IPPROTO_ICMP) { con->handler(con->priv, pkt, len); @@ -587,16 +751,21 @@ static int net_handle_ip(struct eth_device *edev, unsigned char *pkt, int len) pr_debug("%s\n", __func__); - if (len < sizeof(struct ethernet) + sizeof(struct iphdr) || - len < ETHER_HDR_SIZE + ntohs(ip->tot_len)) { + ip = ip_verify_size(pkt, &len); + if (!ip) { pr_debug("%s: bad len\n", __func__); - goto bad; + return 0; } if ((ip->hl_v & 0xf0) != 0x40) goto bad; - if (ip->frag_off & htons(0x1fff)) /* Can't deal w/ fragments */ + /* Can't deal w/ fragments. + * Either a fragment offset (13 bits), or + * MF (More Fragments) from fragment flags (3 bits). + * MF - because first fragment has fragment offset 0 + */ + if (ip->frag_off & htons(0x3fff)) goto bad; if (!net_checksum_ok((unsigned char *)ip, sizeof(struct iphdr))) goto bad; @@ -607,7 +776,7 @@ static int net_handle_ip(struct eth_device *edev, unsigned char *pkt, int len) switch (ip->protocol) { case IPPROTO_ICMP: - return net_handle_icmp(pkt, len); + return net_handle_icmp(edev, pkt, len); case IPPROTO_UDP: return net_handle_udp(pkt, len); } @@ -631,6 +800,20 @@ int net_receive(struct eth_device *edev, unsigned char *pkt, int len) goto out; } + if (edev->rx_monitor) + edev->rx_monitor(edev, pkt, len); + + if (edev->rx_preprocessor) { + ret = edev->rx_preprocessor(edev, &pkt, &len); + if (ret == -ENOMSG) + return 0; + if (ret) { + pr_debug("%s: rx_preprocessor failed %pe\n", __func__, + ERR_PTR(ret)); + return ret; + } + } + switch (et_protlen) { case PROT_ARP: ret = net_handle_arp(edev, pkt, len); @@ -647,13 +830,32 @@ out: return ret; } -static int net_init(void) +void net_free_packets(void **packets, unsigned count) +{ + while (count-- > 0) + net_free_packet(packets[count]); +} + +int net_alloc_packets(void **packets, int count) { + void *packet; int i; - for (i = 0; i < PKTBUFSRX; i++) - NetRxPackets[i] = net_alloc_packet(); + for (i = 0; i < count; i++) { + packet = net_alloc_packet(); + if (!packet) + goto free; + packets[i] = packet; + } + + return 0; +free: + net_free_packets(packets, i); + return -ENOMEM; +} +static int net_init(void) +{ globalvar_add_simple_ip("net.nameserver", &net_nameserver); globalvar_add_simple_string("net.domainname", &net_domainname); globalvar_add_simple_string("net.server", &net_server); @@ -664,6 +866,6 @@ static int net_init(void) postcore_initcall(net_init); -BAREBOX_MAGICVAR_NAMED(global_net_nameserver, global.net.nameserver, "The DNS server used for resolving host names"); -BAREBOX_MAGICVAR_NAMED(global_net_domainname, global.net.domainname, "Domain name used for DNS requests"); -BAREBOX_MAGICVAR_NAMED(global_net_server, global.net.server, "Standard server used for NFS/TFTP"); +BAREBOX_MAGICVAR(global.net.nameserver, "The DNS server used for resolving host names"); +BAREBOX_MAGICVAR(global.net.domainname, "Domain name used for DNS requests"); +BAREBOX_MAGICVAR(global.net.server, "Standard server used for NFS/TFTP"); diff --git a/net/netconsole.c b/net/netconsole.c index ef8b1856b6..43f78997b8 100644 --- a/net/netconsole.c +++ b/net/netconsole.c @@ -1,21 +1,7 @@ -/* - * netconsole.c - network console support - * - * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + +/* netconsole.c - network console support */ #define pr_fmt(fmt) "netconsole: " fmt @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* * NFS support driver - based on etherboot and barebox's tftp.c * @@ -29,7 +31,6 @@ #include <malloc.h> #include <libgen.h> #include <fs.h> -#include <libgen.h> #include <fcntl.h> #include <errno.h> #include <progress.h> @@ -533,7 +534,7 @@ static int nfs_readlink_reply(unsigned char *pkt, unsigned len) static int nfs_read_reply(unsigned char *pkt, unsigned len) { - int rlen; + unsigned int rlen; uint32_t *data; int ret; @@ -552,6 +553,8 @@ static int nfs_read_reply(unsigned char *pkt, unsigned len) rlen = ntohl(net_read_uint32(data + 18)); + rlen = max_t(unsigned int, rlen, len - 19); + ret = write(net_store_fd, (char *)(data + 19), rlen); if (ret < 0) { perror("write"); diff --git a/net/nfs.h b/net/nfs.h deleted file mode 100644 index 7c7a648e46..0000000000 --- a/net/nfs.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * (C) Masami Komiya <mkomiya@sonare.it> 2004 - * - * This program 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 2, or (at - * your option) any later version. - */ - -#ifndef __NFS_H__ -#define __NFS_H__ - -#define SUNRPC_PORT 111 - -#define PROG_PORTMAP 100000 -#define PROG_NFS 100003 -#define PROG_MOUNT 100005 - -#define MSG_CALL 0 -#define MSG_REPLY 1 - -#define PORTMAP_GETPORT 3 - -#define MOUNT_ADDENTRY 1 -#define MOUNT_UMOUNTALL 4 - -#define NFS_LOOKUP 4 -#define NFS_READLINK 5 -#define NFS_READ 6 - -#define NFS_FHSIZE 32 - -#define NFSERR_PERM 1 -#define NFSERR_NOENT 2 -#define NFSERR_ACCES 13 -#define NFSERR_ISDIR 21 -#define NFSERR_INVAL 22 - -/* Block size used for NFS read accesses. A RPC reply packet (including all - * headers) must fit within a single Ethernet frame to avoid fragmentation. - * Chosen to be a power of two, as most NFS servers are optimized for this. */ -#define NFS_READ_SIZE 1024 - -#define NFS_MAXLINKDEPTH 16 - -struct rpc_t { - union { - uint8_t data[2048]; - struct { - uint32_t id; - uint32_t type; - uint32_t rpcvers; - uint32_t prog; - uint32_t vers; - uint32_t proc; - uint32_t data[1]; - } call; - struct { - uint32_t id; - uint32_t type; - uint32_t rstatus; - uint32_t verifier; - uint32_t v2; - uint32_t astatus; - uint32_t data[19]; - } reply; - } u; -}; -extern void NfsStart (char *); /* Begin NFS */ - - -/**********************************************************************/ - -#endif /* __NFS_H__ */ diff --git a/net/ping.c b/net/ping.c index a71f59a805..7b319e532e 100644 --- a/net/ping.c +++ b/net/ping.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <command.h> #include <clock.h> diff --git a/net/rarp.h b/net/rarp.h deleted file mode 100644 index b5b222ee3e..0000000000 --- a/net/rarp.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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. - * - */ - - -#ifndef __RARP_H__ -#define __RARP_H__ - -#ifndef __NET_H__ -#include <net.h> -#endif /* __NET_H__ */ - - -/**********************************************************************/ -/* - * Global functions and variables. - */ - -extern int RarpTry; - -extern void RarpRequest (void); /* Send a RARP request */ - -/**********************************************************************/ - -#endif /* __RARP_H__ */ diff --git a/net/sntp.c b/net/sntp.c index b4e6d6439c..08da012f36 100644 --- a/net/sntp.c +++ b/net/sntp.c @@ -1,18 +1,9 @@ -/* - * This program 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; version 2. - * - * This program 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. - */ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <asm/byteorder.h> #include <asm/unaligned.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #include <command.h> #include <clock.h> #include <net.h> |