1/* $NetBSD: if_eon.c,v 1.70 2011/03/09 22:04:52 dyoung Exp $ */ 2 3/*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)if_eon.c 8.2 (Berkeley) 1/9/95 32 */ 33 34/*********************************************************** 35 Copyright IBM Corporation 1987 36 37 All Rights Reserved 38 39Permission to use, copy, modify, and distribute this software and its 40documentation for any purpose and without fee is hereby granted, 41provided that the above copyright notice appear in all copies and that 42both that copyright notice and this permission notice appear in 43supporting documentation, and that the name of IBM not be 44used in advertising or publicity pertaining to distribution of the 45software without specific, written prior permission. 46 47IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 48ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 49IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 50ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 51WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 52ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 53SOFTWARE. 54 55******************************************************************/ 56 57/* 58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 59 */ 60/* 61 * EON rfc 62 * Layer between IP and CLNL 63 * 64 * TODO: 65 * Put together a current rfc986 address format and get the right offset 66 * for the nsel 67 */ 68 69#include <sys/cdefs.h> 70__KERNEL_RCSID(0, "$NetBSD: if_eon.c,v 1.70 2011/03/09 22:04:52 dyoung Exp $"); 71 72#include "opt_eon.h" 73 74#ifdef EON 75#define NEON 1 76 77 78#include <sys/param.h> 79#include <sys/systm.h> 80#include <sys/mbuf.h> 81#include <sys/buf.h> 82#include <sys/protosw.h> 83#include <sys/socket.h> 84#include <sys/ioctl.h> 85#include <sys/errno.h> 86 87#include <sys/cpu.h> /* XXX for setsoftnet(). This must die. */ 88 89#include <net/if.h> 90#include <net/if_types.h> 91#include <net/if_dl.h> 92#include <net/netisr.h> 93#include <net/route.h> 94 95#include <net/if_ether.h> 96 97#include <netinet/in.h> 98#include <netinet/in_systm.h> 99#include <netinet/in_var.h> 100#include <netinet/ip.h> 101#include <netinet/ip_var.h> 102 103#include <netiso/iso.h> 104#include <netiso/iso_var.h> 105#include <netiso/iso_snpac.h> 106#include <netiso/argo_debug.h> 107#include <netiso/iso_errno.h> 108#include <netiso/eonvar.h> 109 110#define EOK 0 111 112struct ifnet eonif[1]; 113 114void 115eonprotoinit(void) 116{ 117 (void) eonattach(); 118} 119 120struct eon_llinfo eon_llinfo; 121#define PROBE_OK 0; 122 123 124/* 125 * FUNCTION: eonattach 126 * 127 * PURPOSE: autoconf attach routine 128 * 129 * RETURNS: void 130 */ 131 132void 133eonattach(void) 134{ 135 struct ifnet *ifp = eonif; 136 137#ifdef ARGO_DEBUG 138 if (argo_debug[D_EON]) { 139 printf("eonattach()\n"); 140 } 141#endif 142 snprintf(ifp->if_xname, sizeof(ifp->if_xname), "eon%d", 0); 143 ifp->if_mtu = ETHERMTU; 144 ifp->if_softc = NULL; 145 /* since everything will go out over ether or token ring */ 146 147 ifp->if_ioctl = eonioctl; 148 ifp->if_output = eonoutput; 149 ifp->if_type = IFT_EON; 150 ifp->if_addrlen = 0; 151 ifp->if_hdrlen = EONIPLEN; 152 ifp->if_flags = IFF_BROADCAST; 153 if_attach(ifp); 154 if_alloc_sadl(ifp); 155 eonioctl(ifp, SIOCINITIFADDR, ifp->if_dl); 156 eon_llinfo.el_qhdr.link = 157 eon_llinfo.el_qhdr.rlink = &(eon_llinfo.el_qhdr); 158 159#ifdef ARGO_DEBUG 160 if (argo_debug[D_EON]) { 161 printf("eonattach()\n"); 162 } 163#endif 164} 165 166 167/* 168 * FUNCTION: eonioctl 169 * 170 * PURPOSE: io controls - ifconfig 171 * need commands to 172 * link-UP (core addr) (flags: ES, IS) 173 * link-DOWN (core addr) (flags: ES, IS) 174 * must be callable from kernel or user 175 * 176 * RETURNS: nothing 177 */ 178int 179eonioctl(struct ifnet *ifp, u_long cmd, void *data) 180{ 181 struct ifaddr *ifa = data; 182 int error = 0, s = splnet(); 183 184#ifdef ARGO_DEBUG 185 if (argo_debug[D_EON]) { 186 printf("eonioctl (cmd 0x%lx) \n", cmd); 187 } 188#endif 189 190 switch (cmd) { 191 case SIOCINITIFADDR: 192 ifp->if_flags |= IFF_UP; 193 if (ifa->ifa_addr->sa_family != AF_LINK) 194 ifa->ifa_rtrequest = eonrtrequest; 195 break; 196 default: 197 error = ifioctl_common(ifp, cmd, data); 198 break; 199 } 200 splx(s); 201 return (error); 202} 203 204 205void 206eoniphdr(struct eon_iphdr *hdr, const void *loc, struct route *ro, int class) 207{ 208 struct rtentry *rt; 209 struct mbuf mhead; 210 union { 211 struct sockaddr dst; 212 struct sockaddr_in dst4; 213 } u; 214 struct in_addr addr; 215 216 (void)memcpy(&addr, loc, sizeof(addr)); 217 sockaddr_in_init(&u.dst4, &addr, 0); 218 rtcache_setdst(ro, &u.dst); 219 220 if ((rt = rtcache_init(ro)) != NULL) 221 rt->rt_use++; 222 hdr->ei_ip.ip_dst = u.dst4.sin_addr; 223 hdr->ei_ip.ip_p = IPPROTO_EON; 224 hdr->ei_ip.ip_ttl = MAXTTL; 225 hdr->ei_eh.eonh_class = class; 226 hdr->ei_eh.eonh_vers = EON_VERSION; 227 hdr->ei_eh.eonh_csum = 0; 228 mhead.m_data = (void *)&hdr->ei_eh; 229 mhead.m_len = sizeof(struct eon_hdr); 230 mhead.m_next = NULL; 231#ifdef ARGO_DEBUG 232 if (argo_debug[D_EON]) { 233 printf("eonoutput : gen csum (%p, offset %lu, datalen %ld)\n", 234 &mhead, (unsigned long)offsetof(struct eon_hdr, eonh_csum), 235 (long)sizeof(struct eon_hdr)); 236 } 237#endif 238 iso_gen_csum(&mhead, 239 offsetof(struct eon_hdr, eonh_csum), sizeof(struct eon_hdr)); 240} 241/* 242 * FUNCTION: eonrtrequest 243 * 244 * PURPOSE: maintains list of direct eon recipients. 245 * sets up IP route for rest. 246 * 247 * RETURNS: nothing 248 */ 249void 250eonrtrequest(int cmd, struct rtentry *rt, const struct rt_addrinfo *info) 251{ 252 struct rtentry *nrt; 253 unsigned long zerodst = 0; 254 const void *ipaddrloc = &zerodst; 255 struct eon_llinfo *el = (struct eon_llinfo *) rt->rt_llinfo; 256 const struct sockaddr *gate; 257 258 /* 259 * Common Housekeeping 260 */ 261 switch (cmd) { 262 case RTM_DELETE: 263 if (el) { 264 iso_remque(&el->el_qhdr); 265 rtcache_free(&el->el_iproute); 266 Free(el); 267 rt->rt_llinfo = NULL; 268 } 269 return; 270 271 case RTM_ADD: 272 case RTM_RESOLVE: 273 rt->rt_rmx.rmx_mtu = lo0ifp->if_mtu; /* unless better below */ 274 R_Malloc(el, struct eon_llinfo *, sizeof(*el)); 275 rt->rt_llinfo = (void *) el; 276 if (el == NULL) 277 return; 278 memset(el, 0, sizeof(*el)); 279 iso_insque(&el->el_qhdr, &eon_llinfo.el_qhdr); 280 el->el_rt = rt; 281 break; 282 } 283 if (info != NULL && 284 (gate = info->rti_info[RTAX_GATEWAY]) != NULL) { /*XXX*/ 285 switch (gate->sa_family) { 286 case AF_LINK: 287 if (satocsdl(gate)->sdl_alen == 1) 288 el->el_snpaoffset = *(const u_char *)CLLADDR(satocsdl(gate)); 289 else 290 ipaddrloc = CLLADDR(satocsdl(gate)); 291 break; 292 case AF_INET: 293 ipaddrloc = &satocsin(gate)->sin_addr; 294 break; 295 default: 296 return; 297 } 298 } 299 el->el_flags |= RTF_UP; 300 eoniphdr(&el->el_ei, ipaddrloc, &el->el_iproute, EON_NORMAL_ADDR); 301 if ((nrt = rtcache_validate(&el->el_iproute)) != NULL) 302 rt->rt_rmx.rmx_mtu = nrt->rt_rmx.rmx_mtu - sizeof(el->el_ei); 303} 304 305/* 306 * FUNCTION: eonoutput 307 * 308 * PURPOSE: prepend an eon header and hand to IP 309 * ARGUMENTS: (ifp) is points to the ifnet structure for this 310 * unit/device (m) is an mbuf *, *m is a CLNL packet 311 * (dst) is a destination address - have to interp. as 312 * multicast or broadcast or real address. 313 * 314 * RETURNS: unix error code 315 * 316 * NOTES: 317 * 318 */ 319int 320eonoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sdst, 321 struct rtentry *rt) 322{ 323 const struct sockaddr_iso *dst = (const struct sockaddr_iso *)sdst; 324 struct eon_llinfo *el; 325 struct eon_iphdr *ei; 326 struct route *ro; 327 int datalen; 328 struct mbuf *mh; 329 int error = 0, class = 0, alen = 0; 330 const u_char *ipaddrloc = NULL; 331 static struct eon_iphdr eon_iphdr; 332 static struct route route; 333 334#ifdef ARGO_DEBUG 335 if (argo_debug[D_EON]) { 336 printf("eonoutput \n"); 337 } 338#endif 339 340 ifp->if_opackets++; 341 if (rt == NULL || (el = (struct eon_llinfo *)rt->rt_llinfo) == NULL) { 342 if (dst->siso_family == AF_LINK) { 343 const struct sockaddr_dl *sdl = satocsdl(dst); 344 345 ipaddrloc = CLLADDR(sdl); 346 alen = sdl->sdl_alen; 347 } else if (dst->siso_family == AF_ISO && 348 dst->siso_data[0] == AFI_SNA) { 349 alen = dst->siso_nlen - 1; 350 ipaddrloc = (const char *)dst->siso_data + 1; 351 } 352 switch (alen) { 353 case 5: 354 class = ipaddrloc[4]; 355 case 4: 356 ro = &route; 357 ei = &eon_iphdr; 358 memset(ei, 0, sizeof(*ei)); 359 eoniphdr(ei, ipaddrloc, ro, class); 360 goto send; 361 } 362einval: 363 error = EINVAL; 364 goto flush; 365 } 366 if ((el->el_flags & RTF_UP) == 0) { 367 eonrtrequest(RTM_CHANGE, rt, NULL); 368 if ((el->el_flags & RTF_UP) == 0) { 369 error = EHOSTUNREACH; 370 goto flush; 371 } 372 } 373 if ((m->m_flags & M_PKTHDR) == 0) { 374 printf("eon: got non headered packet\n"); 375 goto einval; 376 } 377 ei = &el->el_ei; 378 ro = &el->el_iproute; 379 if (el->el_snpaoffset == 0) 380 ; 381 else if (dst->siso_family == AF_ISO) { 382 memcpy(&ei->ei_ip.ip_dst, &dst->siso_data[el->el_snpaoffset], 383 sizeof(ei->ei_ip.ip_dst)); 384 } else 385 goto einval; 386send: 387 /* put an eon_hdr in the buffer, prepended by an ip header */ 388 datalen = m->m_pkthdr.len + EONIPLEN; 389 if (datalen > IP_MAXPACKET) { 390 error = EMSGSIZE; 391 goto flush; 392 } 393 MGETHDR(mh, M_DONTWAIT, MT_HEADER); 394 if (mh == NULL) { 395 error = ENOBUFS; 396 goto flush; 397 } 398 mh->m_next = m; 399 m = mh; 400 MH_ALIGN(m, sizeof(struct eon_iphdr)); 401 m->m_len = sizeof(struct eon_iphdr); 402 m->m_pkthdr.len = datalen; 403 ei->ei_ip.ip_len = htons(datalen); 404 ifp->if_obytes += datalen; 405 *mtod(m, struct eon_iphdr *) = *ei; 406 407#ifdef ARGO_DEBUG 408 if (argo_debug[D_EON]) { 409 printf("eonoutput dst ip addr : %x\n", ei->ei_ip.ip_dst.s_addr); 410 printf("eonoutput ip_output : eonip header:\n"); 411 dump_buf(ei, sizeof(struct eon_iphdr)); 412 } 413#endif 414 415 error = ip_output(m, NULL, ro, 0, NULL, NULL); 416 m = NULL; 417 if (error) { 418 ifp->if_oerrors++; 419 ifp->if_opackets--; 420 ifp->if_obytes -= datalen; 421 } 422flush: 423 if (m != NULL) 424 m_freem(m); 425 return error; 426} 427 428/* 429 * Strip out IP options, at higher 430 * level protocol in the kernel. 431 */ 432static void 433ip_stripoptions(struct mbuf *m) 434{ 435 struct ip *ip = mtod(m, struct ip *); 436 void *opts; 437 size_t olen; 438 439 olen = (ip->ip_hl << 2) - sizeof(struct ip); 440 opts = (void *)(ip + 1); 441 ip->ip_len = htons(ntohs(ip->ip_len) - olen); 442 ip->ip_hl = sizeof(struct ip) >> 2; 443 memmove((char *)ip + olen, ip, (size_t)olen); 444 m_adj(m, olen); 445} 446 447void 448eoninput(struct mbuf *m, ...) 449{ 450 int iphlen; 451 struct eon_hdr *eonhdr; 452 struct ip *iphdr; 453 struct ifnet *eonifp; 454 int s; 455 va_list ap; 456 457 va_start(ap, m); 458 iphlen = va_arg(ap, int); 459 va_end(ap); 460 461 eonifp = &eonif[0]; /* kludge - really want to give CLNP the ifp 462 * for eon, not for the real device */ 463 464#ifdef ARGO_DEBUG 465 if (argo_debug[D_EON]) { 466 printf("eoninput() %p m_data %p m_len 0x%x dequeued\n", 467 m, (m ? m->m_data : 0), m ? m->m_len : 0); 468 } 469#endif 470 471 if (m == NULL) 472 return; 473 if (iphlen > sizeof(struct ip)) 474 ip_stripoptions(m); 475 if (m->m_len < EONIPLEN) { 476 if ((m = m_pullup(m, EONIPLEN)) == NULL) { 477 IncStat(es_badhdr); 478 drop: 479#ifdef ARGO_DEBUG 480 if (argo_debug[D_EON]) { 481 printf("eoninput: DROP \n"); 482 } 483#endif 484 eonifp->if_ierrors++; 485 m_freem(m); 486 return; 487 } 488 } 489 eonif->if_ibytes += m->m_pkthdr.len; 490 iphdr = mtod(m, struct ip *); 491 /* do a few checks for debugging */ 492 if (iphdr->ip_p != IPPROTO_EON) { 493 IncStat(es_badhdr); 494 goto drop; 495 } 496 /* temporarily drop ip header from the mbuf */ 497 m->m_data += sizeof(struct ip); 498 eonhdr = mtod(m, struct eon_hdr *); 499 if (iso_check_csum(m, sizeof(struct eon_hdr)) != EOK) { 500 IncStat(es_badcsum); 501 goto drop; 502 } 503 m->m_data -= sizeof(struct ip); 504 505#ifdef ARGO_DEBUG 506 if (argo_debug[D_EON]) { 507 printf("eoninput csum ok class 0x%x\n", eonhdr->eonh_class); 508 printf("eoninput: eon header:\n"); 509 dump_buf(eonhdr, sizeof(struct eon_hdr)); 510 } 511#endif 512 513 /* checks for debugging */ 514 if (eonhdr->eonh_vers != EON_VERSION) { 515 IncStat(es_badhdr); 516 goto drop; 517 } 518 m->m_flags &= ~(M_BCAST | M_MCAST); 519 switch (eonhdr->eonh_class) { 520 case EON_BROADCAST: 521 IncStat(es_in_broad); 522 m->m_flags |= M_BCAST; 523 break; 524 case EON_NORMAL_ADDR: 525 IncStat(es_in_normal); 526 break; 527 case EON_MULTICAST_ES: 528 IncStat(es_in_multi_es); 529 m->m_flags |= M_MCAST; 530 break; 531 case EON_MULTICAST_IS: 532 IncStat(es_in_multi_is); 533 m->m_flags |= M_MCAST; 534 break; 535 } 536 eonifp->if_ipackets++; 537 538 { 539 /* put it on the CLNP queue and set soft interrupt */ 540 struct ifqueue *ifq; 541 extern struct ifqueue clnlintrq; 542 543 m->m_pkthdr.rcvif = eonifp; /* KLUDGE */ 544#ifdef ARGO_DEBUG 545 if (argo_debug[D_EON]) { 546 printf("eoninput to clnl IFQ\n"); 547 } 548#endif 549 ifq = &clnlintrq; 550 s = splnet(); 551 if (IF_QFULL(ifq)) { 552 IF_DROP(ifq); 553 m_freem(m); 554 eonifp->if_iqdrops++; 555 eonifp->if_ipackets--; 556 splx(s); 557 return; 558 } 559 IF_ENQUEUE(ifq, m); 560#ifdef ARGO_DEBUG 561 if (argo_debug[D_EON]) { 562 printf( 563 "%p enqueued on clnp Q: m_len 0x%x m_type 0x%x m_data %p\n", 564 m, m->m_len, m->m_type, m->m_data); 565 dump_buf(mtod(m, void *), m->m_len); 566 } 567#endif 568 schednetisr(NETISR_ISO); 569 splx(s); 570 } 571} 572 573void * 574eonctlinput(int cmd, const struct sockaddr *sa, void *dummy) 575{ 576 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; 577#ifdef ARGO_DEBUG 578 if (argo_debug[D_EON]) { 579 printf("eonctlinput: cmd 0x%x addr: ", cmd); 580 dump_isoaddr((const struct sockaddr_iso *)sin); 581 printf("\n"); 582 } 583#endif 584 585 if ((unsigned)cmd >= PRC_NCMDS) 586 return NULL; 587 588 IncStat(es_icmp[cmd]); 589 switch (cmd) { 590 591 case PRC_QUENCH: 592 case PRC_QUENCH2: 593 /* TODO: set the dec bit */ 594 break; 595 case PRC_TIMXCEED_REASS: 596 case PRC_ROUTEDEAD: 597 case PRC_HOSTUNREACH: 598 case PRC_UNREACH_NET: 599 case PRC_IFDOWN: 600 case PRC_UNREACH_HOST: 601 case PRC_HOSTDEAD: 602 case PRC_TIMXCEED_INTRANS: 603 /* TODO: mark the link down */ 604 break; 605 606 case PRC_UNREACH_PROTOCOL: 607 case PRC_UNREACH_PORT: 608 case PRC_UNREACH_SRCFAIL: 609 case PRC_REDIRECT_NET: 610 case PRC_REDIRECT_HOST: 611 case PRC_REDIRECT_TOSNET: 612 case PRC_REDIRECT_TOSHOST: 613 case PRC_MSGSIZE: 614 case PRC_PARAMPROB: 615#if 0 616 printf("eonctlinput: ICMP cmd 0x%x\n", cmd ); 617#endif 618 break; 619 } 620 return NULL; 621} 622 623#endif 624