ioctl() с JNI: сломанный файловый дескриптор

Я пытаюсь взаимодействовать с драйвером Linux tun на Java, как это объясняется здесь.

Как взаимодействовать с драйвером tun для Linux

Но поскольку вы не можете вызывать ioctl() с java, я использую собственный интерфейс Java. Он работает нормально, пока я не читаю и не пишу в один и тот же файл.

Если я это сделаю, я получу это исключение, которое я бы перевел как «FileDescriptor находится в сломанном состоянии»:

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)

Вот код Java:

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();
        }

    }

Вот код C:

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;
}

person Raf    schedule 29.05.2017    source источник
comment
new FileOutputStream(...) обязательно попытается создать новый файл. Попробуйте использовать один RandomAccessFile вместо двух файловых потоков.   -  person user207421    schedule 01.06.2017


Ответы (3)


Дескриптор файла создается не вызовом new File(), а при создании объектов FileInputStream и FileOutputStream. Это означает, что ваш код открывает файл /dev/net/tun дважды (создавая два разных файловых дескриптора).

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

Поэтому ioctl применяется только к inStream, а не к outStream. Попробуйте создать FileOutputStream, используя тот же файловый дескриптор, что и FileInputStream.

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

Изменить: вполне вероятно, что FileInputStream откроет FD только для чтения. Как предложил JayTE, вероятно, лучше сначала создать RandomAccessFile и использовать FD из него для создания двух потоков.

person Pierre P.    schedule 01.06.2017

Г. Фидлер прав, чтение должно быть не меньше MTU интерфейса, а запись не должна превышать MTU. В дополнение к этому я бы проверил, что:

  • Перед попыткой чтения или записи интерфейс активен (ip addr add x.x.x.x/xx dev tun0, ip link set tun0 up)
  • Вы открываете устройство tun только один раз, используя, например, Файл произвольного доступа. Здесь я не уверен, что inStream и outStream имеют один и тот же файловый дескриптор.
person JayTe    schedule 01.06.2017

Обратите внимание, что bytes должен быть не меньше размера MTU интерфейса, например 1500 байт. read() на tun fd считывает ровно весь пакет при каждом вызове.

Перед записью на устройство tun вы должны манипулировать заголовком IP, особенно адресом источника и получателя полученного пакета.

person G. Fiedler    schedule 29.05.2017