summaryrefslogtreecommitdiffstats
path: root/net/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/net.c')
-rw-r--r--net/net.c234
1 files changed, 197 insertions, 37 deletions
diff --git a/net/net.c b/net/net.c
index 0d889ddb52..c06e88fe53 100644
--- a/net/net.c
+++ b/net/net.c
@@ -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");