12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds #include <linux/errno.h>
71da177e4SLinus Torvalds #include <linux/types.h>
81da177e4SLinus Torvalds #include <linux/socket.h>
91da177e4SLinus Torvalds #include <linux/in.h>
101da177e4SLinus Torvalds #include <linux/kernel.h>
1170868eacSRalf Baechle #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/timer.h>
131da177e4SLinus Torvalds #include <linux/string.h>
141da177e4SLinus Torvalds #include <linux/sockios.h>
151da177e4SLinus Torvalds #include <linux/net.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
171da177e4SLinus Torvalds #include <net/ax25.h>
181da177e4SLinus Torvalds #include <linux/inet.h>
191da177e4SLinus Torvalds #include <linux/netdevice.h>
201da177e4SLinus Torvalds #include <linux/if_arp.h>
211da177e4SLinus Torvalds #include <linux/skbuff.h>
221da177e4SLinus Torvalds #include <net/sock.h>
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
241da177e4SLinus Torvalds #include <linux/fcntl.h>
251da177e4SLinus Torvalds #include <linux/termios.h> /* For TIOCINQ/OUTQ */
261da177e4SLinus Torvalds #include <linux/mm.h>
271da177e4SLinus Torvalds #include <linux/interrupt.h>
281da177e4SLinus Torvalds #include <linux/notifier.h>
291da177e4SLinus Torvalds #include <linux/proc_fs.h>
301da177e4SLinus Torvalds #include <linux/stat.h>
311da177e4SLinus Torvalds #include <linux/sysctl.h>
321da177e4SLinus Torvalds #include <net/ip.h>
331da177e4SLinus Torvalds #include <net/arp.h>
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds /*
361da177e4SLinus Torvalds * IP over AX.25 encapsulation.
371da177e4SLinus Torvalds */
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds /*
401da177e4SLinus Torvalds * Shove an AX.25 UI header on an IP packet and handle ARP
411da177e4SLinus Torvalds */
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds #ifdef CONFIG_INET
441da177e4SLinus Torvalds
ax25_hard_header(struct sk_buff * skb,struct net_device * dev,unsigned short type,const void * daddr,const void * saddr,unsigned int len)4546d4e47aSEric W. Biederman static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev,
463b04dddeSStephen Hemminger unsigned short type, const void *daddr,
4795c96174SEric Dumazet const void *saddr, unsigned int len)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds unsigned char *buff;
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /* they sometimes come back to us... */
521da177e4SLinus Torvalds if (type == ETH_P_AX25)
531da177e4SLinus Torvalds return 0;
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds /* header is an AX.25 UI frame from us to them */
561da177e4SLinus Torvalds buff = skb_push(skb, AX25_HEADER_LEN);
571da177e4SLinus Torvalds *buff++ = 0x00; /* KISS DATA */
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds if (daddr != NULL)
601da177e4SLinus Torvalds memcpy(buff, daddr, dev->addr_len); /* Address specified */
611da177e4SLinus Torvalds
621da177e4SLinus Torvalds buff[6] &= ~AX25_CBIT;
631da177e4SLinus Torvalds buff[6] &= ~AX25_EBIT;
641da177e4SLinus Torvalds buff[6] |= AX25_SSSID_SPARE;
651da177e4SLinus Torvalds buff += AX25_ADDR_LEN;
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds if (saddr != NULL)
681da177e4SLinus Torvalds memcpy(buff, saddr, dev->addr_len);
691da177e4SLinus Torvalds else
701da177e4SLinus Torvalds memcpy(buff, dev->dev_addr, dev->addr_len);
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds buff[6] &= ~AX25_CBIT;
731da177e4SLinus Torvalds buff[6] |= AX25_EBIT;
741da177e4SLinus Torvalds buff[6] |= AX25_SSSID_SPARE;
751da177e4SLinus Torvalds buff += AX25_ADDR_LEN;
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds *buff++ = AX25_UI; /* UI */
781da177e4SLinus Torvalds
791da177e4SLinus Torvalds /* Append a suitable AX.25 PID */
801da177e4SLinus Torvalds switch (type) {
811da177e4SLinus Torvalds case ETH_P_IP:
821da177e4SLinus Torvalds *buff++ = AX25_P_IP;
831da177e4SLinus Torvalds break;
841da177e4SLinus Torvalds case ETH_P_ARP:
851da177e4SLinus Torvalds *buff++ = AX25_P_ARP;
861da177e4SLinus Torvalds break;
871da177e4SLinus Torvalds default:
886f74998eSRalf Baechle printk(KERN_ERR "AX.25: ax25_hard_header - wrong protocol type 0x%2.2x\n", type);
891da177e4SLinus Torvalds *buff++ = 0;
901da177e4SLinus Torvalds break;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds
931da177e4SLinus Torvalds if (daddr != NULL)
941da177e4SLinus Torvalds return AX25_HEADER_LEN;
951da177e4SLinus Torvalds
961da177e4SLinus Torvalds return -AX25_HEADER_LEN; /* Unfinished header */
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds
ax25_ip_xmit(struct sk_buff * skb)991d5da757SEric W. Biederman netdev_tx_t ax25_ip_xmit(struct sk_buff *skb)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds struct sk_buff *ourskb;
1021da177e4SLinus Torvalds unsigned char *bp = skb->data;
103006f68b8SRalf Baechle DL5RB ax25_route *route;
104006f68b8SRalf Baechle DL5RB struct net_device *dev = NULL;
1051da177e4SLinus Torvalds ax25_address *src, *dst;
106006f68b8SRalf Baechle DL5RB ax25_digi *digipeat = NULL;
1071da177e4SLinus Torvalds ax25_dev *ax25_dev;
1081da177e4SLinus Torvalds ax25_cb *ax25;
109006f68b8SRalf Baechle DL5RB char ip_mode = ' ';
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds dst = (ax25_address *)(bp + 1);
1121da177e4SLinus Torvalds src = (ax25_address *)(bp + 8);
1131da177e4SLinus Torvalds
11463530abaSEric Dumazet ax25_route_lock_use();
115006f68b8SRalf Baechle DL5RB route = ax25_get_route(dst, NULL);
116006f68b8SRalf Baechle DL5RB if (route) {
117006f68b8SRalf Baechle DL5RB digipeat = route->digipeat;
1181da177e4SLinus Torvalds dev = route->dev;
119006f68b8SRalf Baechle DL5RB ip_mode = route->ip_mode;
1203ff50b79SStephen Hemminger }
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds if (dev == NULL)
1231da177e4SLinus Torvalds dev = skb->dev;
1241da177e4SLinus Torvalds
125*7705d8a7SEric Dumazet rcu_read_lock();
1261da177e4SLinus Torvalds if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
127e18dbd05SEric W. Biederman kfree_skb(skb);
1281da177e4SLinus Torvalds goto put;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds if (bp[16] == AX25_P_IP) {
132006f68b8SRalf Baechle DL5RB if (ip_mode == 'V' || (ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
1331da177e4SLinus Torvalds /*
1341da177e4SLinus Torvalds * We copy the buffer and release the original thereby
1351da177e4SLinus Torvalds * keeping it straight
1361da177e4SLinus Torvalds *
1371da177e4SLinus Torvalds * Note: we report 1 back so the caller will
1381da177e4SLinus Torvalds * not feed the frame direct to the physical device
1391da177e4SLinus Torvalds * We don't want that to happen. (It won't be upset
1401da177e4SLinus Torvalds * as we have pulled the frame from the queue by
1411da177e4SLinus Torvalds * freeing it).
1421da177e4SLinus Torvalds *
1431da177e4SLinus Torvalds * NB: TCP modifies buffers that are still
1441da177e4SLinus Torvalds * on a device queue, thus we use skb_copy()
1451da177e4SLinus Torvalds * instead of using skb_clone() unless this
1461da177e4SLinus Torvalds * gets fixed.
1471da177e4SLinus Torvalds */
1481da177e4SLinus Torvalds
1491da177e4SLinus Torvalds ax25_address src_c;
1501da177e4SLinus Torvalds ax25_address dst_c;
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
1531da177e4SLinus Torvalds kfree_skb(skb);
1541da177e4SLinus Torvalds goto put;
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds
1571da177e4SLinus Torvalds if (skb->sk != NULL)
1581da177e4SLinus Torvalds skb_set_owner_w(ourskb, skb->sk);
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds kfree_skb(skb);
1611da177e4SLinus Torvalds /* dl9sau: bugfix
1621da177e4SLinus Torvalds * after kfree_skb(), dst and src which were pointer
1631da177e4SLinus Torvalds * to bp which is part of skb->data would not be valid
1641da177e4SLinus Torvalds * anymore hope that after skb_pull(ourskb, ..) our
1651da177e4SLinus Torvalds * dsc_c and src_c will not become invalid
1661da177e4SLinus Torvalds */
1671da177e4SLinus Torvalds bp = ourskb->data;
1681da177e4SLinus Torvalds dst_c = *(ax25_address *)(bp + 1);
1691da177e4SLinus Torvalds src_c = *(ax25_address *)(bp + 8);
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
172c1d2bbe1SArnaldo Carvalho de Melo skb_reset_network_header(ourskb);
1731da177e4SLinus Torvalds
1741da177e4SLinus Torvalds ax25=ax25_send_frame(
1751da177e4SLinus Torvalds ourskb,
1761da177e4SLinus Torvalds ax25_dev->values[AX25_VALUES_PACLEN],
1771da177e4SLinus Torvalds &src_c,
178006f68b8SRalf Baechle DL5RB &dst_c, digipeat, dev);
1791da177e4SLinus Torvalds if (ax25) {
1801da177e4SLinus Torvalds ax25_cb_put(ax25);
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds goto put;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds
1861da177e4SLinus Torvalds bp[7] &= ~AX25_CBIT;
1871da177e4SLinus Torvalds bp[7] &= ~AX25_EBIT;
1881da177e4SLinus Torvalds bp[7] |= AX25_SSSID_SPARE;
1891da177e4SLinus Torvalds
1901da177e4SLinus Torvalds bp[14] &= ~AX25_CBIT;
1911da177e4SLinus Torvalds bp[14] |= AX25_EBIT;
1921da177e4SLinus Torvalds bp[14] |= AX25_SSSID_SPARE;
1931da177e4SLinus Torvalds
1941da177e4SLinus Torvalds skb_pull(skb, AX25_KISS_HEADER_LEN);
1951da177e4SLinus Torvalds
196006f68b8SRalf Baechle DL5RB if (digipeat != NULL) {
19753744a4aSVasily Averin if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL)
1981da177e4SLinus Torvalds goto put;
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds skb = ourskb;
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds
20329c4be51SArnaldo Carvalho de Melo ax25_queue_xmit(skb, dev);
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds put:
206*7705d8a7SEric Dumazet rcu_read_unlock();
20763530abaSEric Dumazet ax25_route_lock_unuse();
2081d5da757SEric W. Biederman return NETDEV_TX_OK;
2093b6a94beSEric W. Biederman }
2103b6a94beSEric W. Biederman
2111da177e4SLinus Torvalds #else /* INET */
2121da177e4SLinus Torvalds
ax25_hard_header(struct sk_buff * skb,struct net_device * dev,unsigned short type,const void * daddr,const void * saddr,unsigned int len)21346d4e47aSEric W. Biederman static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev,
2143b04dddeSStephen Hemminger unsigned short type, const void *daddr,
21595c96174SEric Dumazet const void *saddr, unsigned int len)
2161da177e4SLinus Torvalds {
2171da177e4SLinus Torvalds return -AX25_HEADER_LEN;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
ax25_ip_xmit(struct sk_buff * skb)220787fb2bdSkbuild test robot netdev_tx_t ax25_ip_xmit(struct sk_buff *skb)
2213b6a94beSEric W. Biederman {
2221d5da757SEric W. Biederman kfree_skb(skb);
2231d5da757SEric W. Biederman return NETDEV_TX_OK;
2243b6a94beSEric W. Biederman }
2251da177e4SLinus Torvalds #endif
2261da177e4SLinus Torvalds
ax25_validate_header(const char * header,unsigned int len)227ea47781cSWillem de Bruijn static bool ax25_validate_header(const char *header, unsigned int len)
228ea47781cSWillem de Bruijn {
229ea47781cSWillem de Bruijn ax25_digi digi;
230ea47781cSWillem de Bruijn
231ea47781cSWillem de Bruijn if (!len)
232ea47781cSWillem de Bruijn return false;
233ea47781cSWillem de Bruijn
234ea47781cSWillem de Bruijn if (header[0])
235ea47781cSWillem de Bruijn return true;
236ea47781cSWillem de Bruijn
237ea47781cSWillem de Bruijn return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL,
238ea47781cSWillem de Bruijn NULL);
239ea47781cSWillem de Bruijn }
240ea47781cSWillem de Bruijn
2413b04dddeSStephen Hemminger const struct header_ops ax25_header_ops = {
2423b04dddeSStephen Hemminger .create = ax25_hard_header,
243ea47781cSWillem de Bruijn .validate = ax25_validate_header,
2443b04dddeSStephen Hemminger };
2453b04dddeSStephen Hemminger
2463b04dddeSStephen Hemminger EXPORT_SYMBOL(ax25_header_ops);
2471d5da757SEric W. Biederman EXPORT_SYMBOL(ax25_ip_xmit);
248