From e5067d7cd967cb17067de24a162306b79f432b20 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 24 Jun 2015 04:23:46 +0200 Subject: Import newax25-2.4.3.patch.1.bz2 And cleanup the *.orig and *.rej files and whitespace errors that are part of the original patch. Signed-off-by: Ralf Baechle --- net/ax25/ax25_core.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 net/ax25/ax25_core.c (limited to 'net/ax25/ax25_core.c') diff --git a/net/ax25/ax25_core.c b/net/ax25/ax25_core.c new file mode 100644 index 000000000..37d171d83 --- /dev/null +++ b/net/ax25/ax25_core.c @@ -0,0 +1,513 @@ +/* + * 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++; + } +} -- cgit v1.2.3