1/* 2 * $Id: ddp_output.c,v 1.4 2002-01-04 04:45:49 sibaz Exp $ 3 * 4 * Copyright (c) 1990,1991 Regents of The University of Michigan. 5 * All Rights Reserved. See COPYRIGHT. 6 */ 7 8#ifdef HAVE_CONFIG_H 9#include "config.h" 10#endif /* HAVE_CONFIG_H */ 11 12#include <sys/types.h> 13#include <sys/param.h> 14#include <sys/mbuf.h> 15#include <sys/socket.h> 16#include <sys/errno.h> 17#include <atalk/logger.h> 18 19#include <net/if.h> 20#include <net/route.h> 21 22#include <netinet/in.h> 23#undef s_net 24#include <netinet/if_ether.h> 25 26#include "at.h" 27#include "at_var.h" 28#include "endian.h" 29#include "ddp.h" 30#include "ddp_var.h" 31 32u_short at_cksum(); 33int ddp_cksum = 1; 34 35ddp_output( ddp, m ) 36 struct ddpcb *ddp; 37 struct mbuf *m; 38{ 39#ifndef BSD4_4 40 struct mbuf *m0; 41 int len; 42#endif /* ! BSD4_4 */ 43 struct ifnet *ifp; 44 struct at_ifaddr *aa = NULL; 45 struct ddpehdr *deh; 46 u_short net; 47 48#ifdef BSD4_4 49 M_PREPEND( m, sizeof( struct ddpehdr ), M_WAIT ); 50#else /* BSD4_4 */ 51 for ( len = 0, m0 = m; m; m = m->m_next ) { 52 len += m->m_len; 53 } 54 MGET( m, M_WAIT, MT_HEADER ); 55 if ( m == 0 ) { 56 m_freem( m0 ); 57 return( ENOBUFS ); 58 } 59 m->m_next = m0; 60#endif /* BSD4_4 */ 61 62#ifndef BSD4_4 63# define align(a) (((a)+3)&0xfc) 64 m->m_off = MMINOFF + align( SZ_ELAPHDR ); 65 m->m_len = sizeof( struct ddpehdr ); 66#endif /* ! BSD4_4 */ 67 68 deh = mtod( m, struct ddpehdr *); 69 deh->deh_pad = 0; 70 deh->deh_hops = 0; 71 72#ifdef BSD4_4 73 deh->deh_len = m->m_pkthdr.len; 74#else /* BSD4_4 */ 75 deh->deh_len = len + sizeof( struct ddpehdr ); 76#endif /* BSD4_4 */ 77 78 deh->deh_dnet = ddp->ddp_fsat.sat_addr.s_net; 79 deh->deh_dnode = ddp->ddp_fsat.sat_addr.s_node; 80 deh->deh_dport = ddp->ddp_fsat.sat_port; 81 deh->deh_snet = ddp->ddp_lsat.sat_addr.s_net; 82 deh->deh_snode = ddp->ddp_lsat.sat_addr.s_node; 83 deh->deh_sport = ddp->ddp_lsat.sat_port; 84 85 /* 86 * The checksum calculation is done after all of the other bytes have 87 * been filled in. 88 */ 89 if ( ddp_cksum ) { 90 deh->deh_sum = at_cksum( m, sizeof( int )); 91 } else { 92 deh->deh_sum = 0; 93 } 94 deh->deh_bytes = htonl( deh->deh_bytes ); 95 96 return( ddp_route( m, &ddp->ddp_route )); 97} 98 99 u_short 100at_cksum( m, skip ) 101 struct mbuf *m; 102 int skip; 103{ 104 u_char *data, *end; 105 u_int32_t cksum = 0; 106 107 for (; m; m = m->m_next ) { 108 for ( data = mtod( m, u_char * ), end = data + m->m_len; data < end; 109 data++ ) { 110 if ( skip ) { 111 skip--; 112 continue; 113 } 114 cksum = ( cksum + *data ) << 1; 115 if ( cksum & 0x00010000 ) { 116 cksum++; 117 } 118 cksum &= 0x0000ffff; 119 } 120 } 121 122 if ( cksum == 0 ) { 123 cksum = 0x0000ffff; 124 } 125 return( (u_short)cksum ); 126} 127 128ddp_route( m, ro ) 129 struct mbuf *m; 130 struct route *ro; 131{ 132 struct sockaddr_at gate; 133 struct elaphdr *elh; 134 struct mbuf *m0; 135 struct at_ifaddr *aa = NULL; 136 struct ifnet *ifp; 137 int mlen; 138 u_short net; 139 140 if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) { 141#ifdef BSD4_4 142 net = satosat( ro->ro_rt->rt_gateway )->sat_addr.s_net; 143#else /* BSD4_4 */ 144 net = satosat( &ro->ro_rt->rt_gateway )->sat_addr.s_net; 145#endif /* BSD4_4 */ 146 for ( aa = at_ifaddr; aa; aa = aa->aa_next ) { 147 if ( aa->aa_ifp == ifp && 148 ntohs( net ) >= ntohs( aa->aa_firstnet ) && 149 ntohs( net ) <= ntohs( aa->aa_lastnet )) { 150 break; 151 } 152 } 153 } 154 if ( aa == NULL ) { 155 m_freem( m ); 156 return( EINVAL ); 157 } 158 159 /* 160 * There are several places in the kernel where data is added to 161 * an mbuf without ensuring that the mbuf pointer is aligned. 162 * This is bad for transition routing, since phase 1 and phase 2 163 * packets end up poorly aligned due to the three byte elap header. 164 */ 165 if ( aa->aa_flags & AFA_PHASE2 ) { 166 for ( mlen = 0, m0 = m; m0; m0 = m0->m_next ) { 167 mlen += m0->m_len; 168 } 169 if (( m = m_pullup( m, MIN( MLEN, mlen ))) == 0 ) { 170 return( ENOBUFS ); 171 } 172 } else { 173# ifdef notdef 174#ifdef BSD4_4 175 M_PREPEND( m, SZ_ELAPHDR, M_DONTWAIT ); 176 if ( m == NULL ) { 177 return( ENOBUFS ); 178 } 179#else /* BSD4_4 */ 180 m->m_off -= SZ_ELAPHDR; 181 m->m_len += SZ_ELAPHDR; 182#endif /* BSD4_4 */ 183# endif /* notdef */ 184 185 MGET( m0, M_WAIT, MT_HEADER ); 186 if ( m0 == 0 ) { 187 m_freem( m ); 188 return( ENOBUFS ); 189 } 190 m0->m_next = m; 191 m0->m_off = MMINOFF + align( sizeof( struct ether_header )); 192 m0->m_len = SZ_ELAPHDR; 193 m = m0; 194 195 elh = mtod( m, struct elaphdr *); 196 elh->el_snode = satosat( &aa->aa_addr )->sat_addr.s_node; 197 elh->el_type = ELAP_DDPEXTEND; 198 if ( ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) >= 199 ntohs( aa->aa_firstnet ) && 200 ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) <= 201 ntohs( aa->aa_lastnet )) { 202 elh->el_dnode = satosat( &ro->ro_dst )->sat_addr.s_node; 203 } else { 204#ifdef BSD4_4 205 elh->el_dnode = satosat( ro->ro_rt->rt_gateway )->sat_addr.s_node; 206#else /* BSD4_4 */ 207 elh->el_dnode = satosat( &ro->ro_rt->rt_gateway )->sat_addr.s_node; 208#endif /* BSD4_4 */ 209 } 210 } 211 212 if ( ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) >= 213 ntohs( aa->aa_firstnet ) && 214 ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) <= 215 ntohs( aa->aa_lastnet )) { 216 gate = *satosat( &ro->ro_dst ); 217 } else { 218#ifdef BSD4_4 219 gate = *satosat( ro->ro_rt->rt_gateway ); 220#else /* BSD4_4 */ 221 gate = *satosat( &ro->ro_rt->rt_gateway ); 222#endif /* BSD4_4 */ 223 } 224 ro->ro_rt->rt_use++; 225 226#ifdef ultrix 227 /* 228 * SAIEW: We can't make changes to net/if_loop.c, so we don't route 229 * further than this: if it's going to go through the lookback, 230 * short-circuit to ddp_input(). Who needs queuing? 231 * 232 * Note: Passing NULL for the elaphdr is cool, since we'll only ever 233 * try to send long form ddp throught the loopback. 234 */ 235 if ( ifp->if_flags & IFF_LOOPBACK ) { 236#ifdef notdef 237 m->m_off += SZ_ELAPHDR; 238 m->m_len -= SZ_ELAPHDR; 239#endif /* notdef */ 240 ddp_input( m, ifp, (struct elaphdr *)NULL, 2 ); 241 return( 0 ); 242 } 243#endif /* ultrix */ 244 245#ifdef _IBMR2 246 /* 247 * We can't make changes to the interface routines on RS6ks, and 248 * they don't provide hooks for if_output, so we just resolve 249 * our address here, and pass the packet as a raw ethernet packet. 250 * This doesn't work particularly well, if we aren't *on* ethernet, 251 * but it's ok for the moment. 252 */ 253 if ( ! ( ifp->if_flags & IFF_LOOPBACK )) { 254 struct ether_header eh; 255 256 if ( !aarpresolve(( struct arpcom *)ifp, m, 257 &gate, eh.ether_dhost )) { 258 return( 0 ); 259 } 260 eh.ether_type = htons( ETHERTYPE_AT ); 261 gate.sat_family = AF_UNSPEC; 262 bcopy( &eh, (*(struct sockaddr *)&gate).sa_data, 263 sizeof( (*(struct sockaddr *)&gate).sa_data )); 264 } 265#endif /* _IBMR2 */ 266 return((*ifp->if_output)( ifp, m, &gate )); 267} 268