]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: decode protocol numbers of RAW and RAW6 sockets
authorMasatake YAMATO <yamato@redhat.com>
Fri, 3 Jan 2025 18:50:01 +0000 (03:50 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Wed, 20 Aug 2025 12:11:21 +0000 (21:11 +0900)
NOTE: this chage breaks compatibility of existing -Q expression used
in a shell script; the data type of RAW.PROTOCOL has changed from
<number> to <string>.

With this change, lsfd decodes RAW.PROTOCOL with a built-in table
based on linux/in.h and getprotobynumber(3) that may refer to
/etc/protocols internally.

In addition, this change adds RAW.PROTOCOL.RAW column that holds
the original protocol numbers.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
lsfd-cmd/lsfd.1.adoc
lsfd-cmd/lsfd.c
lsfd-cmd/lsfd.h
lsfd-cmd/sock-xinfo.c
tests/expected/lsfd/mkfds-raw
tests/expected/lsfd/mkfds-raw6
tests/ts/lsfd/mkfds-raw
tests/ts/lsfd/mkfds-raw6

index 39a1312c4f69609354e6a36b03f5547c2056297d..7f8b12099be12de72a6329a792f85121131e85b4 100644 (file)
@@ -436,8 +436,13 @@ ICMP echo request id used on the PING socket.
 POS <``number``>::
 File position.
 
-RAW.PROTOCOL <``number``>::
-Protocol number of the raw socket.
+RAW.PROTOCOL <``string``>::
+Protocol name of the raw socket (decoded).
++
+NOTE: The data type of this column was <``number``> from v2.39 to v2.41.
+
+RAW.PROTOCOL.RAW <``number``>::
+Protocol number of the raw socket (raw).
 
 RDEV <``string``>::
 Device ID (if special file).
index 4d05c3b27a48bdf77eaae8869b2b5fd5757392c3..bca615abd6af6ead64b64fa2461ebdbc8a4d3007 100644 (file)
@@ -341,8 +341,11 @@ static const struct colinfo infos[] = {
                                   0,   SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
                                   N_("tty index of the counterpart") },
        [COL_RAW_PROTOCOL]     = { "RAW.PROTOCOL",
+                                  0,   SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
+                                  N_("protocol name of the raw socket (decoded)") },
+       [COL_RAW_PROTOCOL_RAW] = { "RAW.PROTOCOL.RAW",
                                   0,   SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
-                                  N_("protocol number of the raw socket") },
+                                  N_("protocol number of the raw socket (raw)") },
        [COL_RDEV]             = { "RDEV",
                                   0,   SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
                                   N_("device ID (if special file)") },
