2 votes

obtenir l'inode du processus à l'aide de netlink

Je veux essayer de corréler un paquet IP (en utilisant libpcap) à un processus. J'ai eu quelques succès limités en utilisant les fichiers /proc/net/ mais j'ai découvert que sur certaines des machines que j'utilise, ce fichier peut contenir plusieurs milliers de lignes et qu'il n'est pas efficace de l'analyser (la mise en cache a atténué certains problèmes de performance).

J'ai lu que l'utilisation du sous-système netlink sock_diag pouvait aider en interrogeant directement le noyau sur la socket qui m'intéresse. J'ai eu un succès limité avec mes tentatives mais je me suis heurté à un blocage mental sur ce qui ne va pas.

Pour la demande initiale, j'ai :

if (query_fd_) {
    struct {
      nlmsghdr nlh;
      inet_diag_req_v2 id_req;
    } req = {
      .nlh = {
         .nlmsg_len = sizeof(req),
         .nlmsg_type = SOCK_DIAG_BY_FAMILY,
         .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP
      },
      .id_req = {
         .sdiag_family = packet.l3_protocol,
         .sdiag_protocol = packet.l4_protocol,
         .idiag_ext = 0,
         .pad = 0,
         .idiag_states = -1,
         .id = {
           .idiag_sport = packet.src_port,
           .idiag_dport = packet.dst_port
         }
      }
    };
    //packet ips are just binary data stored as strings!
    memcpy(req.id_req.id.idiag_src, packet.src_ip.c_str(), 4);
    memcpy(req.id_req.id.idiag_dst, packet.dst_ip.c_str(), 4);
    struct sockaddr_nl nladdr = {
      .nl_family = AF_NETLINK
    };
    struct iovec iov = {
      .iov_base = &req,
      .iov_len = sizeof(req)
    };
    struct msghdr msg = {
      .msg_name = (void *) &nladdr,
      .msg_namelen = sizeof(nladdr),
      .msg_iov = &iov,
      .msg_iovlen = 1
    };

    // Send message to kernel
    for (;;) {
      if (sendmsg(query_fd_, &msg, 0) < 0) {
        if (errno == EINTR)
          continue;

          perror("sendmsg");
          return false;
      }
      return true;
    }
  }
  return false;

Pour le code de réception, j'ai :

long buffer[8192];
  struct sockaddr_nl nladdr = {
    .nl_family = AF_NETLINK
  };
  struct iovec iov = {
    .iov_base = buffer,
    .iov_len = sizeof(buffer)
  };
  struct msghdr msg = {
    .msg_name = (void *) &nladdr,
    .msg_namelen = sizeof(nladdr),
    .msg_iov = &iov,
    .msg_iovlen = 1
  };
  int flags = 0;

  for (;;) {
    ssize_t rv = recvmsg(query_fd_, &msg, flags);

    // error handling
    if (rv < 0) {
      if (errno == EINTR)
        continue;
      if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
        break;
      perror("Failed to recv from netlink socket");
      return 0;
    }
    if (rv == 0) {
      printf("Unexpected shutdown of NETLINK socket");
      return 0;
    }

    for (const struct nlmsghdr* header = reinterpret_cast<const struct nlmsghdr*>(buffer);
          rv >= 0 && NLMSG_OK(header, static_cast<uint32_t>(rv));
          header = NLMSG_NEXT(header, rv)) {

      // The end of multipart message
      if (header->nlmsg_type == NLMSG_DONE)
        return 0;

      if (header->nlmsg_type == NLMSG_ERROR) {
        const struct nlmsgerr *err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(header));
        if (err == NULL)
          return 100;
        errno = -err->error;
        perror("NLMSG_ERROR");
        return 0;
      }

      if (header->nlmsg_type != SOCK_DIAG_BY_FAMILY) {
        printf("unexpected nlmsg_type %u\n", (unsigned)header->nlmsg_type);
        continue;
      }

      // Get the details....
      const struct inet_diag_msg* diag = reinterpret_cast<inet_diag_msg*>(NLMSG_DATA(header));
      if (header->nlmsg_len < NLMSG_LENGTH(sizeof(*diag))) {
        printf("Message too short %d vs %d\n", header->nlmsg_len, NLMSG_LENGTH(sizeof(*diag)));
        return 0;
      }

      if (diag->idiag_family != PF_INET) {
         printf("unexpected family %u\n", diag->idiag_family);
         return 1;
      }

      return diag->idiag_inode;

Le problème :

La valeur de diag->udiag_inode ne correspond pas à celle que je vois dans la sortie netstat ou dans les fichiers /proc/net/. Est-elle censée correspondre ? Si ce n'est pas le cas, est-il possible d'utiliser cette approche pour récupérer le numéro d'inode du processus afin que je puisse ensuite interroger /proc pour obtenir le PID correspondant ?

Une autre chose que je n'ai pas bien comprise est le NLMSG_DONE lors de la vérification du nlmsg_type dans l'en-tête. Ce que je vois :

1 - TCP 10.0.9.15:51002 -> 192.168.64.11:3128 [15047]
2 - TCP 192.168.64.11:3128 -> 10.0.9.15:51002 [0]
3 - TCP 10.0.9.15:51002 -> 192.168.64.11:3128 [0]
4 - TCP 192.168.64.11:3128 -> 10.0.9.15:51002 [15047]
5 - TCP 10.0.9.15:51002 -> 192.168.64.11:3128 [0]
6 - TCP 192.168.64.11:3128 -> 10.0.9.15:51002 [0]
7 - TCP 10.0.9.15:51002 -> 192.168.64.11:3128 [15047]

J'obtiens donc un numéro d'inode lors de la première requête, puis des retours NLMSG_DONE (la lecture du code a confirmé qu'il s'agissait du chemin). Pourquoi n'ai-je pas le même résultat pour les lignes 1 et 3 ?

J'apprécie toute aide ou conseil.

1voto

john Points 330

J'ai trouvé la réponse et je la poste au cas où quelqu'un la trouverait par hasard :

  1. J'avais un uint16_t comme type de retour du code recv alors qu'en fait il aurait dû être ino_t ou uint32_t. J'ai découvert cela lorsque j'ai remarqué que quelques inodes correspondaient correctement après un nouveau redémarrage, puis cessaient de correspondre après un certain temps sans qu'aucun code n'ait été modifié (le nombre d'inodes s'incrémentant évidemment). L'utilisation du type correct dans le retour de la fonction a résolu le problème (le code que j'ai posté est donc correct !).

  2. Je recevais des messages en plusieurs parties. J'aurais dû boucler pendant que NLM_F_MULTI était défini dans les drapeaux, puis quitter la boucle lorsque j'ai reçu NLMSG_DONE.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X