Source to bsd/netat/ddp.c
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. This file contains Original Code and/or Modifications of
* Original Code as defined in and that are subject to the Apple Public
* Source License Version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. Please obtain a copy of the
* License at http://www.apple.com/publicsource and read it before using
* this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License."
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1987, 1988, 1989 Apple Computer, Inc.
*
* The information contained herein is subject to change without
* notice and should not be construed as a commitment by Apple
* Computer, Inc. Apple Computer, Inc. assumes no responsibility
* for any errors that may appear.
*
* Confidential and Proprietary to Apple Computer, Inc.
*
*
*
*/
#define RESOLVE_DBG /* define debug globals in debug.h */
#include <sysglue.h>
#include <at/appletalk.h>
#include <lap.h>
#include <llap.h>
#include <at/elap.h>
#include <at/ddp.h>
#include <at/ep.h>
#include <nbp.h>
#include <rtmp.h>
#include <at/zip.h>
#include <at/at_lap.h>
#include <at_elap.h>
#include <at_ddp.h>
#include <adsp_local.h>
#include <at_ddp_brt.h>
#include <at_zip.h>
#include <routing_tables.h>
#include <atlog.h>
#include <at_snmp.h>
#include <at_aurp.h>
#include "at_kdebug.h"
/* globals */
/* Queue of LAP interfaces which have registered themselves with DDP */
static at_if_t at_ifQueueHd = { &at_ifQueueHd, &at_ifQueueHd };
/* Pointer to LAP interface which DDP addresses are tied to */
/*at_if_t *at_ifDefault = 0; */
/* DDP statistics */
static at_ddp_stats_t at_ddp_stats;
snmpStats_t snmpStats; /* snmp ddp & echo stats */
/* DDP input queue */
static char *InputQueue = NULL;
/* DDP Socket Table */
ddp_socket_t ddp_socket [DDP_SOCKET_LAST + 1];
at_if_t *ifID_table[IF_TOTAL_MAX];
/* the table of ifID structures, one per
* interface (not just ethernet)
* NOTE: for MH, entry 0 in this table is
* now defined to be the default I/F
*/
/* Appletalk state */
extern at_state_t *at_statep;
/* routing mode special */
void (*ddp_AURPsendx)();
at_if_t *aurp_ifID = 0;
extern routing_needed();
extern pktsIn,pktsOut;
/* extern char ddp_off_flag; */
int pktsDropped,pktsHome;
atlock_t ddpall_lock;
atlock_t ddpinp_lock;
int ot_protoCnt = 0;
char ot_protoT[256];
char ot_atp_socketM[256];
char ot_adsp_socketM[256];
extern int *atp_pidM;
extern int *adsp_pidM;
extern gref_t *atp_inputQ[];
extern CCB *adsp_inputQ[];
at_if_t *forUs(at_ddp_t *);
extern void
routershutdown(),
ddp_brt_shutdown();
void ddp_notify_nbp();
#define MAX_NOTIFY_NBP 16
static struct {
unsigned char socket;
unsigned char ddptype;
unsigned short filler;
int pid;
} notify_nbp_tbl[MAX_NOTIFY_NBP];
void
Xddp_notify_nbp(id)
int id;
{
unsigned char socket;
if ((socket = notify_nbp_tbl[id].socket) != 0) {
notify_nbp_tbl[id].socket = 0;
ddp_notify_nbp(
socket,
notify_nbp_tbl[id].pid,
notify_nbp_tbl[id].ddptype);
}
}
int
ot_ddp_check_socket(socket, pid)
unsigned char socket;
int pid;
{
extern ddp_dev_t ddp_devs[];
int cnt;
dPrintf(D_M_DDP, D_L_INFO, ("ot_ddp_check_socket: %d\n", socket));
cnt = (DDP_SOCK_UPQ(socket) == NULL) ? 0 :
( (ddp_devs[socket].pid == pid) ? 1 : 0 );
if (ot_protoT[3]) { /* ATP */
if ((atp_inputQ[socket] != NULL)
&& (atp_inputQ[socket] != (gref_t *)1)
&& (atp_pidM[socket] == pid))
cnt++;
}
if (ot_protoT[DDP_ADSP]) {
if ((adsp_inputQ[socket] != NULL) && (adsp_pidM[socket] == pid))
cnt++;
}
return(cnt);
}
/****************************************************************/
/* */
/* */
/* Support Routines */
/* */
/* */
/****************************************************************/
/*
* Name:
* ddp_checksum
*
* Description:
* This procedure determines the checksum of an extended DDP datagram.
* Add the unsigned bytes into an unsigned 16-bit accumulator.
* After each add, rotate the sign bit into the low order bit of
* the accumulator. When done, if the checksum is 0, changed into 0xFFFF.
*
* Calling sequence:
* checksum = ddp_checksum(mp, offset)
*
* Parameters:
* mp pointer to the datagram gbuf_t
* offset offset to start at in first gbuf_t block
*
* Return value:
* The DDP checksum.
*
*/
static u_short
ddp_checksum(mp, offset)
register gbuf_t *mp;
register int offset;
{
register u_char *data;
register int length;
register u_short checksum;
checksum = 0;
do {
if (offset >= gbuf_len(mp))
offset -= gbuf_len(mp);
else {
data = ((unsigned char *) gbuf_rptr(mp)) + offset;
length = gbuf_len(mp) - offset;
offset = 0;
/* Portable checksum from 3.0 */
while (length--) {
checksum += *data++;
checksum = (checksum & 0x8000) ?
((checksum << 1) | 1) : (checksum << 1);
}
}
} while ( (mp = gbuf_cont(mp)) );
if (checksum == 0)
checksum = 0xffff;
return(checksum);
}
void ddp_shutdown()
{
routershutdown();
ddp_brt_shutdown();
ddps_shutdown((gref_t *)InputQueue);
InputQueue = NULL;
dPrintf(D_M_DDP, D_L_VERBOSE, ("DDP shutdown completed"));
return;
}
/*
* Name:
* ddp_init
*
* Description:
* Initializes DDP. The streams version allocates a new queue pair to
* be used as the input queue for incoming lap datagrams. The write
* queue of this pair is not used.
*
* Return value:
*
* Priority Level:
* This function is called by the stream head open routines, or
* by the lap layer's ONLINE function via ddp_add_if().
*/
static int
ddp_init()
{
char *q;
char *ddps_init();
void ddp_brt_init();
at_socket socket;
typedef void (*proc_ptr)();
proc_ptr handler;
extern void sip_init();
dPrintf(D_M_DDP, D_L_STARTUP, ("ddp_init starting\n"));
/* If using streams, we may get called more than once */
if (InputQueue == NULL) {
bzero((char *)notify_nbp_tbl, sizeof(notify_nbp_tbl));
if ((q = ddps_init()) == NULL)
return(ENOBUFS);
else {
InputQueue = q;
/* initialize the ddp_socket table */
bzero(ddp_socket,sizeof(ddp_socket));
for (socket = DDP_SOCKET_1st_RESERVED;
socket <= DDP_SOCKET_LAST; socket++)
ddp_socket[socket].number = socket;
bzero(&at_ddp_stats, sizeof(at_ddp_stats_t));
ddp_brt_init();
/* Initialize all the protocols implemented in
* kernel
*/
handler = (proc_ptr)ep_init();
ddp_socket[EP_SOCKET].number = EP_SOCKET;
ddp_socket[EP_SOCKET].flags |= DDP_CALL_HANDLER;
DDP_SOCK_HANDLER(EP_SOCKET) = handler;
handler = (proc_ptr)rtmp_init();
ddp_socket[RTMP_SOCKET].number= RTMP_SOCKET;
ddp_socket[RTMP_SOCKET].flags|=DDP_CALL_HANDLER;
DDP_SOCK_HANDLER(RTMP_SOCKET) = handler;
handler = (proc_ptr)zip_init();
ddp_socket[ZIP_SOCKET].flags|=DDP_CALL_HANDLER;
ddp_socket[ZIP_SOCKET].number = ZIP_SOCKET;
DDP_SOCK_HANDLER(ZIP_SOCKET) = handler;
handler = (proc_ptr)nbp_init();
ddp_socket[NBP_SOCKET].number = NBP_SOCKET;
ddp_socket[NBP_SOCKET].flags |= DDP_CALL_HANDLER;
DDP_SOCK_HANDLER(NBP_SOCKET) = handler;
bzero(ifID_table, sizeof(ifID_table));
sip_init();
}
}
dPrintf(D_M_DDP, D_L_STARTUP, ("ddp_init OK\n"));
return (0);
}
/*
* ddp_add_if()
*
* Description:
* This procedure is called by each LAP interface when it wants to place
* itself online. The LAP interfaces passes in a pointer to its at_if
* struct, which is added to DDP's list of active interfaces (at_ifQueueHd).
* When DDP wants to transmit a packet, it searches this list for the
* interface to use.
*
* If AT_IFF_DEFAULT is set, then this interface is to be brought online
* as the interface DDP socket addresses are tied to. Of course there can
* be only one default interface; we return an error if it's already set.
*
* Calling Sequence:
* ret_status = ddp_add_if(ifID)
*
* Formal Parameters:
* ifID pointer to LAP interface's at_if struct.
*
* Completion Status:
* 0 Procedure successfully completed.
* EALREADY This interface is already online, or there is
* already a default interface.
* ENOBUFS Cannot allocate input queue
*
* Side Effects:
* The global at_ifDefault may be set.
*/
int ddp_add_if(ifID)
register at_if_t *ifID;
{
int status;
int port = -1;
dPrintf(D_M_DDP, D_L_STARTUP, ("ddp_add_if: called, ifID:0x%x\n", (u_int) ifID));
/* Initialize some of the ddp data structures and protocol modules
* like NBP and RTMP.
*/
if (!(at_statep->flags & AT_ST_DDP_INIT)) {
if ((status = ddp_init()) != 0) {
ifID->ddpInputQueue = NULL;
return(status);
}
at_statep->flags |= AT_ST_DDP_INIT;
}
if (ifID->ifFlags & AT_IFF_DEFAULT) {
if (ifID_table[IFID_HOME]) {
return(EEXIST); /* home port already set */
}
else {
port = 0;
ifID_table[IFID_HOME] = ifID;
}
}
else
for (port=IFID_HOME+1; port<IF_TOTAL_MAX; port++) { /* add i/f to port list */
if (!ifID_table[port]) {
ifID_table[port] = ifID;
ifID->ifPort = port; /* set ddp port # in ifID */
break;
}
}
if (port == IF_TOTAL_MAX) { /* no space left */
ifID->ddpInputQueue = NULL;
return(ENOMEM);
}
dPrintf(D_M_DDP, D_L_STARTUP, ("ddp:adding ifID_table[%d]\n", port));
/* Add this interface to the list of online interfaces */
ddp_insque((LIB_QELEM_T *)ifID, (LIB_QELEM_T *)at_ifQueueHd.FwdLink);
ifID->ddpInputQueue = InputQueue;
ATALK_ASSIGN(ifID->ifARouter, 0, 0, 0);
at_statep->ifs_online++;
return (0);
}
/*
* ddp_rem_if()
*
* Description:
* This procedure is called by each LAP interface when it wants to take
* itself offline. The LAP interfaces passes in a pointer to its at_if
* struct; DDP's list of active interfaces (at_ifQueueHd) is searched and
* this interface is removed from the list. DDP can still transmit
* packets as long as this interface is not the default interface; the
* sender will just get ENETUNREACH errors when it tries to send to an
* interface that went offline. However, if the default interface is
* taken offline, we no longer have a node ID to use as a source address
* and DDP must return ENETDOWN when a caller tries to send a packet.
*
* Calling Sequence:
* ret_status = ddp_rem_if(ifID)
*
* Formal Parameters:
* ifID pointer to LAP interface's at_if struct.
*
* Completion Status:
* 0 Procedure successfully completed.
* EALREADY This interface is already offline.
*
* Side Effects:
* The global at_ifDefault may be reset.
*/
int ddp_rem_if(ifID)
register at_if_t *ifID;
{
register at_if_t *ifQueuep;
int s;
ATDISABLE(s, ddpall_lock);
ifQueuep = at_ifQueueHd.FwdLink;
while (ifQueuep != &at_ifQueueHd) {
if (ATALK_EQUAL(ifQueuep->ifThisNode, ifID->ifThisNode)){
trackrouter_rem_if(ifID);
ifID->ifARouterTimer = 0;
ddp_remque((LIB_QELEM_T *)ifQueuep);
if (at_statep->ifs_online == 1) { /* if last interface */
ddp_shutdown();
}
ifID_table[ifID->ifPort] = NULL;
at_statep->ifs_online--;
ATENABLE(s, ddpall_lock);
return (0);
}
ifQueuep = ifQueuep->FwdLink;
}
ATENABLE(s, ddpall_lock);
return (EALREADY);
}
/*
* The user may have registered an NVE with the NBP on a socket. When the
* socket is closed, the NVE should be deleted from NBP's name table. The
* user should delete the NVE before the socket is shut down, but there
* may be circumstances when he can't. So, whenever a DDP socket is closed,
* this routine is used to notify NBP of the socket closure. This would
* help NBP get rid of all NVE's registered on the socket.
*/
void ddp_notify_nbp(socket, pid, ddptype)
unsigned char socket;
int pid;
unsigned char ddptype;
{
register int size;
register gbuf_t *mp;
register at_ddp_t *ddp;
register at_nbp_t *nbp;
void ddp_input();
int id;
/* *** do this only when the default interface is filled in? *** */
if (DDP_SOCK_UPQ(NBP_SOCKET) && ifID_table[IFID_HOME]) {
size = DDP_X_HDR_SIZE + 2; /* for NBP control fields */
if (!(mp = gbuf_alloc(size+8, PRI_HI))) {
dPrintf(D_M_DDP, D_L_ERROR, ("DDP: TROUBLE out of gbuf"));
/* *** find an empty entry? *** */
for (id = 0; id < MAX_NOTIFY_NBP; id++) {
if (notify_nbp_tbl[id].socket == 0) {
notify_nbp_tbl[id].socket = socket;
notify_nbp_tbl[id].ddptype = ddptype;
notify_nbp_tbl[id].pid = pid;
atalk_timeout(Xddp_notify_nbp, id, SYS_HZ/10);
break;
}
}
return;
}
gbuf_wset(mp, size);
ddp = (at_ddp_t *)gbuf_rptr(mp);
nbp = (at_nbp_t *)ddp->data;
nbp->control = NBP_CLOSE_NOTE;
nbp->tuple_count = nbp->at_nbp_id = 0;
((int *)gbuf_wptr(mp))[0] = (int)pid;
((int *)gbuf_wptr(mp))[1] = (int)ddptype;
ddp->type = NBP_DDP_TYPE;
ddp->unused = ddp->hopcount = 0;
DDPLEN_ASSIGN(ddp, size);
UAS_ASSIGN(ddp->checksum, 0);
NET_NET(ddp->src_net, ifID_table[IFID_HOME]->ifThisNode.atalk_net);
ddp->src_node = ifID_table[IFID_HOME]->ifThisNode.atalk_node;
ddp->src_socket = (at_socket)socket;
NET_NET(ddp->dst_net, ifID_table[IFID_HOME]->ifThisNode.atalk_net);
ddp->dst_node = ifID_table[IFID_HOME]->ifThisNode.atalk_node;
ddp->dst_socket = NBP_SOCKET;
ddp_input(mp, 0); /* ifID 0 implies loopback */
}
} /* ddp_notify_nbp */
/* This routine shrinks the ddp header from long to short,
* It also prepends ALAP header and fills up some of the
* fields as appropriate.
*/
static at_ddp_short_t *ddp_shrink_hdr (mp)
register gbuf_t *mp;
{
register at_ddp_t *ddp;
register at_ddp_short_t *ddp_short;
register at_llap_hdr_t *llap;
gbuf_t *newmp;
if ((newmp = (gbuf_t *)gbuf_copym((gbuf_t *) mp)) == (gbuf_t *)NULL)
return ((at_ddp_short_t *)NULL);
gbuf_freem(mp);
mp = newmp;
ddp = (at_ddp_t *)gbuf_rptr(mp);
gbuf_rinc(mp,((DDP_X_HDR_SIZE - DDP_HDR_SIZE) - LLAP_HDR_SIZE));
llap = (at_llap_hdr_t *)gbuf_rptr(mp);
ddp_short = (at_ddp_short_t *)(gbuf_rptr(mp) + LLAP_HDR_SIZE);
llap->destination = ddp->dst_node;
llap->type = LLAP_TYPE_DDP;
ddp_short->length = ddp->length - (DDP_X_HDR_SIZE - DDP_HDR_SIZE);
ddp_short->unused = 0;
return ((at_ddp_short_t *)mp);
}
/* mp points to message of the form {llap, short ddp, ...}.
* Get rid of llap, extend ddp header to make it of the form
* {extended ddp, ... }
*/
static gbuf_t *ddp_extend_hdr (mp)
register gbuf_t *mp;
{
register at_llap_hdr_t *llap;
register at_ddp_short_t *ddp_short;
register at_ddp_t *ddp;
char buf[DDP_HDR_SIZE + LLAP_HDR_SIZE];
gbuf_t *m1, *m2;
/* We need to remove the llap header from the packet and extend the
* short DDP header in to a long one. 5 bytes of additional space
* is required in effect, but we can not afford to put these 5 bytes
* in a separate buffer, since the ddp buffer would end up being
* fragmented into two pieces, which is a no-no. So, we first get
* rid of the llap and ddp short headers and then add the extended
* header.
*/
/* Assuming that the llap and ddp short headers are placed next
* to each other in the same buffer
*/
bcopy(gbuf_rptr(mp), buf, LLAP_HDR_SIZE + DDP_HDR_SIZE);
m1 = ddp_adjmsg(mp, LLAP_HDR_SIZE+DDP_HDR_SIZE) ? mp : 0;
/* If the message did not contain any ddp data bytes, then m would
* be NULL at this point... and we can't just grow a NULL message,
* we need to ALLOC a new one.
*/
if (m1) {
if ((m2 = (gbuf_t *)ddp_growmsg(m1, -DDP_X_HDR_SIZE)) == NULL) {
dPrintf(D_M_DDP, D_L_WARNING,
("Dropping packet - no bufs to extend hdr"));
at_ddp_stats.rcv_dropped_nobuf++;
gbuf_freem(m1);
return(NULL);
}
} else
/* Original message mp has already been freed by ddp_adjmsg if we
* managed to arrive here... this case occurs only when the
* message mp did not contain any ddp data bytes, only lap and
* ddp headers
*/
if ((m2 = gbuf_alloc(AT_WR_OFFSET+DDP_X_HDR_SIZE, PRI_MED)) == NULL) {
dPrintf(D_M_DDP,D_L_WARNING,
("Packet (no data) dropped - no bufs to extend hdr"));
at_ddp_stats.rcv_dropped_nobuf++;
return(NULL);
} else {
gbuf_rinc(m2,AT_WR_OFFSET);
gbuf_wset(m2,DDP_X_HDR_SIZE);
}
/* By the time we arrive here, m2 points to message of the form
* {Extended DDP, ... }
* mp and m1 are either non-existent or irrelevant.
*/
ddp = (at_ddp_t *)gbuf_rptr(m2);
llap = (at_llap_hdr_t *)buf;
ddp_short = (at_ddp_short_t *)(buf + LLAP_HDR_SIZE);
ddp->unused = ddp->hopcount = 0;
ddp->length = ddp_short->length + DDP_X_HDR_SIZE - DDP_HDR_SIZE;
UAS_ASSIGN(ddp->checksum, 0);
NET_NET(ddp->dst_net, ifID_table[IFID_HOME]->ifThisNode.atalk_net);
NET_NET(ddp->src_net, ifID_table[IFID_HOME]->ifThisNode.atalk_net);
ddp->src_node = llap->source;
ddp->dst_node = llap->destination;
ddp->dst_socket = ddp_short->dst_socket;
ddp->src_socket = ddp_short->src_socket;
ddp->type = ddp_short->type;
return (m2);
}
int ddp_get_cfg(cfgp, src_socket)
register at_ddp_cfg_t *cfgp;
at_socket src_socket;
{
register at_if_t *ifID = ifID_table[IFID_HOME];
dPrintf(D_M_DDP, D_L_VERBOSE, ("ddp_get_cfg() entry"));
cfgp->network_up = (ifID != NULL);
cfgp->flags = ifID? ifID->ifFlags : 0;
if (ifID) {
NET_NET(cfgp->node_addr.net, ifID->ifThisNode.atalk_net);
cfgp->node_addr.node = ifID->ifThisNode.atalk_node;
cfgp->node_addr.socket = src_socket;
if (!ROUTING_MODE && (ifID->ifRouterState == NO_ROUTER)) {
NET_ASSIGN(cfgp->router_addr.net, 0);
cfgp->router_addr.node = 0;
} else {
NET_NET(cfgp->router_addr.net, ifID->ifARouter.atalk_net);
cfgp->router_addr.node = ifID->ifARouter.atalk_node;
}
cfgp->netlo = ifID->ifThisCableStart;
cfgp->nethi = ifID->ifThisCableEnd;
}
else {
NET_ASSIGN(cfgp->node_addr.net, 64000);
cfgp->node_addr.node = 128;
NET_ASSIGN(cfgp->router_addr.net, 0);
cfgp->router_addr.node = 0;
cfgp->node_addr.socket = src_socket;
cfgp->netlo = 1;
cfgp->nethi = 64500;
}
return(0);
}
int ddp_get_stats(statsp)
at_ddp_stats_t *statsp;
{
dPrintf(D_M_DDP, D_L_VERBOSE, ("ddp_get_cfg() entry"));
bcopy(&at_ddp_stats, statsp, sizeof(at_ddp_stats));
return(0);
}
/****************************************************************/
/* */
/* */
/* Module Code */
/* */
/* */
/****************************************************************/
/*
* ddp_bind_socket()
*
* Description:
*
* Calling Sequence:
* ret_status = ddp_bind_socket(socketp)
*
* Formal Parameters:
* socketp pointer to a socket table entry
*
* Completion Status:
* 0 Procedure successfully completed.
* EINVAL Invalid well-known socket supplied.
* EACCES User is not super-user.
* ENETDOWN There are no interfaces online.
*
* Side Effects:
* NONE
*/
int
ddp_bind_socket(socketp)
register ddp_socket_t *socketp;
{
register at_socket socket;
u_char proto;
int s;
ATDISABLE(s, ddpall_lock);
if (socketp->dev == NULL)
proto = 0;
else
proto = ((ddp_dev_t *)socketp->dev)->proto;
/* Request for dynamic socket? */
if (socketp->number == 0) {
/* Search table for free one */
for (socket = DDP_SOCKET_LAST-proto;
socket >= DDP_SOCKET_1st_DYNAMIC; socket--)
if (DDP_SOCK_UPQ(socket) == 0) {
if ((proto == DDP_ATP) && atp_inputQ[socket])
continue;
else if ((proto == DDP_ADSP) && adsp_inputQ[socket])
continue;
break;
}
if (socket < DDP_SOCKET_1st_DYNAMIC)
{
ATENABLE(s, ddpall_lock);
return(EADDRNOTAVAIL); /* Error if no free sockets */
}
else
socketp->number = ddp_socket[socket].number;
} else {
/* Asking to open a socket by its number. Check if its legal &
* free.
*/
socket = socketp->number;
if (socket > DDP_SOCKET_LAST)
{
ATENABLE(s, ddpall_lock);
return(EINVAL);
}
if (DDP_SOCK_UPQ(socket) || DDP_SOCK_HANDLER(socket))
{
ATENABLE(s, ddpall_lock);
return(EISCONN);
}
}
if (proto == DDP_ATP) {
if (atp_inputQ[socket])
{
ATENABLE(s, ddpall_lock);
return(EISCONN);
}
ot_atp_socketM[socket] = 1;
} else if (proto == DDP_ADSP) {
if (adsp_inputQ[socket])
{
ATENABLE(s, ddpall_lock);
return(EISCONN);
}
ot_adsp_socketM[socket] = 1;
}
ddp_socket[socket].flags = socketp->flags;
DDP_SOCK_UPQ(socket) = socketp->sock_u.gref;
ATENABLE(s, ddpall_lock);
dPrintf(D_M_DDP, D_L_VERBOSE,
("Socket 0x%x is opened", (u_char)socket));
return(0);
}
/*
* ddp_close_socket()
*
* Description:
* This procedure closes a DDP socket.
* NBP is notified that the socket is being closed so that it can
* deregister any attached names.
*
* Calling Sequence:
* ret_status = ddp_close_socket(socketp)
*
* Formal Parameters:
* socketp pointer to a socket table entry
*
* Completion Status:
* 0 Procedure successfully completed.
* ENOTCONN Socket was not open.
*
* Side Effects:
* NONE
*/
int ddp_close_socket(socketp)
register ddp_socket_t *socketp;
{
register at_socket socket = socketp->number;
/* Make sure socket was open, if not return error */
if (!DDP_SOCK_UPQ(socket))
return (ENOTCONN);
/* Notify NBP that we are closing this DDP socket */
ddp_notify_nbp(socket,
((ddp_dev_t *)socketp->dev)->pid, 0);
/* mark socket as closed */
if (socketp->flags & DDP_CALL_HANDLER)
socketp->sock_u.handler = NULL;
else
socketp->sock_u.gref = NULL;
socketp->flags = 0;
if (((ddp_dev_t *)socketp->dev)->proto == DDP_ATP)
ot_atp_socketM[socket] = 0;
else if (((ddp_dev_t *)socketp->dev)->proto == DDP_ADSP)
ot_adsp_socketM[socket] = 0;
dPrintf(D_M_DDP, D_L_VERBOSE,
("Socket 0x%x is closed", (u_char)socket));
return (0);
}
/* There are various ways a packet may go out.... it may be sent out
* directly to destination node, or sent to a random router or sent
* to a router whose entry exists in Best Router Cache. Following are
* constants used WITHIN this routine to keep track of choice of destination
*/
#define DIRECT_ADDR 1
#define BRT_ENTRY 2
#define BRIDGE_ADDR 3
/*
* ddp_output()
*
* Remarks :
* called to queue a atp/ddp data packet on the network interface.
* It returns 0 normally, and an errno in case of error.
*
*/
int ddp_output(mp, src_socket, src_addr_included)
register gbuf_t **mp;
at_socket src_socket;
int src_addr_included;
{
register at_if_t *ifID, *ifIDTmp;
register at_ddp_t *ddp;
register ddp_brt_t *brt;
register at_net_al dst_net;
register int len;
struct atalk_addr at_dest;
at_if_t *ARouterIf = NULL;
at_ddp_short_t *ddp_short = NULL;
int loop = 0;
int error;
int addr_type;
void ddp_input();
u_char addr_flag, ddp_type;
char *addr = NULL;
register gbuf_t *m, *tmp_m;
at_ddp_t *tmp_ddp;
snmpStats.dd_outReq++;
if ((ifID = ifID_table[IFID_HOME]) == NULL)
return (ENETDOWN);
m = *mp;
ddp = (at_ddp_t *)gbuf_rptr(m);
if (MULTIHOME_MODE && (ifIDTmp = forUs(ddp))) {
ifID = ifIDTmp;
loop = TRUE;
dPrintf(D_M_DDP_LOW, D_L_USR1,
("ddp_out: for us if:%s\n", ifID->ifName));
}
if ((ddp->dst_socket > (unsigned) (DDP_SOCKET_LAST + 1)) ||
(ddp->dst_socket < DDP_SOCKET_1st_RESERVED)) {
dPrintf(D_M_DDP, D_L_ERROR,
("Illegal destination socket on outgoing packet (0x%x)",
ddp->dst_socket));
at_ddp_stats.xmit_bad_addr++;
return (ENOTSOCK);
}
if ((len = gbuf_msgsize(*mp)) > DDP_DATAGRAM_SIZE) {
/* the packet is too large */
dPrintf(D_M_DDP, D_L_ERROR,
("Outgoing packet too long (len=%d bytes)", len));
at_ddp_stats.xmit_bad_length++;
return (EMSGSIZE);
}
at_ddp_stats.xmit_bytes += len;
at_ddp_stats.xmit_packets++;
DDPLEN_ASSIGN(ddp, len);
ddp->hopcount = ddp->unused = 0;
/* If this packet is for the same node, loop it back
* up... Note that for LocalTalk, dst_net zero means "THIS_NET", so
* address 0.nn is eligible for loopback. For Extended EtherTalk,
* dst_net 0 can be used only for cable-wide or zone-wide
* broadcasts (0.ff) and as such, address of the form 0.nn is NOT
* eligible for loopback.
*/
dst_net = NET_VALUE(ddp->dst_net);
/* If our packet is destined for the 'virtual' bridge
* address of NODE==0xFE, replace that address with a
* real bridge address.
*/
if ((ddp->dst_node == 0xfe) &&
((dst_net == 0) ||
(dst_net >= ifID->ifThisCableStart &&
dst_net <= ifID->ifThisCableEnd))) {
NET_NET(ddp->dst_net, ifID->ifARouter.atalk_net);
dst_net = NET_VALUE(ifID->ifARouter.atalk_net);
ddp->dst_node = ifID->ifARouter.atalk_node;
}
loop = ((ddp->dst_node == ifID->ifThisNode.atalk_node) &&
(dst_net == NET_VALUE(ifID->ifThisNode.atalk_net))
);
if (loop) {
gbuf_t *mdata, *mdata_next;
ddp_type = ddp->type;
ddp->src_node = ifID->ifThisNode.atalk_node;
ddp->src_socket = src_socket;
NET_NET(ddp->src_net, ifID->ifThisNode.atalk_net);
dPrintf(D_M_DDP_LOW, D_L_OUTPUT,
("ddp_output: loop to %d:%d port=%d\n",
NET_VALUE(ddp->src_net),
ddp->src_node,
ifID->ifPort));
if (UAS_VALUE(ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(*mp, 4);
UAS_ASSIGN(ddp->checksum, tmp);
}
tmp_m = *mp;
for (tmp_m=gbuf_next(tmp_m); tmp_m; tmp_m=gbuf_next(tmp_m)) {
tmp_ddp = (at_ddp_t *)gbuf_rptr(tmp_m);
DDPLEN_ASSIGN(tmp_ddp, gbuf_msgsize(tmp_m));
tmp_ddp->hopcount = tmp_ddp->unused = 0;
tmp_ddp->src_node = ddp->src_node;
NET_NET(tmp_ddp->src_net, ddp->src_net);
if (UAS_VALUE(tmp_ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(tmp_m, 4);
UAS_ASSIGN(tmp_ddp->checksum, tmp);
}
}
dPrintf(D_M_DDP, D_L_VERBOSE,
("Looping back a packet from socket 0x%x to socket 0x%x",
ddp->src_socket, ddp->dst_socket));
for (mdata = *mp; mdata; mdata = mdata_next) {
mdata_next = gbuf_next(mdata);
gbuf_next(mdata) = 0;
ddp_input(mdata, ifID);
}
return (0);
}
if ((ddp->dst_socket == ZIP_SOCKET) &&
(zip_type_packet(*mp) == ZIP_GETMYZONE)) {
ddp->src_socket = src_socket;
if ((error = zip_handle_getmyzone(ifID, *mp)) != 0)
return(error);
gbuf_freem(*mp);
return (0);
}
/*
* find out the interface on which the packet should go out
*/
for (ifID = at_ifQueueHd.FwdLink; ifID != &at_ifQueueHd; ifID = ifID->FwdLink) {
if ((NET_VALUE(ifID->ifThisNode.atalk_net) == dst_net) || (dst_net == 0))
/* the message is either going out (i) on the same
* NETWORK in case of LocalTalk, or (ii) on the same
* CABLE in case of Extended AppleTalk (EtherTalk).
*/
break;
if ((ifID->ifThisCableStart <= dst_net) &&
(ifID->ifThisCableEnd >= dst_net)
)
/* We're on EtherTalk and the message is going out to
* some other network on the same cable.
*/
break;
if (ARouterIf == NULL && ATALK_VALUE(ifID->ifARouter))
ARouterIf = ifID;
}
dPrintf(D_M_DDP_LOW, D_L_USR1,
("ddp_out: after search ifid:0x%x %s ifID0:0x%x\n",
(u_int) ifID, ifID ? ifID->ifName : "",
(u_int) ifID_table[0]));
if (ifID != &at_ifQueueHd) {
/* located the interface where the packet should
* go.... the "first-hop" destination address
* must be the same as real destination address.
*/
addr_type = DIRECT_ADDR;
} else {
/* no, the destination network number does
* not match known network numbers. If we have
* heard from this network recently, BRT table
* may have address of a router we could use!
*/
if (!ROUTING_MODE && !MULTIHOME_MODE) {
BRT_LOOK (brt, dst_net);
if (brt) {
/* Bingo... BRT has an entry for this network.
* Use the link address as is.
*/
dPrintf(D_M_DDP, D_L_VERBOSE,
("Found BRT entry to send to net 0x%x", dst_net));
at_ddp_stats.xmit_BRT_used++;
addr_type = BRT_ENTRY;
ifID = brt->ifID;
} else {
/* No BRT entry available for dest network... do we
* know of any router at all??
*/
if ((ifID = ARouterIf) != NULL)
addr_type = BRIDGE_ADDR;
else {
dPrintf(D_M_DDP, D_L_WARNING,
("Found no interface to send pkt"));
at_ddp_stats.xmit_bad_addr++;
return (ENETUNREACH);
}
}
}
else { /* We are in multiport mode, so we can bypass all the rest
* and directly ask for the routing of the packet
*/
ddp->src_socket = src_socket;
at_ddp_stats.xmit_BRT_used++;
if (!MULTIPORT_MODE || ifID == &at_ifQueueHd)
ifID = ifID_table[IFID_HOME];
if (!src_addr_included) {
ddp->src_node = ifID->ifThisNode.atalk_node;
NET_NET(ddp->src_net, ifID->ifThisNode.atalk_net);
}
routing_needed(*mp, ifID, TRUE);
return (0);
}
}
/* by the time we land here, we know the interface on
* which this packet is going out.... ifID.
*/
switch (addr_type) {
case DIRECT_ADDR :
at_dest.atalk_unused = 0;
NET_ASSIGN(at_dest.atalk_net, dst_net);
at_dest.atalk_node = ddp->dst_node;
addr_flag = AT_ADDR;
addr = (char *)&at_dest;
break;
case BRT_ENTRY :
addr_flag = ET_ADDR;
addr = (char *)&brt->et_addr;
break;
case BRIDGE_ADDR :
at_dest = ifID->ifARouter;
addr_flag = AT_ADDR;
addr = (char *)&at_dest;
break;
}
/* Irrespective of the interface on which
* the packet is going out, we always put the
* same source address on the packet (unless multihoming mode).
*/
if (MULTIPORT_MODE) {
if (!src_addr_included) {
ddp->src_node = ifID->ifThisNode.atalk_node;
NET_NET(ddp->src_net, ifID->ifThisNode.atalk_net);
}
}
else {
ddp->src_node = ifID_table[IFID_HOME]->ifThisNode.atalk_node;
NET_NET(ddp->src_net, ifID_table[IFID_HOME]->ifThisNode.atalk_net);
}
ddp->src_socket = src_socket;
if (UAS_VALUE(ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(*mp, 4);
UAS_ASSIGN(ddp->checksum, tmp);
}
dPrintf(D_M_DDP, D_L_VERBOSE,
("Packet going out to : net 0x%x, node 0x%x, socket 0x%x on %s",
(ddp_short ? 0 : dst_net),
(ddp_short ? 0 : ddp->dst_node),
(ddp_short ? ddp_short->dst_socket : ddp->dst_socket),ifID->ifName));
dPrintf(D_M_DDP_LOW, D_L_OUTPUT,
("ddp_output: going out to %d:%d skt%d\n",
dst_net, ddp->dst_node, ddp->dst_socket));
if (ifID->ifState != LAP_OFFLINE) {
tmp_m = *mp;
for (tmp_m=gbuf_next(tmp_m); tmp_m; tmp_m=gbuf_next(tmp_m)) {
tmp_ddp = (at_ddp_t *)gbuf_rptr(tmp_m);
DDPLEN_ASSIGN(tmp_ddp, gbuf_msgsize(tmp_m));
tmp_ddp->hopcount = tmp_ddp->unused = 0;
tmp_ddp->src_node = ddp->src_node;
NET_NET(tmp_ddp->src_net, ddp->src_net);
tmp_ddp->dst_node = ddp->dst_node;
NET_NET(tmp_ddp->dst_net, ddp->dst_net);
if (UAS_VALUE(tmp_ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(tmp_m, 4);
UAS_ASSIGN(tmp_ddp->checksum, tmp);
}
}
{
struct etalk_addr dest_addr;
struct atalk_addr dest_at_addr;
int loop = TRUE; /* flag to aarp to loopback (default) */
elap_specifics_t *elapp = (elap_specifics_t *)ifID->ifLapp;
m = *mp;
/* the incoming frame is of the form {flag, address, ddp...}
* where "flag" indicates whether the address is an 802.3
* (link) address, or an appletalk address. If it's an
* 802.3 address, the packet can just go out to the network
* through PAT, if it's an appletalk address, AT->802.3 address
* resolution needs to be done.
* If 802.3 address is known, strip off the flag and 802.3
* address, and prepend 802.2 and 802.3 headers.
*/
if (addr == NULL) {
addr_flag = *(u_char *)gbuf_rptr(m);
gbuf_rinc(m,1);
}
switch (addr_flag) {
case AT_ADDR_NO_LOOP :
loop = FALSE;
/* pass thru */
case AT_ADDR :
if (addr == NULL) {
dest_at_addr = *(struct atalk_addr *)gbuf_rptr(m);
gbuf_rinc(m,sizeof(struct atalk_addr));
} else
dest_at_addr = *(struct atalk_addr *)addr;
break;
case ET_ADDR :
if (addr == NULL) {
dest_addr = *(struct etalk_addr *)gbuf_rptr(m);
gbuf_rinc(m,sizeof(struct etalk_addr));
} else
dest_addr = *(struct etalk_addr *)addr;
break;
default :
dPrintf(D_M_DDP_LOW,D_L_ERROR,
("ddp_output: Unknown addr_flag = 0x%x\n", addr_flag));
gbuf_freel(m); /* unknown address type, chuck it */
return 0;
}
while (gbuf_len(m) == 0) {
tmp_m = m;
m = gbuf_cont(m);
gbuf_freeb(tmp_m);
}
/* At this point, rptr points to ddp header for sure */
if (elapp->elap_if.ifState == LAP_OFFLINE) {
gbuf_freel(m);
return 0;
}
if (elapp->elap_if.ifState == LAP_ONLINE_FOR_ZIP) {
/* see if this is a ZIP packet that we need
* to let through even though network is
* not yet alive!!
*/
if (zip_type_packet(m) == 0) {
gbuf_freel(m);
return 0;
}
}
elapp->stats.xmit_packets++;
elapp->stats.xmit_bytes += gbuf_msgsize(m);
snmpStats.dd_outLong++;
switch (addr_flag) {
case AT_ADDR_NO_LOOP :
case AT_ADDR :
/*
* we don't want elap to be looking into ddp header, so
* it doesn't know net#, consequently can't do
* AMT_LOOKUP. That task left to aarp now.
*/
aarp_send_data(m,elapp,&dest_at_addr, loop);
break;
case ET_ADDR :
pat_output(elapp->pat_id, m, &dest_addr, 0);
break;
}
}
return(0);
}
gbuf_freel(*mp);
return (0);
} /* ddp_output */
void ddp_input(mp, ifID)
register gbuf_t *mp;
register at_if_t *ifID;
{
register at_ddp_t *ddp;
register int msgsize;
register at_socket socket;
register int len;
register at_net_al dst_net;
extern volatile RoutingMix;
KERNEL_DEBUG(DBG_AT_DDP_INPUT | DBG_FUNC_START, 0,0,0,0,0);
/* Makes sure we know the default interface before starting to
* accept incomming packets. If we don't we may end up with a
* null ifID_table[0] and have impredicable results (specially
* in router mode. This is a transitory state (because we can
* begin to receive packet while we're not completly set up yet.
*/
if (ifID_table[IFID_HOME] == (at_if_t *)NULL) {
dPrintf(D_M_DDP, D_L_ERROR,
("dropped incoming packet ifID_home not set yet\n"));
gbuf_freem(mp);
goto out; /* return */
}
/*
* if a DDP packet has been broadcast, we're going to get a copy of
* it here; if it originated at user level via a write on a DDP
* socket; when it gets here, the first block in the chain will be
* empty since it only contained the lap level header which will be
* stripped in the lap level immediately below ddp
*/
if ((mp = (gbuf_t *)ddp_compress_msg(mp)) == NULL) {
dPrintf(D_M_DDP, D_L_ERROR,
("dropped short incoming ET packet (len %d)", 0));
snmpStats.dd_inTotal++;
at_ddp_stats.rcv_bad_length++;
goto out; /* return; */
}
msgsize = gbuf_msgsize(mp);
at_ddp_stats.rcv_bytes += msgsize;
at_ddp_stats.rcv_packets++;
/* if the interface pointer is 0, the packet has been
* looped back by 'write' half of DDP. It is of the
* form {extended ddp,...}. The packet is meant to go
* up to some socket on the same node.
*/
if (ifID == (at_if_t *)NULL) /* if loop back is specified */
ifID = ifID_table[IFID_HOME]; /* that means the home port */
/* the incoming datagram has extended DDP header and is of
* the form {ddp,...}.
*/
if (msgsize < DDP_X_HDR_SIZE) {
dPrintf(D_M_DDP, D_L_ERROR,
("dropped short incoming ET packet (len %d)",
msgsize));
at_ddp_stats.rcv_bad_length++;
gbuf_freem(mp);
goto out; /* return; */
}
/*
* At this point, the message is always of the form
* {extended ddp, ... }.
*/
ddp = (at_ddp_t *)gbuf_rptr(mp);
len = DDPLEN_VALUE(ddp);
if (msgsize != len) {
if ((unsigned) msgsize > len) {
if (len < DDP_X_HDR_SIZE) {
dPrintf(D_M_DDP, D_L_ERROR,
("Length problems, ddp length %d, buffer length %d",
len, msgsize));
snmpStats.dd_tooLong++;
at_ddp_stats.rcv_bad_length++;
gbuf_freem(mp);
goto out; /* return; */
}
/*
* shave off the extra bytes from the end of message
*/
mp = ddp_adjmsg(mp, -(msgsize - len)) ? mp : 0;
if (mp == 0)
goto out; /* return; */
} else {
dPrintf(D_M_DDP, D_L_ERROR,
("Length problems, ddp length %d, buffer length %d",
len, msgsize));
snmpStats.dd_tooShort++;
at_ddp_stats.rcv_bad_length++;
gbuf_freem(mp);
goto out; /* return; */
}
}
socket = ddp->dst_socket;
/*
* We want everything in router mode, specially socket 254 for nbp so we need
* to bypass this test when we are a router.
*/
if (!ROUTING_MODE && (socket > DDP_SOCKET_LAST ||
socket < DDP_SOCKET_1st_RESERVED)) {
dPrintf(D_M_DDP, D_L_WARNING,
("Bad dst socket on incoming packet (0x%x)",
ddp->dst_socket));
at_ddp_stats.rcv_bad_socket++;
gbuf_freem(mp);
goto out; /* return; */
}
/*
* if the checksum is true, then upstream wants us to calc
*/
if (UAS_VALUE(ddp->checksum) &&
(UAS_VALUE(ddp->checksum) != ddp_checksum(mp, 4))) {
dPrintf(D_M_DDP, D_L_WARNING,
("Checksum error on incoming pkt, calc 0x%x, exp 0x%x",
ddp_checksum(mp, 4), UAS_VALUE(ddp->checksum)));
snmpStats.dd_checkSum++;
at_ddp_stats.rcv_bad_checksum++;
gbuf_freem(mp);
goto out; /* return; */
}
/*############### routing input checking */
/* Router mode special: we send "up-stack" packets for this node or coming from any
* other ports, but for the reserved atalk sockets (RTMP, ZIP, NBP [and EP])
* BTW, the way we know it's for the router and not the home port is that the
* MAC (ethernet) address is always the one of the interface we're on, but
* the AppleTalk address must be the one of the home port. If it's a multicast
* or another AppleTalk address, this is the router job's to figure out where it's
* going to go.
*/
dst_net = NET_VALUE(ddp->dst_net);
if (((ddp->dst_node == ifID_table[IFID_HOME]->ifThisNode.atalk_node) &&
(dst_net == NET_VALUE(ifID_table[IFID_HOME]->ifThisNode.atalk_net))) ||
((ddp->dst_node == 255) &&
(((dst_net >= ifID_table[IFID_HOME]->ifThisCableStart) &&
(dst_net <= ifID_table[IFID_HOME]->ifThisCableEnd)) || dst_net == 0)) ||
(socket == RTMP_SOCKET) || (socket == NBP_SOCKET) || (socket == EP_SOCKET) ||
(socket == ZIP_SOCKET) || (ifID->ifRoutingState < PORT_ONLINE) ||
( MULTIPORT_MODE && forUs(ddp))
)
{
gref_t *gref;
extern ddp_dev_t ddp_devs[];
pktsHome++;
snmpStats.dd_inLocal++;
if (ot_protoT[ddp->type]) {
if (ddp->type == DDP_ATP) {
if ((socket == ZIP_SOCKET) || (atp_inputQ[socket] == NULL)
|| (atp_inputQ[socket] == (gref_t *)1))
goto l_continue;
atp_input(mp);
} else if (ddp->type == DDP_ADSP) {
if (adsp_inputQ[socket] == NULL)
goto l_continue;
adsp_input(mp);
}
goto out; /* return; */
}
/*
* Assure that this isn't for a shut-down socket
*/
l_continue:
if ((ddp_devs[socket].flags&DDPF_SHUTDOWN) == 0 &&
(gref = DDP_SOCK_UPQ(socket)) != NULL) {
/* there is an upstream! either a queue or a handler routine */
if (ddp_socket[socket].flags & DDP_CALL_HANDLER) {
dPrintf(D_M_DDP,D_L_INPUT,
("ddp_input: skt hndlr skt %d hdnlr:0x%x\n",
(u_int) socket,
DDP_SOCK_HANDLER(socket)));
(*DDP_SOCK_HANDLER(socket))(mp, ifID);
} else {
dPrintf(D_M_DDP, D_L_INPUT,
("ddp_input: streamq, skt %d\n", socket));
if (DDP_FLAGS(gref) & DDPF_SI) {
if (si_ddp_input(gref, mp)) {
at_ddp_stats.rcv_dropped_nobuf++;
}
} else
atalk_putnext(gref, mp);
}
} else {
dPrintf(D_M_DDP, D_L_VERBOSE,
("Dropping a packet -- dest socket closed"));
at_ddp_stats.rcv_bad_socket++;
gbuf_freem(mp);
snmpStats.dd_noHandler++;
dPrintf(D_M_DDP, D_L_WARNING,
("ddp_input: dropped, hndlr socket %d unknown\n", socket));
}
}
else {
dPrintf(D_M_DDP, D_L_ROUTING, ("ddp_input: routing_needed from port=%d sock=%d\n",
ifID->ifPort, ddp->dst_socket));
snmpStats.dd_fwdReq++;
if (((pktsIn-pktsHome+200) >= RoutingMix) && ((++pktsDropped % 5) == 0)) {
at_ddp_stats.rcv_dropped_nobuf++;
gbuf_freem(mp);
}
else {
routing_needed(mp, ifID, FALSE);
}
}
out:
KERNEL_DEBUG(DBG_AT_DDP_INPUT | DBG_FUNC_END, 0,0,0,0,0);
}
/*
* ddp_router_output()
*
* Remarks :
* This is a modified version of ddp_output for router use.
* The main difference is that the interface on which the packet needs
* to be sent is specified and a *destination* AppleTalk address is passed
* as an argument, this address may or may not be the same as the destination
* address found in the ddp packet... This is the trick about routing, the
* AppleTalk destination of the packet may not be the same as the Enet address
* we send the packet too (ie, we may pass the baby to another router).
*
*/
int ddp_router_output(mp, ifID, addr_type, router_net, router_node, enet_addr)
gbuf_t *mp;
at_if_t *ifID;
int addr_type;
at_net_al router_net;
at_node router_node;
etalk_addr_t *enet_addr;
{
register at_ddp_t *ddp;
struct atalk_addr at_dest;
void ddp_input();
int addr_flag;
char *addr = NULL;
register gbuf_t *m, *tmp_m;
at_ddp_t *tmp_ddp;
if (!ifID || ! IFID_VALID(ifID)) {
dPrintf(D_M_DDP, D_L_WARNING, ("BAD BAD ifID\n"));
gbuf_freel(mp);
return(EPROTOTYPE);
}
ddp = (at_ddp_t *)gbuf_rptr(mp);
if (ifID->ifFlags & AT_IFF_AURP) { /* AURP link? */
if (ddp_AURPsendx) {
for (tmp_m=gbuf_next(mp); tmp_m; tmp_m=gbuf_next(tmp_m)) {
tmp_ddp = (at_ddp_t *)gbuf_rptr(tmp_m);
DDPLEN_ASSIGN(tmp_ddp, gbuf_msgsize(tmp_m));
tmp_ddp->hopcount = tmp_ddp->unused = 0;
tmp_ddp->src_node = ddp->src_node;
NET_NET(tmp_ddp->src_net, ddp->src_net);
tmp_ddp->dst_node = ddp->dst_node;
NET_NET(tmp_ddp->dst_net, ddp->dst_net);
if (UAS_VALUE(tmp_ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(tmp_m, 4);
UAS_ASSIGN(tmp_ddp->checksum, tmp);
}
}
if (router_node == 255)
router_node = 0;
ddp_AURPsendx(AURPCODE_DATAPKT, mp, router_node);
return 0;
} else {
gbuf_freel(mp);
return EPROTOTYPE;
}
}
/* keep some of the tests for now ####### */
if (gbuf_msgsize(mp) > DDP_DATAGRAM_SIZE) {
/* the packet is too large */
dPrintf(D_M_DDP, D_L_WARNING,
("ddp_router_output: Packet too large size=%d\n",
gbuf_msgsize(mp)));
gbuf_freel(mp);
return (EMSGSIZE);
}
switch (addr_type) {
case AT_ADDR :
/*
* Check for packet destined to the home stack
*/
if ((ddp->dst_node == ifID->ifThisNode.atalk_node) &&
(NET_VALUE(ddp->dst_net) == NET_VALUE(ifID->ifThisNode.atalk_net))) {
dPrintf(D_M_DDP_LOW, D_L_ROUTING,
("ddp_r_output: sending back home from port=%d socket=%d\n",
ifID->ifPort, ddp->dst_socket));
UAS_ASSIGN(ddp->checksum, 0);
ddp_input(mp, ifID);
return(0);
}
NET_ASSIGN(at_dest.atalk_net, router_net);
at_dest.atalk_node = router_node;
addr_flag = AT_ADDR_NO_LOOP;
addr = (char *)&at_dest;
dPrintf(D_M_DDP_LOW, D_L_ROUTING_AT,
("ddp_r_output: AT_ADDR out port=%d net %d:%d via rte %d:%d",
ifID->ifPort, NET_VALUE(ddp->dst_net), ddp->dst_node, router_net,
router_node));
break;
case ET_ADDR :
addr_flag = ET_ADDR;
addr = (char *)enet_addr;
dPrintf(D_M_DDP_LOW, D_L_ROUTING,
("ddp_r_output: ET_ADDR out port=%d net %d:%d\n",
ifID->ifPort, NET_VALUE(ddp->dst_net), ddp->dst_node));
break;
}
if (UAS_VALUE(ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(mp, 4);
UAS_ASSIGN(ddp->checksum, tmp);
}
if (ifID->ifState != LAP_OFFLINE) {
for (tmp_m=gbuf_next(mp); tmp_m; tmp_m=gbuf_next(tmp_m)) {
tmp_ddp = (at_ddp_t *)gbuf_rptr(tmp_m);
DDPLEN_ASSIGN(tmp_ddp, gbuf_msgsize(tmp_m));
tmp_ddp->hopcount = tmp_ddp->unused = 0;
tmp_ddp->src_node = ddp->src_node;
NET_NET(tmp_ddp->src_net, ddp->src_net);
tmp_ddp->dst_node = ddp->dst_node;
NET_NET(tmp_ddp->dst_net, ddp->dst_net);
if (UAS_VALUE(tmp_ddp->checksum)) {
u_short tmp;
tmp = ddp_checksum(tmp_m, 4);
UAS_ASSIGN(tmp_ddp->checksum, tmp);
}
}
{
struct etalk_addr dest_addr;
struct atalk_addr dest_at_addr;
int loop = TRUE; /* flag to aarp to loopback (default) */
elap_specifics_t *elapp = (elap_specifics_t *)ifID->ifLapp;
m = mp;
/* the incoming frame is of the form {flag, address, ddp...}
* where "flag" indicates whether the address is an 802.3
* (link) address, or an appletalk address. If it's an
* 802.3 address, the packet can just go out to the network
* through PAT, if it's an appletalk address, AT->802.3 address
* resolution needs to be done.
* If 802.3 address is known, strip off the flag and 802.3
* address, and prepend 802.2 and 802.3 headers.
*/
if (addr == NULL) {
addr_flag = *(u_char *)gbuf_rptr(m);
gbuf_rinc(m,1);
}
switch (addr_flag) {
case AT_ADDR_NO_LOOP :
loop = FALSE;
/* pass thru */
case AT_ADDR :
if (addr == NULL) {
dest_at_addr = *(struct atalk_addr *)gbuf_rptr(m);
gbuf_rinc(m,sizeof(struct atalk_addr));
} else
dest_at_addr = *(struct atalk_addr *)addr;
break;
case ET_ADDR :
if (addr == NULL) {
dest_addr = *(struct etalk_addr *)gbuf_rptr(m);
gbuf_rinc(m,sizeof(struct etalk_addr));
} else
dest_addr = *(struct etalk_addr *)addr;
break;
default :
dPrintf(D_M_DDP_LOW,D_L_ERROR,
("ddp_output: Unknown addr_flag = 0x%x\n", addr_flag));
gbuf_freel(m); /* unknown address type, chuck it */
return 0;
}
while (gbuf_len(m) == 0) {
tmp_m = m;
m = gbuf_cont(m);
gbuf_freeb(tmp_m);
}
/* At this point, rptr points to ddp header for sure */
if (elapp->elap_if.ifState == LAP_OFFLINE) {
gbuf_freel(m);
return 0;
}
if (elapp->elap_if.ifState == LAP_ONLINE_FOR_ZIP) {
/* see if this is a ZIP packet that we need
* to let through even though network is
* not yet alive!!
*/
if (zip_type_packet(m) == 0) {
gbuf_freel(m);
return 0;
}
}
elapp->stats.xmit_packets++;
elapp->stats.xmit_bytes += gbuf_msgsize(m);
snmpStats.dd_outLong++;
switch (addr_flag) {
case AT_ADDR_NO_LOOP :
case AT_ADDR :
/*
* we don't want elap to be looking into ddp header, so
* it doesn't know net#, consequently can't do
* AMT_LOOKUP. That task left to aarp now.
*/
aarp_send_data(m,elapp,&dest_at_addr, loop);
break;
case ET_ADDR :
pat_output(elapp->pat_id, m, &dest_addr, 0);
break;
}
}
return(0);
}
gbuf_freel(mp);
return (0);
}
int gbuf_freel(m)
gbuf_t *m;
{
gbuf_t *tmp_m;
while ((tmp_m = m) != 0) {
m = gbuf_next(m);
gbuf_next(tmp_m) = 0;
gbuf_freem(tmp_m);
}
return (0);
}
/*****************************************/
void rt_delete(NetStop, NetStart)
unsigned short NetStop;
unsigned short NetStart;
{
RT_entry *found;
int s;
ATDISABLE(s, ddpinp_lock);
if ((found = rt_bdelete(NetStop, NetStart)) != 0) {
bzero(found, sizeof(RT_entry));
found->right = RT_table_freelist;
RT_table_freelist = found;
}
ATENABLE(s, ddpinp_lock);
}
int ddp_AURPfuncx(code, param, node)
int code;
void *param;
unsigned char node;
{
extern void zt_add_zonename();
extern void zt_set_zmap();
extern int zt_ent_zindex();
extern void zt_remove_zones();
extern void rtmp_timeout();
extern void rtmp_send_port();
extern elap_specifics_t elap_specifics[];
aurp_rtinfo_t rtinfo;
elap_specifics_t *elapp;
int k;
switch (code) {
case AURPCODE_DATAPKT: /* data packet */
if (aurp_ifID) {
dPrintf(D_M_DDP, D_L_TRACE, ("ddp_AURPfuncx: data, 0x%x, %d\n",
(u_int) aurp_ifID, node));
ddp_input((gbuf_t *)param, aurp_ifID);
} else
gbuf_freem((gbuf_t *)param);
break;
case AURPCODE_REG: /* register/deregister */
if (!ROUTING_MODE)
return -1;
ddp_AURPsendx = (void(*)())param;
if (param) {
/* register AURP callback function */
if (aurp_ifID)
return 0;
elapp = (elap_specifics_t *)&elap_specifics[1];
for (k=1; k < IF_TOTAL_MAX; k++, elapp++) {
if (elapp->pat_id == 0) {
/* assign a port */
elapp->pat_id = k;
aurp_ifID = (at_if_t *)&elapp->elap_if;
aurp_ifID->ifFlags = (AT_IFF_ETHERTALK|RTR_XNET_PORT);
ddp_add_if(aurp_ifID);
aurp_ifID->ifLapp = (void *)elapp;
aurp_ifID->ifState = LAP_ONLINE;
aurp_ifID->ifRoutingState = PORT_ONLINE;
dPrintf(D_M_DDP, D_L_TRACE,
("ddp_AURPfuncx: on, 0x%x\n",
(u_int) aurp_ifID));
ddp_AURPsendx(AURPCODE_DEBUGINFO,
&dbgBits, aurp_ifID->ifPort);
return 0;
}
}
return -1;
} else {
/* deregister AURP callback function */
if (aurp_ifID) {
rtmp_purge(aurp_ifID);
elapp = (elap_specifics_t *)aurp_ifID->ifLapp;
ddp_rem_if(aurp_ifID);
elapp->pat_id = 0;
aurp_ifID->ifLapp = (void *)0;
aurp_ifID->ifState = LAP_OFFLINE;
aurp_ifID->ifRoutingState = PORT_OFFLINE;
dPrintf(D_M_DDP, D_L_TRACE,
("ddp_AURPfuncx: off, 0x%x\n", (u_int) aurp_ifID));
aurp_ifID = 0;
}
}
break;
case AURPCODE_AURPPROTO: /* proto type - AURP */
if (aurp_ifID) {
aurp_ifID->ifFlags |= AT_IFF_AURP;
rtinfo.RT_table = (void *)RT_table;
rtinfo.ZT_table = (void *)ZT_table;
rtinfo.RT_maxentry = RT_MAXENTRY;
rtinfo.ZT_maxentry = ZT_MAXENTRY;
rtinfo.rt_lock = (void *)&ddpinp_lock;
rtinfo.rt_insert = (void *)rt_insert;
rtinfo.rt_delete = (void *)rt_delete;
rtinfo.rt_lookup = (void *)rt_blookup;
rtinfo.zt_add_zname = (void *)zt_add_zonename;
rtinfo.zt_set_zmap = (void *)zt_set_zmap;
rtinfo.zt_get_zindex = (void *)zt_ent_zindex;
rtinfo.zt_remove_zones = (void *)zt_remove_zones;
ddp_AURPsendx(AURPCODE_RTINFO, &rtinfo, 0);
}
break;
}
return 0;
}
at_if_t *forUs(ddp)
register at_ddp_t *ddp;
/* checks to see if address of packet is for one of our interfaces
returns *ifID if it's for us, NULL if not
*/
{
register at_if_t **ifID = &ifID_table[0];
int port;
for (port=0; *ifID && port<IF_TOTAL_MAX; ifID++,port++) {
if ((ddp->dst_node == (*ifID)->ifThisNode.atalk_node) &&
(NET_EQUAL(ddp->dst_net, (*ifID)->ifThisNode.atalk_net))
) {
dPrintf(D_M_DDP_LOW, D_L_ROUTING,
("pkt was for port %d\n", port));
return(*ifID);
}
}
return((at_if_t *)NULL);
}