1*65885579Sriastradh /* $NetBSD: ddp_output.c,v 1.22 2023/03/30 17:48:10 riastradh Exp $ */
25bf5cd24Schristos
35bf5cd24Schristos /*
45bf5cd24Schristos * Copyright (c) 1990,1991 Regents of The University of Michigan.
55bf5cd24Schristos * All Rights Reserved.
65bf5cd24Schristos *
75bf5cd24Schristos * Permission to use, copy, modify, and distribute this software and
85bf5cd24Schristos * its documentation for any purpose and without fee is hereby granted,
95bf5cd24Schristos * provided that the above copyright notice appears in all copies and
105bf5cd24Schristos * that both that copyright notice and this permission notice appear
115bf5cd24Schristos * in supporting documentation, and that the name of The University
125bf5cd24Schristos * of Michigan not be used in advertising or publicity pertaining to
135bf5cd24Schristos * distribution of the software without specific, written prior
145bf5cd24Schristos * permission. This software is supplied as is without expressed or
155bf5cd24Schristos * implied warranties of any kind.
165bf5cd24Schristos *
175bf5cd24Schristos * This product includes software developed by the University of
185bf5cd24Schristos * California, Berkeley and its contributors.
195bf5cd24Schristos *
205bf5cd24Schristos * Research Systems Unix Group
215bf5cd24Schristos * The University of Michigan
225bf5cd24Schristos * c/o Wesley Craig
235bf5cd24Schristos * 535 W. William Street
245bf5cd24Schristos * Ann Arbor, Michigan
255bf5cd24Schristos * +1-313-764-2278
265bf5cd24Schristos * netatalk@umich.edu
275bf5cd24Schristos */
285bf5cd24Schristos
29b60687cbSlukem #include <sys/cdefs.h>
30*65885579Sriastradh __KERNEL_RCSID(0, "$NetBSD: ddp_output.c,v 1.22 2023/03/30 17:48:10 riastradh Exp $");
316f874b81Srjs #include "opt_atalk.h"
32b60687cbSlukem
335bf5cd24Schristos #include <sys/param.h>
345bf5cd24Schristos #include <sys/systm.h>
355bf5cd24Schristos #include <sys/mbuf.h>
365bf5cd24Schristos #include <sys/socket.h>
375bf5cd24Schristos #include <sys/errno.h>
385bf5cd24Schristos #include <sys/syslog.h>
395bf5cd24Schristos
405bf5cd24Schristos #include <net/if.h>
415bf5cd24Schristos #include <net/route.h>
425bf5cd24Schristos #include <net/if_ether.h>
435bf5cd24Schristos
445bf5cd24Schristos #include <netinet/in.h>
455bf5cd24Schristos #undef s_net
465bf5cd24Schristos
475bf5cd24Schristos #include <netatalk/at.h>
485bf5cd24Schristos #include <netatalk/at_var.h>
495bf5cd24Schristos #include <netatalk/ddp.h>
505bf5cd24Schristos #include <netatalk/ddp_var.h>
515bf5cd24Schristos #include <netatalk/at_extern.h>
525bf5cd24Schristos
535bf5cd24Schristos int ddp_cksum = 1;
545bf5cd24Schristos
555bf5cd24Schristos int
ddp_output(struct mbuf * m,struct ddpcb * ddp)56d33ecd31Sriastradh ddp_output(struct mbuf *m, struct ddpcb *ddp)
575bf5cd24Schristos {
585bf5cd24Schristos struct ddpehdr *deh;
595bf5cd24Schristos
6001c8c2e9Sitojun M_PREPEND(m, sizeof(struct ddpehdr), M_DONTWAIT);
6101c8c2e9Sitojun if (!m)
6201c8c2e9Sitojun return (ENOBUFS);
635bf5cd24Schristos
645bf5cd24Schristos deh = mtod(m, struct ddpehdr *);
655bf5cd24Schristos deh->deh_pad = 0;
665bf5cd24Schristos deh->deh_hops = 0;
675bf5cd24Schristos
685bf5cd24Schristos deh->deh_len = m->m_pkthdr.len;
695bf5cd24Schristos
705bf5cd24Schristos deh->deh_dnet = ddp->ddp_fsat.sat_addr.s_net;
715bf5cd24Schristos deh->deh_dnode = ddp->ddp_fsat.sat_addr.s_node;
725bf5cd24Schristos deh->deh_dport = ddp->ddp_fsat.sat_port;
735bf5cd24Schristos deh->deh_snet = ddp->ddp_lsat.sat_addr.s_net;
745bf5cd24Schristos deh->deh_snode = ddp->ddp_lsat.sat_addr.s_node;
755bf5cd24Schristos deh->deh_sport = ddp->ddp_lsat.sat_port;
765bf5cd24Schristos
775bf5cd24Schristos /*
785bf5cd24Schristos * The checksum calculation is done after all of the other bytes have
795bf5cd24Schristos * been filled in.
805bf5cd24Schristos */
815493f188Sdyoung if (ddp_cksum)
825bf5cd24Schristos deh->deh_sum = at_cksum(m, sizeof(int));
835493f188Sdyoung else
845bf5cd24Schristos deh->deh_sum = 0;
855bf5cd24Schristos deh->deh_bytes = htonl(deh->deh_bytes);
865bf5cd24Schristos
875493f188Sdyoung return ddp_route(m, &ddp->ddp_route);
885bf5cd24Schristos }
895bf5cd24Schristos
905bf5cd24Schristos u_short
at_cksum(struct mbuf * m,int skip)915493f188Sdyoung at_cksum(struct mbuf *m, int skip)
925bf5cd24Schristos {
935bf5cd24Schristos u_char *data, *end;
945bf5cd24Schristos u_long cksum = 0;
955bf5cd24Schristos
965bf5cd24Schristos for (; m; m = m->m_next) {
975bf5cd24Schristos for (data = mtod(m, u_char *), end = data + m->m_len;
985bf5cd24Schristos data < end; data++) {
995bf5cd24Schristos if (skip) {
1005bf5cd24Schristos skip--;
1015bf5cd24Schristos continue;
1025bf5cd24Schristos }
1035bf5cd24Schristos cksum = (cksum + *data) << 1;
1045493f188Sdyoung if (cksum & 0x00010000)
1055bf5cd24Schristos cksum++;
1065bf5cd24Schristos cksum &= 0x0000ffff;
1075bf5cd24Schristos }
1085bf5cd24Schristos }
1095bf5cd24Schristos
1105bf5cd24Schristos if (cksum == 0) {
1115bf5cd24Schristos cksum = 0x0000ffff;
1125bf5cd24Schristos }
1135493f188Sdyoung return (u_short)cksum;
1145bf5cd24Schristos }
1155bf5cd24Schristos
1165bf5cd24Schristos int
ddp_route(struct mbuf * m,struct route * ro)1175493f188Sdyoung ddp_route(struct mbuf *m, struct route *ro)
1185bf5cd24Schristos {
11972fa642aSdyoung struct rtentry *rt;
1205bf5cd24Schristos struct sockaddr_at gate;
1215bf5cd24Schristos struct elaphdr *elh;
1225bf5cd24Schristos struct at_ifaddr *aa = NULL;
1235bf5cd24Schristos struct ifnet *ifp = NULL;
1242e878a9dShauke uint16_t net;
1252e878a9dShauke uint8_t loopback = 0;
1264c25fb2fSozaki-r int error;
1275bf5cd24Schristos
128e4774a20Sdyoung if ((rt = rtcache_validate(ro)) != NULL && (ifp = rt->rt_ifp) != NULL) {
1292e878a9dShauke const struct sockaddr_at *dst = satocsat(rtcache_getdst(ro));
1302e878a9dShauke uint16_t dnet = dst->sat_addr.s_net;
1312e878a9dShauke uint8_t dnode = dst->sat_addr.s_node;
13272fa642aSdyoung net = satosat(rt->rt_gateway)->sat_addr.s_net;
1332e878a9dShauke
1345493f188Sdyoung TAILQ_FOREACH(aa, &at_ifaddr, aa_list) {
1352e878a9dShauke if (ntohs(net) >= ntohs(aa->aa_firstnet) &&
1365bf5cd24Schristos ntohs(net) <= ntohs(aa->aa_lastnet)) {
1372e878a9dShauke /* Are we talking to ourselves? */
1382e878a9dShauke if (dnet == aa->aa_addr.sat_addr.s_net &&
1392e878a9dShauke dnode == aa->aa_addr.sat_addr.s_node) {
1402e878a9dShauke /* If to us, redirect to lo0. */
1412e878a9dShauke ifp = lo0ifp;
1422e878a9dShauke }
1432e878a9dShauke /* Or is it a broadcast? */
1442e878a9dShauke else if (dnet == aa->aa_addr.sat_addr.s_net &&
1452e878a9dShauke dnode == 255) {
1462e878a9dShauke /* If broadcast, loop back a copy. */
1472e878a9dShauke loopback = 1;
1482e878a9dShauke }
1495bf5cd24Schristos break;
1505bf5cd24Schristos }
1515bf5cd24Schristos }
1525bf5cd24Schristos }
1535bf5cd24Schristos if (aa == NULL) {
1542e878a9dShauke #ifdef NETATALKDEBUG
1555493f188Sdyoung printf("%s: no address found\n", __func__);
1562e878a9dShauke #endif
1575bf5cd24Schristos m_freem(m);
1584c25fb2fSozaki-r error = EINVAL;
1594c25fb2fSozaki-r goto out;
1605bf5cd24Schristos }
1615bf5cd24Schristos /*
1625bf5cd24Schristos * There are several places in the kernel where data is added to
1635bf5cd24Schristos * an mbuf without ensuring that the mbuf pointer is aligned.
1645bf5cd24Schristos * This is bad for transition routing, since phase 1 and phase 2
1655bf5cd24Schristos * packets end up poorly aligned due to the three byte elap header.
1665bf5cd24Schristos */
1675bf5cd24Schristos if (!(aa->aa_flags & AFA_PHASE2)) {
16801c8c2e9Sitojun M_PREPEND(m, SZ_ELAPHDR, M_DONTWAIT);
1694c25fb2fSozaki-r if (m == NULL) {
1704c25fb2fSozaki-r error = ENOBUFS;
1714c25fb2fSozaki-r goto out;
1724c25fb2fSozaki-r }
1735bf5cd24Schristos
1745bf5cd24Schristos elh = mtod(m, struct elaphdr *);
175*65885579Sriastradh elh->el_snode = aa->aa_addr.sat_addr.s_node;
1765bf5cd24Schristos elh->el_type = ELAP_DDPEXTEND;
1775493f188Sdyoung if (ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) >=
1785bf5cd24Schristos ntohs(aa->aa_firstnet) &&
1795493f188Sdyoung ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) <=
1805bf5cd24Schristos ntohs(aa->aa_lastnet)) {
1812e878a9dShauke elh->el_dnode =
1822e878a9dShauke satocsat(rtcache_getdst(ro))->sat_addr.s_node;
1835bf5cd24Schristos } else {
1845bf5cd24Schristos elh->el_dnode =
18572fa642aSdyoung satosat(rt->rt_gateway)->sat_addr.s_node;
1865bf5cd24Schristos }
1875bf5cd24Schristos }
1885493f188Sdyoung if (ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) >=
1895bf5cd24Schristos ntohs(aa->aa_firstnet) &&
1905493f188Sdyoung ntohs(satocsat(rtcache_getdst(ro))->sat_addr.s_net) <=
1915bf5cd24Schristos ntohs(aa->aa_lastnet)) {
1925493f188Sdyoung gate = *satocsat(rtcache_getdst(ro));
1935bf5cd24Schristos } else {
19472fa642aSdyoung gate = *satosat(rt->rt_gateway);
1955bf5cd24Schristos }
19672fa642aSdyoung rt->rt_use++;
1975bf5cd24Schristos
198da1b3a7aSaidan #if IFA_STATS
199da1b3a7aSaidan aa->aa_ifa.ifa_data.ifad_outbytes += m->m_pkthdr.len;
200da1b3a7aSaidan #endif
201da1b3a7aSaidan
2025bf5cd24Schristos /* XXX */
2032e878a9dShauke if (loopback && rtcache_getdst(ro)->sa_family == AF_APPLETALK) {
2042e878a9dShauke struct mbuf *copym = m_copypacket(m, M_DONTWAIT);
2052e878a9dShauke
2062e878a9dShauke #ifdef NETATALKDEBUG
2072e878a9dShauke printf("Looping back (not AARP).\n");
2082e878a9dShauke #endif
2092e878a9dShauke looutput(lo0ifp, copym, rtcache_getdst(ro), NULL);
2102e878a9dShauke }
2114c25fb2fSozaki-r
2124c25fb2fSozaki-r error = if_output_lock(ifp, ifp, m, (struct sockaddr *)&gate, NULL);
2134c25fb2fSozaki-r out:
2144c25fb2fSozaki-r rtcache_unref(rt, ro);
2154c25fb2fSozaki-r return error;
2165bf5cd24Schristos }
217