/* Vestigial AX.25 link layer, understands only UI frames */ #include #include "machdep.h" #include "mbuf.h" #include "iface.h" #include "timer.h" #include "arp.h" #include "slip.h" #include "ax25.h" #include #ifdef TRACE #include "trace.h" #endif /* AX.25 broadcast address: "QST-0" in shifted ascii */ struct ax25_addr ax25_bdcst = { 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, ('0'<<1) | E, }; struct ax25_addr mycall; int digipeat; /* Controls digipeating */ /* Send IP datagrams in AX.25 UI frames using ARP */ int ax_send(bp,interface,gateway,precedence,delay,throughput,reliability) struct mbuf *bp; struct interface *interface; int32 gateway; char precedence; char delay; char throughput; char reliability; { char *hw_addr,*res_arp(); hw_addr = res_arp(interface,ARP_AX25,gateway,bp); if(hw_addr != NULLCHAR) (*interface->output)(interface,(struct ax25_addr *)hw_addr, interface->hwaddr,PID_IP,bp); } /* Send a raw packet to the KISS TNC using AX.25 link header. * Note that the calling order here must match ec_output * since ARP also uses it. */ kiss_output(interface,dest,src,pid,bp) struct interface *interface; struct ax25_addr *dest; /* Destination AX.25 address (7 bytes, shifted) */ struct ax25_addr *src; /* Source AX.25 address (7 bytes, shifted) */ char pid; /* Protocol ID */ struct mbuf *bp; /* Data field (follows PID) */ { register struct mbuf *hbp; struct mbuf *ax_encode(); struct slip *sp; if((bp = ax_encode(dest,src,pid,bp)) == NULLBUF) return; #ifdef TRACE if(trace & TRACE_AX25){ printf("%s sent:\r\n",interface->name); if((trace & TRACE_HDR) > 1) ax25_dump(bp); if(trace & TRACE_DUMP) hexdump(bp); if(trace & TRACE_ASCII) asciidump(bp); fflush(stdout); } #endif /* Put type field for KISS TNC on front */ if((hbp = alloc_mbuf(1)) == NULLBUF){ free_p(bp); return; } *hbp->data = 0; hbp->cnt = 1; hbp->next = bp; slipq(interface->dev,hbp); } /* Put a AX.25 header on the front of a packet */ struct mbuf * ax_encode(dest,src,pid,bp) struct ax25_addr *dest; /* Destination AX.25 address (7 bytes, shifted) */ struct ax25_addr *src; /* Source AX.25 address (7 bytes, shifted) */ char pid; /* Protocol ID */ struct mbuf *bp; /* Data field (follows PID) */ { register struct ax25_addr *ap; struct mbuf *abp; char *cp; int ndigi; int16 hdr_len; /* Determine length of dest addr */ for(ndigi = 0,ap = dest; (ap->ssid & E) == 0; ap = (struct ax25_addr *) ((char *)ap + AXALEN)) ndigi++; /* Compute header length: * 2 AX.25 address fields for source and dest + * "ndigi" AX.25 address field(s) for digipeaters + * 2 bytes for control and PID fields */ hdr_len = (2 + ndigi)*AXALEN + 2; /* Create AX.25 link level header */ if((abp = alloc_mbuf(hdr_len)) == NULLBUF){ free_p(bp); return NULLBUF; } abp->cnt = hdr_len; /* Now fill it in */ ap = (struct ax25_addr *)(abp->data); bcopy((char *)dest,(char *)ap,AXALEN); ap->ssid &= ~E; ap = (struct ax25_addr *) ((char *)ap + AXALEN); dest = (struct ax25_addr *) ((char *)dest + AXALEN); bcopy((char *)src,(char *)ap,AXALEN); ap->ssid &= ~E; cp = (char *)ap; /* get pointer to last address (source) */ ap = (struct ax25_addr *) ((char *)ap + AXALEN); while(ndigi-- != 0) { bcopy((char *)dest,(char *)ap,AXALEN); dest = (struct ax25_addr *) ((char *)dest + AXALEN); cp = (char *)ap; ap = (struct ax25_addr *) ((char *)ap + AXALEN); } ((struct ax25_addr *)cp)->ssid |= E; /* Mark end of address field */ cp = (char *)ap; /* Point to first byte past address field */ *cp++ = UI; *cp++ = pid; abp->next = bp; /* Link in data field */ return abp; } /* Process incoming KISS TNC frame */ kiss_recv(interface,bp) struct interface *interface; struct mbuf *bp; { pullup(&bp,NULLCHAR,1); /* remove KISS TNC type field */ if(bp != NULLBUF) ax_recv(interface,bp); } /* Process incoming AX.25 packets. * After optional tracing, the address field is examined. If it is * directed to us as a digipeater, repeat it. If it is addressed to * us or to QST-0, kick it upstairs depending on the protocol ID. */ int ax_recv(interface,bp) struct interface *interface; struct mbuf *bp; { void arp_input(),ip_route(); struct ax25_addr *ap,*ap1; char pid,multicast,ours,*control,*cbyte(); int addrsize; struct mbuf *hbp; #ifdef TRACE if(trace & TRACE_AX25){ printf("%s recv:\r\n",interface->name); if((trace & TRACE_HDR) > 1) ax25_dump(bp); if(trace & TRACE_DUMP) hexdump(bp); if(trace & TRACE_ASCII) asciidump(bp); fflush(stdout); } #endif control = cbyte(bp); /* control -> control byte */ ap = (struct ax25_addr *)bp->data; /* -> address field */ addrsize = control - (char *)ap; /* # bytes in address field */ /* Check for either a missing control byte or a residual length * address field */ if(control == NULL || addrsize % AXALEN != 0){ free_p(bp); return; } addrsize /= AXALEN; /* # addresses in address field */ /* Check for invalid address field (too short or odd length) */ if(addrsize < 2) { free_p(bp); return; } /* Rescan, looking for our call in the repeater fields, if any. * Repeat appropriate packets. */ /* for(ap1 = &ap[2]; ap1 < &ap[addrsize]; ap1++){ */ for(ap1 = (struct ax25_addr *) ((char *)ap + 2*AXALEN); ap1 < (struct ax25_addr *) ((char *)ap + addrsize*AXALEN); ap1 = (struct ax25_addr *) ((char *)ap1 + AXALEN)) { if((ap1->ssid & REPEATED) == 0){ /* Check if packet is directed to us as a digipeater */ if(digipeat && addreq(ap1,&mycall)){ /* Yes, kick it back out */ ap1->ssid |= REPEATED; /* Put type field for KISS TNC on front */ if((hbp = alloc_mbuf(1)) == NULLBUF){ free_p(bp); return; } *hbp->data = 0; hbp->cnt = 1; hbp->next = bp; slipq(interface->dev,hbp); } else { /* Addressed to some other digipeater */ free_p(bp); } return; } } /* Packet has passed all repeaters, now look at destination */ ours = 0; if(addreq(&ap[0],&ax25_bdcst)){ multicast = 1; /* Broadcast packet */ } else if(addreq(&ap[0],&mycall)){ multicast = 0; /* Packet directed at us */ ours = 1; #if 0 /* we really do want to see all of the packets, later on */ } else { /* Not for us */ free_p(bp); return; #endif } /* Now remove the header and the control field. Note: This will * have to be changed if the connected mode of AX.25 (ugh!) is ever * implemented */ pullup(&bp,NULLCHAR,1 + addrsize * AXALEN); /* Examine the protocol ID field and switch to the right protocol */ if(pullup(&bp,&pid,1) != 1) return; /* No PID, probably not an I-frame */ switch(pid & 0xff){ case PID_ARP: arp_input(interface,bp); break; case PID_IP: if (!(ours || multicast)) { free_p(bp); break; } ip_route(bp,multicast); break; #ifdef NETROM case PID_NETROM: netrom_input(interface, bp); break; #endif default: free_p(bp); break; } } /* Display or change our AX.25 address */ domycall(argc,argv) int argc; char *argv[]; { char buf[15]; if(argc < 2){ pax25(buf,&mycall); printf("%s\r\n",buf); return 0; } if(setcall(&mycall,argv[1]) == -1) return -1; mycall.ssid |= E; } /* * setcall - convert callsign plus substation ID of the form * "KA9Q-0" to AX.25 (shifted) address format * Address extension bit is left clear * Return -1 on error, 0 if OK */ int setcall(out,call) struct ax25_addr *out; char *call; { int csize; unsigned ssid; register int i; register char *cp,*dp; char c,*index(); if(out == (struct ax25_addr *)NULL || call == NULLCHAR || *call == '\0'){ return -1; } /* Find dash, if any, separating callsign from ssid * Then compute length of callsign field and make sure * it isn't excessive */ dp = index(call,'-'); if(dp == NULLCHAR) csize = strlen(call); else csize = dp - call; if(csize > 6) return -1; /* Now find and convert ssid, if any */ if(dp != NULLCHAR){ dp++; /* skip dash */ ssid = atoi(dp); if(ssid > 15) return -1; } else ssid = 0; /* Copy upper-case callsign, left shifted one bit */ cp = out->call; for(i=0;issid = 0x60 | (ssid << 1); return 0; } static addreq(a,b) register struct ax25_addr *a,*b; { if(bcmp(a->call,b->call,ALEN) != 0) return 0; if((a->ssid & SSID) != (b->ssid & SSID)) return 0; return 1; } /* Convert encoded AX.25 address to printable string */ pax25(e,addr) char *e; struct ax25_addr *addr; { register int i; char c,*cp; cp = addr->call; for(i=6;i != 0;i--){ c = (*cp++ >> 1) & 0x7f; if(c == ' ') break; *e++ = c; } sprintf(e,"-%d",(addr->ssid >> 1) & 0xf); /* ssid */ } /* Print a string of AX.25 addresses in the form * "KA9Q-0 [via N4HY-0,N2DSY-2]" */ psax25(e,addr) register char *e; register struct ax25_addr *addr; { int i; for(i=0;;i++){ pax25(e,addr); if(addr->ssid & E) break; if(i == 0) strcat(e," via "); else strcat(e,","); e += strlen(e); /* addr++; */ addr = (struct ax25_addr *) ((char *)addr + AXALEN); } } /* Return a pointer to the control byte in the given frame */ char * cbyte(fp) register struct mbuf *fp; { register char *cp; register unsigned cnt; if(fp == NULLBUF || fp->data == NULLCHAR) return NULLCHAR; cnt = fp->cnt; cp = fp->data; while(cnt != 0 && (*cp & E) == 0){ cnt--; cp++; } /* If the address field never ended, cnt = 0; if it ended * on the last byte of a frame, cnt = 1. In either case, * there is no control field */ if(cnt <= 1) return NULLCHAR; else return cp + 1; } dokiss(argc,argv) int argc; char *argv[]; { struct interface *ifp; struct mbuf *hbp; int i; char *cp; if(argc < 2){ printf("Interface name missing\r\n"); return 1; } for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){ if(strcmp(argv[1],ifp->name) == 0) break; } if(ifp == NULL){ printf("Interface \"%s\" unknown\r\n",argv[1]); return 1; } if(ifp->output != kiss_output){ printf("Interface \"%s\" not kiss\r\n",argv[1]); return 1; } if(argc < 3){ printf("Data field missing\r\n"); return 1; } /* Number of bytes in message == number of args - 2, since * first two args are "kiss" and the interface name */ if((hbp = alloc_mbuf(argc - 2)) == NULLBUF){ free_p(hbp); return; } hbp->cnt = argc - 2; hbp->next = NULL; for(i=2,cp = hbp->data;i < argc;i++,cp++){ *cp = htoi(argv[i]); } slipq(ifp->dev,hbp); } #ifdef TRACE /* Dump an AX.25 packet header */ ax25_dump(abp) struct mbuf *abp; { struct mbuf *bp; struct ax25_addr src,dest,addr; char tmp[20],*cp,control,cmdrsp,pid; int16 len,type,ftype(); dup_p(&bp,abp,0,len_mbuf(abp)); /* Read and print the destination and source addresses */ if(pullup(&bp,(char *)&dest,AXALEN) != AXALEN) goto quit; if(pullup(&bp,(char *)&src,AXALEN) != AXALEN) goto quit; pax25(tmp,&src); printf("AX25: %s",tmp); pax25(tmp,&dest); printf("->%s",tmp); if((src.ssid & E) == 0){ /* Find the last address entry in the digi string */ printf(" v"); while(pullup(&bp,(char *)&addr,AXALEN) == AXALEN){ pax25(tmp,&addr); #ifdef AMIGA /* this is just personal preference; reminds me of the WA8DED TNC-1 code */ printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "*":""); #else printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "H":""); #endif if(addr.ssid & E) break; /* Found it */ } } if(pullup(&bp,&control,1) != 1) goto quit; putchar(' '); type = ftype(control); switch(type){ case I: printf("I"); break; case SABM: printf("SABM"); break; case DISC: printf("DISC"); break; case DM: printf("DM"); break; case UA: printf("UA"); break; case RR: printf("RR"); break; case RNR: printf("RNR"); break; case REJ: printf("REJ"); break; case FRMR: printf("FRMR"); break; case UI: printf("UI"); break; default: printf("[invalid]"); } if((dest.ssid & C) != (src.ssid & C)){ if(dest.ssid & C) cmdrsp = COMMAND; else cmdrsp = RESPONSE; } else cmdrsp = UNKNOWN; /* Dump poll/final bit */ if(control & PF){ switch(cmdrsp){ case COMMAND: printf("(P)"); break; case RESPONSE: printf("(F)"); break; default: printf("(P/F)"); break; } } /* Dump sequence numbers */ if((type & 0x3) != U) /* I or S frame? */ printf(" NR=%d",(control>>5)&7); if((type & 0x1) == I) /* I frame? */ printf(" NS=%d",(control>>1)&7); if(pullup(&bp,&pid,1) != 1) goto quit; printf(" pid 0x%x\r\n",pid & 0xff); if((trace & TRACE_HDR) > 2){ switch(pid & 0xff){ case PID_ARP: arp_dump(bp); break; case PID_IP: ip_dump(bp); break; #ifdef NETROM case PID_NETROM: netrom_dump(bp); break; #endif } } free_p(bp); fflush(stdout); return; quit: /* I hate go-to's, but sometimes there's no real choice */ free_p(bp); printf("\r\n"); fflush(stdout); } /* Figure out the frame type from the control field * This is done by masking out any sequence numbers and the * poll/final bit after determining the general class (I/S/U) of the frame */ static int16 ftype(control) register char control; { if((control & 1) == 0) /* An I-frame is an I-frame... */ return I; if(control & 2) /* U-frames use all except P/F bit for type */ return(control & ~PF); else /* S-frames use low order 4 bits for type */ return(control & 0xf); } #endif