1/*	$NetBSD: if_ecosubr.c,v 1.35 2010/04/05 07:22:22 joerg Exp $	*/
2
3/*-
4 * Copyright (c) 2001 Ben Harris
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * Copyright (c) 1982, 1989, 1993
31 *	The Regents of the University of California.  All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 *    notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 *    notice, this list of conditions and the following disclaimer in the
40 *    documentation and/or other materials provided with the distribution.
41 * 3. Neither the name of the University nor the names of its contributors
42 *    may be used to endorse or promote products derived from this software
43 *    without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 *
57 *	@(#)if_ethersubr.c	8.2 (Berkeley) 4/4/96
58 */
59
60#include <sys/cdefs.h>
61__KERNEL_RCSID(0, "$NetBSD: if_ecosubr.c,v 1.35 2010/04/05 07:22:22 joerg Exp $");
62
63#include "opt_inet.h"
64#include "opt_pfil_hooks.h"
65
66#include <sys/param.h>
67#include <sys/errno.h>
68#include <sys/kernel.h>
69#include <sys/socket.h>
70#include <sys/sockio.h>
71#include <sys/syslog.h>
72#include <sys/systm.h>
73
74#include <net/if.h>
75#include <net/if_dl.h>
76#include <net/if_eco.h>
77#include <net/if_types.h>
78#include <net/netisr.h>
79#include <net/route.h>
80
81#include <net/bpf.h>
82
83#ifdef INET
84#include <net/ethertypes.h>
85#include <net/if_arp.h>
86#include <netinet/in.h>
87#include <netinet/in_var.h>
88#endif
89#include <netinet/if_inarp.h>
90
91struct eco_retryparms {
92	int	erp_delay;
93	int	erp_count;
94};
95
96/* Default broadcast address */
97static const uint8_t eco_broadcastaddr[] = { 0xff, 0xff };
98
99static int eco_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
100    struct rtentry *);
101static void eco_input(struct ifnet *, struct mbuf *);
102static void eco_start(struct ifnet *);
103static int eco_ioctl(struct ifnet *, u_long, void *);
104
105static int eco_interestingp(struct ifnet *ifp, struct mbuf *m);
106static struct mbuf *eco_immediate(struct ifnet *ifp, struct mbuf *m);
107static struct mbuf *eco_ack(struct ifnet *ifp, struct mbuf *m);
108
109static void eco_defer(struct ifnet *, struct mbuf *, int);
110static void eco_retry_free(struct eco_retry *er);
111static void eco_retry(void *);
112
113void
114eco_ifattach(struct ifnet *ifp, const uint8_t *lla)
115{
116	struct ecocom *ec = (void *)ifp;
117
118	ifp->if_type = IFT_ECONET;
119	ifp->if_addrlen = ECO_ADDR_LEN;
120	ifp->if_hdrlen = ECO_HDR_LEN;
121	ifp->if_dlt = DLT_ECONET;
122	ifp->if_mtu = ECO_MTU;
123
124	ifp->if_output	 = eco_output;
125	ifp->if_input	 = eco_input;
126	ifp->if_start	 = eco_start;
127	ifp->if_ioctl	 = eco_ioctl;
128
129/*	ifp->if_baudrate...; */
130	if_set_sadl(ifp, lla, ECO_ADDR_LEN, FALSE);
131
132	ifp->if_broadcastaddr = eco_broadcastaddr;
133
134	LIST_INIT(&ec->ec_retries);
135
136	bpf_attach(ifp, ifp->if_dlt, ECO_HDR_LEN);
137}
138
139#define senderr(e) do {							\
140	error = (e);							\
141	goto bad;							\
142} while (/*CONSTCOND*/0)
143
144int
145eco_init(struct ifnet *ifp)
146{
147	struct ecocom *ec = (struct ecocom *)ifp;
148
149	if ((ifp->if_flags & IFF_RUNNING) == 0)
150		ec->ec_state = ECO_UNKNOWN;
151	return 0;
152}
153
154void
155eco_stop(struct ifnet *ifp, int disable)
156{
157	struct ecocom *ec = (struct ecocom *)ifp;
158
159	while (!LIST_EMPTY(&ec->ec_retries))
160		eco_retry_free(LIST_FIRST(&ec->ec_retries));
161}
162
163static int
164eco_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst,
165    struct rtentry *rt0)
166{
167	struct eco_header ehdr, *eh;
168	int error;
169	struct mbuf *m = m0, *mcopy = NULL;
170	struct rtentry *rt;
171	int hdrcmplt;
172	int retry_delay, retry_count;
173	struct m_tag *mtag;
174	struct eco_retryparms *erp;
175#ifdef INET
176	struct mbuf *m1;
177	struct arphdr *ah;
178	void *tha;
179	struct eco_arp *ecah;
180#endif
181	ALTQ_DECL(struct altq_pktattr pktattr;)
182
183	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
184		senderr(ENETDOWN);
185	if ((rt = rt0) != NULL) {
186		if ((rt->rt_flags & RTF_UP) == 0) {
187			if ((rt0 = rt = rtalloc1(dst, 1)) != NULL) {
188				rt->rt_refcnt--;
189				if (rt->rt_ifp != ifp)
190					return (*rt->rt_ifp->if_output)
191							(ifp, m0, dst, rt);
192			} else
193				senderr(EHOSTUNREACH);
194		}
195		if ((rt->rt_flags & RTF_GATEWAY) && dst->sa_family != AF_NS) {
196			if (rt->rt_gwroute == 0)
197				goto lookup;
198			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
199				rtfree(rt); rt = rt0;
200			lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
201				if ((rt = rt->rt_gwroute) == 0)
202					senderr(EHOSTUNREACH);
203				/* the "G" test below also prevents rt == rt0 */
204				if ((rt->rt_flags & RTF_GATEWAY) ||
205				    (rt->rt_ifp != ifp)) {
206					rt->rt_refcnt--;
207					rt0->rt_gwroute = 0;
208					senderr(EHOSTUNREACH);
209				}
210			}
211		}
212		if (rt->rt_flags & RTF_REJECT)
213			if (rt->rt_rmx.rmx_expire == 0 ||
214			    time_second < rt->rt_rmx.rmx_expire)
215				senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
216	}
217	/*
218	 * If the queueing discipline needs packet classification,
219	 * do it before prepending link headers.
220	 */
221	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
222
223	hdrcmplt = 0;
224	retry_delay = hz / 16;
225	retry_count = 16;
226	switch (dst->sa_family) {
227#ifdef INET
228	case AF_INET:
229		if (m->m_flags & M_BCAST)
230                	memcpy(ehdr.eco_dhost, eco_broadcastaddr,
231			    ECO_ADDR_LEN);
232
233		else if (!arpresolve(ifp, rt, m, dst, ehdr.eco_dhost))
234			return (0);	/* if not yet resolved */
235		/* If broadcasting on a simplex interface, loopback a copy */
236		if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
237			mcopy = m_copy(m, 0, (int)M_COPYALL);
238		ehdr.eco_port = ECO_PORT_IP;
239		ehdr.eco_control = ECO_CTL_IP;
240		break;
241
242	case AF_ARP:
243		ah = mtod(m, struct arphdr *);
244
245		if (ntohs(ah->ar_pro) != ETHERTYPE_IP)
246			return EAFNOSUPPORT;
247		ehdr.eco_port = ECO_PORT_IP;
248		switch (ntohs(ah->ar_op)) {
249		case ARPOP_REQUEST:
250			ehdr.eco_control = ECO_CTL_ARP_REQUEST;
251			break;
252		case ARPOP_REPLY:
253			ehdr.eco_control = ECO_CTL_ARP_REPLY;
254			break;
255		default:
256			return EOPNOTSUPP;
257		}
258
259		if (m->m_flags & M_BCAST)
260			memcpy(ehdr.eco_dhost, eco_broadcastaddr,
261			    ECO_ADDR_LEN);
262		else {
263			tha = ar_tha(ah);
264			if (tha == NULL)
265				return 0;
266			memcpy(ehdr.eco_dhost, tha, ECO_ADDR_LEN);
267		}
268
269		MGETHDR(m1, M_DONTWAIT, MT_DATA);
270		if (m1 == NULL)
271			senderr(ENOBUFS);
272		M_MOVE_PKTHDR(m1, m);
273		m1->m_len = sizeof(*ecah);
274		m1->m_pkthdr.len = m1->m_len;
275		MH_ALIGN(m1, m1->m_len);
276		ecah = mtod(m1, struct eco_arp *);
277		memset(ecah, 0, m1->m_len);
278		memcpy(ecah->ecar_spa, ar_spa(ah), ah->ar_pln);
279		memcpy(ecah->ecar_tpa, ar_tpa(ah), ah->ar_pln);
280		m_freem(m);
281		m = m1;
282		break;
283#endif
284	case pseudo_AF_HDRCMPLT:
285		hdrcmplt = 1;
286		/* FALLTHROUGH */
287	case AF_UNSPEC:
288		ehdr = *(struct eco_header const *)dst->sa_data;
289		break;
290	default:
291		log(LOG_ERR, "%s: can't handle af%d\n", ifp->if_xname,
292		    dst->sa_family);
293		senderr(EAFNOSUPPORT);
294	}
295
296	if (mcopy)
297		(void) looutput(ifp, mcopy, dst, rt);
298
299	/*
300	 * Add local net header.  If no space in first mbuf,
301	 * allocate another.
302	 */
303	M_PREPEND(m, sizeof (struct eco_header), M_DONTWAIT);
304	if (m == 0)
305		senderr(ENOBUFS);
306	eh = mtod(m, struct eco_header *);
307	*eh = ehdr;
308	if (!hdrcmplt)
309		memcpy(eh->eco_shost, CLLADDR(ifp->if_sadl),
310		    ECO_ADDR_LEN);
311
312	if ((m->m_flags & M_BCAST) == 0) {
313		/* Attach retry info to packet. */
314		mtag = m_tag_get(PACKET_TAG_ECO_RETRYPARMS,
315		    sizeof(struct eco_retryparms), M_NOWAIT);
316		if (mtag == NULL)
317			senderr(ENOBUFS);
318		erp = (struct eco_retryparms *)(mtag + 1);
319		erp->erp_delay = retry_delay;
320		erp->erp_count = retry_count;
321	}
322
323#ifdef PFIL_HOOKS
324	if ((error = pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_OUT)) != 0)
325		return (error);
326	if (m == NULL)
327		return (0);
328#endif
329
330	return ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(&pktattr));
331
332bad:
333	if (m)
334		m_freem(m);
335	return error;
336}
337
338/*
339 * Given a scout, decide if we want the rest of the packet.
340 */
341static int
342eco_interestingp(struct ifnet *ifp, struct mbuf *m)
343{
344	struct eco_header *eh;
345
346	eh = mtod(m, struct eco_header *);
347	switch (eh->eco_port) {
348#ifdef INET
349	case ECO_PORT_IP:
350		return 1;
351#endif
352	}
353	return 0;
354}
355
356static void
357eco_input(struct ifnet *ifp, struct mbuf *m)
358{
359	struct ifqueue *inq;
360	struct eco_header ehdr, *eh;
361	int s, i;
362#ifdef INET
363	struct arphdr *ah;
364	struct eco_arp *ecah;
365	struct mbuf *m1;
366	void *tha;
367#endif
368
369#ifdef PFIL_HOOKS
370	if (pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_IN) != 0)
371		return;
372	if (m == NULL)
373		return;
374#endif
375
376	/* Copy the mbuf header and trim it off. */
377	/* XXX use m_split? */
378	eh = &ehdr;
379	m_copydata(m, 0, ECO_HDR_LEN, (void *)eh);
380	m_adj(m, ECO_HDR_LEN);
381
382	switch (eh->eco_port) {
383#ifdef INET
384	case ECO_PORT_IP:
385		switch (eh->eco_control) {
386		case ECO_CTL_IP:
387			schednetisr(NETISR_IP);
388			inq = &ipintrq;
389			break;
390		case ECO_CTL_ARP_REQUEST:
391		case ECO_CTL_ARP_REPLY:
392			/*
393			 * ARP over Econet is strange, because Econet only
394			 * supports 8 bytes of data in a broadcast packet.
395			 * To cope with this, only the source and destination
396			 * IP addresses are actually contained in the packet
397			 * and we have to infer the rest and build a fake ARP
398			 * packet to pass upwards.
399			 */
400			if (m->m_pkthdr.len != sizeof(struct eco_arp))
401				goto drop;
402			if (m->m_len < sizeof(struct eco_arp)) {
403				m = m_pullup(m, sizeof(struct eco_arp));
404				if (m == NULL) goto drop;
405			}
406			ecah = mtod(m, struct eco_arp *);
407			/* This code derived from arprequest() */
408	       		MGETHDR(m1, M_DONTWAIT, MT_DATA);
409			if (m1 == NULL)
410				goto drop;
411			M_MOVE_PKTHDR(m1, m);
412			m1->m_len = sizeof(*ah) + 2*sizeof(struct in_addr) +
413			    2*ifp->if_data.ifi_addrlen;
414			m1->m_pkthdr.len = m1->m_len;
415			MH_ALIGN(m1, m1->m_len);
416			ah = mtod(m1, struct arphdr *);
417			memset((void *)ah, 0, m1->m_len);
418			ah->ar_pro = htons(ETHERTYPE_IP);
419			ah->ar_hln = ifp->if_data.ifi_addrlen;
420			ah->ar_pln = sizeof(struct in_addr);
421			if (eh->eco_control == ECO_CTL_ARP_REQUEST)
422				ah->ar_op = htons(ARPOP_REQUEST);
423			else
424				ah->ar_op = htons(ARPOP_REPLY);
425			tha = ar_tha(ah);
426			KASSERT(tha != NULL);
427			memcpy(ar_sha(ah), eh->eco_shost, ah->ar_hln);
428			memcpy(tha, eh->eco_dhost, ah->ar_hln);
429			memcpy(ar_spa(ah), ecah->ecar_spa, ah->ar_pln);
430			memcpy(ar_tpa(ah), ecah->ecar_tpa, ah->ar_pln);
431			m_freem(m);
432			m = m1;
433			schednetisr(NETISR_ARP);
434			inq = &arpintrq;
435			break;
436		case ECO_CTL_IPBCAST_REQUEST:
437		{
438			struct sockaddr_storage dst_store;
439			struct sockaddr *dst = (struct sockaddr *)&dst_store;
440
441			/* Queue? */
442			memcpy(eh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
443			eh->eco_control = ECO_CTL_IPBCAST_REPLY;
444			/* dst->sa_len??? */
445			dst->sa_family = AF_UNSPEC;
446			memcpy(dst->sa_data, eh, ECO_HDR_LEN);
447			ifp->if_output(ifp, m, dst, NULL);
448			return;
449		}
450		default:
451			printf("%s: unknown IP stn %s ctl 0x%02x len %d:",
452			    ifp->if_xname, eco_sprintf(eh->eco_shost),
453			    eh->eco_control, m->m_pkthdr.len);
454			if (m->m_len == 0) {
455				m = m_pullup(m, 1);
456				if (m == 0) {
457					printf("\n");
458					goto drop;
459				}
460			}
461			for (i = 0; i < m->m_len; i++)
462				printf(" %02x", mtod(m, uint8_t *)[i]);
463			printf("\n");
464			goto drop;
465		}
466		break;
467#endif
468	default:
469		printf("%s: unknown port stn %s port 0x%02x ctl 0x%02x\n",
470		    ifp->if_xname, eco_sprintf(eh->eco_shost),
471		    eh->eco_port, eh->eco_control);
472	drop:
473		m_freem(m);
474		return;
475	}
476
477	s = splnet();
478	if (IF_QFULL(inq)) {
479		IF_DROP(inq);
480		m_freem(m);
481	} else
482		IF_ENQUEUE(inq, m);
483	splx(s);
484}
485
486static void
487eco_start(struct ifnet *ifp)
488{
489	struct ecocom *ec = (void *)ifp;
490	struct mbuf *m;
491	struct eco_header *eh;
492
493	if (ec->ec_state != ECO_IDLE) return;
494	IFQ_DEQUEUE(&ifp->if_snd, m);
495	if (m == NULL) return;
496	if (ec->ec_claimwire(ifp) == 0) {
497		eh = mtod(m, struct eco_header *);
498		if (eh->eco_port == ECO_PORT_IMMEDIATE) {
499			ec->ec_txframe(ifp, m);
500			ec->ec_state = ECO_IMMED_SENT;
501		} else if (eh->eco_dhost[0] == 255) {
502			ec->ec_txframe(ifp, m);
503			ec->ec_state = ECO_DONE;
504		} else {
505			ec->ec_packet = m;
506			m = m_copym(m, 0, ECO_HDR_LEN, M_DONTWAIT);
507			if (m == NULL) {
508				m_freem(ec->ec_packet);
509				ec->ec_packet = NULL;
510				return;
511			}
512			ec->ec_txframe(ifp, m);
513			ec->ec_state = ECO_SCOUT_SENT;
514		}
515		ifp->if_flags |= IFF_OACTIVE;
516	} else {
517		log(LOG_ERR, "%s: line jammed\n", ifp->if_xname);
518		m_freem(m);
519	}
520}
521
522static int
523eco_ioctl(struct ifnet *ifp, u_long cmd, void *data)
524{
525	struct ifaddr *ifa = (struct ifaddr *)data;
526	int error;
527
528	switch (cmd) {
529	case SIOCINITIFADDR:
530		ifp->if_flags |= IFF_UP;
531		if ((ifp->if_flags & IFF_RUNNING) == 0 &&
532		    (error = (*ifp->if_init)(ifp)) != 0)
533			return error;
534		switch (ifa->ifa_addr->sa_family) {
535#ifdef INET
536		case AF_INET:
537			arp_ifinit(ifp, ifa);
538			break;
539#endif
540		default:
541			break;
542		}
543		return 0;
544	case SIOCSIFMTU:
545		if ((error = ifioctl_common(ifp, cmd, data)) != ENETRESET)
546			return error;
547		else if (ifp->if_flags & IFF_UP)
548			return (*ifp->if_init)(ifp);
549		else
550			return 0;
551		break;
552	case SIOCSIFFLAGS:
553		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
554			return error;
555		switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) {
556		case IFF_RUNNING:
557			/*
558			 * If interface is marked down and it is running,
559			 * then stop and disable it.
560			 */
561			(*ifp->if_stop)(ifp, 1);
562			return 0;
563		case IFF_UP:
564			/*
565			 * If interface is marked up and it is stopped, then
566			 * start it.
567			 */
568			return (*ifp->if_init)(ifp);
569		case IFF_UP|IFF_RUNNING:
570			/*
571			 * Reset the interface to pick up changes in any other
572			 * flags that affect the hardware state.
573			 */
574			return (*ifp->if_init)(ifp);
575		case 0:
576			return 0;
577		}
578		break;
579	default:
580		return ifioctl_common(ifp, cmd, data);
581	}
582
583	return 0;
584}
585
586/*
587 * Handle a raw Econet frame off the interface.  The interface may be
588 * flag-filling for a response.
589 *
590 * May be called from IPL_NET or IPL_SOFTNET.
591 */
592
593struct mbuf *
594eco_inputframe(struct ifnet *ifp, struct mbuf *m)
595{
596	struct ecocom *ec = (void *)ifp;
597	struct eco_header *eh, *eh0;
598	struct mbuf *m0;
599	struct mbuf *reply;
600	int len;
601
602	eh = mtod(m, struct eco_header *);
603	switch (ec->ec_state) {
604	case ECO_IDLE: /* Start of a packet (bcast, immed, scout) */
605		if (m->m_pkthdr.len < ECO_HDR_LEN) {
606			log(LOG_NOTICE, "%s: undersize scout\n",
607			    ifp->if_xname);
608			goto drop;
609		}
610		if (memcmp(eh->eco_dhost, eco_broadcastaddr,
611		    ECO_ADDR_LEN) == 0) {
612			/* Broadcast */
613			eco_input(ifp, m);
614		} else if (memcmp(eh->eco_dhost, CLLADDR(ifp->if_sadl),
615		    ECO_ADDR_LEN) == 0) {
616			/* Unicast for us */
617			if (eh->eco_port == ECO_PORT_IMMEDIATE)
618				return eco_immediate(ifp, m);
619			else {
620				if (eco_interestingp(ifp, m)) {
621					reply = eco_ack(ifp, m);
622					if (reply == NULL) {
623						m_freem(m);
624						return NULL;
625					}
626					ec->ec_state = ECO_SCOUT_RCVD;
627					ec->ec_scout = m;
628					return reply;
629				} else {
630					m_freem(m);
631					return NULL;
632				}
633			}
634		} else
635			/* Not for us.  Throw it away. */
636			m_freem(m);
637		break;
638	case ECO_SCOUT_RCVD: /* Packet data */
639		KASSERT(ec->ec_scout != NULL);
640		m0 = ec->ec_scout;
641		eh0 = mtod(m0, struct eco_header *);
642		if (m->m_pkthdr.len < ECO_SHDR_LEN ||
643		    memcmp(eh->eco_shost, eh0->eco_shost, ECO_ADDR_LEN) != 0 ||
644		    memcmp(eh->eco_dhost, eh0->eco_dhost, ECO_ADDR_LEN) != 0) {
645			log(LOG_NOTICE, "%s: garbled data packet header\n",
646			    ifp->if_xname);
647			goto drop;
648		}
649		reply = eco_ack(ifp, m);
650		/*
651		 * Chop off the small header from this frame, and put
652		 * the scout (which holds the control byte and port)
653		 * in its place.
654		 */
655		ec->ec_scout = NULL;
656		m_adj(m, ECO_SHDR_LEN);
657		len = m0->m_pkthdr.len + m->m_pkthdr.len;
658		m_cat(m0, m);
659		m0->m_pkthdr.len = len;
660		ec->ec_state = ECO_DONE;
661		eco_input(ifp, m0);
662		return reply;
663	case ECO_SCOUT_SENT: /* Scout ack */
664		KASSERT(ec->ec_packet != NULL);
665		m0 = ec->ec_packet;
666		eh0 = mtod(m0, struct eco_header *);
667		if (m->m_pkthdr.len != ECO_SHDR_LEN ||
668		    memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
669		    memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
670			log(LOG_NOTICE, "%s: garbled scout ack\n",
671			    ifp->if_xname);
672			goto drop;
673		}
674		m_freem(m);
675		/* Chop out the control and port bytes. */
676		m0 = m_copym(ec->ec_packet, 0, ECO_SHDR_LEN, M_DONTWAIT);
677		if (m0 == NULL) {
678			m_freem(ec->ec_packet);
679			return NULL;
680		}
681		m = ec->ec_packet;
682		ec->ec_packet = m_copypacket(m, M_DONTWAIT);
683		if (ec->ec_packet == NULL) {
684			m_freem(m0);
685			m_freem(m);
686			return NULL;
687		}
688		m_adj(m, ECO_HDR_LEN);
689		len = m0->m_pkthdr.len + m->m_pkthdr.len;
690		m_cat(m0, m); /* Doesn't update packet header */
691		m0->m_pkthdr.len = len;
692		ec->ec_state = ECO_DATA_SENT;
693		return m0;
694	case ECO_DATA_SENT: /* Data ack */
695		KASSERT(ec->ec_packet != NULL);
696		m0 = ec->ec_packet;
697		eh0 = mtod(m0, struct eco_header *);
698		if (m->m_pkthdr.len != ECO_SHDR_LEN ||
699		    memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
700		    memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
701			log(LOG_NOTICE, "%s: garbled data ack\n",
702			    ifp->if_xname);
703			goto drop;
704		}
705		m_freem(m);
706		m_freem(ec->ec_packet);
707		ec->ec_packet = NULL;
708		ec->ec_state = ECO_DONE;
709		return NULL;
710	default:
711	drop:
712		m_freem(m);
713		break;
714	}
715	return NULL;
716}
717
718/*
719 * Handle an immediate operation, and return the reply, or NULL not to reply.
720 * Frees the incoming mbuf.
721 */
722
723static struct mbuf *
724eco_immediate(struct ifnet *ifp, struct mbuf *m)
725{
726	struct eco_header *eh, *reh;
727	struct mbuf *n;
728	static const uint8_t machinepeek_data[] = { 42, 0, 0, 1 };
729
730	eh = mtod(m, struct eco_header *);
731	switch (eh->eco_control) {
732	case ECO_CTL_MACHINEPEEK:
733		MGETHDR(n, M_DONTWAIT, MT_DATA);
734		if (n == NULL)
735			goto bad;
736		n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN + 4;
737		reh = mtod(n, struct eco_header *);
738		memcpy(reh->eco_dhost, eh->eco_shost,
739		    ECO_ADDR_LEN);
740		memcpy(reh->eco_shost, CLLADDR(ifp->if_sadl),
741		    ECO_ADDR_LEN);
742		memcpy(mtod(n, char *) + ECO_SHDR_LEN, machinepeek_data,
743		    sizeof(machinepeek_data));
744		m_freem(m);
745		return n;
746	default:
747	bad:
748		m_freem(m);
749		return NULL;
750	}
751}
752
753/*
754 * Generate (and return) an acknowledgement for a frame.  Doesn't free the
755 * original frame, since it's probably needed elsewhere.
756 */
757static struct mbuf *
758eco_ack(struct ifnet *ifp, struct mbuf *m)
759{
760	struct eco_header *eh, *reh;
761	struct mbuf *n;
762
763	eh = mtod(m, struct eco_header *);
764	MGETHDR(n, M_DONTWAIT, MT_DATA);
765	if (n == NULL)
766		return NULL;
767	n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN;
768	reh = mtod(n, struct eco_header *);
769	memcpy(reh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
770	memcpy(reh->eco_shost, CLLADDR(ifp->if_sadl), ECO_ADDR_LEN);
771	return n;
772}
773
774void
775eco_inputidle(struct ifnet *ifp)
776{
777	struct ecocom *ec = (void *)ifp;
778	struct mbuf *m;
779	struct m_tag *mtag;
780	struct eco_retryparms *erp;
781
782	switch (ec->ec_state) {
783	case ECO_SCOUT_SENT:
784	case ECO_DATA_SENT:
785	case ECO_IMMED_SENT:
786		/* Outgoing packet failed.  Check if we should retry. */
787		m = ec->ec_packet;
788		ec->ec_packet = NULL;
789		mtag = m_tag_find(m, PACKET_TAG_ECO_RETRYPARMS, NULL);
790		if (mtag == NULL)
791			m_freem(m);
792		else {
793			erp = (struct eco_retryparms *)(mtag + 1);
794			if (--erp->erp_count > 0)
795				eco_defer(ifp, m, erp->erp_delay);
796			else
797				printf("%s: pkt failed\n", ifp->if_xname);
798		}
799		break;
800	case ECO_SCOUT_RCVD:
801		m_freem(ec->ec_scout);
802		ec->ec_scout = NULL;
803		break;
804	default:
805		break;
806	}
807	ec->ec_state = ECO_IDLE;
808	ifp->if_start(ifp);
809}
810
811/*
812 * Convert Econet address to printable (loggable) representation.
813 */
814char *
815eco_sprintf(const uint8_t *ea)
816{
817	static char buf[8];
818
819	if (ea[1] == 0)
820		snprintf(buf, sizeof(buf), "%d", ea[0]);
821	else
822		snprintf(buf, sizeof(buf), "%d.%d", ea[1], ea[0]);
823	return buf;
824}
825
826/*
827 * Econet retry handling.
828 */
829static void
830eco_defer(struct ifnet *ifp, struct mbuf *m, int retry_delay)
831{
832	struct ecocom *ec = (struct ecocom *)ifp;
833	struct eco_retry *er;
834	int s;
835
836	er = malloc(sizeof(*er), M_TEMP, M_NOWAIT);
837	if (er == NULL) {
838		m_freem(m);
839		return;
840	}
841	callout_init(&er->er_callout, 0);
842	er->er_packet = m;
843	er->er_ifp = ifp;
844	s = splnet();
845	LIST_INSERT_HEAD(&ec->ec_retries, er, er_link);
846	splx(s);
847	callout_reset(&er->er_callout, retry_delay, eco_retry, er);
848}
849
850static void
851eco_retry_free(struct eco_retry *er)
852{
853	int s;
854
855	callout_stop(&er->er_callout);
856	m_freem(er->er_packet);
857	s = splnet();
858	LIST_REMOVE(er, er_link);
859	splx(s);
860	callout_destroy(&er->er_callout);
861	free(er, M_TEMP);
862}
863
864static void
865eco_retry(void *arg)
866{
867	struct eco_retry *er = arg;
868	struct mbuf *m;
869	struct ifnet *ifp;
870
871	ifp = er->er_ifp;
872	m = er->er_packet;
873	LIST_REMOVE(er, er_link);
874	(void)ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(NULL));
875	free(er, M_TEMP);
876}
877