/* if_imp.c 4.29 82/04/28 */
* ARPANET IMP interface driver.
* The IMP-host protocol is handled here, leaving
* hardware specifics to the lower level interface driver.
#include "../h/protosw.h"
#include "../net/in_systm.h"
#include "../net/if_imp.h"
#include "../net/if_imphost.h"
#include "../net/ip_var.h"
#include "../net/route.h"
* IMP software status per interface.
* (partially shared with the hardware specific module)
* Each interface is referenced by a network interface structure,
* imp_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its
* address, ... IMP specific structures used in connecting the
* IMP software modules to the hardware specific interface routines
* are stored here. The common structures are made visible to the
* interface driver by passing a pointer to the hardware routine
* NOTE: imp_if and imp_cb are assumed adjacent in hardware code.
struct ifnet imp_if
; /* network visible interface */
struct impcb imp_cb
; /* hooks to hardware module */
u_char imp_state
; /* current state of IMP */
char imp_dropcnt
; /* used during initialization */
* Messages from IMP regarding why
static char *impmessage
[] = {
int impdown(), impinit(), impoutput();
* IMP attach routine. Called from hardware device attach routine
* at configuration time with a pointer to the UNIBUS 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.
struct imp_softc
*sc
= &imp_softc
[ui
->ui_unit
];
register struct ifnet
*ifp
= &sc
->imp_if
;
/* UNIT COULD BE AMBIGUOUS */
ifp
->if_unit
= ui
->ui_unit
;
ifp
->if_mtu
= IMPMTU
- sizeof(struct imp_leader
);
ifp
->if_net
= ui
->ui_flags
;
/* the host and imp fields will be filled in by the imp */
sin
= (struct sockaddr_in
*)&ifp
->if_addr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(ifp
->if_net
, 0);
ifp
->if_output
= impoutput
;
/* reset is handled at the hardware level */
return ((int)&sc
->imp_if
);
* IMP initialization routine: call hardware module to
* setup UNIBUS 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_cb
.ic_init
)(unit
) == 0) {
sc
->imp_state
= IMPS_DOWN
;
sc
->imp_if
.if_flags
&= ~IFF_UP
;
sc
->imp_state
= IMPS_INIT
;
if_rtinit(&sc
->imp_if
, RTF_DIRECT
|RTF_UP
);
struct sockproto impproto
= { PF_IMPLINK
};
struct sockaddr_in impdst
= { AF_IMPLINK
};
struct sockaddr_in impsrc
= { AF_IMPLINK
};
* ARPAnet 1822 input routine.
* Called from hardware input interrupt routine to handle 1822
* IMP-host messages. Type 0 messages (non-control) are
* passed to higher level protocol processors on the basis
* of link number. Other type messages (control) are handled here.
register struct imp_leader
*ip
;
register struct imp_softc
*sc
= &imp_softc
[unit
];
register struct host
*hp
;
register struct ifqueue
*inq
;
struct control_leader
*cp
;
/* 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
)
if (m
->m_len
< sizeof(struct imp_leader
) &&
(m
= m_pullup(m
, sizeof(struct imp_leader
))) == 0)
ip
= mtod(m
, struct imp_leader
*);
if (ip
->il_format
!= IMP_NFF
) {
sc
->imp_if
.if_collisions
++; /* XXX */
if (ip
->il_mtype
!= IMPTYPE_DATA
) {
addr
.s_net
= ip
->il_network
;
addr
.s_net
= sc
->imp_if
.if_net
;
addr
.s_host
= ip
->il_host
;
* 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.
if (sc
->imp_state
!= IMPS_INIT
) {
impmsg(sc
, "leader error");
hostreset(sc
->imp_if
.if_net
);
* IMP going down. Print message, and if not immediate,
* set off a timer to insure things will be reset at the
if (sc
->imp_state
< IMPS_INIT
)
if ((ip
->il_link
& IMP_DMASK
) == 0) {
sc
->imp_state
= IMPS_GOINGDOWN
;
timeout(impdown
, (caddr_t
)sc
, 30 * hz
);
impmsg(sc
, "going down %s",
(u_int
)impmessage
[ip
->il_link
&IMP_DMASK
]);
* 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_DOWN
) {
sc
->imp_state
= IMPS_INIT
;
sc
->imp_dropcnt
= IMP_DROPCNT
;
if (sc
->imp_state
== IMPS_INIT
&& --sc
->imp_dropcnt
> 0)
sin
= (struct sockaddr_in
*)&sc
->imp_if
.if_addr
;
if (sin
->sin_addr
.s_host
!= ip
->il_host
||
sin
->sin_addr
.s_imp
!= ip
->il_imp
) {
sin
->sin_addr
.s_host
= ip
->il_host
;
sin
->sin_addr
.s_imp
= ip
->il_imp
;
impmsg(sc
, "reset (host %d/imp %d)", (u_int
)ip
->il_host
,
sc
->imp_if
.if_flags
|= IFF_UP
;
/* restart output in case something was q'd */
(*sc
->imp_cb
.ic_start
)(sc
->imp_if
.if_unit
);
* RFNM or INCOMPLETE message, send next
* message on the q. We could pass incomplete's
* up to the next level, but this currently isn't
if (hp
= hostlookup(addr
)) {
hp
->h_flags
&= ~HF_INUSE
;
else if (next
= hostdeque(hp
))
(void) impsnd(&sc
->imp_if
, next
);
* Host or IMP can't be reached. Flush any packets
* awaiting transmission and release the host structure.
case IMPTYPE_HOSTUNREACH
: {
impnotify(ip
->il_mtype
, ip
, hostlookup(addr
));
* 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(addr
))
impmsg(sc
, "interface reset");
sc
->imp_if
.if_collisions
++; /* XXX */
* 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
);
m
->m_off
+= sizeof(struct imp_leader
);
impproto
.sp_protocol
= ip
->il_link
;
sin
= (struct sockaddr_in
*)&sc
->imp_if
.if_addr
;
impdst
.sin_addr
= sin
->sin_addr
;;
impsrc
.sin_addr
.s_net
= ip
->il_network
;
impsrc
.sin_addr
.s_host
= ip
->il_host
;
impsrc
.sin_addr
.s_imp
= ip
->il_imp
;
raw_input(m
, &impproto
, (struct sockaddr
*)&impsrc
,
(struct sockaddr
*)&impdst
);
* Bring the IMP down after notification.
sc
->imp_state
= IMPS_DOWN
;
impmsg(sc
, "marked down");
hostreset(sc
->imp_if
.if_net
);
printf("imp%d: ", sc
->imp_if
.if_unit
);
* Process an IMP "error" message, passing this
* up to the higher level protocol.
struct control_leader
*cp
;
in
.s_net
= cp
->dl_network
;
if (cp
->dl_link
!= IMPLINK_IP
)
raw_ctlinput(what
, (caddr_t
)&in
);
ip_ctlinput(what
, (caddr_t
)&in
);
hp
->h_flags
|= (1 << what
);
* 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
;
int x
, dhost
, dimp
, dlink
, len
, dnet
;
* Don't even try if the IMP is unavailable.
if (imp_softc
[ifp
->if_unit
].imp_state
!= IMPS_UP
) {
switch (dst
->sa_family
) {
struct ip
*ip
= mtod(m0
, struct ip
*);
struct sockaddr_in
*sin
= (struct sockaddr_in
*)dst
;
dhost
= sin
->sin_addr
.s_host
;
dimp
= sin
->sin_addr
.s_impno
;
len
= ntohs((u_short
)ip
->ip_len
);
printf("imp%d: can't handle af%d\n", ifp
->if_unit
,
* Add IMP leader. If there's not enough space in the
* first mbuf, allocate another. If that should fail, we
if (m
->m_off
> MMAXOFF
||
MMINOFF
+ sizeof(struct imp_leader
) > m
->m_off
) {
m
->m_len
= sizeof(struct imp_leader
);
m
->m_off
-= sizeof(struct imp_leader
);
m
->m_len
+= sizeof(struct imp_leader
);
imp
= mtod(m
, struct imp_leader
*);
imp
->il_format
= IMP_NFF
;
imp
->il_mtype
= IMPTYPE_DATA
;
imp
->il_imp
= htons((u_short
)dimp
);
htons((u_short
)(len
+ sizeof(struct imp_leader
)) << 3);
imp
->il_flags
= imp
->il_htype
= imp
->il_subtype
= 0;
* 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 imp_leader
*ip
;
register struct host
*hp
;
ip
= mtod(m
, struct imp_leader
*);
* Do RFNM counting for data messages
* (no more than 8 outstanding to any host)
if (ip
->il_mtype
== IMPTYPE_DATA
) {
addr
.s_net
= ip
->il_network
;
addr
.s_net
= ifp
->if_net
; /* XXX */
addr
.s_host
= ip
->il_host
;
if ((hp
= hostlookup(addr
)) == 0)
if (hp
&& (hp
->h_flags
& (HF_DEAD
|HF_UNREACH
))) {
error
= hp
->h_flags
& HF_DEAD
?
EHOSTDEAD
: EHOSTUNREACH
;
hp
->h_flags
&= ~HF_INUSE
;
* If IMP would block, queue until RFNM
if (hp
->h_qcnt
< 8) { /* high water mark */
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
icp
= &imp_softc
[ifp
->if_unit
].imp_cb
;
if (icp
->ic_oactive
== 0)
(*icp
->ic_start
)(ifp
->if_unit
);
* 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?)
register struct imp_softc
*sc
;
register struct control_leader
*cp
;
sc
->imp_dropcnt
= IMP_DROPCNT
;
for (i
= 0; i
< IMP_DROPCNT
+ 1; i
++ ) {
if ((m
= m_getclr(M_DONTWAIT
)) == 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)
(*sc
->imp_cb
.ic_start
)(sc
->imp_if
.if_unit
);