1/* $NetBSD: if_hippisubr.c,v 1.47 2016/04/28 00:16:56 ozaki-r Exp $ */
2
3/*
4 * Copyright (c) 1982, 1989, 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
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: if_hippisubr.c,v 1.47 2016/04/28 00:16:56 ozaki-r Exp $");
34
35#ifdef _KERNEL_OPT
36#include "opt_inet.h"
37#endif
38
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/mbuf.h>
45#include <sys/protosw.h>
46#include <sys/socket.h>
47#include <sys/ioctl.h>
48#include <sys/errno.h>
49#include <sys/syslog.h>
50
51#include <sys/cpu.h>
52
53#include <net/if.h>
54#include <net/netisr.h>
55#include <net/route.h>
56#include <net/if_llc.h>
57#include <net/if_dl.h>
58#include <net/if_types.h>
59
60#include <net/bpf.h>
61
62#include <net/if_hippi.h>
63
64#include <netinet/in.h>
65#if defined(INET) || defined(INET6)
66#include <netinet/in_var.h>
67#endif
68
69#define senderr(e) { error = (e); goto bad;}
70
71#ifndef llc_snap
72#define llc_snap llc_un.type_snap
73#endif
74
75static int hippi_output(struct ifnet *, struct mbuf *,
76 const struct sockaddr *, const struct rtentry *);
77static void hippi_input(struct ifnet *, struct mbuf *);
78
79/*
80 * HIPPI output routine.
81 * Encapsulate a packet of type family for the local net.
82 * I don't know anything about the mapping of AppleTalk or OSI
83 * protocols to HIPPI, so I don't include any code for them.
84 */
85
86static int
87hippi_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst,
88 const struct rtentry *rt)
89{
90 uint16_t htype;
91 uint32_t ifield = 0;
92 int error = 0;
93 struct mbuf *m = m0;
94 struct hippi_header *hh;
95 uint32_t *cci;
96 uint32_t d2_len;
97
98 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
99 senderr(ENETDOWN);
100
101 /* HIPPI doesn't really do broadcast or multicast right now */
102 if (m->m_flags & (M_BCAST | M_MCAST))
103 senderr(EOPNOTSUPP); /* XXX: some other error? */
104
105 /*
106 * If the queueing discipline needs packet classification,
107 * do it before prepending link headers.
108 */
109 IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
110
111 switch (dst->sa_family) {
112#ifdef INET
113 case AF_INET:
114 if (rt) {
115 const struct sockaddr_dl *sdl =
116 satocsdl(rt->rt_gateway);
117 if (sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0)
118 memcpy(&ifield, CLLADDR(sdl), sizeof(ifield));
119 }
120 if (!ifield) /* XXX: bogus check, but helps us get going */
121 senderr(EHOSTUNREACH);
122 htype = htons(ETHERTYPE_IP);
123 break;
124#endif
125
126#ifdef INET6
127 case AF_INET6:
128 if (rt) {
129 const struct sockaddr_dl *sdl =
130 satocsdl(rt->rt_gateway);
131 if (sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0)
132 memcpy(&ifield, CLLADDR(sdl), sizeof(ifield));
133 }
134 if (!ifield) /* XXX: bogus check, but helps us get going */
135 senderr(EHOSTUNREACH);
136 htype = htons(ETHERTYPE_IPV6);
137 break;
138#endif
139
140 default:
141 printf("%s: can't handle af%d\n", ifp->if_xname,
142 dst->sa_family);
143 senderr(EAFNOSUPPORT);
144 }
145
146 if (htype != 0) {
147 struct llc *l;
148 M_PREPEND(m, sizeof (struct llc), M_DONTWAIT);
149 if (m == 0)
150 senderr(ENOBUFS);
151 l = mtod(m, struct llc *);
152 l->llc_control = LLC_UI;
153 l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
154 l->llc_snap.org_code[0] = l->llc_snap.org_code[1] =
155 l->llc_snap.org_code[2] = 0;
156 memcpy((void *) &l->llc_snap.ether_type, (void *) &htype,
157 sizeof(uint16_t));
158 }
159
160 d2_len = m->m_pkthdr.len;
161
162 /*
163 * Add local net header. If no space in first mbuf,
164 * allocate another.
165 */
166
167 M_PREPEND(m, sizeof (struct hippi_header) + 8, M_DONTWAIT);
168 if (m == 0)
169 senderr(ENOBUFS);
170 cci = mtod(m, uint32_t *);
171 memset(cci, 0, sizeof(struct hippi_header) + 8);
172 cci[0] = 0;
173 cci[1] = ifield;
174 hh = (struct hippi_header *) &cci[2];
175 hh->hi_fp.fp_ulp = HIPPI_ULP_802;
176 hh->hi_fp.fp_flags = HIPPI_FP_D1_PRESENT;
177 hh->hi_fp.fp_offsets = htons(sizeof(struct hippi_le));
178 hh->hi_fp.fp_d2_len = htonl(d2_len);
179
180 /* Pad out the D2 area to end on a quadword (64-bit) boundry. */
181
182 if (d2_len % 8 != 0) {
183 static uint32_t buffer[2] = {0, 0};
184 m_copyback(m, m->m_pkthdr.len, 8 - d2_len % 8, (void *) buffer);
185 }
186
187 return ifq_enqueue(ifp, m);
188
189 bad:
190 if (m)
191 m_freem(m);
192 return (error);
193}
194
195/*
196 * Process a received HIPPI packet;
197 * the packet is in the mbuf chain m with
198 * the HIPPI header.
199 */
200
201static void
202hippi_input(struct ifnet *ifp, struct mbuf *m)
203{
204 pktqueue_t *pktq;
205 struct llc *l;
206 uint16_t htype;
207 struct hippi_header *hh;
208
209 if ((ifp->if_flags & IFF_UP) == 0) {
210 m_freem(m);
211 return;
212 }
213
214 /* XXX: need to check flags and drop if bogus! */
215
216 hh = mtod(m, struct hippi_header *);
217
218 ifp->if_ibytes += m->m_pkthdr.len;
219 if (hh->hi_le.le_dest_addr[0] & 1) {
220 if (memcmp(etherbroadcastaddr, hh->hi_le.le_dest_addr,
221 sizeof(etherbroadcastaddr)) == 0)
222 m->m_flags |= M_BCAST;
223 else
224 m->m_flags |= M_MCAST;
225 }
226 if (m->m_flags & (M_BCAST|M_MCAST))
227 ifp->if_imcasts++;
228
229 /* Skip past the HIPPI header. */
230 m_adj(m, sizeof(struct hippi_header));
231
232 l = mtod(m, struct llc *);
233 if (l->llc_dsap != LLC_SNAP_LSAP) {
234 m_freem(m);
235 return;
236 }
237 htype = ntohs(l->llc_snap.ether_type);
238 m_adj(m, 8);
239 switch (htype) {
240#ifdef INET
241 case ETHERTYPE_IP:
242 pktq = ip_pktq;
243 break;
244#endif
245#ifdef INET6
246 case ETHERTYPE_IPV6:
247 pktq = ip6_pktq;
248 break;
249#endif
250 default:
251 m_freem(m);
252 return;
253 }
254
255 if (__predict_false(!pktq_enqueue(pktq, m, 0))) {
256 m_freem(m);
257 }
258}
259
260/*
261 * Handle packet from HIPPI that has no MAC header
262 */
263
264#ifdef INET
265void
266hippi_ip_input(struct ifnet *ifp, struct mbuf *m)
267{
268 int s;
269
270 s = splnet();
271 if (__predict_false(!pktq_enqueue(ip_pktq, m, 0))) {
272 m_freem(m);
273 }
274 splx(s);
275}
276#endif
277
278/*
279 * Perform common duties while attaching to interface list
280 */
281void
282hippi_ifattach(struct ifnet *ifp, void *lla)
283{
284
285 ifp->if_type = IFT_HIPPI;
286 ifp->if_hdrlen = sizeof(struct hippi_header) + 8; /* add CCI */
287 ifp->if_dlt = DLT_HIPPI;
288 ifp->if_mtu = HIPPIMTU;
289 ifp->if_output = hippi_output;
290 ifp->_if_input = hippi_input;
291 ifp->if_baudrate = IF_Mbps(800); /* XXX double-check */
292
293 if_set_sadl(ifp, lla, 6, true);
294
295 bpf_attach(ifp, DLT_HIPPI, sizeof(struct hippi_header));
296}
297