1/* $NetBSD: ddp_output.c,v 1.22 2023/03/30 17:48:10 riastradh 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.22 2023/03/30 17:48:10 riastradh Exp $"); 31#include "opt_atalk.h" 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/mbuf.h> 36#include <sys/socket.h> 37#include <sys/errno.h> 38#include <sys/syslog.h> 39 40#include <net/if.h> 41#include <net/route.h> 42#include <net/if_ether.h> 43 44#include <netinet/in.h> 45#undef s_net 46 47#include <netatalk/at.h> 48#include <netatalk/at_var.h> 49#include <netatalk/ddp.h> 50#include <netatalk/ddp_var.h> 51#include <netatalk/at_extern.h> 52 53int ddp_cksum = 1; 54 55int 56ddp_output(struct mbuf *m, struct ddpcb *ddp) 57{ 58 struct ddpehdr *deh; 59 60 M_PREPEND(m, sizeof(struct ddpehdr), M_DONTWAIT); 61 if (!m) 62 return (ENOBUFS); 63 64 deh = mtod(m, struct ddpehdr *); 65 deh->deh_pad = 0; 66 deh->deh_hops = 0; 67 68 deh->deh_len = m->m_pkthdr.len; 69 70 deh->deh_dnet = ddp->ddp_fsat.sat_addr.s_net; 71 deh->deh_dnode = ddp->ddp_fsat.sat_addr.s_node; 72 deh->deh_dport = ddp->ddp_fsat.sat_port; 73 deh->deh_snet = ddp->ddp_lsat.sat_addr.s_net; 74 deh->deh_snode = ddp->ddp_lsat.sat_addr.s_node; 75 deh->deh_sport = ddp->ddp_lsat.sat_port; 76 77 /* 78 * The checksum calculation is done after all of the other bytes have 79 * been filled in. 80 */ 81 if (ddp_cksum) 82 deh->deh_sum = at_cksum(m, sizeof(int)); 83 else 84 deh->deh_sum = 0; 85 deh->deh_bytes = htonl(deh->deh_bytes); 86 87 return ddp_route(m, &ddp->ddp_route); 88} 89 90u_short 91at_cksum(struct mbuf *m, int skip) 92{ 93 u_char *data, *end; 94 u_long cksum = 0; 95 96 for (; m; m = m->m_next) { 97 for (data = mtod(m, u_char *), end = data + m->m_len; 98 data < end; data++) { 99 if (skip) { 100 skip--; 101 continue; 102 } 103 cksum = (cksum + *data) << 1; 104 if (cksum & 0x00010000) 105 cksum++; 106 cksum &= 0x0000ffff; 107 } 108 } 109 110 if (cksum == 0) { 111 cksum = 0x0000ffff; 112 } 113 return (u_short)cksum; 114} 115 116int 117ddp_route(struct mbuf *m, struct route *ro) 118{ 119 struct rtentry *rt; 120 struct sockaddr_at gate; 121 struct elaphdr *elh; 122 struct at_ifaddr *aa = NULL; 123 struct ifnet *ifp = NULL; 124 uint16_t net; 125 uint8_t loopback = 0; 126 int error; 127 128 if ((rt = rtcache_validate(ro)) != NULL && (ifp = rt->rt_ifp) != NULL) { 129 const struct sockaddr_at *dst = satocsat(rtcache_getdst(ro)); 130 uint16_t dnet = dst->sat_addr.s_net; 131 uint8_t dnode = dst->sat_addr.s_node; 132 net = satosat(rt->rt_gateway)->sat_addr.s_net; 133 134 TAILQ_FOREACH(aa, &at_ifaddr, aa_list) { 135 if (ntohs(net) >= ntohs(aa->aa_firstnet) && 136 ntohs(net) <= ntohs(aa->aa_lastnet)) { 137 /* Are we talking to ourselves? */ 138 if (dnet == aa->aa_addr.sat_addr.s_net && 139 dnode == aa->aa_addr.sat_addr.s_node) { 140 /* If to us, redirect to lo0. */ 141 ifp = lo0ifp; 142 } 143 /* Or is it a broadcast? */ 144 else if (dnet == aa->aa_addr.sat_addr.s_net && 145 dnode == 255) { 146 /* If broadcast, loop back a copy. */ 147 loopback = 1; 148 } 149 break; 150 } 151 } 152 } 153 if (aa == NULL) { 154#ifdef NETATALKDEBUG 155 printf("%s: no address found\n", __func__); 156#endif 157 m_freem(m); 158 error = EINVAL; 159 goto out; 160 } 161 /* 162 * There are several places in the kernel where data is added to 163 * an mbuf without ensuring that the mbuf pointer is aligned. 164 * This is bad for transition routing, since phase 1 and phase 2 165 * packets end up poorly aligned due to the three byte elap header. 166 */ 167 if (!(aa->aa_flags & AFA_PHASE2)) { 168 M_PREPEND(m, SZ_ELAPHDR, M_DONTWAIT); 169 if (m == NULL) { 170 error = ENOBUFS; 171 goto out; 172 } 173 174 elh = mtod(m, struct elaphdr *); 175 elh->el_snode = aa->aa_addr.sat_addr.s_node; 176 elh->el_type = ELAP_DDPEXTEND; 177 if (ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) >= 178 ntohs(aa->aa_firstnet) && 179 ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) <= 180 ntohs(aa->aa_lastnet)) { 181 elh->el_dnode = 182 satocsat(rtcache_getdst(ro))->sat_addr.s_node; 183 } else { 184 elh->el_dnode = 185 satosat(rt->rt_gateway)->sat_addr.s_node; 186 } 187 } 188 if (ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) >= 189 ntohs(aa->aa_firstnet) && 190 ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) <= 191 ntohs(aa->aa_lastnet)) { 192 gate = *satocsat(rtcache_getdst(ro)); 193 } else { 194 gate = *satosat(rt->rt_gateway); 195 } 196 rt->rt_use++; 197 198#if IFA_STATS 199 aa->aa_ifa.ifa_data.ifad_outbytes += m->m_pkthdr.len; 200#endif 201 202 /* XXX */ 203 if (loopback && rtcache_getdst(ro)->sa_family == AF_APPLETALK) { 204 struct mbuf *copym = m_copypacket(m, M_DONTWAIT); 205 206#ifdef NETATALKDEBUG 207 printf("Looping back (not AARP).\n"); 208#endif 209 looutput(lo0ifp, copym, rtcache_getdst(ro), NULL); 210 } 211 212 error = if_output_lock(ifp, ifp, m, (struct sockaddr *)&gate, NULL); 213out: 214 rtcache_unref(rt, ro); 215 return error; 216} 217