/************************************************************************/ /* */ /* ***** ***** */ /* ***** ***** */ /* ***** ***** */ /* ***** ***** */ /* *************** *************** */ /* ***************** ***************** */ /* *************** *************** */ /* ***** ***** TheNetNode */ /* ***** ***** Portable */ /* ***** ***** Network */ /* ***** ***** Software */ /* */ /* File src/l3ip.c (maintained by: DG1KWA) */ /* */ /* This file is part of "TheNetNode" - Software Package */ /* */ /* Copyright (C) 1998 - 2008 NORD>l2port != KERNEL_PORT) { #endif if ( (mbhd->l2fflg != L2CIP) || (!IPpar[mbhd->l2port].ipMode & IP_FORWARDING)) return; #ifdef KERNELIF } #endif rxnxt = mbhd->mbbp; /* Anfang des Headers merken */ ip_len = getchr(mbhd); /* Laenge und Version lesen */ ip.version = (ip_len >> 4) & 0x0f; /* Versionsnummer */ ip_len = (ip_len & 0x0f) << 2; /* und Laenge in Bytes */ mbhd->mbbp = rxnxt; /* zurueck an den Anfang */ if ( (mbhd->mbpc - (--mbhd->mbgc)) < IPLEN || (ip_len < IPLEN) || (ip.version != IPVERSION) #ifdef __WIN32__ || cksum(NULLHEADER, mbhd, (unsigned short)ip_len) != 0) #else || cksum(NULLHEADER, mbhd, ip_len) != 0) #endif /* WIN32 */ { ipInHdrErrors++; return; } /* jetzt wird der IP-Header ausgewertet */ getchr(mbhd); /* skip length/version */ ip.tos = getchr(mbhd); ip.length = get16(mbhd); ip.id = get16(mbhd); ip.offset = get16(mbhd); ip.flags.mf = (ip.offset & 0x2000) ? 1 : 0; ip.flags.df = (ip.offset & 0x4000) ? 1 : 0; ip.offset = (ip.offset & 0x1fff) << 3; ip.ttl = getchr(mbhd); ip.protocol = getchr(mbhd); ip.checksum = get16(mbhd); ip.source = get32(mbhd); ip.dest = get32(mbhd); /* ist das Ziel eine Broadcast-Addresse ? */ rxbroadcast = is_broadcast_address(ip.dest); /* Optionsfeld auswerten */ if ((ip.optlen = ip_len - IPLEN) != 0) for (i = 0; i < ip.optlen; i++) ip.options[i] = getchr(mbhd); length = ip.length - ip_len; /* wenn das Frame direkt an uns gerichtet ist und wir keinen Speicher */ /* mehr haben, muessen wir bescheid sagen */ if (!rxbroadcast && nmbfre < 256) icmp_output(&ip, mbhd, ICMP_QUENCH, 0, NULLICMP); /* Optionsfelder auswerten */ strict = 0; for (opt = ip.options; opt < &ip.options[ip.optlen]; opt += opt_len) { opt_len = opt[1]; if ((opt[0] & OPT_NUMBER) == IP_EOL) break; switch (opt[0] & OPT_NUMBER) { case IP_NOOP: opt_len = 1; break; case IP_SSROUTE: strict = 1; case IP_LSROUTE: if (!is_my_ip_addr(ip.dest)) break; if (opt[2] >= opt_len) break; ptr = opt + opt[2] - 1; memcpy(&ip.dest, ptr, 4); memcpy(ptr, &my_ip_addr, 4); opt[2] += 4; break; case IP_RROUTE: if (opt[2] >= opt_len) { if (!rxbroadcast) { union icmp_args icmp_args; icmp_args.pointer = IPLEN + opt - ip.options; icmp_output(&ip, mbhd, ICMP_PARAM_PROB, 0, &icmp_args); } return; } ptr = opt + opt[2] - 1; memcpy(ptr, &my_ip_addr, 4); opt += 4; break; } } #ifdef TCP_STACK /* Protokoll TCP, */ if ( (ip.protocol == TCP_PTCL) /* und an mich. */ &&(is_my_ip_addr(ip.dest))) { /* An TCP-Stack weiterleiten. */ TCPIPProcess(&ip, mbhd); return; } #endif /* TCP_STACK */ if ((is_my_ip_addr(ip.dest)) || rxbroadcast) { if ( !rxbroadcast && !ip.flags.mf && ip.offset == 0 && ip.protocol == ICMP_PTCL) { icmp_input(&ip, mbhd); ipInReceives++; return; } else { if (!rxbroadcast) icmp_output(&ip, mbhd, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, NULLICMP); ipInUnknownProtos++; return; } } ipForwDatagrams++; /* Wenn die Lifetime des Frames abgelaufen ist, Frame vernichten und */ /* Sender benachrichtigen */ if (--ip.ttl == 0) { icmp_output(&ip, mbhd, ICMP_TIME_EXCEED, 0, NULLICMP); ipInHdrErrors++; return; } /* Einen passenden Eintrag in der routing-Tabelle finden, wenn nichts */ /* passt, einen NetSearch durchfuehren (zZ noch nicht implementiert), */ /* wenn wir dort auch nichts finden, * wird das Frame vernichtet. */ #ifndef IPROUTEMOD if ((rp = rt_find(ip.dest)) == NULLROUTE) { icmp_output(&ip, mbhd, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, NULLICMP); ipOutNoRoutes++; return; } #else /* IPROUTEMOD */ if ((rp = rt_find(ip.dest)) == NULLROUTE) { /* Auf default-route pruefen. */ if ((rp = rt_find(my_default_ip_addr)) == NULLROUTE) { icmp_output(&ip, mbhd, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, NULLICMP); ipOutNoRoutes++; return; } } #endif /* IPROUTEMOD */ /* Den naechsten IP-Router berechnen, an den wir das Frame schicken */ /* (z.B. das Gateway). Wenn der Eintrag kein Gateway aufweist, dann */ /* die Zieladdresse nehmen. Wenn das Gateway nicht das Ziel ist, aber */ /* strictes Routen gefordert wurde, denn Fehler melden ! */ gateway = rp->gateway == 0L ? ip.dest : rp->gateway; if (strict && gateway != ip.dest) { icmp_output(&ip, mbhd, ICMP_DEST_UNREACH, ICMP_ROUTE_FAIL, NULLICMP); ipOutNoRoutes++; return; } /* Maximale Blockgroese lesen, auf Segmentation pruefen Bei NET/ROM */ /* wird immer eine MTU von 236 angenommen, da NET/ROM selber einen */ /* Segmenter enthaelt, wird dieser notfalls zuschlagen. */ #ifndef KERNELIF mtu = (rp->port == NETROM_PORT) ? 236 : portpar[rp->port].mtu; #else switch(rp->port) { case NETROM_PORT: mtu = 236; break; /* Fuer den Kernel eine Extrawurst */ case KERNEL_PORT: mtu = 1500; break; default: mtu = portpar[rp->port].mtu; } #endif /* Frame ist klein genug, dass es ohne Fragmentierung geroutet werden kann */ /* Alle Frames vom Kernel sollten hier lang gehen */ if (ip.length <= mtu) { if ((tbp = htonip(&ip, mbhd, 0)) == NULL) return; send_ip_mb(tbp, rp->port, gateway, ip.tos); return; } /* Das Frame ist zu gross, duerfen wir es zerlegen ? */ if (ip.flags.df && rp->port == NETROM) /* nein, Fehler melden */ { union icmp_args icmp_args; icmp_args.mtu = mtu; icmp_output(&ip, mbhd, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, &icmp_args); ipFragFails++; return; } /* Das Frame bei NETROM aufspalten */ if (rp->port == NETROM) { offset = ip.offset; mf_flag = ip.flags.mf; while (length != 0) { unsigned fragsize; ip.offset = offset; if (length + ip_len <= mtu) { fragsize = length; ip.flags.mf = mf_flag; } else { fragsize = (mtu - ip_len) & 0xfff8; ip.flags.mf = 1; } ip.length = fragsize + ip_len; if ((tbp = htonip(&ip, NULLBUF, 0)) == NULL) return; for (i = fragsize; i != 0; i--) putchr(getchr(mbhd), tbp); send_ip_mb(tbp, rp->port, gateway, ip.tos); ipFragCreates++; offset += fragsize; length -= fragsize; } } /* Frame ist zu gross, aber wir nehmen eine AX25- Fragmentierung vor */ else { if ((tbp = htonip(&ip, mbhd, 0)) == NULL) return; send_ip_mb(tbp, rp->port, gateway, ip.tos); } ipFragOKs++; return; } /************************************************************************ * Function : In der Routing-Tabelle den Weg zu einem Ziel suchen * * Inputs : IP addresse * * Returns : Entweder zeiger auf den Tabelleneintrag oder NULL * * Operation: Erst schauen ob der Cache-Eintrag passt, ansonsten wird die * Tabelle in absteigender Reihenfolge der Addressbits, wenn * einer passt, gehts los *----------------------------------------------------------------------*/ IP_ROUTE * rt_find(register ipaddr target) { register IP_ROUTE *iprp; ipaddr temp = target; register unsigned bits = 32; /* Routentabelle durchgehen, passenden Eintrag suchen */ for (iprp = (IP_ROUTE *)IP_Routes.head; iprp != (IP_ROUTE *)&IP_Routes; iprp = (IP_ROUTE *)iprp->nextip) { if (bits > iprp->bits) temp &= ~0L << (32 - (bits = iprp->bits)); /* Route gefunden */ if (iprp->dest == temp) return (iprp); } return (NULLROUTE); } /************************************************************************ * Function : den IP-Header aufbauen und in ein Buffer speichern * * Inputs : Zeiger auf den Header, den Buffer, Daten & Ckecksum Flag * * Returns : Zeiger auf den neuen Buffer im Netzwerk-format * * Operation: einen neuen Buffer erstellen, den Header abspeichern, * und wenn data nicht NULL ist, die Daten reinspeichern *----------------------------------------------------------------------*/ MBHEAD * htonip(register IP * iphdr, MBHEAD *data, BOOLEAN cflag) { unsigned hdr_len; register unsigned i; register MBHEAD *bufpoi; unsigned fl_offs; unsigned checksum; char huge *ptr, huge * cksum_ptr; hdr_len = IPLEN + iphdr->optlen; if ((bufpoi = ((MBHEAD *)allocb(ALLOC_MBHEAD))) == NULL) return(NULL); #ifdef __WIN32__ putchr((char)((IPVERSION << 4) | (hdr_len >> 2)), bufpoi); #else putchr((IPVERSION << 4) | (hdr_len >> 2), bufpoi); #endif /* WIN32 */ putchr(iphdr->tos, bufpoi); #ifdef __WIN32__ put16((unsigned short)iphdr->length, bufpoi); put16((unsigned short)iphdr->id, bufpoi); #else put16(iphdr->length, bufpoi); put16(iphdr->id, bufpoi); #endif /* WIN32 */ fl_offs = iphdr->offset >> 3; if (iphdr->flags.df) fl_offs |= 0x4000; if (iphdr->flags.mf) fl_offs |= 0x2000; #ifdef __WIN32__ put16((unsigned short)fl_offs, bufpoi); #else put16(fl_offs, bufpoi); #endif /* WIN32 */ putchr(iphdr->ttl, bufpoi); putchr(iphdr->protocol, bufpoi); cksum_ptr = bufpoi->mbbp; put16(0, bufpoi); put32(iphdr->source, bufpoi); put32(iphdr->dest, bufpoi); for (i = 0; i < iphdr->optlen; i++) putchr(iphdr->options[i], bufpoi); ptr = bufpoi->mbbp; i = bufpoi->mbpc; rwndmb(bufpoi); #ifdef __WIN32__ checksum = cflag ? iphdr->checksum : cksum(NULLHEADER, bufpoi, (unsigned short)hdr_len); #else checksum = cflag ? iphdr->checksum : cksum(NULLHEADER, bufpoi, hdr_len); #endif /* WIN32 */ bufpoi->mbbp = cksum_ptr; bufpoi->mbpc = 10; #ifdef __WIN32__ put16((unsigned short)checksum, bufpoi); #else put16(checksum, bufpoi); #endif /* WIN32 */ bufpoi->mbbp = ptr; bufpoi->mbpc = i; if (data != NULL) { i = data->mbpc - data->mbgc; while (i--) putchr(getchr(data), bufpoi); } return (bufpoi); } /* *********************************************************************** * Function : Ein IP datagramm senden * Modelled after the example interface on p 32 of RFC 791 * * Inputs : * * Returns : * * Operation: Netzwerk-Header erzeugen, Daten anfuegen und so tun, als ob * wir das Frame empfangen haetten * ---------------------------------------------------------------------*/ int ip_send(ipaddr source, /* source address */ ipaddr dest, /* Destination address */ unsigned protocol, /* Protocol */ unsigned tos, /* Type of service */ unsigned ttl, /* Time-to-live */ MBHEAD *bp, /* Data portion of datagram */ unsigned short length, /* Optional length of data portion */ unsigned short id, /* Optional identification */ unsigned df) /* Don't-fragment flag */ { register MBHEAD *tbp; IP ip; /* Pointer to IP header */ register IP *ipptr = &ip; static unsigned short id_cntr; /* Datagram serial number */ ipOutRequests++; if (length == 0 && bp != NULL) length = bp->mbpc - bp->mbgc; /* Fill in IP header */ ipptr->tos = tos; ipptr->length = IPLEN + length; ipptr->id = (id == 0) ? id_cntr++ : id; ipptr->offset = 0; ipptr->flags.mf = 0; ipptr->flags.df = df; ipptr->ttl = (ttl == 0) ? ipDefaultTTL : ttl; ipptr->protocol = protocol; ipptr->source = source; ipptr->dest = dest; ipptr->optlen = 0; if ((tbp = htonip(&ip, bp, 0)) == NULL) { dealmb(bp); return(1); } dealmb(bp); tbp->l2fflg = L2CIP; rwndmb(tbp); tbp->l2port = NETROM_PORT; /* der Port ist immer eingeschaltet */ relink((LEHEAD *)tbp, (LEHEAD *)iprxfl.tail); return (0); } /************************************************************************ * Function : Ip Router Interface zu NET/ROM * * Inputs : Frame zum senden, die Gateway-Addresse, tos-flag * * Returns : nix * * Operation: Das Frame wird nach der arp-Tabelle addressiert und in die * L3-Sendeliste gehaengt *----------------------------------------------------------------------*/ void nr_iface(MBHEAD *mhbp, ipaddr gateway) { register unsigned i; register MBHEAD *mb; register ARP_TAB *arp; NODE *np; /* den passenden ARP-Eintrag suchen und pruefen, ob der Node bekannt */ /* und erreichbar ist */ if ( (arp = res_arp(gateway, ARP_NETROM)) != NULLARP && (iscall(arp->callsign, &np, NULL, DG))) { l4pidx = l4pcid = NR_PROTO_IP; /* NETROM IP Family */ l4ahd2 = l4ahd3 = 0; /* unbenutzt ... */ l4aopc = NR4_OP_PID; /* NET/ROM Opcode fuer IP */ mb = gennhd(); /* L3/L4 Header erzeugen */ rwndmb(mhbp); /* Zurueckspulen zum senden */ i = mhbp->mbpc - mhbp->mbgc; /* Daten kopieren */ while (i--) putchr(getchr(mhbp), mb); mb->l2link = (LNKBLK *)np; /* Zielnode setzen */ mb->l4type = L4TNORMAL; /* Prioritaet setzen */ relink((LEHEAD *)mb, (LEHEAD *)l3txl.tail); /* nur umhaengen */ } dealmb(mhbp); } /************************************************************************ * Function : AX.25 Handler fuer den ip router * * Inputs : wie oben fuer NET/ROM * * Returns : nothing * * Operation: Feststellen, ob dg oder vc, dann Frame senden *----------------------------------------------------------------------*/ void l2_iface(MBHEAD *mhbp, unsigned port, ipaddr gateway, unsigned tos) { register ARP_TAB *arp; PTCENT *ptcp; #ifdef EAX25 MHEARD *mhp = NULL; #endif /* IP-Pid setzen */ rwndmb(mhbp); mhbp->l2fflg = L2CIP; /* find the arp entry. Die if none ! */ if ((arp = res_arp(gateway, ARP_AX25)) == NULLARP) { arp_request(gateway, ARP_AX25, port); dealmb(mhbp); } /* Now check to see if we are going to DG or VC it */ else if ( (arp->dgmode & 1) || ( (arp->dgmode == 0) && ( tos & DELAY || ( !(tos & RELIABILITY) /* && (ipPar[port].ipMode & DG_OK) */ )))) { #ifdef __WIN32__ sdui(arp->digi, arp->callsign, myid, (char)port, mhbp); #else sdui(arp->digi, arp->callsign, myid, port, mhbp); #endif /* WIN32 */ dealmb(mhbp); } else /* Virtual Circuit */ { #ifdef __WIN32__ lnkpoi = getlnk((unsigned char)port, myid, arp->callsign, arp->digi); #else lnkpoi = getlnk(port, myid, arp->callsign, arp->digi); #endif /* WIN32 */ if (lnkpoi) { if (lnkpoi->state == L2SDSCED) { ptcp = ptctab + g_uid(lnkpoi, L2_USER); ptcp->state = D_IPLINK; ptcp->ublk = NULL; ptcp->p_uid = NO_UID; #ifdef EAX25 /* TEST DG9OBU */ /* Call in L2-MHeard suchen */ if ((mhp = mh_lookup(&l2heard, arp->callsign)) != NULL) { if ( (mhp->eax_link == TRUE) /* mit EAX.25 gehoert */ && (portpar[mhp->port].eax_behaviour >= 1) /* und auf Port erlaubt */ ) { lnkpoi->bitmask = 0x7F; #ifdef __WIN32__ lnkpoi->maxframe = (unsigned char)portpar[lnkpoi->liport].maxframe_eax; #else lnkpoi->maxframe = portpar[lnkpoi->liport].maxframe_eax; #endif /* WIN32 */ } } #endif newlnk(); /* eventuell erst aufbauen */ } rwndmb(mhbp); i3tolnk(mhbp->l2fflg, lnkpoi, mhbp); /* -> ab in den Link */ } else dealmb(mhbp); } } /************************************************************************ * Function : Reverse ARP fuer eine Addresse/Hardwaretyp durchfuehren * * Inputs : Pointer to internet address and a hardware ( arp ) type * * Returns : Pointer to entry in arp table or a null pointer * * Operation: Die Tabelle linear durchsuchen ob ein Eintrag passt *----------------------------------------------------------------------*/ ARP_TAB * res_arp(register ipaddr target, register unsigned hwtype) { register ARP_TAB *arprp; for (arprp = (ARP_TAB *) Arp_tab.head; arprp != (ARP_TAB *) & Arp_tab; arprp = (ARP_TAB *) arprp->nextar) #ifdef __WIN32__ if ( (char)hwtype == arprp->hwtype #else if ( hwtype == arprp->hwtype #endif /* WIN32 */ && arprp->dest == target) return (arprp); return (NULLARP); } /************************************************************************ * Function : Eine ICMP-Antwort an den Absender des Frames zurueck * schicken, der Aufrufer gibt das Frame frei! * * Inputs : * * Returns : nix * * Operation: Did you ever hear the one about the vampire rabbit ? * Note lots of standard icmp bits are commented out. *----------------------------------------------------------------------*/ void icmp_output(IP *ip, /* Header of offending datagram */ register MBHEAD *data, /* Data portion of datagram */ unsigned type, /* Codes to send */ unsigned code, union icmp_args *args) { MBHEAD *bp, *bp2; ICMP icmp; /* ICMP protocol header */ unsigned short length; /* Total length of reply */ register unsigned short i; char huge *rxnxt; if ((unsigned char)ip->protocol == ICMP_PTCL) { /* Den Header durchsuchen, ob es sicher ist, eine ICMP Nachricht */ /* zurueckzuschicken */ rxnxt = data->mbbp; i = getchr(data) & 0xff; data->mbbp = rxnxt; data->mbgc--; switch (i) { case ICMP_ECHO_REPLY: case ICMP_ECHO: case ICMP_TIMESTAMP: case ICMP_TIME_REPLY: case ICMP_INFO_RQST: case ICMP_INFO_REPLY: break; /* These are all safe */ default: /* Auf eine Fehlermeldung keine Fehlermeldung senden (gibt */ /* sonst einen netten Ping-Pong) */ return; } } /* Berechnen, wieviel wir vom Frame zurueckschicken wollen, das sind */ /* maximal der fremde Header und 8 Bytes */ i = data->mbpc - data->mbgc; if (i > 8) i = 8; length = i + ICMPLEN + IPLEN + ip->optlen; /* Den Header neu aufbauen */ if ((bp = htonip(ip, NULLBUF, 1)) == NULL) return; /* Die Antwort-Daten hineinkopieren */ while (i--) putchr(getchr(data), bp); icmp.type = type; icmp.code = code; icmp.args.unused = 0; switch (type) { /* case ICMP_ECHO: icmpOutEchos++; break; case ICMP_ECHO_REPLY: icmpOutEchoReps++; break; case ICMP_INFO_RQST: break; case ICMP_INFO_REPLY: break; case ICMP_TIMESTAMP: icmpOutTimestamps++; break; case ICMP_ADDR_MASK: icmpOutAddrMasks++; break; case ICMP_ADDR_MASK_REPLY: icmpOutAddrMaskReps++; break; case ICMP_TIME_EXCEED: icmpOutTimeExcds++; break; case ICMP_QUENCH: icmpOutSrcQuenchs++; break; */ case ICMP_PARAM_PROB: /* icmpOutParmProbs++; */ icmp.args.pointer = args->pointer; break; case ICMP_REDIRECT: /* icmpOutRedirects++; */ icmp.args.address = args->address; break; case ICMP_TIME_REPLY: /* icmpOutTimestampReps++; */ icmp.args.echo.id = args->echo.id; icmp.args.echo.seq = args->echo.seq; break; case ICMP_DEST_UNREACH: if (icmp.code == ICMP_FRAG_NEEDED) icmp.args.mtu = args->mtu; /* icmpOutDestUnreachs++; */ break; } /* Jetzt generieren wir das eigentliche ICMP-Frame */ if ((bp2 = htonicmp(&icmp, bp)) == NULL) { dealmb(bp); return; } dealmb(bp); ip_send(my_ip_addr, ip->source, ICMP_PTCL, ip->tos, 0, bp2, length, 0, 0); } /************************************************************************ * Function : Einen ICMP Echo Request erzeugen und absenden * * Inputs : Zieladdresse, Sequenznummer und id, ein optionales * Laengenfeld (maximal 4 Bytes) zur freien Verwendung * * Returns : int - TRUE wenn das Frame erzeugt wurde * * Operation: Ein Frame anlegen und an die Zieladdresse schicken *----------------------------------------------------------------------*/ BOOLEAN pingem(ipaddr target, int seq, int id, int len, char *opt) { MBHEAD *bp, *data; ICMP icmp; /* ICMP protocol header */ /* Pruefen, ob Ziel irgendwie erreichbar ist */ if (rt_find(target) == NULLROUTE) return (FALSE); if ((data = ((MBHEAD *)allocb(ALLOC_MBHEAD))) == NULL) return(FALSE); put32(tic10, data); /* aktuelle Uhrzeit hineinschreiben */ put32((ULONG)userpo, data); /* und den User */ while (len--) putchr(*opt++, data); /* Optionsfeld schreiben */ /* icmpOutEchos++; icmpOutMsgs++; */ icmp.type = ICMP_ECHO; icmp.code = 0; icmp.args.echo.seq = seq; icmp.args.echo.id = id; if ((bp = (htonicmp(&icmp, data))) == NULL) { dealmb(data); return(FALSE); } dealmb(data); if (ip_send(my_ip_addr, target, ICMP_PTCL, LOW_DELAY, 0, bp, 0, 0, 0) == TRUE) return(FALSE); return (TRUE); } /************************************************************************ * Function : Einen ICMP-Header erzeugen, Daten hineinkopieren * * Inputs : Zeiger auf den ICMP-Header und Daten * * Returns : Zeiger auf das fertige Frame * * Operation: Well, there was this traveller in Transylvania, *----------------------------------------------------------------------*/ MBHEAD * htonicmp(register ICMP * icmp, MBHEAD *data) { register MBHEAD *bp; unsigned short checksum; register unsigned putcnt; /* char huge *nxtchr; */ char huge *cksum_ptr; if ((bp = ((MBHEAD *)allocb(ALLOC_MBHEAD))) == NULL) return(NULL); putchr(icmp->type, bp); putchr(icmp->code, bp); cksum_ptr = bp->mbbp; put16(0, bp); /* Clear checksum */ switch (icmp->type) { case ICMP_DEST_UNREACH: put16(0, bp); if (icmp->code == ICMP_FRAG_NEEDED) /* Deering/Mogul max MTU indication */ #ifdef __WIN32__ put16((unsigned short)icmp->args.mtu, bp); #else put16(icmp->args.mtu, bp); #endif /* WIN32 */ else put16(0, bp); break; case ICMP_PARAM_PROB: putchr(icmp->args.pointer, bp); putchr(0, bp); put16(0, bp); break; case ICMP_REDIRECT: put32(icmp->args.address, bp); break; case ICMP_ECHO: case ICMP_ECHO_REPLY: case ICMP_TIMESTAMP: case ICMP_TIME_REPLY: case ICMP_INFO_RQST: case ICMP_INFO_REPLY: #ifdef __WIN32__ put16((unsigned short)icmp->args.echo.id, bp); put16((unsigned short)icmp->args.echo.seq, bp); #else put16(icmp->args.echo.id, bp); put16(icmp->args.echo.seq, bp); #endif /* WIN32 */ break; default: put16(0, bp); put16(0, bp); break; } rwndmb(data); putcnt = data->mbpc; while (putcnt--) putchr(getchr(data), bp); /* Compute checksum, and stash result */ putcnt = bp->mbpc; rwndmb(bp); checksum = cksum(NULLHEADER, bp, bp->mbpc); bp->mbbp = cksum_ptr; bp->mbpc = 2; put16(checksum, bp); bp->mbpc = putcnt; rwndmb(bp); return (bp); } /* *********************************************************************** * Function : Behandlung von eingehenden ICMP-Frames * * Inputs : Dekodierter IP-Header und Framedaten * * Returns : nix * * Operation: Nur eine sehr einfache ICMP-Behandlung durchfuehren, um * PING-Anfragen zu beantworten. * ---------------------------------------------------------------------*/ void icmp_input(register IP * ip, register MBHEAD *bp) { MBHEAD *tbp; register MBHEAD *bp2; ICMP icmp; unsigned length, i; ULONG ping_tic; USRBLK *ping_user; char buf[50]; UWORD adr[4]; length = ip->length - IPLEN - ip->optlen; #ifdef __WIN32__ if (cksum(NULLHEADER, bp, (unsigned short)length) != 0) #else if (cksum(NULLHEADER, bp, length) != 0) #endif /* WIN32 */ return; if (bp->mbpc - bp->mbgc < 8) return; icmp.type = getchr(bp); icmp.code = getchr(bp); /* fremde Pings beantworten */ if (icmp.type == ICMP_ECHO) { get16(bp); icmp.args.echo.id = get16(bp); icmp.args.echo.seq = get16(bp); icmp.type = ICMP_ECHO_REPLY; bp2 = (MBHEAD *)allocb(ALLOC_MBHEAD); if (length > 8) { i = length - 8; while (i--) putchr(getchr(bp), bp2); } if ((tbp = htonicmp(&icmp, bp2)) == NULL) { dealmb(bp2); return; } dealmb(bp2); ip_send(ip->dest, ip->source, ICMP_PTCL, #ifdef __WIN32__ ip->tos, 0, tbp, (unsigned char)length, 0, 0); #else ip->tos, 0, tbp, length, 0, 0); #endif /* WIN32 */ return; } /* eigene Pings dem User ausgeben */ if (icmp.type == ICMP_ECHO_REPLY) { get16(bp); get16(bp); /* id */ get16(bp); /* seq */ ping_tic = get32(bp); ping_user = (void *)get32(bp); ping_tic = (tic10 - ping_tic) * 10; if (ping_tic <= 10) ping_tic = 10; for (i = 0; i < 4; i++) #ifdef __WIN32__ adr[i] = (unsigned short)(ip->source >> 8 * i) & 0xff; #else adr[i] = (ip->source >> 8 * i) & 0xff; #endif /* WIN32 */ sprintf(buf, "%d.%d.%d.%d rtt = %lums, ttl = %u", adr[3], adr[2], adr[1], adr[0], ping_tic, ip->ttl); #ifdef SEND_ASYNC_RESFIX /* Sicher ist sicher. */ buf[256] = 0; #endif /* SEND_ASYNC_RESFIX */ send_async_response(ping_user, "Ping response from ", buf); } } /************************************************************************ * Function : Ein "end-around-carry adjustment" durchfuehren * * Inputs : aktuelle Checksumme * * Returns : korrigierte Pruefsumme * * Operation: * *----------------------------------------------------------------------*/ unsigned short eac(long sum) { register unsigned short csum; #ifdef __WIN32__ sum = csum = (sum >> 16) + (sum & 0xffff); #else while ((csum = sum >> 16) != 0) sum = csum + (sum & 0xffffL); #endif /* WIN32 */ return ((unsigned short)(sum & 0xffffL)); /* nur 16 bits */ } /* *********************************************************************** * Function : Checksum an mhtyp, with optional pseudo-header * * Inputs : pseudo header, buffer to checksum and byte count * * Returns : unsigned integer checksum with eac corrected * * Operation: add up all bytes in a long, in perverse arpa way then eac * ---------------------------------------------------------------------*/ unsigned short cksum(register PSEUDO_HEADER * ph, register MBHEAD *m, register unsigned short len) { long sum; unsigned rxcnt; char huge *rxchr; sum = 0L; /* Sum pseudo-header, if present */ if (ph != NULLHEADER) { #ifndef L1TCPIP sum = ((unsigned *)(&ph->source))[0]; sum += ((unsigned *)(&ph->source))[1]; sum += ((unsigned *)(&ph->dest))[0]; sum += ((unsigned *)(&ph->dest))[1]; sum += ((unsigned)ph->protocol & 0xff); sum += ((unsigned)ph->length); #else sum += (ph->source & 0xffffUL); sum += ((ph->source >> 16) & 0xffffUL); sum += (ph->dest & 0xffffUL); sum += ((ph->dest >> 16) & 0xffffUL); sum += (ph->protocol & 0xff); sum += (ph->length); #endif /* L1TCPIP */ } rxcnt = m->mbgc; rxchr = m->mbbp; while (len > 0) { if (len > 1) { sum += (unsigned)get16(m); len -= 2; } else { #ifndef L1TCPIP sum += ((unsigned)getchr(m) & 0xFF00); #else sum += ((getchr(m) << 8) & 0xFF00); #endif /* L1TCPIP */ len--; } } m->mbgc = rxcnt; m->mbbp = rxchr; return ((unsigned short)(~eac(sum) & 0xffff)); } /* ********************************************************************** * Function : ARP service zur Beantwortung von ARP-Requests und * die Auswertung der Antworten * * Inputs : Zeiger auf das ARP-Frame * * Returns : nix * * Outputs : UI Frame mit dem Ergebnis der Bearbeitung * * Operation: Wenn nach uns gefragt wurde, oder nach einem Eintrag, der * als published gekennzeichnet ist, dann beantworten * ---------------------------------------------------------------------*/ void arp_service(MBHEAD *mhbp) { unsigned char arp_not_revarp; /* char *ptr; */ register MBHEAD *mbhd = mhbp; register ARP_TAB *ap; register unsigned i; ARP_TAB *atp; IP_ROUTE *ipr; /* ARP auf dem Port erlaubt ? */ if (!(IPpar[mbhd->l2port].ipMode & ARP_OK)) { return; } /* Hardware korrekt ? */ if ((arp.hardware = get16(mbhd)) != ARP_AX25) { /* arp_stat.badtype++; */ return; } /* Protokoll ok ? */ if ((arp.protocol = get16(mbhd)) != L2CIP) { /* arp_stat.badtype++; */ return; } arp.hwalen = getchr(mbhd); arp.pralen = getchr(mbhd); /* Laenge(n) ok ? */ if (arp.hwalen > MAXHWALEN || arp.pralen != sizeof(ipaddr)) { /* arp_stat.badlen++; */ return; } /* Opcode */ arp.opcode = get16(mbhd); /* Quell- und Ziel-Adressen (jeweils Call und IP-Adress) */ getfid((char *)arp.shwaddr, mbhd); arp.sprotaddr = get32(mbhd); getfid((char *)arp.thwaddr, mbhd); arp.tprotaddr = get32(mbhd); /* Frame an "QST ? */ if (cmpid((char *)arp.shwaddr, QST)) { /* arp_stat.badaddr++; */ return; } ap = res_arp(arp.sprotaddr, arp.hardware); /* IP-Adresse anhand des ARP-Requests lernen */ if ( (((i = is_my_ip_addr(arp.tprotaddr)) != 0) && ap == NULLARP) || (ap != NULLARP && ap->timer != 0)) { /* ARP-Eintrag machen (mit Timeout) */ arp_add(arp.sprotaddr, mbhd->l2port, (char *)arp.shwaddr, "", 2, ARPtimer, 1, FALSE); /* Falls der Weg zum neu eingetragenen Host nicht oder von einer */ /* Route erfasst wird, die die Frames auf einem anderen Port als dem */ /* Port auf dem das ARP empfangen wurde aussenden wuerde, dann eine */ /* explizite /32 Route zu diesem Host auf dem ARP-Empfangsport eintragen. */ ipr = rt_find((ipaddr)arp.sprotaddr); if ( (ipr == NULLROUTE) || ( (ipr != NULLROUTE) && (ipr->port != mbhd->l2port) ) ) rt_add(arp.sprotaddr, 32, 0L, mbhd->l2port, 0, 0, 0, TRUE); } if ( arp.opcode == REVARP_REQUEST || ( arp.opcode == ARP_REQUEST && ( (i /* = is_my_ip_addr(&arp.tprotaddr) */ ) || ( (ap = res_arp(arp.tprotaddr, arp.hardware)) != NULLARP && ap->publish_flag)))) { arp_not_revarp = (arp.opcode == ARP_REQUEST); if (!arp_not_revarp) for (atp = (ARP_TAB *) Arp_tab.head; atp != (ARP_TAB *) & Arp_tab; atp = (ARP_TAB *) atp->nextar) if ((i = cmpid(atp->callsign, (char *)arp.thwaddr)) != 0) break; if (arp_not_revarp || (i && ap->publish_flag)) { if (arp_not_revarp) { cpyid((char *)arp.thwaddr, (char *)arp.shwaddr); /* if (arp.hardware == ARP_AX25) */ arp.thwaddr[arp.hwalen - 1] |= 1; } cpyid((char *)arp.shwaddr, i ? myid : ap->callsign); arp.tprotaddr = arp_not_revarp ? arp.sprotaddr : ap->dest; arp.sprotaddr = i ? my_ip_addr : ap->dest; arp.opcode = arp_not_revarp ? ARP_REPLY : REVARP_REPLY; arp_send(mhbp->l2port, (char *)arp.thwaddr); /* Arp_stat.inreq++; */ } } } /************************************************************************ * Function : Einen ARP request/response Frame senden ( L2 AX25 ) * * Inputs : Internes ARP-Frame und Port * * Returns : nix * * Outputs : UI Frame mit ARP request/response * *----------------------------------------------------------------------*/ void arp_send(unsigned port, char *hwaddr) { register MBHEAD *mbhd; mbhd = (MBHEAD *)allocb(ALLOC_MBHEAD); #ifdef __WIN32__ put16((unsigned short)arp.hardware, mbhd); put16((unsigned short)arp.protocol, mbhd); #else put16(arp.hardware, mbhd); put16(arp.protocol, mbhd); #endif /* WIN32 */ putchr(arp.hwalen, mbhd); putchr(arp.pralen, mbhd); #ifdef __WIN32__ put16((unsigned short)arp.opcode, mbhd); #else put16(arp.opcode, mbhd); #endif /* WIN32 */ putfid((char *)arp.shwaddr, mbhd); put32(arp.sprotaddr, mbhd); putfid((char *)arp.thwaddr, mbhd); put32(arp.tprotaddr, mbhd); mbhd->l2fflg = L2CARP; rwndmb(mbhd); #ifdef __WIN32__ sdui("", hwaddr, myid, (char)port, mbhd); #else sdui("", hwaddr, myid, port, mbhd); #endif /* WIN32 */ dealmb(mbhd); } /************************************************************************ * Function : Ein ARP request Frame senden (L2 AX25 UI) * * Inputs : Internes ARP-Frame und der port * * Returns : nix * * Outputs : UI Frame mit ARP request senden * *----------------------------------------------------------------------*/ void arp_request(ipaddr gateway, unsigned hwtype, unsigned port) { register unsigned i; arp.hardware = hwtype; arp.protocol = L2CIP; arp.hwalen = L2IDLEN; arp.pralen = sizeof(ipaddr); arp.opcode = ARP_REQUEST; cpyid((char *)arp.shwaddr, myid); arp.sprotaddr = my_ip_addr; arp.tprotaddr = gateway; for (i = 0; i < L2IDLEN; i++) arp.thwaddr[i] = 0; arp_send(port, QST); } /*----------------------------------------------------------------------*/ /* Neue Status-Meldung verarbeiten */ /* 0=nicht verwendet, 1=connectet, 2=disconnectet, 3=busy, 4=Failure */ /*----------------------------------------------------------------------*/ BOOLEAN l2toip(WORD status) { ARP_TAB *ap; PTCENT *ptcp; const char *viap; viap = ndigipt(lnkpoi->viaidl); if (!cmpid(lnkpoi->srcid, myid)) return (FALSE); for (ap = (ARP_TAB *) Arp_tab.head; ap != (ARP_TAB *) & Arp_tab; ap = (ARP_TAB *) ap->nextar) if ( (ap->port == lnkpoi->liport) && cmpidl(ap->digi, viap) && cmpid(ap->callsign, lnkpoi->dstid)) { ptcp = ptctab + g_uid(lnkpoi, L2_USER); switch (status) { case L2MCONNT: case L2MLRESF: case L2MLREST: if (ptcp->state != D_IPLINK) ptcp->state = U_IPLINK; ptcp->ublk = NULL; ptcp->p_uid = NO_UID; break; case L2MDISCF: case L2MBUSYF: case L2MFAILW: clrptc(g_uid(lnkpoi, L2_USER)); } return (TRUE); /* Meldung verarbeitet */ } return (FALSE); } #endif /* End of src/l3ip.c */