semihosting: create file in smh_fs_write_at()
[platform/kernel/u-boot.git] / net / net.c
index c2992a0..43abbac 100644 (file)
--- a/net/net.c
+++ b/net/net.c
@@ -24,7 +24,7 @@
  *                     - name of bootfile
  *     Next step:      ARP
  *
- * LINK_LOCAL:
+ * LINKLOCAL:
  *
  *     Prerequisites:  - own ethernet address
  *     We want:        - own IP address
 #include <image.h>
 #include <log.h>
 #include <net.h>
-#include <net/fastboot.h>
+#include <net6.h>
+#include <ndisc.h>
+#include <net/fastboot_udp.h>
+#include <net/fastboot_tcp.h>
 #include <net/tftp.h>
+#include <net/ncsi.h>
 #if defined(CONFIG_CMD_PCAP)
 #include <net/pcap.h>
 #endif
 #endif
 #include <watchdog.h>
 #include <linux/compiler.h>
+#include <test/test.h>
+#include <net/tcp.h>
+#include <net/wget.h>
 #include "arp.h"
 #include "bootp.h"
 #include "cdp.h"
 #if defined(CONFIG_CMD_WOL)
 #include "wol.h"
 #endif
+#include "dhcpv6.h"
+#include "net_rand.h"
 
 /** BOOTP EXTENTIONS **/
 
@@ -129,6 +138,8 @@ struct in_addr net_dns_server;
 /* Our 2nd DNS IP address */
 struct in_addr net_dns_server2;
 #endif
+/* Indicates whether the pxe path prefix / config file was specified in dhcp option */
+char *pxelinux_configfile;
 
 /** END OF BOOTP EXTENTIONS **/
 
@@ -302,12 +313,12 @@ void net_auto_load(void)
        if (s != NULL && strcmp(s, "NFS") == 0) {
                if (net_check_prereq(NFS)) {
 /* We aren't expecting to get a serverip, so just accept the assigned IP */
-#ifdef CONFIG_BOOTP_SERVERIP
-                       net_set_state(NETLOOP_SUCCESS);
-#else
-                       printf("Cannot autoload with NFS\n");
-                       net_set_state(NETLOOP_FAIL);
-#endif
+                       if (IS_ENABLED(CONFIG_BOOTP_SERVERIP)) {
+                               net_set_state(NETLOOP_SUCCESS);
+                       } else {
+                               printf("Cannot autoload with NFS\n");
+                               net_set_state(NETLOOP_FAIL);
+                       }
                        return;
                }
                /*
@@ -327,12 +338,12 @@ void net_auto_load(void)
        }
        if (net_check_prereq(TFTPGET)) {
 /* We aren't expecting to get a serverip, so just accept the assigned IP */
-#ifdef CONFIG_BOOTP_SERVERIP
-               net_set_state(NETLOOP_SUCCESS);
-#else
-               printf("Cannot autoload with TFTPGET\n");
-               net_set_state(NETLOOP_FAIL);
-#endif
+               if (IS_ENABLED(CONFIG_BOOTP_SERVERIP)) {
+                       net_set_state(NETLOOP_SUCCESS);
+               } else {
+                       printf("Cannot autoload with TFTPGET\n");
+                       net_set_state(NETLOOP_FAIL);
+               }
                return;
        }
        tftp_start(TFTPGET);
@@ -340,8 +351,19 @@ void net_auto_load(void)
 
 static int net_init_loop(void)
 {
-       if (eth_get_dev())
+       static bool first_call = true;
+
+       if (eth_get_dev()) {
                memcpy(net_ethaddr, eth_get_ethaddr(), 6);
+
+               if (IS_ENABLED(CONFIG_IPV6)) {
+                       ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
+                       if (!memcmp(&net_ip6, &net_null_addr_ip6,
+                                   sizeof(struct in6_addr)))
+                               memcpy(&net_ip6, &net_link_local_ip6,
+                                      sizeof(struct in6_addr));
+               }
+       }
        else
                /*
                 * Not ideal, but there's no way to get the actual error, and I
@@ -350,6 +372,12 @@ static int net_init_loop(void)
                 */
                return -ENONET;
 
+       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+               if (first_call && use_ip6) {
+                       first_call = false;
+                       srand_mac(); /* This is for rand used in ip6_send_rs. */
+                       net_loop(RS);
+               }
        return 0;
 }
 
@@ -382,10 +410,13 @@ int net_init(void)
                                (i + 1) * PKTSIZE_ALIGN;
                }
                arp_init();
+               ndisc_init();
                net_clear_handlers();
 
                /* Only need to setup buffer pointers once. */
                first_call = 0;