index 814605a1bf173d4610eec2a6e771967094045b40..4aa7bf2bb55bb180576bd5883af46ae69b996770 100644 (file)
@@ -111,6 +111,7 @@ enum {
        COL_POS,
        COL_PTMX_TTY_INDEX,
        COL_RAW_PROTOCOL,
+       COL_RAW_PROTOCOL_RAW,
        COL_RDEV,
        COL_SIGNALFD_MASK,
        COL_SIZE,
index 438756274c16871eab8a72aa5a92d17222b6f115..6803300cb88a0c5eef752b45ada3acb81ef69d77 100644 (file)
@@ -23,6 +23,7 @@
 #include <fcntl.h>             /* open(2) */
 #include <ifaddrs.h>           /* getifaddrs */
 #include <inttypes.h>          /* SCNu16 */
+#include <netdb.h>             /* getprotobyname */
 #include <net/if.h>            /* if_nametoindex */
 #include <linux/if_ether.h>    /* ETH_P_* */
 #include <linux/net.h>         /* SS_* */
@@ -1436,9 +1437,102 @@ struct raw_xinfo {
        uint16_t protocol;
 };
 
+static const char *raw_decode_protocol(uint16_t proto, bool decoded_as_ipv6)
+{
+       struct protoent *pent;
+
+       if (proto == 0) {
+               if (decoded_as_ipv6)
+                       return "hopopts"; /* IPPROTO_HOPOPTS */
+               else
+                       return "ip"; /* IPPROTO_IP */
+       }
+
+       switch (proto) {
+       case IPPROTO_ICMP:
+               return "icmp";
+       case IPPROTO_IGMP:
+               return "igmp";
+       case IPPROTO_IPIP:
+               return "ipip";
+       case IPPROTO_TCP:
+               return "tcp";
+       case IPPROTO_EGP:
+               return "egp";
+       case IPPROTO_PUP:
+               return "pup";
+       case IPPROTO_UDP:
+               return "udp";
+       case IPPROTO_IDP:
+               return "idp";
+       case IPPROTO_TP:
+               return "tp";
+       case IPPROTO_DCCP:
+               return "dccp";
+       case IPPROTO_IPV6:
+               return "ipv6";
+       case IPPROTO_RSVP:
+               return "rsvp";
+       case IPPROTO_GRE:
+               return "gre";
+       case IPPROTO_ESP:
+               return "esp";
+       case IPPROTO_AH:
+               return "ah";
+       case IPPROTO_MTP:
+               return "mtp";
+       case IPPROTO_BEETPH:
+               return "beetph";
+       case IPPROTO_ENCAP:
+               return "encap";
+       case IPPROTO_PIM:
+               return "pim";
+       case IPPROTO_COMP:
+               return "comp";
+#ifdef IPPROTO_L2TP
+       case IPPROTO_L2TP:
+               return "l2tp";
+#endif
+       case IPPROTO_SCTP:
+               return "sctp";
+       case IPPROTO_UDPLITE:
+               return "udplite";
+       case IPPROTO_MPLS:
+               return "mpls";
+#ifdef IPPROTO_ETHERNET
+       case IPPROTO_ETHERNET:
+               return "ethernet";
+#endif
+       case IPPROTO_RAW:
+               return "raw";
+#ifdef IPPROTO_MPTCP
+       case IPPROTO_MPTCP:
+               return "mptcp";
+#endif
+       case IPPROTO_ROUTING:
+               return "routing";
+       case IPPROTO_FRAGMENT:
+               return "fragment";
+       case IPPROTO_ICMPV6:
+               return "icmpv6";
+       case IPPROTO_NONE:
+               return "none";
+       case IPPROTO_DSTOPTS:
+               return "dstopts";
+       case IPPROTO_MH:
+               return "mh";
+       }
+
+       pent = getprotobynumber(proto);
+       if (pent && pent->p_name)
+               return pent->p_name;
+
+       return NULL;
+}
+
 static char *raw_get_name_common(struct sock_xinfo *sock_xinfo,
                                 struct sock *sock  __attribute__((__unused__)),
-                                const char *port_label)
+                                const char *port_label, bool decode_as_protocol)
 {
        char *str = NULL;
        struct l4_xinfo_class *class = (struct l4_xinfo_class *)sock_xinfo->class;
@@ -1452,24 +1546,44 @@ static char *raw_get_name_common(struct sock_xinfo *sock_xinfo,
 
        if (!inet_ntop(class->family, laddr, local_s, sizeof(local_s)))
                xasprintf(&str, "state=%s", st_str);
-       else if (class->is_any_addr(raddr)
-                || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s)))
-               xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s",
-                         st_str,
-                         port_label,
-                         raw->protocol, local_s);
-       else
-               xasprintf(&str, "state=%s %s=%"PRIu16" laddr=%s raddr=%s",
-                         st_str,
-                         port_label,
-                         raw->protocol, local_s, remote_s);
+       else {
+               const char *protocol_str = NULL;
+               char protocol_buf[ sizeof ("unknown(")
+                                  + sizeof (stringify_value(UINT16_MA))
+                                  + sizeof (")") ];
+
+               if (decode_as_protocol) {
+                       protocol_str = raw_decode_protocol(raw->protocol,
+                                                          class->family == AF_INET6);
+                       if (protocol_str == NULL) {
+                               snprintf(protocol_buf, sizeof(protocol_buf),
+                                        "unknown(%"PRIu16")", raw->protocol);
+                               protocol_str = protocol_buf;
+                       }
+               } else {
+                       snprintf(protocol_buf, sizeof(protocol_buf), "%"PRIu16, raw->protocol);
+                       protocol_str = protocol_buf;
+               }
+
+               if (class->is_any_addr(raddr)
+                   || !inet_ntop(class->family, raddr, remote_s, sizeof(remote_s)))
+                       xasprintf(&str, "state=%s %s=%s laddr=%s",
+                                 st_str,
+                                 port_label,
+                                 protocol_str, local_s);
+               else
+                       xasprintf(&str, "state=%s %s=%s laddr=%s raddr=%s",
+                                 st_str,
+                                 port_label,
+                                 protocol_str, local_s, remote_s);
+       }
        return str;
 }
 
 static char *raw_get_name(struct sock_xinfo *sock_xinfo,
                          struct sock *sock  __attribute__((__unused__)))
 {
-       return raw_get_name_common(sock_xinfo, sock, "protocol");
+       return raw_get_name_common(sock_xinfo, sock, "protocol", true);
 }
 
 static char *raw_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
