1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Internet Protocol			File: net_ip.c
5    *
6    *  This module implements the IP layer (RFC791)
7    *
8    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47
48#include "lib_types.h"
49#include "lib_string.h"
50#include "lib_queue.h"
51#include "lib_malloc.h"
52#include "lib_printf.h"
53
54#include "net_ebuf.h"
55#include "net_ether.h"
56
57#include "net_ip.h"
58#include "net_ip_internal.h"
59
60#include "cfe_error.h"
61
62/*  *********************************************************************
63    *  Forward declarations
64    ********************************************************************* */
65
66static int ip_rx_callback(ebuf_t *buf,void *ref);
67
68/*  *********************************************************************
69    *  _ip_alloc(ipi)
70    *
71    *  Allocate an ebuf and reserve space for the IP header in it.
72    *
73    *  Input parameters:
74    *  	   ipi - IP stack information
75    *
76    *  Return value:
77    *  	   ebuf - an ebuf, or NULL if there are none left
78    ********************************************************************* */
79
80ebuf_t *_ip_alloc(ip_info_t *ipi)
81{
82    ebuf_t *buf;
83
84    buf = eth_alloc(ipi->eth_info,ipi->ip_port);
85
86    if (buf == NULL) return buf;
87
88    ebuf_seek(buf,IPHDR_LENGTH);
89    ebuf_setlength(buf,0);
90
91    return buf;
92}
93
94
95/*  *********************************************************************
96    *  ip_chksum(initcksum,ptr,len)
97    *
98    *  Do an IP checksum for the specified buffer.  You can pass
99    *  an initial checksum if you're continuing a previous checksum
100    *  calculation, such as for UDP headers and pseudoheaders.
101    *
102    *  Input parameters:
103    *  	   initcksum - initial checksum (usually zero)
104    *  	   ptr - pointer to buffer to checksum
105    *  	   len - length of data in bytes
106    *
107    *  Return value:
108    *  	   checksum (16 bits)
109    ********************************************************************* */
110
111uint16_t ip_chksum(uint16_t initcksum,uint8_t *ptr,int len)
112{
113    unsigned int cksum;
114    int idx;
115    int odd;
116
117    cksum = (unsigned int) initcksum;
118
119    odd = len & 1;
120    len -= odd;
121
122    for (idx = 0; idx < len; idx += 2) {
123	cksum += ((unsigned long) ptr[idx] << 8) + ((unsigned long) ptr[idx+1]);
124	}
125
126    if (odd) {		/* buffer is odd length */
127	cksum += ((unsigned long) ptr[idx] << 8);
128	}
129
130    /*
131     * Fold in the carries
132     */
133
134    while (cksum >> 16) {
135	cksum = (cksum & 0xFFFF) + (cksum >> 16);
136	}
137
138    return cksum;
139}
140
141
142/*  *********************************************************************
143    *  _ip_send(ipi,buf,destaddr,proto)
144    *
145    *  Send an IP datagram.  We only support non-fragmented datagrams
146    *  at this time.
147    *
148    *  Input parameters:
149    *  	   ipi - IP stack information
150    *  	   buf - an ebuf
151    *  	   destaddr - destination IP address
152    *  	   proto - IP protocol number
153    *
154    *  Return value:
155    *  	   0 if ok
156    *  	   else error code
157    ********************************************************************* */
158
159int _ip_send(ip_info_t *ipi,ebuf_t *buf,uint8_t *destaddr,uint8_t proto)
160{
161    uint16_t cksum;
162    uint8_t masksrc[IP_ADDR_LEN];
163    uint8_t maskdest[IP_ADDR_LEN];
164    int pktlen;
165    uint8_t *ptr;
166
167    /* Move to the beginning of the IP hdeader */
168
169    ebuf_seek(buf,-IPHDR_LENGTH);
170
171    pktlen = ebuf_length(buf) + IPHDR_LENGTH;
172
173    ipi->ip_id++;
174
175    /* Insert the IP header */
176
177    ebuf_put_u8(buf,IPHDR_VER_4 | IPHDR_LEN_20);
178    ebuf_put_u8(buf,IPHDR_TOS_DEFAULT);
179    ebuf_put_u16_be(buf,pktlen);
180    ebuf_put_u16_be(buf,ipi->ip_id);
181    ebuf_put_u16_be(buf,0);
182    ebuf_put_u8(buf,IPHDR_TTL_DEFAULT);
183    ebuf_put_u8(buf,proto);
184    ebuf_put_u16_be(buf,0);		/* checksum */
185    ebuf_put_bytes(buf,ipi->net_info.ip_addr,IP_ADDR_LEN);
186    ebuf_put_bytes(buf,destaddr,IP_ADDR_LEN);
187
188    /* adjust pointer and add in the header length */
189
190    ebuf_prepend(buf,IPHDR_LENGTH);
191
192    /* Checksum the header */
193
194    ptr = ebuf_ptr(buf);
195    cksum = ip_chksum(0,ptr,IPHDR_LENGTH);
196    cksum = ~cksum;
197    ptr[10] = (cksum >> 8) & 0xFF;
198    ptr[11] = (cksum >> 0) & 0xFF;
199
200    /*
201     * If sending to the IP broadcast address,
202     * send to local broadcast.
203     */
204
205    if (ip_addrisbcast(destaddr)) {
206	eth_send(buf,(uint8_t *) eth_broadcast);
207	eth_free(buf);
208	return 0;
209	}
210
211    /*
212     * If the mask has not been set, don't try to
213     * determine if we should use the gateway or not.
214     */
215
216    if (ip_addriszero(ipi->net_info.ip_netmask)) {
217	return _arp_lookup_and_send(ipi,buf,destaddr);
218	}
219
220    /*
221     * Compute (dest-addr & netmask)  and   (my-addr & netmask)
222     */
223
224    ip_mask(masksrc,destaddr,ipi->net_info.ip_netmask);
225    ip_mask(maskdest,ipi->net_info.ip_addr,ipi->net_info.ip_netmask);
226
227    /*
228     * if destination and my address are on the same subnet,
229     * send the packet directly.  Otherwise, send via
230     * the gateway.
231     */
232
233    if (ip_compareaddr(masksrc,maskdest) == 0) {
234	return _arp_lookup_and_send(ipi,buf,destaddr);
235	}
236    else {
237	/* if no gw configured, drop packet */
238	if (ip_addriszero(ipi->net_info.ip_gateway)) {
239	    eth_free(buf);	/* silently drop */
240	    return 0;
241	    }
242	else {
243	    return _arp_lookup_and_send(ipi,buf,ipi->net_info.ip_gateway);
244	    }
245	}
246
247}
248
249
250/*  *********************************************************************
251    *  ip_rx_callback(buf,ref)
252    *
253    *  Receive callback for IP packets.  This routine is called
254    *  by the Ethernet datalink.  We look up a suitable protocol
255    *  handler and pass the packet off.
256    *
257    *  Input parameters:
258    *  	   buf - ebuf we received
259    *  	   ref - reference data from the ethernet datalink
260    *
261    *  Return value:
262    *  	   ETH_KEEP to keep the packet
263    *  	   ETH_DROP to drop the packet
264    ********************************************************************* */
265
266static int ip_rx_callback(ebuf_t *buf,void *ref)
267{
268    ip_info_t *ipi = ref;
269    uint8_t tmp;
270    int hdrlen;
271    uint8_t *hdr;
272    uint16_t origchksum;
273    uint16_t calcchksum;
274    uint16_t length;
275    uint16_t tmp16;
276    uint8_t proto;
277    uint8_t srcip[IP_ADDR_LEN];
278    uint8_t dstip[IP_ADDR_LEN];
279    ip_protodisp_t *pdisp;
280    int res;
281    int idx;
282
283    hdr = ebuf_ptr(buf);		/* save current posn */
284
285    ebuf_get_u8(buf,tmp);		/* version and header length */
286
287    /*
288     * Check IP version
289     */
290
291    if ((tmp & 0xF0) != IPHDR_VER_4) {
292	goto drop;			/* not IPV4 */
293	}
294    hdrlen = (tmp & 0x0F) * 4;
295
296    /*
297     * Check header size
298     */
299
300    if (hdrlen < IPHDR_LENGTH) {
301	goto drop;			/* header < 20 bytes */
302	}
303
304    /*
305     * Check the checksum
306     */
307    origchksum = ((uint16_t) hdr[10] << 8) | (uint16_t) hdr[11];
308    hdr[10] = hdr[11] = 0;
309    calcchksum = ~ip_chksum(0,hdr,hdrlen);
310    if (calcchksum != origchksum) {
311	goto drop;
312	}
313
314    /*
315     * Okay, now go back and check other fields.
316     */
317
318    ebuf_skip(buf,1);			/* skip TOS field */
319
320    ebuf_get_u16_be(buf,length);
321    ebuf_skip(buf,2);			/* skip ID field */
322
323    ebuf_get_u16_be(buf,tmp16);		/* get Fragment Offset field */
324
325    /*
326     * If the fragment offset field is nonzero, or the
327     * "more fragments" bit is set, then this is a packet
328     * fragment.  Our trivial IP implementation does not
329     * deal with fragments, so drop the packets.
330     */
331    if ((tmp16 & (IPHDR_FRAGOFFSET | IPHDR_MOREFRAGMENTS)) != 0) {
332	goto drop;			/* packet is fragmented */
333	}
334
335    ebuf_skip(buf,1);			/* skip TTL */
336    ebuf_get_u8(buf,proto);		/* get protocol */
337    ebuf_skip(buf,2);			/* skip checksum */
338
339    ebuf_get_bytes(buf,srcip,IP_ADDR_LEN);
340    ebuf_get_bytes(buf,dstip,IP_ADDR_LEN);
341
342    ebuf_skip(buf,hdrlen-IPHDR_LENGTH); /* skip rest of header */
343
344    ebuf_setlength(buf,length-hdrlen);	/* set length to just data portion */
345
346    /*
347     * If our address is not set, let anybody in.  We need this to
348     * properly pass up DHCP replies that get forwarde through routers.
349     * Otherwise, only let in matching addresses or broadcasts.
350     */
351
352    if (!ip_addriszero(ipi->net_info.ip_addr)) {
353	if ((ip_compareaddr(dstip,ipi->net_info.ip_addr) != 0) &&
354	    !(ip_addrisbcast(dstip))) {
355	    goto drop;			/* not for us */
356	    }
357	}
358
359    /*
360     * ebuf's pointer now starts at beginning of protocol data
361     */
362
363    /*
364     * Find matching protocol dispatch
365     */
366
367    pdisp = ipi->ip_protocols;
368    res = ETH_DROP;
369    for (idx = 0; idx < IP_MAX_PROTOCOLS; idx++) {
370	if (pdisp->cb && (pdisp->protocol == proto)) {
371	    res = (*(pdisp->cb))(pdisp->ref,buf,dstip,srcip);
372	    break;
373	    }
374	pdisp++;
375	}
376
377
378    return res;
379
380drop:
381    return ETH_DROP;
382}
383
384
385/*  *********************************************************************
386    *  _ip_init(eth)
387    *
388    *  Initialize the IP layer, attaching it to an underlying Ethernet
389    *  datalink interface.
390    *
391    *  Input parameters:
392    *  	   eth - Ethernet datalink information
393    *
394    *  Return value:
395    *  	   ip_info pointer (IP stack information) or NULL if error
396    ********************************************************************* */
397
398ip_info_t *_ip_init(ether_info_t *eth)
399{
400    ip_info_t *ipi;
401    uint8_t ipproto[2];
402
403    /*
404     * Allocate IP stack info
405     */
406
407    ipi = KMALLOC(sizeof(ip_info_t),0);
408    if (ipi == NULL) return NULL;
409
410    memset(ipi,0,sizeof(ip_info_t));
411
412    ipi->eth_info = eth;
413
414    /*
415     * Initialize ARP
416     */
417
418    if (_arp_init(ipi) < 0) {
419	KFREE(ipi);
420	return NULL;
421	}
422
423    /*
424     * Open the Ethernet portal for IP packets
425     */
426
427    ipproto[0] = (PROTOSPACE_IP >> 8) & 0xFF; ipproto[1] = PROTOSPACE_IP & 0xFF;
428    ipi->ip_port = eth_open(ipi->eth_info,ETH_PTYPE_DIX,(int8_t *)ipproto,ip_rx_callback,ipi);
429
430    if (ipi->ip_port < 0) {
431	_arp_uninit(ipi);
432	KFREE(ipi);
433	return NULL;
434	}
435
436    return ipi;
437}
438
439
440/*  *********************************************************************
441    *  _ip_uninit(ipi)
442    *
443    *  Un-initialize the IP layer, freeing resources
444    *
445    *  Input parameters:
446    *  	   ipi - IP stack information
447    *
448    *  Return value:
449    *  	   nothing
450    ********************************************************************* */
451
452void _ip_uninit(ip_info_t *ipi)
453{
454    /*
455     * Close the IP portal
456     */
457
458    eth_close(ipi->eth_info,ipi->ip_port);
459
460    /*
461     * Turn off the ARP layer.
462     */
463
464    _arp_uninit(ipi);
465
466
467    /*
468     * free strings containing the domain and host names
469     */
470
471    if (ipi->net_info.ip_domain) {
472	KFREE(ipi->net_info.ip_domain);
473	}
474
475    if (ipi->net_info.ip_hostname) {
476	KFREE(ipi->net_info.ip_hostname);
477	}
478
479    /*
480     * Free the stack information
481     */
482
483    KFREE(ipi);
484}
485
486
487/*  *********************************************************************
488    *  _ip_timer_tick(ipi)
489    *
490    *  Called once per second while the IP stack is active.
491    *
492    *  Input parameters:
493    *  	   ipi - ip stack information
494    *
495    *  Return value:
496    *  	   nothing
497    ********************************************************************* */
498
499
500void _ip_timer_tick(ip_info_t *ipi)
501{
502    _arp_timer_tick(ipi);
503}
504
505
506/*  *********************************************************************
507    *  _ip_free(ipi,buf)
508    *
509    *  Free an ebuf allocated via _ip_alloc
510    *
511    *  Input parameters:
512    *  	   ipi - IP stack information
513    *  	   buf - ebuf to free
514    *
515    *  Return value:
516    *  	   nothing
517    ********************************************************************* */
518
519void _ip_free(ip_info_t *ipi,ebuf_t *buf)
520{
521    eth_free(buf);
522}
523
524/*  *********************************************************************
525    *  _ip_getaddr(ipi,buf)
526    *
527    *  Return our IP address (is this used?)
528    *
529    *  Input parameters:
530    *  	   ipi - IP stack information
531    *  	   buf - pointer to 4-byte buffer to receive IP address
532    *
533    *  Return value:
534    *  	   nothing
535    ********************************************************************* */
536
537void _ip_getaddr(ip_info_t *ipi,uint8_t *buf)
538{
539    memcpy(buf,ipi->net_info.ip_addr,IP_ADDR_LEN);
540}
541
542/*  *********************************************************************
543    *  _ip_getparam(ipi,param)
544    *
545    *  Return the value of an IP parameter (address, netmask, etc.).
546    *  The return value may need to be coerced if it's not normally
547    *  a uint8_t* pointer.
548    *
549    *  Input parameters:
550    *  	   ipi - IP stack information
551    *  	   param - parameter number
552    *
553    *  Return value:
554    *  	   parameter value, or NULL if the parameter is invalid or
555    *  	   not set.
556    ********************************************************************* */
557
558uint8_t *_ip_getparam(ip_info_t *ipinfo,int param)
559{
560    uint8_t *ret = NULL;
561
562    switch (param) {
563	case NET_IPADDR:
564	    ret = ipinfo->net_info.ip_addr;
565	    break;
566	case NET_NETMASK:
567	    ret = ipinfo->net_info.ip_netmask;
568	    break;
569	case NET_GATEWAY:
570	    ret = ipinfo->net_info.ip_gateway;
571	    break;
572	case NET_NAMESERVER:
573	    ret = ipinfo->net_info.ip_nameserver;
574	    break;
575	case NET_HWADDR:
576	    ret = ipinfo->arp_hwaddr;
577	    break;
578	case NET_DOMAIN:
579	    ret = (uint8_t *)ipinfo->net_info.ip_domain;
580	    break;
581	case NET_HOSTNAME:
582	    ret = (uint8_t *)ipinfo->net_info.ip_hostname;
583	    break;
584	case NET_SPEED:
585	    return NULL;
586	    break;
587	case NET_LOOPBACK:
588	    return NULL;
589	    break;
590	}
591
592    return ret;
593}
594
595/*  *********************************************************************
596    *  _ip_getparam(ipi,param,value)
597    *
598    *  Set the value of an IP parameter (address, netmask, etc.).
599    *  The value may need to be coerced if it's not normally
600    *  a uint8_t* pointer.
601    *
602    *  Input parameters:
603    *  	   ipi - IP stack information
604    *  	   param - parameter number
605    *	   value - parameter's new value
606    *
607    *  Return value:
608    *  	   0 if ok, else error code
609    ********************************************************************* */
610
611int _ip_setparam(ip_info_t *ipinfo,int param,uint8_t *ptr)
612{
613    int res = -1;
614
615    switch (param) {
616	case NET_IPADDR:
617	    memcpy(ipinfo->net_info.ip_addr,ptr,IP_ADDR_LEN);
618	    _arp_send_gratuitous(ipinfo);
619	    res = 0;
620	    break;
621	case NET_NETMASK:
622	    memcpy(ipinfo->net_info.ip_netmask,ptr,IP_ADDR_LEN);
623	    res = 0;
624	    break;
625	case NET_GATEWAY:
626	    memcpy(ipinfo->net_info.ip_gateway,ptr,IP_ADDR_LEN);
627	    res = 0;
628	    break;
629	case NET_NAMESERVER:
630	    memcpy(ipinfo->net_info.ip_nameserver,ptr,IP_ADDR_LEN);
631	    res = 0;
632	    break;
633	case NET_DOMAIN:
634	    if (ipinfo->net_info.ip_domain) {
635		KFREE(ipinfo->net_info.ip_domain);
636		ipinfo->net_info.ip_domain = NULL;
637		}
638	    if (ptr) ipinfo->net_info.ip_domain = strdup((char *) ptr);
639	    break;
640	case NET_HOSTNAME:
641	    if (ipinfo->net_info.ip_hostname) {
642		KFREE(ipinfo->net_info.ip_hostname);
643		ipinfo->net_info.ip_hostname = NULL;
644		}
645	    if (ptr) ipinfo->net_info.ip_hostname = strdup((char *) ptr);
646	    break;
647	case NET_HWADDR:
648	    memcpy(ipinfo->arp_hwaddr,ptr,ENET_ADDR_LEN);
649	    eth_sethwaddr(ipinfo->eth_info,ptr);
650	    res = 0;
651	    break;
652	case NET_SPEED:
653	    res = eth_setspeed(ipinfo->eth_info,*(int *)ptr);
654	    break;
655	case NET_LOOPBACK:
656	    res = eth_setloopback(ipinfo->eth_info,*(int *)ptr);
657	    break;
658	}
659
660    return res;
661}
662
663/*  *********************************************************************
664    *  _ip_register(ipinfo,proto,cb)
665    *
666    *  Register a protocol handler with the IP layer.  IP client
667    *  protocols such as UDP, ICMP, etc. call this to register their
668    *  callbacks.
669    *
670    *  Input parameters:
671    *  	   ipinfo - IP stack information
672    *  	   proto - IP protocol number
673    *  	   cb - callback routine to register
674    *
675    *  Return value:
676    *  	   nothing
677    ********************************************************************* */
678
679void _ip_register(ip_info_t *ipinfo,
680		  int proto,
681		  int (*cb)(void *ref,ebuf_t *buf,uint8_t *dst,uint8_t *src),void *ref)
682{
683    int idx;
684
685    for (idx = 0; idx < IP_MAX_PROTOCOLS; idx++) {
686	if (ipinfo->ip_protocols[idx].cb == NULL) break;
687	}
688
689    if (idx == IP_MAX_PROTOCOLS) return;
690
691    ipinfo->ip_protocols[idx].protocol = (uint8_t) proto;
692    ipinfo->ip_protocols[idx].cb = cb;
693    ipinfo->ip_protocols[idx].ref = ref;
694}
695
696
697/*  *********************************************************************
698    *  _ip_deregister(ipinfo,proto)
699    *
700    *  Deregister an IP protocol.
701    *
702    *  Input parameters:
703    *  	   ipinfo - IP stack information
704    *  	   proto - protocol number
705    *
706    *  Return value:
707    *  	   nothing
708    ********************************************************************* */
709
710void _ip_deregister(ip_info_t *ipinfo,int proto)
711{
712    int idx;
713
714    for (idx = 0; idx < IP_MAX_PROTOCOLS; idx++) {
715	if (ipinfo->ip_protocols[idx].protocol == (uint8_t) proto) {
716	    ipinfo->ip_protocols[idx].protocol = 0;
717	    ipinfo->ip_protocols[idx].ref = 0;
718	    ipinfo->ip_protocols[idx].cb = NULL;
719	    }
720	}
721}
722