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