@@ -1478,6 +1592,29 @@ static char *raw_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused_
        return xstrdup("raw");
 }
 
+static bool raw_fill_column_common(struct raw_xinfo *raw,
+                                  int column_id,
+                                  char **str,
+                                  bool decoded_as_ipv6)
+{
+       const char *pname;
+
+       switch (column_id) {
+       case COL_RAW_PROTOCOL:
+               pname = raw_decode_protocol(raw->protocol, decoded_as_ipv6);
+               if (pname) {
+                       *str = xstrdup(pname);
+                       return true;
+               }
+               FALLTHROUGH;
+       case COL_RAW_PROTOCOL_RAW:
+               xasprintf(str, "%"PRIu16, raw->protocol);
+               return true;
+       }
+
+       return false;
+}
+
 static bool raw_fill_column(struct proc *proc __attribute__((__unused__)),
                            struct sock_xinfo *sock_xinfo,
                            struct sock *sock __attribute__((__unused__)),
@@ -1489,13 +1626,8 @@ static bool raw_fill_column(struct proc *proc __attribute__((__unused__)),
        if (l3_fill_column_handler(INET, sock_xinfo, column_id, str))
                return true;
 
-       if (column_id == COL_RAW_PROTOCOL) {
-               xasprintf(str, "%"PRIu16,
-                         ((struct raw_xinfo *)sock_xinfo)->protocol);
-               return true;
-       }
-
-       return false;
+       return raw_fill_column_common((struct raw_xinfo *)sock_xinfo,
+                                     column_id, str, false);
 }
 
 static struct sock_xinfo *raw_xinfo_scan_line(const struct sock_xinfo_class *class,
@@ -1564,7 +1696,7 @@ static void load_xinfo_from_proc_raw(ino_t netns_inode, enum sysfs_byteorder byt
 static char *ping_get_name(struct sock_xinfo *sock_xinfo,
                          struct sock *sock  __attribute__((__unused__)))
 {
-       return raw_get_name_common(sock_xinfo, sock, "id");
+       return raw_get_name_common(sock_xinfo, sock, "id", false);
 }
 
 static char *ping_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
@@ -1845,18 +1977,11 @@ static bool raw6_fill_column(struct proc *proc  __attribute__((__unused__)),
                             size_t column_index  __attribute__((__unused__)),
                             char **str)
 {
-       struct raw_xinfo *raw;
-
        if (l3_fill_column_handler(INET6, sock_xinfo, column_id, str))
                return true;
 
-       raw = (struct raw_xinfo *)sock_xinfo;
-       if (column_id == COL_RAW_PROTOCOL) {
-               xasprintf(str, "%"PRIu16, raw->protocol);
-               return true;
-       }
-
-       return false;
+       return raw_fill_column_common((struct raw_xinfo *)sock_xinfo,
+                                     column_id, str, true);
 }
 
 static const struct l4_xinfo_class raw6_xinfo_class = {
index 37baddc774bc8d340f6cc3c71c0bbbbe50a64834..8e45b1e95c199d6d9c555a834ba80bda887e20c2 100644 (file)
@@ -1,2 +1,6 @@
-3 RAW state=established protocol=5 laddr=127.0.0.1 raddr=127.0.0.2 established raw 127.0.0.1 127.0.0.2 5
-ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL: 0
+3 RAW state=established protocol=st laddr=127.0.0.1 raddr=127.0.0.2 established raw 127.0.0.1 127.0.0.2 st 5
+ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW: 0
+3 RAW state=established protocol=tcp laddr=127.0.0.1 raddr=127.0.0.2 established raw 127.0.0.1 127.0.0.2 tcp 6
+ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW: 0
+3 RAW state=established protocol=unknown(61) laddr=127.0.0.1 raddr=127.0.0.2 established raw 127.0.0.1 127.0.0.2 61 61
+ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW: 0
index 2d77e8f3ba730f275ad275a3a198aa0b28723fdc..5ba44ae02d6b371755d4e93a7f317fd8aab126ee 100644 (file)
@@ -1,2 +1,6 @@
-3 RAWv6 state=established protocol=5 laddr=::1 raddr=::ffff:127.0.0.1 established raw ::1 ::ffff:127.0.0.1 5
-ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL: 0
+3 RAWv6 state=established protocol=st laddr=::1 raddr=::ffff:127.0.0.1 established raw ::1 ::ffff:127.0.0.1 st 5
+ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW: 0
+3 RAWv6 state=established protocol=tcp laddr=::1 raddr=::ffff:127.0.0.1 established raw ::1 ::ffff:127.0.0.1 tcp 6
+ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW: 0
+3 RAWv6 state=established protocol=unknown(61) laddr=::1 raddr=::ffff:127.0.0.1 established raw ::1 ::ffff:127.0.0.1 61 61
+ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW: 0
index 57703404408eaae7cc8c619f1fbade4cb70de889..0ff246f5e1da46a67dc55269f9bbf04595c66298 100755 (executable)
@@ -31,19 +31,30 @@ ts_cd "$TS_OUTDIR"
 PID=
 FD=3
 EXPR='(TYPE == "RAW") and (FD == 3)'
-PROTOCOL=5
 
+test_lsfd_raw()
 {
-    coproc MKFDS { "$TS_HELPER_MKFDS" raw $FD protocol=$PROTOCOL; }
+    local protocol=$1
+
+    coproc MKFDS { "$TS_HELPER_MKFDS" raw $FD protocol=$protocol; }
     if read -r -u "${MKFDS[0]}" PID; then
        ${TS_CMD_LSFD} -n \
-                      -o ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL \
+                      -o ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW \
                       -p "${PID}" -Q "${EXPR}"
-       echo 'ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL': $?
+       echo 'ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET.LADDR,INET.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW': $?
        echo DONE >&"${MKFDS[1]}"
     fi
 
     wait "${MKFDS_PID}"
+}
+
+{
+    # 5 is only in /etc/protocol
+    # 6 is in linux/in.h.
+    # 61 is not in neither sources.
+    for p in 5 6 61; do
+       test_lsfd_raw $p
+    done
 } > "$TS_OUTPUT" 2>&1
 
 ts_finalize
index 1eddea28f94be8ce96d0bbfe1e80133d67927995..d50a5d87316fc3550bdf749cad8b942e3a67d7d8 100755 (executable)
@@ -32,19 +32,30 @@ ts_cd "$TS_OUTDIR"
 PID=
 FD=3
 EXPR='(TYPE == "RAWv6") and (FD == 3)'
-PROTOCOL=5
 
+test_lsfd_raw6()
 {
-    coproc MKFDS { "$TS_HELPER_MKFDS" raw6 $FD protocol=$PROTOCOL; }
+    local protocol=$1
+
+    coproc MKFDS { "$TS_HELPER_MKFDS" raw6 $FD protocol=$protocol; }
     if read -r -u "${MKFDS[0]}" PID; then
        ${TS_CMD_LSFD} -n \
-                      -o ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL \
+                      -o ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW \
                       -p "${PID}" -Q "${EXPR}"
-       echo 'ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL': $?
+       echo 'ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,INET6.LADDR,INET6.RADDR,RAW.PROTOCOL,RAW.PROTOCOL.RAW': $?
        echo DONE >&"${MKFDS[1]}"
     fi
 
     wait "${MKFDS_PID}"
+}
+
+{
+    # 5 is only in /etc/protocol
+    # 6 is in linux/in.h.
+    # 61 is not in neither sources.
+    for p in 5 6 61; do
+       test_lsfd_raw6 $p
+    done
 } > "$TS_OUTPUT" 2>&1
 
 ts_finalize