freebsd-dev/sys/netccitt/if_x25subr.c
Julian Elischer cc6a66f20e Reviewed by: julian and jhay@mikom.csir.co.za
Submitted by:	Mike Mitchell, supervisor@alb.asctmd.com

This is a bulk mport of Mike's IPX/SPX protocol stacks and all the
related gunf that goes with it..
it is not guaranteed to work 100% correctly at this time
but as we had several people trying to work on it
I figured it would be better to get it checked in so
they could all get teh same thing to work on..

Mikes been using it for a year or so
but on 2.0

more changes and stuff will be merged in from other developers now that this is in.

Mike Mitchell, Network Engineer
AMTECH Systems Corporation, Technology and Manufacturing
8600 Jefferson Street, Albuquerque, New Mexico 87113 (505) 856-8000
supervisor@alb.asctmd.com
1995-10-26 20:31:59 +00:00

824 lines
20 KiB
C

/*
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
*
* @(#)if_x25subr.c 8.1 (Berkeley) 6/10/93
* $Id: if_x25subr.c,v 1.7 1995/07/29 11:41:21 bde Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <machine/mtpr.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/route.h>
#include <netccitt/x25.h>
#include <netccitt/x25err.h>
#include <netccitt/pk.h>
#include <netccitt/pk_var.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#endif
#ifdef IPX
#include <netipx/ipx.h>
#include <netipx/ipx_if.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#ifdef ISO
int tp_incoming();
#include <netiso/argo_debug.h>
#include <netiso/iso.h>
#include <netiso/iso_var.h>
#endif
struct llinfo_x25 llinfo_x25 = {&llinfo_x25, &llinfo_x25};
#ifndef _offsetof
#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
#endif
struct sockaddr *x25_dgram_sockmask;
struct sockaddr_x25 x25_dgmask = {
_offsetof(struct sockaddr_x25, x25_udata[1]), /* _len */
0, /* _family */
0, /* _net */
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* _addr */
{0}, /* opts */
-1, /* _udlen */
{-1} /* _udata */
};
struct if_x25stats {
int ifx_wrongplen;
int ifx_nophdr;
} if_x25stats;
int x25_autoconnect = 0;
#define senderr(x) {error = x; goto bad;}
/*
* Ancillary routines
*/
static struct llinfo_x25 *
x25_lxalloc(rt)
register struct rtentry *rt;
{
register struct llinfo_x25 *lx;
register struct sockaddr *dst = rt_key(rt);
register struct ifaddr *ifa;
MALLOC(lx, struct llinfo_x25 *, sizeof (*lx), M_PCB, M_NOWAIT);
if (lx == 0)
return lx;
Bzero(lx, sizeof(*lx));
lx->lx_rt = rt;
lx->lx_family = dst->sa_family;
rt->rt_refcnt++;
if (rt->rt_llinfo)
insque(lx, (struct llinfo_x25 *)rt->rt_llinfo);
else {
rt->rt_llinfo = (caddr_t)lx;
insque(lx, &llinfo_x25);
}
for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family == AF_CCITT)
lx->lx_ia = (struct x25_ifaddr *)ifa;
}
return lx;
}
void
x25_lxfree(lx)
register struct llinfo_x25 *lx;
{
register struct rtentry *rt = lx->lx_rt;
register struct pklcd *lcp = lx->lx_lcd;
if (lcp) {
lcp->lcd_upper = 0;
pk_disconnect(lcp);
}
if ((rt->rt_llinfo == (caddr_t)lx) && (lx->lx_next->lx_rt == rt))
rt->rt_llinfo = (caddr_t)lx->lx_next;
else
rt->rt_llinfo = 0;
RTFREE(rt);
remque(lx);
FREE(lx, M_PCB);
}
/*
* Process a x25 packet as datagram;
*/
void
x25_ifinput(lcp, m)
struct pklcd *lcp;
register struct mbuf *m;
{
struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext;
register struct ifnet *ifp;
struct ifqueue *inq;
extern struct timeval time;
int s, len, isr;
void x25_connect_callback();
if (m == 0 || lcp->lcd_state != DATA_TRANSFER) {
x25_connect_callback(lcp, 0);
return;
}
pk_flowcontrol(lcp, 0, 1); /* Generate RR */
ifp = m->m_pkthdr.rcvif;
ifp->if_lastchange = time;
switch (m->m_type) {
default:
if (m)
m_freem(m);
return;
case MT_DATA:
/* FALLTHROUGH */;
}
switch (lx->lx_family) {
#ifdef INET
case AF_INET:
isr = NETISR_IP;
inq = &ipintrq;
break;
#endif
#ifdef IPX
case AF_IPX:
isr = NETISR_IPX;
inq = &ipxintrq;
break;
#endif
#ifdef NS
case AF_NS:
isr = NETISR_NS;
inq = &nsintrq;
break;
#endif
#ifdef ISO
case AF_ISO:
isr = NETISR_ISO;
inq = &clnlintrq;
break;
#endif
default:
m_freem(m);
ifp->if_noproto++;
return;
}
s = splimp();
schednetisr(isr);
if (IF_QFULL(inq)) {
IF_DROP(inq);
m_freem(m);
} else {
IF_ENQUEUE(inq, m);
ifp->if_ibytes += m->m_pkthdr.len;
}
splx(s);
}
void
x25_connect_callback(lcp, m)
register struct pklcd *lcp;
register struct mbuf *m;
{
register struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext;
int do_clear = 1;
if (m == 0)
goto refused;
if (m->m_type != MT_CONTROL) {
printf("x25_connect_callback: should panic\n");
goto refused;
}
switch (pk_decode(mtod(m, struct x25_packet *))) {
case CALL_ACCEPTED:
lcp->lcd_upper = x25_ifinput;
if (lcp->lcd_sb.sb_mb)
lcp->lcd_send(lcp); /* XXX start queued packets */
return;
default:
do_clear = 0;
refused:
lcp->lcd_upper = 0;
lx->lx_lcd = 0;
if (do_clear)
pk_disconnect(lcp);
return;
}
}
#define SA(p) ((struct sockaddr *)(p))
#define RT(p) ((struct rtentry *)(p))
void
x25_dgram_incoming(lcp, m0)
register struct pklcd *lcp;
struct mbuf *m0;
{
register struct rtentry *rt, *nrt;
register struct mbuf *m = m0->m_next; /* m0 has calling sockaddr_x25 */
void x25_rtrequest();
rt = rtalloc1(SA(&lcp->lcd_faddr), 0, 0UL);
if (rt == 0) {
refuse: lcp->lcd_upper = 0;
pk_close(lcp);
return;
}
rt->rt_refcnt--;
if ((nrt = RT(rt->rt_llinfo)) == 0 || rt_mask(rt) != x25_dgram_sockmask)
goto refuse;
if ((nrt->rt_flags & RTF_UP) == 0) {
rt->rt_llinfo = (caddr_t)rtalloc1(rt->rt_gateway, 0, 0UL);
rtfree(nrt);
if ((nrt = RT(rt->rt_llinfo)) == 0)
goto refuse;
nrt->rt_refcnt--;
}
if (nrt->rt_ifa == 0 || nrt->rt_ifa->ifa_rtrequest != x25_rtrequest)
goto refuse;
lcp->lcd_send(lcp); /* confirm call */
x25_rtattach(lcp, nrt);
m_freem(m);
}
/*
* X.25 output routine.
*/
int
x25_ifoutput(ifp, m0, dst, rt)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
register struct rtentry *rt;
{
register struct mbuf *m = m0;
register struct llinfo_x25 *lx;
struct pklcd *lcp;
int s, error = 0;
int plen;
for (plen = 0; m; m = m->m_next)
plen += m->m_len;
m = m0;
if ((ifp->if_flags & IFF_UP) == 0)
senderr(ENETDOWN);
while (rt == 0 || (rt->rt_flags & RTF_GATEWAY)) {
if (rt) {
if (rt->rt_llinfo) {
rt = (struct rtentry *)rt->rt_llinfo;
continue;
}
dst = rt->rt_gateway;
}
if ((rt = rtalloc1(dst, 1, 0UL)) == 0)
senderr(EHOSTUNREACH);
rt->rt_refcnt--;
}
/*
* Sanity checks.
*/
if ((rt->rt_ifp != ifp) ||
(rt->rt_flags & (RTF_CLONING | RTF_GATEWAY)) ||
((lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)) {
senderr(ENETUNREACH);
}
if ((m->m_flags & M_PKTHDR) == 0) {
if_x25stats.ifx_nophdr++;
m = m_gethdr(M_NOWAIT, MT_HEADER);
if (m == 0)
senderr(ENOBUFS);
m->m_pkthdr.len = plen;
m->m_next = m0;
}
if (plen != m->m_pkthdr.len) {
if_x25stats.ifx_wrongplen++;
m->m_pkthdr.len = plen;
}
next_circuit:
lcp = lx->lx_lcd;
if (lcp == 0) {
lx->lx_lcd = lcp = pk_attach((struct socket *)0);
if (lcp == 0)
senderr(ENOBUFS);
lcp->lcd_upper = x25_connect_callback;
lcp->lcd_upnext = (caddr_t)lx;
lcp->lcd_packetsize = lx->lx_ia->ia_xc.xc_psize;
lcp->lcd_flags = X25_MBS_HOLD;
}
switch (lcp->lcd_state) {
case READY:
if (dst->sa_family == AF_INET &&
ifp->if_type == IFT_X25DDN &&
rt->rt_gateway->sa_family != AF_CCITT)
x25_ddnip_to_ccitt(dst, rt);
if (rt->rt_gateway->sa_family != AF_CCITT) {
if ((rt->rt_flags & RTF_XRESOLVE) == 0)
senderr(EHOSTUNREACH);
} else if (x25_autoconnect)
error = pk_connect(lcp,
(struct sockaddr_x25 *)rt->rt_gateway);
if (error)
senderr(error);
/* FALLTHROUGH */
case SENT_CALL:
case DATA_TRANSFER:
if (sbspace(&lcp->lcd_sb) < 0) {
lx = lx->lx_next;
if (lx->lx_rt != rt)
senderr(ENOSPC);
goto next_circuit;
}
if (lx->lx_ia)
lcp->lcd_dg_timer =
lx->lx_ia->ia_xc.xc_dg_idletimo;
pk_send(lcp, m);
break;
default:
/*
* We count on the timer routine to close idle
* connections, if there are not enough circuits to go
* around.
*
* So throw away data for now.
* After we get it all working, we'll rewrite to handle
* actively closing connections (other than by timers),
* when circuits get tight.
*
* In the DDN case, the imp itself closes connections
* under heavy load.
*/
error = ENOBUFS;
bad:
if (m)
m_freem(m);
}
return (error);
}
/*
* Simpleminded timer routine.
*/
void
x25_iftimeout(ifp)
struct ifnet *ifp;
{
register struct pkcb *pkcb = 0;
register struct pklcd **lcpp, *lcp;
int s = splimp();
FOR_ALL_PKCBS(pkcb)
if (pkcb->pk_ia->ia_ifp == ifp)
for (lcpp = pkcb->pk_chan + pkcb->pk_maxlcn;
--lcpp > pkcb->pk_chan;)
if ((lcp = *lcpp) &&
lcp->lcd_state == DATA_TRANSFER &&
(lcp->lcd_flags & X25_DG_CIRCUIT) &&
(lcp->lcd_dg_timer && --lcp->lcd_dg_timer == 0)) {
lcp->lcd_upper(lcp, 0);
}
splx(s);
}
/*
* This routine gets called when validating additions of new routes
* or deletions of old ones.
*/
void
x25_rtrequest(cmd, rt, dst)
int cmd;
register struct rtentry *rt;
struct sockaddr *dst;
{
register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo;
register struct sockaddr_x25 *sa =(struct sockaddr_x25 *)rt->rt_gateway;
register struct pklcd *lcp;
/* would put this pk_init, except routing table doesn't
exist yet. */
if (x25_dgram_sockmask == 0) {
x25_dgram_sockmask =
SA(rn_addmask((caddr_t)&x25_dgmask, 0, 4)->rn_key);
}
if (rt->rt_flags & RTF_GATEWAY) {
if (rt->rt_llinfo)
RTFREE((struct rtentry *)rt->rt_llinfo);
rt->rt_llinfo = (cmd == RTM_ADD) ?
(caddr_t)rtalloc1(rt->rt_gateway, 1, 0UL) : 0;
return;
}
if ((rt->rt_flags & RTF_HOST) == 0)
return;
if (cmd == RTM_DELETE) {
while (rt->rt_llinfo)
x25_lxfree((struct llinfo *)rt->rt_llinfo);
x25_rtinvert(RTM_DELETE, rt->rt_gateway, rt);
return;
}
if (lx == 0 && (lx = x25_lxalloc(rt)) == 0)
return;
if ((lcp = lx->lx_lcd) && lcp->lcd_state != READY) {
/*
* This can only happen on a RTM_CHANGE operation
* though cmd will be RTM_ADD.
*/
if (lcp->lcd_ceaddr &&
Bcmp(rt->rt_gateway, lcp->lcd_ceaddr,
lcp->lcd_ceaddr->x25_len) != 0) {
x25_rtinvert(RTM_DELETE, lcp->lcd_ceaddr, rt);
lcp->lcd_upper = 0;
pk_disconnect(lcp);
}
lcp = 0;
}
x25_rtinvert(RTM_ADD, rt->rt_gateway, rt);
}
int x25_dont_rtinvert = 0;
void
x25_rtinvert(cmd, sa, rt)
register struct sockaddr *sa;
register struct rtentry *rt;
{
struct rtentry *rt2 = 0;
/*
* rt_gateway contains PID indicating which proto
* family on the other end, so will be different
* from general host route via X.25.
*/
if (rt->rt_ifp->if_type == IFT_X25DDN || x25_dont_rtinvert)
return;
if (sa->sa_family != AF_CCITT)
return;
if (cmd != RTM_DELETE) {
rtrequest(RTM_ADD, sa, rt_key(rt), x25_dgram_sockmask,
RTF_PROTO2, &rt2);
if (rt2) {
rt2->rt_llinfo = (caddr_t) rt;
rt->rt_refcnt++;
}
return;
}
rt2 = rt;
if ((rt = rtalloc1(sa, 0, 0UL)) == 0 ||
(rt->rt_flags & RTF_PROTO2) == 0 ||
rt->rt_llinfo != (caddr_t)rt2) {
printf("x25_rtchange: inverse route screwup\n");
return;
} else
rt2->rt_refcnt--;
rtrequest(RTM_DELETE, sa, rt_key(rt2), x25_dgram_sockmask,
0, (struct rtentry **) 0);
}
static struct sockaddr_x25 blank_x25 = {sizeof blank_x25, AF_CCITT};
/*
* IP to X25 address routine copyright ACC, used by permission.
*/
union imp_addr {
struct in_addr ip;
struct imp {
u_char s_net;
u_char s_host;
u_char s_lh;
u_char s_impno;
} imp;
};
/*
* The following is totally bogus and here only to preserve
* the IP to X.25 translation.
*/
x25_ddnip_to_ccitt(src, rt)
struct sockaddr_in *src;
register struct rtentry *rt;
{
register struct sockaddr_x25 *dst = (struct sockaddr_x25 *)rt->rt_gateway;
union imp_addr imp_addr;
int imp_no, imp_port, temp;
char *x25addr = dst->x25_addr;
imp_addr.ip = src->sin_addr;
*dst = blank_x25;
if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */
imp_no = imp_addr.imp.s_impno;
imp_port = imp_addr.imp.s_host;
} else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */
imp_no = imp_addr.imp.s_impno;
imp_port = imp_addr.imp.s_lh;
} else { /* class C */
imp_no = imp_addr.imp.s_impno / 32;
imp_port = imp_addr.imp.s_impno % 32;
}
x25addr[0] = 12; /* length */
/* DNIC is cleared by struct copy above */
if (imp_port < 64) { /* Physical: 0000 0 IIIHH00 [SS] *//* s_impno
* -> III, s_host -> HH */
x25addr[5] = 0; /* set flag bit */
x25addr[6] = imp_no / 100;
x25addr[7] = (imp_no % 100) / 10;
x25addr[8] = imp_no % 10;
x25addr[9] = imp_port / 10;
x25addr[10] = imp_port % 10;
} else { /* Logical: 0000 1 RRRRR00 [SS] *//* s
* _host * 256 + s_impno -> RRRRR */
temp = (imp_port << 8) + imp_no;
x25addr[5] = 1;
x25addr[6] = temp / 10000;
x25addr[7] = (temp % 10000) / 1000;
x25addr[8] = (temp % 1000) / 100;
x25addr[9] = (temp % 100) / 10;
x25addr[10] = temp % 10;
}
}
/*
* This routine is a sketch and is not to be believed!!!!!
*
* This is a utility routine to be called by x25 devices when a
* call request is honored with the intent of starting datagram forwarding.
*/
x25_dg_rtinit(dst, ia, af)
struct sockaddr_x25 *dst;
register struct x25_ifaddr *ia;
{
struct sockaddr *sa = 0;
struct rtentry *rt;
struct in_addr my_addr;
static struct sockaddr_in sin = {sizeof(sin), AF_INET};
if (ia->ia_ifp->if_type == IFT_X25DDN && af == AF_INET) {
/*
* Inverse X25 to IP mapping copyright and courtesy ACC.
*/
int imp_no, imp_port, temp;
union imp_addr imp_addr;
{
/*
* First determine our IP addr for network
*/
register struct in_ifaddr *ina;
for (ina = in_ifaddr; ina; ina = ina->ia_next)
if (ina->ia_ifp == ia->ia_ifp) {
my_addr = ina->ia_addr.sin_addr;
break;
}
}
{
register char *x25addr = dst->x25_addr;
switch (x25addr[5] & 0x0f) {
case 0: /* Physical: 0000 0 IIIHH00 [SS] */
imp_no =
((int) (x25addr[6] & 0x0f) * 100) +
((int) (x25addr[7] & 0x0f) * 10) +
((int) (x25addr[8] & 0x0f));
imp_port =
((int) (x25addr[9] & 0x0f) * 10) +
((int) (x25addr[10] & 0x0f));
break;
case 1: /* Logical: 0000 1 RRRRR00 [SS] */
temp = ((int) (x25addr[6] & 0x0f) * 10000)
+ ((int) (x25addr[7] & 0x0f) * 1000)
+ ((int) (x25addr[8] & 0x0f) * 100)
+ ((int) (x25addr[9] & 0x0f) * 10)
+ ((int) (x25addr[10] & 0x0f));
imp_port = temp >> 8;
imp_no = temp & 0xff;
break;
default:
return (0L);
}
imp_addr.ip = my_addr;
if ((imp_addr.imp.s_net & 0x80) == 0x00) {
/* class A */
imp_addr.imp.s_host = imp_port;
imp_addr.imp.s_impno = imp_no;
imp_addr.imp.s_lh = 0;
} else if ((imp_addr.imp.s_net & 0xc0) == 0x80) {
/* class B */
imp_addr.imp.s_lh = imp_port;
imp_addr.imp.s_impno = imp_no;
} else {
/* class C */
imp_addr.imp.s_impno = (imp_no << 5) + imp_port;
}
}
sin.sin_addr = imp_addr.ip;
sa = (struct sockaddr *)&sin;
} else {
/*
* This uses the X25 routing table to do inverse
* lookup of x25 address to sockaddr.
*/
if (rt = rtalloc1(SA(dst), 0, 0UL)) {
sa = rt->rt_gateway;
rt->rt_refcnt--;
}
}
/*
* Call to rtalloc1 will create rtentry for reverse path
* to callee by virtue of cloning magic and will allocate
* space for local control block.
*/
if (sa && (rt = rtalloc1(sa, 1, 0UL)))
rt->rt_refcnt--;
}
int x25_startproto = 1;
pk_init()
{
/*
* warning, sizeof (struct sockaddr_x25) > 32,
* but contains no data of interest beyond 32
*/
if (x25_startproto) {
pk_protolisten(0xcc, 1, x25_dgram_incoming);
pk_protolisten(0x81, 1, x25_dgram_incoming);
}
}
struct x25_dgproto {
u_char spi;
u_char spilen;
int (*f)();
} x25_dgprototab[] = {
#if defined(ISO) && defined(TPCONS)
{ 0x0, 0, tp_incoming},
#endif
{ 0xcc, 1, x25_dgram_incoming},
{ 0xcd, 1, x25_dgram_incoming},
{ 0x81, 1, x25_dgram_incoming},
};
pk_user_protolisten(info)
register u_char *info;
{
register struct x25_dgproto *dp = x25_dgprototab
+ ((sizeof x25_dgprototab) / (sizeof *dp));
register struct pklcd *lcp;
while (dp > x25_dgprototab)
if ((--dp)->spi == info[0])
goto gotspi;
return ESRCH;
gotspi: if (info[1])
return pk_protolisten(dp->spi, dp->spilen, dp->f);
for (lcp = pk_listenhead; lcp; lcp = lcp->lcd_listen)
if (lcp->lcd_laddr.x25_udlen == dp->spilen &&
Bcmp(&dp->spi, lcp->lcd_laddr.x25_udata, dp->spilen) == 0) {
pk_disconnect(lcp);
return 0;
}
return ESRCH;
}
/*
* This routine transfers an X.25 circuit to or from a routing entry.
* If the supplied circuit is * in DATA_TRANSFER state, it is added to the
* routing entry. If freshly allocated, it glues back the vc from
* the rtentry to the socket.
*/
pk_rtattach(so, m0)
register struct socket *so;
struct mbuf *m0;
{
register struct pklcd *lcp = (struct pklcd *)so->so_pcb;
register struct mbuf *m = m0;
struct sockaddr *dst = mtod(m, struct sockaddr *);
register struct rtentry *rt = rtalloc1(dst, 0, 0UL);
register struct llinfo_x25 *lx;
caddr_t cp;
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define transfer_sockbuf(s, f, l) \
while (m = (s)->sb_mb)\
{(s)->sb_mb = m->m_act; m->m_act = 0; sbfree((s), m); f(l, m);}
if (rt)
rt->rt_refcnt--;
cp = (dst->sa_len < m->m_len) ? ROUNDUP(dst->sa_len) + (caddr_t)dst : 0;
while (rt &&
((cp == 0 && rt_mask(rt) != 0) ||
(cp != 0 && (rt_mask(rt) == 0 ||
Bcmp(cp, rt_mask(rt), rt_mask(rt)->sa_len)) != 0)))
rt = (struct rtentry *)rt->rt_nodes->rn_dupedkey;
if (rt == 0 || (rt->rt_flags & RTF_GATEWAY) ||
(lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)
return ESRCH;
if (lcp == 0)
return ENOTCONN;
switch (lcp->lcd_state) {
default:
return ENOTCONN;
case READY:
/* Detach VC from rtentry */
if (lx->lx_lcd == 0)
return ENOTCONN;
lcp->lcd_so = 0;
pk_close(lcp);
lcp = lx->lx_lcd;
if (lx->lx_next->lx_rt == rt)
x25_lxfree(lx);
lcp->lcd_so = so;
lcp->lcd_upper = 0;
lcp->lcd_upnext = 0;
transfer_sockbuf(&lcp->lcd_sb, sbappendrecord, &so->so_snd);
soisconnected(so);
return 0;
case DATA_TRANSFER:
/* Add VC to rtentry */
lcp->lcd_so = 0;
lcp->lcd_sb = so->so_snd; /* structure copy */
bzero((caddr_t)&so->so_snd, sizeof(so->so_snd)); /* XXXXXX */
so->so_pcb = 0;
x25_rtattach(lcp, rt);
transfer_sockbuf(&so->so_rcv, x25_ifinput, lcp);
soisdisconnected(so);
}
return 0;
}
x25_rtattach(lcp0, rt)
register struct pklcd *lcp0;
struct rtentry *rt;
{
register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo;
register struct pklcd *lcp;
register struct mbuf *m;
if (lcp = lx->lx_lcd) { /* adding an additional VC */
if (lcp->lcd_state == READY) {
transfer_sockbuf(&lcp->lcd_sb, pk_output, lcp0);
lcp->lcd_upper = 0;
pk_close(lcp);
} else {
lx = x25_lxalloc(rt);
if (lx == 0)
return ENOBUFS;
}
}
lx->lx_lcd = lcp = lcp0;
lcp->lcd_upper = x25_ifinput;
lcp->lcd_upnext = (caddr_t)lx;
}