+               if (IS_ENABLED(CONFIG_PROT_TCP))
+                       tcp_set_tcp_state(TCP_CLOSED);
        }
 
        return net_init_loop();
@@ -410,6 +441,16 @@ int net_loop(enum proto_t protocol)
        net_try_count = 1;
        debug_cond(DEBUG_INT_STATE, "--- net_loop Entry\n");
 
+#ifdef CONFIG_PHY_NCSI
+       if (phy_interface_is_ncsi() && protocol != NCSI && !ncsi_active()) {
+               printf("%s: configuring NCSI first\n", __func__);
+               if (net_loop(NCSI) < 0)
+                       return ret;
+               eth_init_state_only();
+               goto restart;
+       }
+#endif
+
        bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
        net_init();
        if (eth_is_on_demand_init()) {
@@ -423,6 +464,7 @@ int net_loop(enum proto_t protocol)
        } else {
                eth_init_state_only();
        }
+
 restart:
 #ifdef CONFIG_USB_KEYBOARD
        net_busy_flag = 0;
@@ -437,6 +479,9 @@ restart:
        debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n");
        net_init_loop();
 
+       if (!test_eth_enabled())
+               return 0;
+
        switch (net_check_prereq(protocol)) {
        case 1:
                /* network not configured */
@@ -466,9 +511,14 @@ restart:
                        tftp_start_server();
                        break;
 #endif
-#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
-               case FASTBOOT:
-                       fastboot_start_server();
+#if defined(CONFIG_UDP_FUNCTION_FASTBOOT)
+               case FASTBOOT_UDP:
+                       fastboot_udp_start_server();
+                       break;
+#endif
+#if defined(CONFIG_TCP_FUNCTION_FASTBOOT)
+               case FASTBOOT_TCP:
+                       fastboot_tcp_start_server();
                        break;
 #endif
 #if defined(CONFIG_CMD_DHCP)
@@ -478,6 +528,10 @@ restart:
                        dhcp_request();         /* Basically same as BOOTP */
                        break;
 #endif
+               case DHCP6:
+                       if (IS_ENABLED(CONFIG_CMD_DHCP6))
+                               dhcp6_start();
+                       break;
 #if defined(CONFIG_CMD_BOOTP)
                case BOOTP:
                        bootp_reset();
@@ -497,11 +551,21 @@ restart:
                        ping_start();
                        break;
 #endif
+#if defined(CONFIG_CMD_PING6)
+               case PING6:
+                       ping6_start();
+                       break;
+#endif
 #if defined(CONFIG_CMD_NFS) && !defined(CONFIG_SPL_BUILD)
                case NFS:
                        nfs_start();
                        break;
 #endif
+#if defined(CONFIG_CMD_WGET)
+               case WGET:
+                       wget_start();
+                       break;
+#endif
 #if defined(CONFIG_CMD_CDP)
                case CDP:
                        cdp_start();
@@ -527,6 +591,15 @@ restart:
                        wol_start();
                        break;
 #endif
+#if defined(CONFIG_PHY_NCSI)
+               case NCSI:
+                       ncsi_probe_packages();
+                       break;
+#endif
+               case RS:
+                       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+                               ip6_send_rs();
+                       break;
                default:
                        break;
                }
