freebsd-dev/sys/netipx/ipx_input.c
John Hay 8c7e2b93a0 Mega IPX commit.
Use the MAC address of an interface for the host part of an IPX address
and not the MAC address of the first interface for every IPX address.
This is more inline with the way others like Novell do it.
Mostly Submitted by: "Serge A. Babkin" <babkin@hq.icb.chel.su>

Take out the error messages (the ip icmp equivalent) with #ifdef IPXERRORMSGS.
This is bogus and as far as I could figure out IPX don't have anything like
it. This is a leftover from its XNS heritage. If nobody complains, I will
take it out completely in a few weeks.

Add some more ipxstat statistics counters.

Make ipxprintfs a sysctl variable and off by default.

Add IPX Netbios "routing" support. This is off by default and can be
switched on with a sysctl knob.

General code cleanup to at least use the same style throughout the IPX
code, but also be more style(9) conformant. Also make a lot of functions
static.

If I don't get any complaints I'll bring all of this over to the 2.2 tree
in a few weeks.
1997-05-10 09:58:58 +00:00

657 lines
15 KiB
C

/*
* Copyright (c) 1995, Mike Mitchell
* Copyright (c) 1984, 1985, 1986, 1987, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ipx_input.c
*
* $Id: ipx_input.c,v 1.12 1997/02/22 09:41:54 peter Exp $
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/netisr.h>
#include <net/raw_cb.h>
#include <netipx/ipx.h>
#include <netipx/spx.h>
#include <netipx/ipx_if.h>
#include <netipx/ipx_pcb.h>
#include <netipx/ipx_var.h>
#include <netipx/ipx_error.h>
int ipxcksum = 0;
SYSCTL_INT(_net_ipx_ipx, OID_AUTO, checksum, CTLFLAG_RW,
&ipxcksum, 0, "");
int ipxprintfs = 0; /* printing forwarding information */
SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxprintfs, CTLFLAG_RW,
&ipxprintfs, 0, "");
int ipxforwarding = 0;
SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxforwarding, CTLFLAG_RW,
&ipxforwarding, 0, "");
int ipxnetbios = 0;
SYSCTL_INT(_net_ipx, OID_AUTO, ipxnetbios, CTLFLAG_RW,
&ipxnetbios, 0, "");
union ipx_net ipx_zeronet;
union ipx_host ipx_zerohost;
union ipx_net ipx_broadnet;
union ipx_host ipx_broadhost;
struct ipxstat ipxstat;
struct sockaddr_ipx ipx_netmask, ipx_hostmask;
static u_short allones[] = {-1, -1, -1};
struct ipxpcb ipxpcb;
struct ipxpcb ipxrawpcb;
struct ifqueue ipxintrq;
int ipxqmaxlen = IFQ_MAXLEN;
long ipx_pexseq;
NETISR_SET(NETISR_IPX, ipxintr);
static int ipx_do_route(struct ipx_addr *src, struct route *ro);
static void ipx_undo_route(struct route *ro);
static void ipx_forward(struct mbuf *m);
/*
* IPX initialization.
*/
void
ipx_init()
{
ipx_broadnet = *(union ipx_net *)allones;
ipx_broadhost = *(union ipx_host *)allones;
ipx_pexseq = time.tv_usec;
ipxintrq.ifq_maxlen = ipxqmaxlen;
ipxpcb.ipxp_next = ipxpcb.ipxp_prev = &ipxpcb;
ipxrawpcb.ipxp_next = ipxrawpcb.ipxp_prev = &ipxrawpcb;
ipx_netmask.sipx_len = 6;
ipx_netmask.sipx_addr.x_net = ipx_broadnet;
ipx_hostmask.sipx_len = 12;
ipx_hostmask.sipx_addr.x_net = ipx_broadnet;
ipx_hostmask.sipx_addr.x_host = ipx_broadhost;
}
/*
* IPX input routine. Pass to next level.
*/
void
ipxintr()
{
register struct ipx *ipx;
register struct mbuf *m;
register struct ipxpcb *ipxp;
struct ipx_ifaddr *ia;
register int i;
int len, s;
char oddshortpacket = 0;
next:
/*
* Get next datagram off input queue and get IPX header
* in first mbuf.
*/
s = splimp();
IF_DEQUEUE(&ipxintrq, m);
splx(s);
if (m == NULL)
return;
/*
* If no IPX addresses have been set yet but the interfaces
* are receiving, can't do anything with incoming packets yet.
*/
if (ipx_ifaddr == NULL)
goto bad;
ipxstat.ipxs_total++;
if ((m->m_flags & M_EXT || m->m_len < sizeof(struct ipx)) &&
(m = m_pullup(m, sizeof(struct ipx))) == 0) {
ipxstat.ipxs_toosmall++;
goto next;
}
/*
* Give any raw listeners a crack at the packet
*/
for (ipxp = ipxrawpcb.ipxp_next; ipxp != &ipxrawpcb;
ipxp = ipxp->ipxp_next) {
struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL);
if (m1 != NULL)
ipx_input(m1, ipxp);
}
ipx = mtod(m, struct ipx *);
len = ntohs(ipx->ipx_len);
if ((len < m->m_pkthdr.len) && (oddshortpacket = len & 1)) {
/*
* If this packet is of odd length, and the length
* inside the header is less than the received packet
* length, preserve garbage byte for possible checksum.
*/
len++;
}
/*
* Check that the amount of data in the buffers
* is as at least much as the IPX header would have us expect.
* Trim mbufs if longer than we expect.
* Drop packet if shorter than we expect.
*/
if (m->m_pkthdr.len < len) {
ipxstat.ipxs_tooshort++;
goto bad;
}
if (m->m_pkthdr.len > len) {
if (m->m_len == m->m_pkthdr.len) {
m->m_len = len;
m->m_pkthdr.len = len;
} else
m_adj(m, len - m->m_pkthdr.len);
}
if (ipxcksum && ((i = ipx->ipx_sum) != 0xffff)) {
ipx->ipx_sum = 0;
if (i != (ipx->ipx_sum = ipx_cksum(m, len))) {
ipxstat.ipxs_badsum++;
goto bad;
}
}
/*
* Propagated (Netbios) packets (type 20) has to be handled
* different. :-(
*/
if (ipx->ipx_pt == IPXPROTO_NETBIOS) {
if (ipxnetbios) {
ipx_output_type20(m);
goto next;
} else
goto bad;
}
/*
* Is this a directed broadcast?
*/
if (ipx_hosteqnh(ipx_broadhost,ipx->ipx_dna.x_host)) {
if ((!ipx_neteq(ipx->ipx_dna, ipx->ipx_sna)) &&
(!ipx_neteqnn(ipx->ipx_dna.x_net, ipx_broadnet)) &&
(!ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet)) &&
(!ipx_neteqnn(ipx->ipx_dna.x_net, ipx_zeronet)) ) {
/*
* If it is a broadcast to the net where it was
* received from, treat it as ours.
*/
for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
if((ia->ia_ifa.ifa_ifp == m->m_pkthdr.rcvif) &&
ipx_neteq(ia->ia_addr.sipx_addr,
ipx->ipx_dna))
goto ours;
/*
* Look to see if I need to eat this packet.
* Algorithm is to forward all young packets
* and prematurely age any packets which will
* by physically broadcasted.
* Any very old packets eaten without forwarding
* would die anyway.
*
* Suggestion of Bill Nesheim, Cornell U.
*/
if (ipx->ipx_tc < IPX_MAXHOPS) {
ipx_forward(m);
goto next;
}
}
/*
* Is this our packet? If not, forward.
*/
} else {
for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
if (ipx_hosteq(ipx->ipx_dna, ia->ia_addr.sipx_addr) &&
(ipx_neteq(ipx->ipx_dna, ia->ia_addr.sipx_addr) ||
ipx_neteqnn(ipx->ipx_dna.x_net, ipx_zeronet)))
break;
if (ia == NULL) {
ipx_forward(m);
goto next;
}
}
ours:
/*
* Locate pcb for datagram.
*/
ipxp = ipx_pcblookup(&ipx->ipx_sna, ipx->ipx_dna.x_port, IPX_WILDCARD);
/*
* Switch out to protocol's input routine.
*/
if (ipxp != NULL) {
if (oddshortpacket) {
m_adj(m, -1);
}
ipxstat.ipxs_delivered++;
if ((ipxp->ipxp_flags & IPXP_ALL_PACKETS) == 0)
switch (ipx->ipx_pt) {
case IPXPROTO_SPX:
spx_input(m, ipxp);
goto next;
#ifdef IPXERRORMSGS
case IPXPROTO_ERROR:
ipx_err_input(m);
goto next;
#endif
}
ipx_input(m, ipxp);
} else {
#ifdef IPXERRORMSGS
ipx_error(m, IPX_ERR_NOSOCK, 0);
#else
goto bad;
#endif
}
goto next;
bad:
m_freem(m);
goto next;
}
#ifdef IPXERRORMSGS
u_char ipxctlerrmap[PRC_NCMDS] = {
ECONNABORTED, ECONNABORTED, 0, 0,
0, 0, EHOSTDOWN, EHOSTUNREACH,
ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
EMSGSIZE, 0, 0, 0,
0, 0, 0, 0
};
#endif
void
ipx_ctlinput(cmd, arg_as_sa, dummy)
int cmd;
struct sockaddr *arg_as_sa; /* XXX should be swapped with dummy */
void *dummy;
{
caddr_t arg = (/* XXX */ caddr_t)arg_as_sa;
struct ipx_addr *ipx;
#ifdef IPXERRORMSGS
struct ipxpcb *ipxp;
struct ipx_errp *errp;
int type;
#endif
if (cmd < 0 || cmd > PRC_NCMDS)
return;
#ifdef IPXERRORMSGS
if (ipxctlerrmap[cmd] == 0)
return; /* XXX */
type = IPX_ERR_UNREACH_HOST;
errp = (struct ipx_errp *)arg;
#endif
switch (cmd) {
struct sockaddr_ipx *sipx;
case PRC_IFDOWN:
case PRC_HOSTDEAD:
case PRC_HOSTUNREACH:
sipx = (struct sockaddr_ipx *)arg;
if (sipx->sipx_family != AF_IPX)
return;
ipx = &sipx->sipx_addr;
break;
default:
#ifdef IPXERRORMSGS
ipx = &errp->ipx_err_ipx.ipx_dna;
type = errp->ipx_err_num;
type = ntohs((u_short)type);
#endif
printf("ipx_ctlinput: cmd %d.\n", cmd);
break;
}
#ifdef IPXERRORMSGS
switch (type) {
case IPX_ERR_UNREACH_HOST:
ipx_pcbnotify(ipx, (int)ipxctlerrmap[cmd], ipx_abort, (long)0);
break;
case IPX_ERR_NOSOCK:
ipxp = ipx_pcblookup(ipx, errp->ipx_err_ipx.ipx_sna.x_port,
IPX_WILDCARD);
if(ipxp && ! ipx_nullhost(ipxp->ipxp_faddr))
ipx_drop(ipxp, (int)ipxctlerrmap[cmd]);
}
#endif
}
#ifdef IPXERRORMSGS
/*
* Forward a packet. If some error occurs return the sender
* an error packet. Note we can't always generate a meaningful
* error message because the IPX errors don't have a large enough repetoire
* of codes and types.
*/
#else
/*
* Forward a packet. If some error occurs drop the packet. IPX don't
* have a way to return errors to the sender.
*/
#endif
struct route ipx_droute;
struct route ipx_sroute;
static void
ipx_forward(m)
struct mbuf *m;
{
register struct ipx *ipx = mtod(m, struct ipx *);
register int error;
#ifdef IPXERRORMSGS
int type, code;
#endif
struct mbuf *mcopy = NULL;
int agedelta = 1;
int flags = IPX_FORWARDING;
int ok_there = 0;
int ok_back = 0;
if (ipxforwarding == 0) {
/* can't tell difference between net and host */
ipxstat.ipxs_cantforward++;
#ifdef IPXERRORMSGS
type = IPX_ERR_UNREACH_HOST, code = 0;
goto senderror;
#else
m_freem(m);
goto cleanup;
#endif
}
ipx->ipx_tc++;
if (ipx->ipx_tc > IPX_MAXHOPS) {
ipxstat.ipxs_cantforward++;
#ifdef IPXERRORMSGS
type = IPX_ERR_TOO_OLD, code = 0;
goto senderror;
#else
m_freem(m);
goto cleanup;
#endif
}
#ifdef IPXERRORMSGS
/*
* Save at most 42 bytes of the packet in case
* we need to generate an IPX error message to the src.
*/
mcopy = m_copy(m, 0, imin((int)ntohs(ipx->ipx_len), 42));
#endif
if ((ok_there = ipx_do_route(&ipx->ipx_dna,&ipx_droute)) == 0) {
ipxstat.ipxs_noroute++;
#ifdef IPXERRORMSGS
type = IPX_ERR_UNREACH_HOST, code = 0;
goto senderror;
#else
m_freem(m);
goto cleanup;
#endif
}
/*
* Here we think about forwarding broadcast packets,
* so we try to insure that it doesn't go back out
* on the interface it came in on. Also, if we
* are going to physically broadcast this, let us
* age the packet so we can eat it safely the second time around.
*/
if (ipx->ipx_dna.x_host.c_host[0] & 0x1) {
struct ipx_ifaddr *ia = ipx_iaonnetof(&ipx->ipx_dna);
struct ifnet *ifp;
if (ia != NULL) {
/* I'm gonna hafta eat this packet */
agedelta += IPX_MAXHOPS - ipx->ipx_tc;
ipx->ipx_tc = IPX_MAXHOPS;
}
if ((ok_back = ipx_do_route(&ipx->ipx_sna,&ipx_sroute)) == 0) {
/* error = ENETUNREACH; He'll never get it! */
ipxstat.ipxs_noroute++;
m_freem(m);
goto cleanup;
}
if (ipx_droute.ro_rt &&
(ifp = ipx_droute.ro_rt->rt_ifp) &&
ipx_sroute.ro_rt &&
(ifp != ipx_sroute.ro_rt->rt_ifp)) {
flags |= IPX_ALLOWBROADCAST;
} else {
ipxstat.ipxs_noroute++;
#ifdef IPXERRORMSGS
type = IPX_ERR_UNREACH_HOST, code = 0;
goto senderror;
#else
m_freem(m);
goto cleanup;
#endif
}
}
/* XXX
* I think the checksum generation is bogus. According to the NLSP
* spec the ipx_tc (hop count) field and the ipx_sum should be
* zero'ed before generating the checksum, ie. it should not be
* necesary to recompute it in the forwarding function.
*/
/* need to adjust checksum */
if (ipxcksum && ipx->ipx_sum != 0xffff) {
union bytes {
u_char c[4];
u_short s[2];
long l;
} x;
register int shift;
x.l = 0;
x.c[0] = agedelta;
shift = (((((int)ntohs(ipx->ipx_len)) + 1) >> 1) - 2) & 0xf;
x.l = ipx->ipx_sum + (x.s[0] << shift);
x.l = x.s[0] + x.s[1];
x.l = x.s[0] + x.s[1];
if (x.l == 0xffff)
ipx->ipx_sum = 0;
else
ipx->ipx_sum = x.l;
} else
ipx->ipx_sum = 0xffff;
error = ipx_outputfl(m, &ipx_droute, flags);
if (error == 0) {
ipxstat.ipxs_forward++;
if (ipxprintfs) {
printf("forward: ");
ipx_printhost(&ipx->ipx_sna);
printf(" to ");
ipx_printhost(&ipx->ipx_dna);
printf(" hops %d\n", ipx->ipx_tc);
}
} else if (mcopy != NULL) {
ipx = mtod(mcopy, struct ipx *);
#ifdef IPXERRORMSGS
type = IPX_ERR_UNSPEC_T, code = 0;
#endif
switch (error) {
case ENETUNREACH:
case EHOSTDOWN:
case EHOSTUNREACH:
case ENETDOWN:
case EPERM:
ipxstat.ipxs_noroute++;
#ifdef IPXERRORMSGS
type = IPX_ERR_UNREACH_HOST;
#endif
break;
case EMSGSIZE:
ipxstat.ipxs_mtutoosmall++;
#ifdef IPXERRORMSGS
type = IPX_ERR_TOO_BIG;
code = 576; /* too hard to figure out mtu here */
#endif
break;
case ENOBUFS:
ipxstat.ipxs_odropped++;
#ifdef IPXERRORMSGS
type = IPX_ERR_UNSPEC_T;
#endif
break;
}
mcopy = NULL;
#ifdef IPXERRORMSGS
senderror:
ipx_error(m, type, code);
#else
m_freem(m);
#endif
}
cleanup:
if (ok_there)
ipx_undo_route(&ipx_droute);
if (ok_back)
ipx_undo_route(&ipx_sroute);
if (mcopy != NULL)
m_freem(mcopy);
}
static int
ipx_do_route(src, ro)
struct ipx_addr *src;
struct route *ro;
{
struct sockaddr_ipx *dst;
bzero((caddr_t)ro, sizeof(*ro));
dst = (struct sockaddr_ipx *)&ro->ro_dst;
dst->sipx_len = sizeof(*dst);
dst->sipx_family = AF_IPX;
dst->sipx_addr = *src;
dst->sipx_addr.x_port = 0;
rtalloc(ro);
if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
return (0);
}
ro->ro_rt->rt_use++;
return (1);
}
static void
ipx_undo_route(ro)
register struct route *ro;
{
if (ro->ro_rt != NULL) {
RTFREE(ro->ro_rt);
}
}
void
ipx_watch_output(m, ifp)
struct mbuf *m;
struct ifnet *ifp;
{
register struct ipxpcb *ipxp;
register struct ifaddr *ifa;
register struct ipx_ifaddr *ia;
/*
* Give any raw listeners a crack at the packet
*/
for (ipxp = ipxrawpcb.ipxp_next; ipxp != &ipxrawpcb;
ipxp = ipxp->ipxp_next) {
struct mbuf *m0 = m_copy(m, 0, (int)M_COPYALL);
if (m0 != NULL) {
register struct ipx *ipx;
M_PREPEND(m0, sizeof(*ipx), M_DONTWAIT);
if (m0 == NULL)
continue;
ipx = mtod(m0, struct ipx *);
ipx->ipx_sna.x_net = ipx_zeronet;
for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
if (ifp == ia->ia_ifp)
break;
if (ia == NULL)
ipx->ipx_sna.x_host = ipx_zerohost;
else
ipx->ipx_sna.x_host =
ia->ia_addr.sipx_addr.x_host;
if (ifp != NULL && (ifp->if_flags & IFF_POINTOPOINT))
for(ifa = ifp->if_addrhead.tqh_first; ifa != NULL;
ifa = ifa->ifa_link.tqe_next) {
if (ifa->ifa_addr->sa_family == AF_IPX) {
ipx->ipx_sna = IA_SIPX(ifa)->sipx_addr;
break;
}
}
ipx->ipx_len = ntohl(m0->m_pkthdr.len);
ipx_input(m0, ipxp);
}
}
}