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