@@ -559,10 +632,15 @@ restart:
         *      someone sets `net_state' to a state that terminates.
         */
        for (;;) {
-               WATCHDOG_RESET();
+               schedule();
                if (arp_timeout_check() > 0)
                        time_start = get_timer(0);
 
+               if (IS_ENABLED(CONFIG_IPV6)) {
+                       if (use_ip6 && (ndisc_timeout_check() > 0))
+                               time_start = get_timer(0);
+               }
+
                /*
                 *      Check the ethernet for a new packet.  The ethernet
                 *      receive routine will process it.
@@ -619,7 +697,13 @@ restart:
                        x = time_handler;
                        time_handler = (thand_f *)0;
                        (*x)();
-               }
+               } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+                       if (time_handler && protocol == RS)
+                               if (!ip6_is_unspecified_addr(&net_gateway6) &&
+                                   net_prefix_length != 0) {
+                                       net_set_state(NETLOOP_SUCCESS);
+                                       net_set_timeout_handler(0, NULL);
+                               }
 
                if (net_state == NETLOOP_FAIL)
                        ret = net_start_again();
@@ -637,7 +721,7 @@ restart:
                                env_set_hex("filesize", net_boot_file_size);
                                env_set_hex("fileaddr", image_load_addr);
                        }
-                       if (protocol != NETCONS)
+                       if (protocol != NETCONS && protocol != NCSI)
                                eth_halt();
                        else
                                eth_halt_state_only();
@@ -816,6 +900,16 @@ int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport,
                                  IPPROTO_UDP, 0, 0, 0);
 }
 
+#if defined(CONFIG_PROT_TCP)
+int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action,
+                       u32 tcp_seq_num, u32 tcp_ack_num)
+{
+       return net_send_ip_packet(net_server_ethaddr, net_server_ip, dport,
+                                 sport, payload_len, IPPROTO_TCP, action,
+                                 tcp_seq_num, tcp_ack_num);
+}
+#endif
+
 int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
                       int payload_len, int proto, u8 action, u32 tcp_seq_num,
                       u32 tcp_ack_num)
@@ -847,6 +941,14 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
                                   payload_len);
                pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
                break;
+#if defined(CONFIG_PROT_TCP)
+       case IPPROTO_TCP:
+               pkt_hdr_size = eth_hdr_size
+                       + tcp_set_tcp_header(pkt + eth_hdr_size, dport, sport,
+                                            payload_len, action, tcp_seq_num,
+                                            tcp_ack_num);
+               break;
+#endif
        default:
                return -EINVAL;
        }
@@ -907,6 +1009,13 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
        int offset8, start, len, done = 0;
        u16 ip_off = ntohs(ip->ip_off);
 
+       /*
+        * Calling code already rejected <, but we don't have to deal
+        * with an IP fragment with no payload.
+        */
+       if (ntohs(ip->ip_len) <= IP_HDR_SIZE)
+               return NULL;
+
        /* payload starts after IP header, this fragment is in there */
        payload = (struct hole *)(pkt_buff + IP_HDR_SIZE);
        offset8 =  (ip_off & IP_OFFS);
@@ -914,6 +1023,10 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
        start = offset8 * 8;
        len = ntohs(ip->ip_len) - IP_HDR_SIZE;
 
+       /* All but last fragment must have a multiple-of-8 payload. */
+       if ((len & 7) && (ip_off & IP_FLAGS_MFRAG))
+               return NULL;
+
        if (start + len > IP_MAXUDP) /* fragment extends too far */
                return NULL;
 
@@ -957,10 +1070,14 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
        }
 
        /*
-        * There is some overlap: fix the hole list. This code doesn't
-        * deal with a fragment that overlaps with two different holes
-        * (thus being a superset of a previously-received fragment).
+        * There is some overlap: fix the hole list. This code deals
+        * with a fragment that overlaps with two different holes
+        * (thus being a superset of a previously-received fragment)
+        * by only using the part of the fragment that fits in the
+        * first hole.
         */
+       if (h->last_byte < start + len)
+               len = h->last_byte - start;
 
        if ((h >= thisfrag) && (h->last_byte <= start + len)) {
                /* complete overlap with hole: remove hole */
@@ -1012,8 +1129,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
        if (!done)
                return NULL;
 
-       localip->ip_len = htons(total_len);
        *lenp = total_len + IP_HDR_SIZE;
+       localip->ip_len = htons(*lenp);
        return localip;
 }
 
@@ -1185,12 +1302,17 @@ void net_process_received_packet(uchar *in_packet, int len)
                rarp_receive(ip, len);
                break;
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+       case PROT_IP6:
+               net_ip6_handler(et, (struct ip6_hdr *)ip, len);
+               break;
+#endif
        case PROT_IP:
                debug_cond(DEBUG_NET_PKT, "Got IP\n");
                /* Before we start poking the header, make sure it is there */
-               if (len < IP_UDP_HDR_SIZE) {
+               if (len < IP_HDR_SIZE) {
                        debug("len bad %d < %lu\n", len,
-                             (ulong)IP_UDP_HDR_SIZE);
+                             (ulong)IP_HDR_SIZE);
                        return;
                }
                /* Check the packet length */
@@ -1199,6 +1321,10 @@ void net_process_received_packet(uchar *in_packet, int len)
                        return;
                }
                len = ntohs(ip->ip_len);
+               if (len < IP_HDR_SIZE) {
+                       debug("bad ip->ip_len %d < %d\n", len, (int)IP_HDR_SIZE);
+                       return;
+               }
                debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
                           len, ip->ip_hl_v & 0xff);
 
@@ -1206,7 +1332,7 @@ void net_process_received_packet(uchar *in_packet, int len)
                if ((ip->ip_hl_v & 0xf0) != 0x40)
                        return;
                /* Can't deal with IP options (headers != 20 bytes) */
-               if ((ip->ip_hl_v & 0x0f) > 0x05)
+               if ((ip->ip_hl_v & 0x0f) != 0x05)
                        return;
                /* Check the Checksum of the header */
                if (!ip_checksum_ok((uchar *)ip, IP_HDR_SIZE)) {
@@ -1253,19 +1379,27 @@ void net_process_received_packet(uchar *in_packet, int len)
                if (ip->ip_p == IPPROTO_ICMP) {
                        receive_icmp(ip, len, src_ip, et);
                        return;
+#if defined(CONFIG_PROT_TCP)
+               } else if (ip->ip_p == IPPROTO_TCP) {
+                       debug_cond(DEBUG_DEV_PKT,
+                                  "TCP PH (to=%pI4, from=%pI4, len=%d)\n",
+                                  &dst_ip, &src_ip, len);
+
+                       rxhand_tcp_f((union tcp_build_pkt *)ip, len);
+                       return;
+#endif
                } else if (ip->ip_p != IPPROTO_UDP) {   /* Only UDP packets */
                        return;
                }
 
-               if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > ntohs(ip->ip_len))
+               if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > len - IP_HDR_SIZE)
                        return;
 
                debug_cond(DEBUG_DEV_PKT,
                           "received UDP (to=%pI4, from=%pI4, len=%d)\n",
                           &dst_ip, &src_ip, len);
 
-#ifdef CONFIG_UDP_CHECKSUM
-               if (ip->udp_xsum != 0) {
+               if (IS_ENABLED(CONFIG_UDP_CHECKSUM) && ip->udp_xsum != 0) {
                        ulong   xsum;
                        u8 *sumptr;
                        ushort  sumlen;
@@ -1298,7 +1432,6 @@ void net_process_received_packet(uchar *in_packet, int len)
                                return;
                        }
                }
-#endif
 
 #if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
                nc_input_packet((uchar *)ip + IP_UDP_HDR_SIZE,
@@ -1321,6 +1454,11 @@ void net_process_received_packet(uchar *in_packet, int len)
                wol_receive(ip, len);
                break;
 #endif
+#ifdef CONFIG_PHY_NCSI
+       case PROT_NCSI:
+               ncsi_receive(et, ip, len);
+               break;
+#endif
        }
 }
 
@@ -1338,6 +1476,14 @@ static int net_check_prereq(enum proto_t protocol)
                }
                goto common;
 #endif
+#if defined(CONFIG_CMD_PING6)
+       case PING6:
+               if (ip6_is_unspecified_addr(&net_ping_ip6)) {
+                       puts("*** ERROR: ping address not given\n");
+                       return 1;
+               }
+               goto common;
+#endif
 #if defined(CONFIG_CMD_DNS)
        case DNS:
                if (net_dns_server.s_addr == 0) {
@@ -1359,7 +1505,14 @@ static int net_check_prereq(enum proto_t protocol)
                /* Fall through */
        case TFTPGET:
        case TFTPPUT:
-               if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
+               if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+                       if (!memcmp(&net_server_ip6, &net_null_addr_ip6,
+                                   sizeof(struct in6_addr)) &&
+                                   !strchr(net_boot_file_name, '[')) {
+                               puts("*** ERROR: `serverip6' not set\n");
+                               return 1;
+                       }
+               } else if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
                        puts("*** ERROR: `serverip' not set\n");
                        return 1;
                }
