FreeBSD/Linux Kernel Cross Reference
sys/net/if_ecosubr.c
1 /* $NetBSD: if_ecosubr.c,v 1.21 2006/06/07 22:33:42 kardel 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.21 2006/06/07 22:33:42 kardel Exp $");
62
63 #include "bpfilter.h"
64 #include "opt_inet.h"
65 #include "opt_pfil_hooks.h"
66
67 #include <sys/param.h>
68 #include <sys/errno.h>
69 #include <sys/kernel.h>
70 #include <sys/socket.h>
71 #include <sys/sockio.h>
72 #include <sys/syslog.h>
73 #include <sys/systm.h>
74
75 #include <net/if.h>
76 #include <net/if_dl.h>
77 #include <net/if_eco.h>
78 #include <net/if_types.h>
79 #include <net/netisr.h>
80 #include <net/route.h>
81
82 #if NBPFILTER > 0
83 #include <net/bpf.h>
84 #endif
85
86 #ifdef INET
87 #include <net/ethertypes.h>
88 #include <net/if_arp.h>
89 #include <netinet/in.h>
90 #include <netinet/in_var.h>
91 #endif
92
93 struct eco_retryparms {
94 int erp_delay;
95 int erp_count;
96 };
97
98 /* Default broadcast address */
99 static const u_int8_t eco_broadcastaddr[] = { 0xff, 0xff };
100
101 static int eco_output(struct ifnet *, struct mbuf *, struct sockaddr *,
102 struct rtentry *);
103 static void eco_input(struct ifnet *, struct mbuf *);
104 static void eco_start(struct ifnet *);
105 static int eco_ioctl(struct ifnet *, u_long, caddr_t);
106
107 static int eco_interestingp(struct ifnet *ifp, struct mbuf *m);
108 static struct mbuf *eco_immediate(struct ifnet *ifp, struct mbuf *m);
109 static struct mbuf *eco_ack(struct ifnet *ifp, struct mbuf *m);
110
111 static void eco_defer(struct ifnet *, struct mbuf *, int);
112 static void eco_retry_free(struct eco_retry *er);
113 static void eco_retry(void *);
114
115 void
116 eco_ifattach(struct ifnet *ifp, const u_int8_t *lla)
117 {
118 struct ecocom *ec = (void *)ifp;
119
120 ifp->if_type = IFT_ECONET;
121 ifp->if_addrlen = ECO_ADDR_LEN;
122 ifp->if_hdrlen = ECO_HDR_LEN;
123 ifp->if_dlt = DLT_ECONET;
124 ifp->if_mtu = ECO_MTU;
125
126 ifp->if_output = eco_output;
127 ifp->if_input = eco_input;
128 ifp->if_start = eco_start;
129 ifp->if_ioctl = eco_ioctl;
130
131 /* ifp->if_baudrate...; */
132 if_alloc_sadl(ifp);
133 memcpy(LLADDR(ifp->if_sadl), lla, ifp->if_addrlen);
134
135 ifp->if_broadcastaddr = eco_broadcastaddr;
136
137 LIST_INIT(&ec->ec_retries);
138
139 #if NBPFILTER > 0
140 bpfattach(ifp, ifp->if_dlt, ECO_HDR_LEN);
141 #endif
142 }
143
144 #define senderr(e) do { \
145 error = (e); \
146 goto bad; \
147 } while (/*CONSTCOND*/0)
148
149 int
150 eco_init(struct ifnet *ifp) {
151 struct ecocom *ec = (struct ecocom *)ifp;
152
153 if ((ifp->if_flags & IFF_RUNNING) == 0)
154 ec->ec_state = ECO_UNKNOWN;
155 return 0;
156 }
157
158 void
159 eco_stop(struct ifnet *ifp, int disable)
160 {
161 struct ecocom *ec = (struct ecocom *)ifp;
162
163 while (!LIST_EMPTY(&ec->ec_retries))
164 eco_retry_free(LIST_FIRST(&ec->ec_retries));
165 }
166
167 static int
168 eco_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
169 struct rtentry *rt0)
170 {
171 struct eco_header ehdr, *eh;
172 int error;
173 struct mbuf *m = m0, *mcopy = NULL;
174 struct rtentry *rt;
175 int hdrcmplt;
176 int retry_delay, retry_count;
177 struct m_tag *mtag;
178 struct eco_retryparms *erp;
179 #ifdef INET
180 struct mbuf *m1;
181 struct arphdr *ah;
182 struct eco_arp *ecah;
183 #endif
184 ALTQ_DECL(struct altq_pktattr pktattr;)
185
186 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
187 senderr(ENETDOWN);
188 if ((rt = rt0) != NULL) {
189 if ((rt->rt_flags & RTF_UP) == 0) {
190 if ((rt0 = rt = rtalloc1(dst, 1)) != NULL) {
191 rt->rt_refcnt--;
192 if (rt->rt_ifp != ifp)
193 return (*rt->rt_ifp->if_output)
194 (ifp, m0, dst, rt);
195 } else
196 senderr(EHOSTUNREACH);
197 }
198 if ((rt->rt_flags & RTF_GATEWAY) && dst->sa_family != AF_NS) {
199 if (rt->rt_gwroute == 0)
200 goto lookup;
201 if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
202 rtfree(rt); rt = rt0;
203 lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
204 if ((rt = rt->rt_gwroute) == 0)
205 senderr(EHOSTUNREACH);
206 /* the "G" test below also prevents rt == rt0 */
207 if ((rt->rt_flags & RTF_GATEWAY) ||
208 (rt->rt_ifp != ifp)) {
209 rt->rt_refcnt--;
210 rt0->rt_gwroute = 0;
211 senderr(EHOSTUNREACH);
212 }
213 }
214 }
215 if (rt->rt_flags & RTF_REJECT)
216 if (rt->rt_rmx.rmx_expire == 0 ||
217 time_second < rt->rt_rmx.rmx_expire)
218 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
219 }
220 /*
221 * If the queueing discipline needs packet classification,
222 * do it before prepending link headers.
223 */
224 IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
225
226 hdrcmplt = 0;
227 retry_delay = hz / 16;
228 retry_count = 16;
229 switch (dst->sa_family) {
230 #ifdef INET
231 case AF_INET:
232 if (m->m_flags & M_BCAST)
233 memcpy(ehdr.eco_dhost, eco_broadcastaddr,
234 ECO_ADDR_LEN);
235
236 else if (!arpresolve(ifp, rt, m, dst, ehdr.eco_dhost))
237 return (0); /* if not yet resolved */
238 /* If broadcasting on a simplex interface, loopback a copy */
239 if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
240 mcopy = m_copy(m, 0, (int)M_COPYALL);
241 ehdr.eco_port = ECO_PORT_IP;
242 ehdr.eco_control = ECO_CTL_IP;
243 break;
244
245 case AF_ARP:
246 ah = mtod(m, struct arphdr *);
247
248 if (ntohs(ah->ar_pro) != ETHERTYPE_IP)
249 return EAFNOSUPPORT;
250 ehdr.eco_port = ECO_PORT_IP;
251 switch (ntohs(ah->ar_op)) {
252 case ARPOP_REQUEST:
253 ehdr.eco_control = ECO_CTL_ARP_REQUEST;
254 break;
255 case ARPOP_REPLY:
256 ehdr.eco_control = ECO_CTL_ARP_REPLY;
257 break;
258 default:
259 return EOPNOTSUPP;
260 }
261
262 if (m->m_flags & M_BCAST)
263 memcpy(ehdr.eco_dhost, eco_broadcastaddr,
264 ECO_ADDR_LEN);
265 else
266 memcpy(ehdr.eco_dhost, ar_tha(ah), ECO_ADDR_LEN);
267
268 MGETHDR(m1, M_DONTWAIT, MT_DATA);
269 if (m1 == NULL)
270 senderr(ENOBUFS);
271 M_MOVE_PKTHDR(m1, m);
272 m1->m_len = sizeof(*ecah);
273 m1->m_pkthdr.len = m1->m_len;
274 MH_ALIGN(m1, m1->m_len);
275 ecah = mtod(m1, struct eco_arp *);
276 memset(ecah, 0, m1->m_len);
277 memcpy(ecah->ecar_spa, ar_spa(ah), ah->ar_pln);
278 memcpy(ecah->ecar_tpa, ar_tpa(ah), ah->ar_pln);
279 m_freem(m);
280 m = m1;
281 break;
282 #endif
283 case pseudo_AF_HDRCMPLT:
284 hdrcmplt = 1;
285 /* FALLTHROUGH */
286 case AF_UNSPEC:
287 eh = (struct eco_header *)dst->sa_data;
288 ehdr = *eh;
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, LLADDR(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
332 bad:
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 */
341 static int
342 eco_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
356 static void
357 eco_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 #endif
367
368 #ifdef PFIL_HOOKS
369 if (pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_IN) != 0)
370 return;
371 if (m == NULL)
372 return;
373 #endif
374
375 /* Copy the mbuf header and trim it off. */
376 /* XXX use m_split? */
377 eh = &ehdr;
378 m_copydata(m, 0, ECO_HDR_LEN, (caddr_t)eh);
379 m_adj(m, ECO_HDR_LEN);
380
381 switch (eh->eco_port) {
382 #ifdef INET
383 case ECO_PORT_IP:
384 switch (eh->eco_control) {
385 case ECO_CTL_IP:
386 schednetisr(NETISR_IP);
387 inq = &ipintrq;
388 break;
389 case ECO_CTL_ARP_REQUEST:
390 case ECO_CTL_ARP_REPLY:
391 /*
392 * ARP over Econet is strange, because Econet only
393 * supports 8 bytes of data in a broadcast packet.
394 * To cope with this, only the source and destination
395 * IP addresses are actually contained in the packet
396 * and we have to infer the rest and build a fake ARP
397 * packet to pass upwards.
398 */
399 if (m->m_pkthdr.len != sizeof(struct eco_arp))
400 goto drop;
401 if (m->m_len < sizeof(struct eco_arp)) {
402 m = m_pullup(m, sizeof(struct eco_arp));
403 if (m == NULL) goto drop;
404 }
405 ecah = mtod(m, struct eco_arp *);
406 /* This code derived from arprequest() */
407 MGETHDR(m1, M_DONTWAIT, MT_DATA);
408 if (m1 == NULL)
409 goto drop;
410 M_MOVE_PKTHDR(m1, m);
411 m1->m_len = sizeof(*ah) + 2*sizeof(struct in_addr) +
412 2*ifp->if_data.ifi_addrlen;
413 m1->m_pkthdr.len = m1->m_len;
414 MH_ALIGN(m1, m1->m_len);
415 ah = mtod(m1, struct arphdr *);
416 bzero((caddr_t)ah, m1->m_len);
417 ah->ar_pro = htons(ETHERTYPE_IP);
418 ah->ar_hln = ifp->if_data.ifi_addrlen;
419 ah->ar_pln = sizeof(struct in_addr);
420 if (eh->eco_control == ECO_CTL_ARP_REQUEST)
421 ah->ar_op = htons(ARPOP_REQUEST);
422 else
423 ah->ar_op = htons(ARPOP_REPLY);
424 memcpy(ar_sha(ah), eh->eco_shost, ah->ar_hln);
425 memcpy(ar_tha(ah), eh->eco_dhost, ah->ar_hln);
426 memcpy(ar_spa(ah), ecah->ecar_spa, ah->ar_pln);
427 memcpy(ar_tpa(ah), ecah->ecar_tpa, ah->ar_pln);
428 m_freem(m);
429 m = m1;
430 schednetisr(NETISR_ARP);
431 inq = &arpintrq;
432 break;
433 case ECO_CTL_IPBCAST_REQUEST:
434 {
435 struct sockaddr_storage dst_store;
436 struct sockaddr *dst = (struct sockaddr *)&dst_store;
437
438 /* Queue? */
439 memcpy(eh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
440 eh->eco_control = ECO_CTL_IPBCAST_REPLY;
441 /* dst->sa_len??? */
442 dst->sa_family = AF_UNSPEC;
443 memcpy(dst->sa_data, eh, ECO_HDR_LEN);
444 ifp->if_output(ifp, m, dst, NULL);
445 return;
446 }
447 default:
448 printf("%s: unknown IP stn %s ctl 0x%02x len %d:",
449 ifp->if_xname, eco_sprintf(eh->eco_shost),
450 eh->eco_control, m->m_pkthdr.len);
451 if (m->m_len == 0) {
452 m = m_pullup(m, 1);
453 if (m == 0) {
454 printf("\n");
455 goto drop;
456 }
457 }
458 for (i = 0; i < m->m_len; i++)
459 printf(" %02x", mtod(m, u_int8_t *)[i]);
460 printf("\n");
461 goto drop;
462 }
463 break;
464 #endif
465 default:
466 printf("%s: unknown port stn %s port 0x%02x ctl 0x%02x\n",
467 ifp->if_xname, eco_sprintf(eh->eco_shost),
468 eh->eco_port, eh->eco_control);
469 drop:
470 m_freem(m);
471 return;
472 }
473
474 s = splnet();
475 if (IF_QFULL(inq)) {
476 IF_DROP(inq);
477 m_freem(m);
478 } else
479 IF_ENQUEUE(inq, m);
480 splx(s);
481 }
482
483 static void
484 eco_start(struct ifnet *ifp)
485 {
486 struct ecocom *ec = (void *)ifp;
487 struct mbuf *m;
488 struct eco_header *eh;
489
490 if (ec->ec_state != ECO_IDLE) return;
491 IFQ_DEQUEUE(&ifp->if_snd, m);
492 if (m == NULL) return;
493 if (ec->ec_claimwire(ifp) == 0) {
494 eh = mtod(m, struct eco_header *);
495 if (eh->eco_port == ECO_PORT_IMMEDIATE) {
496 ec->ec_txframe(ifp, m);
497 ec->ec_state = ECO_IMMED_SENT;
498 } else if (eh->eco_dhost[0] == 255) {
499 ec->ec_txframe(ifp, m);
500 ec->ec_state = ECO_DONE;
501 } else {
502 ec->ec_packet = m;
503 m = m_copym(m, 0, ECO_HDR_LEN, M_DONTWAIT);
504 if (m == NULL) {
505 m_freem(ec->ec_packet);
506 ec->ec_packet = NULL;
507 return;
508 }
509 ec->ec_txframe(ifp, m);
510 ec->ec_state = ECO_SCOUT_SENT;
511 }
512 ifp->if_flags |= IFF_OACTIVE;
513 } else {
514 log(LOG_ERR, "%s: line jammed\n", ifp->if_xname);
515 m_freem(m);
516 }
517 }
518
519 static int
520 eco_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
521 {
522 struct ifreq *ifr = (struct ifreq *)data;
523 struct ifaddr *ifa = (struct ifaddr *)data;
524 int error = 0;
525
526 switch (cmd) {
527 case SIOCSIFADDR:
528 ifp->if_flags |= IFF_UP;
529 switch (ifa->ifa_addr->sa_family) {
530 #ifdef INET
531 case AF_INET:
532 if ((ifp->if_flags & IFF_RUNNING) == 0 &&
533 (error = (*ifp->if_init)(ifp)) != 0)
534 break;
535 arp_ifinit(ifp, ifa);
536 break;
537 #endif
538 default:
539 if ((ifp->if_flags & IFF_RUNNING) == 0)
540 error = (*ifp->if_init)(ifp);
541 break;
542 }
543 break;
544 case SIOCSIFMTU:
545 ifp->if_mtu = ifr->ifr_mtu;
546
547 /* Make sure the device notices the MTU change. */
548 if (ifp->if_flags & IFF_UP)
549 error = (*ifp->if_init)(ifp);
550 break;
551 case SIOCSIFFLAGS:
552 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_RUNNING) {
553 /*
554 * If interface is marked down and it is running,
555 * then stop and disable it.
556 */
557 (*ifp->if_stop)(ifp, 1);
558 } else if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_UP) {
559 /*
560 * If interface is marked up and it is stopped, then
561 * start it.
562 */
563 error = (*ifp->if_init)(ifp);
564 } else if ((ifp->if_flags & IFF_UP) != 0) {
565 /*
566 * Reset the interface to pick up changes in any other
567 * flags that affect the hardware state.
568 */
569 error = (*ifp->if_init)(ifp);
570 }
571 break;
572 default:
573 error = ENOTTY;
574 }
575
576 return error;
577 }
578
579 /*
580 * Handle a raw Econet frame off the interface. The interface may be
581 * flag-filling for a response.
582 *
583 * May be called from IPL_NET or IPL_SOFTNET.
584 */
585
586 struct mbuf *
587 eco_inputframe(struct ifnet *ifp, struct mbuf *m)
588 {
589 struct ecocom *ec = (void *)ifp;
590 struct eco_header *eh, *eh0;
591 struct mbuf *m0;
592 struct mbuf *reply;
593 int len;
594
595 eh = mtod(m, struct eco_header *);
596 switch (ec->ec_state) {
597 case ECO_IDLE: /* Start of a packet (bcast, immed, scout) */
598 if (m->m_pkthdr.len < ECO_HDR_LEN) {
599 log(LOG_NOTICE, "%s: undersize scout\n",
600 ifp->if_xname);
601 goto drop;
602 }
603 if (memcmp(eh->eco_dhost, eco_broadcastaddr,
604 ECO_ADDR_LEN) == 0) {
605 /* Broadcast */
606 eco_input(ifp, m);
607 } else if (memcmp(eh->eco_dhost, LLADDR(ifp->if_sadl),
608 ECO_ADDR_LEN) == 0) {
609 /* Unicast for us */
610 if (eh->eco_port == ECO_PORT_IMMEDIATE)
611 return eco_immediate(ifp, m);
612 else {
613 if (eco_interestingp(ifp, m)) {
614 reply = eco_ack(ifp, m);
615 if (reply == NULL) {
616 m_freem(m);
617 return NULL;
618 }
619 ec->ec_state = ECO_SCOUT_RCVD;
620 ec->ec_scout = m;
621 return reply;
622 } else {
623 m_freem(m);
624 return NULL;
625 }
626 }
627 } else
628 /* Not for us. Throw it away. */
629 m_freem(m);
630 break;
631 case ECO_SCOUT_RCVD: /* Packet data */
632 KASSERT(ec->ec_scout != NULL);
633 m0 = ec->ec_scout;
634 eh0 = mtod(m0, struct eco_header *);
635 if (m->m_pkthdr.len < ECO_SHDR_LEN ||
636 memcmp(eh->eco_shost, eh0->eco_shost, ECO_ADDR_LEN) != 0 ||
637 memcmp(eh->eco_dhost, eh0->eco_dhost, ECO_ADDR_LEN) != 0) {
638 log(LOG_NOTICE, "%s: garbled data packet header\n",
639 ifp->if_xname);
640 goto drop;
641 }
642 reply = eco_ack(ifp, m);
643 /*
644 * Chop off the small header from this frame, and put
645 * the scout (which holds the control byte and port)
646 * in its place.
647 */
648 ec->ec_scout = NULL;
649 m_adj(m, ECO_SHDR_LEN);
650 len = m0->m_pkthdr.len + m->m_pkthdr.len;
651 m_cat(m0, m);
652 m0->m_pkthdr.len = len;
653 ec->ec_state = ECO_DONE;
654 eco_input(ifp, m0);
655 return reply;
656 case ECO_SCOUT_SENT: /* Scout ack */
657 KASSERT(ec->ec_packet != NULL);
658 m0 = ec->ec_packet;
659 eh0 = mtod(m0, struct eco_header *);
660 if (m->m_pkthdr.len != ECO_SHDR_LEN ||
661 memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
662 memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
663 log(LOG_NOTICE, "%s: garbled scout ack\n",
664 ifp->if_xname);
665 goto drop;
666 }
667 m_freem(m);
668 /* Chop out the control and port bytes. */
669 m0 = m_copym(ec->ec_packet, 0, ECO_SHDR_LEN, M_DONTWAIT);
670 if (m0 == NULL) {
671 m_freem(ec->ec_packet);
672 return NULL;
673 }
674 m = ec->ec_packet;
675 ec->ec_packet = m_copypacket(m, M_DONTWAIT);
676 if (ec->ec_packet == NULL) {
677 m_freem(m0);
678 m_freem(m);
679 return NULL;
680 }
681 m_adj(m, ECO_HDR_LEN);
682 len = m0->m_pkthdr.len + m->m_pkthdr.len;
683 m_cat(m0, m); /* Doesn't update packet header */
684 m0->m_pkthdr.len = len;
685 ec->ec_state = ECO_DATA_SENT;
686 return m0;
687 case ECO_DATA_SENT: /* Data ack */
688 KASSERT(ec->ec_packet != NULL);
689 m0 = ec->ec_packet;
690 eh0 = mtod(m0, struct eco_header *);
691 if (m->m_pkthdr.len != ECO_SHDR_LEN ||
692 memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
693 memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
694 log(LOG_NOTICE, "%s: garbled data ack\n",
695 ifp->if_xname);
696 goto drop;
697 }
698 m_freem(m);
699 m_freem(ec->ec_packet);
700 ec->ec_packet = NULL;
701 ec->ec_state = ECO_DONE;
702 return NULL;
703 default:
704 drop:
705 m_freem(m);
706 break;
707 }
708 return NULL;
709 }
710
711 /*
712 * Handle an immediate operation, and return the reply, or NULL not to reply.
713 * Frees the incoming mbuf.
714 */
715
716 static struct mbuf *
717 eco_immediate(struct ifnet *ifp, struct mbuf *m)
718 {
719 struct eco_header *eh, *reh;
720 struct mbuf *n;
721 static const u_int8_t machinepeek_data[] = { 42, 0, 0, 1 };
722
723 eh = mtod(m, struct eco_header *);
724 switch (eh->eco_control) {
725 case ECO_CTL_MACHINEPEEK:
726 MGETHDR(n, M_DONTWAIT, MT_DATA);
727 if (n == NULL)
728 goto bad;
729 n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN + 4;
730 reh = mtod(n, struct eco_header *);
731 memcpy(reh->eco_dhost, eh->eco_shost,
732 ECO_ADDR_LEN);
733 memcpy(reh->eco_shost, LLADDR(ifp->if_sadl),
734 ECO_ADDR_LEN);
735 memcpy(mtod(n, caddr_t) + ECO_SHDR_LEN, machinepeek_data,
736 sizeof(machinepeek_data));
737 m_freem(m);
738 return n;
739 default:
740 bad:
741 m_freem(m);
742 return NULL;
743 }
744 }
745
746 /*
747 * Generate (and return) an acknowledgement for a frame. Doesn't free the
748 * original frame, since it's probably needed elsewhere.
749 */
750 static struct mbuf *
751 eco_ack(struct ifnet *ifp, struct mbuf *m)
752 {
753 struct eco_header *eh, *reh;
754 struct mbuf *n;
755
756 eh = mtod(m, struct eco_header *);
757 MGETHDR(n, M_DONTWAIT, MT_DATA);
758 if (n == NULL)
759 return NULL;
760 n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN;
761 reh = mtod(n, struct eco_header *);
762 memcpy(reh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
763 memcpy(reh->eco_shost, LLADDR(ifp->if_sadl), ECO_ADDR_LEN);
764 return n;
765 }
766
767 void
768 eco_inputidle(struct ifnet *ifp)
769 {
770 struct ecocom *ec = (void *)ifp;
771 struct mbuf *m;
772 struct m_tag *mtag;
773 struct eco_retryparms *erp;
774
775 switch (ec->ec_state) {
776 case ECO_SCOUT_SENT:
777 case ECO_DATA_SENT:
778 case ECO_IMMED_SENT:
779 /* Outgoing packet failed. Check if we should retry. */
780 m = ec->ec_packet;
781 ec->ec_packet = NULL;
782 mtag = m_tag_find(m, PACKET_TAG_ECO_RETRYPARMS, NULL);
783 if (mtag == NULL)
784 m_freem(m);
785 else {
786 erp = (struct eco_retryparms *)(mtag + 1);
787 if (--erp->erp_count > 0)
788 eco_defer(ifp, m, erp->erp_delay);
789 else
790 printf("%s: pkt failed\n", ifp->if_xname);
791 }
792 break;
793 case ECO_SCOUT_RCVD:
794 m_freem(ec->ec_scout);
795 ec->ec_scout = NULL;
796 break;
797 default:
798 break;
799 }
800 ec->ec_state = ECO_IDLE;
801 ifp->if_start(ifp);
802 }
803
804 /*
805 * Convert Econet address to printable (loggable) representation.
806 */
807 char *
808 eco_sprintf(const u_int8_t *ea)
809 {
810 static char buf[8];
811
812 if (ea[1] == 0)
813 snprintf(buf, sizeof(buf), "%d", ea[0]);
814 else
815 snprintf(buf, sizeof(buf), "%d.%d", ea[1], ea[0]);
816 return buf;
817 }
818
819 /*
820 * Econet retry handling.
821 */
822 static void
823 eco_defer(struct ifnet *ifp, struct mbuf *m, int retry_delay)
824 {
825 struct ecocom *ec = (struct ecocom *)ifp;
826 struct eco_retry *er;
827 int s;
828
829 MALLOC(er, struct eco_retry *, sizeof(*er), M_TEMP, M_NOWAIT);
830 if (er == NULL) {
831 m_freem(m);
832 return;
833 }
834 callout_init(&er->er_callout);
835 er->er_packet = m;
836 er->er_ifp = ifp;
837 s = splnet();
838 LIST_INSERT_HEAD(&ec->ec_retries, er, er_link);
839 splx(s);
840 callout_reset(&er->er_callout, retry_delay, eco_retry, er);
841 }
842
843 static void
844 eco_retry_free(struct eco_retry *er)
845 {
846 int s;
847
848 callout_stop(&er->er_callout);
849 m_freem(er->er_packet);
850 s = splnet();
851 LIST_REMOVE(er, er_link);
852 splx(s);
853 FREE(er, M_TEMP);
854 }
855
856 static void
857 eco_retry(void *arg)
858 {
859 struct eco_retry *er = arg;
860 struct mbuf *m;
861 struct ifnet *ifp;
862
863 ifp = er->er_ifp;
864 m = er->er_packet;
865 LIST_REMOVE(er, er_link);
866 (void)ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(NULL));
867 FREE(er, M_TEMP);
868 }
Cache object: 5b5128540c944ed356b9c5455563f9f6
|