FreeBSD/Linux Kernel Cross Reference
sys/net/if_ecosubr.c
1 /* $NetBSD: if_ecosubr.c,v 1.28 2008/03/12 18:22:24 dyoung 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.28 2008/03/12 18:22:24 dyoung 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 uint8_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, void *);
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 uint8_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 (void)sockaddr_dl_setaddr(ifp->if_sadl, ifp->if_sadl->sdl_len, lla,
134 ifp->if_addrlen);
135
136 ifp->if_broadcastaddr = eco_broadcastaddr;
137
138 LIST_INIT(&ec->ec_retries);
139
140 #if NBPFILTER > 0
141 bpfattach(ifp, ifp->if_dlt, ECO_HDR_LEN);
142 #endif
143 }
144
145 #define senderr(e) do { \
146 error = (e); \
147 goto bad; \
148 } while (/*CONSTCOND*/0)
149
150 int
151 eco_init(struct ifnet *ifp) {
152 struct ecocom *ec = (struct ecocom *)ifp;
153
154 if ((ifp->if_flags & IFF_RUNNING) == 0)
155 ec->ec_state = ECO_UNKNOWN;
156 return 0;
157 }
158
159 void
160 eco_stop(struct ifnet *ifp, int disable)
161 {
162 struct ecocom *ec = (struct ecocom *)ifp;
163
164 while (!LIST_EMPTY(&ec->ec_retries))
165 eco_retry_free(LIST_FIRST(&ec->ec_retries));
166 }
167
168 static int
169 eco_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
170 struct rtentry *rt0)
171 {
172 struct eco_header ehdr, *eh;
173 int error;
174 struct mbuf *m = m0, *mcopy = NULL;
175 struct rtentry *rt;
176 int hdrcmplt;
177 int retry_delay, retry_count;
178 struct m_tag *mtag;
179 struct eco_retryparms *erp;
180 #ifdef INET
181 struct mbuf *m1;
182 struct arphdr *ah;
183 struct eco_arp *ecah;
184 #endif
185 ALTQ_DECL(struct altq_pktattr pktattr;)
186
187 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
188 senderr(ENETDOWN);
189 if ((rt = rt0) != NULL) {
190 if ((rt->rt_flags & RTF_UP) == 0) {
191 if ((rt0 = rt = rtalloc1(dst, 1)) != NULL) {
192 rt->rt_refcnt--;
193 if (rt->rt_ifp != ifp)
194 return (*rt->rt_ifp->if_output)
195 (ifp, m0, dst, rt);
196 } else
197 senderr(EHOSTUNREACH);
198 }
199 if ((rt->rt_flags & RTF_GATEWAY) && dst->sa_family != AF_NS) {
200 if (rt->rt_gwroute == 0)
201 goto lookup;
202 if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
203 rtfree(rt); rt = rt0;
204 lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
205 if ((rt = rt->rt_gwroute) == 0)
206 senderr(EHOSTUNREACH);
207 /* the "G" test below also prevents rt == rt0 */
208 if ((rt->rt_flags & RTF_GATEWAY) ||
209 (rt->rt_ifp != ifp)) {
210 rt->rt_refcnt--;
211 rt0->rt_gwroute = 0;
212 senderr(EHOSTUNREACH);
213 }
214 }
215 }
216 if (rt->rt_flags & RTF_REJECT)
217 if (rt->rt_rmx.rmx_expire == 0 ||
218 time_second < rt->rt_rmx.rmx_expire)
219 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
220 }
221 /*
222 * If the queueing discipline needs packet classification,
223 * do it before prepending link headers.
224 */
225 IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
226
227 hdrcmplt = 0;
228 retry_delay = hz / 16;
229 retry_count = 16;
230 switch (dst->sa_family) {
231 #ifdef INET
232 case AF_INET:
233 if (m->m_flags & M_BCAST)
234 memcpy(ehdr.eco_dhost, eco_broadcastaddr,
235 ECO_ADDR_LEN);
236
237 else if (!arpresolve(ifp, rt, m, dst, ehdr.eco_dhost))
238 return (0); /* if not yet resolved */
239 /* If broadcasting on a simplex interface, loopback a copy */
240 if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
241 mcopy = m_copy(m, 0, (int)M_COPYALL);
242 ehdr.eco_port = ECO_PORT_IP;
243 ehdr.eco_control = ECO_CTL_IP;
244 break;
245
246 case AF_ARP:
247 ah = mtod(m, struct arphdr *);
248
249 if (ntohs(ah->ar_pro) != ETHERTYPE_IP)
250 return EAFNOSUPPORT;
251 ehdr.eco_port = ECO_PORT_IP;
252 switch (ntohs(ah->ar_op)) {
253 case ARPOP_REQUEST:
254 ehdr.eco_control = ECO_CTL_ARP_REQUEST;
255 break;
256 case ARPOP_REPLY:
257 ehdr.eco_control = ECO_CTL_ARP_REPLY;
258 break;
259 default:
260 return EOPNOTSUPP;
261 }
262
263 if (m->m_flags & M_BCAST)
264 memcpy(ehdr.eco_dhost, eco_broadcastaddr,
265 ECO_ADDR_LEN);
266 else
267 memcpy(ehdr.eco_dhost, ar_tha(ah), ECO_ADDR_LEN);
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 eh = (struct eco_header *)dst->sa_data;
289 ehdr = *eh;
290 break;
291 default:
292 log(LOG_ERR, "%s: can't handle af%d\n", ifp->if_xname,
293 dst->sa_family);
294 senderr(EAFNOSUPPORT);
295 }
296
297 if (mcopy)
298 (void) looutput(ifp, mcopy, dst, rt);
299
300 /*
301 * Add local net header. If no space in first mbuf,
302 * allocate another.
303 */
304 M_PREPEND(m, sizeof (struct eco_header), M_DONTWAIT);
305 if (m == 0)
306 senderr(ENOBUFS);
307 eh = mtod(m, struct eco_header *);
308 *eh = ehdr;
309 if (!hdrcmplt)
310 memcpy(eh->eco_shost, CLLADDR(ifp->if_sadl),
311 ECO_ADDR_LEN);
312
313 if ((m->m_flags & M_BCAST) == 0) {
314 /* Attach retry info to packet. */
315 mtag = m_tag_get(PACKET_TAG_ECO_RETRYPARMS,
316 sizeof(struct eco_retryparms), M_NOWAIT);
317 if (mtag == NULL)
318 senderr(ENOBUFS);
319 erp = (struct eco_retryparms *)(mtag + 1);
320 erp->erp_delay = retry_delay;
321 erp->erp_count = retry_count;
322 }
323
324 #ifdef PFIL_HOOKS
325 if ((error = pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_OUT)) != 0)
326 return (error);
327 if (m == NULL)
328 return (0);
329 #endif
330
331 return ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(&pktattr));
332
333 bad:
334 if (m)
335 m_freem(m);
336 return error;
337 }
338
339 /*
340 * Given a scout, decide if we want the rest of the packet.
341 */
342 static int
343 eco_interestingp(struct ifnet *ifp, struct mbuf *m)
344 {
345 struct eco_header *eh;
346
347 eh = mtod(m, struct eco_header *);
348 switch (eh->eco_port) {
349 #ifdef INET
350 case ECO_PORT_IP:
351 return 1;
352 #endif
353 }
354 return 0;
355 }
356
357 static void
358 eco_input(struct ifnet *ifp, struct mbuf *m)
359 {
360 struct ifqueue *inq;
361 struct eco_header ehdr, *eh;
362 int s, i;
363 #ifdef INET
364 struct arphdr *ah;
365 struct eco_arp *ecah;
366 struct mbuf *m1;
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 bzero((void *)ah, 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 memcpy(ar_sha(ah), eh->eco_shost, ah->ar_hln);
426 memcpy(ar_tha(ah), eh->eco_dhost, ah->ar_hln);
427 memcpy(ar_spa(ah), ecah->ecar_spa, ah->ar_pln);
428 memcpy(ar_tpa(ah), ecah->ecar_tpa, ah->ar_pln);
429 m_freem(m);
430 m = m1;
431 schednetisr(NETISR_ARP);
432 inq = &arpintrq;
433 break;
434 case ECO_CTL_IPBCAST_REQUEST:
435 {
436 struct sockaddr_storage dst_store;
437 struct sockaddr *dst = (struct sockaddr *)&dst_store;
438
439 /* Queue? */
440 memcpy(eh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
441 eh->eco_control = ECO_CTL_IPBCAST_REPLY;
442 /* dst->sa_len??? */
443 dst->sa_family = AF_UNSPEC;
444 memcpy(dst->sa_data, eh, ECO_HDR_LEN);
445 ifp->if_output(ifp, m, dst, NULL);
446 return;
447 }
448 default:
449 printf("%s: unknown IP stn %s ctl 0x%02x len %d:",
450 ifp->if_xname, eco_sprintf(eh->eco_shost),
451 eh->eco_control, m->m_pkthdr.len);
452 if (m->m_len == 0) {
453 m = m_pullup(m, 1);
454 if (m == 0) {
455 printf("\n");
456 goto drop;
457 }
458 }
459 for (i = 0; i < m->m_len; i++)
460 printf(" %02x", mtod(m, uint8_t *)[i]);
461 printf("\n");
462 goto drop;
463 }
464 break;
465 #endif
466 default:
467 printf("%s: unknown port stn %s port 0x%02x ctl 0x%02x\n",
468 ifp->if_xname, eco_sprintf(eh->eco_shost),
469 eh->eco_port, eh->eco_control);
470 drop:
471 m_freem(m);
472 return;
473 }
474
475 s = splnet();
476 if (IF_QFULL(inq)) {
477 IF_DROP(inq);
478 m_freem(m);
479 } else
480 IF_ENQUEUE(inq, m);
481 splx(s);
482 }
483
484 static void
485 eco_start(struct ifnet *ifp)
486 {
487 struct ecocom *ec = (void *)ifp;
488 struct mbuf *m;
489 struct eco_header *eh;
490
491 if (ec->ec_state != ECO_IDLE) return;
492 IFQ_DEQUEUE(&ifp->if_snd, m);
493 if (m == NULL) return;
494 if (ec->ec_claimwire(ifp) == 0) {
495 eh = mtod(m, struct eco_header *);
496 if (eh->eco_port == ECO_PORT_IMMEDIATE) {
497 ec->ec_txframe(ifp, m);
498 ec->ec_state = ECO_IMMED_SENT;
499 } else if (eh->eco_dhost[0] == 255) {
500 ec->ec_txframe(ifp, m);
501 ec->ec_state = ECO_DONE;
502 } else {
503 ec->ec_packet = m;
504 m = m_copym(m, 0, ECO_HDR_LEN, M_DONTWAIT);
505 if (m == NULL) {
506 m_freem(ec->ec_packet);
507 ec->ec_packet = NULL;
508 return;
509 }
510 ec->ec_txframe(ifp, m);
511 ec->ec_state = ECO_SCOUT_SENT;
512 }
513 ifp->if_flags |= IFF_OACTIVE;
514 } else {
515 log(LOG_ERR, "%s: line jammed\n", ifp->if_xname);
516 m_freem(m);
517 }
518 }
519
520 static int
521 eco_ioctl(struct ifnet *ifp, u_long cmd, void *data)
522 {
523 struct ifreq *ifr = (struct ifreq *)data;
524 struct ifaddr *ifa = (struct ifaddr *)data;
525 int error;
526
527 switch (cmd) {
528 case SIOCSIFADDR:
529 ifp->if_flags |= IFF_UP;
530 switch (ifa->ifa_addr->sa_family) {
531 #ifdef INET
532 case AF_INET:
533 if ((ifp->if_flags & IFF_RUNNING) == 0 &&
534 (error = (*ifp->if_init)(ifp)) != 0)
535 return error;
536 arp_ifinit(ifp, ifa);
537 break;
538 #endif
539 default:
540 if ((ifp->if_flags & IFF_RUNNING) == 0)
541 return (*ifp->if_init)(ifp);
542 break;
543 }
544 return 0;
545 case SIOCSIFMTU:
546 if ((error = ifioctl_common(ifp, command, data)) != ENETRESET)
547 return error;
548 else if (ifp->if_flags & IFF_UP)
549 return (*ifp->if_init)(ifp);
550 else
551 return 0;
552 break;
553 case SIOCSIFFLAGS:
554 switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) {
555 case IFF_RUNNING:
556 /*
557 * If interface is marked down and it is running,
558 * then stop and disable it.
559 */
560 (*ifp->if_stop)(ifp, 1);
561 return 0;
562 case IFF_UP:
563 /*
564 * If interface is marked up and it is stopped, then
565 * start it.
566 */
567 return (*ifp->if_init)(ifp);
568 case IFF_UP|IFF_RUNNING:
569 /*
570 * Reset the interface to pick up changes in any other
571 * flags that affect the hardware state.
572 */
573 return (*ifp->if_init)(ifp);
574 case 0:
575 return 0;
576 }
577 break;
578 default:
579 return ENOTTY;
580 }
581
582 return 0;
583 }
584
585 /*
586 * Handle a raw Econet frame off the interface. The interface may be
587 * flag-filling for a response.
588 *
589 * May be called from IPL_NET or IPL_SOFTNET.
590 */
591
592 struct mbuf *
593 eco_inputframe(struct ifnet *ifp, struct mbuf *m)
594 {
595 struct ecocom *ec = (void *)ifp;
596 struct eco_header *eh, *eh0;
597 struct mbuf *m0;
598 struct mbuf *reply;
599 int len;
600
601 eh = mtod(m, struct eco_header *);
602 switch (ec->ec_state) {
603 case ECO_IDLE: /* Start of a packet (bcast, immed, scout) */
604 if (m->m_pkthdr.len < ECO_HDR_LEN) {
605 log(LOG_NOTICE, "%s: undersize scout\n",
606 ifp->if_xname);
607 goto drop;
608 }
609 if (memcmp(eh->eco_dhost, eco_broadcastaddr,
610 ECO_ADDR_LEN) == 0) {
611 /* Broadcast */
612 eco_input(ifp, m);
613 } else if (memcmp(eh->eco_dhost, CLLADDR(ifp->if_sadl),
614 ECO_ADDR_LEN) == 0) {
615 /* Unicast for us */
616 if (eh->eco_port == ECO_PORT_IMMEDIATE)
617 return eco_immediate(ifp, m);
618 else {
619 if (eco_interestingp(ifp, m)) {
620 reply = eco_ack(ifp, m);
621 if (reply == NULL) {
622 m_freem(m);
623 return NULL;
624 }
625 ec->ec_state = ECO_SCOUT_RCVD;
626 ec->ec_scout = m;
627 return reply;
628 } else {
629 m_freem(m);
630 return NULL;
631 }
632 }
633 } else
634 /* Not for us. Throw it away. */
635 m_freem(m);
636 break;
637 case ECO_SCOUT_RCVD: /* Packet data */
638 KASSERT(ec->ec_scout != NULL);
639 m0 = ec->ec_scout;
640 eh0 = mtod(m0, struct eco_header *);
641 if (m->m_pkthdr.len < ECO_SHDR_LEN ||
642 memcmp(eh->eco_shost, eh0->eco_shost, ECO_ADDR_LEN) != 0 ||
643 memcmp(eh->eco_dhost, eh0->eco_dhost, ECO_ADDR_LEN) != 0) {
644 log(LOG_NOTICE, "%s: garbled data packet header\n",
645 ifp->if_xname);
646 goto drop;
647 }
648 reply = eco_ack(ifp, m);
649 /*
650 * Chop off the small header from this frame, and put
651 * the scout (which holds the control byte and port)
652 * in its place.
653 */
654 ec->ec_scout = NULL;
655 m_adj(m, ECO_SHDR_LEN);
656 len = m0->m_pkthdr.len + m->m_pkthdr.len;
657 m_cat(m0, m);
658 m0->m_pkthdr.len = len;
659 ec->ec_state = ECO_DONE;
660 eco_input(ifp, m0);
661 return reply;
662 case ECO_SCOUT_SENT: /* Scout ack */
663 KASSERT(ec->ec_packet != NULL);
664 m0 = ec->ec_packet;
665 eh0 = mtod(m0, struct eco_header *);
666 if (m->m_pkthdr.len != ECO_SHDR_LEN ||
667 memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
668 memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
669 log(LOG_NOTICE, "%s: garbled scout ack\n",
670 ifp->if_xname);
671 goto drop;
672 }
673 m_freem(m);
674 /* Chop out the control and port bytes. */
675 m0 = m_copym(ec->ec_packet, 0, ECO_SHDR_LEN, M_DONTWAIT);
676 if (m0 == NULL) {
677 m_freem(ec->ec_packet);
678 return NULL;
679 }
680 m = ec->ec_packet;
681 ec->ec_packet = m_copypacket(m, M_DONTWAIT);
682 if (ec->ec_packet == NULL) {
683 m_freem(m0);
684 m_freem(m);
685 return NULL;
686 }
687 m_adj(m, ECO_HDR_LEN);
688 len = m0->m_pkthdr.len + m->m_pkthdr.len;
689 m_cat(m0, m); /* Doesn't update packet header */
690 m0->m_pkthdr.len = len;
691 ec->ec_state = ECO_DATA_SENT;
692 return m0;
693 case ECO_DATA_SENT: /* Data ack */
694 KASSERT(ec->ec_packet != NULL);
695 m0 = ec->ec_packet;
696 eh0 = mtod(m0, struct eco_header *);
697 if (m->m_pkthdr.len != ECO_SHDR_LEN ||
698 memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
699 memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
700 log(LOG_NOTICE, "%s: garbled data ack\n",
701 ifp->if_xname);
702 goto drop;
703 }
704 m_freem(m);
705 m_freem(ec->ec_packet);
706 ec->ec_packet = NULL;
707 ec->ec_state = ECO_DONE;
708 return NULL;
709 default:
710 drop:
711 m_freem(m);
712 break;
713 }
714 return NULL;
715 }
716
717 /*
718 * Handle an immediate operation, and return the reply, or NULL not to reply.
719 * Frees the incoming mbuf.
720 */
721
722 static struct mbuf *
723 eco_immediate(struct ifnet *ifp, struct mbuf *m)
724 {
725 struct eco_header *eh, *reh;
726 struct mbuf *n;
727 static const uint8_t machinepeek_data[] = { 42, 0, 0, 1 };
728
729 eh = mtod(m, struct eco_header *);
730 switch (eh->eco_control) {
731 case ECO_CTL_MACHINEPEEK:
732 MGETHDR(n, M_DONTWAIT, MT_DATA);
733 if (n == NULL)
734 goto bad;
735 n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN + 4;
736 reh = mtod(n, struct eco_header *);
737 memcpy(reh->eco_dhost, eh->eco_shost,
738 ECO_ADDR_LEN);
739 memcpy(reh->eco_shost, CLLADDR(ifp->if_sadl),
740 ECO_ADDR_LEN);
741 memcpy(mtod(n, void *) + ECO_SHDR_LEN, machinepeek_data,
742 sizeof(machinepeek_data));
743 m_freem(m);
744 return n;
745 default:
746 bad:
747 m_freem(m);
748 return NULL;
749 }
750 }
751
752 /*
753 * Generate (and return) an acknowledgement for a frame. Doesn't free the
754 * original frame, since it's probably needed elsewhere.
755 */
756 static struct mbuf *
757 eco_ack(struct ifnet *ifp, struct mbuf *m)
758 {
759 struct eco_header *eh, *reh;
760 struct mbuf *n;
761
762 eh = mtod(m, struct eco_header *);
763 MGETHDR(n, M_DONTWAIT, MT_DATA);
764 if (n == NULL)
765 return NULL;
766 n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN;
767 reh = mtod(n, struct eco_header *);
768 memcpy(reh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
769 memcpy(reh->eco_shost, CLLADDR(ifp->if_sadl), ECO_ADDR_LEN);
770 return n;
771 }
772
773 void
774 eco_inputidle(struct ifnet *ifp)
775 {
776 struct ecocom *ec = (void *)ifp;
777 struct mbuf *m;
778 struct m_tag *mtag;
779 struct eco_retryparms *erp;
780
781 switch (ec->ec_state) {
782 case ECO_SCOUT_SENT:
783 case ECO_DATA_SENT:
784 case ECO_IMMED_SENT:
785 /* Outgoing packet failed. Check if we should retry. */
786 m = ec->ec_packet;
787 ec->ec_packet = NULL;
788 mtag = m_tag_find(m, PACKET_TAG_ECO_RETRYPARMS, NULL);
789 if (mtag == NULL)
790 m_freem(m);
791 else {
792 erp = (struct eco_retryparms *)(mtag + 1);
793 if (--erp->erp_count > 0)
794 eco_defer(ifp, m, erp->erp_delay);
795 else
796 printf("%s: pkt failed\n", ifp->if_xname);
797 }
798 break;
799 case ECO_SCOUT_RCVD:
800 m_freem(ec->ec_scout);
801 ec->ec_scout = NULL;
802 break;
803 default:
804 break;
805 }
806 ec->ec_state = ECO_IDLE;
807 ifp->if_start(ifp);
808 }
809
810 /*
811 * Convert Econet address to printable (loggable) representation.
812 */
813 char *
814 eco_sprintf(const uint8_t *ea)
815 {
816 static char buf[8];
817
818 if (ea[1] == 0)
819 snprintf(buf, sizeof(buf), "%d", ea[0]);
820 else
821 snprintf(buf, sizeof(buf), "%d.%d", ea[1], ea[0]);
822 return buf;
823 }
824
825 /*
826 * Econet retry handling.
827 */
828 static void
829 eco_defer(struct ifnet *ifp, struct mbuf *m, int retry_delay)
830 {
831 struct ecocom *ec = (struct ecocom *)ifp;
832 struct eco_retry *er;
833 int s;
834
835 MALLOC(er, struct eco_retry *, sizeof(*er), M_TEMP, M_NOWAIT);
836 if (er == NULL) {
837 m_freem(m);
838 return;
839 }
840 callout_init(&er->er_callout, 0);
841 er->er_packet = m;
842 er->er_ifp = ifp;
843 s = splnet();
844 LIST_INSERT_HEAD(&ec->ec_retries, er, er_link);
845 splx(s);
846 callout_reset(&er->er_callout, retry_delay, eco_retry, er);
847 }
848
849 static void
850 eco_retry_free(struct eco_retry *er)
851 {
852 int s;
853
854 callout_stop(&er->er_callout);
855 m_freem(er->er_packet);
856 s = splnet();
857 LIST_REMOVE(er, er_link);
858 splx(s);
859 callout_destroy(&er->er_callout);
860 FREE(er, M_TEMP);
861 }
862
863 static void
864 eco_retry(void *arg)
865 {
866 struct eco_retry *er = arg;
867 struct mbuf *m;
868 struct ifnet *ifp;
869
870 ifp = er->er_ifp;
871 m = er->er_packet;
872 LIST_REMOVE(er, er_link);
873 (void)ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(NULL));
874 FREE(er, M_TEMP);
875 }
Cache object: fa3153e0fff8473930be3c19c118260c
|