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