@@ -1370,9 +1523,16 @@ common:
                /* Fall through */
 
        case NETCONS:
-       case FASTBOOT:
+       case FASTBOOT_UDP:
+       case FASTBOOT_TCP:
        case TFTPSRV:
-               if (net_ip.s_addr == 0) {
+               if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+                       if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6,
+                                   sizeof(struct in6_addr))) {
+                               puts("*** ERROR: `ip6addr` not set\n");
+                               return 1;
+                       }
+               } else if (net_ip.s_addr == 0) {
                        puts("*** ERROR: `ipaddr' not set\n");
                        return 1;
                }
@@ -1381,6 +1541,9 @@ common:
 #ifdef CONFIG_CMD_RARP
        case RARP:
 #endif
+#ifdef CONFIG_PHY_NCSI
+       case NCSI:
+#endif
        case BOOTP:
        case CDP:
        case DHCP:
@@ -1540,14 +1703,19 @@ int is_serverip_in_cmd(void)
 int net_parse_bootfile(struct in_addr *ipaddr, char *filename, int max_len)
 {
        char *colon;
+       struct in_addr ip;
+       ip.s_addr = 0;
 
        if (net_boot_file_name[0] == '\0')
                return 0;
 
        colon = strchr(net_boot_file_name, ':');
        if (colon) {
-               if (ipaddr)
-                       *ipaddr = string_to_ip(net_boot_file_name);
+               ip = string_to_ip(net_boot_file_name);
+               if (ipaddr && ip.s_addr)
+                       *ipaddr = ip;
+       }
+       if (ip.s_addr) {
                strncpy(filename, colon + 1, max_len);
        } else {
                strncpy(filename, net_boot_file_name, max_len);