ip.c

Go to the documentation of this file.
00001 /*##############################################################################
00002 
00003 nIP - nano IP stack
00004 
00005 File        : ip.c
00006 
00007 Description : Functions do IP routing
00008 
00009 Copyright notice:
00010 
00011 Copyright (C) 2005 -
00012 Andreas Dittrich, dittrich@informatik.hu-berlin.de
00013 Jon Kowal, kowal@informatik.hu-berlin.de
00014 
00015 This program is free software; you can redistribute it and/or
00016 modify it under the terms of the GNU General Public License
00017 as published by the Free Software Foundation; either version 2
00018 of the License, or (at your option) any later version.
00019 
00020 This program is distributed in the hope that it will be useful,
00021 but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00023 GNU General Public License for more details.
00024 
00025 You should have received a copy of the GNU General Public License
00026 along with this program; if not, write to the Free Software
00027 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 
00029 #############################################################################*/
00030 
00031 #include "dispatcher.h"
00032 #include "inet.h"
00033 #include "os_core.h"
00034 #include "net/net_if.h"
00035 #include "net/arp.h"
00036 #include "net/ip.h"
00037 #include "net/udp.h"
00038 #include "app/dhcp.h"
00039 #include "net/autoip.h"
00040 #include "net/tcp.h"
00041 #include "app/mdns.h"
00042 
00043 uint16_t nip_ipv4_id;
00044 uint8_t  nip_ip_localhost[4] = {127,0,0,1};
00045 uint8_t  nip_ip_broadcast[4] = {255,255,255,255};
00046 uint8_t  nip_ip_null[4]      = {0,0,0,0};
00047 
00048 #if (NIP_IP_ENABLE == 1)
00049 
00050 #if (NIP_MULTICAST_ENABLE == 1)
00051         uint8_t nip_ip_all_hosts[4] = {124,0,0,1};
00052         #if ( NIP_IP_MAX_HOST_GROUPS == 1 )
00053         struct nip_ip_group_membership nip_ip_group;
00054         #else
00055         struct nip_ip_group_membership nip_ip_groups[ NIP_IP_MAX_HOST_GROUPS ];
00056         #endif
00057 #endif
00058 
00059 
00060 #if (NIP_ICMP_ENABLE == 1) && defined ( NIP_ICMP_TEST_NO_MEM_MGR )
00061 struct ICMP_HEADER nip_test_no_mem_icmp;
00062 #endif
00063 
00064 // forward declarations
00065 void nip_ip_disp_deliver( void );
00066 
00067 /** Calculates internet header checksum in network byte order.
00068  * @param buf  pointer to buffer to perform checksum calculation on. MUST NOT be NULL.
00069  * @param size number of bytes to utilize for checksum calculation. Size MUST be
00070  * an even number greater than 0.
00071  */
00072 uint16_t nip_internet_checksum( uint16_t *buf, uint16_t size )
00073 {
00074 /*      uint32_t sum32 = 0;
00075         uint16_t sum16 = 0;
00076         uint16_t test = 0;
00077         size /= 2;
00078         do
00079         {
00080                 test = ntohs( *buf );
00081                 sum32 += test;
00082                 sum16 += *buf;
00083                 if ( sum16 < *buf) sum16++;
00084                 if ( --size == 0  ) break;
00085                 buf++;
00086         } while ( 1 );
00087         sum32  = ( sum32 & 0x0000FFFF) + ( sum32>>16 );
00088         sum32  = ( sum32 & 0x0000FFFF) + ( sum32>>16 );
00089         sum32  = ~sum32;
00090         sum32  = htons( sum32 );
00091         sum16 = ~sum16;
00092         return sum16;*/
00093 
00094 /*      uint16_t sum = 0;
00095         uint16_t test = 0;
00096         size /= 2;
00097         do
00098         {
00099                 test = ntohs( *buf );
00100                 sum += test;
00101                 if ( sum <= test ) sum++;
00102                 if ( --size == 0  ) break;
00103                 buf++;
00104         } while ( 1 );
00105         sum  = ~sum;
00106         sum  = htons( sum );
00107         return sum;*/
00108 
00109 
00110         uint16_t sum = 0;
00111         size /= 2;
00112         do
00113         {
00114                 sum += *buf;
00115                 if ( sum < *buf ) sum++;
00116                 buf++;
00117         } while ( --size > 0 );
00118         return ~sum;
00119 }
00120 
00121 /** Set subnet mask.
00122  *
00123  * Broadcast address will be determined and set automatically from the subnet
00124  * mask.
00125  */
00126 void nip_ip_subnet_set( nip_net_if_id_t if_id, uint8_t *mask )
00127 {
00128         nip_net_if_t *net_if = &nip_net_if_list[ if_id ];
00129         uint8_t      i;
00130 
00131         // set subnet mask
00132         nip_memcpy( net_if->ip_conf.subnet_mask, mask, 4 );
00133 
00134         // set broadcast address
00135         i = 0;
00136         do
00137         {
00138                 net_if->ip_conf.brdcst_addr[i] = net_if->ip_conf.addr[i] | ~net_if->ip_conf.subnet_mask[i];
00139         } while ( ++i < 4 );
00140 }
00141 
00142 /** Perform tasks, that have to be done, when IP interface goes down, such as
00143  * resetting TCP connections, sending mDNS notifications,..
00144  */
00145 void nip_ip_down( nip_net_if_id_t if_id )
00146 {
00147         nip_net_if_t *net_if = &nip_net_if_list[ if_id ];
00148 
00149         ///@todo reset TCP connections
00150 
00151         ///@todo send mDNS bye. To do this, this has to be embedded into the
00152         ///dispatcher statemachine somehow.
00153 #if NIP_MDNS_ENABLE == 1
00154         nip_mdns_interface_down();
00155 #endif
00156 
00157         net_if->ip_conf.state = NIP_IP_CONF_STAT_UNCONFIGURED;
00158 }
00159 
00160 /** Perform tasks that have to be done, when IP interface goes up, such as
00161  * announcing group registrations, performing mDNS checks and lookups,..
00162  */
00163 void nip_ip_up( nip_net_if_id_t if_id )
00164 {
00165 
00166         // if IGMP is enabled: Report group memberships
00167 #if NIP_IGMP_ENABLE == 1
00168         nip_ip_query_groups( if_id );
00169 #endif
00170 
00171         // if MDNS is enabled, Report Host name
00172 #if NIP_MDNS_ENABLE == 1
00173         nip_mdns_interface_up( if_id );
00174 #endif
00175 
00176 }
00177 
00178 /** Set interface IP address.
00179  *
00180  * Subnet mask and broadcast address will be set according to the addresses
00181  * class. To choose a different subnet mask call nip_ip_subnet_set
00182  */
00183 void  nip_ip_addr_set( nip_net_if_id_t if_id, uint8_t *addr )
00184 {
00185         nip_net_if_t *net_if = &nip_net_if_list[ if_id ];
00186 
00187         // Things need only be updated, if ip address changed
00188         if ( nip_memcmp( addr, net_if->ip_conf.addr, 4 ) != 0 )
00189         {
00190                 nip_ip_down( if_id );
00191 
00192                 nip_memcpy( net_if->ip_conf.addr, addr, 4 );
00193 
00194                 // determine subnet mask
00195                 nip_memset( net_if->ip_conf.subnet_mask, 0, 4 );
00196                 net_if->ip_conf.subnet_mask[0] = 255;
00197                 if ( (addr[0] & 0x80) == 0x80 )
00198                         net_if->ip_conf.subnet_mask[1] = 255;
00199                 if ( (addr[0] & 0xC0) == 0xC0 )
00200                         net_if->ip_conf.subnet_mask[2] = 255;
00201 
00202                 // we still have to call nip_ip_subnet_set() to determine the broadcast addr
00203                 nip_ip_subnet_set( if_id, net_if->ip_conf.subnet_mask );
00204 
00205                 // set ip-conf status
00206                 net_if->ip_conf.state = NIP_IP_CONF_STAT_CONFIGURED;
00207 
00208 
00209                 if ( net_if->phy_conf.flags & NIP_PHY_IFF_UP )
00210                 {
00211                         nip_ip_up( if_id );
00212                 }
00213         }
00214 }
00215 
00216 /** Try to aquire an IP address using DHCP or AutoIP
00217  */
00218 void nip_ip_addr_autoconf( nip_net_if_id_t if_id )
00219 {
00220 
00221         nip_ip_down( if_id );
00222 
00223 #if NIP_DHCP_ENABLE   == 1
00224         nip_dhcp_start( if_id );
00225 #endif
00226 #if NIP_AUTOIP_ENABLE == 1
00227         nip_autoip_start( if_id );
00228 #endif
00229 
00230 }
00231 
00232 /** Resolve route for packet and return transaction to transmit through. If the
00233  * trans-parameter is not NULL, that transaction will be used, if routing was
00234  * successfull.
00235  *
00236  * If not given, the network interface for the transmission will be determined
00237  * and a transaction reserved for it. The transaction will be configured with
00238  * the local link target, which is either the target host or a local gateway,
00239  * depending on the routing table.
00240  *
00241  * @note The network interface of the transmission will _NOT_ be set for only
00242  * local targetted transmissions, so don't rely on it!
00243  *
00244  * @param ip_addr pointer to ip address of target
00245  * @param if_id   optional network interface id if you want to restrict routing
00246  *                on an interface.
00247  * @param trans   optional pointer to pre selected transaction
00248  *
00249  * @return pointer to transaction or NULL on error. On error the nip_error
00250  * variable will be set and be either
00251  * - NIP_E_AGAIN            out of mem for transmission, try again later
00252  * - NIP_E_NO_ROUTE_TO_HOST no interface will deliver to given ip_addr
00253  *
00254  * @todo function has not been tested for multiple interface environment.
00255  */
00256 nip_net_if_trans_t *nip_ip_route( uint8_t *target_addr, nip_net_if_id_t if_id,
00257         nip_net_if_trans_t *trans )
00258 {
00259         nip_net_if_id_t    test_if = 0;
00260         nip_net_if_id_t    transmit_if = NIP_NET_NO_IF; /* interface to transmit on */
00261         nip_net_if_id_t    gateway_if = NIP_NET_NO_IF;  /* interface with std gateway*/
00262         nip_net_trans_status_t oldstat;
00263         nip_net_trans_id_t test_trans;
00264         uint8_t            cnt,i,b,l;
00265         uint8_t            *local_addr = NULL;
00266         uint8_t            *gateway;
00267         nip_if_ip_conf_t   *ip;
00268 #if NIP_MULTICAST_ENABLE == 1 && NIP_IP_MAX_HOST_GROUPS > 1
00269         nip_host_group_id_t group;
00270 #endif
00271 #if NIP_AUTOIP_ENABLE == 1
00272         uint8_t            autoip_target = 0;
00273 #endif
00274 
00275         // Three steps for this function to complete
00276         // 1. Make sure we have a transmission
00277         // 2. Determine interface for transmission or local delivery
00278         // 3. Route determined, finish transmission configuration
00279 
00280 
00281         //************************************************************************
00282         // ** S T E P   1:  make sure we have transmission to work with
00283         if ( trans == NULL )
00284         {
00285                 // find idle transaction for interface
00286                 test_trans = 0;
00287 #if ( NIP_NET_IF_TRANS_COUNT > 1 )
00288                 do
00289                 {
00290 #endif
00291                         trans = &nip_net_trans_list[ test_trans ];
00292 
00293 #if ( NIP_NET_IF_TRANS_COUNT > 1 )
00294                         if ( trans->status == NIP_NET_IF_IDLE )
00295                         {
00296                                 // use this transmission
00297                                 break;
00298                         }
00299                 } while ( ++test_trans < NIP_NET_MAX_TRANSMISSIONS );
00300 #endif
00301 
00302                 if ( test_trans >= NIP_NET_MAX_TRANSMISSIONS )
00303                 {
00304                         // no transmission found -> try again later
00305                         nip_error = NIP_E_AGAIN;
00306                         return NULL;
00307                 }
00308 
00309                 // Reset transmission's content
00310                 nip_memset( trans, 0, sizeof( nip_net_if_trans_t ) );
00311         }
00312 
00313         // set transmission state
00314         oldstat = trans->status;
00315         trans->status = NIP_NET_IF_ROUTING;
00316 
00317         //************************************************************************
00318         // ** S T E P   2:  Determine interface for transmission or local delivery
00319 
00320         // loopback-check ( see RFC 3330 for information)
00321         if ( target_addr[0] == 127 )
00322         {
00323                 trans->flags.deliverlocal = 1;
00324                 nip_memcpy( trans->params.ip.src_nw_addr, nip_ip_localhost, 4 );
00325         }
00326         else
00327         {
00328         // routing table is implicit as each interface has a subnet mask and possibly
00329         // a standard gateway.
00330 #if ( NIP_NET_IF_COUNT > 1 )
00331         do
00332         {
00333 #endif // ( NIP_NET_IF_COUNT > 1 )
00334 
00335                 // multicast or broadcast, use first/selected interface
00336                 /// @todo find way to handle multicast and broadcast on multiple interfaces
00337                 ip = & nip_net_if_list[ test_if ].ip_conf;
00338 
00339                 cnt = 0; i = 0; b = 0; l = 0;
00340                 /* Perform subnet mask check on all four bytes of target address. Use
00341                 // i as counter for successfully checked bytes. Also check for possible
00342                 // local network broadcast.*/
00343                 do
00344                 {
00345                         // check subnet
00346                         if ( ( target_addr[cnt] & ip->subnet_mask[cnt]) == (ip->addr[cnt] & ip->subnet_mask[cnt]) )
00347                         {
00348                                 i++;
00349                         }
00350 
00351                         /* Check for broadcasts. That may be either 255.255.255.255 as global case
00352                         // or x.y.z.255 as special case. Both have in common that subnet-mask and
00353                         // ip address put together will always set all bits to 1.
00354                         // eg:
00355                         //   host-ip:      10.20.144.1
00356                         //   subnet mask:  255.255.248.0
00357                         //   broadcast-ip: 10.20.151.255
00358                         //   --> 255.255.248.0 | 10.20.151.255 == 255.255.255.255*/
00359                         if ( target_addr[cnt] == ip->brdcst_addr[cnt] ) b++;
00360 
00361                         // check for locally matching Interface address
00362                         if ( target_addr[cnt] == ip->addr[cnt] ) l++;
00363                 } while ( ++cnt < 4 );
00364 
00365                 // local loopback of broadcasts
00366                 if ( nip_memcmp( target_addr, nip_ip_broadcast, 4 ) == 0 || b == 4 )
00367                 {
00368                         l = 4;
00369                 }
00370 
00371                 // check multicast addresses
00372                 if ( target_addr[0] >= 0xE0 )
00373                 {
00374                         b = 4;
00375 #if NIP_MULTICAST_ENABLE == 1
00376         #if NIP_IP_MAX_HOST_GROUPS == 1
00377                         #define GROUP_ATTRi(attr) nip_ip_group.attr
00378         #else
00379                         group = 0;
00380                         #define GROUP_ATTRi(attr) nip_ip_groups[group].attr
00381                         do {
00382         #endif
00383                         if ((GROUP_ATTRi(state) != NIP_IP_GROUP_NON
00384                         ///\todo include interface check for groups?
00385                          && nip_memcmp( GROUP_ATTRi(addr), target_addr, 4 ) == 0)
00386                          || nip_memcmp( nip_ip_all_hosts, target_addr, 4 ) == 0)
00387                         {
00388                                 l = 4;
00389                         }
00390         #if NIP_IP_MAX_HOST_GROUPS != 1
00391                         } while ( ++group < NIP_IP_MAX_HOST_GROUPS );
00392         #endif
00393 #endif
00394                 }
00395 
00396                 /* If the target address was identified as broadcast for any interface
00397                 // or matched it's local address we have to locally deliver the packet.
00398                 // if that happens we have to make sure we have the source address set
00399                 // to that of the interface, just in case of loopback. */
00400                 if ( l == 4 )
00401                 {
00402                         trans->flags.deliverlocal = 1;
00403                         nip_memcpy( trans->params.ip.src_nw_addr, ip->addr, 4 );
00404                 }
00405 
00406                 // Check for remote route. If interface was given only check for
00407                 // that interface.
00408                 if ( (if_id == NIP_NET_NO_IF || test_if == if_id) &&  transmit_if == NIP_NET_NO_IF )
00409                 {
00410                         ///@todo perfom link-local check
00411 #if NIP_AUTOIP_ENABLE == 1
00412                         if ( target_addr[0] == 169 && target_addr[1] == 254 )
00413                                 autoip_target = 1;
00414 #endif
00415                         /* if all 4 address bytes matched the interface's subnet, the packet
00416                         // should be transmitted over this interface. If multiple interfaces match
00417                         // that criteria, select only the first one.*/
00418                         if ( b == 4
00419 #if NIP_AUTOIP_ENABLE == 1
00420                          || (autoip_target == 1 && target_addr[2] == 255 && target_addr[3] == 255)
00421 #endif
00422                                 )
00423                         {
00424                                 local_addr  = target_addr;
00425                                 transmit_if = test_if;
00426                                 trans->flags.ll_broadcast = 1;
00427                         }
00428                         else
00429                         if ( i == 4
00430 #if NIP_AUTOIP_ENABLE == 1
00431                         // if target OR source is local-link address: send locally
00432                          || autoip_target == 1
00433                          || (ip->addr[0] == 169 && ip->addr[1] == 254)
00434 #endif
00435                         // if interface has no configured IP address all packets will be sent to
00436                         // local network.
00437                          || ( if_id != NIP_NET_NO_IF && nip_memcmp( ip->addr, nip_ip_null, 4 ) == 0 ))
00438                         {
00439                                 local_addr  = target_addr;
00440                                 transmit_if = test_if;
00441                         }
00442                         else
00443                         // subnet-check failed? Check if there is a gateway.
00444                         // Only use the first standard gateway
00445                         if ( ip->std_gateway[0] != 0 && local_addr == NULL)
00446                         {
00447                                 gateway    = ip->std_gateway;
00448                                 gateway_if = test_if;
00449                         }
00450                 }
00451 
00452 #if ( NIP_NET_IF_COUNT > 1 )
00453         } while ( ++test_if < NIP_NET_IF_COUNT );
00454 #endif
00455 
00456         }
00457 
00458         // found no local target? Check gateway.
00459         if ( transmit_if == NIP_NET_NO_IF && gateway_if != NIP_NET_NO_IF )
00460         {
00461                 local_addr  = gateway;
00462                 transmit_if = gateway_if;
00463         }
00464 
00465         if ( transmit_if != NIP_NET_NO_IF )
00466         {
00467                 trans->flags.forward = 1;
00468                 nip_memcpy( trans->params.ip.ll_nw_addr , local_addr , 4 );
00469                 nip_memcpy( trans->params.ip.src_nw_addr, ip->addr, 4 );
00470         }
00471 
00472         // return if interface check failed
00473         if ( ! (trans->flags.forward || trans->flags.deliverlocal) )
00474         {
00475                 trans->status = oldstat;
00476                 nip_error = NIP_E_NO_ROUTE_TO_HOST;
00477                 return NULL;
00478         }
00479 
00480         //************************************************************************
00481         // ** S T E P   3:  Route determined, finish transmission configuration
00482 
00483         trans->if_id  = transmit_if;
00484         nip_memcpy( trans->params.ip.dst_nw_addr, target_addr, 4 );
00485         trans->status = NIP_NET_IF_ROUTED;
00486 
00487         return trans;
00488 }
00489 
00490 
00491 /** Route and send IP packet */
00492 void nip_ip_disp_send( void )
00493 {
00494         struct IP_HEADER   head;
00495         /// @todo use transmissions interface
00496         nip_net_if_trans_t *trans  = nip_disp.next.common.trans;
00497         nip_net_if_id_t    if_id;
00498         nip_net_if_t       *net_if;
00499         nip_phy_if_t       *phy;
00500         nip_if_ip_conf_t   *ip;
00501         uint8_t            *payload, *header;
00502         uint8_t i;
00503         uint16_t           len;
00504 
00505 
00506         switch( trans->status )
00507         {
00508                 case NIP_NET_IF_ROUTE:
00509                         /// @todo move this to seperate state to safe stack, because ip_send already
00510                         /// eats up quite alot of stack.
00511                         if ( NULL == nip_ip_route( trans->params.ip.dst_nw_addr, NIP_NET_NO_IF, trans ) )
00512                         {
00513                                 // Routing failed. No route to host? We can eliminate the possibility of
00514                                 // NIP_E_DEV_BUSY, because we already provided the transmission.
00515                                 trans->status = NIP_NET_IF_SEND_NRT;
00516                                 goto finish;
00517                         }
00518                 case NIP_NET_IF_RTS:
00519                 case NIP_NET_IF_RESOLV_ADDR:
00520                         // Routing done? -> check if transmission needs to be forwarded on the net
00521                         if ( trans->flags.forward == 0 )      goto deliverlocal;
00522 
00523                         // determine interface parameters for remote transmission
00524                         if_id  = trans->if_id;
00525                         net_if = &nip_net_if_list[ if_id ];
00526                         phy    = &net_if->phy_conf;
00527                         ip     = &net_if->ip_conf;
00528 
00529                         // transmission still routing or routed but not yet approved to be sent
00530                         if ( trans->status == NIP_NET_IF_RTS ) goto rts;
00531 
00532                         break;
00533                 default:
00534                         goto finish;
00535         }
00536 
00537 
00538         /// \todo set resolve timestamp
00539 
00540         /* resolve mac address */
00541         /// \todo check resolve timestamp and return error if necessary
00542         // Resolve mac address. We only know how to resolve Ethernet addresses, so
00543         // lets hope other network adapters don't need a local address.
00544         if ( phy->type == NIP_PHY_IF_ETH )
00545         {
00546                 // multicast
00547                 /*
00548                 if ( (trans->params.ip.dst_nw_addr[0] & 0xF0) == 0xE0 )
00549                 {
00550                         /// \todo convert multicast ip to multicast mac
00551                 }
00552                 // broadcast
00553                 else*/
00554 #if (NIP_ARP_ENABLE==1)
00555                 if ( trans->flags.ll_broadcast )
00556 #endif
00557                 {
00558                         i = 0;
00559                         do
00560                                 nip_disp.next.common.remote_hw_addr[i] = 0xFF;
00561                         while (++i < 6 );
00562                 }
00563 #if (NIP_ARP_ENABLE==1)
00564                 // unicast
00565                 else
00566                 {
00567                         trans->status = NIP_NET_IF_RESOLV_ADDR;
00568                         NIP_CURR_CMD = &nip_arp_disp_resolve;
00569                         return;
00570                 }
00571 #endif
00572         }
00573 
00574 rts:      /* ready to send */
00575 
00576         // build header
00577         /// \todo check fragmentation
00578 
00579         len = trans->header_size + trans->payload_size + NIP_IP_DEFAULT_HEADER_SIZE;
00580 
00581         head.ihl          = 5;
00582         head.version      = 4;
00583         head.tos          = 0;
00584         head.total_length = htons( len );
00585 //      head.id           = trans->params.ip.id;
00586         head.id           = ++nip_ipv4_id;
00587         head.frag_offset  = 0;
00588         head.ttl          = trans->params.ip.ttl>0 ? trans->params.ip.ttl : NIP_IP_TTL;
00589         head.protocol     = trans->params.ip.protocol;
00590         head.checksum     = 0x0000;
00591         nip_memcpy( head.src_addr, trans->params.ip.src_nw_addr, 4 );
00592         nip_memcpy( head.dst_addr, trans->params.ip.dst_nw_addr, 4 );
00593         head.checksum     = nip_internet_checksum( (uint16_t*)&head, NIP_IP_DEFAULT_HEADER_SIZE );
00594 
00595 #if (NIP_ICMP_ENABLE == 1) && defined (NIP_ICMP_TEST_NO_MEM_MGR)
00596         if ( head.protocol == NIP_IP_PROTO_ICMP )
00597                 payload = (uint8_t*)&nip_test_no_mem_icmp;
00598         else
00599 #endif
00600 
00601         if ( trans->payload == NIP_MEM_NULL && trans->header == NIP_MEM_NULL )
00602         {
00603                 // nothing to send
00604                 goto finish;
00605         }
00606 
00607         payload = nip_mem_obtain_ptr( trans->payload );
00608         if ( payload == NULL && trans->payload != NIP_MEM_NULL )
00609                 goto finish;
00610 
00611         header  = nip_mem_obtain_ptr( trans->header  );
00612         if ( header == NULL && trans->header != NIP_MEM_NULL )
00613         {
00614                 nip_mem_release_block( trans->payload );
00615                 goto finish;
00616         }
00617 
00618         if ( payload == NULL && header == NULL )
00619         {
00620                 // payload invalid? -> cancel transmission
00621                 if ( nip_error == NIP_E_INVALID_BLOCK )
00622                 {
00623                         trans->status = NIP_NET_IF_DONE;
00624                         goto finish;
00625                 }
00626                 // payload locked -> try again later.
00627                 trans->status = NIP_NET_IF_RESOLV_ADDR;
00628                 return;
00629         }
00630 
00631         // initiate transfer
00632         phy->hard_send_init(
00633                 if_id,
00634                 NULL,
00635                 htons(0x0800),
00636                 nip_disp.next.common.remote_hw_addr,
00637                 phy->hw_addr,
00638                 len
00639         );
00640 
00641 
00642         /// \todo check success of hardware transmission and react accordingly
00643         phy->hard_send( if_id, (uint8_t*)&head, NIP_IP_DEFAULT_HEADER_SIZE );
00644         phy->hard_send( if_id, header , trans->header_size );
00645         phy->hard_send( if_id, payload + trans->payload_off, trans->payload_size );
00646 
00647 #if (NIP_ICMP_ENABLE == 1) && defined (NIP_ICMP_TEST_NO_MEM_MGR)
00648         if ( head.protocol != NIP_IP_PROTO_ICMP )
00649 #endif
00650         if ( payload != NULL )
00651                 nip_mem_release_block( trans->payload );
00652         if ( header  != NULL )
00653                 nip_mem_release_block( trans->header  );
00654 
00655 
00656 deliverlocal:
00657         // attempt local delivery of packet
00658         NIP_CURR_CMD = &nip_ip_disp_deliver;
00659         return;
00660 
00661 finish:
00662         // clean up after transmission failed or succeeded
00663         nip_reset_trans( trans );
00664 }
00665 
00666 
00667 
00668 #if (NIP_ICMP_ENABLE == 1)
00669 /** Read ICMP packet from network interface. To be called by dispatcher.
00670  */
00671 void nip_icmp_disp_receive( void )
00672 {
00673         struct ICMP_HEADER *icmp;
00674         nip_net_if_trans_t *trans = nip_disp.next.common.trans;
00675 
00676         // map icmp header onto packet data
00677 #if defined (NIP_ICMP_TEST_NO_MEM_MGR)
00678         icmp = &nip_test_no_mem_icmp;
00679 #else
00680         icmp = nip_mem_obtain_ptr( trans->payload );
00681         if ( icmp == NULL )
00682         {
00683                 // obtaining pointer impossible. -> drop packet
00684                 /// \todo we could also introduce some timeout and try receiving later,
00685                 /// but this case seems impossible anyways, as we get here straight from
00686                 /// of ip receive (usually) which just successfully obtained that pointer
00687                 /// before.
00688                 goto discard;
00689                 return;
00690         }
00691 #endif
00692         // check checksum
00693         icmp->checksum = nip_internet_checksum( (uint16_t*) icmp,trans->payload_size);
00694         if ( icmp->checksum != 0x00 )
00695         {
00696                 // checksum check failed -> dump packet
00697                 goto discard;
00698         }
00699 
00700         // check message type
00701         switch ( icmp->type )
00702         {
00703                 case NIP_ICMP_T_ECHO_REQ:
00704                         // turn packet into icmp echo reply
00705                         icmp->type = NIP_ICMP_T_ECHO_REP;
00706                         icmp->checksum = 0x00;
00707                         // calculate and set checksum
00708                         icmp->checksum = nip_internet_checksum( (uint16_t*) icmp, trans->payload_size );
00709                         break;
00710 
00711                 default:
00712                         // unhandled ICMP messages
00713                         goto discard;
00714         }
00715 
00716 #if !defined (NIP_ICMP_TEST_NO_MEM_MGR)
00717         // done reading/editing packet
00718         nip_mem_release_block( trans->payload );
00719 #endif
00720         // configure transmission
00721         trans->status             = NIP_NET_IF_ROUTE;
00722         trans->params.ip.protocol = NIP_IP_PROTO_ICMP;
00723         nip_memcpy( trans->params.ip.dst_nw_addr, trans->params.ip.src_nw_addr, 4 );
00724 
00725         // send reply
00726         NIP_CURR_CMD = &nip_ip_disp_send;
00727         return;
00728 
00729 discard:
00730 #if !defined (NIP_ICMP_TEST_NO_MEM_MGR)
00731         nip_mem_release_block( trans->payload );
00732 #endif
00733         nip_reset_trans( trans );
00734         return;
00735 }
00736 #endif // (NIP_ICMP_ENABLE == 1)
00737 
00738 /** Read IP data from network interface. To be called by dispatcher. */
00739 void nip_ip_disp_receive( void )
00740 {
00741         struct IP_HEADER   head;
00742         nip_net_if_id_t    if_id   = NIP_CURR_IF;
00743         nip_net_if_t       *net_if = &nip_net_if_list[ if_id ];
00744         nip_phy_if_t       *phy    = &net_if->phy_conf;
00745 //      nip_if_ip_conf_t   *ip     = &net_if->ip_conf;
00746 //      nip_net_trans_id_t tr = 0;
00747         nip_net_if_trans_t *trans;
00748         uint8_t            cnt;
00749         uint16_t           word;      // 16-bit word for different purposes
00750         uint16_t           *word_ptr; // pointer to a 16-bit word
00751         uint16_t           sum;
00752         cnt = sizeof( struct IP_HEADER );
00753 
00754         // receive ip header
00755         if ( cnt != phy->hard_read( if_id, (uint8_t*) &head, cnt ) )
00756         {
00757                 // Something's wrong with the packet. It's not large enough to hold
00758                 // the minimum IP header. -> forget about it.
00759                 return;///\todo add to statistic
00760         }
00761 
00762         // check IP version. Currently only IPv4 is supported.
00763         if ( head.version != 0x04 )
00764         {
00765                 return;///\todo add to statistic
00766         }
00767         // check destination
00768         trans = nip_ip_route( head.dst_addr, NIP_NET_NO_IF, NULL );
00769         if ( trans == NULL )
00770                 return;///\todo add to statistic
00771 
00772         // configure transmission to be reset after being finished
00773         trans->flags.reset_status = 1;
00774 
00775         // Check options and calculate header checksum
00776         cnt  = 0;
00777         sum  = 0;
00778         word_ptr = (uint16_t*)&head;
00779         ///\todo evaluate options to find end-of-option option. Options will always
00780         ///have a type and length field, each sized on byte. Only option type 0 and
00781         ///option type 1 have no length field. Quit reading the header when reaching
00782         ///option type 0.
00783         do
00784         {
00785                 /// It is possible to calculate the checksum using the network byte order
00786                 /// as that seems to not matter for this specific checksum (carries flow
00787                 /// over on both bytes of the 16-bit words being added).
00788 
00789                 if ( cnt >= sizeof ( struct IP_HEADER )/2 )
00790                 {
00791                         word_ptr = &word;
00792                         // options are being discarded and just read for checksum calculation
00793                         if ( phy->hard_read( if_id, (uint8_t*) word_ptr, 2 ) != 2 ) break;
00794                 }
00795 
00796                 sum += *word_ptr;
00797 
00798                 if ( sum < *word_ptr ) sum++; // carry
00799                 if ( ++cnt >= head.ihl * 2 ) break;
00800                 word_ptr++;
00801 
00802         } while ( 1 );
00803 
00804         if ( sum != 0xFFFF )
00805         {
00806                 // checksum failed
00807                 goto reset_trans;///\todo add to statistic
00808         }
00809 
00810         // ignore TTL for not forwarded packets (we don't forward)
00811 
00812         /// \todo Check fragmentation.
00813         // Drop fragmented packets until they're supported
00814 
00815         // determine payload length
00816         word = ntohs(head.total_length) - cnt*2;
00817 
00818 #if (NIP_ICMP_ENABLE == 1) && defined (NIP_ICMP_TEST_NO_MEM_MGR)
00819         if ( head.protocol == NIP_IP_PROTO_ICMP )
00820         {
00821                 word_ptr = (uint16_t*)&nip_test_no_mem_icmp;
00822         }
00823         else
00824         {
00825 #endif
00826         /*
00827         // set up transmission for header.
00828         trans->status = NIP_NET_IF_RECEIVING;
00829         trans->header = nip_mem_alloc(
00830                 NIP_IP_DEFAULT_HEADER_SIZE,
00831                 NIP_IP_DEFAULT_HEADER_SIZE,
00832                 NIP_MEM_FLG_DELREAD
00833         );
00834         */
00835 
00836 
00837         // fill parameters for next call
00838         nip_memcpy( trans->params.ip.dst_nw_addr, head.dst_addr, 4 );
00839         nip_memcpy( trans->params.ip.src_nw_addr, head.src_addr, 4 );
00840         trans->params.ip.protocol = head.protocol;
00841 
00842         // reserve memory for  IP payload
00843         trans->payload = nip_mem_alloc(
00844                 word,
00845                 word, /* maximize min_length for size check */
00846                 NIP_MEM_FLG_DELREAD,
00847                 NULL
00848         );
00849 
00850         if ( trans->payload == NIP_MEM_NULL )
00851         {
00852                 // not enough memory for payload -> drop packet
00853                 goto reset_trans; /// \todo add to statistic
00854         }
00855 
00856         // Retrieve pointer to read data to
00857         word_ptr = nip_mem_obtain_ptr( trans->payload );
00858 
00859 #if (NIP_ICMP_ENABLE == 1) && defined (NIP_ICMP_TEST_NO_MEM_MGR)
00860         }
00861 #endif
00862 
00863         // mark transmission as used
00864         trans->status = NIP_NET_IF_RECEIVING;
00865         trans->payload_size = word;
00866         trans->flags.free_payload = 1;
00867         word = phy->hard_read( if_id, (uint8_t*) word_ptr, trans->payload_size );
00868         // Read IP Payload
00869         if ( trans->payload_size != word )
00870         {
00871                 // reading packet failed: Packet was not as large as IP header said it was
00872                 // -> free and drop any read data
00873                 goto reset_trans;
00874         }
00875 
00876         // set buffer 'used size'. We have to do that because we have manually added
00877         // data to the buffer without the memory management's knowledge.
00878         nip_mem_set_used( trans->payload, trans->payload_size );
00879 
00880         // Release pointer (unlock block). IMPORTANT!!
00881         nip_mem_release_block( trans->payload );
00882 
00883         // save transmission id for future use
00884         nip_disp.next.common.trans = trans;
00885 
00886         // deliver payload.
00887         ///@note to enable routing you could put nip_ip_disp_send instead of
00888         /// nip_ip_disp_deliver. Additional means to reduce ttl and save ip header
00889         /// should be introduced then, though.
00890         NIP_CURR_CMD = &nip_ip_disp_deliver;
00891         return;
00892 
00893         reset_trans:
00894                         nip_reset_trans( trans );
00895 }
00896 
00897 /** Deliver IP data to transport layer. Any transmission with the flag
00898  * deliverlocal not set to 1 will be discarded.
00899  */
00900 void nip_ip_disp_deliver( void )
00901 {
00902         nip_net_if_trans_t *trans = nip_disp.next.common.trans;
00903 
00904         if ( trans->flags.deliverlocal == 1 )
00905         {
00906 
00907                 // check protocol field and quit packet reading.
00908 #if (NIP_ICMP_ENABLE == 1)
00909                 if ( trans->params.ip.protocol == NIP_IP_PROTO_ICMP )
00910                 {
00911                         NIP_CURR_CMD = &nip_icmp_disp_receive;
00912                         return;///\todo add to statistic
00913                 }
00914 #endif // (NIP_ICMP_ENABLE == 1)
00915 #if (NIP_UDP_ENABLE == 1)
00916                 else if ( trans->params.ip.protocol == NIP_IP_PROTO_UDP )
00917                 {
00918                         NIP_CURR_CMD = &nip_udp_disp_receive;
00919                         return;///\todo add to statistic
00920                 }
00921 #endif // (NIP_UDP_ENABLE == 1)
00922 #if (NIP_TCP_ENABLE == 1)
00923                 else if ( trans->params.ip.protocol == NIP_IP_PROTO_TCP )
00924                 {
00925                         NIP_CURR_CMD = &nip_tcp_disp_receive;
00926                         return;///\todo add to statistic
00927                 }
00928 #endif // (NIP_TCP_ENABLE == 1)
00929 #if (NIP_IGMP_ENABLE == 1)
00930                 else if ( trans->params.ip.protocol == NIP_IP_PROTO_IGMP )
00931                 {
00932                         NIP_CURR_CMD = &nip_igmp_disp_receive;
00933                         return;///\todo add to statistic
00934                 }
00935 #endif // (NIP_TCP_ENABLE == 1)
00936         }
00937         trans->status = NIP_NET_IF_DONE;
00938         // no protocol handler found? -> discard transmission
00939         nip_reset_trans( trans );
00940 
00941         return;///\todo add to statistic
00942 
00943 }
00944 
00945 #if NIP_MULTICAST_ENABLE == 1
00946 
00947 #if NIP_IP_MAX_HOST_GROUPS > 1
00948 /** @return pointer to group membership for given interface and group address.
00949  * If given address is NULL a pointer to an unused membership will be returned.
00950  * NULL will be returned if the query could not be fulfilled.
00951  */
00952 struct nip_ip_group_membership *nip_ip_find_group_membership( nip_net_if_id_t if_id, uint8_t *addr )
00953 {
00954         nip_host_group_id_t group   = 0;
00955         #define GROUP_ATTRi(attr) nip_ip_groups[group].attr
00956 
00957         do
00958         {
00959                 // found host group membership?
00960                 if (( addr != NULL
00961                   && nip_memcmp( addr, GROUP_ATTRi(addr), 4 ) == 0
00962                   && GROUP_ATTRi(state) != NIP_IP_GROUP_NON
00963                   && GROUP_ATTRi(if_id) == if_id
00964                    )
00965                   ||
00966                    ( addr == NULL
00967                   && GROUP_ATTRi(state) == NIP_IP_GROUP_NON
00968                         ))
00969                         return &nip_ip_groups[group];
00970 
00971                 if ( ++group == NIP_IP_MAX_HOST_GROUPS )
00972                 {
00973                         // host is not in group
00974                         return NULL;
00975                 }
00976         } while ( 1 );
00977 
00978 }
00979 #endif
00980 
00981 #if NIP_IGMP_ENABLE == 1
00982 
00983 void nip_ip_disp_check_group( void )
00984 {
00985         nip_mem_handle_t    buf;
00986         nip_net_if_trans_t  *trans;
00987         struct nip_igmp_msg *msg;
00988 
00989 #if NIP_IP_MAX_HOST_GROUPS == 1
00990         #define GROUP_ATTRi(attr) nip_ip_group.attr
00991 #else
00992         nip_host_group_id_t group   = 0;
00993         #define GROUP_ATTRi(attr) nip_ip_groups[group].attr
00994 
00995         do
00996         {
00997 #endif
00998                 if ( GROUP_ATTRi(state) == NIP_IP_GROUP_DELAYING )
00999                 {
01000                         // check timer
01001                         if ( GROUP_ATTRi(t_report) <= nip_tickcount() )
01002                         {
01003                                 // build report
01004                                 buf = nip_mem_alloc(
01005                                   sizeof( struct nip_igmp_msg ),
01006                                   sizeof( struct nip_igmp_msg ),
01007                                   0,
01008                                   NULL
01009                                 );
01010 
01011                                 // not enough memory? -> try again later
01012                                 if ( buf == NIP_MEM_NULL )
01013                                         return;
01014 
01015                                 // get transmission
01016                                 trans = nip_ip_route( nip_ip_all_hosts, NIP_CURR_IF, NULL );
01017 
01018                                 // no transmission or route available? -> try again later
01019                                 if ( trans == NULL )
01020                                 {
01021                                         nip_mem_free( buf );
01022                                         return;
01023                                 }
01024 
01025                                 // fill report
01026                                 msg = nip_mem_obtain_ptr( buf );
01027                                 msg->ver_type = NIP_IGMP_VERTYPE_REPORT;
01028                                 msg->checksum = 0;
01029                                 nip_memcpy( msg->addr, GROUP_ATTRi(addr), 4 );
01030                                 msg->checksum = nip_internet_checksum(
01031                                                     (uint16_t *)msg,
01032                                                     sizeof( struct nip_igmp_msg )
01033                                                 );
01034                                 nip_mem_release_block( buf );
01035 
01036                                 // add to transmission
01037                                 trans->payload = buf;
01038 
01039                                 // don't loopback, don't route
01040                                 trans->flags.deliverlocal = 0;
01041                                 trans->params.ip.ttl = 1;
01042 
01043                                 // transmit
01044                                 trans->status = NIP_NET_IF_RESOLV_ADDR;
01045                                 nip_disp_notify( NIP_DISP_CHECK_TRANS );
01046 
01047                                 // reset group membership status
01048                                 GROUP_ATTRi(state) = NIP_IP_GROUP_IDLE;
01049                         }
01050                 }
01051 
01052 #if NIP_IP_MAX_HOST_GROUPS != 1
01053         } while  ( ++group < NIP_IP_MAX_HOST_GROUPS );
01054 #endif
01055 
01056 }
01057 
01058 /** mark all groups memberships on given interface to send a report */
01059 void nip_ip_query_groups( nip_net_if_id_t if_id )
01060 {
01061         nip_host_group_id_t group   = 0;
01062 #if NIP_IP_MAX_HOST_GROUPS == 1
01063         #define GROUP_ATTRi(attr) nip_ip_group.attr
01064 #else
01065         #define GROUP_ATTRi(attr) nip_ip_groups[group].attr
01066 
01067         do
01068         {
01069 #endif
01070                 if ( GROUP_ATTRi(state) == NIP_IP_GROUP_IDLE && GROUP_ATTRi(if_id) == if_id)
01071                 {
01072                         // find some fairly random value for report-timeout
01073                         GROUP_ATTRi(t_report) = nip_tickcount() + ((&group)[group] % 10);
01074                 }
01075 #if NIP_IP_MAX_HOST_GROUPS != 1
01076         } while ( ++group < NIP_IP_MAX_HOST_GROUPS );
01077 #endif
01078 }
01079 
01080 /** Dispatcher function to be called upon receiving IGMP packet */
01081 void nip_igmp_disp_receive( void )
01082 {
01083         nip_net_if_trans_t *trans = nip_disp.next.common.trans;
01084         struct nip_igmp_msg *msg;
01085 #if NIP_IP_MAX_HOST_GROUPS == 1
01086         #define GROUP_ATTR(attr) nip_ip_group.attr
01087 #else
01088         struct nip_ip_group_membership *group;
01089         #define GROUP_ATTR(attr) group->attr
01090 #endif
01091 
01092 
01093         msg = nip_mem_obtain_ptr( trans->payload );
01094 
01095         ///@todo Check IGMP checksum
01096 
01097         if ( msg->ver_type == NIP_IGMP_VERTYPE_QUERY )
01098         {
01099                 nip_ip_query_groups( NIP_CURR_IF );
01100         }
01101         else
01102         {
01103                 //stop report timer for the group, if we're member of it.
01104 #if NIP_IP_MAX_HOST_GROUPS == 1
01105                 if ( GROUP_ATTR(state) == NIP_IP_GROUP_DELAYING
01106                 && nip_memcmp( GROUP_ATTR(addr), msg->addr, 4 ) == 0
01107                 && GROUP_ATTR(if_id) == NIP_CURR_IF )
01108                         GROUP_ATTR(state) = NIP_IP_GROUP_IDLE;
01109 #else
01110                 group = nip_ip_find_group_membership( NIP_CURR_IF, msg->addr );
01111                 if ( group != NULL )
01112                         GROUP_ATTR(state) = NIP_IP_GROUP_IDLE;
01113 #endif
01114         }
01115 
01116         nip_mem_release_block( trans->payload );
01117         nip_reset_trans( trans );
01118 }
01119 
01120 #endif /* NIP_IGMP_ENABLE */
01121 
01122 
01123 /** Join Host Group
01124  * @return NIP_E_OK or NIP_E_OUT_OF_RESSOURCES. In the latter case you need to
01125  * increase the value of the preconfigured NIP_IP_MAX_HOST_GROUPS definition.
01126 */
01127 nip_error_t nip_ip_join_host_group( nip_net_if_id_t if_id, uint8_t *addr )
01128 {
01129         // find configuration for group membership. This is done differently
01130         // depending on the NIP_IP_MAX_HOST_GROUPS configuration to save program
01131         // memory.
01132 #if NIP_IP_MAX_HOST_GROUPS == 1
01133         #define GROUP_ATTR(attr) nip_ip_group.attr
01134 
01135         if ( GROUP_ATTR(state) != NIP_IP_GROUP_NON
01136           && ( GROUP_ATTR(if_id) != if_id
01137             || nip_memcmp( addr, GROUP_ATTR(addr), 4 ) != 0)
01138                 )
01139         return NIP_E_OUT_OF_RESSOURCES;
01140 
01141 #else
01142         struct nip_ip_group_membership *group;
01143         #define GROUP_ATTR(attr) group->attr
01144 
01145         group = nip_ip_find_group_membership( if_id, addr );
01146         if ( group == NULL )
01147                 group = nip_ip_find_group_membership( if_id, NULL );
01148 
01149         if ( group == NULL )
01150                 return NIP_E_OUT_OF_RESSOURCES;
01151 #endif
01152 
01153 
01154         // set state to join the group on the network if that has not already
01155         // happened
01156         if ( GROUP_ATTR(state) == NIP_IP_GROUP_NON )
01157         {
01158                 // configure group membership
01159                 GROUP_ATTR(if_id) = if_id;
01160                 GROUP_ATTR(member_cnt) = 1;
01161                 GROUP_ATTR(state)      = NIP_IP_GROUP_DELAYING;
01162                 nip_memcpy( GROUP_ATTR(addr), addr, 4 );
01163 #if NIP_IGMP_ENABLE == 1
01164                 GROUP_ATTR(t_report)   = nip_tickcount();
01165                 nip_disp_notify_if( if_id, NIP_DISP_IF_GROUP );
01166                 nip_dispatcher();
01167 #endif
01168         }
01169         else
01170         {
01171                 GROUP_ATTR(member_cnt)++;
01172         }
01173 
01174         return NIP_E_OK;
01175 }
01176 
01177 /** Leave Host Group */
01178 void nip_ip_leave_host_group( nip_net_if_id_t if_id, uint8_t *addr )
01179 {
01180         // find configuration for group membership. This is done differently
01181         // depending on the NIP_IP_MAX_HOST_GROUPS configuration to save program
01182         // memory.
01183 #if NIP_IP_MAX_HOST_GROUPS == 1
01184         #define GROUP_ATTR(attr) nip_ip_group.attr
01185 
01186         if ( GROUP_ATTR(state) == NIP_IP_GROUP_NON
01187           || ( GROUP_ATTR(if_id) != if_id
01188             && nip_memcmp( addr, GROUP_ATTR(addr), 4 ) != 0)
01189                 )
01190         // host was not in group
01191         return;
01192 
01193 #else
01194         struct nip_ip_group_membership *group;
01195         #define GROUP_ATTR(attr) group->attr
01196 
01197         group = nip_ip_find_group_membership( if_id, addr );
01198         if ( group == NULL )
01199                 return;
01200 #endif
01201 
01202         // reduce member count
01203         if ( --GROUP_ATTR(member_cnt) == 0 )
01204         {
01205                 // leave group
01206                 GROUP_ATTR(state) = NIP_IP_GROUP_NON;
01207         }
01208 }
01209 
01210 #endif /*NIP_MULTICAST_ENABLE*/
01211 
01212 #endif /*NIP_IP_ENABLE*/

Generated on Thu Jul 10 01:09:29 2008 for NIP by  doxygen 1.5.5