/*
 * Vince version 1.0.1
 * 
 * 
 * +++++++++++
 *  V I N C E  ---  Vendor Independent Network Control Entity
 * +++++++++++
 * Copyright (c) 1994     Advanced Research Projects Agency (ARPA/CSTO)
 *                                   and the
 *                        Naval Research Laboratory (NRL/CCS)
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the software,
 * derivative works or modified versions, and any portions thereof, and
 * that both notices appear in supporting documentation.
 * 
 * ARPA AND NRL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION
 * AND DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
 * RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * ARPA and NRL request users of this software to return to:
 * 
 *                 vince-distribution@cmf.nrl.navy.mil
 * 
 *                            or
 * 
 *                 Naval Research Laboratory, Code 5590
 *                 Center for Computational Science
 *                 Washington, D.C. 20375-5000
 * 
 * any improvements or extensions that they make and grant ARPA and NRL 
 * the rights to redistribute these changes.
 * 
 */

#include <atmcore/atmcore.h>
#include <protocol/protocol.h>
#include <fcntl.h>
#include <skip/skip.h>
#include <host/ip_service.h>

extern layer create_kernel_layer();

typedef struct llc_protocol {
  layer (*new_function)();
  int snap;
  void *new_context;
  unsigned int stack_flags;
} *llc_protocol;

void host_add_llc_protocol (ip_service i,int snap,layer (*new)(),
			    void *new_context,unsigned int stack_flags)
{ 
  llc_protocol l=(llc_protocol)allocate_memory(sizeof(struct llc_protocol));
  l->new_function=new;
  l->snap=snap;
  l->stack_flags=stack_flags;
  l->new_context=new_context;
  i->llc_protocols=insert(l,i->llc_protocols);
}

static void ip_service_new_translation(ip_service i,address a)
{
  add_translation(i->s->a,a);
  add_translation(a,i->s->a);
}

static layer new_llc_ip_connection (address z,ip_service i)
{
  address a=0;
  if (z) a=find_translation(z,IP_FAMILY);
  return(create_kernel_layer("add_ip_vc %x %x 0",i->ip_vc,
			a?*(int *)address_contents(a):0));
}

static layer assemble_llc_protocols(address a,ip_service i)
{
  layer z,s,bk,bp;
  llc_protocol l;
  layer ll=create_kernel_layer("create_llc");
  list j;
  dolist(j,i->llc_protocols){
    l=(llc_protocol)j->first;
    z=(*l->new_function)(a,l->new_context);
    s=create_kernel_layer("add_llc_snap %08x 0 %04x",ll,l->snap);
    if (l->stack_flags & STACK_KERNEL){
      kstaple(z,s);
    } else {
      create_dumb_interface(&bk,&bp);
      kstaple(bk,s);
      assemble_stack(2,z,bp);
    }
  }
  return(ll);
}

static layer new_host_llc_connection(call_tail t,ip_service i)
{
  address v=0;
  if (t->c->ctype==call_connection_duplex) v=t->c->source;
  return(assemble_llc_protocols(v,i));
}

void add_ip_pvc(port p, int vpi, int vci, ip_service i, address dest)
{
  layer ll,z,s,pr;
  llc_protocol l;
  list j;
  
  if (i->llc_protocols)
    ll=assemble_llc_protocols(dest,i);
  else 
    ll=create_kernel_layer("add_ip_vc %x %x 1", i->ip_vc,
			   *(unsigned int *)address_contents(dest));
  (*p->h->assemble_stack)(p,vpi,vci,i->stack_flags,ll,0,0);
}

static layer new_host_ip_connection(call_tail t,ip_service i)
{
  address a=0;
  layer answer = 0;

  if (t->c->ctype==call_connection_duplex)
    a=find_translation(t->c->source,IP_FAMILY);
  return(create_kernel_layer("add_ip_vc %x %x 0",i->ip_vc,
			     a?*(int *)address_contents(a):0));
}

static void ip_service_call_failure(call c)
{
  kernel_stack_shutdown(c->head);
}

void ip_service_add_ip_vc(ip_service i,address dest,address source,
			  layer l)
{
  if (i->llc_protocols){
    /* the ip_vc stub l gets transparently overwritten should
       the call succeed, but it's existence is problematic */
    create_signalled_atm(i->s,dest,i->service,source,0,0,i->stack_flags,
			 assemble_llc_protocols(dest,i),
			 ip_service_call_failure);
  } else {
    create_signalled_atm(i->s,dest,i->service,source,0,0,i->stack_flags,l,
			 ip_service_call_failure);
  }
}


ip_service bind_host_ip_service(virtual_switch s,address a,int encap,int mtu)
{
  ip_service i=(ip_service)allocate_memory(sizeof(struct ip_service));

  i->s=s;
  i->service=a;
  i->llc_protocols=0;
  switch(encap){
  case 0: i->stack_flags=STACK_AAL_5|STACK_KERNEL|STACK_PRINT; break;
  case 1: i->stack_flags=STACK_AAL_4|STACK_KERNEL|STACK_PRINT; break;
  }
  i->ip_vc=
    create_kernel_layer("create_vince_ip %x %s %d %d",i,sprint_address(a),
			encap,mtu);

  bind_service(s,a,i->stack_flags,new_host_ip_connection,i);
  return(i);
}

ip_service bind_host_llc_service(virtual_switch s,address a,int encap,
				 int mtu)
{
  ip_service i=(ip_service)allocate_memory(sizeof(struct ip_service));

  i->s=s;
  i->service=a;
  i->llc_protocols=0;
  host_add_llc_protocol(i,0x800,new_llc_ip_connection,i,STACK_KERNEL);
  switch(encap){
  case 0: i->stack_flags=STACK_AAL_5|STACK_KERNEL; break;
  case 1: i->stack_flags=STACK_AAL_4|STACK_KERNEL; break;
  }
  i->ip_vc=
    create_kernel_layer("create_vince_ip %x %s %d %d",i,sprint_address(a),
			encap,mtu);

  bind_service(s,a,i->stack_flags,new_host_llc_connection,i);
  return(i);
}

void init_ip_service()
{
  skip_function (bind_host_llc_service,"bind_host_llc_service","x","Saii");
  skip_function (bind_host_ip_service,"bind_host_ip_service","x","Saii");
  skip_function(ip_service_add_ip_vc,"ip_service_add_ip_vc","","xaax");
  skip_function(ip_service_new_translation,"ip_service_new_translation","",
		"xa");
  skip_function(add_ip_pvc,"add_ip_pvc","","pxxxa");
}
