1 /* $NetBSD: ddp_output.c,v 1.20 2016/12/08 05:16:33 ozaki-r Exp $ */ 2 3 /* 4 * Copyright (c) 1990,1991 Regents of The University of Michigan. 5 * All Rights Reserved. 6 * 7 * Permission to use, copy, modify, and distribute this software and 8 * its documentation for any purpose and without fee is hereby granted, 9 * provided that the above copyright notice appears in all copies and 10 * that both that copyright notice and this permission notice appear 11 * in supporting documentation, and that the name of The University 12 * of Michigan not be used in advertising or publicity pertaining to 13 * distribution of the software without specific, written prior 14 * permission. This software is supplied as is without expressed or 15 * implied warranties of any kind. 16 * 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 20 * Research Systems Unix Group 21 * The University of Michigan 22 * c/o Wesley Craig 23 * 535 W. William Street 24 * Ann Arbor, Michigan 25 * +1-313-764-2278 26 * netatalk@umich.edu 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: ddp_output.c,v 1.20 2016/12/08 05:16:33 ozaki-r Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/mbuf.h> 35 #include <sys/socket.h> 36 #include <sys/errno.h> 37 #include <sys/syslog.h> 38 39 #include <net/if.h> 40 #include <net/route.h> 41 #include <net/if_ether.h> 42 43 #include <netinet/in.h> 44 #undef s_net 45 46 #include <netatalk/at.h> 47 #include <netatalk/at_var.h> 48 #include <netatalk/ddp.h> 49 #include <netatalk/ddp_var.h> 50 #include <netatalk/at_extern.h> 51 52 int ddp_cksum = 1; 53 54 int 55 ddp_output(struct mbuf *m, struct ddpcb *ddp) 56 { 57 struct ddpehdr *deh; 58 59 M_PREPEND(m, sizeof(struct ddpehdr), M_DONTWAIT); 60 if (!m) 61 return (ENOBUFS); 62 63 deh = mtod(m, struct ddpehdr *); 64 deh->deh_pad = 0; 65 deh->deh_hops = 0; 66 67 deh->deh_len = m->m_pkthdr.len; 68 69 deh->deh_dnet = ddp->ddp_fsat.sat_addr.s_net; 70 deh->deh_dnode = ddp->ddp_fsat.sat_addr.s_node; 71 deh->deh_dport = ddp->ddp_fsat.sat_port; 72 deh->deh_snet = ddp->ddp_lsat.sat_addr.s_net; 73 deh->deh_snode = ddp->ddp_lsat.sat_addr.s_node; 74 deh->deh_sport = ddp->ddp_lsat.sat_port; 75 76 /* 77 * The checksum calculation is done after all of the other bytes have 78 * been filled in. 79 */ 80 if (ddp_cksum) 81 deh->deh_sum = at_cksum(m, sizeof(int)); 82 else 83 deh->deh_sum = 0; 84 deh->deh_bytes = htonl(deh->deh_bytes); 85 86 return ddp_route(m, &ddp->ddp_route); 87 } 88 89 u_short 90 at_cksum(struct mbuf *m, int skip) 91 { 92 u_char *data, *end; 93 u_long cksum = 0; 94 95 for (; m; m = m->m_next) { 96 for (data = mtod(m, u_char *), end = data + m->m_len; 97 data < end; data++) { 98 if (skip) { 99 skip--; 100 continue; 101 } 102 cksum = (cksum + *data) << 1; 103 if (cksum & 0x00010000) 104 cksum++; 105 cksum &= 0x0000ffff; 106 } 107 } 108 109 if (cksum == 0) { 110 cksum = 0x0000ffff; 111 } 112 return (u_short)cksum; 113 } 114 115 int 116 ddp_route(struct mbuf *m, struct route *ro) 117 { 118 struct rtentry *rt; 119 struct sockaddr_at gate; 120 struct elaphdr *elh; 121 struct at_ifaddr *aa = NULL; 122 struct ifnet *ifp = NULL; 123 uint16_t net; 124 uint8_t loopback = 0; 125 int error; 126 127 if ((rt = rtcache_validate(ro)) != NULL && (ifp = rt->rt_ifp) != NULL) { 128 const struct sockaddr_at *dst = satocsat(rtcache_getdst(ro)); 129 uint16_t dnet = dst->sat_addr.s_net; 130 uint8_t dnode = dst->sat_addr.s_node; 131 net = satosat(rt->rt_gateway)->sat_addr.s_net; 132 133 TAILQ_FOREACH(aa, &at_ifaddr, aa_list) { 134 if (ntohs(net) >= ntohs(aa->aa_firstnet) && 135 ntohs(net) <= ntohs(aa->aa_lastnet)) { 136 /* Are we talking to ourselves? */ 137 if (dnet == aa->aa_addr.sat_addr.s_net && 138 dnode == aa->aa_addr.sat_addr.s_node) { 139 /* If to us, redirect to lo0. */ 140 ifp = lo0ifp; 141 } 142 /* Or is it a broadcast? */ 143 else if (dnet == aa->aa_addr.sat_addr.s_net && 144 dnode == 255) { 145 /* If broadcast, loop back a copy. */ 146 loopback = 1; 147 } 148 break; 149 } 150 } 151 } 152 if (aa == NULL) { 153 #ifdef NETATALKDEBUG 154 printf("%s: no address found\n", __func__); 155 #endif 156 m_freem(m); 157 error = EINVAL; 158 goto out; 159 } 160 /* 161 * There are several places in the kernel where data is added to 162 * an mbuf without ensuring that the mbuf pointer is aligned. 163 * This is bad for transition routing, since phase 1 and phase 2 164 * packets end up poorly aligned due to the three byte elap header. 165 */ 166 if (!(aa->aa_flags & AFA_PHASE2)) { 167 M_PREPEND(m, SZ_ELAPHDR, M_DONTWAIT); 168 if (m == NULL) { 169 error = ENOBUFS; 170 goto out; 171 } 172 173 elh = mtod(m, struct elaphdr *); 174 elh->el_snode = satosat(&aa->aa_addr)->sat_addr.s_node; 175 elh->el_type = ELAP_DDPEXTEND; 176 if (ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) >= 177 ntohs(aa->aa_firstnet) && 178 ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) <= 179 ntohs(aa->aa_lastnet)) { 180 elh->el_dnode = 181 satocsat(rtcache_getdst(ro))->sat_addr.s_node; 182 } else { 183 elh->el_dnode = 184 satosat(rt->rt_gateway)->sat_addr.s_node; 185 } 186 } 187 if (ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) >= 188 ntohs(aa->aa_firstnet) && 189 ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) <= 190 ntohs(aa->aa_lastnet)) { 191 gate = *satocsat(rtcache_getdst(ro)); 192 } else { 193 gate = *satosat(rt->rt_gateway); 194 } 195 rt->rt_use++; 196 197 #if IFA_STATS 198 aa->aa_ifa.ifa_data.ifad_outbytes += m->m_pkthdr.len; 199 #endif 200 201 /* XXX */ 202 if (loopback && rtcache_getdst(ro)->sa_family == AF_APPLETALK) { 203 struct mbuf *copym = m_copypacket(m, M_DONTWAIT); 204 205 #ifdef NETATALKDEBUG 206 printf("Looping back (not AARP).\n"); 207 #endif 208 looutput(lo0ifp, copym, rtcache_getdst(ro), NULL); 209 } 210 211 error = if_output_lock(ifp, ifp, m, (struct sockaddr *)&gate, NULL); 212 out: 213 rtcache_unref(rt, ro); 214 return error; 215 } 216