/* * ax25_core.c: AX.25 core and support functions for NEW-AX.25 * * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), * Jonathan (G4KLX), Alan Cox (GW4PTS) * * Comment: * * Changelog: * * License: This module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include "af_ax25.h" #include "ax25_vj.h" #include "ax25_ddi.h" #include "ax25_core.h" #include "ax25_in.h" #include "ax25_subr.h" #include "ax25_ddi.h" rwlock_t ax25_list_lock = RW_LOCK_UNLOCKED; /* ---------------------------------------------------------------------*/ /* * The null address is defined as a callsign of all spaces with an * SSID of zero. */ ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; /* --------------------------------------------------------------------- */ /* * ax25 -> ascii conversion */ char *ax2asc(ax25_address *a) { static char buf[11]; char c, *s; int n; for (n = 0, s = buf; n < 6; n++) { c = (a->ax25_call[n] >> 1) & 0x7F; if (c != ' ') *s++ = c; } *s++ = '-'; if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { *s++ = '1'; n -= 10; } *s++ = n + '0'; *s++ = '\0'; if (*buf == '\0' || *buf == '-') return "*"; return buf; } /* ---------------------------------------------------------------------*/ /* * ascii -> ax25 conversion */ ax25_address *asc2ax(char *callsign) { static ax25_address addr; char *s; int n; for (s = callsign, n = 0; n < 6; n++) { if (*s != '\0' && *s != '-') addr.ax25_call[n] = *s++; else addr.ax25_call[n] = ' '; addr.ax25_call[n] <<= 1; addr.ax25_call[n] &= 0xFE; } if (*s++ == '\0') { addr.ax25_call[6] = 0x00; return &addr; } addr.ax25_call[6] = *s++ - '0'; if (*s != '\0') { addr.ax25_call[6] *= 10; addr.ax25_call[6] += *s++ - '0'; } addr.ax25_call[6] <<= 1; addr.ax25_call[6] &= 0x1E; return &addr; } /* ---------------------------------------------------------------------*/ /* * Compare two ax.25 addresses */ int ax25cmp(ax25_address *a, ax25_address *b) { int ct = 0; while (ct < 6) { if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ return 1; ct++; } if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ return 0; return 2; /* Partial match */ } /* ---------------------------------------------------------------------*/ /* * Add a socket to the bound sockets list. */ void ax25_insert_cb(ax25_cb *ax25) { if (ax25->device != NULL) { ax25_dev_insert_cb(ax25); return; } ax25->prev = NULL; ax25->next = ax25_list; write_lock(&ax25_list_lock); if (ax25_list != NULL) ax25_list->prev = ax25; ax25_list = ax25; write_unlock(&ax25_list_lock); ax25->inserted = 1; } /* ---------------------------------------------------------------------*/ /* * Socket removal is now protected agains bottom half ints * with a start/end_bh_atomic bracket. There should be no * need to mask interrupts on hardware level. */ void ax25_remove_cb(ax25_cb *ax25) { /* * unbound sockets are not in any list */ if (!ax25->inserted) return; if (ax25->device != NULL) { ax25_dev_remove_cb(ax25); return; } if (ax25_list != NULL) { write_lock(&ax25_list_lock); if (ax25->prev == NULL) ax25_list = ax25->next; else ax25->prev->next = ax25->next; if (ax25->next != NULL) ax25->next->prev = ax25->prev; write_unlock(&ax25_list_lock); } ax25->inserted = 0; } /* ---------------------------------------------------------------------*/ /* * Find an AX.25 control block given both ends. */ ax25_cb *ax25_find_cb(ax25_addr_t *addr, struct net_device *dev) { ax25_cb *s; read_lock(&ax25_list_lock); for (s = ax25_dev_list(dev) ; s != NULL; s = s->next) { if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) continue; if (s->addr.dcount == addr->dcount && !ax25cmp(&s->addr.src, &addr->src) && !ax25cmp(&s->addr.dest, &addr->dest)) { int i; if (addr->dcount == 0) break; if (addr->lastrepeat != s->addr.lastrepeat) continue; i = addr->dcount; while (i--) { if (ax25cmp(&s->addr.digipeater[i], &addr->digipeater[i])) break; } if (i < 0) break; } } read_unlock(&ax25_list_lock); return s; } /* ---------------------------------------------------------------------*/ void ax25_destroy_cb(ax25_cb *ax25) { ax25_remove_cb(ax25); ax25_clear_queues(ax25); ax25_free_cb(ax25); } /* ---------------------------------------------------------------------*/ void ax25_destroy_socket(struct sock *sk) { struct sk_buff *skb; while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { /* * this may be a pending SABM, waiting in the receive queue * to become accepted. But the listener just died :-) */ if (skb->sk != sk) { /* * signal the peer that we have closed the socket. * if he has already disconnected himself, just mark * the socket dead and move to TCP_CLOSE. This is only * for security, as ax25_disconnect should have already * done this. */ if (skb->sk->state == TCP_ESTABLISHED) skb->sk->protinfo.ax25->condition |= AX25_COND_RELEASE; else { printk(KERN_DEBUG "ax25_destroy_socket: TCP_CLOSE\n"); skb->sk->state = TCP_CLOSE; } skb->sk->dead = 1; } kfree_skb(skb); } sk_free(sk); } /* ---------------------------------------------------------------------*/ /* * Fill in a created AX.25 control block with the default * values for a particular device. */ void ax25_fillin_cb(ax25_cb *ax25, struct net_device *dev) { ax25->device = dev; ax25->vs_rtt = -1; if (dev != NULL) { ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1) / 4; ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1); ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2); ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3); ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2); ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN); ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE); ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF); if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) { ax25->seqmask = AX25_ESEQMASK; ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); } else { ax25->seqmask = AX25_SEQMASK; ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); } } else { ax25->rtt = AX25_DEF_T1 / 4; ax25->t1 = AX25_DEF_T1; ax25->t2 = AX25_DEF_T2; ax25->t3 = AX25_DEF_T3; ax25->n2 = AX25_DEF_N2; ax25->paclen = AX25_DEF_PACLEN; ax25->idle = AX25_DEF_IDLE; ax25->backoff = AX25_DEF_BACKOFF; if (AX25_DEF_AXDEFMODE) { ax25->seqmask = AX25_ESEQMASK; ax25->window = AX25_DEF_EWINDOW; } else { ax25->seqmask = AX25_ESEQMASK; ax25->window = AX25_DEF_WINDOW; } } } /* ---------------------------------------------------------------------*/ /* * Create an empty AX.25 control block. */ ax25_cb *ax25_create_cb(void) { ax25_cb *ax25; if ((ax25 = (ax25_cb *)kmalloc(sizeof(ax25_cb), GFP_ATOMIC)) == NULL) return NULL; MOD_INC_USE_COUNT; memset(ax25, 0x00, sizeof(ax25_cb)); skb_queue_head_init(&ax25->write_queue); skb_queue_head_init(&ax25->frag_queue); skb_queue_head_init(&ax25->ack_queue); skb_queue_head_init(&ax25->rcv_queue); ax25->state = AX25_LISTEN; ax25->condition = AX25_COND_SETUP; return ax25; } /* ---------------------------------------------------------------------*/ /* * Free an allocated ax25 control block. This is done to centralise * the MOD count code. */ void ax25_free_cb(ax25_cb *ax25) { if (ax25->slcomp != NULL) { axhc_free(ax25->slcomp); } if (ax25->peer != NULL) { ax25->peer->peer = NULL; } kfree(ax25); MOD_DEC_USE_COUNT; } /* ---------------------------------------------------------------------*/ void ax25_free_sock(struct sock *sk) { if (sk->protinfo.ax25 != NULL) ax25_free_cb(sk->protinfo.ax25); } /* ---------------------------------------------------------------------*/ /* * Given an AX.25 address pull of to, from, digi list, command/response and the start of data * */ unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_pktinfo *pkt_info) { int d = 0; if (len < 15) return NULL; pkt_info->cmdrsp = 0; memcpy(&pkt_info->addr.dest, buf, AX25_ADDR_LEN); if (buf[6] & AX25_CBIT) pkt_info->cmdrsp = AX25_COMMAND; buf += AX25_ADDR_LEN; len -= AX25_ADDR_LEN; memcpy(&pkt_info->addr.src, buf, AX25_ADDR_LEN); if (buf[6] & AX25_CBIT) pkt_info->cmdrsp = AX25_RESPONSE; pkt_info->dama = !(buf[6] & AX25_DAMA_FLAG); pkt_info->addr.lastrepeat = -1; pkt_info->addr.dcount = 0; while (!(buf[6] & AX25_EBIT)) { buf += AX25_ADDR_LEN; len -= AX25_ADDR_LEN; if (d < AX25_MAX_DIGIS && len >= 7) { memcpy(&pkt_info->addr.digipeater[d], buf, AX25_ADDR_LEN); if (buf[6] & AX25_HBIT) pkt_info->addr.lastrepeat = d; ++d; pkt_info->addr.dcount = d; } else return NULL; } return buf + AX25_ADDR_LEN; } /* ---------------------------------------------------------------------*/ /* * Assemble an AX.25 header from the bits */ int ax25_build_addr(unsigned char *buf, ax25_addr_t *addr, int flag, int seqmask) { int len = 0; int ct = 0; memcpy(buf, &addr->dest, AX25_ADDR_LEN); buf[6] &= ~(AX25_EBIT | AX25_CBIT); buf[6] |= AX25_SSSID_SPARE; if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; buf += AX25_ADDR_LEN; len += AX25_ADDR_LEN; memcpy(buf, &addr->src, AX25_ADDR_LEN); buf[6] &= ~(AX25_EBIT | AX25_CBIT); buf[6] &= ~AX25_SSSID_SPARE; if (seqmask == AX25_SEQMASK) buf[6] |= AX25_SSSID_SPARE; else buf[6] |= AX25_ESSID_SPARE; if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT; /* * Fast path the normal digiless path */ if (addr->dcount == 0) { buf[6] |= AX25_EBIT; return 2 * AX25_ADDR_LEN; } buf += AX25_ADDR_LEN; len += AX25_ADDR_LEN; while (ct < addr->dcount) { memcpy(buf, &addr->digipeater[ct], AX25_ADDR_LEN); if (ct <= addr->lastrepeat) buf[6] |= AX25_HBIT; else buf[6] &= ~AX25_HBIT; buf[6] &= ~AX25_EBIT; buf[6] |= AX25_SSSID_SPARE; buf += AX25_ADDR_LEN; len += AX25_ADDR_LEN; ct++; } buf[-1] |= AX25_EBIT; return len; } /* ---------------------------------------------------------------------*/ int ax25_sizeof_addr(ax25_addr_t *addr) { return AX25_ADDR_LEN * (addr->dcount+2); } /* ---------------------------------------------------------------------*/ /* * Invert AX.25 address. May not pass both parameters as same struct */ void ax25_invert_addr(ax25_addr_t *in, ax25_addr_t *out) { ax25_address *ip, *op; int dcount; dcount = out->dcount = in->dcount; out->lastrepeat = dcount - in->lastrepeat - 2; /* source/destination */ out->dest = in->src; out->src = in->dest; /* Invert the digipeaters */ if (dcount) { ip = in->digipeater; op = out->digipeater + (dcount-1); /* pointer scaled! */ while (dcount--) *op-- = *ip++; } }