Subversion Repositories HelenOS

Rev

Rev 3466 | Rev 3685 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 * Copyright (c) 2008 Lukas Mejdrech
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup net
 * @{
 */

/** @file
 */

#include <async.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
#include <task.h>
//#include <thread.h>
#include <unistd.h>
#include <ipc/ipc.h>
#include <ipc/services.h>
//#include <sys/mman.h>

#include "../char_map.h"
#include "../err.h"
#include "../generic_char_map.h"
#include "../measured_strings.h"
#include "../messages.h"
#include "../modules.h"
//#include "../self_test.h"

#include "../netif/netif_device_id_type.h"

#ifdef NETWORKING_module

    #include "../ip/ip.h"
    #include "../tcp/tcp.h"

#endif

#define LO_NAME             "lo"
#define LO_FILENAME         "/sbin/lo"
#define DP8390_ISA_NAME         "dp8390_isa"
#define DP8390_ISA_FILENAME     "/sbin/dp8380_isa"
#define ETHERNET_NAME           "ethernet"
#define ETHERNET_FILENAME       "/sbin/ethernet"
#define IP_NAME             "ip"
#define IP_FILENAME         "/sbin/ip"

typedef struct module_struct    module_t;
typedef module_t *      module_ref;

typedef struct netif        netif_t;
typedef netif_t *       netif_ref;

typedef struct networking_globals   networking_globals_t;

DEVICE_MAP_DECLARE( netifs, netif_t )

GENERIC_CHAR_MAP_DECLARE( measured_strings, measured_string_t )

GENERIC_CHAR_MAP_DECLARE( modules, module_t )

struct module_struct{
    task_id_t   task_id;
    services_t  service;
    int     phone;
    int     usage;
    const char *    name;
    const char *    filename;
};

/** A present network interface device.
 */
struct netif{
    /** A system-unique network interface identifier.
     */
    netif_device_id_t   id;
    /** A serving network interface driver module index.
     */
    module_ref      driver_module;
    /** A serving link layer module index.
     */
    module_ref      link_layer_module;
    /** A serving internet layer module index.
     */
    module_ref      internet_layer_module;
    /** A system-unique network interface name.
     */
    char *          name;
    /** Configuration.
     */
    measured_strings_t  configuration;
};

/** A networking module global variables.
 */
struct networking_globals{
    /** Present network interfaces.
     */
    netifs_t        netifs;
    /** Network interface structure indices by names.
     */
    char_map_t      netif_names;
    /** Available modules.
     */
    modules_t       modules;
    /** Global configuration.
     */
    measured_strings_t  configuration;
};

int     add_module( module_ref * module, modules_ref modules, const char const * name, const char const * filename, services_t service, task_id_t task_id );
static void client_connection( ipc_callid_t iid, ipc_call_t * icall );
measured_string_ref configuration_find( measured_strings_ref configuration, const char * name );
int     main( int argc, char * argv[] );
int     networking_call( ipc_callid_t callid );
int     networking_initialize( void );
int     networking_message( ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2, ipcarg_t arg3, ipcarg_t * result1, ipcarg_t * result2, ipcarg_t * result3 );
//int       parse_line( measured_strings_ref configuration, char * line );
int     add_configuration( measured_strings_ref configuration, const char * name, const char * value );
int     read_configuration( void );
task_id_t   spawn( const char * fname );
int     startup( void );
netif_device_id_t   generate_new_device_id( void );

static networking_globals_t networking_globals;

DEVICE_MAP_IMPLEMENT( netifs, netif_t )

GENERIC_CHAR_MAP_IMPLEMENT( measured_strings, measured_string_t )

GENERIC_CHAR_MAP_IMPLEMENT( modules, module_t )

int add_module( module_ref * module, modules_ref modules, const char * name, const char * filename, services_t service, task_id_t task_id ){
    ERROR_DECLARE;

    module_ref  tmp_module;

    tmp_module = ( module_ref ) malloc( sizeof( module_t ));
    if( ! tmp_module ) return ENOMEM;
    tmp_module->task_id = task_id;
    tmp_module->phone = 0;
    tmp_module->usage = 0;
    tmp_module->name = name;
    tmp_module->filename = filename;
    tmp_module->service = service;
    if( ERROR_OCCURED( modules_add( modules, tmp_module->name, tmp_module ))){
        free( tmp_module );
        return ERROR_CODE;
    }
    if( module ) * module = tmp_module;
    return EOK;
}

static void client_connection( ipc_callid_t iid, ipc_call_t * icall ){
    ipc_callid_t    callid;
    ipc_call_t  call;
    ipcarg_t    arg1, arg2, arg3;
    int     res;

    /* Accept the connection */
    ipc_answer_0( iid, EOK );

    while( true ){
        callid = async_get_call( & call );
        arg1 = 0;
        arg2 = 0;
        arg3 = 0;
#ifdef NETWORKING_module
        if( IS_NET_IL_MESSAGE( call ) || IS_NET_IP_MESSAGE( call )){
            res = ip_call( callid );
            if( res == EOK ){
                res = ip_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
/*      }else if( IS_NET_ARP_MESSAGE( call )){
            res = arp_call( callid );
            if( res == EOK ){
                res = arp_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
*//*        }else if( IS_NET_RARP_MESSAGE( call )){
            res = rarp_call( callid );
            if( res == EOK ){
                res = rarp_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
*//*        }else if( IS_NET_ICMP_MESSAGE( call )){
            res = icmp_call( callid );
            if( res == EOK ){
                res = icmp_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
*//*        }else if( IS_NET_UDP_MESSAGE( call )){
            res = udp_call( callid );
            if( res == EOK ){
                res = udp_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
*/      }else if( IS_NET_TCP_MESSAGE( call )){
            res = tcp_call( callid );
            if( res == EOK ){
                res = tcp_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
/*      }else if( IS_NET_SOCKET_MESSAGE( call )){
            res = socket_call( callid );
            if( res == EOK ){
                res = socket_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
*//*        }else if( IS_NET_LL_MESSAGE( call ) || IS_NET_ETHERNET_MESSAGE( call )){
            res = ethernet_call( callid );
            if( res == EOK ){
                res = ethernet_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
*/      }else{
#endif
            res = networking_call( callid );
            if( res == EOK ){
                res = networking_message( IPC_GET_METHOD( call ), IPC_GET_ARG1( call ), IPC_GET_ARG2( call ), IPC_GET_ARG3( call ), & arg1, & arg2, & arg3 );
            }
#ifdef NETWORKING_module
        }
#endif
        ipc_answer_2( callid, res, arg1, arg2 );
    }
}

int main( int argc, char * argv[] ){

    printf("\nTask %d - Networking: HelenOS Networking subsystem\n", task_get_id());

    return start_service( SERVICE_NETWORKING, NULL, NULL, client_connection, networking_initialize );
}

int networking_call( ipc_callid_t callid ){
    return EOK;
}

int networking_initialize( void ){
    ERROR_DECLARE;

    task_id_t   task_id;

    netifs_initialize( & networking_globals.netifs );
    char_map_initialize( & networking_globals.netif_names );
    modules_initialize( & networking_globals.modules );
    measured_strings_initialize( & networking_globals.configuration );

    // run self tests
//  ERROR_PROPAGATE( self_test());

    ERROR_PROPAGATE( add_module( NULL, & networking_globals.modules, LO_NAME, LO_FILENAME, SERVICE_LO, 0 ));
    ERROR_PROPAGATE( add_module( NULL, & networking_globals.modules, DP8390_ISA_NAME, DP8390_ISA_FILENAME, SERVICE_DP8390_ISA, 0 ));
    ERROR_PROPAGATE( add_module( NULL, & networking_globals.modules, ETHERNET_NAME, ETHERNET_FILENAME, SERVICE_ETHERNET, 0 ));

#ifdef NETWORKING_modular
    task_id = spawn( "/sbin/ip" );
    if( ! task_id ) return EINVAL;
    ERROR_PROPAGATE( add_module( NULL, & networking_globals.modules, IP_NAME, IP_FILENAME, SERVICE_IP, task_id ));
//  if( ! spawn( "/sbin/udp" )) return EINVAL;
    if( ! spawn( "/sbin/tcp" )) return EINVAL;
//  if( ! spawn( "/sbin/socket" )) return EINVAL;
//  not always necesssary
//  if( ! spawn( "/sbin/arp" )) return EINVAL;
//  if( ! spawn( "/sbin/rarp" )) return EINVAL;
//  if( ! spawn( "/sbin/icmp" )) return EINVAL;

#else
#ifdef NETWORKING_module
    ipcarg_t    phonehash;

    ERROR_PROPAGATE( REGISTER_ME( SERVICE_IP, & phonehash ));
    ERROR_PROPAGATE( add_module( NULL, & networking_globals.modules, IP_NAME, IP_FILENAME, SERVICE_IP, task_get_id()));
    ERROR_PROPAGATE( ip_initialize());
//  ERROR_PROPAGATE( REGISTER_ME( SERVICE_ARP, & phonehash ));
//  ERROR_PROPAGATE( arp_initialize());
//  ERROR_PROPAGATE( REGISTER_ME( SERVICE_RARP, & phonehash ));
//  ERROR_PROPAGATE( rarp_initialize());
//  ERROR_PROPAGATE( REGISTER_ME( SERVICE_ICMP, & phonehash ));
//  ERROR_PROPAGATE( icmp_initialize());
//  ERROR_PROPAGATE( REGISTER_ME( SERVICE_UDP, & phonehash ));
//  ERROR_PROPAGATE( udp_initialize());
    ERROR_PROPAGATE( REGISTER_ME( SERVICE_TCP, & phonehash ));
    ERROR_PROPAGATE( tcp_initialize());
//  ERROR_PROPAGATE( REGISTER_ME( SERVICE_SOCKET, & phonehash ));
//  ERROR_PROPAGATE( socket_initialize());
//  ERROR_PROPAGATE( REGISTER_ME( SERVICE_ETHERNET, & phonehash ));
//  ERROR_PROPAGATE( ethernet_initialize());
#endif
#endif

    return EOK;
}

int networking_message( ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2, ipcarg_t arg3, ipcarg_t * result1, ipcarg_t * result2, ipcarg_t * result3 ){
    ERROR_DECLARE;

    measured_string_ref strings;
    char *          data;
    int         index;
    measured_string_ref setting;
    measured_strings_ref    configuration;
    netif_ref       netif;

//  printf( "\nNetworking message: %d\n", method );
    switch( method ){
//      case IPC_M_CONNECT_ME_TO:
        case IPC_M_PHONE_HUNGUP:
            return EOK;
        case NET_NETWORKING_DEVICE:
            // TODO configure, register
            // arg1 = netif id
            printf( "\nNetworking: new netif %d", arg1 );
            return EOK;
        case NET_NETWORKING_GET_DEVICE_CONFIGURATION:
            // arg1 = netif id
            // arg2 = count
            ERROR_PROPAGATE( measured_strings_receive( & strings, & data, arg2 ));
            netif = netifs_find( & networking_globals.netifs, arg1 );
            if( netif ){
                configuration = & netif->configuration;
            }else{
                configuration = NULL;
            }
            for( index = 0; index < arg2; ++ index ){
                setting = measured_strings_find( configuration, strings[ index ].value );
                if( ! setting ){
                    setting = measured_strings_find( & networking_globals.configuration, strings[ index ].value );
                }
                if( setting ){
                    strings[ index ].length = setting->length;
                    strings[ index ].value = setting->value;
                }else{
                    strings[ index ].length = 0;
                    strings[ index ].value = NULL;
                }
            }
            // strings should not contain received data anymore
            free( data );
            ERROR_CODE = measured_strings_reply( strings, arg2 );
            free( strings );
            return ERROR_CODE;
        case NET_NETWORKING_GET_CONFIGURATION:
            // arg1 = count
            ERROR_PROPAGATE( measured_strings_receive( & strings, & data, arg1 ));
            for( index = 0; index < arg1; ++ index ){
                setting = measured_strings_find( & networking_globals.configuration, strings[ index ].value );
                if( setting ){
                    strings[ index ].length = setting->length;
                    strings[ index ].value = setting->value;
                }else{
                    strings[ index ].length = 0;
                    strings[ index ].value = NULL;
                }
            }
            // strings should not contain received data anymore
            free( data );
            ERROR_CODE = measured_strings_reply( strings, arg1 );
            free( strings );
            return ERROR_CODE;
        case NET_NETWORKING_STARTUP:
            return startup();
    }
    return ENOTSUP;
}

/*
int parse_line( measured_strings_ref configuration, char * line ){
    ERROR_DECLARE;

    measured_string_ref setting;
    char *          name;
    char *          value;

    // from the beginning
    name = line;
    // skip spaces
    while( isspace( * name )) ++ name;
    // remember the name start
    value = name;
    // skip the name
    while( isalnum( * value ) || ( * value == '_' )){
        // make uppercase
//      * value = toupper( * value );
        ++ value;
    }
    if( * value == '=' ){
        // terminate the name
        * value = '\0';
    }else{
        // terminate the name
        * value = '\0';
        // skip until '='
        ++ value;
        while(( * value ) && ( * value != '=' )) ++ value;
        // not found?
        if( * value != '=' ) return EINVAL;
    }
    ++ value;
    // skip spaces
    while( isspace( * value )) ++ value;
    // create a bulk measured string till the end
    setting = measured_string_create_bulk( value, -1 );
    if( ! setting ) return ENOMEM;
    // add the configuration setting
    if( ERROR_OCCURED( measured_strings_add( configuration, name, setting ))){
        free( setting );
        return ERROR_CODE;
    }
    return EOK;
}
*/

int add_configuration( measured_strings_ref configuration, const char * name, const char * value ){
    ERROR_DECLARE;

    measured_string_ref setting;

    setting = measured_string_create_bulk( value, 0 );
    if( ! setting ) return ENOMEM;
    // add the configuration setting
    if( ERROR_OCCURED( measured_strings_add( configuration, name, setting ))){
        free( setting );
        return ERROR_CODE;
    }
    return EOK;
}

netif_device_id_t generate_new_device_id( void ){
    return netifs_count( & networking_globals.netifs ) + 1;
}

int read_configuration( void ){
    ERROR_DECLARE;

    netif_ref       netif;
    measured_string_ref setting;
    services_t      internet_service;

    // read general configuration
    ERROR_PROPAGATE( add_configuration( & networking_globals.configuration, "IPV", "4" ));

    // read network interfaces configuration

    // static loopback initialization
    printf( "\nloopback initialization" );
    netif = ( netif_ref ) malloc( sizeof( netif_t ));
    if( ! netif ) return ENOMEM;
    netif->id = generate_new_device_id();
    ERROR_PROPAGATE( measured_strings_initialize( & netif->configuration ));
    if( ERROR_OCCURED( add_configuration( & netif->configuration, "NAME", LO_NAME ))
        || ERROR_OCCURED( add_configuration( & netif->configuration, "NETIF", LO_NAME ))
        || ERROR_OCCURED( add_configuration( & netif->configuration, "IL", IP_NAME ))
        || ERROR_OCCURED( add_configuration( & netif->configuration, "IP_CONFIG", "STATIC" ))
        || ERROR_OCCURED( add_configuration( & netif->configuration, "IP_ADDR", "127.0.0.1" ))
        || ERROR_OCCURED( add_configuration( & netif->configuration, "NETMASK", "255.255.255.255" ))
        ){
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return ERROR_CODE;
    }
    // mandatory name
    printf( "\n\tname" );
    setting = measured_strings_find( & netif->configuration, "NAME" );
    if( ! setting ){
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return EINVAL;
    }
    netif->name = setting->value;
    printf( " %s OK", netif->name );
    // mandatory netif
    printf( "\n\tnetif" );
    setting = measured_strings_find( & netif->configuration, "NETIF" );
    if( ! setting ){
        printf( " unknown" );
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return EINVAL;
    }
//  printf( " find %s in %d?", setting->value, modules_count( & networking_globals.modules ));
    netif->driver_module = modules_find( & networking_globals.modules, setting->value );
    if( ! netif->driver_module ){
        printf( " not found" );
        // TODO register the unknown one
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return EINVAL;
    }
    printf( " found" );
    if( ! netif->driver_module->task_id ){
        netif->driver_module->task_id = spawn( netif->driver_module->filename );
        if( ! netif->driver_module->task_id ){
            measured_strings_destroy( & netif->configuration );
            free( netif );
            return EINVAL;
        }
    }
    if( ! netif->driver_module->phone ){
        printf( " connect?" );
        netif->driver_module->phone = connect_to_service( netif->driver_module->service );
    }
    printf( " connected" );
    if( ERROR_OCCURED( ipc_call_sync_1_0( netif->driver_module->phone, NET_NETIF_PROBE, netif->id ))){
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return ERROR_CODE;
    }
    ++ netif->driver_module->usage;
    printf( " OK" );
    // optional link layer
    printf( "\n\tlink layer" );
    setting = measured_strings_find( & netif->configuration, "LL" );
    if( setting ){
        netif->link_layer_module = modules_find( & networking_globals.modules, setting->value );
        if( ! netif->link_layer_module ){
            // TODO register the unknown one
            -- netif->driver_module->usage;
            measured_strings_destroy( & netif->configuration );
            free( netif );
            return EINVAL;
        }
        if( ! netif->link_layer_module->task_id ){
            netif->link_layer_module->task_id = spawn( netif->link_layer_module->filename );
            if( ! netif->link_layer_module->task_id ){
                -- netif->driver_module->usage;
                measured_strings_destroy( & netif->configuration );
                free( netif );
                return EINVAL;
            }
        }
        if( ! netif->link_layer_module->phone ){
            netif->link_layer_module->phone = connect_to_service( netif->link_layer_module->service );
        }
        if( ERROR_OCCURED( ipc_call_sync_2_0( netif->link_layer_module->phone, NET_LL_DEVICE, netif->id, netif->driver_module->service ))){
            -- netif->driver_module->usage;
            measured_strings_destroy( & netif->configuration );
            free( netif );
            return ERROR_CODE;
        }
        ++ netif->link_layer_module->usage;
        internet_service = netif->link_layer_module->service;
        printf( " OK" );
    }else{
        internet_service = netif->driver_module->service;
        printf( " none" );
    }
    // mandatory internet layer
    printf( "\n\tinternet layer" );
    setting = measured_strings_find( & netif->configuration, "IL" );
    if( ! setting ){
        -- netif->driver_module->usage;
        -- netif->link_layer_module->usage;
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return EINVAL;
    }
    printf( " set %s", setting->value );
    netif->internet_layer_module = modules_find( & networking_globals.modules, setting->value );
    if( ! netif->internet_layer_module ){
        // TODO register the unknown one
        -- netif->driver_module->usage;
        -- netif->link_layer_module->usage;
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return EINVAL;
    }
    printf( " found" );
    if( ! netif->internet_layer_module->task_id ){
        netif->internet_layer_module->task_id = spawn( netif->internet_layer_module->filename );
        if( ! netif->internet_layer_module->task_id ){
            -- netif->driver_module->usage;
            -- netif->link_layer_module->usage;
            measured_strings_destroy( & netif->configuration );
            free( netif );
            return EINVAL;
        }
    }
    if( ! netif->internet_layer_module->phone ){
        printf( " connect" );
        netif->internet_layer_module->phone = connect_to_service( netif->internet_layer_module->service );
    }
    if( ERROR_OCCURED( ipc_call_sync_2_0( netif->internet_layer_module->phone, NET_IL_DEVICE, netif->id, internet_service ))){
        measured_strings_destroy( & netif->configuration );
        free( netif );
        return ERROR_CODE;
    }
    ++ netif->internet_layer_module->usage;
    printf( " OK" );
    if( ERROR_OCCURED( netifs_add( & networking_globals.netifs, netif->id, netif ))){
        free( netif );
        return ERROR_CODE;
    }
    printf( "\nloopback OK" );
    // end of static loopback initialization
    return EOK;
}

task_id_t spawn( const char * fname ){
    const char  * argv[ 2 ];
    task_id_t   res;

//  printf( "Spawning %s\n", fname );
    argv[ 0 ] = fname;
    argv[ 1 ] = NULL;
    res = task_spawn( fname, argv );
    if( res != 0 ){
        /* Success */
        usleep( 50000 );
    }
    return res;
}

int startup( void ){
    ERROR_DECLARE;

    // read configuration files
    if( ERROR_OCCURED( read_configuration())) return ERROR_CODE;

    // start network interfaces and needed modules
//  start_device( "/sbin/lo", "/sbin/dummy_link_layer" );
    return EOK;
}

/** @}
 */