diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2010-06-02 15:22:26 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2010-06-17 08:28:16 +0200 |
commit | 97070487fa67d0a8b04c4e109966bb4c1b40212e (patch) | |
tree | 26c7c116514840d9ac1dcf055030e9706a89ef1a /net | |
parent | 994f95c073caf4df62d1271aef4112ce8aaa8add (diff) | |
download | barebox-97070487fa67d0a8b04c4e109966bb4c1b40212e.tar.gz barebox-97070487fa67d0a8b04c4e109966bb4c1b40212e.tar.xz |
net: Implement a new network stack
The old network stack has some bad limitations:
- network commands are required to call NetLoop() which only returns when
the network layer wants to. Instead we now use a net_poll() function which
returns after handling one packet (or immediately if no packet is
available).
- There can be only one packet handler which makes it impossible to handle
multiple connections
- CamelCaseMakesItHardToRead
The new network stack is implemented as a parallel universe. Currently all
commands still use the old stack. They are converted in subsequent patches.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'net')
-rw-r--r-- | net/net.c | 540 |
1 files changed, 536 insertions, 4 deletions
@@ -77,7 +77,9 @@ #include <net.h> #include <driver.h> #include <errno.h> +#include <init.h> #include <linux/ctype.h> +#include <linux/err.h> #include "tftp.h" #include "rarp.h" #include "nfs.h" @@ -89,7 +91,6 @@ # define ARP_TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT) #endif - /** BOOTP EXTENTIONS **/ IPaddr_t NetOurSubnetMask=0; /* Our subnet mask (0=unknown) */ @@ -407,6 +408,9 @@ NetReceive(uchar * inpkt, int len) pr_debug("packet received\n"); + if (!net_receive(inpkt, len)) + return; + NetRxPkt = inpkt; NetRxPktLen = len; et = (Ethernet_t *)inpkt; @@ -582,7 +586,8 @@ NetReceive(uchar * inpkt, int len) NetCopyIP(&NetServerIP, &arp->ar_data[ 6]); memcpy (NetServerEther, &arp->ar_data[ 0], 6); - (*packetHandler)(0,0,0,0); + if (packetHandler) + (*packetHandler)(0,0,0,0); } break; @@ -649,7 +654,8 @@ NetReceive(uchar * inpkt, int len) * IP header OK. Pass the packet to the current handler. */ /* XXX point to ip packet */ - (*packetHandler)((uchar *)ip, 0, 0, 0); + if (packetHandler) + (*packetHandler)((uchar *)ip, 0, 0, 0); return; #endif default: @@ -701,7 +707,8 @@ NetReceive(uchar * inpkt, int len) /* * IP header OK. Pass the packet to the current handler. */ - (*packetHandler)((uchar *)ip +IP_HDR_SIZE, + if (packetHandler) + (*packetHandler)((uchar *)ip +IP_HDR_SIZE, ntohs(ip->udp_dst), ntohs(ip->udp_src), ntohs(ip->udp_len) - 8); @@ -882,6 +889,30 @@ int string_to_ip(const char *s, IPaddr_t *ip) return 0; } +IPaddr_t getenv_ip(const char *name) +{ + IPaddr_t ip; + const char *var = getenv(name); + + if (!var) + return 0; + + string_to_ip(var, &ip); + + return ip; +} + +int setenv_ip(const char *name, IPaddr_t ip) +{ + char str[sizeof("xxx.xxx.xxx.xxx")]; + + ip_to_string(ip, str); + + setenv(name, str); + + return 0; +} + void VLAN_to_string(ushort x, char *s) { x = ntohs(x); @@ -951,10 +982,28 @@ void ethaddr_to_string(const unsigned char *enetaddr, char *str) enetaddr[4], enetaddr[5]); } +static IPaddr_t net_netmask; /* Our subnet mask (0=unknown) */ +static IPaddr_t net_gateway; /* Our gateways IP address */ + +static unsigned char net_ether[6]; /* Our ethernet address */ +static IPaddr_t net_ip; /* Our IP addr (0 = unknown) */ +static IPaddr_t net_serverip; /* Our IP addr (0 = unknown) */ + +unsigned char *NetRxPackets[PKTBUFSRX]; /* Receive packets */ +static unsigned int net_ip_id; + void net_update_env(void) { struct eth_device *edev = eth_get_current(); + net_ip = dev_get_param_ip(&edev->dev, "ipaddr"); + net_serverip = dev_get_param_ip(&edev->dev, "serverip"); + net_gateway = dev_get_param_ip(&edev->dev, "gateway"); + net_netmask = dev_get_param_ip(&edev->dev, "netmask"); + + string_to_ethaddr(dev_get_param(&edev->dev, "ethaddr"), + net_ether); + NetOurIP = dev_get_param_ip(&edev->dev, "ipaddr"); NetServerIP = dev_get_param_ip(&edev->dev, "serverip"); NetOurGatewayIP = dev_get_param_ip(&edev->dev, "gateway"); @@ -964,3 +1013,486 @@ void net_update_env(void) NetOurEther); } +int net_checksum_ok(unsigned char *ptr, int len) +{ + return net_checksum(ptr, len) + 1; +} + +uint16_t net_checksum(unsigned char *ptr, int len) +{ + uint32_t xsum = 0; + uint16_t *p = (uint16_t *)ptr; + + if (len & 1) + ptr[len] = 0; + + len = (len + 1) >> 1; + + while (len-- > 0) + xsum += *p++; + + xsum = (xsum & 0xffff) + (xsum >> 16); + xsum = (xsum & 0xffff) + (xsum >> 16); + return xsum & 0xffff; +} + +static unsigned char *arp_ether; +static IPaddr_t arp_wait_ip; + +static void arp_handler(struct arprequest *arp) +{ + IPaddr_t tmp; + + /* are we waiting for a reply */ + if (!arp_wait_ip) + return; + + tmp = net_read_ip(&arp->ar_data[6]); + + /* matched waiting packet's address */ + if (tmp == arp_wait_ip) { + /* save address for later use */ + memcpy(arp_ether, &arp->ar_data[0], 6); + + /* no arp request pending now */ + arp_wait_ip = 0; + } +} + +int arp_request(IPaddr_t dest, unsigned char *ether) +{ + char *pkt; + struct arprequest *arp; + uint64_t arp_start; + static char *arp_packet; + struct ethernet *et; + + if (!arp_packet) { + arp_packet = net_alloc_packet(); + if (!arp_packet) + return -ENOMEM; + } + + pkt = arp_packet; + et = (struct ethernet *)arp_packet; + + arp_wait_ip = dest; + + pr_debug("ARP broadcast\n"); + + memset(et->et_dest, 0xff, 6); + memcpy(et->et_src, net_ether, 6); + et->et_protlen = htons(PROT_ARP); + + arp = (struct arprequest *)(pkt + ETHER_HDR_SIZE); + + arp->ar_hrd = htons(ARP_ETHER); + arp->ar_pro = htons(PROT_IP); + arp->ar_hln = 6; + arp->ar_pln = 4; + arp->ar_op = htons(ARPOP_REQUEST); + + memcpy(arp->ar_data, net_ether, 6); /* source ET addr */ + net_write_ip(arp->ar_data + 6, net_ip); /* source IP addr */ + memset(arp->ar_data + 10, 0, 6); /* dest ET addr = 0 */ + + if ((dest & net_netmask) != (net_ip & net_netmask)) { + if (!net_gateway) + arp_wait_ip = dest; + else + arp_wait_ip = net_gateway; + } else { + arp_wait_ip = dest; + } + + net_write_ip(arp->ar_data + 16, arp_wait_ip); + + arp_ether = ether; + + eth_send(arp_packet, ETHER_HDR_SIZE + ARP_HDR_SIZE); + arp_start = get_time_ns(); + + while (arp_wait_ip) { + if (ctrlc()) + return -EINTR; + + if (is_timeout(arp_start, 3 * SECOND)) { + printf("T "); + arp_start = get_time_ns(); + eth_send(arp_packet, ETHER_HDR_SIZE + ARP_HDR_SIZE); + } + + net_poll(); + } + + pr_debug("Got ARP REPLY, set server/gtwy eth addr (%02x:%02x:%02x:%02x:%02x:%02x)\n", + ether[0], ether[1], + ether[2], ether[3], + ether[4], ether[5]); + return 0; +} + +void net_poll(void) +{ + eth_rx(); +} + +static uint16_t net_udp_new_localport(void) +{ + static uint16_t localport; + + localport++; + + if (localport < 1024) + localport = 1024; + + return localport; +} + +IPaddr_t net_get_serverip(void) +{ + return net_serverip; +} + +void net_set_serverip(IPaddr_t ip) +{ + struct eth_device *edev = eth_get_current(); + + net_serverip = ip; + dev_set_param_ip(&edev->dev, "serverip", net_serverip); +} + +void net_set_ip(IPaddr_t ip) +{ + struct eth_device *edev = eth_get_current(); + + net_ip = ip; + dev_set_param_ip(&edev->dev, "ipaddr", net_ip); +} + +IPaddr_t net_get_ip(void) +{ + return net_ip; +} + +void net_set_netmask(IPaddr_t nm) +{ + struct eth_device *edev = eth_get_current(); + + net_netmask = nm; + dev_set_param_ip(&edev->dev, "netmask", net_netmask); +} + +void net_set_gateway(IPaddr_t gw) +{ + struct eth_device *edev = eth_get_current(); + + net_gateway = gw; + dev_set_param_ip(&edev->dev, "gateway", net_gateway); +} + +static LIST_HEAD(connection_list); + +static struct net_connection *net_new(IPaddr_t dest, rx_handler_f *handler) +{ + struct net_connection *con; + int ret; + + if (!is_valid_ether_addr(net_ether)) + return ERR_PTR(-ENETDOWN); + + /* If we don't have an ip only broadcast is allowed */ + if (!net_ip && dest != 0xffffffff) + return ERR_PTR(-ENETDOWN); + + con = xzalloc(sizeof(*con)); + con->packet = memalign(32, PKTSIZE); + memset(con->packet, 0, PKTSIZE); + + con->et = (struct ethernet *)con->packet; + con->ip = (struct iphdr *)(con->packet + ETHER_HDR_SIZE); + con->udp = (struct udphdr *)(con->packet + ETHER_HDR_SIZE + sizeof(struct iphdr)); + con->icmp = (struct icmphdr *)(con->packet + ETHER_HDR_SIZE + sizeof(struct iphdr)); + con->handler = handler; + + if (dest == 0xffffffff) { + memset(con->et->et_dest, 0xff, 6); + } else { + ret = arp_request(dest, con->et->et_dest); + if (ret) + goto out; + } + + con->et->et_protlen = htons(PROT_IP); + memcpy(con->et->et_src, net_ether, 6); + + con->ip->hl_v = 0x45; + con->ip->tos = 0; + con->ip->frag_off = htons(0x4000); /* No fragmentation */; + con->ip->ttl = 255; + net_copy_ip(&con->ip->daddr, &dest); + net_copy_ip(&con->ip->saddr, &net_ip); + + list_add_tail(&con->list, &connection_list); + + return con; +out: + free(con->packet); + free(con); + return ERR_PTR(ret); +} + +struct net_connection *net_udp_new(IPaddr_t dest, uint16_t dport, + rx_handler_f *handler) +{ + struct net_connection *con = net_new(dest, handler); + + if (IS_ERR(con)) + return con; + + con->proto = IPPROTO_UDP; + con->udp->uh_dport = htons(dport); + con->udp->uh_sport = htons(net_udp_new_localport()); + con->ip->protocol = IPPROTO_UDP; + + return con; +} + +struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler) +{ + struct net_connection *con = net_new(dest, handler); + + if (IS_ERR(con)) + return con; + + con->proto = IPPROTO_ICMP; + con->ip->protocol = IPPROTO_ICMP; + + return con; +} + +void net_unregister(struct net_connection *con) +{ + list_del(&con->list); + free(con->packet); + free(con); +} + +int net_ip_send(struct net_connection *con, int len) +{ + con->ip->tot_len = htons(sizeof(struct iphdr) + len); + con->ip->id = htons(net_ip_id++);; + con->ip->check = 0; + con->ip->check = ~net_checksum((unsigned char *)con->ip, sizeof(struct iphdr)); + + eth_send(con->packet, ETHER_HDR_SIZE + sizeof(struct iphdr) + len); + + return 0; +} + +int net_udp_send(struct net_connection *con, int len) +{ + con->udp->uh_ulen = htons(len + 8); + con->udp->uh_sum = 0; + + return net_ip_send(con, sizeof(struct udphdr) + len); +} + +int net_icmp_send(struct net_connection *con, int len) +{ + con->icmp->checksum = ~net_checksum((unsigned char *)con->icmp, + sizeof(struct icmphdr) + len); + + return net_ip_send(con, sizeof(struct icmphdr) + len); +} + +static int net_answer_arp(unsigned char *pkt, int len) +{ + struct arprequest *arp = (struct arprequest *)(pkt + ETHER_HDR_SIZE); + struct ethernet *et = (struct ethernet *)pkt; + unsigned char *packet; + + debug("%s\n", __func__); + + memcpy (et->et_dest, et->et_src, 6); + memcpy (et->et_src, net_ether, 6); + + et->et_protlen = htons(PROT_ARP); + arp->ar_op = htons(ARPOP_REPLY); + memcpy(&arp->ar_data[10], &arp->ar_data[0], 6); + net_copy_ip(&arp->ar_data[16], &arp->ar_data[6]); + memcpy(&arp->ar_data[0], net_ether, 6); + net_copy_ip(&arp->ar_data[6], &net_ip); + + packet = net_alloc_packet(); + if (!packet) + return 0; + memcpy(packet, pkt, ETHER_HDR_SIZE + ARP_HDR_SIZE); + eth_send(packet, ETHER_HDR_SIZE + ARP_HDR_SIZE); + free(packet); + + return 0; +} + +static void net_bad_packet(unsigned char *pkt, int len) +{ +#ifdef DEBUG + /* + * We received a bad packet. for now just dump it. + * We could add more sophisticated debugging here + */ + memory_display(pkt, 0, len, 1); +#endif +} + +static int net_handle_arp(unsigned char *pkt, int len) +{ + struct arprequest *arp; + + debug("%s: got arp\n", __func__); + + /* + * We have to deal with two types of ARP packets: + * - REQUEST packets will be answered by sending our + * IP address - if we know it. + * - REPLY packets are expected only after we asked + * for the TFTP server's or the gateway's ethernet + * address; so if we receive such a packet, we set + * the server ethernet address + */ + arp = (struct arprequest *)(pkt + ETHER_HDR_SIZE); + if (len < ARP_HDR_SIZE) + goto bad; + if (ntohs(arp->ar_hrd) != ARP_ETHER) + goto bad; + if (ntohs(arp->ar_pro) != PROT_IP) + goto bad; + if (arp->ar_hln != 6) + goto bad; + if (arp->ar_pln != 4) + goto bad; + if (net_ip == 0) + return 0; + if (net_read_ip(&arp->ar_data[16]) != net_ip) + return 0; + + switch (ntohs(arp->ar_op)) { + case ARPOP_REQUEST: + return net_answer_arp(pkt, len); + case ARPOP_REPLY: + arp_handler(arp); + return 1; + default: + pr_debug("Unexpected ARP opcode 0x%x\n", ntohs(arp->ar_op)); + return -EINVAL; + } + + return 0; + +bad: + net_bad_packet(pkt, len); + return -EINVAL; +} + +static int net_handle_udp(unsigned char *pkt, int len) +{ + struct iphdr *ip = (struct iphdr *)(pkt + ETHER_HDR_SIZE); + struct net_connection *con; + struct udphdr *udp; + int port; + + udp = (struct udphdr *)(ip + 1); + port = ntohs(udp->uh_dport); + list_for_each_entry(con, &connection_list, list) { + if (con->proto == IPPROTO_UDP && port == ntohs(con->udp->uh_sport)) { + con->handler(pkt, len); + return 0; + } + } + return -EINVAL; +} + +static int net_handle_icmp(unsigned char *pkt, int len) +{ + struct net_connection *con; + + debug("%s\n", __func__); + + list_for_each_entry(con, &connection_list, list) { + if (con->proto == IPPROTO_ICMP) { + con->handler(pkt, len); + return 0; + } + } + return 0; +} + +static int net_handle_ip(unsigned char *pkt, int len) +{ + struct iphdr *ip = (struct iphdr *)(pkt + ETHER_HDR_SIZE); + IPaddr_t tmp; + + debug("%s\n", __func__); + + if (len < sizeof(struct ethernet) + sizeof(struct iphdr) || + len < ETHER_HDR_SIZE + ntohs(ip->tot_len)) { + debug("%s: bad len\n", __func__); + goto bad; + } + + if ((ip->hl_v & 0xf0) != 0x40) + goto bad; + + if (ip->frag_off & htons(0x1fff)) /* Can't deal w/ fragments */ + goto bad; + if (!net_checksum_ok((unsigned char *)ip, sizeof(struct iphdr))) + goto bad; + + tmp = net_read_ip(&ip->daddr); + if (net_ip && tmp != net_ip && tmp != 0xffffffff) + return 0; + + switch (ip->protocol) { + case IPPROTO_ICMP: + return net_handle_icmp(pkt, len); + case IPPROTO_UDP: + return net_handle_udp(pkt, len); + } + + return 0; +bad: + net_bad_packet(pkt, len); + return 0; +} + +int net_receive(unsigned char *pkt, int len) +{ + struct ethernet *et = (struct ethernet *)pkt; + int et_protlen = ntohs(et->et_protlen); + + if (len < ETHER_HDR_SIZE) + return 0; + + switch (et_protlen) { + case PROT_ARP: + return net_handle_arp(pkt, len); + case PROT_IP: + return net_handle_ip(pkt, len); + default: + debug("%s: got unknown protocol type: %d\n", __func__, et_protlen); + return 1; + } +} + +static int net_init(void) +{ + int i; + + for (i = 0; i < PKTBUFSRX; i++) + NetRxPackets[i] = memalign(32, PKTSIZE); + + return 0; +} + +postcore_initcall(net_init); + |