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