* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
* @(#)if_imp.c 7.13 (Berkeley) 6/28/90
* ARPANET IMP (PSN) interface driver.
* The IMP-host protocol (AHIP) is handled here, leaving
* hardware specifics to the lower level interface driver.
#include "machine/mtpr.h"
#include "../net/netisr.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
/* define IMPLEADERS here to get leader printing code */
struct imp_softc imp_softc
[NIMP
];
int nimp
= NIMP
; /* for netstat */
int impqmaxlen
= IFQ_MAXLEN
;
int imphqlen
= 12 + IMP_MAXHOSTMSG
; /* max packets to queue per host */
#define HOSTDEADTIMER (30 * PR_SLOWHZ) /* How long to wait when down */
int impdown(), impinit(), impioctl(), impoutput(), imptimo();
* IMP attach routine. Called from hardware device attach routine
* at configuration time with a pointer to the device structure.
* Sets up local state and returns pointer to base of ifnet+impcb
* structures. This is then used by the device's attach routine
* set up its back pointers.
impattach(hwname
, hwunit
, reset
)
register struct ifnet
*ifp
;
printf("imp%d: not configured\n", impunit
++);
sc
= &imp_softc
[impunit
];
sc
->imp_cb
.ic_hwname
= hwname
;
sc
->imp_cb
.ic_hwunit
= hwunit
;
ifp
->if_mtu
= IMPMTU
- sizeof(struct imp_leader
);
ifp
->if_ioctl
= impioctl
;
ifp
->if_output
= impoutput
;
ifp
->if_watchdog
= imptimo
;
* IMP initialization routine: call hardware module to
* setup resources, init state and get ready for
* NOOPs the IMP should send us, and that we want to drop.
register struct imp_softc
*sc
= &imp_softc
[unit
];
if (sc
->imp_if
.if_addrlist
== 0)
log(imppri
, "impinit\n");
sc
->imp_state
= IMPS_WINIT
;
if ((*sc
->imp_cb
.ic_init
)(sc
->imp_cb
.ic_hwunit
) == 0)
sc
->imp_if
.if_flags
&= ~IFF_UP
;
impintrq
.ifq_maxlen
= impqmaxlen
;
* ARPAnet 1822/AHIP input routine.
* Called from hardware input interrupt routine to handle 1822
* IMP-host messages. Data messages are passed to higher-level
* protocol processors on the basis of link number.
* Other type messages (control) are handled here.
register struct control_leader
*cp
;
#define ip ((struct imp_leader *)cp)
register struct imp_softc
*sc
= &imp_softc
[unit
];
register struct host
*hp
;
register struct ifqueue
*inq
;
* Pull the interface pointer out of the mbuf
* and save for later; adjust mbuf to look at rest of data.
if ((m
->m_flags
&& M_PKTHDR
) == 0)
panic("No header in impinput");
/* verify leader length. */
if (m
->m_len
< sizeof(struct control_leader
) &&
(m
= m_pullup(m
, sizeof(struct control_leader
))) == 0)
cp
= mtod(m
, struct control_leader
*);
if (cp
->dl_mtype
== IMPTYPE_DATA
&&
m
->m_len
< sizeof(struct imp_leader
)) {
if ((m
= m_pullup(m
, sizeof(struct imp_leader
))) == 0)
cp
= mtod(m
, struct control_leader
*);
printleader("impinput", ip
);
if (cp
->dl_format
!= IMP_NFF
) {
* We get 1822L NOOPs and RESET
log(imppri
, "input, format %x mtype %d\n",
cp
->dl_format
, cp
->dl_mtype
);
if (cp
->dl_format
!= IMP_1822L_I2H
||
(cp
->dl_mtype
!= IMPTYPE_NOOP
&&
cp
->dl_mtype
!= IMPTYPE_RESET
)) {
sc
->imp_if
.if_collisions
++; /* XXX */
} else switch (cp
->dl_mtype
) {
* Data for a protocol. Dispatch to the appropriate
* protocol routine (running at software interrupt).
* If this isn't a raw interface, advance pointer
m
->m_len
-= sizeof(struct imp_leader
);
if (m
->m_flags
& M_PKTHDR
)
m
->m_pkthdr
.len
-= sizeof(struct imp_leader
);
m
->m_data
+= sizeof(struct imp_leader
);
* IMP leader error. Reset the IMP and discard the packet.
* According to 1822 document, this message
* will be generated in response to the
* first noop sent to the IMP after
* the host resets the IMP interface.
log(imppri
, "badleader\n");
if (sc
->imp_state
!= IMPS_INIT
) {
impmsg(sc
, "leader error");
* IMP going down. Print message, and if not immediate,
* set off a timer to insure things will be reset at the
type
= cp
->dl_link
& IMP_DMASK
;
when
= (cp
->dl_link
& IMPDOWN_WHENMASK
) >> IMPDOWN_WHENSHIFT
;
log(imppri
, "input DOWN %s %d\n",
impmessage
[type
], when
* IMPDOWN_WHENUNIT
);
if (type
!= IMPDOWN_GOING
&& when
)
impmsg(sc
, "going down %s in %d minutes",
(u_int
)impmessage
[type
], when
* IMPDOWN_WHENUNIT
);
impmsg(sc
, "going down %s", (u_int
)impmessage
[type
]);
if (sc
->imp_state
!= IMPS_UP
)
if (type
== IMPDOWN_GOING
) {
sc
->imp_state
= IMPS_GOINGDOWN
;
timeout(impdown
, (caddr_t
)sc
, IMPTV_DOWN
* hz
);
sc
->imp_state
= IMPS_WINIT
;
* A NOP, usually seen during the initialization sequence.
* Compare the local address with that in the message.
* Reset the local address notion if it doesn't match.
if (sc
->imp_state
== IMPS_WINIT
) {
sc
->imp_state
= IMPS_INIT
;
if (sc
->imp_state
== IMPS_INIT
&& cp
->dl_imp
!= 0) {
struct in_addr leader_addr
;
sin
= (struct sockaddr_in
*)&sc
->imp_if
.if_addrlist
->ifa_addr
;
imp_leader_to_addr(&leader_addr
, cp
, &sc
->imp_if
);
if (sin
->sin_addr
.s_addr
!= leader_addr
.s_addr
) {
impmsg(sc
, "address reset to x%x (%d/%d)",
ntohl(leader_addr
.s_addr
),
sin
->sin_addr
.s_addr
= leader_addr
.s_addr
;
* RFNM or INCOMPLETE message, decrement rfnm count
* and prepare to send next message.
* If the rfnm allows another queued
* message to be sent, bump msgready
* We could pass incomplete's up to the next level,
* but this currently isn't needed.
* Pass "bad" incompletes and rfnms to the raw socket.
if ((hp
= hostlookup((int)cp
->dl_imp
, (int)cp
->dl_host
,
unit
)) == 0 || hp
->h_rfnm
== 0) {
if (cp
->dl_mtype
== IMPTYPE_RFNM
)
* Host or IMP can't be reached. Flush any packets
* awaiting transmission and release the host structure.
* Enqueue for notifying protocols at software interrupt time.
case IMPTYPE_HOSTUNREACH
:
if (hp
= hostlookup((int)cp
->dl_imp
, (int)cp
->dl_host
, unit
)) {
hp
->h_flags
|= (1 << (int)cp
->dl_mtype
);
MIN(hp
->h_qcnt
, IMP_MAXHOSTMSG
- hp
->h_rfnm
);
hp
->h_timer
= HOSTDEADTIMER
;
* Error in data. Clear RFNM status for this host and send
* noops to the IMP to clear the interface.
impmsg(sc
, "data error");
if (hp
= hostlookup((int)cp
->dl_imp
, (int)cp
->dl_host
, unit
)) {
MIN(hp
->h_qcnt
, IMP_MAXHOSTMSG
- hp
->h_rfnm
);
log(imppri
, "reset complete\n");
if (sc
->imp_state
!= IMPS_INIT
) {
impmsg(sc
, "interface reset");
if (sc
->imp_state
!= IMPS_DOWN
) {
sc
->imp_if
.if_flags
|= IFF_UP
;
sc
->imp_if
.if_collisions
++; /* XXX */
* Bring the IMP down after notification.
if (sc
->imp_state
== IMPS_GOINGDOWN
) {
sc
->imp_state
= IMPS_WINIT
;
impmsg(sc
, "marked down");
hostreset(sc
->imp_if
.if_unit
);
log(imppri
, "impdown, state now %d (ignored)\n", sc
->imp_state
);
log(imppri
, "imp%d: %r\n", sc
->imp_if
.if_unit
, fmt
, &a1
);
struct sockproto impproto
= { PF_IMPLINK
};
struct sockaddr_in impdst
= { sizeof (impdst
), AF_INET
};
struct sockaddr_in impsrc
= { sizeof (impsrc
), AF_INET
};
* Pick up the IMP "error" messages enqueued earlier,
* passing these up to the higher level protocol
register struct control_leader
*cp
;
IF_DEQUEUEIF(&impintrq
, m
, ifp
);
cp
= mtod(m
, struct control_leader
*);
imp_leader_to_addr(&impsrc
.sin_addr
, cp
, ifp
);
impproto
.sp_protocol
= cp
->dl_link
;
impdst
.sin_addr
= IA_SIN(ifp
->if_addrlist
)->sin_addr
;
if (cp
->dl_mtype
== IMPTYPE_HOSTDEAD
||
cp
->dl_mtype
== IMPTYPE_HOSTUNREACH
) {
code
= (cp
->dl_mtype
== IMPTYPE_HOSTDEAD
) ?
PRC_HOSTDEAD
: PRC_UNREACH_HOST
;
pfctlinput(code
, (struct sockaddr
*)&impsrc
);
raw_ctlinput(code
, (struct sockaddr
*)&impsrc
);
raw_input(m
, &impproto
, (struct sockaddr
*)&impsrc
,
(struct sockaddr
*)&impdst
);
* ARPAnet 1822 output routine.
* Called from higher level protocol routines to set up messages for
* transmission to the imp. Sets up the header and calls impsnd to
* enqueue the message for this IMP's hardware driver.
register struct ifnet
*ifp
;
register struct imp_leader
*imp
;
register struct mbuf
*m
= m0
;
caddr_t pkt
= mtod(m
, caddr_t
);
* Don't even try if the IMP is unavailable.
if (!IMPS_RUNNING(imp_softc
[ifp
->if_unit
].imp_state
)) {
* If AF_IMPLINK, leader exists; just send.
* Otherwise, construct leader according to address family.
if (dst
->sa_family
!= AF_IMPLINK
) {
* Add IMP leader. If there's not enough space in the
* first mbuf, allocate another. If that should fail, we
M_PREPEND(m
, sizeof(struct imp_leadr
), M_DONTWAIT
);
imp
= mtod(m
, struct imp_leader
*);
imp
->il_format
= IMP_NFF
;
imp
->il_mtype
= IMPTYPE_DATA
;
switch (dst
->sa_family
) {
imp
->il_link
= IMPLINK_IP
;
imp_addr_to_leader((struct control_leader
*)imp
,
((struct sockaddr_in
*)dst
)->sin_addr
.s_addr
);
imp
->il_length
= htons(ntohs((u_short
)
((struct ip
*)pkt
)->ip_len
) << 3);
printf("imp%d: can't handle af%d\n", ifp
->if_unit
,
* Put a message on an interface's output queue.
* Perform RFNM counting: no more than 8 message may be
* in flight to any one host.
register struct control_leader
*imp
;
register struct host
*hp
;
register struct imp_softc
*sc
= &imp_softc
[ifp
->if_unit
];
imp
= mtod(m
, struct control_leader
*);
* Do RFNM counting for data messages
* (no more than 8 outstanding to any host).
* Queue data messages per host if 8 are already outstanding
* or if the hardware interface is already doing output.
* Increment imp_msgready if the message could be sent now,
* but must be queued because the imp output is busy.
if (imp
->dl_mtype
== IMPTYPE_DATA
) {
hp
= hostenter((int)imp
->dl_imp
, (int)imp
->dl_host
,
if (hp
->h_flags
& (HF_DEAD
|HF_UNREACH
))
error
= hp
->h_flags
& HF_DEAD
?
EHOSTDOWN
: EHOSTUNREACH
;
else if (hp
->h_rfnm
< IMP_MAXHOSTMSG
&&
sc
->imp_cb
.ic_oactive
== 0) {
* adjust rfnm count and timer.
} else if (hp
->h_rfnm
+ hp
->h_qcnt
< imphqlen
) {
if (hp
->h_rfnm
+ hp
->h_qcnt
<= IMP_MAXHOSTMSG
)
} else if (sc
->imp_cb
.ic_oactive
== 0)
IF_ENQUEUE(&ifp
->if_snd
, m
);
sc
->imp_if
.if_timer
= IMP_OTIMER
;
(*sc
->imp_cb
.ic_output
)(sc
->imp_cb
.ic_hwunit
, m
);
* Start another output operation on IMP; called from hardware
* transmit-complete interrupt routine at splimp or from imp routines
* when output is not in progress. If there are any packets on shared
* output queue, send them, otherwise send the next data packet for a host.
* Host data packets are sent round-robin based on destination by walking
register struct imp_softc
*sc
;
register struct host
*hp
;
IF_DEQUEUE(&sc
->imp_if
.if_snd
, m
);
sc
->imp_if
.if_timer
= IMP_OTIMER
;
(*sc
->imp_cb
.ic_output
)(sc
->imp_cb
.ic_hwunit
, m
);
if ((m
= sc
->imp_hostq
) == 0 && (m
= sc
->imp_hosts
) == 0)
for (hp
= &mtod(m
, struct hmbuf
*)->hm_hosts
[index
]; ;
if ((m
= m
->m_next
) == 0)
hp
= mtod(m
, struct hmbuf
*)->hm_hosts
;
if (hp
->h_qcnt
&& hp
->h_rfnm
< IMP_MAXHOSTMSG
) {
* Found host entry with another message
* to send. Deliver it to the IMP.
* Start with succeeding host next time.
sc
->imp_hostent
= index
+ 1;
if (m
== sc
->imp_hostq
&& !first
&&
index
+ 1 >= sc
->imp_hostent
) { /* XXX */
log(imppri
, "imp: can't find %d msgready\n",
* Restart output for a host that has received a RFNM
* or incomplete or has timed out while waiting for a RFNM.
* Must be called at splimp.
register struct imp_softc
*sc
;
* If the RFNM moved a queued message into the window,
* update msgready and start IMP if idle.
if (hp
->h_qcnt
> IMP_MAXHOSTMSG
- 1 - hp
->h_rfnm
) {
if (sc
->imp_cb
.ic_oactive
== 0)
if (hp
->h_rfnm
== 0 && hp
->h_qcnt
== 0)
* Send the next message queued for a host
* when ready to send another message to the IMP.
* Called only when output is not in progress.
* Bump RFNM counter and start RFNM timer
* when we send the message to the IMP.
* Must be called at splimp.
register struct imp_softc
*sc
;
register struct host
*hp
;
sc
->imp_if
.if_timer
= IMP_OTIMER
;
(*sc
->imp_cb
.ic_output
)(sc
->imp_cb
.ic_hwunit
, m
);
* "Watchdog" timeout. When the output timer expires,
* we assume we have been blocked by the imp.
* No need to restart, just collect statistics.
imp_softc
[unit
].imp_block
++;
* Put three 1822 NOOPs at the head of the output queue.
* Part of host-IMP initialization procedure.
* (Should return success/failure, but noone knows
* what to do with this, so why bother?)
* This routine is always called at splimp, so we don't
* protect the call to IF_PREPEND.
register struct imp_softc
*sc
;
register struct control_leader
*cp
;
log(imppri
, "impnoops\n");
for (i
= 0; i
< IMP_NOOPCNT
; i
++) {
if ((m
= m_getclr(M_DONTWAIT
, MT_HEADER
)) == 0)
m
->m_len
= sizeof(struct control_leader
);
cp
= mtod(m
, struct control_leader
*);
cp
->dl_mtype
= IMPTYPE_NOOP
;
IF_PREPEND(&sc
->imp_if
.if_snd
, m
);
if (sc
->imp_cb
.ic_oactive
== 0)
* Process an ioctl request.
register struct ifnet
*ifp
;
struct ifaddr
*ifa
= (struct ifaddr
*) data
;
int s
= splimp(), error
= 0;
#define sc ((struct imp_softc *)ifp)
if (ifa
->ifa_addr
.sa_family
!= AF_INET
) {
if ((ifp
->if_flags
& IFF_UP
) == 0)
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
sc
->imp_state
!= IMPS_DOWN
) {
if (sc
->imp_cb
.ic_down
&&
(*sc
->imp_cb
.ic_down
)(sc
->imp_cb
.ic_hwunit
)) {
sc
->imp_state
= IMPS_DOWN
;
} else if (ifp
->if_flags
& IFF_UP
&& sc
->imp_state
== IMPS_DOWN
)
register struct imp_leader
*ip
;
printbyte((char *)ip
, 12);
printf("<fmt=%x,net=%x,flags=%x,mtype=", ip
->il_format
, ip
->il_network
,
if (ip
->il_mtype
<= IMPTYPE_READY
)
printf("%s,", impleaders
[ip
->il_mtype
]);
printf("%x,", ip
->il_mtype
);
printf("htype=%x,host=%x,imp=%x,link=", ip
->il_htype
, ip
->il_host
,
if (ip
->il_link
== IMPLINK_IP
)
printf("%x,", ip
->il_link
);
printf("subtype=%x,len=%x>\n",ip
->il_subtype
,ntohs(ip
->il_length
)>>3);
putchar("0123456789abcdef"[(c
>>((1-j
)*4))&0xf], 0);
* Routine to convert from IMP Leader to InterNet Address.
* This procedure is necessary because IMPs may be assigned Class A, B, or C
* network numbers, but only have 8 bits in the leader to reflect the
* IMP "network number". The strategy is to take the network number from
* the ifnet structure, and blend in the host-on-imp and imp-on-net numbers
* There is no support for "Logical Hosts".
* Class A: Net.Host.0.Imp
* Class B: Net.net.Host.Imp
* Class C: Net.net.net.(Host4|Imp4)
imp_leader_to_addr(ap
, cp
, ifp
)
register struct control_leader
*cp
;
register struct sockaddr_in
*sin
;
int imp
= ntohs(cp
->dl_imp
);
sin
= (struct sockaddr_in
*)(&ifp
->if_addrlist
->ifa_addr
);
final
= ntohl(sin
->sin_addr
.s_addr
);
final
|= (imp
& 0xFF) | ((cp
->dl_host
& 0xFF)<<16);
} else if (IN_CLASSB(final
)) {
final
|= (imp
& 0xFF) | ((cp
->dl_host
& 0xFF)<<8);
final
|= (imp
& 0x0F) | ((cp
->dl_host
& 0x0F)<<4);
ap
->s_addr
= htonl(final
);
* Function to take InterNet address and fill in IMP leader fields.
imp_addr_to_leader(imp
, a
)
register struct control_leader
*imp
;
register u_long addr
= ntohl(a
);
imp
->dl_network
= 0; /* !! */
imp
->dl_host
= ((addr
>>16) & 0xFF);
imp
->dl_imp
= addr
& 0xFF;
} else if (IN_CLASSB(addr
)) {
imp
->dl_host
= ((addr
>>8) & 0xFF);
imp
->dl_imp
= addr
& 0xFF;
imp
->dl_host
= ((addr
>>4) & 0xF);
imp
->dl_imp
= addr
& 0xF;
imp
->dl_imp
= htons(imp
->dl_imp
);