[U-Boot] Request For Comments: wget and TCP listener

Mods to: cmd/net.cnet/Makefilenet/net.cinclude/net.hnet/wget.cnet/wget.hnet/ping.c I do not know how to do patches, I'm a noobat this: cmd/net.c Additions static int do_wget(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){ return netboot_common(WGET, cmdtp, argc, argv);} U_BOOT_CMD( wget, 3, 1, do_wget, "boot image via network using HTTP protocol", "[loadAddress] [[hostIPaddr:]bootfilename]"); net/Makefile addition obj-$(CONFIG_CMD_NFS) += wget.o
net.c complete, look for #ifdef TCP /* * Copied from Linux Monitor (LiMon) - Networking. * * Copyright 1994 - 2000 Neil Russell. * (See License) * Copyright 2000 Roland Borde * Copyright 2000 Paolo Scaffardi * Copyright 2000-2002 Wolfgang Denk, wd@denx.de * Copyright 2017-2018 Duncan Hare, dh@synoia.com * SPDX-License-Identifier: GPL-2.0 */ #define TCP 1 /* * General Desription: * * The user interface supports commands for BOOTP, RARP, and TFTP. * Also, we support ARP internally. Depending on available data, * these interact as follows: * * BOOTP: * * Prerequisites: - own ethernet address * We want: - own IP address * - TFTP server IP address * - name of bootfile * Next step: ARP * * LINK_LOCAL: * * Prerequisites: - own ethernet address * We want: - own IP address * Next step: ARP * * RARP: * * Prerequisites: - own ethernet address * We want: - own IP address * - TFTP server IP address * Next step: ARP * * ARP: * * Prerequisites: - own ethernet address * - own IP address * - TFTP server IP address * We want: - TFTP server ethernet address * Next step: TFTP * * DHCP: * * Prerequisites: - own ethernet address * We want: - IP, Netmask, ServerIP, Gateway IP * - bootfilename, lease time * Next step: - TFTP * * TFTP: * * Prerequisites: - own ethernet address * - own IP address * - TFTP server IP address * - TFTP server ethernet address * - name of bootfile (if unknown, we use a default name * derived from our own IP address) * We want: - load the boot file * Next step: none * * NFS: * * Prerequisites: - own ethernet address * - own IP address * - name of bootfile (if unknown, we use a default name * derived from our own IP address) * We want: - load the boot file * Next step: none * * SNTP: * * Prerequisites: - own ethernet address * - own IP address * We want: - network time * Next step: none * * HTTP/TCP Receiver: * * Prequeisites: - own ethernet adress * - own IP address * - Server IP address * - HTP client * - Bootfile path & name * We want: - Load the Boot file * Next Step HTTPS? */ #define DEBUG_DCH_PKT 1 #define DEBUG_TCP_PKT 0
#include <common.h> #include <command.h> #include <console.h> #include <environment.h> #include <errno.h> #include <net.h> #include <net/tftp.h> #if defined(CONFIG_STATUS_LED) #include <miiphy.h> #include <status_led.h> #endif #include <watchdog.h> #include <linux/compiler.h> #include "arp.h" #include "bootp.h" #include "cdp.h" #if defined(CONFIG_CMD_DNS) #include "dns.h" #endif #include "link_local.h" #include "nfs.h" #include "wget.h" #include "ping.h" #include "rarp.h" #if defined(CONFIG_CMD_SNTP) #include "sntp.h" #endif
DECLARE_GLOBAL_DATA_PTR;
/** BOOTP EXTENTIONS **/
/* Our subnet mask (0=unknown) */ struct in_addr net_netmask; /* Our gateways IP address */ struct in_addr net_gateway; /* Our DNS IP address */ struct in_addr net_dns_server; #if defined(CONFIG_BOOTP_DNS2) /* Our 2nd DNS IP address */ struct in_addr net_dns_server2; #endif
#ifdef CONFIG_MCAST_TFTP /* Multicast TFTP */ struct in_addr net_mcast_addr; #endif
/** END OF BOOTP EXTENTIONS **/
/* Our ethernet address */ u8 net_ethaddr[6]; /* Boot server enet address */ u8 net_server_ethaddr[6]; /* Our IP addr (0 = unknown) */ struct in_addr net_ip; /* Server IP addr (0 = unknown) */ struct in_addr net_server_ip; /* Port numbers */ int dport; int sport; /* Current receive packet */ uchar *net_rx_packet; /* Current rx max packet length */ int net_rx_packet_len; /* IP packet ID */ static unsigned net_ip_id; /* Ethernet bcast address */ const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; const u8 net_null_ethaddr[6]; #if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) void (*push_packet)(void *, int len) = 0; #endif /* Network loop state */ enum net_loop_state net_state; /* Tried all network devices */ int net_restart_wrap; /* Network loop restarted */ static int net_restarted; /* At least one device configured */ static int net_dev_exists;
#ifdef TCP
/* TCP sliding window control */ static u32 tcp_next_expected_seq_num; static struct tcp_sack_v tcp_lost; /* used by us to request re-TX */
struct w_manage { struct sack_edges tcp_hole; enum acked { FALSE, TRUE, FILLED } sent; int age; };
static int tcp_max_hole;
struct w_manage holes[TCP_STREAM_HOLES];
/* TCP option timestamp */
static u32 loc_timestamp; static u32 rmt_timestamp;
/* TCP connection state */ static enum TCP_STATE tcp_state;
#endif
/* XXX in both little & big endian machines 0xFFFF == ntohs(-1) */ /* default is without VLAN */ ushort net_our_vlan = 0xFFFF; /* ditto */ ushort net_native_vlan = 0xFFFF;
/* Boot File name */ char net_boot_file_name[1024]; /* The actual transferred size of the bootfile (in bytes) */ u32 net_boot_file_size; /* Boot file size in blocks as reported by the DHCP server */ u32 net_boot_file_expected_size_in_blocks;
#if defined(CONFIG_CMD_SNTP) /* NTP server IP address */ struct in_addr net_ntp_server; /* offset time from UTC */ int net_ntp_time_offset; #endif
static uchar net_pkt_buf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN]; /* Receive packets */ uchar *net_rx_packets[PKTBUFSRX];
/* * An incoming TCP packet handler for the TCP protocol. * There is also a dymanic function pointer for TCP based commads to * receive incoming traffic after the TCP protocol code has done its work. */
void rxhand_tcp_f( union tcp_build_pkt *b, unsigned len);
/* Current TCP RX packet handler */ static rxhand_f *tcp_packet_handler; /* Current UDP RX packet handler */ static rxhand_f *udp_packet_handler; /* Current ARP RX packet handler */ static rxhand_f *arp_packet_handler; #ifdef CONFIG_CMD_TFTPPUT /* Current ICMP rx handler */ static rxhand_icmp_f *packet_icmp_handler; #endif /* Current timeout handler */ static thand_f *time_handler; /* Time base value */ static ulong time_start; /* Current timeout value */ static ulong time_delta; /* THE transmit packet */ uchar *net_tx_packet;
static int net_check_prereq(enum proto_t protocol);
static int net_try_count;
int __maybe_unused net_busy_flag;
/**********************************************************************/
static int on_bootfile(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
switch (op) { case env_op_create: case env_op_overwrite: copy_filename(net_boot_file_name, value, sizeof(net_boot_file_name)); break; default: break; }
return 0; } U_BOOT_ENV_CALLBACK(bootfile, on_bootfile);
static int on_ipaddr(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_ip = string_to_ip(value);
return 0; } U_BOOT_ENV_CALLBACK(ipaddr, on_ipaddr);
static int on_gatewayip(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_gateway = string_to_ip(value);
return 0; } U_BOOT_ENV_CALLBACK(gatewayip, on_gatewayip);
static int on_netmask(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_netmask = string_to_ip(value);
return 0; } U_BOOT_ENV_CALLBACK(netmask, on_netmask);
static int on_serverip(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_server_ip = string_to_ip(value);
return 0; } U_BOOT_ENV_CALLBACK(serverip, on_serverip);
static int on_nvlan(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_native_vlan = string_to_vlan(value);
return 0; } U_BOOT_ENV_CALLBACK(nvlan, on_nvlan);
static int on_vlan(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_our_vlan = string_to_vlan(value);
return 0; } U_BOOT_ENV_CALLBACK(vlan, on_vlan);
#if defined(CONFIG_CMD_DNS) static int on_dnsip(const char *name, const char *value, enum env_op op, int flags) { if (flags & H_PROGRAMMATIC) return 0;
net_dns_server = string_to_ip(value);
return 0; } U_BOOT_ENV_CALLBACK(dnsip, on_dnsip); #endif
/* * Check if autoload is enabled. If so, use either NFS or TFTP to download * the boot file. */ void net_auto_load(void) { #if defined(CONFIG_CMD_NFS) const char *s = getenv("autoload");
if (s != NULL && strcmp(s, "NFS") == 0) { /* * Use NFS to load the bootfile. */ nfs_start(); return; } #endif if (getenv_yesno("autoload") == 0) { /* * Just use BOOTP/RARP to configure system; * Do not use TFTP to load the bootfile. */ net_set_state(NETLOOP_SUCCESS); return; } tftp_start(TFTPGET); }
static void net_init_loop(void) { if (eth_get_dev()) memcpy(net_ethaddr, eth_get_ethaddr(), 6);
return; }
static void net_clear_handlers(void) { net_set_tcp_handler(NULL); net_set_udp_handler(NULL); net_set_arp_handler(NULL); net_set_timeout_handler(0, NULL); }
static void net_cleanup_loop(void) { net_clear_handlers(); }
void net_set_ports( int server_port, int our_port ) { dport = server_port; sport = our_port; }
#ifdef TCP
enum TCP_STATE net_get_tcp_state( void ) { return(tcp_state); }
void net_print_buffer( uchar raw[], int pkt_len, int payload_len, int hdr_len, bool hide ) { int i; for ( i = pkt_len - payload_len; i < pkt_len; i++ ) { if ( i <= hdr_len ) { printf("%02X", raw[i]); } else if (( raw[i] > 0x19 ) && ( raw[i] < 0x7f )) { putc( raw[i] ); } else if (hide == 0 ) putc( raw[i] ); else printf("%02X", raw[i]);
// else printf ("%s","."); } printf ( "%s", "\n" ); }
int net_find_in_buffer( uchar raw[], int payload_len, uchar field[], int field_len ) { int i,j;
for (i = 0; i < payload_len; i ++ ) { if ( raw[i] == field[0] ) { for (j = 1; j < field_len; j++ ) { if ( raw[i+j] != field[j] ) break; } if ( j == field_len ) return( i ); }
} return ( 0 ); }
u16 net_set_psuedo_header( uchar * pkt, struct in_addr src, struct in_addr dest, int tcp_len, int pkt_len ) { union tcp_build_pkt *b = (union tcp_build_pkt *) pkt; int checksum_len; /* * Psuedo header */ pkt[ pkt_len ] = 0x00;
net_copy_ip((void *)&b->ph.p_src, &src); net_copy_ip((void *)&b->ph.p_dst, &dest); b->ph.rsvd = 0x00; b->ph.p = IPPROTO_TCP; b->ph.len = htons(tcp_len); checksum_len = tcp_len + PSUEDO_HDR_SIZE;
debug_cond(DEBUG_DEV_PKT, "TCP Psuedo Header (to=%pI4, from=%pI4, CheckLen=%d)\n", &b->ph.p_dst, &b->ph.p_src, checksum_len ); /* * If the data is an odd number of bytes, zero the * byte after the last byte so that the header checksum * will work. */
return( compute_ip_checksum( pkt + PSUEDO_PAD_SIZE, checksum_len ));
}
int net_set_ack_options( union tcp_build_pkt *b ) { b->sack.hdr.tcp_hlen = ( TCP_HDR_SIZE >>2 ) << 4;
b->sack.TSopt.kind = TCP_O_TS; b->sack.TSopt.len = TCP_OPT_LEN_A; b->sack.TSopt.TSsnd = htons(loc_timestamp); b->sack.TSopt.TSrcv = rmt_timestamp; b->sack.sack_v.kind = 0x01; b->sack.sack_v.len = 0x00;
if ( tcp_lost.len > 0 ) { b->sack.sack_v.len = tcp_lost.len; b->sack.sack_v.kind = TCP_V_SACK; b->sack.sack_v.hole[1].l = htonl(tcp_lost.hole[1].l); b->sack.sack_v.hole[1].r = htonl(tcp_lost.hole[1].r);
/* * These fields are initialized with NOPs to * provide TCP header alignment padding */
b->sack.sack_v.hole[2].l = htonl(tcp_lost.hole[1].l); b->sack.sack_v.hole[2].r = htonl(tcp_lost.hole[1].r); b->sack.sack_v.hole[3].l = htonl(tcp_lost.hole[2].l); b->sack.sack_v.hole[3].r = htonl(tcp_lost.hole[2].r);
tcp_lost.len = 0; } b->sack.hdr.tcp_hlen = (((TCP_HDR_SIZE + TCP_TSOPT_SIZE + tcp_lost.len + 3) >> 2) << 4);
return( b->sack.hdr.tcp_hlen >> 2); }
void net_set_syn_options( union tcp_build_pkt *b ) { tcp_lost.len = 0; b->ip.hdr.tcp_hlen = 0xa0; /* hdr 10 32 bit words */
b->ip.mss.kind = TCP_O_MSS; b->ip.mss.len = TCP_OPT_LEN_4; b->ip.mss.mss = htons(TCP_MSS); /* tunable parameter */ b->ip.scale.kind = TCP_O_SCL; b->ip.scale.scale = TCP_SCALE; /* tunable parameter */ b->ip.scale.len = TCP_OPT_LEN_3; b->ip.sack_p.kind = TCP_P_SACK ; /* SACK supported */ b->ip.sack_p.len = TCP_OPT_LEN_2; b->ip.TSopt.kind = TCP_O_TS; b->ip.TSopt.len = TCP_OPT_LEN_A; loc_timestamp = get_ticks() % 3072; rmt_timestamp = 0x00000000; b->ip.TSopt.TSsnd = 0; b->ip.TSopt.TSrcv = 0x00000000; b->ip.end = TCP_O_END; }
int net_set_tcp_header(uchar *pkt, int payload_len, u8 action, u32 tcp_seq_num, u32 tcp_ack_num ) { union tcp_build_pkt *b = (union tcp_build_pkt *) pkt; int pkt_hdr_len; int pkt_len; int tcp_len;
b->ip.hdr.tcp_flags = action; pkt_hdr_len = IP_TCP_HDR_SIZE; b->ip.hdr.tcp_hlen = 0x50; /* Header is 5 32 bit words */ /* 4 bits TCP header Length/4 */ /* 4 bits Reserved */ switch (action) /* For options */ { case TCP_SYN: debug_cond(DEBUG_TCP_PKT, "TCP Header:SYN (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);
net_set_syn_options( b ); tcp_seq_num = 0; /* TCP sequence number */ tcp_ack_num = 0; /* TCP Ack Number */ pkt_hdr_len = IP_TCP_O_SIZE; if ( tcp_state == TCP_SYN_SENT) /* Too many sins */ { action = TCP_FIN; tcp_state = TCP_FIN_WAIT_1; } else tcp_state = TCP_SYN_SENT; break; case TCP_ACK: pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options( b ); b->ip.hdr.tcp_flags = action; debug_cond(DEBUG_TCP_PKT, "TCP Header:ACK (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d, Action=%x)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, b->ip.hdr.tcp_flags);
break; case TCP_FIN: debug_cond(DEBUG_TCP_PKT, "TCP Header:FIN (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); payload_len = 0; pkt_hdr_len = IP_TCP_HDR_SIZE; tcp_state = TCP_FIN_WAIT_1; tcp_seq_num++; break; case (TCP_FIN + TCP_ACK): case (TCP_FIN + TCP_ACK + TCP_PUSH): if ( tcp_state == TCP_CLOSE_WAIT ) tcp_state = TCP_CLOSING; default: pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options( b ); b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK; debug_cond(DEBUG_TCP_PKT, "TCP Header:default (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d, Action=%x)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, b->ip.hdr.tcp_flags); } pkt_len = pkt_hdr_len + payload_len; tcp_len = pkt_len - IP_HDR_SIZE;
/* * TCP Header */ b->ip.hdr.tcp_ack = htonl( tcp_ack_num); b->ip.hdr.tcp_src = htons(sport); b->ip.hdr.tcp_dst = htons(dport); b->ip.hdr.tcp_seq = htonl(tcp_seq_num); tcp_seq_num = tcp_seq_num + payload_len;
/* * TCP window size - TCP header variable tcp_win. * Chage tcp_win only if you have an understanding of network overruun, congestion, * TCP segment sizes, TCP windows, TCP scale, queuing theory and packet buffering. * If there are too few buffers, there will be data loss, recovery may work or the * sending TCP, the server, could abort the stream transmission. * MSS is governed by maximum Ethernet frame langth. * The number of buffers is governed by the desire to have a queue of full buffers * to be processed at the destination to maximize throughput. * Temporary memory use for the boot phase on modern SOCs is not considered a constraint to * buffer space. */
b->ip.hdr.tcp_win = htons( PKTBUFSRX * TCP_MSS >> TCP_SCALE );
b->ip.hdr.tcp_xsum = 0x0000; /* Checksum */ b->ip.hdr.tcp_ugr = 0x0000; /* Pointer to urgent data */
b->ip.hdr.tcp_xsum = net_set_psuedo_header( pkt, net_ip, net_server_ip, tcp_len, pkt_len );
/* * IP Header */
net_set_ip_header((uchar *) &b->ip, net_server_ip, net_ip, pkt_len, IPPROTO_TCP);
return ( pkt_hdr_len ); }
#endif
int net_send_ip_packet( int payload_len, int proto, u8 action, u32 tcp_seq_num, u32 tcp_ack_num ) { uchar *pkt; int eth_hdr_size; int pkt_hdr_size; uchar * ether = net_server_ethaddr;
if ( proto == IPPROTO_UDP ) { debug_cond(DEBUG_DEV_PKT, "UDP Send (to=%pI4, from=%pI4, len=%d)\n", &net_server_ip, &net_ip, payload_len); } #ifdef TCP else { debug_cond(DEBUG_TCP_PKT, "TCP Send (to=%pI4, from=%pI4, len=%d, Action=%x)\n", &net_server_ip, &net_ip, payload_len, action ); } #endif
/* make sure the net_tx_packet is initialized (net_init() was called) */ assert(net_tx_packet != NULL); if (net_tx_packet == NULL) return -1;
/* convert to new style broadcast */ if (net_server_ip.s_addr == 0) net_server_ip.s_addr = 0xFFFFFFFF;
/* if broadcast, make the ether address a broadcast and don't do ARP */ if (net_server_ip.s_addr == 0xFFFFFFFF) ether = (uchar *)net_bcast_ethaddr;
pkt = (uchar *)net_tx_packet;
/* *Get ethernet header size and write the ethernet header */
eth_hdr_size = net_set_ether(pkt, ether, PROT_IP);
#ifdef TCP if (proto == IPPROTO_UDP ) { #endif net_set_udp_header(pkt + eth_hdr_size, net_server_ip, dport, sport, payload_len); pkt_hdr_size = IP_UDP_HDR_SIZE; eth_hdr_size = eth_hdr_size + pkt_hdr_size; #ifdef TCP } else pkt_hdr_size = eth_hdr_size + net_set_tcp_header(pkt + eth_hdr_size, payload_len, action, tcp_seq_num, tcp_ack_num ); #endif /* if MAC address was not discovered yet, do an ARP request */ if (memcmp(ether, net_null_ethaddr, 6) == 0) { debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &net_server_ip);
/* save the ip and eth addr for the packet to send after arp */ net_arp_wait_packet_ip = net_server_ip; arp_wait_packet_ethaddr = ether;
/* size of the waiting packet */ arp_wait_tx_packet_size = pkt_hdr_size + payload_len;
/* and do the ARP request */ arp_wait_try = 1; arp_wait_timer_start = get_timer(0); arp_request(); return 1; /* waiting */ } else { debug_cond(DEBUG_DEV_PKT, "sending TCP to %pI4/%pM/%d\n", &net_server_ip, ether, pkt_hdr_size + payload_len); net_send_packet(net_tx_packet, pkt_hdr_size + payload_len); return 0; /* transmitted */ } }
#ifdef TCP
int tcp_hole_age ( int age ) { int i, j = 0, filled = 0;
tcp_lost.len = TCP_OPT_LEN_2; tcp_lost.hole[1].l = TCP_O_NOP; tcp_lost.hole[1].r = TCP_O_NOP; tcp_lost.hole[2].l = TCP_O_NOP; tcp_lost.hole[2].r = TCP_O_NOP;
tcp_lost.kind = TCP_V_SACK;
for (i = 0; (( i == tcp_max_hole ) || ( j == 2 )) ; i++ ) { if (( holes[i].tcp_hole.l != 0 ) && ( holes[i].sent == FALSE )) { if ( holes[i].age <= TCP_HOLE_AGE ) holes[i].age++; else { /* * Ask for hole to be filled by sender on next ack response. */ tcp_lost.len = tcp_lost.len + TCP_OPT_LEN_8; tcp_lost.hole[j].l = holes[i].tcp_hole.l; tcp_lost.hole[j].l = holes[i].tcp_hole.r; holes[i].sent = TRUE; j++; } } if (( holes[i].tcp_hole.l == 0 ) || ( holes[i].sent == FILLED )) filled++;
switch (holes[i].sent) { case FALSE: debug_cond( DEBUG_DCH_PKT, "."); break; case TRUE: debug_cond( DEBUG_DCH_PKT, "o"); break; case FILLED: debug_cond( DEBUG_DCH_PKT, "|"); break; } } debug_cond( DEBUG_DCH_PKT, "\n");
/* * No holes found? Set return code. */
if ( tcp_lost.len == TCP_OPT_LEN_2 ) { tcp_lost.len = 0; return ( filled - tcp_max_hole ); } else return ( 1 ); }
void tcp_hole_fill( u32 tcp_left, u32 tcp_right ) { int i ,j;
for ( i = 0; (( i = tcp_max_hole ) || ( tcp_left != holes[i].tcp_hole.l )); i++ );
if ( tcp_left == holes[i].tcp_hole.l ) { holes[i].sent = FILLED; /* * Move holes to down hole array as preceeding holes become filled */ for ( j = i; (j < tcp_max_hole); j++ ) { holes[j] = holes[j + 1]; } } else debug_cond( DEBUG_DCH_PKT, "Error: Hole not found: Left=%d\n", tcp_left ); }
void tcp_hole_create( u32 tcp_left, u32 tcp_right ) { debug_cond( 1, "Hole Created Left=%d\n", tcp_left );
tcp_lost.len = tcp_lost.len + TCP_OPT_LEN_A; tcp_lost.hole[0].l = tcp_left; tcp_lost.hole[0].l = tcp_right;
tcp_lost.hole[1].l = TCP_O_NOP; tcp_lost.hole[1].r = TCP_O_NOP; tcp_lost.hole[2].l = TCP_O_NOP; tcp_lost.hole[2].r = TCP_O_NOP;
}
void tcp_hole_create_complex( u32 tcp_left, u32 tcp_right ) { int i;
for (i = 0; i < TCP_STREAM_HOLES; i++ ) { /* * Find empty hole struct and fill it */ if ( holes[i].tcp_hole.l == 0 ) holes[i].tcp_hole.l = tcp_left; holes[i].tcp_hole.r = tcp_right; holes[i].sent = FALSE; holes[i].age = 0; if ( i > tcp_max_hole ) tcp_max_hole = i; return; } debug_cond( 1, "Error: Too many TCP erorrs, increase TCP_STREAM_HOLES or get a better network\n"); net_set_state(NETLOOP_FAIL);
}
void init_tcp_holes( u32 tcp_seq_num ) { int i;
for (i = 0; i < TCP_STREAM_HOLES; i++) { holes[i].tcp_hole.l = 0; holes[i].tcp_hole.r = 0; holes[i].sent = FALSE; holes[i].age = 0; }
tcp_next_expected_seq_num = tcp_seq_num; /* * // For production set to 0. * // For testingt set to TCP_STREAM_HOLES - 1 */ tcp_max_hole = TCP_STREAM_HOLES - 1; // For testing. // For production set 0. }
void tcp_parse_options( uchar *o, int o_len) { struct tcp_TSopt *tsopt; uchar *p = o;
for ( p = o; p < (o + o_len); p = p + p[1]) { if (p[1] == 0) return; else { switch ( p[0] ) { case TCP_O_END: return; case TCP_O_MSS: break; case TCP_O_SCL: break; case TCP_P_SACK: break; case TCP_V_SACK: break; case TCP_O_TS : tsopt = (struct tcp_TSopt *) p; rmt_timestamp = tsopt->TSsnd; return; break; } if (p[0] == TCP_O_NOP) p++; } } return; }
u8 tcp_state_machine( u8 tcp_flags, u32 *tcp_seq_num, int payload_len ) { u8 tcp_fin = tcp_flags & TCP_FIN; u8 tcp_syn = tcp_flags & TCP_SYN; u8 tcp_rst = tcp_flags & TCP_RST; u8 tcp_push = tcp_flags & TCP_PUSH; u8 tcp_ack = tcp_flags & TCP_ACK; u8 tcp_urg = tcp_flags & TCP_URG; u8 tcp_ece = tcp_flags & TCP_ECE; u8 tcp_cwr = tcp_flags & TCP_CWR; u8 action = TCP_DATA;
/* * tcp_flags are examined to determine TX action in a given state * tcp_options, if not zero, is a pointer to the TCP options, * to extract the receive timestamp value */
/* * These flags are not supported. */ if (tcp_urg) action = action; if (tcp_ece) action = action; if (tcp_cwr) action = action;
debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY (%x)\n", action); if (tcp_rst) { action = TCP_DATA; tcp_state = TCP_CLOSED; net_set_state( NETLOOP_FAIL ); debug_cond(DEBUG_DCH_PKT, "TCP Reset (%x)\n", tcp_flags); } else switch (tcp_state) { case TCP_CLOSED: /* Should never happen */ debug_cond(DEBUG_TCP_PKT, "TCP CLOSED (%x)\n", tcp_flags); if (tcp_fin) action = TCP_DATA; if (tcp_syn) action = TCP_RST; if (tcp_ack) action = TCP_DATA; break; case TCP_SYN_SENT: debug_cond(DEBUG_TCP_PKT, "TCP_SYN_SENT (%x), %d\n", tcp_flags, *tcp_seq_num); if (tcp_fin) {action = action | TCP_PUSH; tcp_state = TCP_CLOSE_WAIT;} if (tcp_syn) { action = action | TCP_ACK; if (tcp_ack) { *tcp_seq_num = *tcp_seq_num + 1; tcp_state = TCP_ESTABLISHED; /* SACK */ action = action | TCP_PUSH; /* Notify app */ init_tcp_holes( *tcp_seq_num ); } } else if (tcp_ack) action = TCP_DATA; break; case TCP_ESTABLISHED: debug_cond(DEBUG_TCP_PKT, "TCP_ESTABLISHED (%x)\n", tcp_flags ); if (tcp_fin) { /* * Check for holes !! */ *tcp_seq_num = *tcp_seq_num + 1; tcp_next_expected_seq_num ++; action = action | TCP_FIN | TCP_PUSH | TCP_ACK; tcp_state = TCP_CLOSE_WAIT; } else if (tcp_ack) action = TCP_DATA; if (tcp_push) action = action | TCP_PUSH; if (tcp_syn) action = TCP_ACK + TCP_RST;
/* tcp_hole_age( TCP_HOLE_AGE );
if (*tcp_seq_num < tcp_next_expected_seq_num ) tcp_hole_fill( *tcp_seq_num, payload_len ); else { */ if (*tcp_seq_num > tcp_next_expected_seq_num ) { tcp_hole_create( tcp_next_expected_seq_num, *tcp_seq_num ); debug_cond(DEBUG_DCH_PKT, "TCP_ESTABLISHED Seq In %x, Exp %x, action %x\n", *tcp_seq_num, tcp_next_expected_seq_num, action); } if (*tcp_seq_num != tcp_next_expected_seq_num ) net_set_state( NETLOOP_FAIL ); else tcp_next_expected_seq_num = *tcp_seq_num + payload_len; /* } */ debug_cond(DEBUG_TCP_PKT, "TCP_ESTABLISHED (%x), %d\n", action, *tcp_seq_num); break; case TCP_CLOSE_WAIT: debug_cond(DEBUG_TCP_PKT, "TCP_CLOSE_WAIT (%x)\n", tcp_flags); action = TCP_DATA; /* Wait for app */ break; case TCP_FIN_WAIT_2: debug_cond(DEBUG_TCP_PKT, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags); if (tcp_fin) action = TCP_DATA; if (tcp_syn) action = TCP_DATA; if (tcp_ack) {action = TCP_PUSH | TCP_ACK; tcp_state = TCP_CLOSED;} break; case TCP_FIN_WAIT_1: debug_cond(DEBUG_DCH_PKT, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags); if (tcp_fin) action = TCP_ACK | TCP_FIN; tcp_state = TCP_FIN_WAIT_2 ; if (tcp_syn) action = TCP_RST; if (tcp_ack) {tcp_state = TCP_CLOSED; *tcp_seq_num = *tcp_seq_num + 1;} net_set_state( NETLOOP_FAIL ); break; case TCP_CLOSING: debug_cond(DEBUG_TCP_PKT, "TCP_CLOSING (%x)\n", tcp_flags); if (tcp_fin) action = TCP_DATA; if (tcp_syn) action = TCP_RST; if (tcp_ack) {action = TCP_DATA; tcp_state = TCP_CLOSED;} break; } return( action ); }
void rxhand_tcp_f( union tcp_build_pkt *b, unsigned pkt_len ) { int tcp_len = pkt_len - IP_HDR_SIZE; u16 tcp_rx_xsum = b->ip.hdr.ip_sum; u8 tcp_action = TCP_DATA; u8 net_action = TCP_DATA; u32 tcp_seq_num; u32 tcp_ack_num; struct in_addr action_and_state;
int tcp_hdr_len; int payload_len;
/* * Verify ip header */ debug_cond(DEBUG_TCP_PKT, "TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n", &b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len);
debug_cond(DEBUG_TCP_PKT, "____________________________________________\n");
b->ip.hdr.ip_src = net_server_ip; b->ip.hdr.ip_dst = net_ip; b->ip.hdr.ip_sum = 0x0000; if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) { debug_cond(DEBUG_TCP_PKT, "TCP RX IP xum Error (to=%pI4, from=%pI4, len=%d)\n", &net_ip, &net_server_ip, pkt_len); return; }
/* * Build Pseudo header and Verify TCP header */ tcp_rx_xsum = b->ip.hdr.tcp_xsum; b->ip.hdr.tcp_xsum = 0x0000; if (tcp_rx_xsum != net_set_psuedo_header((uchar *)b, b->ip.hdr.ip_src, b->ip.hdr.ip_dst, tcp_len, pkt_len )) { debug_cond(DEBUG_TCP_PKT, "TCP RX TCP xSum Error (to=%pI4, from=%pI4, len=%d)\n", &net_ip, &net_server_ip, tcp_len); return; }
tcp_hdr_len = ( b->ip.hdr.tcp_hlen >> 2);
payload_len = tcp_len - tcp_hdr_len;
if (tcp_hdr_len > TCP_HDR_SIZE) tcp_parse_options((uchar *) b + IP_TCP_HDR_SIZE, tcp_hdr_len - TCP_HDR_SIZE ); /* * Incoming sequence and ack numbers are server's view of the numbers. * The app must swap the numbers when responding. */
tcp_seq_num = ntohl(b->ip.hdr.tcp_seq); tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
tcp_action = tcp_state_machine( b->ip.hdr.tcp_flags, &tcp_seq_num, payload_len );
/* * State altering command to be sent. * The packet sequence and ack numbers are in the tcp_seq_num and tcp_ack_num variables. * The current packet, its position in the date stream, is the in the range of those variables. * * In the "application push" invocation the TCP header with all its information is pointed to by the * packet pointer, and the other variable "repurposed" (or misused) to carry sequence numbers * and TCP state. * * TCP_PUSH from the state machine with a payload length of 0 is a connect or disconnect event */
if (( tcp_action && TCP_PUSH ) || ( payload_len > 0)) { debug_cond(DEBUG_TCP_PKT, "TCP App Notify (action=%x, Seq=%d, Ack=%d, Payload=%d)\n", tcp_action, tcp_seq_num, tcp_ack_num, payload_len );
action_and_state.s_addr = tcp_action; (*tcp_packet_handler) ((uchar *) b + pkt_len - payload_len, tcp_seq_num, action_and_state, tcp_ack_num, payload_len ); } else if ( tcp_action != TCP_DATA ) { debug_cond(DEBUG_TCP_PKT, "TCP Net Action (action=%x, Seq=%d, Ack=%d,Payload=%d)\n", tcp_action, tcp_seq_num, tcp_ack_num, payload_len );
/* * Warning Incoming sequence number are transposed here to TX sequence numbers */
net_action = (tcp_action & (~ TCP_PUSH)); net_send_ip_packet( 0, IPPROTO_TCP, net_action, tcp_ack_num, tcp_seq_num ); } }
#endif
void net_init(void) { static int first_call = 1;
if (first_call) { /* * Setup packet wbuffers, aligned correctly. */ int i;
net_tx_packet = &net_pkt_buf[0] + (PKTALIGN - 1); net_tx_packet -= (ulong)net_tx_packet % PKTALIGN; for (i = 0; i < PKTBUFSRX; i++) { net_rx_packets[i] = net_tx_packet + (i + 1) * PKTSIZE_ALIGN; } arp_init(); net_clear_handlers();
/* Only need to setup buffer pointers once. */ first_call = 0; #ifdef TCP tcp_state = TCP_CLOSED; #endif }
net_init_loop(); }
/**********************************************************************/ /* * Main network processing loop. */
int net_loop(enum proto_t protocol) { int ret = -EINVAL;
net_restarted = 0; net_dev_exists = 0; net_try_count = 1; debug_cond(DEBUG_INT_STATE, "--- net_loop Entry\n");
bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start"); net_init(); if (eth_is_on_demand_init() || protocol != NETCONS) { eth_halt(); eth_set_current(); ret = eth_init(); if (ret < 0) { eth_halt(); return ret; } } else { eth_init_state_only(); } restart: #ifdef CONFIG_USB_KEYBOARD net_busy_flag = 0; #endif net_set_state(NETLOOP_CONTINUE);
/* * Start the ball rolling with the given start function. From * here on, this code is a state machine driven by received * packets and timer events. */ debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n"); net_init_loop();
switch (net_check_prereq(protocol)) { case 1: /* network not configured */ eth_halt(); return -ENODEV;
case 2: /* network device not configured */ break;
case 0: net_dev_exists = 1; net_boot_file_size = 0; switch (protocol) { case TFTPGET: #ifdef CONFIG_CMD_TFTPPUT case TFTPPUT: #endif /* always use ARP to get server ethernet address */ tftp_start(protocol); break; #ifdef CONFIG_CMD_TFTPSRV case TFTPSRV: tftp_start_server(); break; #endif #if defined(CONFIG_CMD_DHCP) case DHCP: bootp_reset(); net_ip.s_addr = 0; dhcp_request(); /* Basically same as BOOTP */ break; #endif
case BOOTP: bootp_reset(); net_ip.s_addr = 0; bootp_request(); break;
#if defined(CONFIG_CMD_RARP) case RARP: rarp_try = 0; net_ip.s_addr = 0; rarp_request(); break; #endif #if defined(CONFIG_CMD_PING) case PING: ping_start(); break; #endif #if defined(CONFIG_CMD_NFS) case NFS: nfs_start(); break; #endif #if defined(CONFIG_CMD_CDP) case CDP: cdp_start(); break; #endif #if defined(CONFIG_NETCONSOLE) && !(CONFIG_SPL_BUILD) case NETCONS: nc_start(); break; #endif #if defined(CONFIG_CMD_SNTP) case SNTP: sntp_start(); break; #endif #if defined(CONFIG_CMD_DNS) case DNS: dns_start(); break; #endif #if defined(CONFIG_CMD_LINK_LOCAL) case LINKLOCAL: link_local_start(); break; #endif #ifdef TCP //#if defined(CONFIG_CMD_WGET) case WGET: wget_start(); break; //#endif #endif
default: break; }
break; }
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) #if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) && \ defined(CONFIG_STATUS_LED) && \ defined(STATUS_LED_RED) /* * Echo the inverted link state to the fault LED. */ if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR)) status_led_set(STATUS_LED_RED, STATUS_LED_OFF); else status_led_set(STATUS_LED_RED, STATUS_LED_ON); #endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */ #endif /* CONFIG_MII, ... */ #ifdef CONFIG_USB_KEYBOARD net_busy_flag = 1; #endif
/* * Main packet reception loop. Loop receiving packets until * someone sets `net_state' to a state that terminates. */ for (;;) { WATCHDOG_RESET(); #ifdef CONFIG_SHOW_ACTIVITY show_activity(1); #endif if (arp_timeout_check() > 0) time_start = get_timer(0);
/* * Check the ethernet for a new packet. The ethernet * receive routine will process it. * Most drivers return the most recent packet size, but not * errors that may have happened. */ eth_rx();
/* * Abort if ctrl-c was pressed. */ if (ctrlc()) { /* cancel any ARP that may not have completed */ net_arp_wait_packet_ip.s_addr = 0;
net_cleanup_loop(); eth_halt(); /* Invalidate the last protocol */ eth_set_last_protocol(BOOTP);
puts("\nAbort\n"); /* include a debug print as well incase the debug messages are directed to stderr */ debug_cond(DEBUG_INT_STATE, "--- net_loop Abort!\n"); ret = -EINTR; goto done; }
/* * Check for a timeout, and run the timeout handler * if we have one. */ if (time_handler && ((get_timer(0) - time_start) > time_delta)) { thand_f *x;
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) #if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) && \ defined(CONFIG_STATUS_LED) && \ defined(STATUS_LED_RED) /* * Echo the inverted link state to the fault LED. */ if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR)) status_led_set(STATUS_LED_RED, STATUS_LED_OFF); else status_led_set(STATUS_LED_RED, STATUS_LED_ON); #endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */ #endif /* CONFIG_MII, ... */ debug_cond(DEBUG_INT_STATE, "--- net_loop timeout\n"); x = time_handler; time_handler = (thand_f *)0; (*x)(); }
if (net_state == NETLOOP_FAIL) ret = net_start_again();
switch (net_state) { case NETLOOP_RESTART: net_restarted = 1; goto restart;
case NETLOOP_SUCCESS: net_cleanup_loop(); if (net_boot_file_size > 0) { printf("Bytes transferred = %d (%x hex)\n", net_boot_file_size, net_boot_file_size); setenv_hex("filesize", net_boot_file_size); setenv_hex("fileaddr", load_addr); } if (protocol != NETCONS) eth_halt(); else eth_halt_state_only();
eth_set_last_protocol(protocol);
ret = net_boot_file_size; debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n"); goto done;
case NETLOOP_FAIL: net_cleanup_loop(); /* Invalidate the last protocol */ eth_set_last_protocol(BOOTP); debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n"); goto done;
case NETLOOP_CONTINUE: continue; } }
done: #ifdef CONFIG_USB_KEYBOARD net_busy_flag = 0; #endif #ifdef CONFIG_CMD_TFTPPUT /* Clear out the handlers */ net_set_udp_handler(NULL); net_set_icmp_handler(NULL); #endif return ret; }
/**********************************************************************/
static void start_again_timeout_handler(void) { net_set_state(NETLOOP_RESTART); }
int net_start_again(void) { char *nretry; int retry_forever = 0; unsigned long retrycnt = 0; int ret;
nretry = getenv("netretry"); if (nretry) { if (!strcmp(nretry, "yes")) retry_forever = 1; else if (!strcmp(nretry, "no")) retrycnt = 0; else if (!strcmp(nretry, "once")) retrycnt = 1; else retrycnt = simple_strtoul(nretry, NULL, 0); } else { retrycnt = 0; retry_forever = 0; }
if ((!retry_forever) && (net_try_count >= retrycnt)) { eth_halt(); net_set_state(NETLOOP_FAIL); /* * We don't provide a way for the protocol to return an error, * but this is almost always the reason. */ return -ETIMEDOUT; }
net_try_count++;
eth_halt(); #if !defined(CONFIG_NET_DO_NOT_TRY_ANOTHER) eth_try_another(!net_restarted); #endif ret = eth_init(); if (net_restart_wrap) { net_restart_wrap = 0; if (net_dev_exists) { net_set_timeout_handler(10000UL, start_again_timeout_handler); net_set_udp_handler(NULL); } else { net_set_state(NETLOOP_FAIL); } } else { net_set_state(NETLOOP_RESTART); } return ret; }
/**********************************************************************/ /* * Miscelaneous bits. */
static void dummy_handler(uchar *pkt, unsigned dport, struct in_addr sip, unsigned sport, unsigned len) { }
rxhand_f *net_get_udp_handler(void) { return udp_packet_handler; }
void net_set_tcp_handler(rxhand_f *f) { debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f); if (f == NULL) tcp_packet_handler = dummy_handler; else tcp_packet_handler = f;
}
void net_set_udp_handler(rxhand_f *f) { debug_cond(DEBUG_INT_STATE, "--- net_loop UDP handler set (%p)\n", f); if (f == NULL) udp_packet_handler = dummy_handler; else udp_packet_handler = f; }
rxhand_f *net_get_arp_handler(void) { return arp_packet_handler; }
void net_set_arp_handler(rxhand_f *f) { debug_cond(DEBUG_INT_STATE, "--- net_loop ARP handler set (%p)\n", f); if (f == NULL) arp_packet_handler = dummy_handler; else arp_packet_handler = f; }
#ifdef CONFIG_CMD_TFTPPUT void net_set_icmp_handler(rxhand_icmp_f *f) { packet_icmp_handler = f; } #endif
void net_set_timeout_handler(ulong iv, thand_f *f) { if (iv == 0) { debug_cond(DEBUG_INT_STATE, "--- net_loop timeout handler cancelled\n"); time_handler = (thand_f *)0; } else { debug_cond(DEBUG_INT_STATE, "--- net_loop timeout handler set (%p)\n", f); time_handler = f; time_start = get_timer(0); time_delta = iv * CONFIG_SYS_HZ / 1000; } }
int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport, int payload_len) { // net_send_udp_orig_packet( ether, dest, dport, sport, payload_len ); // return(0); net_set_ports( dport, sport ); return(net_send_ip_packet( payload_len, IPPROTO_UDP, IPPROTO_UDP, 0, 0)); }
#ifdef CONFIG_IP_DEFRAG /* * This function collects fragments in a single packet, according * to the algorithm in RFC815. It returns NULL or the pointer to * a complete packet, in static storage */ #ifndef CONFIG_NET_MAXDEFRAG #define CONFIG_NET_MAXDEFRAG 16384 #endif #define IP_PKTSIZE (CONFIG_NET_MAXDEFRAG)
#define IP_MAXUDP (IP_PKTSIZE - IP_HDR_SIZE)
/* * this is the packet being assembled, either data or frag control. * Fragments go by 8 bytes, so this union must be 8 bytes long */ struct hole { /* first_byte is address of this structure */ u16 last_byte; /* last byte in this hole + 1 (begin of next hole) */ u16 next_hole; /* index of next (in 8-b blocks), 0 == none */ u16 prev_hole; /* index of prev, 0 == none */ u16 unused; };
static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp) { static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN); static u16 first_hole, total_len; struct hole *payload, *thisfrag, *h, *newh; struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff; uchar *indata = (uchar *)ip; int offset8, start, len, done = 0; u16 ip_off = ntohs(ip->ip_off);
/* payload starts after IP header, this fragment is in there */ payload = (struct hole *)(pkt_buff + IP_HDR_SIZE); offset8 = (ip_off & IP_OFFS); thisfrag = payload + offset8; start = offset8 * 8; len = ntohs(ip->ip_len) - IP_HDR_SIZE;
if (start + len > IP_MAXUDP) /* fragment extends too far */ return NULL;
if (!total_len || localip->ip_id != ip->ip_id) { /* new (or different) packet, reset structs */ total_len = 0xffff; payload[0].last_byte = ~0; payload[0].next_hole = 0; payload[0].prev_hole = 0; first_hole = 0; /* any IP header will work, copy the first we received */ memcpy(localip, ip, IP_HDR_SIZE); }
/* * What follows is the reassembly algorithm. We use the payload * array as a linked list of hole descriptors, as each hole starts * at a multiple of 8 bytes. However, last byte can be whatever value, * so it is represented as byte count, not as 8-byte blocks. */
h = payload + first_hole; while (h->last_byte < start) { if (!h->next_hole) { /* no hole that far away */ return NULL; } h = payload + h->next_hole; }
/* last fragment may be 1..7 bytes, the "+7" forces acceptance */ if (offset8 + ((len + 7) / 8) <= h - payload) { /* no overlap with holes (dup fragment?) */ return NULL; }
if (!(ip_off & IP_FLAGS_MFRAG)) { /* no more fragmentss: truncate this (last) hole */ total_len = start + len; h->last_byte = start + len; }
/* * 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). */
if ((h >= thisfrag) && (h->last_byte <= start + len)) { /* complete overlap with hole: remove hole */ if (!h->prev_hole && !h->next_hole) { /* last remaining hole */ done = 1; } else if (!h->prev_hole) { /* first hole */ first_hole = h->next_hole; payload[h->next_hole].prev_hole = 0; } else if (!h->next_hole) { /* last hole */ payload[h->prev_hole].next_hole = 0; } else { /* in the middle of the list */ payload[h->next_hole].prev_hole = h->prev_hole; payload[h->prev_hole].next_hole = h->next_hole; }
} else if (h->last_byte <= start + len) { /* overlaps with final part of the hole: shorten this hole */ h->last_byte = start;
} else if (h >= thisfrag) { /* overlaps with initial part of the hole: move this hole */ newh = thisfrag + (len / 8); *newh = *h; h = newh; if (h->next_hole) payload[h->next_hole].prev_hole = (h - payload); if (h->prev_hole) payload[h->prev_hole].next_hole = (h - payload); else first_hole = (h - payload);
} else { /* fragment sits in the middle: split the hole */ newh = thisfrag + (len / 8); *newh = *h; h->last_byte = start; h->next_hole = (newh - payload); newh->prev_hole = (h - payload); if (newh->next_hole) payload[newh->next_hole].prev_hole = (newh - payload); }
/* finally copy this fragment and possibly return whole packet */ memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len); if (!done) return NULL;
localip->ip_len = htons(total_len); *lenp = total_len + IP_HDR_SIZE; return localip; }
static inline struct ip_udp_hdr *net_defragment(struct ip_udp_hdr *ip, int *lenp) { u16 ip_off = ntohs(ip->ip_off); if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG))) return ip; /* not a fragment */ return __net_defragment(ip, lenp); }
#else /* !CONFIG_IP_DEFRAG */
static inline struct ip_udp_hdr *net_defragment(struct ip_udp_hdr *ip, int *lenp) { u16 ip_off = ntohs(ip->ip_off); if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG))) return ip; /* not a fragment */ return NULL; } #endif
/** * Receive an ICMP packet. We deal with REDIRECT and PING here, and silently * drop others. * * @parma ip IP packet containing the ICMP */ static void receive_icmp(struct ip_udp_hdr *ip, int len, struct in_addr src_ip, struct ethernet_hdr *et) { struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;
switch (icmph->type) { case ICMP_REDIRECT: if (icmph->code != ICMP_REDIR_HOST) return; printf(" ICMP Host Redirect to %pI4 ", &icmph->un.gateway); break; default: #if defined(CONFIG_CMD_PING) ping_receive(et, ip, len); #endif #ifdef CONFIG_CMD_TFTPPUT if (packet_icmp_handler) packet_icmp_handler(icmph->type, icmph->code, ntohs(ip->udp_dst), src_ip, ntohs(ip->udp_src), icmph->un.data, ntohs(ip->udp_len)); #endif break; } }
void net_process_received_packet(uchar *in_packet, int len) { struct ethernet_hdr *et; struct ip_udp_hdr *ip; struct in_addr dst_ip; struct in_addr src_ip; int eth_proto; #if defined(CONFIG_CMD_CDP) int iscdp; #endif ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
debug_cond(DEBUG_NET_PKT, "packet received\n");
net_rx_packet = in_packet; net_rx_packet_len = len; et = (struct ethernet_hdr *)in_packet;
/* too small packet? */ if (len < ETHER_HDR_SIZE) return;
#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) if (push_packet) { (*push_packet)(in_packet, len); return; } #endif
#if defined(CONFIG_CMD_CDP) /* keep track if packet is CDP */ iscdp = is_cdp_packet(et->et_dest); #endif
myvlanid = ntohs(net_our_vlan); if (myvlanid == (ushort)-1) myvlanid = VLAN_NONE; mynvlanid = ntohs(net_native_vlan); if (mynvlanid == (ushort)-1) mynvlanid = VLAN_NONE;
eth_proto = ntohs(et->et_protlen);
if (eth_proto < 1514) { struct e802_hdr *et802 = (struct e802_hdr *)et; /* * Got a 802.2 packet. Check the other protocol field. * XXX VLAN over 802.2+SNAP not implemented! */ eth_proto = ntohs(et802->et_prot);
ip = (struct ip_udp_hdr *)(in_packet + E802_HDR_SIZE); len -= E802_HDR_SIZE;
} else if (eth_proto != PROT_VLAN) { /* normal packet */ ip = (struct ip_udp_hdr *)(in_packet + ETHER_HDR_SIZE); len -= ETHER_HDR_SIZE;
} else { /* VLAN packet */ struct vlan_ethernet_hdr *vet = (struct vlan_ethernet_hdr *)et;
debug_cond(DEBUG_NET_PKT, "VLAN packet received\n");
/* too small packet? */ if (len < VLAN_ETHER_HDR_SIZE) return;
/* if no VLAN active */ if ((ntohs(net_our_vlan) & VLAN_IDMASK) == VLAN_NONE #if defined(CONFIG_CMD_CDP) && iscdp == 0 #endif ) return;
cti = ntohs(vet->vet_tag); vlanid = cti & VLAN_IDMASK; eth_proto = ntohs(vet->vet_type);
ip = (struct ip_udp_hdr *)(in_packet + VLAN_ETHER_HDR_SIZE); len -= VLAN_ETHER_HDR_SIZE; }
debug_cond(DEBUG_NET_PKT, "Receive from protocol 0x%x\n", eth_proto);
#if defined(CONFIG_CMD_CDP) if (iscdp) { cdp_receive((uchar *)ip, len); return; } #endif
if ((myvlanid & VLAN_IDMASK) != VLAN_NONE) { if (vlanid == VLAN_NONE) vlanid = (mynvlanid & VLAN_IDMASK); /* not matched? */ if (vlanid != (myvlanid & VLAN_IDMASK)) return; }
switch (eth_proto) { case PROT_ARP: arp_receive(et, ip, len); break;
#ifdef CONFIG_CMD_RARP case PROT_RARP: rarp_receive(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) { debug("len bad %d < %lu\n", len, (ulong)IP_UDP_HDR_SIZE); return; } /* Check the packet length */ if (len < ntohs(ip->ip_len)) { debug("len bad %d < %d\n", len, ntohs(ip->ip_len)); return; } len = ntohs(ip->ip_len); debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n", len, ip->ip_hl_v & 0xff);
/* Can't deal with anything except IPv4 */ if ((ip->ip_hl_v & 0xf0) != 0x40) return; /* Can't deal with IP options (headers != 20 bytes) */ if ((ip->ip_hl_v & 0x0f) > 0x05) return; /* Check the Checksum of the header */ if (!ip_checksum_ok((uchar *)ip, IP_HDR_SIZE)) { debug("checksum bad\n"); return; } /* If it is not for us, ignore it */ dst_ip = net_read_ip(&ip->ip_dst); if (net_ip.s_addr && dst_ip.s_addr != net_ip.s_addr && dst_ip.s_addr != 0xFFFFFFFF) { #ifdef CONFIG_MCAST_TFTP if (net_mcast_addr != dst_ip) #endif return; } /* Read source IP address for later use */ src_ip = net_read_ip(&ip->ip_src); /* * The function returns the unchanged packet if it's not * a fragment, and either the complete packet or NULL if * it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL) */ ip = net_defragment(ip, &len); if (!ip) return; /* * watch for ICMP host redirects * * There is no real handler code (yet). We just watch * for ICMP host redirect messages. In case anybody * sees these messages: please contact me * (wd@denx.de), or - even better - send me the * necessary fixes :-) * * Note: in all cases where I have seen this so far * it was a problem with the router configuration, * for instance when a router was configured in the * BOOTP reply, but the TFTP server was on the same * subnet. So this is probably a warning that your * configuration might be wrong. But I'm not really * sure if there aren't any other situations. * * Simon Glass sjg@chromium.org: We get an ICMP when * we send a tftp packet to a dead connection, or when * there is no server at the other end. */ if (ip->ip_p == IPPROTO_ICMP) { receive_icmp(ip, len, src_ip, et); return; } else if (ip->ip_p == IPPROTO_UDP) { /* Only UDP packets */
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) { ulong xsum; ushort *sumptr; ushort sumlen;
xsum = ip->ip_p; xsum += (ntohs(ip->udp_len)); xsum += (ntohl(ip->ip_src.s_addr) >> 16) & 0x0000ffff; xsum += (ntohl(ip->ip_src.s_addr) >> 0) & 0x0000ffff; xsum += (ntohl(ip->ip_dst.s_addr) >> 16) & 0x0000ffff; xsum += (ntohl(ip->ip_dst.s_addr) >> 0) & 0x0000ffff;
sumlen = ntohs(ip->udp_len); sumptr = (ushort *)&(ip->udp_src);
while (sumlen > 1) { ushort sumdata;
sumdata = *sumptr++; xsum += ntohs(sumdata); sumlen -= 2; } if (sumlen > 0) { ushort sumdata;
sumdata = *(unsigned char *)sumptr; sumdata = (sumdata << 8) & 0xff00; xsum += sumdata; } while ((xsum >> 16) != 0) { xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff); } if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) { printf(" UDP wrong checksum %08lx %08x\n", xsum, ntohs(ip->udp_xsum)); return; } } #endif
#if defined(CONFIG_NETCONSOLE) && !(CONFIG_SPL_BUILD) nc_input_packet((uchar *)ip + IP_UDP_HDR_SIZE, src_ip, ntohs(ip->udp_dst), ntohs(ip->udp_src), ntohs(ip->udp_len) - UDP_HDR_SIZE); #endif /* * IP header OK. Pass the packet to the current handler. */ debug_cond(DEBUG_DEV_PKT, "UDP PH (to=%pI4, from=%pI4, len=%d)\n", &dst_ip, &src_ip, len); (*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE, ntohs(ip->udp_dst), src_ip, ntohs(ip->udp_src), ntohs(ip->udp_len) - UDP_HDR_SIZE);
} #ifdef TCP else if (ip->ip_p == IPPROTO_TCP) { /* Only TCP packets */
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);
} #endif
} }
/**********************************************************************/
static int net_check_prereq(enum proto_t protocol) { switch (protocol) { /* Fall through */ #if defined(CONFIG_CMD_PING) case PING: if (net_ping_ip.s_addr == 0) { puts("*** ERROR: ping address not given\n"); return 1; } goto common; #endif #if defined(CONFIG_CMD_SNTP) case SNTP: if (net_ntp_server.s_addr == 0) { puts("*** ERROR: NTP server address not given\n"); return 1; } goto common; #endif #if defined(CONFIG_CMD_DNS) case DNS: if (net_dns_server.s_addr == 0) { puts("*** ERROR: DNS server address not given\n"); return 1; } goto common; #endif #if defined(CONFIG_CMD_NFS) case NFS: #endif /* Fall through */ case TFTPGET: case TFTPPUT: if (net_server_ip.s_addr == 0) { puts("*** ERROR: `serverip' not set\n"); return 1; } #if defined(CONFIG_CMD_PING) || defined(CONFIG_CMD_SNTP) || \ defined(CONFIG_CMD_DNS) common: #endif /* Fall through */
case NETCONS: case TFTPSRV: if (net_ip.s_addr == 0) { puts("*** ERROR: `ipaddr' not set\n"); return 1; } /* Fall through */
#ifdef CONFIG_CMD_RARP case RARP: #endif case BOOTP: case CDP: case DHCP: case LINKLOCAL: if (memcmp(net_ethaddr, "\0\0\0\0\0\0", 6) == 0) { int num = eth_get_dev_index();
switch (num) { case -1: puts("*** ERROR: No ethernet found.\n"); return 1; case 0: puts("*** ERROR: `ethaddr' not set\n"); break; default: printf("*** ERROR: `eth%daddr' not set\n", num); break; }
net_start_again(); return 2; } /* Fall through */ default: return 0; } return 0; /* OK */ } /**********************************************************************/
int net_eth_hdr_size(void) { ushort myvlanid;
myvlanid = ntohs(net_our_vlan); if (myvlanid == (ushort)-1) myvlanid = VLAN_NONE;
return ((myvlanid & VLAN_IDMASK) == VLAN_NONE) ? ETHER_HDR_SIZE : VLAN_ETHER_HDR_SIZE; }
int net_set_ether(uchar *xet, const uchar *dest_ethaddr, uint prot) { struct ethernet_hdr *et = (struct ethernet_hdr *)xet; ushort myvlanid;
myvlanid = ntohs(net_our_vlan); if (myvlanid == (ushort)-1) myvlanid = VLAN_NONE;
memcpy(et->et_dest, dest_ethaddr, 6); memcpy(et->et_src, net_ethaddr, 6); if ((myvlanid & VLAN_IDMASK) == VLAN_NONE) { et->et_protlen = htons(prot); return ETHER_HDR_SIZE; } else { struct vlan_ethernet_hdr *vet = (struct vlan_ethernet_hdr *)xet;
vet->vet_vlan_type = htons(PROT_VLAN); vet->vet_tag = htons((0 << 5) | (myvlanid & VLAN_IDMASK)); vet->vet_type = htons(prot); return VLAN_ETHER_HDR_SIZE; } }
int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot) { ushort protlen;
memcpy(et->et_dest, addr, 6); memcpy(et->et_src, net_ethaddr, 6); protlen = ntohs(et->et_protlen); if (protlen == PROT_VLAN) { struct vlan_ethernet_hdr *vet = (struct vlan_ethernet_hdr *)et; vet->vet_type = htons(prot); return VLAN_ETHER_HDR_SIZE; } else if (protlen > 1514) { et->et_protlen = htons(prot); return ETHER_HDR_SIZE; } else { /* 802.2 + SNAP */ struct e802_hdr *et802 = (struct e802_hdr *)et; et802->et_prot = htons(prot); return E802_HDR_SIZE; } }
void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source, u16 pkt_len, u8 prot) { struct ip_hdr *ip = (struct ip_hdr *)pkt;
/* * Construct an IP header. */ /* IP_HDR_SIZE / 4 (not including UDP) */ ip->ip_hl_v = 0x45; ip->ip_tos = 0; ip->ip_len = htons(pkt_len); ip->ip_id = htons(net_ip_id++); ip->ip_off = htons(IP_FLAGS_DFRAG); /* Don't fragment */ ip->ip_ttl = 255; ip->ip_p = prot; ip->ip_sum = 0; /* already in network byte order */ net_copy_ip((void *)&ip->ip_src, &source); /* already in network byte order */ net_copy_ip((void *)&ip->ip_dst, &dest); ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE); }
void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport, int sport, int len) { struct ip_udp_hdr *ip = (struct ip_udp_hdr *)pkt;
/* * If the data is an odd number of bytes, zero the * byte after the last byte so that the checksum * will work. */ if (len & 1) pkt[IP_UDP_HDR_SIZE + len] = 0;
net_set_ip_header(pkt, dest, net_ip, IP_UDP_HDR_SIZE + len, IPPROTO_UDP);
ip->udp_src = htons(sport); ip->udp_dst = htons(dport); ip->udp_len = htons(UDP_HDR_SIZE + len); ip->udp_xsum = 0; }
void copy_filename(char *dst, const char *src, int size) { if (*src && (*src == '"')) { ++src; --size; }
while ((--size > 0) && *src && (*src != '"')) *dst++ = *src++; *dst = '\0'; }
#if defined(CONFIG_CMD_NFS) || \ defined(CONFIG_CMD_SNTP) || \ defined(CONFIG_CMD_DNS) || \ defined(CONFIG_CMD_WGET) /* * make port a little random (1024-17407) * This keeps the math somewhat trivial to compute, and seems to work with * all supported protocols/clients/servers */ unsigned int random_port(void) { return 1024 + (get_timer(0) % 0x4000); } #endif
void ip_to_string(struct in_addr x, char *s) { x.s_addr = ntohl(x.s_addr); sprintf(s, "%d.%d.%d.%d", (int) ((x.s_addr >> 24) & 0xff), (int) ((x.s_addr >> 16) & 0xff), (int) ((x.s_addr >> 8) & 0xff), (int) ((x.s_addr >> 0) & 0xff) ); }
void vlan_to_string(ushort x, char *s) { x = ntohs(x);
if (x == (ushort)-1) x = VLAN_NONE;
if (x == VLAN_NONE) strcpy(s, "none"); else sprintf(s, "%d", x & VLAN_IDMASK); }
ushort string_to_vlan(const char *s) { ushort id;
if (s == NULL) return htons(VLAN_NONE);
if (*s < '0' || *s > '9') id = VLAN_NONE; else id = (ushort)simple_strtoul(s, NULL, 10);
return htons(id); }
ushort getenv_vlan(char *var) { return string_to_vlan(getenv(var)); }include/net.h /* * LiMon Monitor (LiMon) - Network. * * Copyright 1994 - 2000 Neil Russell. * (See License) * SPDX-License-Identifier: GPL-2.0 * * History * 9/16/00 bor adapted to TQM823L/STK8xxL board, RARP/TFTP boot added * 9/20/2017 dch Added TCP listener */
#ifndef __NET_H__ #define __NET_H__
#if defined(CONFIG_8xx) #include <commproc.h> #endif /* CONFIG_8xx */
#include <asm/cache.h> #include <asm/byteorder.h> /* for nton* / ntoh* stuff */ #define DEBUG_LL_STATE 0 /* Link local state machine changes */ #define DEBUG_DEV_PKT 0 /* Packets or info directed to the device */ #define DEBUG_NET_PKT 0 /* Packets on info on the network at large */ #define DEBUG_INT_STATE 0 /* Internal network state changes */
/* * The number of receive packet buffers, and the required packet buffer * alignment in memory. * */
#define TCP 1
#ifdef TCP #define CONFIG_SYS_RX_ETH_BUFFER 50 /* For TCP */ #endif
#ifdef CONFIG_SYS_RX_ETH_BUFFER # define PKTBUFSRX CONFIG_SYS_RX_ETH_BUFFER #else # define PKTBUFSRX 4 #endif
#define PKTALIGN ARCH_DMA_MINALIGN
/* IPv4 addresses are always 32 bits in size */ struct in_addr { __be32 s_addr; };
/** * An incoming packet handler. * @param pkt pointer to the application packet * @param dport destination UDP port * @param sip source IP address * @param sport source UDP port * @param len packet length */ typedef void rxhand_f(uchar *pkt, unsigned dport, struct in_addr sip, unsigned sport, unsigned len);
/** * An incoming ICMP packet handler. * @param type ICMP type * @param code ICMP code * @param dport destination UDP port * @param sip source IP address * @param sport source UDP port * @param pkt pointer to the ICMP packet data * @param len packet length */ typedef void rxhand_icmp_f(unsigned type, unsigned code, unsigned dport, struct in_addr sip, unsigned sport, uchar *pkt, unsigned len);
/* * A timeout handler. Called after time interval has expired. */ typedef void thand_f(void);
enum eth_state_t { ETH_STATE_INIT, ETH_STATE_PASSIVE, ETH_STATE_ACTIVE };
#ifdef CONFIG_DM_ETH /** * struct eth_pdata - Platform data for Ethernet MAC controllers * * @iobase: The base address of the hardware registers * @enetaddr: The Ethernet MAC address that is loaded from EEPROM or env * @phy_interface: PHY interface to use - see PHY_INTERFACE_MODE_... * @max_speed: Maximum speed of Ethernet connection supported by MAC */ struct eth_pdata { phys_addr_t iobase; unsigned char enetaddr[6]; int phy_interface; int max_speed; };
enum eth_recv_flags { /* * Check hardware device for new packets (otherwise only return those * which are already in the memory buffer ready to process) */ ETH_RECV_CHECK_DEVICE = 1 << 0, };
/** * struct eth_ops - functions of Ethernet MAC controllers * * start: Prepare the hardware to send and receive packets * send: Send the bytes passed in "packet" as a packet on the wire * recv: Check if the hardware received a packet. If so, set the pointer to the * packet buffer in the packetp parameter. If not, return an error or 0 to * indicate that the hardware receive FIFO is empty. If 0 is returned, the * network stack will not process the empty packet, but free_pkt() will be * called if supplied * free_pkt: Give the driver an opportunity to manage its packet buffer memory * when the network stack is finished processing it. This will only be * called when no error was returned from recv - optional * stop: Stop the hardware from looking for packets - may be called even if * state == PASSIVE * mcast: Join or leave a multicast group (for TFTP) - optional * write_hwaddr: Write a MAC address to the hardware (used to pass it to Linux * on some platforms like ARM). This function expects the * eth_pdata::enetaddr field to be populated. The method can * return -ENOSYS to indicate that this is not implemented for this hardware - optional. * read_rom_hwaddr: Some devices have a backup of the MAC address stored in a * ROM on the board. This is how the driver should expose it * to the network stack. This function should fill in the * eth_pdata::enetaddr field - optional */ struct eth_ops { int (*start)(struct udevice *dev); int (*send)(struct udevice *dev, void *packet, int length); int (*recv)(struct udevice *dev, int flags, uchar **packetp); int (*free_pkt)(struct udevice *dev, uchar *packet, int length); void (*stop)(struct udevice *dev); #ifdef CONFIG_MCAST_TFTP int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); #endif int (*write_hwaddr)(struct udevice *dev); int (*read_rom_hwaddr)(struct udevice *dev); };
#define eth_get_ops(dev) ((struct eth_ops *)(dev)->driver->ops)
struct udevice *eth_get_dev(void); /* get the current device */ /* * The devname can be either an exact name given by the driver or device tree * or it can be an alias of the form "eth%d" */ struct udevice *eth_get_dev_by_name(const char *devname); unsigned char *eth_get_ethaddr(void); /* get the current device MAC */
/* Used only when NetConsole is enabled */ int eth_is_active(struct udevice *dev); /* Test device for active state */ int eth_init_state_only(void); /* Set active state */ void eth_halt_state_only(void); /* Set passive state */ #endif
#ifndef CONFIG_DM_ETH struct eth_device { char name[16]; unsigned char enetaddr[6]; phys_addr_t iobase; int state;
int (*init)(struct eth_device *, bd_t *); int (*send)(struct eth_device *, void *packet, int length); int (*recv)(struct eth_device *); void (*halt)(struct eth_device *); #ifdef CONFIG_MCAST_TFTP int (*mcast)(struct eth_device *, const u8 *enetaddr, u8 set); #endif int (*write_hwaddr)(struct eth_device *); struct eth_device *next; int index; void *priv; };
int eth_register(struct eth_device *dev);/* Register network device */ int eth_unregister(struct eth_device *dev);/* Remove network device */
extern struct eth_device *eth_current;
static __always_inline struct eth_device *eth_get_dev(void) { return eth_current; } struct eth_device *eth_get_dev_by_name(const char *devname); struct eth_device *eth_get_dev_by_index(int index); /* get dev @ index */
/* get the current device MAC */ static inline unsigned char *eth_get_ethaddr(void) { if (eth_current) return eth_current->enetaddr; return NULL; }
/* Used only when NetConsole is enabled */ int eth_is_active(struct eth_device *dev); /* Test device for active state */ /* Set active state */ static __always_inline int eth_init_state_only(void) { eth_get_dev()->state = ETH_STATE_ACTIVE;
return 0; } /* Set passive state */ static __always_inline void eth_halt_state_only(void) { eth_get_dev()->state = ETH_STATE_PASSIVE; }
/* * Set the hardware address for an ethernet interface based on 'eth%daddr' * environment variable (or just 'ethaddr' if eth_number is 0). * Args: * base_name - base name for device (normally "eth") * eth_number - value of %d (0 for first device of this type) * Returns: * 0 is success, non-zero is error status from driver. */ int eth_write_hwaddr(struct eth_device *dev, const char *base_name, int eth_number);
int usb_eth_initialize(bd_t *bi); #endif
int eth_initialize(void); /* Initialize network subsystem */ void eth_try_another(int first_restart); /* Change the device */ void eth_set_current(void); /* set nterface to ethcur var */
int eth_get_dev_index(void); /* get the device index */ void eth_parse_enetaddr(const char *addr, uchar *enetaddr); int eth_getenv_enetaddr(const char *name, uchar *enetaddr); int eth_setenv_enetaddr(const char *name, const uchar *enetaddr);
/** * eth_setenv_enetaddr_by_index() - set the MAC address environment variable * * This sets up an environment variable with the given MAC address (@enetaddr). * The environment variable to be set is defined by <@base_name><@index>addr. * If @index is 0 it is omitted. For common Ethernet this means ethaddr, * eth1addr, etc. * * @base_name: Base name for variable, typically "eth" * @index: Index of interface being updated (>=0) * @enetaddr: Pointer to MAC address to put into the variable * @return 0 if OK, other value on error */ int eth_setenv_enetaddr_by_index(const char *base_name, int index, uchar *enetaddr);
/* * Get the hardware address for an ethernet interface . * Args: * base_name - base name for device (normally "eth") * index - device index number (0 for first) * enetaddr - returns 6 byte hardware address * Returns: * Return true if the address is valid. */ int eth_getenv_enetaddr_by_index(const char *base_name, int index, uchar *enetaddr);
int eth_init(void); /* Initialize the device */ int eth_send(void *packet, int length); /* Send a packet */
#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) int eth_receive(void *packet, int length); /* Receive a packet*/ extern void (*push_packet)(void *packet, int length); #endif int eth_rx(void); /* Check for received packets */ void eth_halt(void); /* stop SCC */ const char *eth_get_name(void); /* get name of current device */
#ifdef CONFIG_MCAST_TFTP int eth_mcast_join(struct in_addr mcast_addr, int join); u32 ether_crc(size_t len, unsigned char const *p); #endif
/**********************************************************************/ /* * Protocol headers. */
/* * Ethernet header */
struct ethernet_hdr { u8 et_dest[6]; /* Destination node */ u8 et_src[6]; /* Source node */ u16 et_protlen; /* Protocol or length */ };
/* Ethernet header size */ #define ETHER_HDR_SIZE (sizeof(struct ethernet_hdr))
#define ETH_FCS_LEN 4 /* Octets in the FCS */
struct e802_hdr { u8 et_dest[6]; /* Destination node */ u8 et_src[6]; /* Source node */ u16 et_protlen; /* Protocol or length */ u8 et_dsap; /* 802 DSAP */ u8 et_ssap; /* 802 SSAP */ u8 et_ctl; /* 802 control */ u8 et_snap1; /* SNAP */ u8 et_snap2; u8 et_snap3; u16 et_prot; /* 802 protocol */ }__attribute__ ((packed));
/* 802 + SNAP + ethernet header size */ #define E802_HDR_SIZE (sizeof(struct e802_hdr))
/* * Virtual LAN Ethernet header */ struct vlan_ethernet_hdr { u8 vet_dest[6]; /* Destination node */ u8 vet_src[6]; /* Source node */ u16 vet_vlan_type; /* PROT_VLAN */ u16 vet_tag; /* TAG of VLAN */ u16 vet_type; /* protocol type */ }__attribute__ ((packed));
/* VLAN Ethernet header size */ #define VLAN_ETHER_HDR_SIZE (sizeof(struct vlan_ethernet_hdr))
#define PROT_IP 0x0800 /* IP protocol */ #define PROT_ARP 0x0806 /* IP ARP protocol */ #define PROT_RARP 0x8035 /* IP ARP protocol */ #define PROT_VLAN 0x8100 /* IEEE 802.1q protocol */ #define PROT_IPV6 0x86dd /* IPv6 over bluebook */ #define PROT_PPP_SES 0x8864 /* PPPoE session messages */
#define IPPROTO_ICMP 1 /* Internet Control Message Protocol */ #define IPPROTO_TCP 6 /* Transmission Control Protocol */ #define IPPROTO_UDP 17 /* User Datagram Protocol */
/* * Internet Protocol (IP) header. */ struct ip_hdr { u8 ip_hl_v; /* header length and version */ u8 ip_tos; /* type of service */ u16 ip_len; /* total length */ u16 ip_id; /* identification */ u16 ip_off; /* fragment offset field */ u8 ip_ttl; /* time to live */ u8 ip_p; /* protocol */ u16 ip_sum; /* checksum */ struct in_addr ip_src; /* Source IP address */ struct in_addr ip_dst; /* Destination IP address */ }__attribute__ ((packed));
#define IP_OFFS 0x1fff /* ip offset *= 8 */ #define IP_FLAGS 0xe000 /* first 3 bits */ #define IP_FLAGS_RES 0x8000 /* reserved */ #define IP_FLAGS_DFRAG 0x4000 /* don't fragment */ #define IP_FLAGS_MFRAG 0x2000 /* more fragments */
#define IP_HDR_SIZE (sizeof(struct ip_hdr))
/* * Internet Protocol (IP) + UDP header. */ struct ip_udp_hdr { u8 ip_hl_v; /* header length and version */ u8 ip_tos; /* type of service */ u16 ip_len; /* total length */ u16 ip_id; /* identification */ /* Flags first 3 bits of ip_off */ u16 ip_off; /* fragment offset field */ u8 ip_ttl; /* time to live */ u8 ip_p; /* protocol */ u16 ip_sum; /* checksum */ struct in_addr ip_src; /* Source IP address */ struct in_addr ip_dst; /* Destination IP address */ u16 udp_src; /* UDP source port */ u16 udp_dst; /* UDP destination port */ u16 udp_len; /* Length of UDP packet */ u16 udp_xsum; /* Checksum */ }__attribute__ ((packed));
#define IP_UDP_HDR_SIZE (sizeof(struct ip_udp_hdr)) #define UDP_HDR_SIZE (IP_UDP_HDR_SIZE - IP_HDR_SIZE)
/* * Internet Protocol (IP) + TCP header. */ struct ip_tcp_hdr { u8 ip_hl_v; /* header length and version */ u8 ip_tos; /* type of service */ u16 ip_len; /* total length */ u16 ip_id; /* identification */ u16 ip_off; /* fragment offset field */ u8 ip_ttl; /* time to live */ u8 ip_p; /* protocol */ u16 ip_sum; /* checksum */ struct in_addr ip_src; /* Source IP address */ struct in_addr ip_dst; /* Destination IP address */ u16 tcp_src; /* TCP source port */ u16 tcp_dst; /* TCP destination port */ u32 tcp_seq; /* TCP sequence number */ u32 tcp_ack; /* TCP Acknowledgement number */ u8 tcp_hlen; /* 4 bits TCP header Length/4 */ /* 4 bits Reserved */ /* 2 more bits reserver */ u8 tcp_flags; /* see defines */ u16 tcp_win; /* TCP windows size */ u16 tcp_xsum; /* Checksum */ u16 tcp_ugr; /* Pointer to urgent data */ }__attribute__ ((packed));
#define IP_TCP_HDR_SIZE (sizeof(struct ip_tcp_hdr)) #define TCP_HDR_SIZE (IP_TCP_HDR_SIZE - IP_HDR_SIZE)
#define TCP_DATA 0x00 /* Data Packet - internal use only */ #define TCP_FIN 0x01 /* Finish flag */ #define TCP_SYN 0x02 /* start flag */ #define TCP_RST 0x04 /* reset flag */ #define TCP_PUSH 0x08 /* Push - Notify app */ #define TCP_ACK 0x10 /* Acknowledgement of data received */ #define TCP_URG 0x20 /* Urgent */ #define TCP_ECE 0x40 /* Unknown */ #define TCP_CWR 0x80 /* Unknown */
/* * TCP header options, Seq, MSS, and SACK */
#define TCP_O_END 0x00 /* End of option list */ #define TCP_1_NOP 0x01 /* Single padding NOP */ #define TCP_O_NOP 0x01010101 /* NOPs pad to 32 bit boundary */ #define TCP_O_MSS 0x02 /* MSS Size option */ #define TCP_O_SCL 0x03 /* Window Scale option */ #define TCP_P_SACK 0x04 /* SACK permitted */ #define TCP_V_SACK 0x05 /* SACK values */ #define TCP_O_TS 0x08 /* Timestanp option */ #define TCP_OPT_LEN_2 0x02 #define TCP_OPT_LEN_3 0x03 #define TCP_OPT_LEN_4 0x04 #define TCP_OPT_LEN_6 0x06 #define TCP_OPT_LEN_8 0x08 #define TCP_OPT_LEN_A 0x0a /* TimestampLength */
#define TCP_MSS 1460 /* Max segment size - 1460 */ #define TCP_SCALE 0x01 /* Scale 1 */
struct tcp_mss{ /* TCP Mex Segment size */ u8 kind ; /* 0x02 */ u8 len; /* 0x04 */ u16 mss; /* 1460 - Max segment size */ }__attribute__ ((packed));
struct tcp_scale { /* TCP Windows Scale */ u8 kind; /* 0x03 */ u8 len; /* 0x03 */ u8 scale; /* win shift fat fast networks */ }__attribute__ ((packed));
struct tcp_sack_p { /* SACK permitted */ u8 kind; /* 0x04 */ u8 len; /* Length */ }__attribute__ ((packed));
struct sack_edges { u32 l; u32 r; }__attribute__ ((packed));
#define TCP_STREAM_HOLES 16
#define TCP_SAK_HOLES 3
#define TCP_HOLE_AGE 4
struct tcp_sack_v { u8 kind; /* 0x05 */ u8 len; /* Length */ struct sack_edges hole[TCP_SAK_HOLES]; /* L & R widow edges */ }__attribute__ ((packed));
struct tcp_TSopt{ /* TCP time stamps option */ u8 kind; /* 0x08 */ u8 len; /* 0x0a */ u32 TSsnd; /* Sender timestamp */ u32 TSrcv; /* Receiver timestamp */ }__attribute__ ((packed));
#define TCP_TSOPT_SIZE (sizeof(struct tcp_TSopt))
/* * ip tcp structure with options */
struct ip_tcp_hdr_o { struct ip_tcp_hdr hdr; struct tcp_mss mss; struct tcp_scale scale; struct tcp_sack_p sack_p; struct tcp_TSopt TSopt; u8 end; }__attribute__ ((packed));
#define IP_TCP_O_SIZE (sizeof(struct ip_tcp_hdr_o))
struct ip_tcp_hdr_s { struct ip_tcp_hdr hdr; struct tcp_TSopt TSopt; struct tcp_sack_v sack_v; u8 end; }__attribute__ ((packed));
#define IP_TCP_SACK_SIZE (sizeof(struct ip_tcp_hdr_s))
/* * TCP psuedo header definitions */
struct psuedo_hdr{ u8 padding[8]; /* psuedo header size = ip_tcp hdr size */ struct in_addr p_src; struct in_addr p_dst; u8 rsvd; u8 p; u16 len; }__attribute__ ((packed));
#define PSUEDO_PAD_SIZE 8 #define PSUEDO_HDR_SIZE 12
/* * union for building IP/TCP packet. * build Psuedo header in packed bufferfirst, calculate TCP checksum * then build IP header in packe buffer. */
union tcp_build_pkt{ struct psuedo_hdr ph; struct ip_tcp_hdr_o ip; struct ip_tcp_hdr_s sack; uchar raw[1600]; }__attribute__ ((packed));
/* * TCP STATE MACHINE STATES FOR SOCKET */
enum TCP_STATE { TCP_CLOSED, /* Need to send SYN to connect */ TCP_SYN_SENT, /* Trying to connect, waiting for SYN ACK */ TCP_ESTABLISHED, /* both server and client represents an open connection */ TCP_CLOSE_WAIT, /* Received FIN, passed to app for FIN, ACK response */ TCP_CLOSING, /* Received FIN, sent FIN, ACK waiting for ACK */ TCP_FIN_WAIT_1, /* Sendt FIN waiting for response */ TCP_FIN_WAIT_2 /* Received ACK from FIN sent, waitng for FIN */ };
enum TCP_STATE net_get_tcp_state( void );
/* * TCP incoming packet handler */
typedef void rxhand_tcp(uchar *pkt, unsigned dport, struct in_addr sip, unsigned sport, unsigned len, enum TCP_STATE tcp_state); /* * Address Resolution Protocol (ARP) header. */ struct arp_hdr { u16 ar_hrd; /* Format of hardware address */ # define ARP_ETHER 1 /* Ethernet hardware address */ u16 ar_pro; /* Format of protocol address */ u8 ar_hln; /* Length of hardware address */ # define ARP_HLEN 6 u8 ar_pln; /* Length of protocol address */ # define ARP_PLEN 4 u16 ar_op; /* Operation */ # define ARPOP_REQUEST 1 /* Request to resolve address */ # define ARPOP_REPLY 2 /* Response to previous request */
# define RARPOP_REQUEST 3 /* Request to resolve address */ # define RARPOP_REPLY 4 /* Response to previous request */
/* * The remaining fields are variable in size, according to * the sizes above, and are defined as appropriate for * specific hardware/protocol combinations. */ u8 ar_data[0]; #define ar_sha ar_data[0] #define ar_spa ar_data[ARP_HLEN] #define ar_tha ar_data[ARP_HLEN + ARP_PLEN] #define ar_tpa ar_data[ARP_HLEN + ARP_PLEN + ARP_HLEN] #if 0 u8 ar_sha[]; /* Sender hardware address */ u8 ar_spa[]; /* Sender protocol address */ u8 ar_tha[]; /* Target hardware address */ u8 ar_tpa[]; /* Target protocol address */ #endif /* 0 */ };
#define ARP_HDR_SIZE (8+20) /* Size assuming ethernet */
/* * ICMP stuff (just enough to handle (host) redirect messages) */ #define ICMP_ECHO_REPLY 0 /* Echo reply */ #define ICMP_NOT_REACH 3 /* Detination unreachable */ #define ICMP_REDIRECT 5 /* Redirect (change route) */ #define ICMP_ECHO_REQUEST 8 /* Echo request */
/* Codes for REDIRECT. */ #define ICMP_REDIR_NET 0 /* Redirect Net */ #define ICMP_REDIR_HOST 1 /* Redirect Host */
/* Codes for NOT_REACH */ #define ICMP_NOT_REACH_PORT 3 /* Port unreachable */
struct icmp_hdr { u8 type; u8 code; u16 checksum; union { struct { u16 id; u16 sequence; } echo; u32 gateway; struct { u16 unused; u16 mtu; } frag; u8 data[0]; } un; };
#define ICMP_HDR_SIZE (sizeof(struct icmp_hdr)) #define IP_ICMP_HDR_SIZE (IP_HDR_SIZE + ICMP_HDR_SIZE)
/* * Maximum packet size; used to allocate packet storage. Use * the maxium Ethernet frame size as specified by the Ethernet * standard including the 802.1Q tag (VLAN tagging). * maximum packet size = 1522 * maximum packet size and multiple of 32 bytes = 1536 */ #define PKTSIZE 1522 #define PKTSIZE_ALIGN 1536
/* * Maximum receive ring size; that is, the number of packets * we can buffer before overflow happens. Basically, this just * needs to be enough to prevent a packet being discarded while * we are processing the previous one. */ #define RINGSZ 4 #define RINGSZ_LOG2 2
/**********************************************************************/ /* * Globals. * * Note: * * All variables of type struct in_addr are stored in NETWORK byte order * (big endian). */
/* net.c */ /** BOOTP EXTENTIONS **/ extern struct in_addr net_gateway; /* Our gateway IP address */ extern struct in_addr net_netmask; /* Our subnet mask (0 = unknown) */ /* Our Domain Name Server (0 = unknown) */ extern struct in_addr net_dns_server; #if defined(CONFIG_BOOTP_DNS2) /* Our 2nd Domain Name Server (0 = unknown) */ extern struct in_addr net_dns_server2; #endif extern char net_nis_domain[32]; /* Our IS domain */ extern char net_hostname[32]; /* Our hostname */ extern char net_root_path[64]; /* Our root path */ /** END OF BOOTP EXTENTIONS **/ extern u8 net_ethaddr[6]; /* Our ethernet address */ extern u8 net_server_ethaddr[6]; /* Boot server enet address */ extern struct in_addr net_ip; /* Our IP addr (0 = unknown) */ extern struct in_addr net_server_ip; /* Server IP addr (0 = unknown) */ extern uchar *net_tx_packet; /* THE transmit packet */ extern uchar *net_rx_packets[PKTBUFSRX]; /* Receive packets */ extern uchar *net_rx_packet; /* Current receive packet */ extern int net_rx_packet_len; /* Current rx packet length */ extern const u8 net_bcast_ethaddr[6]; /* Ethernet broadcast address */ extern const u8 net_null_ethaddr[6];
#define VLAN_NONE 4095 /* untagged */ #define VLAN_IDMASK 0x0fff /* mask of valid vlan id */ extern ushort net_our_vlan; /* Our VLAN */ extern ushort net_native_vlan; /* Our Native VLAN */
extern int net_restart_wrap; /* Tried all network devices */
enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, WGET };
extern char net_boot_file_name[1024];/* Boot File name */ /* The actual transferred size of the bootfile (in bytes) */ extern u32 net_boot_file_size; /* Boot file size in blocks as reported by the DHCP server */ extern u32 net_boot_file_expected_size_in_blocks;
#if defined(CONFIG_CMD_DNS) extern char *net_dns_resolve; /* The host to resolve */ extern char *net_dns_env_var; /* the env var to put the ip into */ #endif
#if defined(CONFIG_CMD_PING) extern struct in_addr net_ping_ip; /* the ip address to ping */ #endif
#if defined(CONFIG_CMD_CDP) /* when CDP completes these hold the return values */ extern ushort cdp_native_vlan; /* CDP returned native VLAN */ extern ushort cdp_appliance_vlan; /* CDP returned appliance VLAN */
/* * Check for a CDP packet by examining the received MAC address field */ static inline int is_cdp_packet(const uchar *ethaddr) { extern const u8 net_cdp_ethaddr[6];
return memcmp(ethaddr, net_cdp_ethaddr, 6) == 0; } #endif
#if defined(CONFIG_CMD_SNTP) extern struct in_addr net_ntp_server; /* the ip address to NTP */ extern int net_ntp_time_offset; /* offset time from UTC */ #endif
#if defined(CONFIG_MCAST_TFTP) extern struct in_addr net_mcast_addr; #endif
/* Initialize the network adapter */ void net_init(void); int net_loop(enum proto_t);
/* Load failed. Start again. */ int net_start_again(void);
/* Get size of the ethernet header when we send */ int net_eth_hdr_size(void);
/* Set ethernet header; returns the size of the header */ int net_set_ether(uchar *xet, const uchar *dest_ethaddr, uint prot); int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot);
/* Set IP header */ void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source, u16 pkt_len, u8 prot ); int net_set_tcp_header(uchar *pkt, int len, u8 action, u32 tcp_seq_num, u32 tcp_ack_seq_num ); void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport, int sport, int len); /* Set ports */ void net_set_ports( int server_port, int our_port ); /* Sequence number and SAK management */ void net_tcp_hole_contract( u32 tcp_seq_num, u32 tcp_end_num ); void net_tcp_hole_expand( u32 tcp_seq_num, u32 tcp_end_num );
/* Print packet or messsage */ void net_print_buffer( uchar raw[], int pkt_len, int payload_len, int hdr_len, bool hide ); /* Find string in buffer */ int net_find_in_buffer( uchar raw[], int payload_len, uchar field[], int field_len ); /** * compute_ip_checksum() - Compute IP checksum * * @addr: Address to check (must be 16-bit aligned) * @nbytes: Number of bytes to check (normally a multiple of 2) * @return 16-bit IP checksum */ unsigned compute_ip_checksum(const void *addr, unsigned nbytes);
/** * add_ip_checksums() - add two IP checksums * * @offset: Offset of first sum (if odd we do a byte-swap) * @sum: First checksum * @new_sum: New checksum to add * @return updated 16-bit IP checksum */ unsigned add_ip_checksums(unsigned offset, unsigned sum, unsigned new_sum);
/** * ip_checksum_ok() - check if a checksum is correct * * This works by making sure the checksum sums to 0 * * @addr: Address to check (must be 16-bit aligned) * @nbytes: Number of bytes to check (normally a multiple of 2) * @return true if the checksum matches, false if not */ int ip_checksum_ok(const void *addr, unsigned nbytes);
/* Callbacks */ rxhand_f *net_get_tcp_handler(void); /* Get TCP RX packet handler */ void net_set_tcp_handler(rxhand_f *); /* Set TCP RX packet handler */ rxhand_f *net_get_udp_handler(void); /* Get UDP RX packet handler */ void net_set_udp_handler(rxhand_f *); /* Set UDP RX packet handler */ rxhand_f *net_get_arp_handler(void); /* Get ARP RX packet handler */ void net_set_arp_handler(rxhand_f *); /* Set ARP RX packet handler */ void net_set_icmp_handler(rxhand_icmp_f *f); /* Set ICMP RX handlr*/ void net_set_timeout_handler(ulong, thand_f *); /* Set timeout handlr*/
/* Network loop state */ enum net_loop_state { NETLOOP_CONTINUE, NETLOOP_RESTART, NETLOOP_SUCCESS, NETLOOP_FAIL }; extern enum net_loop_state net_state;
static inline void net_set_state(enum net_loop_state state) { debug_cond(DEBUG_INT_STATE, "--- NetState set to %d\n", state); net_state = state; }
/* Transmit a packet */ static inline void net_send_packet(uchar *pkt, int len) { /* Currently no way to return errors from eth_send() */ (void) eth_send(pkt, len); }
/* * Transmit "net_tx_packet" as UDP packet, performing ARP request if needed * (ether will be populated) * * @param ether Raw packet buffer * @param dest IP address to send the datagram to * @param dport Destination UDP port * @param sport Source UDP port * @param payload_len Length of data after the UDP header * Added TCP support and protocol parameter for sending ip packets, and * shim to support existing udp interface. */
int net_send_ip_packet( int payload_len, int proto, u8 action, u32 tcp_seq_num, u32 tcp_ack_num );
int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport, int payload_len);
/* Processes a received packet */ void net_process_received_packet(uchar *in_packet, int len);
#ifdef CONFIG_NETCONSOLE void nc_start(void); int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port, unsigned src_port, unsigned len); #endif
static __always_inline int eth_is_on_demand_init(void) { #ifdef CONFIG_NETCONSOLE extern enum proto_t net_loop_last_protocol;
return net_loop_last_protocol != NETCONS; #else return 1; #endif }
static inline void eth_set_last_protocol(int protocol) { #ifdef CONFIG_NETCONSOLE extern enum proto_t net_loop_last_protocol;
net_loop_last_protocol = protocol; #endif }
/* * Check if autoload is enabled. If so, use either NFS or TFTP to download * the boot file. */ void net_auto_load(void);
/* * The following functions are a bit ugly, but necessary to deal with * alignment restrictions on ARM. * * We're using inline functions, which had the smallest memory * footprint in our tests. */ /* return IP *in network byteorder* */ static inline struct in_addr net_read_ip(void *from) { struct in_addr ip;
memcpy((void *)&ip, (void *)from, sizeof(ip)); return ip; }
/* return ulong *in network byteorder* */ static inline u32 net_read_u32(u32 *from) { u32 l;
memcpy((void *)&l, (void *)from, sizeof(l)); return l; }
/* write IP *in network byteorder* */ static inline void net_write_ip(void *to, struct in_addr ip) { memcpy(to, (void *)&ip, sizeof(ip)); }
/* copy IP */ static inline void net_copy_ip(void *to, void *from) { memcpy((void *)to, from, sizeof(struct in_addr)); }
/* copy ulong */ static inline void net_copy_u32(u32 *to, u32 *from) { memcpy((void *)to, (void *)from, sizeof(u32)); }
/** * is_zero_ethaddr - Determine if give Ethernet address is all zeros. * @addr: Pointer to a six-byte array containing the Ethernet address * * Return true if the address is all zeroes. */ static inline int is_zero_ethaddr(const u8 *addr) { return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); }
/** * is_multicast_ethaddr - Determine if the Ethernet address is a multicast. * @addr: Pointer to a six-byte array containing the Ethernet address * * Return true if the address is a multicast address. * By definition the broadcast address is also a multicast address. */ static inline int is_multicast_ethaddr(const u8 *addr) { return 0x01 & addr[0]; }
/* * is_broadcast_ethaddr - Determine if the Ethernet address is broadcast * @addr: Pointer to a six-byte array containing the Ethernet address * * Return true if the address is the broadcast address. */ static inline int is_broadcast_ethaddr(const u8 *addr) { return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff; }
/* * is_valid_ethaddr - Determine if the given Ethernet address is valid * @addr: Pointer to a six-byte array containing the Ethernet address * * Check that the Ethernet address (MAC) is not 00:00:00:00:00:00, is not * a multicast address, and is not FF:FF:FF:FF:FF:FF. * * Return true if the address is valid. */ static inline int is_valid_ethaddr(const u8 *addr) { /* FF:FF:FF:FF:FF:FF is a multicast address so we don't need to * explicitly check for it here. */ return !is_multicast_ethaddr(addr) && !is_zero_ethaddr(addr); }
/** * net_random_ethaddr - Generate software assigned random Ethernet address * @addr: Pointer to a six-byte array containing the Ethernet address * * Generate a random Ethernet address (MAC) that is not multicast * and has the local assigned bit set. */ static inline void net_random_ethaddr(uchar *addr) { int i; unsigned int seed = get_timer(0);
for (i = 0; i < 6; i++) addr[i] = rand_r(&seed);
addr[0] &= 0xfe; /* clear multicast bit */ addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ }
/* Convert an IP address to a string */ void ip_to_string(struct in_addr x, char *s);
/* Convert a string to ip address */ struct in_addr string_to_ip(const char *s);
/* Convert a VLAN id to a string */ void vlan_to_string(ushort x, char *s);
/* Convert a string to a vlan id */ ushort string_to_vlan(const char *s);
/* read a VLAN id from an environment variable */ ushort getenv_vlan(char *);
/* copy a filename (allow for "..." notation, limit length) */ void copy_filename(char *dst, const char *src, int size);
/* get a random source port */ unsigned int random_port(void);
/** * update_tftp - Update firmware over TFTP (via DFU) * * This function updates board's firmware via TFTP * * @param addr - memory address where data is stored * @param interface - the DFU medium name - e.g. "mmc" * @param devstring - the DFU medium number - e.g. "1" * * @return - 0 on success, other value on failure */ int update_tftp(ulong addr, char *interface, char *devstring);
/**********************************************************************/
#endif /* __NET_H__ */
net/wget.c
/* * FILE support driver - based on etherboot and U-BOOT's tftp.c * * Duncan Hare dh@synoia.com 2017 * */
#include <common.h> #include <command.h> #include <fat.h> //FAT #include <net.h> #include <mapmem.h>
#ifdef TCP
#define FILE_TEST 0 /* Set to 1 for debug messges */
#define FILE_RETRY_COUNT 30 #ifndef CONFIG_FILE_TIMEOUT # define FILE_TIMEOUT 2000UL #define SERVER_PORT 8081 #else # define FILE_TIMEOUT CONFIG_FILE_TIMEOUT #endif
char bootfile[50] = "GET /r.32.test/boot/cmdline.txt HTTP/1.1\r\n\r\n"; char bootfile1[10] = "GET "; char bootfile2[30] = "/r.32.test/boot/cmdline.txt"; char bootfile3[20] = " HTTP/1.1\r\n\r\n";
uchar content[6] = "<html>"; uchar error404[3] = "404";
static unsigned int file_timeout = FILE_TIMEOUT;
static struct in_addr file_server_ip; static int file_timeout_count; int packets = 0;
static u32 initial_data_seq_num;
enum FILE_STATE { FILE_CLOSED, FILE_CONNECTING, FILE_CONNECTED, FILE_TRANSFERRING, FILE_TRANSFERRED};
static enum FILE_STATE file_state;
static char *file_filename; static char *file_path; static char file_path_buff[2048]; int i = 0;
static void file_timeout_handler(void); void file_fail( char * error_message, unsigned tcp_seq_num, unsigned tcp_ack_num, u8 action );
static inline int store_block(uchar *src, unsigned offset, unsigned len) { ulong newsize = offset + len; #ifdef CONFIG_SYS_DIRECT_FLASH_WGET int i, rc = 0;
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { /* start address in flash? */ if (load_addr + offset >= flash_info[i].start[0]) { rc = 1; break; } }
if (rc) { /* Flash is destination for this packet */ rc = flash_write((uchar *)src, (ulong)(load_addr+offset), len); if (rc) { flash_perror(rc); return -1; } } else #endif /* CONFIG_SYS_DIRECT_FLASH_NFS */ { uchar *ptr = map_sysmem(load_addr + offset, len); memcpy(ptr, src, len); if ( i < 3 ) { printf( "Offset %d, Length %d\n", offset, len ); net_print_buffer( src, 24, 24, 24, 0 ); i++; net_print_buffer( src + len -24, (int)src + len -24, 24, 24, 0); } unmap_sysmem(ptr); }
if (net_boot_file_size < (offset + len)) net_boot_file_size = newsize; return 0; }
/* * File request dispatcher * WARNING, This, and only this, is the place where * SEQUENCE NUMBERS are swapped betweeen incoming (RX) and outgoing (TX) * What is in procedure file_handler() is correct for RX traffic. */ static void file_send( u8 action, unsigned tcp_ack_num, unsigned tcp_seq_num, int len ) { uchar *ptr; uchar *offset;
tcp_ack_num = tcp_ack_num + len;
switch (file_state) { case FILE_CLOSED: debug_cond( FILE_TEST, "File send: send SYN\n"); file_state = FILE_CONNECTING; net_send_ip_packet( 0, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num ); break; case FILE_CONNECTING: debug_cond( FILE_TEST, "File send: send HTTP GET\n");
ptr = net_tx_packet + net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; offset = ptr;
memcpy( offset, &bootfile1, strlen(bootfile1) ); offset = offset + strlen(bootfile1);
// memcpy( offset, &bootfile2, strlen(bootfile2) ); // offset = offset + strlen(bootfile2);
memcpy( offset, file_path, strlen(file_path) ); offset = offset + strlen(file_path);
memcpy( offset, &bootfile3, strlen(bootfile3) ); offset = offset + strlen(bootfile3); net_send_ip_packet(( offset - ptr), IPPROTO_TCP, TCP_DATA, tcp_seq_num, tcp_ack_num ); file_state = FILE_CONNECTED; break; case FILE_CONNECTED: debug_cond( FILE_TEST, "File send: Header\n" ); file_state = FILE_TRANSFERRING; break; case FILE_TRANSFERRING: debug_cond( FILE_TEST, "File send:Transferring \n"); net_send_ip_packet( 0, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num); break; case FILE_TRANSFERRED:
debug_cond( FILE_TEST, "File send:Transferred \n"); net_send_ip_packet( 0, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num); break; } }
void file_success( u8 action, unsigned tcp_seq_num, unsigned tcp_ack_num, int len, int packets ) { file_state = FILE_TRANSFERRED; net_set_state( NETLOOP_SUCCESS ); net_set_timeout_handler( 0, NULL); debug_cond( 1, "Packets received %d, File send:SUCCESS!!!\n", packets ); file_send( action, tcp_seq_num, tcp_ack_num, len ); }
void file_fail( char * error_message, unsigned tcp_seq_num, unsigned tcp_ack_num, u8 action ) { debug_cond( FILE_TEST, error_message ); debug_cond( FILE_TEST, "File Fail\n" ); file_state = FILE_TRANSFERRED; net_set_timeout_handler( 0, NULL); net_set_state( NETLOOP_FAIL ); file_send( action | TCP_FIN, tcp_seq_num, tcp_ack_num, 0 ); }
/* * Interfaces of U-BOOT */ static void file_timeout_handler(void) { if (++file_timeout_count > FILE_RETRY_COUNT) { puts("\nRetry count exceeded; starting again\n"); net_start_again(); } else { puts("T "); net_set_timeout_handler(file_timeout + FILE_TIMEOUT * file_timeout_count, file_timeout_handler); file_send( TCP_DATA, 0, 0, 0 ); } }
/* * In the "application push" invocation the TCP header with all its information is $ * packet pointer, and the other variable "repurposed" (or misused) to carry sequen$ * and TCP state. * * Parms: ((uchar *) b, tcp_ack_num, action_and_state, tcp_seq_num, payload_len ); * */
static void file_handler(uchar *pkt, unsigned tcp_seq_num, struct in_addr action_and_state, unsigned tcp_ack_num, unsigned len ) { enum TCP_STATE file_tcp_state = net_get_tcp_state(); u8 action = action_and_state.s_addr; int offset;
net_set_timeout_handler(file_timeout, file_timeout_handler); packets ++;
switch (file_state) { case FILE_CLOSED: debug_cond( FILE_TEST, "File Handler: Error!, State wrong\n"); break; case FILE_CONNECTING: debug_cond(FILE_TEST, "File Connecting In len=%d, Seq=%d, Ack=%d\n", len, tcp_seq_num, tcp_ack_num ); if ( len == 0 ) { if ( file_tcp_state == TCP_ESTABLISHED ) { debug_cond( FILE_TEST, "File Handler Connecting, send, len=%d\n", len ); file_send( action, tcp_seq_num, tcp_ack_num, len ); } else file_fail( "File Handler Connected Fail\n", tcp_seq_num, tcp_ack_num, action ); } break; case FILE_CONNECTED: debug_cond(FILE_TEST, "File Connected In len=%d, seq=%d, ack=%d\n" , len, tcp_seq_num, tcp_ack_num ); if ( len == 0 ) { file_fail("File not found, no data returned/n" , tcp_seq_num, tcp_ack_num, action ); }
else if ( net_find_in_buffer( pkt, len, error404, 3) > 0 ) { offset = net_find_in_buffer( pkt, len, content, 6 ); net_print_buffer( pkt, offset-4, offset-4, -1, 0 ); pkt[0] = 0x00; file_fail( (char *)pkt, tcp_seq_num, tcp_ack_num, action ); } else /* Got HTTP Header */ { net_print_buffer( pkt, len, len, -1, 0 ); file_state = FILE_TRANSFERRING; initial_data_seq_num = tcp_seq_num + len; file_send( action, tcp_seq_num, tcp_ack_num, len ); } break; case FILE_TRANSFERRING: debug_cond(FILE_TEST, "File Transferring, seq=%d, ack=%d,len=%d\n" , tcp_seq_num, tcp_ack_num, len );
if ( len > 0 ) { offset = tcp_seq_num - initial_data_seq_num; if ( store_block( pkt, offset, len) != 0 ) file_fail( "File store error\n", tcp_seq_num, tcp_ack_num, action ); else switch (file_tcp_state) { case TCP_SYN_SENT: case TCP_CLOSING: case TCP_FIN_WAIT_1: case TCP_FIN_WAIT_2: case TCP_CLOSED: net_set_state(NETLOOP_FAIL); break; case TCP_ESTABLISHED: file_send( TCP_ACK, tcp_seq_num, tcp_ack_num, len ); break; case TCP_CLOSE_WAIT: /* End of file */ file_success( action | TCP_ACK, tcp_seq_num, tcp_ack_num, len, packets ); break; } } else file_send( action , tcp_seq_num, tcp_ack_num, len ); break; case FILE_TRANSFERRED: file_send( TCP_ACK, tcp_seq_num, tcp_ack_num, len ); break; } }
void wget_start(void) { debug("%s\n", __func__);
file_server_ip = net_server_ip; file_path = (char *)file_path_buff;
if (file_path == NULL) { net_set_state(NETLOOP_FAIL); debug("*** ERROR: Fail allocate memory\n"); return; }
if (net_boot_file_name[0] == '\0') { sprintf(file_path, "/fileroot/%02X%02X%02X%02X.img", net_ip.s_addr & 0xFF, (net_ip.s_addr >> 8) & 0xFF, (net_ip.s_addr >> 16) & 0xFF, (net_ip.s_addr >> 24) & 0xFF);
debug("*** Warning: no boot file name; using '%s'\n", file_path); } else { char *p = net_boot_file_name;
p = strchr(p, ':');
if (p != NULL) { file_server_ip = string_to_ip(net_boot_file_name); ++p; strcpy(file_path, p); } else { strcpy(file_path, net_boot_file_name); } }
debug_cond( FILE_TEST, "Using %s device\n", eth_get_name());
debug_cond( FILE_TEST, "File transfer via HTTP from server %pI4; our IP address is %pI4\n", &file_server_ip, &net_ip);
/* Check if we need to send across this subnet */ if (net_gateway.s_addr && net_netmask.s_addr) { struct in_addr our_net; struct in_addr server_net;
our_net.s_addr = net_ip.s_addr & net_netmask.s_addr; server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr; if (our_net.s_addr != server_net.s_addr) debug("; sending through gateway %pI4", &net_gateway); } debug_cond(FILE_TEST, "Filename '%s, %s'.\n", file_path, file_filename);
if (net_boot_file_expected_size_in_blocks) { debug(" Size is 0x%x Bytes = ", net_boot_file_expected_size_in_blocks << 9); print_size(net_boot_file_expected_size_in_blocks << 9, ""); } debug("\nLoad address: 0x%lx\nLoading: *\b", load_addr);
net_set_timeout_handler(file_timeout, file_timeout_handler); net_set_tcp_handler(file_handler);
file_timeout_count = 0; file_state = FILE_CLOSED;
net_set_ports( SERVER_PORT, 4096 + (get_ticks() % 3072));
/* zero out server ether in case the server ip has changed */ memset(net_server_ethaddr, 0, 6);
file_send( TCP_SYN, 0, 0, 0 ); } #endif wget.h /* * Duncan Hare Copyright 2017 */
void wget_start(void); /* Begin NFS */ net/ping.c changes struct icmp_hdr *icmp = (struct icmp_hdr *)(pkt + IP_HDR_SIZE);
net_set_ip_header(pkt, dest, net_ip, IP_ICMP_HDR_SIZE, IPPROTO_ICMP);
icmp->type = ICMP_ECHO_REQUEST; icmp->code = 0; icmp->checksum = 0; icmp->un.echo.id = 0; icmp->un.echo.sequence = htons(ping_seq_number++); icmp->checksum = compute_ip_checksum(icmp, ICMP_HDR_SIZE); n Hare
714 931 7952

Hi Duncan,
On 21 September 2017 at 14:32, Duncan Hare dh@synoia.com wrote:
Mods to: cmd/net.cnet/Makefilenet/net.cinclude/net.hnet/wget.cnet/wget.hnet/ping.c I do not know how to do patches, I'm a noobat this:
There is some documentation here.
https://www.denx.de/wiki/U-Boot/Patches
If you read all of that you will probably be on the right track.
Regards, Simon
participants (2)
-
Duncan Hare
-
Simon Glass