diff options
Diffstat (limited to 'net/net.c')
-rw-r--r-- | net/net.c | 234 |
1 files changed, 197 insertions, 37 deletions
@@ -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); } @@ -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,54 @@ static int net_handle_udp(unsigned char *pkt, int len) return -EINVAL; } -static int net_handle_icmp(unsigned char *pkt, int len) +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 = (struct iphdr *)(pkt + ETHER_HDR_SIZE); + 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); + + 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 = 0; + 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, ETHER_HDR_SIZE + len); + + ret = eth_send(edev, packet, ETHER_HDR_SIZE + len); + + free(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); @@ -596,7 +718,12 @@ static int net_handle_ip(struct eth_device *edev, unsigned char *pkt, int len) 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 +734,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 +758,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 +788,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 +824,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"); |