Question

I'm trying to create a TUN network device with AX.25 encapsulation. What works is: - creating the device - setting its capsulation to ax25

What doesn't work is setting its hardware address. This is crucial in ax.25 communication as it is used to uniquely address a node.

First I create the TUN device:

    struct ifreq ifr = { 0 };

    const char *clone_dev = "/dev/net/tun";
    if ((fd = open(clone_dev, O_RDWR)) == -1)
            error_exit(true, "Failed opening %s for tun device %s", clone_dev, dev_name);

    ifr.ifr_flags = IFF_TUN;

    strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);

    if (ioctl(fd, TUNSETIFF, (void *)&ifr) == -1)
            error_exit(true, "Failed creating tun device %s", dev_name);

this results in:

root@travelmate:/home/folkert# ifconfig bla
bla       Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          POINTOPOINT NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

then I set the interface to AX.25 encapsulation:

    if (ioctl(fd, TUNSETLINK, ARPHRD_AX25) == -1)
            error_exit(true, "Failed setting tun device %s to ARPHRD_AX25", dev_name);

this results in:

root@travelmate:/home/folkert# ifconfig bla
bla       Link encap:AMPR AX.25  HWaddr   
          POINTOPOINT NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

And then it is a matter of setting the hardware address. The hardware address is a string like: FH1GOU-1 First you need to shift each byte of this address one bit to the right. Then, ioctl-magic. This always fails. I've also tried just creating the tun-device and then invoking ifconfig on it which results in a "not supported" error. It could of course be that it is simply not possible to set an ax.25 hardware address on a tun device, but why is it then possible to set ax.25 encapsulation?

    struct sockaddr *sa = &ifr.ifr_ifru.ifru_addr;

    struct sockaddr_ax25 *sap25 = (struct sockaddr_ax25 *)sa;
    memset(sap25, 0x00, sizeof(struct sockaddr_ax25));

    // tried AF_AX25 as well
    sa->sa_family = ARPHRD_AX25;

    char *min = strchr(hwaddr, '-');
    *min = 0x00;

    unsigned int hwaddr_len = strlen(hwaddr);

    char *call_str = sap25->sax25_call.ax25_call;

    unsigned int main_addr_size = sizeof(ax25_address) - 1;
    for(unsigned int idx=0; idx<main_addr_size; idx++)
    {
            int c = idx < hwaddr_len ? toupper(hwaddr[idx]) : ' ';

            call_str[idx] = (c << 1) & 0xfe;
    }

    if (min)
            call_str[main_addr_size] = (atoi(min + 1) << 1) & 0xfe;
    else
            call_str[main_addr_size] = 0x00;

    free(hwaddr);

    printf("%d\n", ioctl(fd, SIOCSIFHWADDR, &ifr));

The last line, the one with the ioctl, always prints -1 (=error).

I've also tried setting the hw-address directly with the TUNSETIFF ioctl but that seems to be ignored.

Any ideas?

Was it helpful?

Solution 2

I've heard since then that tun devices simply do not support ax25.

OTHER TIPS

I think the problem is with "ifr.ifr_flags = IFF_TUN" If you are planning to create TAP device use ifr.ifr_flags = IFF_TAP | IFF_NO_PI;

Not sure if this will help you.

btw something else i tried: simply opening an ethernet encapsulated tap device and bringing it up, forcing bpq to consider it an ethernet port giving it a bpq ax.25 'secondary interface' does NOT work. the packets on the bpq alias interface never make it to the file descriptor of the tap device.

but just changing the encapsulation on a tap device seems to work fine.

ax.25 handling for 'larger setups' in linux is broken anyway. processes can't connect to other processes within the same machine on different callsigns, half the documentation is missing or went hudini when people with tripod free sites died, etc. so indeed it's best to just 'work around it in userspace' for now. we'll get to fixing up the kernel stack later :P the whole thing seems to have been made by people with just 1 tranceiver and 1 terminal program on the computer on the same desk :P

