1

I am trying to interface with the Linux tun driver in Java as it is explained here.

How to interface with the Linux tun driver

But since you can not call ioctl() with java, I am using the Java Native Interface. It is working fine as long as I don't read and write in the same file.

If I do so I get this exception, which I would translate by "The FileDescriptor is in a broken state" :

java.io.IOException: Le descripteur du fichier est dans un mauvais état
    at java.io.FileOutputStream.writeBytes(Native Method)
    at java.io.FileOutputStream.write(FileOutputStream.java:326)
    at WriterThread.main(WriterThread.java:54)

Here is the java code :

public static void main(String[] arg){
        File tunFile = new File("/dev/net/tun");
        FileOutputStream outStream;
        FileInputStream inStream;

        try {

            inStream = new FileInputStream(tunFile);
            outStream = new FileOutputStream(tunFile);
            FileDescriptor fd = inStream.getFD();

            //getting the file descriptor

            Field f = fd.getClass().getDeclaredField("fd");
            f.setAccessible(true);
            int descriptor = f.getInt(fd);


            //use of Java Native Interface
            new TestOuvertureFichier().ioctl(descriptor);

            while(true){
                System.out.println("reading");
                byte[] bytes = new byte[500];
                int l = 0;
                l = inStream.read(bytes);

                //the problem seems to come from here
                outStream.write(bytes,0,l);

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

Here is the C code :

JNIEXPORT void JNICALL Java_TestOuvertureFichier_ioctl(JNIEnv *env,jobject obj, jint descriptor){
      struct ifreq ifr;
      memset(&ifr, 0, sizeof(ifr));
      ifr.ifr_flags = IFF_TUN;
      strncpy(ifr.ifr_name, "tun0", IFNAMSIZ);
      int err;

      if ( (err = ioctl(descriptor, TUNSETIFF, (void *) &ifr)) == -1 ) {
          perror("ioctl TUNSETIFF");exit(1);
      }
      return;
}
Raf
  • 9
  • 4
  • `new FileOutputStream(...)` will surely try to create a new file. Try using *one* `RandomAccessFile` instead of the two file streams. – user207421 Jun 01 '17 at 12:16

3 Answers3

1

G. Fiedler is right, the read should should be at least as big as the interface MTU, and the write should not exceed the MTU. In addition to that, I would check that:

  • Before you attempt to read or write, the interface is up (ip addr add x.x.x.x/xx dev tun0, ip link set tun0 up)
  • You open the tun device only once, using for instance a RandomAccessFile. Here, I'm not sure that the inStream and the outStream have the same file descriptor.
JayTe
  • 56
  • 4
0

Note that bytes should be at least the MTU size of the interface, eg 1500 bytes. The read() on the tun fd reads exactly an entire packet every time it's called.

Before writing to the tun device you should manipulate the IP header, especially the source and destination address of the received packet.

G. Fiedler
  • 664
  • 4
  • 12
0

The file descriptor is not created by the new File() call but when creating the FileInputStream and FileOutputStream objects. Which means that your code opens the /dev/net/tun file twice (creating two different file descriptors).

inStream = new FileInputStream(tunFile);
outStream = new FileOutputStream(tunFile);

Therefore, the ioctl is only applied to inStream, and not to outStream. Try creating the FileOutputStream while using the same file descriptor as the FileInputStream.

outStream = new FileOutputStream(inStream.getFD());    

Edit: It is likely that FileInputStream will open a read-only FD. As JayTE proposed, it is probably better to first create a RandomAccessFile, and use the FD from it to create the two streams.

Pierre P.
  • 16
  • 1