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