It would seem like it is impossible to get this to work at this time. Eventhough tun/tap pertains to be 'universal' there seem to be 2 bugs in it getting in the way: (for TAP. with TUN it's completely impossible to set the hardware address as there is none).

1: in the hardware address structure there seems to be something randomly overwriting the ssid (ethernet macs are 6 bytes, AX.25 addresses are 7 bytes (6 callsign,1 ssid + flags) despite seemingly working fine before it now constantly sets the SSID to 8 ;)

2: even ignoring the first bug, read() never returns any data after setting the encapsulation to AMPR AX.25 and sending some data using axcall or linpac (due to the first bug axcall does 'randomly' complain about not being able to link the port to some /etc/ax25/axports entry as well).

/etc/ax25/axports being hopelessly obsolete as far as we're concerned but other programs still use it, rather than scanning all interfaces, probably due to problems getting interface lists of interfaces without ipv4 in the past, and expect it to match a callsign+ssid, which it randomly won't find...

the code below -should- work according to tun/tap's claims of being 'universal' and the fact that it does set the callsign correctly on BPQ and KISS interfaces, just not the last byte on TAP interfaces.

   #include<sys/types.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #include<sys/ioctl.h>
   #include<sys/socket.h>
   #include<linux/if_tun.h>
   #include<net/if_arp.h>
   #include<net/if.h>
   #include<string.h>
   #include<unistd.h>

   #define AXALEN 7

   int ax25tapcreate(char *tempdev,void *tempcall,int tempflags){
   struct ifreq tempifr;
   int tempfile;
   int tempsock;
   char *tempclonedev="/dev/net/tun";
   if((tempfile=open(tempclonedev,O_RDWR|O_SYNC))<0)return(-1);
   bzero(&tempifr,sizeof(tempifr));
   tempifr.ifr_flags=tempflags;
   if(*tempdev)strncpy(tempifr.ifr_name,tempdev,IFNAMSIZ-1);
   if(ioctl(tempfile,TUNSETIFF,&tempifr)<0){close(tempfile);return(-1);};
   if(ioctl(tempfile,TUNSETLINK,ARPHRD_AX25)==-1){close(tempfile);return(-1);};
   if(ioctl(tempfile,TUNSETPERSIST,0)<0){close(tempfile);return(-1);};

   tempsock=socket(PF_PACKET,SOCK_DGRAM,0);

   bzero(&tempifr,sizeof(tempifr));
   strncpy(tempifr.ifr_name,tempdev,IFNAMSIZ-1);
   bcopy(tempcall,&tempifr.ifr_hwaddr.sa_data,AXALEN);
   tempifr.ifr_hwaddr.sa_family=ARPHRD_AX25;
   if(ioctl(tempsock,SIOCSIFHWADDR,&tempifr)!=0){close(tempsock)close(tempfile);return(-1);};

   bzero(&tempifr,sizeof(tempifr));
   strncpy(tempifr.ifr_name,tempdev,IFNAMSIZ-1);
   tempifr.ifr_mtu=256;
   if(ioctl(tempsock,SIOCSIFMTU,&tempifr<0){close(tempsock);close(tempfile);return(-1);};

   bzero(&tempifr,sizeof(tempifr));
   strncpy(tempifr.ifr_name,tempdev,IFNAMSIZ-1);
   tempifr.ifr_flags=IFF_UP|IFF_RUNNING;
   if(ioctl(tempsock,SIOCSIFFLAGS,&tempifr<0){close(tempsock);close(tempfile);return(-1);};
   close(tempsock);

   return(tempfile);
   };

   //###EXAMPLECODE

   #include<stdio.h>
   #include<stdlib.h>
   #include<stdint.h>
   int main(){
   int fd;
   int n;
   int size;
   uint8_t call[AXALEN];
   call[0]=('C'<<1)&0xFE;
   call[1]=('B'<<1)&0xFE;
   call[2]=('3'<<1)&0xFE;
   call[3]=('R'<<1)&0xFE;
   call[4]=('O'<<1)&0xFE;
   call[5]=('B'<<1)&0xFE;
   call[6]=(4<<1)&0xFE;
   fd=ax25tapcreate("cb3rob0",&call,IFF_TAP);
   if(fd>0){printf("SUCCEEDED: %d\n",fd);system("ifconfig cb3rob0");};
   if(fd<0)exit(1);
   unsigned char buffer[10];
   while(1){
   size=read(fd,&buffer,sizeof(buffer));
   if(size>1)for(n=0;n<size;n++)printf("%02X ",buffer[n]);printf("\n---\n");
   if(size<0)exit(1);
   };//while1
   };

but... it won't work anyway...for now...

    root@user-TW100-E5:/home/user# ./tapcreate 
    SUCCEEDED: 3
    cb3rob0   Link encap:AMPR AX.25  HWaddr CB3ROB-8  
              UP BROADCAST RUNNING  MTU:256  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

note the unaskedfor -8 SSID in the callsign. clearly TAP was not intended for other framing than ethernet.

also note actual, but less severe bug #3 in TUN/TAP... the IFF_BROADCAST flag is on as visible in ifconfig... with no way in hell to turn it off... on an AX.25 interface of all things ;) (now why would an AX.25 interface even have the possibility to turn something as ipv4-ish as 'broadcast' on ;)

Just did it with a tap device (just strip the eventual hardware layer of the packets if you don't want it) and it works fine. (edit: make that it -randomly- worked fine, just the setting the callsign bit, and then even that stopped working due to some bugs described in the last answer ;)

root@user-X551MA:/home/user# ifconfig cb3rob0
cb3rob0   Link encap:AMPR AX.25  HWaddr CB3ROB 

Regardless of it working for tun rather than tap

DO NOTE that the TAP device itself is opened as a FILE HANDLE. (open()) in order to set the hardware address (=callsign) you need to create a temporary NETWORK SOCKET HANDLE (socket())

you also need a socket handle to bring up the interface (IFF_UP | IFF_RUNNING) disable broadcast and arp and set the mtu to something a bit more 'AX.25ish' such as 256 rather than the 1500 default.

open a socket with any supposedly supported protocol family (PF_PACKET works fine, at least for root and do the ioctls on that with the ifr name field set to the name of the already created tap (tun?) device

After that just close the socket again. It's not needed during operations on a tap device other than for configuring it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top